0

I have am using Proc::Daemon in a mod_perl script thusly:

$bindir, $ddir are executable/logfile locations and $jid is a unique per process identifier (to avoid having the same file open by multiple processes). $cmd is loaded with arbitrary perl scripts and arguments.

   my $daemon = Proc::Daemon->new (
            work_dir        => $bindir,
            child_STDOUT    => $ddir.'/'.$jid.'_stdout.log',
            child_STDERR    => $ddir.'/'.$jid.'_stderr.log',
            pid_file        => $ddir.'/'.$jid.'_pid.txt',
            exec_command => $cmd,
    );

    my $pid = $daemon->Init();

The above works fine when using Apache with cgi-script (no mod_perl). In the "$cmd" process, print and print STDERR log to the above defined log files.

When I run the above using mod_perl2, on Ubuntu Linux 14.04 LTS, using Apache2 The pid file gets written with the PID and the above log files are created, but nothing is ever written to the log files. I am able to open new files descriptors within $cmd and write to them, but under mod_perl, it is not sending output to the child_STDOUT and child_STDERR files.

I think I am missing something really obvious. Has anyone else seen this before, and or have any suggestions in getting this to work in mod_perl.

Additional info Using the mpm_prefork module in Apache

Relevant Apache Config

    <Files "*.pl">
            # SetHandler cgi-script # It works if I use cgi-script
            SetHandler perl-script
            PerlOptions -SetupEnv # Tried with and without this
            # PerlHandler ModPerl::RegistryPrefork # Using this made no difference
            PerlHandler ModPerl::Registry

    </Files>
Severun
  • 2,893
  • 1
  • 16
  • 22
  • It probably assumes `fileno(STDOUT)` is 2 (etc) and it isn't, or is otherwise overly special. – ikegami Sep 08 '16 at 04:09
  • I think that's a good hypothesis, but it says in the Proc::Daemon page, The second child closes all open file descriptors (unless you define dont_close_fh and/or dont_close_fd). The second child opens STDIN, STDOUT and STDERR to the location defined in the constructor (new). So it's supposed to close all file descriptors, then open new ones. Maybe mod_perl is somehow changing the global constants of STDIN/STDOUT?? – Severun Sep 08 '16 at 16:39
  • I wouldn't take that as a contradiction of what I said, especially since it talks in terms of STDOUT rather than fd 2. – ikegami Sep 08 '16 at 16:44
  • Yup not necessarily a contradiction, more additional info. The thing that is interesting is that Proc::Daemon is supposed to close all file handles in the child then open up new ones, which means it should not care if STDIN/OUT is 0/1 it should just close them all, then open new ones. Interesting additional note, if I use SetHandler modperl instead of SetHandler perl-script, print and print STDERR in the child go to the specified logfiles, but then prints in regular non-forked scripts no longer go out the connection to the browser. – Severun Sep 08 '16 at 18:29
  • One of the differences between the two is that perl-script does the following: STDIN and STDOUT get tied to the request object $r, which makes possible to read from STDIN and print directly to STDOUT via CORE::print(), instead of implicit calls like $r->puts(). So I guess the question is, how do I untie these for a child process but leave the config for the parent? I might be left with hand-rolling my own fork, but I'd rather not, I liked the whole easiness of Proc::Daemon. – Severun Sep 08 '16 at 18:31
  • Isn't there an `untie` function? But using that might have side effects. Might be better off using `local *STDOUT`; to create a new symbol. Then, you could even associate it with fd 2 – ikegami Sep 08 '16 at 18:41
  • Yup tried that too, but I could not get it to work. I also tried using Apache2::SubProcess, but couldn't get that to work either. What I wound up doing, that works, is documented below. It's so simple, it's a forehead slapper. – Severun Sep 08 '16 at 23:01

1 Answers1

0

OK so there were so many solutions that did not work This is what I wound up doing. I created a script called launchjob.pl:

#!/usr/bin/perl -w

use strict;
use warnings;

use POSIX 'setsid';
use Proc::Daemon;

my $bindir = $ARGV[0];
my $ddir = $ARGV[1];
my $jid = $ARGV[2];
my $cmd = $ARGV[3];

setsid or die "Cannot start a new session: $!";

my $daemon = Proc::Daemon->new (
                work_dir        => $bindir,
                child_STDOUT    => $ddir.'/'.$jid.'_stdout.log',
                child_STDERR    => $ddir.'/'.$jid.'_stderr.log',
                pid_file        => $ddir.'/'.$jid.'_pid.txt',
                exec_command => $cmd,
             );

my $pid = $daemon->Init();

exit 1;

I replaced the code, in the mainline, where I was calling Proc::Daemon with the following:

The following did not work. Proc::Daemon gave me an sh: No such file or directory error.

system('launchjob.pl', $bindir, $ddir, $jid, $cmd);

Instead I used the following which seems to run as expected.

use IPC::Run3; run3("launchjob.pl ".$bindir." ".$ddir." ".$jid." ".$cmd);

This seems to have fixed it right up.

Severun
  • 2,893
  • 1
  • 16
  • 22