OS X launch daemon/agent configuration: Program and ProgramArguments

I tried to configure a launch daemon to OS X but stumbled on a configuration issue that in my opinion isn’t explained very clearly in the documentation. Perhaps I’m the only one to find it unclear but in the case that somebody else should get stuck with the same issue I’ll explain it here.

This issue is about the Program and ProgramArguments keys in the launchd.plist configuration file. Those keys specify the command that should be run as the launch daemon or agent. I won’t explain anything general about launch daemons or agents here since it’s documented in many places already.

The Program and ProgramArguments keys

Here is the relevant documentation that I used first in order to get a launch daemon configured.

From ‘man launchd.plist

Program <string>
This key maps to the first argument of execvp(3).  If this key is missing, then the first element of the array of strings provided to the ProgramArguments will be used instead.  This key is required in the absence of the ProgramArguments key.

ProgramArguments <array of strings>
This key maps to the second argument of execvp(3).  This key is required in the absence of the Program key. Please note: many people are confused by this key. Please read execvp(3) very carefully!

That documentation refers the reader to execvp(3) documentation. Which is stupid in my opinion – it could just explain the parameters there in place and not make half-arsed notes about people getting confused. If users get confused while reading documentation it’s a sign that the documentation should be improved!

Anyway, the Program and ProgramArguments repeat execvp‘s arguments. So let’s read execvp(3) carefully then.

From ‘man execvp‘ (emphasis mine)

int execvp(const char *file, char *const argv[]);
[–]
The initial argument for these functions is the pathname of a file which is to be executed.
[–]
The [–] execvp() [–] function[s] provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the file name associated with the file being executed. [–]

So the first argument (file) is the program to be executed. The second argument (argv) contains the arguments for the program and of those the first one is by convention the name of the program that is executed. So pretty much the same as the ‘file‘ argument!

I know that programs usually get the name of the program file as the first command line argument. And perhaps there is a valid use case for an interface like execvp. But I can’t understand why Apple considered this interface to be so suitable for the launchd.plist configuration files that they decided to carefully replicate it.

How to use the Program and ProgramArguments parameters

So here’s my newly developed guidelines for using these parameters.

  1. never use both Program and ProgramArguments parameters in the same configuration file.
  2. if your program is run without arguments you can use Program and save some typing. But still
  3. you can always use ProgramArguments. The first array member shall be the executable name.

Examples

Using only Program

<key>Program</key>
<string>/bin/date</string>

This runs the ‘date‘ program without any arguments as expected.

Using only ProgramArguments

<key>ProgramArguments</key>
<array>
    <string>/usr/bin/touch</string>
    <string>/tmp/test</string>
</array>

This runs ‘touch /tmp/test‘ as expected.

Using both Program and ProgramArguments (don’t do this unless you really understand what you are asking for and need it)

<key>Program</key>
<string>/usr/bin/touch</string>
<key>ProgramArguments</key>
<array>
    <string>/bin/date</string>
    <string>/tmp/test</string>
</array>

This does not run ‘touch /bin/date /tmp/test‘ as one might think. This runs the equivalent of ‘touch /tmp/test‘. The first member in the arguments array (/bin/date) is effectively dropped. Better avoid this since it’s so confusing.

Leave a Reply

Your email address will not be published.