MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Start Me Up: Writing and Understanding OS X StartupItems

by Andrew Anderson
10/21/2003

There's no denying that Mac OS X is popular among software developers and system administrators. These hard-core users are not the type of people generally associated with the Macintosh because they usually care more about features than user interfaces. What often lures them to OS X is the ease of both system administration and development tasks. On most platforms setting up programs and services that run during the startup process is often frustrating and confusing. Mac OS X, on the other hand, gives the developer or system administrator a precise and simple mechanism for handling the running of programs and services during the startup process.

In this article I will give an overview of how StartupItems fit into the OS X startup process, what you need to do to create a Startup Item, and provide an example of a StartupItem that will run the Apache project's Tomcat Java servlet engine. As you will see, StartupItems are easy to set up and extremely flexible in managing the startup process.

What are StartupItems?

Related Reading

Mac OS X: The Missing Manual, Panther Edition
By David Pogue

StartupItems, as their name implies, are programs that run during OS X's startup process. These programs can terminate after being run (think of emptying the trash on startup) or can be started and run in the background (like running the Apache web server). Though this may seem like a pretty simple service to provide, StartupItems offer several powerful features that differentiate them from similar features in OS X and similar mechanisms in other operating systems. The most powerful of these features are dependency ordering and dependency requirements.

Let's say that you are running both the Apache web server and the Tomcat servlet engine. Tomcat is set up so all of its requests are handled through Apache. In this situation you would want Tomcat to load after Apache is loaded and only if Apache is successfully loaded. Most operating systems would handle the startup process via a shell script. This script would contain code that runs Apache as well as code that runs Tomcat, possibly with some code in between that checks to make sure Apache is loaded properly before running Tomcat.

The script is easy enough to set up, but let's say your Tomcat process makes database calls to MySQL and you only want Tomcat to run if MySQL is loaded. Well, then your MySQL process depends upon an NFS-mounted disk, so you want to only run MySQL if NFS is loaded. This process is quite a bit more complicated, since writing the dependency conditions into a shell script would involve multiple, nested if statements. (If you do not think this example is complicated enough, think about adding IP services, which are required by Apache, MySQL, and Tomcat; Samba required by Apache; LDAP required by Tomcat; and Disk services required by all the other services.)

What does OS X do to get around the problem of writing a complex script? During the startup process OS X puts together a list of all the StartupItems that need to be run and orders this list based on the dependencies of each StartupItem. When a StartupItem is added or removed, no scripts need to be changed; OS X will automatically handle this the next time it starts up. In OS X creating a StartupItem involves creating an executable and a list of attributes, which include the dependencies. That's it, no scripts to write, edit, or test.

StartupItems versus LoginItems

OS X provides several mechanisms for running programs based on events in the login and startup process. Among these mechanisms are StartupItems, LoginItems, and shell startup scripts (used when you start up Terminal or X11). Each of these mechanisms is powerful in its own right, but they each have certain specific uses. The difference between LoginItems and StartupItems is probably the most confusing to those of us who use their Macs as single user machines.

Four major factors differentiate StartupItems from LoginItems. The first difference is the dependency ordering and requirements that we discussed in the last section. StartupItems have dependency ordering and LoginItems do not.

The other three factors that differentiate them are based on the individual processes that are run, specifically:

 StartupItemsLoginItems
When is the the process runduring startupafter a user logs in
Who runs the processby root, but not necessarily as root(more on this later)the user
What types of processes can be runbackground processes and processes that terminate after runningany Mac OS X executable

The Apache StartupItem

Mac OS X looks in two places to build its list of StartupItems: /System/Library/StartupItems and /Library/StartupItems (/Library/StartupItems does not exist by default and you may need to create it). All StartupItems are treated equally when determining the order in which they are run, but /System/Library/StartupItems is reserved for StartupItems that come as part of OS X. The fact is, most of the default OS X StartupItems need to be run in advance of user-defined StartupItems, since the default OS X items provide basic services of the operating system, such as crash reporting, core graphics services, and system accounting.

