Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run "launchctl" command under specific user using NSTask in OS X

My application is launching under root and I need to be able to unload processes using NSTask and launchctl Here is a code I do:

    NSPipe *pipe = [NSPipe pipe];

    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath: @"/bin/launchctl"];
    [task setCurrentDirectoryPath:@"/"];
    [task setStandardError:pipe];

    NSLog(@"/bin/launchctl unload %@", plistAutostartLocation);

    NSArray *arguments;
    arguments = [NSArray arrayWithObjects: enableCommand, plistAutostartLocation, nil];
    [task setArguments: arguments];

    NSFileHandle * read = [pipe fileHandleForReading];

    [task launch];
    [task waitUntilExit];

If process need to be unload is launched under "root" then it unloads successfully if not the fails. The question is how to run "launchctl" under specific user (e.g. "myusername")?

Edit: In terminal if I want to run some command under specific user I do next and it works well:

su - myusername -c "ls /Users/myusername"

But when I try to run "launchctl" under specific user it fails:

su - myusername -c "launchctl load /Library/LaunchAgents/com.google.keystone.agent.plist"

It says: "nothing found to load"

like image 619
Serge Avatar asked Oct 31 '22 22:10

Serge


1 Answers

The failing of your last command is related to bootstrap namespaces, you are trying to load the agent in the wrong bootstrap namespace. The system is creating two different bootstrap namespaces, excerpt from Apple documentation:

It's worth noting the distinction between GUI and non-GUI per-session bootstrap namespaces. A GUI per-session bootstrap namespace is instantiated by the GUI infrastructure (loginwindow and WindowServer) when a user logs in via the GUI. A non-GUI per-session bootstrap namespace is created when a user logs in via SSH. While there is no fundamental difference between these namespaces, GUI-based services, like the Dock, only register themselves in GUI per-session bootstrap namespaces.

The su command is not launched in the same bootsrap namespace used by launchd.

There are two ways to run a command like the ones you want to run in the correct bootstrap namespace:

  • 10.10 and above: use launchctl asuser. This will run any command as it was launched by the specified <myusername>. It is worth to mention that you must run this as root, otherwise the command will fail. Your last command should be run like this:

     launchctl asuser <myusername> launchctl load "/Library/LaunchAgents/com.google.keystone.agent.plist"
    
  • 10.10 and below (since 10.11, SIP blocks this method): use launchctl bsexec. This requires a process id to retrieve the correct namespace in which to run another command. Also, you have to change the UID of the command manually, like this:

    launctl bsexec <pid> su -u <myusername> launchctl load "/Library/LaunchAgents/com.google.keystone.agent.plist"
    
like image 118
MathPlayer Avatar answered Nov 08 '22 14:11

MathPlayer