Changeset 1262

Show
Ignore:
Timestamp:
11/29/06 17:53:40 (5 years ago)
Author:
ilmari
Message:

r9167@vesla: ilmari | 2006-08-06 03:57:36 +0100
Use IPC::Run to run plugins.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • people/ilmari/modularisation-branch/ChangeLog

    r1043 r1262  
    22 
    33  * Node: Add Munin::Node::Runner and convert munin-run to use it. 
     4  * Node: Use IPC::Run to run plugins. 
    45  * Plugins: Add module Munin::Plugin::SNMP and convert plugins to use it. 
    56  * Move Perl modules to {node,server}/lib/. 
  • people/ilmari/modularisation-branch/node/lib/Munin/Node/Runner.pm.in

    r1043 r1262  
    4747use base qw(Exporter); 
    4848use POSIX qw(setsid); 
     49use IPC::Run qw(run timeout new_chunker); 
    4950 
    5051use vars qw($VERSION $DEBUG $conffile $do_version $servicedir $sconfdir 
     
    185186 
    186187sub run_service { 
    187   my ($service,$command,$autoreap) = @_; 
    188   $command ||= ''; 
    189   my @lines = ();; 
    190   my $timed_out = 0; 
    191   if ($services{$service} and ($caddr eq "" or has_access ($service))) { 
    192     my $child = 0; 
     188    my ($service,$command,$autoreap) = @_; 
     189    $command ||= ''; 
     190    my @lines = ();; 
     191    my $timed_out = 0; 
     192    return "# Unknown service $service\n" 
     193      unless $services{$service} and ($caddr eq "" or has_access ($service)); 
     194 
     195    # Untaint $service, we know it's safe (it's in the %services hash) 
     196    $service =~ /^(.*)$/; 
     197    $service = $1; 
     198 
    193199    my $timeout = get_var (\%sconf, $service, 'timeout'); 
    194200    $timeout = $sconf{'timeout'} 
    195        unless defined $timeout and $timeout =~ /^\d+$/; 
     201      unless defined $timeout and $timeout =~ /^\d+$/; 
    196202 
    197203    # Untaint $command 
     
    199205        $command = $1; 
    200206    } else { 
    201         net_write("# Unknown command '$command'\n"); 
    202         return undef; 
    203     } 
    204  
    205     # Untaint $service, we know it's safe (it's in the %services hash) 
    206     $service =~ /^(.*)$/; 
    207     $service = $1; 
    208  
    209     if ($child = open (CHILD, "-|")) { 
    210       eval { 
    211           local $SIG{ALRM} = sub { $timed_out=1; die "$!\n"}; 
    212           alarm($timeout); 
    213           while(<CHILD>) { 
    214             push @lines,$_; 
    215           } 
    216       }; 
    217       if( $timed_out ) { 
    218           reap_children($child, "$service $command: $@"); 
    219           close (CHILD); 
    220           return (); 
    221       } 
    222       unless (close CHILD) 
    223       { 
    224           # If Net::Server::Fork is currently taking care of reaping, 
    225           # we get false errors. Filter them out. 
    226           if ($! and not $autoreap) 
    227           { 
    228               logger ("Error while executing plugin \"$service\": $!"); 
    229           } 
    230           else 
    231           { 
    232               logger ("Plugin \"$service\" exited with status $?. --@lines--"); 
    233           } 
    234       } 
    235     } 
    236     else { 
    237       if ($child == 0) { 
    238         # New process group... 
    239         POSIX::setsid(); 
    240         # Setting environment 
    241         $sconf{$service}{user}    = get_var (\%sconf, $service, 'user'); 
    242         $sconf{$service}{group}   = get_var (\%sconf, $service, 'group'); 
    243         $sconf{$service}{command} = get_var (\%sconf, $service, 'command'); 
    244         get_var (\%sconf, $service, 'env', \%{$sconf{$service}{env}}); 
    245  
    246         if ($< == 0) # If root... 
    247         { 
    248                 # Giving up gid egid uid euid 
    249                 my $u  = (defined $sconf{$service}{'user'}? 
    250                         $sconf{$service}{'user'}: 
    251                         $defuser); 
    252                 my $g  = $defgroup; 
    253                 my $gs = "$g $g" . 
    254                         (defined $sconf{$service}{'group'} ? 
    255                          " $sconf{$service}{group}" : ""); 
    256  
    257                 debug ("Want to run as euid/egid $u/$g\n"); 
    258  
    259                 $( = $g    unless $g == 0; 
    260                 $) = $gs   unless $g == 0; 
    261                 $< = $u    unless $u == 0; 
    262                 $> = $u    unless $u == 0; 
    263  
    264                 if ($> != $u or $g != (split (' ', $)))[0]) 
    265                 { 
    266 #                       net_write ("# Can't drop privileges. Bailing out. (wanted uid=", 
    267 #                           ($sconf{$service}{'user'} || $defuser), " gid=\"", 
    268 #                           $gs, "\"($g), got uid=$> gid=\"$)\"(", 
    269 #                           (split (' ', $)))[0], ").\n"); 
    270                         logger ("Plugin \"$service\" Can't drop privileges. ". 
    271                             "Bailing out. (wanted uid=". 
    272                             ($sconf{$service}{'user'} || $defuser). " gid=\"". 
    273                             $gs. "\"($g), got uid=$> gid=\"$)\"(". 
    274                             (split (' ', $)))[0]. ").\n"); 
    275                         exit 1; 
    276                 } 
    277         } 
    278         debug ("Running as uid/gid/euid/egid $</$(/$>/$)\n"); 
    279         if (!check_perms ("$servicedir/$service")) 
    280         { 
    281             net_write ("# Error: unsafe permissions. Bailing out."); 
    282             logger ("Error: unsafe permissions. Bailing out."); 
    283             exit 2; 
    284         } 
    285  
    286         # Setting environment... 
    287         if (exists $sconf{$service}{'env'} and 
    288                         defined $sconf{$service}{'env'}) 
    289         { 
    290             foreach my $key (keys %{$sconf{$service}{'env'}}) 
    291             { 
    292                 debug ("Setting environment $key=$sconf{$service}{env}{$key}\n"); 
    293                 $ENV{"$key"} = $sconf{$service}{'env'}{$key}; 
    294             } 
    295         } 
    296         if (exists $sconf{$service}{'command'} and 
    297                 defined $sconf{$service}{'command'}) 
    298         { 
    299             my @run = (); 
    300             foreach my $t (@{$sconf{$service}{'command'}}) 
    301             { 
    302                 if ($t =~ /^%c$/) 
    303                 { 
    304                     push (@run, "$servicedir/$service", $command); 
    305                 } 
    306                 else 
    307                 { 
    308                     push (@run, $t); 
    309                 } 
    310             } 
    311             debug ("About to run \"", join (' ', @run), "\"\n"); 
    312             exec (@run) if @run; 
    313         } 
    314         else 
    315         { 
    316             debug ("Execing...\n"); 
    317             exec ("$servicedir/$service", $command); 
    318         } 
    319       } 
    320       else { 
    321         net_write ("# Unable to fork.\n"); 
    322         logger ("Unable to fork."); 
    323       } 
    324     } 
    325     wait; 
    326     alarm(0); 
    327   } 
    328   else { 
    329     return "# Unknown service\n"; 
    330   } 
    331   return (@lines); 
     207        logger("Unknown command '$command'"); 
     208        return "# Unknown command '$command'\n"; 
     209    } 
     210 
     211    my @run; 
     212    if (defined $sconf{$service}{'command'}) { 
     213        @run = map { $_ eq '%c' ? "$servicedir/$service" : $_ } 
     214          @{$sconf{$service}{'command'}} 
     215    } else { 
     216        @run = ("$servicedir/$service", $command); 
     217    } 
     218 
     219    eval { 
     220        run(\@run, 
     221            '>', new_chunker("\n"), sub { push @lines, @_ }, 
     222            '2>', new_chunker("\n"), sub { logger("$service: $_[0]") }, 
     223            init => sub { setup_environment($service) }, 
     224            timeout($timeout, exception => "plugin timed out"), 
     225           ); 
     226    }; 
     227     
     228    if (@!) { 
     229        logger("Error running $service: @!"); 
     230        return "# Error running $service: @!\n"; 
     231    } 
     232    return @lines; 
     233
     234 
     235# Called from the plugin child before exec. 
     236sub setup_environment { 
     237    my $service = shift or die "internal error: service required"; 
     238    POSIX::setsid(); 
     239    # Setting environment 
     240    $sconf{$service}{user}    = get_var (\%sconf, $service, 'user'); 
     241    $sconf{$service}{group}   = get_var (\%sconf, $service, 'group'); 
     242    $sconf{$service}{command} = get_var (\%sconf, $service, 'command'); 
     243    get_var (\%sconf, $service, 'env', \%{$sconf{$service}{env}}); 
     244             
     245    if ($< == 0)                # If root... 
     246    { 
     247        # Giving up gid egid uid euid 
     248        my $u  = (defined $sconf{$service}{'user'}? 
     249                  $sconf{$service}{'user'}: 
     250                  $defuser); 
     251        my $g  = $defgroup; 
     252        my $gs = "$g $g" . 
     253          (defined $sconf{$service}{'group'} ? 
     254           " $sconf{$service}{group}" : ""); 
     255                   
     256        debug ("Want to run as euid/egid $u/$g\n"); 
     257 
     258        $( = $g    unless $g == 0; 
     259        $) = $gs   unless $g == 0; 
     260        $< = $u    unless $u == 0; 
     261        $> = $u    unless $u == 0; 
     262 
     263        if ($> != $u or $g != (split (' ', $)))[0]) { 
     264            #                   net_write ("# Can't drop privileges. Bailing out. (wanted uid=", 
     265            #                       ($sconf{$service}{'user'} || $defuser), " gid=\"", 
     266            #                       $gs, "\"($g), got uid=$> gid=\"$)\"(", 
     267            #                       (split (' ', $)))[0], ").\n"); 
     268            logger ("Plugin \"$service\" Can't drop privileges. ". 
     269                    "Bailing out. (wanted uid=". 
     270                    ($sconf{$service}{'user'} || $defuser). " gid=\"". 
     271                    $gs. "\"($g), got uid=$> gid=\"$)\"(". 
     272                    (split (' ', $)))[0]. ")."); 
     273            exit 1; 
     274        } 
     275    } 
     276    debug ("Running as uid/gid/euid/egid $</$(/$>/$)\n"); 
     277    if (!check_perms ("$servicedir/$service")) { 
     278        net_write ("# Error: unsafe permissions. Bailing out."); 
     279        logger ("Error: unsafe permissions. Bailing out."); 
     280        exit 2; 
     281    } 
     282 
     283    # Setting environment... 
     284    if (defined $sconf{$service}{'env'}) { 
     285        foreach my $key (keys %{$sconf{$service}{'env'}}) { 
     286            debug ("Setting environment $key=$sconf{$service}{env}{$key}\n"); 
     287            $ENV{"$key"} = $sconf{$service}{'env'}{$key}; 
     288        } 
     289    } 
    332290} 
    333291