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 (void) sigaction(SIGINT
, &sa
, (struct sigaction
*)0);
882 setctty(_PATH_CONSOLE
, 0);
886 if (runcom_mode
== BOOT_SCRIPT
)
888 argv
[1] = _PATH_RUNCOM_BOOT
;
889 argv
[2] = requested_transition
== single_user
890 ? "singleuser" : "multiuser";
892 else /* runcom_mode != BOOT_SCRIPT */
894 argv
[1] = _PATH_RUNCOM
;
896 switch(runcom_mode
) {
898 argv
[2] = "autoboot";
901 argv
[2] = "multiuser";
906 if (runcom_verbose
|| runcom_safe
)
911 if (runcom_verbose
) options
[i
++] = 'v';
912 if (runcom_safe
) options
[i
++] = 'x';
927 for (i
= 0; i
<= 4; i
++) {
929 warning("%s argument: %s", _PATH_RUNCOM
, argv
[i
]);
934 sigprocmask(SIG_SETMASK
, &sa
.sa_mask
, (sigset_t
*) 0);
936 execv(_PATH_BSHELL
, argv
);
937 stall("can't exec %s for %s: %m", _PATH_BSHELL
, _PATH_RUNCOM
);
938 _exit(1); /* force single user mode */
942 emergency("can't fork for %s on %s: %m",
943 _PATH_BSHELL
, _PATH_RUNCOM
);
944 while (waitpid(-1, (int *) 0, WNOHANG
) > 0)
946 sleep(STALL_TIMEOUT
);
947 return (state_func_t
) single_user
;
951 * Copied from single_user(). This is a bit paranoid.
954 if ((wpid
= waitpid(-1, &status
, WUNTRACED
)) != -1)
959 warning("wait for %s on %s failed: %m; going to single user mode",
960 _PATH_BSHELL
, _PATH_RUNCOM
);
961 return (state_func_t
) single_user
;
963 if (wpid
== pid
&& WIFSTOPPED(status
)) {
964 warning("init: %s on %s stopped, restarting\n",
965 _PATH_BSHELL
, _PATH_RUNCOM
);
969 } while (wpid
!= pid
);
971 if (WIFSIGNALED(status
) && WTERMSIG(status
) == SIGTERM
&&
972 requested_transition
== catatonia
) {
973 /* /etc/rc executed /sbin/reboot; wait for the end quietly */
981 if (!WIFEXITED(status
)) {
982 warning("%s on %s terminated abnormally, going to single user mode",
983 _PATH_BSHELL
, _PATH_RUNCOM
);
984 return (state_func_t
) single_user
;
987 if (WEXITSTATUS(status
))
988 return (state_func_t
) single_user
;
990 runcom_mode
= AUTOBOOT
; /* the default */
991 /* NB: should send a message to the session logger to avoid blocking. */
992 logwtmp("~", "reboot", "");
993 return (state_func_t
) read_ttys
;
997 * Open the session database.
999 * NB: We could pass in the size here; is it necessary?
1004 if (session_db
&& (*session_db
->close
)(session_db
))
1005 emergency("session database close: %s", strerror(errno
));
1006 if ((session_db
= dbopen(NULL
, O_RDWR
, 0, DB_HASH
, NULL
)) == 0) {
1007 emergency("session database open: %s", strerror(errno
));
1015 * Add a new login session.
1024 key
.data
= &sp
->se_process
;
1025 key
.size
= sizeof sp
->se_process
;
1027 data
.size
= sizeof sp
;
1029 if ((*session_db
->put
)(session_db
, &key
, &data
, 0))
1030 emergency("insert %d: %s", sp
->se_process
, strerror(errno
));
1034 * Delete an old login session.
1042 key
.data
= &sp
->se_process
;
1043 key
.size
= sizeof sp
->se_process
;
1045 if ((*session_db
->del
)(session_db
, &key
, 0))
1046 emergency("delete %d: %s", sp
->se_process
, strerror(errno
));
1050 * Look up a login session by pid.
1053 find_session(pid_t pid
)
1060 key
.size
= sizeof pid
;
1061 if ((*session_db
->get
)(session_db
, &key
, &data
, 0) != 0)
1063 memmove(&ret
, data
.data
, sizeof(ret
));
1068 * Construct an argument vector from a command line.
1071 construct_argv(command
)
1074 register int argc
= 0;
1075 register char **argv
= (char **) malloc(((strlen(command
) + 1) / 2 + 1)
1077 static const char separators
[] = " \t";
1079 if ((argv
[argc
++] = strtok(command
, separators
)) == 0)
1081 while (argv
[argc
++] = strtok((char *) 0, separators
))
1087 * Deallocate a session descriptor.
1090 static __inline__
void free_command(se_cmd_t
*se_cmd
)
1101 register session_t
*sp
;
1103 free(sp
->se_device
);
1104 free_command(&sp
->se_getty
);
1105 free_command(&sp
->se_window
);
1106 free_command(&sp
->se_onerror
);
1107 free_command(&sp
->se_onoption
);
1108 memset(sp
, '\0', sizeof(*sp
)); // a bit of defensive programming
1113 static int setup_command(se_cmd_t
*se_cmd
, char *command
, char *arg
)
1116 char *commandWithArg
;
1118 commandWithArg
= malloc( strlen( command
) + strlen( arg
) + 2);
1119 (void) sprintf(commandWithArg
, "%s %s", command
, arg
);
1121 free_command(se_cmd
);
1123 se_cmd
->path
= commandWithArg
;
1124 se_cmd
->argv
= construct_argv(commandWithArg
);
1125 if (se_cmd
->argv
== NULL
)
1128 se_cmd
->path
= NULL
;
1135 * Calculate getty and if useful window argv vectors.
1144 if ( !setup_command(&sp
->se_getty
, typ
->ty_getty
, typ
->ty_name
) )
1151 && !setup_command(&sp
->se_window
, typ
->ty_window
, typ
->ty_name
) )
1158 && !setup_command(&sp
->se_onerror
, typ
->ty_onerror
, typ
->ty_name
) )
1164 if (typ
->ty_onoption
1165 && !setup_command(&sp
->se_onoption
, typ
->ty_onoption
, typ
->ty_name
) )
1174 warning("can't parse %s for port %s", type
, sp
->se_device
);
1180 * Allocate a new session descriptor.
1183 new_session(sprev
, session_index
, typ
)
1186 register struct ttyent
*typ
;
1188 register session_t
*sp
;
1190 if ((typ
->ty_status
& TTY_ON
) == 0 ||
1191 typ
->ty_name
== 0 ||
1195 sp
= (session_t
*) malloc(sizeof (session_t
));
1196 memset(sp
, 0, sizeof *sp
);
1198 sp
->se_index
= session_index
;
1200 sp
->se_device
= malloc(sizeof(_PATH_DEV
) + strlen(typ
->ty_name
));
1201 (void) sprintf(sp
->se_device
, "%s%s", _PATH_DEV
, typ
->ty_name
);
1203 if (setupargv(sp
, typ
) == 0) {
1213 sprev
->se_next
= sp
;
1214 sp
->se_prev
= sprev
;
1221 * Walk the list of ttys and create sessions for each active line.
1226 int session_index
= 0;
1227 register session_t
*sp
, *snext
;
1228 register struct ttyent
*typ
;
1231 * Destroy any previous session state.
1232 * There shouldn't be any, but just in case...
1234 for (sp
= sessions
; sp
; sp
= snext
) {
1236 clear_session_logs(sp
);
1237 snext
= sp
->se_next
;
1241 if (start_session_db())
1242 return (state_func_t
) single_user
;
1245 * Allocate a session entry for each active port.
1246 * Note that sp starts at 0.
1248 while (typ
= getttyent())
1249 if (snext
= new_session(sp
, ++session_index
, typ
))
1254 return (state_func_t
) multi_user
;
1258 * Start a window system running.
1261 start_window_system(session_t
*sp
)
1266 if ((pid
= fork()) == -1) {
1267 emergency("can't fork for window system on port %s: %m",
1269 /* hope that getty fails and we can try again */
1277 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
1280 emergency("setsid failed (window) %m");
1282 execv(sp
->se_window
.argv
[0], sp
->se_window
.argv
);
1283 stall("can't exec window system '%s' for port %s: %m",
1284 sp
->se_window
.argv
[0], sp
->se_device
);
1289 * Start a login session running.
1298 const char *session_type
= NULL
;
1299 time_t current_time
= time((time_t *) 0);
1301 // Setup the default values;
1302 switch (sp
->se_flags
& SE_GETTY_LAUNCH
)
1305 if (sp
->se_onoption
.path
)
1307 se_cmd
= &sp
->se_onoption
;
1308 session_type
= "onoption";
1314 if (sp
->se_onerror
.path
)
1316 se_cmd
= &sp
->se_onerror
;
1317 session_type
= "onerror";
1324 se_cmd
= &sp
->se_getty
;
1325 session_type
= "getty";
1329 if (sp
->se_window
.path
1330 && ((sp
->se_flags
& SE_GETTY_LAUNCH
) != SE_ONOPTION
))
1332 if (start_window_system(sp
) == -1)
1337 * fork(), not vfork() -- we can't afford to block.
1339 if ((pid
= fork()) == -1) {
1340 emergency("can't fork for %s on port %s: %m",
1341 session_type
, sp
->se_device
);
1348 if (current_time
> sp
->se_started
&&
1349 current_time
- sp
->se_started
< GETTY_SPACING
) {
1350 warning("%s repeating too quickly on port %s, sleeping",
1351 session_type
, sp
->se_device
);
1352 sleep((unsigned) GETTY_SLEEP
);
1356 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
1358 execv(se_cmd
->argv
[0], se_cmd
->argv
);
1359 stall("can't exec %s '%s' for port %s: %m", session_type
,
1360 se_cmd
->argv
[0], sp
->se_device
);
1365 * Collect exit status for a child.
1366 * If an exiting login, start a new login running.
1369 collect_child(pid_t pid
)
1371 register session_t
*sp
, *sprev
, *snext
;
1376 if ( !(sp
= find_session(pid
)) )
1379 clear_session_logs(sp
);
1383 if (sp
->se_flags
& SE_SHUTDOWN
) {
1384 if (sprev
= sp
->se_prev
)
1385 sprev
->se_next
= sp
->se_next
;
1387 sessions
= sp
->se_next
;
1388 if (snext
= sp
->se_next
)
1389 snext
->se_prev
= sp
->se_prev
;
1394 if ((pid
= start_getty(sp
)) == -1) {
1395 /* serious trouble */
1396 requested_transition
= clean_ttys
;
1400 sp
->se_process
= pid
;
1401 sp
->se_started
= time((time_t *) 0);
1402 sp
->se_flags
&= ~SE_GETTY_LAUNCH
; // clear down getty launch type
1407 * Catch a signal and request a state transition.
1410 transition_handler(sig
)
1416 requested_transition
= clean_ttys
;
1419 requested_transition
= death
;
1422 requested_transition
= catatonia
;
1425 requested_transition
= 0;
1431 * Take the system multiuser.
1437 register session_t
*sp
;
1439 requested_transition
= 0;
1442 * If the administrator has not set the security level to -1
1443 * to indicate that the kernel should not run multiuser in secure
1444 * mode, and the run script has not set a higher level of security
1445 * than level 1, then put the kernel into secure mode.
1447 if (getsecuritylevel() == 0)
1448 setsecuritylevel(1);
1450 for (sp
= sessions
; sp
; sp
= sp
->se_next
) {
1453 if ((pid
= start_getty(sp
)) == -1) {
1454 /* serious trouble */
1455 requested_transition
= clean_ttys
;
1458 sp
->se_process
= pid
;
1459 sp
->se_started
= time((time_t *) 0);
1463 while (!requested_transition
)
1468 pid
= waitpid(-1, &status
, 0);
1469 if (!sessions
|| !(sp
= find_session(pid
)))
1472 if (WIFSIGNALED(status
))
1473 sp
->se_flags
|= SE_ONERROR
;
1474 else if (WEXITSTATUS(status
) == REALLY_EXIT_TO_CONSOLE
)
1475 { /* WIFEXITED(status) assumed */
1476 sp
->se_flags
|= SE_ONOPTION
;
1479 sp
->se_flags
|= SE_ONERROR
;
1485 return (state_func_t
) requested_transition
;
1489 * This is an n-squared algorithm. We hope it isn't run often...
1494 register session_t
*sp
, *sprev
;
1495 register struct ttyent
*typ
;
1496 register int session_index
= 0;
1497 register int devlen
;
1500 return (state_func_t
) multi_user
;
1502 devlen
= sizeof(_PATH_DEV
) - 1;
1503 while (typ
= getttyent()) {
1506 for (sprev
= 0, sp
= sessions
; sp
; sprev
= sp
, sp
= sp
->se_next
)
1507 if (strcmp(typ
->ty_name
, sp
->se_device
+ devlen
) == 0)
1511 if (sp
->se_index
!= session_index
) {
1512 warning("port %s changed utmp index from %d to %d",
1513 sp
->se_device
, sp
->se_index
,
1515 sp
->se_index
= session_index
;
1517 if ((typ
->ty_status
& TTY_ON
) == 0 ||
1518 typ
->ty_getty
== 0) {
1519 sp
->se_flags
|= SE_SHUTDOWN
;
1520 kill(sp
->se_process
, SIGHUP
);
1523 sp
->se_flags
&= ~SE_SHUTDOWN
;
1524 if (setupargv(sp
, typ
) == 0) {
1525 warning("can't parse getty for port %s",
1527 sp
->se_flags
|= SE_SHUTDOWN
;
1528 kill(sp
->se_process
, SIGHUP
);
1533 new_session(sprev
, session_index
, typ
);
1538 return (state_func_t
) multi_user
;
1542 * Block further logins.
1547 register session_t
*sp
;
1549 for (sp
= sessions
; sp
; sp
= sp
->se_next
)
1550 sp
->se_flags
|= SE_SHUTDOWN
;
1552 return (state_func_t
) multi_user
;
1566 * Bring the system down to single user.
1571 register session_t
*sp
;
1574 static const int death_sigs
[3] = { SIGHUP
, SIGTERM
, SIGKILL
};
1576 for (sp
= sessions
; sp
; sp
= sp
->se_next
)
1577 sp
->se_flags
|= SE_SHUTDOWN
;
1579 /* NB: should send a message to the session logger to avoid blocking. */
1580 logwtmp("~", "shutdown", "");
1582 for (i
= 0; i
< 3; ++i
) {
1583 if (kill(-1, death_sigs
[i
]) == -1 && errno
== ESRCH
)
1584 return (state_func_t
) single_user
;
1589 if ((pid
= waitpid(-1, (int *)0, 0)) != -1)
1591 while (clang
== 0 && errno
!= ECHILD
);
1593 if (errno
== ECHILD
)
1594 return (state_func_t
) single_user
;
1597 warning("some processes would not die; ps axl advised");
1599 return (state_func_t
) single_user
;