You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Rob Egan <ro...@artistdirect.com> on 2000/06/29 00:37:40 UTC

can't properly append to file from mod_perl script

Hi,

I am relocating content from a non-mod_perl Apache site to a new
mod_perl/1.24 enabled Apache server and I have a problems getting one of the
CGI scripts to behave in mod_perl. All it does is capture e-mail addresses,
and place them in a text file so we can gather them up later and drop them
into a database. If you run the script as a regular CGI (without mod_perl
enabled) it works great. But on the mod_perl enabled server, the script
throws garbage into the file and overwrites previous entries if a user
submits more than one e-mail address in a single session (i.e. they enter
one address, click "Back", then enter another address). However, if the user
submits one e-mail address, then quits their browser, restarts the browser
and enters another e-mail, it works just fine. Does anybody have any idea
why this would happen? I've included the CGI script below with actual URL's
omitted (which was written by some consultant who no longer works here).
Thanks!

-Rob
rob@artistdirect.com

---------begin script text
#!/usr/local/bin/perl

$|=1;

# Enumerate the locking options for clarity...
$LOCK_EX = 2;
$LOCK_UN = 8;
$results_file = "./results.csv";
$cl = $ENV{'CONTENT_LENGTH'};
$rqm = $ENV{'REQUEST_METHOD'};

sub lock {
	# print "locking<BR>";
	flock(RESULTS, $LOCK_EX);
	# print "seeking<BR>";
	# seeks to the end of the file in case
	# someone got while we were waiting for the lock
	seek(RESULTS, 0, 2);
	# print "locked<BR>";
}

sub unlock {
	# print "unlocking<BR>";
	flock(RESULTS, $LOCK_UN);
	# print "unlocked<BR>";
}

sub web_die {
print "Location:  http://<omitted>";
die("\n");
}

sub web_die_action {
print "Location:  http://<omitted>";
die("\n");
}

sub return_error
{
	local ($status, $keyword, $message) = @_;

	print "Content-type: text/html", "\n";
	print "Status: ", $status, " ", $keyword, "\n\n";

	print <<End_of_Error;

<HTML><HEAD><TITLE>Unexpected CGI Error</TITLE></HEAD>
<BODY>
<H1>$keyword</H1>
<HR>$message</HR>
</BODY>
</HTML>
End_of_Error
	exit(1);
}
sub write_entry
{
		# Lock after you open
		&lock();

		$count = 0;
		foreach (keys %FORM)
		{
		 $count++;
		}

		for ($i = 0; $i < $count; $i++)
		{
		  $index = $i + 1;
		  if ($FORM{"FIELD_$index"} eq "")
		   {
		    $FORM{"FIELD_$index"} = "NO ENTRY";
		   }
		  $FORM{"FIELD_$index"} =~ s/\"/\"\"/g;      # ...change " to ""
		  $FORM{"FIELD_$index"} =~ s/\r//g;          # ...kill line feeds
		  $FORM{"FIELD_$index"} =~ s/\n/ /g;         # ...change cr to whitespace
		  $FORM{"FIELD_$index"} = "\"" . $FORM{"FIELD_$index"} . "\"";
		  print RESULTS $FORM{"FIELD_$index"};
		  print RESULTS ","
		 }
		print RESULTS "\n";

		# Unlock before you close
		&unlock();

		close(RESULTS);

		&location;
}

if ($rqm eq "POST")
 {
  read(STDIN, $buffer, $cl);
  @pairs = split(/&/, $buffer);
  $q = 0;
  foreach $pair (@pairs)
   {
    $q++;
    ($name, $value) = split(/=/, $pair);
    $FORM{"FIELD_$q"} = $value;
    $FORM{"FIELD_$q"} =~ tr/+/ /;
    $FORM{"FIELD_$q"} =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    if ($FORM{"FIELD_$q"} eq "0-12")
     {
       &location;
       exit(0);
     }
   }
 }
elsif ($cl > 0)
 {
  &web_die_action;
# Error: form results must be submitted with method POST
 }

