tiny bit of cleanup from the conf merge
[freeside.git] / FS / bin / freeside-sqlradius-radacctd
1 #!/usr/bin/perl -w
2
3 use strict;
4 use vars qw( @part_export );
5 use subs qw(myshutdown);
6 use POSIX qw(:sys_wait_h);
7 #use IO::File;
8 use FS::Daemon qw(daemonize1 drop_root logfile daemonize2 sigint sigterm);
9 use FS::UID qw(adminsuidsetup); #forksuidsetup driver_name dbh myconnect);
10 use FS::Record qw(qsearch); # qsearchs);
11 use FS::part_export;
12 #use FS::svc_acct;
13 #use FS::cust_svc;
14
15 my $user = shift or die &usage;
16
17 #daemonize1('freeside-sqlradius-radacctd', $user); #keep unique pid files w/multi installs
18 daemonize1('freeside-sqlradius-radacctd');
19
20 drop_root();
21
22 #$ENV{HOME} = (getpwuid($>))[7]; #for ssh
23
24 adminsuidsetup $user;
25
26 logfile( "%%%FREESIDE_LOG%%%/sqlradius-radacctd-log.". $FS::UID::datasrc );
27
28 daemonize2();
29
30 #--
31
32 #don't just look for ->can('usage_sessions'), we're sqlradius-specific
33 # (radiator is supposed to be setup with a radacct table)
34
35 @part_export =
36   qsearch('part_export', { 'exporttype' => 'sqlradius' } );
37 push @part_export,
38   qsearch('part_export', { 'exporttype' => 'sqlradius_withdomain' } );
39 push @part_export,
40   qsearch('part_export', { 'exporttype' => 'radiator' } );
41
42 @part_export = grep { ! $_->option('ignore_accounting') } @part_export;
43
44 die "no sqlradius, sqlradius_withdomain or radiator exports without".
45     " ignore_accounting"
46   unless @part_export;
47
48 while (1) {
49
50   #fork off one kid per export (machine)
51   # _>{'_radacct_kid'} is an evil kludge
52   foreach my $part_export ( grep ! $_->{'_radacct_kid'}, @part_export ) {
53  
54     defined( my $pid = fork ) or do {
55       warn "WARNING: can't fork to spawn child for ". $part_export->machine;
56       next;
57     };
58
59     if ( $pid ) {
60       $part_export->{'_radacct_kid'} = $pid;
61       warn "child $pid spawned for ". $part_export->machine;
62     } else { #kid time
63
64       adminsuidsetup($user); #get our own db handle
65
66       until ( sigint || sigterm ) {
67         $part_export->update_svc_acct();
68         sleep 1;
69       }
70
71       warn "child for ". $part_export->machine. " done";
72       exit;
73
74     } #eo kid
75
76   }
77
78   #reap up any kids that died...
79   &reap_kids;
80
81   myshutdown() if sigterm() || sigint();
82
83   sleep 5;
84 }
85
86 #-- 
87
88 sub myshutdown {
89   &reap_kids;
90
91   #kill all the kids
92   kill 'TERM', $_ foreach grep $_, map $_->{'_radacct_kid'}, @part_export;
93
94   my $wait = 12; #wait up to 1 minute
95   while ( ( grep $_->{'_radacct_kid'}, @part_export ) && $wait-- ) {
96     warn "waiting for children to terminate";
97     sleep 5;
98     &reap_kids;
99   }
100   warn "abandoning children" if grep $_->{'_radacct_kid'}, @part_export;
101   die "exiting";
102 }
103
104 sub reap_kids {
105   #warn "reaping kids\n";
106   foreach my $part_export ( grep $_->{'_radacct_kid'}, @part_export ) {
107     my $pid = $part_export->{'_radacct_kid'};
108     my $kid = waitpid($pid, WNOHANG);
109     if ( $kid > 0 ) {
110       $part_export->{'_radacct_kid'} = '';
111     }
112   }
113   #warn "done reaping\n";
114 }
115
116 sub usage {
117   die "Usage:\n\n  freeside-sqlradius-radacctd user\n";
118 }
119
120 =head1 NAME
121
122 freeside-sqlradius-radacctd - Real-time radacct import daemon
123
124 =head1 SYNOPSIS
125
126   freeside-sqlradius-radacctd username
127
128 =head1 DESCRIPTION
129
130 Imports records from an the SQL radacct tables of all sqlradius, 
131 sqlradius_withdomain and radiator exports (except those with the
132 ignore_accounting flag) and updates the svc_acct.seconds for each account.
133 Runs as a daemon and updates the database in real-time.
134
135 B<username> is a username added by freeside-adduser.
136
137 =head1 RADIUS DATABASE CHANGES
138
139 ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL;
140
141 If you want to ignore the existing accountg records, also do:
142
143 UPDATE radacct SET FreesideStatus = 'done' WHERE FreesideStatus IS NULL;
144
145 =head1 SEE ALSO
146
147 =cut
148
149 1;
150