Chapter 4. Startup

The most striking difference between Mac OS X and other flavors of Unix is in how Mac OS X handles the boot process. Gone are the /etc/inittab and /etc/init.d from traditional Unix systems. In their place is a BSD-like startup sequence sandwiched between a Mach[2] foundation and the Aqua user interface.

This chapter describes Mac OS X Leopard’s startup sequence, beginning with the boot loader and progressing to full multiuser mode, at which time the system is ready to accept logins from normal users. The chapter also covers custom startup items, network interface configuration, and Mac OS X’s default system maintenance jobs.

Booting Mac OS X

When the computer is powered up, the firmware—Open Firmware on PowerPC Macs and Extensible Firmware Interface on Intel Macs—is in complete control. After the firmware initializes the hardware, it hands off control to the boot loader, BootX (Power PC) or boot.efi (Intel), which bootstraps the kernel. After a trip into Mach, the control bubbles up into the Berkeley Software Distribution (BSD) subsystem, and from there into the Aqua user interface.

By default, Mac OS X boots graphically. If you’d like to see console messages as you boot, hold down ⌘-V (the “V” stands for “verbose”) as you start the computer. If you’d like to always boot in verbose mode, you can specify a flag in the boot arguments that are stored in your system’s firmware. First, use the command nvram boot-args to make sure there aren’t any flags already set (if there are, and you didn’t set them, you probably should not change this setting). Set your boot arguments to -v with this command:

$ sudo nvram boot-args="-v"

The next time you boot your Mac, it will boot in verbose mode. To turn off this setting, use this command:

$ sudo nvram boot-args=

To boot in single-user mode, hold down ⌘-S as you start the computer. In single-user mode your filesystem is mounted as read-only, which limits what you can do. Although you can enable write access to your filesystem via the mount –uw / command, this is not usually recommended. Single-user mode should generally be used only to repair a system that has been damaged. Unlike with other Unix systems, we do not suggest that you use single-user mode to perform fsck repairs manually. Instead, restart your Mac and boot from the Mac OS X install disc (insert the disc and hold down the C key as your Mac starts up), and then run the Disk Utility (Installer→Open Disk Utility) to repair a problem disk volume.

The Boot Loader

The BootX and boot.efi boot loaders are located in /System/Library/CoreServices. They draw the Apple logo on the screen and proceed to set up the kernel environment. The boot loader first looks for an up-to-date version of the kernel that’s been prelinked to all required kernel extensions (drivers, also known as kexts). If it doesn’t find one, the boot loader loads all the kernel extensions that are cached in the mkext cache. If this cache does not exist, the boot loader loads only those extensions in /System/Library/Extensions that have the OSBundleRequired key in their ExtensionName.kext/Info.plist files. Example 4-1 is an excerpt from the /System/Library/Extensions/System.kext/Info.plist file.

Example 4-1. A portion of a kernel extension’s Info.plist file
<?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>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <!-- multiple keys and strings omitted -->
  </dict>
</plist>

After the required drivers are loaded, the boot loader hands off control to the kernel (/mach_kernel).

Initialization

The kernel first initializes all the data structures needed to support Mach and BSD. Next, the kernel initializes the I/O Kit, which connects the kernel with the set of extensions that correspond to the machine’s hardware configuration. The kernel then finds and mounts the root filesystem. Finally, it launches the first process on the system, launchd, which is responsible for bootstrapping the system as well as launching daemons on behalf of the system or users.

Note

Mac OS X Panther (10.3) and earlier does things differently. The first process the kernel loaded was mach_init, which started Mach message handling. mach_init then launched the BSD init process. In keeping with Unix conventions, init was process ID (PID) 1, even though it was started second. mach_init was given PID 2, and its parent PID was set to 1 (init’s PID). Beginning with Mac OS X Tiger (10.4), launchd replaces both of these processes.

By default, launchd starts up SystemStarter, which is used to start programs that aren’t launched on demand: SystemStarter looks in the /System/Library/StartupItems and then the /Library/StartupItems directories to find items to start (see the SystemStarter” section later in this chapter). Although Mac OS X no longer uses any /etc/rc* scripts to start the system, SystemStarter will run any commands in an /etc/rc.local file and an /etc/rc.shutdown.local file at system startup and shutdown, respectively.

