rdnsd 13.7 KB
Newer Older
Gavin Brown's avatar
Gavin Brown committed
1
#!/usr/bin/perl
Gavin Brown's avatar
Gavin Brown committed
2
# Copyright 2018 CentralNic Ltd. This program is free software; you can
Gavin Brown's avatar
Gavin Brown committed
3 4 5 6
# redistribute it and/or modify it under the same terms as Perl itself.
use Config::Simple;
use Getopt::Long;
use Net::DNS;
7
use POSIX qw(setsid strftime floor);
Gavin Brown's avatar
Gavin Brown committed
8
use Pod::Usage;
Gavin Brown's avatar
Gavin Brown committed
9 10
use Sys::Syslog qw(:standard :macros);
use Time::HiRes qw(time sleep);
Gavin Brown's avatar
Gavin Brown committed
11 12 13 14 15 16 17 18 19 20 21 22
use strict;

#
# get config from command line:
#
my $opts = {};
GetOptions(
	$opts,
	'config=s',
	'debug',
	'loop=s',
	'pidfile=s',
Gavin Brown's avatar
Gavin Brown committed
23
	'family=i',
Gavin Brown's avatar
Gavin Brown committed
24 25 26 27 28 29 30
	'proto=s',
	'question=s',
	'timeout=s',
	'recurse',
	'servers=s',
	'statsfile=s',
	'domains=s',
31
	'percentile=i',
Gavin Brown's avatar
Gavin Brown committed
32
	'optimistic',
Gavin Brown's avatar
Gavin Brown committed
33
	'update=i',
Gavin Brown's avatar
Gavin Brown committed
34 35 36 37 38
	'help',
);

pod2usage('-verbose' => 99, '-sections' => 'USAGE|OPTIONS') if ($opts->{'help'});

Gavin Brown's avatar
Gavin Brown committed
39 40
my $resolver = Net::DNS::Resolver->new;
my $qpacket;
Gavin Brown's avatar
Gavin Brown committed
41 42

#
Gavin Brown's avatar
Gavin Brown committed
43
# these are populated by load_config():
Gavin Brown's avatar
Gavin Brown committed
44
#
Gavin Brown's avatar
Gavin Brown committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
my $debug;
my $loop;
my $pidfile;
my $family;
my $proto;
my $question;
my $timeout;
my $recurse;
my $statsfile;
my $servers;
my $domains;
my $percentile;
my $optimistic;
my $update;

load_config();

die("Both 'Servers' and 'Domains' are present in config (or were provided as arguments): please choose one or the other") if ($servers && $domains);

openlog('rdnsd', 'pid,perror', LOG_DAEMON);
setlogmask(LOG_UPTO(LOG_DEBUG));
Gavin Brown's avatar
Gavin Brown committed
66 67 68

debug('starting');

Gavin Brown's avatar
Gavin Brown committed
69 70 71
#
# these are populated by update_serverlist():
#
Gavin Brown's avatar
Gavin Brown committed
72
my @servers;
Gavin Brown's avatar
Gavin Brown committed
73 74 75
my $reload;
my $refresh;
my $need_update;
Gavin Brown's avatar
Gavin Brown committed
76

Gavin Brown's avatar
Gavin Brown committed
77
update_serverlist();
Gavin Brown's avatar
Gavin Brown committed
78

Gavin Brown's avatar
Gavin Brown committed
79
die('no servers found') if (scalar(@servers) < 1);
Gavin Brown's avatar
Gavin Brown committed
80 81 82 83 84 85 86 87 88

#
# we put statistics data in here
#
my $stats = {};

#
# optionally daemonize:
#
Gavin Brown's avatar
Gavin Brown committed
89
my $daemon;
Gavin Brown's avatar
Gavin Brown committed
90 91 92 93 94 95 96 97 98 99
unless ($debug) {
	if (fork() > 0) {
		exit 0;

	} else {
		setsid();
		chdir('/');
		$0 = '[rdnsd]';

	}
Gavin Brown's avatar
Gavin Brown committed
100
	$daemon = 1;
Gavin Brown's avatar
Gavin Brown committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
}

