cron script won't reboot as it should - bash

I have a Raspberry Pi connected to a VPN via openvpn. Periodically, the connection drops, so I use the following script:
#!/bin/bash
ps -ef | grep -v grep | grep openvpn
if [ $? -eq 1 ] ; then
/sbin/shutdown -r now
fi
I added it to crontab (using sudo crontab -e), I want the script to be executed every 5 minutes:
*/5 * * * * /etc/openvpn/check.sh
The script doesn't work, but it still seems to be executed every five minutes:
tail /var/log/syslog | grep CRON
gives:
Mar 16 21:15:01 raspberrypi CRON[11113]: (root) CMD (/etc/openvpn/check.sh)
...
Moreover, when I run the script manually with sudo ./check.sh, the Pi reboots just like it should.
I don't really understand what's going on here ?
Edit :
As suggested, I added the full path names and went from rebooting the Pi to restarting openvpn:
#!/bin/bash
if ! /bin/ps -ef | /bin/grep '[o]penvpn'; then
cd /etc/openvpn/
/usr/sbin/openvpn --config /etc/openvpn/config.ovpn
fi
The script still doesn't work, although it runs fine when I execute it myself. The script's permissions are 755, so it should be ok ?

The path name of the script matches the final grep so it finds itself, and is satisfied.
The reason this didn't happen interactively was that you didn't run it with a full path.
This is (a twist on) a very common FAQ.
Tangentially, your script contains two very common antipatterns. You are reinventing pidof poorly, and you are examining $? explicitly. Unless you specifically require the exit code to be 1, you should simply be doing
if ! ps -ef | grep -q '[o]penvpn'; then
because the purpose of if is to run a command and examine its exit code; and notice also the trick to use a regex which doesn't match itself. But using pidof also lets you easily examine just the binary executable's file name, not its path.

I finally understood why the script didn't work. Since it was located under /etc/openvpn, the condition if ! ps -ef | grep -q '[o]penvpn' wouldn't return true because of the script being executed. I noticed it when I changed the crontab line to:
*/5 * * * * /etc/openvpn/check.sh >/home/pi/output 2>/home/pi/erroutput
the output file showed the /etc/openvpn/check.sh script being run.
The script now is:
#!/bin/bash
if ! pidof openvpn; then
cd /etc/openvpn/
/usr/sbin/openvpn --config /etc/openvpn/config.ovpn
fi
and this works just fine. Thank you all.

Related

How to check if the cronjob succeeded

I am trying to add a cronjob for patching and I just wanted to know if it has been successful.
I have performed the following:
echo "0 0 * * * root yum -d 0 -y update > /dev/null 2>&1 && shutdown -r +1" >> /etc/cron.d/patch
Now, when I am going to the /var/log/cron, I think all the cron jobs should be listed there. Further, I cannot see any /var/log/syslog. I want to know if my script file (added as patch under /etc/cron.d) has been successful, how can I do that?
Thanks
How to check if the cronjob succeeded
"Edits to a user's crontab and the cron jobs run are all logged by default to /var/log/syslog and that's the first place to check if things are not running as you expect."
$ awk "/clearme/ { print $1 }" /var/log/syslog
Also, set the shebang (the first line):
#!/bin/bash
And ensure you set absolute paths to executable. E.g. datetime should be set as /usr/bin/datetime
So in your case, the command could be:
/usr/bin/yum -d 0 -y update > /dev/null 2>&1 && /usr/bin/shutdown -r +1" >> /etc/cron.d/patch
Cron mails any output on stdout or stderr. To know whether a cron job succeeded or not, make the output dependent on success: all good → no output. Something fishy → echo fishy.
Don't write a long command with such logic in the crontab--put it in a script and start that script from cron.

Cron job won't start again after I stopped it?

I wrote a script to run constantly on startup. If for whatever reason the script were to fail, I wrote a second script to check if it has failed, and if so, run the first script again. I then set this second script as a cronjob to run every minute so that it is constantly checking if the first script is alive.
So to test this, I reboot my system. I can see in htop that the first script is running from start up as expected. Good. I kill the process to test the second script. Sure enough, the second script starts the first script again. Still good. I then kill this process, but the second script won't run again now. It still updates a txt file when I manually start the first script, but the second script just doesn't start the first script like it's supposed to. Is it because I killed the cronjob? Restarting the cron service doesn't fix anything though, so I don't know why my second script isn't running again at all.
First script:
#!/bin/bash
stamp=$(date +%Y%m%d-%H%M)
timeout 10d tcpdump -i eth0 -s 96 -z gzip -C 10 -w /home/user/Documents/${stamp}
Second script:
#!/bin/bash
echo "not running" > /home/working.txt
if (( $(ps -ef | grep -v grep | grep tcpdump.sh | wc -l) > 0 ))
then
echo "tcpdump is running!!!" > /home/working.txt
else
/usr/local/bin/tcpdump.sh start
fi
Any help?
You would probably be better off running a simple for loop as the main script, and that kicks off the tcpdump script in the background, so something like:
#!/bin/bash
while true; do
if ps -ef | grep -v grep | grep -q tcpdump; then
: tcpdump running OK
else
# tcpdump not running - start it off
nohup /usr/local/bin/firstscript.sh start &
fi
sleep 30
done
This checks that "tcpdump.sh" is in the output of the "ps -ef" command - if it is, then do nothing (note that you must have an actual command between the "then" and "else" - the ":" command, which just takes it s arguments and ignores them, is sufficient). If it isn't running, start the first script in the background. Then sleep 30 seconds and check again. (Yes, I could have inverted the test so that I didn't need an empty "then" arm, but it would have made the code less obvious)
You put this script as the one which starts at boot time.
Edit: Do you really want to check for "tcpdump.sh"? Is that what the first script is actually called? Assuming that you actually want to check for the tcpdump program, you could use:
if pgrep tcpdump; then

