0

On OS X 10.9, I'm running a script (e.g., ~/bin/run.sh) via my user crontab (added using crontab -e). This script, under some specific conditions (not related to this question), will run the following command to launch a menubar application:

launchctl load /Library/LaunchAgents/com.opendns.osx.RoamingClientMenubar.plist

When I run this command (either ~/bin/run.sh or the launchctl statement above directly) from the command line normally, the menubar item launches fine.

When this command is run via crontab (again, directly or via ~/bin/run.sh), I receive the message nothing found to load in the cron output (in my mail).

Question: why does this fail when run via cron but not when run on the command line?

I have tried executing it via cron in the simplest way possible:

* * * * * launchctl load /Library/LaunchAgents/com.opendns.osx.RoamingClientMenubar.plist

This doesn't work (I get nothing found to load).

I have tried emulating the cron environment:

  1. Capturing the cron environment by having this execute in cron:

    env > ~/cronenv
    
  2. Then opening a shell with this environment:

    env - `cat ~/cronenv` /bin/sh
    
  3. And finally running the command:

    launchctl load /Library/LaunchAgents/com.opendns.osx.RoamingClientMenubar.plist
    

It runs find in these conditions (I wouldn't expect it to, if something in the cron environment is the culprit).

I have tried running it from crontab as sudo. Nope (nothing found to load).

I have tried running it from crontab with launchctl load -F and launchctl load -w. No luck (nothing found to load).

Permissions on the plist file are:

-rw-r--r--  1 root  wheel  561 Apr 13 20:55 /Library/LaunchAgents/com.opendns.osx.RoamingClientMenubar.plist

What's going on?

(BTW, I know it may seem silly to run a script with a launchctl job from within cron, but because it is run within a shell script it's prevented from being a 100% launchctl-controlled process.)

Update: as requested here is the script that is being run (I've been calling it ~/bin/run.sh), the line in question being #29, and here is the contents of the plist.

Update: the specific solution that works for me, based on @mateusz-szlosek's suggestion to use bsexec, looks like this:

sudo launchctl bsexec "$(ps -axwww | grep Dock | grep -v grep | awk {'print $1'};)" sudo -u $USER launchctl load /Library/LaunchAgents/com.opendns.osx.RoamingClientMenubar.plist

The first sudo is required otherwise the error Couldn't switch to new bootstrap port occurs. The second sudo is to execute launchctl as $USER. The first argument to bsexec is a parent process ID who's context will be used to launch the new process. $(ps -axwww | grep Dock | grep -v grep | awk {'print $1'};) returns the pid of the Dock process, which loads somewhat early in the launchd hierarchy, but under user context.

  • How about bsexec to launch Agent in proper context? Have You checked stackoverflow.com/questions/1106638/… ? – Mateusz Szlosek Aug 3 '15 at 21:11
  • cron has a option load : be verbose when loading crontab files. So it's confusing launchctl's load options with it's own. Try using $() or ` around the command. – user14492 Aug 6 '15 at 12:28
  • @user14492 I don't think that's it because, despite my example above attempting to execute launchctl directly via cron, it actually is cron calling a bash script (~/bin/run.sh) which contains the launchctl commands. – Quinn Comendant Aug 7 '15 at 16:20
  • 3
    Modify your question with the content of run.sh and the plist you want launchd to load. – fd0 Aug 8 '15 at 20:44
  • isn't launchctl started by root, and cron by the user? – CousinCocaine Aug 8 '15 at 21:01
1

The problem is execution context and mach bootstrap in OS X (more info on Apple documentation worth reading). You need to simulate execution from proper context. In OS X it's done using launchctl bsexec command. From TheDarkKnight's answer to Starting/stopping a launchd agent for all users with GUI sessions you have:

Unload Agents

#!/bin/bash
for id in `ps aux | grep -v grep | grep MyAgent | awk {'print $2'}`
do
    launchctl bsexec $id launchctl unload /Library/LaunchAgents/myAgent.plist
done

Replace MyAgent with the name of your Launch Agent.

Load Agents

#!/bin/bash
for pid_uid in $(ps -axo pid,uid,args | grep -i "[l]oginwindow.app" | awk '{print $1 "," $2}'); do

    pid=$(echo $pid_uid | cut -d, -f1)
    uid=$(echo $pid_uid | cut -d, -f2)

    launchctl bsexec "$pid" chroot -u "$uid" / launchctl load /Library/LaunchAgents/myAgent.plist
done

On OS X 10.11 and Later

From the comment by vrrathod about OS X 10.11, El Capitain, use:

launchctl bootstrap gui/$uid /Library/LaunchAgents/myAgent.plist

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .