0

How to run exactly 1 instance of a program as a process? Or alternatively, how to test for the existance of a running program?

Portable methods are preferred.

4
  • No duplication with existing question(s) intended, just trying to share some useful knowledge. Some tags couldn't be added due to lack of reputation points.
    – DannyNiu
    Mar 1, 2020 at 9:46
  • What do you want to accomplish? This smells awfully like an XY question
    – vonbrand
    Mar 1, 2020 at 10:20
  • It's a problem I encountered in web back-end development. I could configure the system for a custom service, but I wanted a portable solution to invoke the service directly in back-end scripts, so that it shares pgid with the HTTP server.
    – DannyNiu
    Mar 1, 2020 at 10:23
  • @vonbrand It's not an XY-question. In C you can do much easily with flock(2) on some file (eg. the executable itself). In GNU/Linux you can use the flock(1) utility. If you know any portable way to do that from the shell, put up.
    – user313992
    Mar 1, 2020 at 10:44

3 Answers 3

0

One can test for writability of a (named) pipe, and with appropriate programming, the existance of an instance of a running program.

Suppose we reserve a filename /tmp/fifo-myapp as the name of a FIFO file (it should be customized for each different application; it usually resides some application-specific folder and is specified as an absolute path):

File1: test-wr-fifo

#!/bin/sh
# invocation: test-wr-fifo <timeout>

timeout="${1:=3}"

if ! [ -p "/tmp/fifo-myapp" ] ; then
    rm -f "/tmp/fifo-myapp"
    mkfifo "/tmp/fifo-myapp"
fi

{ sleep "$timeout" ; kill -s 0 $$ && kill -s TERM $$ ; } &
exec 3>"/tmp/fifo-myapp"

This program will exit with non-successful status when the fifo isn't open and specified time out had reached.

File2: single-inst-prog

#!/bin/sh

exec 3<"/tmp/fifo-myapp"

### Application Code ###

This is the program that we only want 1 instance for.

To invoke single-inst-prog:

./test-wr-fifo || ./single-inst-prog &

This method works because opening of one end of a FIFO would block (when O_NONBLOCK is clear) until the other end is also open; this method also work because we're not reading any data from or writing any to the FIFO, therefore EOF condition and SIGPIPE is never signaled.

5
  • What happens when your test-wr-fifo is run from different directories? or from an unwritable directory?
    – user313992
    Mar 1, 2020 at 10:03
  • @mosvy It's an example placeholder name. In reality it'll be an absolute path specified in environment variable or command-line argument. It's customized for each specific application.
    – DannyNiu
    Mar 1, 2020 at 10:12
  • Post a working example that could be evaluated, not pseudocode.
    – user313992
    Mar 1, 2020 at 10:14
  • @mosvy Done (a bit), anything else you see can be improved?
    – DannyNiu
    Mar 1, 2020 at 10:18
  • 2
    I would rather use mktemp or create the fifo in some place accessible only to the user. Any other user may create /tmp/fifo-myapp as a symlink to some precious file of yours, causing the exec 3>/tmp/fifo-myapp to truncate it.
    – user313992
    Mar 1, 2020 at 10:42
0

I am always wary of race conditions with lock files, so I use a specific named file, and use mv because it is atomic.

Typically, the process uses a permanent filename like /tmp/locks/myProcess.lock. Any process desiring to acquire the lock attempts to change the name to /tmp/locks/myProcess.lock.myPID (using mv(1) for a shell and rename(2) for C). It can then check that its private name now exists. If not, it loops/sleep as appropriate.

With some care, it is possible for the process to append a log to the lock file: when it first attempted to lock, when it succeeded, when it released.

Obviously, it has to remember time of first attempt, as it can only log this after it succeeds. It can also watchdog for excessive delays and raise an alert for investigation (presumably another process copy failed, and didn't have an onExit to free the lock).

2
  • As you've indicated, it's not foolproof if a process fails to release the lock by recreating /tmp/locks/myPorcess.lock. In my version however, when the application process terminates, its end of the FIFO is closed, so that test-wr-fifo will indicate that condition in the OR list.
    – DannyNiu
    Mar 2, 2020 at 0:59
  • @DannyNiu. Yours is a more resilient solution, but I need to work through it to grok it. In my case, I was using the method to control three remote copies of a system monitoring tool, each using ssh to manage locking so only one of them would be active. If the WAN became isolated, they would proceed autonomously. I suspect sockets would then have been needed instead of pipes, and that involves complications with timeouts. Mar 2, 2020 at 10:13
0

Here is a example with a xmessage. Not sure it's OK for you but you can get a try.

#!/bin/sh
xmessage 'hello this is the first xmessage' &
last=$(ps -a | grep '[ ]xmessage$' | awk '{print $1}')
sleep 1
if test $(($(date +%s) % 2)) -eq 0
  then
    kill $last
fi
sleep 1
if ! ps -p $last >/dev/null
  then
    xmessage 'Hi, this is the second xmessage. Visible only if the first is close!' &
fi
2
  • What is this "xmessage" program? Can you annotate your sample with some comments so that we can follow what it's doing?
    – DannyNiu
    Mar 2, 2020 at 1:02
  • man xmessage is a good choice !
    – ctac_
    Mar 7, 2020 at 21:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .