summaryrefslogtreecommitdiffstats
path: root/ldap/admin/src/scripts/template-cl-dump.pl
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/admin/src/scripts/template-cl-dump.pl')
-rwxr-xr-xldap/admin/src/scripts/template-cl-dump.pl307
1 files changed, 307 insertions, 0 deletions
diff --git a/ldap/admin/src/scripts/template-cl-dump.pl b/ldap/admin/src/scripts/template-cl-dump.pl
new file mode 100755
index 00000000..fe89de2a
--- /dev/null
+++ b/ldap/admin/src/scripts/template-cl-dump.pl
@@ -0,0 +1,307 @@
+#{{PERL-EXEC}}
+
+################################################################################
+#
+# Copyright (C) 2002-2004 Netscape Communications Corporation.
+# All rights reserved.
+#
+# FILE: cl-dump.pl
+#
+# SYNOPSIS:
+#
+# cl-dump.pl [-h host] [-p port] [-D bind-dn] -w bind-password | -P bind-cert\
+# [-r replica-roots] [-o output-file] [-c] [-v]\n";
+#
+# cl-dump.pl -i changelog-ldif-file-with-base64encoding [-o output-file] [-c]\n";
+#
+# DESCRIPTION:
+# Dump and decode Netscape Directory Server replication change log
+#
+# OPTIONS:
+#
+# -c Dump and interpret CSN only. This option can be used with or
+# without -i option.
+#
+# -D bind-dn
+# Directory server's bind DN. Default to "cn=Directory Manager" if
+# the option is omitted.
+#
+# -h host
+# Directory server's host. Default to the server where the script
+# is running.
+#
+# -i changelog-ldif-file-with-base64encoding
+# If you already have a ldif-like changelog, but the changes
+# in that file are encoded, you may use this option to
+# decode that ldif-like changelog.
+#
+# -o output-file
+# Path name for the final result. Default to STDOUT if omitted.
+#
+# -p port
+# Directory server's port. Default to 389.
+#
+# -P bind-cert
+# Pathname of binding certificate DB
+#
+# -r replica-roots
+# Specify replica roots whose changelog you want to dump. The replica
+# roots may be seperated by comma. All the replica roots would be
+# dumped if the option is omitted.
+#
+# -v Print the version of this script.
+#
+# -w bind-password
+# Password for the bind DN
+#
+# RESTRICTION:
+# If you are not using -i option, the script should be run when the server
+# is running, and from where the server's changelog directory is accessible.
+#
+# DIAGNOSIS:
+# For environment variable issues, see script template-repl-monitor.pl under
+# DSHOME/bin/slapd/admin/scripts
+#
+################################################################################
+$usage="Usage: $0 [-h host] [-p port] [-D bind-dn] [-w bind-password | -P bind-cert] [-r replica-roots] [-o output-file] [-c] [-v]\n\n $0 -i changelog-ldif-file-with-base64encoding [-o output-file] [-c]";
+
+use Getopt::Std; # Parse command line arguments
+use Mozilla::LDAP::Conn; # LDAP module for Perl
+use Mozilla::LDAP::Utils; # LULU, utilities.
+use Mozilla::LDAP::API; # Used to parse LDAP URL
+use MIME::Base64; # Decode
+
+# Global variables
+
+$version = "Netscape Directory Server Changelog Dump - Version 1.0";
+
+#main
+{
+ # Turn off buffered I/O
+ $| = 1;
+
+ # Check for legal options
+ if (!getopts('h:p:D:w:P:r:o:cvi:')) {
+ print $usage;
+ exit -1;
+ }
+
+ exit -1 if &validateArgs;
+
+ if ($opt_v) {
+ print OUTPUT "$version\n";
+ exit;
+ }
+
+ if (!$opt_i) {
+ &cl_dump_and_decode;
+ }
+ elsif ($opt_c) {
+ &grep_csn ($opt_i);
+ }
+ else {
+ &cl_decode ($opt_i);
+ }
+
+ close (OUTPUT);
+}
+
+# Validate the parameters
+sub validateArgs
+{
+ my ($rc) = 0;
+
+ %ld = Mozilla::LDAP::Utils::ldapArgs();
+ chop ($ld{host} = `hostname`) if !$opt_h;
+ $ld{bind} = "cn=Directory Manager" if !$opt_D;
+ @allreplicas = ($opt_r) if ($opt_r);
+ if ($opt_o && ! open (OUTPUT, ">$opt_o")) {
+ print "Can't create output file $opt_o\n";
+ $rc = -1;
+ }
+ # Open STDOUT if option -o is missing
+ open (OUTPUT, ">-") if !$opt_o;
+
+ return $rc;
+}
+
+# Dump and decode changelog
+# OUTPUT should have been opened before this call
+sub cl_dump_and_decode
+{
+ # Open the connection
+ my ($conn) = new Mozilla::LDAP::Conn (\%ld);
+ if (!$conn) {
+ print OUTPUT qq/Can't connect to $ld{host}:$ld{port} as "$ld{bind}"\n/;
+ return -1;
+ }
+
+ # Get the changelog dir
+ my ($changelogdir);
+ my ($entry) = $conn->search ("cn=changelog5,cn=config", "sub", "(objectClass=*)");
+ while ($entry) {
+ $changelogdir = $entry->{"nsslapd-changelogdir"}[0];
+ last if $changelogdir;
+ $entry = $conn->nextEntry ();
+ }
+
+ # Get all the replicas on the server if -r option is not specified
+ if (!$opt_r) {
+ $entry = $conn->search ("cn=mapping tree,cn=config", "sub",
+ "(objectClass=nsDS5Replica)");
+ while ($entry) {
+ push (@allreplicas, "$entry->{nsDS5ReplicaRoot}[0]");
+ $entry = $conn->nextEntry ();
+ }
+ }
+
+ # Dump the changelog for the replica
+ my (@ldifs);
+ my ($replica);
+ my ($gotldif);
+ my ($ldif);
+ foreach (@allreplicas) {
+ # Reset the script's start time
+ $^T = time;
+
+ $replica = $_;
+ $gotldif = 0;
+
+ # Can't move this line before entering the loop:
+ # no ldif file generated other than for the first
+ # replica.
+ $entry = $conn->newEntry();
+ $entry->setDN ("cn=replica,cn=\"$_\",cn=mapping tree,cn=config");
+ $entry->setValues('nsDS5Task', 'CL2LDIF');
+ $conn->update ($entry);
+
+ #Decode the dumped changelog
+ @ldifs = <$changelogdir/*.ldif>;
+ foreach (@ldifs) {
+ # Skip older ldif files
+ next if ($#ldifs > 0 && (-M $_ > 0));
+ $ldif = $_;
+ $gotldif = 1;
+ &print_header ($replica, 0);
+ if ($opt_c) {
+ &grep_csn ($_);
+ }
+ else {
+ &cl_decode ($_);
+ }
+ # Test op -M doesn't work well so we use rename
+ # here to avoid reading the same ldif file more
+ # than once.
+ rename ($ldif, "$ldif.done");
+ }
+ &print_header ($replica, "Not Found") if !$gotldif;
+ }
+ $conn->close;
+}
+
+sub print_header
+{
+ my ($replica, $ldif) = @_;
+ print OUTPUT "\n# Replica Root: $replica" if $replica;
+ print OUTPUT "\n# LDIF File : $ldif\n" if $ldif;
+}
+
+# Grep and interpret CSNs
+# OUTPUT should have been opened before this call
+sub grep_csn
+{
+ open (INPUT, "@_") || return;
+ &print_header (0, @_);
+
+ my ($csn, $maxcsn, $modts);
+ while (<INPUT>) {
+ next if ($_ !~ /(csn:)|(ruv:)/i);
+ if (/ruv:\s*{.+}\s+(\w+)\s+(\w+)\s+(\w*)/i) {
+ #
+ # RUV with two CSNs and an optional lastModifiedTime
+ #
+ $csn = &csn_to_string($1);
+ $maxcsn = &csn_to_string($2);
+ $modts = $3;
+ if ( $modts =~ /^0+$/ ) {
+ $modts = "";
+ }
+ else {
+ $modts = &csn_to_string($modts);
+ }
+ }
+ elsif (/csn:\s*(\w+)\s+/i || /ruv:\s*{.+}\s+(\w+)\s+/i) {
+ #
+ # Single CSN
+ #
+ $csn = &csn_to_string($1);
+ $maxcsn = "";
+ $modts = "";
+ }
+ else {
+ printf OUTPUT;
+ next;
+ }
+ chop;
+ printf OUTPUT "$_ ($csn";
+ printf OUTPUT "; $maxcsn" if $maxcsn;
+ printf OUTPUT "; $modts" if $modts;
+ printf OUTPUT ")\n";
+ }
+}
+
+sub csn_to_string
+{
+ my ($csn, $tm, $seq, $masterid, $subseq);
+ my ($sec, $min, $hour, $mday, $mon, $year);
+
+ $csn = "@_";
+ return $csn if !$csn;
+
+ ($tm, $seq, $masterid, $subseq) = unpack("a8 a4 a4 a4", $csn);
+ $tm = hex($tm);
+ $seq = hex($seq);
+ $masterid = hex($masterid);
+ $subseq = hex($subseq);
+ ($sec, $min, $hour, $mday, $mon, $year) = localtime ($tm);
+ $mon++;
+ $year += 1900;
+ foreach ($sec, $min, $hour, $mday, $mon) {
+ $_ = "0".$_ if ($_ < 10);
+ }
+ $csn = "$mon/$mday/$year $hour:$min:$sec";
+ $csn .= " $seq $subseq" if ( $seq != 0 || $subseq != 0 );
+
+ return $csn;
+}
+
+# Decode the changelog
+# OUTPUT should have been opened before this call
+sub cl_decode
+{
+ open (INPUT, "@_") || return;
+ &print_header (0, @_);
+
+ my ($encoded);
+ undef $encoded;
+ while (<INPUT>) {
+ # Try to accomodate "changes" in 4.X and "change" in 6.X
+ if (/^changes?::\s*(\S*)/i) {
+ print OUTPUT "change::\n";
+ $encoded = $1;
+ next;
+ }
+ if (!defined ($encoded)) {
+ print OUTPUT;
+ next;
+ }
+ if ($_ eq "\n") {
+ print OUTPUT MIME::Base64::decode($encoded);
+ print OUTPUT "\n";
+ undef $encoded;
+ next;
+ }
+ /^\s*(\S+)\s*\n/;
+ $encoded .= $1;
+ }
+}