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

« back to all changes in this revision

Viewing changes to server/munin-update.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
#!/usr/bin/perl
 
2
#
 
3
# Script to update the RRD-files with current information.
 
4
#
 
5
# $Id: munin-update.in,v 1.8 2004/05/09 21:11:16 jimmyo Exp $
 
6
#
 
7
# $Log: munin-update.in,v $
 
8
# Revision 1.8  2004/05/09 21:11:16  jimmyo
 
9
# New plugin (pm3users) and a bunch of patches from Jacques Caruso.
 
10
#
 
11
# Revision 1.7  2004/02/10 19:27:02  jimmyo
 
12
# Munin-update now properly ignores nodes with "update no".
 
13
#
 
14
# Revision 1.6  2004/01/30 14:28:19  jimmyo
 
15
# More timeouts in munin-update (Deb#222674).
 
16
#
 
17
# Revision 1.5  2004/01/29 18:19:58  jimmyo
 
18
# Made Munin compatible with perl 5.005_03 (patch by Lupe Christoph) (SF#884622)
 
19
#
 
20
# Revision 1.4  2004/01/29 17:40:10  jimmyo
 
21
# Fixed pod typos patched by Lupe Christoph (SF#884092)
 
22
#
 
23
# Revision 1.3  2004/01/29 17:34:06  jimmyo
 
24
# Updated copyright information
 
25
#
 
26
# Revision 1.2  2004/01/15 15:20:01  jimmyo
 
27
# Making things workable after name change. Upping for test verwion.
 
28
#
 
29
# Revision 1.1  2004/01/02 18:50:01  jimmyo
 
30
# Renamed occurrances of lrrd -> munin
 
31
#
 
32
# Revision 1.1.1.1  2004/01/02 15:18:08  jimmyo
 
33
# Import of LRRD CVS tree after renaming to Munin
 
34
#
 
35
# Revision 1.35  2003/12/19 20:53:17  jimmyo
 
36
# ChangeLog
 
37
#
 
38
# Revision 1.34  2003/12/12 21:40:34  jimmyo
 
39
# Minor bugfix
 
40
#
 
41
# Revision 1.33  2003/12/12 19:23:59  jimmyo
 
42
# Fix bug with timeout handling of children.
 
43
#
 
44
# Revision 1.32  2003/12/06 20:21:53  jimmyo
 
45
# Removed forgotten debug info
 
46
#
 
47
# Revision 1.31  2003/12/06 20:09:17  jimmyo
 
48
# Better handling of dying children and timeouts. (Deb#222674)
 
49
#
 
50
# Revision 1.30  2003/12/06 19:12:57  jimmyo
 
51
# Added max_processes config variable. Also, removed zombie-generation code. :-P
 
52
#
 
53
# Revision 1.29  2003/11/15 11:10:29  jimmyo
 
54
# Various fixes
 
55
#
 
56
# Revision 1.28  2003/11/12 12:04:45  jimmyo
 
57
# Make sure extinfo comes accross
 
58
#
 
59
# Revision 1.27  2003/11/07 23:39:09  jimmyo
 
60
# Filter out illegal chars
 
61
#
 
62
# Revision 1.26  2003/11/07 22:10:13  jimmyo
 
63
# Changed use_default_name -> use_node_name. Better name.
 
64
#
 
65
# Revision 1.25  2003/11/07 21:02:24  jimmyo
 
66
# Bugfix when a new node is unreachable.
 
67
#
 
68
# Revision 1.24  2003/11/07 20:46:12  jimmyo
 
69
# Only require Config::General if using old config format.
 
70
#
 
71
# Revision 1.23  2003/11/07 17:43:16  jimmyo
 
72
# Cleanups and log entries
 
73
#
 
74
#
 
75
 
 
76
$|=1;
 
77
 
 
78
use strict;
 
79
use IO::Socket;
 
80
use Munin;
 
81
use Time::HiRes;
 
82
use RRDs;
 
83
use Getopt::Long;
 
84
use POSIX qw(strftime);
 
85
use POSIX ":sys_wait_h";
 
86
use Storable qw(fd_retrieve nstore_fd);
 
87
 
 
88
my $DEBUG=0;
 
89
my $VERSION="@@VERSION@@";
 
90
my $serversocket  = "munin-server-socket.$$";
 
91
my $conffile = "@@CONFDIR@@/munin.conf";
 
92
my $force_root = 0;
 
93
my $do_usage = 0;
 
94
my @limit_hosts = ();
 
95
my @limit_services = ();
 
96
my $update_time= Time::HiRes::time;
 
97
my $do_fork = 1;
 
98
my $do_version = 0;
 
99
my $timeout = 180;
 
100
my $cli_do_fork;
 
101
my $cli_timeout;
 
102
my $print_stdout = 0;
 
103
 
 
104
# Get options
 
105
$do_usage=1  unless 
 
