X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2Fbin%2Ffreeside-sqlradius-radacctd;h=8d8e5233c4068429f86b6128d2b23d0468316eec;hp=4e8d57c51e97ffc6f73840441d39ab120406d8d5;hb=beba6672fb9c9c5769c81f8029bb88cd2bc910e9;hpb=3d08b7bbf79c0131497d4812c552efe8a89d4cf3 diff --git a/FS/bin/freeside-sqlradius-radacctd b/FS/bin/freeside-sqlradius-radacctd index 4e8d57c51..8d8e5233c 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 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; +