]>
git.wh0rd.org - sysvinit.git/blob - src/bootlogd.c
e2eb4d8a68faf635e1f9e7329b8d290834a05928
2 * bootlogd.c Store output from the console during bootup into a file.
3 * The file is usually located on the /var partition, and
4 * gets written (and fsynced) as soon as possible.
6 * Version: @(#)bootlogd 2.86pre 12-Jan-2004 miquels@cistron.nl
8 * Bugs: Uses openpty(), only available in glibc. Sorry.
10 * This file is part of the sysvinit suite,
11 * Copyright (C) 1991-2004 Miquel van Smoorenburg.
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 * *NOTE* *NOTE* *NOTE*
28 * This is a PROOF OF CONCEPT IMPLEMENTATION
30 * I have bigger plans for Debian, but for now
35 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/utsname.h>
54 #include <sys/mount.h>
57 char *Version
= "@(#) bootlogd 2.86 03-Jun-2004 miquels@cistron.nl";
59 #define LOGFILE "/var/log/boot"
62 char *endptr
= ringbuf
+ sizeof(ringbuf
);
63 char *inptr
= ringbuf
;
64 char *outptr
= ringbuf
;
68 int createlogfile
= 0;
77 * Console devices as listed on the kernel command line and
78 * the mapping to actual devices in /dev
85 { "ttyB", "/dev/ttyB%s", NULL
},
86 { "ttySC", "/dev/ttySC%s", "/dev/ttsc/%s" },
87 { "ttyS", "/dev/ttyS%s", "/dev/tts/%s" },
88 { "tty", "/dev/tty%s", "/dev/vc/%s" },
89 { "hvc", "/dev/hvc%s", "/dev/hvc/%s" },
94 * Devices to try as console if not found on kernel command line.
95 * Tried from left to right (as opposed to kernel cmdline).
97 char *defcons
[] = { "tty0", "hvc0", "ttyS0", "ttySC0", "ttyB0", NULL
};
102 void handler(int sig
)
109 * Scan /dev and find the device name.
111 static int findtty(char *res
, int rlen
, dev_t dev
)
117 char *olddir
= getcwd(NULL
, 0);
119 if (chdir(startdir
) < 0 || (dir
= opendir(".")) == NULL
) {
120 int msglen
= strlen(startdir
) + 11;
121 char *msg
= malloc(msglen
);
122 snprintf(msg
, msglen
, "bootlogd: %s", startdir
);
128 while ((ent
= readdir(dir
)) != NULL
) {
129 if (lstat(ent
->d_name
, &st
) != 0)
131 if (S_ISDIR(st
.st_mode
)
132 && 0 != strcmp(".", ent
->d_name
)
133 && 0 != strcmp("..", ent
->d_name
)) {
134 char *path
= malloc(rlen
);
135 snprintf(path
, rlen
, "%s/%s", startdir
, ent
->d_name
);
136 r
= findtty(res
, path
, rlen
, dev
);
138 if (0 == r
) { /* device found, return */
145 if (!S_ISCHR(st
.st_mode
))
147 if (st
.st_rdev
== dev
) {
148 if (strlen(ent
->d_name
) + strlen(startdir
) + 1 >= rlen
) {
149 fprintf(stderr
, "bootlogd: console device name too long\n");
154 snprintf(res
, rlen
, "%s/%s", startdir
, ent
->d_name
);
168 * For some reason, openpty() in glibc sometimes doesn't
169 * work at boot-time. It must be a bug with old-style pty
170 * names, as new-style (/dev/pts) is not available at that
171 * point. So, we find a pty/tty pair ourself if openpty()
172 * fails for whatever reason.
174 int findpty(int *master
, int *slave
, char *name
)
181 if (openpty(master
, slave
, name
, NULL
, NULL
) >= 0)
186 for (i
= 'p'; i
<= 'z'; i
++) {
187 for (j
= '0'; j
<= 'f'; j
++) {
188 if (j
== '9' + 1) j
= 'a';
189 sprintf(pty
, "/dev/pty%c%c", i
, j
);
190 sprintf(tty
, "/dev/tty%c%c", i
, j
);
191 if ((*master
= open(pty
, O_RDWR
|O_NOCTTY
)) >= 0) {
192 *slave
= open(tty
, O_RDWR
|O_NOCTTY
);
201 if (!found
) return -1;
203 if (name
) strcpy(name
, tty
);
208 * See if a console taken from the kernel command line maps
209 * to a character device we know about, and if we can open it.
211 int isconsole(char *s
, char *res
, int rlen
)
219 for (c
= consdev
; c
->cmdline
; c
++) {
220 l
= strlen(c
->cmdline
);
221 if (sl
<= l
) continue;
223 if (strncmp(s
, c
->cmdline
, l
) != 0 || !isdigit(*p
))
225 for (i
= 0; i
< 2; i
++) {
226 snprintf(res
, rlen
, i
? c
->dev1
: c
->dev2
, p
);
227 if ((q
= strchr(res
, ',')) != NULL
) *q
= 0;
228 if ((fd
= open(res
, O_RDONLY
|O_NONBLOCK
)) >= 0) {
238 * Find out the _real_ console. Assume that stdin is connected to
239 * the console device (/dev/console).
241 int consolename(char *res
, int rlen
)
254 if (major(st
.st_rdev
) != 5 || minor(st
.st_rdev
) != 1) {
256 * Old kernel, can find real device easily.
258 int r
= findtty(res
, "/dev", rlen
, st
.st_rdev
);
260 fprintf(stderr
, "bootlogd: cannot find console device "
261 "%d:%d under /dev\n", major(st
.st_rdev
), minor(st
.st_rdev
));
266 if (ioctl(0, TIOCGDEV
, &kdev
) == 0) {
267 int r
= findtty(res
, "/dev", rlen
, (dev_t
)kdev
);
269 fprintf(stderr
, "bootlogd: cannot find console device "
270 "%d:%d under /dev\n", major(kdev
), minor(kdev
));
273 if (errno
!= ENOIOCTLCMD
) return -1;
278 * Read /proc/cmdline.
281 if (stat("/proc", &st2
) < 0) {
282 perror("bootlogd: /proc");
285 if (st
.st_dev
== st2
.st_dev
) {
286 if (mount("proc", "/proc", "proc", 0, NULL
) < 0) {
287 perror("bootlogd: mount /proc");
295 if ((fd
= open("/proc/cmdline", O_RDONLY
)) < 0) {
296 perror("bootlogd: /proc/cmdline");
299 if ((n
= read(fd
, buf
, sizeof(buf
) - 1)) >= 0)
302 perror("bootlogd: /proc/cmdline");
305 if (didmount
) umount("/proc");
310 * OK, so find console= in /proc/cmdline.
311 * Parse in reverse, opening as we go.
317 if (*p
== ' ' || *p
== '\t' || *p
== '\r' || *p
== '\n') {
321 if (strncmp(p
, "console=", 8) == 0 &&
322 isconsole(p
+ 8, res
, rlen
)) {
329 if (r
== 0) return r
;
333 * Okay, no console on the command line -
334 * guess the default console.
336 for (n
= 0; defcons
[n
]; n
++)
337 if (isconsole(defcons
[n
], res
, rlen
))
340 fprintf(stderr
, "bootlogd: cannot deduce real console device\n");
347 * Write data and make sure it's on disk.
349 void writelog(FILE *fp
, unsigned char *ptr
, int len
)
363 fprintf(fp
, "%.24s: ", s
);
374 if (line
.pos
> 0) line
.pos
--;
381 line
.pos
+= (line
.pos
/ 8 + 1) * 8;
382 if (line
.pos
>= (int)sizeof(line
.buf
))
383 line
.pos
= sizeof(line
.buf
) - 1;
391 sprintf(tmp
, "\\%03o", *ptr
);
398 if (tlen
&& (line
.pos
+ tlen
< (int)sizeof(line
.buf
))) {
399 memcpy(line
.buf
+ line
.pos
, tmp
, tlen
);
403 fprintf(fp
, "%s\n", line
.buf
);
404 memset(&line
, 0, sizeof(line
));
410 fdatasync(fileno(fp
));
414 if (outptr
>= endptr
)
421 * Print usage message and exit.
425 fprintf(stderr
, "Usage: bootlogd [-v] [-r] [-d] [-s] [-c] [-p pidfile] [-l logfile]\n");
429 int open_nb(char *buf
)
433 if ((fd
= open(buf
, O_WRONLY
|O_NONBLOCK
|O_NOCTTY
)) < 0)
435 n
= fcntl(fd
, F_GETFL
);
437 fcntl(fd
, F_SETFL
, n
);
443 * We got a write error on the real console. If its an EIO,
444 * somebody hung up our filedescriptor, so try to re-open it.
446 int write_err(int pts
, int realfd
, char *realcons
, int e
)
453 fprintf(stderr
, "bootlogd: writing to console: %s\n",
458 if ((fd
= open_nb(realcons
)) < 0)
464 int main(int argc
, char **argv
)
487 while ((i
= getopt(argc
, argv
, "cdsl:p:rv")) != EOF
) switch(i
) {
495 printf("%s\n", Version
);
514 if (optind
< argc
) usage();
516 signal(SIGTERM
, handler
);
517 signal(SIGQUIT
, handler
);
518 signal(SIGINT
, handler
);
519 signal(SIGTTIN
, SIG_IGN
);
520 signal(SIGTTOU
, SIG_IGN
);
521 signal(SIGTSTP
, SIG_IGN
);
524 * Open console device directly.
526 if (consolename(realcons
, sizeof(realcons
)) < 0)
529 if (strcmp(realcons
, "/dev/tty0") == 0)
530 strcpy(realcons
, "/dev/tty1");
531 if (strcmp(realcons
, "/dev/vc/0") == 0)
532 strcpy(realcons
, "/dev/vc/1");
534 if ((realfd
= open_nb(realcons
)) < 0) {
535 fprintf(stderr
, "bootlogd: %s: %s\n", buf
, strerror(errno
));
540 * Grab a pty, and redirect console messages to it.
545 if (findpty(&ptm
, &pts
, buf
) < 0) {
547 "bootlogd: cannot allocate pseudo tty: %s\n",
552 (void)ioctl(0, TIOCCONS
, NULL
);
554 /* Work around bug in 2.1/2.2 kernels. Fixed in 2.2.13 and 2.3.18 */
555 if ((n
= open("/dev/tty0", O_RDWR
)) >= 0) {
556 (void)ioctl(n
, TIOCCONS
, NULL
);
560 if (ioctl(pts
, TIOCCONS
, NULL
) < 0) {
561 fprintf(stderr
, "bootlogd: ioctl(%s, TIOCCONS): %s\n",
562 buf
, strerror(errno
));
567 * Fork and write pidfile if needed.
570 pid_t child_pid
= fork();
572 case -1: /* I am parent and the attempt to create a child failed */
573 fprintf(stderr
, "bootlogd: fork failed: %s\n",
577 case 0: /* I am the child */
579 default: /* I am parent and got child's pid */
587 if ((fp
= fopen(pidfile
, "w")) != NULL
) {
588 fprintf(fp
, "%d\n", (int)getpid());
595 * Read the console messages from the pty, and write
596 * to the real console and the logfile.
598 while (!got_signal
) {
601 * We timeout after 5 seconds if we still need to
602 * open the logfile. There might be buffered messages
609 if (select(ptm
+ 1, &fds
, NULL
, NULL
, &tv
) == 1) {
611 * See how much space there is left, read.
613 if ((n
= read(ptm
, inptr
, endptr
- inptr
)) >= 0) {
615 * Write data (in chunks if needed)
616 * to the real output device.
621 i
= write(realfd
, p
, m
);
628 * Handle EIO (somebody hung
629 * up our filedescriptor)
631 realfd
= write_err(pts
, realfd
,
633 if (realfd
>= 0) continue;
634 got_signal
= 1; /* Not really */
639 * Increment buffer position. Handle
640 * wraps, and also drag output pointer
641 * along if we cross it.
644 if (inptr
- n
< outptr
&& inptr
> outptr
)
648 if (outptr
>= endptr
)
654 * Perhaps we need to open the logfile.
656 if (fp
== NULL
&& access(logfile
, F_OK
) == 0) {
658 snprintf(buf
, sizeof(buf
), "%s~", logfile
);
659 rename(logfile
, buf
);
661 fp
= fopen(logfile
, "a");
663 if (fp
== NULL
&& createlogfile
)
664 fp
= fopen(logfile
, "a");
667 todo
= inptr
- outptr
;
669 todo
= endptr
- outptr
;
671 writelog(fp
, (unsigned char *)outptr
, todo
);
675 if (!didnl
) fputc('\n', fp
);