#
# write our PID to a file:
#
if (!open(PIDFILE, '>'.$pidfile)) {
	die("Error opening '$pidfile': $!");

} else {
	print PIDFILE $$."\n";
	close(PIDFILE);

	debug('wrote %s', $pidfile);
}

Gavin Brown's avatar
Gavin Brown committed
116 117 118 119 120 121 122 123 124
#
# HUP handler:
#
$SIG{'HUP'} = sub {
	debug('received SIGHUP');
	$reload = 1;
	$refresh = 0;
};

Gavin Brown's avatar
Gavin Brown committed
125 126 127 128
#
# configure signal handler to write stats to log file when we get the USR1
# signal:
#
Gavin Brown's avatar
Gavin Brown committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
$SIG{'USR1'} = sub {
	debug('received SIGUSR1');
	$need_update = 1;
};

my $updated = time();

#
# loop forever:
#
debug('entering main loop');
main_loop() while (1);

sub main_loop {
	update_serverlist() if ($refresh <= time());
	load_config() if ($reload);

	my %times;
	my %sockets;
Gavin Brown's avatar
Gavin Brown committed
148 149

	foreach my $ns (sort(@servers)) {
Gavin Brown's avatar
Gavin Brown committed
150 151 152 153 154 155 156 157
		eval {
			$resolver->nameservers($ns);

			my $result;

			my $t0 = time();
			$result = $resolver->send($qpacket);
			my $dt = (time() - $t0);
Gavin Brown's avatar
Gavin Brown committed
158

Gavin Brown's avatar
Gavin Brown committed
159 160 161 162 163 164 165
			#
			# record stats
			#
			$stats->{$ns}->{'count'}++;
			$stats->{$ns}->{'time'} += $dt;
			if (!$result) {
				debug($resolver->errorstring);
Gavin Brown's avatar
Gavin Brown committed
166 167

			} else {
Gavin Brown's avatar
Gavin Brown committed
168 169
				$stats->{$ns}->{'success'}++;
				push(@{$stats->{$ns}->{'times'}}, $dt);
Gavin Brown's avatar
Gavin Brown committed
170 171

			}
Gavin Brown's avatar
Gavin Brown committed
172 173 174 175 176 177 178 179 180 181 182 183

			#
			# we want to check each server every $loop seconds so $interval
			# is the sleep time between each request:
			#
			my $interval = ($loop / scalar(@servers));
			sleep($interval-$dt) unless ($interval-$dt <= 0);
		};

		if ($@) {
			chomp($@);
			debug(sprintf('error: %s', $@));
Gavin Brown's avatar
Gavin Brown committed
184
		}
Gavin Brown's avatar
Gavin Brown committed
185 186 187 188 189 190 191 192 193 194 195
	}

	update_stats() if ($need_update || $update && time() - $updated >= $update);
}

sub debug {
	return unless $debug;
	my ($fmt, @args) = @_;
	my $message = sprintf($fmt, @args);
	syslog(LOG_DEBUG, $message);
}
196

