Question about Modperl::Util::exit

Question about Modperl::Util::exit

am 20.11.2008 22:50:12 von Bruce Johnson

We just moved some cgi scripts over from an ancient system to our new
server running modperl.

We have require files (that themselves have require files) that check
cookies and login status for these scripts.

If someone is not logged in, this triggers an exit our of one of the
require files to the login page, the original calling script is sent
to the login as a parameter, along with any parameters IT had.

Since we migrated to the new system we've been seeing error messages
like this:

"[Mon Oct 13 00:39:56 2008] [error] ModPerl::Util::exit: (120000) exit
was called at header-apply-locked line 41. Compilation failed in
require at header-apply-combined line 17. Compilation failed in
require at /home/oraweb/perl/apply/applymyapplication.pl line 9."

This happens when the exit is triggered in the nested require file.
which is code like this:

# Check to see if the person was logged in, and if not go to login
page and
# pass the url and parameters
if (!defined($UUAff) || !defined($UUPID)) {
$url = "$path/login.pl?prev_url=$prev_url$llist";
print "Location: $url\n\n";
exit;
}
1;


What is this error, is it a problem, and what do I do to avoid it.

Thanks

--
Bruce Johnson
University of Arizona
College of Pharmacy
Information Technology Group

Institutions do not have opinions, merely customs

Re: Question about Modperl::Util::exit

am 21.11.2008 12:08:20 von aw

Bruce Johnson wrote:
> We just moved some cgi scripts over from an ancient system to our new
> server running modperl.
>
> We have require files (that themselves have require files) that check
> cookies and login status for these scripts.
>
> If someone is not logged in, this triggers an exit our of one of the
> require files to the login page, the original calling script is sent to
> the login as a parameter, along with any parameters IT had.
>
> Since we migrated to the new system we've been seeing error messages
> like this:
>
> "[Mon Oct 13 00:39:56 2008] [error] ModPerl::Util::exit: (120000) exit
> was called at header-apply-locked line 41. Compilation failed in require
> at header-apply-combined line 17. Compilation failed in require at
> /home/oraweb/perl/apply/applymyapplication.pl line 9."
>
> This happens when the exit is triggered in the nested require file.
> which is code like this:
>
> # Check to see if the person was logged in, and if not go to login page and
> # pass the url and parameters
> if (!defined($UUAff) || !defined($UUPID)) {
> $url = "$path/login.pl?prev_url=$prev_url$llist";
> print "Location: $url\n\n";
> exit;
> }
> 1;
>
>
> What is this error, is it a problem, and what do I do to avoid it.
>
Hi.
Until someone else more qualified provides a better and more precise
explanation, let me try :

In short : I think you have a real problem, in the sense that those
scripts do not seem to be compatible with running them under mod_perl.
In my opinion, you will either have to make some serious modifications
to them to run them under mod_perl, or you will have to disable mod_perl
for those scripts.

In many more words :
When you run a normal perl script (outside of Apache), the perl
interpreter is loaded fresh, reads your script, compiles it to some
bytecode, then runs the bytecode. When it encounters an "exit"
instruction, it not only stops executing the script, but it also stops
the perl interpreter itself and returns to the command-line.
That is also what happens when you run perl cgi-bin scripts, without
having mod_perl installed.

mod_perl, among other things, consists of "embedding" a persistent perl
interpreter into Apache itself. Because it is supposed to stay alive
there, and be used for many script executions in a row, it arranges not
to terminate itself when it encounters an "exit". That's why it
redefines "exit", to catch it and not really exit, but just stop the
script, clean up, and be ready to execute the next script.

mod_perl can dramatically improve the speed of running perl cgi-bin
scripts, for two main reasons :
1) the perl interpreter is "embedded" in Apache, so it does not have to
be reloaded each time you run a perl cgi-bin script
2) when the embeded perl interpreter runs a perl cgi-bin script once, it
compiles it, then executes the "compiled" code, but it keeps the
compiled version available. So the next time it has to run the same
script, it does not have to start from scratch, it can just run the
already-compiled version.
In order to do that however, mod_perl initially "re-works" the original
script quite a bit. Basically it converts it from a stand-alone script,
by wrapping it into some code which calls the original script as a
subroutine. One of the things it does for that, is to replace the
original "exit" calls of the script, so that they will not really exit
the perl interpreter, but just do a "return" from the now "subroutined
script".

