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
16 #include <minix/type.h>
17 #include <sys/types.h>
20 #include <sys/svrctl.h>
33 /* Different ttyent structure. */
34 struct ttyent TT_REBOOT
= { "console", "shutdown -d now CTRL-ALT_DEL", "-"};
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
};
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 */
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
);
69 char **construct_argv(char *cmd
);
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 */
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); \
95 sigemptyset(&sa
.sa_mask
);
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
);
124 struct sysgetenv sysgetenv
;
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
;
139 report(2, "sh /etc/rc");
140 _exit(1); /* impossible, we hope */
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.
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
);
173 /* If a signal 1 (SIGHUP) is received, simply reset error counts. */
176 for (linenr
= 0; linenr
< PIDSLOTS
; linenr
++) {
177 slots
[linenr
].errct
= 0;
182 /* Shut down on signal 6 (SIGABRT). */
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
196 /* ty_getty is a string, and TTY_ON is
197 * the way to check for enabled ternimanls. */
198 && (ttyp
->ty_status
& TTY_ON
)
200 && ttyp
->ty_getty
[0] != NULL
202 && slotp
->pid
== NO_PID
203 && slotp
->errct
< ERRCT_DISABLE
)
205 startup(linenr
, ttyp
);
227 static int count
= 0;
229 if (++count
== 2) reboot(RBT_HALT
);
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 */
245 char **ty_getty_argv
;
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 ) {
262 fcntl(err
[1], F_SETFD
, fcntl(err
[1], F_GETFD
) | FD_CLOEXEC
);
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. */
274 if (open(line
, O_RDWR
) < 0 || dup(0) < 0) {
275 write(err
[1], &errno
, sizeof(errno
));
280 /* ty_init not present. */
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) {
288 write(err
[1], &errno
, sizeof(errno
));
294 execute(ttyp
->ty_init
);
295 report(2, ttyp
->ty_init
[0]);
299 while (waitpid(pid
, &status
, 0) != pid
) {}
302 tell(2, ttyp
->ty_name
);
304 tell(2, ttyp
->ty_init
[0]);
305 tell(2, ": bad exit status\n");
307 write(err
[1], &errno
, sizeof(errno
));
313 /* Redirect standard error too. */
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
);
325 /* Execute the getty process. */
326 execute(ttyp
->ty_getty
);
329 /* Oops, disaster strikes. */
330 fcntl(2, F_SETFL
, fcntl(2, F_GETFL
) | O_NONBLOCK
);
332 if (linenr
!= 0) report(2, ty_getty_argv
[0]);
334 if (linenr
!= 0) report(2, ttyp
->ty_getty
[0]);
336 write(err
[1], &errno
, sizeof(errno
));
341 if (ttyp
!= &TT_REBOOT
) slotp
->pid
= pid
;
344 if (read(err
[0], &errno
, sizeof(errno
)) != 0) {
345 /* If an errno value goes down the error pipe: Problems. */
351 /* Device nonexistent, no driver, or no minor device. */
352 slotp
->errct
= ERRCT_DISABLE
;
356 /* Error already reported. */
359 /* Any other error on the line. */
360 report(2, ttyp
->ty_name
);
364 if (++slotp
->errct
>= ERRCT_DISABLE
) {
366 tell(2, ttyp
->ty_name
);
367 tell(2, ": excessive errors, shutting down\n");
375 if (ttyp
!= &TT_REBOOT
) wtmp(LOGIN_PROCESS
, linenr
, ttyp
->ty_name
, pid
);
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
};
385 char *path
[] = { "/sbin", "/bin", "/usr/sbin", "/usr/bin" };
388 if (cmd
[0][0] == '/') {
390 return execve(cmd
[0], cmd
, nullenv
);
394 for (i
= 0; i
< 4; i
++) {
395 if (strlen(path
[i
]) + 1 + strlen(cmd
[0]) + 1 > sizeof(command
)) {
399 strcpy(command
, path
[i
]);
400 strcat(command
, "/");
401 strcat(command
, cmd
[0]);
402 execve(command
, cmd
, nullenv
);
403 if (errno
!= ENOENT
) break;
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 */
419 /* Clear the utmp record. */
420 memset((void *) &utmp
, 0, sizeof(utmp
));
425 /* Make a special reboot record. */
426 strcpy(utmp
.ut_name
, "reboot");
427 strcpy(utmp
.ut_line
, "~");
431 /* A new login, fill in line name. */
432 strncpy(utmp
.ut_line
, line
, sizeof(utmp
.ut_line
));
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
);
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
);
451 if (utmp
.ut_type
!= USER_PROCESS
) return;
452 strncpy(utmp
.ut_name
, "", sizeof(utmp
.ut_name
));
456 /* Finish new utmp entry. */
459 utmp
.ut_time
= time((time_t *) 0);
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
);
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
);
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
);
498 construct_argv(char *cmd
)
501 static const char sep
[] = " \t";
502 char **argv
= malloc(((strlen(cmd
) + 1) / 2 + 1) * sizeof (char *));
507 if ((argv
[argc
++] = strtok(cmd
, sep
)) == 0) {
511 while ((argv
[argc
++] = strtok(NULL
, sep
)) != NULL
)
520 write(fd
, s
, strlen(s
));
523 void report(fd
, label
)
532 tell(fd
, strerror(err
));