After that, launchd starts loginwindow, which authenticates users and sets up their user sessions. From this point on, all remaining services are launched on demand through launchd.

launchd

Mac OS X Tiger introduced the latest and greatest startup scheme, launchd. It has launch-on-demand capabilities and also supports on-demand launching via Mach ports (as does the mach_init.d scheme). launchd additionally offers the ability to launch on demand based on filesystem and Unix domain socket events.

launchd manages two types of services: launch daemons (services that can run even when no user is logged in, such as sshd) and launch agents (services that run on behalf of a logged-in user; for example, when you launch an X11-based application). Launch daemons cannot connect to the window server and thus cannot display a GUI. Launch agents, however, can connect to the window server and can present a GUI. Further, since a launch agent runs on behalf of a user, the agent can access files in that user’s home directory. For example, the OpenSSH server is managed as a launch daemon (see ssh.plist in /System/Library/LaunchDaemons); Spotlight’s menu and results window are managed as a launch agent (see com.apple.Spotlight.plist in /System/Library/LaunchAgents).

The property list (.plist) files for system-installed launch daemons are located in /System/Library/LaunchDaemons. Locally installed daemons (including ones you create yourself) must be installed into /Library/LaunchDaemons. Similarly, system-installed launch agents go into /System/Library/LaunchAgents and locally installed ones go into /Library/LaunchAgents. You can install user-specific launch agents in ~/Library/LaunchAgents.

Note

For an example of a launch daemon property list, see Launching with launchd,” later in this chapter.

You can control launch daemons with the launchctl utility. To enable a daemon that’s disabled (that is, one with a Disabled key in its property list file), use launchctl load -w followed by the path to the property list. For example, the following command would enable the telnet server (the daemon itself is executed as defined in the telnet.plist file):

$ sudo launchctl load -w /System/Library/LaunchDaemons/telnet.plist

You can stop and disable this daemon with unload -w:

$ sudo launchctl unload -w /System/Library/LaunchDaemons/telnet.plist

