From 96fcae9747fcd966ddd92fff45d94402dd3f1a4c Mon Sep 17 00:00:00 2001 From: jmarcus Date: Fri, 8 Feb 2002 16:32:40 +0000 Subject: [PATCH] Merge cnid_maint in from HEAD. Also, eliminate cnid_didname_verify. --- bin/cnid/Makefile.am | 9 +- bin/cnid/cnid_didname_verify.c | 220 ----------------------- bin/cnid/cnid_maint.in | 314 +++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 226 deletions(-) delete mode 100644 bin/cnid/cnid_didname_verify.c create mode 100755 bin/cnid/cnid_maint.in diff --git a/bin/cnid/Makefile.am b/bin/cnid/Makefile.am index 6f3fb13d..c318a3b1 100644 --- a/bin/cnid/Makefile.am +++ b/bin/cnid/Makefile.am @@ -1,12 +1,9 @@ # Makefile.am for bin/cnid/ -CFLAGS = @CFLAGS@ @DB3_CFLAGS@ -LIBS = @LIBS@ @DB3_LIBS@ +EXTRA_DIST = cnid_maint.in if COMPILE_CNID -bin_PROGRAMS = cnid_didname_verify +bin_SCRIPTS = cnid_maint else -bin_PROGRAMS = +bin_SCRIPTS = endif - -cnid_didname_verify_SOURCES = cnid_didname_verify.c diff --git a/bin/cnid/cnid_didname_verify.c b/bin/cnid/cnid_didname_verify.c deleted file mode 100644 index 6e36dc6d..00000000 --- a/bin/cnid/cnid_didname_verify.c +++ /dev/null @@ -1,220 +0,0 @@ -/*- - * See the file LICENSE for redistribution information. - * - * Copyright (c) 1996, 1997, 1998, 1999, 2000 - * Sleepycat Software. All rights reserved. - * - * Modified to check the consistency of didname.db by - * Joe Clarke - * - * $Id: cnid_didname_verify.c,v 1.7.2.1 2001-12-31 20:14:18 srittau Exp $ - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ -#include - -#include - -#include -#include - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif /* ! MIN */ - -#define DBDIDNAME "didname.db" - -int main __P((int, char *[])); -void usage __P((void)); -void version_check __P((void)); - -const char -*progname = "cnid_didname_verify"; /* Program name. */ - -static __inline__ int compare_did(const DBT *a, const DBT *b) { - u_int32_t dida, didb; - - memcpy(&dida, a->data, sizeof(dida)); - memcpy(&didb, b->data, sizeof(didb)); - return dida - didb; -} - -#if DB_VERSION_MINOR > 1 -static int compare_unix(DB *db, const DBT *a, const DBT *b) -#else /* DB_VERSION_MINOR < 1 */ -static int compare_unix(const DBT *a, const DBT *b) -#endif /* DB_VERSION_MINOR */ -{ - u_int8_t *sa, *sb; - int len, ret; - - if ((ret = compare_did(a, b))) - return ret; - - sa = (u_int8_t *) a->data + 4; - sb = (u_int8_t *) b->data + 4; - for (len = MIN(a->size, b->size); len-- > 4; sa++, sb++) - if ((ret = (*sa - *sb))) - return ret; - return a->size - b->size; -} - -int -main(argc, argv) -int argc; -char *argv[]; -{ - extern char *optarg; - extern int optind; - DB *dbp; - DB_ENV *dbenv; - int ch, e_close, exitval, nflag, quiet, ret, t_ret; - char *home; - - version_check(); - - dbenv = NULL; - e_close = exitval = nflag = quiet = 0; - home = NULL; - while ((ch = getopt(argc, argv, "h:NqV")) != EOF) - switch (ch) { - case 'h': - home = optarg; - break; - case 'N': - nflag = 1; - if ((ret = db_env_set_panicstate(0)) != 0) { - fprintf(stderr, - "%s: db_env_set_panicstate: %s\n", - progname, db_strerror(ret)); - exit (1); - } - break; - break; - case 'q': - quiet = 1; - break; - case 'V': - printf("%s\n", db_version(NULL, NULL, NULL)); - exit(0); - case '?': - default: - usage(); - } - argc -= optind; - argv += optind; - - /* - * Create an environment object and initialize it for error - * reporting. - */ - if ((ret = db_env_create(&dbenv, 0)) != 0) { - fprintf(stderr, "%s: db_env_create: %s\n", - progname, db_strerror(ret)); - goto shutdown; - } - e_close = 1; - - /* - * XXX - * We'd prefer to have error output configured while calling - * db_env_create, but there's no way to turn it off once it's - * turned on. - */ - if (!quiet) { - dbenv->set_errfile(dbenv, stderr); - dbenv->set_errpfx(dbenv, progname); - } - -#if DB_VERSION_MINOR > 1 - if (nflag && (ret = dbenv->set_mutexlocks(dbenv, 0)) != 0) { - dbenv->err(dbenv, ret, "set_mutexlocks"); - goto shutdown; - } -#else /* DB_VERSION_MINOR < 1 */ - if (nflag && (ret = db_env_set_mutexlocks(0)) != 0) { - dbenv->err(dbenv, ret, "db_env_set_mutexlocks"); - goto shutdown; - } -#endif /* DB_VERSION_MINOR */ - - /* - * Attach to an mpool if it exists, but if that fails, attach - * to a private region. - */ - if ((ret = dbenv->open(dbenv, - home, DB_INIT_MPOOL | DB_USE_ENVIRON, 0)) != 0 && - (ret = dbenv->open(dbenv, home, - DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0)) != 0) { - dbenv->err(dbenv, ret, "open"); - goto shutdown; - } - - if ((ret = db_create(&dbp, dbenv, 0)) != 0) { - fprintf(stderr, - "%s: db_create: %s\n", progname, db_strerror(ret)); - goto shutdown; - } - - /* This is the crux of the program. We need to make sure we verify - * using the correct sort routine. Else, the database will report - * corruption. */ - dbp->set_bt_compare(dbp, &compare_unix); - - if (!quiet) { - dbp->set_errfile(dbp, stderr); - dbp->set_errpfx(dbp, progname); - } - if ((ret = dbp->verify(dbp, DBDIDNAME, NULL, NULL, 0)) != 0) - dbp->err(dbp, ret, "DB->verify: %s", DBDIDNAME); - if ((t_ret = dbp->close(dbp, 0)) != 0 && ret == 0) { - dbp->err(dbp, ret, "DB->close: %s", DBDIDNAME); - ret = t_ret; - } - if (ret != 0) - goto shutdown; - - if (0) { -shutdown: exitval = 1; - } - if (e_close && (ret = dbenv->close(dbenv, 0)) != 0) { - exitval = 1; - fprintf(stderr, - "%s: dbenv->close: %s\n", progname, db_strerror(ret)); - } - - return (exitval); -} - -void -usage() -{ - fprintf(stderr, "usage: db_verify [-NqV] [-h home] ...\n"); - exit (1); -} - -void -version_check() -{ - int v_major, v_minor, v_patch; - - /* Make sure we're loaded with the right version of the DB library. */ - (void)db_version(&v_major, &v_minor, &v_patch); - if (v_major != DB_VERSION_MAJOR || - v_minor != DB_VERSION_MINOR || v_patch != DB_VERSION_PATCH) { - fprintf(stderr, - "%s: version %d.%d.%d doesn't match library version %d.%d.%d\n", - progname, DB_VERSION_MAJOR, DB_VERSION_MINOR, - DB_VERSION_PATCH, v_major, v_minor, v_patch); - exit (1); - } -} diff --git a/bin/cnid/cnid_maint.in b/bin/cnid/cnid_maint.in new file mode 100755 index 00000000..83b0b5c0 --- /dev/null +++ b/bin/cnid/cnid_maint.in @@ -0,0 +1,314 @@ +#!@PERL@ + +# +# cnid_maint: A script to maintain the consistency of CNID databases. +# +# $Id: cnid_maint.in,v 1.4.2.1 2002-02-08 16:32:40 jmarcus Exp $ +# + +use strict; +use Getopt::Std; +use vars qw( + $APPLE_VOLUMES_FILE + $STOP_CMD + $START_CMD + $PS_CMD + $GREP + $DB_STAT + $DB_RECOVER + $DB_VERIFY + $VERSION + $START_NETATALK + $LOCK_FILE + $HOLDING_LOCK +); + +## Edit ME +$STOP_CMD = '/usr/local/etc/rc.d/netatalk.sh stop'; +$START_CMD = '/usr/local/etc/rc.d/netatalk.sh start'; + +# This ps command needs to output the following fields in the following order: +# USER,PID,PPID,COMMAND +# Below is the example of a BSD ps. A SYSV example is: +# /bin/ps -eflouid,pid,ppid,comm +$PS_CMD = '@PS@ -axouser,pid,ppid,command'; +$DB_STAT = '@DB3_PATH@bin/db_stat'; +$DB_RECOVER = '@DB3_PATH@bin/db_recover'; +$DB_VERIFY = '@DB3_PATH@bin/db_verify'; +$APPLE_VOLUMES_FILE = '@PKGCONFDIR@/AppleVolumes.default'; +## End edit section + +$VERSION = '1.0'; +$GREP = '@GREP@'; +$START_NETATALK = 0; +$LOCK_FILE = tmpdir() . 'cnid_maint.LOCK'; +$HOLDING_LOCK = 0; + +sub LOCK_SH { 1 } +sub LOCK_EX { 2 } +sub LOCK_NB { 4 } +sub LOCK_UN { 8 } + +my $opts = {}; +my $extra_safe = 0; +my $do_verify = 0; +my $remove_logs = 0; + +getopts( 'hsvVl', $opts ); + +if ( $opts->{'v'} ) { + version(); + exit(0); +} +if ( $opts->{'h'} ) { + help(); + exit(0); +} +if ( $opts->{'s'} ) { + $extra_safe = 1; +} +if ( $opts->{'V'} ) { + $do_verify = 1; +} +if ( $opts->{'l'} ) { + $remove_logs = 1; +} + +print "Beginning run of CNID DB Maintanence script at " + . scalar(localtime) . ".\n\n"; + +if ( -f $LOCK_FILE ) { + error( 1, "Lock file $LOCK_FILE exists." ); + end(); +} + +unless ( open( LOCK, ">" . $LOCK_FILE ) ) { + error( 2, "Unable to create $LOCK_FILE: $!" ); +} +flock( LOCK, LOCK_EX ); +$HOLDING_LOCK = 1; + +# Check to see if the AppleVolumes.default file exists. We will use this file +# to get a list of database environments to recover. We will ignore users' +# home directories since that could be a monumental under taking. +if ( !-f $APPLE_VOLUMES_FILE ) { + error( 2, "Unable to locate $APPLE_VOLUMES_FILE" ); +} + +# Use ps to get a list of running afpds. We will track all afpd PIDs that are +# running as root. +unless ( open( PS, $PS_CMD . " | $GREP afpd | $GREP -v grep |" ) ) { + error( 2, "Unable to open a pipe to ps: $!" ); +} + +my $children = 0; +my $processes = 0; +while () { + chomp; + $processes++; + my ( $user, $pid, $ppid, $command ) = split (/\s+/); + if ( ( $user eq "root" && $ppid != 1 ) || ( $user ne "root" ) ) { + $children++; + } +} + +close(PS); + +if ( $children > 1 ) { + + # We have some children. We cannot run recovery. + error( 1, +"Clients are still connected. Database recovery will not be run at this time." + ); + end(); +} + +if ($processes) { + + # Shutdown the running afpds. + $START_NETATALK = 1; + error( 0, "Shutting down afpd process..." ); + error( 2, "Failed to shutdown afpd" ) + if system( $STOP_CMD . ">/dev/null 2>&1" ); +} + +# Now, we parse AppleVolumes.default to get a list of volumes to run recovery +# on. +unless ( open( VOLS, $APPLE_VOLUMES_FILE ) ) { + error( 2, "Unable to open $APPLE_VOLUMES_FILE: $!" ); +} + +my @paths = (); +while () { + s/#.*//; + s/^\s+//; + s/\s+$//; + next unless length; + my ( $path, @options ) = split ( /\s+/, $_ ); + next if ( $path =~ /^~/ ); + my $option = ""; + foreach $option (@options) { + + # We need to check for the dbpath option on each volume. If that + # option is present, we should use its path instead of the actual + # volume path. + if ( $option =~ /^dbpath:/ ) { + push @paths, $'; + } + else { + push @paths, $path; + } + } +} + +close(VOLS); + +my $path = ""; +foreach $path (@paths) { + my $dbpath = $path . "/.AppleDB"; + if ($extra_safe) { + error( 0, + "Checking database environment $dbpath for open connections..." ); + unless ( open( STAT, $DB_STAT . " -h $dbpath -e |" ) ) { + error( 1, "Failed to open a pipe to $DB_STAT: $!" ); + next; + } + + # Now, check each DB environment for any open connections (db_stat calls + # them as references). If a volume has no references, we can do + # recovery on it. Only check this option if the user wants to play + # things extra safe. + my $connections = 0; + while () { + chomp; + s/\s//g; + if (/References\.$/) { + $connections = $`; + last; + } + } + + close(STAT); + + # Print out two different skip messages. This is just for anality. + if ( $connections == 1 ) { + error( 1, "Skipping $dbpath since it has one active connection" ); + next; + } + + if ( $connections > 0 ) { + error( 1, + "Skipping $dbpath since it has $connections active connections" + ); + next; + } + } + + # Run the db_recover command on the environment. + error( 0, "Running db_recover on $dbpath" ); + if ( system( $DB_RECOVER . " -h $dbpath >/dev/null 2>&1" ) ) { + error( 1, "Failed to run db_recover on $dbpath" ); + next; + } + + if ($do_verify) { + error( 0, "Verifying $dbpath/cnid.db" ); + if ( system( $DB_VERIFY . " -q -h $dbpath cnid.db" ) ) { + error( 1, "Verification of $dbpath/cnid.db failed" ); + next; + } + + error( 0, "Verifying $dbpath/devino.db" ); + if ( system( $DB_VERIFY . " -q -h $dbpath devino.db" ) ) { + error( 1, "Verification of $dbpath/devino.db failed" ); + next; + } + + error( 0, "Verifying $dbpath/didname.db" ); + if ( system( $DB_VERIFY . " -q -h $dbpath didname.db" ) ) { + error( 1, "Verification of $dbpath/didname.db failed" ); + next; + } + } + + if ($remove_logs) { + + # Remove the log files if told to do so. + unless ( opendir( DIR, $dbpath ) ) { + error( 1, "Failed to open $dbpath for browsing: $!" ); + next; + } + + my $file = ""; + while ( defined( $file = readdir(DIR) ) ) { + if ( $file =~ /^log\.\d+$/ ) { + error( 0, "Removing $dbpath/$file" ); + unless ( unlink( $dbpath . "/" . $file ) ) { + error( 1, "Failed to remove $dbpath/$file: $!" ); + next; + } + } + } + + closedir(DIR); + } + +} + +end(); + +sub tmpdir { + my $tmpdir; + + foreach ( $ENV{TMPDIR}, "/tmp" ) { + next unless defined && -d && -w _; + $tmpdir = $_; + last; + } + $tmpdir = '' unless defined $tmpdir; + return $tmpdir; +} + +sub error { + my ( $code, $msg ) = @_; + + my $err_types = { + 0 => "INFO", + 1 => "WARNING", + 2 => "ERROR", + }; + + print $err_types->{$code} . ": " . $msg . "\n"; + + end() if ( $code == 2 ); +} + +sub end { + if ($START_NETATALK) { + error( 0, "Restarting Netatalk" ); + if ( system( $START_CMD . " >/dev/null 2>&1" ) ) { + print "ERROR: Failed to restart Netatalk\n"; + } + } + if ($HOLDING_LOCK) { + close(LOCK); + unlink($LOCK_FILE); + } + print "\nRun of CNID DB Maintenance script ended at " + . scalar(localtime) . ".\n"; + exit(0); +} + +sub version { + print "cnid_maint.pl version $VERSION\n"; +} + +sub help { + print "usage: cnid_maint.pl [-hlsvV]\n"; + print "\t-h view this message\n"; + print "\t-l remove transaction logs after running recovery\n"; + print + "\t-s be extra safe in verifying there are no open DB connections\n"; + print "\t-v print version and exit\n"; + print "\t-V run a verification on all database files after recovery\n"; +} -- 2.39.2