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
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.