9

I want to run a process as root at user login.

I created /System/Library/LaunchAgents/eXist.plist with rx—r—r— permissions:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Disabled</key>
        <false/>
        <key>GroupName</key>
        <string>wheel</string>
        <key>Label</key>
        <string>eXist DB</string>
        <key>Program</key>
        <string>/Applications/eXist-db/bin/startup.sh</string>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardErrorPath</key>
        <string>/tmp/eXist DB.err</string>
        <key>StandardOutPath</key>
        <string>/tmp/eXist DB.out</string>
        <key>UserName</key>
        <string>root</string>
    </dict>
</plist>

However, my process continues to run as my user davea instead of root. Any ideas what else I need to do?

I’m using Mac 10.9.1.

1
  • It is odd to run something as root that should run after a particular user logs in - why can't the database start at boot as other root things would do
    – mmmmmm
    Commented Jan 10, 2016 at 18:07

6 Answers 6

8

To get a user LaunchAgent executing a script to run as root you have to do the following:

Modify the permissions of the script. Add the script (or command) to the sudoers file in the context of the user and finally add and load a proper launch agent plist.

This approach might create severe security holes!


In the example below I use the startup.sh of eXit-db 2.2:

  • Create a plist:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Disabled</key>
        <false/>
        <key>Label</key>
        <string>org.eXist_DB</string>
        <key>ProgramArguments</key>
        <array>
            <string>/usr/bin/sudo</string>
            <string>/Applications/eXist-db.app/Contents/Resources/eXist-db/bin/startup.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardErrorPath</key>
        <string>/tmp/eXist_DB.err</string>
        <key>StandardOutPath</key>
        <string>/tmp/eXist_DB.out</string>
        <key>ThrottleInterval</key>
        <integer>10</integer>
    </dict>
    </plist>
    

    and save it as org.eXist_DB.plist in ~/Library/LaunchAgents

    The last key:

        <key>ThrottleInterval</key>
        <integer>10</integer>
    

    might not be necessary. In my VM it was - for unknown reasons.

  • Modify the permissions of startup.sh:

    sudo chown root /Applications/eXist-db.app/Contents/Resources/eXist-db/bin/startup.sh
    sudo chmod 4755 /Applications/eXist-db.app/Contents/Resources/eXist-db/bin/startup.sh
    
  • Modify the sudoers file:

    sudo visudo
    
  • add a line to enable executing a command without the need to enter the sudo password

    # User privilege specification
    root    ALL=(ALL) ALL
    %admin  ALL=(ALL) ALL
    

    ->

    # User privilege specification
    root    ALL=(ALL) ALL
    %admin  ALL=(ALL) ALL
    your_user_name ALL=(ALL) NOPASSWD: /Applications/eXist-db.app/Contents/Resources/eXist-db/bin/startup.sh
    
  • Finally enter:

    launchctl load -w ~/Library/LaunchAgents/org.eXist_DB.plist
    

    to load and start the launch agent

1
  • this is a superb answer, have all the upvotes ;)
    – m1nkeh
    Commented Jan 3, 2022 at 11:40
4

Add the key value pair for UserName:

<key>UserName</key>
<string>root</string>

Place the launchd job ticket in /Library/LaunchDaemons/; the /System folder is reserved exclusively for Apple's use.

To run as the specified user, be sure to place the job ticket in LaunchDaemons. As @Manu mentions in the comment below, the UserName key is ignored for jobs in LaunchAgents.

2
  • 2
    For the record, changing the 'UserName' key doesn't affect a LaunchAgent, it will still run as the logged in user.
    – Manu
    Commented Apr 21, 2016 at 10:05
  • 1
    The question refers specifically to LaunchAgents and running a process after login. LaunchDaemons are a bad fit for processes that rely on things that are only available after login, like a GUI, and as @Manu pointed out this answer does not work for LaunchAgents.
    – Cory Klein
    Commented Jun 29, 2017 at 20:21
1

Use a loginhook. They're deprecated but still work:

Mac OS X: Creating a login hook

2
  • This is not properly tested and doesn't work (at least with the example above and for me)!
    – klanomath
    Commented Jan 10, 2016 at 23:47
  • and the link is now broken
    – volvox
    Commented Jul 1, 2023 at 21:31
1

You can use launchd-oneshot to install a login job run as root with

brew install cybertk/formulae/launchd-oneshot
sudo launchd-oneshot <script> --on-login-as-root
1

Creating a LaunchAgent that runs with sudo/root privileges can be done by simply having sudo be the command that the LaunchAgent executes.

Create PList File

Try placing this minimal example in ~/Library/LaunchAgents/com.sudoexample.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.sudoexample</string>
        <key>ProgramArguments</key>
        <array>
            <string>sudo</string>
            <string>touch</string>
            <string>/tmp/sudoexample</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>StandardOutPath</key>
        <string>/tmp/sudoexample.log</string>
        <key>StandardErrorPath</key>
        <string>/tmp/sudoexample.log</string>
    </dict>
</plist>

Note: Agents should typically have user file ownership, but root ownership seems to work just fine as well.

The above example runs sudo touch /tmp/sudoexample and logs the output to /tmp/sudoexample.log.

If you run launchctl load ~/Library/LaunchAgents/com.sudoexample.plist at this point, it will fail because sudo will try to prompt for a password and launchctl doesn't have any associated tty to serve the password prompt:

$ ls /tmp/
sudoexample.log
$ cat /tmp/sudoexample.log
sudo: no tty present and no askpass program specified

Allow Elevated Privileges

We need to grant your user permission to run sudo touch without a password.

This can be done like so:

echo "$(whoami) ALL=(ALL) NOPASSWD: $(which touch)" | sudo tee /etc/sudoers.d/touch

When you're done, the file should look something like this:

$ sudo cat /etc/sudoers.d/touch
coryklein ALL=(ALL) NOPASSWD: /usr/bin/touch

Test It Out

$ launchctl load ~/Library/LaunchAgents/com.sudoexample.plist
$ ls -l /tmp
total 0
-rw-r--r-- 1 root      wheel 0 Jun 29 14:01 sudoexample
-rw-r--r-- 1 coryklein wheel 0 Jun 29 14:01 sudoexample.log

Now you can see the LaunchAgent successfully ran by creating the root owned /tmp/sudoexample.

Thanks to klanomath's answer for providing the foundation for this one.

0
  • Don’t mess with anything in /System/ unless you want to risk screwing up your system. Seriously. Stay away.

  • Put it in /Library/LaunchDaemons/ instead.

1
  • The problem with this is that it will always run before login. So if your service depends on some GUI, it will break on log-off. I am currently searching for an option run after login but as root, unfortunately without any luck, yet. Commented Jul 8, 2014 at 9:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .