[launchd-dev] Yosemite not starting a system launch agent

James Bucanek subscriber at gloaming.com
Wed Jul 22 17:47:10 PDT 2015


Axel,

Thanks for the feedback. I apologize profusely for the delay in my 
response; I just had too many fires to deal with to give this the 
attention it needed.
> Axel Luttgens <mailto:axel.luttgens at skynet.be>
> June 25, 2015 at 5:20 AM
>> Le 23 juin 2015 à 03:31, James Bucanek a écrit :
>>
>> Greetings,
>>
>> Here's my problem in a nutshell:
>>
>> I have a simple scheduler daemon that runs in the background for logged in users. The problem is that it won’t start when installed in Yosemite (seems to start in all previous versions of OS X that use launchd).
>
> Hello James,
>
> If you allow, some questions for better understanding your context.
>
>
>> Here’s what my program does:
>
> Which program? Some kind of installer?

> More generally, could you describe what exactly you want to achieve, the workflow you are considering? We could then proceed with some trials at home. ;-)

I'm the author of QRecall (www.qrecall.com). It's a backup and archiving 
solution. The feature of interest to this discussion is the installation 
of a light-weight scheduler daemon that manages the automated execution 
of background tasks (backups, maintenance, validity checks, and so on).

The scheduler daemon can be installed in one of two ways. Most users 
install the scheduler as a regular user agent. The scheduler runs only 
when the user is logged in, and that satisfies 90% of my user base. I 
have had no problems installing, or uninstalling, the scheduler as a 
per-user agent on any version of OS X.

However, my program also offers the ability to run scheduled tasks when 
the user is logged out. (In fact, you can even schedule an action to 
start when you log in or out.) For this to work, the scheduler 
(obviously), has to run all the time. For that, I install the scheduler 
as a system-wide agent, instead of a per-user agent. I then employ a 
little trick to make sure the scheduler runs all the time. (See sidebar.)

-- Begin Sidebar --

The installation and running of the scheduler process as a per-user 
daemon is a bit involved. So for completeness, I've include a brief 
explanation of what's going on, although it shouldn't have any bering on 
the issue at hand. So feel free to skip this sidebar, if you're so 
inclined. Or, dig into it, if you think this is where the problem is.

I only run processes as root when absolutely necessary. The scheduler 
doesn't need to run as root; it needs to run as a regular user, and just 
for those users with QRecall installed. This is the area where there's a 
little impedance mismatch between QRecall and OS X.

OS X has the concept of a user agent, a system-wide agent, and a system 
daemon. What is doesn't have is the concept of a "user daemon": a 
process that runs, for a single user, as that user, and runs all the 
time, whether the user is logged in or not.

A long time ago, I posed this problem to the list and got a great 
suggestion (I think it was from Jim Luther) that has served me well for 
years:

(1) Install the scheduler as a system-wide agent. OS X will, by default, 
start a per-user instance of the process for every user that logs in.

(2) Have a global configuration file with a list of UIDs that should be 
running the scheduler all the time. When each per-user instance is 
started launchd, the scheduler simply consults the list. If it's current 
UID is on the list, it runs normally. If it isn't on the list, it 
terminates with a non-zero exit code.

(3) Install a system daemon (QRecallKicker). This daemon runs as root 
and reads the same global list of UIDs. It forks a copy of itself for 
each UID on the list. Each copy switches to running as that UID (which 
it can, 'cause it's root), and then does something that will cause OS X 
to need the bootstrap for that UID. I make a single call to 
bootstrap_look_up() for a non-existent service. The side effect is that 
OS X lazily creates the bootstrap for that UID. As part of the bootstrap 
creation, launchd starts the system-wide agents for that user.

The end result is this: When the system starts up, the QRecallKicker 
daemon "kicks" each of the users that should be running the "always on" 
version of the scheduler. This creates a bootstrap for that user and 
starts its instance of the scheduler agent. If any other users log in, 
their per-user instance of the scheduler is started, and immediately 
terminates because that user isn't on the list.

Once a bootstrap is created, it stay created until OS X restarts. So 
even if the user logs out, the scheduler continues running.

So that's the tortured story of why I install the scheduler as a 
system-wide agent.

-- End Sidebar --

>> 1) I install the com.qrecall.sheduler.plist in /Library/LaunchAgents/ (with the correct ownership and access privileges).
>
> So, do you install the plist yourself, beforehand, or is this done by aforementioned "program"?
>
> Could you show us that plist?
Here's the plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<true/>
</dict>
<key>Label</key>
<string>com.qrecall.scheduler</string>
<key>LimitLoadToSessionType</key>
<string>Background</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Application Support/QRecall/QRecallScheduler</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

>> 2) launchctl load -S Background /Library/LaunchAgents/com.qrecall.scheduler.plist
>> (successful)
>
> I guess this is done by the "program".
> Is it run by a logged in user (in the GUI) and running as that user?

