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>
32 /* Command to execute as a response to the three finger salute. */
33 char *REBOOT_CMD
[] = { "shutdown", "now", "CTRL-ALT-DEL", NULL
};
35 /* Associated fake ttytab entry. */
36 struct ttyent TT_REBOOT
= { "console", "-", REBOOT_CMD
, NULL
};
38 char PATH_UTMP
[] = "/etc/utmp"; /* current logins */
39 char PATH_WTMP
[] = "/usr/adm/wtmp"; /* login/logout history */
41 #define PIDSLOTS 32 /* first this many ttys can be on */
44 int errct
; /* error count */
45 pid_t pid
; /* pid of login process for this tty line */
48 #define ERRCT_DISABLE 10 /* disable after this many errors */
49 #define NO_PID 0 /* pid value indicating no process */
51 struct slotent slots
[PIDSLOTS
]; /* init table of ttys and pids */
53 int gothup
= 0; /* flag, showing signal 1 was received */
54 int gotabrt
= 0; /* flag, showing signal 6 was received */
55 int spawn
= 1; /* flag, spawn processes only when set */
57 void tell(int fd
, char *s
);
58 void report(int fd
, char *label
);
59 void wtmp(int type
, int linenr
, char *line
, pid_t pid
);
60 void startup(int linenr
, struct ttyent
*ttyp
);
61 int execute(char **cmd
);
68 pid_t pid
; /* pid of child process */
69 int fd
; /* generally useful */
70 int linenr
; /* loop variable */
71 int check
; /* check if a new process must be spawned */
72 int sn
; /* signal number */
73 struct slotent
*slotp
; /* slots[] pointer */
74 struct ttyent
*ttyp
; /* ttytab entry */
79 if (fstat(0, &stb) < 0) { \
80 /* Open standard input, output & error. */ \
81 (void) open("/dev/null", O_RDONLY); \
82 (void) open("/dev/log", O_WRONLY); \
86 sigemptyset(&sa
.sa_mask
);
89 /* Default: Ignore every signal (except those that follow). */
90 sa
.sa_handler
= SIG_IGN
;
91 for (sn
= 1; sn
< _NSIG
; sn
++) {
92 sigaction(sn
, &sa
, NULL
);
95 /* Hangup: Reexamine /etc/ttytab for newly enabled terminal lines. */
96 sa
.sa_handler
= onhup
;
97 sigaction(SIGHUP
, &sa
, NULL
);
99 /* Terminate: Stop spawning login processes, shutdown is near. */
100 sa
.sa_handler
= onterm
;
101 sigaction(SIGTERM
, &sa
, NULL
);
103 /* Abort: Sent by the kernel on CTRL-ALT-DEL; shut the system down. */
104 sa
.sa_handler
= onabrt
;
105 sigaction(SIGABRT
, &sa
, NULL
);
107 /* Execute the /etc/rc file. */
108 if ((pid
= fork()) != 0) {
109 /* Parent just waits. */
110 while (wait(NULL
) != pid
) {
111 if (gotabrt
) reboot(RBT_HALT
);
115 struct sysgetenv sysgetenv
;
118 static char *rc_command
[] = { "sh", "/etc/rc", NULL
, NULL
, NULL
};
119 char **rcp
= rc_command
+ 2;
121 /* Get the boot options from the boot environment. */
122 sysgetenv
.key
= "bootopts";
123 sysgetenv
.keylen
= 8+1;
124 sysgetenv
.val
= bootopts
;
125 sysgetenv
.vallen
= sizeof(bootopts
);
126 if (svrctl(MMGETPARAM
, &sysgetenv
) == 0) *rcp
++ = bootopts
;
130 report(2, "sh /etc/rc");
131 _exit(1); /* impossible, we hope */
136 /* Clear /etc/utmp if it exists. */
137 if ((fd
= open(PATH_UTMP
, O_WRONLY
| O_TRUNC
)) >= 0) close(fd
);
139 /* Log system reboot. */
140 wtmp(BOOT_TIME
, 0, NULL
, 0);
142 /* Main loop. If login processes have already been started up, wait for one
143 * to terminate, or for a HUP signal to arrive. Start up new login processes
144 * for all ttys which don't have them. Note that wait() also returns when
145 * somebody's orphan dies, in which case ignore it. If the TERM signal is
146 * sent then stop spawning processes, shutdown time is near.
151 while ((pid
= waitpid(-1, NULL
, check
? WNOHANG
: 0)) > 0) {
152 /* Search to see which line terminated. */
153 for (linenr
= 0; linenr
< PIDSLOTS
; linenr
++) {
154 slotp
= &slots
[linenr
];
155 if (slotp
->pid
== pid
) {
156 /* Record process exiting. */
157 wtmp(DEAD_PROCESS
, linenr
, NULL
, pid
);
164 /* If a signal 1 (SIGHUP) is received, simply reset error counts. */
167 for (linenr
= 0; linenr
< PIDSLOTS
; linenr
++) {
168 slots
[linenr
].errct
= 0;
173 /* Shut down on signal 6 (SIGABRT). */
176 startup(0, &TT_REBOOT
);
179 if (spawn
&& check
) {
180 /* See which lines need a login process started up. */
181 for (linenr
= 0; linenr
< PIDSLOTS
; linenr
++) {
182 slotp
= &slots
[linenr
];
183 if ((ttyp
= getttyent()) == NULL
) break;
185 if (ttyp
->ty_getty
!= NULL
186 && ttyp
->ty_getty
[0] != NULL
187 && slotp
->pid
== NO_PID
188 && slotp
->errct
< ERRCT_DISABLE
)
190 startup(linenr
, ttyp
);
212 static int count
= 0;
214 if (++count
== 2) reboot(RBT_HALT
);
218 void startup(int linenr
, struct ttyent
*ttyp
)
220 /* Fork off a process for the indicated line. */
222 struct slotent
*slotp
; /* pointer to ttyslot */
223 pid_t pid
; /* new pid */
224 int err
[2]; /* error reporting pipe */
225 char line
[32]; /* tty device name */
228 slotp
= &slots
[linenr
];
230 /* Error channel for between fork and exec. */
231 if (pipe(err
) < 0) err
[0] = err
[1] = -1;
233 if ((pid
= fork()) == -1 ) {
242 fcntl(err
[1], F_SETFD
, fcntl(err
[1], F_GETFD
) | FD_CLOEXEC
);
247 /* Construct device name. */
248 strcpy(line
, "/dev/");
249 strncat(line
, ttyp
->ty_name
, sizeof(line
) - 6);
251 /* Open the line for standard input and output. */
254 if (open(line
, O_RDWR
) < 0 || dup(0) < 0) {
255 write(err
[1], &errno
, sizeof(errno
));
259 if (ttyp
->ty_init
!= NULL
&& ttyp
->ty_init
[0] != NULL
) {
260 /* Execute a command to initialize the terminal line. */
262 if ((pid
= fork()) == -1) {
265 write(err
[1], &errno
, sizeof(errno
));
271 execute(ttyp
->ty_init
);
272 report(2, ttyp
->ty_init
[0]);
276 while (waitpid(pid
, &status
, 0) != pid
) {}
279 tell(2, ttyp
->ty_name
);
281 tell(2, ttyp
->ty_init
[0]);
282 tell(2, ": bad exit status\n");
284 write(err
[1], &errno
, sizeof(errno
));
289 /* Redirect standard error too. */
292 /* Execute the getty process. */
293 execute(ttyp
->ty_getty
);
295 /* Oops, disaster strikes. */
296 fcntl(2, F_SETFL
, fcntl(2, F_GETFL
) | O_NONBLOCK
);
297 if (linenr
!= 0) report(2, ttyp
->ty_getty
[0]);
298 write(err
[1], &errno
, sizeof(errno
));
303 if (ttyp
!= &TT_REBOOT
) slotp
->pid
= pid
;
306 if (read(err
[0], &errno
, sizeof(errno
)) != 0) {
307 /* If an errno value goes down the error pipe: Problems. */
313 /* Device nonexistent, no driver, or no minor device. */
314 slotp
->errct
= ERRCT_DISABLE
;
318 /* Error already reported. */
321 /* Any other error on the line. */
322 report(2, ttyp
->ty_name
);
326 if (++slotp
->errct
>= ERRCT_DISABLE
) {
328 tell(2, ttyp
->ty_name
);
329 tell(2, ": excessive errors, shutting down\n");
337 if (ttyp
!= &TT_REBOOT
) wtmp(LOGIN_PROCESS
, linenr
, ttyp
->ty_name
, pid
);
341 int execute(char **cmd
)
343 /* Execute a command with a path search along /sbin:/bin:/usr/sbin:/usr/bin.
345 static char *nullenv
[] = { NULL
};
347 char *path
[] = { "/sbin", "/bin", "/usr/sbin", "/usr/bin" };
350 if (cmd
[0][0] == '/') {
352 return execve(cmd
[0], cmd
, nullenv
);
356 for (i
= 0; i
< 4; i
++) {
357 if (strlen(path
[i
]) + 1 + strlen(cmd
[0]) + 1 > sizeof(command
)) {
361 strcpy(command
, path
[i
]);
362 strcat(command
, "/");
363 strcat(command
, cmd
[0]);
364 execve(command
, cmd
, nullenv
);
365 if (errno
!= ENOENT
) break;
370 void wtmp(type
, linenr
, line
, pid
)
371 int type
; /* type of entry */
372 int linenr
; /* line number in ttytab */
373 char *line
; /* tty name (only good on login) */
374 pid_t pid
; /* pid of process */
376 /* Log an event into the UTMP and WTMP files. */
378 struct utmp utmp
; /* UTMP/WTMP User Accounting */
381 /* Clear the utmp record. */
382 memset((void *) &utmp
, 0, sizeof(utmp
));
387 /* Make a special reboot record. */
388 strcpy(utmp
.ut_name
, "reboot");
389 strcpy(utmp
.ut_line
, "~");
393 /* A new login, fill in line name. */
394 strncpy(utmp
.ut_line
, line
, sizeof(utmp
.ut_line
));
398 /* A logout. Use the current utmp entry, but make sure it is a
399 * user process exiting, and not getty or login giving up.
401 if ((fd
= open(PATH_UTMP
, O_RDONLY
)) < 0) {
402 if (errno
!= ENOENT
) report(2, PATH_UTMP
);
405 if (lseek(fd
, (off_t
) (linenr
+1) * sizeof(utmp
), SEEK_SET
) == -1
406 || read(fd
, &utmp
, sizeof(utmp
)) == -1
408 report(2, PATH_UTMP
);
413 if (utmp
.ut_type
!= USER_PROCESS
) return;
414 strncpy(utmp
.ut_name
, "", sizeof(utmp
.ut_name
));
418 /* Finish new utmp entry. */
421 utmp
.ut_time
= time((time_t *) 0);
426 /* Write new entry to utmp. */
427 if ((fd
= open(PATH_UTMP
, O_WRONLY
)) < 0
428 || lseek(fd
, (off_t
) (linenr
+1) * sizeof(utmp
), SEEK_SET
) == -1
429 || write(fd
, &utmp
, sizeof(utmp
)) == -1
431 if (errno
!= ENOENT
) report(2, PATH_UTMP
);
433 if (fd
!= -1) close(fd
);
440 /* Add new wtmp entry. */
441 if ((fd
= open(PATH_WTMP
, O_WRONLY
| O_APPEND
)) < 0
442 || write(fd
, &utmp
, sizeof(utmp
)) == -1
444 if (errno
!= ENOENT
) report(2, PATH_WTMP
);
446 if (fd
!= -1) close(fd
);
455 write(fd
, s
, strlen(s
));
458 void report(fd
, label
)
467 tell(fd
, strerror(err
));