开发者

Launching app from root account

I am developing a Cocoa GUI app that has an Objective-C daemon. The daemon is launched by Laun开发者_运维技巧chDaemon, 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.


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


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!


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