diff options
Diffstat (limited to 'FS/bin/freeside-selfservice-server')
| -rw-r--r-- | FS/bin/freeside-selfservice-server | 240 | 
1 files changed, 240 insertions, 0 deletions
| diff --git a/FS/bin/freeside-selfservice-server b/FS/bin/freeside-selfservice-server new file mode 100644 index 000000000..2087e7130 --- /dev/null +++ b/FS/bin/freeside-selfservice-server @@ -0,0 +1,240 @@ +#!/usr/bin/perl -w + +use strict; +use vars qw( $FREESIDE_LOG $FREESIDE_LOCK ); +use vars qw( $Debug %kids $kids $max_kids $ssh_pid %old_ssh_pid $keepalives ); +use subs qw( lock_write unlock_write myshutdown usage ); +use Fcntl qw(:flock); +use POSIX qw(:sys_wait_h); +use IO::Handle; +use IO::Select; +use IO::File; +use Storable 2.09 qw(nstore_fd fd_retrieve); +use Net::SSH qw(sshopen2); +use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm); +use FS::UID qw(adminsuidsetup forksuidsetup); +use FS::ClientAPI; +use FS::ClientAPI_SessionCache; + +use FS::Conf; +use FS::cust_svc; + +$FREESIDE_LOG = "%%%FREESIDE_LOG%%%"; +$FREESIDE_LOCK = "%%%FREESIDE_LOCK%%%"; + +$Debug = 1; # 2 will turn on more logging +            # 3 will log packet contents, including passwords + +$max_kids = '10'; #? +$keepalives = 0; #let clientd turn it on, so we don't barf on old ones +$kids = 0; + +my $user = shift or die &usage; +my $machine = shift or die &usage; +my $tag = scalar(@ARGV) ? shift : ''; + +my $lock_file = "$FREESIDE_LOCK/selfservice.$machine.writelock"; + +# to keep pid files unique w/multi machines (and installs!) +# $FS::UID::datasrc not posible +daemonize1("freeside-selfservice-server","$user.$machine"); + +#false laziness w/Daemon::drop_root +my $freeside_gid = scalar(getgrnam('freeside')) +  or die "can't find freeside group\n"; + +open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!"; +chown $FS::UID::freeside_uid, $freeside_gid, $lock_file; + +drop_root(); + +$ENV{HOME} = (getpwuid($>))[7]; #for ssh + +adminsuidsetup $user; + +#logfile("/usr/local/etc/freeside/selfservice.". $FS::UID::datasrc); #MACHINE +logfile("$FREESIDE_LOG/selfservice.$machine.log"); + +daemonize2(); + +my $conf = new FS::Conf; +if ( $conf->exists('selfservice-ignore_quantity') ) { +  $FS::cust_svc::ignore_quantity = 1; +  $FS::cust_svc::ignore_quantity = 1; #now it is used twice. +} + +#clear the signup info cache so an "/etc/init.d/freeside restart" will pick +#up new info... (better as a callback in Signup.pm?) +my $cache = new FS::ClientAPI_SessionCache( { +  'namespace' => 'FS::ClientAPI::Signup', +} ); +$cache->remove('signup_info_cache'); + +my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name? + +my $warnkids=0; +while (1) { +  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,$tag); + +#  nstore_fd(\*writer, {'hi'=>'there'}); + +  warn "entering main loop\n" if $Debug; +  my $undisp = 0; +  my $keepalive_count = 0; +  my $s = IO::Select->new( $reader ); +  while (1) { + +    &reap_kids; + +    warn "waiting for packet from client\n" if $Debug && !$undisp; +    $undisp = 1; +    my @handles = $s->can_read(5); +    unless ( @handles ) { +      myshutdown() if sigint() || sigterm(); +      if ( $keepalives && $keepalive_count++ > 10 ) { +        $keepalive_count = 0; +        lock_write; +        nstore_fd( { _token => '_keepalive' }, $writer ); +        unlock_write; +      } +      next; +    } + +    $undisp = 0; + +    warn "receiving packet from client\n" if $Debug; + +    my $packet = eval { fd_retrieve($reader); }; +    if ( $@ ) { +      warn "Storable error receiving packet from client". +           " (assuming lost connection): $@\n" +        if $Debug; +      if ( $ssh_pid ) { +        warn "sending TERM signal to ssh process $ssh_pid\n" if $Debug; +        kill 'TERM', $ssh_pid; +        $old_ssh_pid{$ssh_pid} = 1; +        $ssh_pid = 0; +      } +      last; +    } +    warn "packet received\n". +         join('', map { " $_=>$packet->{$_}\n" } keys %$packet ) +      if $Debug > 2; + +    if ( $packet->{_packet} eq '_enable_keepalive' ) { +      warn "enabling keep alives\n" if $Debug; +      $keepalives=1; +      next; +    } + +    #prevent runaway forking +    my $warnkids = 0; +    while ( $kids >= $max_kids ) { +      warn "WARNING: maximum $kids children reached\n" unless $warnkids++; +      &reap_kids; +      sleep 1; +    } + +    warn "forking child\n" if $Debug; +    defined( my $pid = fork ) or die "can't fork: $!"; +    if ( $pid ) { +      $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); + +      #get db handle +      #adminsuidsetup($user); + +      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 + +      open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!"; +      lock_write; +      warn "sending response\n" if $Debug; +      nstore_fd($rv, $writer) or die "FATAL: can't send response: $!"; +      $writer->flush or die "FATAL: can't flush: $!"; +      unlock_write; + +      warn "child exiting\n" if $Debug; +      exit; #end-of-kid +    } + +  } + +  myshutdown if sigint() || sigterm(); +  warn "connection lost, reconnecting\n" if $Debug; +  sleep 3; + +} + +### +# 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}; +    } +  } + +  foreach my $pid ( keys %old_ssh_pid ) { +    waitpid($pid, WNOHANG) and delete $old_ssh_pid{$pid}; +  } +  #warn "done reaping\n"; +} + +sub myshutdown { +  &reap_kids; +  my $wait = 12; #wait up to 1 minute +  while ( $kids > 0 && $wait-- ) { +    warn "waiting for $kids children to terminate"; +    sleep 5; +    &reap_kids; +  } +  warn "abandoning $kids children" if $kids; +  kill 'TERM', $ssh_pid if $ssh_pid; +  die "exiting"; +} + +sub lock_write { +  warn "locking $lock_file mutex for write to write stream\n" if $Debug > 1; + +  #broken on freebsd? +  #flock($writer, LOCK_EX) or die "FATAL: can't lock write stream: $!"; + +  flock(LOCKFILE, LOCK_EX) or die "FATAL: can't lock $lock_file: $!"; + +} + +sub unlock_write { +  warn "unlocking $lock_file mutex\n" if $Debug > 1; + +  #broken on freebsd? +  #flock($writer, LOCK_UN) or die "WARNING: can't release write lock: $!"; + +  flock(LOCKFILE, LOCK_UN) or die "FATAL: can't unlock $lock_file: $!"; + +} + +sub usage { +  die "Usage:\n\n  freeside-selfservice-server user machine\n"; +} + | 
