2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
25 * Copyright (c) 1991, 1993
26 * The Regents of the University of California. All rights reserved.
28 * This code is derived from software contributed to Berkeley by
29 * Donn Seeley at Berkeley Software Design, Inc.
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 * must display the following acknowledgement:
41 * This product includes software developed by the University of
42 * California, Berkeley and its contributors.
43 * 4. Neither the name of the University nor the names of its contributors
44 * may be used to endorse or promote products derived from this software
45 * without specific prior written permission.
47 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 #include <sys/param.h>
61 #include <sys/sysctl.h>
83 #include "pathnames.h"
86 * Until the mythical util.h arrives...
88 extern int login_tty
__P((int));
89 extern int logout
__P((const char *));
90 extern void logwtmp
__P((const char *, const char *, const char *));
93 * Sleep times; used to prevent thrashing.
95 #define GETTY_SPACING 5 /* N secs minimum getty spacing */
96 #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */
97 #define WINDOW_WAIT 3 /* wait N secs after starting window */
98 #define STALL_TIMEOUT 30 /* wait N secs after warning */
99 #define DEATH_WATCH 10 /* wait N secs for procs to die */
100 #define FAILED_HW_PASS 5 /* wait N secs before croaking user */
102 void handle
__P((sig_t
, ...));
103 void delset
__P((sigset_t
*, ...));
105 void stall
__P((char *, ...));
106 void warning
__P((char *, ...));
107 void emergency
__P((char *, ...));
108 void disaster
__P((int));
109 void badsys
__P((int));
112 * We really need a recursive typedef...
113 * The following at least guarantees that the return type of (*state_t)()
114 * is sufficiently wide to hold a function pointer.
116 typedef long (*state_func_t
) __P((void));
117 typedef state_func_t (*state_t
) __P((void));
119 state_func_t single_user
__P((void));
120 state_func_t runcom
__P((void));
121 state_func_t read_ttys
__P((void));
122 state_func_t multi_user
__P((void));
123 state_func_t clean_ttys
__P((void));
124 state_func_t catatonia
__P((void));
125 state_func_t death
__P((void));
127 enum { AUTOBOOT
, FASTBOOT
, BOOT_SCRIPT
} runcom_mode
= AUTOBOOT
;
128 int runcom_boot
= 1; /* Run the rc.boot script */
129 int runcom_verbose
= 0;
132 void transition
__P((state_t
));
133 state_t requested_transition
= runcom
;
135 void setctty
__P((char *, int));
138 // gvdl@next.com 14 Aug 1995
139 // - from ~apps/loginwindow_proj/loginwindow/common.h
140 #define REALLY_EXIT_TO_CONSOLE 229
143 // These flags are used in the se_flags field of the init_session structure
144 #define SE_SHUTDOWN 0x1 /* session won't be restarted */
146 // The flags below control what sort of getty is launched.
147 #define SE_GETTY_LAUNCH 0x30 /* What type of getty to launch */
148 #define SE_COMMON 0x00 /* Usual command that is run - getty */
149 #define SE_ONERROR 0x10 /* Command to run if error condition occurs.
150 * This will almost always be the windowserver
151 * and loginwindow. This is so if the w.s.
152 * ever dies, that the naive user (stan)
153 * doesn't ever see the console window. */
154 #define SE_ONOPTION 0x20 /* Command to run when loginwindow exits with
155 * special error code (229). This signifies
156 * that the user typed "console" at l.w. and
157 * l.w. wants to exit and have init run getty
158 * which will then put up a console window. */
160 typedef struct _se_command
{
161 char *path
; /* what to run on that port */
162 char **argv
; /* pre-parsed argument array */
165 typedef struct init_session
{
166 int se_index
; /* index of entry in ttys file */
167 pid_t se_process
; /* controlling process */
168 time_t se_started
; /* used to avoid thrashing */
169 int se_flags
; /* status of session */
170 char *se_device
; /* filename of port */
171 se_cmd_t se_getty
; /* what to run on that port */
172 se_cmd_t se_window
; /* window system (started only once) */
173 se_cmd_t se_onerror
; /* See SE_ONERROR above */
174 se_cmd_t se_onoption
; /* See SE_ONOPTION above */
175 struct init_session
*se_prev
;
176 struct init_session
*se_next
;
179 void free_session
__P((session_t
*));
180 session_t
*new_session
__P((session_t
*, int, struct ttyent
*));
183 char **construct_argv
__P((char *));
184 void collect_child
__P((pid_t
));
185 pid_t start_getty
__P((session_t
*));
186 void transition_handler
__P((int));
187 void alrm_handler
__P((int));
188 void setsecuritylevel
__P((int));
189 int getsecuritylevel
__P((void));
190 int setupargv
__P((session_t
*, struct ttyent
*));
193 void clear_session_logs
__P((session_t
*));
195 int start_session_db
__P((void));
196 void add_session
__P((session_t
*));
197 void del_session
__P((session_t
*));
198 session_t
*find_session
__P((pid_t
));
202 * The mother of all processes.
214 /* Dispose of random users. */
216 (void)fprintf(stderr
, "init: %s\n", strerror(EPERM
));
220 /* System V users like to reexec init. */
222 (void)fprintf(stderr
, "init: already running\n");
227 * Note that this does NOT open a file...
228 * Does 'init' deserve its own facility number?
230 openlog("init", LOG_CONS
|LOG_ODELAY
, LOG_AUTH
);
233 * Create an initial session.
236 warning("initial setsid() failed: %m");
239 * Establish an initial user so that programs running
240 * single user do not freak out and die (like passwd).
242 if (setlogin("root") < 0)
243 warning("setlogin() failed: %m");
246 * This code assumes that we always get arguments through flags,
247 * never through bits set in some random machine register.
253 for (i
= 0; i
<= argc
; i
++) {
255 warning("init argument %d: '%s'", i
, argv
[i
]);
257 warning("init argument %d: ***NULL***", i
);
262 while ((c
= getopt(argc
, argv
, "sfbvx")) != -1) {
264 warning("handling init argument '-%c'", c
);
268 requested_transition
= single_user
;
271 runcom_mode
= FASTBOOT
;
274 runcom_boot
= 0; // Don't runcom rc.boot
283 warning("unrecognized flag '-%c'", c
);
289 warning("ignoring excess arguments");
292 * We catch or block signals rather than ignore them,
293 * so that they get reset on exec.
295 handle(badsys
, SIGSYS
, 0);
296 handle(disaster
, SIGABRT
, SIGFPE
, SIGILL
, SIGSEGV
,
297 SIGBUS
, SIGXCPU
, SIGXFSZ
, 0);
298 handle(transition_handler
, SIGHUP
, SIGTERM
, SIGTSTP
, 0);
299 handle(alrm_handler
, SIGALRM
, 0);
301 delset(&mask
, SIGABRT
, SIGFPE
, SIGILL
, SIGSEGV
, SIGBUS
, SIGSYS
,
302 SIGXCPU
, SIGXFSZ
, SIGHUP
, SIGTERM
, SIGTSTP
, SIGALRM
, 0);
303 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
304 sigemptyset(&sa
.sa_mask
);
306 sa
.sa_handler
= SIG_IGN
;
307 (void) sigaction(SIGTTIN
, &sa
, (struct sigaction
*)0);
308 (void) sigaction(SIGTTOU
, &sa
, (struct sigaction
*)0);
319 int old_rc_mode
= runcom_mode
;
321 runcom_mode
= BOOT_SCRIPT
;
322 if (runcom() == (state_func_t
) single_user
)
323 requested_transition
= single_user
; // Error in script
324 runcom_mode
= old_rc_mode
;
328 * Start the state machine.
330 transition(requested_transition
);
333 * Should never reach here.
339 * Associate a function with a signal handler.
342 handle(sig_t handler
, ...)
348 va_start(ap
, handler
);
350 sa
.sa_handler
= handler
;
351 sigfillset(&mask_everything
);
353 while (sig
= va_arg(ap
, int)) {
354 sa
.sa_mask
= mask_everything
;
355 /* XXX SA_RESTART? */
356 sa
.sa_flags
= sig
== SIGCHLD
? SA_NOCLDSTOP
: 0;
357 sigaction(sig
, &sa
, (struct sigaction
*) 0);
363 * Delete a set of signals from a mask.
366 delset(sigset_t
*maskp
, ...)
372 while (sig
= va_arg(ap
, int))
373 sigdelset(maskp
, sig
);
378 * Log a message and sleep for a while (to give someone an opportunity
379 * to read it and to save log or hardcopy output if the problem is chronic).
380 * NB: should send a message to the session logger to avoid blocking.
383 stall(char *message
, ...)
386 va_start(ap
, message
);
388 vsyslog(LOG_ALERT
, message
, ap
);
390 sleep(STALL_TIMEOUT
);
394 * Like stall(), but doesn't sleep.
395 * If cpp had variadic macros, the two functions could be #defines for another.
396 * NB: should send a message to the session logger to avoid blocking.
399 warning(char *message
, ...)
402 va_start(ap
, message
);
404 vsyslog(LOG_ALERT
, message
, ap
);
409 * Log an emergency message.
410 * NB: should send a message to the session logger to avoid blocking.
413 emergency(char *message
, ...)
416 va_start(ap
, message
);
418 vsyslog(LOG_EMERG
, message
, ap
);
423 * Catch a SIGSYS signal.
425 * These may arise if a system does not support sysctl.
426 * We tolerate up to 25 of these, then throw in the towel.
432 static int badcount
= 0;
440 * Catch an unexpected signal.
446 emergency("fatal signal: %s",
447 sig
< (unsigned) NSIG
? sys_siglist
[sig
] : "unknown signal");
449 sleep(STALL_TIMEOUT
);
450 _exit(sig
); /* reboot */
454 * Get the security level of the kernel.
459 #ifdef KERN_SECURELVL
460 int name
[2], curlevel
;
465 name
[1] = KERN_SECURELVL
;
466 len
= sizeof curlevel
;
467 if (sysctl(name
, 2, &curlevel
, &len
, NULL
, 0) == -1) {
468 emergency("cannot get kernel security level: %s",
479 * Set the security level of the kernel.
482 setsecuritylevel(newlevel
)
485 #ifdef KERN_SECURELVL
486 int name
[2], curlevel
;
489 curlevel
= getsecuritylevel();
490 if (newlevel
== curlevel
)
493 name
[1] = KERN_SECURELVL
;
494 if (sysctl(name
, 2, NULL
, NULL
, &newlevel
, sizeof newlevel
) == -1) {
496 "cannot change kernel security level from %d to %d: %s",
497 curlevel
, newlevel
, strerror(errno
));
501 warning("kernel security level changed from %d to %d",
508 * Change states in the finite state machine.
509 * The initial state is passed as an argument.
516 s
= (state_t
) (*s
)();
520 * Close out the accounting files for a login session.
521 * NB: should send a message to the session logger to avoid blocking.
524 clear_session_logs(sp
)
527 char *line
= sp
->se_device
+ sizeof(_PATH_DEV
) - 1;
530 logwtmp(line
, "", "");
534 * Start a session and allocate a controlling terminal.
535 * Only called by children of init after forking.
545 if ((fd
= open(name
, flags
| O_RDWR
)) == -1) {
546 stall("can't open %s: %m", name
);
549 if (login_tty(fd
) == -1) {
550 stall("can't get %s for controlling terminal: %m", name
);
557 * Taken from etc/halt/halt.c
564 static void shutend(void)
569 for (i
= 0; i
< 10; i
++)
572 logwtmp("~", "shutdown", "");
575 static void do_halt(void)
578 int halthowto
= RB_HALT
;
580 (void) kill(-1, SIGTERM
); /* one chance to catch it */
582 sprintf (sbuf
, "Invalid hardware password, halting machine...\n");
583 write (1, sbuf
, strlen (sbuf
));
585 signal(SIGALRM
, SIG_DFL
);
589 signal(SIGALRM
, alrm_handler
);
590 alarm(FAILED_HW_PASS
);
593 syscall(SYS_reboot
, halthowto
);
597 * Taken from lib/gen/getpass.c
600 static char *gethwpasswd(char *prompt
)
608 (void) tcgetattr(1, &term
);
609 if (echo
= (term
.c_lflag
& ECHO
))
611 term
.c_lflag
&= ~ECHO
;
612 (void) tcsetattr(1, TCSAFLUSH
|TCSASOFT
, &term
);
615 write(2, prompt
, strlen(prompt
));
617 for (p
= pbuf
; (c
= getchar()) != '\n' && c
!= EOF
; )
623 write(2, p
, strlen(p
));
627 term
.c_lflag
|= ECHO
;
628 (void) tcsetattr(1, TCSAFLUSH
|TCSASOFT
, &term
);
635 static char *hw_passwd (void)
638 static char buffer
[12];
639 struct nvram_info nvi
;
642 if ((vidfd
= open ("/dev/vid0", O_RDONLY
, 0)) == -1)
645 if (ioctl (vidfd
, DKIOCGNVRAM
, &nvi
) == -1)
648 if (nvi
.ni_hw_pwd
!= HW_PWD
)
653 for (count
= 0; count
< NVRAM_HW_PASSWD
; count
++)
654 nvi
.ni_ep
[count
] ^= 'N';
655 strncpy(buffer
, nvi
.ni_ep
, NVRAM_HW_PASSWD
);
656 /* ni_ep is not necessarily null terminated */
658 // gvdl I sure hope it is 'cause bad things will happen otherwise
669 do_security_check(void)
677 * If there is a hardware passwd, we want to
678 * prompt the user for it. The write will be
679 * to the console window because of the O_POPUP flag.
681 passwd
= hw_passwd();
682 write (1, "\n\n", 2);
688 try = gethwpasswd ("Enter hardware password:");
689 if (strncmp (try, passwd
, NVRAM_HW_PASSWD
) == 0)
691 execl(shell
, minus
, (char *)0);
696 sprintf (sbuf
, "Password incorrect.\n\n");
697 write (1, sbuf
, strlen (sbuf
));
700 while (++retries
< 3);
703 #elif defined(SECURE)
706 static const char banner
[] =
707 "Enter root password, or ^D to go multi-user\n";
708 char *clear
, *password
;
711 * Check the root password.
712 * We don't care if the console is 'on' by default;
713 * it's the only tty that can be 'off' and 'secure'.
715 typ
= getttynam("console");
716 pp
= getpwnam("root");
717 if (typ
&& (typ
->ty_status
& TTY_SECURE
) == 0 && pp
)
719 write(2, banner
, sizeof banner
- 1);
722 clear
= getpass("Password:");
723 if (clear
== 0 || *clear
== '\0')
725 password
= crypt(clear
, pp
->pw_passwd
);
726 memset(clear
, 0, _PASSWORD_LEN
);
727 if (strcmp(password
, pp
->pw_passwd
) == 0)
729 warning("single-user login failed\n");
738 * Bring the system up single user.
746 char *shell
= _PATH_BSHELL
;
749 * If the kernel is in secure mode, downgrade it to insecure mode.
751 if (getsecuritylevel() > 0)
754 if ((pid
= fork()) == 0) {
756 * Start the single user session.
758 setctty(_PATH_CONSOLE
, O_POPUP
);
764 char altshell
[128], *cp
= altshell
;
768 "Enter pathname of shell or RETURN for sh: "
769 (void)write(STDERR_FILENO
,
770 SHREQUEST
, sizeof(SHREQUEST
) - 1);
771 while ((num
= read(STDIN_FILENO
, cp
, 1)) != -1 &&
772 num
!= 0 && *cp
!= '\n' && cp
< &altshell
[127])
775 if (altshell
[0] != '\0')
778 #endif /* DEBUGSHELL */
782 * We catch all the interesting ones,
783 * and those are reset to SIG_DFL on exec.
786 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
789 * Set up the PATH to be approriate for the root user.
791 setenv("PATH", _PATH_STDPATH
, 1);
794 * We're dropping into the console; set TERM appropriately.
796 setenv("TERM", "vt100", 1);
800 * If the default one doesn't work, try the Bourne shell.
805 emergency("can't exec %s for single user: %m", shell
);
806 execv(_PATH_BSHELL
, argv
);
807 emergency("can't exec %s for single user: %m", _PATH_BSHELL
);
808 sleep(STALL_TIMEOUT
);
814 * We are seriously hosed. Do our best.
816 emergency("can't fork single-user shell, trying again");
817 while (waitpid(-1, (int *) 0, WNOHANG
) > 0)
819 return (state_func_t
) single_user
;
822 requested_transition
= 0;
824 if ((wpid
= waitpid(-1, &status
, WUNTRACED
)) != -1)
829 warning("wait for single-user shell failed: %m; restarting");
830 return (state_func_t
) single_user
;
832 if (wpid
== pid
&& WIFSTOPPED(status
)) {
833 warning("init: shell stopped, restarting\n");
837 } while (wpid
!= pid
&& !requested_transition
);
839 if (requested_transition
)
840 return (state_func_t
) requested_transition
;
842 if (!WIFEXITED(status
)) {
843 if (WTERMSIG(status
) == SIGKILL
) {
845 * reboot(8) killed shell?
847 warning("single user shell terminated.");
848 sleep(STALL_TIMEOUT
);
851 warning("single user shell terminated, restarting");
852 return (state_func_t
) single_user
;
856 runcom_mode
= FASTBOOT
;
857 return (state_func_t
) runcom
;
861 * Run the system startup script.
872 if ((pid
= fork()) == 0) {
873 sigemptyset(&sa
.sa_mask
);
875 sa
.sa_handler
= SIG_IGN
;
876 (void) sigaction(SIGTSTP
, &sa
, (struct sigaction
*)0);
877 (void) sigaction(SIGHUP
, &sa
, (struct sigaction
*)0);
879 setctty(_PATH_CONSOLE
, 0);
883 if (runcom_mode
== BOOT_SCRIPT
)
885 argv
[1] = _PATH_RUNCOM_BOOT
;
886 argv
[2] = requested_transition
== single_user
889 else /* runcom_mode != BOOT_SCRIPT */
891 argv
[1] = _PATH_RUNCOM
;
893 switch(runcom_mode
) {
895 argv
[2] = "autoboot";
898 argv
[2] = "multiuser";
903 if (runcom_verbose
|| runcom_safe
)
908 if (runcom_verbose
) options
[i
++] = 'v';
909 if (runcom_safe
) options
[i
++] = 'x';
924 for (i
= 0; i
<= 4; i
++) {
926 warning("%s argument: %s", _PATH_RUNCOM
, argv
[i
]);
931 sigprocmask(SIG_SETMASK
, &sa
.sa_mask
, (sigset_t
*) 0);
933 execv(_PATH_BSHELL
, argv
);
934 stall("can't exec %s for %s: %m", _PATH_BSHELL
, _PATH_RUNCOM
);
935 _exit(1); /* force single user mode */
939 emergency("can't fork for %s on %s: %m",
940 _PATH_BSHELL
, _PATH_RUNCOM
);
941 while (waitpid(-1, (int *) 0, WNOHANG
) > 0)
943 sleep(STALL_TIMEOUT
);
944 return (state_func_t
) single_user
;
948 * Copied from single_user(). This is a bit paranoid.
951 if ((wpid
= waitpid(-1, &status
, WUNTRACED
)) != -1)
956 warning("wait for %s on %s failed: %m; going to single user mode",
957 _PATH_BSHELL
, _PATH_RUNCOM
);
958 return (state_func_t
) single_user
;
960 if (wpid
== pid
&& WIFSTOPPED(status
)) {
961 warning("init: %s on %s stopped, restarting\n",
962 _PATH_BSHELL
, _PATH_RUNCOM
);
966 } while (wpid
!= pid
);
968 if (WIFSIGNALED(status
) && WTERMSIG(status
) == SIGTERM
&&
969 requested_transition
== catatonia
) {
970 /* /etc/rc executed /sbin/reboot; wait for the end quietly */
978 if (!WIFEXITED(status
)) {
979 warning("%s on %s terminated abnormally, going to single user mode",
980 _PATH_BSHELL
, _PATH_RUNCOM
);
981 return (state_func_t
) single_user
;
984 if (WEXITSTATUS(status
))
985 return (state_func_t
) single_user
;
987 runcom_mode
= AUTOBOOT
; /* the default */
988 /* NB: should send a message to the session logger to avoid blocking. */
989 logwtmp("~", "reboot", "");
990 return (state_func_t
) read_ttys
;
994 * Open the session database.
996 * NB: We could pass in the size here; is it necessary?
1001 if (session_db
&& (*session_db
->close
)(session_db
))
1002 emergency("session database close: %s", strerror(errno
));
1003 if ((session_db
= dbopen(NULL
, O_RDWR
, 0, DB_HASH
, NULL
)) == 0) {
1004 emergency("session database open: %s", strerror(errno
));
1012 * Add a new login session.
1021 key
.data
= &sp
->se_process
;
1022 key
.size
= sizeof sp
->se_process
;
1024 data
.size
= sizeof sp
;
1026 if ((*session_db
->put
)(session_db
, &key
, &data
, 0))
1027 emergency("insert %d: %s", sp
->se_process
, strerror(errno
));
1031 * Delete an old login session.
1039 key
.data
= &sp
->se_process
;
1040 key
.size
= sizeof sp
->se_process
;
1042 if ((*session_db
->del
)(session_db
, &key
, 0))
1043 emergency("delete %d: %s", sp
->se_process
, strerror(errno
));
1047 * Look up a login session by pid.
1050 find_session(pid_t pid
)
1057 key
.size
= sizeof pid
;
1058 if ((*session_db
->get
)(session_db
, &key
, &data
, 0) != 0)
1060 memmove(&ret
, data
.data
, sizeof(ret
));
1065 * Construct an argument vector from a command line.
1068 construct_argv(command
)
1071 register int argc
= 0;
1072 register char **argv
= (char **) malloc(((strlen(command
) + 1) / 2 + 1)
1074 static const char separators
[] = " \t";
1076 if ((argv
[argc
++] = strtok(command
, separators
)) == 0)
1078 while (argv
[argc
++] = strtok((char *) 0, separators
))
1084 * Deallocate a session descriptor.
1087 static __inline__
void free_command(se_cmd_t
*se_cmd
)
1098 register session_t
*sp
;
1100 free(sp
->se_device
);
1101 free_command(&sp
->se_getty
);
1102 free_command(&sp
->se_window
);
1103 free_command(&sp
->se_onerror
);
1104 free_command(&sp
->se_onoption
);
1105 memset(sp
, '\0', sizeof(*sp
)); // a bit of defensive programming
1110 static int setup_command(se_cmd_t
*se_cmd
, char *command
, char *arg
)
1113 char *commandWithArg
;
1115 commandWithArg
= malloc( strlen( command
) + strlen( arg
) + 2);
1116 (void) sprintf(commandWithArg
, "%s %s", command
, arg
);
1118 free_command(se_cmd
);
1120 se_cmd
->path
= commandWithArg
;
1121 se_cmd
->argv
= construct_argv(commandWithArg
);
1122 if (se_cmd
->argv
== NULL
)
1125 se_cmd
->path
= NULL
;
1132 * Calculate getty and if useful window argv vectors.
1141 if ( !setup_command(&sp
->se_getty
, typ
->ty_getty
, typ
->ty_name
) )
1148 && !setup_command(&sp
->se_window
, typ
->ty_window
, typ
->ty_name
) )
1155 && !setup_command(&sp
->se_onerror
, typ
->ty_onerror
, typ
->ty_name
) )
1161 if (typ
->ty_onoption
1162 && !setup_command(&sp
->se_onoption
, typ
->ty_onoption
, typ
->ty_name
) )
1171 warning("can't parse %s for port %s", type
, sp
->se_device
);
1177 * Allocate a new session descriptor.
1180 new_session(sprev
, session_index
, typ
)
1183 register struct ttyent
*typ
;
1185 register session_t
*sp
;
1187 if ((typ
->ty_status
& TTY_ON
) == 0 ||
1188 typ
->ty_name
== 0 ||
1192 sp
= (session_t
*) malloc(sizeof (session_t
));
1193 memset(sp
, 0, sizeof *sp
);
1195 sp
->se_index
= session_index
;
1197 sp
->se_device
= malloc(sizeof(_PATH_DEV
) + strlen(typ
->ty_name
));
1198 (void) sprintf(sp
->se_device
, "%s%s", _PATH_DEV
, typ
->ty_name
);
1200 if (setupargv(sp
, typ
) == 0) {
1210 sprev
->se_next
= sp
;
1211 sp
->se_prev
= sprev
;
1218 * Walk the list of ttys and create sessions for each active line.
1223 int session_index
= 0;
1224 register session_t
*sp
, *snext
;
1225 register struct ttyent
*typ
;
1228 * Destroy any previous session state.
1229 * There shouldn't be any, but just in case...
1231 for (sp
= sessions
; sp
; sp
= snext
) {
1233 clear_session_logs(sp
);
1234 snext
= sp
->se_next
;
1238 if (start_session_db())
1239 return (state_func_t
) single_user
;
1242 * Allocate a session entry for each active port.
1243 * Note that sp starts at 0.
1245 while (typ
= getttyent())
1246 if (snext
= new_session(sp
, ++session_index
, typ
))
1251 return (state_func_t
) multi_user
;
1255 * Start a window system running.
1258 start_window_system(session_t
*sp
)
1263 if ((pid
= fork()) == -1) {
1264 emergency("can't fork for window system on port %s: %m",
1266 /* hope that getty fails and we can try again */
1274 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
1277 emergency("setsid failed (window) %m");
1279 execv(sp
->se_window
.argv
[0], sp
->se_window
.argv
);
1280 stall("can't exec window system '%s' for port %s: %m",
1281 sp
->se_window
.argv
[0], sp
->se_device
);
1286 * Start a login session running.
1295 const char *session_type
= NULL
;
1296 time_t current_time
= time((time_t *) 0);
1298 // Setup the default values;
1299 switch (sp
->se_flags
& SE_GETTY_LAUNCH
)
1302 if (sp
->se_onoption
.path
)
1304 se_cmd
= &sp
->se_onoption
;
1305 session_type
= "onoption";
1311 if (sp
->se_onerror
.path
)
1313 se_cmd
= &sp
->se_onerror
;
1314 session_type
= "onerror";
1321 se_cmd
= &sp
->se_getty
;
1322 session_type
= "getty";
1326 if (sp
->se_window
.path
1327 && ((sp
->se_flags
& SE_GETTY_LAUNCH
) != SE_ONOPTION
))
1329 if (start_window_system(sp
) == -1)
1334 * fork(), not vfork() -- we can't afford to block.
1336 if ((pid
= fork()) == -1) {
1337 emergency("can't fork for %s on port %s: %m",
1338 session_type
, sp
->se_device
);
1345 if (current_time
> sp
->se_started
&&
1346 current_time
- sp
->se_started
< GETTY_SPACING
) {
1347 warning("%s repeating too quickly on port %s, sleeping",
1348 session_type
, sp
->se_device
);
1349 sleep((unsigned) GETTY_SLEEP
);
1353 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
1355 execv(se_cmd
->argv
[0], se_cmd
->argv
);
1356 stall("can't exec %s '%s' for port %s: %m", session_type
,
1357 se_cmd
->argv
[0], sp
->se_device
);
1362 * Collect exit status for a child.
1363 * If an exiting login, start a new login running.
1366 collect_child(pid_t pid
)
1368 register session_t
*sp
, *sprev
, *snext
;
1373 if ( !(sp
= find_session(pid
)) )
1376 clear_session_logs(sp
);
1380 if (sp
->se_flags
& SE_SHUTDOWN
) {
1381 if (sprev
= sp
->se_prev
)
1382 sprev
->se_next
= sp
->se_next
;
1384 sessions
= sp
->se_next
;
1385 if (snext
= sp
->se_next
)
1386 snext
->se_prev
= sp
->se_prev
;
1391 if ((pid
= start_getty(sp
)) == -1) {
1392 /* serious trouble */
1393 requested_transition
= clean_ttys
;
1397 sp
->se_process
= pid
;
1398 sp
->se_started
= time((time_t *) 0);
1399 sp
->se_flags
&= ~SE_GETTY_LAUNCH
; // clear down getty launch type
1404 * Catch a signal and request a state transition.
1407 transition_handler(sig
)
1413 requested_transition
= clean_ttys
;
1416 requested_transition
= death
;
1419 requested_transition
= catatonia
;
1422 requested_transition
= 0;
1428 * Take the system multiuser.
1434 register session_t
*sp
;
1436 requested_transition
= 0;
1439 * If the administrator has not set the security level to -1
1440 * to indicate that the kernel should not run multiuser in secure
1441 * mode, and the run script has not set a higher level of security
1442 * than level 1, then put the kernel into secure mode.
1444 if (getsecuritylevel() == 0)
1445 setsecuritylevel(1);
1447 for (sp
= sessions
; sp
; sp
= sp
->se_next
) {
1450 if ((pid
= start_getty(sp
)) == -1) {
1451 /* serious trouble */
1452 requested_transition
= clean_ttys
;
1455 sp
->se_process
= pid
;
1456 sp
->se_started
= time((time_t *) 0);
1460 while (!requested_transition
)
1465 pid
= waitpid(-1, &status
, 0);
1466 if (!sessions
|| !(sp
= find_session(pid
)))
1469 if (WIFSIGNALED(status
))
1470 sp
->se_flags
|= SE_ONERROR
;
1471 else if (WEXITSTATUS(status
) == REALLY_EXIT_TO_CONSOLE
)
1472 { /* WIFEXITED(status) assumed */
1473 sp
->se_flags
|= SE_ONOPTION
;
1476 sp
->se_flags
|= SE_ONERROR
;
1482 return (state_func_t
) requested_transition
;
1486 * This is an n-squared algorithm. We hope it isn't run often...
1491 register session_t
*sp
, *sprev
;
1492 register struct ttyent
*typ
;
1493 register int session_index
= 0;
1494 register int devlen
;
1497 return (state_func_t
) multi_user
;
1499 devlen
= sizeof(_PATH_DEV
) - 1;
1500 while (typ
= getttyent()) {
1503 for (sprev
= 0, sp
= sessions
; sp
; sprev
= sp
, sp
= sp
->se_next
)
1504 if (strcmp(typ
->ty_name
, sp
->se_device
+ devlen
) == 0)
1508 if (sp
->se_index
!= session_index
) {
1509 warning("port %s changed utmp index from %d to %d",
1510 sp
->se_device
, sp
->se_index
,
1512 sp
->se_index
= session_index
;
1514 if ((typ
->ty_status
& TTY_ON
) == 0 ||
1515 typ
->ty_getty
== 0) {
1516 sp
->se_flags
|= SE_SHUTDOWN
;
1517 kill(sp
->se_process
, SIGHUP
);
1520 sp
->se_flags
&= ~SE_SHUTDOWN
;
1521 if (setupargv(sp
, typ
) == 0) {
1522 warning("can't parse getty for port %s",
1524 sp
->se_flags
|= SE_SHUTDOWN
;
1525 kill(sp
->se_process
, SIGHUP
);
1530 new_session(sprev
, session_index
, typ
);
1535 return (state_func_t
) multi_user
;
1539 * Block further logins.
1544 register session_t
*sp
;
1546 for (sp
= sessions
; sp
; sp
= sp
->se_next
)
1547 sp
->se_flags
|= SE_SHUTDOWN
;
1549 return (state_func_t
) multi_user
;
1563 * Bring the system down to single user.
1568 register session_t
*sp
;
1571 static const int death_sigs
[3] = { SIGHUP
, SIGTERM
, SIGKILL
};
1573 for (sp
= sessions
; sp
; sp
= sp
->se_next
)
1574 sp
->se_flags
|= SE_SHUTDOWN
;
1576 /* NB: should send a message to the session logger to avoid blocking. */
1577 logwtmp("~", "shutdown", "");
1579 for (i
= 0; i
< 3; ++i
) {
1580 if (kill(-1, death_sigs
[i
]) == -1 && errno
== ESRCH
)
1581 return (state_func_t
) single_user
;
1586 if ((pid
= waitpid(-1, (int *)0, 0)) != -1)
1588 while (clang
== 0 && errno
!= ECHILD
);
1590 if (errno
== ECHILD
)
1591 return (state_func_t
) single_user
;
1594 warning("some processes would not die; ps axl advised");
1596 return (state_func_t
) single_user
;