Friday, January 05, 2018

A Very Simple "launchd" Example for MacOS

Arg. I've been struggling for days! to get a simple launchd example to work. None of the examples I found in the googlesphere got me there. Here's my working example.

Although I generally avoid being root for more than a single sudo command, I'm gonna sudo into root for this. Open Terminal (Cmd-Spacebar to open the Search window, then search for and Enter on "terminal".)
$ sudo -i
Then move into the /Users/Shared directory, and create a "Scripts" directory, and then move into that directory:
# cd /Users/Shared
# mkdir Scripts
# cd Scripts
Create a very simple Bash script here using nano:
# nano mysimplescript.sh
(You could also use any other plain-text editor, such as TextEdit (yuk!) or TextWrangler (yum!, but it's a third-party download). Whatever text editor you use, put the following into your "mysimplescript.sh":
#!/bin/bash
touch ITWORKS
Exit out of your text editor, saving the file. (If you use TextEdit, make sure it's a plain text file, etc.)

Make sure the file is readable and executable by root:
# chmod +x mysimplescript.sh
# chown root:wheel mysimplescript.sh
And test it to make sure it works. Currently, there should be no file named "ITWORKS" in the /Users/Shared/Scripts directory. After you run it, there should be such a file.
# ./mysimplescript.sh
If the script worked properly, you'll now have an empty file named "ITWORKS" in the /Users/Shared/Scripts directory. (You'll want to remove this file manually (# rm ITWORKS) afterwards for additional testing.)

Okay, so we have a working script. Now we want to use launchd to run this script once at boot-time.

Create a new file named "local.itworks.plist" (# nano local.itworks.plist), and put the following text into it:
<?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>local.itworks</string>

        <key>ProgramArguments</key>
        <array>
                <string>/Users/Shared/Scripts/mysimplescript.sh</string>
        </array>

        <key>RunAtLoad</key>
        <true/>
</dict>
</plist>    
Now this file needs to be placed in the appropriate directory for launchd items. If it was an Apple-provided .plist file, it'd go in /System/Library/LaunchDaemons (for root-run items) or /System/Library/LaunchAgents (for user-run items). (Daemons and Agents are the same thing; just named differently for who runs them, root or users.) But we're not Apple. If we wanted the item to be a launchd item for just "me", it'd go into my home folder's Library directory, ~/Library/LaunchAgents. But we want this to run as root, on startup, so we'll put it in /Library/LaunchDaemons.
# mv local.itworks.plist /Library/LaunchDaemons
And we need to make sure it has the correct permissions:
# chown root:wheel /Library/LaunchDaemons/local.itworks.plist
# chmod 755 /Library/LaunchDaemons/local.itworks.plist
(You could be more restrictive with the perms, such as chmod 700, but this should be okay for our purposes.)

Now we're ready to test it. Make sure that you have removed "ITWORKS" from the /Users/Shared/Scripts directory, or we won't know if the launchd item works or not, and then tell launchd to load this new service you have created:
# launchctl load /Library/LaunchDaemons/local.itworks.plist
If you see no errors, that's good. If you now see an "ITWORKS" file in the /Users/Shared/Scripts directory, that's great! Your new launchd service works!

You can get a list of running launchd items with:
# launchctl list
or you can narrow that list down with:
# launchctl list | grep local.itworks
If you need to edit the .plist file, you'll need to first stop/unload the service:
# launchctl unload /Library/LaunchDaemons/local.itworks.plist
Hopefully this will get you started with a successful launchd experiment, that will cause your mysimplescript.sh script to run at each boot of the Mac.

No comments: