Click to See Complete Forum and Search --> : How to detect an app already running?
What could be the bash script construct to detect a particular program being
already running on this machine?
For example, I'd like to start a client daemon in an init script if other script
has not already started it.
gehidore
12-21-2004, 09:34 PM
Originally posted by hiwa
What could be the bash script construct to detect a particular program being
already running on this machine?
For example, I'd like to start a client daemon in an init script if other script
has not already started it.
as root:
pgrep progname
If it is running it will return a PID #
goon12
12-22-2004, 10:48 AM
I think you could do something like this:
ps -ef | grep someapp
foo=$?
if [ $foo -eq 0 ]
then
echo "someapp is running."
fi
-goon12
apeekaboo
12-23-2004, 05:40 AM
If there was just at matter of starting some program that isn't running, Id use:
pgrep progname || /usr/bin/progname
Explanation:
The || is treated as 'or' and is executed when the previous command exits with an errorlevel > 0.
You could also use && for 'and' depending on what you want to accomplish, and in this case the later part is executed only if the previous part exits with errorlevel = 0.
Hope this made any sense... :)
I'd use an if-then(-else) statement only if there were an 'else' involved, like:
if pgrep progname; then
do something...
else
do something else...
fi
Thanks.
pgrep progname || /usr/bin/progname
Feel like apeekaboo's solution might be the best.
Does that mean, in natural language, if progname is already running don't execute progname?
Is my understanding right?
apeekaboo
12-23-2004, 06:29 AM
Yes, that is correct.
I think this approach makes the code easier to read, at least for this particular task.
Just bear this in mind; pgrep doesn't check a program for it's path.
This means that another script or program with the same name could already be running and thus pgrep will trigger on this instead.
And what about if two instances are running? Pgrep will evaluate to true (0) if one or more instances of a program or script with that name is already running.
This may or may not be a problem for you.
If you are the only user and know that no other binary is named the same on your system, then this shouldn't be a problem.
In a multiuser environment another approach could perhaps be better.
bwkaz
12-25-2004, 11:38 PM
But what if the user starts two instances of the program at the same time? The pgrep could return false in both scripts, which means you'd end up with 2 instances of the program running.
What you need to do is atomically check for the other program and bail if it's already running. The easiest way to do this is to modify the program to create a lock file somewhere (/var/lock is the usual place to put the files, and they usually contain the PID of the running instance of the program). The lock file is created by the following C code:
int lockfile = open("/var/lock/myprogramname.pid", O_CREAT | O_EXCL);
if(lockfile < 0 && errno == EEXIST) { // the lockfile existed
fprintf(stderr, "Lock file already exists. Exiting.\n");
exit(1);
}
else if(lockfile < 0) { // some other error
perror("Opening lockfile");
exit(2);
}
FILE *lock2 = fdopen(lockfile, "rw");
fprintf(lock2, "%d\n", getpid());
fclose(lock2); Make sure to unlink("/var/lock/myprogramname.pid"); when the program exits. You'll also need a bunch of includes -- check the manpages for open, fdopen, errno, perror, fprintf, fclose, and unlink to get them all.
It would be fairly easy to retry opening the file with fopen("whatever", "r") in the first if branch (where it already exists), then reading the PID out with scanf, then trying to kill(thatpid, 0); it (sending signal 0 doesn't actually do anything to the process other than make sure the PID is valid). If the kill fails (returns something <0), then you can delete the lockfile and try again -- the previous instance died unexpectedly and couldn't clean up its lockfile. If the kill does not fail, then you'd have to exit, because the previous instance is probably running.
apeekaboo
12-26-2004, 05:38 PM
I'm not quite sure what you mean... Pgrep will always return true if it matches an instance of an already running program.
Also, an existing lockfile is no guarantee that a program or daemon is still running as they can sometimes be left behind when a program crash.
bwkaz
12-26-2004, 07:21 PM
Originally posted by apeekaboo
I'm not quite sure what you mean... Pgrep will always return true if it matches an instance of an already running program. But another instance may not be running yet. It's a classic race condition.
Imagine two instances of your script get started. Ignoring SMP systems for the moment, we'll say that they get timesliced in turns on one CPU (SMP does complicate this a bit, but you could get the same end result, so it doesn't matter much).
The first script gets to the spot where the pgrep command is, and the shell executes pgrep. That instance of pgrep iterates through all the running processes, but doesn't get to the point where it exits before it gets timesliced out.
At that point, say the second script gets to run. It gets to the same point before it's timesliced out (pgrep has iterated through all running processes, but hasn't exited yet).
Now, both pgrep processes are just about ready to exit with a 1 status (no processes matched). Both scripts will then start the program, and you'll have 2 instances running.
The O_CREAT | O_EXCL flags guarantee that no matter what, the file creation will be atomic (nothing can interrupt it). If 2 processes call open with those flags, one is guaranteed to fail and the other is guaranteed to succeed (assuming the file really doesn't exist -- if it does, both will fail). The race window is closed.
Also, an existing lockfile is no guarantee that a program or daemon is still running as they can sometimes be left behind when a program crash. Yes, that's why I decided to write the PID into the lock file, and that's why I put the entire last paragraph into my previous reply -- because if the kill-with-0 fails, the process isn't running anymore. ;)
apeekaboo
12-27-2004, 06:15 AM
I'm not saying you're wrong... and I wouldn't know since I'm not a C programmer.. ;)
But all this seems to be out of league for the original question.
I wouldn't consider writing a C program when all I want is an init script.
bwkaz
12-27-2004, 10:21 AM
Uhh... oh. I totally missed the word "init" in the original post. Maybe it's not such a big deal then. ;)
(Though it could be, if you ever move to a parallel bootscript system, like what one of the experimental LFS bootscript SVN branches is doing. It runs all scripts after the first few in parallel, for a much faster boot time because the scripts are mostly sleeping, so why not parallelize that wait time. With normal serial bootscripts, only one script is running at a time, so you can do the pgrep check without any issues. But if multiple scripts are running, another one might be bringing the program up, so you'd need to close the race window still. I don't think many distros do parallel bootscripts, though, so it's probably not a big deal.)
> Constantly campaigning against "using namespace std;" in C++...
Sorry for off-topicking but could you give some explanation?
What could be the sin of 'using' directive?
gehidore
12-27-2004, 06:53 PM
Originally posted by hiwa
> Constantly campaigning against "using namespace std;" in C++...
Sorry for off-topicking but could you give some explanation?
What could be the sin of 'using' directive?
Not sure but I think if you look at it as a non programmer you *might* see the joke there...
bwkaz
12-28-2004, 12:44 AM
No, no, it's a programmer thing. ;)
It's not a "sin" to use "using". It is, however, a "sin" to use "using namespace std;".
See http://www.justlinux.com/forum/showthread.php?s=&postid=675674#post675674 and http://www.justlinux.com/forum/showthread.php?s=&postid=706979#post706979
gehidore
12-28-2004, 01:08 AM
Originally posted by bwkaz
No, no, it's a programmer thing. ;)
Heh, all the "non-programmers" I know who have read your sig over my shoulder always say
"HA HA, what a funny joke, computers cant get 'stds' "
:rolleyes: :rolleyes:
A few days ago, a beginner Java programmer wrote a inner class named Hashtable in a
top-level class which does import java.util.* package. He caught trouble as you might
expect. I simply said to him that you shouldn't use a name which was already used
in your imported package.
A fully qualified name for his Hashtable might be another solution but I generally don't
recommend it because of its awkwardness.
Could the problem of using namespace std in C++ be considered analogous to that
of the Java issue? I might say if you are using a full namespace, be careful for every
other names you use.
bwkaz
12-28-2004, 10:41 AM
Yeah, it's pretty close to the same problem. But just "being careful" won't always save you -- it's really quite difficult to tell what's all defined in the std namespace, and it's easy to redefine some symbol that you pulled in.
In Java, I'm pretty sure that you can also do an:
import java.util.ArrayList;
import java.util.Vector;
etc., etc., for each class in java.util that you're going to use. The same tradeoffs apply: your program is slightly longer, but you don't accidentally pull in classes that you won't use (HashMap, Hashtable, Stack, TimeZone, etc.).
Of course, the namespaces in Java are split up a lot more too (java.util vs. java.io vs. java.math vs. java.text), whereas in C++ it's all in the std namespace. So maybe it's not exactly the same tradeoff: In C++, you have a lot higher chance of pulling in extra symbols that you don't know about, because (for example) all the STL collections are in the same namespace as the I/O symbols (cout, etc.).