【问题标题】:Launching app from root account从 root 帐户启动应用程序
【发布时间】:2011-05-18 04:19:39
【问题描述】:

我正在开发一个具有 Objective-C 守护进程的 Cocoa GUI 应用程序。守护进程由 LaunchDaemon 启动,GUI 由每个用户的 loginItems 启动。

部署更新后,我需要更新守护进程,这很简单,并更新 GUI。我希望能够退出 GUI,替换应用程序,然后在当前运行的每个用户帐户上重新启动它。我想通过以 root 身份运行的守护进程来完成所有这些工作。

我该怎么做: 1) 以 root 身份退出,然后在另一个用户界面中重新启动应用程序? 2) 以 root 身份退出,然后为当前登录的每个用户重新启动特定的 loginItem?

我已经尝试过搜索并且有很多讨论,包括这个similar question,但似乎没有可用的有效解决方案。

非常感谢任何帮助。

【问题讨论】:

    标签: objective-c cocoa launchd


    【解决方案1】:

    因此,我使用 Apple 的支持请求来获得最佳答案,并结合一些在线研究。

    攻击的基本计划是让每个 GUI 实例在守护程序告诉它时重新启动它自己。

    首先,我让守护程序替换了 GUI 的包(.app 文件夹)。您可以在应用程序仍在惊人地运行时执行此操作。这就是 Apple 支持提供帮助的地方。我仍然对可以在运行时更换应用程序感到惊讶,但他们告诉我这样做并且没问题。

    然后我让守护进程向 GUI 发出 DistributedNotification 并显示一条消息以重新启动自身。为了让 GUI 正确响应,我创建了重新启动器类,它将获取它自己的 pid 和包路径,然后我在内存中构建了一个 shell 脚本来杀死 pid,等待 10 秒,然后执行一个 shell“打开 bundlepath.app”,然后它重新启动。

    我使用 NSTask 调用“内存中”的 shell 脚本,它实际上只是一个 @"kill %@; sleep 10; open %@", pid, bundlePath....

    效果惊人!

    感谢您的建议!

    【讨论】:

      【解决方案2】:

      我相信 NSDistributedNotificationCenter 应该可以解决这个问题。请注意,使用 NSDistributedNotificationCenter 在不同用户帐户中的进程之间进行通信本身并不需要 root 权限。

      为了帮助用户帐户之间的协调,它可能有助于区分 GUI 应用程序和守护程序的哪些实例当前处于活动状态且受控制,以及哪些实例是被动的。您可以使用 NSWorkspace 的通知(NSWorkspaceSessionDidBecomeActiveNotification、NSWorkspaceSessionDidResignActiveNotification)来确定用户何时在用户帐户之间切换等,并让您的实例进行相应的设置。

      假设您的 GUI 应用程序和守护程序在 3 个不同的用户帐户中运行实例。例如,如果在活动用户帐户中,您想开始更新过程,您可以使用 NSDistributedNotificationCenter 轻松告诉所有其他实例立即关闭。为此,您需要定义如下内容。

      在 .h 文件中,声明不同通知的名称:

      extern NSString * const MDShouldTerminateImmediatelyNotification;
      

      在(一个)实现文件中,创建名称,并将类设置为对该名称的分布式通知感兴趣,等等:

      NSString * const MDShouldTerminateImmediatelyNotification = @"MDShouldTerminateImmediately";
      
      
      - (id)init {
         if (self = [super init]) {
             [[NSDistributedNotificationCenter defaultCenter]
             addObserver:self
             selector:@selector(shouldTerminateImmediately:)
             name:MDShouldTerminateImmediatelyNotification
             object:nil];
         }
         return self;
      }
      
      - (void)dealloc {
         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
         [super dealloc];
      }
      
      - (void)shouldTerminateImmediately:(NSNotification *)notification {
         if (ourInstanceIsInControl == NO) {
           [NSApp terminate:nil];
          }
      }
      

      在将启动更新过程的类中,您可以执行以下操作来发送通知:

      - (void)beginUpdate {
         [[NSDistributedNotificationCenter defaultCenter]
          postNotificationName:MDShouldTerminateImmediatelyNotification
             object:[self description] // or just nil
             userInfo:nil
             options:NSNotificationDeliverImmediately | NSNotificationPostToAllSessions];
          // continue
      
      }
      

      我认为这至少应该是一个开始工作的开始......

      实际上,如果您正在谈论让一个守护进程实例以 root 身份运行并在所有用户帐户中执行所有操作,您可能需要考虑将该部分分解为 Launchd 代理类型进程(后台进程,在用户级别运行,每个用户帐户都有自己的实例)。

      更多信息:

      Technical Note TN2083 Daemons and Agents

      Root and Login Sessions

      Creating launchd Daemons and Agents

      【讨论】:

      • 谢谢。我现在有很多阅读!我已经在使用分布式对象进行通信。有一个守护进程实例,并且可能为每个用户提供一个 GUI 实例。我可以通过发送通知让 GUI 退出,这不是真正的问题。真正的问题是一旦守护程序指示 GUI 退出然后更新实际的 GUI 应用程序包,我怎样才能让守护程序重新启动 GUI 的所有实例而不打扰用户? - 我现在正在阅读你的链接,看看我是否能在其中找到一些东西。再次感谢。
      • 非常感谢您包含NSNotificationPostToAllSessions,我想在不同的流程所有者之间获得分布式通知! :)
      【解决方案3】:

      在 Apple 的文档中查找“授权服务”。

      【讨论】:

      • 谢谢,我看过你提到的“授权服务”,但它似乎用于将权限提升到 root。我已经在后台以 root 身份运行,需要从我的 root 特权后台任务在用户登录会话中启动应用程序。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      • 2021-04-15
      • 1970-01-01
      • 2012-05-10
      • 1970-01-01
      相关资源
      最近更新 更多