~ubuntu-branches/ubuntu/gutsy/munin/gutsy

« back to all changes in this revision

Viewing changes to node/munin-node.in

  • Committer: Bazaar Package Importer
  • Author(s): Tore Anderson
  • Date: 2004-05-21 20:51:19 UTC
  • Revision ID: james.westby@ubuntu.com-20040521205119-oz8bllbjp9hs80ig
Tags: upstream-0+1.0.0pre5
Import upstream version 0+1.0.0pre5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!@@PERL@@ -wT
 
2
#
 
3
# $Id: munin-node.in,v 1.12 2004/04/27 21:18:33 jimmyo Exp $
 
4
#
 
5
# $Log: munin-node.in,v $
 
6
# Revision 1.12  2004/04/27 21:18:33  jimmyo
 
7
# Fixed a problem in the node when running as a non-root user and using sudo to run the plugins (Deb#236694).
 
8
#
 
9
# Revision 1.11  2004/02/05 18:05:59  jimmyo
 
10
# Improved timeout-handling in node (Deb#224480).
 
11
#
 
12
# Revision 1.10  2004/02/05 17:35:41  jimmyo
 
13
# Made client timeouts configurable (not per plugin).
 
14
#
 
15
# Revision 1.9  2004/02/01 21:34:59  jimmyo
 
16
# Remove dependency on pgrep (use process groups instead). (SF#881049)
 
17
#
 
18
# Revision 1.8  2004/02/01 20:46:29  jimmyo
 
19
# Added better logging of plugin failures in the node. (SF#881045)
 
20
#
 
21
# Revision 1.7  2004/01/29 19:39:00  jimmyo
 
22
# Generic plugins now use printf instead of echo -n, as this is more portable (SF#885564)
 
23
#
 
24
# Revision 1.6  2004/01/29 18:07:52  jimmyo
 
25
# Bugfix from bug introduced 30 minutes ago
 
26
#
 
27
# Revision 1.5  2004/01/29 17:36:19  jimmyo
 
28
# Updated copyright information
 
29
#
 
30
# Revision 1.4  2004/01/29 16:56:54  jimmyo
 
31
# Fixed "group" bug. Added support for multiple and optional groups
 
32
#
 
33
# Revision 1.3  2004/01/17 22:04:29  toreanderson
 
34
# Change the name in process listing to contain only the path to the munin-node
 
35
# executable, without '/usr/bin/perl -wT' prepending it.
 
36
#
 
37
# Revision 1.2  2004/01/15 15:20:01  jimmyo
 
38
# Making things workable after name change. Upping for test verwion.
 
39
#
 
40
# Revision 1.1  2004/01/02 18:50:00  jimmyo
 
41
# Renamed occurrances of lrrd -> munin
 
42
#
 
43
# Revision 1.1.1.1  2004/01/02 15:18:06  jimmyo
 
44
# Import of LRRD CVS tree after renaming to Munin
 
45
#
 
46
# Revision 1.28  2003/12/18 18:51:37  jimmyo
 
47
# added configuration option "ignore_file", which takes regex for files to ignore (e.g. rpmnew/save) (Deb#224265).
 
48
#
 
49
# Revision 1.27  2003/12/18 17:58:18  jimmyo
 
50
# Do a fake clean of the environment because of the taint checking.
 
51
#
 
52
# Revision 1.26  2003/12/17 21:29:26  jimmyo
 
53
# Don\'t try to change uid/gid if not running as root. (Deb#224300)
 
54
#
 
55
# Revision 1.25  2003/12/10 15:30:02  jimmyo
 
56
# Set path before trying to get hostname
 
57
#
 
58
# Revision 1.24  2003/12/10 15:11:40  jimmyo
 
59
# A couple of bugfixes.
 
60
#
 
61
# Revision 1.23  2003/11/17 09:23:08  jimmyo
 
62
# Fix taint checking for getting hostname
 
63
#
 
64
# Revision 1.22  2003/11/17 09:20:09  jimmyo
 
65
# Fix for machines which don't have "host".
 
66
#
 
67
# Revision 1.21  2003/11/07 17:43:16  jimmyo
 
68
# Cleanups and log entries
 
69
#
 
70
#
 
71
 
 
72
package MyPackage;
 
73
 
 
74
use strict;
 
75
use vars qw(@ISA);
 
