How are PIDs modified with exec? - linux

According to the documentation, an exec does not modify the pid of a process.
I'm using a service to lauch my process, and aim to get his pid to save him in /var/run/.
For that i use $!.
My init script make a call to a .sh file that does an exec to an other .sh file. This file then make an exec call to java.
In the end, the pid the java app has is not the one I get in my init script. Why ?
Note : when i make only one sh script that does exec java, that works. But I don't see why adding one exec would change anything.
Code if it can help understanding.
Init script:
$DAEMON > /var/local/red5/log/jvm.stdout 2>&1 &
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
echo $! > $PIDFILE
fi
$DAEMON calls :
if [ -z "$RED5_HOME" ]; then export RED5_HOME=`pwd`; fi
ulimit -n 32767
# start Red5
exec /bin/bash $RED5_HOME/red5.sh > $RED5_HOME/log/jvm.stdout 2>&1 &
And my red5.sh calls java : (with a few exports before that)
# start Red5
exec "$JAVA" "$JYTHON" -Dred5.root="${RED5_HOME}" $JAVA_OPTS -cp "${RED5_CLASSPATH}" "$RED5_MAINCLASS" $RED5_OPTS
Works if i do :
if [ -z "$RED5_HOME" ]; then export RED5_HOME=`pwd`; fi
ulimit -n 32767
# start Red5
exec "$JAVA" "$JYTHON" -Dred5.root="${RED5_HOME}" $JAVA_OPTS -cp "${RED5_CLASSPATH}" "$RED5_MAINCLASS" $RED5_OPTS
Result :
pid via ps -ef: 15950.
pid with $! : 15947
Any idea ?
Thank you.

Your original $DAEMON is starting another new process when you call exec as a background process, so your Java program does run in a separate process than the background process started in the init script. Just run exec with
if [ -z "$RED5_HOME" ]; then export RED5_HOME=`pwd`; fi
ulimit -n 32767
# start Red5
# CHANGE: no ampersand at the end of this line
exec /bin/bash $RED5_HOME/red5.sh > $RED5_HOME/log/jvm.stdout 2>&1

You submit your job in the background when appending a & character, and thus it gets its own new pid.
EDIT: Thanks for the downvote. I meant this line:
exec /bin/bash $RED5_HOME/red5.sh > $RED5_HOME/log/jvm.stdout 2>&1 &

Related

Bash - use 1 stop script for multiple similar services, and kill the correct process only

I have multiple processes running as services on a machine
Before moving from 1 process/service to multiple ones, I used the following script to stop my service
#!/bin/sh
SIGNAL=${SIGNAL:-TERM}
PIDS=$(ps ax | grep -i 'datastream' | grep java | grep -v grep | awk '{print $1}')
if [ -z "$PIDS" ]; then
echo "No Brooklin server to stop"
exit 1
else
kill -s $SIGNAL $PIDS
fi
The issue now is that this script kills all processes of this type if invoked as a service stop command
My services are called for example service-A, service-B, service-C. If I send a service service-C stop command, the current script will stop all 3 processes.
I would like to make the script use the provided service name to determine which process to stop (I can grep A/B/C from the process output to ps, but I haven't managed to tell it how to stop only the process given in the service stop command.
Does anyone have experience handling something similar?
You can try something like below while starting your application which can store your PID in a static file and then you can use the same file to kill the process.
Pasting below one of my start - stop script which I have used in past for churning up multiple processes.
Start Script :-
#!/bin/bash
export PORT=$1
. /application/setEnv.sh
/java/jdk1.8.0_152/bin/java -Xms512m -Xmx2G -XX:+DisableExplicitGC -jar /application/api-1.0-0-all.jar </dev/null >>$LOGDIR/service$PORT.log 2>&1 &
echo $! > /application/service$PORT.pid
disown $!
Stop Script :-
#!/bin/bash
PORT=$1
PID=`cat /application/service$PORT.pid`
if [ ! -z "$PID" ]; then
rm /application/service$PORT.pid
kill -9 $PID >/dev/null 2>&1
if [ $? -gt 0 ]; then
echo "PID file found but no matching process was found. Stop aborted."
exit 1
fi
else
echo "PID file is empty and has been ignored."
fi
mv /application/logs/service$PORT.log /application/logs/service$PORT.log`date +%d%m%Y%H%M%S`
Only change which I can think of is the replace my port utilisation logic viz. $PORT with your service names viz. A/B/C.

Background rsync and pid from a shell script

I have a shell script that does a backup. I set this script in a cron but the problem is that the backup is heavy so it is possible to execute a second rsync before the first ends up.
I thought to launch rsync in a script and then get PID and write a file that script checks if the process exist or not (if this file exist or not).
If I put rsync in background I get the PID but I don't know how to know when rsync ends up but, if I set rsync (no background) I can't get PID before the process finish so I can't write a file whit PID.
I don't know what is the best way to "have rsync control" and know when it finish.
My script
#!/bin/bash
pidfile="/home/${USER}/.rsync_repository"
if [ -f $pidfile ];
then
echo "PID file exists " $(date +"%Y-%m-%d %H:%M:%S")
else
rsync -zrt --delete-before /repository/ /mnt/backup/repositorio/ < /dev/null &
echo $$ > $pidfile
# If I uncomment this 'rm' and rsync is running in background, the file is deleted so I can't "control" when rsync finish
# rm $pidfile
fi
Can anybody help me?!
Thanks in advance !! :)
# check to make sure script isn't still running
# if it's still running then exit this script
sScriptName="$(basename $0)"
if [ $(pidof -x ${sScriptName}| wc -w) -gt 2 ]; then
exit
fi
pidof finds the pid of a process
-x tells it to look for scripts too
${sScriptName} is just the name of the script...you can hardcode this
wc -w returns the word count by words
-gt 2 no more than one instance running (instance plus 1 for the pidof check)
if more than one instance running then exit script
Let me know if this works for you.
Test both for presence of pid file and status of the running process like this:
#!/bin/bash
pidfile="/home/${USER}/.rsync_repository"
is_running =0
if [ -f $pidfile ];
then
echo "PID file exists " $(date +"%Y-%m-%d %H:%M:%S")
previous_pid=`cat $pidfile`
is_running=`ps -ef | grep $previous_pid | wc -l`
fi
if [ $is_running -gt 0 ];
then
echo "Previous process didn't quit yet"
else
rsync -zrt --delete-before /repository/ /mnt/backup/repositorio/ < /dev/null &
echo $$ > $pidfile
fi
Hope this helps!!!