However, all of this is not really compatible with the usage of
"required" pieces of code which themselves call "exit" (which seems to
me a rather strange way of programming anyway).
The way I see this running is :
- perl reads and compiles the top cgi-bin script
- during this compilation, it encounters a "require othercode.pl".
- it suspends the compilation of the original script, pulls in
"othercode.pl", compiles it and runs it. It expects this piece of code
to do things like initialising new symbols etc.., and end with "return 1".
- at which point it continues compiling the original script, with maybe
now some changed environment.
- then it runs the original script, now transformed into a subroutine,
expecting it to end in an "exit" (which has been in the meantime
transformed into a "return").

However now, in your scenario, it encounters an "exit" in the code it
pulled in for the "require othercode.pl". What should it do with this ?
It is still in the process of compiling the original script..
It can certainly not do a real "exit", because that would terminate the
supposedly persistent perl interpreter in Apache.
It can also not do a "return", because to what would it return ?

So, this is stuff that might well work in the context of a
non-persistent perl interpreter (as you would have it in an Apache
without mod_perl), but in a mod_perl context this is really bizarre.
And that is probably why you see all these error messages.

In my subjective opinion thus, I believe you have the following options :
- forget about running this, as such, under mod_perl, and run them under
a non-mod_perl environment, as normal cgi-bin scripts.
- or, hire some good mod_perl programmer to have a look at these scripts
and see if they can be modified to run under mod_perl
(Torsten, are you there ?)
- or, considering that they seem to be used for some AAA purpose, have a
look at the numerous AAA-like modules on CPAN in the Apache2::auth*
series, and see if you could not simply replace these scripts by
something that fills the same purpose (even if for that you have to
slightly modify one of the existing Apache2::auth* modules)

In my opinion still, the 3rd option, if feasible, would be the best,
because it would also give you a much bigger boost in performance, and
probably a much easier and clean configuration, compared to the kind of
"script calling script" method that the current scripts are apparently
using.

There might be another option still : looking at your description above
(very good by the way), I have a feeling that it might be possible to
replace the "require-s" by subroutine calls, which would not have this
issue of "exit within a require-d file". I have not seen the code of
the required files of course, and I do not know how many scripts you
would have to modify, so this is a rather risky shot. But having some
experience with mod_perl based AAA modules, I have some idea of how they
usually work and what they do, and unless yours are particularly
devious, that should not be so outlandish.
A project for a student maybe ?

Re: Question about Modperl::Util::exit

am 24.11.2008 20:30:43 von Perrin Harkins

On Thu, Nov 20, 2008 at 4:50 PM, Bruce Johnson
wrote:
> This happens when the exit is triggered in the nested require file. which is
> code like this:
>
> # Check to see if the person was logged in, and if not go to login page and
> # pass the url and parameters
> if (!defined($UUAff) || !defined($UUPID)) {
> $url = "$path/login.pl?prev_url=$prev_url$llist";
> print "Location: $url\n\n";
> exit;
> }
> 1;

Just replace exit with the appropriate return code.

use Apache2::Const -compile => qw(REDIRECT);
return Apache2::Const::REDIRECT;

- Perrin

Re: Question about Modperl::Util::exit

am 24.11.2008 20:52:20 von aw

Perrin Harkins wrote:
> On Thu, Nov 20, 2008 at 4:50 PM, Bruce Johnson
> wrote:
>> This happens when the exit is triggered in the nested require file. which is
>> code like this:
>>
>> # Check to see if the person was logged in, and if not go to login page and
>> # pass the url and parameters
>> if (!defined($UUAff) || !defined($UUPID)) {
>> $url = "$path/login.pl?prev_url=$prev_url$llist";
>> print "Location: $url\n\n";
>> exit;
>> }
>> 1;
>
> Just replace exit with the appropriate return code.
>
> use Apache2::Const -compile => qw(REDIRECT);
> return Apache2::Const::REDIRECT;
>
> - Perrin
>

Can you do a "return" from a "require"-d file, and what does it do exactly ?

Re: Question about Modperl::Util::exit

am 24.11.2008 22:12:09 von Perrin Harkins

On Mon, Nov 24, 2008 at 2:52 PM, Andr=E9 Warnier wrote:
> Can you do a "return" from a "require"-d file, and what does it do exactl=
y ?

Doing an exit from a file during a require() is not normally a
problem. Try it. It's kind of a bad programming practice, but Perl
will tolerate it. I'm guessing the problem here had to with
ModPerl::Util::exit which turns exit() into an exception rather than a
real exit. It's actually probably harmless, but return is a better
way to get out after sending a redirect.

- Perrin