Bash script to check if running - bash

I have the following in my crontab to sync my folders on the servers as they are run on round robin DNS settings it syncs every minute.
* * * * * rsync -ar --delete -e ssh user#skynet.rizzler.se:/home/user/folder1/ /home/user/folder1/ >/dev/null 2>&1
* * * * * rsync -ar --delete -e ssh user#skynet.rizzler.se:/home/user/folder2/ /home/user/folder2/ >/dev/null 2>&1
This Rsync works for small files, but it's getting bigger so I am wondering how I can get this into a script, put that script into my crontab and each time the script is run, it will check if it's running. If the script is running, it will do nothing; if it's not running, it should go ahead and start the rsync.
Anyone know how I can do this?

You could write the script's PID into a file with echo $$ > /var/run/rsync_job.pid
Then when you start you could first check if that file exists, and if it does read the PID from it and see if that process still exists like
if ps -p $PID &>/dev/null; then
# it's still running
exit
fi

As swornabsent noted in the comments, you can use file-based locks, though that SO link identifies some good reasons not to use it. See this SO question about quick-and-dirty locking in shell scripts, which has a great answer about advantages to directory-based locks instead.
To identify whether a process is running, you may also choose to use pgrep, though that may not be a good idea in the general case due to the time after the check and before the process starts up.

Related

How can I improve this bash script?

