问题 使用GUI会话为所有用户启动/停止launchd代理


我需要能够从根级别守护程序启动/停止每会话GUI代理。

讨论了类似的问题 这里这里 和 这里

我希望能够做的基本上是

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的各种其他排列(包括使用load / unload命令从bsexec调用辅助脚本),但我永远不会让实例以root身份启动,而不会在另一个GUI会话中启动。

我也试过搞乱 su - <user> ... 和 sudo -u <user> ...,但也没有运气(正如许多人在上面的链接文章和其他地方讨论的那样)。

有没有人有任何想法?

编辑: 我尝试使用Graham Lee下面建议的包装工具执行此操作,但是我收到以下错误:

launch_msg(): Socket is not connected

这是我正在使用的命令行命令,包装器和脚本(501是用户ID,63093是另一个登录到系统的用户的launchd的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

7431
2017-07-09 21:50


起源



答案:


运用 launchctl bsexec 是正确的,但您需要启动一个包装工具,在运行“真正的”代理可执行文件之前将UID丢弃给目标用户。哦,这可能更适合寻找 loginwindow 进程,因为那些是登录会话的领导者(但是 launchd 很可能也会工作)。


6
2017-07-09 21:55



当我这样做时(参见上面的包装工具编辑),我得到了一个 launch_msg(): Socket is not connected 错误 - Lawrence Johnston
我是否以正确的方式放弃了UID? - Lawrence Johnston
实际上,我想出了这一个。我设置的测试环境似乎与真正的根级守护进程并不完全相同。谢谢您的帮助。 - Lawrence Johnston


答案:


运用 launchctl bsexec 是正确的,但您需要启动一个包装工具,在运行“真正的”代理可执行文件之前将UID丢弃给目标用户。哦,这可能更适合寻找 loginwindow 进程,因为那些是登录会话的领导者(但是 launchd 很可能也会工作)。


6
2017-07-09 21:55



当我这样做时(参见上面的包装工具编辑),我得到了一个 launch_msg(): Socket is not connected 错误 - Lawrence Johnston
我是否以正确的方式放弃了UID? - Lawrence Johnston
实际上,我想出了这一个。我设置的测试环境似乎与真正的根级守护进程并不完全相同。谢谢您的帮助。 - Lawrence Johnston


看起来每个用户启动的实例不在与从终端启动的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。 是的,代理商将会启动,但正如我从测试中看到的那样,不是在正确的背景下。


4
2018-05-28 19:06



也不会在10.9上工作。代理将在不同的上下文中启动,例如,它无法显示UI(没有错误,只是不可见)。 - esmirnov


根据这里的讨论和 这个脚本,我认为不应该使用包装工具。这两个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”替换为启动代理的名称。

加载代理

#!/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,这应该没问题。


3
2017-07-21 09:00



对于El Capitan(10.11)以下应该工作 launchctl bootstrap gui/$uid /Library/LaunchAgents/myAgent.plist - vrrathod
谢谢@vrrathod,很高兴知道。 - TheDarkKnight


我有同样的问题。 要解决此问题,请在launchd下使用pid,启动启动进程的pid。

你提交'launchctl bsexec'的pid用于找到合适的bootstrap。如果你使用launchd的pid(来自用户上下文)而不是你在root launchd bootstrap中的工作。如果你使用pe。用户的Finder或Dock pid,您可以在这个“每用户”引导程序中工作


1
2017-09-02 09:40