You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Tosh Cooey <to...@1200group.com> on 2010/02/19 22:52:59 UTC

Apache2::SubProcess sucks

Seriously, you know what, the code below doesn't even work.

And if I get rid of the redirect and replace it with some nice pretty text:

print "Content-type: text/html\n\nTesting";

You know what?  The warning for 1 (from the 'for' loop) is printed and 
then it all stops, probably as the browser connection "closes".

Hiding it behind more forks made it work once but then never again.

So it is my belief that Apache2::SubProcess sucks dirty things, and that 
belief won't change until I see proof to the contrary.

On a related note, anyone have tips for avoiding zombies in CGI forking?

Tosh



Tosh Cooey wrote:
> Hi after much trial and all error I am seeing that the browser 
> connection closing is also stopping my subprocess.
> 
> The main ModPerl::Registry program looks like this:
> ### file.pl ###
> use Apache2::SubProcess ();
> use JSON();
> 
> &main(shift);
> 
> sub main {
>   my $r = shift;
>   ...
>   $r->spawn_proc_prog ('/web/html/file_fork.pl', \@argv);
>   return print $cgi->redirect('index.pl');
> }
> ###
> 
> And
> 
> ### file_fork.pl ###
> use JSON();
> use warnings;
> use POSIX 'setsid';
> chdir '/'                or die "Can't chdir to /: $!";
> open STDIN, '/dev/null'  or die "Can't read /dev/null: $!";
> open STDOUT, '+>>', '/tmp/debug.txt' or die "Can't write to 
> /tmp/debug.txt: $!";
> open STDERR, '>&STDOUT'  or die "Can't dup stdout: $!";
> setsid or die "Can't start a new session: $!";
> 
>   # run your code here or call exec to another program
> foreach my $num (1..10) {
>   warn $num;
>   sleep(1);
> }
> ###
> 
> When file.pl is executed it calls file_fork.pl and file_fork starts to 
> print 1, 2, 3 and then stops around 2 or 3 depending on how long my 
> browser is connected.
> 
> Are there any glaring errors that anyone sees?  It should work shouldn't 
> it, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 after 10 seconds...
> 
> Thank-you all!
> 
> Tosh
> 
> 

-- 
McIntosh Cooey - Twelve Hundred Group LLC - http://www.1200group.com/

Re: Apache2::SubProcess sucks

Posted by Scott Gifford <sg...@suspectclass.com>.
2010/2/20 Torsten Förtsch <to...@gmx.net>
[ ... ]

> - is there a portable way to get all open file descriptors of the current
> process? Under Linux one can readdir(/proc/self/fd). On Darwin I once
> simply
> closed all fds from 0 to 1000. Some systems have getdtablesize(2),
> sometimes
> it is getrlimit. Sometimes you can open /dev/something or
> /proc/self/something
> and read the descriptor table at a certain offset. What the module should
> do
> on Windows I simply don't know.
>

I think just closing stdin and stdout is likely to be enough, or maybe all
file descriptors from 0 to $^F .  The OS will close any others that have the
close-on-exec bit set, and the close-on-exec bit is set by default by Perl's
open function.  Anybody setting this to something different probably
understands the consequences.

----Scott.

Re: Apache2::SubProcess sucks

Posted by Jeff McCarrell <jm...@akamai.com>.
re: close all open file descriptors portably

well, if one is on a POSIX system, _SC_OPEN_MAX gives the max integer.
Then just close them all.
Here's my usual recipe for this:

    # close all open file descriptors
    my $max_fd = POSIX::sysconf(&POSIX::_SC_OPEN_MAX);
    $max_fd = ((! defined $max_fd) || $max_fd < 0) ? 64 : $max_fd;
    while (--$max_fd >= 0) {
        next if ($max_fd == fileno($pid_fh));
        POSIX::close($max_fd);
    }

    # now reopen std{in,out,err} on /dev/null
    open(STDIN,  "+>/dev/null");
    open(STDOUT, "+>&STDIN");
    open(STDERR, "+>&STDIN");

Proc::Daemon does the same thing...

HTH,
-- jeff


On 2/20/10 1:10 PM, "Torsten Förtsch" <to...@gmx.net> wrote:

> - is there a portable way to get all open file descriptors of the current
> process? Under Linux one can readdir(/proc/self/fd). On Darwin I once simply 


Re: Apache2::SubProcess sucks

Posted by Torsten Förtsch <to...@gmx.net>.
On Saturday 20 February 2010 21:24:31 Cosimo Streppone wrote:
> That's really valuable, thanks for sharing.
> However, a question:
> 
> is this something that should be included in Apache2::SubProcess
> to make it better? Or is this something that could be published
> another, separate, CPAN distribution?
> Or maybe it's already out there?
> 
Apache2::SubProcess' purpose is more for creating processes similar to CGI-
scripts. A CGI-script is killed by apache if it does not produce output for a 
certain period of time. Also, the process gets killed I think at the end of 
the request.