Here's how the installation of the system-wide agent is accomplished:

(1) A privileged helper service has already been installed via SMJobBless().

(2) When the scheduler needs to be installed this way, the helper 
service is run (as root) with instructions to install the scheduler. The 
helper then:

(2)(a) Creates a .plist for the agent and copies it to 
/Library/LaunchAgents/com.qrecall.scheduler.plist, assigning it the 
correct ownership (root) and permissions (-rw-r--r--) required by launchd.

(2)(b) Runs the command 'launchctl load -S Background 
/Library/LaunchAgents/com.qrecall.scheduler.plist' as root.

(I've always wondered if I should rewrite these steps to use 
SMJobSubmit, but I now see that it's already deprecated.)

Note that this only happens when the scheduler needs to be installed in 
it's "daemon" version. Installation and removal of the per-user 
scheduler agent is handled entirely in userland by the main app.

In the past (pre-10.10), this has always installed and started the agent 
process immediately.

>> 3) launchctl start com.qrecall.scheduler
>> (exits with status 1)
>>
>> And the scheduler process is NOT started. All attempts to get it started through launchctl have failed.
>>
>> However, I know that it’s installed because if you simply restart the system the scheduler deamon starts running like a champ.
>
> For any user that logs in?
> Or for a specific user only?
The scheduler is only going to start for the user's it's configured to 
run for. Having said that, it doesn't appear to start for any user when 
the 'launchctl load' command is issued. Although, as I've mentioned 
before, if you restart the system and log back in, it starts and runs 
just fine.

I hope that this more clearly explains what I'm trying to accomplish. 
Actually, I've already accomplished it; this has been working for years 
but now seems (slightly) broken in 10.10 and 10.11.

If the solution is to use the new launchctl command syntax, I'll jump 
right on it ... just as soon as someone can help me understand what 
command I should be issuing.

I've left the original message attached, since it's been so long since I 
posted it.

Thanks again, in advance, for all the help and ideas.

James Bucanek
> James Bucanek <mailto:subscriber at gloaming.com>
> June 22, 2015 at 6:31 PM
> Greetings,
>
> Here's my problem in a nutshell:
>
> I have a simple scheduler daemon that runs in the background for 
> logged in users. The problem is that it won't start when installed in 
> Yosemite (seems to start in all previous versions of OS X that use 
> launchd).
>
> Here's what my program does:
>
> 1) I install the com.qrecall.sheduler.plist in /Library/LaunchAgents/ 
> (with the correct ownership and access privileges).
>
> 2) launchctl load -S Background 
> /Library/LaunchAgents/com.qrecall.scheduler.plist
> (successful)
>
> 3) launchctl start com.qrecall.scheduler
> (exits with status 1)
>
> And the scheduler process is NOT started. All attempts to get it 
> started through launchctl have failed.
>
> However, I know that it's installed because if you simply restart the 
> system the scheduler deamon starts running like a champ.
>
> Now ... I know what you're going to say: "Dude! You should be using 
> the new launchctl commands introduced in 10.10!"
>
> Tried that. I can't seem to get anywhere with it. Basically, I don't 
> know what I'm doing. I've read the man page for the new launchctl 
> about 20 times and I'm still not sure how I should be using it.
>
> For per-user agents, it seems pretty obvious that you would bootstrap 
> and address the service in the user/login/gui domain (i.e. launchctl 
> enable user/501/com.qrecall.scheduler).
>
> But I don't understand how to treat a system-wide user agent that 
> should be installed in /Library/LaunchAgents. Through trial and error, 
> I've discovered that I can address the current user's instance of the 
> scheduler process for commands like "kill". So this command works:
>
>     launchctl kill TERM user/501/com.qrecall.scheduler
>
> It sends a TERM signal and the daemon restarts.
>
> However, I can't find anyway to use the bootstrap command to install 
> it, the enable command won't start it, and the kickstart command says 
> there's no such service.
>
> It would seem to make sense that a system-wide agent would have to be 
> installed in the system domain, but this command:
>
>     sudo launchctl bootstrap system 
> /Library/LaunchAgents/com.qrecall.scheduler
>
> results in the error 
> "/Library/LaunchAgents/com.qrecall.scheduler.plist: Service cannot 
> load in requested session"
>
> Attempts to use any other domain in the command result in a syntax 
> error, so I'm completely stumped as to how the service should be 
> installed.
>
> I've figured out the I can enable and disable the per-user instance, 
> but it has no effect on the running process. In other words, disabling 
> won't halt the service and enabling it won't start it.
>
> And don't get me started on unbootstrap, which doesn't appear to be 
> implemented.
>
> So, at this point I have my feet planted firmly in mid-air...
>
> James Bucanek
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/launchd-dev/attachments/20150722/9027ac7b/attachment-0001.html>


More information about the launchd-dev mailing list