fix tty inheritance
[unix-history] / usr / src / sbin / init / init.c
CommitLineData
284ed8f8
KM
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Donn Seeley at UUNET Technologies, Inc.
7 *
8 * %sccs.include.redist.c%
528b0614
DF
9 */
10
37c640e2 11#ifndef lint
284ed8f8
KM
12char copyright[] =
13"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
14 All rights reserved.\n";
15#endif /* not lint */
16
17#ifndef lint
6f2ac96c 18static char sccsid[] = "@(#)init.c 6.10 (Berkeley) %G%";
284ed8f8 19#endif /* not lint */
37c640e2 20
7c8ab0e6 21#include <sys/types.h>
284ed8f8
KM
22#include <sys/wait.h>
23#include <db.h>
f570e1ff 24#include <errno.h>
284ed8f8
KM
25#include <fcntl.h>
26#include <signal.h>
284ed8f8
KM
27#include <syslog.h>
28#include <time.h>
9479aa87 29#include <ttyent.h>
284ed8f8 30#include <unistd.h>
869bbd31
KB
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
284ed8f8
KM
34
35#ifdef __STDC__
36#include <stdarg.h>
37#else
38#include <varargs.h>
39#endif
40
41#ifdef SECURE
42#include <pwd.h>
43#endif
44
9074d4b1 45#include "pathnames.h"
7c8ab0e6 46
284ed8f8
KM
47/*
48 * Until the mythical util.h arrives...
49 */
50extern int login_tty __P((int));
51extern int logout __P((const char *));
52extern void logwtmp __P((const char *, const char *, const char *));
7c8ab0e6 53
284ed8f8
KM
54/*
55 * Sleep times; used to prevent thrashing.
56 */
b5775271 57#define GETTY_SPACING 10 /* fork getty on a port every N secs */
284ed8f8
KM
58#define WINDOW_WAIT 3 /* wait N secs after starting window */
59#define STALL_TIMEOUT 30 /* wait N secs after warning */
b5775271 60#define DEATH_WATCH 10 /* wait N secs for procs to die */
7c8ab0e6 61
284ed8f8
KM
62void handle __P((sig_t, ...));
63void delset __P((sigset_t *, ...));
64
65void stall __P((char *, ...));
66void warning __P((char *, ...));
67void emergency __P((char *, ...));
68void disaster __P((int));
69
70/*
71 * We really need a recursive typedef...
72 * The following at least guarantees that the return type of (*state_t)()
73 * is sufficiently wide to hold a function pointer.
74 */
75typedef long (*state_func_t) __P((void));
76typedef state_func_t (*state_t) __P((void));
77
78state_func_t single_user __P((void));
79state_func_t runcom __P((void));
80state_func_t read_ttys __P((void));
81state_func_t multi_user __P((void));
82state_func_t clean_ttys __P((void));
83state_func_t catatonia __P((void));
84state_func_t death __P((void));
85
86enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
87
88void transition __P((state_t));
89state_t requested_transition = runcom;
d0ca48ff 90
284ed8f8
KM
91void setctty __P((char *));
92
93typedef struct session {
94 int se_index; /* index of entry in ttys file */
95 pid_t se_process; /* controlling process */
96 time_t se_started; /* used to avoid thrashing */
97 int se_flags; /* status of session */
98#define SE_SHUTDOWN 0x1 /* session won't be restarted */
99 char *se_device; /* filename of port */
100 char *se_getty; /* what to run on that port */
101 char **se_getty_argv; /* pre-parsed argument array */
102 char *se_window; /* window system (started only once) */
103 char **se_window_argv; /* pre-parsed argument array */
104 struct session *se_prev;
105 struct session *se_next;
106} session_t;
107
108void free_session __P((session_t *));
109session_t *new_session __P((session_t *, int, struct ttyent *));
110session_t *sessions;
111
112char **construct_argv __P((char *));
113void start_window_system __P((session_t *));
b5775271 114void collect_child __P((int));
284ed8f8 115pid_t start_getty __P((session_t *));
284ed8f8
KM
116void transition_handler __P((int));
117void alrm_handler __P((int));
118int clang;
119
120int start_logger __P((void));
121void clear_session_logs __P((session_t *));
122int logger_enable;
123
b5775271 124int start_session_db __P((void));
284ed8f8
KM
125void add_session __P((session_t *));
126void del_session __P((session_t *));
127session_t *find_session __P((pid_t));
128DB *session_db;
129
b5775271
KM
130/*
131 * The mother of all processes.
132 */
284ed8f8 133int
fecfa7b3 134main(argc, argv)
284ed8f8 135 int argc;
fecfa7b3 136 char **argv;
7c8ab0e6 137{
284ed8f8 138 int c;
b5775271 139 struct sigaction sa;
284ed8f8
KM
140 sigset_t mask;
141
869bbd31
KB
142
143 /* Dispose of random users. */
144 if (getuid() != 0) {
145 (void)fprintf(stderr, "init: %s\n", strerror(EPERM));
146 exit (1);
147 }
148
149 /* System V users like to reexec init. */
150 if (getpid() != 1) {
151 (void)fprintf(stderr, "init: already running\n");
152 exit (1);
153 }
4d6fe690
MK
154
155 /*
284ed8f8
KM
156 * Note that this does NOT open a file...
157 * Does 'init' deserve its own facility number?
4d6fe690 158 */
b5775271
KM
159 openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
160
161 /*
162 * Create an initial session.
163 */
164 if (setsid() < 0)
165 syslog(LOG_ERR, "setsid failed (initial) %m");
8d3a6b89 166
284ed8f8
KM
167 /*
168 * This code assumes that we always get arguments through flags,
169 * never through bits set in some random machine register.
170 */
171 while ((c = getopt(argc, argv, "sf")) != -1)
172 switch (c) {
173 case 's':
174 requested_transition = single_user;
175 break;
4d6fe690 176 case 'f':
284ed8f8 177 runcom_mode = FASTBOOT;
8d3a6b89 178 break;
284ed8f8
KM
179 default:
180 warning("unrecognized flag '-%c'", c);
8d3a6b89
TL
181 break;
182 }
284ed8f8
KM
183
184 if (optind != argc)
185 warning("ignoring excess arguments");
186
187 /*
188 * We catch or block signals rather than ignore them,
189 * so that they get reset on exec.
190 */
191 handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
192 SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, 0);
193 handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0);
194 handle(alrm_handler, SIGALRM, 0);
284ed8f8 195 sigfillset(&mask);
b5775271
KM
196 delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
197 SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0);
284ed8f8 198 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
b5775271
KM
199 sigemptyset(&sa.sa_mask);
200 sa.sa_flags = 0;
201 sa.sa_handler = SIG_IGN;
202 (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
203 (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0);
284ed8f8
KM
204
205 /*
206 * Paranoia.
207 */
208 close(0);
209 close(1);
210 close(2);
211
212 /*
213 * Start the state machine.
214 */
215 transition(requested_transition);
216
217 /*
218 * Should never reach here.
219 */
220 return 1;
221}
222
b5775271
KM
223/*
224 * Associate a function with a signal handler.
225 */
284ed8f8
KM
226void
227#ifdef __STDC__
228handle(sig_t handler, ...)
229#else
230handle(va_alist)
231 va_dcl
232#endif
233{
234 int sig;
235 struct sigaction sa;
236 int mask_everything;
237 va_list ap;
238#ifndef __STDC__
239 sig_t handler;
240
241 va_start(ap);
242 handler = va_arg(ap, sig_t);
243#else
244 va_start(ap, handler);
245#endif
246
247 sa.sa_handler = handler;
248 sigfillset(&mask_everything);
249
250 while (sig = va_arg(ap, int)) {
251 sa.sa_mask = mask_everything;
252 /* XXX SA_RESTART? */
253 sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
254 sigaction(sig, &sa, (struct sigaction *) 0);
7c8ab0e6
BJ
255 }
256}
257
b5775271
KM
258/*
259 * Delete a set of signals from a mask.
260 */
284ed8f8
KM
261void
262#ifdef __STDC__
263delset(sigset_t *maskp, ...)
264#else
265delset(va_alist)
266 va_dcl
267#endif
268{
269 int sig;
270 va_list ap;
271#ifndef __STDC__
272 sigset_t *maskp;
273
274 va_start(ap);
275 maskp = va_arg(ap, sigset_t *);
276#else
277 va_start(ap, maskp);
278#endif
279
280 while (sig = va_arg(ap, int))
281 sigdelset(maskp, sig);
282}
abf0db3c 283
284ed8f8
KM
284/*
285 * Log a message and sleep for a while (to give someone an opportunity
286 * to read it and to save log or hardcopy output if the problem is chronic).
bb56e935 287 * NB: should send a message to the session logger to avoid blocking.
284ed8f8
KM
288 */
289void
290#ifdef __STDC__
291stall(char *message, ...)
292#else
293stall(va_alist)
294 va_dcl
295#endif
7c8ab0e6 296{
284ed8f8
KM
297 pid_t pid;
298 va_list ap;
299#ifndef __STDC__
300 char *message;
301
302 va_start(ap);
303 message = va_arg(ap, char *);
304#else
305 va_start(ap, message);
306#endif
307
bb56e935 308 vsyslog(LOG_ALERT, message, ap);
284ed8f8
KM
309 va_end(ap);
310 sleep(STALL_TIMEOUT);
abf0db3c
BJ
311}
312
284ed8f8
KM
313/*
314 * Like stall(), but doesn't sleep.
315 * If cpp had variadic macros, the two functions could be #defines for another.
bb56e935 316 * NB: should send a message to the session logger to avoid blocking.
284ed8f8 317 */
caf94fd3 318void
284ed8f8
KM
319#ifdef __STDC__
320warning(char *message, ...)
321#else
322warning(va_alist)
323 va_dcl
324#endif
abf0db3c 325{
284ed8f8
KM
326 va_list ap;
327#ifndef __STDC__
328 char *message;
329
330 va_start(ap);
331 message = va_arg(ap, char *);
332#else
333 va_start(ap, message);
334#endif
abf0db3c 335
bb56e935 336 vsyslog(LOG_ALERT, message, ap);
284ed8f8 337 va_end(ap);
abf0db3c
BJ
338}
339
284ed8f8 340/*
bb56e935
KM
341 * Log an emergency message.
342 * NB: should send a message to the session logger to avoid blocking.
284ed8f8
KM
343 */
344void
345#ifdef __STDC__
346emergency(char *message, ...)
347#else
348emergency(va_alist)
349 va_dcl
350#endif
abf0db3c 351{
284ed8f8
KM
352 va_list ap;
353#ifndef __STDC__
354 char *message;
abf0db3c 355
284ed8f8
KM
356 va_start(ap);
357 message = va_arg(ap, char *);
358#else
359 va_start(ap, message);
360#endif
361
362 vsyslog(LOG_EMERG, message, ap);
363 va_end(ap);
7c8ab0e6
BJ
364}
365
b5775271
KM
366/*
367 * Catch an unexpected signal.
368 */
284ed8f8
KM
369void
370disaster(sig)
371 int sig;
7c8ab0e6 372{
284ed8f8
KM
373 emergency("fatal signal: %s",
374 sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal");
375
376 sleep(STALL_TIMEOUT);
377 _exit(sig); /* reboot */
7c8ab0e6
BJ
378}
379
284ed8f8
KM
380/*
381 * Change states in the finite state machine.
382 * The initial state is passed as an argument.
383 */
384void
385transition(s)
386 state_t s;
387{
388 for (;;)
389 s = (state_t) (*s)();
390}
391
392/*
393 * We send requests for session logging to another process for two reasons.
394 * First, we don't want to block if the log files go away (e.g. because
395 * one or more are on hard-mounted NFS systems whose server crashes).
396 * Second, despite all the crud already contained in init, it still isn't
397 * right that init should care about session logging record formats and files.
398 * We could use explicit 'Unix' IPC for this, but let's try to be POSIX...
399 */
400int
401start_logger()
7c8ab0e6 402{
284ed8f8
KM
403 static char *argv[] = { _PATH_SLOGGER, 0 };
404 int fd, pfd[2];
405 pid_t pid;
406 sigset_t mask;
407
408 if (pipe(pfd) == -1) {
409 warning("session logging disabled: can't make pipe to %s: %m",
410 argv[0]);
411 return -1;
412 }
413 if ((pid = fork()) == -1) {
414 emergency("session logging disabled: can't fork for %s: %m",
415 argv[0]);
416 return -1;
417 }
7c8ab0e6 418
d0ca48ff 419 if (pid == 0) {
284ed8f8
KM
420 close(pfd[1]);
421 if (pfd[0] != 0) {
422 dup2(pfd[0], 0);
423 close(pfd[0]);
424 }
425 if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != -1) {
426 if (fd != 1)
427 dup2(fd, 1);
428 if (fd != 2)
429 dup2(fd, 2);
430 if (fd != 1 && fd != 2)
c9d9fdd4 431 close(fd);
284ed8f8
KM
432 } else {
433 /* paranoid */
434 close(1);
435 close(2);
c9d9fdd4 436 }
284ed8f8
KM
437 sigemptyset(&mask);
438 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
439 execv(argv[0], argv);
440 stall("can't exec %s: %m", argv[0]);
441 _exit(1);
7c8ab0e6 442 }
284ed8f8
KM
443
444 close(pfd[0]);
445 fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
446 fcntl(pfd[1], F_SETFL, O_NONBLOCK);
447
448 return pfd[1];
7c8ab0e6
BJ
449}
450
b5775271
KM
451/*
452 * Close out the accounting files for a login session.
bb56e935 453 * NB: should send a message to the session logger to avoid blocking.
b5775271 454 */
284ed8f8
KM
455void
456clear_session_logs(sp)
457 session_t *sp;
7c8ab0e6 458{
6f2ac96c
CT
459 char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
460
461 if (logout(line))
462 logwtmp(line, "", "");
7c8ab0e6
BJ
463}
464
d0ca48ff 465/*
284ed8f8
KM
466 * Start a session and allocate a controlling terminal.
467 * Only called by children of init after forking.
d0ca48ff 468 */
caf94fd3 469void
284ed8f8
KM
470setctty(name)
471 char *name;
d0ca48ff 472{
284ed8f8
KM
473 int fd;
474
b5775271 475 (void) revoke(name);
284ed8f8
KM
476 if ((fd = open(name, O_RDWR)) == -1) {
477 stall("can't open %s: %m", name);
478 _exit(1);
479 }
480 if (login_tty(fd) == -1) {
481 stall("can't get %s for controlling terminal: %m", name);
482 _exit(1);
483 }
484}
485
b5775271
KM
486/*
487 * Bring the system up single user.
488 */
284ed8f8
KM
489state_func_t
490single_user()
491{
492 pid_t pid, wpid;
493 int status;
494 sigset_t mask;
495 char *shell = _PATH_BSHELL;
496 char *argv[2];
497#ifdef SECURE
498 struct ttyent *typ;
499 struct passwd *pp;
500 static const char banner[] =
501 "Enter root password, or ^D to go multi-user\n";
502 char *password;
503#endif
504
505 if ((pid = fork()) == 0) {
506 /*
507 * Start the single user session.
508 */
509 setctty(_PATH_CONSOLE);
510
511#ifdef SECURE
512 /*
513 * Check the root password.
514 * We don't care if the console is 'on' by default;
515 * it's the only tty that can be 'off' and 'secure'.
516 */
517 typ = getttynam("console");
518 pp = getpwnam("root");
519 if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) {
520 write(2, banner, sizeof banner - 1);
521 for (;;) {
522 password = getpass("Password:");
523 if (password == 0 || *password == '\0')
524 _exit(0);
f382c801 525 password = crypt(password, pp->pw_passwd);
284ed8f8
KM
526 if (strcmp(password, pp->pw_passwd) == 0)
527 break;
068e4187 528 }
d0ca48ff 529 }
284ed8f8
KM
530#if 0
531 /*
532 * Make the single-user shell be root's standard shell?
533 */
534 if (pp && pp->pw_shell)
535 shell = pp->pw_shell;
536#endif
537 endttyent();
538 endpwent();
539#endif
068e4187 540
25efc242 541 /*
284ed8f8
KM
542 * Unblock signals.
543 * We catch all the interesting ones,
544 * and those are reset to SIG_DFL on exec.
25efc242 545 */
284ed8f8
KM
546 sigemptyset(&mask);
547 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
548
25efc242 549 /*
284ed8f8
KM
550 * Fire off a shell.
551 * If the default one doesn't work, try the Bourne shell.
25efc242 552 */
284ed8f8
KM
553 argv[0] = "-sh";
554 argv[1] = 0;
555 execv(shell, argv);
556 emergency("can't exec %s for single user: %m", shell);
557 execv(_PATH_BSHELL, argv);
558 emergency("can't exec %s for single user: %m", _PATH_BSHELL);
559 sleep(STALL_TIMEOUT);
560 _exit(1);
561 }
562
563 if (pid == -1) {
564 /*
565 * We are seriously hosed. Do our best.
566 */
567 emergency("can't fork single-user shell, trying again");
568 while (waitpid(-1, (int *) 0, WNOHANG) > 0)
b249bb58 569 continue;
284ed8f8
KM
570 return (state_func_t) single_user;
571 }
572
fd1a77cb 573 requested_transition = 0;
b5775271
KM
574 do {
575 if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
576 collect_child(wpid);
577 if (wpid == -1) {
578 if (errno == EINTR)
579 continue;
284ed8f8
KM
580 warning("wait for single-user shell failed: %m; restarting");
581 return (state_func_t) single_user;
582 }
583 if (wpid == pid && WIFSTOPPED(status)) {
584 warning("init: shell stopped, restarting\n");
585 kill(pid, SIGCONT);
b5775271 586 wpid = -1;
25efc242 587 }
fd1a77cb
TH
588 } while (wpid != pid && !requested_transition);
589
590 if (requested_transition)
591 return (state_func_t) requested_transition;
284ed8f8
KM
592
593 if (!WIFEXITED(status)) {
850e7b14
TH
594 if (WTERMSIG(status) == SIGKILL) {
595 /*
596 * reboot(8) killed shell?
597 */
598 warning("single user shell terminated.");
599 sleep(STALL_TIMEOUT);
600 _exit(0);
601 } else {
602 warning("single user shell terminated, restarting");
603 return (state_func_t) single_user;
604 }
284ed8f8
KM
605 }
606
607 runcom_mode = FASTBOOT;
608 return (state_func_t) runcom;
609}
610
b5775271
KM
611/*
612 * Run the system startup script.
613 */
284ed8f8
KM
614state_func_t
615runcom()
616{
617 pid_t pid, wpid;
618 int status;
619 char *argv[4];
b5775271 620 struct sigaction sa;
284ed8f8
KM
621
622 if ((pid = fork()) == 0) {
b5775271
KM
623 sigemptyset(&sa.sa_mask);
624 sa.sa_flags = 0;
625 sa.sa_handler = SIG_IGN;
626 (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
627 (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
628
284ed8f8
KM
629 setctty(_PATH_CONSOLE);
630
631 argv[0] = "sh";
632 argv[1] = _PATH_RUNCOM;
633 argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
634 argv[3] = 0;
635
b5775271 636 sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
284ed8f8
KM
637
638 execv(_PATH_BSHELL, argv);
639 stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
640 _exit(1); /* force single user mode */
641 }
642
643 if (pid == -1) {
644 emergency("can't fork for %s on %s: %m",
645 _PATH_BSHELL, _PATH_RUNCOM);
b5775271 646 while (waitpid(-1, (int *) 0, WNOHANG) > 0)
b249bb58 647 continue;
284ed8f8
KM
648 sleep(STALL_TIMEOUT);
649 return (state_func_t) single_user;
650 }
651
652 /*
b5775271 653 * Copied from single_user(). This is a bit paranoid.
284ed8f8 654 */
b5775271
KM
655 do {
656 if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
657 collect_child(wpid);
658 if (wpid == -1) {
659 if (errno == EINTR)
660 continue;
284ed8f8
KM
661 warning("wait for %s on %s failed: %m; going to single user mode",
662 _PATH_BSHELL, _PATH_RUNCOM);
663 return (state_func_t) single_user;
664 }
665 if (wpid == pid && WIFSTOPPED(status)) {
666 warning("init: %s on %s stopped, restarting\n",
667 _PATH_BSHELL, _PATH_RUNCOM);
668 kill(pid, SIGCONT);
b5775271 669 wpid = -1;
d0ca48ff 670 }
b5775271 671 } while (wpid != pid);
284ed8f8
KM
672
673 if (!WIFEXITED(status)) {
674 warning("%s on %s terminated abnormally, going to single user mode",
675 _PATH_BSHELL, _PATH_RUNCOM);
676 return (state_func_t) single_user;
677 }
678
679 if (WEXITSTATUS(status))
680 return (state_func_t) single_user;
681
682 runcom_mode = AUTOBOOT; /* the default */
bb56e935
KM
683 /* NB: should send a message to the session logger to avoid blocking. */
684 logwtmp("~", "reboot", "");
284ed8f8
KM
685 return (state_func_t) read_ttys;
686}
687
688/*
b5775271
KM
689 * Open the session database.
690 *
691 * NB: We could pass in the size here; is it necessary?
284ed8f8 692 */
b5775271 693int
284ed8f8
KM
694start_session_db()
695{
b5775271
KM
696 if (session_db && (*session_db->close)(session_db))
697 emergency("session database close: %s", strerror(errno));
2bf636e6 698 if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
b5775271
KM
699 emergency("session database open: %s", strerror(errno));
700 return (1);
701 }
702 return (0);
703
284ed8f8
KM
704}
705
b5775271
KM
706/*
707 * Add a new login session.
708 */
284ed8f8
KM
709void
710add_session(sp)
711 session_t *sp;
712{
713 DBT key;
714 DBT data;
715
716 key.data = &sp->se_process;
717 key.size = sizeof sp->se_process;
718 data.data = &sp;
719 data.size = sizeof sp;
720
2bf636e6 721 if ((*session_db->put)(session_db, &key, &data, 0))
b5775271 722 emergency("insert %d: %s", sp->se_process, strerror(errno));
d0ca48ff
SL
723}
724
b5775271
KM
725/*
726 * Delete an old login session.
727 */
284ed8f8
KM
728void
729del_session(sp)
730 session_t *sp;
731{
732 DBT key;
733
734 key.data = &sp->se_process;
735 key.size = sizeof sp->se_process;
736
b5775271
KM
737 if ((*session_db->del)(session_db, &key, 0))
738 emergency("delete %d: %s", sp->se_process, strerror(errno));
284ed8f8
KM
739}
740
b5775271
KM
741/*
742 * Look up a login session by pid.
743 */
284ed8f8
KM
744session_t *
745#ifdef __STDC__
746find_session(pid_t pid)
747#else
748find_session(pid)
749 pid_t pid;
750#endif
7c8ab0e6 751{
284ed8f8
KM
752 DBT key;
753 DBT data;
b249bb58 754 session_t *ret;
7c8ab0e6 755
284ed8f8
KM
756 key.data = &pid;
757 key.size = sizeof pid;
758 if ((*session_db->get)(session_db, &key, &data, 0) != 0)
759 return 0;
b249bb58
CT
760 bcopy(data.data, (char *)&ret, sizeof(ret));
761 return ret;
284ed8f8
KM
762}
763
b5775271
KM
764/*
765 * Construct an argument vector from a command line.
766 */
284ed8f8
KM
767char **
768construct_argv(command)
769 char *command;
770{
771 register int argc = 0;
772 register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
773 * sizeof (char *));
774 static const char separators[] = " \t";
775
776 if ((argv[argc++] = strtok(command, separators)) == 0)
777 return 0;
778 while (argv[argc++] = strtok((char *) 0, separators))
b249bb58 779 continue;
284ed8f8
KM
780 return argv;
781}
782
b5775271
KM
783/*
784 * Deallocate a session descriptor.
785 */
284ed8f8
KM
786void
787free_session(sp)
788 register session_t *sp;
789{
790 free(sp->se_device);
791 free(sp->se_getty);
792 free(sp->se_getty_argv);
793 if (sp->se_window) {
794 free(sp->se_window);
795 free(sp->se_window_argv);
7c8ab0e6 796 }
284ed8f8 797 free(sp);
7c8ab0e6
BJ
798}
799
b5775271
KM
800/*
801 * Allocate a new session descriptor.
802 */
284ed8f8
KM
803session_t *
804new_session(sprev, session_index, typ)
805 session_t *sprev;
806 int session_index;
807 register struct ttyent *typ;
7c8ab0e6 808{
284ed8f8
KM
809 register session_t *sp;
810
811 if ((typ->ty_status & TTY_ON) == 0 ||
812 typ->ty_name == 0 ||
813 typ->ty_getty == 0)
814 return 0;
815
816 sp = (session_t *) malloc(sizeof (session_t));
817
818 sp->se_index = session_index;
819 sp->se_process = 0;
820 sp->se_started = 0;
821 sp->se_flags = 0;
822 sp->se_window = 0;
823
6f2ac96c
CT
824 sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
825 (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
284ed8f8
KM
826
827 sp->se_getty = strdup(typ->ty_getty);
828 sp->se_getty_argv = construct_argv(sp->se_getty);
829 if (sp->se_getty_argv == 0) {
830 warning("can't parse getty for port %s",
831 sp->se_device);
832 free_session(sp);
833 return 0;
43236707 834 }
284ed8f8
KM
835 if (typ->ty_window) {
836 sp->se_window = strdup(typ->ty_window);
837 sp->se_window_argv = construct_argv(sp->se_window);
838 if (sp->se_window_argv == 0) {
839 warning("can't parse window for port %s",
840 sp->se_device);
841 free_session(sp);
842 return 0;
ef9abb5f 843 }
7c8ab0e6 844 }
284ed8f8
KM
845
846 sp->se_next = 0;
847 if (sprev == 0) {
848 sessions = sp;
849 sp->se_prev = 0;
850 } else {
851 sprev->se_next = sp;
852 sp->se_prev = sprev;
853 }
854
855 return sp;
7c8ab0e6
BJ
856}
857
b5775271
KM
858/*
859 * Walk the list of ttys and create sessions for each active line.
860 */
284ed8f8
KM
861state_func_t
862read_ttys()
7c8ab0e6 863{
284ed8f8
KM
864 int session_index = 0;
865 register session_t *sp, *snext;
866 register struct ttyent *typ;
867
868 /*
869 * Destroy any previous session state.
870 * There shouldn't be any, but just in case...
871 */
872 for (sp = sessions; sp; sp = snext) {
873 if (sp->se_process)
874 clear_session_logs(sp);
875 snext = sp->se_next;
876 free_session(sp);
7c8ab0e6 877 }
284ed8f8 878 sessions = 0;
b5775271
KM
879 if (start_session_db())
880 return (state_func_t) single_user;
284ed8f8
KM
881
882 /*
883 * Allocate a session entry for each active port.
884 * Note that sp starts at 0.
885 */
886 while (typ = getttyent())
887 if (snext = new_session(sp, ++session_index, typ))
888 sp = snext;
889
890 endttyent();
891
892 logger_enable = 1;
893 return (state_func_t) multi_user;
7c8ab0e6
BJ
894}
895
b5775271
KM
896/*
897 * Start a window system running.
898 */
caf94fd3 899void
284ed8f8
KM
900start_window_system(sp)
901 session_t *sp;
7c8ab0e6 902{
284ed8f8
KM
903 pid_t pid;
904 sigset_t mask;
f570e1ff 905
284ed8f8
KM
906 if ((pid = fork()) == -1) {
907 emergency("can't fork for window system on port %s: %m",
908 sp->se_device);
909 /* hope that getty fails and we can try again */
910 return;
911 }
d0ca48ff 912
284ed8f8
KM
913 if (pid)
914 return;
915
916 sigemptyset(&mask);
917 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
918
b5775271
KM
919 if (setsid() < 0)
920 emergency("setsid failed (window) %m");
921
284ed8f8
KM
922 execv(sp->se_window_argv[0], sp->se_window_argv);
923 stall("can't exec window system '%s' for port %s: %m",
924 sp->se_window_argv[0], sp->se_device);
925 _exit(1);
926}
927
b5775271
KM
928/*
929 * Start a login session running.
930 */
284ed8f8
KM
931pid_t
932start_getty(sp)
933 session_t *sp;
d0ca48ff 934{
284ed8f8
KM
935 pid_t pid;
936 sigset_t mask;
937 time_t current_time = time((time_t *) 0);
938
939 /*
940 * fork(), not vfork() -- we can't afford to block.
941 */
942 if ((pid = fork()) == -1) {
943 emergency("can't fork for getty on port %s: %m", sp->se_device);
944 return -1;
945 }
946
947 if (pid)
948 return pid;
949
950 if (current_time > sp->se_started &&
951 current_time - sp->se_started < GETTY_SPACING) {
952 warning("getty repeating too quickly on port %s, sleeping",
953 sp->se_device);
954 sleep((unsigned) GETTY_SPACING -
955 (current_time - sp->se_started));
956 }
957
958 if (sp->se_window) {
959 start_window_system(sp);
960 sleep(WINDOW_WAIT);
961 }
962
963 setctty(sp->se_device);
964
965 sigemptyset(&mask);
966 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
967
968 execv(sp->se_getty_argv[0], sp->se_getty_argv);
969 stall("can't exec getty '%s' for port %s: %m",
970 sp->se_getty_argv[0], sp->se_device);
971 _exit(1);
d0ca48ff
SL
972}
973
b5775271
KM
974/*
975 * Collect exit status for a child.
976 * If an exiting login, start a new login running.
977 */
caf94fd3 978void
b5775271 979collect_child(pid)
284ed8f8 980 pid_t pid;
b5775271 981{
284ed8f8 982 register session_t *sp, *sprev, *snext;
f570e1ff 983
b5775271
KM
984 if (! sessions)
985 return;
284ed8f8 986
b5775271
KM
987 if (! (sp = find_session(pid)))
988 return;
284ed8f8 989
b5775271
KM
990 clear_session_logs(sp);
991 del_session(sp);
992 sp->se_process = 0;
284ed8f8 993
b5775271
KM
994 if (sp->se_flags & SE_SHUTDOWN) {
995 if (sprev = sp->se_prev)
996 sprev->se_next = sp->se_next;
997 else
998 sessions = sp->se_next;
999 if (snext = sp->se_next)
1000 snext->se_prev = sp->se_prev;
1001 free_session(sp);
1002 return;
1003 }
284ed8f8 1004
b5775271
KM
1005 if ((pid = start_getty(sp)) == -1) {
1006 /* serious trouble */
1007 requested_transition = clean_ttys;
1008 return;
068e4187 1009 }
284ed8f8 1010
b5775271
KM
1011 sp->se_process = pid;
1012 sp->se_started = time((time_t *) 0);
1013 add_session(sp);
068e4187
RC
1014}
1015
b5775271
KM
1016/*
1017 * Catch a signal and request a state transition.
1018 */
284ed8f8
KM
1019void
1020transition_handler(sig)
1021 int sig;
068e4187 1022{
fd1a77cb 1023
284ed8f8
KM
1024 switch (sig) {
1025 case SIGHUP:
1026 requested_transition = clean_ttys;
1027 break;
1028 case SIGTERM:
1029 requested_transition = death;
1030 break;
1031 case SIGTSTP:
1032 requested_transition = catatonia;
1033 break;
1034 default:
1035 requested_transition = 0;
1036 break;
068e4187 1037 }
068e4187
RC
1038}
1039
b5775271
KM
1040/*
1041 * Take the system multiuser.
1042 */
284ed8f8
KM
1043state_func_t
1044multi_user()
068e4187 1045{
284ed8f8
KM
1046 pid_t pid;
1047 register session_t *sp;
a936c74e 1048
284ed8f8
KM
1049 requested_transition = 0;
1050 logger_enable = 1;
a936c74e 1051
b5775271
KM
1052 for (sp = sessions; sp; sp = sp->se_next) {
1053 if (sp->se_process)
1054 continue;
1055 if ((pid = start_getty(sp)) == -1) {
1056 /* serious trouble */
1057 requested_transition = clean_ttys;
1058 break;
a936c74e 1059 }
b5775271
KM
1060 sp->se_process = pid;
1061 sp->se_started = time((time_t *) 0);
1062 add_session(sp);
1063 }
068e4187 1064
284ed8f8 1065 while (!requested_transition)
b5775271
KM
1066 if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
1067 collect_child(pid);
284ed8f8
KM
1068
1069 return (state_func_t) requested_transition;
1070}
068e4187 1071
284ed8f8
KM
1072/*
1073 * This is an n-squared algorithm. We hope it isn't run often...
1074 */
1075state_func_t
1076clean_ttys()
068e4187 1077{
284ed8f8
KM
1078 register session_t *sp, *sprev;
1079 register struct ttyent *typ;
1080 register int session_index = 0;
f382c801 1081 register int devlen;
068e4187 1082
284ed8f8
KM
1083 if (! sessions)
1084 return (state_func_t) multi_user;
1085
6f2ac96c 1086 devlen = sizeof(_PATH_DEV) - 1;
284ed8f8
KM
1087 while (typ = getttyent()) {
1088 ++session_index;
1089
1090 for (sp = sessions; sp; sprev = sp, sp = sp->se_next)
6f2ac96c 1091 if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
068e4187 1092 break;
284ed8f8
KM
1093
1094 if (sp) {
1095 if (sp->se_index != session_index) {
1096 warning("port %s changed utmp index from %d to %d",
1097 sp->se_device, sp->se_index,
1098 session_index);
1099 sp->se_index = session_index;
1100 }
1101 if (typ->ty_status & TTY_ON)
1102 sp->se_flags &= ~SE_SHUTDOWN;
1103 else {
1104 sp->se_flags |= SE_SHUTDOWN;
1105 kill(sp->se_process, SIGHUP);
068e4187 1106 }
284ed8f8 1107 continue;
068e4187 1108 }
284ed8f8
KM
1109
1110 new_session(sprev, session_index, typ);
f570e1ff 1111 }
284ed8f8
KM
1112
1113 endttyent();
1114
1115 return (state_func_t) multi_user;
1116}
1117
b5775271
KM
1118/*
1119 * Block further logins.
1120 */
284ed8f8
KM
1121state_func_t
1122catatonia()
1123{
1124 register session_t *sp;
1125
1126 for (sp = sessions; sp; sp = sp->se_next)
1127 sp->se_flags |= SE_SHUTDOWN;
1128
1129 return (state_func_t) multi_user;
1130}
1131
b5775271
KM
1132/*
1133 * Note SIGALRM.
1134 */
284ed8f8
KM
1135void
1136alrm_handler(sig)
1137 int sig;
1138{
1139 clang = 1;
1140}
1141
b5775271
KM
1142/*
1143 * Bring the system down to single user.
1144 */
284ed8f8
KM
1145state_func_t
1146death()
1147{
1148 register session_t *sp;
1149 register int i;
b5775271 1150 pid_t pid;
284ed8f8
KM
1151 static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
1152
1153 for (sp = sessions; sp; sp = sp->se_next)
1154 sp->se_flags |= SE_SHUTDOWN;
1155
bb56e935
KM
1156 /* NB: should send a message to the session logger to avoid blocking. */
1157 logwtmp("~", "shutdown", "");
284ed8f8
KM
1158 logger_enable = 0;
1159
1160 for (i = 0; i < 3; ++i) {
1161 if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
1162 return (state_func_t) single_user;
1163
1164 clang = 0;
1165 alarm(DEATH_WATCH);
1166 do
b5775271
KM
1167 if ((pid = waitpid(-1, (int *)0, 0)) != -1)
1168 collect_child(pid);
1169 while (clang == 0 && errno != ECHILD);
284ed8f8 1170
b5775271 1171 if (errno == ECHILD)
284ed8f8
KM
1172 return (state_func_t) single_user;
1173 }
1174
1175 warning("some processes wouldn't die");
1176
1177 return (state_func_t) single_user;
f570e1ff 1178}