Since there is no "Year" parameter in the launchd plist format, how is it possible to create an agent that will run only once and not once a year?
Since your reference "Year", I assume you are using StartCalendarInterval.
If you don't want to run at an interval, use the RunAtLoad key, which will cause it to run once each time the system starts.
<key>RunAtLoad</key>
<true/>
Now if you truly only want the script to run once (ever), don't use launchctl at all, just run it once on the command line and be done with it.
Use the key LaunchOnlyOnce for this:
<key>LaunchOnlyOnce</key>
<true/>
This will launch the agent only once if the system is not rebooted.
Related
Is there a way to detect if audio is being output from the Mac system? Whether it's via headphone jack, usb-c, or bluetooth, is there a process which runs if and only if audio is playing?
I've made a unix script which plays a 15hz sine waveform, the idea is to execute the script every 580 seconds if and only if no audio is playing. If audio is playing, the script won't execute.
I have really good speakers but the drawback is they enter a 'standby' power saver mode every 10 minutes (600 seconds). When I'm at work I don't really care about whether it runs in the background or not* but when I'm at home I tend to miss notifications when speakers enter standby, so the aim of the script is to play the 3 second waveform every 580 seconds if and only if no audio is playing.
I'm not sure if this is super related to what you want but maybe try out Soundflower ? It's super useful for re-routing audio in mac.
You do not necessarily need to use AppleScript to accomplish the goal, although you certainly can if you want.
I'd opt to use a Launch Agent, as in the following example XML plist code:
<?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.my.play.if.no.audio.is.playing</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>[[ $(pmset -g | grep ' sleep') =~ coreaudiod ]] || '/path/to/shell_script'</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartInterval</key>
<integer>580</integer>
</dict>
</plist>
Change '/path/to/shell_script' in the XML plist code to the actual fully qualified pathname of your shell script.
Then save is as, e.g., com.my.play.if.no.audio.is.playing.plist in ~/Library/LaunchAgents/ and load it from Terminal using, e.g.:
cd ~/Library/LaunchAgents/
launchctl load com.my.play.if.no.audio.is.playing.plist
Then every 580 seconds your shell script will execute if there is no audio playing.
Notes:
As coded, it assumes the executable bit is set on your shell script.
If you are going to use Launch Agents and Launch Daemons, I highly recommend you read the manual pages for launchctl, launchd.plist and launchd.
You can read the manual page for command in Terminal by typing man command, then press enter, or for easier reading, just type command and then right-click on it and select: Open man Page
To stop your Launch Agent, in Terminal:
cd ~/Library/LaunchAgents/
launchctl unload com.my.play.if.no.audio.is.playing.plist
You could also instead, include the test condition in your shell script, e.g.:
#!/bin/bash
if [[ ! $(pmset -g | grep ' sleep') =~ coreaudiod ]]; then
# Code to generate your sine waveform goes here.
fi
Then, the command used in the example XML plist code would be:
<key>ProgramArguments</key>
<array>
<string>/path/to/shell_script</string>
</array>
If you really want to use AppleScript, and assuming a Stay Open application, then:
Example AppleScript code:
on run
-- # Add any AppleScript code you what run when the
-- # Stay Open AppleScript application is opened.
end run
on idle
do shell script ¬
"[[ $(pmset -g | grep ' sleep') =~ coreaudiod ]] || '/path/to/shell_script'"
return 580
end idle
on quit
continue quit
end quit
Notes:
When saving the example AppleScript code in Script Editor , set File Format: [Application] and [√] Stay open after run handler.
If your add the test condition to your shell script, as shown further above, then your do shell script command would be:
do shell script "'/path/to/shell_script'"
I tend to stay away from using this type of AppleScript application, as I find that over time they can/may become resource intensive in that they continue consume more RAM. YMMV.
Let's say I have the script below
#! /bin/bash
sleep 300
dt=$(date '+%Y/%m/%d %H:%M:%S')
echo $dt >> /foo/bar/test.txt
When I set up a launchagent to trigger this script every 2 minutes... launchd will wait for the script to finish before triggering it again. So instead of the date every 2 minutes (after a 300 second sleep) I get it every 5 minutes or more. Is there a way for launchd to launch the script every 2 minutes, regardless of whether it has completed or not?
I haven't tested this, but you should be able to have the launch agent run the script in the background, something like this:
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>/path/to/script &</string>
</array>
<key>AbandonProcessGroup</key>
<true/>
Notes: If you need to pass any arguments to your script, include them in the same <string> as the script path, delimited by spaces (rather than as separate <string>s, like you normally would in the launch daemon file). The AbandonProcessGroup key is needed to prevent launchd from "helpfully" killing any leftover subprocesses (like your actual script) when what it thinks of as the daemon process exits.
I am looking for the solution how to open the website at the certain time. Considering the shell programming, it is possible to open website by the command:
$ open http://stackoverflow.com
How to do it at the certain moment if it is possible at all?
You can use crontab to make it.
The crontab is a list of commands that you want to run on a regular
schedule, and also the name of the command used to manage that list.
crontab stands for "cron table," because it uses the job scheduler
cron to execute tasks; cron itself is named after "chronos," the Greek
word for time.
Say You have a script /bin/openURL.sh to open a website,
30 21* * * /bin/OpenURL.sh
means executing it 21 : 30 every day.
More usage about crontab, see http://www.computerhope.com/unix/ucrontab.htm
For one-time jobs (scheduled tasks), at commands are simple to schedule, as demonstrated by John1024's since-deleted answer, but at has drawbacks on OSX:
It must be enabled first; this is a one-time operation: sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist.
You must use sudo (have administrative privileges) to be able to schedule job with at.
If a job produces any output, it will be mailed to the user using mail, as stdout and stderr output (I don't know how to suppress this).
Example: schedule opening http://stackoverflow.com once, at 19:00 (7 PM):
sudo bash -c 'echo "open http://stackoverflow.com" | at 19:00'
Using crontab for periodic jobs is an option on OSX; e.g., to schedule a job that opens http://stackoverflow.com every day at 19:00 (7 PM):
Run crontab -e to open the current user's cronfile in your editor.
Add the following line, save, and close the file:
0 19 * * * open http://stackoverflow.com
If a job produces any output, it will be mailed to the user using mail, as combined stdout and stderr output (I don't know how to suppress this).
However, the official recommendation on OSX is to use launchd for both one-time and periodic jobs:
launchd is very flexible and centralizes all job scheduling; as with crontab, there are system-wide and per-user jobs.
the downside is that the .plist files required to define jobs are cumbersome and non-trivial to create.
Using our previous example:
One-time job: (opens http://stackoverflow.com once, at 19:00 (7 PM))
Create file ~/test.plist (for a one-off job, the location doesn't matter).
Paste the following:
<?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>TestJob</string>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>http://stackoverflow.com</string>
</array>
<key>LaunchOnlyOnce</key>
<true/>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>19</integer>
<key>Minute</key>
<integer>00</integer>
</dict>
</dict>
</plist>
TestJob uniquely identifies your job.
Setting LaunchOnlyOnce to true ensures that the job is only run once.
From Terminal, run launchctl load ~/test.plist to load the job.
Periodic job: (opens http://stackoverflow.com every day at 19:00 (7 PM))
Create file ~/Library/LaunchAgents/testPeriodic.plist
Note: The location matters: ~/Library/LaunchAgents is where per-user job-definition *.plist files must reside in order to be loaded automatically at every logon.
Paste the following:
<?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>TestJobPeriodic</string>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>http://stackoverflow.com</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>19</integer>
<key>Minute</key>
<integer>00</integer>
</dict>
</dict>
</plist>
Again, TestJobPeriodic uniquely identifies your job.
Jobs runs periodically by default (i.e., the absence of LaunchOnlyOnce makes the job periodic).
From Terminal, run launchctl load ~/Library/LaunchAgents/testPeriodic.plist to load the job.
For background information, see https://stackoverflow.com/a/23880156/45375.
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>
I am attempting to set up a .plist file for use with launchctl on OSX. When run from the command line (not using launchctl), our application is executed as:
/path/to/ourapp
... and to terminate our application, we type:
/path/to/ourapp -k
... which causes the new instance of ourapp to properly kill the previous instance that is running.
Now I set up a .plist file to control execution of the application via launchctl, as follows:
// start.plist
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>ourapp</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/ourapp</string>
</array>
<key>OnDemand</key>
<false/>
<key>UserName</key>
<string>daniel347x</string>
<key>SHAuthorizationRight</key>
<string>system.preferences</string>
</dict>
... and I execute it at the command line, as follows:
launchctl load /path/to/start.plist
This works successfully to launch the application.
Unfortunately, when creating a stop.plist, as follows (the only difference is the addition of the -k argument):
// stop.plist
<dict>
<key>Disabled</key>
<false/>
<key>Label</key>
<string>ourapp</string>
<key>ProgramArguments</key>
<array>
<string>/path/to/ourapp</string>
<string>-k</string> // <-- only difference is adding this argument
</array>
<key>OnDemand</key>
<false/>
<key>UserName</key>
<string>daniel347x</string>
<key>SHAuthorizationRight</key>
<string>system.preferences</string>
</dict>
... and executing via
launchctl unload /path/to/stop.plist
... the application does NOT terminate ... therefore, it seems that when using launchctl, the application is not being executed equivalently to
/path/to/ourapp -k
Can someone tell me what launchctl unload is really doing - i.e. is it calling the application from the command line with the given arguments, or not - and what I can do to get my stop.plist to work when using launchctl unload, given whatever it is doing?
High level
No, "launchctl unload" on OSX does not pass "ProgramArguments" to the target application. It removes the plist from the ones that launchd is running.
Short answer
I'm not sure you are using launchctl and launchd the way it was designed, but try:
launchctl load /path/to/stop.plist
Longer answer
The launchctl command is used to control which plists launchd runs at any given time. When you issue:
launchctl load /path/to/start.plist
the plist is loaded into launchd and your "ourapp" is trigged. A critical point is that unless "ourapp" unloads the start.plist, it will still be running in launchd. You can check this by viewing the output from:
launchctl list
The reason your call to unload the stop.plist didn't have any effect is that it was never loaded into launchd to begin with. To be able to stop it, you must first start it with:
launchctl load /path/to/stop.plist
The trick with this is that once you do this, both your start.plist and stop.plist will be running. Obviously, I haven't tested your app/setup, but this could cause weird behavior. Also, I'm not sure what running the "load" again on your start.plist would do since it would already be running.
Form what you've described here, it looks like launchd and launchctl might not be the best approach. If you want to stick with it, I think you would need to add system calls into your "ourapp" so that when it gets issued the kill command it also unloads both the start and the stop plist. Or, write a proxy script that unloads the plists and then calls your "ourapp -k". That way, the next time you use "launchctl load /path/to/start.plist" you won't be trying to start something that's already running.
One other note. You might have just been using the "ourapp" as place holders, but it's worth pointing out that you probably want to use unique Label names. If nothing else, it'll help you keep track of them with "launchctl list". I don't know if launchd itself would have an issue with duplicate names, but it seems safest to avoid the possibility.