I think it could be an Apache2::SubProcess extension. That would mean adding 
the stuff to xs/Apache2/SubProcess/SubProcess_pm in the source tree. But there 
are a few things that are unsolved:

- is there a portable way to get all open file descriptors of the current 
process? Under Linux one can readdir(/proc/self/fd). On Darwin I once simply 
closed all fds from 0 to 1000. Some systems have getdtablesize(2), sometimes 
it is getrlimit. Sometimes you can open /dev/something or /proc/self/something 
and read the descriptor table at a certain offset. What the module should do 
on Windows I simply don't know.

If you can solve this problem with apr-functions I think the it can really be 
in Apache2::SubProcess.

- a way of really restoring the perl environment would be good, so that you 
really can fork off subroutines.

But the real reason why it is not shaped out as a separate module yet is that 
the code is really old and quite seldom needed. The main part was written 
around year 2000 for mp1. At that time I didn't know about POSIX::close and 
thus invented my own version like:

  sub SYS_close {6;}		# works on Linux and Darwin
  sub sysclose {
    syscall &SYS_close, shift()+0;
  }

In short, the code was ugly.

And because my solution is Linux-only.

Torsten

Re: Apache2::SubProcess sucks

Posted by Cosimo Streppone <co...@streppone.it>.
In data 20 febbraio 2010 alle ore 21:16:22, Torsten Förtsch  
<to...@gmx.net> ha scritto:

> On Saturday 20 February 2010 19:25:39 Tosh Cooey wrote:
>> I do enjoy the fact that nobody really seems to have a simple definitive
>> vanilla fork/spawn process down pat, it seems everyone does what I do,
>> trying this and that stumbling about until they come up with some
>> monstrosity like Torsen has that works under the sheer weight of tricks.
>>
> Well, if I'd said: "Tosh take ModPerl::Something from CPAN and call
> ModPerl::Something::spawn" you'd liked it (no matter what it does  
> internally), right?
>
> That sheer weight of tricks comes down to 3 points:
> [...]

That's really valuable, thanks for sharing.
However, a question:

is this something that should be included in Apache2::SubProcess
to make it better? Or is this something that could be published
another, separate, CPAN distribution?
Or maybe it's already out there?

-- 
Cosimo

Re: Apache2::SubProcess sucks

Posted by Tosh Cooey <to...@1200group.com>.
Hey Torsten, I hope you don't think I was disparaging ( 
http://dict.leo.org/?lang=en&lp=ende&search=disparaging ) your help, I 
certainly wasn't and I want to thank you for it.  I was just lamenting 
on a lack of a clear simple example to use as a starting point, much 
like almost every programming language starts out with "Hello World" in 
the first chapter.

So yes, install ModPerl::Something and then use 
ModPerl::Something::Spawn would be great, and it could even come with 
some caveats on how leaving it like that could cause problems, and then 
people could use that as a starting point.

Anyway, I'm just waiting for a couple days with my simple solution in 
production to see if it causes any problems and then I'll post it here 
and try to get something that could be such a starting point.

Tosh



Torsten Förtsch wrote:
> On Saturday 20 February 2010 19:25:39 Tosh Cooey wrote:
>> I do enjoy the fact that nobody really seems to have a simple definitive 
>> vanilla fork/spawn process down pat, it seems everyone does what I do, 
>> trying this and that stumbling about until they come up with some 
>> monstrosity like Torsen has that works under the sheer weight of tricks.
>>
> Well, if I'd said: "Tosh take ModPerl::Something from CPAN and call 
> ModPerl::Something::spawn" you'd liked it (no matter what it does internally), 
> right?
> 
> That sheer weight of tricks comes down to 3 points:
> 
> - you have to close the client socket in the child process or the browser will 
> wait for more data
> 
> - if your process should survive an apache restart it must create its own 
> process group, hence the setsid().
> 
> - accidental leaking file descriptors to child processes is generally 
> considered a security hole
> 
> This is what my function does, it closes all file descriptors except for 
> STDERR and calls setsid, nothing special. Perhaps it would be good to reopen 
> STDIN and STDOUT as /dev/null because many programs don't like closed 
> STDIN/STDOUT but again, no tricks.
> 
> A tricky part would be to really restore the normal Perl environment if you 
> don't want to exec() another program but simply to call a Perl function. 
> Reverting the effect of *CORE::GLOBAL::exit=\&ModPerl::Util::exit in an 
> already compiled function, that is tricky. Or reestablish the connection 
> between %ENV and C-level char *environ[].
> 
> Torsten
> 

-- 
McIntosh Cooey - Twelve Hundred Group LLC - http://www.1200group.com/