106
GetOptions ( "host=s"       => \@limit_hosts,
 
107
             "force-root!"  => \$force_root,
 
108
             "service=s"    => \@limit_services,
 
109
             "config=s"     => \$conffile,
 
110
             "debug!"       => \$DEBUG,
 
111
             "version!"     => \$do_version,
 
112
             "fork!"        => \$cli_do_fork,
 
113
             "timeout=i"    => \$cli_timeout,
 
114
             "stdout!"      => \$print_stdout,
 
115
             "help"         => \$do_usage );
 
116
 
 
117
if ($do_usage)
 
118
{
 
119
    print "Usage: $0 [options]
 
120
 
 
121
Options:
 
122
    --[no]force-root    Force running, even as root. [--noforce-root]
 
123
    --version           View version information.
 
124
    --help              View this message.
 
125
    --service <service> Limit graphed services to <service>. Multiple --service
 
126
                        options may be supplied.
 
127
    --host <host>       Limit graphed hosts to <host>. Multiple --host options
 
128
                        may be supplied.
 
129
    --config <file>     Use <file> as configuration file. 
 
130
                        [@@CONFDIR@@/munin.conf]
 
131
    --[no]debug         View debug messages. [--nodebug]
 
132
    --[no]fork          Don't fork one instance for each host. [--fork]
 
133
    --[no]stdout        Print log messages to stdout as well. [--nostdout]
 
134
    --timeout=<seconds> TCP timeout when talking to clients. [$timeout]
 
135
 
 
136
";
 
137
    exit 0;
 
138
}
 
139
 
 
140
if ($do_version)
 
141
{
 
142
    print "munin-update version $VERSION.\n";
 
143
    print "Written by Audun Ytterdal, Jimmy Olsen, Tore Anderson / Linpro AS\n";
 
144
    print "\n";
 
145
    print "Copyright (C) 2002-2004\n";
 
146
    print "This is free software released under the GNU Public License. There is NO\n";
 
147
    print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
 
148
    exit 0;
 
149
}
 
150
 
 
151
if ($> == 0 and !$force_root)
 
152
{
 
153
    print "You are running this program as root, which is neither smart nor necessary.
 
154
If you really want to run it as root, use the --force-root option. Else, run
 
155
it as the user \"munin\". Aborting.\n\n";
 
156
    exit (1);
 
157
}
 
158
 
 
159
my $config= &munin_readconfig ($conffile);
 
160
 
 
161
my $oldconfig;
 
162
 
 
163
if (-e "$config->{dbdir}/datafile") {
 
164
  $oldconfig= &munin_readconfig("$config->{dbdir}/datafile", 1, 1);
 
165
}
 
166
 
 
167
# CLI parameters override the configuration file.
 
168
if (defined $cli_timeout)
 
169
{
 
170
    $timeout = $cli_timeout;
 
171
}
 
172
elsif (exists $config->{'timeout'})
 
173
{
 
174
    $timeout = $config->{'timeout'};
 
175
}
 
176
if (defined $cli_do_fork)
 
177
{
 
178
    $do_fork = $cli_do_fork;
 
179
}
 
180
elsif (exists $config->{'fork'})
 
181
{
 
182
    $do_fork = ($config->{'fork'} =~ /yes/i ? 1 : 0);
 
183
}
 
184
 
 
185
if (! -d $config->{rundir})
 
186
{
 
187
        mkdir ($config->{rundir}, 0700);
 
188
}
 
189
munin_runlock("$config->{rundir}/munin-update.lock");
 
190
 
 
191
open (LOG,">>$config->{logdir}/munin-update.log") or die "Unable to open $config->{logdir}/munin-update.log\n";
 
192
 
 
193
open (STATS,">$config->{dbdir}/munin-update.stats.tmp") or logger("Unable to open $config->{datadir}/munin-update.stats");
 
194
 
 
195
my %children = ();
 
196
my @queue = ();
 
197
my $bad_procs = 0;
 
198
my $uaddr;
 
199
if ($do_fork)
 
200
{
 
201
# Set up socket
 
202
    $uaddr =  sockaddr_un("$config->{rundir}/$serversocket");
 
203
    socket (Server, PF_UNIX, SOCK_STREAM, 0)     || die "socket: $!";
 
204
    unlink ("$config->{'rundir'}/$serversocket");
 
205
    bind   (Server, $uaddr);
 
206
    chmod (0700, "$config->{rundir}/$serversocket");
 
207
    listen (Server, SOMAXCONN);
 
208
}
 
209
 
 
210
logger("Starting munin-update"); 
 
211
 
 
212
 
 
213
for my $key (keys %{$config->{domain}}) {
 
214
  my $domain_time = Time::HiRes::time;
 
215
  logger ("Processing domain: $key");
 
216
  process_domain($key);
 
217
  $domain_time = sprintf ("%.2f",(Time::HiRes::time - $domain_time));
 
218
  print STATS "UD|$key|$domain_time\n"; 
 
219
  logger ("Processed domain: $key ($domain_time sec)");
 
220
}
 
