~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Minix Cross Reference
Minix/tools/init.c


  1 /* This process is the father (mother) of all Minix user processes.  When
  2  * Minix comes up, this is process number 2, and has a pid of 1.  It
  3  * executes the /etc/rc shell file, and then reads the /etc/ttytab file to
  4  * determine which terminals need a login process.
  5  *
  6  * If the files /usr/adm/wtmp and /etc/utmp exist and are writable, init
  7  * (with help from login) will maintain login accounting.  Sending a
  8  * signal 1 (SIGHUP) to init will cause it to rescan /etc/ttytab and start
  9  * up new shell processes if necessary.  It will not, however, kill off
 10  * login processes for lines that have been turned off; do this manually.
 11  * Signal 15 (SIGTERM) makes init stop spawning new processes, this is
 12  * used by shutdown and friends when they are about to close the system
 13  * down.
 14  */
 15 
 16 #include <sys/types.h>
 17 #include <sys/wait.h>
 18 #include <sys/stat.h>
 19 #include <ttyent.h>
 20 #include <errno.h>
 21 #include <fcntl.h>
 22 #include <limits.h>
 23 #include <signal.h>
 24 #include <string.h>
 25 #include <time.h>
 26 #include <stdlib.h>
 27 #include <unistd.h>
 28 #include <utmp.h>
 29 
 30 /* Command to execute as a response to the three finger salute. */
 31 char *REBOOT_CMD[] =    { "shutdown", "now", "CTRL-ALT-DEL", NULL };
 32 
 33 /* Associated fake ttytab entry. */
 34 struct ttyent TT_REBOOT = { "console", "-", REBOOT_CMD, NULL };
 35 
 36 char PATH_UTMP[] = "/etc/utmp";         /* current logins */
 37 char PATH_WTMP[] = "/usr/adm/wtmp";     /* login/logout history */
 38 
 39 #define PIDSLOTS        32              /* first this many ttys can be on */
 40 
 41 struct slotent {
 42   int errct;                    /* error count */
 43   pid_t pid;                    /* pid of login process for this tty line */
 44 };
 45 
 46 #define ERRCT_DISABLE   10      /* disable after this many errors */
 47 #define NO_PID  0               /* pid value indicating no process */
 48 
 49 struct slotent slots[PIDSLOTS]; /* init table of ttys and pids */
 50 
 51 int gothup = 0;                 /* flag, showing signal 1 was received */
 52 int gotabrt = 0;                /* flag, showing signal 6 was received */
 53 int spawn = 1;                  /* flag, spawn processes only when set */
 54 
 55 void tell(int fd, char *s);
 56 void report(int fd, char *label);
 57 void wtmp(int type, int linenr, char *line, pid_t pid);
 58 void startup(int linenr, struct ttyent *ttyp);
 59 int execute(char **cmd);
 60 void onhup(int sig);
 61 void onterm(int sig);
 62 void onabrt(int sig);
 63 
 64 int main(void)
 65 {
 66   pid_t pid;                    /* pid of child process */
 67   int fd;                       /* generally useful */
 68   int linenr;                   /* loop variable */
 69   int check;                    /* check if a new process must be spawned */
 70   struct slotent *slotp;        /* slots[] pointer */
 71   struct ttyent *ttyp;          /* ttytab entry */
 72   struct sigaction sa;
 73   struct stat stb;
 74 
 75   if (fstat(0, &stb) < 0) {
 76         /* Open standard input, output & error. */
 77         (void) open("/dev/null", O_RDONLY);
 78         (void) open("/dev/console", O_WRONLY);
 79         dup(1);
 80   }
 81 
 82   sigemptyset(&sa.sa_mask);
 83   sa.sa_flags = 0;
 84 
 85   /* Hangup: Reexamine /etc/ttytab for newly enabled terminal lines. */
 86   sa.sa_handler = onhup;
 87   sigaction(SIGHUP, &sa, NULL);
 88 
 89   /* Terminate: Stop spawning login processes, shutdown is near. */
 90   sa.sa_handler = onterm;
 91   sigaction(SIGTERM, &sa, NULL);
 92 
 93   /* Abort: Sent by the kernel on CTRL-ALT-DEL; shut the system down. */
 94   sa.sa_handler = onabrt;
 95   sigaction(SIGABRT, &sa, NULL);
 96 
 97   /* Execute the /etc/rc file. */
 98   if ((pid = fork()) != 0) {
 99         /* Parent just waits. */
100         while (wait(NULL) != pid) {
101                 if (gotabrt) reboot(RBT_HALT);
102         }
103   } else {
104         static char *rc_command[] = { "sh", "/etc/rc", NULL, NULL };
105 
106 #if __minix_vmd
107         /* Minix-vmd: Get the boot options from the boot environment. */
108         rc_command[2] = getenv("bootopts");
109 #else
110         /* Minix: Input from the console. */
111         close(0);
112         (void) open("/dev/console", O_RDONLY);
113 #endif
114 
115         execute(rc_command);
116         report(2, "sh /etc/rc");
117         _exit(1);       /* impossible, we hope */
118   }
119 
120   /* Clear /etc/utmp if it exists. */
121   if ((fd = open(PATH_UTMP, O_WRONLY | O_TRUNC)) >= 0) close(fd);
122 
123   /* Log system reboot. */
124   wtmp(BOOT_TIME, 0, NULL, 0);
125 
126   /* Main loop. If login processes have already been started up, wait for one
127    * to terminate, or for a HUP signal to arrive. Start up new login processes
128    * for all ttys which don't have them. Note that wait() also returns when
129    * somebody's orphan dies, in which case ignore it.  If the TERM signal is
130    * sent then stop spawning processes, shutdown time is near.
131    */
132 
133   check = 1;
134   while (1) {
135         while ((pid = waitpid(-1, NULL, check ? WNOHANG : 0)) > 0) {
136                 /* Search to see which line terminated. */
137                 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
138                         slotp = &slots[linenr];
139                         if (slotp->pid == pid) {
140                                 /* Record process exiting. */
141                                 wtmp(DEAD_PROCESS, linenr, NULL, pid);
142                                 slotp->pid = NO_PID;
143                                 check = 1;
144                         }
145                 }
146         }
147 
148         /* If a signal 1 (SIGHUP) is received, simply reset error counts. */
149         if (gothup) {
150                 gothup = 0;
151                 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
152                         slots[linenr].errct = 0;
153                 }
154                 check = 1;
155         }
156 
157         /* Shut down on signal 6 (SIGABRT). */
158         if (gotabrt) {
159                 gotabrt = 0;
160                 startup(0, &TT_REBOOT);
161         }
162 
163         if (spawn && check) {
164                 /* See which lines need a login process started up. */
165                 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
166                         slotp = &slots[linenr];
167                         if ((ttyp = getttyent()) == NULL) break;
168 
169                         if (ttyp->ty_getty != NULL
170                                 && ttyp->ty_getty[0] != NULL
171                                 && slotp->pid == NO_PID
172                                 && slotp->errct < ERRCT_DISABLE)
173                         {
174                                 startup(linenr, ttyp);
175                         }
176                 }
177                 endttyent();
178         }
179         check = 0;
180   }
181 }
182 
183 void onhup(int sig)
184 {
185   gothup = 1;
186   spawn = 1;
187 }
188 
189 void onterm(int sig)
190 {
191   spawn = 0;
192 }
193 
194 void onabrt(int sig)
195 {
196   static int count;
197 
198   if (++count == 2) reboot(RBT_HALT);
199   gotabrt = 1;
200 }
201 
202 void startup(int linenr, struct ttyent *ttyp)
203 {
204   /* Fork off a process for the indicated line. */
205 
206   struct slotent *slotp;                /* pointer to ttyslot */
207   pid_t pid;                            /* new pid */
208   int err[2];                           /* error reporting pipe */
209   char line[32];                        /* tty device name */
210   int status;
211 
212   slotp = &slots[linenr];
213 
214   /* Error channel for between fork and exec. */
215   if (pipe(err) < 0) err[0] = err[1] = -1;
216 
217   if ((pid = fork()) == -1 ) {
218         report(2, "fork()");
219         sleep(10);
220         return;
221   }
222 
223   if (pid == 0) {
224         /* Child */
225         close(err[0]);
226         fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC);
227 
228         /* A new session. */
229         setsid();
230 
231         /* Construct device name. */
232         strcpy(line, "/dev/");
233         strncat(line, ttyp->ty_name, sizeof(line) - 6);
234 
235         /* Open the line for standard input and output. */
236         close(0);
237         close(1);
238         if (open(line, O_RDWR) < 0 || dup(0) < 0) {
239                 write(err[1], &errno, sizeof(errno));
240                 _exit(1);
241         }
242 
243         if (ttyp->ty_init != NULL && ttyp->ty_init[0] != NULL) {
244                 /* Execute a command to initialize the terminal line. */
245 
246                 if ((pid = fork()) == -1) {
247                         report(2, "fork()");
248                         errno= 0;
249                         write(err[1], &errno, sizeof(errno));
250                         _exit(1);
251                 }
252 
253                 if (pid == 0) {
254                         alarm(10);
255                         execute(ttyp->ty_init);
256                         report(2, ttyp->ty_init[0]);
257                         _exit(1);
258                 }
259 
260                 while (waitpid(pid, &status, 0) != pid) {}
261                 if (status != 0) {
262                         tell(2, "init: ");
263                         tell(2, ttyp->ty_name);
264                         tell(2, ": ");
265                         tell(2, ttyp->ty_init[0]);
266                         tell(2, ": bad exit status\n");
267                         errno = 0;
268                         write(err[1], &errno, sizeof(errno));
269                         _exit(1);
270                 }
271         }
272 
273         /* Redirect standard error too. */
274         dup2(0, 2);
275 
276         /* Execute the getty process. */
277         execute(ttyp->ty_getty);
278 
279         /* Oops, disaster strikes. */
280         fcntl(2, F_SETFL, fcntl(2, F_GETFL) | O_NONBLOCK);
281         if (linenr != 0) report(2, ttyp->ty_getty[0]);
282         write(err[1], &errno, sizeof(errno));
283         _exit(1);
284   }
285 
286   /* Parent */
287   if (ttyp != &TT_REBOOT) slotp->pid = pid;
288 
289   close(err[1]);
290   if (read(err[0], &errno, sizeof(errno)) != 0) {
291         /* If an errno value goes down the error pipe: Problems. */
292 
293         switch (errno) {
294         case ENOENT:
295         case ENODEV:
296         case ENXIO:
297                 /* Device nonexistent, no driver, or no minor device. */
298                 slotp->errct = ERRCT_DISABLE;
299                 close(err[0]);
300                 return;
301         case 0:
302                 /* Error already reported. */
303                 break;
304         default:
305                 /* Any other error on the line. */
306                 report(2, ttyp->ty_name);
307         }
308         close(err[0]);
309 
310         if (++slotp->errct >= ERRCT_DISABLE) {
311                 tell(2, "init: ");
312                 tell(2, ttyp->ty_name);
313                 tell(2, ": excessive errors, shutting down\n");
314         } else {
315                 sleep(5);
316         }
317         return;
318   }
319   close(err[0]);
320 
321   if (ttyp != &TT_REBOOT) wtmp(LOGIN_PROCESS, linenr, ttyp->ty_name, pid);
322   slotp->errct = 0;
323 }
324 
325 
326 int execute(char **cmd)
327 {
328   /* Execute a command with a path search along /sbin:/bin:/usr/sbin:/usr/bin.
329    */
330   static char *nullenv[] = { NULL };
331   char command[128];
332   char *path[] = { "/sbin", "/bin", "/usr/sbin", "/usr/bin" };
333   int i;
334 
335   if (cmd[0][0] == '/') {
336         /* A full path. */
337         return execve(cmd[0], cmd, nullenv);
338   }
339 
340   /* Path search. */
341   for (i = 0; i < 4; i++) {
342         if (strlen(path[i]) + 1 + strlen(cmd[0]) + 1 > sizeof(command)) {
343                 errno= ENAMETOOLONG;
344                 return -1;
345         }
346         strcpy(command, path[i]);
347         strcat(command, "/");
348         strcat(command, cmd[0]);
349         execve(command, cmd, nullenv);
350         if (errno != ENOENT) break;
351   }
352   return -1;
353 }
354 
355 
356 void wtmp(type, linenr, line, pid)
357 int type;                       /* type of entry */
358 int linenr;                     /* line number in ttytab */
359 char *line;                     /* tty name (only good on login) */
360 pid_t pid;                      /* pid of process */
361 {
362 /* Log an event into the UTMP and WTMP files. */
363 
364   struct utmp utmp;             /* UTMP/WTMP User Accounting */
365   int fd;
366 
367   /* Clear the utmp record. */
368   memset((void *) &utmp, 0, sizeof(utmp));
369 
370   /* Fill in utmp. */
371   switch (type) {
372   case BOOT_TIME:
373         /* Make a special reboot record. */
374         strcpy(utmp.ut_name, "reboot");
375         strcpy(utmp.ut_line, "~");
376         break;
377 
378   case LOGIN_PROCESS:
379         /* A new login, fill in line name. */
380         strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
381         break;
382 
383   case DEAD_PROCESS:
384         /* A logout.  Use the current utmp entry, but make sure it is a
385          * user process exiting, and not getty or login giving up.
386          */
387         if ((fd = open(PATH_UTMP, O_RDONLY)) < 0) {
388                 if (errno != ENOENT) report(2, PATH_UTMP);
389                 return;
390         }
391         if (lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
392                 || read(fd, &utmp, sizeof(utmp)) == -1
393         ) {
394                 report(2, PATH_UTMP);
395                 close(fd);
396                 return;
397         }
398         close(fd);
399         if (utmp.ut_type != USER_PROCESS) return;
400         strncpy(utmp.ut_name, "", sizeof(utmp.ut_name));
401         break;
402   }
403 
404   /* Finish new utmp entry. */
405   utmp.ut_pid = pid;
406   utmp.ut_type = type;
407   utmp.ut_time = time((time_t *) 0);
408 
409   switch (type) {
410   case LOGIN_PROCESS:
411   case DEAD_PROCESS:
412         /* Write new entry to utmp. */
413         if ((fd = open(PATH_UTMP, O_WRONLY)) < 0
414                 || lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
415                 || write(fd, &utmp, sizeof(utmp)) == -1
416         ) {
417                 if (errno != ENOENT) report(2, PATH_UTMP);
418         }
419         if (fd != -1) close(fd);
420         break;
421   }
422 
423   switch (type) {
424   case BOOT_TIME:
425   case DEAD_PROCESS:
426         /* Add new wtmp entry. */
427         if ((fd = open(PATH_WTMP, O_WRONLY | O_APPEND)) < 0
428                   || write(fd, &utmp, sizeof(utmp)) == -1
429         ) {
430                 if (errno != ENOENT) report(2, PATH_WTMP);
431         }
432         if (fd != -1) close(fd);
433         break;
434   }
435 }
436 
437 void tell(fd, s)
438 int fd;
439 char *s;
440 {
441         write(fd, s, strlen(s));
442 }
443 
444 void report(fd, label)
445 int fd;
446 char *label;
447 {
448         int err = errno;
449 
450         tell(fd, "init: ");
451         tell(fd, label);
452         tell(fd, ": ");
453         tell(fd, strerror(err));
454         tell(fd, "\n");
455         errno= err;
456 }
457 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.