2009-09-10 08:28:49 +00:00
|
|
|
/*
|
|
|
|
* 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 (C) 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.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
* *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>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char *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;
|
|
|
|
int createlogfile = 0;
|
|
|
|
int syncalot = 0;
|
|
|
|
|
|
|
|
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[] = {
|
|
|
|
{ "ttyB", "/dev/ttyB%s", NULL },
|
|
|
|
{ "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", "ttyB0", NULL };
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Catch signals.
|
|
|
|
*/
|
|
|
|
void handler(int sig)
|
|
|
|
{
|
|
|
|
got_signal = sig;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan /dev and find the device name.
|
|
|
|
*/
|
2011-03-15 13:16:55 +00:00
|
|
|
static int findtty(char *res, const char *startdir, size_t rlen, dev_t dev)
|
2009-09-10 08:28:49 +00:00
|
|
|
{
|
|
|
|
DIR *dir;
|
|
|
|
struct dirent *ent;
|
2009-11-22 22:21:02 +00:00
|
|
|
int r = -1;
|
2009-09-10 08:28:49 +00:00
|
|
|
|
2014-02-07 11:06:57 +00:00
|
|
|
if ((dir = opendir(startdir)) == NULL) {
|
2009-11-22 22:21:02 +00:00
|
|
|
int msglen = strlen(startdir) + 11;
|
|
|
|
char *msg = malloc(msglen);
|
|
|
|
snprintf(msg, msglen, "bootlogd: %s", startdir);
|
|
|
|
perror(msg);
|
|
|
|
free(msg);
|
2009-09-10 08:28:49 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
while ((ent = readdir(dir)) != NULL) {
|
2014-02-07 11:06:57 +00:00
|
|
|
struct stat st;
|
|
|
|
int pathlen = strlen(startdir) + strlen(ent->d_name) + 2;
|
|
|
|
char *path = malloc(pathlen);
|
|
|
|
snprintf(path, pathlen, "%s/%s", startdir, ent->d_name);
|
|
|
|
|
|
|
|
if (lstat(path, &st) != 0) {
|
|
|
|
free(path);
|
2009-09-10 08:28:49 +00:00
|
|
|
continue;
|
2014-02-07 11:06:57 +00:00
|
|
|
}
|
2009-11-22 22:21:02 +00:00
|
|
|
if (S_ISDIR(st.st_mode)
|
|
|
|
&& 0 != strcmp(".", ent->d_name)
|
|
|
|
&& 0 != strcmp("..", ent->d_name)) {
|
|
|
|
r = findtty(res, path, rlen, dev);
|
|
|
|
if (0 == r) { /* device found, return */
|
2014-02-07 11:06:57 +00:00
|
|
|
free(path);
|
2009-11-22 22:21:02 +00:00
|
|
|
closedir(dir);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-02-07 11:06:57 +00:00
|
|
|
free(path);
|
2009-11-22 22:21:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
2014-02-07 11:06:57 +00:00
|
|
|
free(path);
|
|
|
|
path = NULL;
|
2009-09-10 08:28:49 +00:00
|
|
|
if (!S_ISCHR(st.st_mode))
|
|
|
|
continue;
|
|
|
|
if (st.st_rdev == dev) {
|
2011-03-15 13:16:55 +00:00
|
|
|
if ( (strlen(ent->d_name) + strlen(startdir) + 1) >= rlen) {
|
2009-11-22 22:21:02 +00:00
|
|
|
fprintf(stderr, "bootlogd: console device name too long\n");
|
|
|
|
closedir(dir);
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
snprintf(res, rlen, "%s/%s", startdir, ent->d_name);
|
|
|
|
closedir(dir);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-09-10 08:28:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2009-11-22 22:22:09 +00:00
|
|
|
if (!found) return -1;
|
2009-09-10 08:28:49 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2011-03-15 13:16:55 +00:00
|
|
|
int isconsole(char *s, char *res, size_t rlen)
|
2009-09-10 08:28:49 +00:00
|
|
|
{
|
|
|
|
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).
|
|
|
|
*/
|
2011-03-15 13:16:55 +00:00
|
|
|
int consolename(char *res, size_t rlen)
|
2009-09-10 08:28:49 +00:00
|
|
|
{
|
|
|
|
#ifdef TIOCGDEV
|
|
|
|
unsigned int kdev;
|
|
|
|
#endif
|
2010-04-27 19:43:17 +00:00
|
|
|
struct stat st;
|
|
|
|
int n;
|
|
|
|
#ifdef __linux__
|
2009-09-10 08:28:49 +00:00
|
|
|
char buf[256];
|
|
|
|
char *p;
|
2010-04-27 19:43:17 +00:00
|
|
|
struct stat st2;
|
2009-09-10 08:28:49 +00:00
|
|
|
int didmount = 0;
|
2010-04-27 19:43:17 +00:00
|
|
|
int r;
|
2009-09-10 08:28:49 +00:00
|
|
|
int fd;
|
2010-04-27 19:43:17 +00:00
|
|
|
#endif
|
2009-09-10 08:28:49 +00:00
|
|
|
|
|
|
|
fstat(0, &st);
|
|
|
|
if (major(st.st_rdev) != 5 || minor(st.st_rdev) != 1) {
|
|
|
|
/*
|
|
|
|
* Old kernel, can find real device easily.
|
|
|
|
*/
|
2009-11-22 22:21:02 +00:00
|
|
|
int r = findtty(res, "/dev", rlen, st.st_rdev);
|
|
|
|
if (0 != r)
|
|
|
|
fprintf(stderr, "bootlogd: cannot find console device "
|
|
|
|
"%d:%d under /dev\n", major(st.st_rdev), minor(st.st_rdev));
|
|
|
|
return r;
|
2009-09-10 08:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TIOCGDEV
|
2010-03-23 14:37:01 +00:00
|
|
|
# ifndef ENOIOCTLCMD
|
|
|
|
# define ENOIOCTLCMD 515
|
|
|
|
# endif
|
2009-11-22 22:21:02 +00:00
|
|
|
if (ioctl(0, TIOCGDEV, &kdev) == 0) {
|
|
|
|
int r = findtty(res, "/dev", rlen, (dev_t)kdev);
|
|
|
|
if (0 != r)
|
|
|
|
fprintf(stderr, "bootlogd: cannot find console device "
|
|
|
|
"%d:%d under /dev\n", major(kdev), minor(kdev));
|
|
|
|
return r;
|
|
|
|
}
|
2009-09-10 08:28:49 +00:00
|
|
|
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;
|
2014-01-26 13:29:43 +00:00
|
|
|
if ((n = read(fd, buf, sizeof(buf) - 1)) >= 0) {
|
2009-09-10 08:28:49 +00:00
|
|
|
r = 0;
|
2014-01-26 13:29:43 +00:00
|
|
|
buf[sizeof(buf)-1] = 0; /* enforce null termination */
|
|
|
|
} else
|
2009-09-10 08:28:49 +00:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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;
|
2009-11-22 22:23:14 +00:00
|
|
|
dosync = 1;
|
2009-09-10 08:28:49 +00:00
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
line.pos += (line.pos / 8 + 1) * 8;
|
2009-11-14 21:27:13 +00:00
|
|
|
if (line.pos >= (int)sizeof(line.buf))
|
2009-09-10 08:28:49 +00:00
|
|
|
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);
|
2009-11-14 21:27:13 +00:00
|
|
|
if (tlen && (line.pos + tlen < (int)sizeof(line.buf))) {
|
2009-09-10 08:28:49 +00:00
|
|
|
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);
|
2009-11-22 22:23:14 +00:00
|
|
|
if (syncalot) {
|
|
|
|
fdatasync(fileno(fp));
|
|
|
|
}
|
2009-09-10 08:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
outptr += olen;
|
|
|
|
if (outptr >= endptr)
|
|
|
|
outptr = ringbuf;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print usage message and exit.
|
|
|
|
*/
|
|
|
|
void usage(void)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Usage: bootlogd [-v] [-r] [-d] [-s] [-c] [-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;
|
2014-02-07 17:03:04 +00:00
|
|
|
#ifndef __linux__ /* BSD-style ioctl needs an argument. */
|
|
|
|
int on = 1;
|
|
|
|
#endif
|
2009-09-10 08:28:49 +00:00
|
|
|
|
|
|
|
fp = NULL;
|
|
|
|
logfile = LOGFILE;
|
|
|
|
pidfile = NULL;
|
|
|
|
rotate = 0;
|
|
|
|
dontfork = 0;
|
|
|
|
|
|
|
|
while ((i = getopt(argc, argv, "cdsl: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 'c':
|
|
|
|
createlogfile = 1;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
dontfork = 1;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
syncalot = 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;
|
|
|
|
}
|
|
|
|
|
2014-02-07 17:03:04 +00:00
|
|
|
#ifdef __linux__
|
2009-09-10 08:28:49 +00:00
|
|
|
(void)ioctl(0, TIOCCONS, NULL);
|
|
|
|
/* 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
|
2014-02-07 17:03:04 +00:00
|
|
|
#ifdef __linux__
|
|
|
|
if (ioctl(pts, TIOCCONS, NULL) < 0)
|
|
|
|
#else /* BSD usage of ioctl TIOCCONS. */
|
|
|
|
if (ioctl(pts, TIOCCONS, &on) < 0)
|
|
|
|
#endif
|
|
|
|
{
|
2009-09-10 08:28:49 +00:00
|
|
|
fprintf(stderr, "bootlogd: ioctl(%s, TIOCCONS): %s\n",
|
|
|
|
buf, strerror(errno));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fork and write pidfile if needed.
|
|
|
|
*/
|
|
|
|
if (!dontfork) {
|
|
|
|
pid_t child_pid = fork();
|
|
|
|
switch (child_pid) {
|
|
|
|
case -1: /* I am parent and the attempt to create a child failed */
|
|
|
|
fprintf(stderr, "bootlogd: fork failed: %s\n",
|
|
|
|
strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
break;
|
|
|
|
case 0: /* I am the child */
|
|
|
|
break;
|
|
|
|
default: /* I am parent and got child's pid */
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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 && access(logfile, F_OK) == 0) {
|
|
|
|
if (rotate) {
|
|
|
|
snprintf(buf, sizeof(buf), "%s~", logfile);
|
|
|
|
rename(logfile, buf);
|
|
|
|
}
|
|
|
|
fp = fopen(logfile, "a");
|
|
|
|
}
|
|
|
|
if (fp == NULL && createlogfile)
|
|
|
|
fp = fopen(logfile, "a");
|
|
|
|
|
|
|
|
if (inptr >= outptr)
|
|
|
|
todo = inptr - outptr;
|
|
|
|
else
|
|
|
|
todo = endptr - outptr;
|
|
|
|
if (fp && todo)
|
2009-11-14 21:27:13 +00:00
|
|
|
writelog(fp, (unsigned char *)outptr, todo);
|
2009-09-10 08:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fp) {
|
|
|
|
if (!didnl) fputc('\n', fp);
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(pts);
|
|
|
|
close(ptm);
|
|
|
|
close(realfd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|