diff --git a/src/Makefile b/src/Makefile index d56360e..6e0e962 100644 --- a/src/Makefile +++ b/src/Makefile @@ -64,7 +64,7 @@ endif # Additional libs for GNU libc. -ifneq ($(wildcard /usr/lib/libcrypt.a),) +ifneq ($(wildcard /usr/lib*/libcrypt.a),) LCRYPT = -lcrypt endif diff --git a/src/init.c b/src/init.c index 5fe0530..5f7a2b6 100644 --- a/src/init.c +++ b/src/init.c @@ -121,6 +121,8 @@ sig_atomic_t got_signals; /* Set if we received a signal. */ int emerg_shell = 0; /* Start emergency shell? */ int wrote_wtmp_reboot = 1; /* Set when we wrote the reboot record */ int wrote_utmp_reboot = 1; /* Set when we wrote the reboot record */ +int wrote_wtmp_rlevel = 1; /* Set when we wrote the runlevel record */ +int wrote_utmp_rlevel = 1; /* Set when we wrote the runlevel record */ int sltime = 5; /* Sleep time between TERM and KILL */ char *argv0; /* First arguments; show up in ps listing */ int maxproclen; /* Maximal length of argv[0] with \0 */ @@ -189,6 +191,8 @@ struct { { "-WU", D_WROTE_UTMP_REBOOT}, { "-ST", D_SLTIME }, { "-DB", D_DIDBOOT }, + { "-LW", D_WROTE_WTMP_RLEVEL}, + { "-LU", D_WROTE_UTMP_RLEVEL}, { "", 0 } }; struct { @@ -385,6 +389,12 @@ static CHILD *get_record(FILE *f) case D_DIDBOOT: fscanf(f, "%d\n", &did_boot); break; + case D_WROTE_WTMP_RLEVEL: + fscanf(f, "%d\n", &wrote_wtmp_rlevel); + break; + case D_WROTE_UTMP_RLEVEL: + fscanf(f, "%d\n", &wrote_utmp_rlevel); + break; default: if (cmd > 0 || cmd == C_EOF) { oops_error = -1; @@ -1004,6 +1014,14 @@ int spawn(CHILD *ch, int *res) dup(f); dup(f); } + + /* + * 4 Sep 2001, Andrea Arcangeli: + * Fix a race in spawn() that is used to deadlock init in a + * waitpid() loop: must set the childhandler as default before forking + * off the child or the chld_handler could run before the waitpid loop + * has a chance to find its zombie-child. + */ SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART); if ((pid = fork()) < 0) { initlog(L_VB, "cannot fork: %s", @@ -1729,6 +1747,8 @@ int read_level(int arg) } /* Store both the old and the new runlevel. */ + wrote_utmp_rlevel = 0; + wrote_wtmp_rlevel = 0; write_utmp_wtmp("runlevel", "~~", foo + 256*runlevel, RUN_LVL, "~"); thislevel = foo; prevlevel = runlevel; @@ -1929,6 +1949,25 @@ void re_exec(void) initlog(L_CO, "Attempt to re-exec failed"); } +/* + * Redo utmp/wtmp entries if required or requested + * Check for written records and size of utmp + */ +static +void redo_utmp_wtmp(void) +{ + struct stat ustat; + const int ret = stat(UTMP_FILE, &ustat); + + if ((ret < 0) || (ustat.st_size == 0)) + wrote_utmp_rlevel = wrote_utmp_reboot = 0; + + if ((wrote_wtmp_reboot == 0) || (wrote_utmp_reboot == 0)) + write_utmp_wtmp("reboot", "~~", 0, BOOT_TIME, "~"); + + if ((wrote_wtmp_rlevel == 0) || (wrote_wtmp_rlevel == 0)) + write_utmp_wtmp("runlevel", "~~", thislevel + 256 * prevlevel, RUN_LVL, "~"); +} /* * We got a change runlevel request through the @@ -1960,6 +1999,7 @@ void fifo_new_level(int level) if (oldlevel != 'S' && runlevel == 'S') console_stty(); if (runlevel == '6' || runlevel == '0' || runlevel == '1') console_stty(); + if (runlevel > '1' && runlevel < '6') redo_utmp_wtmp(); read_inittab(); fail_cancel(); setproctitle("init [%c]", runlevel); @@ -2243,6 +2283,8 @@ void boot_transitions() } if (loglevel > 0) { initlog(L_VB, "Entering runlevel: %c", runlevel); + wrote_utmp_rlevel = 0; + wrote_wtmp_rlevel = 0; write_utmp_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~"); thislevel = runlevel; prevlevel = oldlevel; @@ -2421,6 +2463,7 @@ int init_main() console_init(); if (!reload) { + int fd; /* Close whatever files are open, and reset the console. */ close(0); @@ -2438,7 +2481,8 @@ int init_main() * Initialize /var/run/utmp (only works if /var is on * root and mounted rw) */ - (void) close(open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)); + if ((fd = open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) >= 0) + close(fd); /* * Say hello to the world diff --git a/src/init.h b/src/init.h index 8a44468..9763140 100644 --- a/src/init.h +++ b/src/init.h @@ -118,6 +118,10 @@ typedef struct _child_ { extern CHILD *family; extern int wrote_wtmp_reboot; extern int wrote_utmp_reboot; +extern int wrote_wtmp_rlevel; +extern int wrote_utmp_rlevel; +extern char thislevel; +extern char prevlevel; /* Tokens in state parser */ #define C_VER 1 @@ -139,4 +143,6 @@ extern int wrote_utmp_reboot; #define D_WROTE_UTMP_REBOOT -7 #define D_SLTIME -8 #define D_DIDBOOT -9 +#define D_WROTE_WTMP_RLEVEL -16 +#define D_WROTE_UTMP_RLEVEL -17 diff --git a/src/last.c b/src/last.c index 38d23ff..da91e82 100644 --- a/src/last.c +++ b/src/last.c @@ -476,14 +476,14 @@ int list(struct utmp *p, time_t t, int what) strcmp(s + 1, domainname) == 0) *s = 0; #endif if (!altlist) { - snprintf(final, sizeof(final), + len = snprintf(final, sizeof(final), fulltime ? "%-8.8s %-12.12s %-16.16s %-24.24s %-26.26s %-12.12s\n" : "%-8.8s %-12.12s %-16.16s %-16.16s %-7.7s %-12.12s\n", p->ut_name, utline, domain, logintime, logouttime, length); } else { - snprintf(final, sizeof(final), + len = snprintf(final, sizeof(final), fulltime ? "%-8.8s %-12.12s %-24.24s %-26.26s %-12.12s %s\n" : "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s %s\n", @@ -491,13 +491,19 @@ int list(struct utmp *p, time_t t, int what) logintime, logouttime, length, domain); } } else - snprintf(final, sizeof(final), + len = snprintf(final, sizeof(final), fulltime ? "%-8.8s %-12.12s %-24.24s %-26.26s %-12.12s\n" : "%-8.8s %-12.12s %-16.16s %-7.7s %-12.12s\n", p->ut_name, utline, logintime, logouttime, length); +#if defined(__GLIBC__) +# if (__GLIBC__ == 2) && (__GLIBC_MINOR__ == 0) + final[sizeof(final)-1] = '\0'; +# endif +#endif + /* * Print out "final" string safely. */ @@ -508,6 +514,9 @@ int list(struct utmp *p, time_t t, int what) putchar('*'); } + if (len < 0 || len >= sizeof(final)) + putchar('\n'); + recsdone++; if (maxrecs && recsdone >= maxrecs) return 1; diff --git a/src/utmp.c b/src/utmp.c index 5a0c101..c1ae0c9 100644 --- a/src/utmp.c +++ b/src/utmp.c @@ -65,6 +65,12 @@ char *line) /* Which line is this */ struct utsname uname_buf; struct timeval tv; + /* + * Can't do much if WTMP_FILE is not present or not writable. + */ + if (access(WTMP_FILE, W_OK) < 0) + return; + /* * Try to open the wtmp file. Note that we even try * this if we have updwtmp() so we can see if the @@ -86,6 +92,23 @@ char *line) /* Which line is this */ */ if (wrote_wtmp_reboot == 0 && type != BOOT_TIME) write_wtmp("reboot", "~~", 0, BOOT_TIME, "~"); + + /* + * Note if we are going to write a runlevel record. + */ + if (type == RUN_LVL) wrote_wtmp_rlevel++; + + /* + * See if we need to write a runlevel record. The reason that + * we are being so paranoid is that when we first tried to + * write the reboot record, /var was possibly not mounted + * yet. As soon as we can open WTMP we write a delayed runlevel record. + */ + if (wrote_wtmp_rlevel == 0 && type != RUN_LVL) { + int runlevel = thislevel; + int oldlevel = prevlevel; + write_wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~"); + } #endif /* @@ -135,9 +158,9 @@ char *oldline) /* Line of old utmp entry. */ struct timeval tv; /* - * Can't do much if UTMP_FILE is not present. + * Can't do much if UTMP_FILE is not present or not writable. */ - if (access(UTMP_FILE, F_OK) < 0) + if (access(UTMP_FILE, W_OK) < 0) return; #ifdef INIT_MAIN @@ -150,10 +173,27 @@ char *oldline) /* Line of old utmp entry. */ * See if we need to write a reboot record. The reason that * we are being so paranoid is that when we first tried to * write the reboot record, /var was possibly not mounted - * yet. As soon as we can open WTMP we write a delayed boot record. + * yet. As soon as we can open UTMP we write a delayed boot record. */ if (wrote_utmp_reboot == 0 && type != BOOT_TIME) write_utmp("reboot", "~~", 0, BOOT_TIME, "~", NULL); + + /* + * Note if we are going to write a runlevel record. + */ + if (type == RUN_LVL) wrote_utmp_rlevel++; + + /* + * See if we need to write a runlevel record. The reason that + * we are being so paranoid is that when we first tried to + * write the reboot record, /var was possibly not mounted + * yet. As soon as we can open UTMP we write a delayed runlevel record. + */ + if (wrote_utmp_rlevel == 0 && type != RUN_LVL) { + int runlevel = thislevel; + int oldlevel = prevlevel; + write_utmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~", NULL); + } #endif /*