Monday, June 01, 2009

Intro to using launchctl with MacPorts

After a long and frustrating day of trying to get some services running (apache and postgres) on a new box, I finally understand the basics of launchctl and how MacPorts interacts with it.

launchd and launchctl
Launchd is Apple's open-source replacement for cron, xinetd, mach_init, and init. You manage launchd services with the launchctl utility.

This article explains how launchd communicates with daemons, which is useful to understand when using launchctl to stop/start services.

Launchd manages named jobs which are specified by having launchctl load a plist file with the launchd config for a task. So for instance, instead of installing a script file in /etc/init.d you would call launchctl load -w /path/to/myservice.plist.

Once loaded into launchd, you can manipulate your service with the start/stop subcommands. Start will start the service, and stop will stop it. Thus, you'd think that you need only load a service once ever and then use stop/start to manage the services. However, you will notice that a service stopped with launchctl stop joblabel often won't seem to have stopped - it's still listed in the process list. But in reality it has stopped, and then restarted! If you look at the PID's before and after you'll notice you have new PIDs after you "stop" the service.

So what's going on?

Launchctl services "ported" from init.d scripts are usually configured with OnDemand=false which causes them to launch automatically at boot time. However, it also signals launchctl to make sure the process is always running! Thus, when you issue the stop command to such a service, a SIGTERM is sent to the service, but launchd immediately notices that it's no longer running and immediately re-spawns the service!

If you want your service to really stop, you can run launchctl remove joblabel, which will unload the service description and stop the job immediately. The downside is that you can not issue a launchctl start joblabel to start the service back up, since it is no longer loaded. You must then reload the service with launchctl load -w /path/to/myservice.plist.

In summary, if you are managing a service that has OnDemand=false, and you want to stop/start your service, use:

[start] launchctl load -w /path/to/myservice.plist
[stop] launchctl remove joblabel

Launchctl and MacPorts
Fortunately, the awesome guys over at MacPorts have built in some convenience commands for managing services installed with MacPorts. Note that this was only recently added and widely adopted; some of the older ports don't support the newer integration points, so just be aware in case this doesn't work for you.

The port command has 2 subcommands for interacting with launchctl, load and unload.

If all you're looking to do is have a reliable way to stop and start services, for instance the apache2 port, simply use:

[start] port load apache2
[stop] port unload apache2

It's that simple!

I hope that this guide helps clear up confusion that I am sure lots of people have experienced; I know it's caused me to waste several hours on many occasions, which is what I decided to finally talk to the MacPorts guys on IRC and write it all down once I figured it out!

Enjoy,
Alan

10 comments:

  1. Thank you for writing this down. I believe that 'daemondo' is used as an intermediary, but I don't know anything beyond this. I wish that this was written in the MacPorts wiki _when_ it was done…
    ReplyDelete
  2. Yes, daemondo is used as an intermediary by macports. IIRC all it does is translate from the SIGNALS used by launchctl to the start/stop commands used by daemons originally written for init.d.
    ReplyDelete
  3. Those port commands are very handy to know shortcuts! Thanks!
    ReplyDelete
  4. Awesome, port command is exactly what i m looking for. Thanks
    ReplyDelete
  5. God bless this blog post.
    ReplyDelete
  6. Dude thanks, this makes sense!
    ReplyDelete
  7. What about arguments? What if your service requires arguments in order to be started? Can these arguments be saved?
    ReplyDelete
  8. See ProgramArguments in http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man5/launchd.plist.5.html
    ReplyDelete
  9. Lesson learned, "stop" means "restart" on Mac... Yuck.

    In order to get "sudo port load " to work if environment variables are needed, check out

    http://craiccomputing.blogspot.com/2010/10/setting-environment-variables-for-sudo.html
    ReplyDelete