5.10. User-Level Startup As Section 5.8 described, user-level startup is initiated when the kernel executes /sbin/launchd as the first user process. We will now look at the implementation and operation of launchd. 5.10.1. launchd launchd is the master bootstrap daemon beginning with Mac OS X 10.4. It subsumes the functionality of the traditional init program and the erstwhile Mac OS X mach_init program. The following are notable features of launchd. It manages both system-wide daemons and per-user agents. An agent is a type of daemon that runs while a user is logged in. Unless the distinction is necessary, we will use the term daemons in this discussion to refer to both daemons and agents. As the first user process, it performs user-level system bootstrap. It handles both single-user and multiuser booting modes. In a multiuser boot, it runs the traditional BSD-style command script (/etc/rc) and sets up daemons whose configuration files are located in designated directories such as /System/Library/LaunchDaemons/, /Library/LaunchDaemons/, /System/Library/LaunchAgents/, /Library/LaunchAgents/, and ~/Library/LaunchAgents/. It supports daemons that are designed to run under the inetd superserver on Unix systems. It can run jobs periodically. A launchd job is an abstraction that consists of a runnable entity (a program) along with the entity's configuration. It allows several aspects of a daemon to be configured through a property list file, rather than the daemon having to programmatically configure itself. It can start daemons on demand, based on a variety of conditions. launchd simplifies the configuration, management, and, in many cases, even creation of daemons. 5.10.1.1. Daemon Configuration and Management launchd provides a set of predefined keys that can be used in a daemon's property list file to specify various runtime aspects of the daemon. The following are examples of such aspects: User and group names (or identifiers) Root and working directories Umask value Environment variables Standard error and standard output redirections Soft and hard resource limits Scheduling priority alterations I/O priority alterations An important ability of launchd is that it can launch daemons when they are needed, rather than having "always on" processes. Such on-demand launching can be based on criteria such as the following: A given periodic interval An incoming connection request on a given TCP port number An incoming connection request on a given AF_UNIX path Modification of a given file system path Appearance or modification of file system entities in a given queue directory The launchd configuration file for a daemon is an XML property list file. Let us look at some examples. Figure 523 shows the configuration file for the SSH daemon. Figure 523. A launchd configuration file $ ls -1 /System/Library/LaunchDaemons bootps.plist com.apple.KernelEventAgent.plist com.apple.atrun.plist com.apple.mDNSResponder.plist ... ssh.plist swat.plist telnet.plist tftp.plist $ cat /System/Library/LaunchDaemons/ssh.plist ... <plist version="1.0"> <dict> <key>Label</key> <string>com.openssh.sshd</string> <key>Program</key> <string>/usr/libexec/sshd-keygen-wrapper</string> <key>ProgramArguments</key> <array> <string>/usr/sbin/sshd</string> <string>-i</string> </array> <key>Sockets</key> <dict> <key>Listeners</key> <dict> <key>SockServiceName</key> <string>ssh</string> <key>Bonjour</key> <array> <string>ssh</string> <string>sftp-ssh</string> </array> </dict> </dict> <key>inetdCompatibility</key> <dict> <key>Wait</key> <false/> </dict> <key>SessionCreate</key> <true/> <key>StandardErrorPath</key> <string>/dev/null</string> </dict> </plist> | The meanings of keys shown in Figure 523 are as follows. The Label key uniquely identifies the job to launchd. This key is mandatory. The Program key is used as the first argument of execvp() by launchd. The ProgramArguments key is used as the second argument of execvp() by launchd. Note that if the Program key is absent, the first element of the ProgramArguments key's array value is used instead. The Sockets key specifies launch-on-demand sockets that allow launchd to determine when to run the job. The SockServiceName key specifies the service name that can be used by the getaddrinfo(3) function to determine the well-known port for this service. The Bonjour key requests registration of the service with the mDNSResponder program. Its value is either a list of names to advertise or a Boolean, in which case the name to advertise is inferred from SockServiceName. The inetdCompatibility key specifies that the daemon expects to run under inetd, and an appropriate compatibility environment should be provided by launchd. The Wait Boolean key specifies the wait or nowait options of inetd. The SessionCreate Boolean key, if set to TRue, causes launchd to use the dlopen interface to call the SessionCreate() function from the Security framework (/System/Library/Frameworks/Security.framework). SessionCreate() creates a security session, wherein a new bootstrap subset port[39] is created for the calling process. [39] We will look at bootstrap ports in Chapter 9.
The StandardErrorPath key causes launchd to open the specified path and duplicate the resultant descriptor to the standard error descriptor. Consider another examplethat of the cron daemon. Its launchd configuration file (com.vix.cron.plist) specifies that /usr/sbin/cron is to be run whenever either the /etc/crontab file or the /var/cron/tabs/ directory is modified. For example, creating a crontab file in /var/cron/tabs/ will cause launchd to run cron. $ cat /System/Library/LaunchDaemons/com.vix.cron.plist ... <dict> <key>Label</key> <string>com.vix.cron</string> <key>ProgramArguments</key> <array> <string>/usr/sbin/cron</string> </array> <key>RunAtLoad</key> <true/> <key>WatchPaths</key> <array> <string>/etc/crontab</string> </array> <key>QueueDirectories</key> <array> <string>/var/cron/tabs</string> </array> </dict> ... 5.10.1.2. Daemon Creation Figure 524 shows an example of creating a trivial launchd job that runs every 10 seconds. The job uses the logger command-line program to write a "hello" message to the system log. Figure 524. Creating a periodic launchd job [View full width] $ whoami amit $ launchctl list $ sudo launchctl list com.apple.KernelEventAgent ... com.apple.ftpd com.openssh.sshd $ cat com.osxbook.periodic.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/ PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.osxbook.periodic</string> <key>ProgramArguments</key> <array> <string>/usr/bin/logger</string> <string>-p</string> <string>crit</string> <string>hello</string> </array> <key>StartInterval</key> <integer>10</integer> </dict> </plist> $ launchctl load com.osxbook.periodic.plist $ launchctl list com.osxbook.periodic $ tail -f /var/log/system.log Jul 4 13:43:15 g5x2 amit: hello Jul 4 13:43:25 g5x2 amit: hello Jul 4 13:43:35 g5x2 amit: hello ^c $ launchctl unload com.osxbook.periodic.plist | Let us also look at how launchd simplifies the programmatic creation of a daemon. Since launchd handles several aspects of a daemon's operation, certain guidelines must be followed while writing a launchd-compliant daemon. Figure 525 shows some examples of relevant guidelines and caveats. Figure 525. Guidelines and caveats for creating launchd-compliant daemons We will now create a trivial network daemon called dummyd, which echos back lines of text sent to it by a client. We can avoid writing any network code by using launchd's inetd-compatibility mode. However, we will take a somewhat longer route in order to demonstrate how a daemon participates in advanced communication with launchd: We will arrange for launchd to provide our daemon a socket file descriptor to call accept() on when there is an incoming client connection. In dummyd's launchd configuration file, we will also specify a variety of settings and then verify from within dummyd that they were set as expected by launchd. Our implementation of dummyd will perform the following primary actions. Install a handler for SIGTERM. Display the settings that we requested launchd to set. We will print the settings on the standard error, which, as specified in dummyd's configuration file, will be sent to a custom log file. Check in with launchd using the launch(3) interface. Use the kqueue(2) mechanism[40] to arrange to be notified about incoming connections. [40] We will discuss the kqueue(2) mechanism in Chapter 9.
Enter a loop that accepts an incoming connection and creates a thread to process the connection. The processing involves reading a newline-terminated string from the client and writing it back to the client. Figure 526 shows the code for dummyd. Figure 526. A trivial echo server called dummyd // dummyd.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/event.h> #include <launch.h> #include <pthread.h> #define MY_LAUNCH_JOBKEY_LISTENERS "Listeners" // error-handling convenience #define DO_RETURN(retval, fmt, ...) { \ fprintf(stderr, fmt, ## __VA_ARGS__); \ return retval; \ } int SIGTERM_handler(int s) { fprintf(stderr, "SIGTERM handled\n"); // primitive SIGTERM handler exit(s); } ssize_t readline(int fd, void *buffer, size_t maxlen) { ssize_t n, bytesread; char c, *bp = buffer; for (n = 1; n < maxlen; n++) { bytesread = read(fd, &c, 1); if (bytesread == 1) { *bp++ = c; if (c == '\n') break; } else if (bytesread == 0) { if (n == 1) return 0; break; } else { if (errno == EINTR) continue; return -1; } } *bp = 0; return n; } void * daemon_loop(void *fd) { ssize_t ret; char buf[512]; for (;;) { // a simple echo loop if ((ret = readline((int)fd, buf, 512)) > 0) write((int)fd, buf, ret); else { close((int)fd); return (void *)0; } } } int main(void) { char path[MAXPATHLEN + 1]; char *val; int fd, kq; size_t i; pthread_t thread; struct kevent kev_init, kev_listener; struct sockaddr_storage ss; socklen_t slen; launch_data_t checkin_response, checkin_request; launch_data_t sockets_dict, listening_fd_array; setbuf(stderr, NULL); // make stderr unbuffered // launchd will send us a SIGTERM while terminating signal(SIGTERM, (sig_t)SIGTERM_handler); // print our cwd: our configuration file specified this if (getcwd(path, MAXPATHLEN)) fprintf(stderr, "Working directory: %s\n", path); // print $DUMMY_VARIABLE: our configuration file specified this fprintf(stderr, "Special enivronment variables: "); if ((val = getenv("DUMMY_VARIABLE"))) fprintf(stderr, "DUMMY_VARIABLE=%s\n", val); if ((kq = kqueue()) == -1) // create a kernel event queue for notification DO_RETURN(EXIT_FAILURE, "kqueue() failed\n"); // prepare to check in with launchd checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN); if (checkin_request == NULL) DO_RETURN(EXIT_FAILURE, "launch_data_new_string(%s) failed (errno = %d)" "\n", LAUNCH_KEY_CHECKIN, errno); checkin_response = launch_msg(checkin_request); // check in with launchd if (checkin_response == NULL) DO_RETURN(EXIT_FAILURE, "launch_msg(%s) failed (errno = %d)\n", LAUNCH_KEY_CHECKIN, errno); if (launch_data_get_type(checkin_response) == LAUNCH_DATA_ERRNO) DO_RETURN(EXIT_FAILURE, "failed to check in with launchd (errno = %d)" "\n", launch_data_get_errno(checkin_response)); // retrieve the contents of the <Sockets> dictionary sockets_dict = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_SOCKETS); if (sockets_dict == NULL) DO_RETURN(EXIT_FAILURE, "no sockets\n"); // retrieve the value of the MY_LAUNCH_JOBKEY_LISTENERS key listening_fd_array = launch_data_dict_lookup(sockets_dict, MY_LAUNCH_JOBKEY_LISTENERS); if (listening_fd_array == NULL) DO_RETURN(EXIT_FAILURE, "no listening socket descriptors\n"); for (i = 0; i < launch_data_array_get_count(listening_fd_array); i++) { launch_data_t fd_i = launch_data_array_get_index(listening_fd_array, i); EV_SET(&kev_init, // the structure to populate launch_data_get_fd(fd_i), // identifier for this event EVFILT_READ, // return on incoming connection EV_ADD, // flags: add the event to the kqueue 0, // filter-specific flags (none) 0, // filter-specific data (none) NULL); // opaque user-defined value (none) if (kevent(kq, // the kernel queue &kev_init, // changelist 1, // nchanges NULL, // eventlist 0, // nevents NULL) == -1) // timeout DO_RETURN(EXIT_FAILURE, "kevent(/* register */) failed\n"); } launch_data_free(checkin_response); while (1) { if ((fd = kevent(kq, NULL, 0, &kev_listener, 1, NULL)) == -1) DO_RETURN(EXIT_FAILURE, "kevent(/* get events */) failed\n"); if (fd == 0) return EXIT_SUCCESS; slen = sizeof(ss); fd = accept(kev_listener.ident, (struct sockaddr *)&ss, &slen); if (fd == -1) continue; if (pthread_create(&thread, (pthread_attr_t *)0, daemon_loop, (void *)fd) != 0) { close(fd); DO_RETURN(EXIT_FAILURE, "pthread_create() failed\n"); } pthread_detach(thread); } return EXIT_SUCCESS; } | The launchd configuration file for dummyd is shown in Figure 527. Note that we specify to launchd that dummyd be run as an inetd-based server listening on TCP port 12345. Moreover, if we need to have IPC communication with launchd, the ServiceIPC Boolean key must be set to TRue. Figure 527. The contents of the com.osxbook.dummyd.plist configuration file [View full width] <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/ PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.osxbook.dummyd</string> <key>ProgramArguments</key> <array> <string>/tmp/dummyd</string> <string>Dummy Daemon</string> </array> <key>OnDemand</key> <true/> <key>WorkingDirectory</key> <string>/tmp</string> <key>EnvironmentVariables</key> <dict> <key>DUMMY_VARIABLE</key> <string>dummyvalue</string> </dict> <key>ServiceIPC</key> <true/> <key>StandardErrorPath</key> <string>/tmp/dummyd.log</string> <key>Sockets</key> <dict> <key>Listeners</key> <dict> <key>Socktype</key> <string>stream</string> <key>SockFamily</key> <string>IPv4</string> <key>SockProtocol</key> <string>TCP</string> <key>SockServiceName</key> <string>12345</string> </dict> </dict> </dict> </plist> | Let us now test dummyd by loading its configuration into launchd using the launchctl command. $ gcc -Wall -o /tmp/dummyd dummyd.c $ launchctl load com.osxbook.dummyd.plist $ launchctl list com.osxbook.dummyd $ ls /tmp/dummyd.log ls: /tmp/dummyd.log: No such file or directory $ ps -axw | grep dummyd | grep -v grep $ netstat -na | grep 12345 tcp4 0 0 *.12345 *.* LISTEN $ telnet 127.0.0.1 12345 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello hello world world ^] telnet> quit Connection closed. $ cat /tmp/dummyd.log Working directory: /private/tmp Special enivronment variables: DUMMY_VARIABLE=dummyvalue $ launchctl unload com.osxbook.dummyd.plist $ 5.10.1.3. launchd Operation Figure 528 shows the high-level operation of launchd, whose own initialization consists of the following primary operations. It creates kernel event queues (kqueues) and registers callbacks with them for various types of events. Examples of callbacks are kqasync_callback(), kqsignal_callback(), and kqfs_callback(), which are for EVFILT_READ, EVFILT_SIGNAL, and EVFILT_FS events, respectively. It initializes several data structures, especially in the conceive_firstborn() internal function. It loads the /etc/launchd.conf configuration file, if it exists. When running in the context of a user, it also looks for the ~/.launchd.conf per-user configuration file. It runs subcommands contained in these files using the launchctl command. It eventually goes into a server loop in which it receives and processes events. The first time the server loop runs in a given contextfor example, during system startupthe init_pre_kevent() function is called to perform critical initialization such as single-user system startup, session creation, and the normal multiuser system startup. Figure 528. A high-level depiction of launchd's operation 5.10.2. Multiuser Startup In a multiuser startup, launchd runs the command script contained in /etc/rc, which in turn follows different execution paths depending on the kind of booting being performed: whether it is a normal boot, whether it is a network boot, or whether the system is booting from a CD-ROM for installation purposes. Figures 529. and 530 show the chain of important events that occur during a local or network-based multiuser boot. Note that in the case of a nonverbose (graphical) boot, /etc/rc eventually runs the /usr/libexec/WaitingForLoginWindow program. This program displays the "Starting Mac OS X . . ." panel with a progress bar. The latter is a dummy progress bar whose rate of progress is based on the contents of the file /var/db/loginwindow.boottime. The file is updated by WaitingForLoginWindow upon every boot, so that upon the next boot WaitingForLoginWindow can use the saved time duration. This way, the program attempts to match the displayed rate of progress with the actual time taken to boot. WaitingForLoginWindow exits when it receives a notification that the loginwindow program (/System/Library/CoreServices/loginwindow.app) is ready to display the login panel. loginwindow is run by launchd too as part of its session launching, which is shown in Figure 531. Figure 529. The sequence of operations performed by /etc/rc Figure 530. The sequence of operations performed by /etc/rc (continued from Figure 529) Figure 531. Overview of session launching by launchd launchd maintains a global list of sessions. As shown in Figure 532, the init_pre_kevent() function launches these sessions. The session list is populated by update_ttys(), which calls getttyent(3) to read entries from /etc/ttys. After /etc/rc exits successfully, update_ttys() is called by runcom_callback(). update_ttys() can also be triggered by sending a hangup (HUP) signal to launchd. Figure 532. Implementation of session creation and launching by launchd // launchd/src/init.c void init_pre_kevent(void) { session_t s; if (single_user_mode && single_user_pid == 0) single_user(); if (run_runcom) runcom(); if (!single_user_mode && !run_runcom && runcom_pid == 0) { ... // Go through the global list of sessions TAILQ_FOREACH(s, &sessions, tqe) { if (s->se_process == 0) session_launch(s); } } } ... static void runcom_callback(...) { ... if (/* /etc/rc exited successfully */) { logwtmp("~", "reboot", ""); update_ttys(); return; } else ... ... } static void session_launch(session_t s) { ... } ... void update_ttys(void) { session_t sp; struct ttyent *ttyp; int session_index = 0; ... while ((ttyp = getttyent())) { ++session_index; // Check all existing sessions to see if ttyp->ty_name // matches any session's device // If no session matches, create new session by calling // session_new() // session_new() adds the session to the global list // of sessions ... } ... } | 5.10.2.1. User Login The login panel displayed by the loginwindow program contains fields for the user to provide login information, which is then used by loginwindow to authenticate the user. The following points are noteworthy about the graphical login provided by loginwindow. You can switch to a text-based login prompt at the console by providing >console as the username, which results in /usr/libexec/getty being run to handle user login. In this case, upon a successful login, the user's shell will be a child of the login process (/usr/bin/login). Similarly, you can cause the system to sleep, restart, or shut down by providing >sleep, >restart, or >shutdown, respectively, as the username. You can configure the system so that a designated user is automatically logged in after system startup, bypassing the loginwindow prompt. This is achieved by saving that user's password in the /etc/kcpassword file, which stores the password in an obfuscated format. The obfuscation scheme uses a position-based mapping of charactersthat is, the ASCII value of a given character in a password is statically mapped to different values based on the character's position in the password string. During software installation, loginwindow can be bypassed after system startup because the installer program is launched automatically. Figure 533 shows the important steps performed by loginwindow. Note that an authenticated user session encapsulates the user's processes, which are typically the children of either the loginwindow or the WindowServer processes. The operating context, or scope, of these processes is different from that of system processes that start before the user logs insuch processes (typically daemons) are started by launchd in the root context. Consequently, they are available to all user sessions. In contrast, an agent, although similar to a daemon in that it too is a background program, runs in the context of a user session. Therefore, in general, daemons are systemwide and agents are user-specific. Moreover, since the WindowServer process runs in a user context, daemons cannot draw graphical user interfaces, whereas agents can. Figure 533. Important steps performed by the loginwindow application To Each Its Own When multiple users are logged in simultaneously through the fast user-switching feature, each user gets an individual graphical loginthat is, there is a per-user loginwindow process, along with the associated processes that loginwindow creates. For example, each login has its own pasteboard server, Finder, and Dock. A remote loginsay, through SSHdoes not launch loginwindow. It involves a traditional Unix-style invocation of the login program (/usr/bin/login), which results in a separate session for that login. However, a remotely logged-in user can communicate with the window server from outside of the console session, provided the user ID is that of either the superuser or the active console user. This enables a remotely logged-in user to launch graphical applicationsfor example, using the open command-line program. |
The Mac OS X login mechanism supports running a custom script with superuser privileges when a user logs in. This scripta login hookis executed by loginwindow. It is enabled on a system-wide basis, but it receives the short name of the logging-in user as its first argument. A login hook can be registered either by editing the /etc/ttys file or by setting a property for loginwindow in the Mac OS X defaults system. In the former case, the line containing the path to loginwindow in /etc/ttys is modified to include the -LoginHook argument, whose value is the login hook's path. # /etc/ttys ... # Before login hook: # # console "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" vt100 on secure onoption="/usr/libexec/getty std.9600" # # After login hook: console "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow -LoginHook /path/to/login/hook/script" vt100 on secure onoption="/usr/libexec/getty std.9600" ... Alternatively, the defaults command can be used to set the LoginHook property for loginwindow. $ sudo defaults write com.apple.loginwindow LoginHook /path/to/login/hook/script 5.10.2.2. User Logout, System Restart, and System Shutdown Figure 534 shows how loginwindow handles the procedures for logging out, restarting, or shutting down the system. Selecting an action from the Apple menu causes the foreground process to send the appropriate Apple Event to loginwindow. An application can also send these events programmatically. For example, the following AppleScript code fragment sends the kAELogOut event to loginwindow: tell application "loginwindow" «event aevtlogo» end tell Figure 534. The handling of user logout, system restart, and system shutdown by loginwindow With reference to Figure 534, when loginwindow sends a kAEQuitApplication to a Cocoa application, the event is not seen as is by the application. Instead, the Application Kit framework calls the applicationShouldTerminate: delegate method of the application. If the application wishes to cancel the termination sequence, it must implement this delegate and return NSTerminateCancel from it. In a graceful termination sequence, loginwindow displays a dialog requesting confirmation from the user. Typically, the dialog has a two-minute countdown timer, after which loginwindow proceeds with the termination sequence. Note that when system shutdown is initiated, launchd will stop each job by sending it a SIGTERM signal. Moreover, launchd sets the SE_SHUTDOWN bit in each session's "flags" variable, which prevents the session from being restarted. Further user logins are also disabled. 5.10.3. Single-User Startup Figure 535 shows the sequence of events that occurs during single-user startup. launchd skips running /etc/rc and creating any sessions, and simply runs the shell defined by the _PATH_BSHELL macro in <paths.h>. Note that a single-user boot implies verbose mode. It is also possible to boot the system and stay in single-user mode by manually executing /etc/rc from the single-user shell prompt. Figure 535. Single-user bootstrap through launchd 5.10.4. Installation Startup An installation boot is triggered when /etc/rc detects the presence of both the /etc/rc.cdrom file and the /System/Installation/ directory. Neither of these two exists on an installed Mac OS X system. The contents of /System/Installation/ on an installation CD-ROM include the following. CDIS/Installation Log.app is an installation log viewer that can show detailed installation progress. It provides options such as Show Errors Only, Show Errors and Progress, and Show Everything. CDIS/LCA.app is the Language Chooser Application. It also contains support for Bluetooth device discovery and setup, along with instructions for setting up Bluetooth peripherals before commencing installation. CDIS/instlogd is a daemon that maintains an external log buffer, which is shared with the installation log viewer. It listens on a local socket on the 127.0.0.1 address. CDIS/preheat.sh is a script thatif presentcan be run to reduce CD-ROM booting time. Packages/ contains various software packagesthat is, "pkg" files. Important operations performed by /etc/rc.cdrom include the following. It disables on-the-fly prebinding for the duration of the CD-ROM boot by setting the DYLD_NO_FIX_PREBINDING environment variable to 1. This prevents dyld from notifying the prebinding agent if an executable could not be launched using its prebinding information for some reason. It sanity-checks the system date and time. The date is explicitly set to April 1, 1976, if it is found to be "less" than that date. It sets the value of the kern.maxvnodes sysctl variable to 2500. It runs the /System/Installation/CDIS/preheat.sh preheat script if it exists. It runs kextd with the -j option, which causes kextd not to jettison the kernel linker. Consequently, kextd loads native drivers in the kernel and exits, allowing the kernel to continue handling all load requests. Using this option along with an appropriate mkext cache improves startup time from a CD-ROM. It brings the loopback interface up with an address of 127.0.0.1 and a netmask of 255.0.0.0. This allows local NetInfo communication. It creates a 512KB RAM disk using the hdik program for in-kernel disk image mounting. This RAM disk is used for /Volumes/. dev=`hdik -drivekey system-image=yes -nomount ram://1024` # 512KB if [ $? -eq 0 ] ; then newfs $dev mount -o union -o nobrowse $dev /Volumes fi It checks for the presence of the Installation Log application. If present, the application is used as a graphical crash catcher, and the CatchExit environment variable is set to GUI. If the application is absent, the variable is set to CLI. It creates a 128KB RAM disk for use by securityd. dev=`hdik -drivekey system-image=yes -nomount ram://256` # 128KB newfs $dev mount -o union -o nobrowse $dev /var/tmp mkdir -m 1777 /var/tmp/mds It creates a 128KB RAM disk that is mounted on /var/run/. The system log daemon (syslogd) needs it to create the /var/run/syslog pipe. It starts the external log buffer daemon (instlogd), the system log daemon (syslogd), and the NetInfo binder daemon (nibindd). It logs the system boot time to the system log for potential debugging or profiling of the installation process. /usr/sbin/sysctl kern.boottime | head -1 | /usr/bin/logger -p install.debug -t "" It calls /usr/libexec/register_mach_bootstrap_servers to start services whose configuration files are located in /etc/mach_init.d/. It calls /usr/bin/pmset to disable sleeping of the display, the disk, and the system. It also prevents the machine from sleeping upon pressing of the power button. Moreover, it tells the power management subsystem to reduce processor speed if necessary. It starts the Crash Reporter daemon (/usr/libexec/crashreporterd). If the CatchExit environment variable is set to CLI, it creates a 1MB RAM disk, creates a file system on it, and mounts it on /Library/Logs/. It runs /etc/rc.cdrom.local if it exists. Deprecated Ways to Bootstrap Servers In Mac OS X 10.4, not all boot-time daemons have been migrated to launchd. Therefore, the system continues to support multiple boot-time daemon startup mechanisms. The /etc/mach_init.d/ and /etc/mach_init_per_user.d/ directories contain property list files for system-wide and per-user daemons, respectively. These daemons can be launched using a bootstrapping mechanism similar to launchd. The mechanism is deprecated beginning with Mac OS X 10.4. In this mechanism, the /usr/libexec/register_mach_bootstrap_servers program parses the daemons' property list files and makes RPC calls to either launchd or mach_init (on Mac OS X 10.3) to create the corresponding services. In particular, the mechanism supports on-demand launching of daemons. The SystemStarter program (/sbin/SystemStarter) handles daemons whose property list files are located in /System/Library/StartupItems/ (system-provided startup items) and /Library/StartupItems/ (user-installed startup items). As we saw in Figure 530, launchd executes the /etc/rc startup script, which calls upon all supported daemon startup mechanisms. |
It starts Apple Type Services by running StartATSServer, which resides in the Support/ subdirectory of the ATS subframework within the Application Services umbrella framework. It starts the pasteboard server (/System/Library/CoreServices/pbs). Finally, /etc/rc.cdrom prepares to launch the installer application (/Application/Utilities/Installer.app) with the appropriate arguments, depending on whether it is an automated installation (the /etc/minstallconfig.xml file exists), a custom installationsay, of an application (the /etc/rc.cdrom.packagePath file exists)or a typical operating system installation. In the latter case, installation begins with the /System/Installation/Packages/OSInstall.mpkg metapackage, which includes BaseSystem.pkg, Essentials.pkg, BSD.pkg, and others as its contents. It also contains the configuration migratory program (ConfMigrator) as a resource. Unless it is an automated installation, the installer program is run through LCA, which displays the installation progress bar. # /etc/rc.cdrom ... LAUNCH=/System/Installation/CDIS/LCA.app/Contents/MacOS/LCA if [ ! -x ${LAUNCH} ]; then LAUNCH=/System/Installation/CDIS/splash fi INSTALLER=/Applications/Utilities/Installer.app/Contents/MacOS/Installer STDARGS="-ExternalLog YES -NSDisabledCharacterPaletteMenuItem YES" EXTRAARGS=`cat /System/Installation/CDIS/AdditionalInstallerArgs 2>/dev/null` ... ${LAUNCH} ${INSTALLER} \ -ReadVerifyMedia YES \ ${STDARGS} \ ${EXTRAARGS} \ /System/Installation/Packages/OSInstall.mpkg \ 2>&1 | /usr/bin/logger -t "" -p install.warn |