I can't flock, it returns 0, any ideas.

G

Guy

I can't flock, it returns 0, any ideas.


This was suppose to be a small test script that would lock a file, the loop
for a certain amount of time, and then close the file.
I wanted to run this script to see if it would affect my other perl script
whi is trying to access the same file.

From what I read, I'm assuming that when you close a file, it also unlocks
it, so I'm guessing that you don't really have to unlock it before closing
it.
Just so you know '$l' is lower case $L. I guess i should choose more legible
scalars. The font here is pretty bad!

Anyway, my HTML page submits the variables f and l.
Variable f is just a delay variable, value of 50 give me like a 10 to 15
second delay. (so I thought the file would be locked for 10 to 15 seconds)
Variable l contains the operation I want to use on flock.
I tried 1,2, and 8, but it doesn't appear to lock the file, and the flock
function returns 0.
I'm not sure about 4 (NB), I think you have to add it to the 1, 2, or 8.
So I even tried with 5, and 6 but it still returns 0.
At first, this was actually hard coded, my command actually read $chk=flock
LCKFILE,2;

When I telnet to the server and do ls -l, it shows that the file has
_rw_rw_rw_
Is there anything I am missing.
This is my first attempt at using flock.
Guy Doucet



#!/usr/local/bin/perl -w

use CGI qw:)standard);
no warnings;

print header();

$fd="../data";
$lockfil="$fd/lock.txt";

$temp=$ENV{'QUERY_STRING'};
@pairs=split(/&/,$temp);
foreach $item(@pairs)
{
($key,$content)=split(/=/,$item,2);$CONFILTERED=~tr/+/
/;$CONFILTERED=~s/%(..)/pack("c",hex($1))/ge;
if($key eq "f"){$f=$content;}
if($key eq "l"){$l=$content;}
}

print"<HTML><BODY>\n";

$gchk=locksys(); # Lock file access
if($gchk==0){while($f>0){$f--;$t=64000;while($t>0){$t--;$a = 1234;$b = 31;$c
= $a * $b / 3;}}&unlocksys;}

print"Done!<BR></BODY></HTML>\n";

sub locksys{
my $chk=3840;
if(-d "$fd"){$chk=3584;if(open(LCKFILE,"$lockfil")){$chk=flock
LCKFILE,$l;}}
print"chk=:$chk:<BR>\n";
return $chk;
}

sub unlocksys{
close LCKFILE;
}
 
J

Jim Mozley

Guy wrote:

This was suppose to be a small test script that would lock a file, the loop
for a certain amount of time, and then close the file.
I wanted to run this script to see if it would affect my other perl script
whi is trying to access the same file.

Assuming this is on unix why not use flock?

Jim
 
J

J. Gleixner

Guy said:
I can't flock, it returns 0, any ideas.

First, have open and flock tell you why it's failing.

Read the perlopentut

perldoc perlopentut

Oh, and since you're using CGI.pm, don't do your own parsing of
QUERY_STRING, (foreach..@pairs.. stuf..) just use the param method
provided by CGI.

perldoc CGI
 
J

Jim Mozley

J. Gleixner said:
First, have open and flock tell you why it's failing.

Read the perlopentut

perldoc perlopentut

Not making excuses for myself really, but I missed the flock because it
was buried somewhere in the code (as well as not enough coffee). I've
used this, admitedley in a non-CGI script.

use Fcntl qw| :flock |;

my $file = 'testfile';

open(FILE, "> $file") or die "Cannot open file: $!";
flock(FILE, LOCK_EX);
print FILE "some text";
close FILE or die "Cannot close file $!"; # also releases lock

Jim
 
B

Ben Morrow

Guy said:
I can't flock, it returns 0, any ideas.

man flock

| RETURN VALUE
| On success, zero is returned. On error, -1 is returned and
| errno is set appropriately.

This is what the syscall returns. Perl maps these values (0 and -1) to
"0 but true" and undef respectively. The upshot of this is that if you
say 'if (flock(...) == 0)' it will always be true: "0 but true" numifies
to 0, as does undef. The correct test is 'if (flock(...))': undef is
false, and "0 but true" is true (that's why Perl uses that special
string rather than the number 0).
I wanted to run this script to see if it would affect my other perl script
whi is trying to access the same file.

Note that flock() locks are advisory, so it won't affect your other
script unless that also uses flock. The rules governing locks are:

1. Locks don't affect anything except other locks: they don't stop
anyone opening, reading, writing, deleting, etc. the file.

