home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl -w
-
- #
- # File:
- # ag_background
- #
- # Authors:
- # Ladislav Slezak <lslezak@suse.cz>
- #
- # Description:
- # Background process agent
- #
- # $Id: ag_background 32980 2006-09-19 14:42:08Z mvidner $
- #
-
-
- use lib "/usr/lib/YaST2/agents_non_y2";
- use ycp;
- use strict;
-
- use IPC::Open3;
-
- # WNOHANG constant
- use POSIX ":sys_wait_h";
-
- # child running flag
- our $child_running = 0;
- our $child_pid = -1;
-
- # max. size of input buffer (number of lines), 0 = unlimited buffer
- my $buffer_size = 0;
-
- # max. size of input buffer for error output, 0 = unlimited buffer
- my $buffer_size_err = 0;
-
- # input line buffer for stdin - store read part of the line
- my $input1 = '';
-
- # input line buffer for stderr - store read part of the line
- my $input2 = '';
-
- # SIGCHLD handler
- sub Handler()
- {
- if ($child_running)
- {
- waitpid(-1, WNOHANG);
- $child_running = 0;
- $child_pid = -1;
-
- # restore default buffer size for the next process
- $buffer_size = 0;
- $buffer_size_err = 0;
- }
- }
-
- # send SIGTERM (and then SIGKILL) to running subprocess
- sub KillSubprocess()
- {
- if ($child_running)
- {
- kill(15, $child_pid); # send SIGTERM
-
- sleep 1; # wait a little bit
-
- if ($child_running) # child process still running?
- {
- kill(9, $child_pid); # send SIGKILL
- }
- return 1;
- }
- else
- {
- return 0;
- }
- }
-
- sub ErrorPath($)
- {
- my ($p) = @_;
-
- y2error("Bad path: $p");
- ycp::Return("");
- }
-
- sub ErrorCommand($)
- {
- my ($c) = @_;
-
- y2error("Bad command: $c");
- ycp::Return("");
- }
-
- # install SIGCHLD handler
- $SIG{CHLD} = \&Handler;
-
- my $stdin_set = my $pipe_set = '';
- vec($stdin_set, fileno(STDIN), 1) = 1;
-
- my $watch = $stdin_set; # watch STDIN
-
- my $total_lines = 0; # total number of lines from script
- my $newlines = 0; # number of new lines since last reading output
- my $total_lines_err = 0; # total number of lines from script
- my $newlines_err = 0; # number of new lines since last reading output
-
- my $store_out = 0;
- my $store_err = 0;
- my @out = (); # new lines since last reading
- my @err = ();
-
- my $pipe_defined = 0;
- my $pipe_defined_err = 0;
-
- my $exit = 0;
-
- $| = 1;
-
- # main loop
- while (1)
- {
- # wait until some data can be read
- select(my $watch_out = $watch, undef, undef, undef);
-
- # get status
- my $stdin_avail = vec($watch_out, fileno(STDIN), 1);
- my $output_avail = ($pipe_defined == 1) ? vec($watch_out, fileno(RD), 1) : 0;
- my $err_avail = ($pipe_defined_err == 1) ? vec($watch_out, fileno(ERR), 1) : 0;
- my $line;
-
- # print "stdin_avail: $stdin_avail output_avail: $output_avail err_avail: $err_avail\n";
-
- if ($stdin_avail) # STDIN data available
- {
- $line = <STDIN>;
-
- if (!defined $line)
- {
- exit;
- }
-
- chomp($line);
-
- my ($command, $path, $arg) = ycp::ParseCommand($line);
-
- if ($command eq 'Execute')
- {
- if ($path eq '.run' || $path eq '.run_output' || $path eq '.run_output_err')
- {
- # another process is already running
- if ($child_running)
- {
- y2error('A subprocess is already running');
- ycp::Return('false');
- }
- elsif (defined $arg)
- {
- $store_out = ($path eq '.run') ? 0 : 1;
- $store_err = ($path eq '.run_output_err') ? 1 : 0;
-
- #reset counters
- $total_lines = 0;
- $newlines = 0;
- @out = ();
-
- $total_lines_err = 0;
- $newlines_err = 0;
- @err = ();
-
- $child_running = 1;
-
- my $start_error = 0;
-
- # start subprocess
- $child_pid = open3(*WRT, *RD, *ERR, "$arg")
- or $start_error = 1;
-
- # if child is running or
- if ($start_error == 0)
- {
- $pipe_set = '';
- vec($pipe_set, fileno(RD), 1) = 1;
- vec($pipe_set, fileno(ERR), 1) = 1;
-
- $watch = $stdin_set | $pipe_set; # watch both sets
- $pipe_defined = 1;
- $pipe_defined_err = 1;
- }
-
- # use non-blocking write
- use Fcntl;
- fcntl(WRT, F_SETFL, O_NONBLOCK);
-
- ycp::Return(!$start_error ? 'true' : 'false');
- }
- }
- elsif ($path eq '.kill')
- {
- ycp::Return(KillSubprocess() ? 'true' : 'false');
- }
- else
- {
- ErrorPath($path);
- }
- }
- elsif ($command eq 'Read')
- {
- if ($path eq '.lines')
- {
- ycp::Return($total_lines);
- }
- if ($path eq '.pid')
- {
- ycp::Return($child_pid);
- }
- elsif ($path eq '.newlines')
- {
- ycp::Return($newlines);
- }
- elsif ($path eq '.newlines_err')
- {
- ycp::Return($newlines_err);
- }
- elsif ($path eq '.lines_err')
- {
- ycp::Return($total_lines_err);
- }
- elsif ($path eq '.store')
- {
- ycp::Return($store_out == 1 ? 'true' : 'false');
- }
- elsif ($path eq '.isrunning')
- {
- ycp::Return($child_running == 1 ? 'true' : 'false');
- }
- elsif ($path eq '.status')
- {
- ycp::Return($exit);
- }
- elsif ($path eq '.newout')
- {
- ycp::Return(\@out, 1);
-
- $newlines = 0;
- @out = ();
- }
- elsif ($path eq '.newerr')
- {
- ycp::Return(\@err, 1);
-
- $newlines_err = 0;
- @err = ();
- }
- elsif ($path eq '.buffer_out')
- {
- ycp::Return($input1, 1);
- $input1 = '';
- }
- elsif ($path eq '.buffer_err')
- {
- ycp::Return($input2, 1);
- $input2 = '';
- }
- elsif ($path eq '.output_open')
- {
- ycp::Return($pipe_defined ? 'true' : 'false');
- }
- elsif ($path eq '.output_open_err')
- {
- ycp::Return($pipe_defined_err ? 'true' : 'false');
- }
- elsif ($path eq '.buffer_size')
- {
- ycp::Return($buffer_size);
- }
- elsif ($path eq '.buffer_size_err')
- {
- ycp::Return($buffer_size_err);
- }
- else
- {
- ErrorPath($path);
- }
- }
- elsif ($command eq 'result')
- {
- if ($child_running)
- {
- KillSubprocess();
- y2warning('Subprocess was killed at \'result\' command');
- }
- exit(0);
- }
- elsif ($command eq 'Write')
- {
- if ($path eq '.buffer_size' || $path eq '.buffer_size_err')
- {
- if (defined $arg)
- {
- if ($arg >= 0)
- {
- if ($path eq '.buffer_size')
- {
- $buffer_size = $arg;
- }
- else
- {
- $buffer_size_err = $arg;
- }
-
- ycp::Return('true');
- }
- else
- {
- ycp::Return('false');
- }
- }
- else
- {
- ycp::Return('false');
- }
- }
- elsif ($path eq '.stdin')
- {
- if ($child_running && defined $arg)
- {
- ycp::Return(syswrite(WRT, $arg)); # add new line char
- }
- else
- {
- ycp::Return(-1);
- }
- }
- else
- {
- ErrorPath($path);
- }
- }
- else
- {
- ErrorCommand($command);
- }
- }
-
- if ($output_avail) # script output available
- {
- if (@out >= $buffer_size && $buffer_size > 0)
- {
- # too many lines in input buffer - wait for STDIN command
- select(my $watch_out = $stdin_set, undef, undef, undef);
- }
- else
- {
- my $len = sysread(RD, $line, 64);
- $input1 = "$input1$line";
-
- my $pos = index($input1, "\n");
-
- if ((defined $len) && $len == 0)
- {
- $pipe_defined = 0;
- $watch = $stdin_set; # now watch only STDIN
-
- # watch also stderr if it is still open
- if ($pipe_defined_err)
- {
- vec($watch, fileno(ERR), 1) = 1;
- }
-
- close(RD);
- $exit = $? >> 8; # high 8 bits are exit value
- }
- else
- {
- while ($pos >= 0)
- {
- $line = substr($input1, 0, $pos);
- $input1 = substr($input1, $pos + 1);
-
- if ($store_out)
- {
- push(@out, $line);
- }
-
- $newlines++;
- $total_lines++;
-
- $pos = index($input1, "\n");
- }
- }
- }
- }
-
- if ($err_avail) # error output available
- {
- if (@err >= $buffer_size_err && $buffer_size_err > 0)
- {
- # too many lines in input buffer - wait for STDIN command
- select(my $watch_out = $stdin_set, undef, undef, undef);
- }
- else
- {
- my $len = sysread(ERR, $line, 64);
- $input2 = "$input2$line";
-
- my $pos = index($input2, "\n");
-
- if ((defined $len) && $len == 0)
- {
- $pipe_defined_err = 0;
- $watch = $stdin_set; # now watch only STDIN
-
- # watch also stdout if it is still open
- if ($pipe_defined)
- {
- vec($watch, fileno(RD), 1) = 1;
- }
-
- close(ERR);
- $exit = $? >> 8; # high 8 bits are exit value
- }
- else
- {
- while ($pos >= 0)
- {
- $line = substr($input2, 0, $pos);
- $input2 = substr($input2, $pos + 1);
-
- if ($store_err)
- {
- push(@err, $line);
- }
-
- $newlines_err++;
- $total_lines_err++;
-
- $pos = index($input2, "\n");
- }
- }
- }
- }
- }
-
-
-