Shell scripts and how to avoid running the same script at the same time on a Linux machine

I have Linux centralize server – Linux 5.X.
In some cases on my Linux server the get_hosts.ksh script could be run from some other different hosts.
For example get_hosts.ksh could run on my Linux machine three or more times at the same time.
My question:
How to avoid running multiple instances of process/script?
A common solution for your problem on *nix systems is to check for a lock file existence.
Usually lock file contains current process PID.
This is an example ksh script:
#!/bin/ksh
pid="/var/run/get_hosts.pid"
trap "rm -f $pid" SIGSEGV
trap "rm -f $pid" SIGINT
if [ -e $pid ]; then
exit # pid file exists, another instance is running, so now we politely exit
else
echo $$ > $pid # pid file doesn't exit, create one and go on
fi
# your normal workflow here...
rm -f $pid # remove pid file just before exiting
exit
UPDATE: Answering to OP comment, I add handling program interruptions and segfaults with trap command.
The normal way of doing this is to write the process id into a file. The first thing the script does is check for the existence of the file, read the pid, check if a process with that pid exists, and for extra paranoia points, if that process actually runs the script. If yes, the script exits.
Here's a simple example. The process in question is a binary, and this script makes sure the binary runs only once. This is not exactly what you need, but you should be able to adapt this:
RUNNING=0
PIDFILE=$PATH_TO/var/run/example.pid
if [ -f $PIDFILE ]
then
PID=`cat $PIDFILE`
ps -eo pid | grep $PID >/dev/null 2>&1
if [ $? -eq 0 ]
then
RUNNING=1
fi
fi
if [ $RUNNING -ne 1 ]
then
run_binary
PID=$!
echo $PID > $PIDFILE
fi
This is not very elaborate but should get you on the right track.
You can use a pid file to keep track of when the process is running. At the top of the script, check for the existence of the pid file and if it doesn't exist, create it and run the script, otherwise return.
Some sample code can be seen in this answer to a similar question.
You might consider using the (optional) lockfile(1) command (provided by procmail package on Debian).
I have a lot of scripts, and using this below code for prevent multiple/simulate run:
PID="/var/scripts/PID.txt" # Temp file
if [ ! -f "$PID" ]; then
echo $$ > "$PID" # Print actual PID into a file
else
ps -p $(cat "$PID") > /dev/null && exit || echo $$ > "$PID"
fi
Building on wallenborn's answer I also added a "staleness" check just in case the PID lock file is beyond a certain expected age in seconds.
# prevent simultaneous executions within an hourish
pid_file="$HOME/.harness.pid"
max_stale_seconds=3600
if [ -f $pid_file ]; then
pid="$(cat "$pid_file")"
let age_in_seconds="$(date +%s) - $(date -r "$pid_file" +%s)"
if ps $pid >/dev/null && [ $age_in_seconds -lt $max_stale_seconds ]; then
exit 1
fi
fi
echo $$>"$pid_file"
trap "rm -f \"$pid_file\"" SIGSEGV
trap "rm -f \"$pid_file\"" SIGINT
This could be made "smarter" to kill off the other executions should the PID be valid but this would be dangerous. Consider a sudden power failure and reset situation where the PID file contains a number that may now reference a completely different process.

Call to daemon in a /etc/init.d script is blocking, not running in background

