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>
Related
I wrote a script to take a picture with my webcam. I want it to run everytime I unlock my mac, so I created a com.example.selfie.plist file in ~/Library/LaunchAgents/. The sh script is working well from a terminal, my plist is working well when I just write text in a file, but I cannot make the imagesnap command to work through the plist file.
selfie.sh
#!/bin/bash
datetime=$(date "+%Y%m%d_%H%M%S")
imagesnap -q -w 2 "/Users/taccle/Documents/Projets/Selfie/Photos/${datetime}.jpg"
com.example.selfie.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.example.selfie</string>
<key>ProgramArguments</key>
<array>
<string>/Users/taccle/Documents/Projets/Selfie/selfie.sh</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Library/Preferences/com.apple.biometrickitd.plist</string>
</array>
</dict>
</plist>
I suppose the issue is coming from Security and Privacy which doesn't allow launchd to access the camera (I'm not sure how I can get the logs when launchd scripts are running).
I found a workaround but I don't like it so much, my plist file calls run_in_command_line.sh which will open a terminal and then run selfie.sh.
#!/bin/bash
osascript -e 'tell application "Terminal"
do script "/Users/taccle/Documents/Projets/Selfie/selfie.sh; exit"
end tell'
It's working well but I would prefer to have only one script and not starting a terminal like this. Do you know if there's any way to give my launchd access to the camera?
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.
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'm creating a Mac Launch Agent for my app. The Launch agent works fine. But the executable bin file prints some console messages,when the exe is started in terminal and all those messages are logged to Mac Console. How can i skip logging those messages to the Mac Console ..?
I have tried adding a shell script as a Lauch Agent, which starts the exe, so that executable won't log messages to the console. But the script doesn't starts the bin.
This is my Launch Agent plist file.
<?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>com.myapp</string>
<key>Program</key>
<string>./bin/MyBin</string>
<key>WorkingDirectory</key>
<string>/Applications/MyApp/</string>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
You can override the default treatment of stdout and stderr for launchd jobs by adding the StandardOutPath and StandardErrorPath keys to your .plist file. To discard all output, add this:
<key>StandardOutPath</key>
<string>/dev/null</string>
<key>StandardErrorPath</key>
<string>/dev/null</string>
You could also redirect one or both to a log file.
The easiest way is as Gordon Davisson said above. However when i need to start more than one executable, then its possible by shell script.
This was my Script :
#!/bin/bash
cd /Applications/MyApp/
dates=`date`
echo "Exe Started at ::: ${dates}" > proc.log
# Starting Exe
./bin/MyBin 1>/dev/null 2>/dev/null & echo $! > proc.pid
I was having trouble with this script. I can see the proc.log filling up, but no sign of my App. Surprisingly when i removed the '&' from the script ,launch agent started working fine.
./bin/MyBin 1>/dev/null 2>/dev/null & echo $! > proc.pid
Changed to
./bin/MyBin 1>/dev/null 2>/dev/null
I m not sure why Mac is making the background process as a difference in launchd. The Script was & is working fine as it is, when i manually run it.
The output can only be redirected before the process begins. In this case if you don't want to change all your code then the executable for your launch agent will need to be a script. The script can then redirect as needed, e.g.
#!/bin/sh
# redirect to a log file, /dev/null, or wherever it should go;
# this is just an example log file destination
`/usr/bin/dirname $0`/run_real_agent > /tmp/my_agent.$$.log 2>&1