Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Writing to flock files in a daemon

by graq (Curate)
on Mar 07, 2002 at 09:12 UTC ( [id://149970]=perlquestion: print w/replies, xml ) Need Help??

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

Hi again Monks.

Well, depending on your definition, what I am writing may not be a daemon. However, what I want is for my script to be running in the background and for it to be unique in the process list.

I came across some behaviour that I do not understand, and am seeking clarification.

  • Why is the PID not being written to the file until after the script is HUPped?
  • How can I get it to write the PID into the file, so that I can check it externally?
    The point being that a third party script (or process) can run which checks that the PID in the file matches that in the process list. Pretend, if you will, that I am overly paranoid and must be assured that there is only ever one of these scripts running. :)
    #!perl -w use strict; use Fcntl qw(LOCK_EX LOCK_NB); my $PID_FILE = '/tmp/_pid_file'; my $TASK = 'test_script'; my $EVENT_DELAY = 30 ; my $DEBUG = 'YES'; eval { main( @ARGV ) }; error( $@ ) if $@; # -------------------------------------------------------------------- +---------- sub main { my $start_mess = "===== Starting $TASK"; $start_mess .= " (@_)" if @_; $start_mess .= " ====="; error( $start_mess ); open( HIGHLANDER, ">>$PID_FILE") or die( "Cannot write to $PID_FIL +E: $!" ); { my $count = 0; { flock HIGHLANDER, LOCK_EX | LOCK_NB and last; sleep 1; redo if ++$count < 3; error( "Script is already running." ); die( "Startup failed, PID file locked." ); } } print HIGHLANDER $$; my $stopped; local $SIG{HUP} = sub { $stopped = 1 }; while( not $stopped ) { error( "DEBUG: Script Running" ) if $DEBUG; sleep $EVENT_DELAY; } my $finish_mess = "===== Stopping $TASK ====="; error( $finish_mess ); close HIGHLANDER; } # -------------------------------------------------------------------- +---------- sub error{ print "$TASK -> WARN: @_\n" }
    Please bear in mind that the code has been altered somewhat to work for this particular snippet.

    <a href="http://www.graq.co.uk">Graq</a>

  • Replies are listed 'Best First'.
    Re: Writing to flock files in a daemon
    by zengargoyle (Deacon) on Mar 07, 2002 at 09:34 UTC

      Check out Proc::PID_File.

      use Proc::PID_File; die "Already running!\n" if hold_pid_file("/var/run/myproc.pid"); &do_something; exit; # pid file will be automatically removed here
    Re: Writing to flock files in a daemon
    by dws (Chancellor) on Mar 07, 2002 at 09:38 UTC
      Why is the PID not being written to the file until after the script is HUPped?

      I'm guessing that you have a buffering problem. There can be a substantial delay between when the script "writes" into the file, and the next event that can cause I/O buffers to be flushed. Consult your favorite reference for instructions on unbuffering (hint: it'll invole the single argument form of select() ).

      You're also openning the lock file in "append" mode. Better to just truncate and rewrite.

        Thanks for the hint so far. I have modified the code, as you suggested. Changed the open to truncate instead of append, and tried using select.

        And yes, /tmp/_pid_file contains the PID.
        This is fine until I run the script a second time, when /tmp/_pid_file gets truncated even though startup fails.

        Any further suggestions?

        # As before .. # ... sub main { my $start_mess = "===== Starting $TASK"; $start_mess .= " (@_)" if @_; $start_mess .= " ====="; error( $start_mess ); my $FH; open( HIGHLANDER, ">$PID_FILE") or die( "Cannot write to $PID_FILE +: $!" ); { my $count = 0; { flock HIGHLANDER, LOCK_EX | LOCK_NB and last; sleep 1; redo if ++$count < 3; error( "Script is already running." ); die( "Startup failed, PID file locked." ); } } $FH = select(HIGHLANDER); $| = 1; select($FH); print HIGHLANDER $$; # .. # ..as before..

        <a href="http://www.graq.co.uk">Graq</a>

          Opening with ">" truncates the file. Since flock is and advisory lock (at least on Unix), it won't stop the second process from doing this. See open or "perldoc -f open".

          I tend to check for the file's existance first and then use "+<$file" if it's there and ">$file" if it's not. I think this is sloppy though since there is probably a race condition since the filecheck and the open occur at two different times (i.e. the file may come into existance between the file check and the open() call if another process was running at the same time). If the lock file is never really deleted after it is first, this probably isn't a problem.

          I'd suggest using sysopen with O_RDWR | O_CREAT and then truncating the file yourself just before you perform the write.

          bluto

    Re: Writing to flock files in a daemon
    by fokat (Deacon) on Mar 08, 2002 at 00:08 UTC
      You've already received very good advice. But since you mentioned you're paranoid, you should also be aware that lock-based solutions do not work reliably under (most) NFS mounts.

      This happens because the locking protocol must be properly supported in the client _and_ in the server. Since so many people just asume this is broken, many vendors don't pay much attention to fixing it.

      One nice way to reasonably insure that your process is the only instance in a machine, is to use a socket. Just grab yourself an unused port number (under < 1024 if you can, as this woule be better) and bind() it to your localhost address.

      The first instance won't have problems. Any subsequent instance will fail at the bind() with an Address already in use error. If you init the pid file after a succesful bind(), you won't wipe the pid file incorrectly. When the first instance dies for whatever reason, the OS will take care of removing the socket for you.

      Binding the socket to the localhost address, prevents it from being reached from another host via the network, so only local processes could connect to it. By choosing a port number < 1024, you insure that only a process with root priviledges could do the same and spoil this scheme.

      I don't know what else do you do in your scenario, but perhaps this socket could also have practical uses, such as pulling or feeding data to the daemon.

      Regards.

    Log In?
    Username:
    Password:

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

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

      No recent polls found