LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   Creating shell script to mimic CRON job and execute an external file (https://www.linuxquestions.org/questions/linux-newbie-8/creating-shell-script-to-mimic-cron-job-and-execute-an-external-file-4175591578/)

NotionCommotion 10-17-2016 08:12 AM

Creating shell script to mimic CRON job and execute an external file
 
Hi,

I wish to create a shell script which executes a PHP file ever 15 seconds. Below is my attempt, and while it "kinda" works, it needs help.

First, I wish only one instance to ever be running. Currently, I can have multiple instances all running at once and updating the database causing havoc.

Second, I haven't figured out how to implement stop, restart, and status, and everything causes it to start.

I appreciate any help. Thanks

vi /etc/init.d/15_second_cron
Code:

#!/bin/bash
# /etc/init.d/15_second_cron

### BEGIN INIT INFO
# Provides:          15_second_cron
# Required-Start:    $remote_fs $syslog
# Required-Stop:    $remote_fs $syslog
# Default-Start:    2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: PHP Loop
# Description:      Initiate a given PHP file which updates the database every 15 seconds
### END INIT INFO

while true
do
 /usr/bin/php /var/www/cron.php &
 sleep 15
done
exit 0

Code:

# Make script executable
chmod +x /etc/init.d/15_second_cron

/etc/init.d/15_second_cron start
chkconfig --levels 235 15_second_cron on


michaelk 10-17-2016 08:45 AM

The update appears to take more then 15 seconds and since the script is sent to the background it allows multiple instances to run. To keep things simple I would run it from rc.local and not as a service.

NotionCommotion 10-17-2016 08:50 AM

Quote:

Originally Posted by michaelk (Post 5619104)
The update appears to take more then 15 seconds and since the script is sent to the background it allows multiple instances to run. To keep things simple I would run it from rc.local and not as a service.

I don't expect the script will take more than 15 seconds. Even if it did, I would still want it to execute on 15 second intervals. The reason I put it in the background is so the 15 second increments will be maintained regardless of the the length it took.

What do you mean by rc.local versus a service?

Thank you

thesnow 10-17-2016 08:51 AM

Your script is sending the PHP execution to the background, so right after it starts so does the sleep timer. If your PHP job ever takes more than 15 seconds, it will kick another one off before the first one ends. If you remove the "&", the sleep timer won't start until the PHP script completes.

Can you clarify what "every 15 seconds" means. Is this on the :00, :15, :30, etc.? If the script takes 5 seconds then the next iteration would only be 10 seconds after the first one completes.

Or 15 seconds after the PHP script finishes you want to start the next one. For example, if it starts at 0 and runs for 5 seconds, then the next iteration would start on :20?

NotionCommotion 10-17-2016 08:58 AM

Quote:

Originally Posted by thesnow (Post 5619108)
Can you clarify what "every 15 seconds" means. Is this on the :00, :15, :30, etc.? If the script takes 5 seconds then the next iteration would only be 10 seconds after the first one completes.

Or 15 seconds after the PHP script finishes you want to start the next one. For example, if it starts at 0 and runs for 5 seconds, then the next iteration would start on :20?

I wish to have records written to a database which are 15 seconds apart. The actual time is not important, and it is also okay if they are not exactly 15 seconds apart, but only close. Thanks

michaelk 10-17-2016 09:05 AM

Depends on the distribution but rc.local is a script and actually a "service" but it is the last thing to run before x starts. Typically you add your commands to /etc/rc.local file.

You can use the time command to determine how long the script actually runs and adjust the sleep variable accordingly although without putting it in the background that should satisfy your 15 seconds apart.

josephj 10-17-2016 05:35 PM

michaelk and thesnow point out the details of why your script/configuration is not working as desired.

You can't have something scheduled by cron to start every 15 minutes if it also sleeps and starts itself in 15 minutes and, most importantly, never ends. You have to do it one way or the other.

It's also important, as thesnow indicates, to keep track of when things really run. You can debug this by writing to a log file somewhere with date/time stamps to see when things really happen. There are a lot of things which can influence when a process is run and how long it takes to complete.

Once things work, you can disable the log if you don't need it for other things, but you might want to leave the code in for the next time things change.

NotionCommotion 10-18-2016 07:36 AM

Quote:

Originally Posted by michaelk (Post 5619121)
Depends on the distribution but rc.local is a script and actually a "service" but it is the last thing to run before x starts. Typically you add your commands to /etc/rc.local file.

You can use the time command to determine how long the script actually runs and adjust the sleep variable accordingly although without putting it in the background that should satisfy your 15 seconds apart.

Ah, I see how I can locate it in rc.local. This should work. Wouldn't I still want it in the background? Otherwise, I will be 15 seconds plus the time it takes to execute apart.

Also, while I "could" put in in rc.local, why wouldn't I wish to create a service, and have it start upon server startup? It seems more (not that I would know) professional?

Thanks!

Code:

-sh-4.1$ cat /etc/rc.local
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local
-sh-4.1$


NotionCommotion 10-18-2016 07:39 AM

Quote:

Originally Posted by josephj (Post 5619363)
You can't have something scheduled by cron to start every 15 minutes if it also sleeps and starts itself in 15 minutes and, most importantly, never ends. You have to do it one way or the other.

I am not using a cron job. The service runs constantly, and initiates an event ever 15 seconds (seconds, and as such a cron won't work).

The part I really needed help on is how to implement start, stop, restart, and status. Right now I just have start, and if I attempt to start it twice, it will do so. Instead, if I try to start it twice, I wish to be told "this service is already running" and not to attempt to start it again.

Thanks

EDIT. I know this is not correct, but end result is something like this but will actually work :)

Code:

#!/bin/bash
# /etc/init.d/15_second_cron

### BEGIN INIT INFO
# Provides:          15_second_cron
# Required-Start:    $remote_fs $syslog
# Required-Stop:    $remote_fs $syslog
# Default-Start:    2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: PHP Loop
# Description:      Initiate a given PHP file which updates the datalogger database every 15 seconds (fix and find a better way!)
### END INIT INFO

# Are functions required???
. /etc/rc.d/init.d/functions

prog=15_second_cron
???? pidfile=${PIDFILE-/var/run/httpd/httpd.pid}
???? lockfile=${LOCKFILE-/var/lock/subsys/httpd}
RETVAL=0

        echo -n $"Starting $prog: "
       
        while true
        do
        /usr/bin/php /var/www/datalogger/cron.php &
        sleep 15
        done
       
        # Will never get here???
       
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && touch ${lockfile}
        return $RETVAL
}

