#!/usr/bin/perl -w # # freeside-selfservice-server # alas, much false laziness with freeside-queued and fs_signup_server. at # least it is slated to replace fs_{signup,passwd,mailadmin}_server # should probably generalize the version in here, or better yet use # Proc::Daemon or somesuch use strict; use vars qw( $kids $max_kids $shutdown $log_file ); use vars qw($ssh_pid); use Fcntl qw(:flock); use POSIX qw(setsid); use IO::Handle; use Storable qw(nstore_fd fd_retrieve); use Net::SSH qw(sshopen2); use FS::UID qw(adminsuidsetup); #use Tie::RefHash; #use FS::Conf; #use FS::Record qw( qsearch qsearchs ); #use FS::cust_main_county; #use FS::cust_main; #use FS::Msgcat qw(gettext); $shutdown = 0; $max_kids = '10'; #? $kids = 0; my $user = shift or die &usage; my $machine = shift or die &usage; my $pid_file = "/var/run/freeside-selfservice-server.$user.pid"; #my $pid_file = "/var/run/freeside-selfservice-server.$user.pid"; $FS::UID::datasrc not posible, but should include machine name at least, hmm &init($user); my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name? my %dispatch = ( 'signup' => \&signup, #'signup_init' => 'signup_init', 'passwd' => \&passwd, ); my $warnkids=0; while (1) { my($reader, $writer) = (new IO::Handle, new IO::Handle); warn "connecting to $machine"; $ssh_pid = sshopen2($machine,$reader,$writer,$clientd); warn "entering main loop"; while (1) { warn "waiting for packet from client"; my $packet = eval { local $SIG{__DIE__}; local $SIG{ALRM} = sub { die "alarm\n" }; #NB: \n required alarm 5; my $p = fd_retrieve($reader); alarm 0; $p; }; if ($@) { die $@ unless $@ eq "alarm\n"; #timeout next unless $shutdown; &shutdown; } warn "packet received"; #prevent runaway forking my $warnkids = 0; while ( $kids >= $max_kids ) { warn "WARNING: maximum $kids children reached" unless $warnkids++; sleep 1; } warn "forking child"; defined( my $pid = fork ) or die "can't fork: $!"; if ( $pid ) { warn "child $pid spawned"; $kids++; } else { #kid time #get new db handle $FS::UID::dbh->{InactiveDestroy} = 1; forksuidsetup($user); my $sub = $dispatch{$packet->{_packet}}; my $rv; if ( $sub ) { warn "calling $sub handler"; $rv = &{$sub}($packet); } else { warn my $error = "WARNING: unknown packet type ". $packet->{_packet}; $rv = { _error => $error }; } $rv->{_token} = $packet->{_token}; #identifier warn "sending response"; flock($writer, LOCK_EX); #acquire write lock nstore_fd($rv, $writer) or die "can't send response: $!"; $writer->flush; flock($writer, LOCK_UN); #release write lock warn "child exiting"; exit; #end-of-kid } } } ### # utility subroutines ### sub init { my $user = shift; chdir "/" or die "Can't chdir to /: $!"; open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; defined(my $pid = fork) or die "Can't fork: $!"; if ( $pid ) { print "freeside-selfservice-server to $machine started with pid $pid\n"; #logging to $log_file exit unless $pid_file; my $pidfh = new IO::File ">$pid_file" or exit; print $pidfh "$pid\n"; exit; } sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; } $SIG{CHLD} = \&REAPER; $shutdown = 0; $SIG{HUP} = sub { warn "SIGHUP received; shutting down\n"; $shutdown++; }; $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $shutdown++; }; $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $shutdown++; }; $SIG{QUIT} = sub { warn "SIGQUIT received; shutting down\n"; $shutdown++; }; $SIG{PIPE} = sub { warn "SIGPIPE received; shutting down\n"; $shutdown++; }; $> = $FS::UID::freeside_uid unless $>; $< = $>; $ENV{HOME} = (getpwuid($>))[7]; #for ssh adminsuidsetup $user; #$log_file = "/usr/local/etc/freeside/selfservice.". $FS::UID::datasrc; #MACHINE NAME $log_file = "/usr/local/etc/freeside/selfservice.$machine.log"; open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!"; setsid or die "Can't start a new session: $!"; open STDERR, '>&STDOUT' or die "Can't dup stdout: $!"; $SIG{__DIE__} = \&_die; $SIG{__WARN__} = \&_logmsg; warn "freeside-selfservice-server starting\n"; } sub shutdown { my $wait = 12; #wait up to 1 minute while ( $kids && $wait-- ) { warn "waiting for $kids children to terminate"; sleep 5; } warn "abandoning $kids children" if $kids; kill 'TERM', $ssh_pid if $ssh_pid; die "exiting"; } sub _die { my $msg = shift; unlink $pid_file if -e $pid_file; _logmsg($msg); } sub _logmsg { chomp( my $msg = shift ); my $log = new IO::File ">>$log_file"; flock($log, LOCK_EX); seek($log, 0, 2); print $log "[server] [". time2str("%a %b %e %T %Y",time). "] [$$] $msg\n"; flock($log, LOCK_UN); close $log; } sub usage { die "Usage:\n\n fs_signup_server user machine\n"; } ### # handlers... should go in their own files eventually... ###