diff options
Diffstat (limited to 'FS/bin/freeside-sqlradius-radacctd')
-rw-r--r-- | FS/bin/freeside-sqlradius-radacctd | 212 |
1 files changed, 121 insertions, 91 deletions
diff --git a/FS/bin/freeside-sqlradius-radacctd b/FS/bin/freeside-sqlradius-radacctd index e98eaa015..4e8d57c51 100644 --- a/FS/bin/freeside-sqlradius-radacctd +++ b/FS/bin/freeside-sqlradius-radacctd @@ -1,122 +1,159 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl -Tw use strict; -use vars qw( @part_export ); -use subs qw(myshutdown); -use POSIX qw(:sys_wait_h); -#use IO::File; -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 vars qw( $log_file $sigterm $sigint ); +use subs qw( _die _logmsg ); +use Fcntl qw(:flock); +use POSIX qw(setsid); +use Date::Format; +use IO::File; +use FS::UID qw(adminsuidsetup); +#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; -#daemonize1('freeside-sqlradius-radacctd', $user); #keep unique pid files w/multi installs -daemonize1('freeside-sqlradius-radacctd'); +#my $pid_file = "/var/run/freeside-sqlradius-radacctd.$user.pid"; +my $pid_file = "/var/run/freeside-sqlradius-radacctd.pid"; -drop_root(); +&daemonize1; -#$ENV{HOME} = (getpwuid($>))[7]; #for ssh +#sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; } +#$SIG{CHLD} = \&REAPER; -adminsuidsetup $user; +$sigterm = 0; +$sigint = 0; +$SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $sigint++; }; +$SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $sigterm++; }; -logfile( "/usr/local/etc/freeside/sqlradius-radacctd-log.". $FS::UID::datasrc ); +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; -daemonize2(); +$> = $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 +adminsuidsetup $user; -#don't just look for ->can('usage_sessions'), we're sqlradius-specific -# (radiator is supposed to be setup with a radacct table) +$log_file= "/usr/local/etc/freeside/sqlradius-radacctd-log.". $FS::UID::datasrc; -@part_export = - qsearch('part_export', { 'exporttype' => 'sqlradius' } ); -push @part_export, - qsearch('part_export', { 'exporttype' => 'sqlradius_withdomain' } ); -push @part_export, - qsearch('part_export', { 'exporttype' => 'radiator' } ); +&daemonize2; -@part_export = grep { ! $_->option('ignore_accounting') } @part_export; +$SIG{__DIE__} = \&_die; +$SIG{__WARN__} = \&_logmsg; -die "no sqlradius, sqlradius_withdomain or radiator exports without". - " ignore_accounting" - unless @part_export; +warn "freeside-sqlradius-radacctd starting\n"; + +#eslaf + +#my $machine = shift or die &usage; #would need to be up higher for real +my @exports = qsearch('part_export', { 'exporttype' => 'sqlradius' } ); while (1) { - #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; + 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; - }; - - if ( $pid ) { - $part_export->{'_radacct_kid'} = $pid; - warn "child $pid spawned for ". $part_export->machine; - } else { #kid time + } - adminsuidsetup($user); #get our own db handle + # find old radacct position + #$lastid = 0; - until ( sigint || sigterm ) { - $part_export->update_svc_acct(); - sleep 1; - } + # 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; + next; + }; - warn "child for ". $part_export->machine. " done"; - exit; + while ( my $radacct = $sth->fetchrow_arrayref({}) ) { - } #eo kid + my $session = new FS::session { + portnum => + svcnum => + login => + #logout => + }; - } + } - #reap up any kids that died... - &reap_kids; + # look for updated radacct records & replace them - myshutdown() if sigterm() || sigint(); + } sleep 5; + } -#-- +#more false laziness w/freeside-queued -sub myshutdown { - &reap_kids; +sub usage { + die "Usage:\n\n freeside-sqlradius-radacctd user\n"; +} - #kill all the kids - kill 'TERM', $_ foreach grep $_, map $_->{'_radacct_kid'}, @part_export; +sub _die { + my $msg = shift; + unlink $pid_file if -e $pid_file; + _logmsg($msg); +} - 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 _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 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'} = ''; - } +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; } - #warn "done reaping\n"; + #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"; +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: $!"; } + +#eslaf + =head1 NAME freeside-sqlradius-radacctd - Real-time radacct import daemon @@ -127,24 +164,17 @@ freeside-sqlradius-radacctd - Real-time radacct import daemon =head1 DESCRIPTION -Imports records from an the SQL radacct tables of all sqlradius, -sqlradius_withdomain and radiator 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 RADIUS DATABASE CHANGES +Imports records from an SQL radacct table in real-time into the session +monitor. -ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL; +This enables per-minute or per-hour charges as well as the +"View active NAS ports" function. -If you want to ignore the existing accountg records, also do: - -UPDATE radacct SET FreesideStatus = 'done' WHERE FreesideStatus IS NULL; +B<username> is a username added by freeside-adduser. =head1 SEE ALSO -=cut +session.html from the base documentation. -1; +=cut |