Gavin Brown's avatar
Gavin Brown committed
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
sub load_config {
	debug('loading configuration');

	#
	# get config from config file:
	#
	my $config = Config::Simple->new('syntax' => 'simple');
	$config->read($opts->{'config'}) if ($opts->{'config'});

	#
	# pick config settings - command line overrides config file overrides defaults.
	# Some options (eg pidfile) have no effect if this function is called because
	# rdnsd received a SIGHUP:
	#
	$debug		= $opts->{'debug'}	|| $config->param('Debug') eq 'true'		|| undef;
	$loop		= $opts->{'loop'}	|| $config->param('Loop')			|| 3;
	$pidfile	= $opts->{'pidfile'}	|| $config->param('PidFile')			|| '/var/run/rdnsd.pid';
	$family		= $opts->{'family'}	|| $config->param('AddressFamily')		|| undef;
	$proto		= $opts->{'proto'}	|| $config->param('Protocol')			|| 'udp';
	$question	= $opts->{'question'}	|| $config->param('Question')			|| '. A IN';
	$timeout	= $opts->{'timeout'}	|| $config->param('Timeout')			|| 1;
	$recurse	= $opts->{'recurse'}	|| $config->param('Recurse') eq 'true'		|| undef;
	$statsfile	= $opts->{'statsfile'}	|| $config->param('StatsFile')			|| '/var/run/rdnsd.log';
	$servers	= $opts->{'servers'}	|| $config->param('Servers')			|| undef;
	$domains	= $opts->{'domains'}	|| $config->param('Domains')			|| undef;
	$percentile	= $opts->{'percentile'}	|| $config->param('Percentile')			|| undef;
	$optimistic	= $opts->{'optimistic'}	|| $config->param('Optimistic') eq 'true'	|| undef;
	$update		= $opts->{'update'}	|| $config->param('UpdateInterval')		|| undef;

	#
	# configure question packet
	#
	my @question = split(/\s+/, $question);
	$qpacket = Net::DNS::Packet->new(@question);
	$qpacket->header->rd($recurse);

	#
	# configure resolver
	#
	$resolver->usevc('tcp' eq $proto);
	$resolver->udp_timeout($timeout);
	$resolver->tcp_timeout($timeout);
	$resolver->persistent_udp(0);
	$resolver->persistent_tcp(0);
	$resolver->force_v4(4 == $family);
	$resolver->force_v6(6 == $family);

	$reload = undef;
}
246

Gavin Brown's avatar
Gavin Brown committed
247 248 249 250
sub update_serverlist {
	if ($servers) {
		# statically defined server list, never refresh
		$refresh = -1;
251

Gavin Brown's avatar
Gavin Brown committed
252
		@servers = ('ARRAY' eq ref($servers) ? @{$servers} : split(/\s*,\s*/, $servers));
253

Gavin Brown's avatar
Gavin Brown committed
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
	} else {
		@servers = ();

		my $resolver = Net::DNS::Resolver->new;
		$resolver->nameservers('8.8.8.8');

		#
		# start with a high value
		#
		my $minttl = ~0;

		my @domains = ('ARRAY' eq ref($domains) ? @{$domains} : split(/\s*,\s*/, $domains));
		foreach my $domain (@domains) {
			my $answer = $resolver->query('_dns._udp.'.$domain, 'SRV');
			if ($answer) {
				foreach my $rr (grep { 'SRV' eq $_->type } $answer->answer) {
					push(@servers, $rr->target);

					#
					# reduce if we see a lower value
					#
					$minttl = $rr->ttl if ($rr->ttl < $minttl);
				}
			}
278 279
		}

Gavin Brown's avatar
Gavin Brown committed
280 281 282 283 284 285
		#
		# if no servers return, retry after 60s, otherwise, refresh after the shortest
		# TTL observed in the answer section
		#
		$refresh = time() + (scalar(@servers) < 1 ? 60 : $minttl);
		debug('server list updated; will be refreshed at %s', scalar(localtime($refresh)));
Gavin Brown's avatar
Gavin Brown committed
286
	}
Gavin Brown's avatar
Gavin Brown committed
287
}
Gavin Brown's avatar
Gavin Brown committed
288

