At my end with launchd - bash

Hi all and thanks for reading this. I have the below launchd 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.me.sendip</string>
<key>Program</key>
<string>/Users/me/Documents/sendip.sh</string>
<key>StartInterval</key>
<integer>60</integer>
<key>StandardErrorPath</key>
<string>/Users/me/Desktop/sendiperr.log</string>
<key>StandardOutPath</key>
<string>/Users/me/Desktop/sendip.log</string>
</dict>
I also have the below script
/Users/me/Documents/Scripts/ip.sh | mail -s "Laptop ip" me#gmail.com
I have tried troubleshooting with all the tools here. The script fires with an empty log file on boot but after that nothing.
Someone please help. I am totally stumped and I am sure its something silly. Much appreciated.

It turns out launchd abandons the sub processes of the script I was trying to run. this is a common problem as it turns out. Found the answer here.

Hmmm. I run a script at hourly intervals using the following plist in 10.9.5.
<?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>Disabled</key>
<false/>
<key>Label</key>
<string>org.myorg.mytask</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/php</string>
<string>/path/to/myscript.php</string>
</array>
<key>QueueDirectories</key>
<array/>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>5</integer>
</dict>
<key>WatchPaths</key>
<array/>
</dict>
</plist>
That runs every hour at 5 minutes past the hour.
I think for you're looking to run once a minute, right? So your version would be:
<?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>Disabled</key>
<false/>
<key>Label</key>
<string>com.me.sendip</string>
<key>ProgramArguments</key>
<array>
<string>/Users/me/Documents/sendip.sh</string>
</array>
<key>QueueDirectories</key>
<array/>
<key>StartInterval</key>
<integer>60</integer>
<key>WatchPaths</key>
<array/>
</dict>
</plist>
You may need to start the service by running this command in Terminal: (it will require your password)
sudo launchctl load com.me.sendip
Edit: Here is a script with more explicit logging:
#!/bin/bash
OUTFILE='/Library/Logs/com.whatever.sendip.log'
ERRFILE='/Library/Logs/com.whatever.sendip.err.log'
SCRIPT='/Users/me/Documents/Scripts/ip.sh'
TARGET_EMAIL='me#gmail.com'
TARGET_SUBJECT='Laptop ip'
VERBOSE=1
if [ "$VERBOSE" -ne 0 ]; then
date >>"$OUTFILE"
date >>"$ERRFILE"
echo -n 'Current user: ' >>"$OUTFILE"
whoami >>"$OUTFILE"
fi
INFO=$("$SCRIPT" 2>>"$ERRFILE")
echo "$INFO" | mail -s "$TARGET_SUBJECT" "$TARGET_EMAIL" >>"$OUTFILE" 2>>"$ERRFILE"
ERROR_CODE="$?"
if [ "$ERROR_CODE" -ne 0 ]; then
echo "Result: Error Code $ERROR_CODE" >>"$OUTFILE"
echo "Info: $INFO" >>"$OUTFILE"
elif [ "$VERBOSE" -ne 0 ]; then
echo "Result: Success" >>"$OUTFILE"
echo "Info: $INFO" >>"$OUTFILE"
fi

Related

Run remote backup on regulary base

