1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
#!/usr/bin/perl -w
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 FS::svc_acct;
#use FS::cust_svc;
my $user = shift or die &usage;
#daemonize1('freeside-sqlradius-radacctd', $user); #keep unique pid files w/multi installs
daemonize1('freeside-sqlradius-radacctd');
drop_root();
#$ENV{HOME} = (getpwuid($>))[7]; #for ssh
adminsuidsetup $user;
logfile( "/usr/local/etc/freeside/sqlradius-radacctd-log.". $FS::UID::datasrc );
daemonize2();
#--
@part_export =
qsearch('part_export', { 'exporttype' => 'sqlradius' } );
push @part_export,
qsearch('part_export', { 'exporttype' => 'sqlradius_withdomain' } );
@part_export = grep { ! $_->option('ignore_accounting') } @part_export;
die "no sqlradius or sqlradius_withdomain exports without ignore_accounting"
unless @part_export;
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;
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
until ( sigint || sigterm ) {
$part_export->update_svc_acct();
sleep 1;
}
warn "child for ". $part_export->machine. " done";
exit;
} #eo kid
}
#reap up any kids that died...
&reap_kids;
myshutdown() if sigterm() || sigint();
sleep 5;
}
#--
sub myshutdown {
&reap_kids;
#kill all the kids
kill 'TERM', $_ foreach grep $_, map $_->{'_radacct_kid'}, @part_export;
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 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'} = '';
}
}
#warn "done reaping\n";
}
sub usage {
die "Usage:\n\n freeside-sqlradius-radacctd user\n";
}
=head1 NAME
freeside-sqlradius-radacctd - Real-time radacct import daemon
=head1 SYNOPSIS
freeside-sqlradius-radacctd username
=head1 DESCRIPTION
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 RADIUS DATABASE CHANGES
ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL;
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;
|