vm: fix potential null deref
[minix.git] / servers / init / init.c
blob445eaf88e6ae74b49e8932e68068ca9256d7dce9
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.
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.
16 #include <minix/type.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #include <sys/stat.h>
20 #include <sys/svrctl.h>
21 #include <ttyent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <time.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <utmp.h>
32 #ifdef __NBSD_LIBC
33 /* Different ttyent structure. */
34 struct ttyent TT_REBOOT = { "console", "shutdown -d now CTRL-ALT_DEL", "-"};
35 #else
36 /* Command to execute as a response to the three finger salute. */
37 char *REBOOT_CMD[] = { "shutdown", "-d", "now", "CTRL-ALT-DEL", NULL };
39 /* Associated fake ttytab entry. */
40 struct ttyent TT_REBOOT = { "console", "-", REBOOT_CMD, NULL };
41 #endif
43 char PATH_UTMP[] = "/etc/utmp"; /* current logins */
44 char PATH_WTMP[] = "/usr/adm/wtmp"; /* login/logout history */
45 char PATH_ROOT_WTMP[] = "/etc/wtmp"; /* wtmp for system up/down events */
47 #define PIDSLOTS 32 /* first this many ttys can be on */
49 struct slotent {
50 int errct; /* error count */
51 pid_t pid; /* pid of login process for this tty line */
54 #define ERRCT_DISABLE 10 /* disable after this many errors */
55 #define NO_PID 0 /* pid value indicating no process */
57 struct slotent slots[PIDSLOTS]; /* init table of ttys and pids */
59 int gothup = 0; /* flag, showing signal 1 was received */
60 int gotabrt = 0; /* flag, showing signal 6 was received */
61 int spawn = 1; /* flag, spawn processes only when set */
63 void tell(int fd, char *s);
64 void report(int fd, char *label);
65 void wtmp(int type, int linenr, char *line, pid_t pid);
66 void startup(int linenr, struct ttyent *ttyp);
67 int execute(char **cmd);
68 #ifdef __NBSD_LIBC
69 char **construct_argv(char *cmd);
70 #endif
71 void onhup(int sig);
72 void onterm(int sig);
73 void onabrt(int sig);
75 int main(void)
77 pid_t pid; /* pid of child process */
78 int fd; /* generally useful */
79 int linenr; /* loop variable */
80 int check; /* check if a new process must be spawned */
81 int sn; /* signal number */
82 struct slotent *slotp; /* slots[] pointer */
83 struct ttyent *ttyp; /* ttytab entry */
84 struct sigaction sa;
85 struct stat stb;
87 #define OPENFDS \
88 if (fstat(0, &stb) < 0) { \
89 /* Open standard input, output & error. */ \
90 (void) open("/dev/null", O_RDONLY); \
91 (void) open("/dev/log", O_WRONLY); \
92 dup(1); \
95 sigemptyset(&sa.sa_mask);
96 sa.sa_flags = 0;
98 /* Default: Ignore every signal (except those that follow). */
99 sa.sa_handler = SIG_IGN;
100 for (sn = 1; sn < _NSIG; sn++) {
101 sigaction(sn, &sa, NULL);
104 /* Hangup: Reexamine /etc/ttytab for newly enabled terminal lines. */
105 sa.sa_handler = onhup;
106 sigaction(SIGHUP, &sa, NULL);
108 /* Terminate: Stop spawning login processes, shutdown is near. */
109 sa.sa_handler = onterm;
110 sigaction(SIGTERM, &sa, NULL);
112 /* Abort: Sent by the kernel on CTRL-ALT-DEL; shut the system down. */
113 sa.sa_handler = onabrt;
114 sigaction(SIGABRT, &sa, NULL);
116 /* Execute the /etc/rc file. */
117 if ((pid = fork()) != 0) {
118 /* Parent just waits. */
119 while (wait(NULL) != pid) {
120 if (gotabrt) reboot(RBT_HALT);
122 } else {
123 #if ! SYS_GETKENV
124 struct sysgetenv sysgetenv;
125 #endif
126 char bootopts[16];
127 static char *rc_command[] = { "sh", "/etc/rc", NULL, NULL, NULL };
128 char **rcp = rc_command + 2;
130 /* Get the boot options from the boot environment. */
131 sysgetenv.key = "bootopts";
132 sysgetenv.keylen = 8+1;
133 sysgetenv.val = bootopts;
134 sysgetenv.vallen = sizeof(bootopts);
135 if (svrctl(PMGETPARAM, &sysgetenv) == 0) *rcp++ = bootopts;
136 *rcp = "start";
138 execute(rc_command);
139 report(2, "sh /etc/rc");
140 _exit(1); /* impossible, we hope */
143 OPENFDS;
145 /* Clear /etc/utmp if it exists. */
146 if ((fd = open(PATH_UTMP, O_WRONLY | O_TRUNC)) >= 0) close(fd);
148 /* Log system reboot. */
149 wtmp(BOOT_TIME, 0, NULL, 0);
151 /* Main loop. If login processes have already been started up, wait for one
152 * to terminate, or for a HUP signal to arrive. Start up new login processes
153 * for all ttys which don't have them. Note that wait() also returns when
154 * somebody's orphan dies, in which case ignore it. If the TERM signal is
155 * sent then stop spawning processes, shutdown time is near.
158 check = 1;
159 while (1) {
160 while ((pid = waitpid(-1, NULL, check ? WNOHANG : 0)) > 0) {
161 /* Search to see which line terminated. */
162 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
163 slotp = &slots[linenr];
164 if (slotp->pid == pid) {
165 /* Record process exiting. */
166 wtmp(DEAD_PROCESS, linenr, NULL, pid);
167 slotp->pid = NO_PID;
168 check = 1;
173 /* If a signal 1 (SIGHUP) is received, simply reset error counts. */
174 if (gothup) {
175 gothup = 0;
176 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
177 slots[linenr].errct = 0;
179 check = 1;
182 /* Shut down on signal 6 (SIGABRT). */
183 if (gotabrt) {
184 gotabrt = 0;
185 startup(0, &TT_REBOOT);
188 if (spawn && check) {
189 /* See which lines need a login process started up. */
190 for (linenr = 0; linenr < PIDSLOTS; linenr++) {
191 slotp = &slots[linenr];
192 if ((ttyp = getttyent()) == NULL) break;
194 if (ttyp->ty_getty != NULL
195 #ifdef __NBSD_LIBC
196 /* ty_getty is a string, and TTY_ON is
197 * the way to check for enabled ternimanls. */
198 && (ttyp->ty_status & TTY_ON)
199 #else
200 && ttyp->ty_getty[0] != NULL
201 #endif
202 && slotp->pid == NO_PID
203 && slotp->errct < ERRCT_DISABLE)
205 startup(linenr, ttyp);
208 endttyent();
210 check = 0;
214 void onhup(int sig)
216 gothup = 1;
217 spawn = 1;
220 void onterm(int sig)
222 spawn = 0;
225 void onabrt(int sig)
227 static int count = 0;
229 if (++count == 2) reboot(RBT_HALT);
230 gotabrt = 1;
233 void startup(int linenr, struct ttyent *ttyp)
235 /* Fork off a process for the indicated line. */
237 struct slotent *slotp; /* pointer to ttyslot */
238 pid_t pid; /* new pid */
239 int err[2]; /* error reporting pipe */
240 char line[32]; /* tty device name */
241 #ifndef __NBSD_LIBC
242 int status;
243 #endif
244 #ifdef __NBSD_LIBC
245 char **ty_getty_argv;
246 #endif
248 slotp = &slots[linenr];
250 /* Error channel for between fork and exec. */
251 if (pipe(err) < 0) err[0] = err[1] = -1;
253 if ((pid = fork()) == -1 ) {
254 report(2, "fork()");
255 sleep(10);
256 return;
259 if (pid == 0) {
260 /* Child */
261 close(err[0]);
262 fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC);
264 /* A new session. */
265 setsid();
267 /* Construct device name. */
268 strcpy(line, "/dev/");
269 strncat(line, ttyp->ty_name, sizeof(line) - 6);
271 /* Open the line for standard input and output. */
272 close(0);
273 close(1);
274 if (open(line, O_RDWR) < 0 || dup(0) < 0) {
275 write(err[1], &errno, sizeof(errno));
276 _exit(1);
279 #ifdef __NBSD_LIBC
280 /* ty_init not present. */
281 #else
282 if (ttyp->ty_init != NULL && ttyp->ty_init[0] != NULL) {
283 /* Execute a command to initialize the terminal line. */
285 if ((pid = fork()) == -1) {
286 report(2, "fork()");
287 errno= 0;
288 write(err[1], &errno, sizeof(errno));
289 _exit(1);
292 if (pid == 0) {
293 alarm(10);
294 execute(ttyp->ty_init);
295 report(2, ttyp->ty_init[0]);
296 _exit(1);
299 while (waitpid(pid, &status, 0) != pid) {}
300 if (status != 0) {
301 tell(2, "init: ");
302 tell(2, ttyp->ty_name);
303 tell(2, ": ");
304 tell(2, ttyp->ty_init[0]);
305 tell(2, ": bad exit status\n");
306 errno = 0;
307 write(err[1], &errno, sizeof(errno));
308 _exit(1);
311 #endif
313 /* Redirect standard error too. */
314 dup2(0, 2);
316 #ifdef __NBSD_LIBC
317 /* Construct argv for execute() */
318 ty_getty_argv = construct_argv(ttyp->ty_getty);
319 if (ty_getty_argv == NULL)
320 report(2, "construct_argv");
322 /* Execute the getty process. */
323 execute(ty_getty_argv);
324 #else
325 /* Execute the getty process. */
326 execute(ttyp->ty_getty);
327 #endif
329 /* Oops, disaster strikes. */
330 fcntl(2, F_SETFL, fcntl(2, F_GETFL) | O_NONBLOCK);
331 #ifdef __NBSD_LIBC
332 if (linenr != 0) report(2, ty_getty_argv[0]);
333 #else
334 if (linenr != 0) report(2, ttyp->ty_getty[0]);
335 #endif
336 write(err[1], &errno, sizeof(errno));
337 _exit(1);
340 /* Parent */
341 if (ttyp != &TT_REBOOT) slotp->pid = pid;
343 close(err[1]);
344 if (read(err[0], &errno, sizeof(errno)) != 0) {
345 /* If an errno value goes down the error pipe: Problems. */
347 switch (errno) {
348 case ENOENT:
349 case ENODEV:
350 case ENXIO:
351 /* Device nonexistent, no driver, or no minor device. */
352 slotp->errct = ERRCT_DISABLE;
353 close(err[0]);
354 return;
355 case 0:
356 /* Error already reported. */
357 break;
358 default:
359 /* Any other error on the line. */
360 report(2, ttyp->ty_name);
362 close(err[0]);
364 if (++slotp->errct >= ERRCT_DISABLE) {
365 tell(2, "init: ");
366 tell(2, ttyp->ty_name);
367 tell(2, ": excessive errors, shutting down\n");
368 } else {
369 sleep(5);
371 return;
373 close(err[0]);
375 if (ttyp != &TT_REBOOT) wtmp(LOGIN_PROCESS, linenr, ttyp->ty_name, pid);
376 slotp->errct = 0;
379 int execute(char **cmd)
381 /* Execute a command with a path search along /sbin:/bin:/usr/sbin:/usr/bin.
383 static char *nullenv[] = { NULL };
384 char command[128];
385 char *path[] = { "/sbin", "/bin", "/usr/sbin", "/usr/bin" };
386 int i;
388 if (cmd[0][0] == '/') {
389 /* A full path. */
390 return execve(cmd[0], cmd, nullenv);
393 /* Path search. */
394 for (i = 0; i < 4; i++) {
395 if (strlen(path[i]) + 1 + strlen(cmd[0]) + 1 > sizeof(command)) {
396 errno= ENAMETOOLONG;
397 return -1;
399 strcpy(command, path[i]);
400 strcat(command, "/");
401 strcat(command, cmd[0]);
402 execve(command, cmd, nullenv);
403 if (errno != ENOENT) break;
405 return -1;
408 void wtmp(type, linenr, line, pid)
409 int type; /* type of entry */
410 int linenr; /* line number in ttytab */
411 char *line; /* tty name (only good on login) */
412 pid_t pid; /* pid of process */
414 /* Log an event into the UTMP and WTMP files. */
416 struct utmp utmp; /* UTMP/WTMP User Accounting */
417 int fd;
419 /* Clear the utmp record. */
420 memset((void *) &utmp, 0, sizeof(utmp));
422 /* Fill in utmp. */
423 switch (type) {
424 case BOOT_TIME:
425 /* Make a special reboot record. */
426 strcpy(utmp.ut_name, "reboot");
427 strcpy(utmp.ut_line, "~");
428 break;
430 case LOGIN_PROCESS:
431 /* A new login, fill in line name. */
432 strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
433 break;
435 case DEAD_PROCESS:
436 /* A logout. Use the current utmp entry, but make sure it is a
437 * user process exiting, and not getty or login giving up.
439 if ((fd = open(PATH_UTMP, O_RDONLY)) < 0) {
440 if (errno != ENOENT) report(2, PATH_UTMP);
441 return;
443 if (lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
444 || read(fd, &utmp, sizeof(utmp)) == -1
446 report(2, PATH_UTMP);
447 close(fd);
448 return;
450 close(fd);
451 if (utmp.ut_type != USER_PROCESS) return;
452 strncpy(utmp.ut_name, "", sizeof(utmp.ut_name));
453 break;
456 /* Finish new utmp entry. */
457 utmp.ut_pid = pid;
458 utmp.ut_type = type;
459 utmp.ut_time = time((time_t *) 0);
461 switch (type) {
462 case LOGIN_PROCESS:
463 case DEAD_PROCESS:
464 /* Write new entry to utmp. */
465 if ((fd = open(PATH_UTMP, O_WRONLY)) < 0
466 || lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1
467 || write(fd, &utmp, sizeof(utmp)) == -1
469 if (errno != ENOENT) report(2, PATH_UTMP);
471 if (fd != -1) close(fd);
472 break;
475 switch (type) {
476 case BOOT_TIME:
477 /* Add new root wtmp entry. */
478 if ((fd = open(PATH_ROOT_WTMP, O_WRONLY | O_APPEND)) < 0
479 || write(fd, &utmp, sizeof(utmp)) == -1
481 if (errno != ENOENT) report(2, PATH_ROOT_WTMP);
483 if (fd != -1) close(fd);
484 /* fall-through */
485 case DEAD_PROCESS:
486 /* Add new wtmp entry. */
487 if ((fd = open(PATH_WTMP, O_WRONLY | O_APPEND)) < 0
488 || write(fd, &utmp, sizeof(utmp)) == -1
490 if (errno != ENOENT) report(2, PATH_WTMP);
492 if (fd != -1) close(fd);
493 break;
497 char **
498 construct_argv(char *cmd)
500 int argc = 0;
501 static const char sep[] = " \t";
502 char **argv = malloc(((strlen(cmd) + 1) / 2 + 1) * sizeof (char *));
504 if (argv == NULL)
505 return NULL;
507 if ((argv[argc++] = strtok(cmd, sep)) == 0) {
508 free(argv);
509 return NULL;
511 while ((argv[argc++] = strtok(NULL, sep)) != NULL)
512 continue;
513 return argv;
516 void tell(fd, s)
517 int fd;
518 char *s;
520 write(fd, s, strlen(s));
523 void report(fd, label)
524 int fd;
525 char *label;
527 int err = errno;
529 tell(fd, "init: ");
530 tell(fd, label);
531 tell(fd, ": ");
532 tell(fd, strerror(err));
533 tell(fd, "\n");
534 errno= err;