blog advertising is good for you


blog advertising is good for you
User login

All About launchd Items (and How To Make One Yourself)

If you want to make a program start at a certain time, for a certain event, or just stay running, the new launchd service in Tiger is for you. No longer do you have to write a shell script in a package with specific permissions and such to get an item to start with the computer. As of Tiger all you need is one plist and the OS handles the rest.

[Plug: There is now a program to edit launchd property list files in a GUI rather than manually-creating the text: Launchd Editor]

launchd replaces a good many parts of Mac OS X in Tiger. Specifically, it absorbs the functionality of:

  • init
  • watchdog
  • cron
  • at
  • (x)inetd
  • rc1
  • Startup Items
  • Login Items2

With all of this power behind it, launchd looks pretty useful, but it goes further adding:

  • Queue folders
  • Watch paths
  • Resource limitations
  • chroot jails for processes

Start your FTP daemon only when needed, and confine it to one part of the OS. Start your SMTP sending daemon only when there’s a queue. Start your Automator workflow as soon as a folder has a file in it. Change your computer’s settings whenever a preference file is updated. The possibilities are almost limitless.

But I’m not here to say what you can do with this (yet). I’m here to show you how to get started.

Property lists are the format of choice in Mac OS X, and launchd is no different. Using Property List editor, create a new list and add the following items:

  • Root dictionary
    • Label string
    • ProgramArguments array
    • OnDemand boolean

Set them as follows:

  • Label: com.apple.TextEdit
  • OnDemand: false
  • ProgramArguments: one child, a string: /Applications/TextEdit.app/Contents/MacOS/TextEdit

Save this file in ~/Library/LaunchAgents/com.apple.TextEdit.plist then go into Terminal and, in your home directory, run:

launchctl load Library/LaunchAgents/com.apple.TextEdit.plist

TextEdit should start right up. Do not quit it for ten seconds. Wait about 15, then quit it. Notice it start up again. Now run:

launchctl unload Library/LaunchAgents/com.apple.TextEdit.plist

Watch TextEdit go away.

Congratulations, you’ve made a launchd item. Now for some fun.

Run:

touch /tmp/start_te

Open the property list up in Property List Editor again and change OnDemand to true. Now add an array WatchPaths and add the string /tmp/start_te to it. Save and run the load command again. Notice how TextEdit doesn’t start? Touch start_te again. TextEdit should start now.

Can you see what fun this could be? No? Let’s have some more.

Remove /tmp/start_te and make the directory /tmp/te/. Leave it empty. This is important.

Rename the WatchPaths item to QueueDirectories and change /tmp/start_te to /tmp/te/ and save. Unload and load the item again. To start TextEdit this time, run:

touch /tmp/te/te

Wait a few seconds and try and quit TextEdit. Won’t stay dead, right? Remove the /tmp/te/te file and then quit it.

As you can probably see by now, this has incredibly useful implications for even the average user. The great part is that ~/Library/LaunchAgents is a supported directory, meaning that when you login the system will load any property list files in there for you automatically when your user session starts. You could, for example, run a local instance of MySQL in your home folder and not bother other users of the machine with a global startup item. Perhaps run a copy of JavaHMO only when you’re logged in, or POPFile or whatever.

There are more options in the manpage for the launchd.plist files so you can see other ways to get things started as well. It’s really quite fun when you get down to it, so have at it.

As of 10.4.0, calendar-date-based events do not appear to work more than once per load. A bug has been filed.

1 Mostly.

2 It can, but it’s not implemented as such right now.

Average rating
(0 votes)
About Adam Knight
Adam Knight's picture

Author Biography

Adam Knight is one of the founders of Mac Geekery and is a geek at heart. Programmer by day, hacker by night, his daily life revolves around the Macintosh platform, which he has been a user and programmer for since the early days of System 7 when his LCII replaced his Apple //c.

In-between tech jobs, he’s managed to learn the basics of any web hacker: PHP, MySQL, Perl, Apache, Linux, *BSD, and the intricacies of ./configure —prefix=~/bombshelter/. Today, codepoet is concentrating on blogging again, writing some software for the Mac by himself (including Notae) and for his company (such as Switchblade) and has a few other toys coming out soon.

Bug him over AIM or email [link fixed].

SDOM A DOM Level 3 Implementation for Scheme (tags: scheme lisp xml) All about launchd items – a cool way to have process run automatically on OS X (tags: macosx)…

Thanks for the launchd teaser. And for creating this cool site, btw, which has rekindled my interest in Drupal.

“As of 10.4.0, calendar-date-based events do not appear to work more than once per load. A bug has been filed.”

Geez… you’d think when replacing cron, if there’s one thing you’re going to get right, it be date based events! Either way, very cool intro, thanks!

