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.
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