76
use Getopt::Long;
 
77
use Net::Server::Fork; # any personality will do
 
78
 
 
79
# "Clean" environment to disable taint-checking on the environment. We _know_
 
80
# that the environment is insecure, but we want to let admins shoot themselves
 
81
# in the foot with it, if they want to.
 
82
foreach my $key (keys %ENV)
 
83
{
 
84
        $ENV{$key} =~ /^(.*)$/;
 
85
        $ENV{$key} = $1;
 
86
}
 
87
 
 
88
$0 =~ /^(.*)$/; # for some strange reason won't "$0 = $0;" work.
 
89
$0 = $1;
 
90
 
 
91
@ISA = qw(Net::Server::Fork);
 
92
my @ORIG_ARGV = @ARGV;
 
93
my %services;
 
94
my %nodes;
 
95
my $servicedir="@@CONFDIR@@/plugins";
 
96
my $sconfdir="@@CONFDIR@@/plugin-conf.d";
 
97
my $conffile="@@CONFDIR@@/munin-node.conf";
 
98
my $FQDN="";
 
99
my $do_usage = 0;
 
100
my $DEBUG = 0;
 
101
my $do_version = 0;
 
102
my $VERSION="@@VERSION@@";
 
103
my $defuser = getpwnam ("nobody");
 
104
my $defgroup= getgrnam ("munin");
 
105
my $paranoia= 0;
 
106
my @ignores = ();
 
107
my %sconf   = ();
 
108
my $timeout = 10;
 
109
 
 
110
$do_usage=1  unless 
 
111
GetOptions ( "config=s"     => \$conffile,
 
112
             "debug!"       => \$DEBUG,
 
113
             "version!"     => \$do_version,
 
114
             "paranoia!"    => \$paranoia,
 
115
             "help"         => \$do_usage );
 
116
 
 
117
if ($do_usage)
 
118
{
 
119
    print "Usage: $0 [options]
 
120
 
 
121
Options:
 
122
    --help              View this message.
 
123
    --config <file>     Use <file> as configuration file. 
 
124
                        [/etc/munin/munin-node.conf]
 
125
    --[no]paranoia      Only run plugins owned by root. Check permissions.
 
126
                        [--noparanoia]
 
127
    --debug             View debug messages.
 
128
    --version           View version information.
 
129
 
 
130
";
 
131
    exit 0;
 
132
}
 
133
 
 
134
if ($do_version)
 
135
{
 
136
        print "munin-node (munin-node) version $VERSION.
 
137
Written by Audun Ytterdal, Jimmy Olsen, Tore Anderson / Linpro AS
 
138
 
 
139
Copyright (C) 2002-2004
 
140
This is free software released under the GNU Public License. There is NO
 
141
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
142
";
 
143
        exit 0;
 
144
}
 
145
 
 
146
# Reset ARGV (for HUPing)
 
147
@ARGV = @ORIG_ARGV;
 
148
 
 
149
# Check permissions of configuration
 
150
 
 
151
if (!&check_perms ($servicedir) or !&check_perms ($conffile))
 
152
{
 
153
        die "Fatal error. Bailing out.";
 
154
}
 
155
 
 
156
if (! -f $conffile) {
 
157
  print "ERROR: Cannot open $conffile\n";
 
158
  exit 1;
 
159
}
 
160
 
 
161
# A hack to overide the hostname if everyhing thing else fails
 
162
open FILE,$conffile or die "Cannot open $conffile\n";
 
