User Has not restarted in X number of days....

bwiessner
Contributor II

Hello All,

Just realized that I have a login policy that over 500 of the computers in my environment do not have yet. Which means my users do not restart their machine .... ever.

Anyway to script something or config something that makes users reboot or forces restart ever 14 days?

12 REPLIES 12

bwiessner
Contributor II

I guess I could just have a blank policy that has restart enabled for a custom event??? Ideas?

andrew
New Contributor

You could get creative with doing a conditional if statement of the "uptime" command, then have this script run daily/weekly/etc.

I have a client where we did something similar. I will see if I can dig up the script.

gregneagle
Valued Contributor

That's a mean sysadmin-y thing to do. If the user has a stable, functional machine, why would you force them to reboot every 14 days? Maybe you should see if whatever its is you are deploying via a login policy could be deployed another way...

andrew
New Contributor

Yeah. Its not the nicest thing to do, but this approach could also be used to send the end user a gentle reminder. In my environment we will have users say "My machine is running slow" or something of that nature only to find out that they have a uptime of 220 days.

AVmcclint
Honored Contributor

It is possible to write a bash script that works with Applescript to check the uptime and then display a popup letting the user know they need to reboot their Mac. I worked on such a thing for weeks only to discover that out company's BigFix system has such a thing already in place. I was able to test it successfully on my Mac but I never got to test it further than that.

For what it's worth, this bit of code was the key to the whole process. It checks for uptime greater than 7 days. I had to get a unix guru to help me build this part. :

#!/bin/bash

up="`uptime | sed -n -e 's/.* up ([0-9][0-9]*) day.*/1/p'`"
 if [ -n "$up" ] && [ "$up" -gt 7 ]
  then osascript ~/Documents/uptime2.scpt
   fi

uptime2.scpt was the Applescript I had that displayed messages with functional [cancel] [restart] [shutdown] buttons. Unfortunately I can't locate that script, but this should give you a good start.

PS. I had the bash script and the applescript files usually saved somewhere safe in /Library/ and I had a LaunchDaemon set to run every 12 hour or something like that.

stevewood
Honored Contributor II
Honored Contributor II

I have a script that I've been using for years up on GitHub: checkUpTime

The script utilizes CocoaDialog to alert the user, along with the mail function to send a message if the user has been up more than 10 days.

#!/bin/sh

# Name: checkUpTime.sh
# Date:  19 Aug 2014
# Author:  Steve Wood (swood@integer.com)
# Purpose:  look for machines that have not been restarted in X number of days.
# Requirements:  cocoaDialog on the local machine
#
# How To Use:  create a policy in your JSS with this script set to run once every day.

## Global Variables and Stuff
logPath='/path/to/store/log/files'  ### <--- enter a path to where you store log files locally
if [[ ! -d "$logPath" ]]; then
    mkdir $logPath