Gavin Brown's avatar
Gavin Brown committed
289 290 291
sub update_stats {
	if (!open(STATSFILE, '>'.$statsfile)) {
		debug("error opening '%s': %s", $statsfile, $!);
Gavin Brown's avatar
Gavin Brown committed
292

Gavin Brown's avatar
Gavin Brown committed
293 294 295 296
		#
		# try again in 30s
		#
		$updated = time() - 30;
Gavin Brown's avatar
Gavin Brown committed
297

Gavin Brown's avatar
Gavin Brown committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
	} else {
		foreach my $ns (sort(@servers)) {
			if ($stats->{$ns}->{'count'} < 1) {
				$stats->{$ns}->{'time'} = 0;

				if ($optimistic) {
					#
					# optimistic, treat server as up
					#
					$stats->{$ns}->{'count'} = 1;
					$stats->{$ns}->{'success'} = 1;

				} else {
					#
					# pessimistic, treat server as down
					#
					$stats->{$ns}->{'count'} = 1;
					$stats->{$ns}->{'success'} = 0;

				}
			}
Gavin Brown's avatar
Gavin Brown committed
319

Gavin Brown's avatar
Gavin Brown committed
320 321 322 323 324
			my $line = sprintf('%s %0.2f %d',
				$ns,
				($stats->{$ns}->{'success'} / $stats->{$ns}->{'count'}),
				1000 * ($stats->{$ns}->{'time'} / $stats->{$ns}->{'count'}),
			);
Gavin Brown's avatar
Gavin Brown committed
325

Gavin Brown's avatar
Gavin Brown committed
326 327 328
			if ($percentile) {
				# sort in ascending order
				my @times = sort(@{$stats->{$ns}->{'times'}});
Gavin Brown's avatar
Gavin Brown committed
329

Gavin Brown's avatar
Gavin Brown committed
330 331
				# find the position which corresponds to the percentile
				my $pos = floor(scalar(@times) * $percentile / 100) - 1;
Gavin Brown's avatar
Gavin Brown committed
332

Gavin Brown's avatar
Gavin Brown committed
333 334 335
				# append the time in milliseconds
				$line .= sprintf(' %d', 1000 * $times[$pos]);
			}
Gavin Brown's avatar
Gavin Brown committed
336

Gavin Brown's avatar
Gavin Brown committed
337
			print STATSFILE $line."\n";
Gavin Brown's avatar
Gavin Brown committed
338 339
		}

Gavin Brown's avatar
Gavin Brown committed
340 341
		debug("wrote stats to '%s'", $statsfile);
		close(STATSFILE);
Gavin Brown's avatar
Gavin Brown committed
342

Gavin Brown's avatar
Gavin Brown committed
343
		$updated = time();
Gavin Brown's avatar
Gavin Brown committed
344

Gavin Brown's avatar
Gavin Brown committed
345 346 347 348
		$need_update = undef;

		$stats = {};
	}
Gavin Brown's avatar
Gavin Brown committed
349 350 351 352 353 354 355 356
}

__END__

=pod

=head1 NAME

Gavin Brown's avatar
Gavin Brown committed
357
C<rdnsd> - a remote DNS server monitoring tool
Gavin Brown's avatar
Gavin Brown committed
358 359 360

=head1 DESCRIPTION

Gavin Brown's avatar
Gavin Brown committed
361
C<rdnsd> is a tool which can be used to monitor the availability and
362 363 364
responsiveness remote DNS servers. Given a list of DNS servers, it will
periodically query each server in turn and record whether a response was
received, and how quickly. This information can then be obtained by
Gavin Brown's avatar
Gavin Brown committed
365
sending a signal to the C<rdnsd> process - a Munin plugin is provided as an
366
example of how this can be achieved.
Gavin Brown's avatar
Gavin Brown committed
367 368 369

=head1 USAGE

Gavin Brown's avatar
Gavin Brown committed
370
	C<rdnsd> [OPTIONS]
Gavin Brown's avatar
Gavin Brown committed
371 372 373 374 375

=head1 OPTIONS

=over

Gavin Brown's avatar
Gavin Brown committed
376
=item * C<--help>
Gavin Brown's avatar
Gavin Brown committed
377 378 379

Display help text.

Gavin Brown's avatar
Gavin Brown committed
380
=item * C<--config=FILE>
Gavin Brown's avatar
Gavin Brown committed
381