163
while (<FILE>) {
 
164
  chomp;
 
165
  s/#.*//;                # no comments
 
166
  s/^\s+//;               # no leading white
 
167
  s/\s+$//;               # no trailing white
 
168
  next unless length;     # anything left?
 
169
  /(^\w*)\s+(.*)/;
 
170
  if (($1 eq "host_name" or $1 eq "hostname") and $2)
 
171
  {
 
172
      $FQDN=$2;
 
173
  }
 
174
  elsif (($1 eq "default_plugin_user" or $1 eq "default_client_user") and $2)
 
175
  {
 
176
      my $tmpid = $2;
 
177
      my $defuser = &get_uid ($tmpid);
 
178
      if (! defined ($defuser))
 
179
      {
 
180
          die "Default user defined in \"$conffile\" does not exist ($tmpid)";
 
181
      }
 
182
  }
 
183
  elsif (($1 eq "default_plugin_group" or $1 eq "default_client_group") and $2)
 
184
  {
 
185
      my $tmpid = $2;
 
186
      $defgroup = &get_gid ($tmpid);
 
187
      if (! defined ($defgroup))
 
188
      {
 
189
          die "Default group defined in \"$conffile\" does not exist ($tmpid)";
 
190
      }
 
191
  }
 
192
  elsif (($1 eq "paranoia") and defined $2)
 
193
  {
 
194
          if ("$2" eq "no" or "$2" eq "false" or "$2" eq "off" or "$2" eq "0")
 
195
          {
 
196
                  $paranoia = 0;
 
197
          }
 
198
          else
 
199
          {
 
200
                  $paranoia = 1;
 
201
          }
 
202
  }
 
203
  elsif (($1 eq "ignore_file") and defined $2)
 
204
  {
 
205
      push @ignores, $2;
 
206
  }
 
207
  elsif (($1 eq "timeout") and defined $2)
 
208
  {
 
209
      $timeout = $2;
 
210
  }
 
211
}
 
212
 
 
213
$FQDN ||= &get_fq_hostname;
 
214
 
 
215
$ENV{FQDN}=$FQDN;
 
216
 
 
217
MyPackage->run(conf_file => $conffile,
 
218
               pid_file => "@@STATEDIR@@/munin-node.pid");
 
219
exit;
 
220
 
 
221
 
 
222
 
 
223
 
 
224
### over-ridden subs below
 
225
 
 
226
sub pre_loop_hook {
 
227
    my $self = shift;
 
228
        print STDERR "In pre_loop_hook.\n" if $DEBUG;
 
229
    &load_services;
 
230
    $self->SUPER::pre_loop_hook;
 
231
}
 
232
 
 
233
sub show_version {
 
234
  print "munins node on $FQDN version: $VERSION\n"
 
235
}
 
236
 
 
237
sub show_nodes {
 
238
  for my $node (keys %nodes) {
 
239
    print "$node\n";
 
240
  }
 
241
  print ".\n";
 
242
}
 
243
 
 
244
sub get_fq_hostname {
 
245
    my $hostname;
 
246
    eval {
 
247
        require Net::Domain;
 
248
        $hostname = Net::Domain::hostfqdn();
 
249
    };
 
250
    return $hostname if $hostname;
 
251
 
 
252
    $hostname = `hostname`;  # Fall$
 
253
    chomp($hostname);
 
254
    $hostname =~ s/\s//g;
 
255
    return $hostname;
 
256
}
 
257
 
 
258
sub load_services {
 
259
    if (opendir (DIR,$sconfdir))
 
260
    {
 
261
FILES:
 
262
        for my $file (grep { -f "$sconfdir/$_" } readdir (DIR))
 
263
        {
 
264
            next if $file =~ m/^\./; # Hidden files
 
265
            next if $file !~ m/^([-\w.]+)$/; # Skip if any weird chars
 
266
            $file = $1; # Not tainted anymore.
 
267
            foreach my $regex (@ignores)
 
268
            {
 
269
                next FILES if $file =~ /$regex/;
 
270
            }
 
271
            if (!&load_auth_file ($sconfdir, $file, \%sconf))
 
272
            {
 
273
                warn "Something wicked happened while reading \"$servicedir/$file\". Check the previous log lines for spesifics.";
 
274
            }
 
275
        }
 
276
        closedir (DIR);
 
277
    }
 
278
    
 
279
    opendir (DIR,$servicedir) || die "Cannot open plugindir: $servicedir $!";
 
280
FILES:
 
281
    for my $file (grep { -f "$servicedir/$_" } readdir(DIR)) {
 
282
        next if $file =~ m/^\./; # Hidden files
 
283
        next if $file =~ m/.conf$/; # Config files
 
284
        next if $file !~ m/^([-\w.]+)$/; # Skip if any weird chars
 
285
        $file = $1; # Not tainted anymore.
 
286
        foreach my $regex (@ignores)
 
287
        {
 
288
            next FILES if $file =~ /$regex/;
 
289
        }
 
290
        next if (! -x "$servicedir/$file"); # File not executeable
 
291
        print "file: '$file'\n" if $DEBUG;
 
292
        $services{$file}=1;
 
293
        my @rows = &run_service($file,"config", 1);
 
294
        my $node = &get_var (\%sconf, $file, 'host_name') || $FQDN;
 
295
 
 
296
        for my $row (@rows) {
 
297
          print "row: $row\n" if $DEBUG;
 
298
          if ($row =~ m/^host_name (.+)$/) {
 
299
            print "Found host_name, using it\n" if $DEBUG;
 
300
            $node = $1;
 
301
          }
 
302
        }
 
303
        $nodes{$node}{$file}=1;
 
304
    }
 
305
    closedir DIR;
 
306
}
 
