我正在开发一个具有Objective-C守护程序的Cocoa GUI应用程序。守护进程由LaunchDaemon启动,GUI使用loginItems为每个用户启动。
部署更新时,我需要更新守护程序,这很简单,并更新GUI。我希望能够退出GUI,替换应用程序,并在当前运行的每个用户帐户上重新启动它。我想从守护进程中做到这一切,守护进程当然是以root身份运行。
我怎么能: 1)以root身份退出然后在另一个用户界面重新启动应用程序? 2)以root身份退出,然后为当前登录的每个用户重新启动特定的loginItem?
我尝试过搜索,有很多讨论,包括similar question,但似乎没有可行的解决方案。
非常感谢任何帮助。
答案 0 :(得分:3)
我相信NSDistributedNotificationCenter应该为此工作。请注意,使用NSDistributedNotificationCenter在不同用户帐户中的进程之间进行通信本身并不需要root权限。
为了帮助协调用户帐户,可能有助于区分GUI应用程序和守护程序的哪个实例当前处于活动状态并处于控制状态,以及哪些实例是被动的。您可以使用NSWorkspace的通知(NSWorkspaceSessionDidBecomeActiveNotification,NSWorkspaceSessionDidResignActiveNotification)来确定用户何时在用户帐户之间切换等,并相应地设置您的实例。
假设您的GUI应用程序和守护程序在3个不同的用户帐户中运行实例。例如,如果在活动用户帐户中,您希望开始更新过程,则可以使用NSDistributedNotificationCenter轻松告知所有其他实例立即关闭。为此,您需要定义如下内容。
在.h文件中,声明不同通知的名称:
extern NSString * const MDShouldTerminateImmediatelyNotification;
在(a)实现文件中,创建名称,并将该类设置为对该名称等的分布式通知感兴趣:
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身份运行一个守护进程实例来执行所有用户帐户中的所有操作,您可能需要考虑将该部分分解为启动代理类型进程(后台进程,在用户级别运行,每个用户帐户都有自己的实例。
欲了解更多信息:
答案 1 :(得分:3)
因此,我使用Apple的支持请求获得最佳答案,并结合一些在线研究。
攻击的基本计划是让守护进程告诉它时每个GUI实例都重新启动。
首先,我让守护进程替换了GUI的bundle(.app文件夹)。您可以在应用仍然运行时做到这一点。这就是Apple支持的帮助。我仍然惊讶于应用程序在运行时可以被替换,但是他们告诉我这样做并且它会没问题。
然后我让守护进程向GUI发出一个DistributedNotification,并显示一条重新启动的消息。为了让GUI正确响应,我创建了重启器类,它将获取它自己的pid和bundle路径,然后我在内存中构建了一个shell脚本,杀死了pid,等了10秒然后执行了一个shell“open bundlepath.app”and it重新启动。
我使用NSTask来调用“内存”shell脚本,它实际上只是一个@“kill%@; sleep 10; open%@”,pid,bundlePath ....
工作惊人!
感谢您的建议!
答案 2 :(得分:0)
在Apple的文档中查找“授权服务”。