382 383 384
Specify the configuration file. See L<CONFIGURATION FILE> for further
details. Arguments passed on the command line will override the contents
of this file.
Gavin Brown's avatar
Gavin Brown committed
385

Gavin Brown's avatar
Gavin Brown committed
386
=item * C<--debug>
Gavin Brown's avatar
Gavin Brown committed
387 388 389

Enable debug mode.

Gavin Brown's avatar
Gavin Brown committed
390
=item * C<--loop=LOOP>
Gavin Brown's avatar
Gavin Brown committed
391 392 393

Set loop duration.

Gavin Brown's avatar
Gavin Brown committed
394
=item * C<--pidfile=FILE>
Gavin Brown's avatar
Gavin Brown committed
395 396 397

Specify pid file.

Gavin Brown's avatar
Gavin Brown committed
398 399 400 401 402
=item * C<--family=(4|6)>

Specify IP version.

=item * C<--proto=QUESTION>
Gavin Brown's avatar
Gavin Brown committed
403 404 405

Specify protocol.

Gavin Brown's avatar
Gavin Brown committed
406
=item * C<--question=QUESTION>
Gavin Brown's avatar
Gavin Brown committed
407 408 409

Specify question.

Gavin Brown's avatar
Gavin Brown committed
410
=item * C<--timeout=TIMEOUT>
Gavin Brown's avatar
Gavin Brown committed
411 412 413

Specify timeout.

Gavin Brown's avatar
Gavin Brown committed
414
=item * C<--recurse>
Gavin Brown's avatar
Gavin Brown committed
415 416 417

Enable recursion.

Gavin Brown's avatar
Gavin Brown committed
418
=item * C<--servers=SERVERS>
Gavin Brown's avatar
Gavin Brown committed
419 420 421

Specify servers to check.

Gavin Brown's avatar
Gavin Brown committed
422
=item * C<--statsfile=FILE>
Gavin Brown's avatar
Gavin Brown committed
423 424 425

Specify stats file.

Gavin Brown's avatar
Gavin Brown committed
426
=item * C<--percentile=PERCENTILE>
427 428 429

Specify a percentile to use when generating statistics.

Gavin Brown's avatar
Gavin Brown committed
430
=item * C<--domains=DOMAINS>
Gavin Brown's avatar
Gavin Brown committed
431 432 433

Specify domain names to query for a list of servers.

Gavin Brown's avatar
Gavin Brown committed
434 435 436 437 438 439 440 441
=item * C<--optimistic>

Enable Optimistic mode.

=item * C<--update=TIME>

Specify automatic stats update interval.

Gavin Brown's avatar
Gavin Brown committed
442 443 444 445
=back

=head1 CONFIGURATION FILE

Gavin Brown's avatar
Gavin Brown committed
446
The easiest way to configure C<rdnsd> is to provide a configuration file.
447
The format is very simple. Here is an example:
Gavin Brown's avatar
Gavin Brown committed
448 449 450 451

	Debug		false
	PidFile		/var/run/rdnsd.pid
	StatsFile	/var/run/rdnsd.log
452
	Percentile	95
Gavin Brown's avatar
Gavin Brown committed
453
	AddressFamily	4
Gavin Brown's avatar
Gavin Brown committed
454 455 456 457 458 459
	Protocol	udp
	Loop		3
	Recurse		false
	Question	. A IN
	Servers		ns1.example.com,ns2.example.net
	Domains		example.com
Gavin Brown's avatar
Gavin Brown committed
460
	Optimistic	false
Gavin Brown's avatar
Gavin Brown committed
461
	UpdateInterval	300
Gavin Brown's avatar
Gavin Brown committed
462

463 464 465
The directives are explained below. As noted above, if the equivalent
command line argument is passed, it will override the value in the
configuration file.
Gavin Brown's avatar
Gavin Brown committed
466 467 468

=over

Gavin Brown's avatar
Gavin Brown committed
469
=item * C<Debug (true|false)>
Gavin Brown's avatar
Gavin Brown committed
470 471 472

Default: false

Gavin Brown's avatar
Gavin Brown committed
473 474
Normally, C<rdnsd> will daemonise once started. If the C<Debug> parameter
is C<true>, C<rdnsd> will stay in the foreground and spam your terminal
475
with debugging information.
Gavin Brown's avatar
Gavin Brown committed
476

Gavin Brown's avatar
Gavin Brown committed
477
=item * C<PidFile /path/to/pid/file>
Gavin Brown's avatar
Gavin Brown committed
478 479 480

Default: var/run/rdnsd.pid

Gavin Brown's avatar
Gavin Brown committed
481
The file where C<rdnsd> will write its pid.
Gavin Brown's avatar
Gavin Brown committed
482

Gavin Brown's avatar
Gavin Brown committed
483
=item * C<StatsFile /path/to/stats/file>
Gavin Brown's avatar
Gavin Brown committed
484 485 486

Default: /var/run/rdnsd.log

Gavin Brown's avatar
Gavin Brown committed
487
The file where C<rdnsd> will write statistics to when signalled. See
488 489
L<OBTAINING STATISTICS> for further information.

Gavin Brown's avatar
Gavin Brown committed
490
=item * C<Percentile PERCENTILE>
491

Gavin Brown's avatar
Gavin Brown committed
492
If this option is set, C<rdnsd> will calculate the response time at the
493
given percentile. See L<STATISTICS FILE FORMAT> for further information.
Gavin Brown's avatar
Gavin Brown committed
494

Gavin Brown's avatar
Gavin Brown committed
495 496 497 498
=item * C<AddressFamily (4|6)>

Specifies whether to prefer IPv4 or IPv6 when talking to nameservers, if
the servers are identified by name rather than address (or when loaded
Gavin Brown's avatar
Gavin Brown committed
499
from SRV records). If not defined, the default behaviour is to prefer
Gavin Brown's avatar
Gavin Brown committed
500 501 502
IPv6.

=item * C<Protocol (udp|tcp)>
Gavin Brown's avatar
Gavin Brown committed
503 504 505 506 507

Default: udp

Specify the transport protocol to use.

Gavin Brown's avatar
Gavin Brown committed
508
=item * C<Loop SECONDS>
Gavin Brown's avatar
Gavin Brown committed
509 510 511

Default: 2

512 513 514
This specifies the length of the main loop. If this is set to 2, then
each server will be checked every 2 seconds. This value can be a decimal
fraction, eg 0.25.
Gavin Brown's avatar
Gavin Brown committed
515

Gavin Brown's avatar
Gavin Brown committed
516
=item * C<Recurse (true|false)>
Gavin Brown's avatar
Gavin Brown committed
517 518 519 520 521

Default: false

Enable recursion.

Gavin Brown's avatar
Gavin Brown committed
522
=item * C<Question QUESTION>
Gavin Brown's avatar
Gavin Brown committed
523 524 525 526 527

Default: example.com. IN A

Specify the DNS question. The format is "QNAME QCLASS QTYPE".

Gavin Brown's avatar
Gavin Brown committed
528
=item * C<Servers SERVERS>
Gavin Brown's avatar
Gavin Brown committed
529 530 531

Default: none

532 533
Specify the servers to be checked. This directive can't be used at the
same time as the "Domains" directive.
Gavin Brown's avatar
Gavin Brown committed
534

Gavin Brown's avatar
Gavin Brown committed
535
=item * C<Domains DOMAINS>
Gavin Brown's avatar
Gavin Brown committed
536

Gavin Brown's avatar
Gavin Brown committed
537
Default: none
Gavin Brown's avatar
Gavin Brown committed
538

539
Rather than specifying a list of nameservers, you can provide a list of
Gavin Brown's avatar
Gavin Brown committed
540 541
domains instead. For each domain, C<rdnsd> will query for SRV records for
C<_dns._udp> under the domain and use the targets of any SRV records
542 543 544
returned.