307
 
 
308
sub print_service {
 
309
  my (@lines) = @_;
 
310
  for my $line (@lines) {
 
311
    print "$line\n";
 
312
  }
 
313
  print ".\n";
 
314
}
 
315
 
 
316
sub list_services {
 
317
    my $node = $_[0] || $FQDN;
 
318
    print join " ", keys %{$nodes{$node}};
 
319
    print "\n";
 
320
}
 
321
 
 
322
sub logger {
 
323
    my $text  = shift;
 
324
    my @date  = localtime (time);
 
325
 
 
326
    printf STDERR ("%d/%02d/%02d-%02d:%02d:%02d %s\n", $date[5]+1900, $date[4]+1, 
 
327
            $date[3], $date[2], $date[1], $date[0], $text);
 
328
}
 
329
 
 
330
sub reap_children {
 
331
  my $child = shift;
 
332
  my $text = shift;
 
333
  return unless $child;
 
334
  if (kill (0, $child)) 
 
335
    { 
 
336
      print ("# timeout pid $child - killing..."); 
 
337
      logger ("Plugin timeout ($timeout seconds): $text (pid $child)");
 
338
      kill (-1, $child); sleep 2; 
 
339
      kill (-9, $child);
 
340
      print ("done\n");
 
341
    } 
 
342
}
 