221
 
 
222
#sub REAPER {
 
223
#   my $child;
 
224
#   my $waitedpid;
 
225
#   while (($waitedpid = waitpid(-1,WNOHANG)) > 0) {
 
226
#       logger ("reaped $waitedpid" . ($? ? " with exit $?" : ''));
 
227
#   }
 
228
#   $SIG{CHLD} = \&REAPER;  # loathe sysV
 
229
#}
 
230
#
 
231
#$SIG{CHLD} = \&REAPER;
 
232
 
 
233
if ($do_fork)
 
234
{
 
235
    $SIG{ALRM} = sub { die "Timed out waiting for children. $!\n"};
 
236
    alarm (240);
 
237
 
 
238
    for (;(%children - $bad_procs > 0);)
 
239
    {
 
240
            eval {
 
241
                $SIG{ALRM} = sub {
 
242
                    foreach my $key (keys %children)
 
243
                    {
 
244
                        if (waitpid ($key, WNOHANG) != 0)
 
245
                        {
 
246
                            my $domain  = $children{$key}->[0];
 
247
                            my $name    = $children{$key}->[1];
 
248
                            my $oldnode = $children{$key}->[3];
 
249
 
 
250
                            logger ("Child has unexpectedly died: $domain -> $name.");
 
251
                            delete $children{$key};
 
252
                            use_old_config ($domain, $name, $oldnode);
 
253
                        }
 
254
                    }
 
255
                    die;
 
256
                };
 
257
 
 
258
                alarm (10);
 
259
                accept (Client, Server);
 
260
            };
 
261
            alarm (0);
 
262
            if ($@)
 
263
            {
 
264
                if (@queue and defined $config->{max_processes} and
 
265
                        $config->{max_processes})
 
266
                {
 
267
                    while (keys %children < ($config->{max_processes}-1-$bad_procs))
 
268
                    {
 
269
                        my $args = pop @queue;
 
270
                        logger ("de-queueing new connection: $args->[1]");
 
271
                        do_node($args->[0], $args->[1], $args->[2], $args->[3]);
 
272
                    }
 
273
                }
 
274
                next;
 
275
            }
 
276
            close STDIN;
 
277
            open (STDIN,  "<&Client")  || die "can't dup client to stdin";
 
278
            
 
279
            my $pid;
 
280
            my $name;
 
281
            my $domain;
 
282
            my $tmpref;
 
283
                eval {
 
284
                        $tmpref = fd_retrieve (\*STDIN);
 
285
                };
 
286
                if ($@)
 
287
                {
 
288
                        $bad_procs++;
 
289
                        logger ("Error communicating with process: $@");
 
290
                }
 
291
                else
 
292
                {
 
293
                        ($pid, $domain, $name) = ($tmpref->[0], $tmpref->[1], $tmpref->[2]);
 
294
                        logger ("connection from $domain -> $name ($pid)");
 
295
 
 
296
                        eval {
 
297
                                $config->{domain}->{$domain}->{node}->{$name} = fd_retrieve (\*STDIN);
 
298
                        };
 
299
                        if ($@)
 
300
                        {
 
301
                                logger ("Error during fd_retrieve of config: $@");
 
302
 
 
303
                                my $domain  = $children{$pid}->[0];
 
304
                                my $name    = $children{$pid}->[1];
 
305
                                my $oldnode = $children{$pid}->[3];
 
306
 
 
307
                                use_old_config ($domain, $name, $oldnode);
 
308
                        }
 
309
                        delete $children{$pid};
 
310
                        waitpid ($pid, 0);
 
311
                        logger ("connection from $domain -> $name ($pid) closed");
 
312
                }
 
313
            if (@queue and defined $config->{max_processes} and
 
314
                    $config->{max_processes} and
 
315
                    keys %children < ($config->{max_processes}-1-$bad_procs))
 
316
            {
 
317
                my $args = pop @queue;
 
318
                logger ("de-queueing new connection: $args->[1]");
 
319
                do_node($args->[0], $args->[1], $args->[2], $args->[3]);
 
320
                close (Client);
 
321
            }
 
322
    }
 
323
    alarm (0);
 
324
}
 
325
 
 
326
if ($bad_procs) # Use old configuration for killed children
 
327
{
 
328
        foreach my $key (keys %children)
 
329
        {
 
330
                my $domain  = $children{$key}->[0];
 
331
                my $name    = $children{$key}->[1];
 
332
                my $node    = $children{$key}->[2];
 
333
                my $oldnode = $children{$key}->[3];
 
334
 
 
335
                use_old_config ($domain, $name, $oldnode);
 
336
                logger ("Attempting to use old configuration for $domain -> $name.");
 
337
        }
 
338
}
 
339
 
 
340
unlink ("$config->{rundir}/$serversocket");
 
341
 
 
342
my $overwrite = &munin_readconfig($conffile);
 
343
$config = &munin_overwrite($config,$overwrite);
 
344
 
 
345
if (&munin_getlock("$config->{rundir}/munin-datafile.lock"))
 
