init.c

Go to the documentation of this file.
00001 /* This process is the father (mother) of all Minix user processes.  When
00002  * Minix comes up, this is process number 2, and has a pid of 1.  It
00003  * executes the /etc/rc shell file, and then reads the /etc/ttytab file to
00004  * determine which terminals need a login process.
00005  *
00006  * If the files /usr/adm/wtmp and /etc/utmp exist and are writable, init
00007  * (with help from login) will maintain login accounting.  Sending a
00008  * signal 1 (SIGHUP) to init will cause it to rescan /etc/ttytab and start
00009  * up new shell processes if necessary.  It will not, however, kill off
00010  * login processes for lines that have been turned off; do this manually.
00011  * Signal 15 (SIGTERM) makes init stop spawning new processes, this is
00012  * used by shutdown and friends when they are about to close the system
00013  * down.
00014  */
00015 
00016 #include <minix/type.h>
00017 #include <sys/types.h>
00018 #include <sys/wait.h>
00019 #include <sys/stat.h>
00020 #include <sys/svrctl.h>
00021 #include <ttyent.h>
00022 #include <errno.h>
00023 #include <fcntl.h>
00024 #include <limits.h>
00025 #include <signal.h>
00026 #include <string.h>
00027 #include <time.h>
00028 #include <stdlib.h>
00029 #include <unistd.h>
00030 #include <utmp.h>
00031 
00032 /* Command to execute as a response to the three finger salute. */
00033 char *REBOOT_CMD[] =    { "shutdown", "now", "CTRL-ALT-DEL", NULL };
00034 
00035 /* Associated fake ttytab entry. */
00036 struct ttyent TT_REBOOT = { "console", "-", REBOOT_CMD, NULL };
00037 
00038 char PATH_UTMP[] = "/etc/utmp";         /* current logins */
00039 char PATH_WTMP[] = "/usr/adm/wtmp";     /* login/logout history */
00040 
00041 #define PIDSLOTS        32              /* first this many ttys can be on */
00042 
00043 struct slotent {
00044   int errct;                    /* error count */
00045   pid_t pid;                    /* pid of login process for this tty line */
00046 };
00047 
00048 #define ERRCT_DISABLE   10      /* disable after this many errors */
00049 #define NO_PID  0               /* pid value indicating no process */
00050 
00051 struct slotent slots[PIDSLOTS]; /* init table of ttys and pids */
00052 
00053 int gothup = 0;                 /* flag, showing signal 1 was received */
00054 int gotabrt = 0;                /* flag, showing signal 6 was received */
00055 int spawn = 1;                  /* flag, spawn processes only when set */
00056 
00057 void tell(int fd, char *s);
00058 void report(int fd, char *label);
00059 void wtmp(int type, int linenr, char *line, pid_t pid);
00060 void startup(int linenr, struct ttyent *ttyp);
00061 int execute(char **cmd);
00062 void onhup(int sig);
00063 void onterm(int sig);
00064 void onabrt(int sig);
00065 
00066 int main(void)
00067 {
00068   pid_t pid;                    /* pid of child process */
00069   int fd;                       /* generally useful */
00070   int linenr;                   /* loop variable */
00071   int check;                    /* check if a new process must be spawned */
00072   struct slotent *slotp;        /* slots[] pointer */
00073   struct ttyent *ttyp;          /* ttytab entry */
00074   struct sigaction sa;
00075   struct stat stb;
00076 
00077   if (fstat(0, &stb) < 0) {
00078         /* Open standard input, output & error. */
00079         (void) open("/dev/null", O_RDONLY);
00080         (void) open("/dev/log", O_WRONLY);
00081         dup(1);
00082   }
00083 
00084   sigemptyset(&sa.sa_mask);
00085   sa.sa_flags = 0;
00086 
00087   /* Hangup: Reexamine /etc/ttytab for newly enabled terminal lines. */
00088   sa.sa_handler = onhup;
00089   sigaction(SIGHUP, &sa, NULL);
00090 
00091   /* Terminate: Stop spawning login processes, shutdown is near. */
00092   sa.sa_handler = onterm;
00093   sigaction(SIGTERM, &sa, NULL);
00094 
00095   /* Abort: Sent by the kernel on CTRL-ALT-DEL; shut the system down. */
00096   sa.sa_handler = onabrt;
00097   sigaction(SIGABRT, &sa, NULL);
00098 
00099   /* Execute the /etc/rc file. */
00100   if ((pid = fork()) != 0) {
00101         /* Parent just waits. */
00102         while (wait(NULL) != pid) {
00103                 if (gotabrt) reboot(RBT_HALT);
00104         }
00105   } else {
00106 #if ! SYS_GETKENV
00107         struct sysgetenv sysgetenv;
00108 #endif
00109         char bootopts[16];
00110         static char *rc_command[] = { "sh", "/etc/rc", NULL, NULL, NULL };
00111         char **rcp = rc_command + 2;
00112 
00113         /* Get the boot options from the boot environment. */
00114         sysgetenv.key = "bootopts";
00115         sysgetenv.keylen = 8+1;
00116         sysgetenv.val = bootopts;
00117         sysgetenv.vallen = sizeof(bootopts);
00118         if (svrctl(MMGETPARAM, &sysgetenv) == 0) *rcp++ = bootopts;
00119         *rcp = "start";
00120 
00121         execute(rc_command);
00122         report(2, "sh /etc/rc");
00123         _exit(1);       /* impossible, we hope */
00124   }
00125 
00126   /* Clear /etc/utmp if it exists. */
00127   if ((fd = open(PATH_UTMP, O_WRONLY | O_TRUNC)) >= 0) close(fd);
00128 
00129   /* Log system reboot. */
00130   wtmp(BOOT_TIME, 0, NULL, 0);
00131 
00132   /* Main loop. If login processes have already been started up, wait for one
00133    * to terminate, or for a HUP signal to arrive. Start up new login processes
00134    * for all ttys which don't have them. Note that wait() also returns when
00135    * somebody's orphan dies, in which case ignore it.  If the TERM signal is
00136    * sent then stop spawning processes, shutdown time is near.
00137    */
00138 
00139   check = 1;
00140   while (1) {
00141         while ((pid = waitpid(-1, NULL, check ? WNOHANG : 0)) > 0) {
00142                 /* Search to see which line terminated. */
00143                 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
00144                         slotp = &slots[linenr];
00145                         if (slotp->pid == pid) {
00146                                 /* Record process exiting. */
00147                                 wtmp(DEAD_PROCESS, linenr, NULL, pid);
00148                                 slotp->pid = NO_PID;
00149                                 check = 1;
00150                         }
00151                 }
00152         }
00153 
00154         /* If a signal 1 (SIGHUP) is received, simply reset error counts. */
00155         if (gothup) {
00156                 gothup = 0;
00157                 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
00158                         slots[linenr].errct = 0;
00159                 }
00160                 check = 1;
00161         }
00162 
00163         /* Shut down on signal 6 (SIGABRT). */
00164         if (gotabrt) {
00165                 gotabrt = 0;
00166                 startup(0, &TT_REBOOT);
00167         }
00168 
00169         if (spawn && check) {
00170                 /* See which lines need a login process started up. */
00171                 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
00172                         slotp = &slots[linenr];
00173                         if ((ttyp = getttyent()) == NULL) break;
00174 
00175                         if (ttyp->ty_getty != NULL
00176                                 && ttyp->ty_getty[0] != NULL
00177                                 && slotp->pid == NO_PID
00178                                 && slotp->errct < ERRCT_DISABLE)
00179                         {
00180                                 startup(linenr, ttyp);
00181                         }
00182                 }
00183                 endttyent();
00184         }
00185         check = 0;
00186   }
00187 }
00188 
00189 void onhup(int sig)
00190 {
00191   gothup = 1;
00192   spawn = 1;
00193 }
00194 
00195 void onterm(int sig)
00196 {
00197   spawn = 0;
00198 }
00199 
00200 void onabrt(int sig)
00201 {
00202   static int count;
00203 
00204   if (++count == 2) reboot(RBT_HALT);
00205   gotabrt = 1;
00206 }
00207 
00208 void startup(int linenr, struct ttyent *ttyp)
00209 {
00210   /* Fork off a process for the indicated line. */
00211 
00212   struct slotent *slotp;                /* pointer to ttyslot */
00213   pid_t pid;                            /* new pid */
00214   int err[2];                           /* error reporting pipe */
00215   char line[32];                        /* tty device name */
00216   int status;
00217 
00218   slotp = &slots[linenr];
00219 
00220   /* Error channel for between fork and exec. */
00221   if (pipe(err) < 0) err[0] = err[1] = -1;
00222 
00223   if ((pid = fork()) == -1 ) {
00224         report(2, "fork()");
00225         sleep(10);
00226         return;
00227   }
00228 
00229   if (pid == 0) {
00230         /* Child */
00231         close(err[0]);
00232         fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC);
00233 
00234         /* A new session. */
00235         setsid();
00236 
00237         /* Construct device name. */
00238         strcpy(line, "/dev/");
00239         strncat(line, ttyp->ty_name, sizeof(line) - 6);
00240 
00241         /* Open the line for standard input and output. */
00242         close(0);
00243         close(1);
00244         if (open(line, O_RDWR) < 0 || dup(0) < 0) {
00245                 write(err[1], &errno, sizeof(errno));
00246                 _exit(1);
00247         }
00248 
00249         if (ttyp->ty_init != NULL && ttyp->ty_init[0] != NULL) {
00250                 /* Execute a command to initialize the terminal line. */
00251 
00252                 if ((pid = fork()) == -1) {
00253                         report(2, "fork()");
00254                         errno= 0;
00255                         write(err[1], &errno, sizeof(errno));
00256                         _exit(1);
00257                 }
00258 
00259                 if (pid == 0) {
00260                         alarm(10);
00261                         execute(ttyp->ty_init);
00262                         report(2, ttyp->ty_init[0]);
00263                         _exit(1);
00264                 }
00265 
00266                 while (waitpid(pid, &status, 0) != pid) {}
00267                 if (status != 0) {
00268                         tell(2, "init: ");
00269                         tell(2, ttyp->ty_name);
00270                         tell(2, ": ");
00271                         tell(2, ttyp->ty_init[0]);
00272                         tell(2, ": bad exit status\n");
00273                         errno = 0;
00274                         write(err[1], &errno, sizeof(errno));
00275                         _exit(1);
00276                 }
00277         }
00278 
00279         /* Redirect standard error too. */
00280         dup2(0, 2);
00281 
00282         /* Execute the getty process. */
00283         execute(ttyp->ty_getty);
00284 
00285         /* Oops, disaster strikes. */
00286         fcntl(2, F_SETFL, fcntl(2, F_GETFL) | O_NONBLOCK);
00287         if (linenr != 0) report(2, ttyp->ty_getty[0]);
00288         write(err[1], &errno, sizeof(errno));
00289         _exit(1);
00290   }
00291 
00292   /* Parent */
00293   if (ttyp != &TT_REBOOT) slotp->pid = pid;
00294 
00295   close(err[1]);
00296   if (read(err[0], &errno, sizeof(errno)) != 0) {
00297         /* If an errno value goes down the error pipe: Problems. */
00298 
00299         switch (errno) {
00300         case ENOENT:
00301         case ENODEV:
00302         case ENXIO:
00303                 /* Device nonexistent, no driver, or no minor device. */
00304                 slotp->errct = ERRCT_DISABLE;
00305                 close(err[0]);
00306                 return;
00307         case 0:
00308                 /* Error already reported. */
00309                 break;
00310         default:
00311                 /* Any other error on the line. */
00312                 report(2, ttyp->ty_name);
00313         }
00314         close(err[0]);
00315 
00316         if (++slotp->errct >= ERRCT_DISABLE) {
00317                 tell(2, "init: ");
00318                 tell(2, ttyp->ty_name);
00319                 tell(2, ": excessive errors, shutting down\n");
00320         } else {
00321                 sleep(5);
00322         }
00323         return;
00324   }
00325   close(err[0]);
00326 
00327   if (ttyp != &TT_REBOOT) wtmp(LOGIN_PROCESS, linenr, ttyp->ty_name, pid);
00328   slotp->errct = 0;
00329 }
00330 
00331 int execute(char **cmd)
00332 {
00333   /* Execute a command with a path search along /sbin:/bin:/usr/sbin:/usr/bin.
00334    */
00335   static char *nullenv[] = { NULL };
00336   char command[128];
00337   char *path[] = { "/sbin", "/bin", "/usr/sbin", "/usr/bin" };
00338   int i;
00339 
00340   if (cmd[0][0] == '/') {
00341         /* A full path. */
00342         return execve(cmd[0], cmd, nullenv);
00343   }
00344 
00345   /* Path search. */
00346   for (i = 0; i < 4; i++) {
00347         if (strlen(path[i]) + 1 + strlen(cmd[0]) + 1 > sizeof(command)) {
00348                 errno= ENAMETOOLONG;
00349                 return -1;
00350         }
00351         strcpy(command, path[i]);
00352         strcat(command, "/");
00353         strcat(command, cmd[0]);
00354         execve(command, cmd, nullenv);
00355         if (errno != ENOENT) break;
00356   }
00357   return -1;
00358 }
00359 
00360 void wtmp(type, linenr, line, pid)
00361 int type;                       /* type of entry */
00362 int linenr;                     /* line number in ttytab */
00363 char *line;                     /* tty name (only good on login) */
00364 pid_t pid;                      /* pid of process */
00365 {
00366 /* Log an event into the UTMP and WTMP files. */
00367 
00368   struct utmp utmp;             /* UTMP/WTMP User Accounting */
00369   int fd;
00370 
00371   /* Clear the utmp record. */
00372   memset((void *) &utmp, 0, sizeof(utmp));
00373 
00374   /* Fill in utmp. */
00375   switch (type) {
00376   case BOOT_TIME:
00377         /* Make a special reboot record. */
00378         strcpy(utmp.ut_name, "reboot");
00379         strcpy(utmp.ut_line, "~");
00380         break;
00381 
00382   case LOGIN_PROCESS:
00383         /* A new login, fill in line name. */
00384         strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
00385         break;
00386 
00387   case DEAD_PROCESS:
00388         /* A logout.  Use the current utmp entry, but make sure it is a
00389          * user process exiting, and not getty or login giving up.
00390          */
00391         if ((fd = open(PATH_UTMP, O_RDONLY)) < 0) {
00392                 if (errno != ENOENT) report(2, PATH_UTMP);
00393                 return;
00394         }
00395         if (lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
00396                 || read(fd, &utmp, sizeof(utmp)) == -1
00397         ) {
00398                 report(2, PATH_UTMP);
00399                 close(fd);
00400                 return;
00401         }
00402         close(fd);
00403         if (utmp.ut_type != USER_PROCESS) return;
00404         strncpy(utmp.ut_name, "", sizeof(utmp.ut_name));
00405         break;
00406   }
00407 
00408   /* Finish new utmp entry. */
00409   utmp.ut_pid = pid;
00410   utmp.ut_type = type;
00411   utmp.ut_time = time((time_t *) 0);
00412 
00413   switch (type) {
00414   case LOGIN_PROCESS:
00415   case DEAD_PROCESS:
00416         /* Write new entry to utmp. */
00417         if ((fd = open(PATH_UTMP, O_WRONLY)) < 0
00418                 || lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
00419                 || write(fd, &utmp, sizeof(utmp)) == -1
00420         ) {
00421                 if (errno != ENOENT) report(2, PATH_UTMP);
00422         }
00423         if (fd != -1) close(fd);
00424         break;
00425   }
00426 
00427   switch (type) {
00428   case BOOT_TIME:
00429   case DEAD_PROCESS:
00430         /* Add new wtmp entry. */
00431         if ((fd = open(PATH_WTMP, O_WRONLY | O_APPEND)) < 0
00432                   || write(fd, &utmp, sizeof(utmp)) == -1
00433         ) {
00434                 if (errno != ENOENT) report(2, PATH_WTMP);
00435         }
00436         if (fd != -1) close(fd);
00437         break;
00438   }
00439 }
00440 
00441 void tell(fd, s)
00442 int fd;
00443 char *s;
00444 {
00445         write(fd, s, strlen(s));
00446 }
00447 
00448 void report(fd, label)
00449 int fd;
00450 char *label;
00451 {
00452         int err = errno;
00453 
00454         tell(fd, "init: ");
00455         tell(fd, label);
00456         tell(fd, ": ");
00457         tell(fd, strerror(err));
00458         tell(fd, "\n");
00459         errno= err;
00460 }

Generated on Fri Apr 14 22:56:55 2006 for minix by  doxygen 1.4.6