Re: Apache2::SubProcess sucks

Posted by Torsten Förtsch <to...@gmx.net>.
On Saturday 20 February 2010 19:25:39 Tosh Cooey wrote:
> I do enjoy the fact that nobody really seems to have a simple definitive 
> vanilla fork/spawn process down pat, it seems everyone does what I do, 
> trying this and that stumbling about until they come up with some 
> monstrosity like Torsen has that works under the sheer weight of tricks.
> 
Well, if I'd said: "Tosh take ModPerl::Something from CPAN and call 
ModPerl::Something::spawn" you'd liked it (no matter what it does internally), 
right?

That sheer weight of tricks comes down to 3 points:

- you have to close the client socket in the child process or the browser will 
wait for more data

- if your process should survive an apache restart it must create its own 
process group, hence the setsid().

- accidental leaking file descriptors to child processes is generally 
considered a security hole

This is what my function does, it closes all file descriptors except for 
STDERR and calls setsid, nothing special. Perhaps it would be good to reopen 
STDIN and STDOUT as /dev/null because many programs don't like closed 
STDIN/STDOUT but again, no tricks.

A tricky part would be to really restore the normal Perl environment if you 
don't want to exec() another program but simply to call a Perl function. 
Reverting the effect of *CORE::GLOBAL::exit=\&ModPerl::Util::exit in an 
already compiled function, that is tricky. Or reestablish the connection 
between %ENV and C-level char *environ[].

Torsten

Re: Apache2::SubProcess sucks

Posted by Tosh Cooey <to...@1200group.com>.
It does, but Proc::Daemon closes a billion file handles and sets new 
process IDs and forks and forks and maybe forks a couple more times for 
good measure... Fingers crossed!

I do enjoy the fact that nobody really seems to have a simple definitive 
vanilla fork/spawn process down pat, it seems everyone does what I do, 
trying this and that stumbling about until they come up with some 
monstrosity like Torsen has that works under the sheer weight of tricks.

Tosh


mackenna@animalhead.com wrote:
> On Feb 20, 2010, at 7:01 AM, Tosh Cooey wrote:
> 
>> Anyway, the solution, at least so far until I run into other problems, 
>> seems to be to just make a system() call and the called program uses 
>> Proc::Daemon and things *seem* to work fine in testing, we'll see when 
>> it hits production...
>>
>> Tosh
>>
> Doesn't a process that calls 'system' wait for the child
> process to complete?  If the parent process is an Apache
> child, is that in keeping with what you're trying to do?
> 
> cmac
> 
> 

-- 
McIntosh Cooey - Twelve Hundred Group LLC - http://www.1200group.com/

Re: Apache2::SubProcess sucks

Posted by ma...@animalhead.com.
On Feb 20, 2010, at 7:01 AM, Tosh Cooey wrote:

> Anyway, the solution, at least so far until I run into other  
> problems, seems to be to just make a system() call and the called  
> program uses Proc::Daemon and things *seem* to work fine in  
> testing, we'll see when it hits production...
>
> Tosh
>
Doesn't a process that calls 'system' wait for the child
process to complete?  If the parent process is an Apache
child, is that in keeping with what you're trying to do?

cmac


Re: Apache2::SubProcess sucks

Posted by Tosh Cooey <to...@1200group.com>.
Maybe it does or doesn't work under MP::R, the answer to that is perhaps 
individual.

Anyway, the solution, at least so far until I run into other problems, 
seems to be to just make a system() call and the called program uses 
Proc::Daemon and things *seem* to work fine in testing, we'll see when 
it hits production...

Tosh



