Skip to main content

Wanted to put this here in case it helps anyone.

To reboot at next start up only, we came up with a PKG that included these three items:

The Launch Daemon loads at startup, and is set to root:wheel and 644 as per Apple, and triggers a script:

<?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.company.RunOnceOnStartupOnly-recon</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Library/Company/Scripts/RunOnceOnReboot-recon.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Script uses /usr/bin/tee for logging to send stdout/stderr to the same log, and is set to root:wheel and 755 as per Apple. Triggered on startup, it runs recon, then deletes the Launch Daemon, then deletes itself:

#!/bin/sh

# determine path of JAMF binary
os_version="$( system_profiler SPSoftwareDataType -xml | grep -A 2 'os_version</key>' | grep -o 'OS X [0-9]+.[0-9]+' | grep -o '[0-9]+.[0-9]+' )"
major_version="$( /bin/echo "$os_version" | grep -o '[0-9]+.' | grep -o '[0-9]+' )"
minor_version="$( /bin/echo "$os_version" | grep -o '.[0-9]+' | grep -o '[0-9]+' )"
if [ "$major_version" -lt 10 ] || [ "$major_version" -eq 10 -a "$minor_version" -lt 7 ];then
    jamfCLIPath="/usr/sbin/jamf"
else
    jamfCLIPath="/usr/local/jamf/bin/jamf"
fi

logFile="/Library/Logs/com.company.RunOnceOnStartupOnly-recon.log"
dateStamp="[ $( date +%Y-%m-%d_%R_%Z ) ]"
launchDaemon="/Library/LaunchDaemons/com.company.RunOnceOnStartupOnly-recon.plist"

# Run recon
/bin/echo "$dateStamp Taking a 60 second nap..." 2>&1 | tee -a "$logFile"
/bin/sleep 60
/bin/echo "$dateStamp Starting recon..." 2>&1 | tee -a "$logFile"
"$jamfCLIPath" recon 2>&1 | tee -a "$logFile"
/bin/echo "$dateStamp Finished recon..." 2>&1 | tee -a "$logFile"

# Delete Launch Daemon
if [ -e "$launchDaemon" ]; then
    /bin/echo "$dateStamp Removing Launch Daemon..." 2>&1 | tee -a "$logFile"
    /bin/rm "$launchDaemon" 2>&1 | tee -a "$logFile"
else
    /bin/echo "$dateStamp Launch Daemon does not exist." 2>&1 | tee -a "$logFile"
fi

# Completed, exiting
/bin/echo "$dateStamp Completed, goodbye." 2>&1 | tee -a "$logFile"
/bin/rm "$0"

Log file goes into the standard location, and includes sufficient logging with date stamp for staff to monitor and troubleshoot /Library/Logs/com.company.RunOnceOnStartupOnly-recon.log.

The naming convention follows our standard, so anyone who needs to run recon once on startup only can add it to a policy.

Thanks to @Andrina for her help during Jamf Nation 2016 (whether she realized it or not!), to @SeanA for the second set of eyes and insight into some of these methods, and to @henryxyz for the tip on tee.

Lastly, to head off any notion of unloading the Launch Daemon in this solution, learned the hard way...

HTH,
Don

For my curiosity, what happens when a launch daemon triggers a script that unloads/deletes said launch daemon? Does unloading kill all child processes including the script itself?


@cerberusss yes, hence the tweet. ;)


There is the nifty launchDaemon key:

<key>AbandonProcessGroup</key>
<true/>

I use that whenever I have a launchD run a script that might have to send an e-mail. It keeps child processes from being killed when the launchD exists.


@donmontalvo Thank you for this script. Works great after few modifications to fit our environment.

  • added sleep 60 right before jamf recon to allow our wireless laptops to connect before recon finishes
  • changed /bin/rm $0 to /bin/rm "$0" because our script location contains a space

@benbass wow, thanks, I'll test that on the next go!

@EdLuo nice, both valid, appreciated...updated script!

@dan.snelson just noticed your post from a month ago, awesome stuff, hope you don't mind my linking to it...the more ideas the better!


Eeyore
@donmontalvo Eeyore says: "Thanks for noticing me."