I need to be able to start/stop a per-session GUI agent from a root level daemon.
Similar issues are discussed here, here and here.
What I want to be able to do is basically
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
but this only starts/stops one instance and it runs as root in the current GUI session. If I leave the sudo off there start I get
task_for_pid() (os/kern) failure
Couldn't switch to new bootstrap port: (ipc/send) invalid port right
I've tried messing around with a variety of other permutations of bsexec (including calling a secondary script from bsexec with the load/unload command), but I can never get the instance to start as anything other than root and never in another GUI session.
I also tried messing around with su - <user> ...
and sudo -u <user> ...
, but had no luck there either (as many people have discussed in the above linked articles and elsewhere).
Does anybody have any thoughts?
EDIT: I tried doing this with a wrapper tool as suggested below by Graham Lee, but I get the following error:
launch_msg(): Socket is not connected
This is the command line command, wrapper, and script I'm using (501 is the userid and 63093 the pid of launchd for another user logged in to the system):
Command line:
sudo launchctl bsexec 63093 /path/TestSetUIDAndExecuteTool 501 /path/LoadBillingDialogAgent
Wrapper:
#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;
}
Script:
/bin/launchctl load -S Aqua /Library/LaunchAgents/com.company.agent.plist
Using launchctl bsexec
is correct, but you need to launch a wrapper tool which drops UID to the target user before running the 'real' agent executable. Oh, and it's probably better to look for loginwindow
processes, as those are the leaders of the login sessions (though launchd
is very likely to work too).