Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Determining if self is already running under WinNT/2000

by lrep (Novice)
on Feb 06, 2003 at 22:18 UTC ( [id://233299]=perlquestion: print w/replies, xml ) Need Help??

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

I would like to put in a check at the beginning of my program to see if there is already an instance of itself already running. The examples code I have seen for getting a process list will only return the path to perl.exe, not the name of the program itself. (I.E. C:/PATH/myprogram.pl ) I would like a method that would work by using only the modules that come with the ActivePerl 5.6 distribution. So Proc::ProcessTable is no good. Thanks.
  • Comment on Determining if self is already running under WinNT/2000

Replies are listed 'Best First'.
Re: Determining if self is already running under WinNT/2000
by jsprat (Curate) on Feb 06, 2003 at 23:03 UTC
    Here's a simple example using Win32::Mutex, a standard module (mutex is short for mutual exclusivity). Save it as mutex.pl and try to run it twice or more. The second instance will die.

    #!/usr/bin/perl use strict; use warnings; use Win32::Mutex; my $mutex = Win32::Mutex->new(1, 'mutex.pl'); my $return = $mutex->wait(0); die "already running" unless $return; while (1){ exit if <> }

    theorbtwo is right that it's not portable - but it is the "Windows®" way of doing this.

      The Win32::Mutex does work on my release of perl. I will have to readup on what this module does, Thanks!!! I am still interested in gaining access to the command line arguments of perl. The information has to be in there somewhere because the NT Resource Kit utilities tlist.exe lists it... C:>tlist.exe 2084 CMD.EXE Command Prompt c:\bin\perl.exe c:\src\prg.pl
Re: Determining if self is already running under WinNT/2000
by grantm (Parson) on Feb 06, 2003 at 23:01 UTC

    I simply use flock with LOCK_NB - if another process has the lock then exit.

Re: Determining if self is already running under WinNT/2000
by BrowserUk (Patriarch) on Feb 06, 2003 at 23:03 UTC

    Probably your easiest way would be to create a sentinal that subsequent copies to check for. This could be as simple as an empty file or directory.

    When the script runs, it checks for its existance, if its found, terminates with a warning. If not, it creates it and then deletes it when if finishes. The problem with this is that if the program terminates abnormally, it can leave the file/directory in situ and refuse to run again until it has been manually deleted. You might also use a registry entry for the same purpose but with the same potential problem. Removing the sentinel in an END{} block might help the cleanup problem, but I don't think it's guarenteed to run under all circumstances.

    A better solution is to use something that automatically idsappears when the program terminates. Eg. Open a tcp or udp port and hold it open. This will disappear when the program terminates abnormaly. However, in some environments, you may fall foul of firewall restrictions.

    Saving (what I think is) the best until last, you can create a named pipe Either locally, or at your domain server. This is a simple process to create and check for using Win32::Pipe which I believe came as standard with AS 5.6.1.

    By creating this locally, you can prevent a 2nd copy running locally. By creating it at the domain controller, you could ensure only 1 copy in the domain.


    Examine what is said, not who speaks.

    The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Re: Determining if self is already running under WinNT/2000
by Mr_Person (Hermit) on Feb 06, 2003 at 22:40 UTC
    You could have the program write its PID out ot a file on startup (using the $$ variable) and then use Proc::ProcessTable to check for the existance of a perl.exe running as that PID. This is how most init scripts on *nix platforms check to see if the program is already running.
Re: Determining if self is already running under WinNT/2000
by Coruscate (Sexton) on Feb 06, 2003 at 22:54 UTC

    On windows, I don't believe it is possible to trace back to the path of the script, and for a very obvious reason: Perl scripts on a win32 machine are executed via a file association: the windows registry contains entries which say that a .pl extension (for example), is to be run by Perl (located at 'c:\perl\bin\perl.exe' for example). So it's not your actual script that is being executed, it is Perl being executed, with your script name passed as an argument to perl.exe. To prove that this is true, you can remove the shebang line from scripts on a windows machine and it will still run (with the exception of CGI scripts, though I'm sure this is an Apache issue, not Perl's).

    I know that *nix machines run a program under the script name rather than perl with the script name passed as an argument. I'm going to assume that this is because you are really executing the script as a program, with the shebang line pointing to the interpreter.

    So, on Win32, it is not possible to get the script name (perhaps a Win32::xxxx module provides this functionality?). I would much rather avoid OS-specific modules however, and describe a cross-platform method of doing so. Use a status file. :) When your program starts up, you check for the existence of this file. If the file exists, you die with a friendly message. If it doesn't exist, you create the file. Do your work, then delete the file to allow another instance of it to start. Simple. You should probably extend the code to check the last modified date/time of the file (check out stat) and use it to see if that date is older than however long your script takes to run. Do that right, and you won't be stuck if your script is forced to exit before it deletes the file. (ie: someone ctrl+z's the script: the status file will continue to exist, though the script is no longer running. Another instance will never start until you manually delete the file). The example (low-level-code-that-needs-some-add-ons) code:

    #!/usr/bin/perl -w use strict; my $stat_file = '/path/to/status.txt'; die "Another instance of this program is running!\n" if (-e $stat_file); open F, '>', $stat_file; close F; # Do all your hard work here # Nice and boring stuff here... yippee! unlink $stat_file;


    If the above content is missing any vital points or you feel that any of the information is misleading, incorrect or irrelevant, please feel free to downvote the post. At the same time, reply to this node or /msg me to tell me what is wrong with the post, so that I may update the node to the best of my ability. If you do not inform me as to why the post deserved a downvote, your vote does not have any significance and will be disregarded.

      If you are going to suggest lock files please do it right using sysopen to aviod race conitions as shown at Re: CGI Question - run external script and then update page and below. Typically you would print the PID to the lockfile:

      use Fcntl qw(O_WRONLY O_CREAT O_EXCL); my $lockfile = '/tmp/lock'; if (sysopen(FH, $lockfile, O_WRONLY | O_CREAT | O_EXCL)) { # sysopen will only work if lockfile does not exist # do stuff close FH; unlink $lockfile or die "Can't unlink lockfile $lockfile $!"; } else { die "$lockfile exists so another instance of the script is running +!"; exit; }

      cheers

      tachyon

      s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      Under ActivePerl on Windows, the $0 variable contains the name of the script. Launching through a a file association makes no difference. ActivePerl puts the script name into the $0 variable instead of the path to the Perl interpreter. The path to the interpreter is in $^X. Under Unix, Perl behaves the same way if you run the interpreter directly: 'perl script.pl'.
Re: Determining if self is already running under WinNT/2000
by fsn (Friar) on Feb 06, 2003 at 22:35 UTC
    Does Win32 have Process IDs like in Unix? If it does, may I humbly suggest using a PID file?

    If it doesn't, what does 'print "$$\n"' give you on a Win32 system?

      Win32 has PIDs as usual, though they tend to be more annoying numbers, since they're really pointers into a kernel memory structure. IOW, you can use all the standard methods for dealing with this.

      You can also use a Mutex kernel-object, which is the standard win32 way of dealing with it. See Win32::Mutex. This has the advantage of not creating files, and not having problems with the lockfiles of dead processes staying around. OTOH, it doesn't have the transparency that lockfiles/pidfiles do, and it's inherently nonportable.


      Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

      Yes, That will print the PID. But Let me put it another way. I am trying to put in a test in the program to stop the user from starting-up more than one instance of the program at a time. So on startup if I count more than one instance running, I can abort out. With a nice message to the user of course. I am trying to resist the idea of pipeing out to an external program like tlist.exe or some favor of ps.exe to get the process list.
Re: Determining if self is already running under WinNT/2000
by John M. Dlugosz (Monsignor) on Feb 08, 2003 at 05:40 UTC
    The most robust way to do that is with a Kernel object of some kind. For example, use a unique GUID for the name, and try creating a Mutex. The return value will tell you whether it was created, or if it opened one by that name that already existed. The latter means that another copy of your program beat you to it, and it is nicely atomic. The only race condition is that when the program quits there may be a small time when another process will see the mutex still there, before it is deleted. For user-launched applications, that is negligable.

    You can use any kind of Kernal object that has a name and that is reference counted by processes rather than having a persistant existance. You can use something suitable for communicating back to the original to tell it to jump to the foreground, or open a new document, or whatnot; or just have all instances coordinate with each other.

    —John

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://233299]
Approved by pfaut
Front-paged by data64
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-04-19 07:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found