added stuff for selfservice_server-quiet, signup_server-quiet, and
[freeside.git] / fs_selfservice / freeside-selfservice-server
index 6146d37..264cbc5 100644 (file)
@@ -8,21 +8,23 @@
 # Proc::Daemon or somesuch
 
 use strict;
-use vars qw( $kids $max_kids $shutdown $log_file );
-use vars qw($ssh_pid);
+use vars qw( $Debug %kids $kids $max_kids $shutdown $log_file $ssh_pid );
 use Fcntl qw(:flock);
-use POSIX qw(setsid);
+use POSIX qw(:sys_wait_h setsid);
 use IO::Handle;
+use IO::Select;
+use IO::File;
 use Storable qw(nstore_fd fd_retrieve);
 use Net::SSH qw(sshopen2);
-use FS::UID qw(adminsuidsetup);
+use FS::UID qw(adminsuidsetup forksuidsetup);
+use FS::ClientAPI;
 
-#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);
+use FS::Conf;
+use FS::cust_bill;
+use FS::cust_pkg;
+
+$Debug = 2; # >= 2 will log packet contents, including potentially compromising
+            # information
 
 $shutdown = 0;
 $max_kids = '10'; #?
@@ -35,77 +37,84 @@ my $pid_file = "/var/run/freeside-selfservice-server.$user.pid";
 
 &init($user);
 
-my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name?
+my $conf = new FS::Conf;
 
-my %dispatch = (
-  'signup' => \&signup,
-  #'signup_init' => 'signup_init',
-  'passwd' => \&passwd,
+if ($conf->exists('selfservice_server-quiet')) {
+    $FS::cust_bill::quiet = 1;
+    $FS::cust_pkg::quiet = 1;
+}
 
-);
+my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name?
 
 my $warnkids=0;
 while (1) {
-  my($reader, $writer) = (new IO::Handle, new IO::Handle);
-  warn "connecting to $machine";
+  my($writer,$reader,$error) = (new IO::Handle, new IO::Handle, new IO::Handle);
+  warn "connecting to $machine\n" if $Debug;
+
   $ssh_pid = sshopen2($machine,$reader,$writer,$clientd);
 
-  warn "entering main loop";
+#  nstore_fd(\*writer, {'hi'=>'there'});
+
+  warn "entering main loop\n" if $Debug;
+  my $undisp = 0;
+  my $s = IO::Select->new( $reader );
   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;
+    &reap_kids;
+
+    warn "waiting for packet from client\n" if $Debug && !$undisp;
+    $undisp = 1;
+    my @handles = $s->can_read(5);
+    unless ( @handles ) {
+      &shutdown if $shutdown;
+      next;
     }
-    warn "packet received";
+
+    $undisp = 0;
+
+    warn "receiving packet from client\n" if $Debug;
+
+    my $packet = fd_retrieve($reader);
+    warn "packet received\n".
+         join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
+      if $Debug > 1;
 
     #prevent runaway forking
     my $warnkids = 0;
     while ( $kids >= $max_kids ) {
-      warn "WARNING: maximum $kids children reached" unless $warnkids++;
+      warn "WARNING: maximum $kids children reached\n" unless $warnkids++;
+      &reap_kids;
       sleep 1;
     }
 
-    warn "forking child";
+    warn "forking child\n" if $Debug;
     defined( my $pid = fork ) or die "can't fork: $!";
     if ( $pid ) {
-      warn "child $pid spawned";
       $kids++;
+      $kids{$pid} = 1;
+      warn "child $pid spawned\n" if $Debug;
     } 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};
+      my $type = $packet->{_packet};
+      warn "calling $type handler\n" if $Debug; 
+      my $rv = eval { FS::ClientAPI->dispatch($type, $packet); };
+      if ( $@ ) {
+        warn my $error = "WARNING: error dispatching $type: $@";
         $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 "sending response\n" if $Debug;
+      flock($writer, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+      nstore_fd($rv, $writer) or die "FATAL: can't send response: $!";
+      $writer->flush or die "FATAL: can't flush: $!";
+      flock($writer, LOCK_UN) or die "WARNING: can't release write lock: $!";
 
-      warn "child exiting";
+      warn "child exiting\n" if $Debug;
       exit; #end-of-kid
     }
 
@@ -117,11 +126,23 @@ while (1) {
 # utility subroutines
 ###
 
+sub reap_kids {
+  #warn "reaping kids\n";
+  foreach my $pid ( keys %kids ) {
+    my $kid = waitpid($pid, WNOHANG);
+    if ( $kid > 0 ) {
+      $kids--;
+      delete $kids{$kid};
+    }
+  }
+  #warn "done reaping\n";
+}
+
 sub init {
   my $user = shift;
 
   chdir "/" or die "Can't chdir to /: $!";
-  open STDIN, '/dev/null'   or die "Can't read /dev/null: $!";
+  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
@@ -131,8 +152,9 @@ sub init {
     exit;
   }
 
-  sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
-  $SIG{CHLD} =  \&REAPER;
+#  sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
+#  #sub REAPER { my $pid = wait; $kids--; $SIG{CHLD} = \&REAPER; }
+#  $SIG{CHLD} =  \&REAPER;
 
   $shutdown = 0;
   $SIG{HUP} = sub { warn "SIGHUP received; shutting down\n"; $shutdown++; };
@@ -141,8 +163,22 @@ sub init {
   $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 $>;
-  $< = $>;
+  #false laziness w/freeside-queued
+  my $freeside_gid = scalar(getgrnam('freeside'))
+    or die "can't setgid to freeside group\n";
+  $) = $freeside_gid;
+  $( = $freeside_gid;
+  #if freebsd can't setuid(), presumably it can't setgid() either.  grr fleabsd
+  ($(,$)) = ($),$();
+  $) = $freeside_gid;
+
+  $> = $FS::UID::freeside_uid;
+  $< = $FS::UID::freeside_uid;
+  #freebsd is sofa king broken, won't setuid()
+  ($<,$>) = ($>,$<);
+  $> = $FS::UID::freeside_uid;
+  #eslaf
+
   $ENV{HOME} = (getpwuid($>))[7]; #for ssh
   adminsuidsetup $user;
 
@@ -163,7 +199,7 @@ sub init {
 
 sub shutdown {
   my $wait = 12; #wait up to 1 minute
-  while ( $kids && $wait-- ) {
+  while ( $kids > 0 && $wait-- ) {
     warn "waiting for $kids children to terminate";
     sleep 5;
   }
@@ -180,10 +216,15 @@ sub _die {
 
 sub _logmsg {
   chomp( my $msg = shift );
+  _do_logmsg( "[server] [". scalar(localtime). "] [$$] $msg\n" );
+}
+
+sub _do_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";
+  print $log "$msg\n";
   flock($log, LOCK_UN);
   close $log;
 }
@@ -192,7 +233,3 @@ sub usage {
   die "Usage:\n\n  fs_signup_server user machine\n";
 }
 
-###
-# handlers... should go in their own files eventually...
-###
-