One of the StartupItems that comes as part of OS X is Apache, the web server that OS X uses to provide personal web sharing and ftp services. /System/Library/StartupItems has a directory named Apache, which contains the files you'll need to make Apache run during the startup process. If you look in this directory you will notice two files: Apache and StartupParameters.plist and one directory: Resources. In this article, we will not be going over the Resources directory, but it contains the files necessary for language localization. (If you want to do language localization on a StartupItem, it is a straightforward process and looking into the Resources directory of any of the default OS X StartupItems will give you an excellent overview of how to proceed.) The two files, StartupParameters.plist and Apache provide the basis for the Apache StartupItem.

The Property List

StartupParameters.plist is a property list that contains the attributes that are used to run Apache. The property list should be at the root level of the subdirectory. This property list contains the following attributes:

{
  Description     = "Apache web server";
  Provides        = ("Web Server");
  Requires        = ("DirectoryServices");
  Uses            = ("Disks", "NFS", "Network Time");
  OrderPreference = "None";
}

"Description" is a simple description of the service, which is only used to describe the file and not during the startup process.

"Provides" specifies the services that this StartupItems provides. Provides can contain multiple services, but it is not recommended. Here Apache provides a "Web Server" service.

"Requires" specifies the services that have to be started before this StartupItem is loaded. The values in this field are those of other StartupItems' "Provides" fields. Apache requires "DirectoryServices" to be loaded before it can be run; if DirectoryServices are not specified to run or fail to run properly Apache won't run.

"Uses" specifics the services that should be started before this StartupItem is loaded. This is similar to the "Requires" attribute except this service will always be loaded, but only after it has tried to execute all the services in "Uses". Apache uses the "Disks", "NFS", and "Network Time" services. OS X will try to load these services, if it can't it will still load Apache, but only after trying these services first.

"OrderPreference" specifies the general time period in which a StartupItem will be executed. This order is less significant than both "Requires" and "Uses" but is used to determine order after evaluating those attributes. The possible values for this attribute are: First, Early, None (default), Late, Last. Apache specifies no OrderPreference as to when it should be loaded.

The Executable File

The second file in this directory is named Apache and is the executable file that OS X will run during the startup process. The first thing to notice is that both the file and the directory are named Apache. The StartupItems specification requires that the executable file has the same name as the subdirectory that contains it and that the executable is in the root level of subdirectory.

Now it's time to take a look at the file:

#!/bin/sh

##
# Apache HTTP Server
##

. /etc/rc.common

StartService ()
{
    if [ "${WEBSERVER:=-NO-}" = "-YES-" ]; then
	ConsoleMessage "Starting Apache web server"
	apachectl start
    fi
}

StopService ()
{
    ConsoleMessage "Stopping Apache web server"
    apachectl stop
}

RestartService ()
{
    if [ "${WEBSERVER:=-NO-}" = "-YES-" ]; then
	ConsoleMessage "Restarting Apache web server"
	apachectl restart
    else
	StopService
    fi
}

RunService "$1"

The file starts like most shell scripts, by making sure the shell executes the script we want to run. After this, the script has a couple of lines of comments that tell us what the file does; in this instance it is the "Apache HTTP Server". The line after this loads the /etc/rc.common file. /etc/rc.common is a script library that Apple provides with useful routines for extracting parameters to scripts. One function that /etc/rc.common provides is RunService. When called with a single argument, RunService will run the function named "Argument"Service. For instance if you were to execute:

RunService "go";

it would execute the function named "GoService". The RunService function makes it easy to use command-line arguments to run different functions in a script, given different input arguments. If you look at the end of the Apache file, you will see the line:

RunService "$1"

"$1" specifies the first argument given to the script, so this command will execute a function based on the first argument to the script.

The executable that is used for a StartupItem needs to be able to accept a single argument that can take on one of three values: start, stop, or restart. This is to allow OS X to start the service during the startup process, stop the service during the shutdown process, or restart the service when specified (all of these can be done using the SystemStarter command in /sbin).

