Hello generous computerists!
So I have a launchd plist item that is calling the below shell script every thirty seconds. It checks to see if a program is running and if it isn't, it restarts it. Or at least that is what it's supposed to do.
The problem is, even when the process has been killed, the shell script is stating that the process is still running. I think this is because somehow the boolean is not being reset (Or something along those lines). Any help?
n=`ps -ef | grep Intel | grep -v grep | wc -l`
if [ $n -gt 0 ]
then
echo `date` CURRENTLY RUNNING. >> /Library/A_Intel_WATCHDOG/A_Intel_WatchLog.txt
else
echo `date` not running. ATTEMPTING RESTART... >> /Library/A_Intel_WATCHDOG/A_Intel_WatchLog.txt
cd /Library/LaunchAgents/
launchctl load com.Intel.plist
fi
EDIT 1
It has been suggested that adding a 'Keep Alive' argument may be a good solution to my general problem. Any comments?
Here would be my updated plist file which should guarantee that my app keeps running perpetually:
<?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</string>
<key>OnDemand</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/Library/LaunchAgents/Contents/Intel</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
</plist>
I would love to hear if people think this is correct or not. Thanks so much!
To troubleshoot the immediate problem, try logging the output of the ps command:
processes=`ps -ef | grep [I]ntel`
if [ -n "$processes" ]
then
echo `date` CURRENTLY RUNNING. "$processes" >> /Library/A_Intel_WATCHDOG/A_Intel_WatchLog.txt
...
I suspect there's something else running (maybe even the script itself) matching Intel; this'll tell you in the log.
However, I think this is irrelevant, because this script seems to be trying to solve a problem that's better solved elsewhere: launchd is entirely capable of monitoring and restarting the processes it manages (it's one of its significant features). Just add
<key>KeepAlive</key>
<true/>
to /Library/LaunchAgents/com.Intel.plist, and launchd will restart the program itself.
BTW, if you for some reason did need to manually restart a launchd-managed process, launchctl load is the wrong incantation -- you want launchd start.
Related
I'm setting up a launchdaemon to run a script that checks if a specific local account is logged in. If that account is logged in, it needs to automatically log back out again. I have a working script, but I can't get a launchagent or launchdaemon to run it.
I've tried it as a launchagent, but that doesn't work, and the specific user is specifically blocked from running scripts. This is running on macOS 10.14.5. It sometimes works when launched by our MDM, but we need it to run even if the MDM is unreachable (thus the local script).
This is my agent so far:
<?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>fubar.plist</string>
<key>ProgramArguments</key>
<array>
<string>/location/of/fubar.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>30</integer>
</dict>
</plist>
And this is the script:
currentUser=$( scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ && ! /loginwindow/ { print $3 }' )
if [ "$currentUser" == "fubarUser" ]; then
echo "$currentUser is fubarUser. Logging out"
killall loginwindow
fi
I expect it to register that fubarUser has logged in, then echo my message and kill the loginwindow thus logging them out.
If I run the script as root in a terminal window, it correctly identifies whoever is logged in. So I know the script itself works. I just can't get a launchagent or launchdaemon to load it.
In my production version it actually echo's to a log file who it sees is logged in, and that correctly registers the current user.
Turns out it was a permissions problem. We have apparently locked down the permissions on the folder where I was hosting the script. I've since moved it to the /Library/Scripts/ folder and have been able to run it smoothly from there.
On our macOs virtual machine, that we use for building, the time sometimes jumps out of no reason. As a workaround I have created this script named test.sh that consistently corrects the time:
#!/bin/bash -e
while true; do
sudo ntpdate -u de.pool.ntp.org >> ntpdate.txt; sleep 30;
done
At the beginning of a build this gets started in the background:
./test.sh &
When the build is finished I'm killing it:
kill $(ps aux | grep test.sh | grep -v grep | awk '{print $2}')
Sometimes the call to update the time takes longer than 30 seconds. Then there are two open calls to the ntp pool and I'm getting a rate limit response. Therefore I want to limit the calls to ntp to only one at a time. How do I achieve this in my while true loop?
A simple way to implement mutual exclusion in Bash is to use a "lockfile." You check if the file exists, and if it does, you do not do the NTP query. If the file does not exist, you create it. A useful enhancement in case of a crash will be to check if the file time is more than a few minutes old, in which case it can be deleted.
Can you try to see if below works for your case.
#!/bin/bash
while true; do
pid=0
sudo ntpdate -u de.pool.ntp.org >> ntpdate.txt & pid=(${!})
wait $pid
done
Thanks for all the suggestions. As Charles Duffy mentions my code already does proceed only when the previous command finished. I've tested it with:
#!/bin/bash -e
while true; do
echo "start ntpdate"
sudo ntpdate -u de.pool.ntp.org >> ntpdate.txt;
echo "going to sleep"
sleep 30;
done
So apparently the sleep timer was too low to run into the rate limiting response. Maybe I will have to raise the sleep time.
As chepner suggested I created a LaunchAgent to handle 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>workaroundAgainstJumpingTime</string>
<key>ProgramArguments</key>
<array>
<string>sudo</string>
<string>ntpdate</string>
<string>-u</string>
<string>de.pool.ntp.org</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/tmp/workaroundAgainstJumpingTime.stderr</string>
<key>StandardOutPath</key>
<string>/tmp/workaroundAgainstJumpingTime.stdout</string>
<key>StartInterval</key>
<integer>30</integer>
</dict>
</plist>
Charles Duffy, if you make an answer out of your comment "Your code already does proceed only when the previous command finished." I'll mark it as accepted answer.
I was looking for an answer to my question on stackoverflow and found a thread (https://superuser.com/questions/229773/run-command-on-startup-login-mac-os-x). That's how I found out about launchd.plist and I made one by myself:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>js.leapmotion</string>
<key>OnDemand</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>/Applications/Utilities/Terminal.app</string>
</array>
<key>LaunchOnlyOnce</key>
<true/>
</dict>
</plist>
this indeed starts my terminal after booting my mac. But now I want it to navigate to a directory and run the command gulp in there.
This again will do the rest, opens up a localhost and so on...
So in the end I just need to run gulp within an specific directory. That's all. If launchd.plist is not the best way to go, then I'm open for alternatives.
Otherwise I would like to know how I can tell the terminal what commands it should run.
Thanks for hints.
cheers
J
You can use the open command to open Terminal and run a script by using the -a flag. For example, open -a Terminal /path/to/myscript will open a new Terminal window and run myscript. Note that when myscript finishes, the session will terminate (i.e. there's no shell). To change that, you can run bash or whatever your preferred shell is as part of the script.
In your case, I would suggest creating a basic script to do whatever you want Terminal to execute, such as :
#!/usr/bin/env bash
cd /path/to/your/directory
/usr/bin/gulp
Save that (making sure it's executable), and then change your launchd plist to match:
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>-a</string>
<string>Terminal</string>
<string>/path/to/yourscript</string>
</array>
I know when using a plist in the launch daemons folder there is a key called <StartInterval>. I much prefer using launchctl when I can because it is a lot quicker then writing out a whole plist, but I have not been able to figure out how to get the process to restart after it has been killed. I have already read the man page and haven't found anything there. Is there a way? Typically I use the following command:
launchctl submit -l somename -p /path/to/script -o output.txt -e errors.txt
But that will not restart the program if it is killed after any time interval.
launchctl submit should already run the program again if it terminates for some reason:
submit -l label [-p executable] [-o path] [-e path] -- command [args]
A simple way of submitting a program to run without a configura-
tion file. This mechanism also tells launchd to keep the program
alive in the event of failure.
-l label
What unique label to assign this job to launchd.
-p program
What program to really execute, regardless of what fol-
lows the -- in the submit sub-command.
-o path Where to send the stdout of the program.
-e path Where to send the stderr of the program.
To run a program again if it exits with an error, set the SuccessfulExit criteria for KeepAlive to false:
<?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>test</string>
<key>ProgramArguments</key>
<array>
<string>bash</string>
<string>-c</string>
<string>n=$((RANDOM%2));say $n;exit $n</string>
</array>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>StartInterval</key>
<integer>60</integer>
</dict>
</plist>
launchd throttles jobs so it takes about 10 seconds for the program to respawn.
i am looking for a way to periodically re-launch an application in OS X. since this application has a GUI i chose the LaunchAgent method to do it. the LaunchAgent will call a shell script, which will quit the application (if running) and then open it again.
so far it works, with the only exception that once my called shell script has ended, the launched GUI application is also quit.
i tried opening the application with trailing "&" and using nohup, but no luck.
this is my LaunchAgent (ignore my stupid StartCalendarInterval settings, they are for debugging only):
<?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.mycompany.myapp_relauncher</string>
<key>ProgramArguments</key>
<array>
<string>/Users/xyz/Desktop/myapp_relauncher.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>21</integer>
<key>Minute</key>
<integer>25</integer>
</dict>
</dict>
</plist>
and this is the called script /Users/xyz/Desktop/myapp_relauncher.sh
#!/bin/bash
function log { echo "myapp: "`date "+%Y%m%d-%H%M%S"`" $1" ; logger -t "myapp" "$1" ; }
# stopping the app if running (code ommitted here)
log "starting application now..."
nohup /Applications/myapp.app/Contents/MacOS/myapp &
log "sleeping a few seconds now..."
sleep 10 # the launched applications stays open
log "done sleeping, quitting."
# script ends, launched application ends too
Add <key>AbandonProcessGroup</key><true/> to your LaunchAgent .plist; without that, when the agent (the script) exits launchd "helpfully" cleans up all leftover subprocesses (technically, anything with the same process group ID), like for example the application.
You want to use disown here. This works without closing the application after the script finishes:
#!/bin/bash
/Applications/Adium.app/Contents/MacOS/Adium &
disown
This is because using & just puts the job into the background. Even though it's in the background it is still a child process of the current script, which is killed when its parent process, the script, finishes. By using disown that relationship is removed and it runs on its own.
You might also just use the open command:
killall myapp; open -jga myapp
open -jg usually opens an application hidden and without raising any windows. If for example TextEdit is open but has no open windows, -jg creates and raises a new default window though. To avoid it, you can test if the application is already open:
<key>ProgramArguments</key>
<array>
<string>bash</string>
<string>-c</string>
<string>pgrep -x TextEdit||open -jga TextEdit</string>
</array>