Launchd runs with manual "launchctl start", but not on StartCalendarInterval - macos

I have a very simple Launch Daemon, com.daily.vocab.plist, which simply runs a basic script. It is supposed to run every day at a certain time (using StartCalendarInterval), but I can't get that to happen. Many posts online are saying it runs a minute or two off, but for me it just never runs.
I have been setting the run time to be 5 minutes in the future while I am testing, and now have it set to run at the 0th second of every minute, just so I can get it to work. Once it's working, I will set it to my desired daily run time.
As advised in this tutorial, it is saved in /Library/LaunchDaemons. I loaded it manually with launchctl load /Library/LaunchDaemons/com.daily.vocab.plist. I do in fact see it has loaded when I run launchctl list. When I manually tell it to run with launchctl start com.daily.vocab, it works immediately as expected. The only issue is that the StartCalendarInterval seems to be ignored. I am on Mavericks on my Macbook.
Here is the file: com.daily.vocab.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.daily.vocab</string>
<key>Program</key>
<string>/Users/MGS/developer/projects/vocab-sms/scripts/DEvocab.sh</string>
<key>StartCalendarInterval</key>
<dict>
<key>Second</key>
<integer>0</integer>
</dict>
</dict>
</plist>
Ideally, I believe I want it to run at 7:15am every day making the StartCalendarInterval section be instead:
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>7</integer>
<key>Minute</key>
<integer>15</integer>
</dict>
If you could even share an example of a LaunchDaemon you got to work using StartCalendarInterval and every step you made to make it work (e.g. running launchctl load x.x.x.plist), that would be really helpful!
Solution: This local daemon should actually be a user agent, so it needed to be saved in and loaded from the ~/Library/LaunchAgents directory. From there, I also needed to unload the agent and reload the agent.
NOTE: Daemons/Agents do not automatically update in launchd until you unload/load. You can't just save the updated .plist and expect it to work!