I'm trying to write a bash script.
The script should check if the MC server is running. If it crashed or stopped it will start the server automatically.
I'll use crontab to run the script every minute. I think I can run it every second it won't stress the CPU too much. I also would like to know when was the server restarted. So I'm going to print the date to the "RestartLog" file.
This is what I have so far:
#!/bin/sh
ps auxw | grep start.sh | grep -v grep > /dev/null
if [ $? != 0 ]
then
cd /home/minecraft/minecraft/ && ./start.sh && echo "Server restarted on: $(date)" >> /home/minecraft/minecraft/RestartLog.txt > /dev/null
fi
I'm just started learning Bash and I'm not sure if this is the right way to do it.
The use of cron is possible, there are other (better) solutions (monit, supervisord etc.). But that is not the question; you asked for "the right way". The right way is difficult to define, but understanding the limits and problems in your code may help you.
Executing with normal cron will happen at most once per minute. That means that you minecraft server may be down 59 seconds before it is restarted.
#!/bin/sh
You should have the #! at the beginning of the line. Don't know if this is a cut/paste problem, but it is rather important. Also, you might want to use #!/bin/bash instead of #!/bin/sh to actually use bash.
ps auxw | grep start.sh | grep -v grep > /dev/null
Some may suggest to use ps -ef but that is a question of taste. You may even use ps -ef | grep [s]tart.sh to prevent using the second grep. The main problem however with this line is that that you are parsing the process-list for a fairly generic start.sh. This may be OK if you have a dedicated server for this, but if there are more users on the server, you run the risk that someone else runs a start.sh for something completely different.
if [ $? != 0 ]
then
There was already a comment about the use of $? and clean code.
cd /home/minecraft/minecraft/ && ./start.sh && echo "Server restarted on: $(date)" >> /home/minecraft/minecraft/RestartLog.txt > /dev/null
It is a good idea to keep a log of the restarts. In this line, you make the execution of the ./start.sh dependent on the fact that the cd succeeds. Also, the echo only gets executed after the ./start.sh exists.
So that leaves me with a question: does start.sh keep on running as long as the server runs (in that case: the ps-test is ok, but the && echo makes no sense, or does start.sh exit while leaving the minecraft-server in the background (in that case the ps-grep won't work correctly, but it makes sense to echo the log record only if start.sh exits correctly).
fi
(no remarks for the fi)
If start.sh blocks until the server exists/crashes, you'd be better off to simply restart it in an infinite loop without the involvement of cron. Simply type in a console (or put into another script):
#!/bin/bash
cd /home/minecraft/minecraft/
while sleep 3; do
echo "$(date) server (re)start" >> restart.log
./start.sh # blocks until server crashes
done
But if it doesn't block (i.e. if start.sh starts the server and then returns, but the server keeps running), you would need to implement a different check to verify if the server is actually still running, other than ps|grep start.sh
PS: To kill the infinite loop you have to Ctrl+C twice: Once to stop ./start.sh and once to exit from the immediate sleep.
You can use monit for this task. See docu. It is available on most linux distributions and has a straightforward config. Find some examples in this post
For your app it will look something like
check process minecraftserver
matching "start.sh"
start program = "/home/minecraft/minecraft/start.sh"
stop program = "/home/minecraft/minecraft/stop.sh"
I wrote this answer because sometimes the most efficient solution is already there and you don't have to code anything. Also follow the suggestions of William Pursell and use the init system of your OS (systemd,upstart,system-v,etc.) to host your scripts.
Find more:
Shell Script For Process Monitoring

How can I tell if a script was run in the background and with nohup?

Ive got a script that takes a quite a long time to run, as it has to handle many thousands of files. I want to make this script as fool proof as possible. To this end, I want to check if the user ran the script using nohup and '&'. E.x.
me#myHost:/home/me/bin $ nohup doAlotOfStuff.sh &. I want to make 100% sure the script was run with nohup and '&', because its a very painful recovery process if the script dies in the middle for whatever reason.
How can I check those two key paramaters inside the script itself? and if they are missing, how can I stop the script before it gets any farther, and complain to the user that they ran the script wrong? Better yet, is there way I can force the script to run in nohup &?
Edit: the server enviornment is AIX 7.1
The ps utility can get the process state. The process state code will contain the character + when running in foreground. Absence of + means code is running in background.
However, it will be hard to tell whether the background script was invoked using nohup. It's also almost impossible to rely on the presence of nohup.out as output can be redirected by user elsewhere at will.
There are 2 ways to accomplish what you want to do. Either bail out and warn the user or automatically restart the script in background.
#!/bin/bash
local mypid=$$
if [[ $(ps -o stat= -p $mypid) =~ "+" ]]; then
echo Running in foreground.
exec nohup $0 "$#" &
exit
fi
# the rest of the script
...
In this code, if the process has a state code +, it will print a warning then restart the process in background. If the process was started in the background, it will just proceed to the rest of the code.
If you prefer to bailout and just warn the user, you can remove the exec line. Note that the exit is not needed after exec. I left it there just in case you choose to remove the exec line.
One good way to find if a script is logging to nohup, is to first check that the nohup.out exists, and then to echo to it and ensure that you can read it there. For example:
echo "complextag"
if ( $(cat nohup.out | grep "complextag" ) != "complextag" );then
# various commands complaining to the user, then exiting
fi
This works because if the script's stdout is going to nohup.out, where they should be going (or whatever out file you specified), then when you echo that phrase, it should be appended to the file nohup.out. If it doesn't appear there, then the script was nut run using nohup and you can scold them, perhaps by using a wall command on a temporary broadcast file. (if you want me to elaborate on that I can).
As for being run in the background, if it's not running you should know by checking nohup.

Trouble Automating Linux (Specifically Raspbian) Scripts

I've been experimenting with Pis for a little while now and am close to completing my first project, I have all the bits working but I'm struggling to put them all together into an automated process.
Basically I have a Pi setup to run a fbi slideshow from a specific folder and I want it to constantly be looking for a pre established Wifi network and when it finds this network it needs to run an update script. I've got these two bits working.
From here, I want the Pi to be turned on and load straight into fbi whilst running the checking script in the background, if the checking script finds the Wifi network then it will run as normal (preferably without ending the slideshow) and when it's done fbi should have an updated selection of images to run (if a restart of fbi is necessary then so be it).
I'm coming up short on achieving this. I can run one script or the other, and if I automate the checking script (cron hasn't worked though I dare say I'm doing something wrong) with rc.local it just gets stuck in a checking loop before login, which kinda makes sense given the script.
Here's the monitoring script:
#!/bin/bash
while true ; do
if ifconfig wlan0 | grep -q "inet addr:" ; then
echo "Wifi connected!"
echo "Initiating Grive sync!"
(cd /home/pi/images/; ./grive -s Pi_Test -V)
sleep 60
else
echo "Wifi disconnected! Attempting to reconnect now."
ifup --force wlan0
sleep 10
fi
done
and in case it's relevant, here's the command the run the fbi slideshow:
fbi -noverbose -a -t 10 -u /home/pi/images/Pi_Test/*.jpg
I do not have a Pi but I have used cron on my VPS which is running a CentOS, but the overall procedure should be similar.
To have some script to be executed by cron, you need to:
Edit /etc/cron.allow
You need to add in your user id to this file so that you can use crontab.
crontab -e
Use this command to add rules you want to fire.
From your description, it seems to me that you already know the syntax to add rules into cron table.
After that, you can use crontab -l to verify your change.
As for stuck at before login, that is very likely due to the while loop. You might want to get rid of the while and sleep because cron is helping you out by invoking your script periodically.
Therefore the following should not suffer from the stuck issue.
if ifconfig wlan0 | grep -q "inet addr:" ; then
echo "Wifi connected!"
echo "Initiating Grive sync!"
(cd /home/pi/images/; ./grive -s Pi_Test -V)
else
echo "Wifi disconnected! Attempting to reconnect now."
ifup --force wlan0
fi
The trick instead, is to have some line similar to this in your cron table
*/1 * * * * /home/David_Legassick/test.sh
The */1 asks cron to call your script test.sh every minute.

restart a php script using shell script

i am using shell script to monitor the working of a php script. My aim is that this php script should not sleep / terminated and must always be running.The code i used is -
ps aux | grep -v grep | grep -q $file || ( nohup php -f $file -print > /var/log/file.log & )
now this idea would not work for cases if the php script got terminated(process status code T). Any idea to handle that case. can such processes be killed permanently and then restarted.
How about just restarting the php interpreter when it dies?
while true ; do php -f $file -print >> /var/log/file.log ; done
Of course, someone could send the script a SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU to cause it to hang, but perhaps that person has a really good reason. You can block them all except SIGSTOP, so maybe that's alright.
Or if the script does something like call read(2) on a device or socket that will never return, this won't really ensure the 'liveness' of your script. (But then you'd use non-blocking IO to prevent this situation, so that's covered.)
Oh yes, you could also stuff it into your /etc/inittab. But I'm not giving you more than a hint about this one, because I think it is probably a bad idea.
And there are many similar tools that already exist: daemontools and Linux Heartbeat are the first two to come to mind.
If the script is exiting after it's been terminater, or if it crashes out, and needs to be restarted, a simple shell script can take care of that.
#!/bin/bash
# runScript.sh - keep a php script running
php -q -f ./cli-script.php -- $#
exec $0 $#;
The exec $0 re-runs the shell script, with the parameters it was given.
To run in the background you can nohup runScript.sh or run it via init.d scripts, upstart, runit or supervisord, among others.

How to make ssh to kill remote process when I interrupt ssh itself?

In a bash script I execute a command on a remote machine through ssh. If user breaks the script by pressing Ctrl+C it only stops the script - not even ssh client. Moreover even if I kill ssh client the remote command is still running...
How can make bash to kill local ssh client and remote command invocation on Crtl+c?
A simple script:
#/bin/bash
ssh -n -x root#db-host 'mysqldump db' -r file.sql
Eventual I found a solution like that:
#/bin/bash
ssh -t -x root#db-host 'mysqldump db' -r file.sql
So - I use '-t' instead of '-n'.
Removing '-n', or using different user than root does not help.
When your ssh session ends, your shell will get a SIGHUP. (hang-up signal). You need to make sure it sends that on to all processes started from it. For bash, try shopt -s huponexit; your_command. That may not work, because the man page says huponexit only works for interactive shells.
I remember running into this with users running jobs on my cluster, and whether they had to use nohup or not (to get the opposite behaviour of what you want) but I can't find anything in the bash man page about whether child processes ignore SIGHUP by default. Hopefully huponexit will do the trick. (You could put that shopt in your .bashrc, instead of on the command line, I think.)
Your ssh -t should work, though, since when the connection closes, reads from the terminal will get EOF or an error, and that makes most programs exit.
Do you know what the options you're passing to ssh do? I'm guessing not. The -n option redirects input from /dev/null, so the process you're running on the remote host probably isn't seeing SIGINT from Ctrl-C.
Now, let's talk about how bad an idea it is to allow remote root logins:
It's a really, really bad idea. Have a look at HOWTO: set up ssh keys for some suggestions how to securely manage remote process execution over ssh. If you need to run something with privileges remotely you'll probably want a solution that involves a ssh public key with embedded command and a script that runs as root courtesy of sudo.
trap "some_command" SIGINT
will execute some_command locally when you press Ctrl+C . help trap will tell you about its other options.
Regarding the ssh issue, i don't know much about ssh. Maybe you can make it call ssh -n -x root#db-host 'killall mysqldump' instead of some_command to kill the remote command?
What if you don't want to require using "ssh -t" (for those as forgetful as I am)?
I stumbled upon looking at the parent PID, because CTRL/C from the initiating session results in the ssh-launched process on the remote process exiting, although its child process continues. By way of example, here's my script that is on the remote server.
#!/bin/bash
Answer=(Alive Dead)
Index=0
while [ ${Index} -eq 0 ]; do
if ! kill -0 ${PPID} 2> /dev/null ; then Index=1; fi
echo "Parent PID ${PPID} is ${Answer[$Index]} at $(date +%Y%m%d%H%M%S%Z)" > ~/NowTime.txt
sleep 1
done
I then invoke it with "ssh remote_server ./test_script.sh"
"watch cat ~/NowTime.txt" on the remote server shows the timestamp in the file increasing and declaring that the parent process is alive; once I hit CTRL/C in the launching process, the script on the remote server notes that its parent process has died, and the script exits.

Resources