346
{
 
347
    &munin_writeconfig("$config->{dbdir}/datafile",$config);
 
348
}
 
349
else
 
350
{
 
351
    warn "Could not create lockfile \"$config->{rundir}/munin-update.lock\"";
 
352
}
 
353
 
 
354
$update_time = sprintf ("%.2f",(Time::HiRes::time - $update_time));
 
355
print STATS "UT|$update_time\n";
 
356
close (STATS);
 
357
rename ("$config->{dbdir}/munin-update.stats.tmp", "$config->{dbdir}/munin-update.stats");
 
358
 
 
359
logger("Munin-update finished ($update_time sec)");
 
360
close (LOG);
 
361
 
 
362
sub process_domain {
 
363
  my ($domain) = @_;
 
364
  for my $key ( keys %{$config->{domain}->{$domain}->{node}}) {
 
365
    if (@limit_hosts and !grep (/^$key$/, @limit_hosts))
 
366
    {
 
367
        print "Skipping host \"$key\" - not in hostlist\n" if $DEBUG;
 
368
        next;
 
369
    }
 
370
    if (defined $config->{max_processes} and $config->{max_processes} and 
 
371
            ($config->{max_processes}-1-$bad_procs) < keys %children)
 
372
    {
 
373
        push (@queue, [$domain, $key, $config->{domain}->{$domain}->{node}->{$key},$oldconfig->{domain}->{$domain}->{node}->{$key}]);
 
374
    }
 
375
    else
 
376
    {
 
377
        do_node($domain,$key ,$config->{domain}->{$domain}->{node}->{$key},$oldconfig->{domain}->{$domain}->{node}->{$key});
 
378
    }
 
379
  }
 
380
}
 
381
 
 
382
sub do_node {
 
383
  my ($domain, $name, $config, $oldconfig) = @_;
 
384
  my $node_time = Time::HiRes::time;
 
385
  logger("Processing node: $name");
 
386
  process_node($domain,$name ,$config,$oldconfig);
 
387
  $node_time = sprintf ("%.2f",(Time::HiRes::time - $node_time));
 
388
  print STATS "UN|$domain|$name|$node_time\n"; 
 
389
  logger ("Processed node: $name ($node_time sec)");
 
390
}
 