Fred Moyer wrote:
> I haven't been following this thread too closely, but is there a
> reason you aren't using PerlHandlers instead of ModPerl::Registry?
> MP::R is meant mostly for CGI migration, so it is probably not widely
> tested with Apache2::SubProcess calls.  I'd suggest trying your
> external call under a PerlHandler instead and see if that works.
> 
> On Fri, Feb 19, 2010 at 1:52 PM, Tosh Cooey <to...@1200group.com> wrote:
>> Seriously, you know what, the code below doesn't even work.
>>
>> And if I get rid of the redirect and replace it with some nice pretty text:
>>
>> print "Content-type: text/html\n\nTesting";
>>
>> You know what?  The warning for 1 (from the 'for' loop) is printed and then
>> it all stops, probably as the browser connection "closes".
>>
>> Hiding it behind more forks made it work once but then never again.
>>
>> So it is my belief that Apache2::SubProcess sucks dirty things, and that
>> belief won't change until I see proof to the contrary.
>>
>> On a related note, anyone have tips for avoiding zombies in CGI forking?
>>
>> Tosh
>>
>>
>>
>> Tosh Cooey wrote:
>>> Hi after much trial and all error I am seeing that the browser connection
>>> closing is also stopping my subprocess.
>>>
>>> The main ModPerl::Registry program looks like this:
>>> ### file.pl ###
>>> use Apache2::SubProcess ();
>>> use JSON();
>>>
>>> &main(shift);
>>>
>>> sub main {
>>>  my $r = shift;
>>>  ...
>>>  $r->spawn_proc_prog ('/web/html/file_fork.pl', \@argv);
>>>  return print $cgi->redirect('index.pl');
>>> }
>>> ###
>>>
>>> And
>>>
>>> ### file_fork.pl ###
>>> use JSON();
>>> use warnings;
>>> use POSIX 'setsid';
>>> chdir '/'                or die "Can't chdir to /: $!";
>>> open STDIN, '/dev/null'  or die "Can't read /dev/null: $!";
>>> open STDOUT, '+>>', '/tmp/debug.txt' or die "Can't write to
>>> /tmp/debug.txt: $!";
>>> open STDERR, '>&STDOUT'  or die "Can't dup stdout: $!";
>>> setsid or die "Can't start a new session: $!";
>>>
>>>  # run your code here or call exec to another program
>>> foreach my $num (1..10) {
>>>  warn $num;
>>>  sleep(1);
>>> }
>>> ###
>>>
>>> When file.pl is executed it calls file_fork.pl and file_fork starts to
>>> print 1, 2, 3 and then stops around 2 or 3 depending on how long my browser
>>> is connected.
>>>
>>> Are there any glaring errors that anyone sees?  It should work shouldn't
>>> it, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 after 10 seconds...
>>>
>>> Thank-you all!
>>>
>>> Tosh
>>>
>>>
>> --
>> McIntosh Cooey - Twelve Hundred Group LLC - http://www.1200group.com/
>>
> 

-- 
McIntosh Cooey - Twelve Hundred Group LLC - http://www.1200group.com/

Re: Apache2::SubProcess sucks

Posted by Fred Moyer <fr...@redhotpenguin.com>.
I haven't been following this thread too closely, but is there a
reason you aren't using PerlHandlers instead of ModPerl::Registry?
MP::R is meant mostly for CGI migration, so it is probably not widely
tested with Apache2::SubProcess calls.  I'd suggest trying your
external call under a PerlHandler instead and see if that works.

On Fri, Feb 19, 2010 at 1:52 PM, Tosh Cooey <to...@1200group.com> wrote:
> Seriously, you know what, the code below doesn't even work.
>
> And if I get rid of the redirect and replace it with some nice pretty text:
>
> print "Content-type: text/html\n\nTesting";
>
> You know what?  The warning for 1 (from the 'for' loop) is printed and then
> it all stops, probably as the browser connection "closes".
>
> Hiding it behind more forks made it work once but then never again.
>
> So it is my belief that Apache2::SubProcess sucks dirty things, and that
> belief won't change until I see proof to the contrary.
>
> On a related note, anyone have tips for avoiding zombies in CGI forking?
>
> Tosh
>
>
>
> Tosh Cooey wrote:
>>
>> Hi after much trial and all error I am seeing that the browser connection
>> closing is also stopping my subprocess.
>>
>> The main ModPerl::Registry program looks like this:
>> ### file.pl ###
>> use Apache2::SubProcess ();
>> use JSON();
>>
>> &main(shift);
>>
>> sub main {
>>  my $r = shift;
>>  ...
>>  $r->spawn_proc_prog ('/web/html/file_fork.pl', \@argv);
>>  return print $cgi->redirect('index.pl');
>> }
>> ###
>>
>> And
>>
>> ### file_fork.pl ###
>> use JSON();
>> use warnings;
>> use POSIX 'setsid';
>> chdir '/'                or die "Can't chdir to /: $!";
>> open STDIN, '/dev/null'  or die "Can't read /dev/null: $!";
>> open STDOUT, '+>>', '/tmp/debug.txt' or die "Can't write to
>> /tmp/debug.txt: $!";
>> open STDERR, '>&STDOUT'  or die "Can't dup stdout: $!";
>> setsid or die "Can't start a new session: $!";
>>
>>  # run your code here or call exec to another program
>> foreach my $num (1..10) {
>>  warn $num;
>>  sleep(1);
>> }
>> ###
>>
>> When file.pl is executed it calls file_fork.pl and file_fork starts to
>> print 1, 2, 3 and then stops around 2 or 3 depending on how long my browser
>> is connected.
>>
>> Are there any glaring errors that anyone sees?  It should work shouldn't
>> it, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 after 10 seconds...
>>
>> Thank-you all!
>>
>> Tosh
>>
>>
>
> --
> McIntosh Cooey - Twelve Hundred Group LLC - http://www.1200group.com/
>