2. You can only hold a lock on an open file. (The details of what
happens when you have several handles open on the same file depend
on the underlying system: don't go there. :)

3. If someone holds a LOCK_EX lock, all attempts to get a lock will
fail. (LOCK_EX means 'exclusive lock'.)

4. If someone holds a LOCK_SH lock, attempts to get a LOCK_EX lock
will fail but attempts to get another LOCK_SH lock will
succeed. (LOCK_SH means 'shared lock'.)

5. If you can't get the lock you are trying for, then if you specify
LOCK_NB (use | rather than +, i.e. 'flock $FH, LOCK_SH | LOCK_NB)
the flock call will fail, and if you don't flock will just not
return until you can.
From what I read, I'm assuming that when you close a file, it also unlocks
it, so I'm guessing that you don't really have to unlock it before closing
it.

That's right. Most uses of LOCK_UN are wrong.
I tried 1,2, and 8, but it doesn't appear to lock the file, and the flock
function returns 0.

Don't do this. Use the constants from Fcntl, both for readability and
portability:
#!/usr/local/bin/perl -w

use CGI qw:)standard);

use Fcntl qw/:flock/;
no warnings;

Why? Using '-w' and then 'no warnings' is particularly perverse... You
should have both these lines:
use warnings;
use strict;
and then you don't need '-w'.
print header();

$fd="../data";

my $fd = "../data";

Insert 'my' similarly for all the variables 'strict' complains about.
$lockfil="$fd/lock.txt";

$temp=$ENV{'QUERY_STRING'};
@pairs=split(/&/,$temp);

foreach $item(@pairs)
{
($key,$content)=split(/=/,$item,2);$CONFILTERED=~tr/+/
/;$CONFILTERED=~s/%(..)/pack("c",hex($1))/ge;
if($key eq "f"){$f=$content;}
if($key eq "l"){$l=$content;}
}

NO. Since you're using CGI.pm, *USE* it. Read the docs if you don't
know how: perldoc CGI.
print"<HTML><BODY>\n";

$gchk=locksys(); # Lock file access

