A few pointers about launchd and daemondo

Scott Haneda talklists at newgeo.com
Wed Nov 4 15:54:32 PST 2009

On Oct 30, 2009, at 2:55 PM, Panagiotis Atmatzidis wrote:

> Greetings,
> I've created my first launchd script. It's for OpenVPN2. Here are  
> the files I've created so far:

Just out of general curiosity, why use openVPN2?  I have been meaning  
to create a set of launchd items that start the built in VPN server on  
OS X.  It is really quite trivial with the inbuilt one.  Just start it  
up, give it a range, plop in some user:pass stuff, and you have a  
pretty nice VPN ready to go.

Apparently OS X Sever has a GUI for this, but OS X Client does not  
even claim to support it, though it does, and has been every day I am  
at the coffee shop :)

What advantages, and what am I missing out on by using this method  
over openVPN?

> devo:/opt/local/etc/LaunchDaemons root# ls -l org.macports.OpenVPN2/
> total 16
> -rwxr-xr-x   1 root  admin   957 Oct 30 23:06 OpenVPN2.wrapper
> -rw-r--r--   1 root  admin  1026 Oct 30 23:39  
> org.macports.OpenVPN2.plist

I do not like the way in which launchd scrips are put into play in  
MacPorts.  I never could understand the magic it was doing under the  
hood.  Since I consider them almost always starting servers of some  
form, I have moved to making my own normal .plist files and putting  
them in files/tld.app.binary.plist, so org.pure-ftpd.ftpd.plist or  

it would seem every time I needed to add something a little out of the  
ordinary I was getting stuck, whereas a plist is rather simple for me  
to understand, and just as easy to copy into place.

> The Wrapper
> [snip... removed wrapper code]	
> the .plist which is an: ln -sf /opt/local/etc/LaunchDaemons/ 
> org.macports.OpenVPN2/org.macports.OpenVPN2.plist /Library/ 
> LaunchDaemons/....
> ------------------------
> <?xml version='1.0' encoding='UTF-8'?>
> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
> "http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
> <plist version='1.0'>
> <dict>
> <key>Label</key><string>org.macports.OpenVPN2</string>
> <key>ProgramArguments</key>
> <array>
>        <string>/opt/local/bin/daemondo</string>
>        <string>--label=OpenVPN2</string>
>        <string>--start-cmd</string>
>        <string>/opt/local/etc/LaunchDaemons/org.macports.OpenVPN2/ 
> OpenVPN2.wrapper</string>
>        <string>start</string>
>        <string>;</string>
>        <string>--stop-cmd</string>
>        <string>/opt/local/etc/LaunchDaemons/org.macports.OpenVPN2/ 
> OpenVPN2.wrapper</string>
>        <string>stop</string>
>        <string>;</string>
>        <string>--restart-cmd</string>
>        <string>/opt/local/etc/LaunchDaemons/org.macports.OpenVPN2/ 
> OpenVPN2.wrapper</string>
>        <string>restart</string>
>        <string>;</string>
>        <string>--pid=none</string>
> </array>
> <key>Debug</key><false/>
> <key>Disabled</key><false/>
> <key>OnDemand</key><false/>
> <key>RunAtLoad</key><true/>
> <key>NetworkState</key><true/>
> </dict>
> </plist>
> However, there are two issues that I can't seem to be able to manage  
> right now.
> The first is that OpenVPN does not start at boot while the module is  
> loaded succesfully. When I login to the system and kill daemondo, it  
> relaunches itself and ovpn works fine. I suspect that the problem is  
> en0. Launchd tries to launchd openvpn before en0 comes up. That's  
> why I put the NetworkState keyword, but it does not seem to effect  
> *any* startup script. I had issues with dnsmasq also in the recent  
> past.

Are you sure that "NetworkState" is what you are looking for?
 From the man page  $man 5 launchd.plist

      NetworkState <boolean>
      If true, the job will be kept alive as long as the network is up,
      where up is defined as at least one non-loopback interface being  
      and having IPv4 or IPv6 addresses assigned to them.  If false, the
      job will be kept alive in the inverse condition.

NetworkState seems to be for keeping a job alive if the network is up,  
or keeping it dead if the network is down.  In your case, the app is  
not up yet.  My interpretation is that since the app never launched  
via launchd, the NetworkState test never comes into play.

I could be wrong, that is just how I am reading that man page.

I have a machine that loads a good chunk of IP's into an interface,  
and I need to make sure they are all up before moving on, or the final  
app will not work correctly.  I used `ipconfig` to make sure this  
happened in a "wrapper" script.

$man ipconfig
There is a lot of good stuff in there that could help you, even just  
the logging verbosity you can kick up and see what is going on.

The argument I used was

      waitall     Blocks until all network services have completed  
		 or have timed out in the process of configuring.
                  This is only useful for initial system start-up time
		 synchronization for legacy network services that are inca-
                  pable of dealing with dynamic network configuration  

If you were to add to your wrapper/start script:
/usr/sbin/ipconfig waitall

Then a basic test of the result of that command:
$? tests the last command ran, as far as I now, or set the result to a  
	if [[ $? -eq 0 ]]
		echo "Interfaces are NOT up"
		echo "Interfaces are up"

You probably have to put that in a while loop, and keep on trying  
until the interfaces are all up.  I would be inclined to put a little  
`sleep 1` in there to reduce the number of checks.  And a counter to  
make sure it does not accidentally run wild and keep running forever,  
sort of a safety net to exit.  If loopcount > 100 then exit type of  

> The second problem with this script is that when I unload it via  
> launchd it does not kill the process. Launchd unloads the script and  
> (probably) will not be launchd (if -w is added) in the next boot but  
> daemondo keeps running the process nevertheless. Is this a normal  
> behaviour?

I can not help on this one, unload should not only remove the job, but  
also stop it if it is running.  Though I find this very dependent on  
how the app itself works.  For example, I never seem to get a  
unloading of Apache2 to stop apache, which I assume is why there are  
tools like `apachectl stop`.

Apache spawns a number of processes of itself as needed, so to me, it  
make sense that launchd would only be able to track the initial app it  
started.  If I had a launchd item that started 10 copies of `find`,  
and then unloaded that launchd, I would imagine I would only stop 9 of  
those copies, the rest would keep running.  Or maybe I only stop the  
script itself, and all the copies of `find` keep running.

Maybe keep that in mind when working on this.

> I'm not *that* worried about the second. I'd prefer to have a  
> solution about the first one, which is the most important for me.

I would get a standalone launchd plist calling a script that does the  
work of starting the job as you want it.  While you can jam all sorts  
of commands and scripting into a launchd file, I find them to be messy  
and hard to debug.  You have to take special care with spaces, quotes,  
and paths and some other little "gotcha" areas.

I find if I can get a local script to start my app as I want it, then  
I just have one command in my plist to call that script, things are a  
lot cleaner.

Once you have that working, it is up to you to use `daemondo` or not.

Hopefully some of this is of help.  I am not very versed in much of  
this, so it is mostly just to point you in a direction that hopefully  
gets you where you need to go.
Scott * If you contact me off list replace talklists@ with scott@ *

More information about the macports-users mailing list