stop() {
        ??? status -p ${pidfile} $httpd > /dev/null
        if [[ $? = 0 ]]; then
                echo -n $"Stopping $prog: "
                killproc -p ${pidfile} -d ${STOP_TIMEOUT} $httpd
        else
                echo -n $"Stopping $prog: "
                success
        fi
        RETVAL=$?
        echo
        [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status -p ${pidfile} $httpd
        RETVAL=$?
        ;;
  restart)
        stop
        start
        ;;
esac

exit $RETVAL


rnturn 10-18-2016 08:01 AM

Quote:

Originally Posted by NotionCommotion (Post 5619569)
Instead, if I try to start it twice, I wish to be told "this service is already running" and not to attempt to start it again.

One simple way to do this is to create lock file in say, /var/tmp, that you create just before you launch the background task. Modify the PHP script to delete the lock file just before it exits. This could be a fixed/statically named file or something you generate on the fly and pass off to the PHP script ("Hey, delete this file when you're done.") If your parent script wakes up and sees the lock file then one of two things have happened: the PHP task isn't finished yet (sleep for another 15s and recheck for the lock file) or the PHP script crashed before removing the lock file. Not sure how you'd want to deal with the last situation.

You might want to add some additional code to count how many times in a row the parent script woke up to find that the lock file was still there and maybe notify you that something's taking too long. That might be a way to deal with the PHP script crashed case: bail after N retries. There are a number of bells and whistles you could add to this.

NotionCommotion 10-18-2016 08:11 AM

Thanks rnturn,

Is the lock file like how the apache script uses: lockfile=${LOCKFILE-/var/lock/subsys/httpd}

What is this file all about? pidfile=${PIDFILE-/var/run/httpd/httpd.pid} all about?

In addition to doing this, my goal is also to learn a proper and repeated approach to creating these scripts. Is there a good one to follow as a model?

Thanks

michaelk 10-18-2016 08:42 AM

It will start at boot using rc.local. In my opinion using an init script is over kill but it can be done.

Have you actually timed your php script to see how long it runs? You want it to run every 15 secs but prevent multiple instances. Since you posted that your script is causing multiple instances that would indicate the php script is taking more then 15 seconds.

Two keep this simple you can use the time command to see how long it runs and then compute the necessary sleep time interval.

The problem is the while loop in your init script. Your service is the while loop which should be separate from the init script. Untested but something like:

Code:

  while true
  do
    t=$( /usr/bin/time -f "%e" "/usr/bin/php /var/www/cron.php" )
    i=$( echo "scale=0; 15-$i" | bc )
    if (( $i>0 ))
    then
      sleep $i
    fi
 
done

You can use the daemon command to put the script in the background from your init script or in the rc.local with &

NotionCommotion 10-18-2016 09:20 AM

Quote:

Originally Posted by michaelk (Post 5619595)
Have you actually timed your php script to see how long it runs? You want it to run every 15 secs but prevent multiple instances. Since you posted that your script is causing multiple instances that would indicate the php script is taking more then 15 seconds.

Ah, I see the source of confusion, and apologize for it.

I don't think the script will take anything close to 15 seconds, but even if it did take more, that is fine.

I wish the script to start at 15 second intervals. If it takes 20 seconds, then yes, there will be two PHP processes running at the same time for 5 seconds, and this is okay.

What I don't want is first starting the shell script service so the PHP runs at 15 second intervals, and then starting the shell script service again so that a who new series of 15 second intervals starts.

michaelk 10-18-2016 10:49 AM

The pid file contains the running process ID of the program. Typically used by the init script to see if the program is already running.

A lock file is used used to indicate that a certain resource is in use and should not be accessed by another process.

Another idea would be to add the timing loop to your cron.php script. I would also make it executable and run it from /usr/bin/local.

http://www.thegeekstuff.com/2012/03/lsbinit-script/

rnturn 10-19-2016 11:51 AM

Quote:

Originally Posted by NotionCommotion (Post 5619583)
Is the lock file like how the apache script uses: lockfile=${LOCKFILE-/var/lock/subsys/httpd}

I'm not sure how Apache uses that lock file (or why it's needed). You typically use lock files to prevent multiple processes from using a resource that can only handle one user at a time. Or to prevent multiple instances of some process from running likely because of one of those single-user-only resources or the process writes to a fixed file/location and having multiple copies of it running would create scrambled data (i.e., a log file may now be your single-user-only object).

There are some who pooh-pooh lock files (we occasionally get into discussions about their (dis)advantages at work) but, when you're writing scripts, they're (IMHO) the simplest way to control access to single-user-only objects.

Quote:

What is this file all about? pidfile=${PIDFILE-/var/run/httpd/httpd.pid} all about?
That kind of file is usually something you see created when a service (like Apache) is started. The Apache shutdown script will look for that file and read the process ID contained in it so it knows what process to send "kill" signals to.

Quote:

In addition to doing this, my goal is also to learn a proper and repeated approach to creating these scripts. Is there a good one to follow as a model?
Probably not one of mine. :D


All times are GMT -5. The time now is 12:56 AM.