Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot

Make a perl script self aware...

by neuroball (Pilgrim)
on Aug 01, 2003 at 17:57 UTC ( #280064=perlquestion: print w/replies, xml ) Need Help??
neuroball has asked for the wisdom of the Perl Monks concerning the following question:

Fellow Perlmonks,

I ask for your advise on how to make a script aware of itself (running as another process).

What I would like to archive is to make a script die when it knows it is already running in another process.

I thought up the following solutions, each having pitfalls though.

Solution a) Script 1 gets its own pid and writes it into a file. Script 2 checks for the file and pings the saved pid to see if the process is still running. If the pid is still existing Script 2 will die. (Script 1 will delete the file upon an error-free exit)
Pitfalls in a) WINDOWS (one of the environments the script has to run on) reuses pids.

Solution b) Do the same as in a) but don't save the PID, just some random piece of data.
Pitfalls in b) I wouldn't know if Script 1 exited in error and forgot to clean up after itself. I.e. Script 2 would always think Script 1 is running.

Does some perlmonk have a better idea of check for existance of a script in memory that would work across platforms???

thanks /neuroball/

Replies are listed 'Best First'.
Re: Make a perl script self aware...
by demerphq (Chancellor) on Aug 01, 2003 at 18:56 UTC

    I think the canonical way to do this (in a script not a module) is

    use Fcntl ':flock'; # import LOCK_* constants INIT{ open *{0} or die "What!? $0:$!"; flock *{0}, LOCK_EX|LOCK_NB or die "$0 is already running somewhere!\n"; }

    It has one disadvantage that on at least one system and on one version of Perl (AS635 on Win2k) you won't see any error message or script generated message as Perl will fail (quietly!?) if you try to run the script while it is already running.


    <Elian> And I do take a kind of perverse pleasure in having an OO assembly language...
      This is by far te best way to go, it resolves issues where the system is powered down fast of the application is terminated without its own end block executing to clean up a pid file or lock file. The OS level lock will get cleared if the application is not running or on a reboot. One of the only reasons to use a PDI file (and there are better ways to do this if need be) is to have a way for other applications to actualy start, stop or send interupts etc to a running proccess.

Re: Make a perl script self aware...
by sgifford (Prior) on Aug 01, 2003 at 18:06 UTC

    The device you're looking for is called a mutex.

    In UNIX, you can use locks for this. Create a lock file, lock it with fcntl. If somebody's already holding the lock (your nonblocking request for a lock is denied), somebody else is already running; if it works, you're in the clear. If the process dies or exits, the OS will automatically remove the lock.

    Surely Windows has a lock that works like this, or some other form of mutex.

Re: Make a perl script self aware...
by snax (Friar) on Aug 01, 2003 at 18:06 UTC
    I suggest you read perlipc carefully -- there's a bunch of ways for processes to talk to one another. Make the script do *something* in order to listen, and make it's first job try to talk to where it "will be listening", and exit if it gets an "I'm already running" response. No such response means the new script should *start* listening, so that the next invocation can be told to bugger off :) There's a small race condition but I imagine that's hard to get around.
Re: Make a perl script self aware...
by skyknight (Hermit) on Aug 01, 2003 at 18:08 UTC

    It is a common idiom to make note of a pid, record it somewhere, and have a program read it in, and check that the program is still running. You're quite unlikely to run into the problem of PID reusage (actually, I'm pretty sure all OSes reuse PIDs; the top number is almost always some power of two), but if you wanted to add another level of safety, it's only a little more work. If you're working with the /proc file system in a unix like environment, I know that some file or other contains the command line that was used to invoke a process. That's how the ps command knows this. Dig around and find where this info lives, and then use it to verify that a given PID is in fact for your script.

Re: Make a perl script self aware...
by shemp (Deacon) on Aug 01, 2003 at 18:09 UTC
    Solution B, with a slight modification should work. As you pointed out, the only problem is with an error exit that doesnt clean up. To force cleanup, even on error, you could write your own die handler tat cleans up after itself.
    $SIG{__DIE__} = { # call the clean up function, then die }
    BTW: You don't actually need to store any data, just check for the files existence.

      To force cleanup, even on error, you could write your own die handler tat cleans up after itself.

      This is a step in the right direction, but unfortunately, this won't be invoked if your process is kill -9'd, or if the host is powered off abruptly, which is why the "check the PID to see if it's running" logic is still useful.

Re: Make a perl script self aware...
by Vorlin (Beadle) on Aug 02, 2003 at 20:24 UTC
    Since you're already looking at the idea of file existence checks, similar ideas can be offered as well as some new ones.

    1) File w/ pid written in at start of script1 - not a bad idea and while PIDs can be reused, the chance of said PID to be reused that soon is pretty unlikely (in my experience). A modification to this would be to ensure that the file is deleted upon exit or die of script1 and script2 checking the existence of the PID in script1's file in the proc table.

    2) Script1 can set a global variable along with item 1 - slightly more error-checking w/o going into IPC manipulation. Have script1 fire off a global variable like SCRIPT1_START=1 and have script2 check the file for the PID as well as whether SCRIPT1_START is 1 (running) or 0 (not running) or even 2 (error encountered, script1 died w/o cleanup, etc). This way, you can check said variable at the start of script2 and know what status script1 is in.

    While other ideas and concepts are much more cleaner, these are just simple Q&D's that might provide what you're looking for. This situation is very similar to the TeamSpeak server status/starts we're working with to ensure that cleanup happens and the next start is successful. Hope this helps and any constructive criticism regarding what I've posted is welcome.
Answer: Re: Make a perl script self aware...
by neuroball (Pilgrim) on Aug 04, 2003 at 22:45 UTC
    Fellow Monks,

    the following is the solution I choose the problem described above.

    I did use demerphq's code, removed the indirect filehandles and commented it for maintenance reasons.

    BTW: demerphq, is there a reason for the use of indirect filehandles? The script works without them, and it's a lot easier to maintain without them.

    As demerphq noted before... the DIE is mute on Windows most of the time.

    Final Code:

    # Needed for "Only allow one process of this script"-rule use Fcntl ':flock'; INIT { open LH, $0 or die "Can't open $0 for locking!\nError: $!\n"; # lock file so that it can only be accessed # by the current running script # DIE is mute (doesn't print to the screen) on Windows flock LH, LOCK_EX|LOCK_NB or die "$0 is already running somewhere!\n"; }
    Thanks for all your help, you are the monks that make this a great community.

      This is an interesting problem indeed.
      I have used this method of opening the script itself and placing an exclusive lock on it for quite sometime, without problems

      However, I have started working with Unix AIX recently and the solution no longer works :(

      I had never had any problems with several Linux distros, FreeBSD, Mac OS X or Windows.

      But sadly under AIX the script fails to get a lock on itself, weather it is running or not.

      I added a $! to the flock like so:
      flock LH, LOCK_EX|LOCK_NB or die "$0 is already running somewhere!\n$!";

      and the OS error message is:
      A file descriptor does not refer to an open file

      Interesting, the error message reads as if the file open failed,
      and yet it was fine as far as I can tell because the 'or die' for open did not get invoked... odd.

      Any suggestions?

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://280064]
Approved by Thelonius
Front-paged by broquaint
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (4)
As of 2017-02-26 06:04 GMT
Find Nodes?
    Voting Booth?
    Before electricity was invented, what was the Electric Eel called?

    Results (371 votes). Check out past polls.