fi
set -xv; exec 1> $logPath/checkUpTime.txt 2>&1
version=1.0
CD="/path/to/cocoaDialog.app/Contents/MacOS/cocoaDialog" ### <--- path to where you store cocoDialog on local machine
NC='/Library/Application Support/JAMF/bin/Management Action.app/Contents/MacOS/Management Action'
jssURL='https://YOUR.JSSSERVER.COM:8443' ### <--- enter your JSS URL
apiUser=$4   ### <--- enter as $4 variable in your script settings
apiPass=$5  ### <--- enter as $5 variable in your script settings
serNum=$(ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}'| sed 's/"//g')
cdTitle="Machine Needs A Restart"
loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`

## set minDays - we start bugging users at this level with just a dialog box
minDays=7

## set maxDays - after we reach maxDays we bug with dialog box AND email
maxDays=15

## Grab user info ##
### Thanks to Bryson Tyrrell (@bryson3Gps) for the code to parse
info=$(curl -s -k -u $apiUser:$apiPass $jssURL/JSSResource/computers/match/$serNum)
email=$(echo $info | /usr/bin/awk -F'<email>|</email>' '{print $2}')
realName=$(echo $info | /usr/bin/awk -F'<realname>|</realname>' '{print $2}')

#### MAIN CODE ####
days=`uptime | awk '{ print $4 }' | sed 's/,//g'`  # grabs the word "days" if it is there
num=`uptime | awk '{ print $3 }'`  # grabs the number of hours or days in the uptime command

## set the body of the email message
message1="Dear $realName"
message1b="Your computer has now been up for $num days.  It is important for you to restart your machine on a regular"
message2="basis to help it run more efficiently and to apply updates and patches that are deployed during the login or logout"
message3="process."
message3a="Please restart your machine ASAP.  If you do not restart, you will continue to get this email and the pop-up"
message4="dialog box daily until you do."
message5="FROM THE IT STAFF"  ### <---  change this to whomever you want

## now the logic

if [ $loggedInUser != "root" ]; then
    if [ $days = "days" ]; then

        if [ $num -gt $minDays ]; then

            if [ $num -gt $maxDays ]; then

                cdIcon="/private/var/inte/icons/redX.icns"
                cdText="Your computer has not been restarted in more than $maxDays days.  Please restart ASAP.  Thank you."

                bubble=`$CD bubble --title "$cdTitle" --no-timeout --text "$cdText" --icon-file $cdIcon`

                if [ $email ]; then

                    echo "$message1

$message1b
$message2
$message3

$message3a
$message4


$message5" | mail -s "URGENT: Restart Your Machine" $email

                fi
            else

                cdIcon="/private/var/inte/icons/ProblemReporter.icns"
                cdText="Your computer has not been restarted in $num days.  Please restart ASAP.  Thank you."
                bubble=`$CD bubble --title "$cdTitle" --no-timeout --text "$cdText" --icon-file $cdIcon`

            fi
        fi
    fi
fi


exit 0

AVmcclint
Honored Contributor

I found the final files and scripts I tested! I forgot that the Applescript path was a bit of a dead end. But it does still use osascript in a bash script.

1:

/Library/Application Support/CompanyName/up.sh

#!/bin/bash

up="`uptime | sed -n -e 's/.* up ([0-9][0-9]*) day.*/1/p'`"
 if [ -n "$up" ] && [ "$up" -gt 7 ]
  then /Library/Application Support/CompanyName/time.sh
   fi

2:

Only if uptime is greater than 7 days (in this case) it will launch /Library/Application Support/CompanyName/time.sh
The "BlueAVMark.icns" is our company logo so that when users see this popup, they will know it is legitimate. You can customize yours however you want.

#!/bin/bash
VARIABLE=$(/usr/bin/osascript <<-EOF
tell application "System Events"
    activate
    set question to display dialog "This computer has not been shut down or restarted for more than 14 days. Please restart as soon as possible to ensure that all security patches deployed to the computer are applied. This also helps the computer run at optimum performance. This message will be repeated every 2 hours until you restart." with title "Company notice: RESTART YOUR COMPUTER" buttons {"Shut Down", "Restart", "Cancel"} with icon file "Macintosh HD:Library:Application Support:Company:BlueAVMark.icns"
    set answer to button returned of question
    if answer is equal to "Shut Down" then
        tell application "System Events"
            shut down
        end tell
    end if
    if answer is equal to "Restart" then
        tell application "System Events"
            restart
        end tell
    end if
    if answer is equal to "Cancel" then
        return
    end if
end tell
EOF)

$VARIABLE
exit 0

3:

/Library/LaunchAgents/com.companyname.uptime.plist (this testing phase was set to run every 60 seconds. You might want to change it to many hours.)

<?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>
    <true/>
    <key>Label</key>
    <string>com.companyname.uptime</string>
    <key>Program</key>
    <string>/Library/Application Support/CompanyName/up.sh</string>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/users/shared/com.companyname.uptime.stderr</string>
    <key>StandardOutPath</key>
    <string>/users/shared/com.companyname.uptime.stdout</string>
    <key>StartInterval</key>
    <integer>60</integer>
</dict>
</plist>

The great thing about this is that it will keep nagging them until they restart their Macs, but it won't force a reboot when a user legitimately can't reboot for whatever reason.
PS... Yeah I know the lengths of time don't all agree. I made lots of changes during the testing phase that I was going to unify once I finished. I just never got around to it. You'll have to make sure the lengths of time all agree before making it "live".

mm2270
Legendary Contributor III

The way we calculate uptime is using sysctl kern.boottime and doing some math to convert it into whatever time format we want, such as hours, days, etc.

For example, the script below will print out the number of hours the Mac has been up, regardless of if the uptime is on the order of hours, days, weeks or months:

#!/bin/bash

bootTimeSecs=$(sysctl kern.boottime | awk -F'[= |,]' '{print $6}')
timeNowSecs=$(date +"%s")

bootTimeHrs=$(($((timeNowSecs-bootTimeSecs))/60/60))

echo "$bootTimeHrs Hours Uptime"

You could also use bc instead of straight bash math calculations to get a value in a decimal format, like "36.5" for example.

As for what to do with that or actions to take, I would agree that this is a sticky area. We never force a reboot, but we may encourage reboots once the Mac has reached a pretty large amount of uptime, especially if the Mac is experiencing issues. Quite remarkable sometimes what a simple reboot can clear up, and all too often Mac users don't seem to understand that no OS was designed to run indefinitely without needing a reboot from time to time. With the OS's saved application states and Resume functionality now, I don't even understand what the issue is with people not rebooting. When I need to do it, I can usually get back to where I was before the reboot fairly quickly.

Still, its bad practice IMO to force it on anyone. All it takes is for such a policy to run on some exec Mac that wasn't properly excluded while he/she is in the middle of some important business or presentation, and you're in the doghouse.

Josh_S
Contributor III

Also, remember, that unless you cache a policy, that policy will not be executed on a given triggering event (login, recurring checkin, etc.) unless it has an active network connection on a network that can reach your JSS at the time of the trigger. In this case, especially if your machines are connecting over Wi-Fi, it's possible that users are restarting, and logging in, but that they can't jump on the network before the login event times out and fails to execute your policy.

I agree with all of the above sentiments that you really shouldn't force restarts unless absolutely necessary, and then make sure it is very well communicated. It's very easy to sow a lot of distrust and ill will with your users that will take a lot of time and effort to repair - if ever. And, especially as your number of managed machines grows, it is very easy to lose that game of whack-a-mole. Your users only have one machine that they have to try and compromise, you have a fleet that you are trying to keep managed. Make your users want to be managed.

For both of these reasons, I echo @gregneagle's sentiments that you should probably try a new way to accomplish your goal.

Look
Valued Contributor III

Just because everyone else posted theirs!

#!/bin/bash
days=`uptime | awk '{ print $4 }' | sed 's/,//g'`
num=`uptime | awk '{ print $3 }'`
# Check if more than a day
if [ $days = "days" ];
then
# More than a day, return the number of days
echo "<result>$num</result>"
else
# Less than a day return 1
echo "<result>1</result>"
fi

McAwesome
Contributor III

@Look Out of curiosity, is there any particular reason why you have that returning a 1 if it hasn't been a day since the last reboot? Seems like it'd be more accurate to return a 0.

Look
Valued Contributor III

@McAwesome I choose 1 because my recon is only daily so the margin for error was upto +1 day so anything less than a day was basically useless information anyway (i.e. this machine was up for less than a day, up to a day ago...).
I was also initially looking for macines that had been up quite a number of days anyway.