343
 
 
344
sub run_service {
 
345
  my ($service,$command,$autoreap) = @_;
 
346
  $command ||="";
 
347
  my @lines = ();;
 
348
  my $timed_out = 0;
 
349
  if ($services{$service}) {
 
350
    my $child = 0;
 
351
 
 
352
    if ($child = open (CHILD, "-|")) {
 
353
      eval {
 
354
          local $SIG{ALRM} = sub { $timed_out=1; die "$!\n"};
 
355
          alarm($timeout);
 
356
          while(<CHILD>) {
 
357
            push @lines,$_;
 
358
          }
 
359
      };
 
360
      if( $timed_out ) {
 
361
          reap_children($child, "$service $command: $@");
 
362
          close (CHILD);
 
363
          return ();
 
364
      }
 
365
      unless (close CHILD)
 
366
      {
 
367
          if ($!)
 
368
          {
 
369
              # If Net::Server::Fork is currently taking care of reaping,
 
370
              # we get false errors. Filter them out.
 
371
              unless (defined $autoreap and $autoreap) 
 
372
              {
 
373
                  logger ("Error while executing plugin \"$service\": $!");
 
374
              }
 
375
          }
 
376
          else
 
377
          {
 
378
              logger ("Plugin \"$service\" exited with status $?. --@lines--");
 
379
          }
 
380
      }
 
381
    }
 
382
    else {
 
383
      if ($child == 0) {
 
384
        # New process group...
 
385
        POSIX::setsid();
 
386
        # Setting environment
 
387
        $sconf{$service}{user}    = &get_var (\%sconf, $service, 'user');
 
388
        $sconf{$service}{group}   = &get_var (\%sconf, $service, 'group');
 
389
        $sconf{$service}{command} = &get_var (\%sconf, $service, 'command');
 
390
        &get_var (\%sconf, $service, 'env', \%{$sconf{$service}{env}});
 
391
        
 
392
        if ($< == 0) # If root...
 
393
        {
 
394
                # Giving up gid egid uid euid
 
395
                my $u  = (defined $sconf{$service}{'user'}?
 
396
                        $sconf{$service}{'user'}:
 
397
                        $defuser);
 
398
                my $g  = $defgroup;
 
399
                my $gs = "$g $g" .
 
400
                        ($sconf{$service}{'group'}?" $sconf{$service}{group}":"");
 
401
 
 
402
                print "# Want to run as euid/egid $u/$g\n" if $DEBUG;
 
403
 
 
404
                $( = $g    unless $g == 0;
 
405
                $) = $gs   unless $g == 0;
 
406
                $< = $u    unless $u == 0;
 
407
                $> = $u    unless $u == 0;
 
408
 
 
409
                if ($> != $u or $g != (split (' ', $)))[0])
 
410
                {
 
411
                        print "# Can't drop privileges. Bailing out. (wanted uid=",
 
412
                            ($sconf{$service}{'user'} || $defuser), " gid=\"",
 
413
                            $gs, "\"($g), got uid=$> gid=\"$)\"(", 
 
414
                            (split (' ', $)))[0], ").\n";
 
415
                        logger ("Plugin \"$service\" Can't drop privileges. ".
 
416
                            "Bailing out. (wanted uid=".
 
417
                            ($sconf{$service}{'user'} || $defuser). " gid=\"".
 
418
                            $gs. "\"($g), got uid=$> gid=\"$)\"(". 
 
419
                            (split (' ', $)))[0]. ").\n");
 
420
                        exit 1;
 
421
                }
 
422
        }
 
423
        print "# Running as uid/gid/euid/egid $</$(/$>/$)\n" if $DEBUG;
 
424
        if (!&check_perms ("$servicedir/$service"))
 
425
        {
 
426
            print "# Error: unsafe permissions. Bailing out.";
 
427
            logger ("Error: unsafe permissions. Bailing out.");
 
428
            exit 2;
 
429
        }
 
430
 
 
431
        # Setting environment...
 
432
        if (exists $sconf{$service}{'env'} and
 
433
                        defined $sconf{$service}{'env'})
 
434
        {
 
435
            foreach my $key (keys %{$sconf{$service}{'env'}})
 
436
            {
 
437
                print "# Setting environment $key=$sconf{$service}{env}{$key}\n" if $DEBUG;
 
438
                $ENV{"$key"} = $sconf{$service}{'env'}{$key};
 
439
            }
 
440
        }
 
441
        if (exists $sconf{$service}{'command'} and 
 
442
                defined $sconf{$service}{'command'})
 
443
        {
 
444
            my @run = ();
 
445
            foreach my $t (@{$sconf{$service}{'command'}})
 
446
            {
 
447
                if ($t =~ /^%c$/)
 
448
                {
 
449
                    push (@run, "$servicedir/$service", $command);
 
450
                }
 
451
                else
 
452
                {
 
453
                    push (@run, $t);
 
454
                }
 
455
            }
 
456
            print STDERR "# About to run \"", join (' ', @run), "\"\n" if $DEBUG;
 
457
            print "# About to run \"", join (' ', @run), "\"\n" if $DEBUG;
 
458
            exec (@run) if @run;
 
459
        }
 
460
        else
 
461
        {
 
462
            print "# Execing...\n" if $DEBUG;
 
463
            exec ("$servicedir/$service", $command);
 
464
        }
 
465
      }
 
466
      else {
 
467
        print "# Unable to fork.\n";
 
468
        logger ("Unable to fork.");
 
469
      }
 
470
    }
 
471
    wait;
 
472
    alarm(0);
 
473
  }
 
474
  else {
 
475
    print "# Unknown service\n";
 
476
  }
 
477
  chomp @lines;
 
478
  return (@lines);
 
479
}
 
480
 
 
481
sub process_request {
 
482
  my $self = shift;
 
483
  print "# munin node at $FQDN\n";
 
484
  local $SIG{ALRM} = sub { logger ("Connection timed out."); die "timeout" };
 
485
  alarm($timeout);
 
486
  while( <STDIN> ){
 
487
    alarm($timeout);
 
488
    chomp;
 
489
    if (m/^list\s*([0-9a-zA-Z\.\-]+)?/) {
 
490
      &list_services($1);
 
491
    }
 
492
    elsif (/^quit/ || /^\./) {
 
493
      exit 1;
 
494
    }
 
495
    elsif (/^version/) {
 
496
      &show_version;
 
497
        }
 
498
    elsif (/^nodes/) {
 
499
      &show_nodes;
 
500
    }
 
501
    elsif (/^fetch\s?(\S*)/) {
 
502
      print_service (&run_service($1)) 
 
503
    }
 
504
    elsif (/^config\s?(\S*)/) {
 
505
      print_service (&run_service($1,"config"));
 
506
    } else  {
 
507
      print "# Unknown command. Try list, nodes, config, fetch, version or quit\n";
 
508
    }
 
509
  }
 
510
}
 
