Is there any better way to run a script as daemon which continuously polls a directory to check the presence of a file? - shell

I have to continuously check if a file is present in a particular directory. I am doing this with filecopy.sh script:
#!/bin/bash
while true;
do
if [ -f /var/tmp/*.*cim ]; then
echo "Checking the file available in the path"
mv /var/tmp/*.*cim /etc/opt/maptranslator/ss7
/etc/init.d/ss7-stack restart
else
continue;
fi
done
I want the filecopy.sh script to run as daemon. I wrote the following script:
#!/bin/bash
case "$1" in
start)
/etc/init.d/filecopy.sh &
echo $!>/var/run/filecopy.pid
;;
stop)
kill `cat /var/run/filecopy.pid`
rm /var/run/filecopy.pid
;;
restart)
$0 stop
$0 start
;;
status)
if [ -e /var/run/filecopy.pid ]; then
echo filecopy.sh is running, pid=`cat /var/run/filecopy.pid`
else
echo filecopy.sh is NOT running
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
esac
exit 0
I would like to know if there is any better way to achieve this.

Write a small C program that calls inotify(7).
See http://man7.org/linux/man-pages/man7/inotify.7.html or man 7 inotify
In this case you are waiting for /var/tmp to change.
However you really really shouldn't be using /var/tmp at all unless you want some random user to hose your protected area. File a security bug against the process that created its pid file in /var/tmp.

Related

Shell script create unexpected file "start" on starting process

I've found and modified a simple shell script to start/stop a jar, but when launching the script it creates an extra empty start file.
I cannot understand why. Any clue?
#!/bin/bash
case $1 in
start)
if [[ -e myprog.pid ]]
then
echo "myprog.pid found. Is myprog already running?"
else
exec java -jar myprog-0.0.1-SNAPSHOT.jar 1>/dev/null 2>$1 &
echo $! > myprog.pid;
fi
;;
stop)
kill $(cat myprog.pid);
rm myprog.pid
;;
*)
echo "usage: myprog {start|stop}" ;;
esac
exit 0
Your problem is 2>$1. That's a typo.
You meant 2>&1.
What you wrote is expanded by the shell as 2>start and creates your file.

How to proceed in the script if file exists ?

how to proceed in the script if file exists?
#!/bin/bash
echo "Start"
# waiting to be exist file
echo "file already exists, continuing"
Do a while if a sleep X, so that it will check the existence of the file every X seconds.
When the file will exist, the while will finish and you will continue with the echo "file already exists, continuining".
#!/bin/bash
echo "Start"
### waiting to be exist file
while [ ! -f "/your/file" ]; # true if /your/file does not exist
do
sleep 1
done
echo "file already exists, continuing"
And goes instead of checking the file existence check if the script
has already completed the background?
Based on the code you posted, I did some changes to make it work completely:
#!/bin/bash
(
sleep 5
) &
PID=$!
echo "the pid is $PID"
while [ ! -z "$(ps -ef | awk -v p=$PID '$2==p')" ]
do
echo "still running"
sleep 1
done
echo "done"
There are OS-specific ways to perform blocking waits on the file system. Linux uses inotify (I forget the BSD equivalent). After installing inotify-tools, you can write code similar to
#!/bin/bash
echo "Start"
inotifywait -e create $FILE & wait_pid=$!
if [[ -f $FILE ]]; then
kill $wait_pid
else
wait $wait_pid
fi
echo "file exists, continuing"
The call to inotifywait does not exit until it receives notification from the operating system that $FILE has been created.
The reason for not simply calling inotifywait and letting it block is that there is a race condition: the file might not exist when you test for it, but it could be created before you can start watching for the creation event. To fix that, we start a background process that waits for the file to be created, then check if it exists. If it does, we can kill inotifywait and proceed. If it does not, inotifywait is already watching for it, so we are guaranteed to see it be created, so we simply wait on the process to complete.
To fedorqui: Is it so good? There is a problem?
#!/bin/bash
(
..
my code
..
) &
PID=$BASHPID or PID=$$
while [ ! ps -ef | grep $PID ]
do
sleep 0
done
Thank you

rc.d start does not terminate?

So I wrote the Arch Linux rc.d script for mongod daemon (following an example), but when I do:
sudo rc.d start mongod
it just gets stuck on:
:: Starting /usr/bin/mongod [BUSY]
and never transitions to "DONE" phase. Any tips?
Here is my script:
#!/bin/bash
# import predefined functions
. /etc/rc.conf
. /etc/rc.d/functions
# Point to the binary
DAEMON=/usr/bin/mongod
# Get the ARGS from the conf
. /etc/conf.d/crond
# Function to get the process id
PID=$(get_pid $DAEMON)
case "$1" in
start)
stat_busy "Starting $DAEMON"
# Check the PID exists - and if it does (returns 0) - do no run
[ -z "$PID" ] && $DAEMON $ARGS &> /dev/null
if [ $? = 0 ]; then
add_daemon $DAEMON
stat_done
else
stat_fail
exit 1
fi
;;
stop)
stat_busy "Stopping $DAEMON"
kill -HUP $PID &>/dev/null
rm_daemon $DAEMON
stat_done
;;
restart)
$0 stop
sleep 1
$0 start
;;
*)
echo "usage: $0 {start|stop|restart}"
esac
I've looked at how apache does it, but I can't figure out what they are doing that's different. Here's a piece of their httpd script:
case "$1" in
start)
stat_busy "Starting Apache Web Server"
[ ! -d /var/run/httpd ] && install -d /var/run/httpd
if $APACHECTL start >/dev/null ; then
add_daemon $daemon_name
stat_done
else
stat_fail
exit 1
fi
;;
For one thing, you are passing an $ARGS variable that is never actually defined. You will probably want to either pass some configuration options, or the location of a mongodb.conf file using the -f or --config option, to inform the daemon of the location of your database, log file, IP bindings, etc.
The mongod defaults assume that you database location is /data/db/. If this does not exist, or the daemon does not have permissions to that location, then the init script will fail.
You should probably also run the daemon with a user account other than yourself or root (the default pacman package creates a user named mongodb), and give this user read/write access to the data path and log file.
[ -z "$PID" ] && /bin/su mongodb -c "/usr/bin/mongod --config /etc/mongodb.conf --fork" > /dev/null
I would suggest referring to the mongodb init script provided in the Arch Community package, and comparing that to what you have here. Or, install MongoDB using pacman, which sets all of this up for you.
If all else fails, add some 'echo' commands inside of your if and else blocks to track down exactly where the init script is hanging, check mongodb's logs, and report back to us.

Problem with pidof in Bash script

I've written a script for me to start and stop my Perforce server. To shutdown the server I use the kill -SIGTERM command with the PID of the server daemon. It works as it should but there are some discrepancies in my script concerning the output behavior.
The script looks as follows:
#!/bin/sh -e
export P4JOURNAL=/var/log/perforce/journal
export P4LOG=/var/log/perforce/p4err
export P4ROOT=/var/local/perforce_depot
export P4PORT=1666
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
. /lib/lsb/init-functions
p4start="p4d -d"
p4stop="p4 admin stop"
p4user=perforce
case "$1" in
start)
log_action_begin_msg "Starting Perforce Server"
daemon -u $p4user -- $p4start;
echo "\n"
;;
stop)
echo "BLABLA"
echo "$(pidof /usr/local/bin/p4d)"
#daemon -u $p4user -- $p4stop;
p4dPid="$(pidof /usr/local/bin/p4d)"
echo $p4dPid
if [ -z "$(pidof /usr/local/bin/p4d)" ]; then
echo "ERROR: No Perforce Server running!"
else
echo "SUCCESS: Found Perforce Server running!\n\t"
echo "Shutting down Perforce Server..."
kill -15 $p4dPid;
fi
echo "\n"
;;
restart)
stop
start
;;
*)
echo "Usage: /etc/init.d/perforce (start|stop|restart)"
exit 1
;;
esac
exit 0
When p4d is running the stop block works as intended, but when there is no p4d running the script with stop only outputs BLABLA and an empty new line because of the echo "$(pidof /usr/local/bin/p4d)". The error message stating that no server is running is never printed. What am I doing wrong here?
PS: The part if [ -z "$(pidof /usr/local/bin/p4d)" ]; then has been changed from if [ -z "$p4dPid" ]; then for debug reasons.
EDIT: I narrowed down the problem. If I don't use the p4dPid variable and comment out the lines p4dPid="$(pidof /usr/local/bin/p4d)" and echo $p4dPid the if block is processed and the error messages is printed. Still I don't unterstand what is causing this behavior.
EDIT 2: Problem solved!
The -e in #!/bin/sh -e was causing the shell to exit the script after any statement returning a non-zero return value.
When your service is not running, the command
echo "$(pidof /usr/local/bin/p4d)"
is processed as
echo ""
because pidof did not return any string. So the command outputs an empty line.
If you do not want this empty line, then just remove this statement, after all you print an error message when the process is not running.
Problem solved!
The -e in #!/bin/sh -e was causing the shell to exit after any statement returning a non-zero return value.

How to check in a bash script if something is running and exit if it is

I have a script that runs every 15 minutes but sometimes if the box is busy it hangs and the next process will start before the first one is finished creating a snowball effect. How can I add a couple lines to the bash script to check to see if something is running first before starting?
You can use pidof -x if you know the process name, or kill -0 if you know the PID.
Example:
if pidof -x vim > /dev/null
then
echo "Vim already running"
exit 1
fi
Why don't set a lock file ?
Something like
yourapp.lock
Just remove it when you process is finished, and check for it before to launch it.
It could be done using
if [ -f yourapp.lock ]; then
echo "The process is already launched, please wait..."
fi
In lieu of pidfiles, as long as your script has a uniquely identifiable name you can do something like this:
#!/bin/bash
COMMAND=$0
# exit if I am already running
RUNNING=`ps --no-headers -C${COMMAND} | wc -l`
if [ ${RUNNING} -gt 1 ]; then
echo "Previous ${COMMAND} is still running."
exit 1
fi
... rest of script ...
pgrep -f yourscript >/dev/null && exit
This is how I do it in one of my cron jobs
lockfile=~/myproc.lock
minutes=60
if [ -f "$lockfile" ]
then
filestr=`find $lockfile -mmin +$minutes -print`
if [ "$filestr" = "" ]; then
echo "Lockfile is not older than $minutes minutes! Another $0 running. Exiting ..."
exit 1
else
echo "Lockfile is older than $minutes minutes, ignoring it!"
rm $lockfile
fi
fi
echo "Creating lockfile $lockfile"
touch $lockfile
and delete the lock file at the end of the script
echo "Removing lock $lockfile ..."
rm $lockfile
For a method that does not suffer from parsing bugs and race conditions, check out:
BashFAQ/045 - How can I ensure that only one instance of a script is running at a time (mutual exclusion)?
I had recently the same question and found from above that kill -0 is best for my case:
echo "Starting process..."
run-process > $OUTPUT &
pid=$!
echo "Process started pid=$pid"
while true; do
kill -0 $pid 2> /dev/null || { echo "Process exit detected"; break; }
sleep 1
done
echo "Done."
To expand on what #bgy says, the safe atomic way to create a lock file if it doesn't exist yet, and fail if it doesn't, is to create a temp file, then hard link it to the standard lock file. This protects against another process creating the file in between you testing for it and you creating it.
Here is the lock file code from my hourly backup script:
echo $$ > /tmp/lock.$$
if ! ln /tmp/lock.$$ /tmp/lock ; then
echo "previous backup in process"
rm /tmp/lock.$$
exit
fi
Don't forget to delete both the lock file and the temp file when you're done, even if you exit early through an error.
Use this script:
FILE="/tmp/my_file"
if [ -f "$FILE" ]; then
echo "Still running"
exit
fi
trap EXIT "rm -f $FILE"
touch $FILE
...script here...
This script will create a file and remove it on exit.

Resources