为所有具有 GUI 会话的用户启动/停止启动代理
Posted
技术标签:
【中文标题】为所有具有 GUI 会话的用户启动/停止启动代理【英文标题】:Starting/stopping a launchd agent for all users with GUI sessions 【发布时间】:2010-11-09 13:11:02 【问题描述】:我需要能够从根级守护进程启动/停止每个会话的 GUI 代理。
here、here 和 here 讨论了类似的问题。
我想做的基本上是
for num in `ps ax | grep [s]bin/launchd | cut -c 1-5`;
do
if [ $num -ne 1 ];
then
sudo launchctl bsexec $num launchctl (un)load -S Aqua /Library/LaunchAgents/com.mycompany.mydaemon.plist;
fi;
done
但这只会启动/停止一个实例,并在当前 GUI 会话中以 root 身份运行。如果我离开 sudo 那里开始我得到
task_for_pid() (os/kern) failure
Couldn't switch to new bootstrap port: (ipc/send) invalid port right
我尝试过处理 bsexec 的各种其他排列(包括使用加载/卸载命令从 bsexec 调用辅助脚本),但我永远无法让实例以 root 以外的任何方式启动,并且永远不会在另一个 GUI 会话。
我也尝试过使用su - <user> ...
和sudo -u <user> ...
,但也没有运气(正如许多人在上面链接的文章和其他地方所讨论的那样)。
有人有什么想法吗?
编辑: 我尝试使用 Graham Lee 下面建议的包装工具执行此操作,但出现以下错误:
launch_msg(): Socket is not connected
这是我正在使用的命令行命令、包装器和脚本(501 是用户 ID,63093 是另一个登录到系统的用户的启动 PID):
命令行:
sudo launchctl bsexec 63093 /path/TestSetUIDAndExecuteTool 501 /path/LoadBillingDialogAgent
包装器:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (argc != 3)
NSLog(@"Tool called with improper arguments");
return -1;
int uid = [[NSString stringWithUTF8String:argv[1]] intValue];
// TODO: REMOVE
NSLog(@"Setting uid to |%i|", uid);
setuid(uid);
// TODO: REMOVE
char *command = (char *)argv[2];
NSLog(@"Executing command |%s|", command);
system(command);
[pool drain];
return 0;
脚本:
/bin/launchctl load -S Aqua /Library/LaunchAgents/com.company.agent.plist
【问题讨论】:
【参考方案1】:根据此处和this script 的讨论,我认为不需要包装工具。这两个 bash 脚本也可能对其他人有所帮助。
卸载代理
#!/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
将“MyAgent”替换为您的 Launch Agent 的名称。
加载代理
#!/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
从根守护程序调用,这将为所有登录用户加载和卸载 myAgent.plist 中引用的启动代理。
请注意,由于 OS X El Capitan (10.11) 中的“无根”,使用 bsexec 可能不再有效,但直到 10.10,这应该没问题。
【讨论】:
对于 El Capitan (10.11) 以下应该可以工作launchctl bootstrap gui/$uid /Library/LaunchAgents/myAgent.plist
【参考方案2】:
我遇到了同样的问题。 要解决这个问题,请使用“under”launchd 的 pid,即 launchd 已启动的进程的 pid。
您提交给“launchctl bsexec”的 pid 用于查找正确的引导程序。如果您使用 launchd 的 pid(来自用户上下文),那么您在 root launchd 引导程序中工作。如果你使用 pe. Finder 或用户的 Dock pid,您可以在此“每用户”引导程序中工作
【讨论】:
【参考方案3】:看起来每个用户启动的实例与从终端启动的 launchctl 不在同一个引导命名空间中运行。
通过使用 Dock.app 作为 pid 捐助者和一些 sudo 魔法:
ps aux | grep Dock.app | grep -v grep | awk 'system("sudo launchctl bsexec "$2" sudo -u "$1" launchctl load -S Aqua /Library/LaunchAgents/com.my.agent.plist")'
可以在所有正在运行的会话中启动代理。
不整洁,但有效。
更新:不会在 10.7 上运行。是的,代理将被启动,但正如我从测试中看到的,不在正确的上下文中。
【讨论】:
也不适用于 10.9。代理将在不同的上下文中启动,例如,它不能显示 UI(没有错误,只是不可见)。【参考方案4】:使用launchctl bsexec
是正确的,但是在运行“真实”代理可执行文件之前,您需要启动一个将UID 放到目标用户的包装工具。哦,寻找loginwindow
进程可能会更好,因为它们是登录会话的领导者(尽管launchd
也很有可能工作)。
【讨论】:
当我这样做时(参见上面使用包装工具的编辑),我得到一个launch_msg(): Socket is not connected
错误
我是否以正确的方式删除 UID?
其实这个我想出来了。看来我设置的测试环境与真正的根级守护进程不太一样。感谢您的帮助。以上是关于为所有具有 GUI 会话的用户启动/停止启动代理的主要内容,如果未能解决你的问题,请参考以下文章