From 13de2e9e05daa9e08056f51630ab6572b953dd37 Mon Sep 17 00:00:00 2001 From: Werner Fink Date: Tue, 24 Nov 2009 10:03:19 +0000 Subject: [PATCH] Add the comment from Andrea Arcangeli about the correct place of setting the default childhandler within spawn(). Make sure that newline is printed out for last(1) even if an utmp record entry is truncated. Check if utmp not only exists but is writable and delay writing out of the utmp runlevel record if utmp is not writable. Be able to find libcrypt also on 64 bit based architectures. --- src/Makefile | 2 +- src/init.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- src/init.h | 6 ++++++ src/last.c | 15 ++++++++++++--- src/utmp.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 107 insertions(+), 8 deletions(-) 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 /*