http://www.perlmonks.org?node_id=11101695

emilbarton has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I have a CGI script calling an external client to gather data from a remote SQL server. Now sometimes for various reasons the process doesn't stop or worse, loops furiously, and I'd like the CGI to be able at least to record the pid and stop it after timeout. Below an example of this external call. I'm sure I'd be able to tinker some solution on my own, but I'm asking your wisdom for a good solution. Thank you in advance.

open MYPIPE, "-|", "$UNID_CLIC $Host $Port $Base $User $Passwd $Query" or ($RSLT = "Can't pop: $!"); while (<MYPIPE>) { $RSLT .= decode utf8=>$_; } close MYPIPE; my @rslt = split /$RES_STX/, $RSLT;

Replies are listed 'Best First'.
Re: How to handle an external process from CGI?
by haukex (Archbishop) on Jun 21, 2019 at 22:21 UTC
    I have a CGI script calling an external client to gather data from a remote SQL server.

    First of all, in general, this is a very dangerous thing to do, and especially so if you haven't properly sanitized all of those variables that you're passing to the external command. It's very easy to get the cleanup wrong. See also perlsec.

    I'm also a bit confused by the code style... if your open fails, it looks like the code will continue on and try to run the while loop, which seems kind of pointless, and should also generate warnings. I do hope you're using strict and warnings?

    Anyway, IPC::Run supports calling external commands with timeouts, so you might want to look at that. I'd also recommend splitting up the command into a list instead of a string (i.e. ($UNID_CLIC,$Host,$Port,...) instead of "$UNID_CLIC $Host $Port ...") because in that case, open* as well as IPC::Run won't pass it to the shell, and you have one less thing to worry about - I wrote about that some more (including some of the possible security risks) here.

    * Update: Except on Perl versions before 5.22 on Win32 (perldelta).

      Many thanks for your answer, I think there's no <MYPIPE> if open fails (I'll check it) but IPC::Run is probably exactly what I need. I'll read your advice in detail.
        I think there's no <MYPIPE> if open fails

        Yes and no: the first <MYPIPE> does return undef, so the while loop never runs, but you should also be getting a warning "readline() on closed filehandle", indicating that something is wrong. A failed open doesn't have to be fatal, there are plenty of ways to handle the control flow:

        if ( open my $fh, ... ) { while (<$fh>) { ... } close $fh; } else { warn "open: $!" } # - or - sub foo { open my $fh, ... or do { warn "open: $!"; return }; while (<$fh>) { ... } close $fh; } # - or - FILE: for my $file (@files) { open my $fh, ... or do { warn "skipping $file: $!"; next FILE }; while (<$fh>) { ... } close $fh; } # - or - use Try::Tiny; try { open my $fh, ... or die $!; while (<$fh>) { ... } close $fh; } catch { warn "error: $_"; }; # same thing, but without the module: eval { open my $fh, ... or die $!; ... ;1 } or do { warn "error: $@"; };

        The fourth and fifth examples are different in that they catch any fatal error inside the block, which may or may not be desired.

        Update: Added the third example, from this thread.

Re: How to handle an external process from CGI?
by Anonymous Monk on Jun 28, 2019 at 06:35 UTC