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( $kids $max_kids $shutdown $log_file );
12 use vars qw($ssh_pid);
17 use Storable qw(nstore_fd fd_retrieve);
18 use Net::SSH qw(sshopen2);
19 use FS::UID qw(adminsuidsetup forksuidsetup);
21 use LockFile::Simple qw(lock unlock);
25 #use FS::Record qw( qsearch qsearchs );
26 #use FS::cust_main_county;
28 #use FS::Msgcat qw(gettext);
34 my $user = shift or die &usage;
35 my $machine = shift or die &usage;
36 my $pid_file = "/var/run/freeside-selfservice-server.$user.pid";
37 #my $pid_file = "/var/run/freeside-selfservice-server.$user.pid"; $FS::UID::datasrc not posible, but should include machine name at least, hmm
38 my $lock_file = "/usr/local/etc/freeside/freeside-selfservice-server.$user.lock";
43 my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name?
47 #'signup_init' => 'signup_init',
54 my($reader, $writer) = (new IO::Handle, new IO::Handle);
55 warn "connecting to $machine\n";
56 $ssh_pid = sshopen2($machine,$reader,$writer,$clientd);
58 # nstore_fd(\*writer, {'hi'=>'there'});
60 warn "entering main loop\n";
64 warn "waiting for packet from client\n" unless $undisp;
68 local $SIG{ALRM} = sub { local $SIG{__DIE__};die "MyAlarm\n" }; #NB: \n required
70 #my $string = <$reader>;
72 my $p = fd_retrieve($reader);
77 &shutdown if $shutdown && $@;
78 die "Fatal error receiving packet from client: $@" if $@ !~ /^MyAlarm/;
79 #die $@ unless $@ eq "alarm\n" || $@ eq 'Alarm'; #?????
81 next unless $shutdown;
84 warn "packet received\n".
85 join('', map { " $_=>$packet->{$_}\n" } keys %$packet );
89 #prevent runaway forking
91 while ( $kids >= $max_kids ) {
92 warn "WARNING: maximum $kids children reached\n" unless $warnkids++;
96 warn "forking child\n";
97 defined( my $pid = fork ) or die "can't fork: $!";
99 warn "child $pid spawned\n";
106 #$FS::UID::dbh->{InactiveDestroy} = 1;
107 forksuidsetup($user);
109 my $sub = $dispatch{$packet->{_packet}};
112 warn "calling $sub handler\n";
113 $rv = &{$sub}($packet);
115 warn my $error = "WARNING: unknown packet type ". $packet->{_packet};
116 $rv = { _error => $error };
118 $rv->{_token} = $packet->{_token}; #identifier
120 warn "sending response\n";
121 flock($writer, LOCK_EX); #acquire write lock
123 nstore_fd($rv, $writer) or die "can't send response: $!";
126 flock($writer, LOCK_UN); #release write lock
128 warn "child exiting\n";
137 # dispatch subroutines (should live elsewhere eventually)
142 use FS::Record qw(qsearchs);
148 #my $domain = qsearchs('svc_domain', { 'domain' => $packet->{'domain'} } )
149 # or return { error => "Domain $domain not found" };
151 my $old_password = $packet->{'old_password'};
152 my $new_password = $packet->{'new_password'};
153 my $new_gecos = $packet->{'new_gecos'};
154 my $new_shell = $packet->{'new_shell'};
157 ( length($old_password) < 13
158 && qsearchs( 'svc_acct', { 'username' => $packet->{'username'},
159 #'domsvc' => $svc_domain->domsvc,
160 '_password' => $old_password } )
162 || qsearchs( 'svc_acct', { 'username' => $packet->{'username'},
163 #'domsvc' => $svc_domain->domsvc,
164 '_password' => $old_password } );
166 unless ( $svc_acct ) { return { error => 'Incorrect password.' } }
168 my %hash = $svc_acct->hash;
169 my $new_svc_acct = new FS::svc_acct ( \%hash );
170 $new_svc_acct->setfield('_password', $new_password )
171 if $new_password && $new_password ne $old_password;
172 $new_svc_acct->setfield('finger',$new_gecos) if $new_gecos;
173 $new_svc_acct->setfield('shell',$new_shell) if $new_shell;
174 my $error = $new_svc_acct->replace($svc_acct);
176 return { error => $error };
181 # utility subroutines
187 chdir "/" or die "Can't chdir to /: $!";
188 open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
189 defined(my $pid = fork) or die "Can't fork: $!";
191 print "freeside-selfservice-server to $machine started with pid $pid\n"; #logging to $log_file
192 exit unless $pid_file;
193 my $pidfh = new IO::File ">$pid_file" or exit;
194 print $pidfh "$pid\n";
198 sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
199 $SIG{CHLD} = \&REAPER;
202 $SIG{HUP} = sub { warn "SIGHUP received; shutting down\n"; $shutdown++; };
203 $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $shutdown++; };
204 $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $shutdown++; };
205 $SIG{QUIT} = sub { warn "SIGQUIT received; shutting down\n"; $shutdown++; };
206 $SIG{PIPE} = sub { warn "SIGPIPE received; shutting down\n"; $shutdown++; };
208 $> = $FS::UID::freeside_uid unless $>;
211 #false laziness w/freeside-queued
212 my $freeside_gid = scalar(getgrnam('freeside'))
213 or die "can't setgid to freeside group\n";
216 #if freebsd can't setuid(), presumably it can't setgid() either. grr fleabsd
220 $> = $FS::UID::freeside_uid;
221 $< = $FS::UID::freeside_uid;
222 #freebsd is sofa king broken, won't setuid()
224 $> = $FS::UID::freeside_uid;
227 $ENV{HOME} = (getpwuid($>))[7]; #for ssh
228 adminsuidsetup $user;
230 #$log_file = "/usr/local/etc/freeside/selfservice.". $FS::UID::datasrc; #MACHINE NAME
231 $log_file = "/usr/local/etc/freeside/selfservice.$machine.log";
233 open STDOUT, '>/dev/null'
234 or die "Can't write to /dev/null: $!";
235 setsid or die "Can't start a new session: $!";
236 # open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
238 $SIG{__DIE__} = \&_die;
239 $SIG{__WARN__} = \&_logmsg;
241 warn "freeside-selfservice-server starting\n";
246 my $wait = 12; #wait up to 1 minute
247 while ( $kids > 0 && $wait-- ) {
248 warn "waiting for $kids children to terminate";
251 warn "abandoning $kids children" if $kids;
252 kill 'TERM', $ssh_pid if $ssh_pid;
258 unlink $pid_file if -e $pid_file;
263 chomp( my $msg = shift );
264 my $log = new IO::File ">>$log_file";
265 flock($log, LOCK_EX);
267 print $log "[server] [". time2str("%a %b %e %T %Y",time). "] [$$] $msg\n";
268 flock($log, LOCK_UN);
273 die "Usage:\n\n fs_signup_server user machine\n";
277 # handlers... should go in their own files eventually...