6

What's the best way to tell from a shell script if the host has restarted since the last time the script was run? I can think of storing the uptime in a file on each run, and checking if it has decreased, but that doesn't seem completely robust (a server might restart start quickly, store a low uptime, then reboot slowly and come up with a higher uptime).

Is there something like a "started at" value which would be guaranteed to change only on a reboot? Or some other good way of detecting a restart?

John Gardeniers
  • 27,262
  • 12
  • 53
  • 108
kdt
  • 1,360
  • 3
  • 22
  • 34

7 Answers7

11

If you don't want to use cron, you could also create a directory in /dev/shm. Since that location is in memory, it will always be empty when the computer starts up. If it's not empty, you haven't rebooted.

Brendan Long
  • 342
  • 1
  • 11
5
  • Create a directory somewhere when the system starts.
  • When your script runs if the directory exists then the system has restarted otherwise not.
  • Get your script to remove the directory.

Use the @reboot directive in /etc/crontab to create the directory when the system starts.

user9517
  • 114,104
  • 20
  • 206
  • 289
2

sar command will help you in this.

http://linux.die.net/man/1/sar

Suku
  • 2,006
  • 13
  • 15
2

Another approach would be to touch a file somewhere in the startup scripts and search for that in the script, the script could then delete once it's run the first time.

Just be careful you deal with runtime level changes and so forth nicely.

Decado
  • 1,949
  • 11
  • 17
2

The sysinfo() OS call gives you the time since the last reboot. This is where uptime gets it data before performing some formatting - its simpler to write you're own wrapper around this than parsing the output of uptime / working out how to read the start time of init (pid 1).

#include <stdio.h>
#include <time.h>
#include <errno.h>

int main(int argc, char **argv)
{
   struct sysinfo info;
   int status;

   status=sysinfo(&info);
   if (status==0) {
      printf("%d\n", info.uptime);
   } else {
      fprintf(STDERR,"error %d occurred\n", errno);
   }
   exit (status);
}

You'll also need to touch a file from your script and poll its mtime (using stat(1) from your script or stat(2) from C)

symcbean
  • 19,931
  • 1
  • 29
  • 49
0

This will virtually get you there, this will check if a file has been accessed since the last boot.

#!/bin/bash

unixTimestamp () {
    date --utc --date "$1" +%s
}

if [ "$1" == "" ];then
        echo "You must specify a filename (e.g. './lastRun.sh foobar.sh')"
        exit 255
fi

FILENAME=$1

if [ -f ${FILENAME} ]; then

        if [ -n ${FILENAME} ]; then
                LASTRUN=`ls -laru ${FILENAME}|cut -f6,7,8 -d' '|sed 's/'${FILENAME}'//g'|sed 's/^ *//;s/ *$//'`
                LASTBOOT=`who -b|sed 's/.*system boot//g'|sed 's/^ *//;s/ *$//'`
                echo "Last Run: '${LASTRUN}'"
                echo "Last Boot: '${LASTBOOT}'"
                if [ "$(unixTimestamp ${LASTRUN})" -lt "$(unixTimestamp ${LASTBOOT})" ]; then
                        echo "${FILENAME} has not been run since last boot."
                        exit 1
                else
                        echo "${FILENAME} has been run since last boot"
                        exit 0
                fi
        fi
else
        echo "Unable to find file '${FILENAME}'..."
        exit 255
fi

If you have run the file it will have been accessed, but it also may have been edited by somebody and not run, or maybe touched.

EDIT

A bit of explanation:

LASTRUN=ls -laru ${FILENAME}|cut -f6,7,8 -d' '|sed 's/'${FILENAME}'//g'|sed 's/^ *//;s/ *$//'

For the above command:

ls -laru ${FILENAME}|cut -f6,7,8 -d' '     # Will take the filename and display the last access time   
|sed 's/'${FILENAME}'//g'                  # Will strip the filename out to leave you with the date and time 
|sed 's/^ *//;s/ *$//'                     # Will strip out whitespace before and after the date/time

This will leave you a date/time ready to convert to a unix timestamp

LASTBOOT=who -b|sed 's/.*system boot//g'|sed 's/^ *//;s/ *$//'

For the above command:

who -b                                     # Will show the time the system last booted
|sed 's/.*system boot//g'                  # Will strip out the text "system boot" to leave you with a date/time
|sed 's/^ *//;s/ *$//'                     # Will strip out whitespace before and after the date/time

This will leave you a date/time ready to convert to a unix timestamp

the unixTimestamp function takes the Date/TIME variable and converts it to a unix timestamp and then it's just a case of seeing which is the higher number. The higher number is the most recent, so we can work out if the last thing that happened was a reboot or a file access.

Ardesco
  • 101
  • 3
0
#!/bin/bash
#
# usage: $(basename $0) FILE
# returns TRUE if the file has been run since last boot
#
[[ $# -ne 1 ]] && { grep 'usage' "$0"; exit 255; }

# access time of script/program
atime="$(stat -c %X "$1")" 

# Last boot as unix timestamp
btime="$(date -d "$(who -b | awk '{ print $3, $4 }')" +%s)" 

if [[ $atime -gt $btime ]]; then
    exit 0 # true
else
    exit 1 # false
fi
JeffG
  • 1,184
  • 6
  • 18
  • CAVEAT: This will improperly return TRUE also, if the script/executable has been edited/built since the last reboot. This can also be used against nonexecutables/scripts to test whether they've been accessed since boot. – JeffG Apr 06 '11 at 21:54