5

我有一个 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,launchctl 会在我身上出现错误:

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 的辅助对象,以完成用户会话工作。有没有办法让这样的代理立即启动并运行?

我将所有 .plist 都放在正确的位置(它们在重新启动后开始运行,下次 launchctl 进行常规加载)所以我的第一个想法就是告诉 launchctl 重新加载。但是所有执行此操作的代码都在 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.
4

1 回答 1

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是苹果公司部署过的最糟糕的“好主意”之一。这个想法非常有用,但是 API 很糟糕。

于 2011-10-25T22:04:07.547 回答