当您的代码作为LaunchDaemon运行时,如何在不重新启动的情况下首次启动LaunchAgent?

时间:2011-10-25 21:45:18

标签: macos daemon nstask launchd

我有一个LaunchDaemon。运行时,它会检查是否安装了SIMBL。如果未安装SIMBL,则使用NSTask在SIMBL.pkg上运行/ usr / sbin / installer。

然后SIMBL的postflight脚本尝试运行launchctl load命令立即启动SIMBL的LaunchAgent:

sudo -u "$USER" -- /bin/launchctl load -F -S Aqua -D user "${LAUNCHD_PLIST}"

这失败了,因为我的LaunchDaemon的NSTask环境没有设置$ USER。

如果我的守护进程使用系统配置框架检测当前用户并使用setEnvironment将其传递给NSTask,则startctl会对我产生错误:

Bug: launchctl.c:2325 (23930):13: (dbfd = open(g_job_overrides_db_path, O_RDONLY | O_EXLOCK | O_CREAT, S_IRUSR | S_IWUSR)) != -1

根据定义,我意识到守护进程不应该在用户会话中运行。出于同样的原因,Apple似乎推荐LaunchAgents作为LaunchDaemons的辅助对象,以进行该用户会话工作。有没有办法让这样的代理人立即投入运行?

我将所有的.plists放在正确的位置(它们在重新启动后开始运行,下次启动时会执行常规加载)所以我的第一个想法是告诉launchctl重新加载。但是all the code to do that is commented out in launchctl.c

//  { "reload",         reload_cmd,             "Reload configuration files and/or directories" },

...

 * In later versions of launchd, I hope to load everything in the first pass,
 * then do the Bonjour magic on the jobs that need it, and reload them, but for now,
 * I haven't thought through the various complexities of reloading jobs, and therefore
 * launchd doesn't have reload support right now.

1 个答案:

答案 0 :(得分:6)

launchd让我疯狂......

经过大量的研究和实验后,这就是我在10.5 +上的表现:

# If possible, tell launchd to start the LaunchAgent. This will fail on 10.4.
# $F[0] is the pid
# $F[1] is the username
# $F[2] is the first word of the command
ps -ww -A -opid,user,command | \
  perl -nae 'if($F[2] =~ /\bloginwindow\b/) { system(
    qq(launchctl bsexec $F[0] su $F[1] -c "launchctl load -w <your_plist>"))
}'

我发现无法直接在10.4上实现这一点。我在10.4上作弊,只是运行LaunchAgent运行的东西,即使它有一个GUI而且你不应该这样做(无论如何你可以在10.4-10.6中;你不能在10.7中)。在10.4上,LaunchAgent在下次重新启动后工作正常。

上面的代码查找loginwindow个进程,并使用bsexec在这些上下文中运行命令。请记住,通过快速用户切换,可以有多个上下文。

一些有用的链接:

IMO,launchd是Apple有史以来最糟糕的“好主意”之一。这个想法非常有用,但API太可怕了。