/[echolot]/branches/snapshot-2003-02-17-branch/pingd
ViewVC logotype

Contents of /branches/snapshot-2003-02-17-branch/pingd

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5