Using launchd in MacOS X

From Tiger onward, the official way to start daemons, run scheduled jobs and implement network listeners has been the launchd system. launchd could theoretically replace both init, cron, at and inetd/xinetd

Read the manual pages for launchd, launchctl and launchd.plist!

Here are some quickstart tips:

In /Library/Launchdaemons are plists that define services that are supposed to start automatically.

Here is an example of a plist for ipfw, for a user defined IP filter:

com.apple.ipfw.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.apple.ipfw</string>
        <key>ProgramArguments</key>
        <array>
                <string>/sbin/ipfw</string>
                <string>/etc/rc.ipfw</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>ServiceDescription</key>
        <string>IP filter rules</string>
        <key>StandardErrorPath</key>
        <string>/var/log/ipfw.log</string>
        <key>StandardOutPath</key>
        <string>/var/log/ipfw.log</string>
  </dict>
  </plist>

To load and activate a daemon like this, you can run

  launchctl load -w /Library/LaunchDaemons/com.apple.ipfw.plist

This activates the daemon and marks it as active in the definition file

The command launchctl list lists all active daemons.

The command

  launchctl unload -w /Library/LaunchDaemons/comp.apple.ipfw.plist

deactivates the daemon and marks it as passive until you load it again.

Here is a slightly more advanced example, of a daemon that I wanted to be able to start and stop as I pleased:

com.apica.proxy-sniffer.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>EnvironmentVariables</key>
        <dict>
                <key>CLASSPATH</key>
                <string>.:/usr/local/prxsniff:/usr/local/prxsniff/prxsniff.jar</string>
        </dict>
        <key>Label</key>
        <string>com.apica.proxy-sniffer</string>
        <key>OnDemand</key>
        <true/>
        <key>Program</key>
        <string>/usr/bin/java</string>
        <key>ProgramArguments</key>
        <array>
                <string>/usr/bin/java</string>
                <string>-Xmx256m</string>
                <string>-Xbootclasspath/p:/usr/local/prxsniff/prxsniff.jar</string>
                <string>ProxySniffer</string>
                <string>-WebAdmin</string>
                <string>-debug</string>
                <string>-JobController</string>
                <string>-ExecAgent</string>
                <string>-tz</string>
                <string>ECT</string>
        </array>
        <key>RunAtLoad</key>
        <false/>
        <key>ServiceDescription</key>
        <string>Proxy Sniffer</string>
        <key>ServiceIPC</key>
        <false/>
        <key>StandardErrorPath</key>
        <string>/var/log/prxsniff.log</string>
        <key>StandardOutPath</key>
        <string>/var/log/prxsniff.log</string>
        <key>WorkingDirectory</key>
        <string>/usr/local/prxsniff</string>
  </dict>
  </plist>

An active daemon can be started and stopped using launchctl, via its "label":

  launchctl start com.apica.proxy-sniffer
  launchctl stop com.apica.proxy-sniffer