[Indentation sorted out: you'll find code much easier to work with if
you indent properly]
if($gchk==0) {

So here you just want
if($gchk) {
while($f>0) {
$f--;
$t=64000;
while($t>0) {
$t--;
$a = 1234;
$b = 31;
$c = $a * $b / 3;
}
}

AARGH! No no no, this is not how you pause the script!
See 'perldoc -f sleep'.
&unlocksys;

Don't use &sub unless you know what it does and why you need it:
unlocksys();
}

print"Done!<BR></BODY></HTML>\n";

[more indentation-adjusting]
sub locksys{
my $chk=3840;
if(-d "$fd") {
$chk=3584;
if(open(LCKFILE,"$lockfil")) {

This will fail if lock.txt doesn't exist. Since you're just using it
for locking, you can just nuke it each time with
open LCKFILE, ">$lockfil";
..
$chk=flock LCKFILE,$l;

This is silly. You don't need to pass the type of lock to aquire in as
a CGI parameter. Those programs which only read whatever it is you are
locking should use
$chk = flock LCKFILE, LOCK_SH;
and those that write should use
$chk = flock LCKFILE, LOCK_EX;
.. The rules given above were designed with this in mind. If you are
just trying to make sure you have only one copy of your script running
at a time, use LOCK_EX.

If the open failed, don't you want to know why? Perl will tell you if
you ask it nicely:
else {
print "Open failed: $!";
}

I would be very much inclined to structure this whole thing rather
differently. I would have:

sub locksys {
my $file = shift;

open my $FH, ">", $file or die "can't open lockfile: $!";
flock $FH, LOCK_EX or die "can't aquire lock: $!";

return $FH;
}

There are several points here:

1. 'my $file = shift' means we take the file to lock as an argument.

2. 'my $FH' creates a what is called a 'lexical filehandle'. Basically
this means that the file is automatically closed as soon as all
references to it go out of scope, so you don't need &unlocksys any
more.

3. We return this filehandle from the sub, so that the file isn't just
closed straight away (that would be rather silly). You use it like
this:

#...program...

{
my $LOCK = locksys("../data/lock.txt");

#... do stuff that relies on having the lock
}
#... $LOCK goes out of scope here, so the file is closed and the lock
# lost.

4. If any of the syscalls fails, we 'die'. This will terminate the
program with an error message. You can use the CGI::Carp module
(see its docs) to get the error sent somewhere sensible when
running CGI scripts.

This is really a matter of style: you could just as well choose to
return undef or something instead, and write your own error message
with $! up in the main script. You may find this approach easier if
you want to fail softly rather than aborting the script and don't
feel like messing with eval {}.

5. I assume here that if flock fails it is a fatal error. If flock may
fail non-fatally (either because you used LOCK_NB or because your
system doesn't restart syscalls if you catch a signal) you will
need to investigte the Errno module to find the reason for its
failure, and take appropriate action.

Ben
 
R

Randal L. Schwartz

Jim> open(FILE, "> $file") or die "Cannot open file: $!";
Jim> flock(FILE, LOCK_EX);

This doesn't make any sense. You've already potentially blown away
someone else's written data if they got the flock before you.

You cannot modify the file before obtaining the flock. And "open >"
definitely modifies the file (truncating it).

"open >>" is probably what you wanted, followed by a seek FILE, 0, 0.
 
B

Ben Morrow

Jim> open(FILE, "> $file") or die "Cannot open file: $!";
Jim> flock(FILE, LOCK_EX);

This doesn't make any sense. You've already potentially blown away
someone else's written data if they got the flock before you.

I think the file in question was just being used as a semaphore: i.e. it
never had any real data; and the 'create-if-nonexistent' semantics are
important.
"open >>" is probably what you wanted, followed by a seek FILE, 0, 0.

Errr... that does something quite different, no? Perhaps you meant
'+<'? Or maybe 'truncate 0' after aquiring the lock.

If the file does contain data, then I would say that you have to use
sysopen, as you want O_CREAT | O_RDWR but not O_TRUNC:

sysopen FILE, $file, O_RDWR | O_CREAT or die $!;

Ben
 
G

Guy

WOW!!!
Thanks to everyone for all the information.
I will try to put all or at least most of these suggestions to use.

With respect to flock, my perl 5 book says it returns 1 if true, and '' if
false, whatever that means.
In any case, I changed one thing and now it appears to work.

open(LCKFILE,"$lockfil"); # returns zero when I try to flock
open(LCKFILE,">$lockfil"); # returns one when I try to flock

As for the parsing, I thought it would be more portable by doing it the long
way.
I guess it's from my older days of writing, or maybe I was just doing it
wrong back then too!

Thanks for putting so much time to this.
Cheers all.
Guy Doucet


Ben Morrow said:
Guy said:
I can't flock, it returns 0, any ideas.

man flock

| RETURN VALUE
| On success, zero is returned. On error, -1 is returned and
| errno is set appropriately.

This is what the syscall returns. Perl maps these values (0 and -1) to
"0 but true" and undef respectively. The upshot of this is that if you
say 'if (flock(...) == 0)' it will always be true: "0 but true" numifies
to 0, as does undef. The correct test is 'if (flock(...))': undef is
false, and "0 but true" is true (that's why Perl uses that special
string rather than the number 0).
I wanted to run this script to see if it would affect my other perl script
whi is trying to access the same file.

Note that flock() locks are advisory, so it won't affect your other
script unless that also uses flock. The rules governing locks are:

1. Locks don't affect anything except other locks: they don't stop
anyone opening, reading, writing, deleting, etc. the file.

2. You can only hold a lock on an open file. (The details of what
happens when you have several handles open on the same file depend
on the underlying system: don't go there. :)

3. If someone holds a LOCK_EX lock, all attempts to get a lock will
fail. (LOCK_EX means 'exclusive lock'.)

4. If someone holds a LOCK_SH lock, attempts to get a LOCK_EX lock
will fail but attempts to get another LOCK_SH lock will
succeed. (LOCK_SH means 'shared lock'.)

5. If you can't get the lock you are trying for, then if you specify
LOCK_NB (use | rather than +, i.e. 'flock $FH, LOCK_SH | LOCK_NB)
the flock call will fail, and if you don't flock will just not
return until you can.
From what I read, I'm assuming that when you close a file, it also unlocks
it, so I'm guessing that you don't really have to unlock it before closing
it.

That's right. Most uses of LOCK_UN are wrong.
I tried 1,2, and 8, but it doesn't appear to lock the file, and the flock
function returns 0.

Don't do this. Use the constants from Fcntl, both for readability and
portability:
#!/usr/local/bin/perl -w

use CGI qw:)standard);

use Fcntl qw/:flock/;
no warnings;

Why? Using '-w' and then 'no warnings' is particularly perverse... You
should have both these lines:
use warnings;
use strict;
and then you don't need '-w'.
print header();

$fd="../data";

my $fd = "../data";

Insert 'my' similarly for all the variables 'strict' complains about.
$lockfil="$fd/lock.txt";

$temp=$ENV{'QUERY_STRING'};
@pairs=split(/&/,$temp);

foreach $item(@pairs)
{
($key,$content)=split(/=/,$item,2);$CONFILTERED=~tr/+/
/;$CONFILTERED=~s/%(..)/pack("c",hex($1))/ge;
if($key eq "f"){$f=$content;}
if($key eq "l"){$l=$content;}
}

NO. Since you're using CGI.pm, *USE* it. Read the docs if you don't
know how: perldoc CGI.
print"<HTML><BODY>\n";

$gchk=locksys(); # Lock file access

[Indentation sorted out: you'll find code much easier to work with if
you indent properly]
if($gchk==0) {

So here you just want
if($gchk) {
while($f>0) {
$f--;
$t=64000;
while($t>0) {
$t--;
$a = 1234;
$b = 31;
$c = $a * $b / 3;
}
}

AARGH! No no no, this is not how you pause the script!
See 'perldoc -f sleep'.
&unlocksys;

Don't use &sub unless you know what it does and why you need it:
unlocksys();
}

print"Done!<BR></BODY></HTML>\n";

[more indentation-adjusting]
sub locksys{
my $chk=3840;
if(-d "$fd") {
$chk=3584;
if(open(LCKFILE,"$lockfil")) {

This will fail if lock.txt doesn't exist. Since you're just using it
for locking, you can just nuke it each time with
open LCKFILE, ">$lockfil";
.
$chk=flock LCKFILE,$l;

This is silly. You don't need to pass the type of lock to aquire in as
a CGI parameter. Those programs which only read whatever it is you are
locking should use
$chk = flock LCKFILE, LOCK_SH;
and those that write should use
$chk = flock LCKFILE, LOCK_EX;
. The rules given above were designed with this in mind. If you are
just trying to make sure you have only one copy of your script running
at a time, use LOCK_EX.

If the open failed, don't you want to know why? Perl will tell you if
you ask it nicely:
else {
print "Open failed: $!";
}

I would be very much inclined to structure this whole thing rather
differently. I would have:

sub locksys {
my $file = shift;

open my $FH, ">", $file or die "can't open lockfile: $!";
flock $FH, LOCK_EX or die "can't aquire lock: $!";

return $FH;
}

There are several points here:

1. 'my $file = shift' means we take the file to lock as an argument.

2. 'my $FH' creates a what is called a 'lexical filehandle'. Basically
this means that the file is automatically closed as soon as all
references to it go out of scope, so you don't need &unlocksys any
more.

3. We return this filehandle from the sub, so that the file isn't just
closed straight away (that would be rather silly). You use it like
this:

#...program...

{
my $LOCK = locksys("../data/lock.txt");

#... do stuff that relies on having the lock
}
#... $LOCK goes out of scope here, so the file is closed and the lock
# lost.

4. If any of the syscalls fails, we 'die'. This will terminate the
program with an error message. You can use the CGI::Carp module
(see its docs) to get the error sent somewhere sensible when
running CGI scripts.

This is really a matter of style: you could just as well choose to
return undef or something instead, and write your own error message
with $! up in the main script. You may find this approach easier if
you want to fail softly rather than aborting the script and don't
feel like messing with eval {}.

5. I assume here that if flock fails it is a fatal error. If flock may
fail non-fatally (either because you used LOCK_NB or because your
system doesn't restart syscalls if you catch a signal) you will
need to investigte the Errno module to find the reason for its
failure, and take appropriate action.

Ben

--
perl -e'print map {/.(.)/s} sort unpack "a2"x26, pack "N"x13,
qw/1632265075 1651865445 1685354798 1696626283 1752131169 1769237618
1801808488 1830841936 1886550130 1914728293 1936225377 1969451372
2047502190/' #
(e-mail address removed)
 
G

Guy

Actually, it _was_ just a dummy file, but yes what you say makes sense.
thanks again to all.


Randal L. Schwartz said:
Jim> open(FILE, "> $file") or die "Cannot open file: $!";
Jim> flock(FILE, LOCK_EX);

This doesn't make any sense. You've already potentially blown away
someone else's written data if they got the flock before you.

You cannot modify the file before obtaining the flock. And "open >"
definitely modifies the file (truncating it).

"open >>" is probably what you wanted, followed by a seek FILE, 0, 0.

--
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<[email protected]> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl
training!
 
B

Ben Morrow

Guy said:
With respect to flock, my perl 5 book says it returns 1 if true, and '' if
false, whatever that means.

You're right, so it does... I've only ever tested the return for
truth.

OK, what I said holds about /most/ syscalls in Perl... :)

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top