
The jobs are now triggered by anything that can run a notifyutil command from any user on the system.


If I end up wanting more than a few triggers, I’m going to have to centralize everything better. I’ve been able to automate the generation and loading of the plist files it uses, so adding jobs is a bit faster now. If I expand on this idea, the first thing to do is hook up sockets that I can read and write data to so that I can control everything from one central script and pass arguments through the launchd agent. Using the methods I currently have set up, I can’t pass anything to the daemon it just keeps the listener scripts running in the background to handle the notifications. The launchd and notifyutil combination isn’t perfect, at least not in my current implementation. LaunchControl can set up jobs as root (Lingon X can, too, now), quickly load and unload agents, edit every known key with a helpful wizard (with expert mode for adding your own) and - my favorite feature - shows you at a glance what’s loaded, what’s running and what has errors (with descriptive info). Yes, I’ve come to prefer LaunchControl over (even the latest incarnation) of Lingon. With tools like LaunchControl, setting up launchd jobs and confirming their status is really simple. notifyd simply provided me with the easiest way to communicate with these processes. The reason I wanted to do this with launchd is because it provides the ability to run scripts as any user (including root), manages keeping the listeners alive automatically, and runs invisibly and without additional hooks and setup requirements. I pulled a few hairs out trying to solve various problems, and this is what it came down to. I wanted to be able to control more aspects of my computer remotely than standard methods would allow. I use these to mark whether jobs are running or idle, with 0 being “inactive,” 1 being “listening,” and 2 being “running.” You can query the value of the key with notifyutil -g key.name. Start a listener for a key with notifyutil -1 key.name where the -1 can be any number and it will wait for that number of posts before yielding.Īn active key can have a state set using notifyutil -s key.name 123, where 123 is the numeric value to assign. For my Jekyll listeners I’ve been using a convention like “ploy” and “jekyll.generate”. You can name your keys whatever you like, you just have to know the name of it to call it later. It registers “keys” such as foo.bar and allows you to listen to them, post values to them and query their value. notifyd/notifyutil ( man page) The notification system in OS X is an easy way to post “messages” between shells and across users. It’s easy to add your own tasks and have run in the background as any user. It sets the id.running key to 2 to indicate that its own job has startedĪ few details launchd ( man page) A system built into OS X that handles scheduled, constant and triggered tasks for the system.If the running.id state is 2, it pauses and polls for the current job to finish (id.running = 1).

When that key is posted to, notifyutil -1 yields and the script continues:.The script launches notifyutil -1 with the unique key, causing it to wait for a notification to be posted to that key before continuing.The script argument sets a unique notification key for the instance and assigns a set of tasks for that trigger.The first instance of the script that opens creates a shared id.running key for determining if a job is running (0 = unset, 1 = no job running, 2 = job running).each job calls the same script with a different argument.Here’s the summary of the system I’m using to build my Jekyll blog by having scripts in protected shells set up the necessary environment and run Rake tasks from them. If something doesn’t make sense, point it out in the comments and I’ll fill in the blanks.

It’s pretty complex to describe, though, so I’ll probably end up leaving some holes in the story. It happens to work around some frustrations I found with other solutions, but I kind of got on a kick of really wanting to make this work. Let me start this by saying that I’m fully aware that there are multiple ways to accomplish this feat, and that my choice may not have been the perfect one.