###################################################
if (! (-e $results_file))
{
	if (open (RESULTS, ">results.csv"))
	{
		&write_entry;
	}
	else
	{
		&return_error (500, "Results Error", "Cannot create results.csv to store
entries.");
	}
}

else
{
	if (! ((-r $results_file) && (-w $results_file)) )
	{
		&return_error (500, "Results Error", "Cannot read or write to
results.csv.");
	}
	else
	{
		open(RESULTS, ">>results.csv") || &web_die;
		&write_entry;
	}
}
exit(0);
sub location
{
print "Location:  http://<omitted>";
}


Re: can't properly append to file from mod_perl script

Posted by Stas Bekman <st...@stason.org>.
> On Thu, 29 Jun 2000, Stas Bekman wrote:
> 
> > When using file locking one must make sure, that if the script has been
> > stopped before the close() was called, to use an END block under Registry
> > or $r->register_cleanup() anywhere. See:
> > http://perl.apache.org/guide/debug.html#Handling_the_User_pressed_Stop_
> > http://perl.apache.org/guide/debug.html#Cleanup_Code
> > 
> > Otherwise you might get stuck with a stale lock, which will be never
> > removed unless the same process will call open() on the same filehandler.
> > And it'd be the same filehandler only if you use "open, IN ..." style, if
> > you use Symbol module or perl5.6 filehandler autovivification a new unique
> > filehandler will be created when the same code will run again.
> > 
> > So close() under mod_perl is not enough when locking is used.
> 
> Are you suggesting that more has to be done even if you use my $fh type
> opening? I'm not sure I believe you, if thats what you're suggesting - the
> second paragraph there seems to confuse things. If you use my $fh style
> opening (either the Symbol or 5.6 way) you need worry not about unlocking
> files or cleanup code for unlocking/closing files - it's all done by the
> garbage collector. (you should close your filehandles anyway though for
> the sake of cleanliness and not leaving around locks, but my point was
> merely the necessity of it).

Frank were talking about using globals. I forgot to stress this point. My
post is correct only when assuming that one uses globals.

so if you use 

  use vars ($fh)
  $fh = Symbol::gensym; # or Apache::Symbol
  open $fh, "foo" ...

it holds. Now thinking again about 5.6, I'm not sure about this:

  use vars ($fh)
  open $fh, "foo"...

is the auto-vivication of a unique filehandler works in this case? Or
only when one uses:

  open my $fh, "foo"...

if the latter is correct, my statement is incorrect regrading the perl5.6
autovivication, and only these two cases hold:

  # 1
  open IN, "foo" ....

  # 2
  use vars ($fh)
  $fh = Symbol::gensym; # or Apache::Symbol
  open $fh, "foo" ...

In both you should worry to add a cleanup code.

_____________________________________________________________________
Stas Bekman              JAm_pH     --   Just Another mod_perl Hacker
http://stason.org/       mod_perl Guide  http://perl.apache.org/guide 
mailto:stas@stason.org   http://perl.org     http://stason.org/TULARC
http://singlesheaven.com http://perlmonth.com http://sourcegarden.org




Re: can't properly append to file from mod_perl script

Posted by Matt Sergeant <ma...@sergeant.org>.
On Thu, 29 Jun 2000, Stas Bekman wrote:

> When using file locking one must make sure, that if the script has been
> stopped before the close() was called, to use an END block under Registry
> or $r->register_cleanup() anywhere. See:
> http://perl.apache.org/guide/debug.html#Handling_the_User_pressed_Stop_
> http://perl.apache.org/guide/debug.html#Cleanup_Code
> 
> Otherwise you might get stuck with a stale lock, which will be never
> removed unless the same process will call open() on the same filehandler.
> And it'd be the same filehandler only if you use "open, IN ..." style, if
> you use Symbol module or perl5.6 filehandler autovivification a new unique
> filehandler will be created when the same code will run again.
> 
> So close() under mod_perl is not enough when locking is used.

Are you suggesting that more has to be done even if you use my $fh type
opening? I'm not sure I believe you, if thats what you're suggesting - the
second paragraph there seems to confuse things. If you use my $fh style
opening (either the Symbol or 5.6 way) you need worry not about unlocking
files or cleanup code for unlocking/closing files - it's all done by the
garbage collector. (you should close your filehandles anyway though for
the sake of cleanliness and not leaving around locks, but my point was
merely the necessity of it).

-- 
<Matt/>

Fastnet Software Ltd. High Performance Web Specialists
Providing mod_perl, XML, Sybase and Oracle solutions
Email for training and consultancy availability.
http://sergeant.org | AxKit: http://axkit.org


Re: can't properly append to file from mod_perl script

Posted by Stas Bekman <st...@stason.org>.
On 29 Jun 2000, Frank D. Cringle wrote:

> Vivek Khera <kh...@kciLink.com> writes:
> > >>>>> "RE" == Rob Egan <ro...@artistdirect.com> writes:
> > 
> > RE> CGI scripts to behave in mod_perl. All it does is capture e-mail addresses,
> > RE> and place them in a text file so we can gather them up later and drop them
> > RE> into a database. If you run the script as a regular CGI (without mod_perl
> > RE> enabled) it works great. But on the mod_perl enabled server, the script
> > RE> throws garbage into the file and overwrites previous entries if a user
> > RE> submits more than one e-mail address in a single session (i.e. they enter
> > 
> > It has nothing to do with the person quitting the browser.  It has to
> > do with your script using global variables.  Turn on "use strict" and
> > perl warnings and see what your error log says.
> > 
> > Then go to the mod perl guide and search for "sticky variables".
> 
> But since they are all global (no use of my), they won't be sticky.
> 
> There is plenty wrong with the script, although I can't specifically
> correlate the symptoms with the mistakes.  Sometimes the file is
> referred to as $results_file and in other places by its literal name.
> The 'if (-e $results_file) open ">..." else open ">>..."' code is
> unnecessary and will break if 2 processes hit the first if
> concurrently.  Just open it in append mode.  If it isn't there it will
> be created.  If you don't have write permission the open will fail.
> The locking is classically broken "# Unlock before you close" No!
> Wrong!.  Just close the file.  That has the desired side-effects of
> first flushing the buffered data and then unlocking the file.

When using file locking one must make sure, that if the script has been
stopped before the close() was called, to use an END block under Registry
or $r->register_cleanup() anywhere. See:
http://perl.apache.org/guide/debug.html#Handling_the_User_pressed_Stop_
http://perl.apache.org/guide/debug.html#Cleanup_Code

Otherwise you might get stuck with a stale lock, which will be never
removed unless the same process will call open() on the same filehandler.
And it'd be the same filehandler only if you use "open, IN ..." style, if
you use Symbol module or perl5.6 filehandler autovivification a new unique
filehandler will be created when the same code will run again.

So close() under mod_perl is not enough when locking is used.

_____________________________________________________________________
Stas Bekman              JAm_pH     --   Just Another mod_perl Hacker
http://stason.org/       mod_perl Guide  http://perl.apache.org/guide 
mailto:stas@stason.org   http://perl.org     http://stason.org/TULARC
http://singlesheaven.com http://perlmonth.com http://sourcegarden.org



Re: can't properly append to file from mod_perl script

Posted by "Frank D. Cringle" <fd...@cliwe.ping.de>.
Vivek Khera <kh...@kciLink.com> writes:
> >>>>> "RE" == Rob Egan <ro...@artistdirect.com> writes:
> 
> RE> CGI scripts to behave in mod_perl. All it does is capture e-mail addresses,
> RE> and place them in a text file so we can gather them up later and drop them
> RE> into a database. If you run the script as a regular CGI (without mod_perl
> RE> enabled) it works great. But on the mod_perl enabled server, the script
> RE> throws garbage into the file and overwrites previous entries if a user
> RE> submits more than one e-mail address in a single session (i.e. they enter
> 
> It has nothing to do with the person quitting the browser.  It has to
> do with your script using global variables.  Turn on "use strict" and
> perl warnings and see what your error log says.
> 
> Then go to the mod perl guide and search for "sticky variables".

But since they are all global (no use of my), they won't be sticky.

There is plenty wrong with the script, although I can't specifically
correlate the symptoms with the mistakes.  Sometimes the file is
referred to as $results_file and in other places by its literal name.
The 'if (-e $results_file) open ">..." else open ">>..."' code is
unnecessary and will break if 2 processes hit the first if
concurrently.  Just open it in append mode.  If it isn't there it will
be created.  If you don't have write permission the open will fail.
The locking is classically broken "# Unlock before you close" No!
Wrong!.  Just close the file.  That has the desired side-effects of
first flushing the buffered data and then unlocking the file.

-- 
Frank Cringle,      fdc@cliwe.ping.de
voice: (+49 2304) 467101; fax: 943357

Re: can't properly append to file from mod_perl script

Posted by Vivek Khera <kh...@kciLink.com>.
>>>>> "RE" == Rob Egan <ro...@artistdirect.com> writes:

RE> CGI scripts to behave in mod_perl. All it does is capture e-mail addresses,
RE> and place them in a text file so we can gather them up later and drop them
RE> into a database. If you run the script as a regular CGI (without mod_perl
RE> enabled) it works great. But on the mod_perl enabled server, the script
RE> throws garbage into the file and overwrites previous entries if a user
RE> submits more than one e-mail address in a single session (i.e. they enter

It has nothing to do with the person quitting the browser.  It has to
do with your script using global variables.  Turn on "use strict" and
perl warnings and see what your error log says.

Then go to the mod perl guide and search for "sticky variables".

-- 
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Vivek Khera, Ph.D.                Khera Communications, Inc.
Internet: khera@kciLink.com       Rockville, MD       +1-301-545-6996
GPG & MIME spoken here            http://www.khera.org/~vivek/