source: trunk/minix/servers/init/init.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

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