(RADIATOR) Forking for accounting
Matthew Trout
MatthewTrout at businessserve.co.uk
Wed Apr 30 10:22:26 CDT 2003
Radiator as provided does not honour the Fork clause in AuthBySQL when
dealing with accounting packets; in order to achieve this without producing
unexpected effects on configs that expect this (mis?)-behaviour, I added a
ForkOnAccounting configuration directive with a patch to AuthSQL.pm.
Running radiator with this patch (and a minor one to Util.pm to add a
%{ZeroUnless:} syntax to ensure my accounting queries are valid SQL) I got a
marked performance increase (LDAP auth, MySQL logging with slightly tweaked
versions of the provided examples to do a single INSERT on Stop packets for
speed); however when the server came under heavy load I noticed a number of
odd behaviours.
1) Radiator repeatedly reported the error 'Lost connection to MySQL server'
when running queries, although these queries all seemed to have run
successfully - this got steadily worse as the load increased
2) When the load reached a certain point, I started getting
LDAP_OPERATIONS_ERROR occasionally, which I traced (with the aid of google)
to being a result of an error calling asn_read in Convert::ASN1::IO - i.e.
the connection had been lost here also
3) Radiator would occasionally lock entirely and cease replying (the
frequency of this also increases with server load); it appears that the
master and one of its child processes were locked waiting for each other -
i.e.
# ps -ef | grep radius
root 21305 15003 0 12:51:14 ? 0:00 /usr/local/bin/perl
/usr/local/bin/radiusd
root 15003 1 0 10:49:06 ? 9:41 /usr/local/bin/perl
/usr/local/bin/radiusd
root 21325 14940 0 13:17:13 pts/2 0:00 grep radius
# truss -p 15003
read(4, 0x00A03758, 4) (sleeping...)
signotifywait() (sleeping...)
lwp_cond_wait(0xFF0F5548, 0xFF0F5558, 0xFF0EEDB0) (sleeping...)
# truss -p 21305
read(4, 0x00A03758, 8192) (sleeping...)
signotifywait() (sleeping...)
lwp_sema_wait(0xFE30DE30) (sleeping...)
# truss -p 15003
read(4, 0x00A03758, 4) (sleeping...)
signotifywait() (sleeping...)
lwp_cond_wait(0xFF0F5548, 0xFF0F5558, 0xFF0EEDB0) (sleeping...)
Has anybody experienced similar issues with the normal Fork directive?
Hugh, Mike, can you enlighten me as to whether this is an issue with the way
my patch calls the Radiator forking code (I've attempted to keep it as close
to the way the handlerFork method is normally called as possible) or an
issue with the forking code itself? I've had -ahem- fun (FSVO) with Solaris'
parent/child process handling and Perl before, so this may be
platform-specific.
My Radiator config (sans secrets) is -
<SessionDatabase SQL>
Identifier DSL-SessDB
DBSource DBI:mysql:database=radius_session_dsl;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
AddQuery insert into RADONLINE\
(USERNAME, NASIDENTIFIER, NASPORT, ACCTSESSIONID,\
TIME_STAMP, FRAMEDIPADDRESS, NASPORTTYPE,
SERVICETYPE,\
CALLFROM, CALLTO)\
values ('%u', '%1', %2, %3, %{Timestamp},\
'%{Framed-IP-Address}', '%{NAS-Port-Type}',
'%{Service-Type}',\
'%{Calling-Station-Id}', '%{Called-Station-Id}')
</SessionDatabase SQL>
<SessionDatabase SQL>
Identifier FRIACO-SessDB
DBSource
DBI:mysql:database=radius_session_friaco;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
AddQuery insert into RADONLINE\
(USERNAME, NASIDENTIFIER, NASPORT, ACCTSESSIONID,\
TIME_STAMP, FRAMEDIPADDRESS, NASPORTTYPE,
SERVICETYPE,\
CALLFROM, CALLTO)\
values ('%u', '%1', %2, %3, %{Timestamp},\
'%{Framed-IP-Address}', '%{NAS-Port-Type}',
'%{Service-Type}',\
'%{Calling-Station-Id}', '%{Called-Station-Id}')
</SessionDatabase SQL>
<SessionDatabase SQL>
Identifier Dial-SessDB
DBSource
DBI:mysql:database=radius_session_dial;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
AddQuery insert into RADONLINE\
(USERNAME, NASIDENTIFIER, NASPORT, ACCTSESSIONID,\
TIME_STAMP, FRAMEDIPADDRESS, NASPORTTYPE,
SERVICETYPE,\
CALLFROM, CALLTO)\
values ('%u', '%1', %2, %3, %{Timestamp},\
'%{Framed-IP-Address}', '%{NAS-Port-Type}',
'%{Service-Type}',\
'%{Calling-Station-Id}', '%{Called-Station-Id}')
</SessionDatabase SQL>
<AddressAllocator SQL>
Identifier DialAllocator
DBSource
DBI:mysql:database=radius_session_dial;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
DefaultLeasePeriod 86400
LeaseReclaimInterval 3600
AllocateQuery update RADPOOL set STATE=1,TIME_STAMP=%0,
NASIP='%N', NASPORT=%{NAS-Port}, \
EXPIRY=%1, USERNAME=%2 where YIADDR='%3' and
TIME_STAMP %4
DeallocateQuery update RADPOOL set STATE=0,TIME_STAMP=%t where
NASIP='%N' and NASPORT=%{NAS-Port}
<AddressPool YC-Radius>
Subnetmask 255.255.255.255
Range 212.248.147.1 212.248.147.255
Range 212.248.148.1 212.248.148.255
Range 212.248.149.1 212.248.149.255
</AddressPool>
</AddressAllocator>
<AddressAllocator SQL>
Identifier ADSLAllocator
DBSource DBI:mysql:database=radius_session_dsl;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
DefaultLeasePeriod 7776000
LeaseReclaimInterval 3600
AllocateQuery update RADPOOL set STATE=1,TIME_STAMP=%0,
NASIP='%N', NASPORT=%{NAS-Port}, \
EXPIRY=%1, USERNAME=%2 where YIADDR='%3' and
TIME_STAMP %4
DeallocateQuery update RADPOOL set STATE=0,TIME_STAMP=%t where
NASIP='%N' and NASPORT=%{NAS-Port}
<AddressPool BT-ADSL-Radius>
Subnetmask 255.255.255.255
Range 62.69.97.0 62.69.97.255
Range 62.69.98.0 62.69.98.255
</AddressPool>
</AddressAllocator>
#<Handler Client-Identifier = BT-ADSL-Radius,Acct-Status-Type=/.+/>
# SessionDatabase DSL-SessDB
# AcctLogFileName %L/adsl-acct-detail
# <AuthBy TEST>
# # Return a Packet
# </AuthBy>
#</Handler>
<Handler Client-Identifier = BT-ADSL-Radius,Acct-Status-Type=/.+/>
SessionDatabase DSL-SessDB
# AcctLogFileName %L/adsl-acct-detail
AuthByPolicy DoAllAuths
<AuthBy SQL>
ForkOnAccounting
Identifier AcctSTOP
AuthSelect
DBSource
DBI:mysql:database=radius_session_dsl;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
HandleAcctStatusTypes Stop
AcctSQLStatement insert into usertime \
( \
server,id,name,\
time_on,port,port_type,service_type,protocol,\
ip,call_to,call_from,InBytes,OutBytes,\
term_cause,auth,\
start_time,stop_time,\
connect_info\
) \
values \
(\
inet_aton('%{NAS-IP-Address}'),\
'%{ZeroUnless:Acct-Session-Id}',\
'%{User-Name}',\
\
%{ZeroUnless:Acct-Session-Time},\
%{ZeroUnless:NAS-Port},\
'%{NAS-Port-Type}',\
'%{Service-Type}',\
'%{Framed-Protocol}',\
\
'%{Framed-IP-Address}',\
'%{Called-Station-Id}',\
'%{Calling-Station-Id}',\
%{ZeroUnless:Acct-Input-Octets},\
%{ZeroUnless:Acct-Output-Octets},\
\
'%{Acct-Terminate-Cause}',\
%{ZeroUnless:Auth-Type},\
From_unixtime(%{ZeroUnless:Timestamp} -
%{ZeroUnless:Acct-Session-Time}),\
From_unixtime(%{ZeroUnless:Timestamp}),\
'%{Connect-Info}'\
)
</AuthBy>
<AuthBy DYNADDRESS>
AddressAllocator ADSLAllocator
PoolHint %{Client:Identifier}
</AuthBy>
<AuthBy TEST>
# Return a Packet
</AuthBy>
</Handler>
<Handler Client-Identifier = BT-FRIACO-Radius,Acct-Status-Type=/.+/>
SessionDatabase FRIACO-SessDB
# AcctLogFileName %L/friaco-acct-detail
AuthByPolicy DoAllAuths
<AuthBy SQL>
ForkOnAccounting
Identifier AcctSTOP
AuthSelect
DBSource
DBI:mysql:database=radius_session_friaco;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
HandleAcctStatusTypes Stop
AcctSQLStatement insert into usertime \
( \
server,id,name,\
time_on,port,port_type,service_type,protocol,\
ip,call_to,call_from,InBytes,OutBytes,\
term_cause,auth,\
start_time,stop_time,\
connect_info\
) \
values \
(\
inet_aton('%{NAS-IP-Address}'),\
'%{ZeroUnless:Acct-Session-Id}',\
'%{User-Name}',\
\
%{ZeroUnless:Acct-Session-Time},\
%{ZeroUnless:NAS-Port},\
'%{NAS-Port-Type}',\
'%{Service-Type}',\
'%{Framed-Protocol}',\
\
'%{Framed-IP-Address}',\
'%{Called-Station-Id}',\
'%{Calling-Station-Id}',\
%{ZeroUnless:Acct-Input-Octets},\
%{ZeroUnless:Acct-Output-Octets},\
\
'%{Acct-Terminate-Cause}',\
%{ZeroUnless:Auth-Type},\
From_unixtime(%{ZeroUnless:Timestamp} -
%{ZeroUnless:Acct-Session-Time}),\
From_unixtime(%{ZeroUnless:Timestamp}),\
'%{Connect-Info}'\
)
</AuthBy>
<AuthBy TEST>
# Return a Packet
</AuthBy>
</Handler>
<Handler Client-Identifier = /CVX1/,Acct-Status-Type=/.+/>
SessionDatabase Dial-SessDB
# AcctLogFileName %L/dial-acct-detail
AuthByPolicy DoAllAuths
<AuthBy SQL>
ForkOnAccounting
Identifier AcctSTOP
AuthSelect
DBSource
DBI:mysql:database=radius_session_dial;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
HandleAcctStatusTypes Stop
AcctSQLStatement insert into usertime \
( \
server,id,name,\
time_on,port,port_type,service_type,protocol,\
ip,call_to,call_from,InBytes,OutBytes,\
term_cause,auth,\
start_time,stop_time,\
connect_info\
) \
values \
(\
inet_aton('%{NAS-IP-Address}'),\
'%{ZeroUnless:Acct-Session-Id}',\
'%{User-Name}',\
\
%{ZeroUnless:Acct-Session-Time},\
%{ZeroUnless:NAS-Port},\
'%{NAS-Port-Type}',\
'%{Service-Type}',\
'%{Framed-Protocol}',\
\
'%{Framed-IP-Address}',\
'%{Called-Station-Id}',\
'%{Calling-Station-Id}',\
%{ZeroUnless:Acct-Input-Octets},\
%{ZeroUnless:Acct-Output-Octets},\
\
'%{Acct-Terminate-Cause}',\
%{ZeroUnless:Auth-Type},\
From_unixtime(%{ZeroUnless:Timestamp} -
%{ZeroUnless:Acct-Session-Time}),\
From_unixtime(%{ZeroUnless:Timestamp}),\
'%{Connect-Info}'\
)
</AuthBy>
<AuthBy TEST>
# Return a Packet
</AuthBy>
</Handler>
<Handler Client-Identifier = /YC-Radius/,Acct-Status-Type=/.+/>
SessionDatabase Dial-SessDB
# AcctLogFileName %L/dial-acct-detail
AuthByPolicy DoAllAuths
<AuthBy SQL>
ForkOnAccounting
Identifier AcctSTOP
AuthSelect
DBSource
DBI:mysql:database=radius_session_dial;host=10.7.6.15
DBUsername automan
DBAuth 6o1ahadoto
HandleAcctStatusTypes Stop
AcctSQLStatement insert into usertime \
( \
server,id,name,\
time_on,port,port_type,service_type,protocol,\
ip,call_to,call_from,InBytes,OutBytes,\
term_cause,auth,\
start_time,stop_time,\
connect_info\
) \
values \
(\
inet_aton('%{NAS-IP-Address}'),\
'%{ZeroUnless:Acct-Session-Id}',\
'%{User-Name}',\
\
%{ZeroUnless:Acct-Session-Time},\
%{ZeroUnless:NAS-Port},\
'%{NAS-Port-Type}',\
'%{Service-Type}',\
'%{Framed-Protocol}',\
\
'%{Framed-IP-Address}',\
'%{Called-Station-Id}',\
'%{Calling-Station-Id}',\
%{ZeroUnless:Acct-Input-Octets},\
%{ZeroUnless:Acct-Output-Octets},\
\
'%{Acct-Terminate-Cause}',\
%{ZeroUnless:Auth-Type},\
From_unixtime(%{ZeroUnless:Timestamp} -
%{ZeroUnless:Acct-Session-Time}),\
From_unixtime(%{ZeroUnless:Timestamp}),\
'%{Connect-Info}'\
)
</AuthBy>
<AuthBy DYNADDRESS>
AddressAllocator DialAllocator
PoolHint %{Client:Identifier}
</AuthBy>
<AuthBy TEST>
# Return a Packet
</AuthBy>
</Handler>
<Handler Client-Identifier = BT-FRIACO-Radius>
#<Handler DEFAULT>
# PreAuthHook sub {
${$_[0]}->addAttrByNum($Radius::Radius::EAP_MESSAGE,1); }
SessionDatabase FRIACO-SessDB
<AuthBy GROUP>
AuthByPolicy ContinueUntilAccept
<AuthBy LDAP2>
Debug 255
NoDefault
HoldServerConnection
Host 10.7.9.13
AuthDN cn=directory manager
AuthPassword dir-ad1rmas
BaseDN ou=customers, ou=people, dc=bsve.net,
o=internet
#UsernameAttr uid
#PasswordAttr friacopassword
AuthAttrDef FRIACO-todr, Time, check
SearchFilter
(&(objectclass=friacouser)(csid=0%{Calling-Station-Id})(!(suspended=yes)))
AddToReply Service-Type = Framed-User, \
Framed-Protocol = PPP, \
Framed-IP-Address = 255.255.255.254, \
Framed-IP-Netmask = 255.255.255.255, \
Framed-Routing = None, \
Framed-Compression = Van-Jacobsen-TCP-IP, \
Framed-MTU = 1500, \
Session-Timeout = "until Time"
</AuthBy>
<AuthBy LDAP2>
# Debug 255
NoDefault
HoldServerConnection
Host 10.7.9.13
AuthDN cn=directory manager
AuthPassword dir-ad1rmas
BaseDN ou=customers, ou=people, dc=bsve.net,
o=internet
AuthAttrDef FRIACO-todr, Time, check
UsernameAttr friacousername
PasswordAttr friacopassword
SearchFilter
(&(%0=%1)(objectClass=FRIACOuser)(!(suspended=yes)))
AddToReply Service-Type = Framed-User, \
Framed-Protocol = PPP, \
Framed-IP-Address = 255.255.255.254, \
Framed-IP-Netmask = 255.255.255.255, \
Framed-Routing = None, \
Framed-Compression = Van-Jacobsen-TCP-IP, \
Framed-MTU = 1500, \
Session-Timeout = "until Time"
</AuthBy>
</AuthBy>
PostAuthHook sub { (${$_[1]}->get_attr('Session-Timeout') > 7200) &&
${$_[1]}->change_attr('Session-Timeout',7200); }
</Handler>
<Handler Client-Identifier = BT-ADSL-Radius>
AuthByPolicy ContinueWhileAccept
<AuthBy LDAP2>
# Debug 255
NoDefault
HoldServerConnection
Host 10.7.9.13
AuthDN cn=directory manager
AuthPassword dir-ad1rmas
BaseDN ou=customers, ou=people, dc=bsve.net,
o=internet
UsernameAttr uid
PasswordAttr userpassword
SearchFilter
(&(%0=%1)(objectClass=adsluser)(!(adslsuspended=1)))
AuthAttrDef framed-ip-address, Framed-IP-Address, reply
AuthAttrDef routed-network-address, _ipbase, request
AuthAttrDef routed-network-netmask, _netmask, request
AuthAttrDef explicitnexthopaddress, _nexthop, request
PostSearchHook sub { if ($_[2]->get_attr('_ipbase')) { \
$_[3]->get_reply->add_attr('cisco-avpair', \
'ip:route='.$_[2]->get_attr('_ipbase'). \
' '.$_[2]->get_attr('_netmask'). \
( $_[2]->get_attr('_nexthop') \
? '
'.$_[3]->get_reply->get_attr('Framed-IP-Address') \
: '')); \
} }
AddToReply Service-Type = Framed-User, \
Framed-Protocol = PPP, \
cisco-avpair = "ip:dns-servers=62.69.64.134
62.69.64.135"
</AuthBy>
<AuthBy DYNADDRESS>
AddressAllocator ADSLAllocator
PoolHint %{Client:Identifier}
</AuthBy>
</Handler>
<Handler Client-Identifier = /CVX1/>
RewriteUsername s/^([^\@]+).*/$1\@bsdial.net/
<AuthBy LDAP2>
# Debug 255
NoDefault
HoldServerConnection
Host 10.7.9.13
AuthDN cn=directory manager
AuthPassword dir-ad1rmas
BaseDN ou=customers, ou=people, dc=bsve.net,
o=internet
UsernameAttr uid
PasswordAttr userpassword
SearchFilter
(&(%0=%1)(dialup-nasclass=%{Client:Identifier})(objectClass=dialuser)(!(susp
ended=1)))
AuthAttrDef dialup-ip-address, Framed-IP-Address, reply
AuthAttrDef dialup-netmask, Framed-IP-Netmask, reply
AuthAttrDef dialup-maximum-bundled-channels,
CVX-Maximum-Channels, reply
AuthAttrDef dialup-routing-metric, Ascend-Metric, reply
PostSearchHook sub {
$_[3]->get_reply->get_attr('CVX-Maximum-Channels') \
||
$_[3]->get_reply->add_attr('CVX-Maximum-Channels', 2); }
AddToReply Service-Type = Framed-User, \
Framed-Protocol = PPP, \
Framed-Routing = None, \
CVX-Idle-Limit = 1800
</AuthBy>
</Handler>
<Handler Client-Identifier = YC-Radius, Called-Station-Id =
/0?8450591005|0?8450591010/>
RewriteUsername s/^([^\@]+).*/$1\@bsdial.net/
<AuthBy DYNADDRESS>
AddressAllocator DialAllocator
PoolHint %{Client:Identifier}
AcceptIfMissing
AddToReply Service-Type = Framed-User, \
Framed-Protocol = PPP, \
Framed-Routing = None, \
CVX-Idle-Limit = 1800, \
CVX-Maximum-Channels = 2, \
cisco-avpair = "ip:dns-servers=62.69.64.134 62.69.64.135"
</AuthBy>
</Handler>
<Handler Client-Identifier = YC-Radius>
RewriteUsername s/^([^\@]+).*/$1\@bsdial.net/
AuthByPolicy ContinueWhileAccept
<AuthBy LDAP2>
# Debug 255
NoDefault
HoldServerConnection
Host 10.7.9.13
AuthDN cn=directory manager
AuthPassword dir-ad1rmas
BaseDN ou=customers, ou=people, dc=bsve.net,
o=internet
UsernameAttr uid
PasswordAttr userpassword
SearchFilter
(&(%0=%1)(dialup-nasclass=YC-Radius)(objectClass=dialuser)(!(suspended=1)))
AuthAttrDef dialup-ip-address, Framed-IP-Address, reply
AuthAttrDef dialup-netmask, Framed-IP-Netmask, reply
AuthAttrDef dialup-maximum-bundled-channels,
CVX-Maximum-Channels, reply
PostSearchHook sub {
$_[3]->get_reply->get_attr('CVX-Maximum-Channels') \
||
$_[3]->get_reply->add_attr('CVX-Maximum-Channels', 2); }
AddToReply Service-Type = Framed-User, \
Framed-Protocol = PPP, \
Framed-Routing = None, \
CVX-Idle-Limit = 1800, \
cisco-avpair = "ip:dns-servers=62.69.64.134
62.69.64.135"
</AuthBy>
<AuthBy DYNADDRESS>
AddressAllocator DialAllocator
PoolHint %{Client:Identifier}
</AuthBy>
</Handler>
Logs look like the following (have included a fragment with both errors)
Mon Apr 28 18:36:45 2003: ERR: ldap search failed with error
LDAP_OPERATIONS_ERROR.
Mon Apr 28 18:36:45 2003: ERR: Disconnecting from LDAP server (server
10.7.9.13:389).
Mon Apr 28 18:37:08 2003: ERR: do failed for 'update RADPOOL set
STATE=0,TIME_STAMP=1051551428 where NASIP='212.248.254.18' and NASP
ORT=266': Lost connection to MySQL server during query
Mon Apr 28 18:37:08 2003: ERR: do failed for 'insert into usertime (
server,id,name,time_on,port,port_type,service_type,protocol,ip,
call_to,call_from,InBytes,OutBytes,term_cause,auth,start_time,stop_time,conn
ect_info) values (inet_aton('212.248.254.18'),'0000DF28'
,'gas370840 at bsdial.net',83,266,'ISDN','Framed-User','PPP','212.248.147.115',
'08450591000','1314400032',4289,2229,'User-Request',0,Fr
om_unixtime(1051551428 - 83),From_unixtime(1051551428),'')': Lost connection
to MySQL server during query
The patches used follow; they were both taken against Radiator 3.5, but
apply cleanly against 3.6 (which is what we're currently running, on Solaris
2.8)
*** /usr/local/src/Radiator-3.5/Radius/AuthSQL.pm Fri Nov 29 06:10:07
2002
--- AuthSQL.pm Thu Apr 10 11:34:18 2003
***************
*** 23,28 ****
--- 23,29 ----
'AcctColumnDef' => 'stringarray',
'HandleAcctStatusTypes' => 'counthash',
'AcctInsertQuery' => 'string',
+ 'ForkOnAccounting' => 'flag',
);
#####################################################################
***************
*** 113,118 ****
--- 114,138 ----
if $self->{AccountingAlivesOnly}
&& $status_type ne 'Alive';
+ # Now we might fork before running the accounting query/ies
+ # Should only do this if the database response is "slow"
+ return ($main::IGNORE, 'forked')
+ if $self->{ForkOnAccounting} && !$self->handlerFork();
+
+ # (Re)-connect to the database if necessary, so we can use
+ # the quote function.
+ # No reply will be sent to the original requester if we
+ # fail to connect
+ if (!$self->reconnect)
+ {
+ &Radius::Util::logAccounting
+ ($p, undef,
+ $self->{AcctFailedLogFileName},
+ $self->{AcctLogFileFormat})
+ if $self->{AcctFailedLogFileName};
+ return ($main::IGNORE, 'Database failure');
+ }
+
# If AcctSQLStatement is set, parse the strings and execute them
# Contributed by Nicholas Barrington <nbarrington at smart.net.au>
map {$self->do(&Radius::Util::format_special($_, $p))}
***************
*** 122,141 ****
# If AcctColumnDef is set, build an insert statment
if (defined $self->{AcctColumnDef})
{
- # (Re)-connect to the database if necessary, so we can use
- # the quote function.
- # No reply will be sent to the original requester if we
- # fail to connect
- if (!$self->reconnect)
- {
- &Radius::Util::logAccounting
- ($p, undef,
- $self->{AcctFailedLogFileName},
- $self->{AcctLogFileFormat})
- if $self->{AcctFailedLogFileName};
- return ($main::IGNORE, 'Database failure');
- }
-
# Add each column defined by AcctColumnDef
# Courtesy Phil Freed ptf at cybertours.com
my ($cols, $vals) = $self->getExtraCols($p);
--- 142,147 ----
*** /usr/local/src/Radiator-3.5/Radius/Util.pm Tue Dec 17 05:07:31 2002
--- Util.pm Wed Apr 9 18:01:53 2003
***************
*** 334,339 ****
--- 334,340 ----
$s =~ s/%\{Reply:([^{]+)\}/{$rpacket ? $rpacket->get_attr($1) :
''}/egs;
$s =~ s/%\{Client:([^{]+)\}/{$p ? $p->{Client}{$1} : ''}/egs;
$s =~ s/%\{Handler:([^{]+)\}/{$p ? $p->{Handler}{$1} : ''}/egs;
+ $s =~ s/%\{ZeroUnless:([^{]+)\}/{($p && $p->get_attr($1)) ?
$p->get_attr($1) : '0'}/egs;
# $s =~ s/%\{Eval:([^{]+)\}/{eval($1)}/egs;
$s =~ s/%\{([^{]+)\}/{$p ? $p->get_attr($1) : ''}/egs;
===
Archive at http://www.open.com.au/archives/radiator/
Announcements on radiator-announce at open.com.au
To unsubscribe, email 'majordomo at open.com.au' with
'unsubscribe radiator' in the body of the message.
More information about the radiator
mailing list