1. Remove a rather strangely gratuitous bit of profanity
[unix-history] / sbin / init.bsdi / init.c
CommitLineData
0a708c0e
NW
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 Berkeley Software Design, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38char copyright[] =
39"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
40 All rights reserved.\n";
41#endif /* not lint */
42
43#ifndef lint
44static char sccsid[] = "@(#)init.c 6.22 (Berkeley) 6/2/93";
45#endif /* not lint */
46
47#include <sys/param.h>
48#ifndef NOSYSCTL
49#include <sys/sysctl.h>
50#endif
51#include <sys/wait.h>
52
53#include <db.h>
54#include <errno.h>
55#include <fcntl.h>
56#include <signal.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <syslog.h>
61#include <time.h>
62#include <ttyent.h>
63#include <unistd.h>
22860a22 64#include <sys/reboot.h>
0a708c0e
NW
65
66#ifdef __STDC__
67#include <stdarg.h>
68#else
69#include <varargs.h>
70#endif
71
72#ifdef SECURE
73#include <pwd.h>
74#endif
75
76#include "pathnames.h"
77
78/*
79 * Until the mythical util.h arrives...
80 */
81extern int login_tty __P((int));
82extern int logout __P((const char *));
83extern void logwtmp __P((const char *, const char *, const char *));
84
85/*
86 * Sleep times; used to prevent thrashing.
87 */
88#define GETTY_SPACING 5 /* N secs minimum getty spacing */
89#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */
0a3a98ff 90#define GETTY_NSPACE 3 /* max. spacing count to bring reaction */
0a708c0e
NW
91#define WINDOW_WAIT 3 /* wait N secs after starting window */
92#define STALL_TIMEOUT 30 /* wait N secs after warning */
93#define DEATH_WATCH 10 /* wait N secs for procs to die */
94
95void handle __P((sig_t, ...));
96void delset __P((sigset_t *, ...));
97
98void stall __P((char *, ...));
99void warning __P((char *, ...));
100void emergency __P((char *, ...));
101void disaster __P((int));
102void badsys __P((int));
103
104/*
105 * We really need a recursive typedef...
106 * The following at least guarantees that the return type of (*state_t)()
107 * is sufficiently wide to hold a function pointer.
108 */
109typedef long (*state_func_t) __P((void));
110typedef state_func_t (*state_t) __P((void));
111
112state_func_t single_user __P((void));
113state_func_t runcom __P((void));
114state_func_t read_ttys __P((void));
115state_func_t multi_user __P((void));
116state_func_t clean_ttys __P((void));
117state_func_t catatonia __P((void));
118state_func_t death __P((void));
119
120enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
22860a22 121int noreboot = 0;
0a708c0e
NW
122
123void transition __P((state_t));
124state_t requested_transition = runcom;
125
126void setctty __P((char *));
127
128typedef struct init_session {
129 int se_index; /* index of entry in ttys file */
130 pid_t se_process; /* controlling process */
131 time_t se_started; /* used to avoid thrashing */
132 int se_flags; /* status of session */
133#define SE_SHUTDOWN 0x1 /* session won't be restarted */
0a3a98ff 134 int se_nspace; /* spacing count */
0a708c0e 135 char *se_device; /* filename of port */
49866386
AC
136 char *se_getty; /* what to run on that port */
137 char *se_getty_argv_space; /* pre-parsed argument array space */
0a708c0e 138 char **se_getty_argv; /* pre-parsed argument array */
49866386
AC
139 char *se_window; /* window system (started only once) */
140 char *se_window_argv_space; /* pre-parsed argument array space */
0a708c0e 141 char **se_window_argv; /* pre-parsed argument array */
49866386 142 char *se_type; /* default terminal type */
0a708c0e
NW
143 struct init_session *se_prev;
144 struct init_session *se_next;
145} session_t;
146
147void free_session __P((session_t *));
148session_t *new_session __P((session_t *, int, struct ttyent *));
149session_t *sessions;
150
151char **construct_argv __P((char *));
152void start_window_system __P((session_t *));
153void collect_child __P((pid_t));
154pid_t start_getty __P((session_t *));
155void transition_handler __P((int));
156void alrm_handler __P((int));
157void setsecuritylevel __P((int));
158int getsecuritylevel __P((void));
159int setupargv __P((session_t *, struct ttyent *));
160int clang;
161
162void clear_session_logs __P((session_t *));
163
164int start_session_db __P((void));
165void add_session __P((session_t *));
166void del_session __P((session_t *));
167session_t *find_session __P((pid_t));
168DB *session_db;
169
170/*
171 * The mother of all processes.
172 */
173int
174main(argc, argv)
175 int argc;
176 char **argv;
177{
178 int c;
179 struct sigaction sa;
180 sigset_t mask;
181
182
183 /* Dispose of random users. */
184 if (getuid() != 0) {
185 (void)fprintf(stderr, "init: %s\n", strerror(EPERM));
186 exit (1);
187 }
188
189 /* System V users like to reexec init. */
190 if (getpid() != 1) {
191 (void)fprintf(stderr, "init: already running\n");
192 exit (1);
193 }
194
195 /*
196 * Note that this does NOT open a file...
197 * Does 'init' deserve its own facility number?
198 */
199 openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH);
200
201 /*
202 * Create an initial session.
203 */
204 if (setsid() < 0)
205 warning("initial setsid() failed: %m");
206
207 /*
208 * This code assumes that we always get arguments through flags,
209 * never through bits set in some random machine register.
210 */
211 while ((c = getopt(argc, argv, "sf")) != -1)
212 switch (c) {
213 case 's':
214 requested_transition = single_user;
215 break;
216 case 'f':
217 runcom_mode = FASTBOOT;
218 break;
219 default:
220 warning("unrecognized flag '-%c'", c);
221 break;
222 }
223
224 if (optind != argc)
225 warning("ignoring excess arguments");
226
227 /*
228 * We catch or block signals rather than ignore them,
229 * so that they get reset on exec.
230 */
231 handle(badsys, SIGSYS, 0);
232 handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV,
233 SIGBUS, SIGXCPU, SIGXFSZ, 0);
22860a22 234 handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, 0);
0a708c0e
NW
235 handle(alrm_handler, SIGALRM, 0);
236 sigfillset(&mask);
237 delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS,
22860a22 238 SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGTSTP, SIGALRM, 0);
0a708c0e
NW
239 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
240 sigemptyset(&sa.sa_mask);
241 sa.sa_flags = 0;
242 sa.sa_handler = SIG_IGN;
243 (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0);
244 (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0);
245
246 /*
247 * Paranoia.
248 */
249 close(0);
250 close(1);
251 close(2);
252
253 /*
254 * Start the state machine.
255 */
256 transition(requested_transition);
257
258 /*
259 * Should never reach here.
260 */
261 return 1;
262}
263
264/*
265 * Associate a function with a signal handler.
266 */
267void
268#ifdef __STDC__
269handle(sig_t handler, ...)
270#else
271handle(va_alist)
272 va_dcl
273#endif
274{
275 int sig;
276 struct sigaction sa;
277 int mask_everything;
278 va_list ap;
279#ifndef __STDC__
280 sig_t handler;
281
282 va_start(ap);
283 handler = va_arg(ap, sig_t);
284#else
285 va_start(ap, handler);
286#endif
287
288 sa.sa_handler = handler;
289 sigfillset(&mask_everything);
290
291 while (sig = va_arg(ap, int)) {
292 sa.sa_mask = mask_everything;
293 /* XXX SA_RESTART? */
294 sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0;
295 sigaction(sig, &sa, (struct sigaction *) 0);
296 }
297 va_end(ap);
298}
299
300/*
301 * Delete a set of signals from a mask.
302 */
303void
304#ifdef __STDC__
305delset(sigset_t *maskp, ...)
306#else
307delset(va_alist)
308 va_dcl
309#endif
310{
311 int sig;
312 va_list ap;
313#ifndef __STDC__
314 sigset_t *maskp;
315
316 va_start(ap);
317 maskp = va_arg(ap, sigset_t *);
318#else
319 va_start(ap, maskp);
320#endif
321
322 while (sig = va_arg(ap, int))
323 sigdelset(maskp, sig);
324 va_end(ap);
325}
326
327/*
328 * Log a message and sleep for a while (to give someone an opportunity
329 * to read it and to save log or hardcopy output if the problem is chronic).
330 * NB: should send a message to the session logger to avoid blocking.
331 */
332void
333#ifdef __STDC__
334stall(char *message, ...)
335#else
336stall(va_alist)
337 va_dcl
338#endif
339{
340 va_list ap;
341#ifndef __STDC__
342 char *message;
343
344 va_start(ap);
345 message = va_arg(ap, char *);
346#else
347 va_start(ap, message);
348#endif
349
350 vsyslog(LOG_ALERT, message, ap);
351 va_end(ap);
352 sleep(STALL_TIMEOUT);
353}
354
355/*
356 * Like stall(), but doesn't sleep.
357 * If cpp had variadic macros, the two functions could be #defines for another.
358 * NB: should send a message to the session logger to avoid blocking.
359 */
360void
361#ifdef __STDC__
362warning(char *message, ...)
363#else
364warning(va_alist)
365 va_dcl
366#endif
367{
368 va_list ap;
369#ifndef __STDC__
370 char *message;
371
372 va_start(ap);
373 message = va_arg(ap, char *);
374#else
375 va_start(ap, message);
376#endif
377
378 vsyslog(LOG_ALERT, message, ap);
379 va_end(ap);
380}
381
382/*
383 * Log an emergency message.
384 * NB: should send a message to the session logger to avoid blocking.
385 */
386void
387#ifdef __STDC__
388emergency(char *message, ...)
389#else
390emergency(va_alist)
391 va_dcl
392#endif
393{
394 va_list ap;
395#ifndef __STDC__
396 char *message;
397
398 va_start(ap);
399 message = va_arg(ap, char *);
400#else
401 va_start(ap, message);
402#endif
403
404 vsyslog(LOG_EMERG, message, ap);
405 va_end(ap);
406}
407
408/*
409 * Catch a SIGSYS signal.
410 *
411 * These may arise if a system does not support sysctl.
412 * We tolerate up to 25 of these, then throw in the towel.
413 */
414void
415badsys(sig)
416 int sig;
417{
418 static int badcount = 0;
419
420 if (badcount++ < 25)
421 return;
422 disaster(sig);
423}
424
425/*
426 * Catch an unexpected signal.
427 */
428void
429disaster(sig)
430 int sig;
431{
432 emergency("fatal signal: %s",
433 sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal");
434
435 sleep(STALL_TIMEOUT);
436 _exit(sig); /* reboot */
437}
438
439/*
440 * Get the security level of the kernel.
441 */
442int
443getsecuritylevel()
444{
445#ifdef KERN_SECURELVL
446 int name[2], curlevel;
447 size_t len;
448 extern int errno;
449
450 name[0] = CTL_KERN;
451 name[1] = KERN_SECURELVL;
452 len = sizeof curlevel;
453 if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) {
454 emergency("cannot get kernel security level: %s",
455 strerror(errno));
456 return (-1);
457 }
458 return (curlevel);
459#else
460 return (-1);
461#endif
462}
463
464/*
465 * Set the security level of the kernel.
466 */
467void
468setsecuritylevel(newlevel)
469 int newlevel;
470{
471#ifdef KERN_SECURELVL
472 int name[2], curlevel;
473 extern int errno;
474
475 curlevel = getsecuritylevel();
476 if (newlevel == curlevel)
477 return;
478 name[0] = CTL_KERN;
479 name[1] = KERN_SECURELVL;
480 if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) {
481 emergency(
482 "cannot change kernel security level from %d to %d: %s",
483 curlevel, newlevel, strerror(errno));
484 return;
485 }
486#ifdef SECURE
487 warning("kernel security level changed from %d to %d",
488 curlevel, newlevel);
489#endif
490#endif
491}
492
493/*
494 * Change states in the finite state machine.
495 * The initial state is passed as an argument.
496 */
497void
498transition(s)
499 state_t s;
500{
501 for (;;)
502 s = (state_t) (*s)();
503}
504
505/*
506 * Close out the accounting files for a login session.
507 * NB: should send a message to the session logger to avoid blocking.
508 */
509void
510clear_session_logs(sp)
511 session_t *sp;
512{
513 char *line = sp->se_device + sizeof(_PATH_DEV) - 1;
514
515 if (logout(line))
516 logwtmp(line, "", "");
517}
518
519/*
520 * Start a session and allocate a controlling terminal.
521 * Only called by children of init after forking.
522 */
523void
524setctty(name)
525 char *name;
526{
527 int fd;
528
529 (void) revoke(name);
df08b487 530#ifdef BROKEN_DTR
0a708c0e 531 sleep (2); /* leave DTR low */
df08b487 532#endif
0a708c0e
NW
533 if ((fd = open(name, O_RDWR)) == -1) {
534 stall("can't open %s: %m", name);
535 _exit(1);
536 }
537 if (login_tty(fd) == -1) {
538 stall("can't get %s for controlling terminal: %m", name);
539 _exit(1);
540 }
541}
542
543/*
544 * Bring the system up single user.
545 */
546state_func_t
547single_user()
548{
549 pid_t pid, wpid;
550 int status;
551 sigset_t mask;
552 char *shell = _PATH_BSHELL;
553 char *argv[2];
554#ifdef SECURE
555 struct ttyent *typ;
556 struct passwd *pp;
557 static const char banner[] =
558 "Enter root password, or ^D to go multi-user\n";
559 char *clear, *password;
560#endif
561
562 /*
563 * If the kernel is in secure mode, downgrade it to insecure mode.
564 */
565 if (getsecuritylevel() > 0)
566 setsecuritylevel(0);
567
22860a22
NW
568 if ( noreboot > 0) {
569 /* Instead of going single user, let's halt the machine */
570 sync();
571 alarm(2);
572 pause();
573 reboot(RB_HALT);
574 _exit(0);
575 }
576
0a708c0e
NW
577 if ((pid = fork()) == 0) {
578 /*
579 * Start the single user session.
580 */
581 setctty(_PATH_CONSOLE);
582
583#ifdef SECURE
584 /*
585 * Check the root password.
586 * We don't care if the console is 'on' by default;
587 * it's the only tty that can be 'off' and 'secure'.
588 */
589 typ = getttynam("console");
590 pp = getpwnam("root");
591 if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) {
592 write(2, banner, sizeof banner - 1);
593 for (;;) {
594 clear = getpass("Password:");
595 if (clear == 0 || *clear == '\0')
596 _exit(0);
597 password = crypt(clear, pp->pw_passwd);
598 bzero(clear, _PASSWORD_LEN);
599 if (strcmp(password, pp->pw_passwd) == 0)
600 break;
601 warning("single-user login failed\n");
602 }
603 }
604 endttyent();
605 endpwent();
606#endif /* SECURE */
607
608#ifdef DEBUGSHELL
609 {
610 char altshell[128], *cp = altshell;
611 int num;
612
613#define SHREQUEST \
614 "Enter pathname of shell or RETURN for sh: "
615 (void)write(STDERR_FILENO,
616 SHREQUEST, sizeof(SHREQUEST) - 1);
617 while ((num = read(STDIN_FILENO, cp, 1)) != -1 &&
618 num != 0 && *cp != '\n' && cp < &altshell[127])
619 cp++;
620 *cp = '\0';
621 if (altshell[0] != '\0')
622 shell = altshell;
623 }
624#endif /* DEBUGSHELL */
625
626 /*
627 * Unblock signals.
628 * We catch all the interesting ones,
629 * and those are reset to SIG_DFL on exec.
630 */
631 sigemptyset(&mask);
632 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
633
634 /*
635 * Fire off a shell.
636 * If the default one doesn't work, try the Bourne shell.
637 */
638 argv[0] = "-sh";
639 argv[1] = 0;
640 execv(shell, argv);
641 emergency("can't exec %s for single user: %m", shell);
642 execv(_PATH_BSHELL, argv);
643 emergency("can't exec %s for single user: %m", _PATH_BSHELL);
644 sleep(STALL_TIMEOUT);
645 _exit(1);
646 }
647
648 if (pid == -1) {
649 /*
650 * We are seriously hosed. Do our best.
651 */
652 emergency("can't fork single-user shell, trying again");
653 while (waitpid(-1, (int *) 0, WNOHANG) > 0)
654 continue;
655 return (state_func_t) single_user;
656 }
657
658 requested_transition = 0;
659 do {
660 if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
661 collect_child(wpid);
662 if (wpid == -1) {
663 if (errno == EINTR)
664 continue;
665 warning("wait for single-user shell failed: %m; restarting");
666 return (state_func_t) single_user;
667 }
668 if (wpid == pid && WIFSTOPPED(status)) {
669 warning("init: shell stopped, restarting\n");
670 kill(pid, SIGCONT);
671 wpid = -1;
672 }
673 } while (wpid != pid && !requested_transition);
674
675 if (requested_transition)
676 return (state_func_t) requested_transition;
677
678 if (!WIFEXITED(status)) {
679 if (WTERMSIG(status) == SIGKILL) {
680 /*
681 * reboot(8) killed shell?
682 */
683 warning("single user shell terminated.");
684 sleep(STALL_TIMEOUT);
685 _exit(0);
686 } else {
687 warning("single user shell terminated, restarting");
688 return (state_func_t) single_user;
689 }
690 }
691
692 runcom_mode = FASTBOOT;
693 return (state_func_t) runcom;
694}
695
696/*
697 * Run the system startup script.
698 */
699state_func_t
700runcom()
701{
702 pid_t pid, wpid;
703 int status;
704 char *argv[4];
705 struct sigaction sa;
706
707 if ((pid = fork()) == 0) {
708 sigemptyset(&sa.sa_mask);
709 sa.sa_flags = 0;
710 sa.sa_handler = SIG_IGN;
711 (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0);
712 (void) sigaction(SIGHUP, &sa, (struct sigaction *)0);
713
714 setctty(_PATH_CONSOLE);
715
716 argv[0] = "sh";
717 argv[1] = _PATH_RUNCOM;
718 argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0;
719 argv[3] = 0;
720
721 sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0);
722
723 execv(_PATH_BSHELL, argv);
724 stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM);
725 _exit(1); /* force single user mode */
726 }
727
728 if (pid == -1) {
729 emergency("can't fork for %s on %s: %m",
730 _PATH_BSHELL, _PATH_RUNCOM);
731 while (waitpid(-1, (int *) 0, WNOHANG) > 0)
732 continue;
733 sleep(STALL_TIMEOUT);
734 return (state_func_t) single_user;
735 }
736
737 /*
738 * Copied from single_user(). This is a bit paranoid.
739 */
740 do {
741 if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
742 collect_child(wpid);
743 if (wpid == -1) {
744 if (errno == EINTR)
745 continue;
746 warning("wait for %s on %s failed: %m; going to single user mode",
747 _PATH_BSHELL, _PATH_RUNCOM);
748 return (state_func_t) single_user;
749 }
750 if (wpid == pid && WIFSTOPPED(status)) {
751 warning("init: %s on %s stopped, restarting\n",
752 _PATH_BSHELL, _PATH_RUNCOM);
753 kill(pid, SIGCONT);
754 wpid = -1;
755 }
756 } while (wpid != pid);
757
758 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM &&
759 requested_transition == catatonia) {
760 /* /etc/rc executed /sbin/reboot; wait for the end quietly */
761 sigset_t s;
762
763 sigfillset(&s);
764 for (;;)
765 sigsuspend(&s);
766 }
767
768 if (!WIFEXITED(status)) {
769 warning("%s on %s terminated abnormally, going to single user mode",
770 _PATH_BSHELL, _PATH_RUNCOM);
771 return (state_func_t) single_user;
772 }
773
774 if (WEXITSTATUS(status))
775 return (state_func_t) single_user;
776
777 runcom_mode = AUTOBOOT; /* the default */
778 /* NB: should send a message to the session logger to avoid blocking. */
779 logwtmp("~", "reboot", "");
780 return (state_func_t) read_ttys;
781}
782
783/*
784 * Open the session database.
785 *
786 * NB: We could pass in the size here; is it necessary?
787 */
788int
789start_session_db()
790{
791 if (session_db && (*session_db->close)(session_db))
792 emergency("session database close: %s", strerror(errno));
793 if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) {
794 emergency("session database open: %s", strerror(errno));
795 return (1);
796 }
797 return (0);
798
799}
800
801/*
802 * Add a new login session.
803 */
804void
805add_session(sp)
806 session_t *sp;
807{
808 DBT key;
809 DBT data;
810
811 key.data = &sp->se_process;
812 key.size = sizeof sp->se_process;
813 data.data = &sp;
814 data.size = sizeof sp;
815
816 if ((*session_db->put)(session_db, &key, &data, 0))
817 emergency("insert %d: %s", sp->se_process, strerror(errno));
818}
819
820/*
821 * Delete an old login session.
822 */
823void
824del_session(sp)
825 session_t *sp;
826{
827 DBT key;
828
829 key.data = &sp->se_process;
830 key.size = sizeof sp->se_process;
831
832 if ((*session_db->del)(session_db, &key, 0))
833 emergency("delete %d: %s", sp->se_process, strerror(errno));
834}
835
836/*
837 * Look up a login session by pid.
838 */
839session_t *
840#ifdef __STDC__
841find_session(pid_t pid)
842#else
843find_session(pid)
844 pid_t pid;
845#endif
846{
847 DBT key;
848 DBT data;
849 session_t *ret;
850
851 key.data = &pid;
852 key.size = sizeof pid;
853 if ((*session_db->get)(session_db, &key, &data, 0) != 0)
854 return 0;
855 bcopy(data.data, (char *)&ret, sizeof(ret));
856 return ret;
857}
858
859/*
860 * Construct an argument vector from a command line.
861 */
862char **
863construct_argv(command)
864 char *command;
865{
866 register int argc = 0;
867 register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1)
868 * sizeof (char *));
869 static const char separators[] = " \t";
870
871 if ((argv[argc++] = strtok(command, separators)) == 0)
872 return 0;
873 while (argv[argc++] = strtok((char *) 0, separators))
874 continue;
875 return argv;
876}
877
878/*
879 * Deallocate a session descriptor.
880 */
881void
882free_session(sp)
883 register session_t *sp;
884{
885 free(sp->se_device);
886 if (sp->se_getty) {
887 free(sp->se_getty);
49866386 888 free(sp->se_getty_argv_space);
0a708c0e
NW
889 free(sp->se_getty_argv);
890 }
891 if (sp->se_window) {
892 free(sp->se_window);
49866386 893 free(sp->se_window_argv_space);
0a708c0e
NW
894 free(sp->se_window_argv);
895 }
49866386
AC
896 if (sp->se_type)
897 free(sp->se_type);
0a708c0e
NW
898 free(sp);
899}
900
901/*
902 * Allocate a new session descriptor.
903 */
904session_t *
905new_session(sprev, session_index, typ)
906 session_t *sprev;
907 int session_index;
908 register struct ttyent *typ;
909{
910 register session_t *sp;
911
912 if ((typ->ty_status & TTY_ON) == 0 ||
913 typ->ty_name == 0 ||
914 typ->ty_getty == 0)
915 return 0;
916
917 sp = (session_t *) malloc(sizeof (session_t));
918 bzero(sp, sizeof *sp);
919
920 sp->se_index = session_index;
921
922 sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name));
923 (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name);
924
925 if (setupargv(sp, typ) == 0) {
926 free_session(sp);
927 return (0);
928 }
929
930 sp->se_next = 0;
931 if (sprev == 0) {
932 sessions = sp;
933 sp->se_prev = 0;
934 } else {
935 sprev->se_next = sp;
936 sp->se_prev = sprev;
937 }
938
939 return sp;
940}
941
942/*
943 * Calculate getty and if useful window argv vectors.
944 */
945int
946setupargv(sp, typ)
947 session_t *sp;
948 struct ttyent *typ;
949{
950
951 if (sp->se_getty) {
952 free(sp->se_getty);
49866386 953 free(sp->se_getty_argv_space);
0a708c0e
NW
954 free(sp->se_getty_argv);
955 }
956 sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2);
957 (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name);
49866386
AC
958 sp->se_getty_argv_space = strdup(sp->se_getty);
959 sp->se_getty_argv = construct_argv(sp->se_getty_argv_space);
0a708c0e
NW
960 if (sp->se_getty_argv == 0) {
961 warning("can't parse getty for port %s", sp->se_device);
962 free(sp->se_getty);
49866386
AC
963 free(sp->se_getty_argv_space);
964 sp->se_getty = sp->se_getty_argv_space = 0;
0a708c0e
NW
965 return (0);
966 }
49866386
AC
967 if (sp->se_window) {
968 free(sp->se_window);
969 free(sp->se_window_argv_space);
970 free(sp->se_window_argv);
971 }
972 sp->se_window = sp->se_window_argv_space = 0;
973 sp->se_window_argv = 0;
0a708c0e 974 if (typ->ty_window) {
0a708c0e 975 sp->se_window = strdup(typ->ty_window);
49866386
AC
976 sp->se_window_argv_space = strdup(sp->se_window);
977 sp->se_window_argv = construct_argv(sp->se_window_argv_space);
0a708c0e
NW
978 if (sp->se_window_argv == 0) {
979 warning("can't parse window for port %s",
980 sp->se_device);
49866386 981 free(sp->se_window_argv_space);
0a708c0e 982 free(sp->se_window);
49866386 983 sp->se_window = sp->se_window_argv_space = 0;
0a708c0e
NW
984 return (0);
985 }
986 }
49866386
AC
987 if (sp->se_type)
988 free(sp->se_type);
989 sp->se_type = typ->ty_type ? strdup(typ->ty_type) : 0;
0a708c0e
NW
990 return (1);
991}
992
993/*
994 * Walk the list of ttys and create sessions for each active line.
995 */
996state_func_t
997read_ttys()
998{
999 int session_index = 0;
1000 register session_t *sp, *snext;
1001 register struct ttyent *typ;
1002
1003 /*
1004 * Destroy any previous session state.
1005 * There shouldn't be any, but just in case...
1006 */
1007 for (sp = sessions; sp; sp = snext) {
1008 if (sp->se_process)
1009 clear_session_logs(sp);
1010 snext = sp->se_next;
1011 free_session(sp);
1012 }
1013 sessions = 0;
1014 if (start_session_db())
1015 return (state_func_t) single_user;
1016
1017 /*
1018 * Allocate a session entry for each active port.
1019 * Note that sp starts at 0.
1020 */
1021 while (typ = getttyent())
1022 if (snext = new_session(sp, ++session_index, typ))
1023 sp = snext;
1024
1025 endttyent();
1026
1027 return (state_func_t) multi_user;
1028}
1029
1030/*
1031 * Start a window system running.
1032 */
1033void
1034start_window_system(sp)
1035 session_t *sp;
1036{
1037 pid_t pid;
1038 sigset_t mask;
49866386 1039 char term[64], *env[2];
0a708c0e
NW
1040
1041 if ((pid = fork()) == -1) {
1042 emergency("can't fork for window system on port %s: %m",
1043 sp->se_device);
1044 /* hope that getty fails and we can try again */
1045 return;
1046 }
1047
1048 if (pid)
1049 return;
1050
1051 sigemptyset(&mask);
1052 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
1053
1054 if (setsid() < 0)
1055 emergency("setsid failed (window) %m");
1056
49866386
AC
1057 if (sp->se_type) {
1058 /* Don't use malloc after fork */
1059 strcpy(term, "TERM=");
1060 strcat(term, sp->se_type);
1061 env[0] = term;
1062 env[1] = 0;
1063 }
1064 else
1065 env[0] = 0;
1066 execve(sp->se_window_argv[0], sp->se_window_argv, env);
0a708c0e
NW
1067 stall("can't exec window system '%s' for port %s: %m",
1068 sp->se_window_argv[0], sp->se_device);
1069 _exit(1);
1070}
1071
1072/*
1073 * Start a login session running.
1074 */
1075pid_t
1076start_getty(sp)
1077 session_t *sp;
1078{
1079 pid_t pid;
1080 sigset_t mask;
1081 time_t current_time = time((time_t *) 0);
49866386 1082 char term[64], *env[2];
0a708c0e
NW
1083
1084 /*
1085 * fork(), not vfork() -- we can't afford to block.
1086 */
1087 if ((pid = fork()) == -1) {
1088 emergency("can't fork for getty on port %s: %m", sp->se_device);
1089 return -1;
1090 }
1091
1092 if (pid)
1093 return pid;
1094
1095 if (current_time > sp->se_started &&
1096 current_time - sp->se_started < GETTY_SPACING) {
0a3a98ff
AC
1097 if (++sp->se_nspace > GETTY_NSPACE) {
1098 sp->se_nspace = 0;
1099 warning("getty repeating too quickly on port %s, sleeping %d secs",
1100 sp->se_device, GETTY_SLEEP);
0a708c0e
NW
1101 sleep((unsigned) GETTY_SLEEP);
1102 }
0a3a98ff
AC
1103 }
1104 else
1105 sp->se_nspace = 0;
0a708c0e
NW
1106
1107 if (sp->se_window) {
1108 start_window_system(sp);
1109 sleep(WINDOW_WAIT);
1110 }
1111
1112 sigemptyset(&mask);
1113 sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0);
1114
49866386
AC
1115 if (sp->se_type) {
1116 /* Don't use malloc after fork */
1117 strcpy(term, "TERM=");
1118 strcat(term, sp->se_type);
1119 env[0] = term;
1120 env[1] = 0;
1121 }
1122 else
1123 env[0] = 0;
1124 execve(sp->se_getty_argv[0], sp->se_getty_argv, env);
0a708c0e
NW
1125 stall("can't exec getty '%s' for port %s: %m",
1126 sp->se_getty_argv[0], sp->se_device);
1127 _exit(1);
1128}
1129
1130/*
1131 * Collect exit status for a child.
1132 * If an exiting login, start a new login running.
1133 */
1134void
1135#ifdef __STDC__
1136collect_child(pid_t pid)
1137#else
1138collect_child(pid)
1139 pid_t pid;
1140#endif
1141{
1142 register session_t *sp, *sprev, *snext;
1143
1144 if (! sessions)
1145 return;
1146
1147 if (! (sp = find_session(pid)))
1148 return;
1149
1150 clear_session_logs(sp);
1151 del_session(sp);
1152 sp->se_process = 0;
1153
1154 if (sp->se_flags & SE_SHUTDOWN) {
1155 if (sprev = sp->se_prev)
1156 sprev->se_next = sp->se_next;
1157 else
1158 sessions = sp->se_next;
1159 if (snext = sp->se_next)
1160 snext->se_prev = sp->se_prev;
1161 free_session(sp);
1162 return;
1163 }
1164
1165 if ((pid = start_getty(sp)) == -1) {
1166 /* serious trouble */
1167 requested_transition = clean_ttys;
1168 return;
1169 }
1170
1171 sp->se_process = pid;
1172 sp->se_started = time((time_t *) 0);
1173 add_session(sp);
1174}
1175
1176/*
1177 * Catch a signal and request a state transition.
1178 */
1179void
1180transition_handler(sig)
1181 int sig;
1182{
1183
1184 switch (sig) {
1185 case SIGHUP:
1186 requested_transition = clean_ttys;
1187 break;
22860a22
NW
1188 case SIGINT:
1189 noreboot++;
0a708c0e
NW
1190 case SIGTERM:
1191 requested_transition = death;
1192 break;
1193 case SIGTSTP:
1194 requested_transition = catatonia;
1195 break;
1196 default:
1197 requested_transition = 0;
1198 break;
1199 }
1200}
1201
1202/*
1203 * Take the system multiuser.
1204 */
1205state_func_t
1206multi_user()
1207{
1208 pid_t pid;
1209 register session_t *sp;
1210
1211 requested_transition = 0;
1212
1213 /*
1214 * If the administrator has not set the security level to -1
1215 * to indicate that the kernel should not run multiuser in secure
1216 * mode, and the run script has not set a higher level of security
1217 * than level 1, then put the kernel into secure mode.
1218 */
1219 if (getsecuritylevel() == 0)
1220 setsecuritylevel(1);
1221
1222 for (sp = sessions; sp; sp = sp->se_next) {
1223 if (sp->se_process)
1224 continue;
1225 if ((pid = start_getty(sp)) == -1) {
1226 /* serious trouble */
1227 requested_transition = clean_ttys;
1228 break;
1229 }
1230 sp->se_process = pid;
1231 sp->se_started = time((time_t *) 0);
1232 add_session(sp);
1233 }
1234
1235 while (!requested_transition)
1236 if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
1237 collect_child(pid);
1238
1239 return (state_func_t) requested_transition;
1240}
1241
1242/*
1243 * This is an n-squared algorithm. We hope it isn't run often...
1244 */
1245state_func_t
1246clean_ttys()
1247{
1248 register session_t *sp, *sprev;
1249 register struct ttyent *typ;
1250 register int session_index = 0;
1251 register int devlen;
49866386 1252 char *old_getty, *old_window, *old_type;
0a708c0e
NW
1253
1254 if (! sessions)
1255 return (state_func_t) multi_user;
1256
1257 devlen = sizeof(_PATH_DEV) - 1;
1258 while (typ = getttyent()) {
1259 ++session_index;
1260
1261 for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next)
1262 if (strcmp(typ->ty_name, sp->se_device + devlen) == 0)
1263 break;
1264
1265 if (sp) {
1266 if (sp->se_index != session_index) {
1267 warning("port %s changed utmp index from %d to %d",
1268 sp->se_device, sp->se_index,
1269 session_index);
1270 sp->se_index = session_index;
1271 }
1272 if ((typ->ty_status & TTY_ON) == 0 ||
1273 typ->ty_getty == 0) {
1274 sp->se_flags |= SE_SHUTDOWN;
1275 kill(sp->se_process, SIGHUP);
1276 continue;
1277 }
1278 sp->se_flags &= ~SE_SHUTDOWN;
49866386
AC
1279 old_getty = sp->se_getty ? strdup(sp->se_getty) : 0;
1280 old_window = sp->se_window ? strdup(sp->se_window) : 0;
1281 old_type = sp->se_type ? strdup(sp->se_type) : 0;
0a708c0e
NW
1282 if (setupargv(sp, typ) == 0) {
1283 warning("can't parse getty for port %s",
1284 sp->se_device);
1285 sp->se_flags |= SE_SHUTDOWN;
1286 kill(sp->se_process, SIGHUP);
1287 }
49866386
AC
1288 else if ( !old_getty
1289 || !old_type && sp->se_type
1290 || old_type && !sp->se_type
1291 || !old_window && sp->se_window
1292 || old_window && !sp->se_window
1293 || strcmp(old_getty, sp->se_getty) != 0
1294 || old_window && strcmp(old_window, sp->se_window) != 0
1295 || old_type && strcmp(old_type, sp->se_type) != 0
3be802d9 1296 ) {
49866386 1297 /* Don't set SE_SHUTDOWN here */
3be802d9
AC
1298 sp->se_nspace = 0;
1299 sp->se_started = 0;
49866386 1300 kill(sp->se_process, SIGHUP);
3be802d9 1301 }
49866386
AC
1302 if (old_getty)
1303 free(old_getty);
1304 if (old_getty)
1305 free(old_window);
1306 if (old_type)
1307 free(old_type);
0a708c0e
NW
1308 continue;
1309 }
1310
1311 new_session(sprev, session_index, typ);
1312 }
1313
1314 endttyent();
1315
1316 return (state_func_t) multi_user;
1317}
1318
1319/*
1320 * Block further logins.
1321 */
1322state_func_t
1323catatonia()
1324{
1325 register session_t *sp;
1326
1327 for (sp = sessions; sp; sp = sp->se_next)
1328 sp->se_flags |= SE_SHUTDOWN;
1329
1330 return (state_func_t) multi_user;
1331}
1332
1333/*
1334 * Note SIGALRM.
1335 */
1336void
1337alrm_handler(sig)
1338 int sig;
1339{
1340 clang = 1;
1341}
1342
1343/*
1344 * Bring the system down to single user.
1345 */
1346state_func_t
1347death()
1348{
1349 register session_t *sp;
1350 register int i;
1351 pid_t pid;
1352 static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL };
1353
1354 for (sp = sessions; sp; sp = sp->se_next)
1355 sp->se_flags |= SE_SHUTDOWN;
1356
1357 /* NB: should send a message to the session logger to avoid blocking. */
1358 logwtmp("~", "shutdown", "");
1359
1360 for (i = 0; i < 3; ++i) {
1361 if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
1362 return (state_func_t) single_user;
1363
1364 clang = 0;
1365 alarm(DEATH_WATCH);
1366 do
1367 if ((pid = waitpid(-1, (int *)0, 0)) != -1)
1368 collect_child(pid);
1369 while (clang == 0 && errno != ECHILD);
1370
1371 if (errno == ECHILD)
1372 return (state_func_t) single_user;
1373 }
1374
1375 warning("some processes would not die; ps axl advised");
1376
1377 return (state_func_t) single_user;
1378}