Generally the idea is good. Some issues in the code, though.
Plus I wonder if the long stdout/err replacements could just be changed
to use dup2()... (There's more that could be improved, eg. csync+psync
could be one socketpair())

On Fri, Dec 15, 2017 at 09:13:33AM +0100, Thomas Lamprecht wrote:
> We forced line wise flushing of the workers STDOUT and STDERR to
> capture the final status (TASK OK/TASK ERROR).
> Thus, if the code executed in the worker wanted to flush explicitly,
> e.g., when the last output wasn't new line terminated but needed to
> reach the users eyes, the parent just ignored that.
> This leads to confusing results in CLI handlers using fork_workers.
> 
> So remove the buffering logic completely and introduce a separate
> pipe for sending the final status.
> 
> Signed-off-by: Thomas Lamprecht <t.lampre...@proxmox.com>
> ---
>  src/PVE/RESTEnvironment.pm | 56 
> +++++++++++++++++++++++++---------------------
>  1 file changed, 30 insertions(+), 26 deletions(-)
> 
> diff --git a/src/PVE/RESTEnvironment.pm b/src/PVE/RESTEnvironment.pm
> index 0ad6dba..3a2bf8e 100644
> --- a/src/PVE/RESTEnvironment.pm
> +++ b/src/PVE/RESTEnvironment.pm
> @@ -396,6 +396,7 @@ sub fork_worker {
>  
>      my @psync = POSIX::pipe();
>      my @csync = POSIX::pipe();
> +    my @ctrlfd = POSIX::pipe() if $sync;
>  
>      my $node = $self->{nodename};
>  
> @@ -427,9 +428,11 @@ sub fork_worker {
>       POSIX::setsid();
>  
>       POSIX::close ($psync[0]);
> +     POSIX::close ($ctrlfd[0]) if $sync;
>       POSIX::close ($csync[1]);
>  
>       $outfh = $sync ? $psync[1] : undef;
> +     my $resfh = $sync ? $ctrlfd[1] : undef;
>  
>       eval {
>           PVE::INotify::inotify_close();
> @@ -450,6 +453,7 @@ sub fork_worker {
>                   if !open(STDIN, "</dev/null");
>  
>               $outfh = PVE::Tools::upid_open($upid);
> +             $resfh = $outfh;
>           }
>  
>  
> @@ -499,10 +503,14 @@ sub fork_worker {
>           chomp $err;
>           $err =~ s/\n/ /mg;
>           syslog('err', $err);
> -         print STDERR "TASK ERROR: $err\n";
> +         my $msg = "TASK ERROR: $err\n";
> +         POSIX::write($resfh, $msg, length($msg));
> +         POSIX::close($resfh) if $sync;
>           POSIX::_exit(-1);
>       } else {
> -         print STDERR "TASK OK\n";
> +         my $msg = "TASK OK\n";
> +         POSIX::write($resfh, $msg, length($msg));
> +         POSIX::close($resfh) if $sync;
>           POSIX::_exit(0);
>       }
>       kill(-9, $$);
> @@ -511,6 +519,7 @@ sub fork_worker {
>      # parent
>  
>      POSIX::close ($psync[1]);
> +    POSIX::close ($ctrlfd[1]);

Needs to check $sync
    pvedaemon[1617]: Use of uninitialized value in subroutine entry at 
/usr/share/perl5/PVE/RESTEnvironment.pm line 522.

>      POSIX::close ($csync[0]);
>  
>      my $readbuf = '';
> @@ -562,7 +571,6 @@ sub fork_worker {
>  
>      if ($sync) {
>       my $count;
> -     my $outbuf = '';
>       my $int_count = 0;
>       eval {
>           local $SIG{INT} = local $SIG{QUIT} = local $SIG{TERM} = sub {
> @@ -591,38 +599,34 @@ sub fork_worker {
>                   }
>                   last if $count == 0; # eof
>  
> -                 $outbuf .= $readbuf;
> -                 while ($outbuf =~ 
> s/^(([^\010\r\n]*)(\r|\n|(\010)+|\r\n))//s) {
> -                     my $line = $1;
> -                     my $data = $2;
> -                     if ($data =~ m/^TASK OK$/) {
> -                         # skip
> -                     } elsif ($data =~ m/^TASK ERROR: (.+)$/) {
> -                         print STDERR "$1\n";
> -                     } else {
> -                         print $line;
> -                     }
> -                     if ($outfh) {
> -                         print $outfh $line;
> -                         $outfh->flush();
> -                     }
> -                 }
> +                 print $readbuf;
> +                 select->flush();
> +
> +                 # write always to task log (output and control messages)

But control messages aren't always read here as $ctrlfd isn't in
$select (and doesn't have to be since it's only filled when the client
exits, and read below.)

> +                 print $outfh $readbuf;
> +                 $outfh->flush();
>               } else {
>                   # some commands daemonize without closing stdout
>                   last if !PVE::ProcFSTools::check_process_running($cpid);
>               }
>           }
> +
> +         # get status (error or OK)
> +         POSIX::read($ctrlfd[0], $readbuf, 4096);

Again: $ctrlfd validity depends on $sync.

(We could always have a $ctrlfd thoguh?)

> +         if ($readbuf =~ m/^TASK OK\n?$/) {
> +             print $outfh $readbuf;
> +         } elsif ($readbuf =~ m/^TASK ERROR: (.*)\n?$/) {
> +             print STDERR "$1\n";
> +             print $outfh "\n$readbuf";
> +         } else {
> +             die "got unexpected control message: $readbuf\n";
> +         }
> +         $outfh->flush();
>       };
>       my $err = $@;
>  
>       POSIX::close($psync[0]);
> -
> -     if ($outbuf) { # just to be sure
> -         print $outbuf;
> -         if ($outfh) {
> -             print $outfh $outbuf;
> -         }
> -     }
> +     POSIX::close($ctrlfd[0]);
>  
>       if ($err) {
>           $err =~ s/\n/ /mg;
> -- 
> 2.11.0

_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to