511
 
 
512
sub get_uid
 
513
{
 
514
    my $user = shift;
 
515
    return undef if (!defined $user);
 
516
 
 
517
    if ($user !~ /\d/)
 
518
    {
 
519
        $user = getpwnam ($user);
 
520
    }
 
521
    return $user;
 
522
}
 
523
 
 
524
sub get_gid
 
525
{
 
526
    my $group = shift;
 
527
    return undef if (!defined $group);
 
528
 
 
529
    if ($group !~ /\d/)
 
530
    {
 
531
        $group = getgrnam ($group);
 
532
    }
 
533
    return $group;
 
534
}
 
535
 
 
536
sub load_auth_file 
 
537
{
 
538
    my ($dir, $file, $sconf) = @_;
 
539
    my $service = $file;
 
540
 
 
541
    if (!defined $dir or !defined $file or !defined $sconf)
 
542
    {
 
543
        return undef;
 
544
    }
 
545
 
 
546
    return undef if (!&check_perms ($dir));
 
547
    return undef if (!&check_perms ("$dir/$file"));
 
548
 
 
549
    if (!open (IN, "$dir/$file"))
 
550
    {
 
551
        warn "Could not open file \"$dir/$file\" for reading ($!), skipping plugin\n";
 
552
        return undef;
 
553
    }
 
554
    while (<IN>)
 
555
    {
 
556
        chomp;
 
557
        s/#.*$//;
 
558
        next unless /\S/;
 
559
        s/\s+$//g;
 
560
        print "DEBUG: Config: $service: $_\n" if $DEBUG;
 
561
        if (/^\s*\[([^\]]+)\]\s*$/)
 
562
        {
 
563
            $service = $1;
 
564
        }
 
565
        elsif (/^\s*user\s+(\S+)\s*$/)
 
566
        {
 
567
            my $tmpid = $1;
 
568
            $sconf->{$service}{'user'} = &get_uid ($tmpid);
 
569
            print "DEBUG: Config: $service->uid = ", $sconf->{$service}{'user'}, "\n" if $DEBUG;
 
570
            if (!defined $sconf->{$service}{'user'})
 
571
            {
 
572
                warn "User \"$tmpid\" in configuration file \"$dir/$file\" nonexistant. Skipping plugin.";
 
573
                return undef;
 
574
            }
 
575
        }
 
576
        elsif (/^\s*group\s+(.+)\s*$/)
 
577
        {
 
578
            my $tmpid = $1;
 
579
            foreach my $group (split /\s*,\s*/, $tmpid)
 
580
            {
 
581
                my $optional = 0;
 
582
 
 
583
                if ($group =~ /^\(([^)]+)\)$/)
 
584
                {
 
585
                    $optional = 1;
 
586
                    $group = $1;
 
587
                }
 
588
 
 
589
                my $g = &get_gid ($group);
 
590
                print "DEBUG: Config: $service->gid = ", $sconf->{$service}{'group'}, "\n" if $DEBUG;
 
591
                if (!defined $g and !$optional)
 
592
                {
 
593
                    warn "Group \"$group\" in configuration file \"$dir/$file\" nonexistant. Skipping plugin.";
 
594
                    return undef;
 
595
                }
 
596
                elsif (!defined $g and $optional)
 
597
                {
 
598
                    print "DEBUG: Skipping \"$group\" (optional).\n" if $DEBUG;
 
599
                    next;
 
600
                }
 
601
                if (!defined $sconf->{$service}{'group'})
 
602
                {
 
603
                    $sconf->{$service}{'group'} = $g;
 
604
                }
 
605
                else
 
606
                {
 
607
                    $sconf->{$service}{'group'} .= " $g";
 
608
                }
 
609
            }
 
610
        }
 
611
        elsif (/^\s*command\s+(.+)\s*$/)
 
612
        {
 
613
            @{$sconf->{$service}{'command'}} = split (/\s+/, $1);
 
614
        }
 
615
        elsif (/^\s*host_name\s+(.+)\s*$/)
 
616
        {
 
617
            $sconf->{$service}{'host_name'} = $1;
 
618
        }
 
619
        elsif (/^\s*env\s+([^=\s]+)\s*=\s*(.+)$/)
 
