⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bootlogd.c

📁 sysvinit--linux系统下的init
💻 C
字号:
/* * bootlogd.c	Store output from the console during bootup into a file. *		The file is usually located on the /var partition, and *		gets written (and fsynced) as soon as possible. * * Version:	@(#)bootlogd  2.86pre  12-Jan-2004  miquels@cistron.nl * * Bugs:	Uses openpty(), only available in glibc. Sorry. * *		This file is part of the sysvinit suite, *		Copyright 1991-2004 Miquel van Smoorenburg. * *		This program is free software; you can redistribute it and/or *		modify it under the terms of the GNU General Public License *		as published by the Free Software Foundation; either version *		2 of the License, or (at your option) any later version. * *				*NOTE* *NOTE* *NOTE* *			This is a PROOF OF CONCEPT IMPLEMENTATION * *		I have bigger plans for Debian, but for now *		this has to do ;) * */#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/utsname.h>#include <time.h>#include <stdio.h>#include <errno.h>#include <malloc.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <getopt.h>#include <dirent.h>#include <fcntl.h>#include <pty.h>#include <ctype.h>#ifdef __linux__#include <sys/mount.h>#endifchar *Version = "@(#) bootlogd 2.86 03-Jun-2004 miquels@cistron.nl";#define LOGFILE	"/var/log/boot"char ringbuf[32768];char *endptr = ringbuf + sizeof(ringbuf);char *inptr  = ringbuf;char *outptr = ringbuf;int got_signal = 0;int didnl = 1;struct line {	char buf[256];	int pos;} line;/* *	Console devices as listed on the kernel command line and *	the mapping to actual devices in /dev */struct consdev {	char	*cmdline;	char	*dev1;	char	*dev2;} consdev[] = {	{ "ttySC",	"/dev/ttySC%s",		"/dev/ttsc/%s"	},	{ "ttyS",	"/dev/ttyS%s",		"/dev/tts/%s"	},	{ "tty",	"/dev/tty%s",		"/dev/vc/%s"	},	{ "hvc",	"/dev/hvc%s",		"/dev/hvc/%s"	},	{ NULL,		NULL,			NULL		},};/* *	Devices to try as console if not found on kernel command line. *	Tried from left to right (as opposed to kernel cmdline). */char *defcons[] = { "tty0", "hvc0", "ttyS0", "ttySC0", NULL };/* *	Catch signals. */void handler(int sig){	got_signal = sig;}/* *	Scan /dev and find the device name. *	Side-effect: directory is changed to /dev * *	FIXME: scan subdirectories for devfs support ? */int findtty(char *res, int rlen, dev_t dev){	DIR		*dir;	struct dirent	*ent;	struct stat	st;	int		r = 0;	if (chdir("/dev") < 0 || (dir = opendir(".")) == NULL) {		perror("bootlogd: /dev");		return -1;	}	while ((ent = readdir(dir)) != NULL) {		if (lstat(ent->d_name, &st) != 0)			continue;		if (!S_ISCHR(st.st_mode))			continue;		if (st.st_rdev == dev) {			break;		}	}	if (ent == NULL) {		fprintf(stderr, "bootlogd: cannot find console device "			"%d:%d in /dev\n", major(dev), minor(dev));		r = -1;	} else if (strlen(ent->d_name) + 5 >= rlen) {		fprintf(stderr, "bootlogd: console device name too long\n");		r = -1;	} else		snprintf(res, rlen, "/dev/%s", ent->d_name);	closedir(dir);	return r;}/* *	For some reason, openpty() in glibc sometimes doesn't *	work at boot-time. It must be a bug with old-style pty *	names, as new-style (/dev/pts) is not available at that *	point. So, we find a pty/tty pair ourself if openpty() *	fails for whatever reason. */int findpty(int *master, int *slave, char *name){	char	pty[16];	char	tty[16];	int	i, j;	int	found;	if (openpty(master, slave, name, NULL, NULL) >= 0)		return 0;	found = 0;	for (i = 'p'; i <= 'z'; i++) {		for (j = '0'; j <= 'f'; j++) {			if (j == '9' + 1) j = 'a';			sprintf(pty, "/dev/pty%c%c", i, j);			sprintf(tty, "/dev/tty%c%c", i, j);			if ((*master = open(pty, O_RDWR|O_NOCTTY)) >= 0) {				*slave = open(tty, O_RDWR|O_NOCTTY);				if (*slave >= 0) {					found = 1;					break;				}			}		}		if (found) break;	}	if (found < 0) return -1;	if (name) strcpy(name, tty);	return 0;}/* *	See if a console taken from the kernel command line maps *	to a character device we know about, and if we can open it. */int isconsole(char *s, char *res, int rlen){	struct consdev	*c;	int		l, sl, i, fd;	char		*p, *q;	sl = strlen(s);	for (c = consdev; c->cmdline; c++) {		l = strlen(c->cmdline);		if (sl <= l) continue;		p = s + l;		if (strncmp(s, c->cmdline, l) != 0 || !isdigit(*p))			continue;		for (i = 0; i < 2; i++) {			snprintf(res, rlen, i ? c->dev1 : c->dev2, p);			if ((q = strchr(res, ',')) != NULL) *q = 0;			if ((fd = open(res, O_RDONLY|O_NONBLOCK)) >= 0) {				close(fd);				return 1;			}		}	}	return 0;}/* *	Find out the _real_ console. Assume that stdin is connected to *	the console device (/dev/console). */int consolename(char *res, int rlen){#ifdef TIOCGDEV	unsigned int	kdev;#endif	struct stat	st, st2;	char		buf[256];	char		*p;	int		didmount = 0;	int		n, r;	int		fd;	fstat(0, &st);	if (major(st.st_rdev) != 5 || minor(st.st_rdev) != 1) {		/*		 *	Old kernel, can find real device easily.		 */		return findtty(res, rlen, st.st_rdev);	}#ifdef TIOCGDEV	if (ioctl(0, TIOCGDEV, &kdev) == 0)		return findtty(res, rlen, (dev_t)kdev);	if (errno != ENOIOCTLCMD) return -1;#endif#ifdef __linux__	/*	 *	Read /proc/cmdline.	 */	stat("/", &st);	if (stat("/proc", &st2) < 0) {		perror("bootlogd: /proc");		return -1;	}	if (st.st_dev == st2.st_dev) {		if (mount("proc", "/proc", "proc", 0, NULL) < 0) {			perror("bootlogd: mount /proc");			return -1;		}		didmount = 1;	}	n = 0;	r = -1;	if ((fd = open("/proc/cmdline", O_RDONLY)) < 0) {		perror("bootlogd: /proc/cmdline");	} else {		buf[0] = 0;		if ((n = read(fd, buf, sizeof(buf) - 1)) >= 0)			r = 0;		else			perror("bootlogd: /proc/cmdline");		close(fd);	}	if (didmount) umount("/proc");	if (r < 0) return r;	/*	 *	OK, so find console= in /proc/cmdline.	 *	Parse in reverse, opening as we go.	 *	 *	Valid console devices: ttySC, ttyS, tty, hvc.	 */	p = buf + n;	*p-- = 0;	r = -1;	while (p >= buf) {		if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {			*p-- = 0;			continue;		}		if (strncmp(p, "console=", 8) == 0 &&		    isconsole(p + 8, res, rlen)) {			r = 0;			break;		}		p--;	}	if (r == 0) return r;#endif	/*	 *	Okay, no console on the command line -	 *	guess the default console.	 */	for (n = 0; defcons[n]; n++)		if (isconsole(defcons[n], res, rlen))			return 0;	fprintf(stderr, "bootlogd: cannot deduce real console device\n");	return -1;}/* *	Write data and make sure it's on disk. */void writelog(FILE *fp, unsigned char *ptr, int len){	time_t		t;	char		*s;	char		tmp[8];	int		olen = len;	int		dosync = 0;	int		tlen;	while (len > 0) {		tmp[0] = 0;		if (didnl) {			time(&t);			s = ctime(&t);			fprintf(fp, "%.24s: ", s);			didnl = 0;		}		switch (*ptr) {			case 27: /* ESC */				strcpy(tmp, "^[");				break;			case '\r':				line.pos = 0;				break;			case 8: /* ^H */				if (line.pos > 0) line.pos--;				break;			case '\n':				didnl = 1;				dosync = 1;				break;			case '\t':				line.pos += (line.pos / 8 + 1) * 8;				if (line.pos >= sizeof(line.buf))					line.pos = sizeof(line.buf) - 1;				break;			case  32 ... 127:			case 161 ... 255:				tmp[0] = *ptr;				tmp[1] = 0;				break;			default:				sprintf(tmp, "\\%03o", *ptr);				break;		}		ptr++;		len--;		tlen = strlen(tmp);		if (tlen && (line.pos + tlen < sizeof(line.buf))) {			memcpy(line.buf + line.pos, tmp, tlen);			line.pos += tlen;		}		if (didnl) {			fprintf(fp, "%s\n", line.buf);			memset(&line, 0, sizeof(line));		}	}	if (dosync) {		fflush(fp);		fdatasync(fileno(fp));	}	outptr += olen;	if (outptr >= endptr)		outptr = ringbuf;}/* *	Print usage message and exit. */void usage(void){	fprintf(stderr, "Usage: bootlogd [-v] [-r] [-d] [-p pidfile] [-l logfile]\n");	exit(1);}int open_nb(char *buf){	int	fd, n;	if ((fd = open(buf, O_WRONLY|O_NONBLOCK|O_NOCTTY)) < 0)		return -1;	n = fcntl(fd, F_GETFL);	n &= ~(O_NONBLOCK);	fcntl(fd, F_SETFL, n);	return fd;}/* *	We got a write error on the real console. If its an EIO, *	somebody hung up our filedescriptor, so try to re-open it. */int write_err(int pts, int realfd, char *realcons, int e){	int	fd;	if (e != EIO) {werr:		close(pts);		fprintf(stderr, "bootlogd: writing to console: %s\n",			strerror(e));		return -1;	}	close(realfd);	if ((fd = open_nb(realcons)) < 0)		goto werr;	return fd;}int main(int argc, char **argv){	FILE		*fp;	struct timeval	tv;	fd_set		fds;	char		buf[1024];	char		realcons[1024];	char		*p;	char		*logfile;	char		*pidfile;	int		rotate;	int		dontfork;	int		ptm, pts;	int		realfd;	int		n, m, i;	int		todo;	fp = NULL;	logfile = LOGFILE;	pidfile = NULL;	rotate = 0;	dontfork = 0;	while ((i = getopt(argc, argv, "dl:p:rv")) != EOF) switch(i) {		case 'l':			logfile = optarg;			break;		case 'r':			rotate = 1;			break;		case 'v':			printf("%s\n", Version);			exit(0);			break;		case 'p':			pidfile = optarg;			break;		case 'd':			dontfork = 1;			break;		default:			usage();			break;	}	if (optind < argc) usage();	signal(SIGTERM, handler);	signal(SIGQUIT, handler);	signal(SIGINT,  handler);	signal(SIGTTIN,  SIG_IGN);	signal(SIGTTOU,  SIG_IGN);	signal(SIGTSTP,  SIG_IGN);	/*	 *	Open console device directly.	 */	if (consolename(realcons, sizeof(realcons)) < 0)		return 1;	if (strcmp(realcons, "/dev/tty0") == 0)		strcpy(realcons, "/dev/tty1");	if (strcmp(realcons, "/dev/vc/0") == 0)		strcpy(realcons, "/dev/vc/1");	if ((realfd = open_nb(realcons)) < 0) {		fprintf(stderr, "bootlogd: %s: %s\n", buf, strerror(errno));		return 1;	}	/*	 *	Grab a pty, and redirect console messages to it.	 */	ptm = -1;	pts = -1;	buf[0] = 0;	if (findpty(&ptm, &pts, buf) < 0) {		fprintf(stderr,			"bootlogd: cannot allocate pseudo tty: %s\n",			strerror(errno));		return 1;	}	(void)ioctl(0, TIOCCONS, NULL);#if 1	/* Work around bug in 2.1/2.2 kernels. Fixed in 2.2.13 and 2.3.18 */	if ((n = open("/dev/tty0", O_RDWR)) >= 0) {		(void)ioctl(n, TIOCCONS, NULL);		close(n);	}#endif	if (ioctl(pts, TIOCCONS, NULL) < 0) {		fprintf(stderr, "bootlogd: ioctl(%s, TIOCCONS): %s\n",			buf, strerror(errno));		return 1;	}	/*	 *	Fork and write pidfile if needed.	 */	if (!dontfork) {		if (fork())			exit(1);		setsid();	}	if (pidfile) {		unlink(pidfile);		if ((fp = fopen(pidfile, "w")) != NULL) {			fprintf(fp, "%d\n", (int)getpid());			fclose(fp);		}		fp = NULL;	}	/*	 *	Read the console messages from the pty, and write	 *	to the real console and the logfile.	 */	while (!got_signal) {		/*		 *	We timeout after 5 seconds if we still need to		 *	open the logfile. There might be buffered messages		 *	we want to write.		 */		tv.tv_sec = 0;		tv.tv_usec = 500000;		FD_ZERO(&fds);		FD_SET(ptm, &fds);		if (select(ptm + 1, &fds, NULL, NULL, &tv) == 1) {			/*			 *	See how much space there is left, read.			 */			if ((n = read(ptm, inptr, endptr - inptr)) >= 0) {				/*				 *	Write data (in chunks if needed)				 *	to the real output device.				 */				m = n;				p = inptr;				while (m > 0) {					i = write(realfd, p, m);					if (i >= 0) {						m -= i;						p += i;						continue;					}					/*					 *	Handle EIO (somebody hung					 *	up our filedescriptor)					 */					realfd = write_err(pts, realfd,						realcons, errno);					if (realfd >= 0) continue;					got_signal = 1; /* Not really */					break;				}				/*				 *	Increment buffer position. Handle				 *	wraps, and also drag output pointer				 *	along if we cross it.				 */				inptr += n;				if (inptr - n < outptr && inptr > outptr)					outptr = inptr;				if (inptr >= endptr)					inptr = ringbuf;				if (outptr >= endptr)					outptr = ringbuf;			}		}		/*		 *	Perhaps we need to open the logfile.		 */		if (fp == NULL && rotate && access(logfile, F_OK) == 0) {			snprintf(buf, sizeof(buf), "%s~", logfile);			rename(logfile, buf);		}		if (fp == NULL)			fp = fopen(logfile, "a");		if (inptr >= outptr)			todo = inptr - outptr;		else			todo = endptr - outptr;		if (fp && todo)			writelog(fp, outptr, todo);	}	if (fp) {		if (!didnl) fputc('\n', fp);		fclose(fp);	}	close(pts);	close(ptm);	close(realfd);	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -