home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / lib / YaST2 / servers_non_y2 / ag_background next >
Text File  |  2006-11-29  |  8KB  |  445 lines

  1. #!/usr/bin/perl -w
  2.  
  3. #
  4. # File:
  5. #   ag_background 
  6. #
  7. # Authors:
  8. #   Ladislav Slezak <lslezak@suse.cz>
  9. #
  10. # Description:
  11. #   Background process agent 
  12. #
  13. # $Id: ag_background 32980 2006-09-19 14:42:08Z mvidner $
  14. #
  15.  
  16.  
  17. use lib "/usr/lib/YaST2/agents_non_y2";
  18. use ycp;
  19. use strict;
  20.  
  21. use IPC::Open3;
  22.  
  23. # WNOHANG constant
  24. use POSIX ":sys_wait_h";
  25.  
  26. # child running flag
  27. our $child_running = 0;
  28. our $child_pid = -1;
  29.  
  30. # max. size of input buffer (number of lines), 0 = unlimited buffer
  31. my $buffer_size = 0;
  32.  
  33. # max. size of input buffer for error output, 0 = unlimited buffer
  34. my $buffer_size_err = 0;
  35.  
  36. # input line buffer for stdin - store read part of the line
  37. my $input1 = '';
  38.  
  39. # input line buffer for stderr - store read part of the line
  40. my $input2 = '';
  41.  
  42. # SIGCHLD handler
  43. sub Handler()
  44. {
  45.     if ($child_running)
  46.     {
  47.     waitpid(-1, WNOHANG);
  48.         $child_running = 0;
  49.     $child_pid = -1;
  50.  
  51.     # restore default buffer size for the next process
  52.     $buffer_size = 0;
  53.     $buffer_size_err = 0;
  54.     }
  55. }
  56.  
  57. # send SIGTERM (and then SIGKILL) to running subprocess
  58. sub KillSubprocess()
  59. {
  60.     if ($child_running)
  61.     {
  62.     kill(15, $child_pid);       # send SIGTERM
  63.  
  64.     sleep 1;                    # wait a little bit
  65.  
  66.     if ($child_running)         # child process still running?
  67.     {
  68.         kill(9, $child_pid);    # send SIGKILL
  69.     }
  70.     return 1;
  71.     }
  72.     else
  73.     {
  74.     return 0;
  75.     }
  76. }
  77.  
  78. sub ErrorPath($)
  79. {
  80.     my ($p) = @_;
  81.  
  82.     y2error("Bad path: $p");
  83.     ycp::Return("");
  84. }
  85.  
  86. sub ErrorCommand($)
  87. {
  88.     my ($c) = @_;
  89.     
  90.     y2error("Bad command: $c");
  91.     ycp::Return("");
  92. }
  93.  
  94. # install SIGCHLD handler
  95. $SIG{CHLD} = \&Handler;
  96.  
  97. my $stdin_set = my $pipe_set = '';
  98. vec($stdin_set, fileno(STDIN), 1) = 1;
  99.  
  100. my $watch = $stdin_set;    # watch STDIN
  101.  
  102. my $total_lines = 0;    # total number of lines from script
  103. my $newlines = 0;    # number of new lines since last reading output
  104. my $total_lines_err = 0;    # total number of lines from script
  105. my $newlines_err = 0;    # number of new lines since last reading output
  106.  
  107. my $store_out = 0;
  108. my $store_err = 0;
  109. my @out = ();        # new lines since last reading
  110. my @err = ();
  111.  
  112. my $pipe_defined = 0;
  113. my $pipe_defined_err = 0;
  114.  
  115. my $exit = 0;
  116.  
  117. $| = 1;
  118.  
  119. # main loop
  120. while (1) 
  121. {
  122.     # wait until some data can be read
  123.     select(my $watch_out = $watch, undef, undef, undef);
  124.  
  125.     # get status
  126.     my $stdin_avail = vec($watch_out, fileno(STDIN), 1);
  127.     my $output_avail = ($pipe_defined == 1) ? vec($watch_out, fileno(RD), 1) : 0;
  128.     my $err_avail = ($pipe_defined_err == 1) ? vec($watch_out, fileno(ERR), 1) : 0;
  129.     my $line;
  130.  
  131. #    print "stdin_avail: $stdin_avail  output_avail: $output_avail   err_avail: $err_avail\n";
  132.  
  133.     if ($stdin_avail)    # STDIN data available
  134.     {
  135.     $line = <STDIN>;
  136.  
  137.     if (!defined $line)
  138.     {
  139.         exit;
  140.     }
  141.     
  142.     chomp($line);
  143.  
  144.     my ($command, $path, $arg) = ycp::ParseCommand($line);
  145.  
  146.     if ($command eq 'Execute')
  147.     {
  148.         if ($path eq '.run' || $path eq '.run_output' || $path eq '.run_output_err')
  149.         {
  150.         # another process is already running
  151.         if ($child_running)
  152.         {
  153.             y2error('A subprocess is already running');
  154.             ycp::Return('false');
  155.         }
  156.         elsif (defined $arg)
  157.         {
  158.             $store_out = ($path eq '.run') ? 0 : 1;
  159.             $store_err = ($path eq '.run_output_err') ? 1 : 0;
  160.  
  161.             #reset counters
  162.             $total_lines = 0;
  163.             $newlines = 0;
  164.             @out = ();
  165.  
  166.             $total_lines_err = 0;
  167.             $newlines_err = 0;
  168.             @err = ();
  169.  
  170.             $child_running = 1;
  171.  
  172.             my $start_error = 0;
  173.  
  174.             # start subprocess
  175.             $child_pid = open3(*WRT, *RD, *ERR, "$arg")
  176.             or $start_error = 1;
  177.  
  178.             # if child is running or 
  179.             if ($start_error == 0)
  180.             {
  181.             $pipe_set = '';
  182.             vec($pipe_set, fileno(RD), 1) = 1;
  183.             vec($pipe_set, fileno(ERR), 1) = 1;
  184.  
  185.             $watch = $stdin_set | $pipe_set;       # watch both sets
  186.             $pipe_defined = 1;
  187.             $pipe_defined_err = 1;
  188.             }
  189.  
  190.             # use non-blocking write
  191.             use Fcntl;
  192.             fcntl(WRT, F_SETFL, O_NONBLOCK);
  193.  
  194.             ycp::Return(!$start_error ? 'true' : 'false');
  195.         }
  196.         }
  197.         elsif ($path eq '.kill')
  198.         {
  199.             ycp::Return(KillSubprocess() ? 'true' : 'false');
  200.         }
  201.         else
  202.         {
  203.         ErrorPath($path);
  204.         }
  205.     }
  206.     elsif ($command eq 'Read')
  207.     {
  208.         if ($path eq '.lines')
  209.         {
  210.         ycp::Return($total_lines);
  211.         }
  212.         if ($path eq '.pid')
  213.         {
  214.         ycp::Return($child_pid);
  215.         }
  216.         elsif ($path eq '.newlines')
  217.         {
  218.         ycp::Return($newlines);
  219.         }
  220.         elsif ($path eq '.newlines_err')
  221.         {
  222.         ycp::Return($newlines_err);
  223.         }
  224.         elsif ($path eq '.lines_err')
  225.         {
  226.         ycp::Return($total_lines_err);
  227.         }
  228.         elsif ($path eq '.store')
  229.         {
  230.         ycp::Return($store_out == 1 ? 'true' : 'false');
  231.         }
  232.         elsif ($path eq '.isrunning')
  233.         {
  234.         ycp::Return($child_running == 1 ? 'true' : 'false');
  235.         }
  236.         elsif ($path eq '.status')
  237.         {
  238.         ycp::Return($exit);
  239.         }
  240.         elsif ($path eq '.newout')
  241.         {
  242.         ycp::Return(\@out, 1);
  243.         
  244.         $newlines = 0;
  245.         @out = ();
  246.         }
  247.         elsif ($path eq '.newerr')
  248.         {
  249.         ycp::Return(\@err, 1);
  250.         
  251.         $newlines_err = 0;
  252.         @err = ();
  253.         }
  254.         elsif ($path eq '.buffer_out')
  255.         {
  256.         ycp::Return($input1, 1);
  257.         $input1 = '';
  258.         }
  259.         elsif ($path eq '.buffer_err')
  260.         {
  261.         ycp::Return($input2, 1);
  262.         $input2 = '';
  263.         }
  264.         elsif ($path eq '.output_open')
  265.         {
  266.         ycp::Return($pipe_defined ? 'true' : 'false');
  267.         }
  268.         elsif ($path eq '.output_open_err')
  269.         {
  270.         ycp::Return($pipe_defined_err ? 'true' : 'false');
  271.         }
  272.         elsif ($path eq '.buffer_size')
  273.         {
  274.         ycp::Return($buffer_size);
  275.         }
  276.         elsif ($path eq '.buffer_size_err')
  277.         {
  278.         ycp::Return($buffer_size_err);
  279.         }
  280.         else
  281.         {
  282.         ErrorPath($path);
  283.         }
  284.     }
  285.     elsif ($command eq 'result')
  286.     {
  287.         if ($child_running)
  288.         {
  289.         KillSubprocess();
  290.         y2warning('Subprocess was killed at \'result\' command');
  291.         }
  292.         exit(0);
  293.     }
  294.     elsif ($command eq 'Write')
  295.     {
  296.         if ($path eq '.buffer_size' || $path eq '.buffer_size_err')
  297.         {
  298.         if (defined $arg)
  299.         {
  300.             if ($arg >= 0)
  301.             {
  302.             if ($path eq '.buffer_size')
  303.             {
  304.                 $buffer_size = $arg;
  305.             }
  306.             else
  307.             {
  308.                 $buffer_size_err = $arg;
  309.             }
  310.  
  311.             ycp::Return('true');
  312.             }
  313.             else
  314.             {
  315.             ycp::Return('false');
  316.             }
  317.         }
  318.         else
  319.         {
  320.             ycp::Return('false');
  321.         }
  322.         }
  323.         elsif ($path eq '.stdin')
  324.         {
  325.         if ($child_running && defined $arg)
  326.         {
  327.             ycp::Return(syswrite(WRT, $arg));    # add new line char
  328.         }
  329.         else
  330.         {
  331.             ycp::Return(-1);
  332.         }
  333.         }
  334.         else
  335.         {
  336.         ErrorPath($path);
  337.         }
  338.     }
  339.     else
  340.     {
  341.         ErrorCommand($command);
  342.     }
  343.     }
  344.         
  345.     if ($output_avail)     # script output available
  346.     {
  347.     if (@out >= $buffer_size && $buffer_size > 0)
  348.     {
  349.         # too many lines in input buffer - wait for STDIN command
  350.         select(my $watch_out = $stdin_set, undef, undef, undef);
  351.     }
  352.     else
  353.     {
  354.         my $len = sysread(RD, $line, 64);
  355.         $input1 = "$input1$line";
  356.         
  357.         my $pos = index($input1, "\n");
  358.         
  359.         if ((defined $len) && $len == 0)
  360.         {
  361.         $pipe_defined = 0;
  362.         $watch = $stdin_set;    # now watch only STDIN
  363.  
  364.         # watch also stderr if it is still open
  365.         if ($pipe_defined_err)
  366.         {
  367.             vec($watch, fileno(ERR), 1) = 1;
  368.         }
  369.  
  370.         close(RD);
  371.         $exit = $? >> 8;    # high 8 bits are exit value
  372.         }
  373.         else
  374.         {
  375.         while ($pos >= 0)
  376.         {
  377.             $line = substr($input1, 0, $pos);
  378.             $input1 = substr($input1, $pos + 1);
  379.  
  380.             if ($store_out)
  381.             {
  382.             push(@out, $line);
  383.             }
  384.             
  385.             $newlines++;
  386.             $total_lines++;
  387.  
  388.             $pos = index($input1, "\n");
  389.         }
  390.         }
  391.     }
  392.     }
  393.  
  394.     if ($err_avail)     # error output available
  395.     {
  396.     if (@err >= $buffer_size_err && $buffer_size_err > 0)
  397.     {
  398.         # too many lines in input buffer - wait for STDIN command
  399.         select(my $watch_out = $stdin_set, undef, undef, undef);
  400.     }
  401.     else
  402.     {
  403.         my $len = sysread(ERR, $line, 64);
  404.         $input2 = "$input2$line";
  405.         
  406.         my $pos = index($input2, "\n");
  407.         
  408.         if ((defined $len) && $len == 0)
  409.         {
  410.         $pipe_defined_err = 0;
  411.         $watch = $stdin_set;    # now watch only STDIN
  412.  
  413.         # watch also stdout if it is still open
  414.         if ($pipe_defined)
  415.         {
  416.             vec($watch, fileno(RD), 1) = 1;
  417.         }
  418.  
  419.         close(ERR);
  420.         $exit = $? >> 8;    # high 8 bits are exit value
  421.         }
  422.         else
  423.         {
  424.         while ($pos >= 0)
  425.         {
  426.             $line = substr($input2, 0, $pos);
  427.             $input2 = substr($input2, $pos + 1);
  428.  
  429.             if ($store_err)
  430.             {
  431.             push(@err, $line);
  432.             }
  433.             
  434.             $newlines_err++;
  435.             $total_lines_err++;
  436.  
  437.             $pos = index($input2, "\n");
  438.         }
  439.         }
  440.     }
  441.     }
  442. }
  443.  
  444.  
  445.