I wrote a shell script which checks if a remote server is reachable.
If it is reachable, it runs via ssh a command at a different server.
After that, it does some rsync tasks.
Everything works well, so I wrote a plist-files to run the whole staff every 4 hours.
What's work:
The script is triggered as expected (I saw it at stdout-file).
launchctl start myjob works fine too (I saw it at stdout-file).
My problem:
If the job starts during the macbook sleeps, the ping works well, but the ssh command doesn't come back. The command is executed at the server, but the script stops there.
If I kill the process with the ssh-command the script finish well.
What can I do to get more information. What could be the error? What could be another solution (Timemachine is not an option, because I want a general readable backup)?
Mac OS BigSur 11.4 M1-Chip
Here my plist-files
?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>de.stanetz.backup</string>
<key>Program</key>
<string>/usr/local/sbin/remoteBackup.nsh</string>
<key>RunAtLoad</key>
<false/>
<key>StandardOutPath</key>
<string>/var/log/launch-stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/launch-stderr.log</string>
<key>Debug</key>
<true/>
<key>StartCalendarInterval</key>
<array>
<dict>
<key>Hour</key>
<integer>9</integer>
<key>Minute</key>
<integer>15</integer>
</dict>
</array>
<key>ExitTimeOut</key>
<integer>360</integer>
<key>TimeOut</key>
<integer>360</integer>
</dict>
</plist>
Update: I changed the ssh command to ssh -vv pooh 'echo "this runs remotely"' >/tmp/so-debug.log 2>&1. Which works fine.
If I call debug1: Sending command: /usr/local/sbin/handleBackupPlatte.nsh open via ssh (which runs 10 seconds), the complete shell-script finished, but I found in the log:
debug2: channel 0: send eof
debug2: channel 0: input drain -> closed
debug1: channel 0: free: client-session, nchannels 1
debug1: fd 0 clearing O_NONBLOCK
debug1: fd 2 clearing O_NONBLOCK
Killed by signal 15.
The duration of time till the remote-script finished seems to be the problem.
#!/bin/bash
echo "this runs remotely"
sleep 10
echo "for 10 seconds"
shows the effect. But I can't make the script faster :-/ Any ideas, except to send the ssh command to background?
Update 2: It seems that every network call which needs 10 seconds will hang and never finished if the mac book is in sleep-state.
I switch to a different approach. I install sleepwatcher which notify if a macbook is wake up. Furthermore, I combined this with a script which ensures that not on every wake up the backup is running.
Here the script
#!/bin/bash
#set -x
set -eu
## RETURN_CODES
# 11 -> BACKUP_ALREADY_RUNNING
# 12 -> NIGHT -> No Backup
# 13 -> Last backup is too young.
#VARIABLES
LOCK_FILE="/tmp/.backup.lock"
LAST_SUCCESS_FILE="/var/log/.backup.last_success"
RUN_CONTROL="/var/log/throttle.log"
MIN_BACKUP_AGE=240
cleanup() {
exitCode=$?
date +"Exit Throttle at %d.%m.%Y %H:%M:%S with $exitCode" >> ${RUN_CONTROL}
if [[ "$exitCode" -ne 11 ]]; then
rm ${LOCK_FILE}
fi
echo "$(tail -n 100 ${RUN_CONTROL})" > ${RUN_CONTROL}
exit $exitCode
}
trap cleanup EXIT
test -e ${LOCK_FILE} && echo "${LOCK_FILE} exists" && exit 11
touch ${LOCK_FILE}
hour=$(date +%H)
if [ "$hour" -gt 22 ] || [ "$hour" -lt 7 ]; then
exit 12
fi
test -e "$(find ${LAST_SUCCESS_FILE} -mmin -${MIN_BACKUP_AGE})" && exit 13 || true
/usr/local/sbin/remoteBackup.nsh
echo "$(tail ${LAST_SUCCESS_FILE})" > ${LAST_SUCCESS_FILE}
date >> ${LAST_SUCCESS_FILE}
and the /Library/LaunchDaemons/de.bernhard-baehr.sleepwatcher.plist
<?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>de.bernhard-baehr.sleepwatcher</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/sbin/sleepwatcher</string>
<string>-V</string>
<string>-w /usr/local/sbin/throttledBackup.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/var/log/sleepwatcher-stdout.log</string>
<key>StandardErrorPath</key>
<string>/var/log/sleepwatcher-stderr.log</string>
</dict>
</plist>

AppleScript code to edit my plist

Using AppleScript, how can I edit key PhoneNumber to a new value and delete key UserActive?
I have read Apple's documentation, but I did not get any success. Thanks for 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>Person</key>
<dict>
<key>Adult</key>
<dict>
<key>Names</key>
<dict>
<key>MyName</key>
<dict>
<key>Adress</key>
<dict>
<key>Profissional</key>
<dict>
<key>CelNumber</key>
<integer>4</integer>
<key>PhoneNumber</key>
<integer>5</integer>
</dict>
<key>UserActive</key>
<integer>0</integer>
</dict>
for edit plist file it's more simple use /usr/libexec/PlistBuddy in your script you can with PlistBuddy
Clear type Clears out all existing entries, and creates root of type
type. See below for a list of types.
Print [entry]
Prints value of entry. If an entry is not specified, prints
entire file. See below for an explanation of how entry works.
Set entry value
Sets the value at entry to value.
Add entry type [value]
Adds entry with type type and optional value value. See
below for a list of types.
One example for see in Terminal window : Controller:IOPCIMatch from AMD5000Controller.kext or AMDRadeonX3000.kext
tell application "Terminal"
activate
do script "/usr/libexec/PlistBuddy -c \"Print :IOKitPersonalities:Controller:IOPCIMatch \" /System/Library/Extensions/AMD5000Controller.kext/Contents/info.plist;
/usr/libexec/PlistBuddy -c \"Print :IOKitPersonalities:AMDCedarGraphicsAccelerator\" /System/Library/Extensions/AMDRadeonX3000.kext/Contents/info.plist
"
end tell
tell application "Terminal"
activate
do script "/usr/libexec/PlistBuddy -c \"Print :IOKitPersonalities:Controller:IOPCIMatch \" /System/Library/Extensions/AMD5000Controller.kext/Contents/info.plist;
/usr/libexec/PlistBuddy -c \"Print :IOKitPersonalities:AMDCedarGraphicsAccelerator\" /System/Library/Extensions/AMDRadeonX3000.kext/Contents/info.plist
"
end tell

