从 root 帐户启动应用程序

发布于 2024-10-05 10:54:13 字数 478 浏览 9 评论 0 原文

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

部署更新时,我需要更新守护程序(这很简单)并更新 GUI。我希望能够退出 GUI,替换应用程序,并在当前运行的每个用户帐户上重新启动它。我想从守护进程中完成所有这一切,当然该守护进程是以 root 身份运行的。

我怎样才能: 1) 作为root,退出然后在另一个用户界面中重新启动应用程序? 2)作为 root,退出然后为当前登录的每个用户重新启动特定的登录项?

我尝试过搜索,有很多讨论,包括这个 类似的问题,但似乎没有可用的可行解决方案。

非常感谢任何帮助。

I am developing a Cocoa GUI app that has an Objective-C daemon. The daemon is launched by LaunchDaemon, the GUI is launched using loginItems for each user.

When an update is deployed I need to update the daemon, which is simple, and update the GUI. I would like to be able to exit the GUI, replace the app, and relaunch it on each user account that it is currently running on. I would like to do all this from the daemon which of course is running as root.

How can I either:
1) as root, quit and then re-launch an app in another user's interface?
2) as root, quit and then re-launch a particular loginItem for each user currently logged it?

I have tried searching and there are lots of discussions including this similar question, but there doesn't appear to be a working solution available.

Any help is greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怂人 2024-10-12 10:54:14

我相信 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 Agent 类型进程(后台进程,在用户级别运行,每个用户帐户都有自己的实例)。

有关详细信息:

技术说明 TN2083 守护程序和代理

Root 和登录会话

创建launchd 守护进程和代理

I believe NSDistributedNotificationCenter should work for this. Note that using NSDistributedNotificationCenter to communicate between processes in different user accounts does not, in and of itself, require root privileges.

To help with coordination between user accounts, it might help to distinguish which instance of the GUI app and daemon are currently active and in control, and which instances are passive. You can use NSWorkspace's notifications (NSWorkspaceSessionDidBecomeActiveNotification, NSWorkspaceSessionDidResignActiveNotification) to determine when a user switches between user accounts, etc. and have your instances set themselves accordingly.

Say your GUI app and daemon have instances running in 3 different user accounts. If in the active user account, you want to begin the update process, you could use NSDistributedNotificationCenter to easily tell all other instances to shutdown immediately, for example. To do that, you'd define something like the following.

In an .h file, declare the names of your different notifications:

extern NSString * const MDShouldTerminateImmediatelyNotification;

in (an) implementation file, create the names, and set the class up to be interested in a distributed notification by that name, etc.:

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];
    }
}

In the class that would initiate the update process, you'd do something like this to send the notification:

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

}

That should at least be a beginning to work with, I'd think....

Actually, if you are talking about having one single daemon instance running as root that does everything in all user accounts, you may need to consider factoring that part out into a Launchd Agent type process (background process, runs at user level, each user account would have its own instance).

For more info:

Technical Note TN2083 Daemons and Agents

Root and Login Sessions

Creating launchd Daemons and Agents

心清如水 2024-10-12 10:54:14

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

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

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

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

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

效果惊人!

感谢您的建议!

So I used a support request with Apple to get the best answer, combined with some online research.

The basic plan of attack was to have each GUI instance relaunch itself when the daemon told it to.

Firstly, I had the daemon replace the GUI's bundle (.app folder). You can do this while the app is still running amazingly. That's where Apple support helped out. I'm still amazed that an app can be replaced while it's running but they told me to do this and that it would be ok.

Then I had the daemon fire a DistributedNotification to the GUI with a message to restart itself. To get the GUI to respond correctly I created restarter class which would grab it's own pid and bundle path, then I built a shell script in memory that killed the pid, waited 10 seconds and then performed a shell "open bundlepath.app" and it relaunched.

I used NSTask to invoke the "in memory" shell script, which was really just a @"kill %@; sleep 10; open %@", pid, bundlePath....

Works amazingly!

Thanks for your suggestions!

ま柒月 2024-10-12 10:54:14

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

Look for "authorization services" in Apple's documentation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文