3 # freeside-selfservice-server
5 # alas, much false laziness with freeside-queued and fs_signup_server. at
6 # least it is slated to replace fs_{signup,passwd,mailadmin}_server
7 # should probably generalize the version in here, or better yet use
8 # Proc::Daemon or somesuch
11 use vars qw( $Debug %kids $kids $max_kids $shutdown $log_file $ssh_pid );
13 use POSIX qw(:sys_wait_h setsid);
17 use Storable qw(nstore_fd fd_retrieve);
18 use Net::SSH qw(sshopen2);
20 use FS::UID qw(adminsuidsetup forksuidsetup);
24 #use FS::Record qw( qsearch qsearchs );
25 #use FS::cust_main_county;
27 #use FS::Msgcat qw(gettext);
29 $Debug = 1; # >= 2 will log packet contents, including potentially compromising
36 my $user = shift or die &usage;
37 my $machine = shift or die &usage;
38 my $pid_file = "/var/run/freeside-selfservice-server.$user.pid";
39 #my $pid_file = "/var/run/freeside-selfservice-server.$user.pid"; $FS::UID::datasrc not posible, but should include machine name at least, hmm
43 my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name?
47 #'signup_init' => 'signup_init',
54 my($writer,$reader,$error) = (new IO::Handle, new IO::Handle, new IO::Handle);
55 warn "connecting to $machine\n" if $Debug;
57 $ssh_pid = sshopen2($machine,$reader,$writer,$clientd);
59 # nstore_fd(\*writer, {'hi'=>'there'});
61 warn "entering main loop\n" if $Debug;
63 my $s = IO::Select->new( $reader );
68 warn "waiting for packet from client\n" if $Debug && !$undisp;
70 my @handles = $s->can_read(5);
72 &shutdown if $shutdown;
78 warn "receiving packet from client\n" if $Debug;
80 my $packet = fd_retrieve($reader);
81 warn "packet received\n".
82 join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
85 #prevent runaway forking
87 while ( $kids >= $max_kids ) {
88 warn "WARNING: maximum $kids children reached\n" unless $warnkids++;
93 warn "forking child\n" if $Debug;
94 defined( my $pid = fork ) or die "can't fork: $!";
98 warn "child $pid spawned\n" if $Debug;
102 $FS::UID::dbh->{InactiveDestroy} = 1;
103 forksuidsetup($user);
105 my $sub = $dispatch{$packet->{_packet}};
108 warn "calling $sub handler\n" if $Debug;
109 $rv = &{$sub}($packet);
111 warn my $error = "WARNING: unknown packet type ". $packet->{_packet};
112 $rv = { _error => $error };
114 $rv->{_token} = $packet->{_token}; #identifier
116 warn "sending response\n" if $Debug;
117 flock($writer, LOCK_EX) or die "FATAL: can't lock write stream: $!";
118 nstore_fd($rv, $writer) or die "FATAL: can't send response: $!";
119 $writer->flush or die "FATAL: can't flush: $!";
120 flock($writer, LOCK_UN) or die "WARNING: can't release write lock: $!";
122 warn "child exiting\n" if $Debug;
131 # dispatch subroutines (should live elsewhere eventually)
136 use FS::Record qw(qsearchs);
142 #my $domain = qsearchs('svc_domain', { 'domain' => $packet->{'domain'} } )
143 # or return { error => "Domain $domain not found" };
145 my $old_password = $packet->{'old_password'};
146 my $new_password = $packet->{'new_password'};
147 my $new_gecos = $packet->{'new_gecos'};
148 my $new_shell = $packet->{'new_shell'};
151 ( length($old_password) < 13
152 && qsearchs( 'svc_acct', { 'username' => $packet->{'username'},
153 #'domsvc' => $svc_domain->domsvc,
154 '_password' => $old_password } )
156 || qsearchs( 'svc_acct', { 'username' => $packet->{'username'},
157 #'domsvc' => $svc_domain->domsvc,
158 '_password' => $old_password } );
160 unless ( $svc_acct ) { return { error => 'Incorrect password.' } }
162 my %hash = $svc_acct->hash;
163 my $new_svc_acct = new FS::svc_acct ( \%hash );
164 $new_svc_acct->setfield('_password', $new_password )
165 if $new_password && $new_password ne $old_password;
166 $new_svc_acct->setfield('finger',$new_gecos) if $new_gecos;
167 $new_svc_acct->setfield('shell',$new_shell) if $new_shell;
168 my $error = $new_svc_acct->replace($svc_acct);
170 return { error => $error };
175 # utility subroutines
179 #warn "reaping kids\n";
180 foreach my $pid ( keys %kids ) {
181 my $kid = waitpid($pid, WNOHANG);
187 #warn "done reaping\n";
193 chdir "/" or die "Can't chdir to /: $!";
194 open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
195 defined(my $pid = fork) or die "Can't fork: $!";
197 print "freeside-selfservice-server to $machine started with pid $pid\n"; #logging to $log_file
198 exit unless $pid_file;
199 my $pidfh = new IO::File ">$pid_file" or exit;
200 print $pidfh "$pid\n";
204 # sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
205 # #sub REAPER { my $pid = wait; $kids--; $SIG{CHLD} = \&REAPER; }
206 # $SIG{CHLD} = \&REAPER;
209 $SIG{HUP} = sub { warn "SIGHUP received; shutting down\n"; $shutdown++; };
210 $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $shutdown++; };
211 $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $shutdown++; };
212 $SIG{QUIT} = sub { warn "SIGQUIT received; shutting down\n"; $shutdown++; };
213 $SIG{PIPE} = sub { warn "SIGPIPE received; shutting down\n"; $shutdown++; };
215 #false laziness w/freeside-queued
216 my $freeside_gid = scalar(getgrnam('freeside'))
217 or die "can't setgid to freeside group\n";
220 #if freebsd can't setuid(), presumably it can't setgid() either. grr fleabsd
224 $> = $FS::UID::freeside_uid;
225 $< = $FS::UID::freeside_uid;
226 #freebsd is sofa king broken, won't setuid()
228 $> = $FS::UID::freeside_uid;
231 $ENV{HOME} = (getpwuid($>))[7]; #for ssh
232 adminsuidsetup $user;
234 #$log_file = "/usr/local/etc/freeside/selfservice.". $FS::UID::datasrc; #MACHINE NAME
235 $log_file = "/usr/local/etc/freeside/selfservice.$machine.log";
237 open STDOUT, '>/dev/null'
238 or die "Can't write to /dev/null: $!";
239 setsid or die "Can't start a new session: $!";
240 open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
242 $SIG{__DIE__} = \&_die;
243 $SIG{__WARN__} = \&_logmsg;
245 warn "freeside-selfservice-server starting\n";
250 my $wait = 12; #wait up to 1 minute
251 while ( $kids > 0 && $wait-- ) {
252 warn "waiting for $kids children to terminate";
255 warn "abandoning $kids children" if $kids;
256 kill 'TERM', $ssh_pid if $ssh_pid;
262 unlink $pid_file if -e $pid_file;
267 chomp( my $msg = shift );
268 _do_logmsg( "[server] [". scalar(localtime). "] [$$] $msg\n" );
272 chomp( my $msg = shift );
273 my $log = new IO::File ">>$log_file";
274 flock($log, LOCK_EX);
277 flock($log, LOCK_UN);
282 die "Usage:\n\n fs_signup_server user machine\n";