391
 
 
392
sub process_node {
 
393
  my ($domain,$name,$node,$oldnode) = @_;
 
394
  return if (exists ($node->{fetch_data}) and !$node->{fetch_data});
 
395
  return if (exists ($node->{update}) and $node->{update} ne "yes");
 
396
 
 
397
  # Then we fork...
 
398
  if ($do_fork)
 
399
  {
 
400
      my $pid = fork;
 
401
      if (!defined($pid)) 
 
402
      { # Something went wrong
 
403
              warn "cannot fork: $!"; 
 
404
              return; 
 
405
      } elsif ($pid) 
 
406
      { # I'm the parent
 
407
              $children{$pid} = [$domain, $name, $node, $oldnode];
 
408
              return; 
 
409
      } # else I'm the child -- go spawn
 
410
  }
 
411
 
 
412
  $0 .= " [$name]";
 
413
 
 
414
  # First we get lock...
 
415
  unless (&munin_getlock("$config->{rundir}/munin-$domain-$name.lock"))
 
416
  {
 
417
    logger ("Could not get lock for $node -> $name. Skipping node.");
 
418
    if ($do_fork)
 
419
    { # Send the old config to the server before we die
 
420
        socket (SOCK, PF_UNIX, SOCK_STREAM, 0)   || die "socket: $!";
 
421
        connect (SOCK, sockaddr_un ("$config->{rundir}/$serversocket")) || die "connect: $!";
 
422
        if (ref $oldnode) {
 
423
          $config->{domain}->{$domain}->{node}->{$name} = $oldnode;
 
424
          alarm (0); # Don't want to interrupt this.
 
425
          my @tmp = ($$, $domain, $name);
 
426
          nstore_fd \@tmp, \*SOCK || die "Could not nstore_fd: $!";
 
427
          nstore_fd \%{$config->{domain}->{$domain}->{node}->{$name}}, \*SOCK;
 
428
          close SOCK;
 
429
        }
 
430
        exit 1;
 
431
    }
 
432
    else
 
433
    {
 
434
        return 0;
 
435
    }
 
436
  }
 
437
 
 
438
  my $socket = new IO::Socket::INET ('PeerAddr' => "$node->{address}:".
 
439
                  ($node->{port} || $config->{domain}->{$domain}->{port} || 
 
440
                   $config->{port} || "4949"), 
 
441
                  'Proto'    => "tcp", "Timeout" => $timeout);
 
442
  my $err = ($socket ? "" : $!);
 
443
 
 
444
  if ($do_fork)
 
445
  {
 
446
      $SIG{ALRM} = sub { close $socket; die "$!\n"};
 
447
      alarm ($timeout);
 
448
 
 
449
      my @tmp = ($$, $domain, $name);
 
450
 
 
451
      if (!$socket) {
 
452
        logger ("Could not connect to $name($node->{address}): $err - Attempting to use old configuration");
 
453
        # If we can't reach the client. Using old Configuration.
 
454
        if (ref $oldnode) {
 
455
          $config->{domain}->{$domain}->{node}->{$name} = $oldnode;
 
456
          alarm (0); # Don't want to interrupt this.
 
457
          socket (SOCK, PF_UNIX, SOCK_STREAM, 0)   || die "socket: $!";
 
458
          connect (SOCK, sockaddr_un ("$config->{rundir}/$serversocket")) || die "connect: $!";
 
459
          nstore_fd \@tmp, \*SOCK || die "Could not nstore_fd: $!";
 
460
          nstore_fd \%{$config->{domain}->{$domain}->{node}->{$name}}, \*SOCK;
 
461
          alarm ($timeout);
 
462
          close SOCK;
 
463
        }
 
464
        else
 
465
        { # Well, we'll have to give _something_ to the server, or it'll time out.
 
466
          socket (SOCK, PF_UNIX, SOCK_STREAM, 0)   || die "socket: $!";
 
467
          connect (SOCK, sockaddr_un ("$config->{rundir}/$serversocket")) || die "connect: $!";
 
468
          nstore_fd \@tmp, \*SOCK || die "Could not nstore_fd: $!";
 
469
          nstore_fd ({}, \*SOCK);
 
470
        }
 
471
      } else {
 
472
                if (!&config_node($domain,$name,$node,$oldnode,$socket))
 
473
                {
 
474
                    $config->{domain}->{$domain}->{node}->{$name} = $oldnode;
 
475
                    socket (SOCK, PF_UNIX, SOCK_STREAM, 0)   || die "socket: $!";
 
476
                    connect (SOCK, sockaddr_un ("$config->{rundir}/$serversocket")) || die "connect: $!";
 
477
                    nstore_fd \@tmp, \*SOCK || die "Could not nstore_fd: $!";
 
478
                    nstore_fd \%{$config->{domain}->{$domain}->{node}->{$name}}, \*SOCK;
 
479
                    close SOCK;
 
480
                    exit 1;
 
481
                }
 
482
                &fetch_node($domain,$name,$node,$socket);
 
483
                close $socket;
 
484
                alarm (0); # Don't want to interrupt this.
 
485
                socket (SOCK, PF_UNIX, SOCK_STREAM, 0)   || die "socket: $!";
 
486
                connect (SOCK, sockaddr_un ("$config->{rundir}/$serversocket")) || die "connect: $!";
 
487
                nstore_fd \@tmp, \*SOCK || die "Could not nstore_fd: $!";
 
488
                nstore_fd \%{$config->{domain}->{$domain}->{node}->{$name}}, \*SOCK;
 
489
                alarm ($timeout);
 
490
                close SOCK;
 
491
      }
 
492
      alarm (0);
 
493
      exit;
 
494
  }
 
495
  else # No forking...
 
496
  {
 
497
      if (!$socket) {
 
498
        logger ("Could not connect to $name($node->{address}): $err\nAttempting to use old configuration");
 
499
        # If we can't reach the client. Using old Configuration.
 
500
        if (ref $oldnode) {
 
501
          $config->{domain}->{$domain}->{node}->{$name} = $oldnode;
 
502
        }
 
503
      } else {
 
504
                next unless (&config_node($domain,$name,$node,$oldnode,$socket));
 
505
                &fetch_node($domain,$name,$node,$socket);
 
506
                close $socket;
 
507
      }
 
508
 
 
509
  }
 
510
}
 
511
 
 
512
sub read_socket_single {
 
513
    my( $socket ) = @_;
 
514
    my $timed_out=0;
 
515
    my $res;
 
516
    eval {
 
517
      local $SIG{ALRM} = sub { $timed_out=1; close $socket; die "$!\n"};
 
518
      alarm( $timeout );
 
519
      $res = <$socket>;
 
520
      chomp $res;
 
521
      alarm 0;
 
522
    };
 
523
    if ($timed_out)
 
524
    {
 
525
        logger ("Socket read timed out: $@\n");
 
526
        return undef;
 
527
    }
 
528
    return $res;
 
529
}
 
530
 
 
531
 
 
532
sub read_socket {
 
533
    my ($socket) = @_;
 
534
    my @array;
 
535
    my $timed_out=0;
 
536
    eval {
 
537
      local $SIG{ALRM} = sub { $timed_out=1; close $socket; die "$!\n"};
 
538
      alarm( $timeout );
 
539
      while (<$socket>) {
 
540
        chomp;
 
541
        last if (/^\.$/);
 
542
        push @array,$_;
 
543
      }
 
544
      alarm 0;
 
545
    };
 
546
    if ($timed_out)
 
547
    {
 
548
        logger ("Socket read timed out: $@\n");
 
549
        return undef;
 
550
    }
 
551
    return (@array);
 
552
}
 
