/[echolot]/trunk/pingd
ViewVC logotype

Contents of /trunk/pingd

Parent Directory Parent Directory | Revision Log Revision Log


Revision 105 - (show annotations) (download)
Wed Jul 3 12:54:31 2002 UTC (10 years, 11 months ago) by weasel
File size: 13953 byte(s)
Also show date on done
1 #!/usr/bin/perl -wT
2
3 # (c) 2002 Peter Palfrader <peter@palfrader.org>
4 # $Id: pingd,v 1.16 2002/07/03 12:54:31 weasel Exp $
5 #
6
7 =pod
8
9 =head1 NAME
10
11 pingd - echolot ping daemon
12
13 =head1 SYNOPSIS
14
15 =over
16
17 =item B<pingd> B<start>
18
19 =item B<pingd> B<stop>
20
21 =item B<pingd> B<process>
22
23 =item B<pingd> B<add> I<address> [I<address> ...]
24
25 =item B<pingd> B<delete> I<address> [I<address> ...]
26
27 =item B<pingd> B<set> option=value [option=value..] I<address> [I<address> ...]
28
29 =item B<pingd> B<setremailercaps> I<capsstring>
30
31 =item B<pingd> B<deleteremailercaps> I<address>
32
33 =item B<pingd> B<getkeyconf>
34
35 =item B<pingd> B<dumpconf>
36
37 =back
38
39 =head1 DESCRIPTION
40
41 pingd is a the heart of echolot. Echolot is a pinger for anonymous remailers.
42
43 A Pinger in the context of anonymous remailers is a program that regularily
44 sends messages through remailers to check their reliability. It then calculates
45 reliability statistics which are used by remailer clients to choose the chain
46 of remailers to use.
47
48 Additionally it collects configuration parameters and keys of all remailers and
49 offers them in a format readable by remailer clients.
50
51 When called without parameters pingd schedules tasks like sending pings,
52 processing incoming mail and requesting remailer-xxx data and runs them in
53 configurable intervalls.
54
55 =head1 COMMANDS
56
57 =over
58
59 =item B<start>
60
61 Start the ping daemon.
62
63 =item B<stop>
64
65 Send the running pingd process a SIGTERM.
66
67 =item B<process>
68
69 Sends a HUP signal to the daemon which instructs it to process the commands.
70
71 For other affects of sending the HUP Signal see the SIGNALS section below.
72
73 =item B<add> I<address> [I<address> ...]
74
75 Add I<address> to the list of remailers to query for
76 keys and confs.
77
78 =item B<delete> I<address> [I<address> ...]
79
80 Delete I<address> from the list of remailers to query for
81 keys and confs. Delete all statistics and key for that remailer.
82
83 =item B<set> option=value [option=value..] I<address> [I<address> ...]
84
85 Possible options and values:
86
87 =over
88
89 =item B<showit=>{B<on>,B<off>}
90
91 Set B<showit> (show remailer in mlist, rlist etc.) for remailer I<address> to
92 either B<on> or B<off>.
93
94 =item B<pingit=>{B<on>,B<off>}
95
96 Set B<pingit> (send out pings to that remailer) for remailer I<address> to
97 either B<on> or B<off>.
98
99 =item B<fetch=>{B<on>,B<off>}
100
101 Set B<fetch> (fetch remailer-key and remailer-conf) for remailer I<address> to
102 either B<on> or B<off>.
103
104 =back
105
106 =item B<setremailercaps> I<capsstring>
107
108 Some remailers (Mixmaster V2 - currently lcs and passthru2) don't return a
109 useable remailer-conf message. For such remailers you need to set it manually.
110
111 For instance:
112
113 ./pingd setremailercaps '$remailer{"passthru2"} = "<mixer@immd1.informatik.uni-erlangen.de> mix middle";'
114 ./pingd setremailercaps '$remailer{"lcs"} = "<mix@anon.lcs.mit.edu> mix klen1000";'
115
116 =item B<deleteremailercaps> I<address>
117
118 Delete remailer-conf data for I<address>. The config data will be reset from
119 the next valid remailer-conf reply by the remailer.
120
121 =item B<getkeyconf>
122
123 Send a command to immediatly request keys and configuration from remailers.
124
125 =item B<dumpconf>
126
127 Dumps the current configuration to standard output.
128
129 =back
130
131 =head1 OPTIONS
132
133 =over
134
135 =item --verbose
136
137 Verbose mode. Causes B<pingd> to print debugging messages about its progress.
138
139 =item --help
140
141 Print a short help and exit sucessfully.
142
143 =item --nohup
144
145 Usefull only when passwd with the B<add>, B<set>, B<setremailercaps>,
146 B<deleteremailercaps> or B<getkeyconf> command.
147
148 Don't send a HUP signal to the daemon which instructs it to process the
149 commands after adding the command to the task list.
150
151 Per default such a signal is sent.
152
153 =item --detach
154
155 Usefull only when passwd with the B<start> command.
156
157 Tell B<pingd> to detach.
158
159 =back
160
161 =head1 FILES
162
163 F<pingd.conf>
164
165 =head1 SIGNALS
166
167 On B<SIGINT>, B<SIGQUIT>, and B<SIGTERM> B<pingd> will schedule a shutdown
168 for as soon as the current actions are finished or immediatly if no actions are
169 currently beeing processed. It will then write all metadata and pingdata to
170 disk and close all files cleanly before exiting.
171
172 On B<SIGHUP> <pingd> will execute any pending commands from the commands file
173 (B<commands.txt> per default). It also closes and reopens the file 'output'
174 which is used for stdout and stderr in case the daemon was told to detach.
175 This can be used if you want to rotate that file.
176
177 =head1 AUTHOR
178
179 Peter Palfrader E<lt>peter@palfrader.orgE<gt>
180
181 =head1 SEE ALSO
182
183 echolot Documentation
184
185 =head1 BUGS
186
187 Please report them at E<lt>URL:http://savannah.gnu.org/bugs/?group=echolotE<gt>
188
189 =cut
190
191 use strict;
192 use XML::Parser;
193 use XML::Dumper;
194 use Getopt::Long;
195 use English;
196 use Carp;
197 use lib qw{ . lib };
198 use Echolot::Config;
199 use Echolot::Globals;
200 use Echolot::Storage::File;
201 use Echolot::Scheduler;
202 use Echolot::Conf;
203 use Echolot::Mailin;
204 use Echolot::Pinger;
205 use Echolot::Stats;
206 use Echolot::Commands;
207
208 $ENV{'PATH'} = '/bin:/usr/bin';
209 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
210
211
212 my $redirected_stdio = 0;
213
214 sub setSigHandlers() {
215 $SIG{'HUP'} = sub {
216 print "Got SIGINT. scheduling readcommands\n";
217 Echolot::Globals::get()->{'scheduler'}->schedule('readcommands', time() );
218 if ($redirected_stdio) {
219 close STDOUT;
220 close STDERR;
221 open (STDOUT, ">>output") or die ("Cannot open 'output' as STDOUT\n");
222 open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n");
223 };
224 };
225 $SIG{'INT'} = sub {
226 print "Got SIGINT. scheduling exit\n";
227 Echolot::Globals::get()->{'scheduler'}->schedule('exit', time() );
228 };
229 $SIG{'QUIT'} = sub {
230 print "Got SIGQUIT. scheduling exit\n";
231 Echolot::Globals::get()->{'scheduler'}->schedule('exit', time() );
232 };
233 $SIG{'TERM'} = sub {
234 print "Got SIGTERM. scheduling exit\n";
235 Echolot::Globals::get()->{'scheduler'}->schedule('exit', time() );
236 };
237 };
238
239
240
241 sub commit_prospective_address() {
242 Echolot::Globals::get()->{'storage'}->commit_prospective_address();
243 };
244 sub expire() {
245 Echolot::Globals::get()->{'storage'}->expire();
246 };
247
248
249
250
251
252 sub command_adddelete(@) {
253 my $command = shift @_;
254 my @argv = @_;
255
256 die ("command_adddelete requires command\n") unless defined $command;
257 die ("add requires argument <address>\n") unless scalar @argv;
258 my @addresses;
259 for my $address (@argv) {
260 die ("argument <address> is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
261 push @addresses, $address;
262 };
263 for my $address (@addresses) {
264 Echolot::Commands::addCommand("$command $address");
265 };
266 };
267
268 sub command_set(@) {
269 my @argv = @_;
270
271 my @settings;
272 while (scalar @argv && $argv[0] =~ /^(showit|pingit|fetch)=(on|off)$/) {
273 push @settings, $argv[0];
274 shift @argv;
275 };
276
277 my @addresses;
278 for my $address (@argv) {
279 die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
280 push @addresses, $address;
281 };
282
283 for my $address (@argv) {
284 for my $setting (@settings) {
285 Echolot::Commands::addCommand("set $address $setting");
286 };
287 };
288 };
289
290 sub command_setremailercaps(@) {
291 my @argv = @_;
292
293 my @caps;
294 for my $caps (@argv) {
295 my ($remailer_nick, $remailer_address) = ($caps =~ /^\s* \$remailer{"(.*)"} \s*=\s* "<(.*@.*)>.*"; \s*$/ix);
296 die ("caps '$caps' is not a valid remailer caps line\n") unless (defined $remailer_nick && defined $remailer_address);
297 push @caps, {
298 address => $remailer_address,
299 caps => $caps };
300 };
301 for my $caps (@caps) {
302 Echolot::Commands::addCommand("setremailercaps ".$caps->{'address'}." ".$caps->{'caps'});
303 };
304 };
305
306 sub command_deleteremailercaps(@) {
307 my @argv = @_;
308
309 my @addresses;
310 for my $address (@argv) {
311 die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
312 push @addresses, $address;
313 };
314
315 for my $address (@argv) {
316 Echolot::Commands::addCommand("deleteremailercaps $address");
317 };
318 };
319
320
321 sub pid_exists() {
322 return (-e Echolot::Config::get()->{'pidfile'});
323 };
324
325 sub daemon_run() {
326 die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n")
327 if pid_exists();
328 open (PIDFILE, '>'.Echolot::Config::get()->{'pidfile'}) or
329 croak ("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!\n");
330 print PIDFILE "$PROCESS_ID ".Echolot::Globals::get()->{'hostname'}." ".time()."\n";
331 close PIDFILE;
332
333 Echolot::Globals::initStorage();
334 setSigHandlers();
335
336 Echolot::Globals::get()->{'scheduler'} = new Echolot::Scheduler;
337 my $scheduler = Echolot::Globals::get()->{'scheduler'};
338 $scheduler->add('exit' , -1 , 0, 'exit' );
339 $scheduler->add('readcommands' , -1 , 0, \&Echolot::Commands::processCommands );
340
341 $scheduler->add('processmail' , Echolot::Config::get()->{'processmail'} , 0, \&Echolot::Mailin::process );
342 $scheduler->add('ping' , Echolot::Config::get()->{'pinger_interval'} , 0, \&Echolot::Pinger::send_pings );
343 $scheduler->add('buildstats' , Echolot::Config::get()->{'buildstats'} , 0, \&Echolot::Stats::build );
344
345 $scheduler->add('commitprospectives' , Echolot::Config::get()->{'commitprospectives'} , 0, \&commit_prospective_address );
346 $scheduler->add('expire' , Echolot::Config::get()->{'expire'} , 0, \&expire );
347 $scheduler->add('getkeyconf' , Echolot::Config::get()->{'getkeyconf'} , 0, \&Echolot::Conf::send_requests );
348
349 $scheduler->run();
350
351 Echolot::Globals::get()->{'storage'}->commit();
352 Echolot::Globals::get()->{'storage'}->finish();
353
354 unlink (Echolot::Config::get()->{'pidfile'}) or
355 cluck ("Cannot unlink pidfile ".Echolot::Config::get()->{'pidfile'});
356 };
357
358 sub send_sig($) {
359 my ($sig) = @_;
360
361 die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' does exist\n")
362 unless pid_exists();
363 open (PIDFILE, '<'.Echolot::Config::get()->{'pidfile'}) or
364 croak ("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!\n");
365 my $line = <PIDFILE>;
366 close PIDFILE;
367
368 my ($pid, $host, $time) = $line =~ /^(\d+) \s+ (\S+) \s+ (\d+) \s* $/x or
369 croak ("Cannot parse pidfile '$line'\n");
370 my $sent = kill $sig, $pid;
371 ($sent == 1) or
372 croak ("Did not send signal $sig to exactly one process but $sent. (pidfile reads $line)\n");
373 };
374
375 sub daemon_hup() {
376 send_sig(1);
377 };
378
379 sub daemon_stop() {
380 send_sig(15);
381 };
382
383 sub make_dirs() {
384 for my $dir (
385 Echolot::Config::get()->{'resultdir'},
386 Echolot::Config::get()->{'private_resultdir'},
387 Echolot::Config::get()->{'gnupghome'},
388 Echolot::Config::get()->{'tmpdir'},
389 Echolot::Config::get()->{'storage'}->{'File'}->{'basedir'}
390 ) {
391 if ( ! -d $dir ) {
392 mkdir ($dir) or
393 croak ("Cannot create directory $dir: $!\n");
394 };
395 };
396 for my $dir (
397 Echolot::Config::get()->{'mailindir'},
398 Echolot::Config::get()->{'mailindir'}.'/cur',
399 Echolot::Config::get()->{'mailindir'}.'/tmp',
400 Echolot::Config::get()->{'mailindir'}.'/new',
401 Echolot::Config::get()->{'mailerrordir'},
402 Echolot::Config::get()->{'mailerrordir'}.'/cur',
403 Echolot::Config::get()->{'mailerrordir'}.'/tmp',
404 Echolot::Config::get()->{'mailerrordir'}.'/new'
405 ) {
406 if ( ! -d $dir ) {
407 mkdir ($dir, 0700) or
408 croak ("Cannot create directory $dir: $!\n");
409 };
410 };
411 };
412
413
414
415
416
417
418
419
420
421
422
423
424 my $params;
425 Getopt::Long::config('bundling');
426 if (!GetOptions (
427 'help' => \$params->{'help'},
428 'verbose' => \$params->{'verbose'},
429 'nohup' => \$params->{'nohup'},
430 'detach' => \$params->{'detach'},
431 )) {
432 die ("$PROGRAM_NAME: Usage: $PROGRAM_NAME [-fwhv]\n");
433 };
434 if ($params->{'help'}) {
435 print ("Usage: $PROGRAM_NAME [options]\n"); #FIXME
436 exit 0;
437 };
438
439 my $COMMAND = shift @ARGV;
440 die ("command required\n") unless defined $COMMAND;
441
442
443 Echolot::Config::init( $params );
444 chdir( Echolot::Config::get()->{'homedir'} );
445 Echolot::Globals::init();
446
447
448 if ($COMMAND eq 'add' || $COMMAND eq 'delete') {
449 command_adddelete($COMMAND, @ARGV);
450 if (!$params->{'nohup'} && pid_exists()) {
451 daemon_hup()
452 } else {
453 print "Don't forget to run $PROGRAM_NAME process. You may also use --hup in the future\n";
454 };
455 } elsif ($COMMAND eq 'set') {
456 command_set(@ARGV);
457 if (!$params->{'nohup'} && pid_exists()) {
458 daemon_hup()
459 } else {
460 print "Don't forget to run $PROGRAM_NAME process. You may also use --hup in the future\n";
461 };
462 } elsif ($COMMAND eq 'setremailercaps') {
463 command_setremailercaps(@ARGV);
464 if (!$params->{'nohup'} && pid_exists()) {
465 daemon_hup()
466 } else {
467 print "Don't forget to run $PROGRAM_NAME process. You may also use --hup in the future\n";
468 };
469 } elsif ($COMMAND eq 'deleteremailercaps') {
470 command_deleteremailercaps(@ARGV);
471 if (!$params->{'nohup'} && pid_exists()) {
472 daemon_hup()
473 } else {
474 print "Don't forget to run $PROGRAM_NAME process. You may also use --hup in the future\n";
475 };
476 } elsif ($COMMAND eq 'getkeyconf') {
477 Echolot::Commands::addCommand("getkeyconf");
478 if (!$params->{'nohup'} && pid_exists()) {
479 daemon_hup()
480 } else {
481 print "Don't forget to run $PROGRAM_NAME process. You may also use --hup in the future\n";
482 };
483 } elsif ($COMMAND eq 'process') {
484 daemon_hup();
485 } elsif ($COMMAND eq 'stop') {
486 daemon_stop();
487 } elsif ($COMMAND eq 'start') {
488 die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n")
489 if pid_exists();
490 make_dirs();
491 if ($params->{'detach'}) {
492 print "Detaching.\n";
493 unless (fork()) {
494 close STDOUT;
495 close STDERR;
496 open (STDOUT, ">>output") or die ("Cannot open 'output' as STDOUT\n");
497 open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n");
498 close STDIN;
499 $redirected_stdio = 1;
500 print "Startup at ".scalar localtime().".\n";
501 daemon_run();
502 print "done at ".scalar localtime().".\n";
503 };
504 } else {
505 daemon_run();
506 };
507 } elsif ($COMMAND eq 'dumpconf') {
508 Echolot::Config::dump();
509 } elsif ($COMMAND eq 'convert') {
510 Echolot::Globals::initStorage();
511 setSigHandlers();
512
513 Echolot::Globals::get()->{'storage'}->convert();
514
515 Echolot::Globals::get()->{'storage'}->commit();
516 Echolot::Globals::get()->{'storage'}->finish();
517 } else {
518 die ("Command $COMMAND unknown");
519 };
520
521 exit 0;
522
523 # vim: set ts=4 shiftwidth=4:

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.5