summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Conf.pm7
-rw-r--r--FS/FS/Daemon.pm91
-rw-r--r--FS/FS/part_export/sqlradius.pm83
-rw-r--r--FS/FS/svc_acct.pm35
-rw-r--r--FS/MANIFEST2
-rw-r--r--FS/bin/freeside-queued96
-rw-r--r--FS/bin/freeside-selfservice-server131
-rw-r--r--FS/bin/freeside-sqlradius-radacctd202
-rw-r--r--FS/t/Daemon.t5
-rw-r--r--init.d/freeside-init8
10 files changed, 359 insertions, 301 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index eb02ff1..8ca2c1b 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -1448,6 +1448,13 @@ httemplate/docs/config.html
'type' => [qw( checkbox textarea )],
},
+ {
+ 'key' => 'svc_acct-usage_suspend',
+ 'section' => 'billing',
+ 'description' => 'Suspends the package an account belongs to when svc_acct.seconds is decremented to 0 or below (accounts with an empty seconds value are ignored). Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+ 'type' => 'checkbox',
+ },
+
);
1;
diff --git a/FS/FS/Daemon.pm b/FS/FS/Daemon.pm
new file mode 100644
index 0000000..3e64f79
--- /dev/null
+++ b/FS/FS/Daemon.pm
@@ -0,0 +1,91 @@
+package FS::Daemon;
+
+use vars qw( @ISA @EXPORT_OK );
+use vars qw( $pid_dir $me $pid_file $sigint $sigterm $logfile );
+use Exporter;
+use Fcntl qw(:flock);
+use POSIX qw(setsid);
+use Date::Format;
+
+#this is a simple refactoring of the stuff from freeside-queued, just to
+#avoid duplicate code. eventually this should use something from CPAN.
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw( daemonize1 drop_root daemonize2 sigint sigterm logfile );
+
+$pid_dir = '/var/run';
+
+sub daemonize1 {
+ $me = shift;
+
+ $pid_file = "$pid_dir/$me";
+ $pid_file .= '.'.shift if scalar(@_);
+ $pid_file .= '.pid';
+
+ 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 "$me started with pid $pid\n"; #logging to $log_file\n";
+ 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;
+ $sigterm = 0;
+ $sigint = 0;
+ $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; };
+ $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; };
+}
+
+sub drop_root {
+ my $freeside_gid = scalar(getgrnam('freeside'))
+ or die "can't find 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;
+}
+
+sub daemonize2 {
+ 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 "$me starting\n";
+}
+
+sub sigint { $sigint; }
+sub sigterm { $sigterm; }
+
+sub logfile { $logfile = shift; } #_logmsg('test'); }
+
+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 ">>$logfile";
+ flock($log, LOCK_EX);
+ seek($log, 0, 2);
+ print $log "[". time2str("%a %b %e %T %Y",time). "] [$$] $msg\n";
+ flock($log, LOCK_UN);
+ close $log;
+}
+
diff --git a/FS/FS/part_export/sqlradius.pm b/FS/FS/part_export/sqlradius.pm
index b9fef20..03802b2 100644
--- a/FS/FS/part_export/sqlradius.pm
+++ b/FS/FS/part_export/sqlradius.pm
@@ -2,8 +2,10 @@ package FS::part_export::sqlradius;
use vars qw(@ISA $DEBUG %info %options $notes1 $notes2);
use Tie::IxHash;
-use FS::Record qw( dbh );
+use FS::Record qw( dbh qsearch );
use FS::part_export;
+use FS::svc_acct;
+use FS::export_svc;
@ISA = qw(FS::part_export);
@@ -466,5 +468,84 @@ sub usage_sessions {
}
+=item update_svc_acct
+
+=cut
+
+sub update_svc_acct {
+ my $self = shift;
+
+ my $dbh = sqlradius_connect( map $self->option($_),
+ qw( datasrc username password ) );
+
+ my @fields = qw( radacctid username realm acctsessiontime );
+
+ my @param = ();
+ my $where = '';
+
+ my $sth = $dbh->prepare("
+ SELECT RadAcctId, UserName, Realm, AcctSessionTime
+ FROM radacct
+ WHERE FreesideStatus IS NULL
+ AND AcctStopTime != 0
+ ") or die $dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+
+ while ( my $row = $sth->fetchrow_arrayref ) {
+ my($RadAcctId, $UserName, $Realm, $AcctSessionTime) = @$row;
+ warn "processing record: ".
+ "$RadAcctId ($UserName\@$Realm for ${AcctSessionTime}s"
+ if $DEBUG;
+
+ my %search = ( 'username' => $UserName );
+ my $extra_sql = '';
+ if ( ref($self) =~ /withdomain/ ) { #well...
+ $extra_sql = " AND '$Realm' = ( SELECT domain FROM svc_domain
+ WHERE svc_domain.svcnum = svc_acct.domsvc ) ";
+ my $svc_domain = qsearch
+ }
+
+ my @svc_acct =
+ grep { qsearch( 'export_svc', { 'exportnum' => $self->exportnum,
+ 'svcpart' => $_->cust_svc->svcpart, } )
+ }
+ qsearch( 'svc_acct',
+ { 'username' => $UserName },
+ '',
+ $extra_sql
+ );
+
+ my $errinfo = "for RADIUS detail RadAcctID $RadAcctId ".
+ "(UserName $UserName, Realm $Realm)";
+ my $status = 'skipped';
+ if ( !@svc_acct ) {
+ warn "WARNING: no svc_acct record found $errinfo - skipping\n";
+ } elsif ( scalar(@svc_acct) > 1 ) {
+ warn "WARNING: multiple svc_acct records found $errinfo - skipping\n";
+ } else {
+ my $svc_acct = $svc_acct[0];
+ warn "found svc_acct ". $svc_acct->svcnum. " $errinfo\n" if $DEBUG;
+ if ( $svc_acct->seconds !~ /^$/ ) {
+ warn " svc_acct.seconds found (". $svc_acct->seconds.
+ ") - decrementing\n"
+ if $DEBUG;
+ $svc_acct->decrement_seconds($AcctSessionTime);
+ $status = 'done';
+ } else {
+ warn " no existing seconds value for svc_acct - skiping\n" if $DEBUG;
+ }
+ }
+
+ warn "setting FreesideStatus to $status $errinfo\n" if $DEBUG;
+ my $psth = $dbh->prepare("UPDATE radacct
+ SET FreesideStatus = ?
+ WHERE RadAcctId = ?"
+ ) or die $dbh->errstr;
+ $psth->execute($status, $RadAcctId) or die $psth->errstr;
+
+ }
+
+}
+
1;
diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm
index d806fe9..1daf83a 100644
--- a/FS/FS/svc_acct.pm
+++ b/FS/FS/svc_acct.pm
@@ -1120,6 +1120,41 @@ sub acct_snarf {
qsearch('acct_snarf', { 'svcnum' => $self->svcnum } );
}
+=item decrement_seconds SECONDS
+
+Decrements the I<seconds> field of this record by the given amount.
+
+=cut
+
+sub decrement_seconds {
+ my( $self, $seconds ) = @_;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $sth = dbh->prepare(
+ 'UPDATE svc_acct SET seconds = seconds - ? WHERE svcnum = ?'
+ ) or die dbh->errstr;;
+ $sth->execute($seconds, $self->svcnum) or die $sth->errstr;
+ if ( $conf->exists('svc_acct-usage_suspend')
+ && $self->seconds - $seconds <= 0 ) {
+ #my $error = $self->suspend;
+ my $error = $self->cust_svc->cust_pkg->suspend;
+ die $error if $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+}
+
=item seconds_since TIMESTAMP
Returns the number of seconds this account has been online since TIMESTAMP,
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 9916fed..971fe40 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -33,6 +33,7 @@ FS/ClientAPI/passwd.pm
FS/ClientAPI/MyAccount.pm
FS/Conf.pm
FS/ConfItem.pm
+FS/Daemon.pm
FS/Misc.pm
FS/Record.pm
FS/Report.pm
@@ -158,6 +159,7 @@ t/ClientAPI.t
t/ClientAPI_SessionCache.t
t/Conf.t
t/ConfItem.t
+t/Daemon.t
t/Misc.t
t/Record.t
t/Report.t
diff --git a/FS/bin/freeside-queued b/FS/bin/freeside-queued
index a3fb7e5..b9ef09c 100644
--- a/FS/bin/freeside-queued
+++ b/FS/bin/freeside-queued
@@ -1,13 +1,11 @@
#!/usr/bin/perl -w
use strict;
-use vars qw( $log_file $sigterm $sigint $kids $max_kids %kids );
-use subs qw( _die _logmsg );
-use Fcntl qw(:flock);
-use POSIX qw(:sys_wait_h setsid);
-use Date::Format;
+use vars qw( $DEBUG $kids $max_kids %kids );
+use POSIX qw(:sys_wait_h);
use IO::File;
use FS::UID qw(adminsuidsetup forksuidsetup driver_name dbh myconnect);
+use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm);
use FS::Record qw(qsearch qsearchs);
use FS::queue;
use FS::queue_depend;
@@ -18,40 +16,24 @@ use FS::svc_acct;
use Net::SSH 0.07;
use FS::part_export;
+$DEBUG = 0;
+
$max_kids = '10'; #guess it should be a config file...
$kids = 0;
my $user = shift or die &usage;
-#my $pid_file = "/var/run/freeside-queued.$user.pid";
-my $pid_file = "/var/run/freeside-queued.pid";
-
-&daemonize1;
-
-#sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
-#$SIG{CHLD} = \&REAPER;
+warn "starting daemonization (forking)\n" if $DEBUG;
+#daemonize1('freeside-queued',$user); #to keep pid files unique w/multi installs
+daemonize1('freeside-queued');
-$sigterm = 0;
-$sigint = 0;
-$SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; };
-$SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; };
+warn "dropping privledges\n" if $DEBUG;
+drop_root();
-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;
$ENV{HOME} = (getpwuid($>))[7]; #for ssh
+warn "connecting to database\n" if $DEBUG;
$@ = 'not connected';
while ( $@ ) {
eval { adminsuidsetup $user; };
@@ -62,14 +44,12 @@ while ( $@ ) {
}
}
-$log_file = "/usr/local/etc/freeside/queuelog.". $FS::UID::datasrc;
-
-&daemonize2;
+logfile( "/usr/local/etc/freeside/queuelog.". $FS::UID::datasrc );
-$SIG{__DIE__} = \&_die;
-$SIG{__WARN__} = \&_logmsg;
+warn "completing daemonization (detaching))\n" if $DEBUG;
+daemonize2();
-warn "freeside-queued starting\n";
+#--
my $warnkids=0;
while (1) {
@@ -225,11 +205,11 @@ while (1) {
}
} continue {
- if ( $sigterm ) {
+ if ( sigterm() ) {
warn "received TERM signal; exiting\n";
exit;
}
- if ( $sigint ) {
+ if ( sigint() ) {
warn "received INT signal; exiting\n";
exit;
}
@@ -239,48 +219,6 @@ sub usage {
die "Usage:\n\n freeside-queued user\n";
}
-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 "[". time2str("%a %b %e %T %Y",time). "] [$$] $msg\n";
- flock($log, LOCK_UN);
- close $log;
-}
-
-sub daemonize1 {
-
- 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-queued started with pid $pid\n"; #logging to $log_file\n";
- exit unless $pid_file;
- my $pidfh = new IO::File ">$pid_file" or exit;
- print $pidfh "$pid\n";
- exit;
- }
- #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: $!";
-
-}
-
-sub daemonize2 {
- 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: $!";
-}
-
sub reap_kids {
foreach my $pid ( keys %kids ) {
my $kid = waitpid($pid, WNOHANG);
diff --git a/FS/bin/freeside-selfservice-server b/FS/bin/freeside-selfservice-server
index c045893..c73349a 100644
--- a/FS/bin/freeside-selfservice-server
+++ b/FS/bin/freeside-selfservice-server
@@ -1,23 +1,16 @@
#!/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( $Debug %kids $kids $max_kids $shutdown $log_file $ssh_pid
- $keepalives );
-use subs qw( lock_write unlock_write );
+use vars qw( $Debug %kids $kids $max_kids $ssh_pid $keepalives );
+use subs qw( lock_write unlock_write myshutdown usage );
use Fcntl qw(:flock);
-use POSIX qw(:sys_wait_h setsid);
+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;
@@ -28,7 +21,6 @@ use FS::cust_pkg;
$Debug = 1; # 2 will turn on more logging
# 3 will log packet contents, including passwords
-$shutdown = 0;
$max_kids = '10'; #?
$keepalives = 0; #let clientd turn it on, so we don't barf on old ones
$kids = 0;
@@ -37,12 +29,31 @@ my $user = shift or die &usage;
my $machine = shift or die &usage;
my $tag = scalar(@ARGV) ? shift : '';
+my $lock_file = "/usr/local/etc/freeside/selfservice.$machine.writelock";
+
+
+# to keep pid files unique w/multi machines (and installs!)
# $FS::UID::datasrc not posible
-my $pid_file = "/var/run/freeside-selfservice-server.$user.$machine.pid";
+daemonize1("freeside-selfservice-server","$user.$machine");
-my $lock_file = "/usr/local/etc/freeside/selfservice.$machine.writelock";
+#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("/usr/local/etc/freeside/selfservice.$machine.log");
+
+daemonize2();
-&init($user);
my $conf = new FS::Conf;
@@ -69,7 +80,7 @@ while (1) {
$undisp = 1;
my @handles = $s->can_read(5);
unless ( @handles ) {
- &shutdown if $shutdown;
+ myshutdown() if sigint() || sigterm();
if ( $keepalives && $keepalive_count++ > 10 ) {
$keepalive_count = 0;
lock_write;
@@ -150,7 +161,7 @@ while (1) {
}
- &shutdown if $shutdown;
+ myshutdown if sigint() || sigterm();
warn "connection lost, reconnecting\n" if $Debug;
sleep 3;
@@ -172,70 +183,7 @@ sub reap_kids {
#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: $!";
- 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--; }
-# #sub REAPER { my $pid = wait; $kids--; $SIG{CHLD} = \&REAPER; }
-# $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++; };
-
- #false laziness w/freeside-queued
- my $freeside_gid = scalar(getgrnam('freeside'))
- or die "can't setgid to freeside group\n";
-
- open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
- chown $FS::UID::freeside_uid, $freeside_gid, $lock_file;
-
- $) = $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;
-
- #$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 {
+sub myshutdown {
&reap_kids;
my $wait = 12; #wait up to 1 minute
while ( $kids > 0 && $wait-- ) {
@@ -248,27 +196,6 @@ sub shutdown {
die "exiting";
}
-sub _die {
- my $msg = shift;
- unlink $pid_file if -e $pid_file;
- _logmsg($msg);
-}
-
-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 "$msg\n";
- flock($log, LOCK_UN);
- close $log;
-}
-
sub lock_write {
warn "locking $lock_file mutex for write to write stream\n" if $Debug > 1;
diff --git a/FS/bin/freeside-sqlradius-radacctd b/FS/bin/freeside-sqlradius-radacctd
index 4e8d57c..8d8e523 100644
--- a/FS/bin/freeside-sqlradius-radacctd
+++ b/FS/bin/freeside-sqlradius-radacctd
@@ -1,159 +1,116 @@
#!/usr/bin/perl -Tw
use strict;
-use vars qw( $log_file $sigterm $sigint );
-use subs qw( _die _logmsg );
-use Fcntl qw(:flock);
-use POSIX qw(setsid);
-use Date::Format;
+use vars qw( @part_export );
+use subs qw(myshutdown);
+use POSIX qw(:sys_wait_h);
use IO::File;
-use FS::UID qw(adminsuidsetup);
-#use FS::Record qw(qsearch qsearchs);
-#use FS::part_export;
+use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm);
+use FS::UID qw(adminsuidsetup); #forksuidsetup driver_name dbh myconnect);
+use FS::Record qw(qsearch); # qsearchs);
+use FS::part_export;
#use FS::svc_acct;
#use FS::cust_svc;
-#lots of false laziness w/freeside-queued
-
my $user = shift or die &usage;
-#my $pid_file = "/var/run/freeside-sqlradius-radacctd.$user.pid";
-my $pid_file = "/var/run/freeside-sqlradius-radacctd.pid";
-
-&daemonize1;
-
-#sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
-#$SIG{CHLD} = \&REAPER;
-
-$sigterm = 0;
-$sigint = 0;
-$SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; };
-$SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; };
-
-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;
+#daemonize1('freeside-sqlradius-radacctd', $user); #keep unique pid files w/multi installs
+daemonize1('freeside-sqlradius-radacctd');
-$> = $FS::UID::freeside_uid;
-$< = $FS::UID::freeside_uid;
-#freebsd is sofa king broken, won't setuid()
-($<,$>) = ($>,$<);
-$> = $FS::UID::freeside_uid;
+drop_root();
#$ENV{HOME} = (getpwuid($>))[7]; #for ssh
+
adminsuidsetup $user;
-$log_file= "/usr/local/etc/freeside/sqlradius-radacctd-log.". $FS::UID::datasrc;
+logfile( "/usr/local/etc/freeside/sqlradius-radacctd-log.". $FS::UID::datasrc );
-&daemonize2;
+daemonize2();
-$SIG{__DIE__} = \&_die;
-$SIG{__WARN__} = \&_logmsg;
+#--
-warn "freeside-sqlradius-radacctd starting\n";
+@part_export =
+ qsearch('part_export', { 'exporttype' => 'sqlradius' } );
+push @part_export,
+ qsearch('part_export', { 'exporttype' => 'sqlradius_withdomain' } );
-#eslaf
+@part_export = grep { ! $_->option('ignore_accounting') } @part_export;
-#my $machine = shift or die &usage; #would need to be up higher for real
-my @exports = qsearch('part_export', { 'exporttype' => 'sqlradius' } );
+die "no sqlradius or sqlradius_withdomain exports without ignore_accounting"
+ unless @part_export;
while (1) {
- my %seen = ();
- foreach my $export ( @exports ) {
- next if $seen{$export->option('datasrc')}++;
- my $dbh = DBI->connect(
- map { $export->option($_) } qw( datasrc username password )
- ) or do {
- warn "can't connect to ". $export->option('datasrc'). ": ". $DBI::errstr;
- next;
- }
-
- # find old radacct position
- #$lastid = 0;
-
- # get new radacct records
- my $sth = $dbh->prepare('SELECT * FROM radacct WHERE radacctid > ?') or do {
- warn "can't select in radacct table from ". $export->option('datasrc').
- ": ". $dbh->errstr;
+ #fork off one kid per export (machine)
+ # _>{'_radacct_kid'} is an evil kludge
+ foreach my $part_export ( grep ! $_->{'_radacct_kid'}, @part_export ) {
+
+ defined( my $pid = fork ) or do {
+ warn "WARNING: can't fork to spawn child for ". $part_export->machine;
next;
};
- while ( my $radacct = $sth->fetchrow_arrayref({}) ) {
+ if ( $pid ) {
+ $part_export->{'_radacct_kid'} = $pid;
+ warn "child $pid spawned for ". $part_export->machine;
+ } else { #kid time
- my $session = new FS::session {
- portnum =>
- svcnum =>
- login =>
- #logout =>
- };
+ adminsuidsetup($user); #get our own db handle
- }
+ until ( sigint || sigterm ) {
+ $part_export->update_svc_acct();
+ sleep 1;
+ }
- # look for updated radacct records & replace them
+ warn "child for ". $part_export->machine. " done";
+ exit;
+
+ } #eo kid
}
- sleep 5;
+ #reap up any kids that died...
+ &reap_kids;
+ myshutdown() if sigterm() || sigint();
+
+ sleep 5;
}
-#more false laziness w/freeside-queued
+#--
-sub usage {
- die "Usage:\n\n freeside-sqlradius-radacctd user\n";
-}
+sub myshutdown {
+ &reap_kids;
-sub _die {
- my $msg = shift;
- unlink $pid_file if -e $pid_file;
- _logmsg($msg);
-}
+ #kill all the kids
+ kill 'TERM', $_ foreach grep $_, map $_->{'_radacct_kid'}, @part_export;
-sub _logmsg {
- chomp( my $msg = shift );
- my $log = new IO::File ">>$log_file";
- flock($log, LOCK_EX);
- seek($log, 0, 2);
- print $log "[". time2str("%a %b %e %T %Y",time). "] [$$] $msg\n";
- flock($log, LOCK_UN);
- close $log;
+ my $wait = 12; #wait up to 1 minute
+ while ( ( grep $_->{'_radacct_kid'}, @part_export ) && $wait-- ) {
+ warn "waiting for children to terminate";
+ sleep 5;
+ &reap_kids;
+ }
+ warn "abandoning children" if grep $_->{'_radacct_kid'}, @part_export;
+ die "exiting";
}
-sub daemonize1 {
-
- 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-sqlradius-radacctd started with pid $pid\n";
- #logging to $log_file\n";
- exit unless $pid_file;
- my $pidfh = new IO::File ">$pid_file" or exit;
- print $pidfh "$pid\n";
- exit;
+sub reap_kids {
+ #warn "reaping kids\n";
+ foreach my $part_export ( grep $_->{'_radacct_kid'}, @part_export ) {
+ my $pid = $part_export->{'_radacct_kid'};
+ my $kid = waitpid($pid, WNOHANG);
+ if ( $kid > 0 ) {
+ $part_export->{'_radacct_kid'} = '';
+ }
}
- #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: $!";
-
+ #warn "done reaping\n";
}
-sub daemonize2 {
- 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: $!";
+sub usage {
+ die "Usage:\n\n freeside-sqlradius-radacctd user\n";
}
-
-#eslaf
-
=head1 NAME
freeside-sqlradius-radacctd - Real-time radacct import daemon
@@ -164,17 +121,24 @@ freeside-sqlradius-radacctd - Real-time radacct import daemon
=head1 DESCRIPTION
-Imports records from an SQL radacct table in real-time into the session
-monitor.
-
-This enables per-minute or per-hour charges as well as the
-"View active NAS ports" function.
+Imports records from an the SQL radacct tables of all sqlradius and
+sqlradius_withdomain exports (except those with the ignore_accounting flag) and
+updates the svc_acct.seconds for each account. Runs as a daemon and updates
+the database in real-time.
B<username> is a username added by freeside-adduser.
-=head1 SEE ALSO
+=head1 RADIUS DATABASE CHANGES
+
+ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL;
-session.html from the base documentation.
+If you want to ignore the existing accountg records, also do:
+
+UPDATE TABLE radacct SET FreesideStatus = 'done' WHERE FreesideStatus IS NULL;
+
+=head1 SEE ALSO
=cut
+1;
+
diff --git a/FS/t/Daemon.t b/FS/t/Daemon.t
new file mode 100644
index 0000000..24893fd
--- /dev/null
+++ b/FS/t/Daemon.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Daemon;
+$loaded=1;
+print "ok 1\n";
diff --git a/init.d/freeside-init b/init.d/freeside-init
index 57801dd..95598e9 100644
--- a/init.d/freeside-init
+++ b/init.d/freeside-init
@@ -21,6 +21,10 @@ case "$1" in
freeside-queued $QUEUED_USER
echo "done."
+ echo -n "Starting freeside-sqlradius-radacctd: "
+ freeside-sqlradius-radacctd $QUEUED_USER
+ echo "done."
+
for MACHINE in $SELFSERVICE_MACHINES; do
echo -n "Starting freeside-selfservice-server to $MACHINE: "
freeside-selfservice-server $SELFSERVICE_USER $MACHINE
@@ -34,6 +38,10 @@ case "$1" in
kill `cat /var/run/freeside-queued.pid`
echo "done."
+ echo -n "Stopping freeside-sqlradius-radacctd: "
+ kill `cat /var/run/freeside-sqlradius-radacctd.pid`
+ echo "done."
+
if [ -e /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid ]
then
echo -n "Stopping (old) freeside-selfservice-server: "