summaryrefslogtreecommitdiff
path: root/trunk/pingd
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/pingd')
-rwxr-xr-xtrunk/pingd854
1 files changed, 854 insertions, 0 deletions
diff --git a/trunk/pingd b/trunk/pingd
new file mode 100755
index 0000000..39681b6
--- /dev/null
+++ b/trunk/pingd
@@ -0,0 +1,854 @@
+#!/usr/bin/perl -w
+#
+# $Id$
+#
+# This file is part of Echolot - a Pinger for anonymous remailers.
+#
+# Copyright (c) 2002, 2003, 2004 Peter Palfrader <peter@palfrader.org>
+#
+# This program is free software. you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+$| = 1;
+
+=pod
+
+=head1 NAME
+
+pingd - echolot ping daemon
+
+=head1 SYNOPSIS
+
+=over
+
+=item B<pingd> B<start>
+
+=item B<pingd> B<stop>
+
+=item B<pingd> B<process>
+
+=item B<pingd> B<add> I<address> [I<address> ...]
+
+=item B<pingd> B<delete> I<address> [I<address> ...]
+
+=item B<pingd> B<disable> I<address> [I<address> ...]
+
+=item B<pingd> B<enable> I<address> [I<address> ...]
+
+=item B<pingd> B<set> option=value [option=value..] I<address> [I<address> ...]
+
+=item B<pingd> B<setremailercaps> I<capsstring>
+
+=item B<pingd> B<deleteremailercaps> I<address>
+
+=item B<pingd> B<getkeyconf> [I<address> [I<address> ...]]
+
+=item B<pingd> B<sendpings> [I<address> [I<address> ...]]
+
+=item B<pingd> B<sendchainpings> I<address>B<:>I<address> [I<address>B<:>I<address> ...]
+
+=item B<pingd> B<buildstats>
+
+=item B<pingd> B<buildkeys>
+
+=item B<pingd> B<buildthesaurus>
+
+=item B<pingd> B<buildfromlines>
+
+=item B<pingd> B<summary>
+
+=item B<pingd> B<dumpconf>
+
+=back
+
+=head1 DESCRIPTION
+
+pingd is the heart of echolot. Echolot is a pinger for anonymous remailers.
+
+A Pinger in the context of anonymous remailers is a program that regularily
+sends messages through remailers to check their reliability. It then calculates
+reliability statistics which are used by remailer clients to choose the chain
+of remailers to use.
+
+Additionally it collects configuration parameters and keys of all remailers and
+offers them in a format readable by remailer clients.
+
+When called without parameters pingd schedules tasks like sending pings,
+processing incoming mail and requesting remailer-xxx data and runs them at
+configurable intervalls.
+
+=head1 COMMANDS
+
+=over
+
+=item B<start>
+
+Start the ping daemon.
+
+=item B<stop>
+
+Send the running pingd process a SIGTERM.
+
+=item B<process>
+
+Sends a HUP signal to the daemon which instructs it to process the commands.
+
+For other effects of sending the HUP Signal see the SIGNALS section below.
+
+=item B<add> I<address> [I<address> ...]
+
+Add I<address> to the list of remailers to query for
+keys and confs.
+
+=item B<delete> I<address> [I<address> ...]
+
+Delete I<address> from the list of remailers to query for
+keys and confs. Delete all statistics and keys for that remailer.
+
+Note that echolot will add back this remailer if it learns of it from other
+remailers again. If you do not want that, use the disable command.
+
+=item B<disable> I<address> [I<address> ...]
+
+Shorthand for B<set showit=off> B<pingit=off> B<fetch=off>. This makes echolot
+completely ignore that remailer, until you enable it again.
+
+=item B<enable> I<address> [I<address> ...]
+
+Shorthand for B<set showit=on> B<pingit=on> B<fetch=on>.
+
+=item B<set> option=value [option=value..] I<address> [I<address> ...]
+
+Possible options and values:
+
+=over
+
+=item B<showit=>{B<on>,B<off>}
+
+Set B<showit> (show remailer in mlist, rlist etc.) for remailer I<address> to
+either B<on> or B<off>.
+
+=item B<pingit=>{B<on>,B<off>}
+
+Set B<pingit> (send out pings to that remailer) for remailer I<address> to
+either B<on> or B<off>.
+
+=item B<fetch=>{B<on>,B<off>}
+
+Set B<fetch> (fetch remailer-key and remailer-conf) for remailer I<address> to
+either B<on> or B<off>.
+
+=back
+
+=item B<setremailercaps> I<capsstring>
+
+Some remailers (Mixmaster V2 - currently lcs and passthru2) don't return a
+useable remailer-conf message. For such remailers you need to set it manually.
+
+For instance:
+
+ ./pingd setremailercaps '$remailer{"passthru2"} = "<mixer@immd1.informatik.uni-erlangen.de> mix middle";'
+ ./pingd setremailercaps '$remailer{"lcs"} = "<mix@anon.lcs.mit.edu> mix klen1000";'
+
+=item B<deleteremailercaps> I<address>
+
+Delete remailer-conf data for I<address>. The config data will be reset from
+the next valid remailer-conf reply by the remailer.
+
+=item B<getkeyconf> [I<address> [I<address> ...]]
+
+Send a command to immediatly request keys and configuration from remailers.
+If no addresses are given requests will be sent to all remailers.
+
+=item B<sendpings> [I<address> [I<address> ...]]
+
+Send a command to immediatly send pings to the given remailers.
+If no addresses are given requests will be sent to all remailers.
+
+=item B<sendchainpings> I<address>B<:>I<address> [I<address>B<:>I<address> ...]
+
+Send a command to immediatly send chain pings to the given chains.
+A chain is two remailer addresses seperated by a colon.
+
+=item B<buildstats>
+
+Send a command to immediatly rebuild stats.
+
+=item B<buildkeys>
+
+Send a command to immediatly rebuild the keyrings.
+
+=item B<buildthesaurus>
+
+Send a command to immediatly rebuild the Thesaurus.
+
+=item B<buildfromlines>
+
+Send a command to immediatly rebuild the From Header lines page.
+
+=item B<summary>
+
+Print a status summary of all known addresses to the log (level notice).
+
+=item B<dumpconf>
+
+Dumps the current configuration to standard output.
+
+=back
+
+=head1 OPTIONS
+
+=over
+
+=item B<--basedir>
+
+The home directory to which everything else is relative. See the BASE
+DIRECTORY section below.
+
+=item B<--verbose>
+
+Verbose mode. Causes B<pingd> to print debugging messages about its progress.
+
+=item B<--quiet>
+
+Quiet mode. Be even quieter than normal.
+
+=item B<--help>
+
+Print a short help message and exit sucessfully.
+
+=item B<--version>
+
+Print version number and exit sucessfully.
+
+=item B<--nohup>
+
+Usefull only with the B<add>, B<set>, B<setremailercaps>,
+B<deleteremailercaps>, B<getkeyconf>, B<sendpings>, B<sendchainpings>,
+B<buildstats>, B<buildkeys>, B<buildthesaurus>, B<buildfromlines>,
+or B<summary> command.
+
+Don't send a HUP signal to the daemon which instructs it to process the
+commands after adding the command to the task list.
+
+By default such a signal is sent.
+
+=item B<--process>
+
+Usefull only with the B<start> command.
+
+Read and process the commands file on startup.
+
+=item B<--detach>
+
+Usefull only with the B<start> command.
+
+Tell B<pingd> to detach.
+
+=back
+
+=head1 BASE DIRECTORY
+
+The home directory to which everything else is relative.
+
+Basedir defaults to whatever directory the B<pingd> binary is located. It can
+be overridden by the B<ECHOLOT_HOME> environment variable which in turn is
+weaker than the B<--basedir> setting.
+
+This directory is then used to locate the configuration file B<pingd.conf> (see
+FILES below).
+
+The B<homedir> setting in B<pingd.conf> finally sets the base directory.
+
+=head1 FILES
+
+The configuration file is searched in these places in this order:
+
+=over
+
+=item the file pointed to by the B<ECHOLOT_CONF> environment variable
+
+=item <basedir>/pingd.conf
+
+=item $HOME/echolot/pingd.conf
+
+=item $HOME/pingd.conf
+
+=item $HOME/.pingd.conf
+
+=item /etc/echolot/pingd.conf
+
+=item /etc/pingd.conf
+
+=back
+
+=head1 ENVIRONMENT
+
+=over
+
+=item ECHOLOT_CONF echolot config file (see section FILES)
+
+=item ECHOLOT_HOME echolot base directory (see section BASE DIRECTORY)
+
+=back
+
+=head1 SIGNALS
+
+On B<SIGINT>, B<SIGQUIT>, and B<SIGTERM> B<pingd> will schedule a shutdown
+for as soon as the current actions are finished or immediatly if no actions are
+currently being processed. It will then write all metadata and pingdata to
+disk and close all files cleanly before exiting.
+
+On B<SIGHUP> <pingd> will execute any pending commands from the commands file
+(B<commands.txt> by default). It also closes and reopens the file 'output'
+which is used for stdout and stderr when the daemon is running detached.
+This can be used if you want to rotate that file.
+
+=head1 AUTHOR
+
+Peter Palfrader E<lt>peter@palfrader.orgE<gt>
+
+=head1 BUGS
+
+Please report them at E<lt>URL:http://alioth.debian.org/projects/echolot/<gt>
+
+=cut
+
+use strict;
+use FindBin qw{ $Bin };
+use lib ( $Bin, "$Bin/lib" );
+use Getopt::Long;
+use English;
+use Carp;
+use Echolot::Config;
+use Echolot::Globals;
+use Echolot::Storage::File;
+use Echolot::Scheduler;
+use Echolot::Conf;
+use Echolot::Mailin;
+use Echolot::Pinger;
+use Echolot::Chain;
+use Echolot::Stats;
+use Echolot::Commands;
+use Echolot::Thesaurus;
+use Echolot::Fromlines;
+use Echolot::Report;
+use Echolot::Log;
+use POSIX qw(setsid);
+
+delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+
+my $VERSION = '2.1.8';
+
+
+my $redirected_stdio = 0;
+
+sub setSigHandlers() {
+ $SIG{'HUP'} = sub {
+ Echolot::Log::info("Got SIGHUP. scheduling readcommands.");
+ Echolot::Globals::get()->{'scheduler'}->schedule('readcommands', 0, time() );
+ if ($redirected_stdio) {
+ close STDOUT;
+ close STDERR;
+ my $logfile = Echolot::Config::get()->{'logfile'};
+ open (STDOUT, ">>$logfile") or die ("Cannot open '$logfile' as STDOUT\n");
+ open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n");
+ };
+ Echolot::Log::reopen();
+ };
+ $SIG{'INT'} = sub {
+ Echolot::Log::info("Got SIGINT. scheduling exit.");
+ Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, 0 );
+ };
+ $SIG{'QUIT'} = sub {
+ Echolot::Log::info("Got SIGQUIT. scheduling exit.");
+ Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, 0 );
+ };
+ $SIG{'TERM'} = sub {
+ Echolot::Log::info("Got SIGTERM. scheduling exit.");
+ Echolot::Globals::get()->{'scheduler'}->schedule('exit', 0, 0 );
+ };
+ $SIG{'PIPE'} = sub {
+ Echolot::Log::error("Got SIGPIPE");
+ };
+};
+
+
+
+sub commit_prospective_address() {
+ Echolot::Globals::get()->{'storage'}->commit_prospective_address();
+};
+sub expire() {
+ Echolot::Globals::get()->{'storage'}->expire();
+};
+sub metadata_backup() {
+ Echolot::Globals::get()->{'storage'}->metadata_backup();
+};
+
+
+
+
+
+sub command_adddelete(@) {
+ my $command = shift @_;
+ my @argv = @_;
+
+ die ("command_adddelete requires command\n") unless defined $command;
+ die ("add requires argument <address>\n") unless scalar @argv;
+ my @addresses;
+ for my $address (@argv) {
+ die ("argument <address> is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
+ push @addresses, $address;
+ };
+ for my $address (@addresses) {
+ Echolot::Commands::addCommand("$command $address");
+ };
+};
+
+sub command_set(@) {
+ my @argv = @_;
+
+ my @settings;
+ while (scalar @argv && $argv[0] =~ /^(show(?:it)?|ping(?:it)?|fetch(?:it)?)=(on|off)$/) {
+ my $name = $1;
+ my $value = $2;
+
+ $name = 'showit' if ($name eq 'show');
+ $name = 'pingit' if ($name eq 'ping');
+ $name = 'fetch' if ($name eq 'fetchit');
+
+ push @settings, "$name=$value";
+ shift @argv;
+ };
+
+ my @addresses;
+ for my $address (@argv) {
+ die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
+ push @addresses, $address;
+ };
+
+ for my $address (@argv) {
+ for my $setting (@settings) {
+ Echolot::Commands::addCommand("set $address $setting");
+ };
+ };
+};
+
+sub command_enabledisable (@) {
+ my @argv = @_;
+
+ my $command = shift @argv;
+ my $value;
+ if ($command eq 'enable') {
+ $value='on';
+ } elsif ($command eq 'disable') {
+ $value='off';
+ } else {
+ die ("argument $command is not enable or disable. Shouldn't even be here.\n");
+ };
+ my @cmd = ("showit=$value", "pingit=$value", "fetch=$value");
+ push @cmd, @argv;
+ command_set(@cmd);
+}
+
+
+sub command_setremailercaps(@) {
+ my @argv = @_;
+
+ my @caps;
+ for my $caps (@argv) {
+ my ($remailer_nick, $remailer_address) = ($caps =~ /^\s* \$remailer{"(.*)"} \s*=\s* "<(.*@.*)>.*"; \s*$/ix);
+ die ("caps '$caps' is not a valid remailer caps line\n") unless (defined $remailer_nick && defined $remailer_address);
+ push @caps, {
+ address => $remailer_address,
+ caps => $caps };
+ };
+ for my $caps (@caps) {
+ Echolot::Commands::addCommand("setremailercaps ".$caps->{'address'}." ".$caps->{'caps'});
+ };
+};
+
+sub command_deleteremailercaps(@) {
+ my @argv = @_;
+
+ my @addresses;
+ for my $address (@argv) {
+ die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
+ push @addresses, $address;
+ };
+
+ for my $address (@addresses) {
+ Echolot::Commands::addCommand("deleteremailercaps $address");
+ };
+};
+
+sub command_getkeyconf(@) {
+ my @argv = @_;
+
+ my @addresses;
+ for my $address (@argv) {
+ die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
+ push @addresses, $address;
+ };
+
+ push @addresses, 'all' unless (scalar @addresses);
+
+ for my $address (@addresses) {
+ Echolot::Commands::addCommand("getkeyconf $address");
+ };
+};
+
+sub command_sendpings(@) {
+ my @argv = @_;
+
+ my @addresses;
+ for my $address (@argv) {
+ die ("argument $address is not a valid email address\n") unless ($address =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
+ push @addresses, $address;
+ };
+
+ push @addresses, 'all' unless (scalar @addresses);
+
+ for my $address (@addresses) {
+ Echolot::Commands::addCommand("sendpings $address");
+ };
+};
+
+sub command_sendchainpings(@) {
+ my @argv = @_;
+
+ my @args;
+ for my $arg (@argv) {
+ my ($addr1, $addr2) = split /:/, $arg;
+ die ("Argument $arg is not in the form address1:address2") unless defined $addr1 && defined $addr2;
+ die ("Address 1 in $arg ('$addr1') is not a valid email address\n") unless ($addr1 =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
+ die ("Address 2 in $arg ('$addr2') is not a valid email address\n") unless ($addr2 =~ /^[a-zA-Z0-9+._-]+\@[a-zA-Z0-9+.-]+$/ );
+ push @args, [ $addr1, $addr2 ];
+ };
+
+ for my $arg (@args) {
+ Echolot::Commands::addCommand("sendchainpings $arg->[0] $arg->[1]");
+ };
+};
+
+
+sub pid_exists($) {
+ my ($remove_stale) = @_;
+
+ my $pidfile = Echolot::Config::get()->{'pidfile'};
+ if (! $remove_stale) {
+ return (-e $pidfile);
+ } else {
+ if (!-e $pidfile) {
+ return 0;
+ };
+
+ open (PIDFILE, $pidfile) or
+ die ("Cannot open pidfile '$pidfile': $!.\n");
+ my $line = <PIDFILE>;
+ close PIDFILE;
+
+ my ($pid, $host, $time) = $line =~ /^(\d+) \s+ (\S+) \s+ (\d+) \s* $/x or
+ die ("Cannot parse pidfile '$pidfile' line '$line'.\n");
+
+ (Echolot::Globals::get()->{'hostname'} eq $host) or
+ die ("Pidfile exists and is from another host.\n");
+ ($host ne 'unknown') or
+ die ("Pidfile exists and hostname is unknown.\n");
+ ($time < time()) or
+ die ("Pidfile exists and timestamp is in the future.\n");
+ my $sent = kill 0, $pid;
+ ($sent == 0) or
+ die ("Pidfile exists and process $pid running.\n");
+ warn ("Removing stale pidfile.\n");
+ unlink ($pidfile) or
+ die ("Removing stale pidfile $pidfile failed.\n");
+ return 0;
+ }
+
+
+};
+
+sub daemon_run($) {
+ my ($process) = @_;
+
+ Echolot::Log::logdie("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n")
+ if pid_exists(0);
+ open (PIDFILE, '>'.Echolot::Config::get()->{'pidfile'}) or
+ Echolot::Log::logdie("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!");
+ print PIDFILE "$PROCESS_ID ".Echolot::Globals::get()->{'hostname'}." ".time()."\n";
+ close PIDFILE;
+
+ Echolot::Globals::initStorage();
+ setSigHandlers();
+
+ Echolot::Globals::get()->{'scheduler'} = new Echolot::Scheduler;
+ my $scheduler = Echolot::Globals::get()->{'scheduler'};
+ $scheduler->add('exit' , -1 , 0, 0, 'exit' );
+ $scheduler->add('readcommands' , -1 , 0, 1, \&Echolot::Commands::processCommands );
+
+ $scheduler->add('processmail' , Echolot::Config::get()->{'processmail'} , 0, 1, \&Echolot::Mailin::process );
+ $scheduler->add('ping' , Echolot::Config::get()->{'pinger_interval'} , 0, 0, \&Echolot::Pinger::send_pings );
+ $scheduler->add('chainping' , Echolot::Config::get()->{'chainpinger_interval'} , 0, 0, \&Echolot::Chain::send_pings );
+ $scheduler->add('buildstats' , Echolot::Config::get()->{'buildstats'} , 0, 1, \&Echolot::Stats::build_stats );
+ $scheduler->add('buildkeys' , Echolot::Config::get()->{'buildkeys'} , 0, 1, \&Echolot::Stats::build_keys );
+ $scheduler->add('buildthesaurus' , Echolot::Config::get()->{'buildthesaurus'} , 0, 1, \&Echolot::Thesaurus::build_thesaurus );
+ $scheduler->add('buildfromlines' , Echolot::Config::get()->{'buildfromlines'} , 0, 1, \&Echolot::Fromlines::build_fromlines );
+
+ $scheduler->add('metadata_backup' , Echolot::Config::get()->{'metadata_backup'} , 0, 1, \&metadata_backup );
+ $scheduler->add('commitprospectives' , Echolot::Config::get()->{'commitprospectives'} , 0, 1, \&commit_prospective_address );
+ $scheduler->add('expire' , Echolot::Config::get()->{'expire'} , 0, 1, \&expire );
+ $scheduler->add('getkeyconf' , Echolot::Config::get()->{'getkeyconf_interval'} , 0, 0, \&Echolot::Conf::send_requests );
+ $scheduler->add('check_resurrection' , Echolot::Config::get()->{'check_resurrection'} , 0, 0, \&Echolot::Conf::check_resurrection );
+ $scheduler->add('cleanup_tmp' , Echolot::Config::get()->{'cleanup_tmpdir'} , 0, 1, \&Echolot::Tools::cleanup_tmp );
+
+ $scheduler->add('summary' , Echolot::Config::get()->{'summary'} , 0, 1, \&Echolot::Report::print_summary );
+
+ Echolot::Globals::get()->{'scheduler'}->schedule('readcommands', 0, time() )
+ if ($process);
+
+ $scheduler->run();
+
+ Echolot::Globals::get()->{'storage'}->commit();
+ Echolot::Globals::get()->{'storage'}->finish();
+
+ unlink (Echolot::Config::get()->{'pidfile'}) or
+ Echolot::Log::warn ("Cannot unlink pidfile ".Echolot::Config::get()->{'pidfile'}.".");
+};
+
+sub send_sig($) {
+ my ($sig) = @_;
+
+ die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' does not exist\n")
+ unless pid_exists(0);
+ open (PIDFILE, '<'.Echolot::Config::get()->{'pidfile'}) or
+ confess ("Cannot open pidfile '".Echolot::Config::get()->{'pidfile'}."': $!\n");
+ my $line = <PIDFILE>;
+ close PIDFILE;
+
+ my ($pid, $host, $time) = $line =~ /^(\d+) \s+ (\S+) \s+ (\d+) \s* $/x or
+ confess ("Cannot parse pidfile '$line'\n");
+ my $sent = kill $sig, $pid;
+ ($sent == 1) or
+ confess ("Did not send signal $sig to exactly one process but $sent. (pidfile reads $line)\n");
+};
+
+sub daemon_hup() {
+ send_sig(1);
+};
+
+sub daemon_stop() {
+ send_sig(15);
+};
+
+sub make_dirs() {
+ for my $dir (
+ Echolot::Config::get()->{'resultdir'},
+ Echolot::Config::get()->{'thesaurusdir'},
+ ) {
+ if ( ! -d $dir ) {
+ mkdir ($dir, 0755) or
+ confess ("Cannot create directory $dir: $!\n");
+ };
+ };
+ my @dirs = (
+ Echolot::Config::get()->{'private_resultdir'},
+ Echolot::Config::get()->{'gnupghome'},
+ Echolot::Config::get()->{'mixhome'},
+ Echolot::Config::get()->{'tmpdir'},
+ Echolot::Config::get()->{'storage'}->{'File'}->{'basedir'},
+ Echolot::Config::get()->{'mailerrordir'},
+ Echolot::Config::get()->{'mailerrordir'}.'/cur',
+ Echolot::Config::get()->{'mailerrordir'}.'/tmp',
+ Echolot::Config::get()->{'mailerrordir'}.'/new');
+ push @dirs, (
+ Echolot::Config::get()->{'mailin'}.'/cur',
+ Echolot::Config::get()->{'mailin'}.'/tmp',
+ Echolot::Config::get()->{'mailin'}.'/new' )
+ if (-d Echolot::Config::get()->{'mailin'});
+
+ for my $dir (@dirs) {
+ if ( ! -d $dir ) {
+ mkdir ($dir, 0700) or
+ confess ("Cannot create directory $dir: $!\n");
+ };
+ };
+};
+
+sub hup_if_wanted($) {
+ my ($nohup) = @_;
+ if (!$nohup && pid_exists(0)) {
+ daemon_hup()
+ } else {
+ print "Don't forget to run $PROGRAM_NAME process.\n";
+ };
+};
+
+
+
+
+
+
+
+
+($EUID == 0) and
+ die("$PROGRAM_NAME should not be run as root.\n");
+
+my $params = { basedir => $Bin };
+$params->{'basedir'} = $ENV{'ECHOLOT_HOME'} if (defined $ENV{'ECHOLOT_HOME'});
+
+Getopt::Long::config('bundling');
+if (!GetOptions (
+ 'help' => \$params->{'help'},
+ 'version' => \$params->{'version'},
+ 'verbose+' => \$params->{'verbose'},
+ 'nohup' => \$params->{'nohup'},
+ 'detach' => \$params->{'detach'},
+ 'process' => \$params->{'process'},
+ 'basedir' => \$params->{'basedir'},
+ 'quiet' => \$params->{'quiet'},
+ )) {
+ die ("$PROGRAM_NAME: Usage: $PROGRAM_NAME [-fwhv]\n");
+};
+if ($params->{'help'}) {
+ print ("Usage: $PROGRAM_NAME [options] command\n");
+ print ("See man pingd or perldoc pingd for more info.\n");
+ print ("echolot $VERSION - (c) 2002, 2003, 2004 Peter Palfrader <peter\@palfrader.org>\n");
+ print ("http://alioth.debian.org/projects/echolot/\n");
+ print ("\n");
+ print ("Commands:\n");
+ print (" start starts echolot pingd\n");
+ print (" signals pingd to ... \n");
+ print (" stop ... shutdown\n");
+ print (" process ... reopen outfile and process commands\n");
+ print (" add ... add a remailer address\n");
+ print (" delete ... delete a remailer address\n");
+ print (" disable ... disable a remailer\n");
+ print (" enable ... enable a remailer\n");
+ print (" set ... set remailer options\n");
+ print (" setremailercaps ... set remailer capabilities manually\n");
+ print (" deleteremailercaps ... delete remailer capabilities manually\n");
+ print (" getkeyconf ... request remailer-xxx data immediatly\n");
+ print (" sendpings ... request immediate sending of pings\n");
+ print (" sendchainpings ... request immediate sending of chainpings\n");
+ print (" buildstats ... build remailer stats immediatly\n");
+ print (" buildkeys ... buid keyrings immediatly\n");
+ print (" buildthesaurus ... build thesaurus immediatly\n");
+ print (" buildfromlines ... build fromlines immediatly\n");
+ print (" summary ... print status summary to log\n");
+ print (" dumpconf ... dump configuration\n");
+ exit 0;
+};
+if ($params->{'version'}) {
+ print ("echolot $VERSION\n");
+ print ("(c) 2002, 2003, 2004 Peter Palfrader <peter\@palfrader.org>\n");
+ print ("http://alioth.debian.org/projects/echolot/\n");
+ exit 0;
+};
+$params->{'quiet'} = undef if ($params->{'verbose'});
+
+my $COMMAND = shift @ARGV;
+die ("command required\n") unless defined $COMMAND;
+
+
+Echolot::Config::init( $params );
+chdir( Echolot::Config::get()->{'homedir'} );
+Echolot::Globals::init( version => $VERSION);
+
+
+if ($COMMAND eq 'add' || $COMMAND eq 'delete') {
+ command_adddelete($COMMAND, @ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'set') {
+ command_set(@ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'enable' || $COMMAND eq 'disable') {
+ command_enabledisable($COMMAND, @ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'deleteremailercaps') {
+ command_deleteremailercaps(@ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'setremailercaps') {
+ command_setremailercaps(@ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'getkeyconf') {
+ command_getkeyconf(@ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'sendpings') {
+ command_sendpings(@ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'sendchainpings') {
+ command_sendchainpings(@ARGV);
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'buildstats') {
+ Echolot::Commands::addCommand("buildstats");
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'buildkeys') {
+ Echolot::Commands::addCommand("buildkeys");
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'buildthesaurus') {
+ Echolot::Commands::addCommand("buildthesaurus");
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'buildfromlines') {
+ Echolot::Commands::addCommand("buildfromlines");
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'summary') {
+ Echolot::Commands::addCommand("summary");
+ hup_if_wanted($params->{'nohup'});
+} elsif ($COMMAND eq 'process') {
+ daemon_hup();
+} elsif ($COMMAND eq 'stop') {
+ daemon_stop();
+} elsif ($COMMAND eq 'start') {
+ # FIXME: remove stale pid files
+ die ("Pidfile '".Echolot::Config::get()->{'pidfile'}."' exists\n")
+ if pid_exists(1);
+ Echolot::Log::init();
+ make_dirs();
+ Echolot::Config::check_binaries();
+ if ($params->{'detach'}) {
+ print "Detaching.\n" unless ($params->{'quiet'});
+ Echolot::Log::debug("Detaching.");
+ exit(0) if (fork());
+ POSIX::setsid();
+ exit(0) if (fork());
+ my $logfile = Echolot::Config::get()->{'logfile'};
+ open (STDOUT, ">>$logfile") or die ("Cannot open '$logfile' as STDOUT\n");
+ open (STDERR, ">&STDOUT") or die ("Cannot dup STDOUT as STDERR\n");
+ open (STDIN , "</dev/null") or die ("Cannot open /dev/null as STDIN\n");
+ $redirected_stdio = 1;
+ Echolot::Log::info "Starting up.";
+ daemon_run( $params->{'process'} );
+ Echolot::Log::info "Shutdown complete.";
+ } else {
+ daemon_run( $params->{'process'} );
+ };
+} elsif ($COMMAND eq 'dumpconf') {
+ Echolot::Config::dump();
+} elsif ($COMMAND eq 'convert') {
+ Echolot::Globals::initStorage();
+ setSigHandlers();
+
+ Echolot::Globals::get()->{'storage'}->convert();
+
+ Echolot::Globals::get()->{'storage'}->commit();
+ Echolot::Globals::get()->{'storage'}->finish();
+} else {
+ die ("Command $COMMAND unknown");
+};
+
+exit 0;
+
+# vim: set ts=4 shiftwidth=4: