The Future of Init, Part IIb: OS X Launchd28 Oct '05 - 01:08 by benr
First and foremost, I must thank Mr. Nathan 'RbdPngn' Ingersoll, Enlightenment Core Developer and father of the Enlightenment Widget Library the EFL official widget toolkit, for trusting me enough to give me root access on his beloved Mac running Tiger. Without access to his system and the ability to experiement I wouldn't have been able to produce this entry.
First released with Mac OS X 10.4 (aka: Tiger), launchd was introduced as a full init replacement to dramatically change the way the system was managed and introduce a whole new way of thinking about the job of init. In prior releases the old BSD rc system was used, supplimented with SystemStarter, which we discussed last time around. But SystemStarter didn't go nearly far enough, it was a system that was better than SysV init (/etc/init.d/, /etc/rc2.d/, etc) but simply isn't a robust and full featured system capable of taking over init's job.
Launchd is said to be "one daemon to rule them all", but that statement doesn't go far enough to convey a sense of what it really is. Yes, as a true init replacement it runs as PID1, called by the kernel as part of the boot proccess, but there is more too it than that. The beauty of launchd is that its the first init system that really was designed in a wholistic manner, considering the various needs of a UNIX system and solving a variety of them at once. But how?
Typically we think of an init system as the thing that starts up proccess when the system starts and shuts them down when it reboots or powers off. To some degree init can manage these processes via methods like inittab respawn. Newer systems add even more control by leveraging a daemon to watch proccesses and restart them if they fail, but lets think about this in a diffrent way entirely. Instead of thinking about managing services lets think of the init system as a basic sort of job schedualer. What other types of things on a UNIX system might fall into the job schedualer category? Inetd does, it starts a daemon when requested and then shuts it down when its not. And both cron and at are job schedualers too. These 3 diffrent tools (init, inetd, and cron/at) can be thought of as very similar things, except that init starts things once and lets the run for long periods of time, inetd fires things on demand, and cron/at fire things on schedualed intervals. They aren't so diffrent. This is what Apple had in mind when it created launchd. Launchd can take these tools that we typically think of as very diffrent and bring them together.
Some of the chief benifits of pulling together so many elements of the system together is that you need only one daemon instead of 3+, you enable centralized control and management, and you eliminate the headache of all these diffrent tools implementation details. Look no further than the configuration files, init has rc scripts, SystemStarter has its XML and scripts, cron has crontabs, inetd has its own config files, so on and so forth. Thanks to launchd you don't have to spread things across the system in diffren config files that act completely diffrently and consolidate to a single tool.
Configuring launchd is very similar to SystemStarter. Within /System/Library/LaunchDaemons are a variety of plist's, one per service. These plists are fundimentally similar to those used by SystemStarter. Have a look at the ftp.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>Disabled</key> <true/> <key>Label</key> <string>com.apple.ftpd</string> <key>Program</key> <string>/usr/libexec/ftpd</string> <key>ProgramArguments</key> <array> <string>ftpd</string> <string>-l</string> </array> <key>inetdCompatibility</key> <dict> <key>Wait</key> <false/> </dict> <key>Sockets</key> <dict> <key>Listeners</key> <dict> <key>SockServiceName</key> <string>ftp</string> <key>Bonjour</key> <true/> </dict> </dict> </dict> </plist>
You can see that the XML plist contains a dictionary made up of key/value pairs. Disabled, true. Program, /usr/libexec/ftpd. You notice that there isn't a reference to a script containing start/stop/restart scripts similar to SystemStarter though. In fact, on the Tiger system that I looked at none of the launchd plists referenced scripts. As of the Tiger release of OS X SystemStarter and launchd co-exist, so services that need more than a single command to start are still handled by SystemStarter, but this is said to change in the future.
Another think you'll notice about the plist above is that its an inetd service. Whereas SystemStarter provided an OnDemand key, here we see syntax similar to that found in inetd configs.
In order to use a launchd service you must load its configuration using the launchctl command. Once the configuration (plist) is loaded we can use the same tool to list loaded services:
$ sudo launchctl load -w /System/Library/LaunchDaemons/ntalk.plist $ sudo launchctl list .... com.apple.ntalkd
Notice that I'm passing the -w argument to launchctl, in my testing this was required. The -w flag removes the disabled key from the service, thus starting the service when it was loaded. If you don't use -w you'll get a "nothing found to load" error.
One aspect of launchd that I had to get used to was that the output of launchctl was based on your uid. Notice that when I list the services as a user (benr) I see nothing, but when I do it again with sudo (root) I see all the running services:
speedy:/System/Library/LaunchDaemons benr$ launchctl list speedy:/System/Library/LaunchDaemons benr$ sudo launchctl list com.apple.KernelEventAgent com.apple.mDNSResponder com.apple.nibindd com.apple.periodic-daily com.apple.periodic-monthly com.apple.periodic-weekly com.apple.portmap com.apple.syslogd com.vix.cron org.postfix.master org.xinetd.xinetd com.openssh.sshd
One of the kool things about launchctl is that it can be used as a shell. This has its benifits:
launchd% help usage: launchctl
load Load configuration files and/or directories unload Unload configuration files and/or directories start Start specified jobs stop Stop specified jobs list List jobs and information about jobs setenv Set an environmental variable in launchd unsetenv Unset an environmental variable in launchd getenv Get an environmental variable from launchd export Export shell settings from launchd limit View and adjust launchd resource limits stdout Redirect launchd's standard out to the given path stderr Redirect launchd's standard error to the given path shutdown Prepare for system shutdown reloadttys Reload /etc/ttys getrusage Get resource usage statistics from launchd log Adjust the logging level or mask of launchd umask Change launchd's umask help This help output
But perhaps my favorite feature of launchd is its ability to manage the resource usage of itself and its children. Have a look at the limits:
speedy:/System/Library/LaunchDaemons benr$ sudo launchctl limit cpu unlimited unlimited filesize unlimited unlimited data 6291456 unlimited stack 8388608 67108864 core 0 unlimited rss unlimited unlimited memlock unlimited unlimited maxproc 100 532 maxfiles 256 unlimited
These limits can be set globally or changed in the plist per service. Using launchctl we view the resource usage thus far by both launchd itself and its children like so:
launchd% getrusage children 42.164101 user time used 89.070940 system time used 0 max resident set size 0 shared text memory size 0 unshared data size 0 unshared stack size 0 page reclaims 0 page faults 0 swaps 46847 block input operations 40149 block output operations 27457 messages sent 11113 messages received 652 signals received 113062 voluntary context switches 0 involuntary context switches
The resource controls aren't nearly as advanced as Solaris's but they are better integrated. Currently there is not way to specify resource control within an SMF manifest, but perhaps something we'll have soon.
One thing that puzzled me was launchd's supposed replacment of cron, because launchd actually has a plist for Vixie-CRON. Why? Perhaps its a modified verson of it, but I found no evidence of that. I did play with at a bit but it didn't work properly. Theoretically I should have been able to schedual a job using at and then used launchctl list to see it queued to run. I didn't, but I didn't have enough time to really investigate.
All in all, launchd has an exceptional scope. Its bold and its forward looking. It certainly isn't as well rounded from an administrative point of view as InitNG or SMF but its got some definate possibliities that the others don't. Tiger was just the debut, what we really need to watch for is what happens in Mac OS X 10.5... will SystemStart go away? Will we actually see launchd be the one stop shop that it was meant to be? All signs that I see lead to "it should" right now, but we won't know for sure untill it arrives (or someone sends me a beta).
I want to point out that one of the chief advantages of launchd is supposed to be its API. Launchd is said to give the programmer unsurpased control in code, unlike they've ever had before. Certainly on a GUI driven platform like OS X this is of utmost importance. But that discussion is beyond the scope of this document, so I'll leave you to fend on your own.
For more information about launchd check out the following resources:
- Getting Started with launchd: ADC Article
- Introduction to System Startup Programming Topics: ADC Article
- Launchd in depth: Written by Josh Wisenbaker of AFP548; this is the article to read, this guy knows his shit. Best resource I found.
- Launchd entry on Wikipedia: Very good, a must read!
- MacGeekery's Launchd Recipes: I didn't find this useful at all, but have a read anyway.
- Ars Technica discusses Tiger and Launchd
- launchctl man page
- launchd.plist man page
- "Does launchd Beat cron?": Slashdot post
- What's New for UNIX Users?[PDF]: Slides from Dave Zarzyck's (Father of launchd) USENIX 2005 presentation.
Hope you come back soon!! Your pictures are great.