Is it possible to evaluate an expression in launchd's ProgramArguments array?

I have a ~/Library/LaunchAgents/setenv.JAVA_HOME.plist file which contains a /bin/launchctl call as follows:
<?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>setenv.JAVA_HOME</string>
<key>ProgramArguments</key>
<array>
<string>/bin/launchctl</string>
<string>setenv</string>
<string>JAVA_HOME</string>
<string>$(/usr/libexec/java_home -v1.8)</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>
Problem is, the $(/usr/libexec/java_home -v1.8) expression is not evaluated and instead the JAVA_HOME environment variable is assigned the literal value $(/usr/libexec/java_home -v1.8).
Question is: is it possible to compose my plist file so that the expression is evaluated and not treated as a literal value? And, if so, how?
As you've discovered, <string> entries are passed as string literals, but $(/usr/libexec/java_home -v1.8) is a bash shell expression. According to the launchd.plist documentation, ProgramArguments only takes an array of strings, so there does not appear to be any way of marking an argument as an expression.
However, I think the simple solution is to run /bin/bash with the -c argument, followed by the command line you want to execute.
<?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.so</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>/bin/launchctl setenv JAVA_HOME $(/usr/libexec/java_home -v1.8)</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>

launchctl status returns 2

I'm trying to write a launchd script that will run a Python script every hour.
I created the following file in ~/Library/LaunchAgents/
com.b00tahead.engage-check.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.b00tahead.engage-check</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Frameworks/Python.framework/Versions/3.5/bin/python3</string>
<string>Projects/engage_scripts/engage_check.py</string>
</array>
<key>StartInterval</key>
<integer>3600</integer>
</dict>
</plist>
I then run:
launchctl load ~/Library/LaunchAgents/com.b00tahead.engage-check.plist
launchctl start com.b00tahead.engage-check
When I run launchctl list it shows the process as:
PID Status Label
- 2 com.b00tahead.engage-check
I'm not exactly sure what that return code indicates. I can run the script just fine through the terminal as python3 Projects/engage_scripts/engage_check.py
The Python script writes some data to a .txt file on my desktop.
This is what launchctl list com.b00tahead.engage-check returns:
{
"LimitLoadToSessionType" = "Aqua";
"Label" = "com.b00tahead.engage-check";
"TimeOut" = 30;
"OnDemand" = true;
"LastExitStatus" = 512;
"Program" = "/Library/Frameworks/Python.framework/Versions/3.5/bin/python3";
"ProgramArguments" = (
"/Library/Frameworks/Python.framework/Versions/3.5/bin/python3";
"Projects/engage_scripts/engage_check.py";
);
};
My guess is that the argument in ProgramArguments should have an absolute path (not relative such as Projects/...).
In case of error, system log should be checked, e.g.
tail -f /var/log/system.log
or custom log can be used by specifying these lines:
<key>StandardOutPath</key>
<string>/var/log/myjob.log</string>
<key>StandardErrorPath</key>
<string>/var/log/myjob.log</string>
See: Debugging launchd Jobs section of Creating Launch Daemons and Agents page.

Inside shell script: write Python output to file works from terminal, "Input stream is empty" when running from launchd

I wrote a short shell script to write the contents of an Evernote note to a file with Geeknote, which runs under python:
#!/bin/bash
/usr/bin/python /Users/me/Git/geeknote/geeknote.py find --search "Hobbies To Do" > /dev/null
/usr/bin/python -u /Users/me/Git/geeknote/geeknote.py show "Hobbies To Do" | tee /Users/me/Programming/output.txt
The script works fine when I run it from terminal (it writes the output of the second python command to output.txt), although ideally I'd like the script to run periodically on its own. So I tried creating a .plist/LaunchAgent to run the script, but when I do that the only thing that gets written to output.txt is Input stream is empty...can't figure out why. Right now my .plist looks 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>com.me.geektoolcron</string>
<key>ProgramArguments</key>
<array>
<string>/Users/me/Programming/script.sh</string>
</array>
<key>StartInterval</key>
<integer>10</integer>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
You could try replacing the second python command with something like echo a or replacing tee with a redirection. A script like this worked for me with the same plist:
#!/bin/bash
date | tee /tmp/f
launchd also supports directing stdout to a file:
<key>StandardOutPath</key>
<string>/Users/me/Programming/output.txt</string>

Resources