launchd plists saved in /Library/... are daemons / agents in the local domain and have to be loaded with sudo. Without sudo you're loading the daemon in the user domain which doesn't match the actual path.
Since you are calling a script in the user domain anyway, save the script in ~/Library/LaunchAgents (it's not a daemon). The plist syntax is supposed to be correct.

will have to change the permission for the bash file
chmod u+v /Users/MGS/developer/projects/vocab-sms/scripts/DEvocab.sh

Related

How do I write a script that restarts an application that runs in the Terminal on Mac server after the machine reboots?

I have an application that normally runs in Terminal. This requires changing directory to the application directory [cd /mydirectory] and launching the application ./PurplesoftServer
I am struggling with writing a launch script to restart the application if the server restarts for any reason. I saved the script in /Library/LaunchAgents
This is my script:
```<?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>PSM.Restart</string>
<key>ProgramArguments</key>
<array>
<string>/Users/stephenjay/Documents/HW/HW Customers/ACME server 200814</string>
<string>./PurpleSoftServer</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>```
I also ran the following commands in Terminal in an effort to load the script:
sudo launchctl load /Library/LaunchAgents/PSM.Restart.plist
sudo launchctl start PurplesoftServer
Everything appears to run - after I gave ownership of the plist to root:wheel
However, the application doesn't end up running, neither after running the Sudo commands nor after actually restarting the Mac server.
What have I missed?
The first item in the ProgramArguments array should be the path to the executable, not a directory to cd to. You need to use the WorkingDirectory key instead. Something like this:
...
<key>ProgramArguments</key>
<array>
<string>./PurpleSoftServer</string>
</array>
<key> WorkingDirectory</key>
<string>/Users/stephenjay/Documents/HW/HW Customers/ACME server 200814</string>
...
BTW, you'll need to use sudo launchctl unload to unload the bad version, then sudo launchctl load again to load the fixed version.
BTW2, sudo launchctl list PSM.Restart will give you more info about the run status of the item.

Launchd task unloads when computer restarts

I have a simple launchd plist file that I am using to run a shell script that is designed to restart the computer:
<?xml vesion="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.sag95.restartscript</string>
<key>KeepAlive</key>
<false/>
<key>RunAtLoad</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>/Users/sag95/Desktop/Scripts/restartscript.sh</string>
</array>
<key>StartInterval</key>
<integer>300</integer>
</dict>
</plist>
The script file is a single line force restart command(password in real file, not placed here):
echo <password> | sudo -S shutdown -r now
Once I load the plist file using
launchctl load com.sag95.restartscript.plist
it appears in the list (launchctl list). Currently I have the plist file to run the script every 5 minutes. After the 5 minutes of loading, the script is called the first time and subsequently restarts the computer. Once I login, the launchd plist file is no longer in the list (launchctl list). I waited 30 minutes and it hasn't run again to restart my computer.
My question is why is the com.sag95.restartscript plist file unloading after the restart/force reboot?
launchctl load only loads it for the current session. Next time you log in, it loads your list of launch agents from /System/Library/LaunchAgents/*.plist, /Library/LaunchAgents/*.plist, and /Users/sag95/Library/LaunchAgents/*.plist. If the file isn't in one of these folders, it will not get loaded.
Assuming you want it to run only when you are logged in, place the file in /Users/sag95/Library/LaunchAgents. If you wanted it to run for any logged in user, put it in /Library/LaunchAgents instead (but with this specific script, the password will only work for your account, so this wouldn't be useful).

Converting cron to launchd - MAILTO

Migrating from an old mac to a new one.
Trying to migrate the user crontab I had on the old mac.
Now I know I can probably get cron to run by creating /etc/crontab - but given that apple has deprecated it in favour of launchd - I thought I'd take a swing at migrating my crontab to launchd plist files. Always fun to learn something new :)
The crontab isn't that hard - I tend to set up shell scripts that do the work and then just call them at scheduled times.
I can call these fine from the plist at the same times - no issues.
But - at the top of my crontab I had MAILTO=my-gmail-email-address - so that instead of sending mail to the local user on the box's mail spool it sent them to gmail for me.
I can't see that you can configure this for a launchd plist file.
So - before I go modify all my scripts to redirect all output to tmp files then mail the tmp file (would have to do this in quite a few scripts) - is there a way to do this at the launchd level?
I'm afraid launchd(8) does not support mailing of stdout/stderr out of the box. Here's a workaround:
Redirect output via StandardOutPath and StandardErrorPath to files in a dedicated directory. Then setup a job which mails every file in this directory. Trigger this job with the QueueDirectories key. Make sure this script removes the files after mailing them.
one way to convert to launchd is the lazy way (how i do it)
You pay $0.99 for Lingon from the app store; then you can just fill out a few fields and it makes the launchd...
otherwise:
a launchd would look like this
<?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>name of launchd</string>
<key>ProgramArguments</key>
<array>
<string>[terminal command to execute]</string>
</array>
<key>RunAtLoad</key>
<[(true)/(false)]/>
<key>StartCalendarInterval</key>
<dict>
[any of these options are removable]
[for everyday remove the Day and Weekday tags]
<key>Day</key>
<integer>[day of the month]</integer>
<key>Weekday</key>
<integer>[day of the week 0-7 - (sunday=0);]</integer>
<key>Hour</key>
<integer>[hour of day (military time)]</integer>
<key>Minute</key>
<integer>[minute]</integer>
</dict>
</dict>
</plist>
any of the [] are either comments (must be removed) or a list of options
Use of the launchctl command would be used to mount the launchd

How can I debug a Launchd script that doesn't run on startup?

I have some Launchd scripts from homebrew. However I have to manually run them when I restart my computer:
launchctl load -w ~/Library/LaunchAgents/com.mysql.mysqld.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>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.mysql.mysqld</string>
<key>Program</key>
<string>/Users/dash/.local/Cellar/mysql/5.1.49/bin/mysqld_safe</string>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>dash</string>
<key>WorkingDirectory</key>
<string>/Users/dash/.local/var</string>
</dict>
</plist>
I thought this should happen on startup. What am I missing?
Best way I found to debug, in your plist:
<key>StandardErrorPath</key>
<string>/tmp/mycommand.err</string>
<key>StandardOutPath</key>
<string>/tmp/mycommand.out</string>
Open Console app, in "All Messages" you should see entries when your app fails or succeeds. Like this:
4/28/15 10:43:19.938 AM com.apple.xpc.launchd[1]: (mycommand[18704]) Service exited with abnormal code: 1
The issue I had was with ProgramArguments takes each item of command as <string> item in the array.
EDIT:
In my case, generating a simple wrapper for shell script worked even better.
This script sets up basic folder structure to make a shell script into an OS X "app" - https://gist.github.com/mathiasbynens/674099. This might work better for your mysql -u arg1 command.
For me, other solutions so far do not help me. My problem is kinda hard to debug, because the plist file is correct, the script is running fine alone in a terminal. Everything looks fine, but doesn't work.
I checked the log file by executing
tail -f /var/log/system.log
And then by unloading and loading the service again with the commands:
launchctl unload ~/Library/LaunchAgents/example.plist
launchctl load ~/Library/LaunchAgents/example.plist
I found an error message from the log file:
Program specified by service is not a Mach-O executable file.
What does it really mean? I didn't google. But, I feel it's because I didn't add #!/bin/bash at the beginning of the shell script. Because I am lazy to add this line sometimes.
After adding the heading, everything works fine.
The start command expects the job Label as it's argument, so you would start it using the following...
launchctl start com.myfile.hostname.plist
To stop, simply do the following...
launchctl stop com.myfile.hostname.plist
Once all your testing is complete you would log out then in to load it or if your plist file is in the users Library folder type the following...
launchctl load ~/Library/LaunchAgents/com.myfile.hostname.plist
One possibility: Look in the directory:
/private/var/db/launchd.db/
and fine the "com.apple.launchd.peruser.###" file for your user. Open that and see if there is a entry like:
<key>com.mysql.mysqld.plist</key>
<dict>
<key>Disabled</key>
<true/>
</dict>
If so, try setting it to <false/>. Another file to look in for the same thing is:
/private/var/db/launchd.db/com.apple.launchd/overrides.plist
From OSX Yosemite 10.10 Onwards launchctl commands are changed.
Following commands will be required to start service automatically at the reboot.
sudo launchctl bootstrap system /Library/LaunchDaemons/${YOUR_SERVICE_NAME}.plist
sudo launchctl enable system/${YOUR_SERVICE_NAME}
sudo launchctl kickstart -kp system/${YOUR_SERVICE_NAME}
Note: It will launch service with user root and start system-wide.
References:
Launchctl Man Page(https://ss64.com/osx/launchctl.html)
Try renaming it. Change the filename to:
~/Library/LaunchAgents/com.mysql.mysqld2.plist
and the Label section in the plist to:
<key>Label</key>
<string>com.mysql.mysqld2</string>
If you save a backup copy, make sure to move it outside of your ~/Library/LaunchAgents/ directory.
Finally, instead of using launchctl to load it, just logout and log back in. That will let launchd pick it up from your "LaunchAgents" directory by itself and take one more variable (i.e. launchctl) out of the mix.

Creating a timed launchd plist

I'm trying to create a plist for the LaunchAgents folder that will run perpetually and call a shell script every 30 seconds. It started with a template that I got here and that I tried to tailor to fit my needs, but it's still not working. Any help?
<?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.Intel_Watchdog</string>
<key>ProgramArguments</key>
<array>
<string>/Library/A_Intel_WATCHDOG/A_WatchDog.sh</string>
</array>
<key>StandardErrorPath</key>
<string>/dev/null</string>
<key>StandardOutPath</key>
<string>/dev/null</string>
<key>StartInterval</key>
<integer>30</integer>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Thanks so much!
I dropped this in my LaunchDaemons directory, and it worked fine. So, here're some things to check:
Is the .plist getting loaded? Note that the contents of the LaunchAgents folders are loaded at login, so you either need to log out and back in, or manually load it with launchctl load ~/Library/LaunchAgents/com.Intel_Watchdog.plist (or whatever/wherever the file is). You can check its status with launchctl list.
Is /Library/A_Intel_WATCHDOG/A_WatchDog.sh executable? Check the system log (/var/log/system.log) to see if launchd is having trouble launching it.
Is the script running into trouble before it does its job? Try adding date >>/tmp/watchdog.log to it, right after the shebang, and see if anything appears in /tmp/watchdog.log. If it does, try changing the StandardErrorPath and StandardOutPath to an actual file so you can see what's going on as the script runs. Note that you'll have to unload and reload the .plist to get the change to take effect (logging out and back in would do it).
Finally, although it shouldn't cause trouble, your label doesn't follow the standard convention (unless you actually own the domain Intel_Watchdog.com). If this is for local-only use, name it something like local.Intel_Watchdog instead. If you're going to publish this item, you should base the label on a domain you own (in reverse order, with ".Intel_Watchdog" added to the end) -- see Wikipedia's entry on the reverse domain name system.

Resources