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.luttg...@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:subscri...@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


_______________________________________________
launchd-dev mailing list
launchd-dev@lists.macosforge.org
https://lists.macosforge.org/mailman/listinfo/launchd-dev

Reply via email to