Shell Script works fine manually, but fails to exectue through Crontab

So i can't seem to fix this issue i'm having with Crontab. I have a shell script i want to run every 15 minutes. I can execute the script manually but Crontab will not launch it successfully. It's weird, I'll set it to execute at a certain time and i can check that the process is running, but it never actually executes. Here's the settings i have in Crontab.
0, 15, 30, 45 * * * root /home/rpitc/Desktop/Script/Refresh
Here's what the Shell Script looks Like.
#!/bin/bash
service=wfica
if (( $(ps -ef | grep -v grep | grep $service | wc -l) > 0 ))
then
echo "$service is running!!!"
else
/home/rpitc/Desktop/Script/Iceweasel.sh & /home/rpitc/Desktop/Script/Login.sh
fi
I've read on here that it could be the path that's creating the issue so what i did was copy all of scripts to the /bin path changed the scripts appropriately, but it still would not execute. Please help, this is being ridiculous!
I think the syntax should be (whitout "root" and without spaces), when using "crontab -e":
0,15,30,45 * * * /home/rpitc/Desktop/Script/Refresh
If you're using a file in "/etc/cron.d/", the crontab entry should look like:
0,15,30,45 * * * root /home/rpitc/Desktop/Script/Refresh

Bash Script not executing command

I have a bash script that is supposed to check for a running instance of a program and if its not running execute a command (echo for testing purposes). The problem is, that bash isn't executing the command even though it works when I copy/paste the command directly to the command line. I imagine my issue is simple and caused by my naivety with the script language.
The line in question:
ps -ef | grep -v grep | grep $SCRIPT > /dev/null || echo "`date` - $0 : $SCRIPT stopped. Restarting..."
If I paste it to the command line it works perfect, but when I run my script nothing happens. Suggestions?
EDIT: I believe I've resolved the issue. The script that is run to check the queue_reader.sh script I foolishly named check_queue_reader.sh" so the ps -ef command was finding itself preventing the || from executing.
See edit on original post.
The script I was running was called "check_queue_reader.sh" and was using grep to search ps -ef for "queue_reader.sh". This caused the script to locate itself thus failing the check when it got to ||

Linux crontab doesnt launch a script

I have this user crontab (accessed via the command crontab -e):
# m h dom mon dow command
*/3 * * * * sh /home/FRAPS/Desktop/cronCheck.sh
The script cronCheck.sh looks like that:
#!/bin/sh
SERVICE='Script'
if ps ax | grep -v grep | grep -i "$SERVICE" > /dev/null
then
echo "######## $SERVICE service running, everything is fine ##################\n" >> CronReport.txt
else
echo "$SERVICE is not running. Launching it now\n" >> CronReport.txt
perl Script.pl
fi
When I launch the script (cronCheck.sh) from its own directory, it works like a charm, but when cron launches it, it always "# $SERVICE service running, everything is fine ###"
despite 'Script' is not running.
Thanks,
Here's an even better way to write that conditional:
services=$(ps -e -o comm | grep -cFi "$SERVICE")
case "$services" in
(0)
# restart service
;;
(1)
# everything is fine
;;
(*)
# more than one copy is running
;;
esac
By using ps -e -o comm you avoid having to do the silly grep -v grep thing, because only the actual process name appears in the ps output, not the arguments. And grep -cFi counts up the matches and gives you a number, so you don't have to deal with the exit status of a pipeline.
Also, as other posters have implied, you should lead off this script by setting the PATH variable.
PATH=/bin:/usr/bin:/sbin:/usr/sbin
export PATH
You might or might not want to put /usr/local/bin at the beginning of that list, depending on your system. Don't do it if you don't need anything from there.
Final piece of advice: When writing scripts that will execute without user supervision (such as cron jobs), it's a good idea to put set -e at the beginning. That makes them exit unsuccessfully if any command fails.
You need to put the grep -v grep after the grep -i "$SERVICE". The way you have it now it's guaranteed to be true.
Checking the return status of a pipe like that could be problematic. You should either check the $PIPESTATUS array, or you can pipe the final grep into wc -l to count the number of lines.
cron typically does not set up a lot of the environment like a user account does. You may need to modify your script to get things setup properly.
Cron jobs don't get the same environment settings that you get at a shell prompt - those are generally set up by your shell on login - so you want to use absolute rather than relative paths throughout. (i.e. don't assume the PATH environment variable will exist or be set up the same as it is for you at a shell prompt, and don't assume the script will run with PWD set to your home directory, etc.) So:
in your crontab entry replace sh with /bin/sh (or remove it if cronCheck.sh is executable, the shebang line will do).
in cronCheck.sh add paths to the log file and the perl script.
cronCheck.sh should end up looking something like:
#!/bin/sh
SERVICE='Script'
if ps ax | grep -v grep | grep -i "$SERVICE" > /dev/null
then
echo "######## $SERVICE service running, everything is fine ##################\n" >> CronReport.txt
else
# Specify absolute path to a log file that's writeable for the user the
# cron runs as (probably you). Example: /tmp/CronReport.txt
echo "$SERVICE is not running. Launching it now\n" >> /tmp/CronReport.txt
# Specify absolute path to both perl and the script. Example: /usr/bin/perl
# and /home/FRAPS/scripts/Script.pl
/usr/bin/perl /home/FRAPS/scripts/Script.pl
fi
(Again you can get rid of the /usr/bin/perl bit if Script.pl is executable and has the path to the right perl in the shebang line.)

Resources