620
        {
 
621
            $sconf->{$service}{'env'}{$1} = $2;
 
622
            print "Saving $service->env->$1 = $2...\n" if $DEBUG;
 
623
            warn "Warning: Deprecated format in \"$dir/$file\" under \"[$service]\" (\"env $1=$2\" should be rewritten to \"env.$1 $2\").";
 
624
        }
 
625
        elsif (/^\s*env\.(\S+)\s+(.+)$/)
 
626
        {
 
627
            $sconf->{$service}{'env'}{$1} = $2;
 
628
            print "Saving $service->env->$1 = $2...\n" if $DEBUG;
 
629
        }
 
630
        elsif (/^\s*(\w+)\s+(.+)$/)
 
631
        {
 
632
            $sconf->{$service}{'env'}{"lrrd_$1"} = $2;
 
633
            print "Saving $service->env->lrrd_$1 = $2...\n" if $DEBUG;
 
634
            warn "Warning: Deprecated format in \"$dir/$file\" under \"[$service]\" (\"$1 $2\" should be rewritten to \"env lrrd_$1=$2\").";
 
635
        }
 
636
        elsif (/\S/)
 
637
        {
 
638
            warn "Warning: Unknown config option in \"$dir/$file\" under \"[$service]\": $_";
 
639
        }
 
640
 
 
641
    }
 
642
    close (IN);
 
643
 
 
644
    return 1;
 
645
}
 
646
 
 
647
sub check_perms
 
648
{
 
649
    my $target = shift;
 
650
    my @stat;
 
651
    return undef if (!defined $target);
 
652
        return 1 if (!$paranoia);
 
653
 
 
654
    if (! -e "$target")
 
655
    {
 
656
        warn "Failed to check permissions on nonexistant target: \"$target\"";
 
657
        return undef;
 
658
    }
 
659
 
 
660
    @stat = stat ($target);
 
661
    if (!$stat[4] == 0 or
 
662
        ($stat[5] != 0 and $stat[2] & 00020) or
 
663
        ($stat[2] & 00002))
 
664
    {
 
665
        warn "Warning: \"$target\" has dangerous permissions (", sprintf ("%04o", $stat[2] & 07777), ").";
 
666
        return 0;
 
667
    }
 
668
 
 
669
    if (-f "$target") # Check dir as well
 
670
    {
 
671
        (my $dirname = $target) =~ s/[^\/]+$//;
 
672
        return &check_perms ($dirname);
 
673
    }
 
674
 
 
675
    return 1;
 
676
}
 
677
 
 
678
sub get_var
 
679
{
 
680
    my $sconf   = shift;
 
681
    my $name    = shift;
 
682
    my $var     = shift;
 
683
    my $env     = shift;
 
684
 
 
685
    if ($var eq 'env' and !defined $env)
 
686
    {
 
687
        %{$env} = ();
 
688
    }
 
689
    
 
690
    if ($var ne 'env' and exists $sconf->{$name}{$var})
 
691
    {
 
692
        return $sconf->{$name}{$var};
 
693
    }
 
694
    # Deciding environment
 
695
    foreach my $wildservice (grep (/\*$/, reverse sort keys %{$sconf}))
 
696
    {
 
697
        (my $tmpservice = $wildservice) =~ s/\*$//;
 
698
        next unless ($name =~ /^$tmpservice/);
 
699
        print "Checking $wildservice...\n" if $DEBUG;
 
700
 
 
701
        if ($var eq 'env')
 
702
        {
 
703
            if (exists $sconf->{$wildservice}{'env'})
 
704
            {
 
705
                foreach my $key (keys %{$sconf->{$wildservice}{'env'}})
 
706
                {
 
707
                    if (! exists $sconf->{$name}{'env'}{$key})
 
708
                    {
 
709
                        $sconf->{$name}{'env'}{$key} = $sconf->{$wildservice}{'env'}{$key};
 
710
                        print "Saving $wildservice->$key\n" if $DEBUG;
 
711
                    }
 
712
                }
 
713
            }
 
714
        }
 
715
        else
 
716
        {
 
717
            if (! exists $sconf->{$name}{$var} and
 
718
                    exists $sconf->{$wildservice}{$var})
 
719
            {
 
720
                return ($sconf->{$wildservice}{$var});
 
721
            }
 
722
        }
 
723
    }
 
724
    return $env;
 
725
}
 