I have a Perl script that I want to daemonize. Basically this perl script will read a directory every 30 seconds, read the files that it finds and then process the data. To keep it simple here consider the following Perl script (called synpipe_server, there is a symbolic link of this script in /usr/sbin/) :
#!/usr/bin/perl
use strict;
use warnings;
my $continue = 1;
$SIG{'TERM'} = sub { $continue = 0; print "Caught TERM signal\n"; };
$SIG{'INT'} = sub { $continue = 0; print "Caught INT signal\n"; };
my $i = 0;
while ($continue) {
#do stuff
print "Hello, I am running " . ++$i . "\n";
sleep 3;
}
So this script basically prints something every 3 seconds.
Then, as I want to daemonize this script, I've also put this bash script (also called synpipe_server) in /etc/init.d/ :
#!/bin/bash
# synpipe_server : This starts and stops synpipe_server
#
# chkconfig: 12345 12 88
# description: Monitors all production pipelines
# processname: synpipe_server
# pidfile: /var/run/synpipe_server.pid
# Source function library.
. /etc/rc.d/init.d/functions
pname="synpipe_server"
exe="/usr/sbin/synpipe_server"
pidfile="/var/run/${pname}.pid"
lockfile="/var/lock/subsys/${pname}"
[ -x $exe ] || exit 0
RETVAL=0
start() {
echo -n "Starting $pname : "
daemon ${exe}
RETVAL=$?
PID=$!
echo
[ $RETVAL -eq 0 ] && touch ${lockfile}
echo $PID > ${pidfile}
}
stop() {
echo -n "Shutting down $pname : "
killproc ${exe}
RETVAL=$?
echo
if [ $RETVAL -eq 0 ]; then
rm -f ${lockfile}
rm -f ${pidfile}
fi
}
restart() {
echo -n "Restarting $pname : "
stop
sleep 2
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status ${pname}
;;
restart)
restart
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
;; esac
exit 0
So, (if I have well understood the doc for daemon) the Perl script should run in the background and the output should be redirected to /dev/null if I execute :
service synpipe_server start
But here is what I get instead :
[root#master init.d]# service synpipe_server start
Starting synpipe_server : Hello, I am running 1
Hello, I am running 2
Hello, I am running 3
Hello, I am running 4
Caught INT signal
[ OK ]
[root#master init.d]#
So it starts the Perl script but runs it without detaching it from the current terminal session, and I can see the output printed in my console ... which is not really what I was expecting. Moreover, the PID file is empty (or with a line feed only, no pid returned by daemon).
Does anyone have any idea of what I am doing wrong ?
EDIT : maybe I should say that I am on a Red Hat machine.
Scientific Linux SL release 5.4 (Boron)
Thanks,
Tony
I finally re-wrote the start function in the bash init script, and I am not using daemon anymore.
start() {
echo -n "Starting $pname : "
#daemon ${exe} # Not working ...
if [ -s ${pidfile} ]; then
RETVAL=1
echo -n "Already running !" && warning
echo
else
nohup ${exe} >/dev/null 2>&1 &
RETVAL=$?
PID=$!
[ $RETVAL -eq 0 ] && touch ${lockfile} && success || failure
echo
echo $PID > ${pidfile}
fi
}
I check that the pid file is not existing already (if so, just write a warning). If not, I use
nohup ${exe} >/dev/null 2>&1 &
to start the script.
I don't know if it is safe this way (?) but it works.
The proper way to daemonize a process is have it detach from the terminal by itself. This is how most larger software suites do it, for instance, apache.
The rationale behind daemon not doing what you would expect from its name, and how to make a unix process detach into the background, can be found here in section 1.7 How do I get my program to act like a daemon?
Simply invoking a program in the background isn't really adequate for
these long-running programs; that does not correctly detach the
process from the terminal session that started it. Also, the
conventional way of starting daemons is simply to issue the command
manually or from an rc script; the daemon is expected to put itself
into the background.
For further reading on this topic: What's the difference between nohup and a daemon?
According to man daemon correct syntax is
daemon [options] -- [command] [command args]
Your init script startup should run something like:
daemon --pidfile ${pidfile} -- ${exe}
As said here, it seems that the process needs to be sent to the background using &.
Daemon don’t do it for you.

How to tell the status of a Linux Daemon

We have a Linux Daemon in c and a bash script to start it. The daemon sometimes fail to start because of some configuration file errors but the script reports the daemon was started successfully. A snippet of the script is shown as below, could someone tell me what's wrong with the script?
...
case "$1" in
start)
echo -n "Starting Demo Daemon: "
sudo -u demouser env DEMO_HOME=$DEMO_HOME /usr/local/demouser/bin/democtl startup > /dev/null 2> /dev/null
if [ "$?" = "0" ]; then
echo_success
else
echo_failure
fi
echo
;;
...
Thanks!
I feel there is nothing wrong with the script,it is the reponsibility of daemon to return non zero exit status if failed to start properly and based on those the script will display the message.(which i think it does)
You can add following line in your script to get running status of your Linux Daemon
status=`ps -aef |grep "\/usr\/local\/demouser\/bin\/democtl" |grep -v grep|wc -l`
if [ "$status" = "1" ]; then
echo_success
else
echo_failure
fi

Resources