I've coded my launch daemon and a launch agent for macOS. When the app is installed, I need to run both the daemon and the agent. I place the .plist for the deamon into /Library/LaunchDaemons and then run (as admin):
launchctl load /Library/LaunchDaemons/com.example.MyDaemon.plist
and since the plist has:
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
the daemon starts up right away.
But I'm struggling to do the same with my launch agent. Here's it's plist that I place into /Library/LaunchAgents directory for it to start for every user and also in the login screen:
<?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>LimitLoadToSessionType</key>
<array>
<string>Aqua</string>
<string>LoginWindow</string>
</array>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>com.example.MyAgent.plist</string>
<key>Program</key>
<string>/Library/PrivilegedHelperTools/com.example/MyAgent</string>
<key>ProgramArguments</key>
<array>
<string>-d</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
when I try to run it as:
launchctl load /Library/LaunchAgents/com.example.MyAgent.plist
I get an error:
Warning: Expecting a LaunchDaemons path since the command was ran as
root. Got LaunchAgents instead. launchctl bootstrap is a recommended
alternative. /Library/LaunchAgents/com.example.MyAgent.plist:
Path had bad ownership/permissions Load failed: 122: Path had bad
ownership/permissions
what am I doing wrong?
I cant make daemon launches only one time on startup.
I created file /Library/LaunchDaemons/local.gz.startup.plist
Then I run loaded it with launchd command
<?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.gz.startup</string>
<key>ProgramArguments</key>
<array>
<string>/etc/rc.local</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
rc.local:
#!/bin/bash
sudo mkdir /Volumes/ESP
sudo mount -t msdos /dev/disk0s1 /Volumes/ESP/
....
sudo umount -f /Volumes/ESP/
exit
This works only when I load it manually with launchd command, not at startup.
I have tried another parameters, but it (rc.local) begins work continuously, it mounts and unmounts /dev/disk0s1.
How can I make it start only once on boot?
I'm trying to write a Launchd script to execute a simple sh script that will 1) launch mopidy 2) mkfifo /tmp/mopidy.fifo 3) pass data from port 5555 into this file using socat.
I've tried setting up a mopidy.plist LaunchAgent that executes a mopidy.sh script file at login. I've verified that the LaunchAgent gets started correctly and that the script has execution permissions. I've also tried the Program Arguments approach by passing the script as a one-liner to /bin/bash, but when I try that I get errors logged saying that its unable to find socat.
mopidy.sh
nohup mopidy;
mkfifo /tmp/mopidy.fifo;
while :; do
socat -d -d -T 1 -u UDP4-LISTEN:5555 OPEN:/tmp/mopidy.fifo;
done
mopidy.plist
<!-- Starts mopidy server -->
<?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.me.mopidy.plist</string>
<key>Program</key>
<string>/Users/me/.config/scripts/mopidy.sh</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/me/logfile.log</string>
<key>StandardErrorPath</key>
<string>/Users/me/error_logfile.log</string>
</dict>
</plist>
I am able to execute the bash script normally without error from the command line, however it doesn't seem like the service is able to because the /tmp/modipy.fifo file never gets created and the listener is never made. The logfile and error_logfile are never populated either.
I was able to get it working by including environment variables. I don't fully understand this, but everything works now with the following:
<!-- Starts mopidy server -->
<?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.me.mopidy.plist</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/sbin</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/me/.config/scripts/mopidy.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/me/logfile.log</string>
<key>StandardErrorPath</key>
<string>/Users/me/error_logfile.log</string>
</dict>
</plist>
I am trying to run a very simple AppleScript periodically using a launchd agent but it won't do anything aside from writing the AppleScript contents to stdout.
My launchd Agent at ~/Library/LaunchAgents/com.nn.test.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.nn.test</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/osascript</string>
<string>-e</string>
<string>"display dialog \"Hi\""</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>Debug</key>
<true/>
<key>StartInterval</key>
<integer>7200</integer>
<key>StandardErrorPath</key>
<string>/tmp/test</string>
<key>StandardOutPath</key>
<string>/tmp/testout</string>
</dict>
</plist>
I am running by:
$ launchctl unload com.nn.test.plist
$ launchctl load com.nn.test.plist
$ launchctl run com.nn.test
Result is cat /tmp/testout
display dialog "Hi"
While /tmp/test is empty.
Expected behaviour would be that a dialog opens just like when I run the command directly in the shell (which works) but it seems nothing is happening. What am I doing wrong and why do I not see any error messages in any error log?
Might this have to do with Mojave's enhanced security model? Can't AppleScript be run from launchd agents any more?
I think you are doubly-stringified!
You need:
<array>
<string>/usr/bin/osascript</string>
<string>-e</string>
<string>display dialog "Hi"</string>
</array>
and:
launchctl start com.nn.test
I am trying to write a launchd.plist file for my node server. I am using forever to run my node server. I would like the server to start on boot. I would also like to wait for the mongodb launchd plist to run first.
I installed mongobb using homebrew and it came with a launchd.plist already. I have executed the following:
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
plist for mongodb is:
<!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>homebrew.mxcl.mongodb</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/opt/mongodb/mongod</string>
<string>run</string>
<string>--config</string>
<string>/usr/local/etc/mongod.conf</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<false/>
<key>WorkingDirectory</key>
<string>/usr/local</string>
<key>StandardErrorPath</key>
<string>/usr/local/var/log/mongodb/output.log</string>
<key>StandardOutPath</key>
<string>/usr/local/var/log/mongodb/output.log</string>
<key>HardResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>1024</integer>
</dict>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>1024</integer>
</dict>
</dict>
</plist>
If I shutdown the computer and restart mongodb fires up as it should.
However my node server is not starting. Any ideas?
<?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>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>Label</key>
<string>com.test.app</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/forever</string>
<string>-a</string>
<string>-l</string>
<string>/var/log/app/app.log</string>
<string>-e</string>
<string>/var/log/app/app_error.log</string>
<string>/data/server/app.js</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>3600</integer>
</dict>
</plist>
EDIT:
writing to log file and I see this:
env: node: No such file or directory
I think this means that the node binary cannot be found. I can echo $PATH and /usr/local/bin is in my path. I can start node from the terminal. Ideas?
Add environment Variables worked for me.
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin/:$PATH</string>
</dict>
You may also need to add WorkingDirectory to your node app.
<key>WorkingDirectory</key>
<string>path/to/your/node/app</string>
I had this problem too, but I solved it using an Automator app that runs at startup.
Open Automator and choose, New Application
Insert in your workflow "Run Shell Script"
Use this code in the shell script, changing the paths to your paths
export PATH=/usr/local/bin/:$PATH
cd /path/to/your/nodejs/app
forever start app.js
Go to System Preferences >> User & Groups and click on Login Items tab
Add your Automator app and be happy.
The important part of the solution is the first line of the script (adding your bin to the path). It would probably work to add a Startup Item pointed at a bash script too (and no Automator script), feel free to try!
Add node before forever:
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/node</string>
<string>/usr/local/bin/forever</string>
<string>/path/to/app.js</string>
</array>
This is not an answer of the original questions, but I was looking for a simple way to start a node server (and keep it running) after reboot. I found pm2 to be a much easier to set up than the solutions for forever above.
# install pm2
npm install pm2 -g
# start server
pm2 start app.js
# start pm2 after reboot (might need sudo)
$ pm2 startup
http://pm2.keymetrics.io/docs/usage/startup/
I'm not sure when node-launchd came out. However, it seems to be the more reliable solution.
The solution in which a workflow is created and added into the login item is also well appreciated. However, the problem is that if the app is served on a server and when the server is restarted, I wonder the app will be started before the user login to the system. Didn't try, though.