bjossi has asked for the wisdom of the Perl Monks concerning the following question:

Well - I was so sure this has already been done million times over but found no leads that it has been posted yet. I want my script to run in background, tail a logfile record changes to a SQL host and die() if it cannot connect to a SQL host. That is the easy part and dealt with. But - I also want to start it by cron say every minute in case it is not running. More than one copy should not be running. So at the start, it should check if another copy is already running, if so, do a die immidiately otherwise continue.
  • Comment on How do I determine if my script is already running?

Replies are listed 'Best First'.
Re: How do I determine if my script is already running?
by KM (Priest) on Feb 19, 2001 at 04:44 UTC
    You could always flock() a lockfile, and if the script can not obtain a lock, you know another instance of the script is running.

    I generally do this. As well, when starting a file from cron I like to run a shell script from the cron (which will call the Perl script) which greps a ps to see if the script is running (and in some cases if it is being edited in vi). Not sure if others do something similar, but I got into the habbit some years back when we had a policy of doing this (at a past job).

    Cheers,
    KM

Re: How do I determine if my script is already running?
by japhy (Canon) on Feb 19, 2001 at 06:44 UTC
    Here's a trick I thought up. Here's what your program should look like:
    #!/usr/bin/perl open MY_PID, "> /tmp/do_stuff.pid" or die "can't write to /tmp/do_stuff.pid: $!"; print MY_PID $$; close MY_PID; # rest of code
    And here's what the cron should execute:
    #!/usr/bin/perl open PID, "/tmp/do_stuff.pid" or spawn(); chomp(my $pid = <PID>); close PID; kill -HUP => $pid or spawn(); sub spawn { fork and exit; exec "/usr/bin/perl", "my_prog.pl"; }
    You know, you could make it even simpler. Just have the program create a file, and make sure the file gets deleted in an END { ... } block, so that, if the program stops, the file goes away. That's not 100% safe though, since the file could be deleted by something else. So nevermind, don't do that. Do the previous stuff instead. If you want to.

    japhy -- Perl and Regex Hacker
Re: How do I determine if my script is already running?
by goldclaw (Scribe) on Feb 19, 2001 at 04:46 UTC
    Several options available:
    • use some sort of /proc interface (if you have that on your system), and search for your script. Set the name of the script by assigning to $0;
    • Parse the output of ps -ef your self
    • use a run file. On startup, create a file with sysopen and the O_EXCL bit set. If that fails, it means it exists, and you can assume your script is alive and kicking. If it succeds, do your stuff, and just make sure that you have an END block that closes the file, and deletes it. Also be sure to catch any signal that will kill your script, and close/delete the file on those events as well.

    GoldClaw UPDATE:Of cource flock'ing is better. However, make sure to catch any signals that will kill perl, and do a normal exit. If not, I belive perl will die without releasing the lock.

      Perl's signal handling is unreliable. I do not believe that you need to catch signals or do any cleanup. My understanding is that the OS is responsible for flock. If your process is terminated unexpectedly, the OS is responsible for noting that your filehandles are now closed and your locks are ended.

      If anyone has any documentation to the contrary, please show me. Also a failure script would be of interest. I am currently relying on this behaviour in production and if it is unreliable, I would really like to know. In the meantime you can take the low-tech approach and run the following script from multiple xterms while trying to create a problem...

      use Fcntl qw(:flock); my $file = "my.lock"; open(FOO, ">> $file") or die "Cannot write to $file: $!"; print "Getting the lock\n"; flock (FOO, LOCK_EX) or die "Cannot flock $file: $!"; print "Locked by $$, hit enter to exit: "; <STDIN>;
      At least on Linux I am unable to get this to act in anything other than the expected manner...
        If your process is terminated unexpectedly, the OS is responsible for noting that your filehandles are now closed and your locks are ended.

        FWIW, Stevens agress with you in Advanced Programming In The Unix Environment.

        Of course, YMMV.

        Peace,
        -McD

Re: How do I determine if my script is already running?
by bjossi (Initiate) on Feb 19, 2001 at 06:35 UTC
    Well - it wasn't too hard - here is one way to do it:
    # Make sure there is not another copy of this script running my $lockfile = '/var/run/proggie_check'; open (LOCK, ">$lockfile") || die; # No rights? - die flock(LOCK,2|4) || die; # Lock it, no waitin' or die. # --- We are the only copy around ---
(tye)Re: How do I determine if my script is already running?
by tye (Sage) on Feb 19, 2001 at 21:55 UTC

    I'm surprised noone mentioned my favorite:

    #!/usr/bin/perl -w use strict; use Fcntl qw( LOCK_EX LOCK_NB ); flock(DATA,LOCK_EX|LOCK_NB) or die "This script ($0) is already running.\n"; #... __END__

    I recently discovered that this works but isn't as pretty under Win32 since flock() is mandatory (with LOCK_EX meaning that no other handle can be open to that file, even within the same process and LOCK_SH meaning that no other handle can be open for writing to that file). So under Win32 the script just refuses to run. My current version of Perl doesn't even report an error message. O-:

            - tye (but my friends call me "Tye")
Re: How do I determine if my script is already running?
by life_monger (Hermit) on Feb 19, 2001 at 22:49 UTC

    When you say cron, I assume you are running on UNIX. If you want to keep this process running, you may want to look into using init(8). You can add an entry to /etc/inittab that looks something like:

    u1:1235:respawn:/path/to/script.pl

    This will cause the process to restart automagically should it not be running. You'll have to define the proper behavior for the case where the database is unreachable. Using 'respawn' will start up a new process if the original dies.

    Hope that helps...