726
 
 
727
1;
 
728
 
 
729
=head1 NAME
 
730
 
 
731
munin-node - A daemon to gather information in cooperation with the main
 
732
Munin program
 
733
 
 
734
=head1 SYNOPSIS
 
735
 
 
736
munin-node [--options]
 
737
 
 
738
=head1 OPTIONS
 
739
 
 
740
=over 5
 
741
 
 
742
=item B<< --config <configfile> >>
 
743
 
 
744
Use E<lt>fileE<gt> as configuration file. [/etc/munin/munin-node.conf]
 
745
 
 
746
=item B< --[no]paranoia >
 
747
 
 
748
Only run plugins owned by root. Check permissions as well. [--noparanoia]
 
749
 
 
750
=item B< --help >
 
751
 
 
752
View this help message.
 
753
 
 
754
=item B< --debug >
 
755
 
 
756
View debug messages.
 
757
 
 
758
=back
 
759
 
 
760
=head1 DESCRIPTION
 
761
 
 
762
Munin's node is a daemon that Munin connects to fetch data. This data is
 
763
stored in .rrd-files, and later graphed and htmlified. It's designed to
 
764
let it be very easy to graph new datasources.
 
765
 
 
766
Munin-node is a small perlscript listening to port 4949 using
 
767
Net::Server. It reads all the plugins in /etc/munin/plugins/ on startup.
 
768
The node accepts the following commands:
 
769
 
 
770
=over 5
 
771
 
 
772
=item B<< list [node] >>
 
773
 
 
774
list available plugins for host. If no hostname is specified, list plugins
 
775
on host running munin-node
 
776
 
 
777
=item B<< nodes >>
 
778
 
 
779
List nodes that has plugins in this munin-node.
 
780
 
 
781
=item B<< config <plugin> >>
 
782
 
 
783
output plugin configuration
 
784
 
 
785
=item B<< fetch <plugin> >>
 
786
 
 
787
output plugin values
 
788
 
 
789
=item B<< version >>
 
790
 
 
791
Print versionstring
 
792
 
 
793
=item B<< quit >>
 
794
 
 
795
disconnect
 
796
 
 
797
=back
 
798
 
 
799
=head2 Plugins
 
800
 
 
801
These plugins can be in you language of choice: bash, perl, python, C. The
 
802
plugins can be run in two modes: with and without the "config"-parameter. When
 
803
run with "config" as parameter, the plugin should output the configuration of
 
804
the graph. When run without parameters, the plugin should output just values
 
805
 
 
806
        # /etc/munin/plugins/load config
 
807
        host_name 
 
808
        graph_title Load average
 
809
        graph_args --base 1000 -l 0
 
810
        graph_vlabel load
 
811
        load.label load
 
812
        load.draw LINE2
 
813
        load.warning 10
 
814
        load.critical 120
 
815
 
 
816
        # /etc/munin/plugins/load
 
817
        load.value 0.43
 
818
 
 
819
For more information, see the documentation section at L<http://munin.sf.net/>.
 
820
 
 
821
=head1 FILES
 
822
 
 
823
        @@CONFDIR@@/munin-node.conf
 
824
        @@CONFDIR@@/plugins/*
 
825
        @@CONFDIR@@/plugin-conf.d/*
 
826
        @@STATEDIR@@/munin-node.pid
 
827
        @@LOGDIR@@/munin-node
 
828
 
 
829
=head1 VERSION
 
830
 
 
831
This is munin-node v@@VERSION@@
 
832
 
 
833
=head1 AUTHORS
 
834
 
 
835
Audun Ytterdal, Jimmy Olsen, and Tore Anderson.
 
836
 
 
837
=head1 BUGS
 
838
 
 
839
munin-node does, as of now, not check the syntax of the configuration file.
 
840
 
 
841
Please report other bugs in the bug tracker at L<http://munin.sf.net/>.
 
842
 
 
843
=head1 COPYRIGHT
 
844
 
 
845
Copyright � 2002 Audun Ytterdal, Jimmy Olsen, and Tore Anderson / Linpro AS.
 
846
 
 
847
This is free software; see the source for copying conditions. There is
 
848
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
 
849
PURPOSE.
 
850
 
 
851
This program is released under the GNU General Public License
 
852
 
 
853
=cut
 
854
 
 
855
# vim:syntax=perl