dbupdate.pl fix
- Date: Wed, 14 Dec 1994 11:50:17 +0100
Folks,
dbupdate has a small bug where it can fail to do the proper indexing
when handling multiple indexes (ie a mix or routes, inetnums and
inet-rtrs). It can fail to use the correct index when one of the
objects generates warnings and/or errors. Please replace
src/dbupdate.pl with the one below and run a "make install" in the top
level database directory.
-Marten
PS For all who are running "older" versions of the DB or have not kept
up with all patches I have been sending out, the version on
ftp://ncc.ripe.net/dbase-beta/dbase-beta.tar.gz is kept up-to-date.
#!PERL
# $RCSfile: dbupdate.pl,v $
# $Revision: 0.31 $
# $Author: marten $
# $Date: 1994/12/14 10:40:55 $
# This is a client that will update objects read from a file directly
# in the database, without interference of updated.
# It will update the object in the database that is linked to the
# source field of the object, so better make sure you have a source field
# and that it has a database associated to it ...
@INC = ("LIBDIR", @INC);
require "adderror.pl";
require "cldb.pl";
require "dbadd.pl";
require "dbclose.pl";
require "dbopen.pl";
require "defines.pl";
require "enparse.pl";
require "entype.pl";
require "enwrite.pl";
require "getopts.pl";
require "handle.pl";
require "misc.pl";
require "rconf.pl";
require "rfc822.pl";
require "syslog.pl";
# Parse options:
#
# -l logfile - log output to file in stead of STDOUT
# -v - verbose output (LONGACK)
# -M - treat input file (or STDIN) as mail and compose
# and send ack mail back
# -A - assume "assign" mode, only add will be allowed
# usually set by parsing mail headers
# -H - handle assignment mode. Will only accept persons
# with nic-handle "assign" and return person entry
# with the handle assigned and filled in.
# (very RIPE database dependent)
&Getopts('l:vMAHV');
# Need this below for running perl in tainted mode.
$ENV{"PATH"} = "";
$ENV{"SHELL"} = "/bin/sh";
$ENV{"IFS"} = "";
# Read config file from RIPEDBCNF, or set to default.
$conffile=$ENV{"RIPEDBCNF"};
$conffile= "DEFCONFIG" unless $conffile;
print STDERR "dbupdate - reading config\n" if $opt_V;
&rconf($conffile);
# Save STDOUT
open(SAVEOUT, ">&STDOUT");
# Open one ack file. Proper handling (stdout, logfile or mail) is handled
# all the way at the end .... But, we open the logfile if needed already
# because we need to exit if we cannot even create that file ...
# If -M option is specified, -l is overruled.
if ($opt_l) {
open(LOGFILE, ">$opt_l") || die "Cannot create $opt_l: $!";
}
open(STDOUT, ">$TMPDIR/dbupdack.$$") ||
&syslog("ERRLOG", "cannot create tmp ack file: $!");
# Make STDOUT unbuffered
select(STDOUT); $| = 1;
#
# printstat ( arg )
#
# int arg /* 0=failed, 1=ok, 2=noop */
#
# prints verbose version of the update result.
#
sub printstat {
local($stat) = @_;
local($type) = &entype(*entry);
print STDERR "dbupdate - printstat($stat) called\n" if $opt_V;
print STDERR "dbupdate - printstat 1\n" if $opt_V;
if ($hasdelete) {
print "Delete ";
} else {
print "Update ";
}
print STDERR "dbupdate - printstat 2\n" if $opt_V;
if ($stat == 1) { print "OK: ";}
elsif ($stat == 2) { print "NOOP: ";}
else { print "FAILED: "; }
print STDERR "dbupdate - printstat 3\n" if $opt_V;
print "[$ATTL{$type}] $entry{$type}\n\n";
print STDERR "dbupdate - printstat 4\n" if $opt_V;
}
#
# doaction ( )
#
# does all the work.
#
sub doaction {
local($file) = @_;
local($donestat) = 0;
print STDERR "dbupdate - doaction($file) called\n" if $opt_V;
while(1) {
$donestat = 0;
print STDERR "dbupdate - calling enparse\n" if $opt_V;
($parsestat, %entry) = &enparse($file);
# next if nothing was read
next if $parsestat == $NOK;
# return if only thing read was EOF
return if $parsestat == $EOF;
$somethingfound = 1;
print STDERR "dbupdate - read an object\n" if $opt_V;
# Now, let's check whether this is a delete request or not
# If it is, we have to skip all syntax checks ...
# A wrongly defined delete attribute will return a 0,
# and add a error message.
$hasdelete = &hasdelete(*entry);
# object parsing generated an error, output object with
# errors and return. If we are deleting an object, then
# forget about it, we do not want to return with syntax
# errors
local($type) = &entype(*entry);
# now if we are running in -H mode, just next when we have
# not found a person.
if ($opt_H) {
if ($type ne "pn") {
print "No person object found\n";
next;
} else {
if ($entry{"nh"} !~ /^[aA][sS][sS][iI][gG][nN]$/) {
&adderror(*entry, "nichandle \"assign\" expected");
print "\n" if &enwrite(*entry,1,1);
next;
}
}
}
if (($parsestat == $O_ERROR) && (!$hasdelete)) {
print STDERR "dbupdate - object has error and no delete\n" if $opt_V;
if ($opt_v) { &printstat(0); }
$haserror = 1;
print "\n" if &enwrite(*entry,1,1,1);
next;
}
# If we have to delete, remove all parse errors and warnings
# and set status to OK: we do not want deletes to be checked
if ($hasdelete) {
&rmwarnings(*entry);
&rmerrors(*entry);
$parsestat= $O_OK;
}
# object parsed OK or has only warnings
if ($parsestat == $O_OK || $parsestat == $O_WARNING) {
# open database file associated with "so" for writing
local(*db) = 'currentdb';
print STDERR "dbupdate - opening database\n" if $opt_V;
if (&dbopen(db, *entry, 1)) {
# Open classless database
print STDERR "dbupdate - opening classless db\n" if $opt_V;
&dbclopen(*entry,1);
# Looks like we have some locking problem, so let's
# lock the thing before we do anything else ...
print STDERR "dbupdate - locking databases\n" if $opt_V;
&dblock(*db);
# do we delete or not ?
if ($hasdelete) {
print STDERR "dbupdate - deleting entry\n" if $opt_V;
$dbstat = &dbdel(*db, *entry);
# We do handle generation, so after a delete, we
# have to delete the handle from that database
# as well.
if ($DOHANDLE && $HANDLEATTR{$type}) {
if ($dbstat == $OK) {
&DeleteHandle(*entry);
}
}
} else {
# We do handle generation, check whether we have
# to assign a nic handle (if nh value is "assign")
# Can be a request handle of form: assign handle
# Line has already been syntax checked, so no worries
# there. Errors are added by AssignHandle, so all we
# need is an extra check.
if ($DOHANDLE && $HANDLEATTR{$type}) {
if ($entry{$HANDLEATTR{$type}} =~ /^[Aa][Ss][Ss][Ii][Gg][Nn]\s*(.*)$/) {
local($handle) = &AssignHandle(*entry, $1);
} else {
# new object, we may have to put the handle
# in the database. AddHandle will return if
# if the handle is already in handle database
&AddHandle(*entry);
}
}
if (!&haserror(*entry)) {
# NEW assignments && !person
if ($opt_A && ($type ne "pn")) {
print STDERR "dbupdate - calling dbadd\n" if $opt_V;
$dbstat = &dbadd(*db, *entry);
} else {
print STDERR "dbupdate - calling add_or_mod\n" if $opt_V;
$dbstat = &dbadd_or_modify(*db, *entry);
}
} else {
# Fake dbstat, has error due to handle
# generation ...
$dbstat = $OK;
}
}
# Totally yucky, but I do not know how to
# do this better right now. The thing is that
# every exit code but E_NOOP and OK are
# errors, so catch NOOP first, and print
# verbose warning if needed, then OK, and
# then handle all the others as errors.
# noop, just print stat if verbose
print STDERR "dbupdate - doing acks\n" if $opt_V;
if ($dbstat == $E_NOOP && $opt_v) {
&printstat(2);
$donestat = 1;
}
elsif (($dbstat != $OK) && ($dbstat != $E_NOOP)){
&adderror(*entry, "$MESSAGE[$dbstat]");
}
# Object has errors, so print, and next
if (&haserror(*entry)) {
print STDERR "dbupdate - object has errors\n" if $opt_V;
$haserror = 1;
if ($opt_v) { &printstat(0); }
print "\n" if &enwrite(*entry,1,1);
&dbunlock(*db);
&dbclose(*db);
&dbclclose();
next;
}
# object has only warnings, so it must have
# been processed. print and next.
elsif (&haswarning(*entry)) {
print STDERR "dbupdate - object has warnings\n" if $opt_V;
$haserror = 1;
if (!$donestat && $opt_v) {
&printstat(1);
}
print "\n" if &enwrite(*entry,1,1);
&dbunlock(*db);
&dbclose(*db);
&dbclclose();
next;
}
# all was OK, so only print stat if verbose
if ($opt_v && !$donestat) { &printstat(1); }
# all was OK, print object if nichandle assign mode
if ($opt_H) {
&enwrite(*entry,1,1);
}
&dbunlock(*db);
&dbclose(*db);
&dbclclose();
}
else {
# Not too good, probably permission problem
# if this is given as output ...
&adderror(*entry, "Failed to open DB file: $!");
&adderror(*entry, "Please check the \"source:\" value");
&adderror(*entry, "Contact <$HUMAILBOX> if source seems ok");
&printstat(0);
&enwrite(*entry,1,1);
}
}
}
close(TMP);
}
#
# Main program
#
# We want to make a local copy of the input, because we need to do mutliple
# things with it ...
open(COPY, ">$TMPDIR/dbupdcopy.$$") || die "Cannot open copy file: $!";
select(COPY); $| = 1; select(STDOUT);
while (<>) {
print COPY;
}
close(COPY);
# Now we open the copy to actually process
open(COPY, "$TMPDIR/dbupdcopy.$$") || die "Cannot open copy: $!";
# We have a mail, so let's first parse the headers ...
if ($opt_M) {
local($stat) = &parserfc822(COPY);
# If we have at least a return address, compose the header of
# the ack mail
if ($stat) {
if ($TESTMODE) {
print "To: $DEFMAIL\n";
} else {
print "To: $FROM\n";
}
eval "print \"$MHEADER\";";
eval "print \"$MAILTXT\";";
# If not we are in trouble once more ...
} else {
print "Header could not be parsed ...\n";
}
}
# Take all the stuff from file COPY in doaction. It will process the
# whole stuff.
&doaction(COPY);
if (!$somethingfound) {
print "** No objects were found in your message **\n";
}
elsif ($haserror) {
print "$ACKERR" unless $opt_H;
} else {
print "$ACKOK" unless $opt_H;
}
print $ACKSIG unless $opt_H;
close(COPY);
close(STDOUT);
open(STDOUT, ">&SAVEOUT");
# Output the ack, if -M specified then no logfile or stdout will be given.
if ($opt_M) {
system("$MAILCMD < $TMPDIR/dbupdack.$$");
} else {
open(TMP, "$TMPDIR/dbupdack.$$");
while (<TMP>) {
if ($opt_l) {
print LOGFILE;
} else {
print;
}
}
close(TMP);
}
# We sent out the ack, now send out the notifications if needed
if (%notify) {
&SendNotifications();
}
if (%forward) {
&SendForwardMails();
}
# log all stuff to the right places
# todays YYMMDD
local($s,$m,$h,$md,$mo,$y,$wd,$yd,$is) = localtime(time);
$mo+=1;
$mo = "0".$mo unless $mo > 9;
$md = "0".$md unless $md > 9;
$y = "0".$y unless $y > 9;
$YYMMDD = "$y$mo$md";
# This may seem yucky, but is needed to untaint the filename ...
$filename = $LOGFILE{"UPDLOG"}."/".$YYMMDD;
$filename =~ /(.*)/;
$realfile = $1;
# first let's log the updates send in or via stdin
if (open(LOG, ">>$realfile")) {
&lock(LOG);
if ($opt_M) {
print LOG "\n>>> MAIL UPDATE <<<\n\n";
} else {
print LOG "\n>>> STDIN UPDATE <<<\n\n";
}
open(TMP, "$TMPDIR/dbupdcopy.$$");
while (<TMP>) {
print LOG;
}
close(TMP);
close(LOG);
&unlock(LOG);
} else {
&syslog("ERRLOG", "dbupdate cannot open $LOGFILE{\"UPDLOG\"}/$YYMMDD");
}
# then we log the acknowledgement
$filename = $LOGFILE{"ACKLOG"}."/".$YYMMDD;
$filename =~ /(.*)/;
$realfile = $1;
if (open(LOG, ">>$realfile")) {
&lock(LOG);
if ($opt_M) {
print LOG "\n>>> MAIL ACK <<<\n\n";
} else {
print LOG "\n>>> STDIN ACK <<<\n\n";
}
open(TMP, "$TMPDIR/dbupdack.$$");
while (<TMP>) {
print LOG;
}
close(TMP);
close(LOG);
&unlock(LOG);
} else {
&syslog("ERRLOG", "dbupdate cannot open $LOGFILE{\"ACKLOG\"}/$YYMMDD");
}
# remove the temp stuff we made
unlink("$TMPDIR/dbtmp.$$");
unlink("$TMPDIR/dbupdcopy.$$");
unlink("$TMPDIR/dbupdack.$$");