The SRV record is checked once at start-up, so if the list of hosts
Gavin Brown's avatar
Gavin Brown committed
545
changes, you will need to restart C<rdnsd>.
Gavin Brown's avatar
Gavin Brown committed
546

Gavin Brown's avatar
Gavin Brown committed
547
=item * C<Optimistic (true|false)>
Gavin Brown's avatar
Gavin Brown committed
548 549 550

Default: false

Gavin Brown's avatar
Gavin Brown committed
551
This parameter controls what happens when C<rdnsd> outputs statistics but
Gavin Brown's avatar
Gavin Brown committed
552 553 554 555
finds a server in its list that it has not yet had time to send a
query to. If its value is true, then the server will be reported as up;
if false, it will be reported as down.

Gavin Brown's avatar
Gavin Brown committed
556 557 558 559 560 561 562
=item * C<UpdateInterval TIME>

Default: none

This paramter tells C<rdnsd> to update the statistics file every C<TIME>
seconds. If set, C<rdnds> will ignore the C<USR1> signal (see below).

Gavin Brown's avatar
Gavin Brown committed
563 564 565 566
=back

=head1 OBTAINING STATISTICS

Gavin Brown's avatar
Gavin Brown committed
567 568
If C<UpdateInterval> is not set, you can get statistics out of C<rdnsd>
by sending it the C<USR1> signal:
Gavin Brown's avatar
Gavin Brown committed
569 570 571

	$ kill -USR1 `cat /path/to/pid/file`

Gavin Brown's avatar
Gavin Brown committed
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
This will cause C<rdnsd> to dump its current data to the statistics
file.

B<NOTE:> if you have C<N> servers and a C<Loop> value of C<M>, you must
be careful not to send the USR1 signal to C<rdnsd> more often than every
C<N x M> seconds, otherwise C<rdnsd> will not have enough time to test
every server. You probably want to send the signal about every C<3 x N x M>
seconds if you want reliable statistics.

Note that C<rdnsd> will not immediately update the file upon receiving
the C<USR1> signal: you may need to wait for the current loop iteration
to complete before the stats file is updated.

=head1 RELOADING CONFIGURATION

C<rdnsd> will reload its configuration if you send it a C<SIGHUP>:

	$ kill -HUP `cat /path/to/pid/file`
Gavin Brown's avatar
Gavin Brown committed
590 591 592

=head2 STATISTICS FILE FORMAT

593 594 595 596
The statistics file will contain one line for each server that is being
checked. Each line contains the nameserver checked, the response rate as
a decimal fraction, and the average response time (in milliseconds), for
example:
Gavin Brown's avatar
Gavin Brown committed
597 598 599

	ns0.example.com 1.00 25

600 601 602 603 604 605 606 607 608
If the C<Percentile> option is set in the config file (or the
C<--percentile> argument was given), an additional value will appear at
the end of the line:

	ns0.example.com 1.00 25 36

This value is the response time (in milliseconds) at the given
percentile.

Gavin Brown's avatar
Gavin Brown committed
609
Once the file has been written, C<rdnsd>'s internal data is reset, so
610
subsequent signals will produce fresh statistical data.
Gavin Brown's avatar
Gavin Brown committed
611 612 613 614 615

=head1 SEE ALSO

=over

Gavin Brown's avatar
Gavin Brown committed
616
=item * L<https://www.centralnic.com/>
Gavin Brown's avatar
Gavin Brown committed
617

Gavin Brown's avatar
Gavin Brown committed
618
=item * L<http://www.net-dns.org/>
Gavin Brown's avatar
Gavin Brown committed
619 620 621 622 623

=back

=head1 COPYRIGHT

Gavin Brown's avatar
Gavin Brown committed
624
C<rdnsd> is Copyright 2013 CentralNic Ltd. All rights reserved. This
625 626
program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
Gavin Brown's avatar
Gavin Brown committed
627 628

=cut