553
 
 
554
sub config_node {
 
555
  my ($domain,$name,$node,$oldnode,$socket) = @_;
 
556
  my $clientdomain = read_socket_single ($socket);
 
557
  my $fetchdomain;
 
558
  chomp($clientdomain) if $clientdomain;
 
559
  if (!$clientdomain) {
 
560
      logger("Got unknown reply from client: '$clientdomain' skipping");
 
561
      return 0;
 
562
  }
 
563
  $clientdomain =~ s/\#.*(?:lrrd|munins) (?:client|node) at //;
 
564
  if (exists $node->{'use_node_name'} and $node->{'use_node_name'} =~ /^\s*y(?:es)\s*$/i)
 
565
  {
 
566
      $fetchdomain = $clientdomain;
 
567
  }
 
568
  elsif (exists $node->{'use_default_name'} and $node->{'use_default_name'} =~ /^\s*y(?:es)\s*$/i)
 
569
  {
 
570
      $fetchdomain = $clientdomain;
 
571
  }
 
572
  else
 
573
  {
 
574
      $fetchdomain = $name;
 
575
  }
 
576
  my $nodeconf_time = Time::HiRes::time;
 
577
  logger("Configuring node: $name") if $DEBUG;
 
578
  my @services;
 
579
  eval {
 
580
    local $SIG{ALRM} = sub { die "Could not run list on $name ($fetchdomain): $!\n"};
 
581
    alarm 5; # Should be enough to check the list
 
582
    print $socket "list $fetchdomain\n";
 
583
    my $list = <$socket>;
 
584
    chomp $list;
 
585
    @services = split / /,$list;
 
586
    alarm 0;
 
587
  };
 
588
  if ($@) {
 
589
    die unless ($@ =~ m/Could not run list/);
 
590
      logger ("Could not get list from $node->{address}: $!\nAttempting to use old configuration");
 
591
    if (ref $oldnode) {
 
592
      $config->{domain}->{$domain}->{node}->{$name} = $oldnode;
 
593
    }
 
594
    @services = [];
 
595
  }
 
596
 
 
597
  for my $service (@services) {
 
598
    my $servname = $service;
 
599
    $servname =~ s/\W/_/g;
 
600
    next if (exists ($node->{client}->{$servname}->{fetch_data}) and
 
601
             $node->{client}->{$servname}->{fetch_data} == 0);
 
602
    next if (exists ($node->{client}->{$servname}->{update}) and 
 
603
             !$node->{client}->{$servname}->{update} eq "yes");
 
604
    next if (@limit_services and !grep (/^$servname$/, @limit_services));
 
605
    my @graph_order = (exists $node->{client}->{$servname}->{graph_order} ? 
 
606
                       split (/\s+/, $node->{client}->{$servname}->{graph_order}) : ());
 
607
    my $serviceconf_time = Time::HiRes::time;
 
608
    if ($servname ne $service)
 
609
    {
 
610
        $node->{client}->{$servname}->{realservname} = $service;
 
611
    }
 
612
    logger("Configuring service: $name->$servname") if $DEBUG;
 
613
    print $socket "config $service\n";
 
614
    my @lines = read_socket($socket);
 
615
    return unless $socket;
 
616
    next unless (@lines);
 
617
    for (@lines) {
 
618
      if (/\# timeout/) {
 
619
        logger("Client reported timeout in configuration of $servname");
 
620
        if ($oldnode->{client}->{$servname}) {
 
621
          logger("Attempting to use old configuration");
 
622
          $config->{domain}->{$domain}->{node}->{$name}->{client}->{$servname} = $oldnode->{client}->{$servname};
 
623
        } else {
 
624
          logger("Skipping configuration of $servname");
 
625
          delete $node->{client}->{$servname};
 
626
        }
 
627
      }
 
628
      elsif (/^(\w+)\.(\w+)\s+(.+)/) {
 
629
        my ($client,$type,$value) = ($1,$2,$3);
 
630
        $client = substr ($client,-18);
 
631
        $node->{client}->{$servname}->{$client.".".$type} = "$value";
 
632
        logger ("config: $name->$client.$type = $value") if $DEBUG;
 
633
        if ($type eq "label") {
 
634
          push (@graph_order,$client)
 
635
            unless grep (/^$client$/, @graph_order);
 
636
        }
 
637
      } elsif (/(^[^\s\#]+)\s+(.+)/) {
 
638
        my ($keyword) = $1;
 
639
        my ($value) = $2;
 
640
        $node->{client}->{$servname}->{$keyword} = $value;
 
641
        logger ("Config: $keyword = $value") if $DEBUG;
 
642
        if ($keyword eq "graph_order") {
 
643
          @graph_order = split (/\s+/, $node->{client}->{$servname}->{graph_order});
 
644
        }
 
645
      }
 
646
    }
 
647
    for my $subservice (keys %{$node->{client}->{$servname}}) {
 
648
      my ($client,$type) = split /\./,$subservice;
 
649
      my ($value) = $node->{client}->{$servname}->{$subservice};
 
650
      if ($type eq "label") {
 
651
        my $fname = "$config->{dbdir}/$domain/$name-$servname-$client-" . 
 
652
            lc substr (($node->{client}->{$servname}->{"$client.type"}||"GAUGE"),0,1).
 
653
            ".rrd";
 
654
        if (! -f "$fname") {
 
655
          logger ("creating rrd-file for $servname->$subservice");
 
656
          mkdir "$config->{dbdir}/$domain/",0777;
 
657
          RRDs::create ("$fname",
 
658
                        "DS:42:".($node->{client}->{$servname}->{"$client.type"} || "GAUGE").":600:".
 
659
                        (defined $node->{client}->{$servname}->{"$client.min"} ? 
 
660
                         $node->{client}->{$servname}->{"$client.min"} :
 
661
                         "U") . ":" . ($node->{client}->{$servname}->{"$client.max"} || "U"),
 
662
                        "RRA:AVERAGE:0.5:1:576",
 
663
                        "RRA:MIN:0.5:1:576",
 
664
                        "RRA:MAX:0.5:1:576",
 
665
                        "RRA:AVERAGE:0.5:6:432", # 9 days
 
666
                        "RRA:MIN:0.5:6:432",
 
667
                        "RRA:MAX:0.5:6:432",
 
668
                        "RRA:AVERAGE:0.5:24:540", # 45 days
 
669
                        "RRA:MIN:0.5:24:540",
 
670
                        "RRA:MAX:0.5:24:540",
 
671
                        "RRA:AVERAGE:0.5:288:450", # 450 days
 
672
                        "RRA:MIN:0.5:288:450",
 
673
                        "RRA:MAX:0.5:288:450");
 
674
          if (my $ERROR = RRDs::error) {
 
675
            logger ("Unable to create \"$fname\": $ERROR");
 
676
          }
 
677
        }
 
678
    }
 
679
      $node->{client}->{$servname}->{graph_order} = join(' ',@graph_order);
 
680
    }
 
681
    $serviceconf_time = sprintf ("%.2f",(Time::HiRes::time - $serviceconf_time));
 
682
    print STATS "CS|$domain|$name|$servname|$serviceconf_time\n";
 
683
    logger ("Configured service: $name -> $servname ($serviceconf_time sec)");
 
684
  }
 
685
  $nodeconf_time = sprintf ("%.2f",(Time::HiRes::time - $nodeconf_time));
 
686
  print STATS "CN|$domain|$name|$nodeconf_time\n";
 
687
  logger("Configured node: $name ($nodeconf_time sec)");
 
688
  return 1;
 
689
}
 
690
 
 
691
sub fetch_node {
 
692
  my ($domain,$name,$node,$socket) = @_;
 
693
  my $nodefetch_time = Time::HiRes::time;
 
694
  logger("Fetching node: $name") if $DEBUG;
 
695
  for my $service (keys %{$node->{client}}) {
 
696
    my $servicefetch_time = Time::HiRes::time;
 
697
    logger("Fetching service: $name->$service") if $DEBUG;
 
698
    next if (exists ($node->{client}->{$service}->{fetch_data}) and 
 
699
             $node->{client}->{$service}->{fetch_data} == 0);
 
700
    next if (exists ($node->{client}->{$service}->{update}) and 
 
701
             !$node->{client}->{$service}->{update} eq "yes");
 
702
    next if (@limit_services and !grep (/^$service$/, @limit_services));
 
703
    my $realservname = $node->{client}->{$service}->{realservname} || $service;
 
704
    delete $node->{client}->{$service}->{realservname}
 
705
        if exists $node->{client}->{$service}->{realservname};
 
706
    print $socket "fetch $realservname\n";
 
707
    my @lines = &read_socket($socket);
 
708
    return unless $socket;
 
709
    for (@lines) {
 
710
      if (/\# timeout/) {
 
711
        logger("Client reported timeout in fetching of $service");
 
712
      }
 
713
      elsif (/(\w+)\.value\s+(.+)/) {
 
714
        my $key = $1;
 
715
        my $value = $2;
 
716
        my $comment = $3;
 
717
        $key = substr($key,-18);
 
718
        my $fname = "$config->{dbdir}/$domain/$name-$service-$key-".
 
719
          lc substr (($node->{client}->{$service}->{$key.".type"}||"GAUGE"),0,1).
 
720
            ".rrd";
 
721
        logger("Updating $fname with $value") if $DEBUG;
 
722
        RRDs::update ("$fname", "N:$value");
 
723
        if (my $ERROR = RRDs::error) {
 
724
          logger ("Unable to update $fname: $ERROR");
 
725
        }
 
726
      }
 
727
      elsif (/(\w+)\.extinfo\s+(.+)/) {
 
728
        $config->{domain}->{$domain}->{node}->{$name}->{client}->{$service}->{$1.".extinfo"} = $2;
 
729
      }
 
730
    }
 
731
    $servicefetch_time = sprintf ("%.2f",(Time::HiRes::time - $servicefetch_time));
 
732
    logger ("Fetched service: $name -> $service ($servicefetch_time sec)");
 
733
    print STATS "FS|$domain|$name|$service|$servicefetch_time\n";
 
734
  }
 
735
  $nodefetch_time = sprintf ("%.2f",(Time::HiRes::time - $nodefetch_time));
 
736
  logger ("Fetched node: $name ($nodefetch_time sec)");
 
737
  print STATS "FN|$domain|$name|$nodefetch_time\n";
 
738
}
 
739
 
 
740
sub use_old_config
 
741
{
 
742
    my $domain  = shift;
 
743
    my $name    = shift;
 
744
    my $oldnode = shift;
 
745
 
 
746
    $config->{domain}->{$domain}->{node}->{$name} = $oldnode;
 
747
    logger ("Attempting to use old configuration for $domain -> $name.");
 
748
}
 
749
 
 
750
 
 
751
 
 
752
sub logger {
 
753
  my ($comment) = @_;
 
754
  my $now = strftime "%b %d %H:%M:%S", localtime;
 
755
  print ("$now - $$: $comment\n") if $print_stdout;
 
756
  print LOG ("$now - $$: $comment\n");
 
757
  flush LOG;
 
758
}
 
759
 
 
760
 
 
761
 
 
762
1;
 
763
 
 
764
=head1 NAME
 
765
 
 
766
munin-update - A program to gather data from machines running munin-node
 
767
 
 
768
=head1 SYNOPSIS
 
769
 
 
770
munin-update [options]
 
771
 
 
772
=head1 OPTIONS
 
773
 
 
774
=over 5
 
775
 
 
776
=item B<< --[no]force-root >>
 
777
 
 
778
Force running as root (stupid and unnecessary). [--noforce-root]
 
779
 
 
780
=item B<< --service <service> >>
 
781
 
 
782
Limit fetched data to those of E<lt>serviceE<gt>. Multiple --service options may be supplied. [unset]
 
783
 
 
784
=item B<< --host <host> >>
 
785
 
 
786
Limit fetched data to those from E<lt>host<gt>. Multiple --host options may be supplied. [unset]
 
787
 
 
788
=item B<< --config <file> >>
 
789
 
 
790
Use E<lt>fileE<gt> as configuration file. [@@CONFDIR@@/munin.conf]
 
791
 
 
792
=item B<< --help >>
 
793
 
 
794
View help message.
 
795
 
 
796
=item B<< --[no]debug >>
 
797
 
 
798
If set, view debug messages. [--nodebug]
 
799
 
 
800
=item B<< --[no]fork >>
 
801
 
 
802
If set, will fork off one process for each host. [--fork]
 
803
 
 
804
=item B<< --[no]stdout >>
 
805
 
 
806
If set, will print log messages to stdout as well as syslog. [--nostdout]
 
807
 
 
808
=item B<< --timeout <seconds> >>
 
809
 
 
810
Set the network timeout to <seconds>. [180]
 
811
 
 
812
=back
 
813
 
 
814
=head1 DESCRIPTION
 
815
 
 
816
Munin-update is a part of the package Munin, which is used in
 
817
combination with Munin's node.  Munin is a group of programs to gather
 
818
data from Munin's nodes, graph them, create html-pages, and optionally
 
819
warn Nagios about any off-limit values.
 
820
 
 
821
Munin-update does the gathering. It is usually only used from within
 
822
munin-cron.
 
823
 
 
824
It contacts each host's munin-node in turn, gathers data from it, and
 
825
stores them in .rrd-files. If necessary, it will create the rrd-files
 
826
and the directories to store them in.
 
827
 
 
828
=head1 FILES
 
829
 
 
830
        @@CONFDIR@@/munin.conf
 
831
        @@DBDIR@@/*
 
832
        @@LOGDIR@@/munin-update
 
833
        @@STATEDIR@@/*
 
834
 
 
835
=head1 VERSION
 
836
 
 
837
This is munin-update version @@VERSION@@
 
838
 
 
839
=head1 AUTHORS
 
840
 
 
841
Audun Ytterdal and Jimmy Olsen.
 
842
 
 
843
=head1 BUGS
 
844
 
 
845
munin-update does, as of now, not check the syntax of the configuration file.
 
846
 
 
847
Please report other bugs in the bug tracker at L<http://munin.sf.net/>.
 
848
 
 
849
=head1 COPYRIGHT
 
850
 
 
851
Copyright � 2002-2004 Audun Ytterdal, Jimmy Olsen, and Tore Anderson / Linpro AS.
 
852
 
 
853
This is free software; see the source for copying conditions. There is
 
854
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
 
855
PURPOSE.
 
856
 
 
857
This program is released under the GNU General Public License
 
858
 
 
859
=cut
 
860