* Test out PAM support, extend PAM support by providing environment
[sysvinit.git] / src / init.c
index 1d7371cf2153fcd9a307f565b3a3d72ae89fb842..d8cc3f22868589f1610fb34f06dbc0d769ec691c 100644 (file)
@@ -7,8 +7,8 @@
  *
  * Version:    @(#)init.c  2.86  30-Jul-2004  miquels@cistron.nl
  */
-#define VERSION "2.86"
-#define DATE    "31-Jul-2004"
+#define VERSION "2.89"
+#define DATE    "26-Mar-2010"
 /*
  *             This file is part of the sysvinit suite,
  *             Copyright (C) 1991-2004 Miquel van Smoorenburg.
 #include <sys/time.h>
 
 #ifdef WITH_SELINUX
-#include <selinux/selinux.h>
+#  include <selinux/selinux.h>
+#  include <sys/mount.h>
 #endif
 
-
 #ifdef __i386__
-#  if (__GLIBC__ >= 2)
+#  ifdef __GLIBC__
      /* GNU libc 2.x */
 #    define STACK_DEBUG 1
 #    if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
        /* Only glibc 2.0 needs this */
 #      include <sigcontext.h>
+#    elif ( __GLIBC__ > 2) && ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
+#      include <bits/sigcontext.h>
 #    endif
 #  endif
 #endif
 
+#ifdef USE_PAM
+#  include <security/pam_appl.h>
+#  include <security/pam_misc.h>
+#endif
+
 #include "init.h"
 #include "initreq.h"
 #include "paths.h"
@@ -121,11 +128,12 @@ 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 */
 struct utmp utproto;           /* Only used for sizeof(utproto.ut_id) */
-char *user_console = NULL;     /* User console device */
 char *console_dev;             /* Console device. */
 int pipe_fd = -1;              /* /dev/initctl */
 int did_boot = 0;              /* Did we already do BOOT* stuff? */
@@ -190,6 +198,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 {
@@ -213,6 +223,7 @@ char *extra_env[NR_EXTRA_ENV];
  *     This only works correctly because the linux select updates
  *     the elapsed time in the struct timeval passed to select!
  */
+static
 void do_sleep(int sec)
 {
        struct timeval tv;
@@ -228,6 +239,7 @@ void do_sleep(int sec)
 /*
  *     Non-failing allocation routines (init cannot fail).
  */
+static
 void *imalloc(size_t size)
 {
        void    *m;
@@ -240,7 +252,7 @@ void *imalloc(size_t size)
        return m;
 }
 
-
+static
 char *istrdup(char *s)
 {
        char    *m;
@@ -257,6 +269,7 @@ char *istrdup(char *s)
  *     Send the state info of the previous running init to
  *     the new one, in a version-independant way.
  */
+static
 void send_state(int fd)
 {
        FILE    *fp;
@@ -386,6 +399,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;
@@ -443,6 +462,7 @@ static CHILD *get_record(FILE *f)
  *     Read the complete state info from the state pipe.
  *     Returns 0 on success
  */
+static
 int receive_state(int fd)
 {
        FILE    *f;
@@ -490,6 +510,7 @@ static int setproctitle(char *fmt, ...)
 /*
  *     Set console_dev to a working console.
  */
+static
 void console_init(void)
 {
        int fd;
@@ -497,9 +518,7 @@ void console_init(void)
        int tried_vtmaster = 0;
        char *s;
 
-       if (user_console) {
-               console_dev = user_console;
-       } else if ((s = getenv("CONSOLE")) != NULL)
+       if ((s = getenv("CONSOLE")) != NULL)
                console_dev = s;
        else {
                console_dev = CONSOLE;
@@ -529,6 +548,7 @@ void console_init(void)
 /*
  *     Open the console with retries.
  */
+static
 int console_open(int mode)
 {
        int f, fd = -1;
@@ -544,7 +564,7 @@ int console_open(int mode)
         */
        for(f = 0; f < 5; f++) {
                if ((fd = open(console_dev, m)) >= 0) break;
-               usleep(100);
+               usleep(10000);
        }
 
        if (fd < 0) return fd;
@@ -560,6 +580,7 @@ int console_open(int mode)
 /*
  *     We got a signal (HUP PWR WINCH ALRM INT)
  */
+static
 void signal_handler(int sig)
 {
        ADDSET(got_signals, sig);
@@ -568,7 +589,12 @@ void signal_handler(int sig)
 /*
  *     SIGCHLD: one of our children has died.
  */
-void chld_handler()
+static
+# ifdef __GNUC__
+void chld_handler(int sig __attribute__((unused)))
+# else
+void chld_handler(int sig)
+# endif
 {
        CHILD           *ch;
        int             pid, st;
@@ -608,7 +634,12 @@ void chld_handler()
  *
  *     The SIGCONT handler
  */
-void cont_handler()
+static
+# ifdef __GNUC__
+void cont_handler(int sig __attribute__((unused)))
+# else
+void cont_handler(int sig)
+# endif
 {
        got_cont = 1;
 }
@@ -616,6 +647,7 @@ void cont_handler()
 /*
  *     Fork and dump core in /.
  */
+static
 void coredump(void)
 {
        static int              dumped = 0;
@@ -649,8 +681,13 @@ void coredump(void)
  *     If we have the info, print where it occured.
  *     Then sleep 30 seconds and try to continue.
  */
+static
 #if defined(STACK_DEBUG) && defined(__linux__)
+# ifdef __GNUC__
+void segv_handler(int sig __attribute__((unused)), struct sigcontext ctx)
+# else
 void segv_handler(int sig, struct sigcontext ctx)
+# endif
 {
        char    *p = "";
        int     saved_errno = errno;
@@ -665,7 +702,11 @@ void segv_handler(int sig, struct sigcontext ctx)
        errno = saved_errno;
 }
 #else
-void segv_handler()
+# ifdef __GNUC__
+void segv_handler(int sig __attribute__((unused)))
+# else
+void segv_handler(int sig)
+# endif
 {
        int     saved_errno = errno;
 
@@ -680,7 +721,12 @@ void segv_handler()
 /*
  *     The SIGSTOP & SIGTSTP handler
  */
-void stop_handler()
+static
+# ifdef __GNUC__
+void stop_handler(int sig __attribute__((unused)))
+# else
+void stop_handler(int sig)
+# endif
 {
        int     saved_errno = errno;
 
@@ -693,6 +739,7 @@ void stop_handler()
 /*
  *     Set terminal settings to reasonable defaults
  */
+static
 void console_stty(void)
 {
        struct termios tty;
@@ -703,29 +750,58 @@ void console_stty(void)
                return;
        }
 
+#ifdef __FreeBSD_kernel__
+       /*
+        * The kernel of FreeBSD expects userland to set TERM.  Usually, we want
+        * "cons25".  Later, gettys might disagree on this (i.e. we're not using
+        * syscons) but some boot scripts, like /etc/init.d/xserver-xorg, still
+        * need a non-dumb terminal.
+        */
+       putenv ("TERM=cons25");
+#endif
+
        (void) tcgetattr(fd, &tty);
 
        tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
        tty.c_cflag |= HUPCL|CLOCAL|CREAD;
 
-       tty.c_cc[VINTR]  = 3;   /* ctrl('c') */
-       tty.c_cc[VQUIT]  = 28;  /* ctrl('\\') */
-       tty.c_cc[VERASE] = 127;
-       tty.c_cc[VKILL]  = 24;  /* ctrl('x') */
-       tty.c_cc[VEOF]   = 4;   /* ctrl('d') */
-       tty.c_cc[VTIME]  = 0;
-       tty.c_cc[VMIN]   = 1;
-       tty.c_cc[VSTART] = 17;  /* ctrl('q') */
-       tty.c_cc[VSTOP]  = 19;  /* ctrl('s') */
-       tty.c_cc[VSUSP]  = 26;  /* ctrl('z') */
+       tty.c_cc[VINTR]     = CINTR;
+       tty.c_cc[VQUIT]     = CQUIT;
+       tty.c_cc[VERASE]    = CERASE; /* ASCII DEL (0177) */
+       tty.c_cc[VKILL]     = CKILL;
+       tty.c_cc[VEOF]      = CEOF;
+       tty.c_cc[VTIME]     = 0;
+       tty.c_cc[VMIN]      = 1;
+       tty.c_cc[VSWTC]     = _POSIX_VDISABLE;
+       tty.c_cc[VSTART]    = CSTART;
+       tty.c_cc[VSTOP]     = CSTOP;
+       tty.c_cc[VSUSP]     = CSUSP;
+       tty.c_cc[VEOL]      = _POSIX_VDISABLE;
+       tty.c_cc[VREPRINT]  = CREPRINT;
+       tty.c_cc[VDISCARD]  = CDISCARD;
+       tty.c_cc[VWERASE]   = CWERASE;
+       tty.c_cc[VLNEXT]    = CLNEXT;
+       tty.c_cc[VEOL2]     = _POSIX_VDISABLE;
 
        /*
         *      Set pre and post processing
         */
        tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY;
+#ifdef IUTF8 /* Not defined on FreeBSD */
+       tty.c_iflag |= IUTF8;
+#endif /* IUTF8 */
        tty.c_oflag = OPOST|ONLCR;
        tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE;
 
+#if defined(SANE_TIO) && (SANE_TIO == 1)
+       /*
+        *      Disable flow control (-ixon), ignore break (ignbrk),
+        *      and make nl/cr more usable (sane).
+        */
+       tty.c_iflag |=  IGNBRK;
+       tty.c_iflag &= ~(BRKINT|INLCR|IGNCR|IXON);
+       tty.c_oflag &= ~(OCRNL|ONLRET);
+#endif
        /*
         *      Now set the terminal line.
         *      We don't care about non-transmitted output data
@@ -789,6 +865,47 @@ void initlog(int loglevel, char *s, ...)
 }
 
 
+#ifdef USE_PAM
+static pam_handle_t *pamh = NULL;
+# ifdef __GNUC__
+static int
+init_conv(int num_msg, const struct pam_message **msgm,
+         struct pam_response **response __attribute__((unused)),
+         void *appdata_ptr __attribute__((unused)))
+# else
+static int
+init_conv(int num_msg, const struct pam_message **msgm,
+         struct pam_response **response, void *appdata_ptr)
+# endif
+{
+       int i;
+       for (i = 0; i < num_msg; i++) {
+               const struct pam_message *msg = msgm[i];
+               if (msg == (const struct pam_message*)0)
+                       continue;
+               if (msg->msg == (char*)0)
+                       continue;
+               switch (msg->msg_style) {
+               case PAM_ERROR_MSG:
+               case PAM_TEXT_INFO:
+                       initlog(L_VB, "pam_message %s", msg->msg);
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+static const struct pam_conv conv = { init_conv, NULL };
+# define PAM_FAIL_CHECK(func, args...) \
+       { \
+               if ((pam_ret = (func)(args)) != PAM_SUCCESS) { \
+                       initlog(L_VB, "%s", pam_strerror(pamh, pam_ret)); \
+                       goto pam_error; \
+               } \
+       }
+#endif /* USE_PAM */
+
+
 /*
  *     Build a new environment for execve().
  */
@@ -797,25 +914,44 @@ char **init_buildenv(int child)
        char            i_lvl[] = "RUNLEVEL=x";
        char            i_prev[] = "PREVLEVEL=x";
        char            i_cons[32];
+       char            i_shell[] = "SHELL=" SHELL;
        char            **e;
+#ifdef USE_PAM
+       char            **pamenv = (char**)0;
+#endif
        int             n, i;
 
        for (n = 0; environ[n]; n++)
                ;
-       n += NR_EXTRA_ENV + 8;
+       n += NR_EXTRA_ENV;
+       if (child) {
+#ifdef USE_PAM
+               pamenv = pam_getenvlist(pamh);
+               for (i = 0; pamenv[i]; i++)
+                       ;
+               n += i;
+#endif
+               n += 8;
+       }
        e = calloc(n, sizeof(char *));
 
        for (n = 0; environ[n]; n++)
                e[n] = istrdup(environ[n]);
 
-       for (i = 0; i < NR_EXTRA_ENV; i++)
+       for (i = 0; i < NR_EXTRA_ENV; i++) {
                if (extra_env[i])
                        e[n++] = istrdup(extra_env[i]);
+       }
 
        if (child) {
+#ifdef USE_PAM
+               for (i = 0; pamenv[i]; i++)
+                       e[n++] = istrdup(pamenv[i]);
+#endif
                snprintf(i_cons, sizeof(i_cons), "CONSOLE=%s", console_dev);
                i_lvl[9]   = thislevel;
                i_prev[10] = prevlevel;
+               e[n++] = istrdup(i_shell);
                e[n++] = istrdup(i_lvl);
                e[n++] = istrdup(i_prev);
                e[n++] = istrdup(i_cons);
@@ -844,11 +980,12 @@ void init_freeenv(char **e)
  *     This function is too long and indents too deep.
  *
  */
-int spawn(CHILD *ch, int *res)
+static
+pid_t spawn(CHILD *ch, int *res)
 {
   char *args[16];              /* Argv array */
   char buf[136];               /* Line buffer */
-  int f, st, rc;               /* Scratch variables */
+  int f, st;                   /* Scratch variables */
   char *ptr;                   /* Ditto */
   time_t t;                    /* System time */
   int oldAlarm;                        /* Previous alarm value */
@@ -958,7 +1095,9 @@ int spawn(CHILD *ch, int *res)
        sigprocmask(SIG_BLOCK, &nmask, &omask);
 
        if ((pid = fork()) == 0) {
-
+#ifdef USE_PAM
+               int pam_ret;
+#endif
                close(0);
                close(1);
                close(2);
@@ -984,6 +1123,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",
@@ -991,6 +1138,7 @@ int spawn(CHILD *ch, int *res)
                                exit(1);
                        }
                        if (pid > 0) {
+                               pid_t rc;
                                /*
                                 *      Ignore keyboard signals etc.
                                 *      Then wait for child to exit.
@@ -1045,6 +1193,32 @@ int spawn(CHILD *ch, int *res)
                        dup(f);
                }
 
+#ifdef USE_PAM
+               PAM_FAIL_CHECK(pam_start, "init", "root" , &conv, &pamh);
+               PAM_FAIL_CHECK(pam_set_item, pamh, PAM_TTY, console_dev);
+               PAM_FAIL_CHECK(pam_acct_mgmt, pamh, PAM_SILENT);
+               PAM_FAIL_CHECK(pam_open_session, pamh, PAM_SILENT);
+               PAM_FAIL_CHECK(pam_setcred, pamh, PAM_ESTABLISH_CRED|PAM_SILENT);
+#endif
+               /*
+                * Update utmp/wtmp file prior to starting
+                * any child.  This MUST be done right here in
+                * the child process in order to prevent a race
+                * condition that occurs when the child
+                * process' time slice executes before the
+                * parent (can and does happen in a uniprocessor
+                * environment).  If the child is a getty and
+                * the race condition happens, then init's utmp
+                * update will happen AFTER the getty runs
+                * and expects utmp to be updated already!
+                *
+                * Do NOT log if process field starts with '+'
+                * FIXME: that's for compatibility with *very*
+                * old getties - probably it can be taken out.
+                */
+               if (ch->process[0] != '+')
+                       write_utmp_wtmp("", ch->id, getpid(), INIT_PROCESS, "");
+
                /* Reset all the signals, set up environment */
                for(f = 1; f < NSIG; f++) SETSIG(sa, f, SIG_DFL, SA_RESTART);
                environ = init_buildenv(1);
@@ -1064,6 +1238,15 @@ int spawn(CHILD *ch, int *res)
                        execvp(args[1], args + 1);
                }
                initlog(L_VB, "cannot execute \"%s\"", args[1]);
+
+               if (ch->process[0] != '+')
+                       write_utmp_wtmp("", ch->id, getpid(), DEAD_PROCESS, NULL);
+#ifdef USE_PAM
+               (void)pam_setcred(pamh, PAM_DELETE_CRED|PAM_SILENT);
+               pam_ret = pam_close_session(pamh, PAM_SILENT);
+       pam_error:
+               pam_end(pamh, pam_ret);
+#endif
                exit(1);
        }
        *res = pid;
@@ -1083,6 +1266,7 @@ int spawn(CHILD *ch, int *res)
 /*
  *     Start a child running!
  */
+static
 void startup(CHILD *ch)
 {
        /*
@@ -1108,15 +1292,7 @@ void startup(CHILD *ch)
                case ONDEMAND:
                case RESPAWN:
                        ch->flags |= RUNNING;
-                       if (spawn(ch, &(ch->pid)) < 0) break;
-                       /*
-                        *      Do NOT log if process field starts with '+'
-                        *      FIXME: that's for compatibility with *very*
-                        *      old getties - probably it can be taken out.
-                        */
-                       if (ch->process[0] != '+')
-                               write_utmp_wtmp("", ch->id, ch->pid,
-                                       INIT_PROCESS, "");
+                       (void)spawn(ch, &(ch->pid));
                        break;
        }
 }
@@ -1125,6 +1301,7 @@ void startup(CHILD *ch)
 /*
  *     Read the inittab file.
  */
+static
 void read_inittab(void)
 {
   FILE         *fp;                    /* The INITTAB file */
@@ -1257,7 +1434,7 @@ void read_inittab(void)
        strncpy(ch->id, id, sizeof(utproto.ut_id) + 1); /* Hack for different libs. */
        strncpy(ch->process, process, sizeof(ch->process) - 1);
        if (rlevel[0]) {
-               for(f = 0; f < sizeof(rlevel) - 1 && rlevel[f]; f++) {
+               for(f = 0; f < (int)sizeof(rlevel) - 1 && rlevel[f]; f++) {
                        ch->rlevel[f] = rlevel[f];
                        if (ch->rlevel[f] == 's') ch->rlevel[f] = 'S';
                }
@@ -1505,6 +1682,7 @@ void read_inittab(void)
  *     The entries that do not belong here at all are removed
  *     from the list.
  */
+static
 void start_if_needed(void)
 {
        CHILD *ch;              /* Pointer to child */
@@ -1550,6 +1728,7 @@ void start_if_needed(void)
 /*
  *     Ask the user on the console for a runlevel
  */
+static
 int ask_runlevel(void)
 {
        const char      prompt[] = "\nEnter runlevel: ";
@@ -1578,6 +1757,7 @@ int ask_runlevel(void)
  *     Search the INITTAB file for the 'initdefault' field, with the default
  *     runlevel. If this fails, ask the user to supply a runlevel.
  */
+static
 int get_init_default(void)
 {
        CHILD *ch;
@@ -1626,6 +1806,7 @@ int get_init_default(void)
  *     the "old" INITLVL and arg == 0, try to read the new
  *     runlevel from that file first.
  */
+static
 int read_level(int arg)
 {
        CHILD           *ch;                    /* Walk through list */
@@ -1694,7 +1875,14 @@ int read_level(int arg)
                        initlog(L_VB, "Switching to runlevel: %c", foo);
        }
 
-       if (foo == 'Q') return runlevel;
+       if (foo == 'Q') {
+#if defined(SIGINT_ONLYONCE) && (SIGINT_ONLYONCE == 1)
+               /* Re-enable signal from keyboard */
+               struct sigaction sa;
+               SETSIG(sa, SIGINT, signal_handler, 0);
+#endif
+               return runlevel;
+       }
 
        /* Check if this is a runlevel a, b or c */
        if (strchr("ABC", foo)) {
@@ -1717,6 +1905,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;
@@ -1731,6 +1921,7 @@ int read_level(int arg)
  *     longer than 5 minutes, or inittab was read again due
  *     to user interaction.
  */
+static
 void fail_check(void)
 {
        CHILD   *ch;                    /* Pointer to child structure */
@@ -1763,6 +1954,7 @@ void fail_check(void)
 }
 
 /* Set all 'Fail' timers to 0 */
+static
 void fail_cancel(void)
 {
        CHILD *ch;
@@ -1777,6 +1969,7 @@ void fail_cancel(void)
 /*
  *     Start up powerfail entries.
  */
+static
 void do_power_fail(int pwrstat)
 {
        CHILD *ch;
@@ -1810,6 +2003,7 @@ void do_power_fail(int pwrstat)
 /*
  *     Check for state-pipe presence
  */
+static
 int check_pipe(int fd)
 {
        struct timeval  t;
@@ -1830,6 +2024,7 @@ int check_pipe(int fd)
 /*
  *      Make a state-pipe.
  */
+static
 int make_pipe(int fd)
 {
        int fds[2];
@@ -1847,6 +2042,7 @@ int make_pipe(int fd)
 /*
  *     Attempt to re-exec.
  */
+static
 void re_exec(void)
 {
        CHILD           *ch;
@@ -1917,11 +2113,31 @@ 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
  *     init.fifo. Process it.
  */
+static
 void fifo_new_level(int level)
 {
 #if CHANGE_WAIT
@@ -1948,9 +2164,10 @@ 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);
+                       setproctitle("init [%c]", (int)runlevel);
                }
        }
 }
@@ -1961,6 +2178,7 @@ void fifo_new_level(int level)
  *     encoded as KEY=VAL\0KEY=VAL\0\0. With "=VAL" it means
  *     setenv, without it means unsetenv.
  */
+static
 void initcmd_setenv(char *data, int size)
 {
        char            *env, *p, *e, *eq;
@@ -2018,6 +2236,7 @@ void initcmd_setenv(char *data, int size)
  *             the 2.2 kernel credential stuff to see who we're talking to.
  *     
  */
+static
 void check_init_fifo(void)
 {
   struct init_request  request;
@@ -2142,18 +2361,6 @@ void check_init_fifo(void)
                case INIT_CMD_SETENV:
                        initcmd_setenv(request.i.data, sizeof(request.i.data));
                        break;
-               case INIT_CMD_CHANGECONS:
-                       if (user_console) {
-                               free(user_console);
-                               user_console = NULL;
-                       }
-                       if (!request.i.bsd.reserved[0])
-                               user_console = NULL;
-                       else
-                               user_console = strdup(request.i.bsd.reserved);
-                       console_init();
-                       quit = 1;
-                       break;
                default:
                        initlog(L_VB, "got unimplemented initrequest.");
                        break;
@@ -2172,6 +2379,7 @@ void check_init_fifo(void)
  *     This function is used in the transition
  *     sysinit (-> single user) boot -> multi-user.
  */
+static
 void boot_transitions()
 {
   CHILD                *ch;
@@ -2243,10 +2451,12 @@ 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;
-               setproctitle("init [%c]", runlevel);
+               setproctitle("init [%c]", (int)runlevel);
        }
   }
 }
@@ -2255,6 +2465,7 @@ void boot_transitions()
  *     Init got hit by a signal. See which signal it is,
  *     and act accordingly.
  */
+static
 void process_signals()
 {
   CHILD                *ch;
@@ -2279,6 +2490,11 @@ void process_signals()
   }
 
   if (ISMEMBER(got_signals, SIGINT)) {
+#if defined(SIGINT_ONLYONCE) && (SIGINT_ONLYONCE == 1)
+       /* Ignore any further signal from keyboard */
+       struct sigaction sa;
+       SETSIG(sa, SIGINT, SIG_IGN, SA_RESTART);
+#endif
        INITDBG(L_VB, "got SIGINT");
        /* Tell ctrlaltdel entry to start up */
        for(ch = family; ch; ch = ch->next)
@@ -2341,7 +2557,7 @@ void process_signals()
                            runlevel == '1') console_stty();
                        read_inittab();
                        fail_cancel();
-                       setproctitle("init [%c]", runlevel);
+                       setproctitle("init [%c]", (int)runlevel);
                        DELSET(got_signals, SIGHUP);
                }
        }
@@ -2360,12 +2576,12 @@ void process_signals()
 /*
  *     The main loop
  */ 
-int init_main()
+static
+void init_main(void)
 {
   CHILD                        *ch;
   struct sigaction     sa;
   sigset_t             sgt;
-  pid_t                        rc;
   int                  f, st;
 
   if (!reload) {
@@ -2421,6 +2637,7 @@ int init_main()
   console_init();
 
   if (!reload) {
+       int fd;
 
        /* Close whatever files are open, and reset the console. */
        close(0);
@@ -2438,7 +2655,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
@@ -2449,6 +2667,7 @@ int init_main()
         *      See if we have to start an emergency shell.
         */
        if (emerg_shell) {
+               pid_t rc;
                SETSIG(sa, SIGCHLD, SIG_DFL, SA_RESTART);
                if (spawn(&ch_emerg, &f) > 0) {
                        while((rc = wait(&st)) != f)
@@ -2518,12 +2737,14 @@ int init_main()
 /*
  * Tell the user about the syntax we expect.
  */
+static
 void usage(char *s)
 {
        fprintf(stderr, "Usage: %s {-e VAR[=VAL] | [-t SECONDS] {0|1|2|3|4|5|6|S|s|Q|q|A|a|B|b|C|c|U|u}}\n", s);
        exit(1);
 }
 
+static
 int telinit(char *progname, int argc, char **argv)
 {
 #ifdef TELINIT_USES_INITLVL
@@ -2575,12 +2796,28 @@ int telinit(char *progname, int argc, char **argv)
                request.sleeptime = sltime;
        }
 
+       /* Change to the root directory. */
+       chdir("/");
+
        /* Open the fifo and write a command. */
        /* Make sure we don't hang on opening /dev/initctl */
        SETSIG(sa, SIGALRM, signal_handler, 0);
        alarm(3);
-       if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0 &&
-           write(fd, &request, sizeof(request)) == sizeof(request)) {
+       if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0) {
+               ssize_t p = 0;
+               size_t s  = sizeof(request);
+               void *ptr = &request;
+
+               while (s > 0) {
+                       p = write(fd, ptr, s);
+                       if (p < 0) {
+                               if (errno == EINTR || errno == EAGAIN)
+                                       continue;
+                               break;
+                       }
+                       ptr += p;
+                       s -= p;
+               }
                close(fd);
                alarm(0);
                return 0;
@@ -2624,13 +2861,17 @@ int main(int argc, char **argv)
        char                    *p;
        int                     f;
        int                     isinit;
+#ifdef WITH_SELINUX
        int                     enforce = 0;
+#endif
 
        /* Get my own name */
        if ((p = strrchr(argv[0], '/')) != NULL)
                p++;
        else
                p = argv[0];
+
+       /* Common umask */
        umask(022);
 
        /* Quick check */
@@ -2644,9 +2885,10 @@ int main(int argc, char **argv)
         */
        isinit = (getpid() == 1);
        for (f = 1; f < argc; f++) {
-               if (!strcmp(argv[f], "-i") || !strcmp(argv[f], "--init"))
+               if (!strcmp(argv[f], "-i") || !strcmp(argv[f], "--init")) {
                        isinit = 1;
                        break;
+               }
        }
        if (!isinit) exit(telinit(p, argc, argv));
 
@@ -2663,7 +2905,7 @@ int main(int argc, char **argv)
                for (f = 0; f < argc; f++)
                        maxproclen += strlen(argv[f]) + 1;
                reload = 1;
-               setproctitle("init [%c]",runlevel);
+               setproctitle("init [%c]", (int)runlevel);
 
                init_main();
        }
@@ -2689,25 +2931,30 @@ int main(int argc, char **argv)
        }
 
 #ifdef WITH_SELINUX
-       if (getenv("SELINUX_INIT") == NULL && !is_selinux_enabled()) {
-         putenv("SELINUX_INIT=YES");
-         if (selinux_init_load_policy(&enforce) == 0 ) {
-           execv(myname, argv);
-         } else {
-           if (enforce > 0) {
-             /* SELinux in enforcing mode but load_policy failed */
-             /* At this point, we probably can't open /dev/console, so log() won't work */
-                   fprintf(stderr,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now.\n");
-             exit(1);
+       if (getenv("SELINUX_INIT") == NULL) {
+         const int rc = mount("proc", "/proc", "proc", 0, 0);
+         if (is_selinux_enabled() > 0) {
+           putenv("SELINUX_INIT=YES");
+           if (rc == 0) umount2("/proc", MNT_DETACH);
+           if (selinux_init_load_policy(&enforce) == 0) {
+             execv(myname, argv);
+           } else {
+             if (enforce > 0) {
+               /* SELinux in enforcing mode but load_policy failed */
+               /* At this point, we probably can't open /dev/console, so log() won't work */
+               fprintf(stderr,"Unable to load SELinux Policy. Machine is in enforcing mode. Halting now.\n");
+               exit(1);
+             }
            }
          }
+         if (rc == 0) umount2("/proc", MNT_DETACH);
        }
 #endif  
        /* Start booting. */
        argv0 = argv[0];
        argv[1] = NULL;
        setproctitle("init boot");
-       init_main(dfl_level);
+       init_main();
 
        /*NOTREACHED*/
        return 0;