To support these three arguments there are three functions in the Apache file: StartService, StopService, and RestartService. These functions do exactly what their names imply: they start, stop, or restart the service that this executable defines. To create your own StartupItem executable all you need to do is replace the code in the StartService, StopService, and RestartService functions with code that will handle your program or service. (Of course, any program that accepts a single argument with the values start, stop, and restart can be a StartupItem. This method is by far the simplest way.)

You may also notice that all of the functions make a call to "ConsoleMessage". ConsoleMessage prints values to the OS X startup console. This "console" is the part of the screen that displays the messages that appear during the startup process.

Creating Your Own StartupItem

Now that we have seen what a StartupItem looks like, it is time to create your own. The first step is to create a directory that is named for the service you want to create. I would recommend creating this directory in your user account and you will copy the files into /Library/StartupItems when you have completed creating them. Since we are creating a StartupItem to start the Tomcat servlet engine, we will name our directory "Tomcat".

The next step is to create a property list file and our executable file. The easiest way to create these files is to copy them from the Apache StartupItem in /System/Library/StartupItems. Like so:

[ToadHall:~/projects/startupitems/Tomcat] mrtoad% cp /System/Library/StartupItems/Apache/Apache ./Tomcat
[ToadHall:~/projects/startupitems/Tomcat] mrtoad% cp /System/Library/StartupItems/Apache/StartupParameters.plist .

Once we have these files created we need to edit them so we can load Tomcat instead of Apache. We will start in StartupParameters.plist. The name of our service will be "Tomcat servlet engine"; it will provide "Servlet Engine" service, it will require "DirectoryServices"; it will use "NFS"; and it will have no order preference.

So the file should look like:

{
  Description     = "Tomcat servlet engine";
  Provides        = ("Servlet Engine");
  Requires        = ("DirectoryServices");
  Uses            = ("NFS");
  OrderPreference = "None";
} 

Next, we need to edit the executable file Tomcat. In this instance I am going to use Tomcat version 4.1, which on my machine is stored in /usr/local/jakarta-tomcat-4.1.18. This version of Tomcat has startup and shutdown scripts in its bin directory. The files are appropriately named: startup.sh and shutdown.sh. Both startup.sh and shutdown.sh require that the JAVA_HOME environment variable is set to the Java home directory on your machine. The default Java home directory in OS X is /Library/Java/Home.

When editing the file, we need to make five changes. First, we should change the comment at the beginning of the file specifying the name of the service. Next, we need to edit the three functions to use the Tomcat startup and shutdown scripts and finally, we need to set the JAVA_HOME environment variable before issuing the RunService function.

Our file should then look like this:

#!/bin/sh

##
# Tomcat Servlet Engine
##

. /etc/rc.common

StartService ()
{
    ConsoleMessage "Starting Tomcat"
    /usr/local/jakarta-tomcat-4.1.18/bin/startup.sh
}

StopService ()
{
    ConsoleMessage "Stopping Tomcat"
    /usr/local/jakarta-tomcat-4.1.18/bin/shutdown.sh
}

RestartService ()
{
    ConsoleMessage "Restarting Tomcat"
    /usr/local/jakarta-tomcat-4.1.18/bin/shutdown.sh
    /usr/local/jakarta-tomcat-4.1.18/bin/startup.sh
}

JAVA_HOME=/Library/Java/Home; export JAVA_HOME
RunService "$1"
The final step is to copy the directory into /Library/StartupItems, like this:

[ToadHall:~/projects/startupitems] mrtoad% cp -r Tomcat /Library/StartupItems/

note: cp -r recursively copies the directory

Once the directory is copied, it is time to restart to test it out.

During the bootup process, depending on the speed of your machine, you should be able to see "Starting Tomcat" displayed on the console(if your machine is too fast, you might not catch it). Once OS X is booted, you can check to see if Tomcat is running by trying to load a JSP or servlet from your browser. If you use the standard Tomcat setup, you can test it at: http://localhost:8080/index.jsp.

The Finish Line

As you can see, StartupItems are a powerful yet simple framework for managing the startup process. Once you get through the background information and understand what is going on, StartupItems are easy to both set up and use.

Andrew Anderson is a software developer and consultant in Chicago who's been using and programming on the Mac as long as he can remember.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.