For more information, see the launchctl manpage and Apple Technical Note 2083, “Daemons and Agents” (http://developer.apple.com/technotes/tn2005/tn2083.html), which has an in-depth explanation of launchd and other facilities for managing background processes.

Note

Peter Borg’s Lingon (http://lingon.sourceforge.net) is an open source graphical tool for creating and editing launchd configuration files.

SystemStarter

SystemStarter examines /System/Library/StartupItems and /Library/StartupItems for applications that should be started at boot time. /Library/StartupItems contains items for locally installed applications. /System/Library/StartupItems contains items for the system. You should not modify these or add your own items here.

Because many of SystemStarter’s responsibilities are now handled by launchd, the number of startup items has dramatically decreased since Mac OS X 10.3. However, some third-party applications continue to add startup items here rather than using the preferred launchd facility.

Mach Bootstrap Services

Mac OS X Panther introduced Mach bootstrap services, which are services that a process can launch using Mach messaging (a messaging facility supported by the Mac OS X kernel). Services can be loaded at two points: at system startup and at user login, which includes local and remote (such as SSH) logins. System startup scripts go into one of the /etc/mach_init*.d directories. Bootstrap service daemons are identified to the system by using the ServiceName key in their .plist files. The operating system can load a bootstrap service on demand if the OnDemand option is set to true (this is the default); it will either launch the service or wake it if it is sleeping (when a bootstrap service goes unused for a period of time, it can sleep).

As of Mac OS X 10.5, few services are started in this fashion; most of the operating system has moved over to launchd.

Creating Programs that Run Automatically

You have two choices for automatically starting applications: you can start them when a user logs in, or start them when the system boots up. On most Unix systems, startup applications reside in either the /etc/rc.local script or the /etc/init.d directory. Under Mac OS 9, you could add a startup item by putting its alias in System Folder/Startup Items. Mac OS X has a different approach, described in the following sections.

Login Preferences

To start an application each time you log in, use the Login Items tab of the System Preferences Accounts panel. This is a good choice for user applications, such as Stickies or an instant messenger program. These preferences are saved in ~/Library/Preferences/loginwindow.plist. There is also a global (or system-wide) counterpart to this file, located at /Library/Preferences/loginwindow.plist. Some third-party applications will stash startup items in the global file, so check there if you can’t otherwise track down the source of a mysterious startup item.

Note

The global loginwindow.plist file is owned by root. To edit it, change its permissions using the Finder (Control-click or right-click, select Get Info, and click the lock to authenticate) or the command line. Then, double-click it to edit it in the Property List Editor, save it, and change the permissions back to their original values.

SystemStarter

If you compile and install a daemon, you’ll probably want it to start at boot time. In most cases, you can start a daemon using launchd. But in some cases, you may want to use the (now deprecated) approach used in Mac OS X 10.3 and earlier: SystemStarter, introduced in the Initialization” section of this chapter. This is because some of the Unix programs that you are likely to find in the wild (or write yourself) do things the old-school Unix way, which will annoy launchd. For example, the launchd.plist(5) manpage specifically warns against using launchd with applications that call daemon (a Unix utility that spawns a program that runs without a user) or act like it (by spawning a subprogram and exiting, for example).

What’s more, launchd would prefer that you don’t do any of the following:

  • Set up the user ID or group ID.

  • Set up the working directory.

  • Invoke chroot(2) or setsid(2).

  • Close “stray” file descriptors.

  • Change stdio(3) to /dev/null.

  • Set up resource limits with setrusage(2).

  • Set up priorities with setpriority(2).

  • Ignore the SIGTERM signal.

Warning

Although launch daemons do not have a facility for invoking an explicit shutdown script, they will be killed by launchd when you shut down/reboot the system or explicitly stop them. Many applications, including database servers such as MySQL, know what to do when they are killed; in the case of MySQL, it shuts down cleanly, logging that fact to the system log with the message “Normal shutdown.”

If you are setting up a daemon that cannot abide by the launchd restrictions, or one that need its hand held by a shutdown script, you should create a startup item, as described in this section. Otherwise, you should use launchd (see the upcoming section Launching with launchd”).

It’s possible to modify many Unix daemons to behave themselves under launchd. If you peruse the Darwin source code at http://www.opensource.apple.com/darwinsource/, you’ll find launchd-specific patches for many of the Unix daemons, such as OpenSSH and cron. For example, Apple’s source code for cron.c contains this little snippet to make everything launchd-safe:

#ifdef __APPLE__
/* Don't daemonize when run by launchd */
  if (getppid() != 1 && daemon(1, 0) == −1) {
#else
  if (daemon(1, 0) == −1) {
#endif

As time goes on, you’ll probably find that popular open source packages will incorporate Apple’s patches into their official code releases.

Consider the MySQL database server. To start it up, you use a program called mysqld_safe, which in turn starts the MySQL database server. However, to shut it down, you issue the command mysqladmin shutdown. If you use launchd to manage starting up and shutting down MySQL, it will kill the MySQL server in a less-than-graceful manner (fortunately, MySQL knows how to handle this, but some other systems may not be as flexible). If, on the other hand, you use a startup item, you can define how the process gets shut down.

A startup item is controlled by three things: a folder (such as /Library/StartupItems/MyItem), a shell script with the same name as the directory (such as MyItem), and a property list named StartupParameters.plist. The shell script and the property list must appear at the top level of the startup item’s folder. You can also create a Resources directory to hold localized resources, but this is not mandatory.

To set up a MySQL startup item, create the directory /Library/StartupItems/MySQL as root. Then, create two files in that directory: the startup script MySQL and the property list StartupParameters.plist. The MySQL file must be an executable because it is a shell script:

$ sudo mkdir /Library/StartupItems/MySQL
$ sudo touch /Library/StartupItems/MySQL/MySQL
$ sudo touch /Library/StartupItems/MySQL/StartupParameters.plist
$ sudo chmod +x /Library/StartupItems/MySQL/MySQL

After you put the right information into these two files (as directed in the following sections), MySQL will be launched at each boot. Use your favorite text-only editor to edit these files and put the information into them. Because the files are owned by root, you will have to authenticate to use them. Smultron and TextMate are two editors that will allow you to authenticate in order to edit root’s files; if you prefer to use vi from the Terminal, you can run it under sudo, as in sudo vi /Library/StartupItems/MySQL/MySQL.

The startup script

The startup script should be a shell script with StartService(), StopService(), and RestartService() functions. The contents of /Library/StartupItems/MySQL/MySQL are shown in Example 4-2. The function call at the bottom of the script invokes the RunService() function from /etc/rc.common (this is a file that is part of Mac OS X), which in turn invokes StartService(), StopService(), or RestartService(), depending on whether the script was invoked with an argument of start, stop, or restart.

Example 4-2. A MySQL startup script
#!/bin/sh

# Source common setup, including hostconfig.
#
. /etc/rc.common

StartService()
{
  # Don't start unless MySQL is enabled in /etc/hostconfig
  if [ "${MYSQL:=-NO-}" = "-YES-" ]; then
    ConsoleMessage "Starting MySQL"
    /usr/local/mysql/bin/mysqld_safe --user=mysql --skip-networking &
  fi
}

StopService()
{
  ConsoleMessage "Stopping MySQL"
  # If you've set a root password within mysql, you may
  # need to add --password=password on the next line.
  /usr/local/mysql/bin/mysqladmin shutdown
}

RestartService()
{
  # Don't restart unless MySQL is enabled in /etc/hostconfig
  if [ "${MYSQL:=-NO-}" = "-YES-" ]; then
    ConsoleMessage "Restarting MySQL"
    StopService
    StartService
  else
    StopService
  fi
}

RunService "$1"

Because it consults the settings of the $MYSQL environment variable, the startup script won’t do anything unless you’ve enabled MySQL in the /etc/hostconfig file. To do this, edit /etc/hostconfig in a text editor, and add this line:

MYSQL=-YES-

Note

Mac OS X does not recognize any special connections between hostconfig entries and startup scripts. Instead, the startup script sources the /etc/rc.common file, which in turn sources hostconfig. The directives in hostconfig are merely environment variables, and the startup script checks the values of the variables that control its behavior (in this case, $MYSQL).

The property list

The property list (StartupParameters.plist) contains attributes that describe the item and determine its place in the startup sequence. It can be in XML or NeXT format. The NeXT format uses NeXTSTEP-style property lists, as shown in Example 4-3.

Example 4-3. The MySQL startup parameters as a NeXT property list
{
  Description = "MySQL";
  Provides = ("MySQL");
  Requires = ("Network");
  OrderPreference = "Late";
}

The XML format adheres to the PropertyList.dtd Document Type Definition (DTD). You can use your favorite text editor or the Property List Editor (/Developer/Applications/Utilities) to create your own XML property list, as shown in Example 4-4.

Example 4-4. The MySQL startup parameters as an XML property list
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist
          SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
  <dict>
    <key>Description</key>
    <string>MySQL</string>
    <key>Provides</key>
    <array>
      <string>MySQL</string>
    </array>
    <key>Requires</key>
    <array>
      <string>Network</string>
    </array>
    <key>OrderPreference</key>
    <string>Late</string>
  </dict>
</plist>

The following list describes the various keys you can use in a startup parameters property list:

Description

This is a phrase that describes the item.

Provides

This is an array of services that the item provides (e.g., Apache provides “Web Server”). These services should be globally unique. In the event that SystemStarter finds two items that provide the same service, it starts the first one it finds.

Requires

This is an array of services on which the item depends. It should correspond to another item’s Provides attribute. If a required service cannot be started, the system won’t start the item.

Uses

This is similar to Requires, but it is a weaker association. If SystemStarter can find a matching service, it will start it. If it can’t, the dependent item will still start.

OrderPreference

The Requires and Uses attributes imply a particular order, in that dependent items will be started after the services on which they depend. You can specify First, Early, None (the default), Late, or Last here. SystemStarter does its best to satisfy this preference, but dependency orders prevail.

You can now manually start, restart, and stop MySQL by invoking SystemStarter from the command line:

$ sudo SystemStarter start MySQL
$ sudo SystemStarter restart MySQL
$ sudo SystemStarter stop MySQL

Launching with launchd

Creating a launchd startup item (a launch agent or launch daemon) is more declarative than procedural. Instead of writing scripts that directly control your daemon, you create an XML .plist file with as much information as you can possibly provide; this tells Mac OS X how it should handle starting the server.

You can use a launch daemon to start up MySQL, in fact. You lose the ability to specify that mysqladmin shutdown be run when you are terminating MySQL, but MySQL can shut down gracefully even when launchd kills it outright. Here’s a modified version of the MySQL startup script that ships with the Mac OS X Leopard server. Save it in /Library/LaunchDaemons/org.mysql.mysqld.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
          "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>org.mysql.mysqld</string>
    <key>OnDemand</key>
    <false/>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/local/mysql/bin/mysqld</string>
      <string>--user=mysql</string>
      <string>--skip-networking</string>
    </array>
    <key>ServiceIPC</key>
    <false/>
  </dict>
</plist>

The first key/string pair defines the label that identifies this daemon (org.mysql.mysqld). This can be used with some launchctl(1) commands. The second pair (OnDemand: false) indicates that mysqld is not an on-demand daemon: it should be started as soon as possible and kept running until it is unloaded (either explicitly or at system shutdown). The ProgramArguments key simply specifies the command line used to launch the program, and ServiceIPC: false indicates that mysqld is unable to communicate with launchd using interprocess communication. After you create this file, you can load it and enable it with this command:

$ sudo launchctl load -w /Library/LaunchDaemons/org.mysql.mysqld.plist

Since this is not an OnDemand daemon, it’s started immediately. To unload it (and shut it down), use:

$ sudo launchctl unload -w /Library/LaunchDaemons/org.mysql.mysqld.plist

Note

Note that we launch mysqld here, rather than starting MySQL with mysqld_safe, as we did with the startup item. This is because upon shutdown, launchd will try to kill the program it started; however, it won’t be able to kill mysqld_safe, because it stays around until mysqld dies. In other words, mysqladmin shutdown knows exactly what to kill, but launchd doesn’t.

For more information on launching with launchd, see the launchd.plist(5) manpage.

Periodic Jobs

Like other flavors of Unix, Mac OS X supports cron to schedule tasks for periodic execution. Each user’s cron jobs are controlled by configuration files that you can edit with crontab -e. (To list the contents of the file, use crontab -l.) Beginning with Mac OS X Tiger, the global crontab (/etc/crontab) has been replaced with three launch daemons. The original crontab looked like this:

15 3 * * *       root    periodic daily
30 4 * * 6       root    periodic weekly
30 5 1 * *       root    periodic monthly

But now, each line has been replaced by a file in /System/Library/LaunchDaemons (com.apple.periodic-daily.plist, com.apple.periodic-weekly.plist, and com.apple.periodic-monthly.plist) that uses the StartCalendar key to specify when it is to be run. For example, here is the com.apple.periodic-daily.plist file:

<?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.apple.periodic-daily</string>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/sbin/periodic</string>
      <string>daily</string>
    </array>
    <key>LowPriorityIO</key>
    <true/>
    <key>Nice</key>
    <integer>1</integer>
    <key>StartCalendarInterval</key>
    <dict>
      <key>Hour</key>
      <integer>3</integer>
      <key>Minute</key>
      <integer>15</integer>
    </dict>
  </dict>
</plist>

Note

These .plists launch the periodic jobs in the wee hours of the morning, but launchd does not skip jobs even if your computer is shut off: the next time the computer wakes up or boots up, the missed jobs will be run.

These three launch daemons run the scripts contained in subdirectories of the /etc/periodic directory: /etc/periodic/daily, /etc/periodic/weekly, and /etc/periodic/monthly. Each of these directories contains one or more scripts:

/etc/periodic/daily/100.clean-logs
/etc/periodic/daily/110.clean-tmps
/etc/periodic/daily/130.clean-msgs
/etc/periodic/daily/430.status-rwho
/etc/periodic/daily/500.daily
/etc/periodic/monthly/200.accounting
/etc/periodic/monthly/500.monthly
/etc/periodic/monthly/999.local
/etc/periodic/weekly/310.locate
/etc/periodic/weekly/320.whatis
/etc/periodic/weekly/999.local

You should not modify these files, because they may be replaced by future system updates. Instead, create an /etc/daily.local, /etc/weekly.local, or /etc/monthly.local file to hold your site-specific cron jobs. The cron jobs are simply shell scripts that contain commands to be run as root. The local cron jobs are invoked at the end of the 500.daily, 999.weekly, and 999.monthly scripts found in the /etc/periodic subdirectory. Within a directory, the files with lower numbers in their names execute before scripts with higher numbers.



[2] Mach is a microkernel operating system developed at Carnegie Mellon University. The Mac OS X kernel, xnu, is a hybrid of Mach and BSD.

Get Mac OS X For Unix Geeks, 4th Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.