--- blogd.c +++ blogd.c 2006-08-10 18:41:55.000000000 +0200 @@ -155,25 +155,60 @@ static void reset_signal(int sig, struct * To be able to reconnect to real tty on EIO */ static char * tty; +static char * second; static void reconnect(int fd) { int newfd = -1; - if ((newfd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY)) < 0) - error("can not open %s: %s\n", tty, strerror(errno)); - - if (newfd != 1) - dup2(newfd, 1); - if (newfd != 2) - dup2(newfd, 2); - - if (fd == 1 || fd == 2) - goto out; - if (newfd != fd) - dup2(newfd, fd); -out: - if (newfd > 2) - close(newfd); + switch (fd) { + case 0: /* Standard in */ + + if (tty == (char*)0) + break; + + if ((newfd = open(tty, O_RDWR|O_NONBLOCK|O_NOCTTY)) < 0) + error("can not open %s: %s\n", tty, strerror(errno)); + + if (newfd != 0) { + dup2(newfd, 0); + close(newfd); + } + + break; + + case 1: /* Standard out */ + case 2: /* Standard error */ + + if (tty == (char*)0) + break; + + if ((newfd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY)) < 0) + error("can not open %s: %s\n", tty, strerror(errno)); + + if (newfd != 1) + dup2(newfd, 1); + if (newfd != 2) + dup2(newfd, 2); + if (newfd > 2) + close(newfd); + + break; + + default: /* IO of second console */ + + if (second == (char*)0) + break; + + if ((newfd = open(second, O_WRONLY|O_NONBLOCK|O_NOCTTY)) < 0) + error("can not open %s: %s\n", second, strerror(errno)); + + if (newfd != fd) { + dup2(newfd, fd); + close(newfd); + } + + break; + } } /* @@ -181,7 +216,7 @@ out: */ int main(int argc, char *argv[]) { - int fd, flags; + int fd, fd2, flags; int ptm, pts, cntrtty = 1; pid_t pid, ppid = getppid(); char ptsname[NAME_MAX+1]; @@ -198,7 +233,7 @@ int main(int argc, char *argv[]) if (argc == 2) tty = argv[1]; else - tty = fetchtty(getpid(), ppid); + tty = fetchtty(getpid(), ppid, NULL); if (!tty || !*tty) error("can not discover real system console tty, boot logging disabled.\n"); @@ -241,6 +276,38 @@ int main(int argc, char *argv[]) if (!w.ws_col) w.ws_row = 80; + fd2 = -1; + do { + + if ((second = secondtty(tty)) == (char*)0) + break; + + if ((fd2 = open(second, O_WRONLY|O_NONBLOCK|O_NOCTTY)) < 0) { + warn("can not open %s: %s\n", second, strerror(errno)); + goto out; + } + + if ((flags = fcntl(fd2, F_GETFL)) < 0) { + warn("can not get terminal flags of %s: %s\n", second, strerror(errno)); + close(fd2); + fd2 = -1; + goto out; + } + + flags &= ~(O_NONBLOCK); + flags |= O_NOCTTY; + if (fcntl(fd2, F_SETFL, flags) < 0) { + warn("can not set terminal flags of %s: %s\n", second, strerror(errno)); + close(fd2); + fd2 = -1; + goto out; + } + + out: + free(second); + + } while (0); + if (openpty(&ptm, &pts, ptsname, &t, &w) < 0) error("can not open pty/tty pair: %s\n", strerror(errno)); @@ -268,12 +335,15 @@ int main(int argc, char *argv[]) dup2(fd, 1); dup2(fd, 2); close(ptm); - close(fd); + if (fd > 2) + close(fd); break; case -1: close(pts); close(ptm); close(fd); + if (fd2 > 0) + close(fd2); error("can not fork to become daemon: %s\n", strerror(errno)); break; default: @@ -282,12 +352,13 @@ int main(int argc, char *argv[]) close(pts); close(ptm); close(fd); + if (fd2 > 0) + close(fd2); fprintf(stdout, "\rBoot logging started on %s(%s) at %.24s\n", tty, name, stt); fflush(stdout); exit(0); } - free(name); - prepareIO(reconnect, pidfile, 0, 1); + prepareIO(reconnect, pidfile, 0, 1, fd2); while (!signaled) safeIO(); @@ -297,6 +368,10 @@ int main(int argc, char *argv[]) if (!cntrtty) kill(ppid, SIGCONT); + if (fd2 > 0) { + (void)tcflush(fd2, TCOFLUSH); + close(fd2); + } (void)tcflush(1, TCOFLUSH); close(1); (void)tcflush(2, TCOFLUSH); --- libconsole.c +++ libconsole.c 2006-08-10 18:41:23.000000000 +0200 @@ -158,41 +158,52 @@ static void (*vc_reconnect)(int fd) = NU static inline void safeout (int fd, const char *ptr, size_t s) { int saveerr = errno; - static int repeated; + int repeated = 0; + static int eiocount; - repeated = 0; while (s > 0) { ssize_t p = write (fd, ptr, s); if (p < 0) { - if (repeated++ > 1000) - error("Repeated error on writing to fd %d: %s\n", fd, STRERR); - if (errno == EPIPE) + if (errno == EPIPE) { + warn("error on writing to fd %d: %s\n", fd, STRERR); exit (0); + } if (errno == EINTR) { errno = 0; continue; } if (errno == EAGAIN) { /* Kernel 2.6 seems todo this very often */ + int ret; fd_set check; - struct timeval two = {2, 0}; - errno = 0; + if (repeated++ > 1000) + error("repeated error on writing to fd %d: %s\n", fd, STRERR); + FD_ZERO (&check); FD_SET (fd, &check); /* Avoid high load: wait upto two seconds if system is not ready */ - select(fd + 1, (fd_set*)0, &check, (fd_set*)0, &two); errno = 0; + do { + struct timeval two = {2, 0}; + ret = select(fd + 1, (fd_set*)0, &check, (fd_set*)0, &two); + + } while ((ret < 0) && (errno == EINTR)); + + if (ret < 0) + error("can not write to fd %d: %s\n", fd, STRERR); + errno = 0; continue; } - if (errno == EIO && vc_reconnect) { + if (errno == EIO) { + if ((eiocount++ > 10) || !vc_reconnect) + error("can not write to fd %d: %s\n", fd, STRERR); (*vc_reconnect)(fd); - vc_reconnect = NULL; errno = 0; continue; } - error("Can not write to fd %d: %s\n", fd, STRERR); + error("can not write to fd %d: %s\n", fd, STRERR); } repeated = 0; ptr += p; @@ -267,6 +278,7 @@ out: */ static FILE * flog = NULL; static int fdwrite = -1; +static int fdsec = -1; static int fdread = -1; static int fdfifo = -1; @@ -714,12 +726,13 @@ static void *action(void *dummy) static void (*rw_connect)(void) = NULL; static const char *fifo_name = _PATH_BLOG_FIFO; -void prepareIO(void (*rfunc)(int), void (*cfunc)(void), const int in, const int out) +void prepareIO(void (*rfunc)(int), void (*cfunc)(void), const int in, const int out, const int second) { vc_reconnect = rfunc; rw_connect = cfunc; fdread = in; fdwrite = out; + fdsec = second; if (fifo_name && fdfifo < 0) { struct stat st; @@ -729,7 +742,7 @@ void prepareIO(void (*rfunc)(int), void (void)mkfifo(fifo_name, 0600); errno = 0; if (!stat(fifo_name, &st) && S_ISFIFO(st.st_mode)) { - if ((fdfifo = open(fifo_name, O_RDWR)) < 0) + if ((fdfifo = open(fifo_name, O_RDWR|O_NOCTTY)) < 0) warn("can not open named fifo %s: %s\n", fifo_name, STRERR); } } @@ -769,12 +782,17 @@ static void more_input (struct timeval * const ssize_t cnt = safein(fdread, (char*)trans, sizeof(trans)); if (cnt > 0) { - parselog(trans, cnt); /* Parse and make copy of the input */ + parselog(trans, cnt); /* Parse and make copy of the input */ + + safeout(fdwrite, (char*)trans, cnt); /* Write copy of input to real tty */ + (void)tcdrain(fdwrite); - safeout(fdwrite, (char*)trans, cnt); /* Write copy of input to real tty */ - tcdrain(fdwrite); + if (fdsec > 0) { + safeout(fdsec, (char*)trans, cnt); /* Write copy of input to second tty */ + (void)tcdrain(fdsec); + } - flushlog(); + flushlog(); } } @@ -883,6 +901,8 @@ void closeIO(void) } else warn("no message logging because /var file system is not accessible\n"); (void)tcdrain(fdwrite); /* Hold in sync with console */ + if (fdsec > 0) + (void)tcdrain(fdsec); /* Hold in sync with second console */ do { /* @@ -926,6 +946,8 @@ void closeIO(void) flog = NULL; xout: (void)tcdrain(fdwrite); + if (fdsec > 0) + (void)tcdrain(fdsec); return; } @@ -941,7 +963,7 @@ static void ctty(pid_t pid, unsigned int int fd; sprintf(fetched, "/proc/%d/stat", (int)pid); - if ((fd = open(fetched, O_RDONLY)) < 0) + if ((fd = open(fetched, O_RDONLY|O_NOCTTY)) < 0) error("can not open(%s): %s\n", fetched, STRERR); cnt = safein(fd, fetched, sizeof(fetched)); close(fd); @@ -1034,12 +1056,11 @@ static int checkdev(char ** retname, uns int found = 0; struct dirent * d; struct stat st; - char * name = NULL; static int deep; memset(&st, 0, sizeof(struct stat)); while ((d = readdir(dev))) { - name = d->d_name; + char * name = d->d_name; if (*name == '.') continue; @@ -1143,6 +1164,14 @@ static int checkdev(char ** retname, uns } found++; + + /* + * Allocate memory to be able to return several + * different buffers for different files names. + */ + name = strdup(name); + if (!name) + error("checkdev(): %s\n", STRERR); *retname = name; break; } @@ -1151,7 +1180,7 @@ static int checkdev(char ** retname, uns } /* main routine to fetch tty */ -char * fetchtty(const pid_t pid, const pid_t ppid) +char * fetchtty(const pid_t pid, const pid_t ppid, unsigned int *mjmi) { unsigned int tty = 0, found = 0; char * name = NULL; @@ -1167,14 +1196,15 @@ char * fetchtty(const pid_t pid, const p if (!(name = ttyname(0)) || !strcmp(name, "/dev/console")) tty = fallback(pid, ppid); else { - strcpy(lnk, name); - free(name); - name = lnk; + name = strdup(name); + if (!name) + error("fetchtty(): %s\n", STRERR); goto out; } #ifdef TIOCGDEV } #endif + if (mjmi) *mjmi = tty; if (!(dev = opendir("/dev"))) error("can not opendir(/dev): %s\n", STRERR); @@ -1186,8 +1216,131 @@ char * fetchtty(const pid_t pid, const p if (!name) goto out; - if (!found) - *name = '\0'; + if (!found) { + free(name); + name = (char*)0; + } out: return name; } + +/* Do we have some more system console around? */ +char * secondtty(char * compare) +{ + char buf[1024], *ptr, *shcmp, *res = (char*)0; + unsigned int tty = 0, found = 0; + int fd = -1, len; + char * name = (char*)0; + DIR * dev; + + if ((fd = open("/proc/cmdline", O_RDONLY|O_NOCTTY)) < 0) { + warn("can not open /proc/cmdline\n"); + goto out; + } + + if ((len = read(fd, buf, sizeof(buf) - 1)) < 0) { + warn("can not read /proc/cmdline\n"); + close(fd); + goto out; + } + close(fd); + + if (len == 0) + goto out; + + shcmp = compare; + if (!strncmp(shcmp, "/dev/", 5)) + shcmp += 5; + + /* + * Check for e.g. /dev/tty[1-9] which is equal to /dev/tty0 + */ + if (!strncmp(shcmp, "tty", 3)) { + size_t len = strspn(shcmp + 3, "123456789"); + + if (strlen(shcmp) == len + 3) { + compare = "/dev/tty0"; + shcmp = "tty0"; + } + } + + ptr = &buf[len]; + while (ptr >= &buf[0]) { + if (*ptr == ',' || *ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') { + *ptr-- = 0; + continue; + } + if (*ptr == 'c' && !strncmp(ptr, "console=", 8)) { + char * console = ptr + 8; + + if (!strncmp(console, "/dev/", 5)) + console += 5; + + /* + * Compare known console tty with that of the kernel command + * line. If already known skip this console tty and search + * for the next one. + */ + if (strcmp(shcmp, console)) { + res = console; /* New device not identical to tty */ + break; + } + } + ptr--; + } + + if (!res) + goto out; + + if (!(dev = opendir("/dev"))) + error("can not opendir(/dev): %s\n", STRERR); + pushd("/dev"); + + /* Open second console e.g. /dev/tty0 */ + if ((fd = open(res, O_RDWR|O_NONBLOCK|O_NOCTTY)) < 0) + goto out; + + /* + * We do this because if we would write out the buffered + * messages to e.g. /dev/tty0 after this we would (re)read + * those and buffer them again which leads to an endless loop. + */ +#ifdef TIOCGDEV + if (ioctl (fd, TIOCGDEV, &tty) < 0) { + if (errno == EINVAL && !getenv("NOTIOCGDEV")) + warn("Warning: the ioctl TIOCGDEV is not known by the kernel\n"); + close(fd); + popd(); + closedir(dev); + goto out; + } +#else +# error The ioctl TIOCGDEV is not defined (SuSE TIOCGDEV patch is missed) +#endif + close(fd); + + /* Try to open the real device e.g. /dev/tty1 */ + found = checkdev(&name, tty, dev); + + popd(); + closedir(dev); + + if (!name) + goto out; + + if (!found) { + free(name); + name = (char*)0; + goto out; + } + + if (!strcmp(compare, name)) { + free(name); + name = (char*)0; + goto out; /* Already in use */ + } + + return name; +out: + return (char*)0; +} --- libconsole.h +++ libconsole.h 2006-08-10 18:41:28.000000000 +0200 @@ -1,6 +1,7 @@ extern void pushd(const char * path); extern void popd(void); -extern char * fetchtty(const pid_t pid, const pid_t ppid); -extern void prepareIO(void (*rfunc)(int), void (*cfunc)(void), const int in, const int out); +extern char * fetchtty(const pid_t pid, const pid_t ppid, unsigned int *mjmi); +extern char * secondtty(char * compare); +extern void prepareIO(void (*rfunc)(int), void (*cfunc)(void), const int in, const int out, const int second); extern void safeIO (void); extern void closeIO(void); --- showconsole.8 +++ showconsole.8 2006-08-10 18:55:37.000000000 +0200 @@ -16,6 +16,7 @@ Setconsole \- sets the underlying tty of .SH SYNOPSIS .\" .B showconsole +.RI [ -n ] .PP .B setconsole /dev/tty < /dev/console .SH DESCRIPTION @@ -38,6 +39,15 @@ with and exactly one argument, a valid character device is given. \." +.SH OPTIONS +.TP +.B \-n +Return the major and minor device numbers instead of +the device file name. This can be used to asked the +kernel for the major and minor device numbers of a not +existing device file in +.IR /dev . +\." .SH BUGS .B showconsole needs a mounted --- showconsole.c +++ showconsole.c 2006-08-10 18:51:15.000000000 +0200 @@ -51,7 +51,7 @@ void warn (const char *fmt, ...) */ int main(int argc, char *argv[]) { - char * tty = NULL; + char * tty = NULL, numeric = 0; myname = basename(*argv); if (!strcmp(myname, "setconsole")) { @@ -73,8 +73,30 @@ int main(int argc, char *argv[]) close(fdc); goto out; } - tty = fetchtty(getpid(), getppid()); - if (tty) + + if (argc == 2) { + const char* opt = argv[1]; + if (opt && *opt++ == '-' && *opt++ == 'n' && *opt == '\0') + numeric++; + else + error("Usage: %s [-n]\n", myname); + } else if (argc > 2) + error("Usage: %s [-n]\n", myname); + + if (numeric) { + unsigned int dev = 0; + (void)fetchtty(getpid(), getppid(), &dev); + + if (dev) + printf("%u %u\n", major(dev), minor(dev)); + else { + error("real tty unknown\n"); + fprintf(stderr, "real tty unknown\n"); + } + goto out; + } + + if ((tty = fetchtty(getpid(), getppid(), NULL))) printf("%s\n", tty); else { error("real tty unknown\n");