in reply to Re: Perl script crashing at lockfile ?
in thread Perl script crashing at lockfile ?

>>Why shell out to create lockfiles anyway?

I didn't write it, and I don't know Perl very well at all.

>>Why not just do it native say like

sysopen($fh, "$dir/$name", O_CREAT | O_EXCL | O_RDWR, 0600);

Thanks for supplying the code. I don't know why the person wanted to lock the file. Here is a bigger snippet of what is happening, down to the unlink, which I think is like the "unlock" ??

system ("lockfile -2 -r 5 $base_dir/.lock") == 0 or diehtml("Lock error: ", $? >> 8, "\n"); # TODO stop stderr of system # create unique suffix if (-f "$base_dir/$seq_file") { open(SEQ, "+<$base_dir/$seq_file") or diehtml("Error opening seq file: $!\n"); $seq = ; seek SEQ, 0, 0; } else { open(SEQ, ">$base_dir/$seq_file") or diehtml("Error creating seq file: $!\n"); $seq = 0; } $outfile .= sprintf "%7.7d", $seq; $ordernumber = $seq; #store this order no. before 'next' is cal +c. print SEQ ++$seq; close SEQ or warn "Something wrong closing seq: $!\n"; unlink "$base_dir/.lock" or diehtml("Unlock error: $!\n"); open(ORDERFILE, ">$outfile") or diehtml("Can't open order records: $!\n"); print ORDERFILE @_; close ORDERFILE or warn "Something fishy with closing the order: $ +!\n"; }

The file SEQ, is just a line line ASCII file with the next order number in it.

Peter

Replies are listed 'Best First'.
Re: Re: Re: Perl script crashing at lockfile ?
by tachyon (Chancellor) on Oct 01, 2003 at 12:44 UTC

    I didn't write it, and I don't know Perl very well at all.

    We know. Regardless you have come to the right place for help.

    The purpose of a lockfile is to tell other processes (ie copies of a CGI) to bugger off and wait their turn.

    Anyway here is a pure Perl replacement that is identical to the lockfile call (well it is not IDENTICAL) because this actually works as the system call to lockfile should....

    my $got_lock; use Fcntl; # to get constants for O_CREAT | O_EXCL | O_RDWR for ( 0 .. 5 ) { if ( sysopen(my $fh, "$base_dir/.lock", O_CREAT | O_EXCL | O_RDWR, + 0600) ) { $got_lock = 1; close $fh; last; } sleep 2; } diehtml("Lock error $!\n") unless $got_lock;

    That should be a cut and paste replacement for this:

    system ("lockfile -2 -r 5 $base_dir/.lock") == 0 or diehtml("Lock error: ", $? >> 8, "\n"); # TODO stop stderr of system

    cheers

    tachyon

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

      Is there a line vaguely like:
      while (-e "$base_dir/.lock") { sleep 1; }

      just above the code you have shown us????

      Definitely not, no 'while' or 'sleep' code. It's all drop down, except for a few subs.

      Thanks for supplying the pure Perl replacement code, I'll try that today. Would I still leave this line the same ?

      unlink "$base_dir/.lock" or diehtml("Unlock error: $!\n");

      Thanks a lot,

      Peter

        Yes you absolutely need to remove the lockfile, so leave the unlink. The point of the question is that a lockfile is a semaphore - is says (to other code, usually including other instances of itself) hang on everyone, I am using this file/dir/process. When I am done I will delete the lockfile so why don't you just wait around until I am done, then you can create a lockfile and have x/y/z to yourself.

        One problem with lockfiles and CGI is that if a CGI process starts, creates the lockfile but dies before it deletes it (user presses stop button for instance) the lockfile remains and will never get deleted. For this reason flocking a filehandle is often preferred. This way you process gets the exclusive aceess it wants but the lock is released when the process dies, FH goes out of scope.....

        One typical use of lockfiles is to stop two instances of a process starting. The first thing the process does is check for the lockfile and refuses to start if it exists. This is fine provided the lockfile always gets removed. What if the process that created it dies before removing the lockfile......You either have to delete it by hand or do something like this:

        #!/usr/bin/perl -w $|++; use strict; use Fcntl; use POSIX qw(setsid); my $PROGRAM = 'cleaner.pl'; my $LOCKFILE = "/var/lock/$PROGRAM"; # use a lockfile to prevent spawning duplicate processes unless ( sysopen(my $fh, $LOCKFILE, O_CREAT | O_EXCL | O_RDWR, 0600) ) + { close $fh; # the lockfile already exists print "Lockfile $LOCKFILE aready exists.\nChecking for running $PR +OGRAM process..."; # sleep for a couple of seconds to make sure we don't race # by this time the process should be in the process table sleep 2; my @ps = grep{ m/\Q$PROGRAM\E/ } `ps -C $PROGRAM`; # we expect 1 copy (us) running # print "@ps\n"; die "\nThere is already a copy of $PROGRAM running!\n" if @ps > 1; print "None!\n"; } # now demonize it defined(my $pid = fork) or die "Can't fork: $!"; exit 0 if $pid; chdir '/' or die "Can't chdir to /: $!"; umask 0; setsid() or die "Can't start a new session: $!"; $SIG{INT} = $SIG{TERM} = sub { unlink $LOCKFILE or warn "Could not unlink $LOCKFILE for $PROGRAM\ +n"; $dbh->disconnect if $dbh; exit; }; $SIG{HUP} = sub { warn "Caught HUP " . time() . "\n"; }; print "Started $0 daemon OK\n\n"; open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!"; open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!"; # this is the main loop that runs forever, once every 30 seconds # we don't want to die so we eval our function in case it chokes # we have closed STDERR so we won't know if it is choking # but we can change the code as required for debugging. while (1) { eval{ blah() }; sleep 30; }

        Here is some unix demon process code we use. You will note that it recovers from the dead lockfile issue using the following logic. If the lockfile exists*, also ensure that the process that created it exists. If we have a lockfile but no 'parent' process assume abnormal termination and start a new instance.

        * Actually it does not check for lockfile existence in the way you might expect. It actually tries to create it with an exclusive lock - provided the perms are OK this will only fail if it already exists. You could do this:

        unless ( -e $lockfile ) { open F, ">$lockfile" or die; }

        However there is a race condition here as there is a small but real delay between checking if the lockfile exists and creating it. It is therefore possible that two process could check for a lockfile at the exact same time, find it absent and this create it. The sysopen method is ATOMIC and thus avoids this rare but real problem. It is atomic because the existence check and creation happen as a single operation that either succeeds or fails.

        Note the signal handlers that let us unlink the lockfile if we get an INT or TERM signal before we exit.....

        cheers

        tachyon

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

Re: Re: Re: Perl script crashing at lockfile ?
by peterr (Scribe) on Oct 02, 2003 at 02:00 UTC
    I don't know why the person wanted to lock the file.

    I actually found a post on comp.lang.perl.misc that was from the guy that wrote it; here is his algorithm:

    enter program while (1) look for lockfile if lockfile does not exist exit sleep 5 seconds } create lockfile ..... do normal program stuff ...... unlink lockfile exit

    However, if the process died before removing the lock, it crashed the web server. The post was 7 years ago, so no doubt what has been suggested here will be _much_ better. :)

    Peter