How To Create A Recurring Restart Notification Message In OS X

I’ve put together the following package and its contents to show how to create an installation package for Mac OS X that will show a dialog window to the user when they have not restarted their computer for over four days.
First i will list all of the scripts and the launch agent with a brief description of what each one does followed by how to package them for the installer.
UptimePreflight.sh
Pre-installation script to unload and remove any existing launch agents just in case you wanted to update this in the future.

#!/bin/bash
 
## Unload and remove previous launch daemon if it exists ##
if who | grep -q console; then
if [ -a "/Library/LaunchAgents/com.company.uptime.plist" ]
then
	echo "Unloading LaunchAgent"
	launchctl unload /Library/LaunchAgents/com.company.uptime.plist
	rm -rf /Library/LaunchAgents/com.company.uptime.plist
	rm -rf /var/uptime/
fi
 
if [ -a "/Library/LaunchDaemons/com.company.uptime.plist" ]
then
	echo "Unloading LaunchDaemon"
	launchctl unload /Library/LaunchDaemons/com.company.uptime.plist
	rm -rf /Library/LaunchDaemons/com.company.uptime.plist
	rm -rf /var/uptime/
fi
else
	exit 1
fi

UptimeLaunchAgent.sh
The first part of this script adds osascript to System Preferences > Security & Privacy > Accessibility under the Privacy tab. If we did not add this then the user would see a security prompt asking them to allow access to osascript when the uptime.sh script is run. Next it gets the logged in user’s UID and the Finder’s process id. Lastly it unloads the com.company.uptime launch agent and then relaunches it as the user.

#!/bin/bash
chmod 655 /var/uptime/appIcon.icns
sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db "INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','/usr/bin/osascript',1,1,1,NULL)"
 
# if someone is logged in
if who | grep -q console; then
 
# get the logged in user's uid
LOGGED_IN_UID=`ls -ln /dev/console | awk '{ print $3 }'`
 
# use the uid and pgrep to find the Finder process id
FINDER_PID=`pgrep -U ${LOGGED_IN_UID} Finder`
 
# use launchctl bsexec to run applescript code in the same Mach bootstrap namespace hierachy as the Finder
launchctl bsexec "${FINDER_PID}" osascript -e '
tell app "Finder"
do shell script "
launchctl unload -S Aqua /Library/LaunchAgents/com.company.uptime.plist
launchctl load -S Aqua /Library/LaunchAgents/com.company.uptime.plist
"
end tell
'
exit 0
fi

Com.company.uptime.plist
Below is the payload of the com.company.uptime plist. This is the LaunchDaemon that will call the uptime.sh script. Basically all this does is run the uptime.sh every 3600 seconds or 60 minutes. It runs as the locally logged in user.

<?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.uptime</string>
	<key>ProgramArguments</key>
	<array>
		<string>/var/uptime/uptime.sh</string>
	</array>
	<key>RunAtLoad</key>
	<true/>
	<key>StartInterval</key>
	<integer>3600</integer>
</dict>
</plist>

Uptime.sh
This script first checks to see if the computers display is asleep or turned off. If the display is not awake then exits. If the display is awake then it checks to see how many days it has been since it was last rebooted. If the number of days is greater than 4 it will then call the uptime.scpt appllescript.

#!/bin/bash
lastRestart=$(uptime | cut -d , -f 1 | awk '{print $3}')
lastRestartDays=$(uptime | cut -d , -f 1 | awk '{print $4}')
displayState=$(ioreg -n IODisplayWrangler | grep -i IOPowerManagement | perl -pe 's/^.*DevicePowerState\"=([0-9]+).*$/\1/')
 
if [[ "$displayState" < "1" ]]
then
	exit 0
fi
 
sleep 2
 
if [[ "$lastRestartDays" =~ "days" ]]
then
	if [[ "$lastRestart" -gt "4" ]]
	then
osascript /var/uptime/uptime.scpt
	fi
fi

Uptime.scpt
This is the applescript that displays the prompt to the user letting them know how many days it has been since they rebooted and that it is a good idea to reboot daily.

set UT to (do shell script "uptime")
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to space
set now to text item 1 of UT
set AppleScript's text item delimiters to "up"
set UpT to words 1 thru 4 of text item 2 of UT
set AppleScript's text item delimiters to tid
tell UpT to set msg to item 1
 
set result to display dialog "It has been " & msg & " days since you last restarted your computer." & return & return & "It is recommended that you restart your computer every day." & "
 
Please restart your computer at your earliest convenience." with title "Technology Notice" with icon file "var:uptime:appIcon.icns" buttons {"Restart", "OK"} default button "OK" giving up after 1800
if button returned of the result is "Restart" then tell application "Finder" to restart

This is what the dialog will look like:

Applescript restart prompt
Applescript dialog prompting the user to restart their computer.

 

Packaging Everything Before Deployment

Here is what the payload of the package looks like. When the install package is run it will place all of the scripts and launch agent in the correct directories with the correct permissions so that everything will run like it supposed to.
upTimeGenericPayload
Here are the scripts that will run before the payload is installed and after the payload has been installed.
upTimeGenericScripts
When the installer runs the firs thing that will happen is the uptimePreflight.sh script will run to clean up and remove any previous versions of this package. Once the Pre-Installation script has finished running then all of the contents of our package payload are put into place. Then the uptimeLaunchAgent.sh script will run that adds osascript to System Preferences > Security & Privacy > Accessibility under the Privacy tab. The uptimeLaunchAgent.sh script also gets the logged in user’s UID and the Finder’s process id before it unloads the com.company.uptime launch agent and then relaunches it as the user. Now that everything has been successfully installed, the launch agent will run the uptime.sh script every hour. The uptime.sh script then runs the checks to see if the user has not restarted their computer in more than four days. If it has been more than four days since they restarted their computer then the uptime.scpt applescript will run which is what creates the dialog window that prompts the user to restart their computer. If the user clicks the OK button then they will be prompted again in another hour until they restart their computer. If they click the Restart button then the computer will reboot.

Leave a Reply

Your email address will not be published. Required fields are marked *

*