0

We have a script that is used to sync some directories to a USB disk drive. It is set to run once a day but often takes longer than that.

To ensure that multiple copies of the script don't run at the same time, we check the list of processes and if our script is present, we immediately exit.

#!/bin/bash

#check if we are already running
running=$(ps aux | /usr/bin/grep -i "usb_sync" | /usr/bin/grep -v grep | /usr/bin/grep -c bash)
echo "usb_sync $running" >/opt/local/backup/usb_sync_log
#If we are, the quit
if [ $running -gt 1 ] ; then
  exit 0
fi

The problem is that this check works fine when running it via sudo and manual invocation through the CLI. However when it is run through cron, it will start regardless. I have tried a couple of different variations but they all seem to run.

This is on FreeNAS 11.2.

tl8
  • 131
  • 1
  • 4
  • Shouldn't it be `if [ $tunning -gt 0 ] ; then` ... if there is one running, then the count will be 1, and so the condition will be true. – Tim Jul 17 '19 at 23:43
  • We want 1 to be running at all time, > 1 allows the original instance to continue (As it is already running when we check). – tl8 Jul 17 '19 at 23:58
  • Inside your script, try logging the output of running=$(ps aux | /usr/bin/grep -i "usb_sync" | /usr/bin/grep -v grep | /usr/bin/grep -c bash). – bgtvfr Jul 18 '19 at 07:47
  • Using cron to ensure a process is running is really dirty in a production environnement. Nowadays, you may want to read https://stackoverflow.com/questions/13627440/how-do-i-ensure-a-process-is-running-even-if-it-kills-itself-it-needs-to-be-r/13627784 – bgtvfr Jul 18 '19 at 08:14
  • 1
    In general you can avoid the whole problem of grep matching it's own progress (which you work around with `ps aux | /usr/bin/grep -i "usb_sync" | /usr/bin/grep -v grep ` ) by using `pgrep` – HBruijn Jul 18 '19 at 08:54

2 Answers2

0

A pid file is a common approach to prevent multiple executions of a script. It can be used like this:

#!/bin/bash

PIDFILE=/home/vagrant/forever.pid
if [ -f $PIDFILE ]
then
  PID=$(cat $PIDFILE)
  ps -p $PID > /dev/null 2>&1
  if [ $? -eq 0 ]
  then
    echo "Process already running"
    exit 1
  else
    ## Process not found assume not running
    echo $$ > $PIDFILE
    if [ $? -ne 0 ]
    then
      echo "Could not create PID file"
      exit 1
    fi
  fi
else
  echo $$ > $PIDFILE
  if [ $? -ne 0 ]
  then
    echo "Could not create PID file"
    exit 1
  fi
fi

sleep 25d
rm $PIDFILE

Where sleep 25d is the actual command to be executed. I recommend to read this blog post for a in deep explanation of the various methods to handle this problem. Script above is taken from there.

The access rights of the user to execute the script as set in the cron file should be checked. The current script could also just fail to identify if the script is already running because the access rights are not sufficient. In any way using a pid file seem like a better and less error prone solution to handle this problem.

Henrik Pingel
  • 8,676
  • 2
  • 24
  • 38
0

Can be done much easier. Create a flag file. At the beginning of your script maybe use:

    ## Basic config
SCRIPTNAME=$(basename "$0")
RUNFILE="${SCRIPTNAME}.run"
# 25h in seconds adjust to your needs.
MAX_AGE=90000


    # check for running process
    if [ -f "$RUNFILE" ]; then
      echo "process still running - exit"
      if [ $(( $(date +%s) - $(date +%s --reference $RUNFILE) )) -gt ${MAX_AGE} ]; then
         rm $RUNFILE;
      else 
         exit
      fi
    fi
    touch $RUNFILE

<your code>

rm ${RUNFILE}

Content of the runfile could be the PID of the running Process. Would be easier to analyse of there is an process running.

Instead of

touch ${RUNFILE}

use

echo $$ >${RUNFILE}