Why is the BASH script being executed twice? I’m using launchd

bashlaunchdsleep-wake

I have one plist associated with this launchd agent. There is one agent listed in launchctl list, and it calls my bash script, called rc.wakeup, whenever the mac is woken up from sleep. There is only one copy of this bash script. The code in the bash script is not repeated. Despite this, the bash script is being executed twice in its entirety. Why?

I have created an install bash script and an uninstall bash script. I have been executing these in series an an attempt to get both scripts working so this program can be used by anyone. I believe improperly installing/uninstalling these files has caused the problem, although I have no idea why it would.

Here is the 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>/usr/local/sbin/sleepwatcher</string>
            <string>-V</string>
            <string>-s /etc/rc.sleep</string>
            <string>-w /etc/rc.wakeup</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>

Here is the install script I use to install the program:

#!/bin/bash
sudo mkdir -p /usr/local/sbin /usr/local/share/man/man8
sudo cp ~/Desktop/mysecureview/sleepwatcher_2.2/sleepwatcher /usr/local/sbin
sudo cp ~/Desktop/mysecureview/sleepwatcher_2.2/sleepwatcher.8     /usr/local/share/man/man8

mkdir ~/mysecureview
mkdir ~/mysecureview/log
mkdir ~/mysecureview/pics

cp ~/Desktop/mysecureview/imagesnap-master/imagesnap ~/mysecureview/imagesnap

mkdir ~/Library/LaunchAgents
cp ~/Desktop/mysecureview/sleepwatcher_2.2/config/de.bernhard-baehr.sleepwatcher-20compatibility.plist ~/Library/LaunchAgents/de.bernhard-baehr.sleepwatcher-20compatibility.plist

launchctl load ~/Library/LaunchAgents/de.bernhard-baehr.sleepwatcher-20compatibility.plist

sudo cp ~/Desktop/mysecureview/sleepwatcher_2.2/config/rc.wakeup /etc/rc.wakeup
sudo cp ~/Desktop/mysecureview/sleepwatcher_2.2/config/rc.wakeup /etc/rc.sleep

cp ~/Desktop/mysecureview/num.txt ~/mysecureview/num.txt
cp ~/Desktop/mysecureview/logs.txt ~/mysecureview/logs.txt
cp ~/Desktop/mysecureview/compile.bash ~/mysecureview/compile.bash
cp ~/Desktop/mysecureview/uninstall ~/mysecureview/uninstall

mkdir ~/mysecureview/logs.txt
echo "========================"
echo "~Installation Succesful~"
echo "========================"

here is the uninstallation script:

#!/bin/bash
launchctl unload ~/Library/LaunchAgents/de.bernhard-baehr.sleepwatcher-20compatibility.plist;
rm ~/Library/LaunchAgents/de.bernhard-baehr.sleepwatcher-20compatibility.plist
sudo rm /etc/rc.wakeup
sudo rm /etc/rc.sleep

sudo killall sleepwatcher
sudo rm -rf /Library/StartupItems/SleepWatcher

launchctl unload de.bernhard-baehr.sleepwatcher
launchctl remove de.bernhard-baehr.sleepwatcher

rm ~/mysecureview/num.txt
rm ~/mysecureview/compile.bash
rm ~/mysecureview/imagesnap
rm ~/mysecureview/logs.txt

for i in {1..100}
do
        p=".txt"
        x="$i$p"
        rm ~/mysecureview/log/$x
done

rmdir ~/mysecureview/log

echo "====================="  
echo "~Uninstall Successful"
echo "====================="

Here is the actual script being executed when the computer is woken up:

for user in `echo 'show State:/Users/ConsoleUser' | scutil | awk     '/kCGSSessionUserNameKey/ { print $3 }'`; do
        home=`eval echo ~$user`
        if [ -x "$home/.wakeup" ]; then
                logger -t $0 "executing .wakeup of user $user"
                su - $user -c "$home/.wakeup"
        fi
done

#The following is the MySecureView script used to capture the picture and its information.
cd ~/mysecureview

# take a picture.
./imagesnap

#get the log index. Create a variable for and iterate the index.
numPics=$(cat ~/mysecureview/num.txt)
numPics=$[numPics+1]
jpg=".jpg"
index="$numPics$jpg"

#We only allow 100 log entries to exist at a time.
if [ $numPics == 100 ]; then
        numPics=1 
fi

#temp storage for the newest picture.
cp ~/mysecureview/snapshot.jpg ~/mysecureview/pics/$index

rm ~/mysecureview/snapshot.jpg
rm ~/mysecureview/date.txt

# temporary storage for the most recent log entry.
date=$(date)
entry="$numPics $date"

logNum="$numPics.txt"
echo "$entry" > ~/mysecureview/log/$logNum

#store the index for next use.
echo "$numPics" > ~/mysecureview/num.txt

#compile each and every log entry .txt file into the logs.txt file.
bash compile.bash

Best Answer

There's a lot going on here.

You've got a daemon called SleepWatcher 2.2 which is being kept alive by launchd. It supposedly runs your /etc/wakeup.rc script on computer wake. Your script then calls the program imagesnap.

Your script is being executed twice. I'd first look at launchctl list and then ps to see if SleepWatcher is running two instances. If not, then I'd point the finger at SleepWatcher calling the on-wake script twice.

Unless you want to dig into SleepWatcher's source, you will need to make your script resilient to multiple calls. Maybe touching a lock file at the beginning of execution and removing it some seconds after completion? Your script would then check for the existence of this lock file and abort if it exists.

PS: I'd put your wakeup.rc file somewhere in the user's home folder, not in system-wide /etc because it's a user-level launchd agent, not a root-level one. Maybe ~/mysecureview/.wakeup.