Unless I’m missing something, the treatment of calendar based events in launchd (once working) isn’t much of a replacement for cron. The launchd.plist manpage indicates that StartCalendarInterval is a dictionary of integers (Minute, Hour, Day, etc.) where “The semantics are much like crontab(5)”. Well in a crontab, these parameters are not just integers. Any idea how to do the equivalent of this cron command:

50 8-20/2 * * * root chmod -R 775 /Users/Shared/*

i.e. run said command every two hours each day from 8:50am to 8:50pm

?

Adam Knight's picture

I noticed that as well. It doesn’t seem to support both intervals and time periods, but it is a 1.0 version. I’m sure that they’ll support that later on.

Of course, the source code is ridiculously simple so any Joe programmer could add that in and submit it as well.

cp

Inetd and xinetd allows you to control access to services based on the source IP address, can the same be done through launchd?

Adam Knight's picture

It does not appear launchd has a provision for this, but my presumption is that they want you to use the firewall for such things. Makes more sense, really.

cp

It looks like through Tiger Apple has implemented the “service” command on the command line. I’ve used this with Linux and BSD where you can enable or disable services on the system. It says it’s a script.

at the command prompt type:

service —list

It also appears xinetd still runs as a process, and Apple has created an xinet.d directory in /private/etc where configuration files for the services reside.

If you do a more on the “service” script you’ll see a function for restarting xinet.

you can use the service command like this:

sudo service telnet start

and if you do that you can do:

cat /private/etc/xinet.d/telnet

and you’ll see it did this:

service telnet
{ disable = no socket_type = stream wait = no user = root server = /usr/libexec/telnetd groups = yes flags = REUSE
}

If you stop it the “disable” will become “yes”

If you upgraded from Panther then you’ll find a bunch of xinetd configuration scripts in /etc/xinetd.d/ but if you performed a clean installation, the directory will be empty. Tiger on a clean installation will use launchd to launch services while on an upgraded system, it will use xinetd. And the “service” script adapts itself accordingly. On my system (a clean install) - after I ran:
sudo service telnet start
There was no file called /etc/xinetd.d/telnet, but when I ran:
sudo launchctl list
The com.apple.telnetd daemon was running.

Adam Knight's picture

Now that’s just kick-ass.

cp

Could you elaborate on the OnDemand key listed in the launchd.plist manpage? If I set the StartInterval as 2700 (45 minutes) and OnDemand as true, will the job run every 45 minutes constantly? Or must I set OnDemand to false?

Adam Knight's picture

It should be set to true. OnDemand basically means “Do I start this now and keep it running, or wait for a condition?”

cp

adsorption in chromatography adsorption in chromatography,lake highland park high school lake highland park high school,clarks buslines qu clarks buslines qu,baytown sexual assault texas baytown sexual assault texas,clarks ebay clarks ebay,victorias sec victorias sec,silo course highland recreation area mi silo course highland recreation area mi,the daily cartoonist the daily cartoonist,highland county ohio jail highland county ohio jail,baytown trails baytown trails,directory of textile importers in france directory of textile importers in france,the one hundred and one dalmatians book characters the one hundred and one dalmatians book characters,rockhouse negril jamaica rockhouse negril jamaica,riverhead mental health riverhead mental health,fleetwood mac landslide guitar tab fleetwood mac landslide guitar tab,l233 l233,skate park baytown skate park baytown,atlantis marine aquarium riverhead ny atlantis marine aquarium riverhead ny,nobilis the secrets of the da vinci code walk through nobilis the secrets of the da vinci code walk through,english highland cattle english highland cattle,

For those interested there’s a Q&A session on IRC tomorrow evening with Dave Zarzycki – the author of launchd – details as posted by Dave:

Date: 5/25/2005
Time: 4pm PDT till when I go to bed
Channel: #launchd
Network: irc.freenode.net

Why am I doing this?

I’ve been tasked to write a HOWTO/FAQ for launchd.

Now, as the guy who wrote launchd, I’m the best and worst person to write such a document.

That’s why I need your help. Feel free to come and hang out and I’ll try and answer as many questions as I can.

See ya then,

davez

Any chance it’s available anywhere yet?

… but I’m not really that much of a geek that I know how to use the Property Editor…!

How do I actually add the instructions (with or without the preceeding bullets?);

Label string
ProgramArguments array
OnDemand boolean

etc…?

Are they typed in or are some bits selected from options?

All help gratefully received.

Eeyore
My House

Adam Knight's picture

You’re in luck, I just completed an editor for these lists.

Launchd Editor

cp

Nice intro, but does anyone have snmpd running via launchd?

So far, I haven’t been successful.. Sad

Adam Knight's picture

Make sure you’re using the -f option to not fork it, and use the “maintain” recipe in the Launchd Recipes article. You cannot use the sockets form with that program as it doesn’t even work with inetd.

cp

Please excuse any weird formatting and missing greater than/less than and forward-slash symbols from this mail, but I have not yet figured out a way to make them appear… Please assume that all the bits like key and array and string are there and correct

I have this as /Library/LaunchDaemons/Fetchmail.plist





Label
FetchmailfromDemon
Program
/usr/bin/fetchmail
ProgramArguments

/usr/bin/fetchmail
–fetchmailrc
/etc/mail/fetchmailrc
–logfile
/var/log/fetchmail.log
–silent

RunAtLoad


but, at launch, it does this (from /var/log/system.log):

Jun 21 03:42:08 pmac-g5 launchd: FetchmailFromDemon: exited with exit code: 5
Jun 21 03:42:08 pmac-g5 launchd: FetchmailFromDemon: 9 more failures without living at least 60 seconds will cause job removal

here is the fetchmailrc:

set daemon 120
poll pop3.demon.co.uk proto pop3
aka demon.net
aka mailstore
no dns
localdomains xxxxxxx.xxxxxx.co.uk xxxxxxxxxx.demon.co.uk localhost: user “xxxxxxxxxx” pass “xxxxxxxx” to * here
fetchall
preconnect ’echo date – Connecting’
postconnect ’echo date – Disconnected’

Any ideas why this won’t work? I understand that this might not work for on-demand running by launchd as it turns itself into a daemon when it is run, but I just want to run it once at load time, and then just leave it as a daemon.

fetchmail runs fine from the command line and used to run fine from a StartupItem.

What the heck is an error code 5 - can’t find any definitions for launchd error codes any where on the ’net.

Adam Knight's picture

The fetchmail daemon likely exited with code 5, not launchd. Launchd is upset that fetchmail didn’t live for 60 seconds (as the manpage documents, this is required).

Run a shell script, instead, and put “sleep 60” in before fetchmail runs, then send the result of fetchmail back as the result of the script so that it’s reported in the error log correctly. If you still get error 5, then it’s certainly fetchmail.

cp

Yep, quite right. launchd didn’t like the fact that fetchmail immediately becomes a background/daemon process due to the first line of the fetchmailrc. I thought that was only for on-demand daemons/agents, but it would seem not. From man launchctl:

 A daemon or agent launched by launchd MUST NOT do the following in the
 process directly launched by launchd:

       o   fork(2) and have the parent process exit(3) or _exit(2).
       o   Call daemon(3)

which seems to be what happens with fetchmail.

Now, I run a script called Fetchmail_runner, viz:

 pmac-g5:/usr/local/sbin root# more /Library/LaunchDaemons/Fetchmail.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>Fetchmail_from_Demon</string>
   <key>Program</key>
   <string>/usr/local/sbin/fetchmail_runner</string>
   <key>ProgramArguments</key>
   <array>
     <string>/usr/local/sbin/fetchmail_runner</string>
   </array>
   <key>RunAtLoad</key>
   <true/>
 </dict>
 </plist>

which runs:

 pmac-g5:/usr/local/sbin root# more Fetchmail_runner  
 #!/bin/sh  
 sleep 65  
 /usr/bin/fetchmail --fetchmailrc /etc/mail/fetchmailrc --logfile /var/log/fetchmail.log --silent || echo error: $?

and that works like a charm!

Thanks, Geoff.

Would it have been better, stylistically as well as to ensure that the process actually runs, to remove the line in the fetchmailrc that makes it run as a demon polling every 2 minutes, and just have launchd run the process every 2 minutes?

Anyone hazard a guess at the preferred solution?

G.

Adam Knight's picture

That’s likely the best solution of all.

cp

Someone (specifically, Dave Zarzycki, according to Wikipedia) made an oversight in the design or implementation of launchd. In my opinion it is a very cool piece of software that all geeky Mac users can get a lot out of, but one thing that it does not do well is take the place of cron. When an agent has the StartInterval key in its .plist file, so that it is run every N seconds, the restriction that it must run for at least 60 seconds simply should not apply. But it does. So running fetchmail using StartInterval instead of as a polling process doesn’t work, because fetching email typically doesn’t take 60 seconds. Sure, putting fetchmail inside a shell script and adding a sleep before/after it solves the 60 second problem, but who can stomach that?

Unfortunately, the 60 second problem is not the only one. fetchmail exits with status 1 when there is no mail, so if you want to run fetchmail periodically from launchd, you probably want to report 1 as 0 in your script, or else launchd will think bad things are happening when bad things aren’t happening. (Maybe there is a fetchmail option to report “no mail” as a zero exit status?)

So…the stylistic argument for running fetchmail via StartInterval is, I think, not the right one. There remains the argument that launchd will enable us to not keep bunches of daemon processes hanging around unnecessarily. In the case of fetchmail, however, this means forking it every N seconds. N, in my case, is quite small, so there would be a lot of forking. I’d much rather fork fetchmail once and have it sitting in memory, since it doesn’t leak.

I hope this makes sense.

Right so if anyone else out there feels like launchd is really what ought to be running fetchmail, I have either some help or a question for you, depending on how far along you are. If you haven’t gotten it going yet, putting this file in $HOME/Library/LaunchAgents/fetchmail.plist should be sufficient to cause fetchmail to be run by launchd, upon your next login (or sooner?).

<?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>fetchmail</string>
<key>OnDemand</key>
<false/>
<key>RunAtLoad</key>
<true/>
<key>Program</key>
<string>/usr/bin/fetchmail</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/fetchmail</string>
<string>-d120</string>
<string>-N</string>
</array>
</dict>
</plist>

Now, the question…well, there are a couple of questions. First, when, exactly, does launchd start fetchmail? When I log in or when the system boots? Second, how can I get it to stop fetchmail—and specifically, how can I get it to stop fetchmail when I log out? It leaves fetchmail running and even issuing a launchctl stop fetchmail does not kill the process. ARGH!

(By the way, does anyone else think it would be cooler if the math questions you had to solve were a little bit harder, like “What is the Galois group of x^5^+6x^2^+^3?”)

white:~/Library/LaunchAgents paul$ launchctl load com.apple.TextEdit.plist
launchctl: unknown subcommand “automount”
com.apple.TextEdit: Invalid argument

Any idea what this is about?

Adam Knight's picture

I might if you posted the plist you’re trying to load…

Include it in-between PRE tags for best display.

I had Program Arguments as two words, rather than InterCapped™ which prevented it from running at all. I still see the automount error, but I suspect it’s something it’s inheriting from my login. It doesn’t happen on a different system I tested it on.

Speaking of automount, I suspect this could be used to load the remote disk that holds my iTunes collection (who thinks 30Gb is enough disk space?). I had automount working on Panther but Tiger changed some things I never caught up on.

Anyway, thanks for the tutorial.

white:~/Library/LaunchAgents paul$ 2006-02-20 22:01:03.608 TextEdit[26746] CFLog (0): CFMessagePort: bootstrap_register(): failed 1103 (0x44f), port = 0x3003, name = 'com.apple.TextEdit.ServiceProvider'
See /usr/include/servers/bootstrap_defs.h for the error codes.
2006-02-20 22:01:03.772 TextEdit[26746] CFLog (99): CFMessagePortCreateLocal(): failed to name Mach port (com.apple.TextEdit.ServiceProvider)
the plist follows:
<?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.TextEdit</string>
	<key>OnDemand</key>
	<false/>
	<key>ProgramArguments</key>
	<array>
		<string>/Applications/TextEdit.app/Contents/MacOS/TextEdit</string>
	</array>
</dict>
</plist>
Adam Knight's picture

If you check the file it mentions you’ll note that error 1103 is BOOTSTRAP_SERVICE_ACTIVE which would indicate either the module is loaded or something is using a resource it needs.

I’d recommend verifying the file is good, first. Open it in Launchd Editor and re-save it to make sure it’s in a valid format and try and load that.

OK, this is too cool. I have been using a shell script to run an ssh tunnel from my wireless boxes to the wired gateway at my house, but I have to remember to re-run it when systems wake from sleep.

Now, I may not have to do that anymore and — wonder of wonders — I may be able to dispense with the shell script. Though it looks like I may have to have some kind of PID file to prevent it from being run more that one.

Here’s what I have: substitute your own gateway system for XXXX (I connect to 3128 because it’s a squid proxy cache).

<?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>org.paulbeard.tunnel</string>
	<key>OnDemand</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>/usr/bin/ssh -N -p 22 -C -c 3des XXXX -L 8080/XXXX/3128
</string>
	</array>
</dict>
</plist>

Perhaps there are more refined controls that can detect and prevent duplication.

But the bottomline is, this is what I have been looking for.

Is there a way to have launchd respond immediately when a service (such as AppleSpell) exceeds a certain threshold of cpu cycles? I am currently trying to monitor when AppleSpell comes into play while the user types text in any Apple text processor application. The unix ps command shows the active services and their cpu rates.

In case there is no way to work the task above, I wish to know how to trigger a shell script at startup using launchd. I assumed it would simply activate all loaded plists within ~/Library/LaunchAgents/ however this does not appear to be the case (unless I’m doing something wrong).

Post new comment
The content of this field is kept private and will not be shown publicly.
16 + 3 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.