2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (c) 1991, 1993
27 * The Regents of the University of California. All rights reserved.
29 * This code is derived from software contributed to Berkeley by
30 * Donn Seeley at Berkeley Software Design, Inc.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 #include <sys/param.h>
62 #include <sys/sysctl.h>
84 #include "pathnames.h"
87 * Until the mythical util.h arrives...
89 extern int login_tty
__P((int));
90 extern int logout
__P((const char *));
91 extern void logwtmp
__P((const char *, const char *, const char *));
94 * Sleep times; used to prevent thrashing.
96 #define GETTY_SPACING 5 /* N secs minimum getty spacing */
97 #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */
98 #define WINDOW_WAIT 3 /* wait N secs after starting window */
99 #define STALL_TIMEOUT 30 /* wait N secs after warning */
100 #define DEATH_WATCH 10 /* wait N secs for procs to die */
101 #define FAILED_HW_PASS 5 /* wait N secs before croaking user */
103 void handle
__P((sig_t
, ...));
104 void delset
__P((sigset_t
*, ...));
106 void stall
__P((char *, ...));
107 void warning
__P((char *, ...));
108 void emergency
__P((char *, ...));
109 void disaster
__P((int));
110 void badsys
__P((int));
113 * We really need a recursive typedef...
114 * The following at least guarantees that the return type of (*state_t)()
115 * is sufficiently wide to hold a function pointer.
117 typedef long (*state_func_t
) __P((void));
118 typedef state_func_t (*state_t
) __P((void));
120 state_func_t single_user
__P((void));
121 state_func_t runcom
__P((void));
122 state_func_t read_ttys
__P((void));
123 state_func_t multi_user
__P((void));
124 state_func_t clean_ttys
__P((void));
125 state_func_t catatonia
__P((void));
126 state_func_t death
__P((void));
128 enum { AUTOBOOT
, FASTBOOT
, BOOT_SCRIPT
} runcom_mode
= AUTOBOOT
;
129 int runcom_boot
= 1; /* Run the rc.boot script */
130 int runcom_verbose
= 0;
133 void transition
__P((state_t
));
134 state_t requested_transition
= runcom
;
136 void setctty
__P((char *, int));
139 // gvdl@next.com 14 Aug 1995
140 // - from ~apps/loginwindow_proj/loginwindow/common.h
141 #define REALLY_EXIT_TO_CONSOLE 229
144 // These flags are used in the se_flags field of the init_session structure
145 #define SE_SHUTDOWN 0x1 /* session won't be restarted */
147 // The flags below control what sort of getty is launched.
148 #define SE_GETTY_LAUNCH 0x30 /* What type of getty to launch */
149 #define SE_COMMON 0x00 /* Usual command that is run - getty */
150 #define SE_ONERROR 0x10 /* Command to run if error condition occurs.
151 * This will almost always be the windowserver
152 * and loginwindow. This is so if the w.s.
153 * ever dies, that the naive user (stan)
154 * doesn't ever see the console window. */
155 #define SE_ONOPTION 0x20 /* Command to run when loginwindow exits with
156 * special error code (229). This signifies
157 * that the user typed "console" at l.w. and
158 * l.w. wants to exit and have init run getty
159 * which will then put up a console window. */
161 typedef struct _se_command
{
162 char *path
; /* what to run on that port */
163 char **argv
; /* pre-parsed argument array */
166 typedef struct init_session
{
167 int se_index
; /* index of entry in ttys file */
168 pid_t se_process
; /* controlling process */
169 time_t se_started
; /* used to avoid thrashing */
170 int se_flags
; /* status of session */
171 char *se_device
; /* filename of port */
172 se_cmd_t se_getty
; /* what to run on that port */
173 se_cmd_t se_window
; /* window system (started only once) */
174 se_cmd_t se_onerror
; /* See SE_ONERROR above */
175 se_cmd_t se_onoption
; /* See SE_ONOPTION above */
176 struct init_session
*se_prev
;
177 struct init_session
*se_next
;
180 void free_session
__P((session_t
*));
181 session_t
*new_session
__P((session_t
*, int, struct ttyent
*));
184 char **construct_argv
__P((char *));
185 void collect_child
__P((pid_t
));
186 pid_t start_getty
__P((session_t
*));
187 void transition_handler
__P((int));
188 void alrm_handler
__P((int));
189 void setsecuritylevel
__P((int));
190 int getsecuritylevel
__P((void));
191 int setupargv
__P((session_t
*, struct ttyent
*));
194 void clear_session_logs
__P((session_t
*));
196 int start_session_db
__P((void));
197 void add_session
__P((session_t
*));
198 void del_session
__P((session_t
*));
199 session_t
*find_session
__P((pid_t
));
203 * The mother of all processes.
215 /* Dispose of random users. */
217 (void)fprintf(stderr
, "init: %s\n", strerror(EPERM
));
221 /* System V users like to reexec init. */
223 (void)fprintf(stderr
, "init: already running\n");
228 * Note that this does NOT open a file...
229 * Does 'init' deserve its own facility number?
231 openlog("init", LOG_CONS
|LOG_ODELAY
, LOG_AUTH
);
234 * Create an initial session.
237 warning("initial setsid() failed: %m");
240 * Establish an initial user so that programs running
241 * single user do not freak out and die (like passwd).
243 if (setlogin("root") < 0)
244 warning("setlogin() failed: %m");
247 * This code assumes that we always get arguments through flags,
248 * never through bits set in some random machine register.
254 for (i
= 0; i
<= argc
; i
++) {
256 warning("init argument %d: '%s'", i
, argv
[i
]);
258 warning("init argument %d: ***NULL***", i
);
263 while ((c
= getopt(argc
, argv
, "sfbvx")) != -1) {
265 warning("handling init argument '-%c'", c
);
269 requested_transition
= single_user
;
272 runcom_mode
= FASTBOOT
;
275 runcom_boot
= 0; // Don't runcom rc.boot
284 warning("unrecognized flag '-%c'", c
);
290 warning("ignoring excess arguments");
293 * We catch or block signals rather than ignore them,
294 * so that they get reset on exec.
296 handle(badsys
, SIGSYS
, 0);
297 handle(disaster
, SIGABRT
, SIGFPE
, SIGILL
, SIGSEGV
,
298 SIGBUS
, SIGXCPU
, SIGXFSZ
, 0);
299 handle(transition_handler
, SIGHUP
, SIGTERM
, SIGTSTP
, 0);
300 handle(alrm_handler
, SIGALRM
, 0);
302 delset(&mask
, SIGABRT
, SIGFPE
, SIGILL
, SIGSEGV
, SIGBUS
, SIGSYS
,
303 SIGXCPU
, SIGXFSZ
, SIGHUP
, SIGTERM
, SIGTSTP
, SIGALRM
, 0);
304 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
305 sigemptyset(&sa
.sa_mask
);
307 sa
.sa_handler
= SIG_IGN
;
308 (void) sigaction(SIGTTIN
, &sa
, (struct sigaction
*)0);
309 (void) sigaction(SIGTTOU
, &sa
, (struct sigaction
*)0);
320 int old_rc_mode
= runcom_mode
;
322 runcom_mode
= BOOT_SCRIPT
;
323 if (runcom() == (state_func_t
) single_user
)
324 requested_transition
= single_user
; // Error in script
325 runcom_mode
= old_rc_mode
;
329 * Start the state machine.
331 transition(requested_transition
);
334 * Should never reach here.
340 * Associate a function with a signal handler.
343 handle(sig_t handler
, ...)
349 va_start(ap
, handler
);
351 sa
.sa_handler
= handler
;
352 sigfillset(&mask_everything
);
354 while (sig
= va_arg(ap
, int)) {
355 sa
.sa_mask
= mask_everything
;
356 /* XXX SA_RESTART? */
357 sa
.sa_flags
= sig
== SIGCHLD
? SA_NOCLDSTOP
: 0;
358 sigaction(sig
, &sa
, (struct sigaction
*) 0);
364 * Delete a set of signals from a mask.
367 delset(sigset_t
*maskp
, ...)
373 while (sig
= va_arg(ap
, int))
374 sigdelset(maskp
, sig
);
379 * Log a message and sleep for a while (to give someone an opportunity
380 * to read it and to save log or hardcopy output if the problem is chronic).
381 * NB: should send a message to the session logger to avoid blocking.
384 stall(char *message
, ...)
387 va_start(ap
, message
);
389 vsyslog(LOG_ALERT
, message
, ap
);
391 sleep(STALL_TIMEOUT
);
395 * Like stall(), but doesn't sleep.
396 * If cpp had variadic macros, the two functions could be #defines for another.
397 * NB: should send a message to the session logger to avoid blocking.
400 warning(char *message
, ...)
403 va_start(ap
, message
);
405 vsyslog(LOG_ALERT
, message
, ap
);
410 * Log an emergency message.
411 * NB: should send a message to the session logger to avoid blocking.
414 emergency(char *message
, ...)
417 va_start(ap
, message
);
419 vsyslog(LOG_EMERG
, message
, ap
);
424 * Catch a SIGSYS signal.
426 * These may arise if a system does not support sysctl.
427 * We tolerate up to 25 of these, then throw in the towel.
433 static int badcount
= 0;
441 * Catch an unexpected signal.
447 emergency("fatal signal: %s",
448 sig
< (unsigned) NSIG
? sys_siglist
[sig
] : "unknown signal");
450 sleep(STALL_TIMEOUT
);
451 _exit(sig
); /* reboot */
455 * Get the security level of the kernel.
460 #ifdef KERN_SECURELVL
461 int name
[2], curlevel
;
466 name
[1] = KERN_SECURELVL
;
467 len
= sizeof curlevel
;
468 if (sysctl(name
, 2, &curlevel
, &len
, NULL
, 0) == -1) {
469 emergency("cannot get kernel security level: %s",
480 * Set the security level of the kernel.
483 setsecuritylevel(newlevel
)
486 #ifdef KERN_SECURELVL
487 int name
[2], curlevel
;
490 curlevel
= getsecuritylevel();
491 if (newlevel
== curlevel
)
494 name
[1] = KERN_SECURELVL
;
495 if (sysctl(name
, 2, NULL
, NULL
, &newlevel
, sizeof newlevel
) == -1) {
497 "cannot change kernel security level from %d to %d: %s",
498 curlevel
, newlevel
, strerror(errno
));
502 warning("kernel security level changed from %d to %d",
509 * Change states in the finite state machine.
510 * The initial state is passed as an argument.
517 s
= (state_t
) (*s
)();
521 * Close out the accounting files for a login session.
522 * NB: should send a message to the session logger to avoid blocking.
525 clear_session_logs(sp
)
528 char *line
= sp
->se_device
+ sizeof(_PATH_DEV
) - 1;
531 logwtmp(line
, "", "");
535 * Start a session and allocate a controlling terminal.
536 * Only called by children of init after forking.
546 if ((fd
= open(name
, flags
| O_RDWR
)) == -1) {
547 stall("can't open %s: %m", name
);
550 if (login_tty(fd
) == -1) {
551 stall("can't get %s for controlling terminal: %m", name
);
558 * Taken from etc/halt/halt.c
565 static void shutend(void)
570 for (i
= 0; i
< 10; i
++)
573 logwtmp("~", "shutdown", "");
576 static void do_halt(void)
579 int halthowto
= RB_HALT
;
581 (void) kill(-1, SIGTERM
); /* one chance to catch it */
583 sprintf (sbuf
, "Invalid hardware password, halting machine...\n");
584 write (1, sbuf
, strlen (sbuf
));
586 signal(SIGALRM
, SIG_DFL
);
590 signal(SIGALRM
, alrm_handler
);
591 alarm(FAILED_HW_PASS
);
594 syscall(SYS_reboot
, halthowto
);
598 * Taken from lib/gen/getpass.c
601 static char *gethwpasswd(char *prompt
)
609 (void) tcgetattr(1, &term
);
610 if (echo
= (term
.c_lflag
& ECHO
))
612 term
.c_lflag
&= ~ECHO
;
613 (void) tcsetattr(1, TCSAFLUSH
|TCSASOFT
, &term
);
616 write(2, prompt
, strlen(prompt
));
618 for (p
= pbuf
; (c
= getchar()) != '\n' && c
!= EOF
; )
624 write(2, p
, strlen(p
));
628 term
.c_lflag
|= ECHO
;
629 (void) tcsetattr(1, TCSAFLUSH
|TCSASOFT
, &term
);
636 static char *hw_passwd (void)
639 static char buffer
[12];
640 struct nvram_info nvi
;
643 if ((vidfd
= open ("/dev/vid0", O_RDONLY
, 0)) == -1)
646 if (ioctl (vidfd
, DKIOCGNVRAM
, &nvi
) == -1)
649 if (nvi
.ni_hw_pwd
!= HW_PWD
)
654 for (count
= 0; count
< NVRAM_HW_PASSWD
; count
++)
655 nvi
.ni_ep
[count
] ^= 'N';
656 strncpy(buffer
, nvi
.ni_ep
, NVRAM_HW_PASSWD
);
657 /* ni_ep is not necessarily null terminated */
659 // gvdl I sure hope it is 'cause bad things will happen otherwise
670 do_security_check(void)
678 * If there is a hardware passwd, we want to
679 * prompt the user for it. The write will be
680 * to the console window because of the O_POPUP flag.
682 passwd
= hw_passwd();
683 write (1, "\n\n", 2);
689 try = gethwpasswd ("Enter hardware password:");
690 if (strncmp (try, passwd
, NVRAM_HW_PASSWD
) == 0)
692 execl(shell
, minus
, (char *)0);
697 sprintf (sbuf
, "Password incorrect.\n\n");
698 write (1, sbuf
, strlen (sbuf
));
701 while (++retries
< 3);
704 #elif defined(SECURE)
707 static const char banner
[] =
708 "Enter root password, or ^D to go multi-user\n";
709 char *clear
, *password
;
712 * Check the root password.
713 * We don't care if the console is 'on' by default;
714 * it's the only tty that can be 'off' and 'secure'.
716 typ
= getttynam("console");
717 pp
= getpwnam("root");
718 if (typ
&& (typ
->ty_status
& TTY_SECURE
) == 0 && pp
)
720 write(2, banner
, sizeof banner
- 1);
723 clear
= getpass("Password:");
724 if (clear
== 0 || *clear
== '\0')
726 password
= crypt(clear
, pp
->pw_passwd
);
727 memset(clear
, 0, _PASSWORD_LEN
);
728 if (strcmp(password
, pp
->pw_passwd
) == 0)
730 warning("single-user login failed\n");
739 * Bring the system up single user.
747 char *shell
= _PATH_BSHELL
;
750 * If the kernel is in secure mode, downgrade it to insecure mode.
752 if (getsecuritylevel() > 0)
755 if ((pid
= fork()) == 0) {
757 * Start the single user session.
759 setctty(_PATH_CONSOLE
, O_POPUP
);
765 char altshell
[128], *cp
= altshell
;
769 "Enter pathname of shell or RETURN for sh: "
770 (void)write(STDERR_FILENO
,
771 SHREQUEST
, sizeof(SHREQUEST
) - 1);
772 while ((num
= read(STDIN_FILENO
, cp
, 1)) != -1 &&
773 num
!= 0 && *cp
!= '\n' && cp
< &altshell
[127])
776 if (altshell
[0] != '\0')
779 #endif /* DEBUGSHELL */
783 * We catch all the interesting ones,
784 * and those are reset to SIG_DFL on exec.
787 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
790 * Set up the PATH to be approriate for the root user.
792 setenv("PATH", _PATH_STDPATH
, 1);
795 * We're dropping into the console; set TERM appropriately.
797 setenv("TERM", "vt100", 1);
801 * If the default one doesn't work, try the Bourne shell.
806 emergency("can't exec %s for single user: %m", shell
);
807 execv(_PATH_BSHELL
, argv
);
808 emergency("can't exec %s for single user: %m", _PATH_BSHELL
);
809 sleep(STALL_TIMEOUT
);
815 * We are seriously hosed. Do our best.
817 emergency("can't fork single-user shell, trying again");
818 while (waitpid(-1, (int *) 0, WNOHANG
) > 0)
820 return (state_func_t
) single_user
;
823 requested_transition
= 0;
825 if ((wpid
= waitpid(-1, &status
, WUNTRACED
)) != -1)
830 warning("wait for single-user shell failed: %m; restarting");
831 return (state_func_t
) single_user
;
833 if (wpid
== pid
&& WIFSTOPPED(status
)) {
834 warning("init: shell stopped, restarting\n");
838 } while (wpid
!= pid
&& !requested_transition
);
840 if (requested_transition
)
841 return (state_func_t
) requested_transition
;
843 if (!WIFEXITED(status
)) {
844 if (WTERMSIG(status
) == SIGKILL
) {
846 * reboot(8) killed shell?
848 warning("single user shell terminated.");
849 sleep(STALL_TIMEOUT
);
852 warning("single user shell terminated, restarting");
853 return (state_func_t
) single_user
;
857 runcom_mode
= FASTBOOT
;
858 return (state_func_t
) runcom
;
862 * Run the system startup script.
873 if ((pid
= fork()) == 0) {
874 sigemptyset(&sa
.sa_mask
);
876 sa
.sa_handler
= SIG_IGN
;
877 (void) sigaction(SIGTSTP
, &sa
, (struct sigaction
*)0);
878 (void) sigaction(SIGHUP
, &sa
, (struct sigaction
*)0);
880 setctty(_PATH_CONSOLE
, 0);
884 if (runcom_mode
== BOOT_SCRIPT
)
886 argv
[1] = _PATH_RUNCOM_BOOT
;
887 argv
[2] = requested_transition
== single_user
888 ? "singleuser" : "multiuser";
890 else /* runcom_mode != BOOT_SCRIPT */
892 argv
[1] = _PATH_RUNCOM
;
894 switch(runcom_mode
) {
896 argv
[2] = "autoboot";
899 argv
[2] = "multiuser";
904 if (runcom_verbose
|| runcom_safe
)
909 if (runcom_verbose
) options
[i
++] = 'v';
910 if (runcom_safe
) options
[i
++] = 'x';
925 for (i
= 0; i
<= 4; i
++) {
927 warning("%s argument: %s", _PATH_RUNCOM
, argv
[i
]);
932 sigprocmask(SIG_SETMASK
, &sa
.sa_mask
, (sigset_t
*) 0);
934 execv(_PATH_BSHELL
, argv
);
935 stall("can't exec %s for %s: %m", _PATH_BSHELL
, _PATH_RUNCOM
);
936 _exit(1); /* force single user mode */
940 emergency("can't fork for %s on %s: %m",
941 _PATH_BSHELL
, _PATH_RUNCOM
);
942 while (waitpid(-1, (int *) 0, WNOHANG
) > 0)
944 sleep(STALL_TIMEOUT
);
945 return (state_func_t
) single_user
;
949 * Copied from single_user(). This is a bit paranoid.
952 if ((wpid
= waitpid(-1, &status
, WUNTRACED
)) != -1)
957 warning("wait for %s on %s failed: %m; going to single user mode",
958 _PATH_BSHELL
, _PATH_RUNCOM
);
959 return (state_func_t
) single_user
;
961 if (wpid
== pid
&& WIFSTOPPED(status
)) {
962 warning("init: %s on %s stopped, restarting\n",
963 _PATH_BSHELL
, _PATH_RUNCOM
);
967 } while (wpid
!= pid
);
969 if (WIFSIGNALED(status
) && WTERMSIG(status
) == SIGTERM
&&
970 requested_transition
== catatonia
) {
971 /* /etc/rc executed /sbin/reboot; wait for the end quietly */
979 if (!WIFEXITED(status
)) {
980 warning("%s on %s terminated abnormally, going to single user mode",
981 _PATH_BSHELL
, _PATH_RUNCOM
);
982 return (state_func_t
) single_user
;
985 if (WEXITSTATUS(status
))
986 return (state_func_t
) single_user
;
988 runcom_mode
= AUTOBOOT
; /* the default */
989 /* NB: should send a message to the session logger to avoid blocking. */
990 logwtmp("~", "reboot", "");
991 return (state_func_t
) read_ttys
;
995 * Open the session database.
997 * NB: We could pass in the size here; is it necessary?
1002 if (session_db
&& (*session_db
->close
)(session_db
))
1003 emergency("session database close: %s", strerror(errno
));
1004 if ((session_db
= dbopen(NULL
, O_RDWR
, 0, DB_HASH
, NULL
)) == 0) {
1005 emergency("session database open: %s", strerror(errno
));
1013 * Add a new login session.
1022 key
.data
= &sp
->se_process
;
1023 key
.size
= sizeof sp
->se_process
;
1025 data
.size
= sizeof sp
;
1027 if ((*session_db
->put
)(session_db
, &key
, &data
, 0))
1028 emergency("insert %d: %s", sp
->se_process
, strerror(errno
));
1032 * Delete an old login session.
1040 key
.data
= &sp
->se_process
;
1041 key
.size
= sizeof sp
->se_process
;
1043 if ((*session_db
->del
)(session_db
, &key
, 0))
1044 emergency("delete %d: %s", sp
->se_process
, strerror(errno
));
1048 * Look up a login session by pid.
1051 find_session(pid_t pid
)
1058 key
.size
= sizeof pid
;
1059 if ((*session_db
->get
)(session_db
, &key
, &data
, 0) != 0)
1061 memmove(&ret
, data
.data
, sizeof(ret
));
1066 * Construct an argument vector from a command line.
1069 construct_argv(command
)
1072 register int argc
= 0;
1073 register char **argv
= (char **) malloc(((strlen(command
) + 1) / 2 + 1)
1075 static const char separators
[] = " \t";
1077 if ((argv
[argc
++] = strtok(command
, separators
)) == 0)
1079 while (argv
[argc
++] = strtok((char *) 0, separators
))
1085 * Deallocate a session descriptor.
1088 static __inline__
void free_command(se_cmd_t
*se_cmd
)
1099 register session_t
*sp
;
1101 free(sp
->se_device
);
1102 free_command(&sp
->se_getty
);
1103 free_command(&sp
->se_window
);
1104 free_command(&sp
->se_onerror
);
1105 free_command(&sp
->se_onoption
);
1106 memset(sp
, '\0', sizeof(*sp
)); // a bit of defensive programming
1111 static int setup_command(se_cmd_t
*se_cmd
, char *command
, char *arg
)
1114 char *commandWithArg
;
1116 commandWithArg
= malloc( strlen( command
) + strlen( arg
) + 2);
1117 (void) sprintf(commandWithArg
, "%s %s", command
, arg
);
1119 free_command(se_cmd
);
1121 se_cmd
->path
= commandWithArg
;
1122 se_cmd
->argv
= construct_argv(commandWithArg
);
1123 if (se_cmd
->argv
== NULL
)
1126 se_cmd
->path
= NULL
;
1133 * Calculate getty and if useful window argv vectors.
1142 if ( !setup_command(&sp
->se_getty
, typ
->ty_getty
, typ
->ty_name
) )
1149 && !setup_command(&sp
->se_window
, typ
->ty_window
, typ
->ty_name
) )
1156 && !setup_command(&sp
->se_onerror
, typ
->ty_onerror
, typ
->ty_name
) )
1162 if (typ
->ty_onoption
1163 && !setup_command(&sp
->se_onoption
, typ
->ty_onoption
, typ
->ty_name
) )
1172 warning("can't parse %s for port %s", type
, sp
->se_device
);
1178 * Allocate a new session descriptor.
1181 new_session(sprev
, session_index
, typ
)
1184 register struct ttyent
*typ
;
1186 register session_t
*sp
;
1188 if ((typ
->ty_status
& TTY_ON
) == 0 ||
1189 typ
->ty_name
== 0 ||
1193 sp
= (session_t
*) malloc(sizeof (session_t
));
1194 memset(sp
, 0, sizeof *sp
);
1196 sp
->se_index
= session_index
;
1198 sp
->se_device
= malloc(sizeof(_PATH_DEV
) + strlen(typ
->ty_name
));
1199 (void) sprintf(sp
->se_device
, "%s%s", _PATH_DEV
, typ
->ty_name
);
1201 if (setupargv(sp
, typ
) == 0) {
1211 sprev
->se_next
= sp
;
1212 sp
->se_prev
= sprev
;
1219 * Walk the list of ttys and create sessions for each active line.
1224 int session_index
= 0;
1225 register session_t
*sp
, *snext
;
1226 register struct ttyent
*typ
;
1229 * Destroy any previous session state.
1230 * There shouldn't be any, but just in case...
1232 for (sp
= sessions
; sp
; sp
= snext
) {
1234 clear_session_logs(sp
);
1235 snext
= sp
->se_next
;
1239 if (start_session_db())
1240 return (state_func_t
) single_user
;
1243 * Allocate a session entry for each active port.
1244 * Note that sp starts at 0.
1246 while (typ
= getttyent())
1247 if (snext
= new_session(sp
, ++session_index
, typ
))
1252 return (state_func_t
) multi_user
;
1256 * Start a window system running.
1259 start_window_system(session_t
*sp
)
1264 if ((pid
= fork()) == -1) {
1265 emergency("can't fork for window system on port %s: %m",
1267 /* hope that getty fails and we can try again */
1275 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
1278 emergency("setsid failed (window) %m");
1280 execv(sp
->se_window
.argv
[0], sp
->se_window
.argv
);
1281 stall("can't exec window system '%s' for port %s: %m",
1282 sp
->se_window
.argv
[0], sp
->se_device
);
1287 * Start a login session running.
1296 const char *session_type
= NULL
;
1297 time_t current_time
= time((time_t *) 0);
1299 // Setup the default values;
1300 switch (sp
->se_flags
& SE_GETTY_LAUNCH
)
1303 if (sp
->se_onoption
.path
)
1305 se_cmd
= &sp
->se_onoption
;
1306 session_type
= "onoption";
1312 if (sp
->se_onerror
.path
)
1314 se_cmd
= &sp
->se_onerror
;
1315 session_type
= "onerror";
1322 se_cmd
= &sp
->se_getty
;
1323 session_type
= "getty";
1327 if (sp
->se_window
.path
1328 && ((sp
->se_flags
& SE_GETTY_LAUNCH
) != SE_ONOPTION
))
1330 if (start_window_system(sp
) == -1)
1335 * fork(), not vfork() -- we can't afford to block.
1337 if ((pid
= fork()) == -1) {
1338 emergency("can't fork for %s on port %s: %m",
1339 session_type
, sp
->se_device
);
1346 if (current_time
> sp
->se_started
&&
1347 current_time
- sp
->se_started
< GETTY_SPACING
) {
1348 warning("%s repeating too quickly on port %s, sleeping",
1349 session_type
, sp
->se_device
);
1350 sleep((unsigned) GETTY_SLEEP
);
1354 sigprocmask(SIG_SETMASK
, &mask
, (sigset_t
*) 0);
1356 execv(se_cmd
->argv
[0], se_cmd
->argv
);
1357 stall("can't exec %s '%s' for port %s: %m", session_type
,
1358 se_cmd
->argv
[0], sp
->se_device
);
1363 * Collect exit status for a child.
1364 * If an exiting login, start a new login running.
1367 collect_child(pid_t pid
)
1369 register session_t
*sp
, *sprev
, *snext
;
1374 if ( !(sp
= find_session(pid
)) )
1377 clear_session_logs(sp
);
1381 if (sp
->se_flags
& SE_SHUTDOWN
) {
1382 if (sprev
= sp
->se_prev
)
1383 sprev
->se_next
= sp
->se_next
;
1385 sessions
= sp
->se_next
;
1386 if (snext
= sp
->se_next
)
1387 snext
->se_prev
= sp
->se_prev
;
1392 if ((pid
= start_getty(sp
)) == -1) {
1393 /* serious trouble */
1394 requested_transition
= clean_ttys
;
1398 sp
->se_process
= pid
;
1399 sp
->se_started
= time((time_t *) 0);
1400 sp
->se_flags
&= ~SE_GETTY_LAUNCH
; // clear down getty launch type
1405 * Catch a signal and request a state transition.
1408 transition_handler(sig
)
1414 requested_transition
= clean_ttys
;
1417 requested_transition
= death
;
1420 requested_transition
= catatonia
;
1423 requested_transition
= 0;
1429 * Take the system multiuser.
1435 register session_t
*sp
;
1437 requested_transition
= 0;
1440 * If the administrator has not set the security level to -1
1441 * to indicate that the kernel should not run multiuser in secure
1442 * mode, and the run script has not set a higher level of security
1443 * than level 1, then put the kernel into secure mode.
1445 if (getsecuritylevel() == 0)
1446 setsecuritylevel(1);
1448 for (sp
= sessions
; sp
; sp
= sp
->se_next
) {
1451 if ((pid
= start_getty(sp
)) == -1) {
1452 /* serious trouble */
1453 requested_transition
= clean_ttys
;
1456 sp
->se_process
= pid
;
1457 sp
->se_started
= time((time_t *) 0);
1461 while (!requested_transition
)
1466 pid
= waitpid(-1, &status
, 0);
1467 if (!sessions
|| !(sp
= find_session(pid
)))
1470 if (WIFSIGNALED(status
))
1471 sp
->se_flags
|= SE_ONERROR
;
1472 else if (WEXITSTATUS(status
) == REALLY_EXIT_TO_CONSOLE
)
1473 { /* WIFEXITED(status) assumed */
1474 sp
->se_flags
|= SE_ONOPTION
;
1477 sp
->se_flags
|= SE_ONERROR
;
1483 return (state_func_t
) requested_transition
;
1487 * This is an n-squared algorithm. We hope it isn't run often...
1492 register session_t
*sp
, *sprev
;
1493 register struct ttyent
*typ
;
1494 register int session_index
= 0;
1495 register int devlen
;
1498 return (state_func_t
) multi_user
;
1500 devlen
= sizeof(_PATH_DEV
) - 1;
1501 while (typ
= getttyent()) {
1504 for (sprev
= 0, sp
= sessions
; sp
; sprev
= sp
, sp
= sp
->se_next
)
1505 if (strcmp(typ
->ty_name
, sp
->se_device
+ devlen
) == 0)
1509 if (sp
->se_index
!= session_index
) {
1510 warning("port %s changed utmp index from %d to %d",
1511 sp
->se_device
, sp
->se_index
,
1513 sp
->se_index
= session_index
;
1515 if ((typ
->ty_status
& TTY_ON
) == 0 ||
1516 typ
->ty_getty
== 0) {
1517 sp
->se_flags
|= SE_SHUTDOWN
;
1518 kill(sp
->se_process
, SIGHUP
);
1521 sp
->se_flags
&= ~SE_SHUTDOWN
;
1522 if (setupargv(sp
, typ
) == 0) {
1523 warning("can't parse getty for port %s",
1525 sp
->se_flags
|= SE_SHUTDOWN
;
1526 kill(sp
->se_process
, SIGHUP
);
1531 new_session(sprev
, session_index
, typ
);
1536 return (state_func_t
) multi_user
;
1540 * Block further logins.
1545 register session_t
*sp
;
1547 for (sp
= sessions
; sp
; sp
= sp
->se_next
)
1548 sp
->se_flags
|= SE_SHUTDOWN
;
1550 return (state_func_t
) multi_user
;
1564 * Bring the system down to single user.
1569 register session_t
*sp
;
1572 static const int death_sigs
[3] = { SIGHUP
, SIGTERM
, SIGKILL
};
1574 for (sp
= sessions
; sp
; sp
= sp
->se_next
)
1575 sp
->se_flags
|= SE_SHUTDOWN
;
1577 /* NB: should send a message to the session logger to avoid blocking. */
1578 logwtmp("~", "shutdown", "");
1580 for (i
= 0; i
< 3; ++i
) {
1581 if (kill(-1, death_sigs
[i
]) == -1 && errno
== ESRCH
)
1582 return (state_func_t
) single_user
;
1587 if ((pid
= waitpid(-1, (int *)0, 0)) != -1)
1589 while (clang
== 0 && errno
!= ECHILD
);
1591 if (errno
== ECHILD
)
1592 return (state_func_t
) single_user
;
1595 warning("some processes would not die; ps axl advised");
1597 return (state_func_t
) single_user
;