diff options
author | ivan <ivan> | 2000-12-03 20:25:20 +0000 |
---|---|---|
committer | ivan <ivan> | 2000-12-03 20:25:20 +0000 |
commit | b90f8cdac9371c219a72dda16f8deecc7c44fc28 (patch) | |
tree | 566f55939a488f29e5a79821aef6102669afe721 | |
parent | 3a95cc316da367ffd248ba29ac594f3efbc9db61 (diff) |
session monitor updates
-rw-r--r-- | FS/FS/Record.pm | 19 | ||||
-rw-r--r-- | FS/FS/nas.pm | 24 | ||||
-rw-r--r-- | FS/FS/port.pm | 27 | ||||
-rw-r--r-- | FS/FS/session.pm | 28 | ||||
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | fs_sesmon/FS-SessionClient/MANIFEST | 4 | ||||
-rw-r--r-- | fs_sesmon/FS-SessionClient/Makefile.PL | 2 | ||||
-rw-r--r-- | fs_sesmon/FS-SessionClient/SessionClient.pm | 22 | ||||
-rw-r--r-- | fs_sesmon/FS-SessionClient/bin/freeside-login | 2 | ||||
-rw-r--r-- | fs_sesmon/FS-SessionClient/bin/freeside-logout (renamed from fs_sesmon/FS-SessionClient/bin/freeside-logoff) | 7 | ||||
-rw-r--r-- | fs_sesmon/FS-SessionClient/fs_sessiond | 10 | ||||
-rw-r--r-- | fs_sesmon/FS-SessionClient/test.pl | 3 | ||||
-rw-r--r-- | fs_sesmon/fs_session_server | 42 | ||||
-rwxr-xr-x | htdocs/browse/nas.cgi | 25 | ||||
-rwxr-xr-x | htdocs/view/svc_acct.cgi | 9 |
15 files changed, 159 insertions, 69 deletions
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 59472c898..18541d2db 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -149,22 +149,32 @@ sub create { } } -=item qsearch TABLE, HASHREF +=item qsearch TABLE, HASHREF, SELECT, EXTRA_SQL Searches the database for all records matching (at least) the key/value pairs in HASHREF. Returns all the records found as `FS::TABLE' objects if that module is loaded (i.e. via `use FS::cust_main;'), otherwise returns FS::Record objects. +###oops, argh, FS::Record::new only lets us create database fields. +#Normal behaviour if SELECT is not specified is `*', as in +#C<SELECT * FROM table WHERE ...>. However, there is an experimental new +#feature where you can specify SELECT - remember, the objects returned, +#although blessed into the appropriate `FS::TABLE' package, will only have the +#fields you specify. This might have unwanted results if you then go calling +#regular FS::TABLE methods +#on it. + =cut sub qsearch { - my($table, $record) = @_; + my($table, $record, $select, $extra_sql ) = @_; + $select ||= '*'; my $dbh = dbh; my @fields = grep exists($record->{$_}), fields($table); - my $statement = "SELECT * FROM $table"; + my $statement = "SELECT $select FROM $table"; if ( @fields ) { $statement .= " WHERE ". join(' AND ', map { if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) { @@ -178,6 +188,7 @@ sub qsearch { } } @fields ); } + $statement .= " $extra_sql" if defined($extra_sql); warn $statement if $DEBUG; my $sth = $dbh->prepare_cached($statement) or croak $dbh->errstr; @@ -895,7 +906,7 @@ sub hfields { =head1 VERSION -$Id: Record.pm,v 1.9 2000-11-07 15:00:37 ivan Exp $ +$Id: Record.pm,v 1.10 2000-12-03 20:25:20 ivan Exp $ =head1 BUGS diff --git a/FS/FS/nas.pm b/FS/FS/nas.pm index 873c9bce6..53e0fbc34 100644 --- a/FS/FS/nas.pm +++ b/FS/FS/nas.pm @@ -2,8 +2,8 @@ package FS::nas; use strict; use vars qw( @ISA ); -use FS::Record qw(); -#use FS::Record qw( qsearch qsearchs ); +use FS::Record qw(qsearchs); #qsearch); +use FS::UID qw( dbh ); #to lock the tables for heartbeat; ugh, MySQL-specific @ISA = qw(FS::Record); @@ -124,18 +124,32 @@ Updates the timestamp for this nas =cut sub heartbeat { - warn "warning: heartbeat unimplemented!" + my($self, $timestamp) = @_; + my $dbh = dbh; + my $sth = $dbh->prepare("LOCK TABLES nas WRITE"); + $sth->execute or die $sth->errstr; #die? + my $lock_self = qsearchs('nas', { 'nasnum' => $self->nasnum } ) + or die "can't find own record for $self nasnum ". $self->nasnum; + if ( $timestamp > $lock_self->last ) { + my $new_self = new FS::nas ( { $lock_self->hash } ); + $new_self->last($timestamp); + #is there a reason to? #$self->last($timestamp); + $new_self->replace($lock_self); + }; + $sth = $dbh->prepare("UNLOCK TABLES"); + $sth->execute or die $sth->errstr; #die? } =back =head1 VERSION -$Id: nas.pm,v 1.2 2000-11-07 15:00:37 ivan Exp $ +$Id: nas.pm,v 1.3 2000-12-03 20:25:20 ivan Exp $ =head1 BUGS -The author forgot to customize this manpage. +The B<heartbeat> method is MySQL-specific. Yuck. It's also not quite +perfectly subclassable, which is much less yuck. =head1 SEE ALSO diff --git a/FS/FS/port.pm b/FS/FS/port.pm index ee4611d21..e96e71c0f 100644 --- a/FS/FS/port.pm +++ b/FS/FS/port.pm @@ -118,23 +118,38 @@ sub check { =item session -Returns the currently open session, or if no session is currently open, the -most recent session. See L<FS::session>. +Returns the currently open session on this port, or if no session is currently +open, the most recent session. See L<FS::session>. =cut - +sub session { + my $self = shift; + qsearchs('session', { 'portnum' => $self->portnum }, '*', + 'ORDER BY login DESC LIMIT 1' ); +} =back =head1 VERSION -$Id: port.pm,v 1.2 2000-12-03 13:44:05 ivan Exp $ +$Id: port.pm,v 1.3 2000-12-03 20:25:20 ivan Exp $ =head1 BUGS The author forgot to customize this manpage. +The session method won't deal well if you have multiple open sessions on a +port, for example if your RADIUS server drops B<stop> records. Suggestions for +how to deal with this sort of lossage welcome; should we close the session +when we get a new session on that port? Tag it as invalid somehow? Close it +one second after it was opened? *sigh* Maybe FS::session shouldn't let you +create overlapping sessions, at least folks will find out their logging is +dropping records. + +If you think the above refers multiple user logins you need to read the +manpages again. + =head1 SEE ALSO L<FS::Record>, schema.html from the base documentation. @@ -147,8 +162,8 @@ added hfields ivan@sisd.com 97-nov-13 $Log: port.pm,v $ -Revision 1.2 2000-12-03 13:44:05 ivan -beginnings of web status for session monitor +Revision 1.3 2000-12-03 20:25:20 ivan +session monitor updates Revision 1.1 2000/10/27 20:18:32 ivan oops, also necessary for session monitor diff --git a/FS/FS/session.pm b/FS/FS/session.pm index b85a5822f..027708d98 100644 --- a/FS/FS/session.pm +++ b/FS/FS/session.pm @@ -94,6 +94,9 @@ sub insert { $error = $self->check; return $error if $error; + return "a session on that port is already open!" + if qsearchs('session', { 'portnum' => $self->portnum, 'logout' => '' } ); + $self->setfield('login', time()) unless $self->getfield('login'); $error = $self->SUPER::insert; @@ -124,7 +127,7 @@ it is replaced with the current time. =cut sub replace { - my $self = shift; + my($self, $old) = @_; my $error; local $SIG{HUP} = 'IGNORE'; @@ -139,7 +142,7 @@ sub replace { $self->setfield('logout', time()) unless $self->getfield('logout'); - $error = $self->SUPER::replace; + $error = $self->SUPER::replace($old); return $error if $error; $self->nas_heartbeat($self->getfield('logout')); @@ -188,15 +191,32 @@ sub nas_heartbeat { $nas->heartbeat(shift); } +=item svc_acct + +Returns the svc_acct record associated with this session (see L<FS::svc_acct>). + +=cut + +sub svc_acct { + my $self = shift; + qsearchs('svc_acct', { 'svcnum' => $self->svcnum } ); +} + =back =head1 VERSION -$Id: session.pm,v 1.2 2000-11-07 15:00:37 ivan Exp $ +$Id: session.pm,v 1.3 2000-12-03 20:25:20 ivan Exp $ =head1 BUGS -The author forgot to customize this manpage. +Maybe you shouldn't be able to insert a session if there's currently an open +session on that port. Or maybe the open session on that port should be flagged +as problematic? autoclosed? *sigh* + +Hmm, sessions refer to current svc_acct records... probably need to constrain +deletions to svc_acct records such that no svc_acct records are deleted which +have a session (even if long-closed). =head1 SEE ALSO @@ -1,4 +1,4 @@ -$Id: TODO,v 1.52 2000-10-27 20:15:50 ivan Exp $ +$Id: TODO,v 1.53 2000-12-03 20:25:20 ivan Exp $ If you are interested in helping with any of these, please join the *development* mailing list (send a blank message to @@ -6,6 +6,8 @@ ivan-freeside-devel-subscribe@sisd.com) to avoid duplication of effort. --- +first package select field in edit/cust_main.cgi isn't sticky on errors, yuck + > 1. A Web Form to the user get his account added automatically . The > /etc/raddb/users and /etc/passwd would be updated automatically (these > file are on the same machine Freeside is). I guess the the Add diff --git a/fs_sesmon/FS-SessionClient/MANIFEST b/fs_sesmon/FS-SessionClient/MANIFEST index 6da7b22e9..9da279a87 100644 --- a/fs_sesmon/FS-SessionClient/MANIFEST +++ b/fs_sesmon/FS-SessionClient/MANIFEST @@ -6,6 +6,6 @@ SessionClient.pm test.pl fs_sessiond cgi/logon.cgi -cgi/logoff.cgi +cgi/logout.cgi bin/freeside-login -bin/freeside-logoff +bin/freeside-logout diff --git a/fs_sesmon/FS-SessionClient/Makefile.PL b/fs_sesmon/FS-SessionClient/Makefile.PL index 1f598474a..137b6b8bd 100644 --- a/fs_sesmon/FS-SessionClient/Makefile.PL +++ b/fs_sesmon/FS-SessionClient/Makefile.PL @@ -4,7 +4,7 @@ use ExtUtils::MakeMaker; WriteMakefile( 'NAME' => 'FS::SessionClient', 'VERSION_FROM' => 'SessionClient.pm', # finds $VERSION - 'EXE_FILES' => [ qw(fs_sessiond freeside-login freeside-logoff) ], + 'EXE_FILES' => [ qw(fs_sessiond bin/freeside-login bin/freeside-logout) ], 'INSTALLSCRIPT' => '/usr/local/sbin', 'PERM_RWX' => '750', ); diff --git a/fs_sesmon/FS-SessionClient/SessionClient.pm b/fs_sesmon/FS-SessionClient/SessionClient.pm index 97332cb26..8a0ff705f 100644 --- a/fs_sesmon/FS-SessionClient/SessionClient.pm +++ b/fs_sesmon/FS-SessionClient/SessionClient.pm @@ -10,7 +10,7 @@ use IO::Handle; $VERSION = '0.01'; @ISA = qw( Exporter ); -@EXPORT_OK = qw( login logoff portnum ); +@EXPORT_OK = qw( login logout portnum ); $fs_sessiond_socket = "/usr/local/freeside/fs_sessiond_socket"; @@ -30,7 +30,7 @@ FS::SessionClient - Freeside session client API =head1 SYNOPSIS - use FS::SessionClient qw( login portnum logoff ); + use FS::SessionClient qw( login portnum logout ); $error = login ( { 'username' => $username, @@ -43,10 +43,10 @@ FS::SessionClient - Freeside session client API $portnum = portnum( { 'nasnum' => $nasnum, 'nasport' => $nasport } ) or die "unknown nasnum/nasport"; - $error = logoff ( { + $error = logout ( { 'username' => $username, 'password' => $password, - 'logoff' => $timestamp, + 'logout' => $timestamp, 'portnum' => $portnum, } ); @@ -73,13 +73,13 @@ Returns a scalar error message, or the empty string for success. =item portnum HASHREF should contain a single key: ip, or the two keys: nasnum and nasport. -Returns a portnum suitable for the login and logoff subroutines, or false +Returns a portnum suitable for the login and logout subroutines, or false on error. -=item logoff HASHREF +=item logout HASHREF -HASHREF should have the following keys: usrename, password, logoff and portnum. -logoff is a UNIX timestamp; if not specified, will default to the current time. +HASHREF should have the following keys: usrename, password, logout and portnum. +logout is a UNIX timestamp; if not specified, will default to the current time. Starts a new session for the specified user and portnum. The password is optional, but must be correct if specified. @@ -89,9 +89,11 @@ Returns a scalar error message, or the empty string for success. sub AUTOLOAD { my $hashref = shift; + my $method = $AUTOLOAD; + $method =~ s/^.*:://; socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; connect(SOCK, sockaddr_un($fs_sessiond_socket)) or die "connect: $!"; - print SOCK "$AUTOLOAD\n"; + print SOCK "$method\n"; print SOCK join("\n", %{$hashref}, 'END' ), "\n"; SOCK->flush; @@ -104,7 +106,7 @@ sub AUTOLOAD { =head1 VERSION -$Id: SessionClient.pm,v 1.2 2000-11-07 15:00:37 ivan Exp $ +$Id: SessionClient.pm,v 1.3 2000-12-03 20:25:20 ivan Exp $ =head1 BUGS diff --git a/fs_sesmon/FS-SessionClient/bin/freeside-login b/fs_sesmon/FS-SessionClient/bin/freeside-login index 6ca4455f8..a6d475169 100644 --- a/fs_sesmon/FS-SessionClient/bin/freeside-login +++ b/fs_sesmon/FS-SessionClient/bin/freeside-login @@ -1,6 +1,6 @@ #!/usr/bin/perl -Tw -#false-laziness hack w freeside-logoff +#false-laziness hack w freeside-logout use strict; use FS::SessionClient qw( login portnum ); diff --git a/fs_sesmon/FS-SessionClient/bin/freeside-logoff b/fs_sesmon/FS-SessionClient/bin/freeside-logout index f7b876b33..9b4ecfe23 100644 --- a/fs_sesmon/FS-SessionClient/bin/freeside-logoff +++ b/fs_sesmon/FS-SessionClient/bin/freeside-logout @@ -3,7 +3,7 @@ #false-laziness hack w freeside-login use strict; -use FS::SessionClient qw( logoff portnum ); +use FS::SessionClient qw( logout portnum ); my $username = shift; @@ -24,7 +24,7 @@ if ( scalar(@ARGV) == 1 ) { &usage; } -my $error = login ( { +my $error = logout ( { 'username' => $username, 'portnum' => $portnum, } ); @@ -32,6 +32,5 @@ my $error = login ( { warn $error if $error; sub usage { - die "Usage:\n\n freeside-logoff username ( portnum | ip | nasnum nasport )"; + die "Usage:\n\n freeside-logout username ( portnum | ip | nasnum nasport )"; } - diff --git a/fs_sesmon/FS-SessionClient/fs_sessiond b/fs_sesmon/FS-SessionClient/fs_sessiond index 74d3aab31..d7284b252 100644 --- a/fs_sesmon/FS-SessionClient/fs_sessiond +++ b/fs_sesmon/FS-SessionClient/fs_sessiond @@ -12,7 +12,7 @@ use vars qw( $Debug ); $Debug = 1; -my $fs_session_socket = "/usr/local/freeside/fs_sessiond_socket"; +my $fs_sessiond_socket = "/usr/local/freeside/fs_sessiond_socket"; $ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin'; $ENV{'SHELL'} = '/bin/sh'; @@ -29,10 +29,10 @@ warn "$me starting\n" if $Debug; #nothing to read from server warn "$me creating $fs_sessiond_socket\n" if $Debug; -my $uaddr = sockaddr_un($fs_signupd_socket); +my $uaddr = sockaddr_un($fs_sessiond_socket); my $proto = getprotobyname('tcp'); socket(Server,PF_UNIX,SOCK_STREAM,0) or die "socket: $!"; -unlink($fs_signup_socket); +unlink($fs_sessiond_socket); bind(Server, $uaddr) or die "bind: $!"; listen(Server,SOMAXCONN) or die "listen: $!"; @@ -44,8 +44,8 @@ for ( ; $paddr = accept(Client,Server); close Client) { if ( $command eq 'login' || $command eq 'logout' || $command eq 'portnum' ) { warn "$me reading data from local client\n" if $Debug; - my @data, $dos; - push @data, scalar(<Client>) until $dos++ == 99 || $data[$#data] != "END\n"; + my( @data, $dos ); + push @data, scalar(<Client>) until $dos++ == 99 || $data[$#data] eq "END\n"; if ( $dos == 99 ) { warn "$me WARNING: DoS attempt!" } else { diff --git a/fs_sesmon/FS-SessionClient/test.pl b/fs_sesmon/FS-SessionClient/test.pl index d05201b66..4b9ae17e0 100644 --- a/fs_sesmon/FS-SessionClient/test.pl +++ b/fs_sesmon/FS-SessionClient/test.pl @@ -8,7 +8,8 @@ BEGIN { $| = 1; print "1..1\n"; } END {print "not ok 1\n" unless $loaded;} -use FS::SessionClient; +#use FS::SessionClient; +#sigh, "not running as the freeside user" $loaded = 1; print "ok 1\n"; diff --git a/fs_sesmon/fs_session_server b/fs_sesmon/fs_session_server index 46e53d118..0930a3c00 100644 --- a/fs_sesmon/fs_session_server +++ b/fs_sesmon/fs_session_server @@ -6,9 +6,9 @@ use strict; use vars qw( $opt $Debug ); use IO::Handle; -use Net::SSH qw(sshopen3) +use Net::SSH qw(sshopen2); use FS::UID qw(adminsuidsetup); -use FS::Record qw( qsearch qsearchs ); +use FS::Record qw( qsearchs ); #qsearch ); #use FS::cust_main_county; #use FS::cust_main; use FS::session; @@ -31,25 +31,28 @@ while (1) { my($reader, $writer) = (new IO::Handle, new IO::Handle); $writer->autoflush(1); warn "$me Connecting to $machine\n" if $Debug; - sshopen2($machine,$reader,$writer,$fs_signupd); + sshopen2($machine,$reader,$writer,$fs_sessiond); warn "$me Entering main loop\n" if $Debug; while (1) { warn "$me Reading (waiting for) data\n" if $Debug; - my $command = scalar(<$reader)); + my $command = scalar(<$reader>); + chomp $command; #DoS protection here too, to protect against a compromised client? *sigh* - while ( ( my $key = scalar(<$reader>) ) != "END\n" ) { + my %hash; + while ( ( my $key = scalar(<$reader>) ) ne "END\n" ) { chomp $key; chomp( $hash{$key} = scalar(<$reader>) ); } if ( $command eq 'login' ) { - $error = &login(\%hash); + my $error = &login(\%hash); print $writer "$error\n"; - } elsif ( $command eq 'logoff' ) { - $error = &logoff(\%hash); + } elsif ( $command eq 'logout' ) { + my $error = &logout(\%hash); print $writer "$error\n"; } elsif ( $command eq 'portnum' ) { + my $port; if ( exists $hash{'ip'} ) { $hash{'ip'} =~ /^([\d\.]+)$/ or $1='nomatch'; $port = qsearchs('port', { 'ip' => $1 } ); @@ -60,13 +63,13 @@ while (1) { } print $writer ( $port ? $port->portnum : '' ), "\n"; } else { - warn "$me WARNING: unrecognized command"; + warn "$me WARNING: unrecognized command: $command"; } } #won't ever reach without code above to throw out of loop, but... close $writer; close $reader; - warn "connection to $machine lost!\n" + warn "connection to $machine lost!\n"; sleep 5; warn "reconnecting...\n"; } @@ -75,10 +78,10 @@ sub login { my $href = shift; $href->{'username'} =~ /^([a-z0-9_\-\.]+)$/ or return "Illegal username"; my $username = $1; - $svc_acct = qsearchs('svc_acct', { 'username' => $username } ) + my $svc_acct = qsearchs('svc_acct', { 'username' => $username } ) or return "Unknown user"; return "Incorrect password" - if defined($href->{'password'}) + if exists($href->{'password'}) && $href->{'password'} ne $svc_acct->_password; my $session = new FS::session { 'portnum' => $href->{'portnum'}, @@ -92,18 +95,19 @@ sub logout { my $href = shift; $href->{'username'} =~ /^([a-z0-9_\-\.]+)$/ or return "Illegal username"; my $username = $1; - $svc_acct = qsearchs('svc_acct', { 'username' => $username } ) + my $svc_acct = qsearchs('svc_acct', { 'username' => $username } ) or return "Unknown user"; return "Incorrect password" - if defined($href->{'password'}) + if exists($href->{'password'}) && $href->{'password'} ne $svc_acct->_password; - my $session = qsearchs FS::session { + my $session = qsearchs( 'session', { 'portnum' => $href->{'portnum'}, 'svcnum' => $svc_acct->svcnum, - 'logoff' => '', - }; - return "No currently open sessios found for that user/port!" unless $session; - my $nsession = new FS::session ( { $old->hash } ); + 'logout' => '', + } ); + return "No currently open sessions found for that user/port!" unless $session; + my $nsession = new FS::session ( { $session->hash } ); + warn "$nsession replacing $session"; $nsession->replace($session); } diff --git a/htdocs/browse/nas.cgi b/htdocs/browse/nas.cgi index 2aafbc1fd..a65235b1e 100755 --- a/htdocs/browse/nas.cgi +++ b/htdocs/browse/nas.cgi @@ -33,10 +33,29 @@ foreach my $nas ( sort { $a->nasnum <=> $b->nasnum } qsearch( 'nas', {} ) ) { ; foreach my $port ( sort { $a->nasport <=> $b->nasport || $a->portnum <=> $b->portnum - } qsearch( 'port' ) ) { + } qsearch( 'port', { 'nasnum' => $nas->nasnum } ) ) { + my $session = $port->session; + my($user, $since, $pretty_since, $duration); + if ( ! $session ) { + $user = "(empty)"; + $since = 0; + $pretty_since = "(never)"; + $duration = ''; + } elsif ( $session->logout ) { + $user = "(empty)"; + $since = $session->logout; + } else { + my $svc_acct = $session->svc_acct; + $user = "<A HREF=\"$p/view/svc_acct.cgi?". $svc_acct->svcnum. "\">". + $svc_acct->username. "</A>"; + $since = $session->login; + } + $pretty_since = time2str("%c", $since) if $since; + $duration = pretty_interval( $now - $since ). " ago" + unless defined($duration); print "<TR><TD>". $port->nasport. "</TD><TD>". $port->portnum. "</TD><TD>". - $port->ip. "</TD><TD>". 'user'. "</TD><TD>". 'since'. "</TD><TD>". - 'duration'. "</TD></TR>" + $port->ip. "</TD><TD>$user</TD><TD>$pretty_since". + "</TD><TD>$duration</TD></TR>" ; } print "</TABLE><BR>"; diff --git a/htdocs/view/svc_acct.cgi b/htdocs/view/svc_acct.cgi index e203c7ec0..22c3d76f2 100755 --- a/htdocs/view/svc_acct.cgi +++ b/htdocs/view/svc_acct.cgi @@ -1,6 +1,6 @@ #!/usr/bin/perl -Tw # -# $Id: svc_acct.cgi,v 1.10 1999-04-14 11:27:06 ivan Exp $ +# $Id: svc_acct.cgi,v 1.11 2000-12-03 20:25:20 ivan Exp $ # # Usage: svc_acct.cgi svcnum # http://server.name/path/svc_acct.cgi?svcnum @@ -33,7 +33,10 @@ # displays arbitrary radius attributes ivan@sisd.com 98-aug-16 # # $Log: svc_acct.cgi,v $ -# Revision 1.10 1999-04-14 11:27:06 ivan +# Revision 1.11 2000-12-03 20:25:20 ivan +# session monitor updates +# +# Revision 1.10 1999/04/14 11:27:06 ivan # showpasswords config option to show passwords # # Revision 1.9 1999/04/08 12:00:19 ivan @@ -138,7 +141,7 @@ $password = ''; $svc_acct_pop = qsearchs('svc_acct_pop',{'popnum'=>$svc_acct->popnum}); print "<BR>POP: <B>", $svc_acct_pop->city, ", ", $svc_acct_pop->state, - " (", $svc_acct_pop->ac, ")/", $svc_acct_pop->exch, "<\B>" + " (", $svc_acct_pop->ac, ")/", $svc_acct_pop->exch, "</B>" if $svc_acct_pop; if ($svc_acct->uid ne '') { |