Services on OS X : launchctl, Y U do this ?

If you are on OS X and are a PHP developer, you probably are in one of these situations :

  1. You use the standard PHP + Apache + MySQL OS X Installation
  2. You use some kind of container system like docker to manage your dev environment
  3. You install your own versions of the PHP binary and/or webserver to match your production environment

I'm personally in case #3. I'm using brew of course, like many of us (If you don't know Homebrew, check it out).

So I've installed the versions and flavors I wanted, namely Nginx to replace Apache, PHP-FPM (5.6) and MariaDB in lieu of MySQL.

Homebrew makes this task quite easy but then, I have to manually manage these services since OS X won't really have any nice wrappers to do so (remember this handy "Enable Web Sharing" preference back in Mountain Lion ?).

Here comes launchctl.

launchctl

Oh, my. I already have a hard time with systemctl on debian, so launchctl is like a nightmare on OS X.

If you ever want to deal with a service, may it be a system service or some other service you have installed, launchctl needs the full path of the relevant plist file just to be able to talk to it.

So if I want to start MySQL :

launchctl start ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist

Really ? Let's take a moment and appreciate the fact that we have launch agents in different places in OS X :

  • ~/Library/LaunchAgents/
  • /Library/LaunchAgents/
  • /Library/LaunchDaemons/

.. so that when we want to start or stop a service, we need a few minutes to figure out the full path of the thing.

But hey, if you want to get the status only, it's simpler though :

$ launchctl list | grep mariadb
394	    0	homebrew.mxcl.mariadb

Well, yeah ... but what does that mean ? Is my service active because it has some kind of PID ? Or maybe it's in a dead state, or active (exited), or maybe something else ?

From the man page :

The first column displays the PID of the job if
it is running. The second column displays the last exit status
of the job.

Ok, but was that the last time it exited, or is it exited right now since there is a code, i.e. is it persisted if I relaunch a service and it doesn't exit right away ?

So, well, I'm not a big fan of launchctl. I find it confusing.

Enters lunchy.

lunchy

gem install lunchy

It's not perfect, but it's a great plus. lunchy is a Ruby tool that wraps launchctl and provide us with a more sensible API.

I can't agree more with Mike Perham (the developer) who says :

Don't you hate OSX's launchctl? You have to give it exact filenames. The syntax is annoyingly different from Linux's nice, simple init system and overly verbose. It's just not a very developer-friendly tool.

What is really cool with lunchy is that it uses patterns to match services, and not exact names. So I can do :

lunchy restart ng

And it will gladly match Nginx and restart the service for me. Rad !

Moreover, lunchy ls wraps the list command with a plus : it lists only the user processes, not all of them (which you can have by doing sudo lunchy ls), so my output is not cluttered with loads of gibberish like com.apple.mdworker.sizing, com.apple.SpeechRecognitionCore.brokerd or com.apple.metadata.mdbulkimport of which I really don't care. No, I have a neat listing of what I want :

Now that's great and handy.
Thanks Mike !