macOS launchctl commands

Creating this post so I can refer to it later when I want to do some launchctl related thing. This tool is used to control launchd which is the equivalent of systemd and other service managers on the macOS.

To view services

Important to specify sudo else you only get your own services. Notice the difference in output between these two:

To view the details of a particular service

Starting and stopping services

You stop and start to restart.

Types of services

There’s two types of services as far as launchd is concerned.

  • Agents are services run for the logged in user (the output of my launchctl list command above without a sudo). They obviously require someone to be logged in to run.
    • These are stored in ~/Library/LaunchAgents (empty on my system) and /Library/LaunchAgents (on my system ssh-agent is the only one I recognize, but there’s a whole bunch more) and /System/Library/LaunchAgents (on my system I have iStat Menu, Karabiner, Citrix WorkSpace, etc.).
    • It would appear that /System/Library/LaunchAgents have agents which have a GUI presence while /Library/LaunchAgents are GUI-less?
  • Daemons are services run by the system either as the root user or any other username specified in the service definition. They don’t require anyone to be logged in.
    • These are stored in /System/Library/LaunchDaemons (the ones provided by Apple) and /Library/LaunchDaemons (the ones from 3rd parties; on my system I have Karabiner, iStat Menu, Docker – presumeably the user agents talk to these).

Service Definitions

Service Definitions are stored as .plist files in the above directories. There doesn’t seem to be easy way of finding the .plist file of a service. The closest I could find was do a launchctl dumpstate and grep for the service name.

That is a bit of a hit and miss I suppose.

A better way is to use the print subcommand:

The argument to be this has to be system or user (depending on whether it’s a system service or user service) followed by the service name, with a / in between.

Thus for the SSH agent, the above command would be:

Loading (Installing), Unloading (Uninstalling), Enabling, Disabling services

It’s obvious what they do. I hadn’t realized you need to load/ install and enable separately. Thought it would be just one step.

Loading & Uninstalling require the path to the .plist file. The other two require the service to be called with a system/ or user/ prefix as above.

You can also install/ load a service and give it a kick to start (maybe you installed and the service is set to disabled so you’d like to do a one-time start):

The kickstart subcommand can take -p as a switch to print the PID or -k to kill any currently running instance before starting the new one.

If you want to reload a service after its .plist file is changed you have to unload and load it. Simply enabling/ disabling or starting/ stopping won’t help. (It’s like systemctl daemon-reload).

Disabled Services

You can find disabled services with the print-disabled subcommand. This requires you to specify system to show the status for system services; or user/<uid> for the UID (User ID) in question (there’s other options too but I am going to skip that for now, check launchctl(1)).

Examples:

Here Karabiner is not disabled as its value is false. The others are actually disabled. Bit confusing, eh?

Another one:

That gives login associations too. Not sure what that is.

An interesting aside on disabled services. Here’s what my SSH .plist has:

Whoa, it’s disabled? But it’s not, coz I can access SSH.

Turns out this is ignored? From the manpage, under the load section we have the following:

Hmm, so 1) you’d do load -w to load/ install a .plist file and have it not be disabled even though the file might say so, and 2) more importantly the Disabled key in the .plist file is irrelevant as the actual state is stored in some other place. Weird.

Basically, stick with the print-disabled sub-command rather than the .plist file.

Elsewhere

For when this blog post isn’t enough for me: