Commit 34871785 authored by Mac Newbold's avatar Mac Newbold

Changes to snmpit to stop creation/renaming of vlans that already exist, in...

Changes to snmpit to stop creation/renaming of vlans that already exist, in preparation for vlansync, among other things. Also added vlansync, and made a small change to vlandiff.
parent b0a86206
......@@ -31,8 +31,6 @@ use snmpit_cisco;
use snmpit_apc;
use snmpit_lib;
snmpit_lib::init($DBNAME, $debug);
$| = 1; # Turn off line buffering on output
my $device;
......@@ -41,6 +39,8 @@ my $eid;
my $t = 0; #VLANs from db table
my $n; #VLAN # to move ports into
my %vlantable = ();
my @vlanlist = ();
my %vlansbyname = ();
&myMain;
......@@ -105,18 +105,49 @@ sub myMain {
if ($g) { $device->getStats; }
if (@vlan) {
getvlans();
if (vlanexists($m,@vlan)) {
print "VLAN $m exists with ",join(" ",sort @vlan),"\n";
} else {
$device->vlanLock;
$device->setupVlan($m,@vlan);
$device->vlanUnlock;
}
}
if ($f) {
getvlans();
my @VLANS= ();
my @names= ();
my $m="";
my @vlan=();
&ReadIRFile(\$f,\@names,\@VLANS);
$device->vlanLock;
$device->setupVlan($m,@vlan);
while (@VLANS) {
@vlan = @{shift(@VLANS)};
$m = shift @names;
if (vlanexists($m,@vlan)) {
print "VLAN $m exists with ",join(" ",sort @vlan),"\n";
} else {
print "Making $m with ",join(' ',sort @vlan),"\n" if $debug;
$device->setupVlan($m,@vlan);
}
}
$device->vlanUnlock;
}
if ($f) { &AutoVLANConfig(\$f); }
if ($t) {
getvlans();
$device->vlanLock;
my @vlan;
foreach $id (keys %vlantable) {
print "Making VLAN '$id' with $vlantable{$id}\n" if $debug;
$device->setupVlan($id,split(" ",$vlantable{$id}));
@vlan = sort split(" ",$vlantable{$id});
if (vlanexists($id,@vlan)) {
print "VLAN $id exists with ",join(" ",@vlan),"\n";
} else {
print "Making VLAN '$id' with @vlan\n" if $debug;
$device->setupVlan($id,@vlan);
}
}
$device->vlanUnlock;
}
......@@ -130,7 +161,29 @@ sub myMain {
}
#List VLANs is always last so that changes made are reflected
if ($l) { $device->listVlans; }
if ($l) {
my @vlans;
getvlans();
@vlans = @vlanlist;
print "ID Name\t\t\t Members of VLAN\n";
print "-"x75,"\n";
my $id;
my $name;
my $memberstr;
# I'm trying out using formats. See perldoc perlform for details.
format vlan =
@<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$id,$name, $memberstr
~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$memberstr
.
$FORMAT_NAME = "vlan";
while (@vlans) {
($id,$name,$memberstr) = split ("\t",shift @vlans);
write;
}
$FORMAT_NAME = "STDOUT"; # Go back to the normal format
}
if ($powerop) {
foreach $outlet (@outlets) {
......@@ -139,22 +192,25 @@ sub myMain {
}
}
sub getvlans {
if ( @vlanlist ) { return; }
@vlanlist = $device->listVlans;
foreach $entry (@vlanlist) {
my ($id,$name,$memberstr) = split ("\t", $entry);
$vlansbyname{$name} = $memberstr;
print "vlansbyname: $name -> $memberstr\n" if $debug;
}
}
sub AutoVLANConfig {
local(*f) = @_;
my @VLANS= ();
my @names= ();
&ReadIRFile(*f,\@names,\@VLANS);
$device->vlanLock;
my $m="";
my @vlan=();
while (@VLANS) {
@vlan = @{shift(@VLANS)};
$m = shift @names;
print "Making $m with: ",join(' ',@vlan),"\n" if $debug;
$device->setupVlan($m,@vlan);
sub vlanexists {
my $m = shift;
my $members = join(" ",sort @_);
print "Checking $m ($members)...\n" if $debug;
if (defined $vlansbyname{$m} && $vlansbyname{$m} eq $members) {
print "Found $m == $vlansbyname{$m}\n" if $debug;
return 1;
}
$device->vlanUnlock;
return 0;
}
sub ParseArgs {
......@@ -214,10 +270,14 @@ sub ParseArgs {
}
}
elsif (/^-r(.*)/) {
@r = ();
push(@r, ($1 ? $1 : shift(@CMDS)));
while ( @CMDS>0 && ! ($CMDS[0] =~ /^(-|\+)(.*)/ ) ) {
push(@r, shift(@CMDS));
}
print "remove: '@r' ($#r)\n" if $debug;
if ($#r == 0) { die("Which VLANs should I remove?\n"); }
if (join(" ",@r) =~ /[^0-9 ]/) { die("Removal list '@r' invalid.\n"); }
}
elsif (/^-v(.*)/) {$v = 1;}
elsif (/^-b(.*)/) {$b = 0;}
......@@ -283,6 +343,21 @@ sub ParseArgs {
die("What shall I do?\n")
if (!($d||$e||$a||$spd||$dup||$n||$s||$g||@vlan||$f||$t||@r||$l
||$powerop));
die("Can't enable and disable at the same time.\n") if ($d && $e);
die("Can't use auto with duplex or speed.\n") if(($a=~/en/)&&($dup||$spd));
die("Which ports do I control?\n") if (!@p&&($d||$e||$dup||$spd||$a||$n));
$dup = "\L$dup\E"; #lowercase it all...
die("Invalid duplex $dup: Must be full or half\n")
if(!($dup=~/^full$|^half$|^$/));
die("Invalid speed $spd: Must be 10 or 100\n")
if (!($spd=~/^10$|^100$|^0$/));
if (defined($spd) && ($spd != 0)) { $spd= "${spd}mbit"; }
die("Only one of -f, -t and -vlan may be used.\n")
if (($f && @vlan) || ($f && $t) || ($t && @vlan));
die("Project and expt must be specified with -t: '-t <pid> <eid>'\n")
if ($t && ((!defined $pid) || (!defined $eid)));
# If everything is okay so far, initialize snmpit_lib
snmpit_lib::init($DBNAME, $debug);
if (!defined($i)||!$i||$i eq "") { $i = "cisco"; }
if ( defined Dev($i) ) {
if (! ($i =~ /[a-zA-Z]/) ) { $i = Dev($i); } # i is now name
......@@ -306,7 +381,8 @@ sub ParseArgs {
if ($debug) {
if (ref($device)) {
print "Checking type: ",$device->isa(snmpit_intel)," for intel, ";
print $device->isa(snmpit_cisco)," for cisco\n";
print $device->isa(snmpit_cisco)," for cisco, ";
print $device->isa(snmpit_apc)," for APC\n";
} else { print "Type '$device' is not a ref.\n"; }
}
if ( $i =~ /[a-zA-Z]/) { $i = Dev($i); }
......@@ -315,8 +391,6 @@ sub ParseArgs {
($d||$e||$a||$spd||$dup||$n||$s||$g||@vlan||$f||@r||$l));
die("\"$i\" is not a power controller.\n")
if (! $device->isa(snmpit_apc) && ($powerop || @outlets) );
die("Can't enable and disable at the same time.\n") if ($d && $e);
die("Can't use auto with duplex or speed.\n") if(($a=~/en/)&&($dup||$spd));
my $num=0;
if ($device->isa(snmpit_intel)) {
while( @p != 0 && $num < @p ) {
......@@ -325,13 +399,6 @@ sub ParseArgs {
$num++;
}
}
die("Which ports do I control?\n") if (!@p&&($d||$e||$dup||$spd||$a||$n));
$dup = "\L$dup\E"; #lowercase it all...
die("Invalid duplex $dup: Must be full or half\n")
if(!($dup=~/^full$|^half$|^$/));
die("Invalid speed $spd: Must be 10 or 100\n")
if (!($spd=~/^10$|^100$|^0$/));
if (defined($spd) && ($spd != 0)) { $spd= "${spd}mbit"; }
print "original ports: \t@p\n" if ($debug && @p);
@p = map(lc,@p);
@p = map {
......@@ -354,7 +421,6 @@ sub ParseArgs {
while(@vlan != 0 && $num < @vlan) {
$vlan[$num] = lc $vlan[$num];
print "Checking node $vlan[$num] for validity...\n" if $debug;
if ($vlan[$num] =~ /(sh\d+)(-\d)?(:\d)?/ ) { $vlan[$num] = "$1-1:0"; }
if (defined macport($vlan[$num])) {
if ($vlan[$num]=~/^([a-f]|\d)*$/i) {
$vlan[$num]=macport($vlan[$num]);
......@@ -372,19 +438,16 @@ sub ParseArgs {
}
$num++;
}
die("Only one of -f, -t and -vlan may be used.\n")
if (($f && @vlan) || ($f && $t) || ($t && @vlan));
die("Project and expt must be specified with -t: '-t <pid> <eid>'\n")
if ($t && ((!defined $pid) || (!defined $eid)));
if ($t) {
%vlantable = tableVlans($pid,$eid);
if ( 0 == (keys %vlantable)) {
if ( ! vlanid("$pid:$eid")) {
print "There were no VLANs in the database table for '$pid' '$eid'\n";
exit(0);
} else {
foreach $id (keys %vlantable) {
print "id: $id\t$vlantable{$id}\n" if $debug;
foreach $node (split (" ",$vlantable{$id})) {
foreach $id ( split(" ",vlanid("$pid:$eid")) ) {
my $members = vlanmemb($id);
$vlantable{$id} = $members;
print "id: $id\t$members\n" if $debug;
foreach $node (split (" ",$members)) {
if (!defined macport($node)) {
die("Invalid node found in VLANs table: '$node'\n");
}
......@@ -407,6 +470,7 @@ sub ReadIRFile {
my %exists = ();
my $skip = 1;
my $name = "";
print "Opening '$f' to read vlans...\n" if $debug;
if ( -e "$TB/etc/$f") {
open(IR,"$TB/etc/$f")
|| die("Couldn't open $TB/etc/$f\n");
......@@ -430,9 +494,6 @@ sub ReadIRFile {
while (@v != 0 && $num < @v) {
$v[$num] = lc $v[$num];
print "Checking node $v[$num] for validity...\n" if $debug;
if ($v[$num] =~ /(sh\d+)(-\d)?(:\d)?/ ) {
$v[$num] = "$1-1:0";
}
if (defined macport($v[$num])) {
if ($v[$num]=~/^([a-f]|\d)*$/i) {
$v[$num]=macport($v[$num]);
......
......@@ -199,11 +199,11 @@ sub setupVlan {
print "***Okay=$okay, Attempts=$attempts (limit of 3)\n" if $debug;
my $N = 2;
my $RetVal = $sess->get([[$VlanRowStatus,$N]]);
print "Row $N got '$RetVal' " if $debug;
print "Row $N got '$RetVal' " if $debug > 1;
while ($RetVal ne '') {
$N += 1;
$RetVal = $sess->get([[$VlanRowStatus,$N]]);
print "Row $N got '$RetVal' " if $debug;
print "Row $N got '$RetVal' " if $debug > 1;
}
print "\nUsing Row $N\n" if $debug;
my $SAID = pack("H*",sprintf("%08x",$N+100000));
......@@ -470,25 +470,15 @@ sub listVlans {
}
$RetVal = $sess->getnext($VlanPortVlan);
@data = @{$VlanPortVlan};
} while ( $data[0] =~ /^vlanPortVlan/ ) ;
print "ID Name\t\t\t Members of VLAN\n";
print "--------------------------------------------------\n";
my $id;
my $memberstr;
# I'm trying out using formats. See perldoc perlform for details.
format vlan =
@<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
($id),($Names{$id}), $memberstr
~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$memberstr
.
$FORMAT_NAME = "vlan";
foreach $id ( sort num keys (%Names) ) {
$memberstr = (defined(@{$Members{$id}}) ?
join(" ",sort alphanum(@{$Members{$id}})) : "");
write;
} while ( $data[0] =~ /^vlanPortVlan/ );
my @list = ();
foreach $id (sort num keys %Names) {
push @list, join("\t", $id, $Names{$id},
(defined(@{$Members{$id}}) ?
join(" ",sort (@{$Members{$id}})) : ""));
}
$FORMAT_NAME = "STDOUT"; # Go back to the normal format
print join("\n",@list),"\n" if $debug > 1;
return @list;
}
sub showPorts {
......
......@@ -17,8 +17,8 @@ vlanLock Get a vlan editing lock/token/buffer, and
vlanUnlock Release it when done
setupVlan (name,ports) - takes a list of ports, and vlans them
removeVlan (vlan) - takes vlan number
listVlans returns a list of entries, with each entry having an
ID, a name, and a list of members (pcX:Y)
listVlans returns a list of entries, with each entry having an ID,
a name, and a list of members (pcX:Y), tab delimited
Notes:
showPorts and getStats currently print their results directly. This
......@@ -38,13 +38,7 @@ It supports new(ip,args) as above, and power(cmd,port) where cmd is
snmpit library interface
------------------------
init(dbname) initialize the module
macport(pcX:Y || MAC) return MAC || pcX:Y address respectively
portnum(pcX:Y || node:port) return node:mod.port or pcX:Y
Dev(name || IP) return IP/name
NodeCheck(pcX:Y) return true (1) if okay, false (0) if it failed
tableVlans(pid,eid) returns hash: key=id, val=members
(see comment at top of snmpit_lib.pm)
The snmpit library handles all the database stuff (except some power
control info). It makes available all the translations that a module
might need to do.
The snmpit library handles all the database transactions. It makes
available all the translations that a module might need to do.
......@@ -7,13 +7,12 @@
# portnum(pcX:Y || node:port) return node:mod.port or pcX:Y
# Dev(name || IP) return IP/name
# NodeCheck(pcX:Y) return true (1) if okay, false (0) if it failed
# tableVlans(pid,eid) returns hash: key=id, val=members
package snmpit_lib;
use Exporter;
@ISA = ("Exporter");
@EXPORT = qw( macport portnum Dev NodeCheck tableVlans );
@EXPORT = qw( macport portnum Dev NodeCheck vlanmemb vlanid);
use English;
use Mysql;
......@@ -24,13 +23,19 @@ my $dbh;
my $sth;
my %Devices=();
# %Devices maps device names to device IPs
# Devices maps device names to device IPs
my %Interfaces=();
# %Interfaces maps pcX:Y<==>MAC
# Interfaces maps pcX:Y<==>MAC
my %Ports=();
# %Ports maps pcX:Y<==>switch:port
# Ports maps pcX:Y<==>switch:port
my %vlanmembers=();
# vlanmembers maps id -> members
my %vlanids=();
# vlanids maps pid:eid <==> id
my $self = (getpwuid($UID))[0]
|| die "Cannot figure out who you are!\n";
......@@ -38,8 +43,6 @@ print "User is '$self' (UID $UID)\n" if $debug;
my $admin=0;
my $init = 0 ; # Start out uninitialized
sub init {
$DBNAME = shift;
$debug = shift || $debug;
......@@ -64,6 +67,16 @@ sub Dev {
return $Devices{$val};
}
sub vlanmemb {
my $val = shift || "";
return $vlanmembers{$val};
}
sub vlanid {
my $val = shift || "";
return $vlanids{$val};
}
sub ReadTranslationTable {
# This function fills in %Interfaces and %Ports
# They hold pcX:Y<==>MAC and pcX:Y<==>switch:port respectively
......@@ -77,12 +90,12 @@ sub ReadTranslationTable {
$name = "$_[0]:$_[1]";
if ($_[2] != 1) {$name .=$_[2]; }
$mac = "$_[3]";
if ($name =~ /(sh\d+)(-\d)(:\d)?/ ) { $name = "$1$3"; }
$Interfaces{$name} = $mac;
$Interfaces{$mac} = $name;
print "Got $mac <==> $name\n" if $debug > 1;
print "Interfaces: $mac <==> $name\n" if $debug > 1;
}
print "FILLING %Devices\n" if $debug;
$sth = $dbh->query("select i.node_id,i.IP from interfaces as i ".
"left join nodes as n on n.node_id=i.node_id ".
......@@ -112,7 +125,39 @@ sub ReadTranslationTable {
print "switchport='$switchport'\n" if $debug > 2;
$Ports{$name} = $switchport;
$Ports{$switchport} = $name;
print "READ: '$name' <==> '$switchport'\n" if $debug > 1;
print "Ports: '$name' <==> '$switchport'\n" if $debug > 1;
}
print "FILLING %vlanmembers\n" if $debug;
$sth = $dbh->
query("select id,members from vlans");
while (@row = $sth->fetchrow_array()) {
my @list = split(" ",$row[1]);
foreach $port (@list) {
my ($node,$card) = split(":",$port);
if ($card =~ /[a-zA-Z]/) {
# specified ala ethX
my $sth2 = $dbh->
query("select card from interfaces where node_id='$node' ".
"and iface='$card'");
$card = ($sth2->fetchrow_array())[0];
print "Had '$port', changed to '$node:$card'\n" if $debug > 2;
$port = "$node:$card";
}
}
$vlanmembers{$row[0]} = join(" ",sort @list);
print "vlanmembers: $row[0] -> $vlanmembers{$row[0]}\n" if $debug >1;
}
print "FILLING %vlanids\n" if $debug;
$sth = $dbh->
query("select pid,eid,id from vlans");
while (@row = $sth->fetchrow_array()) {
$vlanids{"$row[0]:$row[1]"} =
join(" ",split(" ",($vlanids{"$row[0]:$row[1]"}||"")),$row[2]);
$vlanids{$row[2]} = "$row[0]:$row[1]";
print "vlanids: '$row[0]:$row[1]' <==> ",$vlanids{"$row[0]:$row[1]"},"\n"
if $debug > 1;
}
}
......@@ -160,33 +205,6 @@ sub NodeCheck {
return 0;
}
sub tableVlans {
my $pid = shift;
my $eid = shift;
#returns hash, key=id, val=members
my %table = ();
$sth = $dbh->
query("select id,members from vlans where pid='$pid' and eid='$eid'");
while (@row = $sth->fetchrow_array()) {
my @list = split(" ",$row[1]);
foreach $port (@list) {
my ($node,$card) = split(":",$port);
if ($node =~ /^(sh\d+)/) { $node = "$1-1"; }
if ($card =~ /[a-zA-Z]/) {
# specified ala ethX
my $sth2 = $dbh->
query("select card from interfaces where node_id='$node' ".
"and iface='$card'");
$card = ($sth2->fetchrow_array())[0];
print "Had '$port', changed to '$node:$card'\n" if $debug;
$port = "$node:$card";
}
}
$table{$row[0]} = join(" ",@list);
}
return %table;
}
# End with true
1;
......@@ -17,6 +17,7 @@ my $sth;
my %table = ();
my %switch= ();
my %id = ();
# Get all the vlans from the table
......@@ -54,7 +55,9 @@ while(<LIST>) {
if (!defined $3) { $list = ""; } else { $list = $3; }
if ($2 ne "default") {
$switch{$2} = join(" ", sort split(" ",$list));
print "On switch: vlan $2: $switch{$2}\n" if $debug;
$id{$1} = $2;
$id{$2} = $1;
print "On switch: vlan #$1 $2: $switch{$2}\n" if $debug;
}
}
close(LIST);
......@@ -86,7 +89,7 @@ if (keys %table) {
if (keys %switch) {
print "On switch only:\n";
foreach $key(sort keys %switch) {
print "$key\t$switch{$key}\n";
print "#$id{$key}: $key\t$switch{$key}\n";
}
$rv++;
}
......
......@@ -4,96 +4,82 @@
# by removing extra vlans/ports, and adding missing ones
#
die("Unimplemented so far... coming soon. - Mac\n");
my $TBROOT = "@prefix@";
my $DBNAME = "@TBDBNAME@";
use English;
$ENV{'PATH'} = '@prefix@/sbin:@prefix@/bin:/bin';
my $debug = 0;
use Mysql;
my $dbh;
my $sth;
my %table = ();
my %switch= ();
# Get all the vlans from the table
$dbh = Mysql->connect("localhost",$DBNAME,"script","none");
$sth = $dbh->
query("select id,members from vlans");
while (@row = $sth->fetchrow_array()) {
my @list = split(" ",$row[1]);
foreach $port (@list) {
my ($node,$card) = split(":",$port);
#if ($node =~ /^(sh\d+)/) { $node = "$1-1"; }
if ($card =~ /[a-zA-Z]/) {
# specified ala ethX
my $sth2 = $dbh->
query("select card from interfaces where node_id='$node' ".
"and iface='$card'");
$card = ($sth2->fetchrow_array())[0];
print "Had '$port', changed to '$node:$card'\n" if $debug;
$port = "$node:$card";
}
}
$table{$row[0]} = join(" ",sort @list);
print "In table: vlan $row[0]: $table{$row[0]}\n" if $debug;
my @extras = ();
my %missing = ();
my $force = 0;
my $args = ":".join(":",@ARGV).":";
if ($args =~ /:-h/) {
die("Usage:\nvlansync [-h[elp]] [-f[orce]]\n".
"vlansync will add any missing VLANs from the database to the switch.\n".
"The -f[orce] option will delete any extra vlans from the switch.\n");
}
# Get all the vlans from the switch
my $list;
open(LIST,"snmpit -l |") || die ("vlandiff: couldn't run snmpit: $!\n");
while(<LIST>) {
chop;
if (/(^ID)|(^--)|(^1 )/) { next; }
s/[\t ]+/ /g;
/(\d+)\s+(\S+)(?:\s+(.*)?)?/;
if (!defined $3) { $list = ""; } else { $list = $3; }
if ($2 ne "default") {
$switch{$2} = join(" ", sort split(" ",$list));
print "On switch: vlan $2: $switch{$2}\n" if $debug;
# Keep this disabled until its safe (after merging dbtoir branch)
#if ($args =~ /:-f/) { $force = 1; }
open(DIFF,"vlandiff |");
my $table = 0;
my $switch = 0;
sub num { $a <=> $b } # for sorting numerically...
while (<DIFF>) {
chomp;
if ( /table/ ) { $table = 1; $switch = 0; next; }
if ( /switch/ ) { $table = 0; $switch = 1; next; }
if ( $table ) {
my ($id, $memb) = split("\t");
$missing{$id} = $memb;
print "Missing $id: $memb\n" if $debug > 1;
}
if ( $switch) {
if ( /^\#(\d+): /) {
push(@extras, $1);
print "Extra: $1\n" if $debug > 1;
} else { die("Couldn't find VLAN number in '$_'\n"); }
}
}
close(LIST);
# Compare all of them...
@extras = sort num @extras;
foreach $key (sort keys %table) {
if ( defined $switch{$key} && ($switch{$key} eq $table{$key}) ) {
# If its in both lists, delete it from both
print "vlan table:$key ($table{$key}) matches switch:$key ($switch{$key})\n"
if $debug;
delete $switch{$key};
delete $table{$key};
if ($debug) {
print "Found missing:\n";
foreach $id (sort num keys %missing) {
print "$id: $missing{$id}\n";
}
print "Found extras: @extras\n";
}
# Print out the difference
if ($force && $#extras > 0 ) {
print "Removing extra VLANs: @extras\n";
system("snmpit -r @extras");
}
my $rv = 0;
if ( (scalar keys %missing) == 0) { exit(0); }
if (keys %table) {
print "In vlans table only:\n";
foreach $key(sort keys %table) {
print "$key\t$table{$key}\n";
}
$rv++;
print "Creating missing vlans:\n";
my $tmpfile = "/tmp/vlansync-$PID";
open(TMP,">$tmpfile") || die("Couldn't open $tmpfile for writing: $!\n");
print TMP "start vlan\n";
foreach $id (sort num keys %missing) {
print "$id $missing{$id}\n";
print TMP "$id $missing{$id}\n";
}
print TMP "end vlan\n";
close TMP;
if (keys %switch) {
print "On switch only:\n";
foreach $key(sort keys %switch) {
print "$key\t$switch{$key}\n";
}
$rv++;
}
system("snmpit -f $tmpfile");
system("rm -f $tmpfile");
# Return 0 if same, 1 for a one-sided diff, 2 for a two-sided diff
# (Could be changed to return total # of different vlans)
exit($rv);
exit(0);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment