| 1 |
#!/usr/bin/perl -w
|
| 2 |
|
| 3 |
$| = 1;
|
| 4 |
|
| 5 |
# (c) 2002, 2003 Peter Palfrader <peter@palfrader.org>
|
| 6 |
# $Id: pingd,v 1.96 2003/02/16 03:06:51 weasel Exp $
|
| 7 |
#
|
| 8 |
|
| 9 |
=pod
|
| 10 |
|
| 11 |
=head1 NAME
|
| 12 |
|
| 13 |
pingd - echolot ping daemon
|
| 14 |
|
| 15 |
=head1 SYNOPSIS
|
| 16 |
|
| 17 |
=over
|
| 18 |
|
| 19 |
=item B<pingd> B<start>
|
| 20 |
|
| 21 |
=item B<pingd> B<stop>
|
| 22 |
|
| 23 |
=item B<pingd> B<process>
|
| 24 |
|
| 25 |
=item B<pingd> B<add> I<address> [I<address> ...]
|
| 26 |
|
| 27 |
=item B<pingd> B<delete> I<address> [I<address> ...]
|
| 28 |
|
| 29 |
=item B<pingd> B<set> option=value [option=value..] I<address> [I<address> ...]
|
| 30 |
|
| 31 |
=item B<pingd> B<setremailercaps> I<capsstring>
|
| 32 |
|
| 33 |
=item B<pingd> B<deleteremailercaps> I<address>
|
| 34 |
|
| 35 |
=item B<pingd> B<getkeyconf> [I<address> [I<address> ...]]
|
| 36 |
|
| 37 |
=item B<pingd> B<sendpings> [I<address> [I<address> ...]]
|
| 38 |
|
| 39 |
=item B<pingd> B<buildstats>
|
| 40 |
|
| 41 |
=item B<pingd> B<buildkeys>
|
| 42 |
|
| 43 |
=item B<pingd> B<buildthesaurus>
|
| 44 |
|
| 45 |
=item B<pingd> B<dumpconf>
|
| 46 |
|
| 47 |
=back
|
| 48 |
|
| 49 |
=head1 DESCRIPTION
|
| 50 |
|
| 51 |
pingd is the heart of echolot. Echolot is a pinger for anonymous remailers.
|
| 52 |
|
| 53 |
A Pinger in the context of anonymous remailers is a program that regularily
|
| 54 |
sends messages through remailers to check their reliability. It then calculates
|
| 55 |
reliability statistics which are used by remailer clients to choose the chain
|
| 56 |
of remailers to use.
|
| 57 |
|
| 58 |
Additionally it collects configuration parameters and keys of all remailers and
|
| 59 |
offers them in a format readable by remailer clients.
|
| 60 |
|
| 61 |
When called without parameters pingd schedules tasks like sending pings,
|
| 62 |
processing incoming mail and requesting remailer-xxx data and runs them at
|
| 63 |
configurable intervalls.
|
| 64 |
|
| 65 |
=head1 COMMANDS
|
| 66 |
|
| 67 |
=over
|
| 68 |
|
| 69 |
=item B<start>
|
| 70 |
|
| 71 |
Start the ping daemon.
|
| 72 |
|
| 73 |
=item B<stop>
|
| 74 |
|
| 75 |
Send the running pingd process a SIGTERM.
|
| 76 |
|
| 77 |
=item B<process>
|
| 78 |
|
| 79 |
Sends a HUP signal to the daemon which instructs it to process the commands.
|
| 80 |
|
| 81 |
For other effects of sending the HUP Signal see the SIGNALS section below.
|
| 82 |
|
| 83 |
=item B<add> I<address> [I<address> ...]
|
| 84 |
|
| 85 |
Add I<address> to the list of remailers to query for
|
| 86 |
keys and confs.
|
| 87 |
|
| 88 |
=item B<delete> I<address> [I<address> ...]
|
| 89 |
|
| 90 |
Delete I<address> from the list of remailers to query for
|
| 91 |
keys and confs. Delete all statistics and keys for that remailer.
|
| 92 |
|
| 93 |
=item B<set> option=value [option=value..] I<address> [I<address> ...]
|
| 94 |
|
| 95 |
Possible options and values:
|
| 96 |
|
| 97 |
=over
|
| 98 |
|
| 99 |
=item B<showit=>{B<on>,B<off>}
|
| 100 |
|
| 101 |
Set B<showit> (show remailer in mlist, rlist etc.) for remailer I<address> to
|
| 102 |
either B<on> or B<off>.
|
| 103 |
|
| 104 |
=item B<pingit=>{B<on>,B<off>}
|
| 105 |
|
| 106 |
Set B<pingit> (send out pings to that remailer) for remailer I<address> to
|
| 107 |
either B<on> or B<off>.
|
| 108 |
|
| 109 |
=item B<fetch=>{B<on>,B<off>}
|
| 110 |
|
| 111 |
Set B<fetch> (fetch remailer-key and remailer-conf) for remailer I<address> to
|
| 112 |
either B<on> or B<off>.
|
| 113 |
|
| 114 |
=back
|
| 115 |
|
| 116 |
=item B<setremailercaps> I<capsstring>
|
| 117 |
|
| 118 |
Some remailers (Mixmaster V2 - currently lcs and passthru2) don't return a
|
| 119 |
useable remailer-conf message. For such remailers you need to set it manually.
|
| 120 |
|
| 121 |
For instance:
|
| 122 |
|
| 123 |
./pingd setremailercaps '$remailer{"passthru2"} = "<mixer@immd1.informatik.uni-erlangen.de> mix middle";'
|
| 124 |
./pingd setremailercaps '$remailer{"lcs"} = "<mix@anon.lcs.mit.edu> mix klen1000";'
|
| 125 |
|
| 126 |
=item B<deleteremailercaps> I<address>
|
| 127 |
|
| 128 |
Delete remailer-conf data for I<address>. The config data will be reset from
|
| 129 |
the next valid remailer-conf reply by the remailer.
|
| 130 |
|
| 131 |
=item B<getkeyconf> [I<address> [I<address> ...]]
|
| 132 |
|
| 133 |
Send a command to immediatly request keys and configuration from remailers.
|
| 134 |
If no addresses are given requests will be sent to all remailers.
|
| 135 |
|
| 136 |
=item B<sendpings> [I<address> [I<address> ...]]
|
| 137 |
|
| 138 |
Send a command to immediatly send pings to the given remailers.
|
| 139 |
If no addresses are given requests will be sent to all remailers.
|
| 140 |
|
| 141 |
=item B<buildstats>
|
| 142 |
|
| 143 |
Send a command to immediatly rebuild stats.
|
| 144 |
|
| 145 |
=item B<buildkeys>
|
| 146 |
|
| 147 |
Send a command to immediatly rebuild the keyrings.
|
| 148 |
|
| 149 |
=item B<buildthesaurus>
|
| 150 |
|
| 151 |
Send a command to immediatly rebuild the Thesaurus.
|
| 152 |
|
| 153 |
=item B<dumpconf>
|
| 154 |
|
| 155 |
Dumps the current configuration to standard output.
|
| 156 |
|
| 157 |
=back
|
| 158 |
|
| 159 |
=head1 OPTIONS
|
| 160 |
|
| 161 |
=over
|
| 162 |
|
| 163 |
=item B<--basedir>
|
| 164 |
|
| 165 |
The home directory to which everything else is relative. See the BASE
|
| 166 |
DIRECTORY section below.
|
| 167 |
|
| 168 |
=item B<--verbose>
|
| 169 |
|
| 170 |
Verbose mode. Causes B<pingd> to print debugging messages about its progress.
|
| 171 |
|
| 172 |
=item B<--quiet>
|
| 173 |
|
| 174 |
Quiet mode. Be even quieter than normal.
|
| 175 |
|
| 176 |
=item B<--help>
|
| 177 |
|
| 178 |
Print a short help message and exit sucessfully.
|
| 179 |
|
| 180 |
=item B<--version>
|
| 181 |
|
| 182 |
Print version number and exit sucessfully.
|
| 183 |
|
| 184 |
=item B<--nohup>
|
| 185 |
|
| 186 |
Usefull only with the B<add>, B<set>, B<setremailercaps>,
|
| 187 |
B<deleteremailercaps>, B<getkeyconf>, B<sendpings>, B<buildstats>,
|
| 188 |
B<buildkeys>, or B<buildthesaurus> command.
|
| 189 |
|
| 190 |
Don't send a HUP signal to the daemon which instructs it to process the
|
| 191 |
commands after adding the command to the task list.
|
| 192 |
|
| 193 |
By default such a signal is sent.
|
| 194 |
|
| 195 |
=item B<--process>
|
| 196 |
|
| 197 |
Usefull only with the B<start> command.
|
| 198 |
|
| 199 |
Read and process the commands file on startup.
|
| 200 |
|
| 201 |
=item B<--detach>
|
| 202 |
|
| 203 |
Usefull only with the B<start> command.
|
| 204 |
|
| 205 |
Tell B<pingd> to detach.
|
| 206 |
|
| 207 |
=back
|
| 208 |
|
| 209 |
=head1 BASE DIRECTORY
|
| 210 |
|
| 211 |
The home directory to which everything else is relative.
|
| 212 |
|
| 213 |
Basedir defaults to whatever directory the B<pingd> binary is located. It can
|
| 214 |
be overridden by the B<ECHOLOT_HOME> environment variable which in turn is
|
| 215 |
weaker than the B<--basedir> setting.
|
| 216 |
|
| 217 |
This directory is then used to locate the configuration file B<pingd.conf> (see
|
| 218 |
FILES below).
|
| 219 |
|
| 220 |
The B<homedir> setting in B<pingd.conf> finally sets the base directory.
|
| 221 |
|
| 222 |
=head1 FILES
|
| 223 |
|
| 224 |
The configuration file is searched in these places in this order:
|
| 225 |
|
| 226 |
=over
|
| 227 |
|
| 228 |
=item the file pointed to by the B<ECHOLOT_CONF> environment variable
|
| 229 |
|
| 230 |
=item <basedir>/pingd.conf
|
| 231 |
|
| 232 |
=item $HOME/echolot/pingd.conf
|
| 233 |
|
| 234 |
=item $HOME/pingd.conf
|
| 235 |
|
| 236 |
=item $HOME/.pingd.conf
|
| 237 |
|
| 238 |
=item /etc/echolot/pingd.conf
|
| 239 |
|
| 240 |
=item /etc/pingd.conf
|
| 241 |
|
| 242 |
=back
|
| 243 |
|
| 244 |
=head1 ENVIRONMENT
|
| 245 |
|
| 246 |
=over
|
| 247 |
|
| 248 |
=item ECHOLOT_CONF echolot config file (see section FILES)
|
| 249 |
|
| 250 |
=item ECHOLOT_HOME echolot base directory (see section BASE DIRECTORY)
|
| 251 |
|
| 252 |
=back
|
| 253 |
|
| 254 |
=head1 SIGNALS
|
| 255 |
|
| 256 |
On B<SIGINT>, B<SIGQUIT>, and B<SIGTERM> B<pingd> will schedule a shutdown
|
| 257 |
for as soon as the current actions are finished or immediatly if no actions are
|
| 258 |
currently being processed. It will then write all metadata and pingdata to
|
| 259 |
disk and close all files cleanly before exiting.
|
| 260 |
|
| 261 |
On B<SIGHUP> <pingd> will execute any pending commands from the commands file
|
| 262 |
(B<commands.txt> by default). It also closes and reopens the file 'output'
|
| 263 |
which is used for stdout and stderr when the daemon is running detached.
|
| 264 |
This can be used if you want to rotate that file.
|
| 265 |
|
| 266 |
=head1 AUTHOR
|
| 267 |
|
| 268 |
Peter Palfrader E<lt>peter@palfrader.orgE<gt>
|
| 269 |
|
| 270 |
=head1 BUGS
|
| 271 |
|
| 272 |
Please report them at E<lt>URL:http://savannah.gnu.org/bugs/?group=echolotE<gt>
|
| 273 |
|
| 274 |
=cut
|
| 275 |
|
| 276 |
use strict;
|
| 277 |
use FindBin qw{ $Bin };
|
| 278 |
use lib ( $Bin, "$Bin/lib" );
|
| 279 |
use Getopt::Long;
|
| 280 |
use English;
|
| 281 |
use Carp;
|
| 282 |
use Echolot::Config;
|
| 283 |
use Echolot::Globals;
|
| 284 |
use Echolot::Storage::File;
|
| 285 |
use Echolot::Scheduler;
|
| 286 |
use Echolot::Conf;
|
| 287 |
use Echolot::Mailin;
|
| 288 |
use Echolot::Pinger;
|
| 289 |
use Echolot::Chain;
|
| 290 |
use Echolot::Stats;
|
| 291 |
use Echolot::Commands;
|
| 292 |
use Echolot::Thesaurus;
|
| 293 |
use Echolot::Log;
|
| 294 |
use POSIX qw(setsid);
|
| 295 |
|
| 296 |
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
|
| 297 |
|
| 298 |
|
| 299 |
my $VERSION = '2.0.10';
|
| 300 |
|
| 301 |
|
| 302 |
my $redirected_stdio = 0;
|
| 303 |
|
| 304 |
sub setSigHandlers() {
|
| 305 |
$SIG{'HUP'} = sub {
|
| 306 |
Echolot::Log::info("Got SIGHUP. scheduling readcommands.");
|
| 307 |
Echolot::Globals::get()->{'scheduler'}->schedule('readcommands', 0, time() );
|
| 308 |
if ($redirected_stdio) {
|
| 309 |
close STDOUT;
|
| 310 |
close STDERR;
|
| 311 |
my $logfile = Echolot::Config::get()->{'logfile'};
|
| 312 |
open (STDOUT, ">>$logfile") or die ("Cannot open '$logfile' as STDOUT\n");
|
| 313 |
open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n");
|
| 314 |
};
|
| 315 |
Echolot::Log::reopen();
|
| 316 |
};
|
| 317 |
$SIG{'INT'} = sub {
|
| 318 |
Echolot::Log::info("Got SIGINT. scheduling exit.");
|
| 319 |
Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, time() );
|
| 320 |
};
|
| 321 |
$SIG{'QUIT'} = sub {
|
| 322 |
Echolot::Log::info("Got SIGINT. scheduling exit.");
|
| 323 |
Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, time() );
|
| 324 |
};
|
| 325 |
$SIG{'TERM'} = sub {
|
| 326 |
Echolot::Log::info("Got SIGINT. scheduling exit.");
|
| 327 |
Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, time() );
|
| 328 |
};
|
| 329 |
};
|
| 330 |
|
| 331 |
|
| 332 |
|
| 333 |
sub commit_prospective_address() {
|
| 334 |
Echolot::Globals::get()->{'storage'}->commit_prospective_address();
|
| 335 |
};
|
| 336 |
sub expire() {
|
| 337 |
Echolot::Globals::get()->{'storage'}->expire();
|
| 338 |
};
|
| 339 |
sub metadata_backup() {
|
| 340 |
Echolot::Globals::get()->{'storage'}->metadata_backup();
|
| 341 |
};
|
| 342 |
|
| 343 |
|
| 344 |
|
| 345 |
|
| 346 |
|
| 347 |
sub command_adddelete(@) {
|
| 348 |
my $command = shift @_;
|
| 349 |
my @argv = @_;
|
| 350 |
|
| 351 |
die ("command_adddelete requires command\n") unless defined $command;
|
| 352 |
die ("add requires argument <address>\n") unless scalar @argv;
|
| 353 |
my @addresses;
|
| 354 |
for my $address (@argv) {
|
| 355 |
die ("argument <address> is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
|
| 356 |
push @addresses, $address;
|
| 357 |
};
|
| 358 |
for my $address (@addresses) {
|
| 359 |
Echolot::Commands::addCommand("$command $address");
|
| 360 |
};
|
| 361 |
};
|
| 362 |
|
| 363 |
sub command_set(@) {
|
| 364 |
my @argv = @_;
|
| 365 |
|
| 366 |
my @settings;
|
| 367 |
while (scalar @argv && $argv[0] =~ /^(showit|pingit|fetch)=(on|off)$/) {
|
| 368 |
push @settings, $argv[0];
|
| 369 |
shift @argv;
|
| 370 |
};
|
| 371 |
|
| 372 |
my @addresses;
|
| 373 |
for my $address (@argv) {
|
| 374 |
die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
|
| 375 |
push @addresses, $address;
|
| 376 |
};
|
| 377 |
|
| 378 |
for my $address (@argv) {
|
| 379 |
for my $setting (@settings) {
|
| 380 |
Echolot::Commands::addCommand("set $address $setting");
|
| 381 |
};
|
| 382 |
};
|
| 383 |
};
|
| 384 |
|
| 385 |
sub command_setremailercaps(@) {
|
| 386 |
my @argv = @_;
|
| 387 |
|
| 388 |
my @caps;
|
| 389 |
for my $caps (@argv) {
|
| 390 |
my ($remailer_nick, $remailer_address) = ($caps =~ /^\s* \$remailer{"(.*)"} \s*=\s* "<(.*@.*)>.*"; \s*$/ix);
|
| 391 |
die ("caps '$caps' is not a valid remailer caps line\n") unless (defined $remailer_nick && defined $remailer_address);
|
| 392 |
push @caps, {
|
| 393 |
address => $remailer_address,
|
| 394 |
caps => $caps };
|
| 395 |
};
|
| 396 |
for my $caps (@caps) {
|
| 397 |
Echolot::Commands::addCommand("setremailercaps ".$caps->{'address'}." ".$caps->{'caps'});
|
| 398 |
};
|
| 399 |
};
|
| 400 |
|
| 401 |
sub command_deleteremailercaps(@) {
|
| 402 |
my @argv = @_;
|
| 403 |
|
| 404 |
my @addresses;
|
| 405 |
for my $address (@argv) {
|
| 406 |
die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
|
| 407 |
push @addresses, $address;
|
| 408 |
};
|
| 409 |
|
| 410 |
for my $address (@addresses) {
|
| 411 |
Echolot::Commands::addCommand("deleteremailercaps $address");
|
| 412 |
};
|
| 413 |
};
|
| 414 |
|
| 415 |
sub command_getkeyconf(@) {
|
| 416 |
my @argv = @_;
|
| 417 |
|
| 418 |
my @addresses;
|
| 419 |
for my $address (@argv) {
|
| 420 |
die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
|
| 421 |
push @addresses, $address;
|
| 422 |
};
|
| 423 |
|
| 424 |
push @addresses, 'all' unless (scalar @addresses);
|
| 425 |
|
| 426 |
for my $address (@addresses) {
|
| 427 |
Echolot::Commands::addCommand("getkeyconf $address");
|
| 428 |
};
|
| 429 |
};
|
| 430 |
|
| 431 |
sub command_sendpings(@) {
|
| 432 |
my @argv = @_;
|
| 433 |
|
| 434 |
my @addresses;
|
| 435 |
for my $address (@argv) {
|
| 436 |
die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
|
| 437 |
push @addresses, $address;
|
| 438 |
};
|
| 439 |
|
| 440 |
push @addresses, 'all' unless (scalar @addresses);
|
| 441 |
|
| 442 |
for my $address (@addresses) {
|
| 443 |
Echolot::Commands::addCommand("sendpings $address");
|
| 444 |
};
|
| 445 |
};
|
| 446 |
|
| 447 |
|
| 448 |
sub pid_exists($) {
|
| 449 |
my ($remove_stale) = @_;
|
| 450 |
|
| 451 |
my $pidfile = Echolot::Config::get()->{'pidfile'};
|
| 452 |
if (! $remove_stale) {
|
| 453 |
return (-e $pidfile);
|
| 454 |
} else {
|
| 455 |
if (!-e $pidfile) {
|
| 456 |
return 0;
|
| 457 |
};
|
| 458 |
|
| 459 |
open (PIDFILE, $pidfile) or
|
| 460 |
die ("Cannot open pidfile '$pidfile': $!.\n");
|
| 461 |
my $line = <PIDFILE>;
|
| 462 |
close PIDFILE;
|
| 463 |
|
| 464 |
my ($pid, $host, $time) = $line =~ /^(\d+) \s+ (\S+) \s+ (\d+) \s* $/x or
|
| 465 |
die ("Cannot parse pidfile '$pidfile' line '$line'.\n");
|
| 466 |
|
| 467 |
(Echolot::Globals::get()->{'hostname'} eq $host) or
|
| 468 |
die ("Pidfile exists and is from another host.\n");
|
| 469 |
($host ne 'unknown') or
|
| 470 |
die ("Pidfile exists and hostname is unknown.\n");
|
| 471 |
($time < time()) or
|
| 472 |
die ("Pidfile exists and timestamp is in the future.\n");
|
| 473 |
my $sent = kill 0, $pid;
|
| 474 |
($sent == 0) or
|
| 475 |
die ("Pidfile exists and process $pid running.\n");
|
| 476 |
warn ("Removing stale pidfile.\n");
|
| 477 |
unlink ($pidfile) or
|
| 478 |
die ("Removing stale pidfile $pidfile failed.\n");
|
| 479 |
return 0;
|
| 480 |
}
|
| 481 |
|
| 482 |
|
| 483 |
};
|
| 484 |
|
| 485 |
sub daemon_run($) {
|
| 486 |
my ($process) = @_;
|
| 487 |
|
| 488 |
Echolot::Log::logdie("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n")
|
| 489 |
if pid_exists(0);
|
| 490 |
open (PIDFILE, '>'.Echolot::Config::get()->{'pidfile'}) or
|
| 491 |
Echolot::Log::logdie("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!");
|
| 492 |
print PIDFILE "$PROCESS_ID ".Echolot::Globals::get()->{'hostname'}." ".time()."\n";
|
| 493 |
close PIDFILE;
|
| 494 |
|
| 495 |
Echolot::Globals::initStorage();
|
| 496 |
setSigHandlers();
|
| 497 |
|
| 498 |
Echolot::Globals::get()->{'scheduler'} = new Echolot::Scheduler;
|
| 499 |
my $scheduler = Echolot::Globals::get()->{'scheduler'};
|
| 500 |
$scheduler->add('exit' , -1 , 0, 'exit' );
|
| 501 |
$scheduler->add('readcommands' , -1 , 0, \&Echolot::Commands::processCommands );
|
| 502 |
|
| 503 |
$scheduler->add('processmail' , Echolot::Config::get()->{'processmail'} , 0, \&Echolot::Mailin::process );
|
| 504 |
$scheduler->add('ping' , Echolot::Config::get()->{'pinger_interval'} , 0, \&Echolot::Pinger::send_pings );
|
| 505 |
$scheduler->add('chainping' , Echolot::Config::get()->{'chainpinger_interval'} , 0, \&Echolot::Chain::send_pings )
|
| 506 |
if Echolot::Config::get()->{'do_chainpings'};
|
| 507 |
$scheduler->add('buildstats' , Echolot::Config::get()->{'buildstats'} , 0, \&Echolot::Stats::build_stats );
|
| 508 |
$scheduler->add('buildkeys' , Echolot::Config::get()->{'buildkeys'} , 0, \&Echolot::Stats::build_keys );
|
| 509 |
$scheduler->add('buildthesaurus' , Echolot::Config::get()->{'buildthesaurus'} , 0, \&Echolot::Thesaurus::build_thesaurus );
|
| 510 |
|
| 511 |
$scheduler->add('metadata_backup' , Echolot::Config::get()->{'metadata_backup'} , 0, \&metadata_backup );
|
| 512 |
$scheduler->add('commitprospectives' , Echolot::Config::get()->{'commitprospectives'} , 0, \&commit_prospective_address );
|
| 513 |
$scheduler->add('expire' , Echolot::Config::get()->{'expire'} , 0, \&expire );
|
| 514 |
$scheduler->add('getkeyconf' , Echolot::Config::get()->{'getkeyconf_interval'} , 0, \&Echolot::Conf::send_requests );
|
| 515 |
$scheduler->add('check_resurrection' , Echolot::Config::get()->{'check_resurrection'} , 0, \&Echolot::Conf::check_resurrection );
|
| 516 |
|
| 517 |
Echolot::Globals::get()->{'scheduler'}->schedule('readcommands', 0, time() )
|
| 518 |
if ($process);
|
| 519 |
|
| 520 |
$scheduler->run();
|
| 521 |
|
| 522 |
Echolot::Globals::get()->{'storage'}->commit();
|
| 523 |
Echolot::Globals::get()->{'storage'}->finish();
|
| 524 |
|
| 525 |
unlink (Echolot::Config::get()->{'pidfile'}) or
|
| 526 |
Echolot::Log::warn ("Cannot unlink pidfile ".Echolot::Config::get()->{'pidfile'}.".");
|
| 527 |
};
|
| 528 |
|
| 529 |
sub send_sig($) {
|
| 530 |
my ($sig) = @_;
|
| 531 |
|
| 532 |
die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' does not exist\n")
|
| 533 |
unless pid_exists(0);
|
| 534 |
open (PIDFILE, '<'.Echolot::Config::get()->{'pidfile'}) or
|
| 535 |
confess ("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!\n");
|
| 536 |
my $line = <PIDFILE>;
|
| 537 |
close PIDFILE;
|
| 538 |
|
| 539 |
my ($pid, $host, $time) = $line =~ /^(\d+) \s+ (\S+) \s+ (\d+) \s* $/x or
|
| 540 |
confess ("Cannot parse pidfile '$line'\n");
|
| 541 |
my $sent = kill $sig, $pid;
|
| 542 |
($sent == 1) or
|
| 543 |
confess ("Did not send signal $sig to exactly one process but $sent. (pidfile reads $line)\n");
|
| 544 |
};
|
| 545 |
|
| 546 |
sub daemon_hup() {
|
| 547 |
send_sig(1);
|
| 548 |
};
|
| 549 |
|
| 550 |
sub daemon_stop() {
|
| 551 |
send_sig(15);
|
| 552 |
};
|
| 553 |
|
| 554 |
sub make_dirs() {
|
| 555 |
for my $dir (
|
| 556 |
Echolot::Config::get()->{'resultdir'},
|
| 557 |
Echolot::Config::get()->{'thesaurusdir'},
|
| 558 |
) {
|
| 559 |
if ( ! -d $dir ) {
|
| 560 |
mkdir ($dir, 0755) or
|
| 561 |
confess ("Cannot create directory $dir: $!\n");
|
| 562 |
};
|
| 563 |
};
|
| 564 |
my @dirs = (
|
| 565 |
Echolot::Config::get()->{'private_resultdir'},
|
| 566 |
Echolot::Config::get()->{'gnupghome'},
|
| 567 |
Echolot::Config::get()->{'mixhome'},
|
| 568 |
Echolot::Config::get()->{'tmpdir'},
|
| 569 |
Echolot::Config::get()->{'storage'}->{'File'}->{'basedir'},
|
| 570 |
Echolot::Config::get()->{'mailerrordir'},
|
| 571 |
Echolot::Config::get()->{'mailerrordir'}.'/cur',
|
| 572 |
Echolot::Config::get()->{'mailerrordir'}.'/tmp',
|
| 573 |
Echolot::Config::get()->{'mailerrordir'}.'/new');
|
| 574 |
push @dirs, (
|
| 575 |
Echolot::Config::get()->{'mailin'}.'/cur',
|
| 576 |
Echolot::Config::get()->{'mailin'}.'/tmp',
|
| 577 |
Echolot::Config::get()->{'mailin'}.'/new' )
|
| 578 |
if (-d Echolot::Config::get()->{'mailin'});
|
| 579 |
|
| 580 |
for my $dir (@dirs) {
|
| 581 |
if ( ! -d $dir ) {
|
| 582 |
mkdir ($dir, 0700) or
|
| 583 |
confess ("Cannot create directory $dir: $!\n");
|
| 584 |
};
|
| 585 |
};
|
| 586 |
};
|
| 587 |
|
| 588 |
sub hup_if_wanted($) {
|
| 589 |
my ($nohup) = @_;
|
| 590 |
if (!$nohup && pid_exists(0)) {
|
| 591 |
daemon_hup()
|
| 592 |
} else {
|
| 593 |
print "Don't forget to run $PROGRAM_NAME process.\n";
|
| 594 |
};
|
| 595 |
};
|
| 596 |
|
| 597 |
|
| 598 |
|
| 599 |
|
| 600 |
|
| 601 |
|
| 602 |
|
| 603 |
|
| 604 |
|
| 605 |
|
| 606 |
my $params = { basedir => $Bin };
|
| 607 |
$params->{'basedir'} = $ENV{'ECHOLOT_HOME'} if (defined $ENV{'ECHOLOT_HOME'});
|
| 608 |
|
| 609 |
Getopt::Long::config('bundling');
|
| 610 |
if (!GetOptions (
|
| 611 |
'help' => \$params->{'help'},
|
| 612 |
'version' => \$params->{'version'},
|
| 613 |
'verbose+' => \$params->{'verbose'},
|
| 614 |
'nohup' => \$params->{'nohup'},
|
| 615 |
'detach' => \$params->{'detach'},
|
| 616 |
'process' => \$params->{'process'},
|
| 617 |
'basedir' => \$params->{'basedir'},
|
| 618 |
'quiet' => \$params->{'quiet'},
|
| 619 |
)) {
|
| 620 |
die ("$PROGRAM_NAME: Usage: $PROGRAM_NAME [-fwhv]\n");
|
| 621 |
};
|
| 622 |
if ($params->{'help'}) {
|
| 623 |
print ("Usage: $PROGRAM_NAME [options] command\n");
|
| 624 |
print ("See man pingd or perldoc pingd for more info.\n");
|
| 625 |
print ("echolot $VERSION - (c) 2002, 2003 Peter Palfrader <peter\@palfrader.org>\n");
|
| 626 |
print ("http://savannah.gnu.org/projects/echolot/\n");
|
| 627 |
print ("\n");
|
| 628 |
print ("Commands:\n");
|
| 629 |
print (" start starts echolot pingd\n");
|
| 630 |
print (" signals pingd to ... \n");
|
| 631 |
print (" stop ... shutdown\n");
|
| 632 |
print (" process ... reopen outfile and process commands\n");
|
| 633 |
print (" add ... add a remailer address\n");
|
| 634 |
print (" delete ... delete a remailer address\n");
|
| 635 |
print (" set ... set remailer options\n");
|
| 636 |
print (" setremailercaps ... set remailer capabilities manually\n");
|
| 637 |
print (" deleteremailercaps ... delete remailer capabilities manually\n");
|
| 638 |
print (" getkeyconf ... request remailer-xxx data immediatly\n");
|
| 639 |
print (" sendpings ... request immediate sending of pings\n");
|
| 640 |
print (" buildstats ... build remailer stats immediatly\n");
|
| 641 |
print (" buildkeys ... buid keyrings immediatly\n");
|
| 642 |
print (" buildthesaurus ... build thesaurus immediatly\n");
|
| 643 |
print (" dumpconf dump configuration\n");
|
| 644 |
exit 0;
|
| 645 |
};
|
| 646 |
if ($params->{'version'}) {
|
| 647 |
print ("echolot $VERSION\n");
|
| 648 |
print ("(c) 2002, 2003 Peter Palfrader <peter\@palfrader.org>\n");
|
| 649 |
print ("http://savannah.gnu.org/projects/echolot/\n");
|
| 650 |
exit 0;
|
| 651 |
};
|
| 652 |
$params->{'quiet'} = undef if ($params->{'verbose'});
|
| 653 |
|
| 654 |
my $COMMAND = shift @ARGV;
|
| 655 |
die ("command required\n") unless defined $COMMAND;
|
| 656 |
|
| 657 |
|
| 658 |
Echolot::Config::init( $params );
|
| 659 |
chdir( Echolot::Config::get()->{'homedir'} );
|
| 660 |
Echolot::Globals::init( version => $VERSION);
|
| 661 |
|
| 662 |
|
| 663 |
if ($COMMAND eq 'add' || $COMMAND eq 'delete') {
|
| 664 |
command_adddelete($COMMAND, @ARGV);
|
| 665 |
hup_if_wanted($params->{'nohup'});
|
| 666 |
} elsif ($COMMAND eq 'set') {
|
| 667 |
command_set(@ARGV);
|
| 668 |
hup_if_wanted($params->{'nohup'});
|
| 669 |
} elsif ($COMMAND eq 'deleteremailercaps') {
|
| 670 |
command_deleteremailercaps(@ARGV);
|
| 671 |
hup_if_wanted($params->{'nohup'});
|
| 672 |
} elsif ($COMMAND eq 'setremailercaps') {
|
| 673 |
command_setremailercaps(@ARGV);
|
| 674 |
hup_if_wanted($params->{'nohup'});
|
| 675 |
} elsif ($COMMAND eq 'getkeyconf') {
|
| 676 |
command_getkeyconf(@ARGV);
|
| 677 |
hup_if_wanted($params->{'nohup'});
|
| 678 |
} elsif ($COMMAND eq 'sendpings') {
|
| 679 |
command_sendpings(@ARGV);
|
| 680 |
hup_if_wanted($params->{'nohup'});
|
| 681 |
} elsif ($COMMAND eq 'buildstats') {
|
| 682 |
Echolot::Commands::addCommand("buildstats");
|
| 683 |
hup_if_wanted($params->{'nohup'});
|
| 684 |
} elsif ($COMMAND eq 'buildkeys') {
|
| 685 |
Echolot::Commands::addCommand("buildkeys");
|
| 686 |
hup_if_wanted($params->{'nohup'});
|
| 687 |
} elsif ($COMMAND eq 'buildthesaurus') {
|
| 688 |
Echolot::Commands::addCommand("buildthesaurus");
|
| 689 |
hup_if_wanted($params->{'nohup'});
|
| 690 |
} elsif ($COMMAND eq 'process') {
|
| 691 |
daemon_hup();
|
| 692 |
} elsif ($COMMAND eq 'stop') {
|
| 693 |
daemon_stop();
|
| 694 |
} elsif ($COMMAND eq 'start') {
|
| 695 |
# FIXME: remove stale pid files
|
| 696 |
die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n")
|
| 697 |
if pid_exists(1);
|
| 698 |
Echolot::Log::init();
|
| 699 |
make_dirs();
|
| 700 |
if ($params->{'detach'}) {
|
| 701 |
print "Detaching.\n" unless ($params->{'quiet'});
|
| 702 |
Echolot::Log::debug("Detaching.");
|
| 703 |
exit(0) if (fork());
|
| 704 |
POSIX::setsid();
|
| 705 |
exit(0) if (fork());
|
| 706 |
my $logfile = Echolot::Config::get()->{'logfile'};
|
| 707 |
open (STDOUT, ">>$logfile") or die ("Cannot open '$logfile' as STDOUT\n");
|
| 708 |
open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n");
|
| 709 |
open (STDIN , "</dev/null") or die ("Cannot open /dev/null as STDIN\n");
|
| 710 |
$redirected_stdio = 1;
|
| 711 |
Echolot::Log::info "Starting up.";
|
| 712 |
daemon_run( $params->{'process'} );
|
| 713 |
Echolot::Log::info "Shutdown complete.";
|
| 714 |
} else {
|
| 715 |
daemon_run( $params->{'process'} );
|
| 716 |
};
|
| 717 |
} elsif ($COMMAND eq 'dumpconf') {
|
| 718 |
Echolot::Config::dump();
|
| 719 |
} elsif ($COMMAND eq 'convert') {
|
| 720 |
Echolot::Globals::initStorage();
|
| 721 |
setSigHandlers();
|
| 722 |
|
| 723 |
Echolot::Globals::get()->{'storage'}->convert();
|
| 724 |
|
| 725 |
Echolot::Globals::get()->{'storage'}->commit();
|
| 726 |
Echolot::Globals::get()->{'storage'}->finish();
|
| 727 |
} else {
|
| 728 |
die ("Command $COMMAND unknown");
|
| 729 |
};
|
| 730 |
|
| 731 |
exit 0;
|
| 732 |
|
| 733 |
# vim: set ts=4 shiftwidth=4:
|