summaryrefslogtreecommitdiff
path: root/FS/bin/freeside-sqlradius-radacctd
blob: e98eaa01552ce809bc8a6d3d25fd03904d765122 (plain)
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
145
146
147
148
149
150
#!/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();

#--

#don't just look for ->can('usage_sessions'), we're sqlradius-specific
# (radiator is supposed to be setup with a radacct table)

@part_export =
  qsearch('part_export', { 'exporttype' => 'sqlradius' } );
push @part_export,
  qsearch('part_export', { 'exporttype' => 'sqlradius_withdomain' } );
push @part_export,
  qsearch('part_export', { 'exporttype' => 'radiator' } );

@part_export = grep { ! $_->option('ignore_accounting') } @part_export;

die "no sqlradius, sqlradius_withdomain or radiator 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, 
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

ALTER TABLE radacct ADD COLUMN FreesideStatus varchar(32) NULL;

If you want to ignore the existing accountg records, also do:

UPDATE radacct SET FreesideStatus = 'done' WHERE FreesideStatus IS NULL;

=head1 SEE ALSO

=cut

1;