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 <Security/Authorization.h>
61 #include <Security/AuthorizationTags.h>
62 #include <Security/AuthSession.h>
64 #include <mach/port.h>
66 #include <sys/types.h>
67 #include <sys/queue.h>
68 #include <sys/param.h>
69 #include <sys/sysctl.h>
72 #include <sys/resource.h>
93 #include "bootstrap_internal.h"
95 #define _PATH_RUNCOM "/etc/rc"
98 * Sleep times; used to prevent thrashing.
100 #define GETTY_SPACING 5 /* N secs minimum getty spacing */
101 #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */
102 #define STALL_TIMEOUT 30 /* wait N secs after warning */
103 #define DEATH_WATCH 10 /* wait N secs for procs to die */
104 #define FAILED_HW_PASS 5 /* wait N secs before croaking user */
106 static void stall(char *, ...);
108 static void single_user_callback(void *, struct kevent
*);
109 static kq_callback kqsingle_user_callback
= single_user_callback
;
110 static void runcom_callback(void *, struct kevent
*);
111 static kq_callback kqruncom_callback
= runcom_callback
;
113 static void single_user(void);
114 static void runcom(void);
116 static bool runcom_verbose
= false;
117 static bool runcom_safe
= false;
118 static bool runcom_fsck
= true;
119 static bool runcom_netboot
= false;
120 static bool single_user_mode
= false;
121 static bool run_runcom
= true;
122 static pid_t single_user_pid
= 0;
123 static pid_t runcom_pid
= 0;
125 static void setctty(const char *, int);
127 // gvdl@next.com 14 Aug 1995
128 // - from ~apps/loginwindow_proj/loginwindow/common.h
129 #define REALLY_EXIT_TO_CONSOLE 229
132 // These flags are used in the se_flags field of the init_session structure
133 #define SE_SHUTDOWN 0x1 /* session won't be restarted */
135 // The flags below control what sort of getty is launched.
136 #define SE_GETTY_LAUNCH 0x30 /* What type of getty to launch */
137 #define SE_COMMON 0x00 /* Usual command that is run - getty */
138 #define SE_ONERROR 0x10 /* Command to run if error condition occurs.
139 * This will almost always be the windowserver
140 * and loginwindow. This is so if the w.s.
141 * ever dies, that the naive user (stan)
142 * doesn't ever see the console window. */
143 #define SE_ONOPTION 0x20 /* Command to run when loginwindow exits with
144 * special error code (229). This signifies
145 * that the user typed "console" at l.w. and
146 * l.w. wants to exit and have init run getty
147 * which will then put up a console window. */
149 typedef struct _se_command
{
150 char *path
; /* what to run on that port */
151 char **argv
; /* pre-parsed argument array */
154 typedef struct init_session
{
155 kq_callback se_callback
; /* run loop callback */
156 int se_index
; /* index of entry in ttys file */
157 pid_t se_process
; /* controlling process */
158 time_t se_started
; /* used to avoid thrashing */
159 int se_flags
; /* status of session */
160 char *se_device
; /* filename of port */
161 se_cmd_t se_getty
; /* what to run on that port */
162 se_cmd_t se_onerror
; /* See SE_ONERROR above */
163 se_cmd_t se_onoption
; /* See SE_ONOPTION above */
164 TAILQ_ENTRY(init_session
) tqe
;
167 static TAILQ_HEAD(sessionshead
, init_session
) sessions
= TAILQ_HEAD_INITIALIZER(sessions
);
169 static void session_new(int, struct ttyent
*);
170 static void session_free(session_t
);
171 static void session_launch(session_t
);
172 static void session_reap(session_t
);
173 static void session_callback(void *, struct kevent
*);
175 static char **construct_argv(char *);
176 static void setsecuritylevel(int);
177 static int getsecuritylevel(void);
178 static int setupargv(session_t
, struct ttyent
*);
181 init_boot(bool sflag
, bool vflag
, bool xflag
)
183 int nbmib
[2] = { CTL_KERN
, KERN_NETBOOT
};
185 size_t nbsz
= sizeof(nb
);
188 single_user_mode
= true;
192 runcom_verbose
= true;
196 if (sysctl(nbmib
, 2, &nb
, &nbsz
, NULL
, 0) == 0) {
197 /* The following assignment of nb to itself if the size of data
198 * returned is 32 bits instead of 64 is a clever C trick to
199 * move the 32 bits on big endian systems to the least
200 * significant bytes of the 64 mem variable.
202 * On little endian systems, this is effectively a no-op.
205 nb
= *(uint32_t *)&nb
;
207 runcom_netboot
= true;
209 syslog(LOG_WARNING
, "sysctl(\"kern.netboot\") %m");
215 init_pre_kevent(void)
219 if (single_user_mode
&& single_user_pid
== 0)
225 if (!single_user_mode
&& !run_runcom
&& runcom_pid
== 0) {
227 * If the administrator has not set the security level to -1
228 * to indicate that the kernel should not run multiuser in secure
229 * mode, and the run script has not set a higher level of security
230 * than level 1, then put the kernel into secure mode.
232 if (getsecuritylevel() == 0)
235 TAILQ_FOREACH(s
, &sessions
, tqe
) {
236 if (s
->se_process
== 0)
243 stall(char *message
, ...)
246 va_start(ap
, message
);
248 vsyslog(LOG_ALERT
, message
, ap
);
250 sleep(STALL_TIMEOUT
);
254 getsecuritylevel(void)
256 int name
[2], curlevel
;
260 name
[1] = KERN_SECURELVL
;
261 len
= sizeof (curlevel
);
262 if (sysctl(name
, 2, &curlevel
, &len
, NULL
, 0) == -1) {
263 syslog(LOG_ALERT
, "cannot get kernel security level: %m");
270 setsecuritylevel(int newlevel
)
272 int name
[2], curlevel
;
274 curlevel
= getsecuritylevel();
275 if (newlevel
== curlevel
)
278 name
[1] = KERN_SECURELVL
;
279 if (sysctl(name
, 2, NULL
, NULL
, &newlevel
, sizeof newlevel
) == -1) {
280 syslog(LOG_ALERT
, "cannot change kernel security level from %d to %d: %m",
284 syslog(LOG_INFO
, "kernel security level changed from %d to %d",
289 * Start a session and allocate a controlling terminal.
290 * Only called by children of init after forking.
293 setctty(const char *name
, int flags
)
298 if ((fd
= open(name
, flags
| O_RDWR
)) == -1) {
299 stall("can't open %s: %m", name
);
302 if (login_tty(fd
) == -1) {
303 stall("can't get %s for controlling terminal: %m", name
);
313 if (getsecuritylevel() > 0)
316 if ((single_user_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
)) == -1) {
317 syslog(LOG_ERR
, "can't fork single-user shell, trying again: %m");
319 } else if (single_user_pid
== 0) {
320 setctty(_PATH_CONSOLE
, O_POPUP
);
322 setenv("TERM", "vt100", 1);
323 setenv("SafeBoot", runcom_safe
? "-x" : "", 1);
324 setenv("VerboseFlag", "-v", 1); /* single user mode implies verbose mode */
325 setenv("FsckSlash", runcom_fsck
? "-F" : "", 1);
326 setenv("NetBoot", runcom_netboot
? "-N" : "", 1);
329 fprintf(stdout
, "Singleuser boot -- fsck not done\n");
330 fprintf(stdout
, "Root device is mounted read-only\n\n");
331 fprintf(stdout
, "If you want to make modifications to files:\n");
332 fprintf(stdout
, "\t/sbin/fsck -fy\n\t/sbin/mount -uw /\n\n");
333 fprintf(stdout
, "If you wish to boot the system, but stay in single user mode:\n");
334 fprintf(stdout
, "\tsh /etc/rc\n");
340 setpriority(PRIO_PROCESS
, 0, 0);
341 execv(_PATH_BSHELL
, argv
);
342 syslog(LOG_ERR
, "can't exec %s for single user: %m", _PATH_BSHELL
);
343 sleep(STALL_TIMEOUT
);
347 if (kevent_mod(single_user_pid
, EVFILT_PROC
, EV_ADD
,
348 NOTE_EXIT
, 0, &kqsingle_user_callback
) == -1)
349 single_user_callback(NULL
, NULL
);
354 single_user_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
356 int status
, r
= single_user_pid
;
358 #ifdef PID1_REAP_ADOPTED_CHILDREN
359 status
= pid1_child_exit_status
;
361 r
= waitpid(single_user_pid
, &status
, 0);
364 if (r
!= single_user_pid
) {
366 syslog(LOG_ERR
, "single_user_callback(): waitpid(): %m");
368 syslog(LOG_ERR
, "single_user_callback(): waitpid() returned 0");
372 if (WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
) {
373 syslog(LOG_INFO
, "single user shell terminated, restarting");
375 single_user_mode
= false;
377 syslog(LOG_INFO
, "single user shell terminated.");
379 if (WTERMSIG(status
) != SIGKILL
)
380 single_user_mode
= true;
386 static struct timeval runcom_start_tv
= { 0, 0 };
388 * Run the system startup script.
397 gettimeofday(&runcom_start_tv
, NULL
);
399 if ((runcom_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
)) == -1) {
400 syslog(LOG_ERR
, "can't fork for %s on %s: %m", _PATH_BSHELL
, _PATH_RUNCOM
);
401 sleep(STALL_TIMEOUT
);
403 single_user_mode
= true;
405 } else if (runcom_pid
> 0) {
408 if (kevent_mod(runcom_pid
, EVFILT_PROC
, EV_ADD
,
409 NOTE_EXIT
, 0, &kqruncom_callback
) == -1) {
410 runcom_callback(NULL
, NULL
);
415 setctty(_PATH_CONSOLE
, 0);
417 if ((vdisable
= fpathconf(STDIN_FILENO
, _PC_VDISABLE
)) == -1) {
418 syslog(LOG_WARNING
, "fpathconf(\"%s\") %m", _PATH_CONSOLE
);
419 } else if (tcgetattr(STDIN_FILENO
, &term
) == -1) {
420 syslog(LOG_WARNING
, "tcgetattr(\"%s\") %m", _PATH_CONSOLE
);
422 term
.c_cc
[VINTR
] = vdisable
;
423 term
.c_cc
[VKILL
] = vdisable
;
424 term
.c_cc
[VQUIT
] = vdisable
;
425 term
.c_cc
[VSUSP
] = vdisable
;
426 term
.c_cc
[VSTART
] = vdisable
;
427 term
.c_cc
[VSTOP
] = vdisable
;
428 term
.c_cc
[VDSUSP
] = vdisable
;
430 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &term
) == -1)
431 syslog(LOG_WARNING
, "tcsetattr(\"%s\") %m", _PATH_CONSOLE
);
435 argv
[1] = _PATH_RUNCOM
;
438 setenv("SafeBoot", runcom_safe
? "-x" : "", 1);
439 setenv("VerboseFlag", runcom_verbose
? "-v" : "", 1);
440 setenv("FsckSlash", runcom_fsck
? "-F" : "", 1);
441 setenv("NetBoot", runcom_netboot
? "-N" : "", 1);
443 setpriority(PRIO_PROCESS
, 0, 0);
444 execv(_PATH_BSHELL
, argv
);
445 stall("can't exec %s for %s: %m", _PATH_BSHELL
, _PATH_RUNCOM
);
450 runcom_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
453 struct timeval runcom_end_tv
, runcom_total_tv
;
455 pid_t r
= runcom_pid
;
457 gettimeofday(&runcom_end_tv
, NULL
);
458 timersub(&runcom_end_tv
, &runcom_start_tv
, &runcom_total_tv
);
459 sec
= runcom_total_tv
.tv_sec
;
460 sec
+= (double)runcom_total_tv
.tv_usec
/ (double)1000000;
461 syslog(LOG_INFO
, "%s finished in: %.3f seconds", _PATH_RUNCOM
, sec
);
463 #ifdef PID1_REAP_ADOPTED_CHILDREN
464 status
= pid1_child_exit_status
;
466 r
= waitpid(runcom_pid
, &status
, 0);
469 if (r
== runcom_pid
) {
473 syslog(LOG_ERR
, "waitpid() for '%s %s' failed: %m", _PATH_BSHELL
, _PATH_RUNCOM
);
475 syslog(LOG_ERR
, "waitpid() for '%s %s' returned 0", _PATH_BSHELL
, _PATH_RUNCOM
);
476 syslog(LOG_ERR
, "going to single user mode");
477 single_user_mode
= true;
481 if (WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
) {
482 logwtmp("~", "reboot", "");
485 } else if (WIFSIGNALED(status
) && (WTERMSIG(status
) == SIGTERM
|| WTERMSIG(status
) == SIGKILL
)) {
489 syslog(LOG_ERR
, "%s on %s terminated abnormally, going to single user mode",
490 _PATH_BSHELL
, _PATH_RUNCOM
);
491 single_user_mode
= true;
495 * Construct an argument vector from a command line.
498 construct_argv(command
)
502 char **argv
= (char **) malloc(((strlen(command
) + 1) / 2 + 1)
504 static const char separators
[] = " \t";
506 if ((argv
[argc
++] = strtok(command
, separators
)) == 0)
508 while ((argv
[argc
++] = strtok(NULL
, separators
)))
514 * Deallocate a session descriptor.
517 static void free_command(se_cmd_t
*se_cmd
)
526 session_free(session_t s
)
528 TAILQ_REMOVE(&sessions
, s
, tqe
);
530 if (kevent_mod(s
->se_process
, EVFILT_PROC
, EV_ADD
,
531 NOTE_EXIT
, 0, &kqsimple_zombie_reaper
) == -1)
534 kill(s
->se_process
, SIGHUP
);
537 free_command(&s
->se_getty
);
538 free_command(&s
->se_onerror
);
539 free_command(&s
->se_onoption
);
543 static int setup_command(se_cmd_t
*se_cmd
, char *command
, char *arg
)
545 char *commandWithArg
;
547 asprintf(&commandWithArg
, "%s %s", command
, arg
);
549 free_command(se_cmd
);
551 se_cmd
->path
= commandWithArg
;
552 se_cmd
->argv
= construct_argv(commandWithArg
);
553 if (se_cmd
->argv
== NULL
) {
562 * Calculate getty and if useful window argv vectors.
571 if ( !setup_command(&sp
->se_getty
, typ
->ty_getty
, typ
->ty_name
) )
578 && !setup_command(&sp
->se_onerror
, typ
->ty_onerror
, typ
->ty_name
) )
585 && !setup_command(&sp
->se_onoption
, typ
->ty_onoption
, typ
->ty_name
) )
594 syslog(LOG_WARNING
, "can't parse %s for port %s", type
, sp
->se_device
);
600 * Allocate a new session descriptor.
603 session_new(session_index
, typ
)
609 if ((typ
->ty_status
& TTY_ON
) == 0 ||
614 s
= calloc(1, sizeof(struct init_session
));
616 s
->se_callback
= session_callback
;
617 s
->se_index
= session_index
;
619 TAILQ_INSERT_TAIL(&sessions
, s
, tqe
);
621 asprintf(&s
->se_device
, "%s%s", _PATH_DEV
, typ
->ty_name
);
623 if (setupargv(s
, typ
) == 0)
628 session_launch(session_t s
)
633 const char *session_type
= NULL
;
634 time_t current_time
= time(NULL
);
636 // Setup the default values;
637 switch (s
->se_flags
& SE_GETTY_LAUNCH
) {
639 if (s
->se_onoption
.path
) {
640 se_cmd
= &s
->se_onoption
;
641 session_type
= "onoption";
646 if (s
->se_onerror
.path
) {
647 se_cmd
= &s
->se_onerror
;
648 session_type
= "onerror";
654 se_cmd
= &s
->se_getty
;
655 session_type
= "getty";
659 /* fork(), not vfork() -- we can't afford to block. */
660 if ((pid
= fork_with_bootstrap_port(launchd_bootstrap_port
)) == -1) {
661 syslog(LOG_ERR
, "can't fork for %s on port %s: %m",
662 session_type
, s
->se_device
);
669 s
->se_started
= time(NULL
);
670 s
->se_flags
&= ~SE_GETTY_LAUNCH
; // clear down getty launch type
671 if (kevent_mod(pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &s
->se_callback
) == -1)
676 if (current_time
> s
->se_started
&&
677 current_time
- s
->se_started
< GETTY_SPACING
) {
678 syslog(LOG_WARNING
, "%s repeating too quickly on port %s, sleeping",
679 session_type
, s
->se_device
);
684 sigprocmask(SIG_SETMASK
, &mask
, NULL
);
686 setpriority(PRIO_PROCESS
, 0, 0);
688 if (strcmp(se_cmd
->argv
[0], "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow"))
689 launchd_SessionCreate(se_cmd
->argv
[0]);
691 execv(se_cmd
->argv
[0], se_cmd
->argv
);
692 stall("can't exec %s '%s' for port %s: %m", session_type
,
693 se_cmd
->argv
[0], s
->se_device
);
698 session_callback(void *obj
, struct kevent
*kev
__attribute__((unused
)))
703 if (s
->se_flags
& SE_SHUTDOWN
) {
711 session_reap(session_t s
)
713 pid_t pr
= s
->se_process
;
717 #ifdef PID1_REAP_ADOPTED_CHILDREN
718 status
= pid1_child_exit_status
;
720 pr
= waitpid(s
->se_process
, &status
, 0);
725 syslog(LOG_ERR
, "waitpid(): %m");
728 syslog(LOG_ERR
, "waitpid() == 0");
731 if (WIFSIGNALED(status
)) {
732 syslog(LOG_WARNING
, "%s port %s exited abnormally: %s",
733 s
->se_getty
.path
, s
->se_device
, strsignal(WTERMSIG(status
)));
734 s
->se_flags
|= SE_ONERROR
;
735 } else if (WEXITSTATUS(status
) == REALLY_EXIT_TO_CONSOLE
) {
736 /* WIFEXITED(status) assumed */
737 s
->se_flags
|= SE_ONOPTION
;
739 s
->se_flags
|= SE_ONERROR
;
742 line
= s
->se_device
+ sizeof(_PATH_DEV
) - 1;
744 logwtmp(line
, "", "");
750 * This is an n-squared algorithm. We hope it isn't run often...
757 int session_index
= 0;
760 devlen
= sizeof(_PATH_DEV
) - 1;
761 while ((typ
= getttyent())) {
764 TAILQ_FOREACH(sp
, &sessions
, tqe
) {
765 if (strcmp(typ
->ty_name
, sp
->se_device
+ devlen
) == 0)
770 session_new(session_index
, typ
);
774 if (sp
->se_index
!= session_index
) {
775 syslog(LOG_INFO
, "port %s changed utmp index from %d to %d",
776 sp
->se_device
, sp
->se_index
,
778 sp
->se_index
= session_index
;
781 if ((typ
->ty_status
& TTY_ON
) == 0 ||
782 typ
->ty_getty
== 0) {
787 sp
->se_flags
&= ~SE_SHUTDOWN
;
789 if (setupargv(sp
, typ
) == 0) {
790 syslog(LOG_WARNING
, "can't parse getty for port %s",
800 * Block further logins.
807 TAILQ_FOREACH(s
, &sessions
, tqe
)
808 s
->se_flags
|= SE_SHUTDOWN
;
812 * Bring the system down to single user.
818 static const int death_sigs
[3] = { SIGHUP
, SIGTERM
, SIGKILL
};
822 single_user_mode
= true;
824 /* NB: should send a message to the session logger to avoid blocking. */
825 logwtmp("~", "shutdown", "");
827 for (i
= 0; i
< 3; ++i
) {
828 if (kill(-1, death_sigs
[i
]) == -1 && errno
== ESRCH
)
830 syslog(LOG_ERR
, "we should be trying to detect a valid clean-up");
834 syslog(LOG_WARNING
, "some processes would not die; ps axl advised");
837 #ifdef PID1_REAP_ADOPTED_CHILDREN
838 __private_extern__
bool init_check_pid(pid_t p
)
843 TAILQ_FOREACH(s
, &sessions
, tqe
) {
844 if (s
->se_process
== p
) {
845 EV_SET(&kev
, p
, EVFILT_PROC
, 0, 0, 0, s
);
846 s
->se_callback(s
, &kev
);
850 if (single_user_pid
== p
) {
851 single_user_callback(NULL
, NULL
);
854 if (runcom_pid
== p
) {
855 runcom_callback(NULL
, NULL
);