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

Honorable monks,

I am seeking a solution to the following problem:

I have a script that runs in "demon mode" on a server, and does something every 10 seconds or so. More specifically, it looks for new files, and if it has found some, it does something else, and then copies files from folder A to folder B.

The script makes use of GetOpt::Long.

The script is not very bulletproof, though.

Occasionally I want to perform a one-off task, i.e. calling the same script with some other options, e.g. to move files from C to D.

If the script I am using for the one-off task is identical with the instance of the running script, it should immediately exit without doing any further work. If the options are different, it should perform its task. If the one-off task is already running, another attempt to run a second instance should fail.

How can I detect the command line options the permanently-running script has been called with? Is there a best way to do this? Has this problem been solved before (maybe with a special-purpose variant of GetOpt::Long or some obscure feature of GetOpt::Long) Just grab the %opts hash and compare entries?

  • Comment on Detect options of script runnning in a demon mode

Replies are listed 'Best First'.
Re: Detect options of script runnning in a demon mode
by tirwhan (Abbot) on Feb 12, 2008 at 11:04 UTC

    Normally I'd just have the daemon print the command line it is called with to a specific file and have the script read that file.

    If for some reason that doesn't work and you happen to be on Linux, you can get the exact command line a process was called with by examining the /proc/<process id>/cmdline file. So, for example, the below code will print out the command line:

    #!/usr/bin/perl use warnings; use strict; open (my $fh,"<","/proc/$$/cmdline") or die "Can't access proc file: $ +!"; my $line = (<$fh>); close $fh or die "Can't close proc file: $!"; my @cmdline = split("\0",$line); print join(" ",@cmdline)."\n";

    All dogma is stupid.
Re: Detect options of script runnning in a demon mode
by andreas1234567 (Vicar) on Feb 12, 2008 at 11:33 UTC
    I'd log it to a file and be sure to include pid. Then a simple file tail or grep on the pid would reveal the arguments:
    use strict; use warnings; use Getopt::Long; use Log::Dispatch::FileRotate; use Log::Log4perl; my $conf = q( log4perl.category.foo = DEBUG, FileRotateAppender log4perl.appender.FileRotateAppender = Log::Dispatch::File +Rotate log4perl.appender.FileRotateAppender.filename = 667540.log log4perl.appender.FileRotateAppender.mode = append log4perl.appender.FileRotateAppender.size = 10000 log4perl.appender.FileRotateAppender.max = 5 log4perl.appender.FileRotateAppender.layout = PatternLayout log4perl.appender.FileRotateAppender.layout.ConversionPattern=[%P] % +d %M %F:%L: %m%n ); Log::Log4perl::init( \$conf ); my $log = Log::Log4perl::get_logger("foo"); my $man = undef; my $woman = undef; GetOptions( 'man' => \$man, 'woman' => \$woman, ) or die(qq{Usage: $0 [--man] [--woman]}); # Set explicit to 0 or 1 $man = ($man) ? 1 : 0; $woman = ($woman) ? 1 : 0; $log->info(qq{man=$man}) if $log->is_info(); $log->info(qq{woman=$woman}) if $log->is_info(); # ------ main ------ # then do deamon stuff __END__ $ perl 667540.pl $ cat 667540.log [3561] 2008/02/12 12:29:51 main:: 667540.pl:35: man=0 [3561] 2008/02/12 12:29:51 main:: 667540.pl:36: woman=0 $ perl 667540.pl --man $ cat 667540.log [3561] 2008/02/12 12:29:51 main:: 667540.pl:35: man=0 [3561] 2008/02/12 12:29:51 main:: 667540.pl:36: woman=0 [3574] 2008/02/12 12:30:00 main:: 667540.pl:35: man=1 [3574] 2008/02/12 12:30:00 main:: 667540.pl:36: woman=0 $ perl 667540.pl --man --woman $ cat 667540.log [3561] 2008/02/12 12:29:51 main:: 667540.pl:35: man=0 [3561] 2008/02/12 12:29:51 main:: 667540.pl:36: woman=0 [3574] 2008/02/12 12:30:00 main:: 667540.pl:35: man=1 [3574] 2008/02/12 12:30:00 main:: 667540.pl:36: woman=0 [3576] 2008/02/12 12:30:05 main:: 667540.pl:35: man=1 [3576] 2008/02/12 12:30:05 main:: 667540.pl:36: woman=1
    --
    Andreas
Re: Detect options of script runnning in a demon mode
by pc88mxer (Vicar) on Feb 12, 2008 at 16:09 UTC
    Some problems I see with the above solutions are 1) you have to determine the pid(s) of the potentially interfering processes; 2) you have make sure that the processes assigned to those pids are instances of your daemon. You'll have trouble if your daemon exits and another process is assigned the same pid.

    One classic way of handling this problem is to have the daemon acquire a lock on a specific resource, and usually things are set up so that the lock is automatically released when the process exits. The challenge in your case is to come up with a command line -> resource mapping which will do what you want.

    One simple idea is to map the command line to a file path. For instance, map "-a 1 -b 2 -c 3" to "/some/dir/locks/-a 1 -b 2 -c 3". Then use flock or File::lockf to obtain an exclusive lock on that file. You'll have to make sure that the path will work in all cases (e.g. be careful of special characters in your command line, path length, etc.) Another option is to use mysql's locking feature which allows you to identify a resource by an arbitrary string.

    Some issues I'll think you'll run into:

    • You might have to canonicalize your command line. E.g. is "-a 1 -b 2" the same as "-b 2 -a 1"?
    • Is the instance identified by all the command line arguments? If not, you'll have to omit the ones that don't contribute to the identity of the daemon when mapping the command line to a resource.

    In general when I've had to do this I've only needed a couple of different locks per daemon -- most of the time just one. If you tell us more about your application and its possible command line arguments, we can advise you on the what the best locking mechanism would be.

Re: Detect options of script runnning in a demon mode
by knbknb (Acolyte) on Feb 13, 2008 at 09:26 UTC

    Thanks to all who have replied; I really appreciate your suggestions.

    I'll try the resource-locking approach first, and the post some examples of my command line options on a later date.