diff options
author | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
---|---|---|
committer | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
commit | b2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch) | |
tree | cf58939393a9032182c4fbc4441164a9456e82f8 /ldap/admin/src/scripts | |
download | ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip |
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/admin/src/scripts')
24 files changed, 28328 insertions, 0 deletions
diff --git a/ldap/admin/src/scripts/template-bak2db.pl b/ldap/admin/src/scripts/template-bak2db.pl new file mode 100644 index 00000000..10cb99bc --- /dev/null +++ b/ldap/admin/src/scripts/template-bak2db.pl @@ -0,0 +1,89 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +sub usage { + print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n"); + print(STDERR " : -a dirname [-t dbtype]\n"); + print(STDERR " Opts: -D rootdn - Directory Manager\n"); + print(STDERR " : -w password - Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for Directory Manager's password\n"); + print(STDERR " : -j filename - Read Directory Manager's password from file\n"); + print(STDERR " : -a dirname - backup directory\n"); + print(STDERR " : -t dbtype - database type (default: ldbm database)\n"); + print(STDERR " : -v - verbose\n"); +} +$taskname = ""; +$archivedir = ""; +$dbtype = "ldbm database"; +$dsroot = "{{DS-ROOT}}"; +$mydsroot = "{{MY-DS-ROOT}}"; +$verbose = 0; +$rootdn = ""; +$passwd = ""; +$passwdfile = ""; +$i = 0; +while ($i <= $#ARGV) { + if ("$ARGV[$i]" eq "-a") { # backup directory + $i++; $archivedir = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-D") { # Directory Manager + $i++; $rootdn = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-w") { # Directory Manager's password + $i++; $passwd = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-j") { # Read Directory Manager's password from a file + $i++; $passwdfile = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-t") { # database type + $i++; $dbtype = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-v") { # verbose + $verbose = 1; + } else { + &usage; exit(1); + } + $i++; +} +if ($passwdfile ne ""){ +# Open file and get the password + unless (open (RPASS, $passwdfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $passwd = <RPASS>; + chomp($passwd); + close(RPASS); +} elsif ($passwd eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $passwd = ReadLine(0); +# chomp($passwd); +# ReadMode('normal'); +} +if ( $rootdn eq "" || $passwd eq "") { &usage; exit(1); } +($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time); +$mn++; $yr += 1900; +$taskname = "restore_${yr}_${mn}_${dy}_${h}_${m}_${s}"; +if ($archivedir eq "") { + &usage; exit(1); +} +$dn = "dn: cn=$taskname, cn=restore, cn=tasks, cn=config\n"; +$misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n"; +$cn = "cn: $taskname\n"; +$nsarchivedir = "nsArchiveDir: $archivedir\n"; +$nsdbtype = "nsDatabaseType: $dbtype\n"; +$entry = "${dn}${misc}${cn}${nsarchivedir}${nsdbtype}"; +$vstr = ""; +if ($verbose != 0) { $vstr = "-v"; } +chdir("$dsroot{{SEP}}shared{{SEP}}bin"); +open(FOO, "| $dsroot{{SEP}}shared{{SEP}}bin{{SEP}}ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" ); +print(FOO "$entry"); +close(FOO); 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; + } +} diff --git a/ldap/admin/src/scripts/template-db2bak.pl b/ldap/admin/src/scripts/template-db2bak.pl new file mode 100644 index 00000000..f9514997 --- /dev/null +++ b/ldap/admin/src/scripts/template-db2bak.pl @@ -0,0 +1,89 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +sub usage { + print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n"); + print(STDERR " [-a dirname] [-t dbtype]\n"); + print(STDERR " Opts: -D rootdn - Directory Manager\n"); + print(STDERR " : -w password - Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for Directory Manager's password\n"); + print(STDERR " : -j filename - Read Directory Manager's password from file\n"); + print(STDERR " : -a dirname - backup directory\n"); + print(STDERR " : -t dbtype - database type (default: ldbm database)\n"); + print(STDERR " : -v - verbose\n"); +} +$taskname = ""; +$archivedir = ""; +$dbtype = "ldbm database"; +$dsroot = "{{DS-ROOT}}"; +$mydsroot = "{{MY-DS-ROOT}}"; +$verbose = 0; +$rootdn = ""; +$passwd = ""; +$passwdfile = ""; +$i = 0; +while ($i <= $#ARGV) { + if ("$ARGV[$i]" eq "-a") { # backup directory + $i++; $archivedir = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-D") { # Directory Manager + $i++; $rootdn = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-w") { # Directory Manager's password + $i++; $passwd = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-j") { # Read Directory Manager's password from a file + $i++; $passwdfile = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-t") { # database type + $i++; $dbtype = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-v") { # verbose + $verbose = 1; + } else { + &usage; exit(1); + } + $i++; +} +if ($passwdfile ne ""){ +# Open file and get the password + unless (open (RPASS, $passwdfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $passwd = <RPASS>; + chomp($passwd); + close(RPASS); +} elsif ($passwd eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $passwd = ReadLine(0); +# chomp($passwd); +# ReadMode('normal'); +} +if ( $rootdn eq "" || $passwd eq "") { &usage; exit(1); } +($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time); +$mn++; $yr += 1900; +$taskname = "backup_${yr}_${mn}_${dy}_${h}_${m}_${s}"; +if ($archivedir eq "") { + $archivedir = "${mydsroot}{{SEP}}bak{{SEP}}${yr}_${mn}_${dy}_${h}_${m}_${s}"; +} +$dn = "dn: cn=$taskname, cn=backup, cn=tasks, cn=config\n"; +$misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n"; +$cn = "cn: $taskname\n"; +$nsarchivedir = "nsArchiveDir: $archivedir\n"; +$nsdbtype = "nsDatabaseType: $dbtype\n"; +$entry = "${dn}${misc}${cn}${nsarchivedir}${nsdbtype}"; +$vstr = ""; +if ($verbose != 0) { $vstr = "-v"; } +chdir("$dsroot{{SEP}}shared{{SEP}}bin"); +open(FOO, "| $dsroot{{SEP}}shared{{SEP}}bin{{SEP}}ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" ); +print(FOO "$entry"); +close(FOO); diff --git a/ldap/admin/src/scripts/template-db2index.pl b/ldap/admin/src/scripts/template-db2index.pl new file mode 100644 index 00000000..237cf952 --- /dev/null +++ b/ldap/admin/src/scripts/template-db2index.pl @@ -0,0 +1,195 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +sub usage { + print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n"); + print(STDERR " -n instance [-t attributeName[:indextypes[:matchingrules]]]\n"); + print(STDERR " Opts: -D rootdn - Directory Manager\n"); + print(STDERR " : -w password - Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for Directory Manager's password\n"); + print(STDERR " : -j filename - Read Directory Manager's password from file\n"); + print(STDERR " : -n instance - instance to be indexed\n"); + print(STDERR " : -t attributeName[:indextypes[:matchingrules]]\n"); + print(STDERR " - attribute: name of the attribute to be indexed\n"); + print(STDERR " If omitted, all the indexes defined \n"); + print(STDERR " for that instance are generated.\n"); + print(STDERR " - indextypes: comma separated index types\n"); + print(STDERR " - matchingrules: comma separated matrules\n"); + print(STDERR " Example: -t foo:eq,pres\n"); + print(STDERR " : -v - version\n"); +} + +$instance = ""; +$rootdn = ""; +$passwd = ""; +$passwdfile = ""; +$attribute_arg = ""; +$vlvattribute_arg = ""; +$verbose = 0; + +$dsroot = "{{DS-ROOT}}"; +$mydsroot = "{{MY-DS-ROOT}}"; + +$i = 0; +while ($i <= $#ARGV) +{ + if ("$ARGV[$i]" eq "-n") + { + # instance + $i++; $instance = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-D") + { + # Directory Manager + $i++; $rootdn = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-w") + { + # Directory Manager's password + $i++; $passwd = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-j") + { + # Read Directory Manager's password from a file + $i++; $passwdfile = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-t") + { + # Attribute to index + $i++; $attribute_arg = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-T") + { + # Vlvattribute to index + $i++; $vlvattribute_arg = $ARGV[$i]; + } + elsif ("$ARGV[$i]" eq "-v") + { + # verbose + $verbose = 1; + } + else + { + &usage; exit(1); + } + $i++; +} + +if ($passwdfile ne ""){ +# Open file and get the password + unless (open (RPASS, $passwdfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $passwd = <RPASS>; + chomp($passwd); + close(RPASS); +} elsif ($passwd eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $passwd = ReadLine(0); +# chomp($passwd); +# ReadMode('normal'); +} + +if ( $rootdn eq "" || $passwd eq "" ) +{ + &usage; + exit(1); +} + +$vstr = ""; +if ($verbose != 0) +{ + $vstr = "-v"; +} + +($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time); +$mn++; $yr += 1900; +$taskname = "db2index_${yr}_${mn}_${dy}_${h}_${m}_${s}"; + +if ( $instance eq "" ) +{ + &usage; + exit(1); +} +else +{ + # No attribute name has been specified: let's get them from the configuration + $attribute=""; + $indexes_list=""; + $vlvattribute=""; + $vlvindexes_list=""; + if ( $attribute_arg eq "" && $vlvattribute_arg eq "" ) + { + # Get the list of indexes from the entry + $indexes_list="$dsroot{{SEP}}shared{{SEP}}bin{{SEP}}ldapsearch $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -s one " . + "-b \"cn=index,cn=\"$instance\", cn=ldbm database,cn=plugins,cn=config\" \"(&(objectclass=*)(nsSystemIndex=false))\" cn"; + + # build the values of the attribute nsIndexAttribute + open(LDAP1, "$indexes_list |"); + while (<LDAP1>) { + s/\n //g; + if (/^cn: (.*)\n/) { + $IndexAttribute="nsIndexAttribute"; + $attribute="$attribute$IndexAttribute: $1\n"; + } + } + close(LDAP1); + if ( $attribute eq "" ) + { + # No attribute to index, just exit + exit(0); + } + + # Get the list of indexes from the entry + $vlvindexes_list="$dsroot{{SEP}}shared{{SEP}}bin{{SEP}}ldapsearch $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -s sub -b \"cn=\"$instance\", cn=ldbm database,cn=plugins,cn=config\" \"objectclass=vlvIndex\" cn"; + + # build the values of the attribute nsIndexVlvAttribute + open(LDAP1, "$vlvindexes_list |"); + while (<LDAP1>) { + s/\n //g; + if (/^cn: (.*)\n/) { + $vlvIndexAttribute="nsIndexVlvAttribute"; + $vlvattribute="$vlvattribute$vlvIndexAttribute: $1\n"; + } + } + close(LDAP1); + } + else + { + if ( $attribute_arg ne "" ) + { + $attribute="nsIndexAttribute: $attribute_arg\n"; + } + if ( $vlvattribute_arg ne "" ) + { + $vlvattribute="nsIndexVlvAttribute: $vlvattribute_arg\n"; + } + } + + # Build the task entry to add + + $dn = "dn: cn=$taskname, cn=index, cn=tasks, cn=config\n"; + $misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n"; + $cn = "cn: $taskname\n"; + $nsinstance = "nsInstance: ${instance}\n"; + + $entry = "${dn}${misc}${cn}${nsinstance}${attribute}${vlvattribute}"; +} +chdir("$dsroot{{SEP}}shared{{SEP}}bin"); +open(FOO, "| $dsroot{{SEP}}shared{{SEP}}bin{{SEP}}ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" ); +print(FOO "$entry"); +close(FOO); diff --git a/ldap/admin/src/scripts/template-db2ldif.pl b/ldap/admin/src/scripts/template-db2ldif.pl new file mode 100644 index 00000000..cd5b6065 --- /dev/null +++ b/ldap/admin/src/scripts/template-db2ldif.pl @@ -0,0 +1,215 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +sub usage { + print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n"); + print(STDERR " {-n instance}* | {-s include}* [{-x exclude}*] \n"); + print(STDERR " [-m] [-M] [-u] [-C] [-N] [-U] [-a filename]\n"); + print(STDERR " Opts: -D rootdn - Directory Manager\n"); + print(STDERR " : -w password - Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for Directory Manager's password\n"); + print(STDERR " : -j filename - Read Directory Manager's password from file\n"); + print(STDERR " : -n instance - instance to be exported\n"); + print(STDERR " : -a filename - output ldif file\n"); + print(STDERR " : -s include - included suffix(es)\n"); + print(STDERR " : -x exclude - excluded suffix(es)\n"); + print(STDERR " : -m - minimal base64 encoding\n"); + print(STDERR " : -M - output ldif is stored in multiple files\n"); + print(STDERR " these files are named : <instance>_<filename>\n"); + print(STDERR " by default, all instances are stored in <filename>\n"); + print(STDERR " : -r - export replica\n"); + print(STDERR " : -u - do not export unique id\n"); + print(STDERR " : -C - use main db file only\n"); + print(STDERR " : -N - suppress printing sequential number\n"); + print(STDERR " : -U - output ldif is not folded\n"); + print(STDERR " : -E - Decrypt encrypted data when exporting\n"); + print(STDERR " : -1 - do not print version line\n"); + print(STDERR " : -v - verbose\n"); +} + +@instances = ( + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "" +); +@included = ( + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "" +); +@excluded = ( + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "" +); +$maxidx = 50; +$nowrap = 0; +$nobase64 = 0; +$noversion = 0; +$nouniqueid = 0; +$useid2entry = 0; +$onefile = 1; +$printkey = 1; +$taskname = ""; +$ldiffile = ""; +$doreplica = 0; +$dsroot = "{{DS-ROOT}}"; +$mydsroot = "{{MY-DS-ROOT}}"; +$verbose = 0; +$rootdn = ""; +$passwd = ""; +$passwdfile = ""; +$i = 0; +$insti = 0; +$incli = 0; +$excli = 0; +$decrypt_on_export = 0; +while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-n" ) { # instances + $i++; + if ($insti < $maxidx) { + $instances[$insti] = $ARGV[$i]; $insti++; + } else { + &usage; exit(1); + } + } elsif ("$ARGV[$i]" eq "-s") { # included suffix + $i++; + if ($incli < $maxidx) { + $included[$incli] = $ARGV[$i]; $incli++; + } else { + &usage; exit(1); + } + } elsif ("$ARGV[$i]" eq "-x") { # excluded suffix + $i++; + if ($excli < $maxidx) { + $excluded[$excli] = $ARGV[$i]; $excli++; + } else { + &usage; exit(1); + } + } elsif ("$ARGV[$i]" eq "-a") { # ldif file + $i++; $ldiffile = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-D") { # Directory Manager + $i++; $rootdn = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-w") { # Directory Manager's password + $i++; $passwd = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-j") { # Read Directory Manager's password from a file + $i++; $passwdfile = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-M") { # multiple ldif file + $onefile = 0; + } elsif ("$ARGV[$i]" eq "-o") { # one ldif file + $onefile = 1; + } elsif ("$ARGV[$i]" eq "-u") { # no dump unique id + $nouniqueid = 1; + } elsif ("$ARGV[$i]" eq "-C") { # use id2entry + $useid2entry = 1; + } elsif ("$ARGV[$i]" eq "-N") { # does not print key + $printkey = 0; + } elsif ("$ARGV[$i]" eq "-r") { # export replica + $doreplica = 1; + } elsif ("$ARGV[$i]" eq "-m") { # no base64 + $nobase64 = 1; + } elsif ("$ARGV[$i]" eq "-U") { # no wrap + $nowrap = 1; + } elsif ("$ARGV[$i]" eq "-1") { # no version line + $noversion = 1; + } elsif ("$ARGV[$i]" eq "-E") { # decrypt + $decrypt_on_export = 1; + } elsif ("$ARGV[$i]" eq "-v") { # verbose + $verbose = 1; + } else { + &usage; exit(1); + } + $i++; +} +if ($passwdfile ne ""){ +# Open file and get the password + unless (open (RPASS, $passwdfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $passwd = <RPASS>; + chomp($passwd); + close(RPASS); +} elsif ($passwd eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $passwd = ReadLine(0); +# chomp($passwd); +# ReadMode('normal'); +} +if (($instances[0] eq "" && $included[0] eq "") || $rootdn eq "" || $passwd eq "") { &usage; exit(1); } +($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time); +$mn++; $yr += 1900; +$taskname = "export_${yr}_${mn}_${dy}_${h}_${m}_${s}"; +if ($ldiffile eq "") { + $ldiffile = "${mydsroot}{{SEP}}ldif{{SEP}}${yr}_${mn}_${dy}_${h}_${m}_${s}.ldif"; +} +$dn = "dn: cn=$taskname, cn=export, cn=tasks, cn=config\n"; +$misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n"; +$cn = "cn: $taskname\n"; +$i = 0; +$nsinstance = ""; +while ("" ne "$instances[$i]") { + $nsinstance = "${nsinstance}nsInstance: $instances[$i]\n"; + $i++; +} +$i = 0; +$nsincluded = ""; +while ("" ne "$included[$i]") { + $nsincluded = "${nsincluded}nsIncludeSuffix: $included[$i]\n"; + $i++; +} +$i = 0; +$nsexcluded = ""; +while ("" ne "$excluded[$i]") { + $nsexcluded = "${nsexcluded}nsExcludeSuffix: $excluded[$i]\n"; + $i++; +} +$nsreplica = ""; +if ($doreplica != 0) { $nsreplica = "nsExportReplica: true\n"; } +$nsnobase64 = ""; +if ($nobase64 != 0) { $nsnobase64 = "nsMinimalEncoding: true\n"; } +$nsnowrap = ""; +if ($nowrap != 0) { $nsnowrap = "nsNoWrap: true\n"; } +$nsnoversion = ""; +if ($noversion != 0) { $nsnoversion = "nsNoVersionLine: true\n"; } +$nsnouniqueid = ""; +if ($nouniqueid != 0) { $nsnouniqueid = "nsDumpUniqId: false\n"; } +$nsuseid2entry = ""; +if ($useid2entry != 0) { $nsuseid2entry = "nsUseId2Entry: true\n"; } +$nsonefile = ""; +if ($onefile != 0) { $nsonefile = "nsUseOneFile: true\n"; } +if ($onefile == 0) { $nsonefile = "nsUseOneFile: false\n"; } +$nsexportdecrypt = ""; +if ($decrypt_on_export != 0) { $nsexportdecrypt = "nsExportDecrypt: true\n"; } +$nsprintkey = ""; +if ($printkey == 0) { $nsprintkey = "nsPrintKey: false\n"; } +$nsldiffile = "nsFilename: ${ldiffile}\n"; +$entry = "${dn}${misc}${cn}${nsinstance}${nsincluded}${nsexcluded}${nsreplica}${nsnobase64}${nsnowrap}${nsnoversion}${nsnouniqueid}${nsuseid2entry}${nsonefile}${nsexportdecrypt}${nsprintkey}${nsldiffile}"; +$vstr = ""; +if ($verbose != 0) { $vstr = "-v"; } +chdir("$dsroot{{SEP}}shared{{SEP}}bin"); +open(FOO, "| $dsroot{{SEP}}shared{{SEP}}bin{{SEP}}ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" ); +print(FOO "$entry"); +close(FOO); diff --git a/ldap/admin/src/scripts/template-dsml-activate.pl b/ldap/admin/src/scripts/template-dsml-activate.pl new file mode 100644 index 00000000..392e164f --- /dev/null +++ b/ldap/admin/src/scripts/template-dsml-activate.pl @@ -0,0 +1,198 @@ +#{{PERL-EXEC}} + +use Getopt::Std; +use File::Copy "cp"; +use warnings; +#use strict; + +sub usage { + print (STDERR "Arguments:\n"); + print (STDERR " -i - install\n"); + print (STDERR " -u - uninstall\n"); + print (STDERR " -p - optional dsml port, defaults to 8080\n\n"); + + exit 100; +} + +# Process the command line arguments +{ + usage() if (!getopts('uip:')); + + $DSMLPORT=8080; + $DSMLPORT=$opt_p if ($opt_p); + my $SERVERNAME = "{{SERVER-NAME}}"; + my $SERVERROOT = "{{DS-ROOT}}"; + my $PATH="{{SEP}}admin-serv{{SEP}}config{{SEP}}"; + my @FILES= ( "server.xml", "web-apps.xml", "obj.conf", "jvm12.conf" ); + + die "-i or -u required" if (!($opt_i || $opt_u)); + die "-i OR -u required" if ($opt_i && $opt_u); + + if ($opt_i) { + if (-e "$SERVERROOT{{SEP}}clients{{SEP}}dsmlgw{{SEP}}dsmlgw.cfg" ) { + print STDERR "leaving existing dsmlgw.cfg untouched.\n"; + } else { + open DSMLCFG, ">$SERVERROOT{{SEP}}clients{{SEP}}dsmlgw{{SEP}}dsmlgw.cfg"; + select DSMLCFG; + print "\#properties file for the Netscape DSMLGW\n\nServerHost=${SERVERNAME}\nServerPort={{SERVER-PORT}}\nBindDN=\nBindPW=\n\n"; + print "MinLoginPool=1\nMaxLoginPool=2\nMinPool=3\nMaxPool=15\n"; + close DSMLCFG; + } + + open WEB, ">$SERVERROOT{{SEP}}clients{{SEP}}dsmlgw{{SEP}}WEB-INF{{SEP}}web.xml"; + select WEB; + print <<EOF; +<?xml version="1.0" encoding="ISO-8859-1"?> + +<!DOCTYPE web-app SYSTEM "../web-app_2_3.dtd"> + +<web-app> + <display-name>Apache-Axis</display-name> + + <listener> + <listener-class>org.apache.axis.transport.http.AxisHTTPSessionListener</listener-class> + </listener> + + <servlet> + <servlet-name>AxisServlet</servlet-name> + <display-name>Apache-Axis Servlet</display-name> + <servlet-class> + org.apache.axis.transport.http.AxisServlet + </servlet-class> + </servlet> + + <servlet-mapping> + <servlet-name>AxisServlet</servlet-name> + <url-pattern>/services/*</url-pattern> + </servlet-mapping> + + <session-config> + <!-- Default to 5 minute session timeouts --> + <session-timeout>5</session-timeout> + </session-config> + + <mime-mapping> + <extension>wsdl</extension> + <mime-type>text/xml</mime-type> + </mime-mapping> + + + <mime-mapping> + <extension>xsd</extension> + <mime-type>text/xml</mime-type> + </mime-mapping> + + <welcome-file-list> + <welcome-file>index.html</welcome-file> + <welcome-file>index.jsp</welcome-file> + <welcome-file>index.jws</welcome-file> + </welcome-file-list> + +</web-app> + +EOF + + + close WEB; + } + + foreach $file (@FILES) { + + if ($opt_u) { + # restore from backups + print STDOUT "${file}.bak -> ${file}\n"; + rename("${SERVERROOT}${PATH}${file}.bak","${SERVERROOT}${PATH}${file}"); + } + if ($opt_i) { + # make backups + print STDOUT "${file} -> ${file}.bak\n"; + cp("${SERVERROOT}${PATH}${file}","${SERVERROOT}${PATH}${file}.bak"); + + if ( $file eq "server.xml") { + open SERVER, "${SERVERROOT}${PATH}${file}" || die("Could not open file!"); + @raw_data=<SERVER>; + close SERVER; + + $i=0; + + while ($line = $raw_data[$i++]) { + #if ($line =~ /CONNECTIONGROUP.*servername=\"([\w.?]+)\"/ ){ + #$SERVERNAME = $1; + #} + + if ($line =~ /\<\/LS/ ) { + splice @raw_data, $i++,0, + (" <LS id=\"dsml-listener\" ip=\"0.0.0.0\" port=\"${DSMLPORT}\" security=\"off\" acceptorthreads=\"1\" blocking=\"no\">\n", + " <CONNECTIONGROUP id=\"dsml_default\" matchingip=\"default\" servername=\"${SERVERNAME}\" defaultvs=\"dsml-serv\"/></LS>\n" ); + $i+=2; + } + + if ($line =~ /\<\/VSCLASS/ ) { + splice @raw_data, $i, 0, + (" <VSCLASS id=\"dsml\" objectfile=\"obj.conf\" rootobject=\"dsmlgw\" acceptlanguage=\"off\">\n" . + "<VARS nice=\"\" docroot=\"${SERVERROOT}{{SEP}}clients{{SEP}}dsmlgw\" webapps_file=\"web-apps.xml\" webapps_enable=\"on\" />\n" . + " <VS id=\"dsml-serv\" state=\"on\" connections=\"dsml_default\" urlhosts=\"${SERVERNAME}\" mime=\"mime1\" aclids=\"acl1\">\n" . + " <USERDB id=\"default\" database=\"default\"/>\n </VS>\n </VSCLASS>\n"); + $i++; + } + + + } + open SERVER, "> ${SERVERROOT}${PATH}${file}" || die("Could not open file!"); + select SERVER; + print @raw_data; + close SERVER; + } + + if ( $file eq "web-apps.xml" ) { + open WEBAPPS, "> ${SERVERROOT}${PATH}${file}"; + select WEBAPPS; + print STDERR "adding necessary entry to $file.\n"; + print <<EOF; +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE vs PUBLIC "-//Netscape Communications Corp.; Netscape//DTD Virtual Server Web Applications 6.1//EN" "http://developer.netscape.com/products/servers/enterprise/dtds/nes-webapps_6_1.dtd"> +<vs> + <web-app uri="/axis" dir="${SERVERROOT}{{SEP}}clients{{SEP}}dsmlgw" enable="true"/> +</vs> + +EOF + + close WEBAPPS; + + } + + if ( $file eq "obj.conf" ) { + open OBJ, ">> ${SERVERROOT}${PATH}${file}"; + select OBJ; + print STDERR "adding necessary entry to $file.\n"; + print <<EOF; +<Object name="dsmlgw"> +ObjectType fn=type-by-extension +ObjectType fn=force-type type=text/plain +Service fn="NSServletService" type="magnus-internal/servlet" +Service method=(GET|HEAD|POST) type=*~magnus-internal/* fn=send-file +Error fn="admin-error" reason="server error" +AddLog fn="admin40_flex_log" name="access" +NameTrans fn="NSServletNameTrans" name="servlet" +PathCheck fn=find-pathinfo +PathCheck fn=find-index index-names="index.html,home.html" +Service type="magnus-internal/jsp" fn="NSServletService" +</Object> + +EOF + + close OBJ; + + + } + + if ( $file eq "jvm12.conf" ) { + open JVM, ">> ${SERVERROOT}${PATH}${file}"; + select JVM; + print STDERR "adding necessary entry to $file.\n"; + print "jvm.option=-Duser.home=${SERVERROOT}{{SEP}}clients{{SEP}}dsmlgw"; + close JVM; + } + } + } +} diff --git a/ldap/admin/src/scripts/template-ldif2db.pl b/ldap/admin/src/scripts/template-ldif2db.pl new file mode 100644 index 00000000..c552af69 --- /dev/null +++ b/ldap/admin/src/scripts/template-ldif2db.pl @@ -0,0 +1,193 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +sub usage { + print(STDERR "Usage: $0 [-v] -D rootdn { -w password | -w - | -j filename } \n"); + print(STDERR " -n instance | {-s include}* [{-x exclude}*] [-O] [-c]\n"); + print(STDERR " [-g [string]] [-G namespace_id] {-i filename}*\n"); + print(STDERR " Opts: -D rootdn - Directory Manager\n"); + print(STDERR " : -w password - Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for Directory Manager's password\n"); + print(STDERR " : -j filename - Read Directory Manager's password from file\n"); + print(STDERR " : -n instance - instance to be imported to\n"); + print(STDERR " : -i filename - input ldif file(s)\n"); + print(STDERR " : -s include - included suffix\n"); + print(STDERR " : -x exclude - excluded suffix(es)\n"); + print(STDERR " : -O - only create core db, no attr indexes\n"); + print(STDERR " : -c size - merge chunk size\n"); + print(STDERR " : -g [string] - string is \"none\" or \"deterministic\"\n"); + print(STDERR " : none - unique id is not generated\n"); + print(STDERR " : deterministic - generate name based unique id (-G name)\n"); + print(STDERR " : by default - generate time based unique id\n"); + print(STDERR " : -G name - namespace id for name based uniqueid (-g deterministic)\n"); + print(STDERR " : -E - Encrypt data when importing\n"); + print(STDERR " : -v - verbose\n"); +} + +@ldiffiles = ( + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "" +); +@included = ( + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "" +); +@excluded = ( + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "" +); +$maxidx = 50; +$instance = ""; +$noattrindexes = 0; +$mergechunksiz = 0; +$genuniqid = "time"; +$uniqidname = ""; +$taskname = ""; +$dsroot = "{{DS-ROOT}}"; +$mydsroot = "{{MY-DS-ROOT}}"; +$verbose = 0; +$rootdn = ""; +$passwd = ""; +$passwdfile = ""; +$i = 0; +$ldifi = 0; +$incli = 0; +$excli = 0; +$encrypt_on_import = 0; +while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-i" ) { # ldiffiles + $i++; + if ($ldifi < $maxidx) { + $ldiffiles[$ldifi] = $ARGV[$i]; $ldifi++; + } else { + &usage; exit(1); + } + } elsif ("$ARGV[$i]" eq "-s") { # included suffix + $i++; + if ($incli < $maxidx) { + $included[$incli] = $ARGV[$i]; $incli++; + } else { + &usage; exit(1); + } + } elsif ("$ARGV[$i]" eq "-x") { # excluded suffix + $i++; + if ($excli < $maxidx) { + $excluded[$excli] = $ARGV[$i]; $excli++; + } else { + &usage; exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # instance + $i++; $instance = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-D") { # Directory Manager + $i++; $rootdn = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-w") { # Directory Manager's password + $i++; $passwd = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-j") { # Read Directory Manager's password from a file + $i++; $passwdfile = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-O") { # no attr indexes + $noattrindexes = 1; + } elsif ("$ARGV[$i]" eq "-c") { # merge chunk size + $i++; $mergechunksiz = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-g") { # generate uniqueid + if (("$ARGV[$i+1]" ne "") && !("$ARGV[$i+1]" =~ /^-/)) { + $i++; + if ("$ARGV[$i]" eq "none") { + $genuniqid = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "deterministic") { + $genuniqid = $ARGV[$i]; + } + } + } elsif ("$ARGV[$i]" eq "-G") { # namespace id + $i++; $uniqidname = $ARGV[$i]; + } elsif ("$ARGV[$i]" eq "-v") { # verbose + $verbose = 1; + } elsif ("$ARGV[$i]" eq "-E") { # encrypt on import + $encrypt_on_import = 1; + } else { + &usage; exit(1); + } + $i++; +} +if ($passwdfile ne ""){ +# Open file and get the password + unless (open (RPASS, $passwdfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $passwd = <RPASS>; + chomp($passwd); + close(RPASS); +} elsif ($passwd eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $passwd = ReadLine(0); +# chomp($passwd); +# ReadMode('normal'); +} +if (($instance eq "" && $included[0] eq "") || $ldiffiles[0] eq "" || $rootdn eq "" || $passwd eq "") { &usage; exit(1); } +($s, $m, $h, $dy, $mn, $yr, $wdy, $ydy, $r) = localtime(time); +$mn++; $yr += 1900; +$taskname = "import_${yr}_${mn}_${dy}_${h}_${m}_${s}"; +$dn = "dn: cn=$taskname, cn=import, cn=tasks, cn=config\n"; +$misc = "changetype: add\nobjectclass: top\nobjectclass: extensibleObject\n"; +$cn = "cn: $taskname\n"; +if ($instance ne "") { + $nsinstance = "nsInstance: ${instance}\n"; +} +$i = 0; +$nsldiffiles = ""; +while ("" ne "$ldiffiles[$i]") { + $nsldiffiles = "${nsldiffiles}nsFilename: $ldiffiles[$i]\n"; + $i++; +} +$i = 0; +$nsincluded = ""; +while ("" ne "$included[$i]") { + $nsincluded = "${nsincluded}nsIncludeSuffix: $included[$i]\n"; + $i++; +} +$i = 0; +$nsexcluded = ""; +while ("" ne "$excluded[$i]") { + $nsexcluded = "${nsexcluded}nsExcludeSuffix: $excluded[$i]\n"; + $i++; +} +$nsnoattrindexes = ""; +if ($noattrindexes != 0) { $nsnoattrindexes = "nsImportIndexAttrs: false\n"; } +$nsimportencrypt = ""; +if ($encrypt_on_import != 0) { $nsimportencrypt = "nsImportEncrypt: true\n"; } +$nsmergechunksiz = "nsImportChunkSize: ${mergechunksiz}\n"; +$nsgenuniqid = "nsUniqueIdGenerator: ${genuniqid}\n"; +$nsuniqidname = ""; +if ($uniqidname ne "") { $nsuniqidname = "nsUniqueIdGeneratorNamespace: ${uniqidname}\n"; } +$entry = "${dn}${misc}${cn}${nsinstance}${nsincluded}${nsexcluded}${nsldiffiles}${nsnoattrindexes}${nsimportencrypt}${nsmergechunksiz}${nsgenuniqid}${nsuniqidname}"; +$vstr = ""; +if ($verbose != 0) { $vstr = "-v"; } +chdir("$dsroot{{SEP}}shared{{SEP}}bin"); +open(FOO, "| $dsroot{{SEP}}shared{{SEP}}bin{{SEP}}ldapmodify $vstr -h {{SERVER-NAME}} -p {{SERVER-PORT}} -D \"$rootdn\" -w \"$passwd\" -a" ); +print(FOO "$entry"); +close(FOO); diff --git a/ldap/admin/src/scripts/template-migrate50to51 b/ldap/admin/src/scripts/template-migrate50to51 new file mode 100644 index 00000000..c9c32276 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrate50to51 @@ -0,0 +1,2778 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Migrate a 5.0 directory server to a 5.1 directory server + +####################################################################################################### +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Entry; +use Mozilla::LDAP::LDIF; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::localtime; +use File::Basename; +use Class::Struct ; + +####################################################################################################### + +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o oldInstancePath -n newInstancePath [-t tracelevel] [-L logfile]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - new 5.x Directory Manager\n"); + print(STDERR " : -w password - new 5.x Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for new 5.x Directory Manager's password\n"); + print(STDERR " : -j filename - Read new 5.x Directory Manager's password from file\n"); + print(STDERR " : -p port - new 5.x Directory Server port\n"); + print(STDERR " : -o oldInstancePath - Path of the old instance to migrate \n"); + print(STDERR " : -n newInstancePath - Path of the new 5.x instance\n"); + print(STDERR " : [-t tracelevel] - specify the level of trace (0..3)\n"); + print(STDERR " : [-L logfile] - specify the file to log the migration report \n"); + } +######################################################################################################## + +BEGIN { + + require 'uname.lib' ; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + ${SEP} = $isNT ? ";" : ":" ; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + + # If this variable is set, all file/directory creation will make sure the mode + # and ownership of the destination is the same as the source + $PRESERVE = 1 if (!$isNT); + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + if ($isNT) { + $os = "WINNT"; + } else { + $os = &uname("-s"); + } + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + } + if ( $os eq "AIX" ) { + $dll_suffix = "_shr.a"; + } + elsif ( $os eq "HP-UX" ) { + $dll_suffix = ".sl"; + } + elsif ( $os eq "WINNT" ) { + $dll_suffix = ".dll"; + } + else { + $dll_suffix = ".so"; + } + $slapdExecName = $isNT ? 'slapd.exe' : 'ns-slapd'; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; +} + +SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + + # old parameters + ${oldDir} = "" ; + ${oldname} = "" ; + ${oldHome} = "" ; + ${oldConfDir} = "" ; + ${oldlocaluser} ; + ${olduid} ; + ${oldgid} ; + + # new parameters + ${root} = "{{DS-ROOT}}" ; + ${type} = "" ; + ${newname} = "" ; + ${newport} = "" ; + ${rootDN} = "" ; + ${rootpwd} = "" ; + ${localhost} = "" ; + ${LogFileReport} = "" ; + ${newuid} ; + ${localuser} ; + ${newgid} ; + $NO_INPUT_USER = 0 ; # by default user can give inputs during the migration process + ${curdir} = getCwd(); + ${slapdExecDir} = "${root}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + # in 5.1 the replica Id is setup to a static value + $replicaIdvalue = 65535; + + # specify the level of trace + $TRACELEVEL=1; + + $LDAP_SERVER_UNREACHABLE = 81; + + # get input users + &getParameters() ; + ${oldDir} = &normalizeDir("${oldDir}"); + ${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; + ${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; + ${oldSchemaDir} = "${oldConfDir}schema${PATHSEP}"; + ${oldDSEldif} = "${oldConfDir}dse.ldif"; + ${serverHome} = "${root}${PATHSEP}$type-$newname" ; + ${schemaDir} = "$serverHome${PATHSEP}config${PATHSEP}schema${PATHSEP}"; + ${DSEldif} = "$serverHome${PATHSEP}config${PATHSEP}dse.ldif"; + ${ldif_rep} = "${oldConfDir}${PATHSEP}ldif${PATHSEP}" ; + ${oldSlapdExecDir} = "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + open(LOGFILE, ">> $LogFileReport"); + + printTrace("\noldDir: $oldDir, oldHome: $oldHome, \noldConfDir: $oldConfDir, \nldif_rep: $ldif_rep, \nrootDN: $rootDN, \nPort: $newport, \nNewname: $newname\n",3); + printTrace("\nLIB_PATH: $LIB_PATH",4); + + if (!(-d $serverHome)) { + printMsg("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + printMsg("\n$oldHome doesn't exist\n"); + exit(1); + } + + +%HashParametersName = (); + +# The following hash displays only general server parameters to migrate under cn=config +%GeneralSrvParamToMigrate = ( + 'nsslapd-accesscontrol'=> '\n', + 'nsslapd-errorlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-logging-enabled'=> '\n', + 'nsslapd-auditlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-level'=> '\n', + 'nsslapd-accesslog-logbuffering'=> '\n', + 'nsslapd-accesslog-logexpirationtime'=> '\n', + 'nsslapd-accesslog-logexpirationtimeunit'=> '\n', + 'nsslapd-accesslog-logmaxdiskspace'=> '\n', + 'nsslapd-accesslog-logminfreediskspace'=> '\n', + 'nsslapd-accesslog-logrotationtime'=> '\n', + 'nsslapd-accesslog-logrotationtimeunit'=> '\n', + 'nsslapd-accesslog-maxlogsize'=> '\n', + 'nsslapd-accesslog-maxLogsPerDir'=> '\n', + 'nsslapd-attribute-name-exceptions'=> '\n', + 'nsslapd-auditlog-logexpirationtime'=> '\n', + 'nsslapd-auditlog-logexpirationtimeunit'=> '\n', + 'nsslapd-auditlog-logmaxdiskspace'=> '\n', + 'nsslapd-auditlog-logminfreediskspace'=> '\n', + 'nsslapd-auditlog-logrotationtime'=> '\n', + 'nsslapd-auditlog-logrotationtimeunit'=> '\n', + 'nsslapd-auditlog-maxlogsize'=> '\n', + 'nsslapd-auditlog-maxLogsPerDir'=> '\n', + 'nsslapd-certmap-basedn'=> '\n', + 'nsslapd-ds4-compatible-schema'=> '\n', + 'nsslapd-enquote-sup-oc'=> '\n', + 'nsslapd-errorlog-level'=> '\n', + 'nsslapd-errorlog-logexpirationtime'=> '\n', + 'nsslapd-errorlog-logexpirationtimeunit'=> '\n', + 'nsslapd-errorlog-logmaxdiskspace'=> '\n', + 'nsslapd-errorlog-logminfreediskspace'=> '\n', + 'nsslapd-errorlog-logrotationtime'=> '\n', + 'nsslapd-errorlog-logrotationtimeunit'=> '\n', + 'nsslapd-errorlog-maxlogsize'=> '\n', + 'nsslapd-errorlog-maxlogsperdir'=> '\n', + 'nsslapd-groupevalnestlevel'=> '\n', + 'nsslapd-idletimeout'=> '\n', + 'nsslapd-ioblocktimeout'=> '\n', + 'nsslapd-lastmod'=> '\n', + 'nsslapd-listenhost'=> '\n', + 'nsslapd-maxdescriptors'=> '\n', + 'nsslapd-nagle'=> '\n', + 'nsslapd-readonly'=> '\n', + 'nsslapd-referralmode'=> '\n', + 'nsslapd-plugin-depends-on-name'=> '\n', + 'nsslapd-plugin-depends-on-type'=> '\n', + 'nsslapd-referral'=> '\n', + 'nsslapd-reservedescriptors'=> '\n', + 'nsslapd-rootpwstoragescheme'=> '\n', + 'nsslapd-schemacheck'=> '\n', + 'nsslapd-secureport'=> '\n', + 'nsslapd-security'=> '\n', + 'nsslapd-sizelimit'=> '\n', + 'nsslapd-ssl3ciphers'=> '\n', + 'nsslapd-timelimit'=> '\n', + 'passwordchange'=> '\n', + 'passwordchecksyntax'=> '\n', + 'passwordexp'=> '\n', + 'passwordhistory'=> '\n', + 'passwordinhistory'=> '\n', + 'passwordlockout'=> '\n', + 'passwordlockoutduration'=> '\n', + 'passwordmaxage'=> '\n', + 'passwordmaxfailure'=> '\n', + 'passwordminage'=> '\n', + 'passwordminlength'=> '\n', + 'passwordmustchange'=> '\n', + 'passwordresetfailurecount' => '\n', + 'passwordstoragescheme' => '\n', + 'passwordunlock' => '\n', + 'passwordwarning' => '\n' +); + +# the following hash displays global parameters related to database stored under cn=config,cn=ldbm database,cn=plugins,cn=config +%GlobalConfigLDBMparamToMigrate = ( + 'nsslapd-allidsthreshold' => '\n', + 'nsslapd-lookthroughlimit' => '\n', + 'nsslapd-mode' => '\n', + 'nsslapd-dbcachesize' => '\n', + 'nsslapd-cache-autosize' => '\n', + 'nsslapd-cache-autosize-split' => '\n', + 'nsslapd-db-transaction-logging' => '\n', + 'nsslapd-import-cachesize' => '\n' +); + +# the following hash displays specific parameters to each backends and stored under cn=DBname,cn=ldbm database,cn=plugins,cn=config +%LDBMparamToMigrate = ( + 'nsslapd-cachesize' => '\n', + 'nsslapd-cachememsize' => '\n', + 'nsslapd-readonly' => '\n', + 'nsslapd-require-index' => '\n' +); + + +%ChainingConfigParams = ( + 'nsactivechainingcomponents' => '\n', + 'nstransmittedcontrols' => '\n' + ); + +%ChainingDefaultInstanceConfigParams = ( + 'nsabandonedsearchcheckinterval' => '\n', + 'nsbindconnectionslimit' => '\n', + 'nsbindtimeout' => '\n', + 'nsbindretrylimit' => '\n', + 'nshoplimit' => '\n', + 'nsmaxresponsedelay' => '\n', + 'nsmaxtestresponsedelay' => '\n', + 'nschecklocalaci' => '\n', + 'nsconcurrentbindlimit' => '\n', + 'nsconcurrentoperationslimit' => '\n', + 'nsconnectionlife' => '\n', + 'nsoperationconnectionslimit' => '\n', + 'nsproxiedauthorization' => '\n', + 'nsreferralonscopedsearch' => '\n', + 'nsslapd-sizelimit' => '\n', + 'nsslapd-timelimit' => '\n' +); + +%changelog5params = ( + 'nsslapd-changelogmaxage' => '\n', + 'nsslapd-changelogmaxentries' => '\n' + ); + +@SNMPparams = ( + 'nssnmpenabled', + 'nssnmporganization', + 'nssnmplocation', + 'nssnmpcontact', + 'nssnmpdescription', + 'nssnmpmasterhost', + 'nssnmpmasterport', + 'nssnmpenabled', + 'aci' + ); + +%stdIncludes = ( + "." => "\n", + ".." => "\n", + "30ns-common.ldif " => "\n", + "50ns-mail.ldif " => "\n", + "50ns-news.ldif" => "\n", + "50iplanet-servicemgt.ldif"=> "\n", + "50ns-mcd-browser.ldif" => "\n", + "50ns-proxy.ldif" => "\n", + "00core.ldif" => "\n", + "50ns-admin.ldif" => "\n", + "50ns-mcd-config.ldif " => "\n", + "50ns-value.ldif" => "\n", + "05rfc2247.ldif" => "\n", + "50ns-calendar.ldif" => "\n", + "50ns-mcd-li.ldif" => "\n", + "50ns-wcal.ldif" => "\n", + "05rfc2927.ldif" => "\n", + "50ns-certificate.ldif" => "\n", + "50ns-mcd-mail.ldif" => "\n", + "50ns-web.ldif" => "\n", + "10rfc2307.ldif" => "\n", + "50ns-compass.ldif" => "\n", + "50ns-media.ldif" => "\n", + "20subscriber.ldif" => "\n", + "50ns-delegated-admin.ldif"=> "\n", + "50ns-mlm.ldif" => "\n", + "25java-object.ldif" => "\n", + "50ns-directory.ldif" => "\n", + "50ns-msg.ldif" => "\n", + "28pilot.ldif" => "\n", + "50ns-legacy.ldif" => "\n", + "50ns-netshare.ldif" => "\n" +); + + +# Backends migrated (Backend CN attribute value) +@BACKENDS = () ; +# All pairs of suffix-backend are registered in this hashtable +%oldBackends = () ; + +#store the backend instances to migrate +@LDBM_backend_instances = (); + +#store the mapping tree +@Mapping_tree_entries = (); + +#store the suffix and the associated chaining backend +%oldChainingBackends = (); + +#store the multiplexor bind entries to migrate +%MultiplexorBindDNEntriesToMigrate = (); + +#store the Replica bind DN entries to migrate +%ReplicaBindDNEntriesToMigrate = (); + +# list of standard plugin's in version 4 +%stdPlugins = ( + "7-bit check" => "\n", + "acl plugin" => "\n", + "acl preoperation" => "\n", + "binary syntax" => "\n", + "case exact string syntax" => "\n", + "case ignore string syntax" => "\n", + "chaining database" => "\n", + "class of service" => "\n", + "country string syntax" => "\n", + "distinguished name syntax" => "\n", + "generalized time syntax" => "\n", + "integer syntax" => "\n", + "internationalization plugin" => "\n", + "ldbm database" => "\n", + "legacy replication plugin" => "\n", + "multimaster replication plugin" => "\n", + "octet string syntax" => "\n", + "clear" => "\n", + "crypt" => "\n", + "ns-mta-md5" => "\n", + "sha" => "\n", + "ssha" => "\n", + "postal address syntax" => "\n", + "referential integrity postoperation" => "\n", + "retro changelog plugin" => "\n", + "roles plugin" => "\n", + "telephone syntax" => "\n", + "uid uniqueness" => "\n", + "uri syntax" => "\n" + ); + +# list of indexes that have disappeared from the 5.1 schema compared to 5.0 +%deniedIndexes = ( + 'dncomp' => "\n" +); + +@default_indexes = (); +@indexes = (); + +# list of user added Plugin's. In 5.x, they 'll need to be recompiled +@badPlugins = () ; + +@pluginAttrs = ( + "objectclass", + "cn", + "nsslapd-pluginpath", + "nsslapd-plugininitfunc", + "nsslapd-plugintype", + "nsslapd-pluginenabled", + "nsslapd-plugin-depends-on-type", + "nsslapd-pluginid", + "nsslapd-pluginversion", + "nsslapd-pluginvendor" + ); + +@nsds5replicaAttrs = ( + 'objectclass', + 'nsDS5ReplicaRoot', + 'nsDS5ReplicaType', + 'nsDS5ReplicaLegacyConsumer', + 'nsDS5flags', + 'nsDS5ReplicaId', + 'nsDS5ReplicaPurgeDelay', + 'nsDS5ReplicaBinddn', + 'cn', + 'nsDS5ReplicaReferral' + ); + +# array of replicas to migrate +@new51replicas = (); + +# array of replication agreements to migrate +@replicationAgreements = (); + +# Shutdown the legacy Directory instance +printTrace("\nShutdown the legacy Directory Server instance: ${oldHome}",0); +&stopServer($oldDir, 'slapd-'.$oldname); + +# compare LDIF standard config files with standard ones +CompareStdConfigFiles() ; +die "\n\n The version of product you want to migrate is not a 5.x iPlanet Directory Server\n" unless ($oldVersion == 5) ; + +# get the hostname of the new LDAP server +my $LDAPservername = &getLDAPservername(); + +# get the uid and gid of the 5.1 slapd user +($localuser, $newuid, $newgid) = getuid_gid(); +# get the uid and gid of the 5.0 slapd user +($oldlocaluser, $olduid, $oldgid) = getolduid_gid(); +printTrace("\n5.1 localuser: $localuser, uid: $newuid, gid: $newgid",2); +printTrace("\n5.0 localuser: $oldlocaluser, uid: $olduid, gid: $oldgid",2); + +# backup 5.1 configuration files in <root_server51>/slapd-instancename/config +printTrace("\nBackup $serverHome${PATHSEP}config on $serverHome${PATHSEP}config_backup ...",0); +&backupConfigFiles(); + +# migrate the schema (need to stop and start the 5.1 server) +printTrace("\nMigrate the schema...",0); +MigrateSchema(); + +# start the server unless it is already started +&startServer() unless (isDirectoryAlive()); + +############### Connect to the 5.1 LDAP Directory Server ###################### +$ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"} ; + +die "\n Migration aborted. Check your 5.0 and 5.1 iPlanet Directory Server are installed on the same machine \n" if ( $LDAPservername == -1 ); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + +# Cconnection to 5.1 LDAP server is successful ! +printTrace("\nConnected to $Version.$Minor LDAP server",0) ; + +# Parse the main configuration file: dse.ldif +printTrace("\n\nParse the old DSE ldif file: $oldDSEldif *****",0, 1); +printTrace("\nThis may take a while ...\n",0); +&MigrateDSEldif(); + +#migrate LDBM backend instances +printTrace("\n\nMigrate LDBM backend instances...",0,1); +&migrateLDBM_backend_instances(); + +#migrate mapping tree entries +printTrace("\n\nMigrate mapping tree...",0,1); +&migrateMappingTree(); + +#migrate default indexes +printTrace("\n\nMigrate default indexes...",0,1); +migrateDefaultIndexes(); + +#migrate indexes +printTrace("\n\nMigrate indexes...",0,1); +migrateIndexes(); + +#migrate replicas +printTrace("\n\nMigrate replicas...",0,1); +&MigrateNSDS5_replica(); + +#migrate replication agreements +printTrace("\n\nMigrate replication agreements...",0,1); +&MigrateNSDS_replication_agreement(); + +#migrate key/cert databases +printTrace("\n\nMigrate key/cert databases...",0,1); +&MigrateSSL(); + +# migrate certmap.conf +printTrace("\n\nMigrate Certmap.conf...",0,1); +&MigrateCertmap() ; + +################## Close the connection to 5.1 LDAP Server ##################### +printTrace("\n\n***** Close the LDAP connection to the 5.1 Directory Server instance ***** ",0); +$conn->close; + + +################## stop the 5.x instance and Export/Import the data, restart the server ################## +if (@BACKENDS) { + &stopServer($root,'slapd-'.$newname); + printTrace("\nData processing...\n",0,1) ; + # migrate data for each backend: 5.0 -> LDIF files + &manydb2Ldif($ldif_rep); + + # migrate LDIF data to the 5.1 database: LDIF -> 5.1 + &manyLdif2db($ldif_rep); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); + &startServer() unless (isDirectoryAlive()); +} +else { + printTrace("\nINFORMATION - There are no 5.0 non-standard or non-already existing suffixes to migrate\n",0); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); +} + +printMsg("\n\n ****** End of migration ******\n\n"); + +close(LOGFILE); + + +########################################################################################### +# get input users +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # 4.x instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # 5.x instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe 5.x instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # 5.x DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + $NO_INPUT_USER = 1 ; + } elsif ("$ARGV[$i]" eq "-L") { # migration logfile + $LogFileReport = $ARGV[++$i] ; + } + else { + print("\nThe option $ARGV[$i] is not recognized"); + &usage() ; + exit(1); + } + $i++; + } + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + if ($exit) { + &usage() ; + exit(1); + } + +} + +################################################################################################### + +sub MigrateSchema{ + my $FilesChanged = ""; + my $AllDiffs = ""; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(SCHEMADIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldSchemaDir: $!"; + + foreach $file (readdir(SCHEMADIR)) { + if (! exists($stdIncludes{lc($file)})) { + my $new51file = $schemaDir . $file; + if (-f $new51file ) { + # The ldif file already exists. Make a diff and warn the user if different. + if (diff($new51file, $oldSchemaDir.$file)) { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $new51file); + } + } + else { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $new51file); + } + } + } + closedir(SCHEMADIR); + if ($AllDiffs) { + printMsg("\n\n***********************************************************************"); + printMsg("\nThe following LDIF files have been migrated:"); + printMsg("$AllDiffs"); + printMsg("\n*************************************************************************\n\n"); + } + &startServer() if (! isDirectoryAlive()); +} + + +################################################################################################### +# This subroutine is used to parse the dse.ldif file and call specific routines to act with entries +sub MigrateDSEldif { + printTrace("\nMigrate DSE entries...",1); + my $tempoAlreadyDone = 0; + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + SWITCH: { + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + parseLDBM_backend_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "MAPPING_TREE"){ + parseMapping_tree($entry); + last SWITCH; + } + if ($typeOfEntry eq "DEFAULT_INDEX"){ + parseDefaultIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "INDEX"){ + parseIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "STANDARD_PLUGIN"){ + migrateStdPlugin($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_NODE"){ + migrateConfig_Node($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_LDBM_DATABASE"){ + migrateConfig_LDBM_database($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_CONFIG"){ + migrateChainingBE_config($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_INSTANCE"){ + migrateChainingBE_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS5_REPLICA"){ + parseNSDS5_replica($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS_REPLICATION_AGREEMENT"){ + parseNSDS_replication_agreement($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHANGELOG5"){ + migrateChangelog5($entry); + last SWITCH; + } + if ($typeOfEntry eq "REPLICATION"){ + migrateReplication($entry); + last SWITCH; + } + if ($typeOfEntry eq "SECURITY"){ + migrateSecurity($entry); + last SWITCH; + } + if ($typeOfEntry eq "SNMP"){ + migrateSNMP($entry); + last SWITCH; + } + } + + } + close(DSELDIF); +} + +############################################################################# +# returns the "type of an entry". If the entry is not to be migrated its type is "NOT_MIGRATED_TYPE" + +sub getTypeOfEntry{ + my $entry = shift; + my $DN = $entry->getDN(1) ; # 1 is to normalize the returned DN + if (($DN =~ /cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "LDBM_BACKEND_INSTANCE"; + } + if (($DN =~ /cn=mapping tree,cn=config$/i) && (isObjectclass($entry,"nsMappingTree"))) { + return "MAPPING_TREE"; + } + if (($DN =~ /cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsIndex"))) { + return "DEFAULT_INDEX"; + } + if (isObjectclass($entry,"nsIndex")) { + return "INDEX"; + } + if ((isObjectclass($entry,"nsSlapdPlugin")) && (isStdPlugin($entry))) { + return "STANDARD_PLUGIN"; + } + if ($DN =~ /^cn=config$/i) { + return "CONFIG_NODE"; + } + if ($DN =~ /^cn=config,cn=ldbm database,cn=plugins,cn=config$/i) { + return "CONFIG_LDBM_DATABASE"; + } + if (($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i) || ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i)){ + return "CHAINING_BACKEND_CONFIG"; + } + if (($DN =~ /cn=chaining database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "CHAINING_BACKEND_INSTANCE"; + } + if (isObjectclass($entry,"nsDS5Replica")) { + return "NSDS5_REPLICA"; + } + if (isObjectclass($entry,"nsDS5ReplicationAgreement")) { + return "NSDS_REPLICATION_AGREEMENT"; + } + if ($DN =~ /^cn=changelog5,cn=config$/i) { + return "CHANGELOG5"; + } + if (($DN =~ /cn=replication,cn=config$/i) && ($DN !~ /^cn=replication,cn=config$/i)) { + return "REPLICATION"; + } + if ($DN =~ /cn=encryption,cn=config$/i) { + return "SECURITY"; + } + if ($DN =~ /^cn=SNMP,cn=config$/i) { + return "SNMP"; + } + return "NOT_MIGRATED_TYPE"; +} + +############################################################################# + + + +############################################################################# +# returns 1 if the objectclass given in parameter is present in the objectclasses values of the entry +# given in parameter, 0 else + +sub isObjectclass { + my $entry = shift; + my $objectclass = shift; + return ($entry->hasValue("objectclass",$objectclass,1)); +} + +############################################################################# + +sub isStdPlugin { + my $entry = shift; + my $CN = $entry->{cn}[0]; + if (isObjectclass($entry,"nsSlapdPlugin")) { + return 1 if ($stdPlugins{lc($CN)}); + } + return 0; +} + + +############################################################################# + +sub alreadyExistsIn51{ + my $entry = shift; + my $mustExist = shift; + my $DN = $entry->getDN(1); # 1 to normalize the DN + return searchEntry($DN, $mustExist); +} + +############################################################################# +sub searchEntry { + my $DN = shift; + my $mustExist = shift; + my $res = $conn->search($DN, "base", "objectclass=*"); + my $cpt = 5; + if ($res) { + return $res; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to search $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->search($DN, "base", "objectclass=*"); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + return $res ; + } + elsif (($errorCode eq $LDAP_SERVER_UNREACHABLE) || ($mustExist)) { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** Failed to search: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + } + return 0; + } +} + + +############################################################################# + +sub addEntryTo51{ + my $entry = shift; + my $typeOfEntry = shift; + my $trace = shift; + my $res = $conn->add($entry); + my $DN = $entry->getDN(1); + my $cpt = 5; + if ($res) { + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to add $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->add($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry: Add Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + +############################################################################# + +sub updateEntry{ + my $entry = shift; + my $typeOfEntry = shift; + my $CHECK = shift; + my $trace = shift; + my $cpt = 5; + if ($CHECK) { + if (! hasChanged($entry, $typeOfEntry)) { + return 1; + } + } + my $res = $conn->update($entry); + my $DN = $entry->getDN(1); + if ($res) { + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1 ; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to update $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->update($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry - Update Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + + +############################################################################# +# returns 1 if the entry to migrate and the current entry are different one another + +sub hasChanged { + my $entry = shift; + my $typeOfEntry = shift; + my $DN = $entry->getDN(1); + my $new51entry = searchEntry($DN,1); + return 1 if (! $new51entry); # we shoudn't be in that case ... + # do the stuff to check wether the entry has changed or not given its type + if (($typeOfEntry eq "DEFAULT_INDEX") || ($typeOfEntry eq "INDEX")){ + my @indexTypes = $entry->getValues("nsIndexType"); + my @new51indexTypes = $new51entry->getValues("nsIndexType"); + my @nsmatchingrules = $entry->getValues("nsmatchingrule"); + my @new51nsmatchingrules = $new51entry->getValues("nsmatchingrule"); + return 1 if (Diffs(\@indexTypes, \@new51indexTypes)); + return 1 if (Diffs(\@nsmatchingrules,\@new51nsmatchingrules)); + return 0; + } + if ($typeOfEntry eq "CHANGELOG5"){ + printTrace("\nCheck wether changelog has changed or not",3); + my @params = keys(%changelog5params); + foreach $param (@params){ + my @values = $entry->getValues($param); + my @new51values = $new51entry->getValues($param); + return 1 if (Diffs(\@values,\@new51values)); + } + return 0; + } + if ($typeOfEntry eq "SNMP"){ + foreach $param (@SNMPparams){ + my @values = $entry->getValues($param); + my @new51values = $new51entry->getValues($param); + return 1 if (Diffs(\@values,\@new51values)); + } + return 0; + } + # we don't know how to compare such type of entry => just return 1 + return 1 ; +} + +sub isAsystemIndex { + my $index = shift; + return ($index->hasValue("nsSystemIndex","true",1)); +} + + +sub updatePathInPluginArgs { + my $plugin = shift; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $cont = 1; + my $Unix_oldDir = ${oldDir} ; + my $Unix_root = ${root} ; + grep { s@\\@/@g } $Unix_oldDir if $isNT; + grep { s@\\@/@g } $Unix_root if $isNT; + while ($cont) { + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + $_ = $plugin->{$arg}[0] ; + s@$Unix_oldDir@$Unix_root@ig ; + s/$type-$oldname/$type-$newname/ig ; + $plugin->setValues($arg, $_) ; + } + else { + $cont = 0 ; + } + $argNum++; + } + return $plugin; +} + + +sub Diffs { + my $valuesToMigrate = shift; + my $currentValues = shift; + return 1 if (getDiff(\@{$valuesToMigrate},\@{$currentValues})); + return 1 if (getDiff(\@{$currentValues},\@{$valuesToMigrate})); + return 0 ; +} + +sub getDiff { + # we get references to arrays + my $elements = shift ; + my $existing_elements = shift ; + my %count = () ; + my %countEE = () ; + @diff = () ; + foreach $e (@{$elements}, @{$existing_elements}) { $count{$e}++ ;} + foreach $e (@{existing_elements}) { $countEE{$e}++ ;} + foreach $e (@{$elements}) { + # if $e is only present in @$elements, we push it to the diff array + if (($count{$e} == 1) && ($countEE{$e} == 0)) { + push @diff, $e ; + } + } + return @diff ; +} + +sub registerSuffix_Backend { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldBackends{$suffix} = $CN; +} + + +############################################################################# +# # +# # +# # +############################################################################# +sub migrateLDBM_backend_instances { + foreach $entry (@LDBM_backend_instances) { + my $DN = $entry->getDN(1); # 1 is to normalize the DN + my $CN = $entry->{cn}[0]; + if ($DN =~/cn=netscaperoot,cn=ldbm database/i){ + printTrace("\n\n*** INFORMATION - NetscapeRoot is NOT migrated",0); + } + else { + if(alreadyExistsIn51($entry)){ + printMsg("\n\n*** LDBM_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + printTrace("\nWe should add the backend instance $DN",3); + my $suffixarg = "nsslapd-suffix" ; + my $suffixname= $entry->{$suffixarg}[0] ; + my $new51entry = $conn->newEntry() ; + $new51entry->setDN($DN); + $new51entry->setValues("objectclass", "top", "extensibleObject", "nsBackendInstance" ); + $new51entry->setValues("cn", $CN ); + $new51entry->setValues($suffixarg, $suffixname); + my @params = keys(%LDBMparamToMigrate); + foreach $param (@params) { + my @values = $entry->getValues($param); + $new51entry->setValues($param, @values) if (@values); + } + if (addEntryTo51($new51entry, "LDBM_BACKEND_INSTANCE",1)) { + push @BACKENDS, $CN; + } + } + } + } +} + +sub parseLDBM_backend_instance { + my $entry = shift; + ®isterSuffix_Backend($entry); + push @LDBM_backend_instances, $entry; +} + +############################################################################# +sub migrateMappingTree { + foreach $entry (@Mapping_tree_entries) { + my $DN = $entry->getDN(1); # 1 si to normalize the DN + if ($DN =~/cn=\"o=netscaperoot\",cn=mapping tree,cn=config/i){ + # DO NOTHING + } + else { + if(alreadyExistsIn51($entry)){ + printMsg("\n\n*** MAPPING_TREE - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + addEntryTo51($entry, "MAPPING_TREE",1); + } + } + } +} + + +sub parseMapping_tree{ + my $entry = shift; + push @Mapping_tree_entries, $entry; +} + +############################################################################# +sub migrateDefaultIndexes { + foreach $index (@default_indexes) { + my $CN = $index->{cn}[0]; + my $new51index ; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)})) { + if ($new51index = alreadyExistsIn51($index)) { + if (! isAsystemIndex($new51index)) { + updateEntry($index, "DEFAULT_INDEX", 1, 2); + } + } + else { + addEntryTo51($index, "DEFAULT_INDEX", 2); + } + } + } +} + + +sub parseDefaultIndex{ + my $index = shift; + push @default_indexes, $index; +} + +############################################################################# + +sub migrateIndexes { + foreach $index (@indexes) { + my $CN = $index->{cn}[0]; + my $new51index; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)}) && (DN !~ /cn=netscaperoot,cn=index/i)){ + if ($new51index = alreadyExistsIn51($index)) { + if (! isAsystemIndex($new51index)) { + updateEntry($index, "INDEX", 1, 2); + } + } + else { + addEntryTo51($index, "INDEX", 2); + } + } + } +} + +sub parseIndex{ + my $index = shift; + push @indexes, $index; +} + +############################################################################# + +sub newLDIFplugin { + my $current51plugin = shift; + my $DN = $current51plugin->getDN(1); + my $new51plugin = $conn->newEntry() ; + $new51plugin->setDN($DN); + foreach $Attr (@pluginAttrs) { + my @values = $current51plugin->getValues($Attr); + $new51plugin->setValues($Attr, @values) if (@values); + } + return $new51plugin; +} + +sub migrateStdPlugin{ + my $plugin = shift; + my $DN = $plugin->getDN(1); + my $pluginEnable = "nsslapd-pluginEnabled"; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $current51plugin ; + if ($current51plugin = alreadyExistsIn51($plugin, 1)) { + $plugin = updatePathInPluginArgs($plugin); + my $pluginEnableValue = $plugin->{$pluginEnable}[0]; + my $cont = 1; + my $pluginHasChanged = 0; + my $new51plugin = &newLDIFplugin($current51plugin); + if (! $current51plugin->hasValue($pluginEnable,$pluginEnableValue,1)){ + $new51plugin->setValues($pluginEnable, $pluginEnableValue); + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + while($cont){ + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + my @values = $plugin->getValues($arg); + my $value = $values[0] ; + $new51plugin->setValues($arg, $value) if (@values); + if ($current51plugin->exists($arg)) { + if (! $current51plugin->hasValue($arg,$value,1)) { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + if ($current51plugin->exists($arg)) { + # Just Warn the user. Do nothing. + printTrace("\nCompared to 5.0, the current 5.1 plugin $DN belongs this attribute: $arg",2); + } + else { + $cont = 0 ; + } + } + $argNum++; + } + updateEntry($new51plugin, "STANDARD_PLUGIN", 0, 1) if ($pluginHasChanged); + } +} + +############################################################################# + +sub migrateConfig_Node{ + my $config_node = shift; + my @params = keys(%GeneralSrvParamToMigrate); + my $hasChanged = 0; + my $new51config_node; + if ($new51config_node = alreadyExistsIn51($config_node, 1)){ + foreach $param (@params) { + if ($config_node->exists($param)){ + my @valuesToMigrate = $config_node->getValues($param); + if (@valuesToMigrate){ + if ($new51config_node->exists($param)){ + my @currentValues = $new51config_node->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $new51config_node->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + else { + $new51config_node->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + } + } + updateEntry($new51config_node, "CONFIG_NODE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateConfig_LDBM_database{ + my $config_ldbm = shift; + my @params = keys(%GlobalConfigLDBMparamToMigrate); + my $hasChanged = 0; + my $new51config_ldbm ; + if ($new51config_ldbm = alreadyExistsIn51($config_ldbm, 1)) { + foreach $param (@params) { + if ($config_ldbm->exists($param)){ + my @valuesToMigrate = $config_ldbm->getValues($param); + if (@valuesToMigrate){ + if ($new51config_ldbm->exists($param)){ + my @currentValues = $new51config_ldbm->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $new51config_ldbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $new51config_ldbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($new51config_ldbm, "CONFIG_LDBM_DATABASE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateChainingBE_config{ + my $chaining_config = shift; + my $DN = $chaining_config->getDN(1); + my @params = (); + my $hasChanged = 0; + my $new51chaining_config; + if ($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i){ + $new51chaining_config = searchEntry("cn=config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingConfigParams); + } + if ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i){ + $new51chaining_config = searchEntry("cn=default instance config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingDefaultInstanceConfigParams); + } + foreach $param (@params) { + if ($chaining_config->exists($param)){ + my @valuesToMigrate = $chaining_config->getValues($param); + if (@valuesToMigrate){ + printTrace("\nParam: $param values To migrate: @valuesToMigrate",3); + if ($new51chaining_config->exists($param)){ + my @currentValues = $new51chaining_config->getValues($param); + printTrace("\nParam: $param 51 current values: @currentValues",3); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $new51chaining_config->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $new51chaining_config->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($new51chaining_config, "CHAINING_BACKEND_CONFIG", 0, 1) if ($hasChanged); +} + +############################################################################# + +sub registerSuffix_ChainingBE { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldChainingBackends{$suffix} = $CN; +} + +sub storeMultiplexorBindDN { + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + if ($chaining_instance->exists("nsMultiplexorBindDN")){ + my $bindDN = $chaining_instance->{nsMultiplexorBindDN}[0]; + my $new51bindDN = searchEntry($bindDN); + if (! $new51bindDN){ + # the bindDN entry doesn't yet exist in 5.1 => it will have to be migrated + $MultiplexorBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in 5.1 + } + } + +} + +sub importMultiplexorBindDNEntries { + # import all entries present in @MultiplexorBindDNEntriesToMigrate in 5.1 + my @MultiplexorBindDNs = keys (%MultiplexorBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + foreach $bindDN (@MultiplexorBindDNs) { + printTrace("\nimportMultiplexorBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # check wether the backend has been imported in 5.1 or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the binf DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to 5.1 + $conn->close if ($conn); +} + +sub migrateChainingBE_instance{ + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + ®isterSuffix_ChainingBE($chaining_instance); + if (alreadyExistsIn51($chaining_instance)) { + # already exists + printMsg("\n\n*** CHAINING_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($chaining_instance, "nsmultiplexorcredentials"); + addEntryTo51($chaining_instance, "CHAINING_BACKEND_INSTANCE", 1); + storeMultiplexorBindDN($chaining_instance); + } +} + +############################################################################# + +# create a new LDIF representation of a 5.1 replica consumer +sub newLDIFreplica { + my $replica = shift; + my $DN = $replica->getDN(1); + my $new51replica = $conn->newEntry() ; + my $MASTER_OR_MULTIMASTER = "3" ; + $new51replica->setDN($DN); + foreach $Attr (@nsds5replicaAttrs) { + my @values = $replica->getValues($Attr); + $new51replica->setValues($Attr, @values) if (@values); + } + my $replicaType = $replica->{nsDS5ReplicaType}[0]; + if ($replicaType eq $MASTER_OR_MULTIMASTER) { + my @nsState = $replica->getValues("nsState"); + $new51replica->setValues("nsState", @nsState); + } + else { + $new51replica->setValues("nsDS5ReplicaId", $replicaIdvalue); + } + return $new51replica; +} + +sub MigrateNSDS5_replica{ + foreach $replica (@new51replicas) { + my $DN = $replica->getDN(1); + my $new51replica; + if (alreadyExistsIn51($replica)) { + # replica already exists + printMsg("\n\n*** NSDS5_REPLICA - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + $new51replica = &newLDIFreplica($replica); + addEntryTo51($new51replica, "NSDS5_REPLICA", 1); + storeReplicaBindDN($replica); + } + } +} + +sub parseNSDS5_replica{ + my $replica = shift; + push @new51replicas, $replica; +} + +sub storeReplicaBindDN { + my $replica = shift; + my $DN = $replica->getDN(1); + if ($replica->exists("nsDS5ReplicaBindDN")){ + my $bindDN = $replica->{nsDS5ReplicaBindDN}[0]; + my $new51bindDN = searchEntry($bindDN); + if (! $new51bindDN){ + # the bindDN entry doesn't yet exist in 5.1 => it will have to be migrated + $ReplicaBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in 5.1 + } + } +} + + +sub importReplicaBindDNEntries { + # import all entries present in @ReplicaBindDNEntriesToMigrate in 5.1 + my @ReplicaBindDNs = keys (%ReplicaBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + foreach $bindDN (@ReplicaBindDNs) { + printTrace("\nimportReplicaBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # check wether the backend has been imported in 5.1 or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the binf DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to 5.1 + $conn->close if ($conn); +} + +sub alreadyMigrated { + my $backendToCheck = shift; + foreach $backend (@BACKENDS) { + return 1 if ($backend eq $backendToCheck); + } + return 0 ; +} + +sub belongsSuffix { + my $suffix = shift; + my $bindDN = shift; + return ($bindDN =~ /$suffix\s*$/i); +} + +sub length { + my $suffix = shift; + my $count = 0; + while ($suffix =~ /./g) { + $count++; + } + return $count ; +} + +sub getBackendtoExportFrom { + my $bindDN = shift ; + my $sizeOfSuffix = 0 ; + my $NULL = ""; + my @oldSuffixes = keys(%oldBackends); + my @oldChainingSuffixes = keys(%oldChainingBackends); + my $bindDN_backend = $NULL; + foreach $suffix (@oldSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is: $suffix",3); + if ((belongsSuffix($suffix,$bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $oldBackends{$suffix}; + printTrace("\ngetBackendtoExportFrom: bindDN_backend: $bindDN_backend, sizeOfSuffix: $sizeOfSuffix",3); + } + } + foreach $suffix (@oldChainingSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is a chained suffix: $suffix",3); + if ((belongsSuffix($suffix,$bindDN)) && (length($suffix) > $sizeOfSuffix)) { + printMsg("\n\n*** Entry stored on a remote backend - $bindDN"); + printMsg("\n*** We don't migrate it"); + return $NULL; + } + } + return $bindDN_backend ; +} + + +sub getBackendtoImportTo { + my $bindDN = shift; + my $sizeOfSuffix = 0; + my $NULL = ""; + my $suffixArg = "nsslapd-suffix"; + my $bindDN_backend = $NULL; + open( DSELDIF, "< $DSEldif" ) || die "Can't open $DSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + my $suffix = $entry->{$suffixArg}[0]; + if ((belongsSuffix($suffix,$bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $entry->{cn}[0]; + } + } + } + close(DSELDIF); + return $bindDN_backend ; +} + + +sub ExportAndAddEntry { + my $DN = shift; + my $backendtoExportFrom = shift; + my $ldif_dir = shift; + my $ldif = "$ldif_dir${PATHSEP}$backendtoExportFrom.ldif" ; + # first: export entry pointed out by the $DN to $ldif file + $ENV{"$LIB_PATH"}="$oldDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "\ncan't create $ldif_dir to store temporary ldif files\n"; + } + chdir($oldSlapdExecDir) or die "\nCould not change directory to $oldSlapdExecDir: $!\n"; + &db2Ldif($ldif, $backendtoExportFrom, $DN); + chdir($curdir) or die "\nCould not change directory to $curdir: $!\n"; + + # then: Add it to 5.1 + if (! $conn) { + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + } + open( BINDDNLDIF, "< $ldif" ) || die "\nCan't open $ldif: $!: \n"; + my $in = new Mozilla::LDAP::LDIF(*BINDDNLDIF) ; + while ($entry = readOneEntry $in) { + my $entryDN = $entry->getDN(1); + if ($DN eq $entryDN) { + addEntryTo51($entry, "nsds5ReplicaBindDN", 0); + } + } + close(BINDDNLDIF); + # remove the ldif file after the import + unlink($ldif) ; +} + +############################################################################# +sub MigrateNSDS_replication_agreement { + foreach $replicationAgreement (@replicationAgreements) { + my $DN = $replicationAgreement->getDN(1); + if (alreadyExistsIn51($replicationAgreement)){ + # replication agreement already exists + printMsg("\n\n*** NSDS_REPLICATION_AGREEMENT - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($replicationAgreement, "nsDS5ReplicaCredentials"); + addEntryTo51($replicationAgreement, "NSDS_REPLICATION_AGREEMENT", 1); + } + } +} + + +sub parseNSDS_replication_agreement{ + my $replicationAgreement = shift; + push @replicationAgreements, $replicationAgreement ; +} + +############################################################################# + +sub migrateChangelog5{ + my $changelog = shift; + my $DN = $changelog->getDN(1); + my $changelogdir = "nsslapd-changelogdir"; + if (alreadyExistsIn51($changelog)){ + # cn=changelog5,cn=config already exists in 5.1 + my $new51changelog = searchEntry($DN); + my @new51changelodir = $new51changelog->getValues($changelogdir); + $changelog->setValues($changelogdir, @new51changelogdir); + updateEntry($changelog, "CHANGELOG5", 0, 1); + } + else { + # cn=changelog5,cn=config need to be created in 5.1. + # the changelogdir value must be setup to <root_server51>/slapd-instance/changelogdb + $changelog->setValues($changelogdir,"${serverHome}${PATHSEP}changelogdb"); + addEntryTo51($changelog, "CHANGELOG5", 1); + } +} + +############################################################################# + +sub migrateReplication{ + my $replication = shift; + my $DN = $replication->getDN(1); + if (alreadyExistsIn51($replication)){ + # replication agreement already exists + printMsg("\n\n*** $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + addEntryTo51($replication, "REPLICATION", 1); + } +} + +############################################################################# + +sub migrateSecurity{ + my $security = shift; + if (alreadyExistsIn51($security)){ + # already exists in 5.1 + updateEntry($security, "SECURITY", 0, 1); + } + else { + addEntryTo51($security, "SECURITY", 1); + } +} + +############################################################################# + +sub migrateSNMP{ + my $snmp = shift; + if (alreadyExistsIn51($snmp)){ + # already exists in 5.1 + updateEntry($snmp, "SNMP", 0, 1); + } + else { + addEntryTo51($snmp, "SNMP", 1); + } +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + my $sep = shift ; + + if ($sep) { + print "\n-------------------------------------------------------------------------"; + print LOGFILE "\n-------------------------------------------------------------------------"; + } + + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg ; + } +} + +############################################################################# +# this subroutine implements a very stupid version of diff + +sub diff { + my $f1 = shift; + my $f2 = shift; + my $lineToBeginWith = shift; + my $NULL = "" ; + my $diff_f1 = $NULL ; + my $diff_f2 = $NULL ; + my $retval = $NULL ; + my $ret; + open(F1, "$f1") or die "Could not open file $f1"; + open(F2, "$f2") or close(F1), die "Could not open file $f2"; + + while (defined($l1 = <F1>)) { + if ($lineToBeginWith){ + $lineToBeginWith -- ; + next ; + } + next if ($l1 =~ /^\#/); + $ret = defined($l2 = <F2>); + if ($ret) { + $ret = defined($l2 = <F2>) while ($ret && ($l2 =~ /^\#/)) ; + if ($ret) { + if (!($l1 eq $l2)) { + + # ignore whitespace + $l1_clean = $l1 ; + $l2_clean = $l2 ; + $l1_clean =~ s/\s//g; + $l2_clean =~ s/\s//g; + + if (!($l1_clean eq $l2_clean)) { + $diff_f1 .= "${l1}" unless ($l1_clean eq $NULL); + $diff_f2 .= "${l2}" unless ($l2_clean eq $NULL); + } + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + + while (defined($l2 = <F2>)) { + if (($l2 =~ /^\#/) || ($l2 =~ /^\s*$/)) { + next ; + } + else { + $diff_f2 .= "${l2}" ; + } + } + + close(F1); + close(F2); + + $retval .= "- differences present in your config file but not in standard file:\n\n". "$diff_f1\n" if ($diff_f1) ; + $retval .= "- differences present in standard file but not in your config file:\n\n" . "$diff_f2" if ($diff_f2) ; + return $retval ; +} + +sub CompareStdConfigFiles { + # Compare each configuration file against its default version. If it has changed, + # notify the user that the file has changed and will need to be checked by the + # user. This should be safe to do because there should be no path information + # stored in these conf files, which are just schema stuff. + # printTrace("\nCheck if standard configuration files have changed",3); + + # get the version of the DS to migrate + ($oldVersion, $oldMinor) = &getVersion($oldDir); + # get the version of the new DS + ($Version, $Minor) = &getVersion($root); + + my $origFilePath = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}install${PATHSEP}schema${PATHSEP}" ; + my $FilesChanged = ""; + my $AllDiffs = "***********************************************************************"; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(CONFDIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldConfDir: $!"; + + foreach $file (readdir(CONFDIR)) { + $origFile = $origFilePath . $file ; + $configFile = $oldSchemaDir . $file ; + if (( exists($stdIncludes{lc($file)})) && (-f $origFile)) { + $diffs = &diff($configFile, $origFile, $lineToBegin); + $lineToBegin = 0 if $lineToBegin ; + if ($diffs) { + $FilesChanged .= "\n$configFile"; + $AllDiffs .= "\n$configFile is different than the standard configuration file" ; + $AllDiffs .= "\nYou will need to check this file and make sure its changes are compatible "; + $AllDiffs .= "with the new directory server\nHere are the differences:\n"; + $AllDiffs .= "$diffs \n\n"; + $AllDiffs .= "***********************************************************************"; + } + else { + $NoChanges .= "\n$configFile"; + } + } + } + closedir(CONFDIR); + +if ($FilesChanged) { + printTrace("\nNo changes to old configuration files:$NoChanges",3) ; + printTrace("\n***********************************************************************",3) ; + printMsg("\nThe following standard files have been modified: $FilesChanged"); + if ($NO_INPUT_USER) { + # do nothing + } + else { + printMsg("\nDo you want to see the differences Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("$AllDiffs"); + } + printMsg("\nDo you want to continue the migration Yes/No [No] ?"); + $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + } + } +} + + + +############################################################################# + +# this is used to run the system() call, capture exit and signal codes, +# and die() upon badness; the first argument is a directory to change +# dir to, if any, and the rest are passed to system() +sub mySystem { + my $rc = &mySystemNoDie(@_); + my ($dir, @args) = @_; + if ($rc == 0) { +# success + } elsif ($rc == 0xff00) { + die "Error executing @args: error code $rc: $!"; + } elsif ($rc > 0x80) { + $rc >>= 8; + die "Error executing @args: error code $rc: $!"; + } else { + if ($rc & 0x80) { + $rc &= ~0x80; + } + die "Error executing @args: received signal $rc: $!"; + } + + # usually won't get return value + return $rc; +} + +# This version does not die but just returns the error code +sub mySystemNoDie { + my ($dir, @args) = @_; + if ($dir && ($dir ne "")) { + chdir($dir) or die "Could not change directory to $dir: $!"; + } + my $cmd = $args[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = 0xffff & system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd @fixargs\n"; + $rc = 0xffff & system {$cmd} @fixargs; + } + chdir(${curdir}) or die "Could not change directory to $curdir: $!"; + return $rc; +} + +########################################################################################### +# # +# Export/Import of the backends in @BACKENDS # +# # +########################################################################################### + +sub manydb2Ldif { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}="$oldDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "can't create $ldif_dir to store temporary ldif files"; + } + chdir($oldSlapdExecDir) or die "Could not change directory to $oldSlapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &db2Ldif($ldif, $backend); + } + print " Done.\n"; + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + +sub db2Ldif { + my $ldif = shift ; + my $backend = shift ; + my $include_suffix = shift ; + my $db2ldif_param ; + if ($include_suffix) { + $db2ldif_param = "db2ldif -D $oldHome -n $backend -a $ldif -s \"$include_suffix\""; + } + else { + $db2ldif_param = "db2ldif -D $oldHome -n $backend -a $ldif"; + } + open(DB2LDIF, "${quote}${quote}$slapdExecName${quote} $db2ldif_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + my $ii = 0; + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + printMsg($_); + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the 5.x slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif) { + chown( $newuid, $newgid, $ldif) or printMsg("\nUnable to change the ownership of $ldif to $localuser") ; + } + } +} + +sub manyLdif2db { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + chdir($slapdExecDir) or die "Could not change directory to $slapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &Ldif2db($ldif, $backend); + } + # remove the empty ldif directory + rmdir($ldif_dir); + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + + +sub Ldif2db { + my $ldif = shift ; + my $backend = shift ; + my $ldif2db_param = "ldif2db -D $serverHome -n $backend -i $ldif"; + open(LDIF2DB, "${quote}${quote}$slapdExecName${quote} $ldif2db_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + while (<LDIF2DB>) { + printMsg($_); + } + close(LDIF2DB); + # remove the ldif file after the import + unlink($ldif) ; +} + + +########################################################################################### +# # +# Running/Stopping the Server # +# # +########################################################################################### + + + +sub isDirectoryAlive { + die "\n Migration aborted. Check your 5.0 and 5.1 iPlanet Directory Server are installed on the same machine \n" if ( $LDAPservername == -1 ); + my $test_conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd); + if ($test_conn) { + $test_conn->close(); + return 1; + } + else { + return 0 ; + } +} + + +sub startServer { + my $instanceDir = ${serverHome} ; + my $errLog = $instanceDir . $PATHSEP . 'logs' . $PATHSEP . 'errors'; + # emulate tail -f + # if the last line we see does not contain "slapd started", try again + my $done = 0; + my $started = 0; + my $code = 0; + my $lastLine = ""; + my $timeout = time + 240; # 4 minutes + $ENV{"$LIB_PATH"}="${root}${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + + my $startCmd = $instanceDir . $PATHSEP . 'start' . $script_suffix; + if (! -f $startCmd) { + $startCmd = $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix; + } + $code = &mySystem($instanceDir,$startCmd); + open(IN, $errLog) or die "Could not open error log $errLog: $!"; + my $pos = tell(IN); + while (($done == 0) && (time < $timeout)) { + for (; ($done == 0) && ($_ = <IN>); $pos = tell(IN)) { + $lastLine = $_; + # print; + # the server has already been started and shutdown once . . . + if (/slapd started\./) { + $started++; + if ($started == 2) { + $done = 1; + } + # sometimes the server will fail to come up; in that case, restart it + } elsif (/Initialization Failed/) { + # print "Server failed to start: $_"; + $code = &mySystem($instanceDir, $startCmd); + # sometimes the server will fail to come up; in that case, restart it + } elsif (/exiting\./) { + # print "Server failed to start: $_"; + #$code = &mySystem($startCmd); + $code = &mySystem($instanceDir, $startCmd); + } + } + if ($lastLine =~ /PR_Bind/) { + # server port conflicts with another one, just report and punt + print $lastLine; + print "This server cannot be started until the other server on this\n"; + print "port is shutdown.\n"; + $done = 1; + } + if ($done == 0) { + # rest a bit, then . . . + sleep(2); + # . . . reset the EOF status of the file desc + seek(IN, $pos, 0); + } + } + close(IN); + + sleep(5); + die "\nUnable to start the $Version.$Minor iPlanet Directory Server\n" unless (isDirectoryAlive()); + + return 0; +} + +sub stopServer { + my $root = shift; + my $name = shift; + $maxStopIterations = 5; + print "\nShutting down server $name . . .\n"; + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote; + if (! -f $stopCmd) { + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote; + } + + if (! -f $stopCmd) { + # no stop command, probably a 1.X system; for NT, we'll try net stop + # for unix, we'll get the pid and kill it + if ($isNT) { + $stopCmd = 'net stop ' . $name; + } else { + # see if there is a pid file + $pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' . + $PATHSEP . 'pid'; + if (open(PIDFILE, $pidfile)) { + chomp($pid = <PIDFILE>); + close(PIDFILE); + while ($maxStopIterations-- && !$exitCode) { + $exitCode = kill(15, $pid); + } + $stopCmd = undef; + } + } + } + + # keep looping until the stop cmd returns an error code, which usually + # means that what ever we want to stop is stopped, or some other error + # occurred e.g. permission, or no such service + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + while ($stopCmd && $maxStopIterations-- && $exitCode) { + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + } + + if (!$maxStopIterations) { + print "Warning: could not shutdown the server: $!\n"; + } + sleep(10) ; + $exitCode = 0; +} + + +sub runAndIgnoreOutput { + my $cmd = shift; + printMsg("."); + open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!"; + printMsg("."); + sleep(1); # allow pipe to fill with data + printMsg("."); + while (<RUNCMD>) { +# print; + } + my $code = close(RUNCMD); +# print "runAndIgnore: code=$code status=$?\n"; + return $?; +} + +############################################################################# +# migrate SSL info + +sub MigrateSSL { + my $secPwd = 'bidon' ; + # copy the SSL directory + ©Dir("$oldHome${PATHSEP}ssl","$serverHome${PATHSEP}ssl") if (-d "$oldHome${PATHSEP}ssl"); + # copy the cert db and key files + if ( -d "$oldDir${PATHSEP}alias") { + $aliasDir = "$root${PATHSEP}alias"; + if (! -d $aliasDir) { + mkdir($aliasDir, 0750); + } + my $keydb = "$aliasDir${PATHSEP}slapd-$newname-key3.db" ; + my $certdb = "$aliasDir${PATHSEP}slapd-$newname-cert7.db" ; + my $old_keydb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ; + my $old_certdb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db"; + my $keydb_backup = "$aliasDir${PATHSEP}slapd-$newname-key3.db_backup" ; + my $certdb_backup = "$aliasDir${PATHSEP}slapd-$newname-cert7.db_backup" ; + if (-f $old_keydb) { + if (-f $keydb) { + if ($NO_INPUT_USER) { + printMsg("\n$keydb already exists. backup in $keydb_backup ..."); + ©BinFile($keydb,$keydb_backup); + ©BinFile($old_keydb,$keydb); + } + else { + print("\n\n$keydb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_keydb,$keydb); + } + } + } + else { + ©BinFile($old_keydb,$keydb); + } + } + if (-f $old_certdb) { + if (-f $certdb) { + if ($NO_INPUT_USER) { + printMsg("\n$certdb already exists. backup in $certdb_backup ..."); + ©BinFile($certdb,$certdb_backup); + ©BinFile($old_certdb,$certdb); + } + else { + print("\n\n$certdb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_certdb,$certdb); + } + } + } + else { + ©BinFile($old_certdb,$certdb); + } + } + # copy the old password file + if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") { + ©BinFile( + "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt", + "$aliasDir${PATHSEP}$type-$newname-pin.txt" + ); + } + } + +} + +sub DisableSSL { + my $entry = $conn->search("cn=config","base","objectclass=*"); + my $LDAPparam = "nsslapd-security" ; + my $Value = "off" ; + if ($entry->{$LDAPparam}[0] ne $Value) { + printTrace("\nDisable SSL...",1); + $entry->setValues($LDAPparam, $Value); + } + my $res = $conn->update($entry); + if ($res) { + printTrace("\nSSL disabled",2); + } + else { + printMsg("\nCan't disabled SSL. The server may have problems to start"); + } +} + +# enable the migration of client authentication informations +sub MigrateCertmap { + # backup the old certmap.conf and replace it with the new one + my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"; + my $newCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf" ; + my $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + if (-f $oldCertmap) { + if ($NO_INPUT_USER) { + printMsg("\n$newCertmap has been backup in $backupCertmap"); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up the file $newCertmap [$backupCertmap] ?") ; + my $Answer = <STDIN> ; + $backupCertmap = $Answer if ($Answer ne "\n"); + chomp($backupCertmap); + printTrace("\nDest: .$backupCertmap.",4); + if (-e $backupCertmap) { + printMsg("\n\n$backupCertmap already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup file: $newCertmap in $backupCertmap",4); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + } + else { + } +} + +sub hasChangedoldCertmap { + my $certmapfile = shift ; + my @reference = ("certmap default default", + "default:DNComps", + "default:FilterComps e") ; + my $cpt = 0 ; + printTrace("\nhasChangedoldCertmap",3); + open(CERTMAP,"< $certmapfile"); + while (<CERTMAP>) { + if ((! /^\s*#/) && (! /^\s*$/)) { + my $ref = $reference[$cpt] ; + printTrace("\nValue: $_, ref: $ref",4); + if (! /^\s*$ref\s*$/) { + return 1 ; + } + else { + $cpt++ ; + } + } + } + close (CERTMAP); + printTrace("\ncpt: $cpt",4); + if ($cpt < $#reference) { + return 1 ; + } + else { + return 0 ; + } +} + + +########################################################################################### +# # +# Copy directory and files functions # +# # +########################################################################################### + + +sub copyDir { + my $src = shift; + my $dest = shift; + my $exclude = shift; + + opendir( SRC, $src ) or die "Can't open directory $src: $!: "; + my $mode; + my $uid; + my $gid; + mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest ); + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + local ( @files ) = readdir ( SRC ); + closedir( SRC ); + for ( @files ) { + if ( $_ eq "." || $_ eq ".." ) { + next; + } elsif ( $exclude && /$exclude/ ) { + next; + } elsif( -d "$src${PATHSEP}$_") { + ©Dir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); + } else { + ©BinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_"); + } + } +} + +sub copyBinFile { + my $src = shift; + my $dest = shift; + my $buf = ""; + my $bufsize = 8192; + + open( SRC, $src ) || die "Can't open $src: $!\n"; + # if we are given a directory destination instead of a file, extract the + # filename portion of the source to use as the destination filename + if (-d $dest) { + $dest = $dest . $PATHSEP . &basename($src); + } + open( DEST, ">$dest" ) || die "Can't create $dest: $!\n"; + binmode SRC; + binmode DEST; + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + while (read(SRC, $buf, $bufsize)) { + print DEST $buf; + } + close( SRC ); + close( DEST ); +} + +############################################################################################################# +# backup 5.x configuration files # +# backup the directory <root_server5>/slapd-instance/config dans <root_server5>/slapd-instance/BackupConfig # # +# # +############################################################################################################# + + +sub backupConfigFiles { + # backup the 5.x config files + my $src = "$serverHome${PATHSEP}config" ; + my $dest = "$serverHome${PATHSEP}config_backup" ; + if ($NO_INPUT_USER) { + printMsg("\n$src has been backup in $dest"); + ©Dir($src,$dest); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up your configuration directory [$dest] ?") ; + my $Answer = <STDIN> ; + $dest = $Answer if ($Answer ne "\n"); + chomp($dest); + printTrace("\nDest: .$dest.",4); + if (-e $dest) { + printMsg("\n\n$dest already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $dest = "$serverHome${PATHSEP}config_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup Directory: $src in $dest",4); + ©Dir($src,$dest); + } +} +############################################################################# + +sub getLDAPservername { + my $oldLDAPservername; + my $LDAPservername; + my $localhost = "nsslapd-localhost"; + open(OLDDSELDIF, "< $oldDSEldif") or die "\nError: could not open old config file $oldDSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*OLDDSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $oldLDAPservername = $values[0]; + printTrace("\nName of the old LDAP server: $oldLDAPservername",3); + } + break; + } + } + close(OLDSELDIF); + + open( DSELDIF, "< $DSEldif" ) || die "\nCan't open $DSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $LDAPservername = $values[0]; + printTrace("\nName of the new LDAP server: $LDAPservername",3); + } + break; + } + } + close(DSELDIF); + # check ol and new Directory Instance are installed on the same physical machine. + if (lc($oldLDAPservername) ne lc($LDAPservername)) { + # warn the user he tries to migrate a 4.x server installed on a different machine from the 5.x one + printMsg("\n\nYour old instance is on $oldLDAPservername, whereas your new instance is on $LDAPservername. Migration on different machines is not supported. Do you want to continue ? Yes/No [No]:") ; + if (! (<STDIN> =~ /yes|y/i)) { + return -1; + } + } + return $LDAPservername ; +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir) or die "Could not change directory to $dir$progDir: $!";; + } + $ENV{"$LIB_PATH"}="$dir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + #print; + if (/^Netscape-Directory\/(\d+)\.(\d+)\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + elsif (/^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + elsif (/^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + } + $code = close(F); +# print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + + +############################################################################################### +sub normalizeDir { + my $dir = shift ; + my $dir_prec = "" ; + while ($dir_prec ne $dir) { + $dir_prec = $dir ; + if ($isNT) { + grep { s@\\\\@\\@g } $dir ; + } + else { + grep { s@//@/@g } $dir ; + } + } + return $dir ; +} + + +############################################################################################### + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################################### +# get uid and group id of the 5.x slapd server. +# The uid is done through the nsslapd-localuser attribute + +sub getuid_gid { + my $newuid ; + my $newgid ; + my $localuser ; + my $localuser_attr = "nsslapd-localuser" ; + if (! $isNT) { + &startServer() unless (isDirectoryAlive()); + my $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; + my $entry = $conn->search("cn=config ", "base","objectclass=*", 0, ($localuser_attr)) ; + # Tests wether we succeed to get the entry cn=config + die "\nCan't get the entry cn=config \n" unless ($entry); + my @values = $entry->getValues($localuser_attr); + $conn->close(); + if ($#values == -1 || ($values[0] eq "") ) { # tests wether the nsslapd-localuser attribute has a value + printMsg("\nNo localuser has been found in the configuration of the directory. "); + if ($NO_INPUT_USER) { + printMsg("\nWe considered nobody as the localuser"); + $localuser = "nobody" ; + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\nUnder what user does your $Version.$Minor directory server run [nobody] ? ") ; + $localuser = <STDIN> ; + chomp($localuser); + $localuser = "nobody" if ($localuser eq ""); + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + if ($newuid) { + $Ask = 0 ; + } + else { + printMsg("\nError: $localuser is unknown from the system "); + } + } + } + } + else { + $localuser = $values[0]; # returns the first value (we should only have one localuser) + my $size = $#values ; + } + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + return ($localuser, $newuid, $newgid) ; + } + else { + return () ; + } +} + +sub getolduid_gid { + my $oldlocaluser ; + my $localuserAttr = "nsslapd-localuser"; + my $entry ; + if (! $isNT) { + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "CONFIG_NODE") { + $oldlocaluser = $entry->{$localuserAttr}[0] if ($entry->exists($localuserAttr)); + break ; + } + } + close(DSE); + ($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ; + return ($oldlocaluser, $olduid, $oldgid) ; + } + else { + return (); + } +} +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} + +################################ +# Need to migrate the credential. +# If the credential can not be migrated, leave it at it is +################################ +sub migrate_credential{ + my $entry_to_modify = shift; + my $credentials_attr = shift; + my @old_value = $entry_to_modify->getValues($credentials_attr); + my $migratecredExecName = 'migratecred'; + + my @new_cred = `${quote}${quote}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}admin${PATHSEP}bin${PATHSEP}$migratecredExecName${quote} -o $oldHome -n $serverHome -c @old_value`; + + if ( $? == 0 ) + { + $entry_to_modify->setValues($credentials_attr, @new_cred); + } +} + diff --git a/ldap/admin/src/scripts/template-migrate5to6 b/ldap/admin/src/scripts/template-migrate5to6 new file mode 100644 index 00000000..3cc5bd58 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrate5to6 @@ -0,0 +1,3043 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Migrate a 5.x directory server to a 6.2 directory server + +####################################################################################################### +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Entry; +use Mozilla::LDAP::LDIF; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::localtime; +use File::Basename; +use Class::Struct ; + +####################################################################################################### + +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o oldInstancePath -n newInstancePath [-t tracelevel] [-L logfile]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - new 6.2 Directory Manager\n"); + print(STDERR " : -w password - new 6.2 Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for new 6.2 Directory Manager's password\n"); + print(STDERR " : -j filename - Read new 6.2 Directory Manager's password from file\n"); + print(STDERR " : -p port - new 6.2 Directory Server port\n"); + print(STDERR " : -o oldInstancePath - Path of the old instance to migrate \n"); + print(STDERR " : -n newInstancePath - Path of the new 6.2 instance\n"); + print(STDERR " : [-d dataPath] - Path to directory containing data files to import into new instance\n"); + print(STDERR " : [-v oldVersion] - Version of old instance (obtained by running $slapdExecName -v\n"); + print(STDERR " : [-t tracelevel] - (optional) specify the level of trace (0..3)\n"); + print(STDERR " : [-L logfile] - (optional) specify the file to log the migration report \n"); + } +######################################################################################################## + +BEGIN { + + require 'uname.lib' ; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + ${SEP} = $isNT ? ";" : ":" ; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + + # If this variable is set, all file/directory creation will make sure the mode + # and ownership of the destination is the same as the source + $PRESERVE = 1 if (!$isNT); + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + if ($isNT) { + $os = "WINNT"; + } else { + $os = &uname("-s"); + } + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + } + if ( $os eq "AIX" ) { + $dll_suffix = "_shr.a"; + } + elsif ( $os eq "HP-UX" ) { + $dll_suffix = ".sl"; + } + elsif ( $os eq "WINNT" ) { + $dll_suffix = ".dll"; + } + else { + $dll_suffix = ".so"; + } + $slapdExecName = $isNT ? 'slapd.exe' : './ns-slapd'; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; +} + +SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + + # old parameters + ${oldDir} = "" ; + ${oldname} = "" ; + ${oldHome} = "" ; + ${oldConfDir} = "" ; + ${oldlocaluser} ; + ${olduid} ; + ${oldgid} ; + + # new parameters + ${root} = "{{DS-ROOT}}" ; + ${type} = "" ; + ${newname} = "" ; + ${newport} = "" ; + ${rootDN} = "" ; + ${rootpwd} = "" ; + ${localhost} = "" ; + ${LogFileReport} = "" ; + ${newuid} ; + ${localuser} ; + ${newgid} ; + $NO_INPUT_USER = 0 ; # by default user can give inputs during the migration process + ${curdir} = getCwd(); + ${slapdExecDir} = "${root}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + # in 6.2 the replica Id is setup to a static value + $replicaIdvalue = 65535; + + # specify the level of trace + $TRACELEVEL=1; + + $LDAP_SERVER_UNREACHABLE = 81; + + # get input users + &getParameters() ; + ${oldDir} = &normalizeDir("${oldDir}"); + ${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; + ${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; + ${oldSchemaDir} = "${oldConfDir}schema${PATHSEP}"; + ${oldDSEldif} = "${oldConfDir}dse.ldif"; + ${serverHome} = "${root}${PATHSEP}$type-$newname" ; + ${schemaDir} = "$serverHome${PATHSEP}config${PATHSEP}schema${PATHSEP}"; + ${DSEldif} = "$serverHome${PATHSEP}config${PATHSEP}dse.ldif"; + ${ldif_rep} = "${oldConfDir}ldif${PATHSEP}" ; + ${oldSlapdExecDir} = "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + open(LOGFILE, ">> $LogFileReport"); + + printTrace("\noldDir: $oldDir, oldHome: $oldHome, \noldConfDir: $oldConfDir, \nldif_rep: $ldif_rep, \nrootDN: $rootDN, \nPort: $newport, \nNewname: $newname\n",3); + printTrace("\nLIB_PATH: $LIB_PATH",4); + + if (!(-d $serverHome)) { + printMsg("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + printMsg("\n$oldHome doesn't exist\n"); + exit(1); + } + + if ($olddatadir && !(-d $olddatadir)) { + print("\n$olddatadir doesn't exist\n"); + exit(1); + } + + +%HashParametersName = (); + +# The following hash displays only general server parameters to migrate under cn=config +%GeneralSrvParamToMigrate = ( + 'nsslapd-accesscontrol'=> '\n', + 'nsslapd-errorlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-logging-enabled'=> '\n', + 'nsslapd-auditlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-level'=> '\n', + 'nsslapd-accesslog-logbuffering'=> '\n', + 'nsslapd-accesslog-logexpirationtime'=> '\n', + 'nsslapd-accesslog-logexpirationtimeunit'=> '\n', + 'nsslapd-accesslog-logmaxdiskspace'=> '\n', + 'nsslapd-accesslog-logminfreediskspace'=> '\n', + 'nsslapd-accesslog-logrotationtime'=> '\n', + 'nsslapd-accesslog-logrotationtimeunit'=> '\n', + 'nsslapd-accesslog-maxlogsize'=> '\n', + 'nsslapd-accesslog-maxLogsPerDir'=> '\n', + 'nsslapd-attribute-name-exceptions'=> '\n', + 'nsslapd-auditlog-logexpirationtime'=> '\n', + 'nsslapd-auditlog-logexpirationtimeunit'=> '\n', + 'nsslapd-auditlog-logmaxdiskspace'=> '\n', + 'nsslapd-auditlog-logminfreediskspace'=> '\n', + 'nsslapd-auditlog-logrotationtime'=> '\n', + 'nsslapd-auditlog-logrotationtimeunit'=> '\n', + 'nsslapd-auditlog-maxlogsize'=> '\n', + 'nsslapd-auditlog-maxLogsPerDir'=> '\n', + 'nsslapd-certmap-basedn'=> '\n', + 'nsslapd-ds4-compatible-schema'=> '\n', + 'nsslapd-enquote-sup-oc'=> '\n', + 'nsslapd-errorlog-level'=> '\n', + 'nsslapd-errorlog-logexpirationtime'=> '\n', + 'nsslapd-errorlog-logexpirationtimeunit'=> '\n', + 'nsslapd-errorlog-logmaxdiskspace'=> '\n', + 'nsslapd-errorlog-logminfreediskspace'=> '\n', + 'nsslapd-errorlog-logrotationtime'=> '\n', + 'nsslapd-errorlog-logrotationtimeunit'=> '\n', + 'nsslapd-errorlog-maxlogsize'=> '\n', + 'nsslapd-errorlog-maxlogsperdir'=> '\n', + 'nsslapd-groupevalnestlevel'=> '\n', + 'nsslapd-idletimeout'=> '\n', + 'nsslapd-ioblocktimeout'=> '\n', + 'nsslapd-lastmod'=> '\n', + 'nsslapd-listenhost'=> '\n', + 'nsslapd-maxdescriptors'=> '\n', + 'nsslapd-nagle'=> '\n', + 'nsslapd-readonly'=> '\n', + 'nsslapd-referralmode'=> '\n', + 'nsslapd-plugin-depends-on-name'=> '\n', + 'nsslapd-plugin-depends-on-type'=> '\n', + 'nsslapd-referral'=> '\n', + 'nsslapd-reservedescriptors'=> '\n', + 'nsslapd-rootpwstoragescheme'=> '\n', + 'nsslapd-schemacheck'=> '\n', + 'nsslapd-secureport'=> '\n', + 'nsslapd-security'=> '\n', + 'nsslapd-sizelimit'=> '\n', + 'nsslapd-ssl3ciphers'=> '\n', + 'nsslapd-timelimit'=> '\n', + 'passwordchange'=> '\n', + 'passwordchecksyntax'=> '\n', + 'passwordexp'=> '\n', + 'passwordhistory'=> '\n', + 'passwordinhistory'=> '\n', + 'passwordlockout'=> '\n', + 'passwordlockoutduration'=> '\n', + 'passwordmaxage'=> '\n', + 'passwordmaxfailure'=> '\n', + 'passwordminage'=> '\n', + 'passwordminlength'=> '\n', + 'passwordmustchange'=> '\n', + 'passwordresetfailurecount' => '\n', + 'passwordstoragescheme' => '\n', + 'passwordunlock' => '\n', + 'passwordwarning' => '\n' +); + +# the following hash displays global parameters related to database stored under cn=config,cn=ldbm database,cn=plugins,cn=config +%GlobalConfigLDBMparamToMigrate = ( + 'nsslapd-allidsthreshold' => '\n', + 'nsslapd-lookthroughlimit' => '\n', + 'nsslapd-mode' => '\n', + 'nsslapd-dbcachesize' => '\n', + 'nsslapd-cache-autosize' => '\n', + 'nsslapd-cache-autosize-split' => '\n', + 'nsslapd-db-transaction-logging' => '\n', + 'nsslapd-import-cachesize' => '\n' +); + +# the following hash displays specific parameters to each backends and stored under cn=DBname,cn=ldbm database,cn=plugins,cn=config +%LDBMparamToMigrate = ( + 'nsslapd-cachesize' => '\n', + 'nsslapd-cachememsize' => '\n', + 'nsslapd-readonly' => '\n', + 'nsslapd-require-index' => '\n' +); + + +%ChainingConfigParams = ( + 'nsactivechainingcomponents' => '\n', + 'nstransmittedcontrols' => '\n' + ); + +%ChainingDefaultInstanceConfigParams = ( + 'nsabandonedsearchcheckinterval' => '\n', + 'nsbindconnectionslimit' => '\n', + 'nsbindtimeout' => '\n', + 'nsbindretrylimit' => '\n', + 'nshoplimit' => '\n', + 'nsmaxresponsedelay' => '\n', + 'nsmaxtestresponsedelay' => '\n', + 'nschecklocalaci' => '\n', + 'nsconcurrentbindlimit' => '\n', + 'nsconcurrentoperationslimit' => '\n', + 'nsconnectionlife' => '\n', + 'nsoperationconnectionslimit' => '\n', + 'nsproxiedauthorization' => '\n', + 'nsreferralonscopedsearch' => '\n', + 'nsslapd-sizelimit' => '\n', + 'nsslapd-timelimit' => '\n' +); + +%changelog5params = ( + 'nsslapd-changelogmaxage' => '\n', + 'nsslapd-changelogmaxentries' => '\n' + ); + +@SNMPparams = ( + 'nssnmpenabled', + 'nssnmporganization', + 'nssnmplocation', + 'nssnmpcontact', + 'nssnmpdescription', + 'nssnmpmasterhost', + 'nssnmpmasterport', + 'nssnmpenabled', + 'aci' + ); + +%stdIncludes = ( + "." => "\n", + ".." => "\n", + "30ns-common.ldif " => "\n", + "50ns-mail.ldif " => "\n", + "50ns-news.ldif" => "\n", + "50iplanet-servicemgt.ldif"=> "\n", + "50netscape-servicemgt.ldif"=> "\n", + "50ns-mcd-browser.ldif" => "\n", + "50ns-proxy.ldif" => "\n", + "00core.ldif" => "\n", + "50ns-admin.ldif" => "\n", + "50ns-mcd-config.ldif " => "\n", + "50ns-value.ldif" => "\n", + "05rfc2247.ldif" => "\n", + "50ns-calendar.ldif" => "\n", + "50ns-mcd-li.ldif" => "\n", + "50ns-wcal.ldif" => "\n", + "05rfc2927.ldif" => "\n", + "50ns-certificate.ldif" => "\n", + "50ns-mcd-mail.ldif" => "\n", + "50ns-web.ldif" => "\n", + "10rfc2307.ldif" => "\n", + "50ns-compass.ldif" => "\n", + "50ns-media.ldif" => "\n", + "20subscriber.ldif" => "\n", + "50ns-delegated-admin.ldif"=> "\n", + "50ns-mlm.ldif" => "\n", + "25java-object.ldif" => "\n", + "50ns-directory.ldif" => "\n", + "50ns-msg.ldif" => "\n", + "28pilot.ldif" => "\n", + "50ns-legacy.ldif" => "\n", + "50ns-netshare.ldif" => "\n" +); + + +# Backends migrated (Backend CN attribute value) +@BACKENDS = () ; +# All pairs of suffix-backend are registered in this hashtable +%oldBackends = () ; + +#store the backend instances to migrate +@LDBM_backend_instances = (); + +#store the mapping tree +@Mapping_tree_entries = (); + +#store the suffix and the associated chaining backend +%oldChainingBackends = (); + +#store the multiplexor bind entries to migrate +%MultiplexorBindDNEntriesToMigrate = (); + +#store the Replica bind DN entries to migrate +%ReplicaBindDNEntriesToMigrate = (); + +# list of standard plugins +%stdPlugins = ( + "7-bit check" => "\n", + "acl plugin" => "\n", + "acl preoperation" => "\n", + "binary syntax" => "\n", + "case exact string syntax" => "\n", + "case ignore string syntax" => "\n", + "chaining database" => "\n", + "class of service" => "\n", + "country string syntax" => "\n", + "distinguished name syntax" => "\n", + "generalized time syntax" => "\n", + "integer syntax" => "\n", + "internationalization plugin" => "\n", + "ldbm database" => "\n", + "legacy replication plugin" => "\n", + "multimaster replication plugin" => "\n", + "octet string syntax" => "\n", + "clear" => "\n", + "crypt" => "\n", + "ns-mta-md5" => "\n", + "sha" => "\n", + "ssha" => "\n", + "postal address syntax" => "\n", + "referential integrity postoperation" => "\n", + "retro changelog plugin" => "\n", + "roles plugin" => "\n", + "telephone syntax" => "\n", + "uid uniqueness" => "\n", + "uri syntax" => "\n" + ); + +# list of indexes that have disappeared from the new schema compared to 5.0 +%deniedIndexes = ( + 'dncomp' => "\n" +); + +@default_indexes = (); +@indexes = (); + +# list of user added Plugin's. In 6.2, they 'll need to be recompiled +@badPlugins = () ; + +@pluginAttrs = ( + "objectclass", + "cn", + "nsslapd-pluginpath", + "nsslapd-plugininitfunc", + "nsslapd-plugintype", + "nsslapd-pluginenabled", + "nsslapd-plugin-depends-on-type", + "nsslapd-pluginid", + "nsslapd-pluginversion", + "nsslapd-pluginvendor" + ); + +@nsds5replicaAttrs = ( + 'objectclass', + 'nsDS5ReplicaRoot', + 'nsDS5ReplicaType', + 'nsDS5ReplicaLegacyConsumer', + 'nsDS5flags', + 'nsDS5ReplicaId', + 'nsDS5ReplicaPurgeDelay', + 'nsDS5ReplicaBinddn', + 'cn', + 'nsDS5ReplicaReferral' + ); + +# array of replicas to migrate +@new6replicas = (); + +# array of replication agreements to migrate +@replicationAgreements = (); + +# compare LDIF standard config files with standard ones +CompareStdConfigFiles() ; +die "\n\n The version of product you want to migrate is not a 5.x Directory Server\n" unless ($oldVersion == 5) ; + +# Shutdown the legacy Directory instance +printTrace("\nShutdown the legacy Directory Server instance: ${oldHome}",0); +&stopServer($oldDir, 'slapd-'.$oldname); + +# get the hostname of the new LDAP server +my $LDAPservername = &getLDAPservername(); + +# get the uid and gid of the 6.2 slapd user +($localuser, $newuid, $newgid) = getuid_gid(); +# get the uid and gid of the 5.x slapd user +($oldlocaluser, $olduid, $oldgid) = getolduid_gid(); +printTrace("\n6.2 localuser: $localuser, uid: $newuid, gid: $newgid",2); +printTrace("\n5.x localuser: $oldlocaluser, uid: $olduid, gid: $oldgid",2); + +# backup 6.2 configuration files in <6server_root>/slapd-instancename/config +printTrace("\nBackup $serverHome${PATHSEP}config on $serverHome${PATHSEP}config_backup ...",0); +&backupConfigFiles(); + +# migrate the schema (need to stop and start the 6.2 server) +printTrace("\nMigrate the schema...",0); +MigrateSchema(); + +# start the server unless it is already started +&startServer() unless (isDirectoryAlive()); + +############### Connect to the 6.2 LDAP Directory Server ###################### +$ENV{"$LIB_PATH"} = $new_libpath; + +die "\n Migration aborted. Make sure your old and new Directory Server are installed on the same machine \n" if ( $LDAPservername == -1 ); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + +# Cconnection to 6.2 LDAP server is successful ! +printTrace("\nConnected to $Version.$Minor LDAP server",0) ; + +# Parse the main configuration file: dse.ldif +printTrace("\n\nParse the old DSE ldif file: $oldDSEldif *****",0, 1); +printTrace("\nThis may take a while ...\n",0); +&MigrateDSEldif(); + +#migrate LDBM backend instances +printTrace("\n\nMigrate LDBM backend instances...",0,1); +&migrateLDBM_backend_instances(); + +#migrate mapping tree entries +printTrace("\n\nMigrate mapping tree...",0,1); +&migrateMappingTree(); + +#migrate default indexes +printTrace("\n\nMigrate default indexes...",0,1); +migrateDefaultIndexes(); + +#migrate indexes +printTrace("\n\nMigrate indexes...",0,1); +migrateIndexes(); + +#migrate replicas +printTrace("\n\nMigrate replicas...",0,1); +&MigrateNSDS5_replica(); + +#migrate replication agreements +printTrace("\n\nMigrate replication agreements...",0,1); +&MigrateNSDS_replication_agreement(); + +#migrate key/cert databases +printTrace("\n\nMigrate key/cert databases...",0,1); +&MigrateSSL(); + +# migrate certmap.conf +printTrace("\n\nMigrate Certmap.conf...",0,1); +&MigrateCertmap() ; + +################## Close the connection to 6.2 LDAP Server ##################### +printTrace("\n\n***** Close the LDAP connection to the new Directory Server instance ***** ",0); +$conn->close; + + +################## stop the new instance and Export/Import the data, restart the server ################## +if (@BACKENDS) { + &stopServer($root,'slapd-'.$newname); + if ($olddatadir) { + printTrace("\nData already contained in $olddatadir...\n",0,1) ; + $ldif_rep = "$olddatadir${PATHSEP}"; + } else { + printTrace("\nData processing...\n",0,1) ; + # migrate data for each backend: 5.x -> LDIF files + &manydb2Ldif($ldif_rep); + } + + # migrate LDIF data to the new database: LDIF -> New + &manyLdif2db($ldif_rep); + &migrateChangelog(); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); + &startServer() unless (isDirectoryAlive()); +} +else { + printTrace("\nINFORMATION - There are no non-standard or non-already existing suffixes to migrate\n",0); + &migrateChangelog(); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); +} + +printMsg("\n\n ****** End of migration ******\n\n"); + +close(LOGFILE); + + +########################################################################################### +# get input users +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # new instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe new instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # new DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-d") { # old instance LDIF data dir + if (! $olddatadir ) { + $olddatadir = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-v") { # old version + if (! $oldversionstr ) { + $oldversionstr = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + $NO_INPUT_USER = 1 ; + } elsif ("$ARGV[$i]" eq "-L") { # migration logfile + $LogFileReport = $ARGV[++$i] ; + } + else { + print("\nThe option $ARGV[$i] is not recognized"); + &usage() ; + exit(1); + } + $i++; + } + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + if ($exit) { + &usage() ; + exit(1); + } + +} + +################################################################################################### + +sub MigrateSchema{ + my $FilesChanged = ""; + my $AllDiffs = ""; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(SCHEMADIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldSchemaDir: $!"; + + foreach $file (readdir(SCHEMADIR)) { + if (! exists($stdIncludes{lc($file)})) { + my $newSchemaFile = $schemaDir . $file; + if (-f $newSchemaFile ) { + # The ldif file already exists. Make a diff and warn the user if different. + if (diff($newSchemaFile, $oldSchemaDir.$file)) { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $newSchemaFile); + } + } + else { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $newSchemaFile); + } + } + } + closedir(SCHEMADIR); + if ($AllDiffs) { + printMsg("\n\n***********************************************************************"); + printMsg("\nThe following LDIF files have been migrated:"); + printMsg("$AllDiffs"); + printMsg("\n*************************************************************************\n\n"); + } + &startServer() if (! isDirectoryAlive()); +} + + +################################################################################################### +# This subroutine is used to parse the dse.ldif file and call specific routines to act with entries +sub MigrateDSEldif { + printTrace("\nMigrate DSE entries...",1); + my $tempoAlreadyDone = 0; + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + SWITCH: { + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + parseLDBM_backend_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "MAPPING_TREE"){ + parseMapping_tree($entry); + last SWITCH; + } + if ($typeOfEntry eq "DEFAULT_INDEX"){ + parseDefaultIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "INDEX"){ + parseIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "STANDARD_PLUGIN"){ + migrateStdPlugin($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_NODE"){ + migrateConfig_Node($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_LDBM_DATABASE"){ + migrateConfig_LDBM_database($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_CONFIG"){ + migrateChainingBE_config($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_INSTANCE"){ + migrateChainingBE_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS5_REPLICA"){ + parseNSDS5_replica($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS_REPLICATION_AGREEMENT"){ + parseNSDS_replication_agreement($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHANGELOG5"){ + migrateChangelog5($entry); + last SWITCH; + } + if ($typeOfEntry eq "REPLICATION"){ + migrateReplication($entry); + last SWITCH; + } + if ($typeOfEntry eq "SECURITY"){ + migrateSecurity($entry); + last SWITCH; + } + if ($typeOfEntry eq "SNMP"){ + migrateSNMP($entry); + last SWITCH; + } + } + + } + close(DSELDIF); +} + +############################################################################# +# returns the "type of an entry". If the entry is not to be migrated its type is "NOT_MIGRATED_TYPE" + +sub getTypeOfEntry{ + my $entry = shift; + my $DN = $entry->getDN(1) ; # 1 is to normalize the returned DN + if (($DN =~ /cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "LDBM_BACKEND_INSTANCE"; + } + if (($DN =~ /cn=mapping tree,cn=config$/i) && (isObjectclass($entry,"nsMappingTree"))) { + return "MAPPING_TREE"; + } + if (($DN =~ /cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsIndex"))) { + return "DEFAULT_INDEX"; + } + if (isObjectclass($entry,"nsIndex")) { + return "INDEX"; + } + if ((isObjectclass($entry,"nsSlapdPlugin")) && (isStdPlugin($entry))) { + return "STANDARD_PLUGIN"; + } + if ($DN =~ /^cn=config$/i) { + return "CONFIG_NODE"; + } + if ($DN =~ /^cn=config,cn=ldbm database,cn=plugins,cn=config$/i) { + return "CONFIG_LDBM_DATABASE"; + } + if (($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i) || ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i)){ + return "CHAINING_BACKEND_CONFIG"; + } + if (($DN =~ /cn=chaining database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "CHAINING_BACKEND_INSTANCE"; + } + if (isObjectclass($entry,"nsDS5Replica")) { + return "NSDS5_REPLICA"; + } + if (isObjectclass($entry,"nsDS5ReplicationAgreement")) { + return "NSDS_REPLICATION_AGREEMENT"; + } + if ($DN =~ /^cn=changelog5,cn=config$/i) { + return "CHANGELOG5"; + } + if (($DN =~ /cn=replication,cn=config$/i) && ($DN !~ /^cn=replication,cn=config$/i)) { + return "REPLICATION"; + } + if ($DN =~ /cn=encryption,cn=config$/i) { + return "SECURITY"; + } + if ($DN =~ /^cn=SNMP,cn=config$/i) { + return "SNMP"; + } + return "NOT_MIGRATED_TYPE"; +} + +############################################################################# + + + +############################################################################# +# returns 1 if the objectclass given in parameter is present in the objectclasses values of the entry +# given in parameter, 0 else + +sub isObjectclass { + my $entry = shift; + my $objectclass = shift; + return ($entry->hasValue("objectclass",$objectclass,1)); +} + +############################################################################# + +sub isStdPlugin { + my $entry = shift; + my $CN = $entry->{cn}[0]; + if (isObjectclass($entry,"nsSlapdPlugin")) { + return 1 if ($stdPlugins{lc($CN)}); + } + return 0; +} + + +############################################################################# + +sub alreadyExistsInNew{ + my $entry = shift; + my $mustExist = shift; + my $DN = $entry->getDN(1); # 1 to normalize the DN + # We have a name change of "uid uniqueness" plugin in DS6.x + # to "attribute uniqueness" + $DN =~ s/uid\ uniqueness/attribute\ uniqueness/ if ($DN =~ /uid\ uniqueness/); + return searchEntry($DN, $mustExist); +} + +############################################################################# +sub searchEntry { + my $DN = shift; + my $mustExist = shift; + my $res = $conn->search($DN, "base", "objectclass=*"); + my $cpt = 5; + if ($res) { + return $res; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to search $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->search($DN, "base", "objectclass=*"); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + return $res ; + } + elsif (($errorCode eq $LDAP_SERVER_UNREACHABLE) || ($mustExist)) { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** Failed to search: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + } + return 0; + } +} + + +############################################################################# + +sub addEntryToNew{ + my $entry = shift; + my $typeOfEntry = shift; + my $trace = shift; + my $res = $conn->add($entry); + my $DN = $entry->getDN(1); + my $cpt = 5; + if ($res) { + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to add $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->add($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry: Add Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + +############################################################################# + +sub updateEntry{ + my $entry = shift; + my $typeOfEntry = shift; + my $CHECK = shift; + my $trace = shift; + my $cpt = 5; + if ($CHECK) { + if (! hasChanged($entry, $typeOfEntry)) { + return 1; + } + } + my $res = $conn->update($entry); + my $DN = $entry->getDN(1); + if ($res) { + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1 ; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to update $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->update($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry - Update Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + + +############################################################################# +# returns 1 if the entry to migrate and the current entry are different one another + +sub hasChanged { + my $entry = shift; + my $typeOfEntry = shift; + my $DN = $entry->getDN(1); + my $newEntry = searchEntry($DN,1); + return 1 if (! $newEntry); # we shoudn't be in that case ... + # do the stuff to check wether the entry has changed or not given its type + if (($typeOfEntry eq "DEFAULT_INDEX") || ($typeOfEntry eq "INDEX")){ + my @indexTypes = $entry->getValues("nsIndexType"); + my @newIndexTypes = $newEntry->getValues("nsIndexType"); + my @nsmatchingrules = $entry->getValues("nsmatchingrule"); + my @newMatchingRules = $newEntry->getValues("nsmatchingrule"); + return 1 if (Diffs(\@indexTypes, \@newIndexTypes)); + return 1 if (Diffs(\@nsmatchingrules,\@newMatchingRules)); + return 0; + } + if ($typeOfEntry eq "CHANGELOG5"){ + printTrace("\nCheck wether changelog has changed or not",3); + my @params = keys(%changelog5params); + foreach $param (@params){ + my @values = $entry->getValues($param); + my @newValues = $newEntry->getValues($param); + return 1 if (Diffs(\@values,\@newValues)); + } + return 0; + } + if ($typeOfEntry eq "SNMP"){ + foreach $param (@SNMPparams){ + my @values = $entry->getValues($param); + my @newValues = $newEntry->getValues($param); + return 1 if (Diffs(\@values,\@newValues)); + } + return 0; + } + # we don't know how to compare such type of entry => just return 1 + return 1 ; +} + +sub isAsystemIndex { + my $index = shift; + return ($index->hasValue("nsSystemIndex","true",1)); +} + + +sub updatePathInPluginArgs { + my $plugin = shift; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $cont = 1; + my $Unix_oldDir = ${oldDir} ; + my $Unix_root = ${root} ; + grep { s@\\@/@g } $Unix_oldDir if $isNT; + grep { s@\\@/@g } $Unix_root if $isNT; + while ($cont) { + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + $_ = $plugin->{$arg}[0] ; + s@$Unix_oldDir@$Unix_root@ig ; + s/$type-$oldname/$type-$newname/ig ; + $plugin->setValues($arg, $_) ; + } + else { + $cont = 0 ; + } + $argNum++; + } + return $plugin; +} + + +sub Diffs { + my $valuesToMigrate = shift; + my $currentValues = shift; + return 1 if (getDiff(\@{$valuesToMigrate},\@{$currentValues})); + return 1 if (getDiff(\@{$currentValues},\@{$valuesToMigrate})); + return 0 ; +} + +sub getDiff { + # we get references to arrays + my $elements = shift ; + my $existing_elements = shift ; + my %count = () ; + my %countEE = () ; + @diff = () ; + foreach $e (@{$elements}, @{$existing_elements}) { $count{$e}++ ;} + foreach $e (@{existing_elements}) { $countEE{$e}++ ;} + foreach $e (@{$elements}) { + # if $e is only present in @$elements, we push it to the diff array + if (($count{$e} == 1) && ($countEE{$e} == 0)) { + push @diff, $e ; + } + } + return @diff ; +} + +sub registerSuffix_Backend { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldBackends{$suffix} = $CN; +} + + +############################################################################# +# # +# # +# # +############################################################################# +sub migrateLDBM_backend_instances { + foreach $entry (@LDBM_backend_instances) { + my $DN = $entry->getDN(1); # 1 is to normalize the DN + my $CN = $entry->{cn}[0]; + my $expLdif; + my $confirm = "No"; + my $dest = "$serverHome${PATHSEP}db_backup" ; + my $newSlapdExecDir = "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + + if ($DN =~/cn=netscaperoot,cn=ldbm database/i){ + printTrace("\n\n*** INFORMATION - NetscapeRoot is NOT migrated",0); + } + else { + if(alreadyExistsInNew($entry)){ + printMsg("\n\n*** LDBM_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will overwrite existing database"); + printMsg("\nDo you want to continue Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("Do you want to export the existing data Yes/No [Yes] ?"); + my $answer = <STDIN> ; + if (!($answer =~ /n|no/i)) { + mkdir $dest, 0700 unless (-d $dest); + $expLdif = "$dest${PATHSEP}$CN.ldif"; + while (!($confirm =~ /y|yes/i)) { + printMsg("\nEnter the full pathname of the file [$expLdif]:") ; + $answer = <STDIN> ; + chomp($expLdif = $answer) unless ($answer eq "\n"); + printMsg("\nExisting data will be exported under $expLdif"); + printMsg("\nContinue Yes/No [No] ?"); + $confirm = <STDIN>; + } + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($newSlapdExecDir) or die "\nCould not change directory to $newSlapdExecDir: $!\n"; + printTrace("\nNow backing up database $CN in $expLdif\n",0); + &stopServer($root,'slapd-'.$newname); + &db2Ldif($expLdif, $CN, $serverHome); + &startServer() unless (isDirectoryAlive()); + } + push @BACKENDS, $CN; + } else { + printMsg("\n*** Migration will not update it"); + break; + } + } else { + printTrace("\nWe should add the backend instance $DN",3); + my $suffixarg = "nsslapd-suffix" ; + my $suffixname= $entry->{$suffixarg}[0] ; + my $newEntry = $conn->newEntry() ; + $newEntry->setDN($DN); + $newEntry->setValues("objectclass", "top", "extensibleObject", "nsBackendInstance" ); + $newEntry->setValues("cn", $CN ); + $newEntry->setValues($suffixarg, $suffixname); + my @params = keys(%LDBMparamToMigrate); + foreach $param (@params) { + my @values = $entry->getValues($param); + $newEntry->setValues($param, @values) if (@values); + } + if (addEntryToNew($newEntry, "LDBM_BACKEND_INSTANCE",1)) { + push @BACKENDS, $CN; + } + } + } + } +} + +sub parseLDBM_backend_instance { + my $entry = shift; + ®isterSuffix_Backend($entry); + push @LDBM_backend_instances, $entry; +} + +############################################################################# +sub migrateMappingTree { + foreach $entry (@Mapping_tree_entries) { + my $DN = $entry->getDN(1); # 1 si to normalize the DN + if ($DN =~/cn=\"o=netscaperoot\",cn=mapping tree,cn=config/i){ + # DO NOTHING + } + else { + if(alreadyExistsInNew($entry)){ + printMsg("\n\n*** MAPPING_TREE - $DN already exists"); + printMsg("\n*** Migration will not add the suffix"); + } + else { + addEntryToNew($entry, "MAPPING_TREE",1); + } + } + } +} + + +sub parseMapping_tree{ + my $entry = shift; + push @Mapping_tree_entries, $entry; +} + +############################################################################# +sub migrateDefaultIndexes { + foreach $index (@default_indexes) { + my $CN = $index->{cn}[0]; + my $newIndex ; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)})) { + if ($newIndex = alreadyExistsInNew($index)) { + if (! isAsystemIndex($newIndex)) { + updateEntry($index, "DEFAULT_INDEX", 1, 2); + } + } + else { + addEntryToNew($index, "DEFAULT_INDEX", 2); + } + } + } +} + + +sub parseDefaultIndex{ + my $index = shift; + push @default_indexes, $index; +} + +############################################################################# + +sub migrateIndexes { + foreach $index (@indexes) { + my $CN = $index->{cn}[0]; + my $newIndex; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)}) && (DN !~ /cn=netscaperoot,cn=index/i)){ + if ($newIndex = alreadyExistsInNew($index)) { + if (! isAsystemIndex($newIndex)) { + updateEntry($index, "INDEX", 1, 2); + } + } + else { + addEntryToNew($index, "INDEX", 2); + } + } + } +} + +sub parseIndex{ + my $index = shift; + push @indexes, $index; +} + +############################################################################# + +sub newLDIFplugin { + my $currentPlugin = shift; + my $DN = $currentPlugin->getDN(1); + my $newPlugin = $conn->newEntry() ; + $newPlugin->setDN($DN); + foreach $Attr (@pluginAttrs) { + my @values = $currentPlugin->getValues($Attr); + $newPlugin->setValues($Attr, @values) if (@values); + } + return $newPlugin; +} + +sub migrateStdPlugin{ + my $plugin = shift; + my $DN = $plugin->getDN(1); + my $pluginEnable = "nsslapd-pluginEnabled"; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $currentPlugin ; + if ($currentPlugin = alreadyExistsInNew($plugin, 1)) { + $plugin = updatePathInPluginArgs($plugin); + my $pluginEnableValue = $plugin->{$pluginEnable}[0]; + my $cont = 1; + my $pluginHasChanged = 0; + my $newPlugin = &newLDIFplugin($currentPlugin); + if (! $currentPlugin->hasValue($pluginEnable,$pluginEnableValue,1)){ + $newPlugin->setValues($pluginEnable, $pluginEnableValue); + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + while($cont){ + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + my @values = $plugin->getValues($arg); + my $value = $values[0] ; + $newPlugin->setValues($arg, $value) if (@values); + if ($currentPlugin->exists($arg)) { + if (! $currentPlugin->hasValue($arg,$value,1)) { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + if ($currentPlugin->exists($arg)) { + # Just Warn the user. Do nothing. + printTrace("\nCompared to the old instance, the current new plugin $DN belongs this attribute: $arg",2); + } + else { + $cont = 0 ; + } + } + $argNum++; + } + updateEntry($newPlugin, "STANDARD_PLUGIN", 0, 1) if ($pluginHasChanged); + } +} + +############################################################################# + +sub migrateConfig_Node{ + my $config_node = shift; + my @params = keys(%GeneralSrvParamToMigrate); + my $hasChanged = 0; + my $newConfigNode; + if ($newConfigNode = alreadyExistsInNew($config_node, 1)){ + foreach $param (@params) { + if ($config_node->exists($param)){ + my @valuesToMigrate = $config_node->getValues($param); + if (@valuesToMigrate){ + if ($newConfigNode->exists($param)){ + my @currentValues = $newConfigNode->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newConfigNode->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + else { + $newConfigNode->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + } + } + updateEntry($newConfigNode, "CONFIG_NODE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateConfig_LDBM_database{ + my $config_ldbm = shift; + my @params = keys(%GlobalConfigLDBMparamToMigrate); + my $hasChanged = 0; + my $newConfigLdbm ; + if ($newConfigLdbm = alreadyExistsInNew($config_ldbm, 1)) { + foreach $param (@params) { + if ($config_ldbm->exists($param)){ + my @valuesToMigrate = $config_ldbm->getValues($param); + if (@valuesToMigrate){ + if ($newConfigLdbm->exists($param)){ + my @currentValues = $newConfigLdbm->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newConfigLdbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $newConfigLdbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($newConfigLdbm, "CONFIG_LDBM_DATABASE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateChainingBE_config{ + my $chaining_config = shift; + my $DN = $chaining_config->getDN(1); + my @params = (); + my $hasChanged = 0; + my $newChainingConfig; + if ($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i){ + $newChainingConfig = searchEntry("cn=config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingConfigParams); + } + if ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i){ + $newChainingConfig = searchEntry("cn=default instance config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingDefaultInstanceConfigParams); + } + foreach $param (@params) { + if ($chaining_config->exists($param)){ + my @valuesToMigrate = $chaining_config->getValues($param); + if (@valuesToMigrate){ + printTrace("\nParam: $param values To migrate: @valuesToMigrate",3); + if ($newChainingConfig->exists($param)){ + my @currentValues = $newChainingConfig->getValues($param); + printTrace("\nParam: $param new current values: @currentValues",3); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newChainingConfig->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $newChainingConfig->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($newChainingConfig, "CHAINING_BACKEND_CONFIG", 0, 1) if ($hasChanged); +} + +############################################################################# + +sub registerSuffix_ChainingBE { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldChainingBackends{$suffix} = $CN; +} + +sub storeMultiplexorBindDN { + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + if ($chaining_instance->exists("nsMultiplexorBindDN")){ + my $bindDN = $chaining_instance->{nsMultiplexorBindDN}[0]; + my $newBindDN = searchEntry($bindDN); + if (! $newBindDN){ + # the bindDN entry doesn't yet exist in new => it will have to be migrated + $MultiplexorBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in new + } + } + +} + +sub importMultiplexorBindDNEntries { + # import all entries present in @MultiplexorBindDNEntriesToMigrate in new + my @MultiplexorBindDNs = keys (%MultiplexorBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + foreach $bindDN (@MultiplexorBindDNs) { + printTrace("\nimportMultiplexorBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # check wether the backend has been imported in new or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the binf DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to new + $conn->close if ($conn); +} + +sub migrateChainingBE_instance{ + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + ®isterSuffix_ChainingBE($chaining_instance); + if (alreadyExistsInNew($chaining_instance)) { + # already exists + printMsg("\n\n*** CHAINING_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($chaining_instance, "nsmultiplexorcredentials"); + addEntryToNew($chaining_instance, "CHAINING_BACKEND_INSTANCE", 1); + storeMultiplexorBindDN($chaining_instance); + } +} + +############################################################################# + +# create a new LDIF representation of a new replica consumer +sub newLDIFreplica { + my $replica = shift; + my $DN = $replica->getDN(1); + my $newReplica = $conn->newEntry() ; + my $MASTER_OR_MULTIMASTER = "3" ; + $newReplica->setDN($DN); + foreach $Attr (@nsds5replicaAttrs) { + my @values = $replica->getValues($Attr); + $newReplica->setValues($Attr, @values) if (@values); + } + my $replicaType = $replica->{nsDS5ReplicaType}[0]; + if ($replicaType eq $MASTER_OR_MULTIMASTER) { + my @nsState = $replica->getValues("nsState"); + $newReplica->setValues("nsState", @nsState); + } + else { + $newReplica->setValues("nsDS5ReplicaId", $replicaIdvalue); + } + return $newReplica; +} + +sub MigrateNSDS5_replica{ + foreach $replica (@new6replicas) { + my $DN = $replica->getDN(1); + my $newReplica; + my @removeAttrs = qw(nsstate nsds5replicaname nsds5replicachangecount); + for (@removeAttrs) { + $replica->remove($_); + } + if (alreadyExistsInNew($replica)) { + # replica already exists + printMsg("\n\n*** NSDS5_REPLICA - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + $newReplica = &newLDIFreplica($replica); + addEntryToNew($newReplica, "NSDS5_REPLICA", 1); + } + storeReplicaBindDN($replica); + } +} + +sub parseNSDS5_replica{ + my $replica = shift; + push @new6replicas, $replica; +} + +sub storeReplicaBindDN { + my $replica = shift; + my $DN = $replica->getDN(1); + if ($replica->exists("nsDS5ReplicaBindDN")){ + my $bindDN = $replica->{nsDS5ReplicaBindDN}[0]; + my $newBindDN = searchEntry($bindDN); + if (! $newBindDN){ + # the bindDN entry doesn't yet exist in new => it will have to be migrated + $ReplicaBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in new + } + } +} + + +sub importReplicaBindDNEntries { + # import all entries present in @ReplicaBindDNEntriesToMigrate in new + my @ReplicaBindDNs = keys (%ReplicaBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + my $replBind_entry = ""; + my @bindDN_elements = ""; + my $bindDN_parent = ""; + my $parentBind_entry = ""; + foreach $bindDN (@ReplicaBindDNs) { + printTrace("\nimportReplicaBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # If backend is from config, read the entry from dse.ldif and add to new - NGK + if ($backendtoExportFrom eq "cn=config") { + my $norm_bindDN = normalizeDN($bindDN); + @bindDN_elements = ldap_explode_dn($norm_bindDN, 0); +# @bindDN_elements = split(/,/,$norm_bindDN); + my $junk = shift(@bindDN_elements); + if ($#bindDN_elements >= 1) { + $bindDN_parent = normalizeDN(join(",", @bindDN_elements)); + } + printTrace("\nOpening DSE.ldif",3); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF); + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1); + if ($DN eq $norm_bindDN) { + $replBind_entry = $entry; + } + if ($bindDN_parent ne "") { + if ($DN eq $bindDN_parent) { + $parentBind_entry = $entry; + } + } + } + close(DSELDIF); + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + if ($bindDN_parent ne "") { + addEntryToNew($parentBind_entry, BINDDN_PARENT, 0); + } + printTrace("\nAdding BindDN with addEntryToNew",3); + addEntryToNew($replBind_entry, BINDDN, 0); + } else { + # check wether the backend has been imported in new or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the bind DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to new + $conn->close if ($conn); +} + +sub alreadyMigrated { + my $backendToCheck = shift; + foreach $backend (@BACKENDS) { + return 1 if ($backend eq $backendToCheck); + } + return 0 ; +} + +sub belongsSuffix { + my $suffix = shift; + my $bindDN = shift; + return ($bindDN =~ /$suffix\s*$/i); +} + +sub length { + my $suffix = shift; + my $count = 0; + while ($suffix =~ /./g) { + $count++; + } + return $count ; +} + +sub getBackendtoExportFrom { + my $bindDN = shift ; + my $sizeOfSuffix = 0 ; + my $NULL = ""; + my @oldSuffixes = keys(%oldBackends); + my @oldChainingSuffixes = keys(%oldChainingBackends); + my $bindDN_backend = $NULL; + my $config = "cn=config"; + + my $norm_bindDN = normalizeDN($bindDN); + # Check if bindDN exists in cn=config - NGK + if (belongsSuffix($config,$norm_bindDN)) { + $bindDN_backend = $config; + printTrace("\ngetBackendtoExportFrom: bindDN_backend: $bindDN_backend",3); + } else { + foreach $suffix (@oldSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is: $suffix",3); + if ((belongsSuffix($suffix,$norm_bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $oldBackends{$suffix}; + printTrace("\ngetBackendtoExportFrom: bindDN_backend: $bindDN_backend, sizeOfSuffix: $sizeOfSuffix",3); + } + } + foreach $suffix (@oldChainingSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is a chained suffix: $suffix",3); + if ((belongsSuffix($suffix,$norm_bindDN)) && (length($suffix) > $sizeOfSuffix)) { + printMsg("\n\n*** Entry stored on a remote backend - $norm_bindDN"); + printMsg("\n*** We don't migrate it"); + return $NULL; + } + } + } + return $bindDN_backend; +} + + +sub getBackendtoImportTo { + my $bindDN = shift; + my $sizeOfSuffix = 0; + my $NULL = ""; + my $suffixArg = "nsslapd-suffix"; + my $bindDN_backend = $NULL; + open( DSELDIF, "< $DSEldif" ) || die "Can't open $DSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + my $suffix = $entry->{$suffixArg}[0]; + if ((belongsSuffix($suffix,$bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $entry->{cn}[0]; + } + } + } + close(DSELDIF); + return $bindDN_backend ; +} + + +sub ExportAndAddEntry { + my $DN = shift; + my $backendtoExportFrom = shift; + my $ldif_dir = shift; + my $ldif = "$ldif_dir${PATHSEP}$backendtoExportFrom.ldif" ; + # first: export entry pointed out by the $DN to $ldif file + $ENV{"$LIB_PATH"}=$old_libpath; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "\ncan't create $ldif_dir to store temporary ldif files\n"; + } + chdir($oldSlapdExecDir) or die "\nCould not change directory to $oldSlapdExecDir: $!\n"; + &db2Ldif($ldif, $backendtoExportFrom, $oldHome, $DN); + chdir($curdir) or die "\nCould not change directory to $curdir: $!\n"; + + # then: Add it to new + if (! $conn) { + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + } + open( BINDDNLDIF, "< $ldif" ) || die "\nCan't open $ldif: $!: \n"; + my $in = new Mozilla::LDAP::LDIF(*BINDDNLDIF) ; + while ($entry = readOneEntry $in) { + my $entryDN = $entry->getDN(1); + if ($DN eq $entryDN) { + addEntryToNew($entry, "nsds5ReplicaBindDN", 0); + } + } + close(BINDDNLDIF); + # remove the ldif file after the import + unlink($ldif) ; +} + +############################################################################# +sub MigrateNSDS_replication_agreement { + foreach $replicationAgreement (@replicationAgreements) { + my $DN = $replicationAgreement->getDN(1); + if (alreadyExistsInNew($replicationAgreement)){ + # replication agreement already exists + printMsg("\n\n*** NSDS_REPLICATION_AGREEMENT - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($replicationAgreement, "nsDS5ReplicaCredentials"); + addEntryToNew($replicationAgreement, "NSDS_REPLICATION_AGREEMENT", 1); + } + } +} + + +sub parseNSDS_replication_agreement{ + my $replicationAgreement = shift; + push @replicationAgreements, $replicationAgreement ; +} + +############################################################################# + +sub migrateChangelog5{ + my $changelog = shift; + my $DN = $changelog->getDN(1); + my $changelogdir = "nsslapd-changelogdir"; + if (alreadyExistsInNew($changelog)){ + # cn=changelog5,cn=config already exists in new + my $newChangelog = searchEntry($DN); + my @newChangelogdir = $newChangelog->getValues($changelogdir); + $changelog->setValues($changelogdir, @newChangelogdir); + updateEntry($changelog, "CHANGELOG5", 0, 1); + } + else { + # cn=changelog5,cn=config need to be created in new. + # the changelogdir value must be setup to <new_root_server>/slapd-instance/changelogdb + $changelog->setValues($changelogdir,"${serverHome}${PATHSEP}changelogdb"); + addEntryToNew($changelog, "CHANGELOG5", 1); + } +} + + +sub migrateChangelog { + my $oldchangelogdir = ""; + my $newchangelogdir = ""; + my $changelogdir = "nsslapd-changelogdir"; + my $CL5DN = "cn=changelog5,cn=config"; + printTrace("\n\n***** Migrate Changelog...",0,1); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF); + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "CHANGELOG5"){ + $oldchangelogdir = ($entry->getValues($changelogdir))[0]; + } + } + close(DSELDIF); + if ($oldchangelogdir) { + # If using olddatadir to migrate from, the path of the changelogdb + # from the dse.ldif may not match the path where the old server + # root was archived. We may need to modify oldchangelogdir so the + # copy of the changelog files succeeds. + unless(-e $oldchangelogdir) { + if($olddatadir) { + my @cldbpath = split(/\//,$oldchangelogdir); + until($cldbpath[0] =~/^slapd-/) { + shift(@cldbpath); + } + my $tmpcldbpath = join(${PATHSEP}, @cldbpath); + $oldchangelogdir = "$oldDir${PATHSEP}$tmpcldbpath"; + } + # If oldchangelogdir still looks to be wrong, prompt for the + # location instead of just failing on the copydir operation + # and bombing out of the migration. + unless(-e $oldchangelogdir) { + print("\n\nThe old changelog directory \"$oldchangelogdir\" doesn't exist. Please enter the correct path: "); + $oldchangelogdir = <STDIN>; + } + } + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + my $newChangelog = searchEntry($CL5DN); + $newchangelogdir = ($newChangelog->getValues($changelogdir))[0]; + stopServer($root,'slapd-'.$newname); + printTrace("\ncopying $oldchangelogdir${PATHSEP}* to $newchangelogdir",3); + copyDir("$oldchangelogdir","$newchangelogdir"); + + # We need to modify the DBVERSION file for a new verision of the db + open(DBVERSION,">$newchangelogdir${PATHSEP}DBVERSION") || die "Can't overwrite $newchangelogdir${PATHSEP}DBVERSION: $! "; + print DBVERSION "Changelog5/NSMMReplicationPlugin/3.0"; + close(DBVERSION); + + &startServer() unless (isDirectoryAlive()); + } +} + +############################################################################# + +sub migrateReplication{ + my $replication = shift; + my $DN = $replication->getDN(1); + if (alreadyExistsInNew($replication)){ + # replication agreement already exists + printMsg("\n\n*** $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + addEntryToNew($replication, "REPLICATION", 1); + } +} + +############################################################################# + +sub migrateSecurity{ + my $security = shift; + if ($entry->hasValue("objectClass", "nsEncryptionConfig")) { + my $certfile = "alias/slapd-" . $newname . "-cert8.db"; + my $keyfile = "alias/slapd-" . $newname. "-key3.db"; + $entry->setValues("nsCertfile",$certfile) if ! $entry->hasValue("nsCertfile",$certfile); + $entry->setValues("nsKeyfile",$keyfile) if ! $entry->hasValue("nsKeyfile",$keyfile); + } + if (alreadyExistsInNew($security)){ + # already exists in new + updateEntry($security, "SECURITY", 0, 1); + } + else { + addEntryToNew($security, "SECURITY", 1); + } +} + +############################################################################# + +sub migrateSNMP{ + my $snmp = shift; + if (alreadyExistsInNew($snmp)){ + # already exists in new + updateEntry($snmp, "SNMP", 0, 1); + } + else { + addEntryToNew($snmp, "SNMP", 1); + } +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + my $sep = shift ; + + if ($sep) { + print "\n-------------------------------------------------------------------------"; + print LOGFILE "\n-------------------------------------------------------------------------"; + } + + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg ; + } +} + +############################################################################# +# this subroutine implements a very stupid version of diff + +sub diff { + my $f1 = shift; + my $f2 = shift; + my $lineToBeginWith = shift; + my $NULL = "" ; + my $diff_f1 = $NULL ; + my $diff_f2 = $NULL ; + my $retval = $NULL ; + my $ret; + open(F1, "$f1") or die "Could not open file $f1"; + open(F2, "$f2") or close(F1), die "Could not open file $f2"; + + while (defined($l1 = <F1>)) { + if ($lineToBeginWith){ + $lineToBeginWith -- ; + next ; + } + next if ($l1 =~ /^\#/); + $ret = defined($l2 = <F2>); + if ($ret) { + $ret = defined($l2 = <F2>) while ($ret && ($l2 =~ /^\#/)) ; + if ($ret) { + if (!($l1 eq $l2)) { + + # ignore whitespace + $l1_clean = $l1 ; + $l2_clean = $l2 ; + $l1_clean =~ s/\s//g; + $l2_clean =~ s/\s//g; + + if (!($l1_clean eq $l2_clean)) { + $diff_f1 .= "${l1}" unless ($l1_clean eq $NULL); + $diff_f2 .= "${l2}" unless ($l2_clean eq $NULL); + } + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + + while (defined($l2 = <F2>)) { + if (($l2 =~ /^\#/) || ($l2 =~ /^\s*$/)) { + next ; + } + else { + $diff_f2 .= "${l2}" ; + } + } + + close(F1); + close(F2); + + $retval .= "- differences present in your config file but not in standard file:\n\n". "$diff_f1\n" if ($diff_f1) ; + $retval .= "- differences present in standard file but not in your config file:\n\n" . "$diff_f2" if ($diff_f2) ; + return $retval ; +} + +sub CompareStdConfigFiles { + # Compare each configuration file against its default version. If it has changed, + # notify the user that the file has changed and will need to be checked by the + # user. This should be safe to do because there should be no path information + # stored in these conf files, which are just schema stuff. + # printTrace("\nCheck if standard configuration files have changed",3); + + # get the version of the DS to migrate + ($oldVersion, $oldMinor) = &getVersion($oldDir, $oldversionstr); + # get the version of the new DS + ($Version, $Minor) = &getVersion($root); + + # get old LIB_PATH + $old_libpath = &getLibPath($oldDir, $oldVersion, $oldMinor); + # get new LIB_PATH + $new_libpath = &getLibPath($root, $Version, $Minor); + + my $origFilePath = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}install${PATHSEP}schema${PATHSEP}" ; + my $FilesChanged = ""; + my $AllDiffs = "***********************************************************************"; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(CONFDIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldConfDir: $!"; + + foreach $file (readdir(CONFDIR)) { + $origFile = $origFilePath . $file ; + $configFile = $oldSchemaDir . $file ; + if (( exists($stdIncludes{lc($file)})) && (-f $origFile)) { + $diffs = &diff($configFile, $origFile, $lineToBegin); + $lineToBegin = 0 if $lineToBegin ; + if ($diffs) { + $FilesChanged .= "\n$configFile"; + $AllDiffs .= "\n$configFile is different than the standard configuration file" ; + $AllDiffs .= "\nYou will need to check this file and make sure its changes are compatible "; + $AllDiffs .= "with the new directory server\nHere are the differences:\n"; + $AllDiffs .= "$diffs \n\n"; + $AllDiffs .= "***********************************************************************"; + } + else { + $NoChanges .= "\n$configFile"; + } + } + } + closedir(CONFDIR); + +if ($FilesChanged) { + printTrace("\nNo changes to old configuration files:$NoChanges",3) ; + printTrace("\n***********************************************************************",3) ; + printMsg("\nThe following standard files have been modified: $FilesChanged"); + if ($NO_INPUT_USER) { + # do nothing + } + else { + printMsg("\nDo you want to see the differences Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("$AllDiffs"); + } + printMsg("\nDo you want to continue the migration Yes/No [No] ?"); + $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + } + } +} + + + +############################################################################# + +# this is used to run the system() call, capture exit and signal codes, +# and die() upon badness; the first argument is a directory to change +# dir to, if any, and the rest are passed to system() +sub mySystem { + my $rc = &mySystemNoDie(@_); + my ($dir, @args) = @_; + if ($rc == 0) { +# success + } elsif ($rc == 0xff00) { + die "Error executing @args: error code $rc: $!"; + } elsif ($rc > 0x80) { + $rc >>= 8; + die "Error executing @args: error code $rc: $!"; + } else { + if ($rc & 0x80) { + $rc &= ~0x80; + } + die "Error executing @args: received signal $rc: $!"; + } + + # usually won't get return value + return $rc; +} + +# This version does not die but just returns the error code +sub mySystemNoDie { + my ($dir, @args) = @_; + if ($dir && ($dir ne "")) { + chdir($dir) or die "Could not change directory to $dir: $!"; + } + my $cmd = $args[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = 0xffff & system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd @fixargs\n"; + $rc = 0xffff & system {$cmd} @fixargs; + } + chdir(${curdir}) or die "Could not change directory to $curdir: $!"; + return $rc; +} + +########################################################################################### +# # +# Export/Import of the backends in @BACKENDS # +# # +########################################################################################### + +sub manydb2Ldif { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}=$old_libpath; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "can't create $ldif_dir to store temporary ldif files"; + } + chdir($oldSlapdExecDir) or die "Could not change directory to $oldSlapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &db2Ldif($ldif, $backend, $oldHome); + } + print " Done.\n"; + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + +sub db2Ldif { + my $ldif = shift ; + my $backend = shift ; + my $home = shift ; + my $include_suffix = shift ; + my $db2ldif_param ; + if ($include_suffix) { + $db2ldif_param = "db2ldif -r -D $home -n $backend -a $ldif -s \"$include_suffix\""; + } + else { + $db2ldif_param = "db2ldif -r -D $home -n $backend -a $ldif"; + } + open(DB2LDIF, "${quote}${quote}$slapdExecName${quote} $db2ldif_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + my $ii = 0; + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + printMsg($_); + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the 5.x slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif) { + chown( $newuid, $newgid, $ldif) or printMsg("\nUnable to change the ownership of $ldif to $localuser") ; + } + } +} + +sub manyLdif2db { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($slapdExecDir) or die "Could not change directory to $slapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &Ldif2db($ldif, $backend); + } + # remove the empty ldif directory + # but not if using the data dir + if (!$olddatadir) { + rmdir($ldif_dir); + } + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + + +sub Ldif2db { + my $ldif = shift ; + my $backend = shift ; + my $ldif2db_param = "ldif2db -D $serverHome -n $backend -i $ldif"; + open(LDIF2DB, "${quote}${quote}$slapdExecName${quote} $ldif2db_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + while (<LDIF2DB>) { + printMsg($_); + } + close(LDIF2DB); + # remove the ldif file after the import + # but not if using the data dir + if (!$olddatadir) { + unlink($ldif) ; + } +} + + +########################################################################################### +# # +# Running/Stopping the Server # +# # +########################################################################################### + + + +sub isDirectoryAlive { + die "\n Migration aborted. Make sure your old and new Directory Servers are installed on the same machine \n" if ( $LDAPservername == -1 ); + my $test_conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd); + if ($test_conn) { + $test_conn->close(); + return 1; + } + else { + return 0 ; + } +} + + +sub startServer { + my $instanceDir = ${serverHome} ; + my $errLog = $instanceDir . $PATHSEP . 'logs' . $PATHSEP . 'errors'; + # emulate tail -f + # if the last line we see does not contain "slapd started", try again + my $done = 0; + my $started = 0; + my $code = 0; + my $lastLine = ""; + my $timeout = time + 240; # 4 minutes + $ENV{"$LIB_PATH"}=$new_libpath; + + my $startCmd = $instanceDir . $PATHSEP . 'start' . $script_suffix; + if (! -f $startCmd) { + $startCmd = $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix; + } + $code = &mySystem($instanceDir,$startCmd); + open(IN, $errLog) or die "Could not open error log $errLog: $!"; + my $pos = tell(IN); + while (($done == 0) && (time < $timeout)) { + for (; ($done == 0) && ($_ = <IN>); $pos = tell(IN)) { + $lastLine = $_; + # print; + # the server has already been started and shutdown once . . . + if (/slapd started\./) { + $started++; + if ($started == 2) { + $done = 1; + } + # sometimes the server will fail to come up; in that case, restart it + } elsif (/Initialization Failed/) { + # print "Server failed to start: $_"; + $code = &mySystem($instanceDir, $startCmd); + # sometimes the server will fail to come up; in that case, restart it + } elsif (/exiting\./) { + # print "Server failed to start: $_"; + #$code = &mySystem($startCmd); + $code = &mySystem($instanceDir, $startCmd); + } + } + if ($lastLine =~ /PR_Bind/) { + # server port conflicts with another one, just report and punt + print $lastLine; + print "This server cannot be started until the other server on this\n"; + print "port is shutdown.\n"; + $done = 1; + } + if ($done == 0) { + # rest a bit, then . . . + sleep(2); + # . . . reset the EOF status of the file desc + seek(IN, $pos, 0); + } + } + close(IN); + + sleep(5); + die "\nUnable to start the $Version.$Minor Directory Server\n" unless (isDirectoryAlive()); + + return 0; +} + +sub stopServer { + my $root = shift; + my $name = shift; + $maxStopIterations = 5; + print "\nShutting down server $name . . .\n"; + $ENV{"$LIB_PATH"}=$new_libpath; + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote; + if (! -f $stopCmd) { + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote; + } + + if (! -f $stopCmd) { + # no stop command, probably a 1.X system; for NT, we'll try net stop + # for unix, we'll get the pid and kill it + if ($isNT) { + $stopCmd = 'net stop ' . $name; + } else { + # see if there is a pid file + $pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' . + $PATHSEP . 'pid'; + if (open(PIDFILE, $pidfile)) { + chomp($pid = <PIDFILE>); + close(PIDFILE); + while ($maxStopIterations-- && !$exitCode) { + $exitCode = kill(15, $pid); + } + $stopCmd = undef; + } + } + } + + # keep looping until the stop cmd returns an error code, which usually + # means that what ever we want to stop is stopped, or some other error + # occurred e.g. permission, or no such service + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + while ($stopCmd && $maxStopIterations-- && $exitCode) { + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + } + + if (!$maxStopIterations) { + print "Warning: could not shutdown the server: $!\n"; + } + sleep(10) ; + $exitCode = 0; +} + + +sub runAndIgnoreOutput { + my $cmd = shift; + printMsg("."); + open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!"; + printMsg("."); + sleep(1); # allow pipe to fill with data + printMsg("."); + while (<RUNCMD>) { +# print; + } + my $code = close(RUNCMD); +# print "runAndIgnore: code=$code status=$?\n"; + return $?; +} + +############################################################################# +# migrate SSL info + +sub MigrateSSL { + my $secPwd = 'bidon' ; + # copy the SSL directory + ©Dir("$oldHome${PATHSEP}ssl","$serverHome${PATHSEP}ssl") if (-d "$oldHome${PATHSEP}ssl"); + # copy the cert db and key files + if ( -d "$oldDir${PATHSEP}alias") { + $aliasDir = "$root${PATHSEP}alias"; + if (! -d $aliasDir) { + mkdir($aliasDir, 0750); + } + &stopServer($root,'slapd-'.$newname); + my $keydb = "$aliasDir${PATHSEP}slapd-$newname-key3.db" ; + my $certdb = "$aliasDir${PATHSEP}slapd-$newname-cert8.db" ; + my $certdb7 = "$aliasDir${PATHSEP}slapd-$newname-cert7.db" ; + my $old_keydb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ; + my $old_certdb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db"; + my $keydb_backup = "$aliasDir${PATHSEP}slapd-$newname-key3.db_backup" ; + my $certdb_backup = "$aliasDir${PATHSEP}slapd-$newname-cert7.db_backup" ; + if (-f $old_keydb) { + if (-f $keydb) { + if ($NO_INPUT_USER) { + printMsg("\n$keydb already exists. backup in $keydb_backup ..."); + ©BinFile($keydb,$keydb_backup); + ©BinFile($old_keydb,$keydb); + } + else { + print("\n\n$keydb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_keydb,$keydb); + } + } + } + else { + ©BinFile($old_keydb,$keydb); + } + } + if (-f $old_certdb) { + $mode = (stat($old_certdb))[2] if $PRESERVE; + if (-f $certdb) { + if ($NO_INPUT_USER) { + printMsg("\n$certdb already exists. backup in $certdb_backup ..."); + ©BinFile($certdb,$certdb_backup); + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + else { + print("\n\n$certdb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + } + } + else { + ©BinFile($old_certdb,$certdb7); + } + } + # copy the old password file + if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") { + ©BinFile( + "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt", + "$aliasDir${PATHSEP}$type-$newname-pin.txt" + ); + } + &startServer(); + if ($PRESERVE) { + chown($newuid,$newgid,$certdb) || print "Failed to set uid $newuid gid $newgid on $certdb : $!\n"; + chmod($mode,$certdb) || print "Failed to set mode $mode on $certdb : $!\n"; + } + } + +} + +sub DisableSSL { + my $entry = $conn->search("cn=config","base","objectclass=*"); + my $LDAPparam = "nsslapd-security" ; + my $Value = "off" ; + if ($entry->{$LDAPparam}[0] ne $Value) { + printTrace("\nDisable SSL...",1); + $entry->setValues($LDAPparam, $Value); + } + my $res = $conn->update($entry); + if ($res) { + printTrace("\nSSL disabled",2); + } + else { + printMsg("\nCan't disabled SSL. The server may have problems to start"); + } +} + +# enable the migration of client authentication informations +sub MigrateCertmap { + # backup the old certmap.conf and replace it with the new one + my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"; + my $newCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf" ; + my $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + if (-f $oldCertmap) { + if ($NO_INPUT_USER) { + printMsg("\n$newCertmap has been backup in $backupCertmap"); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up the file $newCertmap [$backupCertmap] ?") ; + my $Answer = <STDIN> ; + $backupCertmap = $Answer if ($Answer ne "\n"); + chomp($backupCertmap); + printTrace("\nDest: .$backupCertmap.",4); + if (-e $backupCertmap) { + printMsg("\n\n$backupCertmap already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup file: $newCertmap in $backupCertmap",4); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + } + else { + } +} + +sub hasChangedoldCertmap { + my $certmapfile = shift ; + my @reference = ("certmap default default", + "default:DNComps", + "default:FilterComps e") ; + my $cpt = 0 ; + printTrace("\nhasChangedoldCertmap",3); + open(CERTMAP,"< $certmapfile"); + while (<CERTMAP>) { + if ((! /^\s*#/) && (! /^\s*$/)) { + my $ref = $reference[$cpt] ; + printTrace("\nValue: $_, ref: $ref",4); + if (! /^\s*$ref\s*$/) { + return 1 ; + } + else { + $cpt++ ; + } + } + } + close (CERTMAP); + printTrace("\ncpt: $cpt",4); + if ($cpt < $#reference) { + return 1 ; + } + else { + return 0 ; + } +} + + +########################################################################################### +# # +# Copy directory and files functions # +# # +########################################################################################### + + +sub copyDir { + my $src = shift; + my $dest = shift; + my $exclude = shift; + + opendir( SRC, $src ) or die "Can't open directory $src: $!: "; + my $mode; + my $uid; + my $gid; + mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest ); + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + local ( @files ) = readdir ( SRC ); + closedir( SRC ); + for ( @files ) { + if ( $_ eq "." || $_ eq ".." ) { + next; + } elsif ( $exclude && /$exclude/ ) { + next; + } elsif( -d "$src${PATHSEP}$_") { + ©Dir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); + } else { + ©BinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_"); + } + } +} + +sub copyBinFile { + my $src = shift; + my $dest = shift; + my $buf = ""; + my $bufsize = 8192; + + open( SRC, $src ) || die "Can't open $src: $!\n"; + # if we are given a directory destination instead of a file, extract the + # filename portion of the source to use as the destination filename + if (-d $dest) { + $dest = $dest . $PATHSEP . &basename($src); + } + open( DEST, ">$dest" ) || die "Can't create $dest: $!\n"; + binmode SRC; + binmode DEST; + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + while (read(SRC, $buf, $bufsize)) { + print DEST $buf; + } + close( SRC ); + close( DEST ); +} + +############################################################################################################# +# backup 5.x configuration files # +# backup the directory <root_server5>/slapd-instance/config dans <root_server5>/slapd-instance/BackupConfig # # +# # +############################################################################################################# + + +sub backupConfigFiles { + # backup the 5.x config files + my $src = "$serverHome${PATHSEP}config" ; + my $dest = "$serverHome${PATHSEP}config_backup" ; + if ($NO_INPUT_USER) { + printMsg("\n$src has been backup in $dest"); + ©Dir($src,$dest); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up your configuration directory [$dest] ?") ; + my $Answer = <STDIN> ; + $dest = $Answer if ($Answer ne "\n"); + chomp($dest); + printTrace("\nDest: .$dest.",4); + if (-e $dest) { + printMsg("\n\n$dest already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $dest = "$serverHome${PATHSEP}config_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup Directory: $src in $dest",4); + ©Dir($src,$dest); + } +} +############################################################################# + +sub getLDAPservername { + my $oldLDAPservername; + my $LDAPservername; + my $localhost = "nsslapd-localhost"; + open(OLDDSELDIF, "< $oldDSEldif") or die "\nError: could not open old config file $oldDSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*OLDDSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $oldLDAPservername = $values[0]; + printTrace("\nName of the old LDAP server: $oldLDAPservername",3); + } + break; + } + } + close(OLDSELDIF); + + open( DSELDIF, "< $DSEldif" ) || die "\nCan't open $DSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $LDAPservername = $values[0]; + printTrace("\nName of the new LDAP server: $LDAPservername",3); + } + break; + } + } + close(DSELDIF); + # check ol and new Directory Instance are installed on the same physical machine. + if (lc($oldLDAPservername) ne lc($LDAPservername)) { + # warn the user he tries to migrate a 4.x server installed on a different machine from the 5.x one + printMsg("\n\nYour old instance is on $oldLDAPservername, whereas your new instance is on $LDAPservername. Migration on different machines is not supported. Do you want to continue ? Yes/No [No]:") ; + if (! (<STDIN> =~ /yes|y/i)) { + return -1; + } + } + return $LDAPservername ; +} + +############################################################################# + +sub getLibPath { + my $myDir = shift; + my $myVersion = shift; + my $myMinor = shift; + + if ($isNT) { + return $ENV{"$LIB_PATH"}; + } + if (($myVersion >= 6) && ($myMinor >= 2)) { + return + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + } else { + return "$myDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + } +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $versionstr = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + if (!$versionstr) { # version not specified on cmd line - find it + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $cur_libpath=$ENV{"$LIB_PATH"}; + $ENV{"$LIB_PATH"}= + "$dir${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory/ || /^iPlanet-Directory/i) { + $versionstr = $_; + last; + } + } + $code = close(F); + # print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}=$cur_libpath; + } + + if ($versionstr =~ /^Netscape-Directory\/(\d+)\.(\d+)(?:b\d)*\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } elsif ($versionstr =~ /(\d+)\.(\d+)/) { + $version = $1; + $minor = $2; + } + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################################### +sub normalizeDir { + my $dir = shift ; + my $dir_prec = "" ; + while ($dir_prec ne $dir) { + $dir_prec = $dir ; + if ($isNT) { + grep { s@\\\\@\\@g } $dir ; + } + else { + grep { s@//@/@g } $dir ; + } + } + return $dir ; +} + + +############################################################################################### + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################################### +# get uid and group id of the 5.x slapd server. +# The uid is done through the nsslapd-localuser attribute + +sub getuid_gid { + my $newuid ; + my $newgid ; + my $localuser ; + my $localuser_attr = "nsslapd-localuser" ; + if (! $isNT) { + &startServer() unless (isDirectoryAlive()); + my $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; + my $entry = $conn->search("cn=config ", "base","objectclass=*", 0, ($localuser_attr)) ; + # Tests wether we succeed to get the entry cn=config + die "\nCan't get the entry cn=config \n" unless ($entry); + my @values = $entry->getValues($localuser_attr); + $conn->close(); + if ($#values == -1 || ($values[0] eq "") ) { # tests wether the nsslapd-localuser attribute has a value + printMsg("\nNo localuser has been found in the configuration of the directory. "); + if ($NO_INPUT_USER) { + printMsg("\nWe considered nobody as the localuser"); + $localuser = "nobody" ; + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\nUnder what user does your $Version.$Minor directory server run [nobody] ? ") ; + $localuser = <STDIN> ; + chomp($localuser); + $localuser = "nobody" if ($localuser eq ""); + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + if ($newuid) { + $Ask = 0 ; + } + else { + printMsg("\nError: $localuser is unknown from the system "); + } + } + } + } + else { + $localuser = $values[0]; # returns the first value (we should only have one localuser) + my $size = $#values ; + } + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + return ($localuser, $newuid, $newgid) ; + } + else { + return () ; + } +} + +sub getolduid_gid { + my $oldlocaluser ; + my $localuserAttr = "nsslapd-localuser"; + my $entry ; + if (! $isNT) { + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "CONFIG_NODE") { + $oldlocaluser = $entry->{$localuserAttr}[0] if ($entry->exists($localuserAttr)); + break ; + } + } + close(DSE); + ($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ; + return ($oldlocaluser, $olduid, $oldgid) ; + } + else { + return (); + } +} +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} + +################################ +# Need to migrate the credential. +# If the credential can not be migrated, leave it at it is +################################ +sub migrate_credential{ + my $entry_to_modify = shift; + my $credentials_attr = shift; + my @old_value = $entry_to_modify->getValues($credentials_attr); + my $migratecredExecName = 'migratecred'; + my $credOldHome = $oldHome; + my $credServerHome = $serverHome; + + if ($isNT) { + # oldHome may be pointing to the archived copy of the + # instance dir which may be different than the path that + # the instance was originally installed as on Windows. If + # this path is not the original install path, then the + # credential will not be migrated correctly. We should + # prompt the user on Windows for the correct path. + + print "\n\nThe old instance path must be the same as where it was"; + print "\ninitially installed, not where it was archived in order"; + print "\nfor this step to succeed. Please verify that the path"; + print "\nis correct. Note that case sensitivity is important here."; + print "\n\nOld Instance Directory: $credOldHome"; + print "\nIs this correct? (y/n): "; + chomp(my $answer = <STDIN>); + if (!($answer =~ /y|yes/i)) { + print "\nPlease enter the correct path for the old instance directory: "; + chomp($credOldHome = <STDIN>); + } + + print "\n\nThe new instance path must also be correct for this step"; + print "\nto succeed. Please verify that the path is correct. Note"; + print "\nthat case sensitivity is important here."; + print "\n\nNew Instance Directory: $credServerHome"; + print "\nIs this correct? (y/n): "; + chomp(my $answer = <STDIN>); + if (!($answer =~ /y|yes/i)) { + print "\nPlease enter the correct path for the new instance directory: "; + chomp($credServerHome = <STDIN>); + } + } +# print "\nMigratecred command is: ${quote}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}$migratecredExecName${quote} -o $credOldHome -n $credServerHome -c @old_value\n"; + + my @new_cred = `${quote}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}$migratecredExecName${quote} -o $credOldHome -n $credServerHome -c @old_value`; + + if ( $? == 0 ) + { + $entry_to_modify->setValues($credentials_attr, @new_cred); + } +} + diff --git a/ldap/admin/src/scripts/template-migrate5to7 b/ldap/admin/src/scripts/template-migrate5to7 new file mode 100644 index 00000000..50aead79 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrate5to7 @@ -0,0 +1,3043 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Migrate a 5.x directory server to a 7.0 directory server + +####################################################################################################### +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Entry; +use Mozilla::LDAP::LDIF; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::localtime; +use File::Basename; +use Class::Struct ; + +####################################################################################################### + +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o oldInstancePath -n newInstancePath [-t tracelevel] [-L logfile]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - new 7.0 Directory Manager\n"); + print(STDERR " : -w password - new 7.0 Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for new 7.0 Directory Manager's password\n"); + print(STDERR " : -j filename - Read new 7.0 Directory Manager's password from file\n"); + print(STDERR " : -p port - new 7.0 Directory Server port\n"); + print(STDERR " : -o oldInstancePath - Path of the old instance to migrate \n"); + print(STDERR " : -n newInstancePath - Path of the new 7.0 instance\n"); + print(STDERR " : [-d dataPath] - Path to directory containing data files to import into new instance\n"); + print(STDERR " : [-v oldVersion] - Version of old instance (obtained by running $slapdExecName -v\n"); + print(STDERR " : [-t tracelevel] - (optional) specify the level of trace (0..3)\n"); + print(STDERR " : [-L logfile] - (optional) specify the file to log the migration report \n"); + } +######################################################################################################## + +BEGIN { + + require 'uname.lib' ; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + ${SEP} = $isNT ? ";" : ":" ; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + + # If this variable is set, all file/directory creation will make sure the mode + # and ownership of the destination is the same as the source + $PRESERVE = 1 if (!$isNT); + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + if ($isNT) { + $os = "WINNT"; + } else { + $os = &uname("-s"); + } + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + } + if ( $os eq "AIX" ) { + $dll_suffix = "_shr.a"; + } + elsif ( $os eq "HP-UX" ) { + $dll_suffix = ".sl"; + } + elsif ( $os eq "WINNT" ) { + $dll_suffix = ".dll"; + } + else { + $dll_suffix = ".so"; + } + $slapdExecName = $isNT ? 'slapd.exe' : './ns-slapd'; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; +} + +SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + + # old parameters + ${oldDir} = "" ; + ${oldname} = "" ; + ${oldHome} = "" ; + ${oldConfDir} = "" ; + ${oldlocaluser} ; + ${olduid} ; + ${oldgid} ; + + # new parameters + ${root} = "{{DS-ROOT}}" ; + ${type} = "" ; + ${newname} = "" ; + ${newport} = "" ; + ${rootDN} = "" ; + ${rootpwd} = "" ; + ${localhost} = "" ; + ${LogFileReport} = "" ; + ${newuid} ; + ${localuser} ; + ${newgid} ; + $NO_INPUT_USER = 0 ; # by default user can give inputs during the migration process + ${curdir} = getCwd(); + ${slapdExecDir} = "${root}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + # in 7.0 the replica Id is setup to a static value + $replicaIdvalue = 65535; + + # specify the level of trace + $TRACELEVEL=1; + + $LDAP_SERVER_UNREACHABLE = 81; + + # get input users + &getParameters() ; + ${oldDir} = &normalizeDir("${oldDir}"); + ${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; + ${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; + ${oldSchemaDir} = "${oldConfDir}schema${PATHSEP}"; + ${oldDSEldif} = "${oldConfDir}dse.ldif"; + ${serverHome} = "${root}${PATHSEP}$type-$newname" ; + ${schemaDir} = "$serverHome${PATHSEP}config${PATHSEP}schema${PATHSEP}"; + ${DSEldif} = "$serverHome${PATHSEP}config${PATHSEP}dse.ldif"; + ${ldif_rep} = "${oldConfDir}ldif${PATHSEP}" ; + ${oldSlapdExecDir} = "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + open(LOGFILE, ">> $LogFileReport"); + + printTrace("\noldDir: $oldDir, oldHome: $oldHome, \noldConfDir: $oldConfDir, \nldif_rep: $ldif_rep, \nrootDN: $rootDN, \nPort: $newport, \nNewname: $newname\n",3); + printTrace("\nLIB_PATH: $LIB_PATH",4); + + if (!(-d $serverHome)) { + printMsg("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + printMsg("\n$oldHome doesn't exist\n"); + exit(1); + } + + if ($olddatadir && !(-d $olddatadir)) { + print("\n$olddatadir doesn't exist\n"); + exit(1); + } + + +%HashParametersName = (); + +# The following hash displays only general server parameters to migrate under cn=config +%GeneralSrvParamToMigrate = ( + 'nsslapd-accesscontrol'=> '\n', + 'nsslapd-errorlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-logging-enabled'=> '\n', + 'nsslapd-auditlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-level'=> '\n', + 'nsslapd-accesslog-logbuffering'=> '\n', + 'nsslapd-accesslog-logexpirationtime'=> '\n', + 'nsslapd-accesslog-logexpirationtimeunit'=> '\n', + 'nsslapd-accesslog-logmaxdiskspace'=> '\n', + 'nsslapd-accesslog-logminfreediskspace'=> '\n', + 'nsslapd-accesslog-logrotationtime'=> '\n', + 'nsslapd-accesslog-logrotationtimeunit'=> '\n', + 'nsslapd-accesslog-maxlogsize'=> '\n', + 'nsslapd-accesslog-maxLogsPerDir'=> '\n', + 'nsslapd-attribute-name-exceptions'=> '\n', + 'nsslapd-auditlog-logexpirationtime'=> '\n', + 'nsslapd-auditlog-logexpirationtimeunit'=> '\n', + 'nsslapd-auditlog-logmaxdiskspace'=> '\n', + 'nsslapd-auditlog-logminfreediskspace'=> '\n', + 'nsslapd-auditlog-logrotationtime'=> '\n', + 'nsslapd-auditlog-logrotationtimeunit'=> '\n', + 'nsslapd-auditlog-maxlogsize'=> '\n', + 'nsslapd-auditlog-maxLogsPerDir'=> '\n', + 'nsslapd-certmap-basedn'=> '\n', + 'nsslapd-ds4-compatible-schema'=> '\n', + 'nsslapd-enquote-sup-oc'=> '\n', + 'nsslapd-errorlog-level'=> '\n', + 'nsslapd-errorlog-logexpirationtime'=> '\n', + 'nsslapd-errorlog-logexpirationtimeunit'=> '\n', + 'nsslapd-errorlog-logmaxdiskspace'=> '\n', + 'nsslapd-errorlog-logminfreediskspace'=> '\n', + 'nsslapd-errorlog-logrotationtime'=> '\n', + 'nsslapd-errorlog-logrotationtimeunit'=> '\n', + 'nsslapd-errorlog-maxlogsize'=> '\n', + 'nsslapd-errorlog-maxlogsperdir'=> '\n', + 'nsslapd-groupevalnestlevel'=> '\n', + 'nsslapd-idletimeout'=> '\n', + 'nsslapd-ioblocktimeout'=> '\n', + 'nsslapd-lastmod'=> '\n', + 'nsslapd-listenhost'=> '\n', + 'nsslapd-maxdescriptors'=> '\n', + 'nsslapd-nagle'=> '\n', + 'nsslapd-readonly'=> '\n', + 'nsslapd-referralmode'=> '\n', + 'nsslapd-plugin-depends-on-name'=> '\n', + 'nsslapd-plugin-depends-on-type'=> '\n', + 'nsslapd-referral'=> '\n', + 'nsslapd-reservedescriptors'=> '\n', + 'nsslapd-rootpwstoragescheme'=> '\n', + 'nsslapd-schemacheck'=> '\n', + 'nsslapd-secureport'=> '\n', + 'nsslapd-security'=> '\n', + 'nsslapd-sizelimit'=> '\n', + 'nsslapd-ssl3ciphers'=> '\n', + 'nsslapd-timelimit'=> '\n', + 'passwordchange'=> '\n', + 'passwordchecksyntax'=> '\n', + 'passwordexp'=> '\n', + 'passwordhistory'=> '\n', + 'passwordinhistory'=> '\n', + 'passwordlockout'=> '\n', + 'passwordlockoutduration'=> '\n', + 'passwordmaxage'=> '\n', + 'passwordmaxfailure'=> '\n', + 'passwordminage'=> '\n', + 'passwordminlength'=> '\n', + 'passwordmustchange'=> '\n', + 'passwordresetfailurecount' => '\n', + 'passwordstoragescheme' => '\n', + 'passwordunlock' => '\n', + 'passwordwarning' => '\n' +); + +# the following hash displays global parameters related to database stored under cn=config,cn=ldbm database,cn=plugins,cn=config +%GlobalConfigLDBMparamToMigrate = ( + 'nsslapd-allidsthreshold' => '\n', + 'nsslapd-lookthroughlimit' => '\n', + 'nsslapd-mode' => '\n', + 'nsslapd-dbcachesize' => '\n', + 'nsslapd-cache-autosize' => '\n', + 'nsslapd-cache-autosize-split' => '\n', + 'nsslapd-db-transaction-logging' => '\n', + 'nsslapd-import-cachesize' => '\n' +); + +# the following hash displays specific parameters to each backends and stored under cn=DBname,cn=ldbm database,cn=plugins,cn=config +%LDBMparamToMigrate = ( + 'nsslapd-cachesize' => '\n', + 'nsslapd-cachememsize' => '\n', + 'nsslapd-readonly' => '\n', + 'nsslapd-require-index' => '\n' +); + + +%ChainingConfigParams = ( + 'nsactivechainingcomponents' => '\n', + 'nstransmittedcontrols' => '\n' + ); + +%ChainingDefaultInstanceConfigParams = ( + 'nsabandonedsearchcheckinterval' => '\n', + 'nsbindconnectionslimit' => '\n', + 'nsbindtimeout' => '\n', + 'nsbindretrylimit' => '\n', + 'nshoplimit' => '\n', + 'nsmaxresponsedelay' => '\n', + 'nsmaxtestresponsedelay' => '\n', + 'nschecklocalaci' => '\n', + 'nsconcurrentbindlimit' => '\n', + 'nsconcurrentoperationslimit' => '\n', + 'nsconnectionlife' => '\n', + 'nsoperationconnectionslimit' => '\n', + 'nsproxiedauthorization' => '\n', + 'nsreferralonscopedsearch' => '\n', + 'nsslapd-sizelimit' => '\n', + 'nsslapd-timelimit' => '\n' +); + +%changelog5params = ( + 'nsslapd-changelogmaxage' => '\n', + 'nsslapd-changelogmaxentries' => '\n' + ); + +@SNMPparams = ( + 'nssnmpenabled', + 'nssnmporganization', + 'nssnmplocation', + 'nssnmpcontact', + 'nssnmpdescription', + 'nssnmpmasterhost', + 'nssnmpmasterport', + 'nssnmpenabled', + 'aci' + ); + +%stdIncludes = ( + "." => "\n", + ".." => "\n", + "30ns-common.ldif " => "\n", + "50ns-mail.ldif " => "\n", + "50ns-news.ldif" => "\n", + "50iplanet-servicemgt.ldif"=> "\n", + "50netscape-servicemgt.ldif"=> "\n", + "50ns-mcd-browser.ldif" => "\n", + "50ns-proxy.ldif" => "\n", + "00core.ldif" => "\n", + "50ns-admin.ldif" => "\n", + "50ns-mcd-config.ldif " => "\n", + "50ns-value.ldif" => "\n", + "05rfc2247.ldif" => "\n", + "50ns-calendar.ldif" => "\n", + "50ns-mcd-li.ldif" => "\n", + "50ns-wcal.ldif" => "\n", + "05rfc2927.ldif" => "\n", + "50ns-certificate.ldif" => "\n", + "50ns-mcd-mail.ldif" => "\n", + "50ns-web.ldif" => "\n", + "10rfc2307.ldif" => "\n", + "50ns-compass.ldif" => "\n", + "50ns-media.ldif" => "\n", + "20subscriber.ldif" => "\n", + "50ns-delegated-admin.ldif"=> "\n", + "50ns-mlm.ldif" => "\n", + "25java-object.ldif" => "\n", + "50ns-directory.ldif" => "\n", + "50ns-msg.ldif" => "\n", + "28pilot.ldif" => "\n", + "50ns-legacy.ldif" => "\n", + "50ns-netshare.ldif" => "\n" +); + + +# Backends migrated (Backend CN attribute value) +@BACKENDS = () ; +# All pairs of suffix-backend are registered in this hashtable +%oldBackends = () ; + +#store the backend instances to migrate +@LDBM_backend_instances = (); + +#store the mapping tree +@Mapping_tree_entries = (); + +#store the suffix and the associated chaining backend +%oldChainingBackends = (); + +#store the multiplexor bind entries to migrate +%MultiplexorBindDNEntriesToMigrate = (); + +#store the Replica bind DN entries to migrate +%ReplicaBindDNEntriesToMigrate = (); + +# list of standard plugins +%stdPlugins = ( + "7-bit check" => "\n", + "acl plugin" => "\n", + "acl preoperation" => "\n", + "binary syntax" => "\n", + "case exact string syntax" => "\n", + "case ignore string syntax" => "\n", + "chaining database" => "\n", + "class of service" => "\n", + "country string syntax" => "\n", + "distinguished name syntax" => "\n", + "generalized time syntax" => "\n", + "integer syntax" => "\n", + "internationalization plugin" => "\n", + "ldbm database" => "\n", + "legacy replication plugin" => "\n", + "multimaster replication plugin" => "\n", + "octet string syntax" => "\n", + "clear" => "\n", + "crypt" => "\n", + "ns-mta-md5" => "\n", + "sha" => "\n", + "ssha" => "\n", + "postal address syntax" => "\n", + "referential integrity postoperation" => "\n", + "retro changelog plugin" => "\n", + "roles plugin" => "\n", + "telephone syntax" => "\n", + "uid uniqueness" => "\n", + "uri syntax" => "\n" + ); + +# list of indexes that have disappeared from the new schema compared to 5.0 +%deniedIndexes = ( + 'dncomp' => "\n" +); + +@default_indexes = (); +@indexes = (); + +# list of user added Plugin's. In 7.0, they 'll need to be recompiled +@badPlugins = () ; + +@pluginAttrs = ( + "objectclass", + "cn", + "nsslapd-pluginpath", + "nsslapd-plugininitfunc", + "nsslapd-plugintype", + "nsslapd-pluginenabled", + "nsslapd-plugin-depends-on-type", + "nsslapd-pluginid", + "nsslapd-pluginversion", + "nsslapd-pluginvendor" + ); + +@nsds5replicaAttrs = ( + 'objectclass', + 'nsDS5ReplicaRoot', + 'nsDS5ReplicaType', + 'nsDS5ReplicaLegacyConsumer', + 'nsDS5flags', + 'nsDS5ReplicaId', + 'nsDS5ReplicaPurgeDelay', + 'nsDS5ReplicaBinddn', + 'cn', + 'nsDS5ReplicaReferral' + ); + +# array of replicas to migrate +@new6replicas = (); + +# array of replication agreements to migrate +@replicationAgreements = (); + +# compare LDIF standard config files with standard ones +CompareStdConfigFiles() ; +die "\n\n The version of product you want to migrate is not a 5.x Directory Server\n" unless ($oldVersion == 5) ; + +# Shutdown the legacy Directory instance +printTrace("\nShutdown the legacy Directory Server instance: ${oldHome}",0); +&stopServer($oldDir, 'slapd-'.$oldname); + +# get the hostname of the new LDAP server +my $LDAPservername = &getLDAPservername(); + +# get the uid and gid of the 7.0 slapd user +($localuser, $newuid, $newgid) = getuid_gid(); +# get the uid and gid of the 5.x slapd user +($oldlocaluser, $olduid, $oldgid) = getolduid_gid(); +printTrace("\n7.0 localuser: $localuser, uid: $newuid, gid: $newgid",2); +printTrace("\n5.x localuser: $oldlocaluser, uid: $olduid, gid: $oldgid",2); + +# backup 7.0 configuration files in <6server_root>/slapd-instancename/config +printTrace("\nBackup $serverHome${PATHSEP}config on $serverHome${PATHSEP}config_backup ...",0); +&backupConfigFiles(); + +# migrate the schema (need to stop and start the 7.0 server) +printTrace("\nMigrate the schema...",0); +MigrateSchema(); + +# start the server unless it is already started +&startServer() unless (isDirectoryAlive()); + +############### Connect to the 7.0 LDAP Directory Server ###################### +$ENV{"$LIB_PATH"} = $new_libpath; + +die "\n Migration aborted. Make sure your old and new Directory Server are installed on the same machine \n" if ( $LDAPservername == -1 ); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + +# Cconnection to 7.0 LDAP server is successful ! +printTrace("\nConnected to $Version.$Minor LDAP server",0) ; + +# Parse the main configuration file: dse.ldif +printTrace("\n\nParse the old DSE ldif file: $oldDSEldif *****",0, 1); +printTrace("\nThis may take a while ...\n",0); +&MigrateDSEldif(); + +#migrate LDBM backend instances +printTrace("\n\nMigrate LDBM backend instances...",0,1); +&migrateLDBM_backend_instances(); + +#migrate mapping tree entries +printTrace("\n\nMigrate mapping tree...",0,1); +&migrateMappingTree(); + +#migrate default indexes +printTrace("\n\nMigrate default indexes...",0,1); +migrateDefaultIndexes(); + +#migrate indexes +printTrace("\n\nMigrate indexes...",0,1); +migrateIndexes(); + +#migrate replicas +printTrace("\n\nMigrate replicas...",0,1); +&MigrateNSDS5_replica(); + +#migrate replication agreements +printTrace("\n\nMigrate replication agreements...",0,1); +&MigrateNSDS_replication_agreement(); + +#migrate key/cert databases +printTrace("\n\nMigrate key/cert databases...",0,1); +&MigrateSSL(); + +# migrate certmap.conf +printTrace("\n\nMigrate Certmap.conf...",0,1); +&MigrateCertmap() ; + +################## Close the connection to 7.0 LDAP Server ##################### +printTrace("\n\n***** Close the LDAP connection to the new Directory Server instance ***** ",0); +$conn->close; + + +################## stop the new instance and Export/Import the data, restart the server ################## +if (@BACKENDS) { + &stopServer($root,'slapd-'.$newname); + if ($olddatadir) { + printTrace("\nData already contained in $olddatadir...\n",0,1) ; + $ldif_rep = "$olddatadir${PATHSEP}"; + } else { + printTrace("\nData processing...\n",0,1) ; + # migrate data for each backend: 5.x -> LDIF files + &manydb2Ldif($ldif_rep); + } + + # migrate LDIF data to the new database: LDIF -> New + &manyLdif2db($ldif_rep); + &migrateChangelog(); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); + &startServer() unless (isDirectoryAlive()); +} +else { + printTrace("\nINFORMATION - There are no non-standard or non-already existing suffixes to migrate\n",0); + &migrateChangelog(); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); +} + +printMsg("\n\n ****** End of migration ******\n\n"); + +close(LOGFILE); + + +########################################################################################### +# get input users +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # new instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe new instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # new DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-d") { # old instance LDIF data dir + if (! $olddatadir ) { + $olddatadir = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-v") { # old version + if (! $oldversionstr ) { + $oldversionstr = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + $NO_INPUT_USER = 1 ; + } elsif ("$ARGV[$i]" eq "-L") { # migration logfile + $LogFileReport = $ARGV[++$i] ; + } + else { + print("\nThe option $ARGV[$i] is not recognized"); + &usage() ; + exit(1); + } + $i++; + } + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + if ($exit) { + &usage() ; + exit(1); + } + +} + +################################################################################################### + +sub MigrateSchema{ + my $FilesChanged = ""; + my $AllDiffs = ""; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(SCHEMADIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldSchemaDir: $!"; + + foreach $file (readdir(SCHEMADIR)) { + if (! exists($stdIncludes{lc($file)})) { + my $newSchemaFile = $schemaDir . $file; + if (-f $newSchemaFile ) { + # The ldif file already exists. Make a diff and warn the user if different. + if (diff($newSchemaFile, $oldSchemaDir.$file)) { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $newSchemaFile); + } + } + else { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $newSchemaFile); + } + } + } + closedir(SCHEMADIR); + if ($AllDiffs) { + printMsg("\n\n***********************************************************************"); + printMsg("\nThe following LDIF files have been migrated:"); + printMsg("$AllDiffs"); + printMsg("\n*************************************************************************\n\n"); + } + &startServer() if (! isDirectoryAlive()); +} + + +################################################################################################### +# This subroutine is used to parse the dse.ldif file and call specific routines to act with entries +sub MigrateDSEldif { + printTrace("\nMigrate DSE entries...",1); + my $tempoAlreadyDone = 0; + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + SWITCH: { + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + parseLDBM_backend_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "MAPPING_TREE"){ + parseMapping_tree($entry); + last SWITCH; + } + if ($typeOfEntry eq "DEFAULT_INDEX"){ + parseDefaultIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "INDEX"){ + parseIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "STANDARD_PLUGIN"){ + migrateStdPlugin($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_NODE"){ + migrateConfig_Node($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_LDBM_DATABASE"){ + migrateConfig_LDBM_database($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_CONFIG"){ + migrateChainingBE_config($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_INSTANCE"){ + migrateChainingBE_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS5_REPLICA"){ + parseNSDS5_replica($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS_REPLICATION_AGREEMENT"){ + parseNSDS_replication_agreement($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHANGELOG5"){ + migrateChangelog5($entry); + last SWITCH; + } + if ($typeOfEntry eq "REPLICATION"){ + migrateReplication($entry); + last SWITCH; + } + if ($typeOfEntry eq "SECURITY"){ + migrateSecurity($entry); + last SWITCH; + } + if ($typeOfEntry eq "SNMP"){ + migrateSNMP($entry); + last SWITCH; + } + } + + } + close(DSELDIF); +} + +############################################################################# +# returns the "type of an entry". If the entry is not to be migrated its type is "NOT_MIGRATED_TYPE" + +sub getTypeOfEntry{ + my $entry = shift; + my $DN = $entry->getDN(1) ; # 1 is to normalize the returned DN + if (($DN =~ /cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "LDBM_BACKEND_INSTANCE"; + } + if (($DN =~ /cn=mapping tree,cn=config$/i) && (isObjectclass($entry,"nsMappingTree"))) { + return "MAPPING_TREE"; + } + if (($DN =~ /cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsIndex"))) { + return "DEFAULT_INDEX"; + } + if (isObjectclass($entry,"nsIndex")) { + return "INDEX"; + } + if ((isObjectclass($entry,"nsSlapdPlugin")) && (isStdPlugin($entry))) { + return "STANDARD_PLUGIN"; + } + if ($DN =~ /^cn=config$/i) { + return "CONFIG_NODE"; + } + if ($DN =~ /^cn=config,cn=ldbm database,cn=plugins,cn=config$/i) { + return "CONFIG_LDBM_DATABASE"; + } + if (($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i) || ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i)){ + return "CHAINING_BACKEND_CONFIG"; + } + if (($DN =~ /cn=chaining database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "CHAINING_BACKEND_INSTANCE"; + } + if (isObjectclass($entry,"nsDS5Replica")) { + return "NSDS5_REPLICA"; + } + if (isObjectclass($entry,"nsDS5ReplicationAgreement")) { + return "NSDS_REPLICATION_AGREEMENT"; + } + if ($DN =~ /^cn=changelog5,cn=config$/i) { + return "CHANGELOG5"; + } + if (($DN =~ /cn=replication,cn=config$/i) && ($DN !~ /^cn=replication,cn=config$/i)) { + return "REPLICATION"; + } + if ($DN =~ /cn=encryption,cn=config$/i) { + return "SECURITY"; + } + if ($DN =~ /^cn=SNMP,cn=config$/i) { + return "SNMP"; + } + return "NOT_MIGRATED_TYPE"; +} + +############################################################################# + + + +############################################################################# +# returns 1 if the objectclass given in parameter is present in the objectclasses values of the entry +# given in parameter, 0 else + +sub isObjectclass { + my $entry = shift; + my $objectclass = shift; + return ($entry->hasValue("objectclass",$objectclass,1)); +} + +############################################################################# + +sub isStdPlugin { + my $entry = shift; + my $CN = $entry->{cn}[0]; + if (isObjectclass($entry,"nsSlapdPlugin")) { + return 1 if ($stdPlugins{lc($CN)}); + } + return 0; +} + + +############################################################################# + +sub alreadyExistsInNew{ + my $entry = shift; + my $mustExist = shift; + my $DN = $entry->getDN(1); # 1 to normalize the DN + # We have a name change of "uid uniqueness" plugin in DS6.x + # to "attribute uniqueness" + $DN =~ s/uid\ uniqueness/attribute\ uniqueness/ if ($DN =~ /uid\ uniqueness/); + return searchEntry($DN, $mustExist); +} + +############################################################################# +sub searchEntry { + my $DN = shift; + my $mustExist = shift; + my $res = $conn->search($DN, "base", "objectclass=*"); + my $cpt = 5; + if ($res) { + return $res; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to search $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->search($DN, "base", "objectclass=*"); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + return $res ; + } + elsif (($errorCode eq $LDAP_SERVER_UNREACHABLE) || ($mustExist)) { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** Failed to search: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + } + return 0; + } +} + + +############################################################################# + +sub addEntryToNew{ + my $entry = shift; + my $typeOfEntry = shift; + my $trace = shift; + my $res = $conn->add($entry); + my $DN = $entry->getDN(1); + my $cpt = 5; + if ($res) { + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to add $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->add($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry: Add Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + +############################################################################# + +sub updateEntry{ + my $entry = shift; + my $typeOfEntry = shift; + my $CHECK = shift; + my $trace = shift; + my $cpt = 5; + if ($CHECK) { + if (! hasChanged($entry, $typeOfEntry)) { + return 1; + } + } + my $res = $conn->update($entry); + my $DN = $entry->getDN(1); + if ($res) { + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1 ; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to update $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->update($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry - Update Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + + +############################################################################# +# returns 1 if the entry to migrate and the current entry are different one another + +sub hasChanged { + my $entry = shift; + my $typeOfEntry = shift; + my $DN = $entry->getDN(1); + my $newEntry = searchEntry($DN,1); + return 1 if (! $newEntry); # we shoudn't be in that case ... + # do the stuff to check wether the entry has changed or not given its type + if (($typeOfEntry eq "DEFAULT_INDEX") || ($typeOfEntry eq "INDEX")){ + my @indexTypes = $entry->getValues("nsIndexType"); + my @newIndexTypes = $newEntry->getValues("nsIndexType"); + my @nsmatchingrules = $entry->getValues("nsmatchingrule"); + my @newMatchingRules = $newEntry->getValues("nsmatchingrule"); + return 1 if (Diffs(\@indexTypes, \@newIndexTypes)); + return 1 if (Diffs(\@nsmatchingrules,\@newMatchingRules)); + return 0; + } + if ($typeOfEntry eq "CHANGELOG5"){ + printTrace("\nCheck wether changelog has changed or not",3); + my @params = keys(%changelog5params); + foreach $param (@params){ + my @values = $entry->getValues($param); + my @newValues = $newEntry->getValues($param); + return 1 if (Diffs(\@values,\@newValues)); + } + return 0; + } + if ($typeOfEntry eq "SNMP"){ + foreach $param (@SNMPparams){ + my @values = $entry->getValues($param); + my @newValues = $newEntry->getValues($param); + return 1 if (Diffs(\@values,\@newValues)); + } + return 0; + } + # we don't know how to compare such type of entry => just return 1 + return 1 ; +} + +sub isAsystemIndex { + my $index = shift; + return ($index->hasValue("nsSystemIndex","true",1)); +} + + +sub updatePathInPluginArgs { + my $plugin = shift; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $cont = 1; + my $Unix_oldDir = ${oldDir} ; + my $Unix_root = ${root} ; + grep { s@\\@/@g } $Unix_oldDir if $isNT; + grep { s@\\@/@g } $Unix_root if $isNT; + while ($cont) { + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + $_ = $plugin->{$arg}[0] ; + s@$Unix_oldDir@$Unix_root@ig ; + s/$type-$oldname/$type-$newname/ig ; + $plugin->setValues($arg, $_) ; + } + else { + $cont = 0 ; + } + $argNum++; + } + return $plugin; +} + + +sub Diffs { + my $valuesToMigrate = shift; + my $currentValues = shift; + return 1 if (getDiff(\@{$valuesToMigrate},\@{$currentValues})); + return 1 if (getDiff(\@{$currentValues},\@{$valuesToMigrate})); + return 0 ; +} + +sub getDiff { + # we get references to arrays + my $elements = shift ; + my $existing_elements = shift ; + my %count = () ; + my %countEE = () ; + @diff = () ; + foreach $e (@{$elements}, @{$existing_elements}) { $count{$e}++ ;} + foreach $e (@{existing_elements}) { $countEE{$e}++ ;} + foreach $e (@{$elements}) { + # if $e is only present in @$elements, we push it to the diff array + if (($count{$e} == 1) && ($countEE{$e} == 0)) { + push @diff, $e ; + } + } + return @diff ; +} + +sub registerSuffix_Backend { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldBackends{$suffix} = $CN; +} + + +############################################################################# +# # +# # +# # +############################################################################# +sub migrateLDBM_backend_instances { + foreach $entry (@LDBM_backend_instances) { + my $DN = $entry->getDN(1); # 1 is to normalize the DN + my $CN = $entry->{cn}[0]; + my $expLdif; + my $confirm = "No"; + my $dest = "$serverHome${PATHSEP}db_backup" ; + my $newSlapdExecDir = "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + + if ($DN =~/cn=netscaperoot,cn=ldbm database/i){ + printTrace("\n\n*** INFORMATION - NetscapeRoot is NOT migrated",0); + } + else { + if(alreadyExistsInNew($entry)){ + printMsg("\n\n*** LDBM_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will overwrite existing database"); + printMsg("\nDo you want to continue Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("Do you want to export the existing data Yes/No [Yes] ?"); + my $answer = <STDIN> ; + if (!($answer =~ /n|no/i)) { + mkdir $dest, 0700 unless (-d $dest); + $expLdif = "$dest${PATHSEP}$CN.ldif"; + while (!($confirm =~ /y|yes/i)) { + printMsg("\nEnter the full pathname of the file [$expLdif]:") ; + $answer = <STDIN> ; + chomp($expLdif = $answer) unless ($answer eq "\n"); + printMsg("\nExisting data will be exported under $expLdif"); + printMsg("\nContinue Yes/No [No] ?"); + $confirm = <STDIN>; + } + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($newSlapdExecDir) or die "\nCould not change directory to $newSlapdExecDir: $!\n"; + printTrace("\nNow backing up database $CN in $expLdif\n",0); + &stopServer($root,'slapd-'.$newname); + &db2Ldif($expLdif, $CN, $serverHome); + &startServer() unless (isDirectoryAlive()); + } + push @BACKENDS, $CN; + } else { + printMsg("\n*** Migration will not update it"); + break; + } + } else { + printTrace("\nWe should add the backend instance $DN",3); + my $suffixarg = "nsslapd-suffix" ; + my $suffixname= $entry->{$suffixarg}[0] ; + my $newEntry = $conn->newEntry() ; + $newEntry->setDN($DN); + $newEntry->setValues("objectclass", "top", "extensibleObject", "nsBackendInstance" ); + $newEntry->setValues("cn", $CN ); + $newEntry->setValues($suffixarg, $suffixname); + my @params = keys(%LDBMparamToMigrate); + foreach $param (@params) { + my @values = $entry->getValues($param); + $newEntry->setValues($param, @values) if (@values); + } + if (addEntryToNew($newEntry, "LDBM_BACKEND_INSTANCE",1)) { + push @BACKENDS, $CN; + } + } + } + } +} + +sub parseLDBM_backend_instance { + my $entry = shift; + ®isterSuffix_Backend($entry); + push @LDBM_backend_instances, $entry; +} + +############################################################################# +sub migrateMappingTree { + foreach $entry (@Mapping_tree_entries) { + my $DN = $entry->getDN(1); # 1 si to normalize the DN + if ($DN =~/cn=\"o=netscaperoot\",cn=mapping tree,cn=config/i){ + # DO NOTHING + } + else { + if(alreadyExistsInNew($entry)){ + printMsg("\n\n*** MAPPING_TREE - $DN already exists"); + printMsg("\n*** Migration will not add the suffix"); + } + else { + addEntryToNew($entry, "MAPPING_TREE",1); + } + } + } +} + + +sub parseMapping_tree{ + my $entry = shift; + push @Mapping_tree_entries, $entry; +} + +############################################################################# +sub migrateDefaultIndexes { + foreach $index (@default_indexes) { + my $CN = $index->{cn}[0]; + my $newIndex ; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)})) { + if ($newIndex = alreadyExistsInNew($index)) { + if (! isAsystemIndex($newIndex)) { + updateEntry($index, "DEFAULT_INDEX", 1, 2); + } + } + else { + addEntryToNew($index, "DEFAULT_INDEX", 2); + } + } + } +} + + +sub parseDefaultIndex{ + my $index = shift; + push @default_indexes, $index; +} + +############################################################################# + +sub migrateIndexes { + foreach $index (@indexes) { + my $CN = $index->{cn}[0]; + my $newIndex; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)}) && (DN !~ /cn=netscaperoot,cn=index/i)){ + if ($newIndex = alreadyExistsInNew($index)) { + if (! isAsystemIndex($newIndex)) { + updateEntry($index, "INDEX", 1, 2); + } + } + else { + addEntryToNew($index, "INDEX", 2); + } + } + } +} + +sub parseIndex{ + my $index = shift; + push @indexes, $index; +} + +############################################################################# + +sub newLDIFplugin { + my $currentPlugin = shift; + my $DN = $currentPlugin->getDN(1); + my $newPlugin = $conn->newEntry() ; + $newPlugin->setDN($DN); + foreach $Attr (@pluginAttrs) { + my @values = $currentPlugin->getValues($Attr); + $newPlugin->setValues($Attr, @values) if (@values); + } + return $newPlugin; +} + +sub migrateStdPlugin{ + my $plugin = shift; + my $DN = $plugin->getDN(1); + my $pluginEnable = "nsslapd-pluginEnabled"; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $currentPlugin ; + if ($currentPlugin = alreadyExistsInNew($plugin, 1)) { + $plugin = updatePathInPluginArgs($plugin); + my $pluginEnableValue = $plugin->{$pluginEnable}[0]; + my $cont = 1; + my $pluginHasChanged = 0; + my $newPlugin = &newLDIFplugin($currentPlugin); + if (! $currentPlugin->hasValue($pluginEnable,$pluginEnableValue,1)){ + $newPlugin->setValues($pluginEnable, $pluginEnableValue); + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + while($cont){ + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + my @values = $plugin->getValues($arg); + my $value = $values[0] ; + $newPlugin->setValues($arg, $value) if (@values); + if ($currentPlugin->exists($arg)) { + if (! $currentPlugin->hasValue($arg,$value,1)) { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + if ($currentPlugin->exists($arg)) { + # Just Warn the user. Do nothing. + printTrace("\nCompared to the old instance, the current new plugin $DN belongs this attribute: $arg",2); + } + else { + $cont = 0 ; + } + } + $argNum++; + } + updateEntry($newPlugin, "STANDARD_PLUGIN", 0, 1) if ($pluginHasChanged); + } +} + +############################################################################# + +sub migrateConfig_Node{ + my $config_node = shift; + my @params = keys(%GeneralSrvParamToMigrate); + my $hasChanged = 0; + my $newConfigNode; + if ($newConfigNode = alreadyExistsInNew($config_node, 1)){ + foreach $param (@params) { + if ($config_node->exists($param)){ + my @valuesToMigrate = $config_node->getValues($param); + if (@valuesToMigrate){ + if ($newConfigNode->exists($param)){ + my @currentValues = $newConfigNode->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newConfigNode->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + else { + $newConfigNode->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + } + } + updateEntry($newConfigNode, "CONFIG_NODE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateConfig_LDBM_database{ + my $config_ldbm = shift; + my @params = keys(%GlobalConfigLDBMparamToMigrate); + my $hasChanged = 0; + my $newConfigLdbm ; + if ($newConfigLdbm = alreadyExistsInNew($config_ldbm, 1)) { + foreach $param (@params) { + if ($config_ldbm->exists($param)){ + my @valuesToMigrate = $config_ldbm->getValues($param); + if (@valuesToMigrate){ + if ($newConfigLdbm->exists($param)){ + my @currentValues = $newConfigLdbm->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newConfigLdbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $newConfigLdbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($newConfigLdbm, "CONFIG_LDBM_DATABASE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateChainingBE_config{ + my $chaining_config = shift; + my $DN = $chaining_config->getDN(1); + my @params = (); + my $hasChanged = 0; + my $newChainingConfig; + if ($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i){ + $newChainingConfig = searchEntry("cn=config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingConfigParams); + } + if ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i){ + $newChainingConfig = searchEntry("cn=default instance config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingDefaultInstanceConfigParams); + } + foreach $param (@params) { + if ($chaining_config->exists($param)){ + my @valuesToMigrate = $chaining_config->getValues($param); + if (@valuesToMigrate){ + printTrace("\nParam: $param values To migrate: @valuesToMigrate",3); + if ($newChainingConfig->exists($param)){ + my @currentValues = $newChainingConfig->getValues($param); + printTrace("\nParam: $param new current values: @currentValues",3); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newChainingConfig->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $newChainingConfig->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($newChainingConfig, "CHAINING_BACKEND_CONFIG", 0, 1) if ($hasChanged); +} + +############################################################################# + +sub registerSuffix_ChainingBE { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldChainingBackends{$suffix} = $CN; +} + +sub storeMultiplexorBindDN { + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + if ($chaining_instance->exists("nsMultiplexorBindDN")){ + my $bindDN = $chaining_instance->{nsMultiplexorBindDN}[0]; + my $newBindDN = searchEntry($bindDN); + if (! $newBindDN){ + # the bindDN entry doesn't yet exist in new => it will have to be migrated + $MultiplexorBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in new + } + } + +} + +sub importMultiplexorBindDNEntries { + # import all entries present in @MultiplexorBindDNEntriesToMigrate in new + my @MultiplexorBindDNs = keys (%MultiplexorBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + foreach $bindDN (@MultiplexorBindDNs) { + printTrace("\nimportMultiplexorBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # check wether the backend has been imported in new or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the binf DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to new + $conn->close if ($conn); +} + +sub migrateChainingBE_instance{ + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + ®isterSuffix_ChainingBE($chaining_instance); + if (alreadyExistsInNew($chaining_instance)) { + # already exists + printMsg("\n\n*** CHAINING_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($chaining_instance, "nsmultiplexorcredentials"); + addEntryToNew($chaining_instance, "CHAINING_BACKEND_INSTANCE", 1); + storeMultiplexorBindDN($chaining_instance); + } +} + +############################################################################# + +# create a new LDIF representation of a new replica consumer +sub newLDIFreplica { + my $replica = shift; + my $DN = $replica->getDN(1); + my $newReplica = $conn->newEntry() ; + my $MASTER_OR_MULTIMASTER = "3" ; + $newReplica->setDN($DN); + foreach $Attr (@nsds5replicaAttrs) { + my @values = $replica->getValues($Attr); + $newReplica->setValues($Attr, @values) if (@values); + } + my $replicaType = $replica->{nsDS5ReplicaType}[0]; + if ($replicaType eq $MASTER_OR_MULTIMASTER) { + my @nsState = $replica->getValues("nsState"); + $newReplica->setValues("nsState", @nsState); + } + else { + $newReplica->setValues("nsDS5ReplicaId", $replicaIdvalue); + } + return $newReplica; +} + +sub MigrateNSDS5_replica{ + foreach $replica (@new6replicas) { + my $DN = $replica->getDN(1); + my $newReplica; + my @removeAttrs = qw(nsstate nsds5replicaname nsds5replicachangecount); + for (@removeAttrs) { + $replica->remove($_); + } + if (alreadyExistsInNew($replica)) { + # replica already exists + printMsg("\n\n*** NSDS5_REPLICA - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + $newReplica = &newLDIFreplica($replica); + addEntryToNew($newReplica, "NSDS5_REPLICA", 1); + } + storeReplicaBindDN($replica); + } +} + +sub parseNSDS5_replica{ + my $replica = shift; + push @new6replicas, $replica; +} + +sub storeReplicaBindDN { + my $replica = shift; + my $DN = $replica->getDN(1); + if ($replica->exists("nsDS5ReplicaBindDN")){ + my $bindDN = $replica->{nsDS5ReplicaBindDN}[0]; + my $newBindDN = searchEntry($bindDN); + if (! $newBindDN){ + # the bindDN entry doesn't yet exist in new => it will have to be migrated + $ReplicaBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in new + } + } +} + + +sub importReplicaBindDNEntries { + # import all entries present in @ReplicaBindDNEntriesToMigrate in new + my @ReplicaBindDNs = keys (%ReplicaBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + my $replBind_entry = ""; + my @bindDN_elements = ""; + my $bindDN_parent = ""; + my $parentBind_entry = ""; + foreach $bindDN (@ReplicaBindDNs) { + printTrace("\nimportReplicaBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # If backend is from config, read the entry from dse.ldif and add to new - NGK + if ($backendtoExportFrom eq "cn=config") { + my $norm_bindDN = normalizeDN($bindDN); + @bindDN_elements = ldap_explode_dn($norm_bindDN, 0); +# @bindDN_elements = split(/,/,$norm_bindDN); + my $junk = shift(@bindDN_elements); + if ($#bindDN_elements >= 1) { + $bindDN_parent = normalizeDN(join(",", @bindDN_elements)); + } + printTrace("\nOpening DSE.ldif",3); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF); + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1); + if ($DN eq $norm_bindDN) { + $replBind_entry = $entry; + } + if ($bindDN_parent ne "") { + if ($DN eq $bindDN_parent) { + $parentBind_entry = $entry; + } + } + } + close(DSELDIF); + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + if ($bindDN_parent ne "") { + addEntryToNew($parentBind_entry, BINDDN_PARENT, 0); + } + printTrace("\nAdding BindDN with addEntryToNew",3); + addEntryToNew($replBind_entry, BINDDN, 0); + } else { + # check wether the backend has been imported in new or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the bind DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to new + $conn->close if ($conn); +} + +sub alreadyMigrated { + my $backendToCheck = shift; + foreach $backend (@BACKENDS) { + return 1 if ($backend eq $backendToCheck); + } + return 0 ; +} + +sub belongsSuffix { + my $suffix = shift; + my $bindDN = shift; + return ($bindDN =~ /$suffix\s*$/i); +} + +sub length { + my $suffix = shift; + my $count = 0; + while ($suffix =~ /./g) { + $count++; + } + return $count ; +} + +sub getBackendtoExportFrom { + my $bindDN = shift ; + my $sizeOfSuffix = 0 ; + my $NULL = ""; + my @oldSuffixes = keys(%oldBackends); + my @oldChainingSuffixes = keys(%oldChainingBackends); + my $bindDN_backend = $NULL; + my $config = "cn=config"; + + my $norm_bindDN = normalizeDN($bindDN); + # Check if bindDN exists in cn=config - NGK + if (belongsSuffix($config,$norm_bindDN)) { + $bindDN_backend = $config; + printTrace("\ngetBackendtoExportFrom: bindDN_backend: $bindDN_backend",3); + } else { + foreach $suffix (@oldSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is: $suffix",3); + if ((belongsSuffix($suffix,$norm_bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $oldBackends{$suffix}; + printTrace("\ngetBackendtoExportFrom: bindDN_backend: $bindDN_backend, sizeOfSuffix: $sizeOfSuffix",3); + } + } + foreach $suffix (@oldChainingSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is a chained suffix: $suffix",3); + if ((belongsSuffix($suffix,$norm_bindDN)) && (length($suffix) > $sizeOfSuffix)) { + printMsg("\n\n*** Entry stored on a remote backend - $norm_bindDN"); + printMsg("\n*** We don't migrate it"); + return $NULL; + } + } + } + return $bindDN_backend; +} + + +sub getBackendtoImportTo { + my $bindDN = shift; + my $sizeOfSuffix = 0; + my $NULL = ""; + my $suffixArg = "nsslapd-suffix"; + my $bindDN_backend = $NULL; + open( DSELDIF, "< $DSEldif" ) || die "Can't open $DSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + my $suffix = $entry->{$suffixArg}[0]; + if ((belongsSuffix($suffix,$bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $entry->{cn}[0]; + } + } + } + close(DSELDIF); + return $bindDN_backend ; +} + + +sub ExportAndAddEntry { + my $DN = shift; + my $backendtoExportFrom = shift; + my $ldif_dir = shift; + my $ldif = "$ldif_dir${PATHSEP}$backendtoExportFrom.ldif" ; + # first: export entry pointed out by the $DN to $ldif file + $ENV{"$LIB_PATH"}=$old_libpath; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "\ncan't create $ldif_dir to store temporary ldif files\n"; + } + chdir($oldSlapdExecDir) or die "\nCould not change directory to $oldSlapdExecDir: $!\n"; + &db2Ldif($ldif, $backendtoExportFrom, $oldHome, $DN); + chdir($curdir) or die "\nCould not change directory to $curdir: $!\n"; + + # then: Add it to new + if (! $conn) { + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + } + open( BINDDNLDIF, "< $ldif" ) || die "\nCan't open $ldif: $!: \n"; + my $in = new Mozilla::LDAP::LDIF(*BINDDNLDIF) ; + while ($entry = readOneEntry $in) { + my $entryDN = $entry->getDN(1); + if ($DN eq $entryDN) { + addEntryToNew($entry, "nsds5ReplicaBindDN", 0); + } + } + close(BINDDNLDIF); + # remove the ldif file after the import + unlink($ldif) ; +} + +############################################################################# +sub MigrateNSDS_replication_agreement { + foreach $replicationAgreement (@replicationAgreements) { + my $DN = $replicationAgreement->getDN(1); + if (alreadyExistsInNew($replicationAgreement)){ + # replication agreement already exists + printMsg("\n\n*** NSDS_REPLICATION_AGREEMENT - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($replicationAgreement, "nsDS5ReplicaCredentials"); + addEntryToNew($replicationAgreement, "NSDS_REPLICATION_AGREEMENT", 1); + } + } +} + + +sub parseNSDS_replication_agreement{ + my $replicationAgreement = shift; + push @replicationAgreements, $replicationAgreement ; +} + +############################################################################# + +sub migrateChangelog5{ + my $changelog = shift; + my $DN = $changelog->getDN(1); + my $changelogdir = "nsslapd-changelogdir"; + if (alreadyExistsInNew($changelog)){ + # cn=changelog5,cn=config already exists in new + my $newChangelog = searchEntry($DN); + my @newChangelogdir = $newChangelog->getValues($changelogdir); + $changelog->setValues($changelogdir, @newChangelogdir); + updateEntry($changelog, "CHANGELOG5", 0, 1); + } + else { + # cn=changelog5,cn=config need to be created in new. + # the changelogdir value must be setup to <new_root_server>/slapd-instance/changelogdb + $changelog->setValues($changelogdir,"${serverHome}${PATHSEP}changelogdb"); + addEntryToNew($changelog, "CHANGELOG5", 1); + } +} + + +sub migrateChangelog { + my $oldchangelogdir = ""; + my $newchangelogdir = ""; + my $changelogdir = "nsslapd-changelogdir"; + my $CL5DN = "cn=changelog5,cn=config"; + printTrace("\n\n***** Migrate Changelog...",0,1); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF); + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "CHANGELOG5"){ + $oldchangelogdir = ($entry->getValues($changelogdir))[0]; + } + } + close(DSELDIF); + if ($oldchangelogdir) { + # If using olddatadir to migrate from, the path of the changelogdb + # from the dse.ldif may not match the path where the old server + # root was archived. We may need to modify oldchangelogdir so the + # copy of the changelog files succeeds. + unless(-e $oldchangelogdir) { + if($olddatadir) { + my @cldbpath = split(/\//,$oldchangelogdir); + until($cldbpath[0] =~/^slapd-/) { + shift(@cldbpath); + } + my $tmpcldbpath = join(${PATHSEP}, @cldbpath); + $oldchangelogdir = "$oldDir${PATHSEP}$tmpcldbpath"; + } + # If oldchangelogdir still looks to be wrong, prompt for the + # location instead of just failing on the copydir operation + # and bombing out of the migration. + unless(-e $oldchangelogdir) { + print("\n\nThe old changelog directory \"$oldchangelogdir\" doesn't exist. Please enter the correct path: "); + $oldchangelogdir = <STDIN>; + } + } + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + my $newChangelog = searchEntry($CL5DN); + $newchangelogdir = ($newChangelog->getValues($changelogdir))[0]; + stopServer($root,'slapd-'.$newname); + printTrace("\ncopying $oldchangelogdir${PATHSEP}* to $newchangelogdir",3); + copyDir("$oldchangelogdir","$newchangelogdir"); + + # We need to modify the DBVERSION file for a new verision of the db + open(DBVERSION,">$newchangelogdir${PATHSEP}DBVERSION") || die "Can't overwrite $newchangelogdir${PATHSEP}DBVERSION: $! "; + print DBVERSION "Changelog5/NSMMReplicationPlugin/3.0"; + close(DBVERSION); + + &startServer() unless (isDirectoryAlive()); + } +} + +############################################################################# + +sub migrateReplication{ + my $replication = shift; + my $DN = $replication->getDN(1); + if (alreadyExistsInNew($replication)){ + # replication agreement already exists + printMsg("\n\n*** $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + addEntryToNew($replication, "REPLICATION", 1); + } +} + +############################################################################# + +sub migrateSecurity{ + my $security = shift; + if ($entry->hasValue("objectClass", "nsEncryptionConfig")) { + my $certfile = "alias/slapd-" . $newname . "-cert8.db"; + my $keyfile = "alias/slapd-" . $newname. "-key3.db"; + $entry->setValues("nsCertfile",$certfile) if ! $entry->hasValue("nsCertfile",$certfile); + $entry->setValues("nsKeyfile",$keyfile) if ! $entry->hasValue("nsKeyfile",$keyfile); + } + if (alreadyExistsInNew($security)){ + # already exists in new + updateEntry($security, "SECURITY", 0, 1); + } + else { + addEntryToNew($security, "SECURITY", 1); + } +} + +############################################################################# + +sub migrateSNMP{ + my $snmp = shift; + if (alreadyExistsInNew($snmp)){ + # already exists in new + updateEntry($snmp, "SNMP", 0, 1); + } + else { + addEntryToNew($snmp, "SNMP", 1); + } +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + my $sep = shift ; + + if ($sep) { + print "\n-------------------------------------------------------------------------"; + print LOGFILE "\n-------------------------------------------------------------------------"; + } + + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg ; + } +} + +############################################################################# +# this subroutine implements a very stupid version of diff + +sub diff { + my $f1 = shift; + my $f2 = shift; + my $lineToBeginWith = shift; + my $NULL = "" ; + my $diff_f1 = $NULL ; + my $diff_f2 = $NULL ; + my $retval = $NULL ; + my $ret; + open(F1, "$f1") or die "Could not open file $f1"; + open(F2, "$f2") or close(F1), die "Could not open file $f2"; + + while (defined($l1 = <F1>)) { + if ($lineToBeginWith){ + $lineToBeginWith -- ; + next ; + } + next if ($l1 =~ /^\#/); + $ret = defined($l2 = <F2>); + if ($ret) { + $ret = defined($l2 = <F2>) while ($ret && ($l2 =~ /^\#/)) ; + if ($ret) { + if (!($l1 eq $l2)) { + + # ignore whitespace + $l1_clean = $l1 ; + $l2_clean = $l2 ; + $l1_clean =~ s/\s//g; + $l2_clean =~ s/\s//g; + + if (!($l1_clean eq $l2_clean)) { + $diff_f1 .= "${l1}" unless ($l1_clean eq $NULL); + $diff_f2 .= "${l2}" unless ($l2_clean eq $NULL); + } + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + + while (defined($l2 = <F2>)) { + if (($l2 =~ /^\#/) || ($l2 =~ /^\s*$/)) { + next ; + } + else { + $diff_f2 .= "${l2}" ; + } + } + + close(F1); + close(F2); + + $retval .= "- differences present in your config file but not in standard file:\n\n". "$diff_f1\n" if ($diff_f1) ; + $retval .= "- differences present in standard file but not in your config file:\n\n" . "$diff_f2" if ($diff_f2) ; + return $retval ; +} + +sub CompareStdConfigFiles { + # Compare each configuration file against its default version. If it has changed, + # notify the user that the file has changed and will need to be checked by the + # user. This should be safe to do because there should be no path information + # stored in these conf files, which are just schema stuff. + # printTrace("\nCheck if standard configuration files have changed",3); + + # get the version of the DS to migrate + ($oldVersion, $oldMinor) = &getVersion($oldDir, $oldversionstr); + # get the version of the new DS + ($Version, $Minor) = &getVersion($root); + + # get old LIB_PATH + $old_libpath = &getLibPath($oldDir, $oldVersion, $oldMinor); + # get new LIB_PATH + $new_libpath = &getLibPath($root, $Version, $Minor); + + my $origFilePath = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}install${PATHSEP}schema${PATHSEP}" ; + my $FilesChanged = ""; + my $AllDiffs = "***********************************************************************"; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(CONFDIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldConfDir: $!"; + + foreach $file (readdir(CONFDIR)) { + $origFile = $origFilePath . $file ; + $configFile = $oldSchemaDir . $file ; + if (( exists($stdIncludes{lc($file)})) && (-f $origFile)) { + $diffs = &diff($configFile, $origFile, $lineToBegin); + $lineToBegin = 0 if $lineToBegin ; + if ($diffs) { + $FilesChanged .= "\n$configFile"; + $AllDiffs .= "\n$configFile is different than the standard configuration file" ; + $AllDiffs .= "\nYou will need to check this file and make sure its changes are compatible "; + $AllDiffs .= "with the new directory server\nHere are the differences:\n"; + $AllDiffs .= "$diffs \n\n"; + $AllDiffs .= "***********************************************************************"; + } + else { + $NoChanges .= "\n$configFile"; + } + } + } + closedir(CONFDIR); + +if ($FilesChanged) { + printTrace("\nNo changes to old configuration files:$NoChanges",3) ; + printTrace("\n***********************************************************************",3) ; + printMsg("\nThe following standard files have been modified: $FilesChanged"); + if ($NO_INPUT_USER) { + # do nothing + } + else { + printMsg("\nDo you want to see the differences Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("$AllDiffs"); + } + printMsg("\nDo you want to continue the migration Yes/No [No] ?"); + $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + } + } +} + + + +############################################################################# + +# this is used to run the system() call, capture exit and signal codes, +# and die() upon badness; the first argument is a directory to change +# dir to, if any, and the rest are passed to system() +sub mySystem { + my $rc = &mySystemNoDie(@_); + my ($dir, @args) = @_; + if ($rc == 0) { +# success + } elsif ($rc == 0xff00) { + die "Error executing @args: error code $rc: $!"; + } elsif ($rc > 0x80) { + $rc >>= 8; + die "Error executing @args: error code $rc: $!"; + } else { + if ($rc & 0x80) { + $rc &= ~0x80; + } + die "Error executing @args: received signal $rc: $!"; + } + + # usually won't get return value + return $rc; +} + +# This version does not die but just returns the error code +sub mySystemNoDie { + my ($dir, @args) = @_; + if ($dir && ($dir ne "")) { + chdir($dir) or die "Could not change directory to $dir: $!"; + } + my $cmd = $args[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = 0xffff & system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd @fixargs\n"; + $rc = 0xffff & system {$cmd} @fixargs; + } + chdir(${curdir}) or die "Could not change directory to $curdir: $!"; + return $rc; +} + +########################################################################################### +# # +# Export/Import of the backends in @BACKENDS # +# # +########################################################################################### + +sub manydb2Ldif { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}=$old_libpath; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "can't create $ldif_dir to store temporary ldif files"; + } + chdir($oldSlapdExecDir) or die "Could not change directory to $oldSlapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &db2Ldif($ldif, $backend, $oldHome); + } + print " Done.\n"; + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + +sub db2Ldif { + my $ldif = shift ; + my $backend = shift ; + my $home = shift ; + my $include_suffix = shift ; + my $db2ldif_param ; + if ($include_suffix) { + $db2ldif_param = "db2ldif -r -D $home -n $backend -a $ldif -s \"$include_suffix\""; + } + else { + $db2ldif_param = "db2ldif -r -D $home -n $backend -a $ldif"; + } + open(DB2LDIF, "${quote}${quote}$slapdExecName${quote} $db2ldif_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + my $ii = 0; + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + printMsg($_); + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the 5.x slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif) { + chown( $newuid, $newgid, $ldif) or printMsg("\nUnable to change the ownership of $ldif to $localuser") ; + } + } +} + +sub manyLdif2db { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($slapdExecDir) or die "Could not change directory to $slapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &Ldif2db($ldif, $backend); + } + # remove the empty ldif directory + # but not if using the data dir + if (!$olddatadir) { + rmdir($ldif_dir); + } + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + + +sub Ldif2db { + my $ldif = shift ; + my $backend = shift ; + my $ldif2db_param = "ldif2db -D $serverHome -n $backend -i $ldif"; + open(LDIF2DB, "${quote}${quote}$slapdExecName${quote} $ldif2db_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + while (<LDIF2DB>) { + printMsg($_); + } + close(LDIF2DB); + # remove the ldif file after the import + # but not if using the data dir + if (!$olddatadir) { + unlink($ldif) ; + } +} + + +########################################################################################### +# # +# Running/Stopping the Server # +# # +########################################################################################### + + + +sub isDirectoryAlive { + die "\n Migration aborted. Make sure your old and new Directory Servers are installed on the same machine \n" if ( $LDAPservername == -1 ); + my $test_conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd); + if ($test_conn) { + $test_conn->close(); + return 1; + } + else { + return 0 ; + } +} + + +sub startServer { + my $instanceDir = ${serverHome} ; + my $errLog = $instanceDir . $PATHSEP . 'logs' . $PATHSEP . 'errors'; + # emulate tail -f + # if the last line we see does not contain "slapd started", try again + my $done = 0; + my $started = 0; + my $code = 0; + my $lastLine = ""; + my $timeout = time + 240; # 4 minutes + $ENV{"$LIB_PATH"}=$new_libpath; + + my $startCmd = $instanceDir . $PATHSEP . 'start' . $script_suffix; + if (! -f $startCmd) { + $startCmd = $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix; + } + $code = &mySystem($instanceDir,$startCmd); + open(IN, $errLog) or die "Could not open error log $errLog: $!"; + my $pos = tell(IN); + while (($done == 0) && (time < $timeout)) { + for (; ($done == 0) && ($_ = <IN>); $pos = tell(IN)) { + $lastLine = $_; + # print; + # the server has already been started and shutdown once . . . + if (/slapd started\./) { + $started++; + if ($started == 2) { + $done = 1; + } + # sometimes the server will fail to come up; in that case, restart it + } elsif (/Initialization Failed/) { + # print "Server failed to start: $_"; + $code = &mySystem($instanceDir, $startCmd); + # sometimes the server will fail to come up; in that case, restart it + } elsif (/exiting\./) { + # print "Server failed to start: $_"; + #$code = &mySystem($startCmd); + $code = &mySystem($instanceDir, $startCmd); + } + } + if ($lastLine =~ /PR_Bind/) { + # server port conflicts with another one, just report and punt + print $lastLine; + print "This server cannot be started until the other server on this\n"; + print "port is shutdown.\n"; + $done = 1; + } + if ($done == 0) { + # rest a bit, then . . . + sleep(2); + # . . . reset the EOF status of the file desc + seek(IN, $pos, 0); + } + } + close(IN); + + sleep(5); + die "\nUnable to start the $Version.$Minor Directory Server\n" unless (isDirectoryAlive()); + + return 0; +} + +sub stopServer { + my $root = shift; + my $name = shift; + $maxStopIterations = 5; + print "\nShutting down server $name . . .\n"; + $ENV{"$LIB_PATH"}=$new_libpath; + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote; + if (! -f $stopCmd) { + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote; + } + + if (! -f $stopCmd) { + # no stop command, probably a 1.X system; for NT, we'll try net stop + # for unix, we'll get the pid and kill it + if ($isNT) { + $stopCmd = 'net stop ' . $name; + } else { + # see if there is a pid file + $pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' . + $PATHSEP . 'pid'; + if (open(PIDFILE, $pidfile)) { + chomp($pid = <PIDFILE>); + close(PIDFILE); + while ($maxStopIterations-- && !$exitCode) { + $exitCode = kill(15, $pid); + } + $stopCmd = undef; + } + } + } + + # keep looping until the stop cmd returns an error code, which usually + # means that what ever we want to stop is stopped, or some other error + # occurred e.g. permission, or no such service + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + while ($stopCmd && $maxStopIterations-- && $exitCode) { + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + } + + if (!$maxStopIterations) { + print "Warning: could not shutdown the server: $!\n"; + } + sleep(10) ; + $exitCode = 0; +} + + +sub runAndIgnoreOutput { + my $cmd = shift; + printMsg("."); + open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!"; + printMsg("."); + sleep(1); # allow pipe to fill with data + printMsg("."); + while (<RUNCMD>) { +# print; + } + my $code = close(RUNCMD); +# print "runAndIgnore: code=$code status=$?\n"; + return $?; +} + +############################################################################# +# migrate SSL info + +sub MigrateSSL { + my $secPwd = 'bidon' ; + # copy the SSL directory + ©Dir("$oldHome${PATHSEP}ssl","$serverHome${PATHSEP}ssl") if (-d "$oldHome${PATHSEP}ssl"); + # copy the cert db and key files + if ( -d "$oldDir${PATHSEP}alias") { + $aliasDir = "$root${PATHSEP}alias"; + if (! -d $aliasDir) { + mkdir($aliasDir, 0750); + } + &stopServer($root,'slapd-'.$newname); + my $keydb = "$aliasDir${PATHSEP}slapd-$newname-key3.db" ; + my $certdb = "$aliasDir${PATHSEP}slapd-$newname-cert8.db" ; + my $certdb7 = "$aliasDir${PATHSEP}slapd-$newname-cert7.db" ; + my $old_keydb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ; + my $old_certdb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db"; + my $keydb_backup = "$aliasDir${PATHSEP}slapd-$newname-key3.db_backup" ; + my $certdb_backup = "$aliasDir${PATHSEP}slapd-$newname-cert7.db_backup" ; + if (-f $old_keydb) { + if (-f $keydb) { + if ($NO_INPUT_USER) { + printMsg("\n$keydb already exists. backup in $keydb_backup ..."); + ©BinFile($keydb,$keydb_backup); + ©BinFile($old_keydb,$keydb); + } + else { + print("\n\n$keydb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_keydb,$keydb); + } + } + } + else { + ©BinFile($old_keydb,$keydb); + } + } + if (-f $old_certdb) { + $mode = (stat($old_certdb))[2] if $PRESERVE; + if (-f $certdb) { + if ($NO_INPUT_USER) { + printMsg("\n$certdb already exists. backup in $certdb_backup ..."); + ©BinFile($certdb,$certdb_backup); + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + else { + print("\n\n$certdb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + } + } + else { + ©BinFile($old_certdb,$certdb7); + } + } + # copy the old password file + if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") { + ©BinFile( + "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt", + "$aliasDir${PATHSEP}$type-$newname-pin.txt" + ); + } + &startServer(); + if ($PRESERVE) { + chown($newuid,$newgid,$certdb) || print "Failed to set uid $newuid gid $newgid on $certdb : $!\n"; + chmod($mode,$certdb) || print "Failed to set mode $mode on $certdb : $!\n"; + } + } + +} + +sub DisableSSL { + my $entry = $conn->search("cn=config","base","objectclass=*"); + my $LDAPparam = "nsslapd-security" ; + my $Value = "off" ; + if ($entry->{$LDAPparam}[0] ne $Value) { + printTrace("\nDisable SSL...",1); + $entry->setValues($LDAPparam, $Value); + } + my $res = $conn->update($entry); + if ($res) { + printTrace("\nSSL disabled",2); + } + else { + printMsg("\nCan't disabled SSL. The server may have problems to start"); + } +} + +# enable the migration of client authentication informations +sub MigrateCertmap { + # backup the old certmap.conf and replace it with the new one + my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"; + my $newCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf" ; + my $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + if (-f $oldCertmap) { + if ($NO_INPUT_USER) { + printMsg("\n$newCertmap has been backup in $backupCertmap"); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up the file $newCertmap [$backupCertmap] ?") ; + my $Answer = <STDIN> ; + $backupCertmap = $Answer if ($Answer ne "\n"); + chomp($backupCertmap); + printTrace("\nDest: .$backupCertmap.",4); + if (-e $backupCertmap) { + printMsg("\n\n$backupCertmap already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup file: $newCertmap in $backupCertmap",4); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + } + else { + } +} + +sub hasChangedoldCertmap { + my $certmapfile = shift ; + my @reference = ("certmap default default", + "default:DNComps", + "default:FilterComps e") ; + my $cpt = 0 ; + printTrace("\nhasChangedoldCertmap",3); + open(CERTMAP,"< $certmapfile"); + while (<CERTMAP>) { + if ((! /^\s*#/) && (! /^\s*$/)) { + my $ref = $reference[$cpt] ; + printTrace("\nValue: $_, ref: $ref",4); + if (! /^\s*$ref\s*$/) { + return 1 ; + } + else { + $cpt++ ; + } + } + } + close (CERTMAP); + printTrace("\ncpt: $cpt",4); + if ($cpt < $#reference) { + return 1 ; + } + else { + return 0 ; + } +} + + +########################################################################################### +# # +# Copy directory and files functions # +# # +########################################################################################### + + +sub copyDir { + my $src = shift; + my $dest = shift; + my $exclude = shift; + + opendir( SRC, $src ) or die "Can't open directory $src: $!: "; + my $mode; + my $uid; + my $gid; + mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest ); + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + local ( @files ) = readdir ( SRC ); + closedir( SRC ); + for ( @files ) { + if ( $_ eq "." || $_ eq ".." ) { + next; + } elsif ( $exclude && /$exclude/ ) { + next; + } elsif( -d "$src${PATHSEP}$_") { + ©Dir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); + } else { + ©BinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_"); + } + } +} + +sub copyBinFile { + my $src = shift; + my $dest = shift; + my $buf = ""; + my $bufsize = 8192; + + open( SRC, $src ) || die "Can't open $src: $!\n"; + # if we are given a directory destination instead of a file, extract the + # filename portion of the source to use as the destination filename + if (-d $dest) { + $dest = $dest . $PATHSEP . &basename($src); + } + open( DEST, ">$dest" ) || die "Can't create $dest: $!\n"; + binmode SRC; + binmode DEST; + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + while (read(SRC, $buf, $bufsize)) { + print DEST $buf; + } + close( SRC ); + close( DEST ); +} + +############################################################################################################# +# backup 5.x configuration files # +# backup the directory <root_server5>/slapd-instance/config dans <root_server5>/slapd-instance/BackupConfig # # +# # +############################################################################################################# + + +sub backupConfigFiles { + # backup the 5.x config files + my $src = "$serverHome${PATHSEP}config" ; + my $dest = "$serverHome${PATHSEP}config_backup" ; + if ($NO_INPUT_USER) { + printMsg("\n$src has been backup in $dest"); + ©Dir($src,$dest); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up your configuration directory [$dest] ?") ; + my $Answer = <STDIN> ; + $dest = $Answer if ($Answer ne "\n"); + chomp($dest); + printTrace("\nDest: .$dest.",4); + if (-e $dest) { + printMsg("\n\n$dest already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $dest = "$serverHome${PATHSEP}config_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup Directory: $src in $dest",4); + ©Dir($src,$dest); + } +} +############################################################################# + +sub getLDAPservername { + my $oldLDAPservername; + my $LDAPservername; + my $localhost = "nsslapd-localhost"; + open(OLDDSELDIF, "< $oldDSEldif") or die "\nError: could not open old config file $oldDSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*OLDDSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $oldLDAPservername = $values[0]; + printTrace("\nName of the old LDAP server: $oldLDAPservername",3); + } + break; + } + } + close(OLDSELDIF); + + open( DSELDIF, "< $DSEldif" ) || die "\nCan't open $DSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $LDAPservername = $values[0]; + printTrace("\nName of the new LDAP server: $LDAPservername",3); + } + break; + } + } + close(DSELDIF); + # check ol and new Directory Instance are installed on the same physical machine. + if (lc($oldLDAPservername) ne lc($LDAPservername)) { + # warn the user he tries to migrate a 4.x server installed on a different machine from the 5.x one + printMsg("\n\nYour old instance is on $oldLDAPservername, whereas your new instance is on $LDAPservername. Migration on different machines is not supported. Do you want to continue ? Yes/No [No]:") ; + if (! (<STDIN> =~ /yes|y/i)) { + return -1; + } + } + return $LDAPservername ; +} + +############################################################################# + +sub getLibPath { + my $myDir = shift; + my $myVersion = shift; + my $myMinor = shift; + + if ($isNT) { + return $ENV{"$LIB_PATH"}; + } + if (($myVersion >= 6) && ($myMinor >= 2)) { + return + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + } else { + return "$myDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + } +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $versionstr = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + if (!$versionstr) { # version not specified on cmd line - find it + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $cur_libpath=$ENV{"$LIB_PATH"}; + $ENV{"$LIB_PATH"}= + "$dir${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory/ || /^iPlanet-Directory/i) { + $versionstr = $_; + last; + } + } + $code = close(F); + # print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}=$cur_libpath; + } + + if ($versionstr =~ /^Netscape-Directory\/(\d+)\.(\d+)(?:b\d)*\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } elsif ($versionstr =~ /(\d+)\.(\d+)/) { + $version = $1; + $minor = $2; + } + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################################### +sub normalizeDir { + my $dir = shift ; + my $dir_prec = "" ; + while ($dir_prec ne $dir) { + $dir_prec = $dir ; + if ($isNT) { + grep { s@\\\\@\\@g } $dir ; + } + else { + grep { s@//@/@g } $dir ; + } + } + return $dir ; +} + + +############################################################################################### + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################################### +# get uid and group id of the 5.x slapd server. +# The uid is done through the nsslapd-localuser attribute + +sub getuid_gid { + my $newuid ; + my $newgid ; + my $localuser ; + my $localuser_attr = "nsslapd-localuser" ; + if (! $isNT) { + &startServer() unless (isDirectoryAlive()); + my $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; + my $entry = $conn->search("cn=config ", "base","objectclass=*", 0, ($localuser_attr)) ; + # Tests wether we succeed to get the entry cn=config + die "\nCan't get the entry cn=config \n" unless ($entry); + my @values = $entry->getValues($localuser_attr); + $conn->close(); + if ($#values == -1 || ($values[0] eq "") ) { # tests wether the nsslapd-localuser attribute has a value + printMsg("\nNo localuser has been found in the configuration of the directory. "); + if ($NO_INPUT_USER) { + printMsg("\nWe considered nobody as the localuser"); + $localuser = "nobody" ; + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\nUnder what user does your $Version.$Minor directory server run [nobody] ? ") ; + $localuser = <STDIN> ; + chomp($localuser); + $localuser = "nobody" if ($localuser eq ""); + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + if ($newuid) { + $Ask = 0 ; + } + else { + printMsg("\nError: $localuser is unknown from the system "); + } + } + } + } + else { + $localuser = $values[0]; # returns the first value (we should only have one localuser) + my $size = $#values ; + } + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + return ($localuser, $newuid, $newgid) ; + } + else { + return () ; + } +} + +sub getolduid_gid { + my $oldlocaluser ; + my $localuserAttr = "nsslapd-localuser"; + my $entry ; + if (! $isNT) { + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "CONFIG_NODE") { + $oldlocaluser = $entry->{$localuserAttr}[0] if ($entry->exists($localuserAttr)); + break ; + } + } + close(DSE); + ($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ; + return ($oldlocaluser, $olduid, $oldgid) ; + } + else { + return (); + } +} +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} + +################################ +# Need to migrate the credential. +# If the credential can not be migrated, leave it at it is +################################ +sub migrate_credential{ + my $entry_to_modify = shift; + my $credentials_attr = shift; + my @old_value = $entry_to_modify->getValues($credentials_attr); + my $migratecredExecName = 'migratecred'; + my $credOldHome = $oldHome; + my $credServerHome = $serverHome; + + if ($isNT) { + # oldHome may be pointing to the archived copy of the + # instance dir which may be different than the path that + # the instance was originally installed as on Windows. If + # this path is not the original install path, then the + # credential will not be migrated correctly. We should + # prompt the user on Windows for the correct path. + + print "\n\nThe old instance path must be the same as where it was"; + print "\ninitially installed, not where it was archived in order"; + print "\nfor this step to succeed. Please verify that the path"; + print "\nis correct. Note that case sensitivity is important here."; + print "\n\nOld Instance Directory: $credOldHome"; + print "\nIs this correct? (y/n): "; + chomp(my $answer = <STDIN>); + if (!($answer =~ /y|yes/i)) { + print "\nPlease enter the correct path for the old instance directory: "; + chomp($credOldHome = <STDIN>); + } + + print "\n\nThe new instance path must also be correct for this step"; + print "\nto succeed. Please verify that the path is correct. Note"; + print "\nthat case sensitivity is important here."; + print "\n\nNew Instance Directory: $credServerHome"; + print "\nIs this correct? (y/n): "; + chomp(my $answer = <STDIN>); + if (!($answer =~ /y|yes/i)) { + print "\nPlease enter the correct path for the new instance directory: "; + chomp($credServerHome = <STDIN>); + } + } +# print "\nMigratecred command is: ${quote}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}$migratecredExecName${quote} -o $credOldHome -n $credServerHome -c @old_value\n"; + + my @new_cred = `${quote}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}$migratecredExecName${quote} -o $credOldHome -n $credServerHome -c @old_value`; + + if ( $? == 0 ) + { + $entry_to_modify->setValues($credentials_attr, @new_cred); + } +} + diff --git a/ldap/admin/src/scripts/template-migrate6to7 b/ldap/admin/src/scripts/template-migrate6to7 new file mode 100644 index 00000000..26f117b6 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrate6to7 @@ -0,0 +1,3049 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Migrate a 6.x directory server to a 7.0 directory server + +####################################################################################################### +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Entry; +use Mozilla::LDAP::LDIF; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::localtime; +use File::Basename; +use Class::Struct ; + +####################################################################################################### + +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o oldInstancePath -n newInstancePath [-t tracelevel] [-L logfile]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - new 7.0 Directory Manager\n"); + print(STDERR " : -w password - new 7.0 Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for new 7.0 Directory Manager's password\n"); + print(STDERR " : -j filename - Read new 7.0 Directory Manager's password from file\n"); + print(STDERR " : -p port - new 7.0 Directory Server port\n"); + print(STDERR " : -o oldInstancePath - Path of the old instance to migrate \n"); + print(STDERR " : -n newInstancePath - Path of the new 7.0 instance\n"); + print(STDERR " : [-d dataPath] - Path to directory containing data files to import into new instance\n"); + print(STDERR " : [-v oldVersion] - Version of old instance (obtained by running $slapdExecName -v\n"); + print(STDERR " : [-t tracelevel] - (optional) specify the level of trace (0..3)\n"); + print(STDERR " : [-L logfile] - (optional) specify the file to log the migration report \n"); + } +######################################################################################################## + +BEGIN { + + require 'uname.lib' ; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + ${SEP} = $isNT ? ";" : ":" ; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + + # If this variable is set, all file/directory creation will make sure the mode + # and ownership of the destination is the same as the source + $PRESERVE = 1 if (!$isNT); + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + if ($isNT) { + $os = "WINNT"; + } else { + $os = &uname("-s"); + } + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + } + if ( $os eq "AIX" ) { + $dll_suffix = "_shr.a"; + } + elsif ( $os eq "HP-UX" ) { + $dll_suffix = ".sl"; + } + elsif ( $os eq "WINNT" ) { + $dll_suffix = ".dll"; + } + else { + $dll_suffix = ".so"; + } + $slapdExecName = $isNT ? 'slapd.exe' : './ns-slapd'; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; +} + +SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + + # old parameters + ${oldDir} = "" ; + ${oldname} = "" ; + ${oldHome} = "" ; + ${oldConfDir} = "" ; + ${oldlocaluser} ; + ${olduid} ; + ${oldgid} ; + + # new parameters + ${root} = "{{DS-ROOT}}" ; + ${type} = "" ; + ${newname} = "" ; + ${newport} = "" ; + ${rootDN} = "" ; + ${rootpwd} = "" ; + ${localhost} = "" ; + ${LogFileReport} = "" ; + ${newuid} ; + ${localuser} ; + ${newgid} ; + $NO_INPUT_USER = 0 ; # by default user can give inputs during the migration process + ${curdir} = getCwd(); + ${slapdExecDir} = "${root}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + # in 7.0 the replica Id is setup to a static value + $replicaIdvalue = 65535; + + # specify the level of trace + $TRACELEVEL=1; + + $LDAP_SERVER_UNREACHABLE = 81; + + # get input users + &getParameters() ; + ${oldDir} = &normalizeDir("${oldDir}"); + ${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; + ${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; + ${oldSchemaDir} = "${oldConfDir}schema${PATHSEP}"; + ${oldDSEldif} = "${oldConfDir}dse.ldif"; + ${serverHome} = "${root}${PATHSEP}$type-$newname" ; + ${schemaDir} = "$serverHome${PATHSEP}config${PATHSEP}schema${PATHSEP}"; + ${DSEldif} = "$serverHome${PATHSEP}config${PATHSEP}dse.ldif"; + ${ldif_rep} = "${oldConfDir}ldif${PATHSEP}" ; + ${oldSlapdExecDir} = "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + open(LOGFILE, ">> $LogFileReport"); + + printTrace("\noldDir: $oldDir, oldHome: $oldHome, \noldConfDir: $oldConfDir, \nldif_rep: $ldif_rep, \nrootDN: $rootDN, \nPort: $newport, \nNewname: $newname\n",3); + printTrace("\nLIB_PATH: $LIB_PATH",4); + + if (!(-d $serverHome)) { + printMsg("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + printMsg("\n$oldHome doesn't exist\n"); + exit(1); + } + + if ($olddatadir && !(-d $olddatadir)) { + print("\n$olddatadir doesn't exist\n"); + exit(1); + } + + +%HashParametersName = (); + +# The following hash displays only general server parameters to migrate under cn=config +%GeneralSrvParamToMigrate = ( + 'nsslapd-accesscontrol'=> '\n', + 'nsslapd-errorlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-logging-enabled'=> '\n', + 'nsslapd-auditlog-logging-enabled'=> '\n', + 'nsslapd-accesslog-level'=> '\n', + 'nsslapd-accesslog-logbuffering'=> '\n', + 'nsslapd-accesslog-logexpirationtime'=> '\n', + 'nsslapd-accesslog-logexpirationtimeunit'=> '\n', + 'nsslapd-accesslog-logmaxdiskspace'=> '\n', + 'nsslapd-accesslog-logminfreediskspace'=> '\n', + 'nsslapd-accesslog-logrotationtime'=> '\n', + 'nsslapd-accesslog-logrotationtimeunit'=> '\n', + 'nsslapd-accesslog-maxlogsize'=> '\n', + 'nsslapd-accesslog-maxLogsPerDir'=> '\n', + 'nsslapd-attribute-name-exceptions'=> '\n', + 'nsslapd-auditlog-logexpirationtime'=> '\n', + 'nsslapd-auditlog-logexpirationtimeunit'=> '\n', + 'nsslapd-auditlog-logmaxdiskspace'=> '\n', + 'nsslapd-auditlog-logminfreediskspace'=> '\n', + 'nsslapd-auditlog-logrotationtime'=> '\n', + 'nsslapd-auditlog-logrotationtimeunit'=> '\n', + 'nsslapd-auditlog-maxlogsize'=> '\n', + 'nsslapd-auditlog-maxLogsPerDir'=> '\n', + 'nsslapd-certmap-basedn'=> '\n', + 'nsslapd-ds4-compatible-schema'=> '\n', + 'nsslapd-enquote-sup-oc'=> '\n', + 'nsslapd-errorlog-level'=> '\n', + 'nsslapd-errorlog-logexpirationtime'=> '\n', + 'nsslapd-errorlog-logexpirationtimeunit'=> '\n', + 'nsslapd-errorlog-logmaxdiskspace'=> '\n', + 'nsslapd-errorlog-logminfreediskspace'=> '\n', + 'nsslapd-errorlog-logrotationtime'=> '\n', + 'nsslapd-errorlog-logrotationtimeunit'=> '\n', + 'nsslapd-errorlog-maxlogsize'=> '\n', + 'nsslapd-errorlog-maxlogsperdir'=> '\n', + 'nsslapd-groupevalnestlevel'=> '\n', + 'nsslapd-idletimeout'=> '\n', + 'nsslapd-ioblocktimeout'=> '\n', + 'nsslapd-lastmod'=> '\n', + 'nsslapd-listenhost'=> '\n', + 'nsslapd-maxdescriptors'=> '\n', + 'nsslapd-nagle'=> '\n', + 'nsslapd-readonly'=> '\n', + 'nsslapd-referralmode'=> '\n', + 'nsslapd-plugin-depends-on-name'=> '\n', + 'nsslapd-plugin-depends-on-type'=> '\n', + 'nsslapd-referral'=> '\n', + 'nsslapd-reservedescriptors'=> '\n', + 'nsslapd-rootpwstoragescheme'=> '\n', + 'nsslapd-schemacheck'=> '\n', + 'nsslapd-secureport'=> '\n', + 'nsslapd-security'=> '\n', + 'nsslapd-sizelimit'=> '\n', + 'nsslapd-ssl3ciphers'=> '\n', + 'nsslapd-timelimit'=> '\n', + 'passwordchange'=> '\n', + 'passwordchecksyntax'=> '\n', + 'passwordexp'=> '\n', + 'passwordhistory'=> '\n', + 'passwordinhistory'=> '\n', + 'passwordlockout'=> '\n', + 'passwordlockoutduration'=> '\n', + 'passwordmaxage'=> '\n', + 'passwordmaxfailure'=> '\n', + 'passwordminage'=> '\n', + 'passwordminlength'=> '\n', + 'passwordmustchange'=> '\n', + 'passwordresetfailurecount' => '\n', + 'passwordstoragescheme' => '\n', + 'passwordunlock' => '\n', + 'passwordwarning' => '\n' +); + +# the following hash displays global parameters related to database stored under cn=config,cn=ldbm database,cn=plugins,cn=config +%GlobalConfigLDBMparamToMigrate = ( + 'nsslapd-allidsthreshold' => '\n', + 'nsslapd-lookthroughlimit' => '\n', + 'nsslapd-mode' => '\n', + 'nsslapd-dbcachesize' => '\n', + 'nsslapd-cache-autosize' => '\n', + 'nsslapd-cache-autosize-split' => '\n', + 'nsslapd-db-transaction-logging' => '\n', + 'nsslapd-import-cachesize' => '\n' +); + +# the following hash displays specific parameters to each backends and stored under cn=DBname,cn=ldbm database,cn=plugins,cn=config +%LDBMparamToMigrate = ( + 'nsslapd-cachesize' => '\n', + 'nsslapd-cachememsize' => '\n', + 'nsslapd-readonly' => '\n', + 'nsslapd-require-index' => '\n' +); + + +%ChainingConfigParams = ( + 'nsactivechainingcomponents' => '\n', + 'nstransmittedcontrols' => '\n' + ); + +%ChainingDefaultInstanceConfigParams = ( + 'nsabandonedsearchcheckinterval' => '\n', + 'nsbindconnectionslimit' => '\n', + 'nsbindtimeout' => '\n', + 'nsbindretrylimit' => '\n', + 'nshoplimit' => '\n', + 'nsmaxresponsedelay' => '\n', + 'nsmaxtestresponsedelay' => '\n', + 'nschecklocalaci' => '\n', + 'nsconcurrentbindlimit' => '\n', + 'nsconcurrentoperationslimit' => '\n', + 'nsconnectionlife' => '\n', + 'nsoperationconnectionslimit' => '\n', + 'nsproxiedauthorization' => '\n', + 'nsreferralonscopedsearch' => '\n', + 'nsslapd-sizelimit' => '\n', + 'nsslapd-timelimit' => '\n' +); + +%changelog5params = ( + 'nsslapd-changelogmaxage' => '\n', + 'nsslapd-changelogmaxentries' => '\n' + ); + +@SNMPparams = ( + 'nssnmpenabled', + 'nssnmporganization', + 'nssnmplocation', + 'nssnmpcontact', + 'nssnmpdescription', + 'nssnmpmasterhost', + 'nssnmpmasterport', + 'nssnmpenabled', + 'aci' + ); + +%stdIncludes = ( + "." => "\n", + ".." => "\n", + "30ns-common.ldif " => "\n", + "50ns-mail.ldif " => "\n", + "50ns-news.ldif" => "\n", + "50iplanet-servicemgt.ldif"=> "\n", + "50netscape-servicemgt.ldif"=> "\n", + "50ns-mcd-browser.ldif" => "\n", + "50ns-proxy.ldif" => "\n", + "00core.ldif" => "\n", + "50ns-admin.ldif" => "\n", + "50ns-mcd-config.ldif " => "\n", + "50ns-value.ldif" => "\n", + "05rfc2247.ldif" => "\n", + "50ns-calendar.ldif" => "\n", + "50ns-mcd-li.ldif" => "\n", + "50ns-wcal.ldif" => "\n", + "05rfc2927.ldif" => "\n", + "50ns-certificate.ldif" => "\n", + "50ns-mcd-mail.ldif" => "\n", + "50ns-web.ldif" => "\n", + "10rfc2307.ldif" => "\n", + "50ns-compass.ldif" => "\n", + "50ns-media.ldif" => "\n", + "20subscriber.ldif" => "\n", + "50ns-delegated-admin.ldif"=> "\n", + "50ns-mlm.ldif" => "\n", + "25java-object.ldif" => "\n", + "50ns-directory.ldif" => "\n", + "50ns-msg.ldif" => "\n", + "28pilot.ldif" => "\n", + "50ns-legacy.ldif" => "\n", + "50ns-netshare.ldif" => "\n" +); + + +# Backends migrated (Backend CN attribute value) +@BACKENDS = () ; +# All pairs of suffix-backend are registered in this hashtable +%oldBackends = () ; + +#store the backend instances to migrate +@LDBM_backend_instances = (); + +#store the mapping tree +@Mapping_tree_entries = (); + +#store the suffix and the associated chaining backend +%oldChainingBackends = (); + +#store the multiplexor bind entries to migrate +%MultiplexorBindDNEntriesToMigrate = (); + +#store the Replica bind DN entries to migrate +%ReplicaBindDNEntriesToMigrate = (); + +# list of standard plugins +%stdPlugins = ( + "7-bit check" => "\n", + "acl plugin" => "\n", + "acl preoperation" => "\n", + "binary syntax" => "\n", + "case exact string syntax" => "\n", + "case ignore string syntax" => "\n", + "chaining database" => "\n", + "class of service" => "\n", + "country string syntax" => "\n", + "distinguished name syntax" => "\n", + "generalized time syntax" => "\n", + "integer syntax" => "\n", + "internationalization plugin" => "\n", + "ldbm database" => "\n", + "legacy replication plugin" => "\n", + "multimaster replication plugin" => "\n", + "octet string syntax" => "\n", + "clear" => "\n", + "crypt" => "\n", + "ns-mta-md5" => "\n", + "sha" => "\n", + "ssha" => "\n", + "postal address syntax" => "\n", + "referential integrity postoperation" => "\n", + "retro changelog plugin" => "\n", + "roles plugin" => "\n", + "telephone syntax" => "\n", + "uid uniqueness" => "\n", + "uri syntax" => "\n" + ); + +# list of indexes that have disappeared from the new schema compared to 6.x +%deniedIndexes = ( + 'dncomp' => "\n" +); + +@default_indexes = (); +@indexes = (); + +# list of user added Plugin's. In 7.0, they 'll need to be recompiled +@badPlugins = () ; + +@pluginAttrs = ( + "objectclass", + "cn", + "nsslapd-pluginpath", + "nsslapd-plugininitfunc", + "nsslapd-plugintype", + "nsslapd-pluginenabled", + "nsslapd-plugin-depends-on-type", + "nsslapd-pluginid", + "nsslapd-pluginversion", + "nsslapd-pluginvendor" + ); + +@nsds5replicaAttrs = ( + 'objectclass', + 'nsDS5ReplicaRoot', + 'nsDS5ReplicaType', + 'nsDS5ReplicaLegacyConsumer', + 'nsDS5flags', + 'nsDS5ReplicaId', + 'nsDS5ReplicaPurgeDelay', + 'nsDS5ReplicaBinddn', + 'cn', + 'nsDS5ReplicaReferral' + ); + +# array of replicas to migrate +@new6replicas = (); + +# array of replication agreements to migrate +@replicationAgreements = (); + +# compare LDIF standard config files with standard ones +CompareStdConfigFiles() ; +die "\n\n The version of product you want to migrate is not a 6.x Directory Server\n" unless ($oldVersion == 6) ; + +# Shutdown the legacy Directory instance +printTrace("\nShutdown the legacy Directory Server instance: ${oldHome}",0); +&stopServer($oldDir, 'slapd-'.$oldname); + +# get the hostname of the new LDAP server +my $LDAPservername = &getLDAPservername(); + +# get the uid and gid of the 7.0 slapd user +($localuser, $newuid, $newgid) = getuid_gid(); +# get the uid and gid of the 6.x slapd user +($oldlocaluser, $olduid, $oldgid) = getolduid_gid(); +printTrace("\n7.0 localuser: $localuser, uid: $newuid, gid: $newgid",2); +printTrace("\n6.x localuser: $oldlocaluser, uid: $olduid, gid: $oldgid",2); + +# backup 7.0 configuration files in <6server_root>/slapd-instancename/config +printTrace("\nBackup $serverHome${PATHSEP}config on $serverHome${PATHSEP}config_backup ...",0); +&backupConfigFiles(); + +# migrate the schema (need to stop and start the 7.0 server) +printTrace("\nMigrate the schema...",0); +MigrateSchema(); + +# start the server unless it is already started +&startServer() unless (isDirectoryAlive()); + +############### Connect to the 7.0 LDAP Directory Server ###################### +$ENV{"$LIB_PATH"} = $new_libpath; + +die "\n Migration aborted. Make sure your old and new Directory Server are installed on the same machine \n" if ( $LDAPservername == -1 ); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + +# Cconnection to 7.0 LDAP server is successful ! +printTrace("\nConnected to $Version.$Minor LDAP server",0) ; + +# Parse the main configuration file: dse.ldif +printTrace("\n\nParse the old DSE ldif file: $oldDSEldif *****",0, 1); +printTrace("\nThis may take a while ...\n",0); +&MigrateDSEldif(); + +#migrate LDBM backend instances +printTrace("\n\nMigrate LDBM backend instances...",0,1); +&migrateLDBM_backend_instances(); + +#migrate mapping tree entries +printTrace("\n\nMigrate mapping tree...",0,1); +&migrateMappingTree(); + +#migrate default indexes +printTrace("\n\nMigrate default indexes...",0,1); +migrateDefaultIndexes(); + +#migrate indexes +printTrace("\n\nMigrate indexes...",0,1); +migrateIndexes(); + +#migrate replicas +printTrace("\n\nMigrate replicas...",0,1); +&MigrateNSDS5_replica(); + +#migrate replication agreements +printTrace("\n\nMigrate replication agreements...",0,1); +&MigrateNSDS_replication_agreement(); + +#migrate key/cert databases +printTrace("\n\nMigrate key/cert databases...",0,1); +&MigrateSSL(); + +# migrate certmap.conf +printTrace("\n\nMigrate Certmap.conf...",0,1); +&MigrateCertmap() ; + +################## Close the connection to 7.0 LDAP Server ##################### +printTrace("\n\n***** Close the LDAP connection to the new Directory Server instance ***** ",0); +$conn->close; + + +################## stop the new instance and Export/Import the data, restart the server ################## +if (@BACKENDS) { + &stopServer($root,'slapd-'.$newname); + if ($olddatadir) { + printTrace("\nData already contained in $olddatadir...\n",0,1) ; + $ldif_rep = "$olddatadir${PATHSEP}"; + } else { + printTrace("\nData processing...\n",0,1) ; + # migrate data for each backend: 6.x -> LDIF files + &manydb2Ldif($ldif_rep); + } + + # migrate LDIF data to the new database: LDIF -> New + &manyLdif2db($ldif_rep); + &migrateChangelog(); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); + &startServer() unless (isDirectoryAlive()); +} +else { + printTrace("\nINFORMATION - There are no non-standard or non-already existing suffixes to migrate\n",0); + &migrateChangelog(); + printTrace("\n***** Migrate ReplicaBindDN entries...\n",0,1); + &importReplicaBindDNEntries(); + printTrace("\n***** Migrate MultiplexorBindDN entries...\n",0,1); + &importMultiplexorBindDNEntries(); +} + +printMsg("\n\n ****** End of migration ******\n\n"); + +close(LOGFILE); + + +########################################################################################### +# get input users +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # new instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe new instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # new DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-d") { # old instance LDIF data dir + if (! $olddatadir ) { + $olddatadir = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-v") { # old version + if (! $oldversionstr ) { + $oldversionstr = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + $NO_INPUT_USER = 1 ; + } elsif ("$ARGV[$i]" eq "-L") { # migration logfile + $LogFileReport = $ARGV[++$i] ; + } + else { + print("\nThe option $ARGV[$i] is not recognized"); + &usage() ; + exit(1); + } + $i++; + } + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + if ($exit) { + &usage() ; + exit(1); + } + +} + +################################################################################################### + +sub MigrateSchema{ + my $FilesChanged = ""; + my $AllDiffs = ""; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(SCHEMADIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldSchemaDir: $!"; + + foreach $file (readdir(SCHEMADIR)) { + if (! exists($stdIncludes{lc($file)})) { + my $newSchemaFile = $schemaDir . $file; + if (-f $newSchemaFile ) { + # The ldif file already exists. Make a diff and warn the user if different. + if (diff($newSchemaFile, $oldSchemaDir.$file)) { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $newSchemaFile); + } + } + else { + &stopServer($root,'slapd-'.$newname) if (isDirectoryAlive()); + $AllDiffs .= "\n$file"; + copyBinFile("$oldSchemaDir$file", $newSchemaFile); + } + } + } + closedir(SCHEMADIR); + if ($AllDiffs) { + printMsg("\n\n***********************************************************************"); + printMsg("\nThe following LDIF files have been migrated:"); + printMsg("$AllDiffs"); + printMsg("\n*************************************************************************\n\n"); + } + &startServer() if (! isDirectoryAlive()); +} + + +################################################################################################### +# This subroutine is used to parse the dse.ldif file and call specific routines to act with entries +sub MigrateDSEldif { + printTrace("\nMigrate DSE entries...",1); + my $tempoAlreadyDone = 0; + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + SWITCH: { + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + parseLDBM_backend_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "MAPPING_TREE"){ + parseMapping_tree($entry); + last SWITCH; + } + if ($typeOfEntry eq "DEFAULT_INDEX"){ + parseDefaultIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "INDEX"){ + parseIndex($entry); + last SWITCH; + } + if ($typeOfEntry eq "STANDARD_PLUGIN"){ + migrateStdPlugin($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_NODE"){ + migrateConfig_Node($entry); + last SWITCH; + } + if ($typeOfEntry eq "CONFIG_LDBM_DATABASE"){ + migrateConfig_LDBM_database($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_CONFIG"){ + migrateChainingBE_config($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHAINING_BACKEND_INSTANCE"){ + migrateChainingBE_instance($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS5_REPLICA"){ + parseNSDS5_replica($entry); + last SWITCH; + } + if ($typeOfEntry eq "NSDS_REPLICATION_AGREEMENT"){ + parseNSDS_replication_agreement($entry); + last SWITCH; + } + if ($typeOfEntry eq "CHANGELOG5"){ + migrateChangelog5($entry); + last SWITCH; + } + if ($typeOfEntry eq "REPLICATION"){ + migrateReplication($entry); + last SWITCH; + } + if ($typeOfEntry eq "SECURITY"){ + migrateSecurity($entry); + last SWITCH; + } + if ($typeOfEntry eq "SNMP"){ + migrateSNMP($entry); + last SWITCH; + } + } + + } + close(DSELDIF); +} + +############################################################################# +# returns the "type of an entry". If the entry is not to be migrated its type is "NOT_MIGRATED_TYPE" + +sub getTypeOfEntry{ + my $entry = shift; + my $DN = $entry->getDN(1) ; # 1 is to normalize the returned DN + if (($DN =~ /cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "LDBM_BACKEND_INSTANCE"; + } + if (($DN =~ /cn=mapping tree,cn=config$/i) && (isObjectclass($entry,"nsMappingTree"))) { + return "MAPPING_TREE"; + } + if (($DN =~ /cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsIndex"))) { + return "DEFAULT_INDEX"; + } + if (isObjectclass($entry,"nsIndex")) { + return "INDEX"; + } + if ((isObjectclass($entry,"nsSlapdPlugin")) && (isStdPlugin($entry))) { + return "STANDARD_PLUGIN"; + } + if ($DN =~ /^cn=config$/i) { + return "CONFIG_NODE"; + } + if ($DN =~ /^cn=config,cn=ldbm database,cn=plugins,cn=config$/i) { + return "CONFIG_LDBM_DATABASE"; + } + if (($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i) || ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i)){ + return "CHAINING_BACKEND_CONFIG"; + } + if (($DN =~ /cn=chaining database,cn=plugins,cn=config$/i) && (isObjectclass($entry,"nsBackendInstance"))) { + return "CHAINING_BACKEND_INSTANCE"; + } + if (isObjectclass($entry,"nsDS5Replica")) { + return "NSDS5_REPLICA"; + } + if (isObjectclass($entry,"nsDS5ReplicationAgreement")) { + return "NSDS_REPLICATION_AGREEMENT"; + } + if ($DN =~ /^cn=changelog5,cn=config$/i) { + return "CHANGELOG5"; + } + if (($DN =~ /cn=replication,cn=config$/i) && ($DN !~ /^cn=replication,cn=config$/i)) { + return "REPLICATION"; + } + if ($DN =~ /cn=encryption,cn=config$/i) { + return "SECURITY"; + } + if ($DN =~ /^cn=SNMP,cn=config$/i) { + return "SNMP"; + } + return "NOT_MIGRATED_TYPE"; +} + +############################################################################# + + + +############################################################################# +# returns 1 if the objectclass given in parameter is present in the objectclasses values of the entry +# given in parameter, 0 else + +sub isObjectclass { + my $entry = shift; + my $objectclass = shift; + return ($entry->hasValue("objectclass",$objectclass,1)); +} + +############################################################################# + +sub isStdPlugin { + my $entry = shift; + my $CN = $entry->{cn}[0]; + if (isObjectclass($entry,"nsSlapdPlugin")) { + return 1 if ($stdPlugins{lc($CN)}); + } + return 0; +} + + +############################################################################# + +sub alreadyExistsInNew{ + my $entry = shift; + my $mustExist = shift; + my $DN = $entry->getDN(1); # 1 to normalize the DN + # We have a name change of "uid uniqueness" plugin in DS6.x + # to "attribute uniqueness" + $DN =~ s/uid\ uniqueness/attribute\ uniqueness/ if ($DN =~ /uid\ uniqueness/); + return searchEntry($DN, $mustExist); +} + +############################################################################# +sub searchEntry { + my $DN = shift; + my $mustExist = shift; + my $res = $conn->search($DN, "base", "objectclass=*"); + my $cpt = 5; + if ($res) { + return $res; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to search $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->search($DN, "base", "objectclass=*"); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + return $res ; + } + elsif (($errorCode eq $LDAP_SERVER_UNREACHABLE) || ($mustExist)) { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** Failed to search: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + } + return 0; + } +} + + +############################################################################# + +sub addEntryToNew{ + my $entry = shift; + my $typeOfEntry = shift; + my $trace = shift; + my $res = $conn->add($entry); + my $DN = $entry->getDN(1); + my $cpt = 5; + if ($res) { + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to add $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->add($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Add successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry: Add Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + +############################################################################# + +sub updateEntry{ + my $entry = shift; + my $typeOfEntry = shift; + my $CHECK = shift; + my $trace = shift; + my $cpt = 5; + if ($CHECK) { + if (! hasChanged($entry, $typeOfEntry)) { + return 1; + } + } + my $res = $conn->update($entry); + my $DN = $entry->getDN(1); + if ($res) { + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1 ; + } + else { + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && cpt && (! $res)) { + printMsg("\ntry to reconnect to update $DN"); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $res = $conn->update($entry); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + if ($res){ + printTrace("\n$typeOfEntry - Update successfull: $DN",$trace); + return 1; + } + else { + my $msg = $conn->getErrorString(); + printMsg("\n\n*** $typeOfEntry - Update Failed: $DN"); + printMsg("\n*** Error Msg: $msg, Error code: $errorCode"); + return 0; + } + } +} + + +############################################################################# +# returns 1 if the entry to migrate and the current entry are different one another + +sub hasChanged { + my $entry = shift; + my $typeOfEntry = shift; + my $DN = $entry->getDN(1); + my $newEntry = searchEntry($DN,1); + return 1 if (! $newEntry); # we shoudn't be in that case ... + # do the stuff to check wether the entry has changed or not given its type + if (($typeOfEntry eq "DEFAULT_INDEX") || ($typeOfEntry eq "INDEX")){ + my @indexTypes = $entry->getValues("nsIndexType"); + my @newIndexTypes = $newEntry->getValues("nsIndexType"); + my @nsmatchingrules = $entry->getValues("nsmatchingrule"); + my @newMatchingRules = $newEntry->getValues("nsmatchingrule"); + return 1 if (Diffs(\@indexTypes, \@newIndexTypes)); + return 1 if (Diffs(\@nsmatchingrules,\@newMatchingRules)); + return 0; + } + if ($typeOfEntry eq "CHANGELOG5"){ + printTrace("\nCheck wether changelog has changed or not",3); + my @params = keys(%changelog5params); + foreach $param (@params){ + my @values = $entry->getValues($param); + my @newValues = $newEntry->getValues($param); + return 1 if (Diffs(\@values,\@newValues)); + } + return 0; + } + if ($typeOfEntry eq "SNMP"){ + foreach $param (@SNMPparams){ + my @values = $entry->getValues($param); + my @newValues = $newEntry->getValues($param); + return 1 if (Diffs(\@values,\@newValues)); + } + return 0; + } + # we don't know how to compare such type of entry => just return 1 + return 1 ; +} + +sub isAsystemIndex { + my $index = shift; + return ($index->hasValue("nsSystemIndex","true",1)); +} + + +sub updatePathInPluginArgs { + my $plugin = shift; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $cont = 1; + my $Unix_oldDir = ${oldDir} ; + my $Unix_root = ${root} ; + grep { s@\\@/@g } $Unix_oldDir if $isNT; + grep { s@\\@/@g } $Unix_root if $isNT; + while ($cont) { + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + $_ = $plugin->{$arg}[0] ; + s@$Unix_oldDir@$Unix_root@ig ; + s/$type-$oldname/$type-$newname/ig ; + $plugin->setValues($arg, $_) ; + } + else { + $cont = 0 ; + } + $argNum++; + } + return $plugin; +} + + +sub Diffs { + my $valuesToMigrate = shift; + my $currentValues = shift; + return 1 if (getDiff(\@{$valuesToMigrate},\@{$currentValues})); + return 1 if (getDiff(\@{$currentValues},\@{$valuesToMigrate})); + return 0 ; +} + +sub getDiff { + # we get references to arrays + my $elements = shift ; + my $existing_elements = shift ; + my %count = () ; + my %countEE = () ; + @diff = () ; + foreach $e (@{$elements}, @{$existing_elements}) { $count{$e}++ ;} + foreach $e (@{existing_elements}) { $countEE{$e}++ ;} + foreach $e (@{$elements}) { + # if $e is only present in @$elements, we push it to the diff array + if (($count{$e} == 1) && ($countEE{$e} == 0)) { + push @diff, $e ; + } + } + return @diff ; +} + +sub registerSuffix_Backend { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldBackends{$suffix} = $CN; +} + + +############################################################################# +# # +# # +# # +############################################################################# +sub migrateLDBM_backend_instances { + foreach $entry (@LDBM_backend_instances) { + my $DN = $entry->getDN(1); # 1 is to normalize the DN + my $CN = $entry->{cn}[0]; + my $expLdif; + my $confirm = "No"; + my $dest = "$serverHome${PATHSEP}db_backup" ; + my $newSlapdExecDir = "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + + if ($DN =~/cn=netscaperoot,cn=ldbm database/i){ + printTrace("\n\n*** INFORMATION - NetscapeRoot is NOT migrated",0); + } + else { + if(alreadyExistsInNew($entry)){ + printMsg("\n\n*** LDBM_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will overwrite existing database"); + printMsg("\nDo you want to continue Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("Do you want to export the existing data Yes/No [Yes] ?"); + my $answer = <STDIN> ; + if (!($answer =~ /n|no/i)) { + mkdir $dest, 0700 unless (-d $dest); + $expLdif = "$dest${PATHSEP}$CN.ldif"; + while (!($confirm =~ /y|yes/i)) { + printMsg("\nEnter the full pathname of the file [$expLdif]:") ; + $answer = <STDIN> ; + chomp($expLdif = $answer) unless ($answer eq "\n"); + printMsg("\nExisting data will be exported under $expLdif"); + printMsg("\nContinue Yes/No [No] ?"); + $confirm = <STDIN>; + } + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($newSlapdExecDir) or die "\nCould not change directory to $newSlapdExecDir: $!\n"; + printTrace("\nNow backing up database $CN in $expLdif\n",0); + &stopServer($root,'slapd-'.$newname); + &db2Ldif($expLdif, $CN, $serverHome); + &startServer() unless (isDirectoryAlive()); + } + push @BACKENDS, $CN; + } else { + printMsg("\n*** Migration will not update it"); + break; + } + } else { + printTrace("\nWe should add the backend instance $DN",3); + my $suffixarg = "nsslapd-suffix" ; + my $suffixname= $entry->{$suffixarg}[0] ; + my $newEntry = $conn->newEntry() ; + $newEntry->setDN($DN); + $newEntry->setValues("objectclass", "top", "extensibleObject", "nsBackendInstance" ); + $newEntry->setValues("cn", $CN ); + $newEntry->setValues($suffixarg, $suffixname); + my @params = keys(%LDBMparamToMigrate); + foreach $param (@params) { + my @values = $entry->getValues($param); + $newEntry->setValues($param, @values) if (@values); + } + if (addEntryToNew($newEntry, "LDBM_BACKEND_INSTANCE",1)) { + push @BACKENDS, $CN; + } + } + } + } +} + +sub parseLDBM_backend_instance { + my $entry = shift; + ®isterSuffix_Backend($entry); + push @LDBM_backend_instances, $entry; +} + +############################################################################# +sub migrateMappingTree { + foreach $entry (@Mapping_tree_entries) { + my $DN = $entry->getDN(1); # 1 si to normalize the DN + if ($DN =~/cn=\"o=netscaperoot\",cn=mapping tree,cn=config/i){ + # DO NOTHING + } + else { + if(alreadyExistsInNew($entry)){ + printMsg("\n\n*** MAPPING_TREE - $DN already exists"); + printMsg("\n*** Migration will not add the suffix"); + } + else { + addEntryToNew($entry, "MAPPING_TREE",1); + } + } + } +} + + +sub parseMapping_tree{ + my $entry = shift; + push @Mapping_tree_entries, $entry; +} + +############################################################################# +sub migrateDefaultIndexes { + foreach $index (@default_indexes) { + my $CN = $index->{cn}[0]; + my $newIndex ; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)})) { + if ($newIndex = alreadyExistsInNew($index)) { + if (! isAsystemIndex($newIndex)) { + updateEntry($index, "DEFAULT_INDEX", 1, 2); + } + } + else { + addEntryToNew($index, "DEFAULT_INDEX", 2); + } + } + } +} + + +sub parseDefaultIndex{ + my $index = shift; + push @default_indexes, $index; +} + +############################################################################# + +sub migrateIndexes { + foreach $index (@indexes) { + my $CN = $index->{cn}[0]; + my $newIndex; + if ((! isAsystemIndex($index)) && (! $deniedIndexes{lc($CN)}) && (DN !~ /cn=netscaperoot,cn=index/i)){ + if ($newIndex = alreadyExistsInNew($index)) { + if (! isAsystemIndex($newIndex)) { + updateEntry($index, "INDEX", 1, 2); + } + } + else { + addEntryToNew($index, "INDEX", 2); + } + } + } +} + +sub parseIndex{ + my $index = shift; + push @indexes, $index; +} + +############################################################################# + +sub newLDIFplugin { + my $currentPlugin = shift; + my $DN = $currentPlugin->getDN(1); + my $newPlugin = $conn->newEntry() ; + $newPlugin->setDN($DN); + foreach $Attr (@pluginAttrs) { + my @values = $currentPlugin->getValues($Attr); + $newPlugin->setValues($Attr, @values) if (@values); + } + return $newPlugin; +} + +sub migrateStdPlugin{ + my $plugin = shift; + my $DN = $plugin->getDN(1); + my $pluginEnable = "nsslapd-pluginEnabled"; + my $argNum = 0; + my $argPrefix = "nsslapd-pluginarg"; + my $currentPlugin ; + if ($currentPlugin = alreadyExistsInNew($plugin, 1)) { + $plugin = updatePathInPluginArgs($plugin); + my $pluginEnableValue = $plugin->{$pluginEnable}[0]; + my $cont = 1; + my $pluginHasChanged = 0; + my $newPlugin = &newLDIFplugin($currentPlugin); + if (! $currentPlugin->hasValue($pluginEnable,$pluginEnableValue,1)){ + $newPlugin->setValues($pluginEnable, $pluginEnableValue); + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + while($cont){ + my $arg = $argPrefix . $argNum ; + if ($plugin->exists($arg)) { + my @values = $plugin->getValues($arg); + my $value = $values[0] ; + $newPlugin->setValues($arg, $value) if (@values); + if ($currentPlugin->exists($arg)) { + if (! $currentPlugin->hasValue($arg,$value,1)) { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + $pluginHasChanged = 1 unless ($pluginHasChanged); + } + } + else { + if ($currentPlugin->exists($arg)) { + # Just Warn the user. Do nothing. + printTrace("\nCompared to the old instance, the current new plugin $DN belongs this attribute: $arg",2); + } + else { + $cont = 0 ; + } + } + $argNum++; + } + updateEntry($newPlugin, "STANDARD_PLUGIN", 0, 1) if ($pluginHasChanged); + } +} + +############################################################################# + +sub migrateConfig_Node{ + my $config_node = shift; + my @params = keys(%GeneralSrvParamToMigrate); + my $hasChanged = 0; + my $newConfigNode; + if ($newConfigNode = alreadyExistsInNew($config_node, 1)){ + foreach $param (@params) { + if ($config_node->exists($param)){ + my @valuesToMigrate = $config_node->getValues($param); + if (@valuesToMigrate){ + if ($newConfigNode->exists($param)){ + my @currentValues = $newConfigNode->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newConfigNode->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + else { + $newConfigNode->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + printTrace("\nParam to update: $param with value @valuesToMigrate",3); + } + } + } + } + updateEntry($newConfigNode, "CONFIG_NODE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateConfig_LDBM_database{ + my $config_ldbm = shift; + my @params = keys(%GlobalConfigLDBMparamToMigrate); + my $hasChanged = 0; + my $newConfigLdbm ; + if ($newConfigLdbm = alreadyExistsInNew($config_ldbm, 1)) { + foreach $param (@params) { + if ($config_ldbm->exists($param)){ + my @valuesToMigrate = $config_ldbm->getValues($param); + if (@valuesToMigrate){ + if ($newConfigLdbm->exists($param)){ + my @currentValues = $newConfigLdbm->getValues($param); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newConfigLdbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $newConfigLdbm->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($newConfigLdbm, "CONFIG_LDBM_DATABASE", 0, 1) if ($hasChanged); + } +} + +############################################################################# + +sub migrateChainingBE_config{ + my $chaining_config = shift; + my $DN = $chaining_config->getDN(1); + my @params = (); + my $hasChanged = 0; + my $newChainingConfig; + if ($DN =~ /^cn=config,cn=chaining database,cn=plugins,cn=config$/i){ + $newChainingConfig = searchEntry("cn=config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingConfigParams); + } + if ($DN =~ /^cn=default instance config,cn=chaining database,cn=plugins,cn=config$/i){ + $newChainingConfig = searchEntry("cn=default instance config,cn=chaining database,cn=plugins,cn=config"); + @params = keys(%ChainingDefaultInstanceConfigParams); + } + foreach $param (@params) { + if ($chaining_config->exists($param)){ + my @valuesToMigrate = $chaining_config->getValues($param); + if (@valuesToMigrate){ + printTrace("\nParam: $param values To migrate: @valuesToMigrate",3); + if ($newChainingConfig->exists($param)){ + my @currentValues = $newChainingConfig->getValues($param); + printTrace("\nParam: $param new current values: @currentValues",3); + if (Diffs(\@valuesToMigrate, \@currentValues)) { + $newChainingConfig->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + else { + $newChainingConfig->setValues($param, @valuesToMigrate); + $hasChanged = 1 unless ($hasChanged); + } + } + } + } + updateEntry($newChainingConfig, "CHAINING_BACKEND_CONFIG", 0, 1) if ($hasChanged); +} + +############################################################################# + +sub registerSuffix_ChainingBE { + my $ldbmDatabase = shift; + my $CN = $ldbmDatabase->{cn}[0]; + my $suffixArg = "nsslapd-suffix"; + my $suffix = $ldbmDatabase->{$suffixArg}[0]; + $oldChainingBackends{$suffix} = $CN; +} + +sub storeMultiplexorBindDN { + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + if ($chaining_instance->exists("nsMultiplexorBindDN")){ + my $bindDN = $chaining_instance->{nsMultiplexorBindDN}[0]; + my $newBindDN = searchEntry($bindDN); + if (! $newBindDN){ + # the bindDN entry doesn't yet exist in new => it will have to be migrated + $MultiplexorBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in new + } + } + +} + +sub importMultiplexorBindDNEntries { + # import all entries present in @MultiplexorBindDNEntriesToMigrate in new + my @MultiplexorBindDNs = keys (%MultiplexorBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + foreach $bindDN (@MultiplexorBindDNs) { + printTrace("\nimportMultiplexorBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # check wether the backend has been imported in new or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the binf DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to new + $conn->close if ($conn); +} + +sub migrateChainingBE_instance{ + my $chaining_instance = shift; + my $DN = $chaining_instance->getDN(1); + ®isterSuffix_ChainingBE($chaining_instance); + if (alreadyExistsInNew($chaining_instance)) { + # already exists + printMsg("\n\n*** CHAINING_BACKEND_INSTANCE - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($chaining_instance, "nsmultiplexorcredentials"); + addEntryToNew($chaining_instance, "CHAINING_BACKEND_INSTANCE", 1); + storeMultiplexorBindDN($chaining_instance); + } +} + +############################################################################# + +# create a new LDIF representation of a new replica consumer +sub newLDIFreplica { + my $replica = shift; + my $DN = $replica->getDN(1); + my $newReplica = $conn->newEntry() ; + my $MASTER_OR_MULTIMASTER = "3" ; + $newReplica->setDN($DN); + foreach $Attr (@nsds5replicaAttrs) { + my @values = $replica->getValues($Attr); + $newReplica->setValues($Attr, @values) if (@values); + } + my $replicaType = $replica->{nsDS5ReplicaType}[0]; + if ($replicaType eq $MASTER_OR_MULTIMASTER) { + my @nsState = $replica->getValues("nsState"); + # nsState omitted because it is incomatible between 32 and 64 bit + # servers. Bug 624441 + # $newReplica->setValues("nsState", @nsState); + } + else { + $newReplica->setValues("nsDS5ReplicaId", $replicaIdvalue); + } + return $newReplica; +} + +sub MigrateNSDS5_replica{ + foreach $replica (@new6replicas) { + my $DN = $replica->getDN(1); + my $newReplica; + my @removeAttrs = qw(nsstate nsds5replicaname nsds5replicachangecount); + for (@removeAttrs) { + $replica->remove($_); + } + if (alreadyExistsInNew($replica)) { + # replica already exists + printMsg("\n\n*** NSDS5_REPLICA - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + $newReplica = &newLDIFreplica($replica); + addEntryToNew($newReplica, "NSDS5_REPLICA", 1); + } + storeReplicaBindDN($replica); + } +} + +sub parseNSDS5_replica{ + my $replica = shift; + push @new6replicas, $replica; +} + +sub storeReplicaBindDN { + my $replica = shift; + my $DN = $replica->getDN(1); + if ($replica->exists("nsDS5ReplicaBindDN")){ + my $bindDN = $replica->{nsDS5ReplicaBindDN}[0]; + my $newBindDN = searchEntry($bindDN); + if (! $newBindDN){ + # the bindDN entry doesn't yet exist in new => it will have to be migrated + $ReplicaBindDNEntriesToMigrate{$bindDN}="\n" ; + printTrace("\nThe bindDN: $bindDN need to be migrated",3); + } + else { + # do nothing as the entry already exists in new + } + } +} + + +sub importReplicaBindDNEntries { + # import all entries present in @ReplicaBindDNEntriesToMigrate in new + my @ReplicaBindDNs = keys (%ReplicaBindDNEntriesToMigrate); + my $ldif_dir = $ldif_rep; + my $replBind_entry = ""; + my @bindDN_elements = ""; + my $bindDN_parent = ""; + my $parentBind_entry = ""; + foreach $bindDN (@ReplicaBindDNs) { + printTrace("\nimportReplicaBindDNEntries: bindDN to migrate: $bindDN",3); + # get the backend in which is stored the bind DN entry + my $backendtoExportFrom = getBackendtoExportFrom($bindDN); + printTrace("\nbackendtoExportFrom is: $backendtoExportFrom",3); + # If backend is from config, read the entry from dse.ldif and add to new - NGK + if ($backendtoExportFrom eq "cn=config") { + my $norm_bindDN = normalizeDN($bindDN); + @bindDN_elements = ldap_explode_dn($norm_bindDN, 0); +# @bindDN_elements = split(/,/,$norm_bindDN); + my $junk = shift(@bindDN_elements); + if ($#bindDN_elements >= 1) { + $bindDN_parent = normalizeDN(join(",", @bindDN_elements)); + } + printTrace("\nOpening DSE.ldif",3); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF); + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1); + if ($DN eq $norm_bindDN) { + $replBind_entry = $entry; + } + if ($bindDN_parent ne "") { + if ($DN eq $bindDN_parent) { + $parentBind_entry = $entry; + } + } + } + close(DSELDIF); + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + if ($bindDN_parent ne "") { + addEntryToNew($parentBind_entry, BINDDN_PARENT, 0); + } + printTrace("\nAdding BindDN with addEntryToNew",3); + addEntryToNew($replBind_entry, BINDDN, 0); + } else { + # check wether the backend has been imported in new or not + if (! alreadyMigrated($backendtoExportFrom)) { + if ($backendtoExportFrom ne $NULL) { + # if not imported => we need to import the bind DN entry + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + &ExportAndAddEntry($bindDN, $backendtoExportFrom, $ldif_dir); + } + else { + # do nothing + } + } + } + } + # remove the empty ldif directory + rmdir($ldif_dir) if (-d $ldif_dir); + # close the LDAP connection to new + $conn->close if ($conn); +} + +sub alreadyMigrated { + my $backendToCheck = shift; + foreach $backend (@BACKENDS) { + return 1 if ($backend eq $backendToCheck); + } + return 0 ; +} + +sub belongsSuffix { + my $suffix = shift; + my $bindDN = shift; + return ($bindDN =~ /$suffix\s*$/i); +} + +sub length { + my $suffix = shift; + my $count = 0; + while ($suffix =~ /./g) { + $count++; + } + return $count ; +} + +sub getBackendtoExportFrom { + my $bindDN = shift ; + my $sizeOfSuffix = 0 ; + my $NULL = ""; + my @oldSuffixes = keys(%oldBackends); + my @oldChainingSuffixes = keys(%oldChainingBackends); + my $bindDN_backend = $NULL; + my $config = "cn=config"; + + my $norm_bindDN = normalizeDN($bindDN); + # Check if bindDN exists in cn=config - NGK + if (belongsSuffix($config,$norm_bindDN)) { + $bindDN_backend = $config; + printTrace("\ngetBackendtoExportFrom: bindDN_backend: $bindDN_backend",3); + } else { + foreach $suffix (@oldSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is: $suffix",3); + if ((belongsSuffix($suffix,$norm_bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $oldBackends{$suffix}; + printTrace("\ngetBackendtoExportFrom: bindDN_backend: $bindDN_backend, sizeOfSuffix: $sizeOfSuffix",3); + } + } + foreach $suffix (@oldChainingSuffixes){ + printTrace("\ngetBackendtoExportFrom: suffix to compare with is a chained suffix: $suffix",3); + if ((belongsSuffix($suffix,$norm_bindDN)) && (length($suffix) > $sizeOfSuffix)) { + printMsg("\n\n*** Entry stored on a remote backend - $norm_bindDN"); + printMsg("\n*** We don't migrate it"); + return $NULL; + } + } + } + return $bindDN_backend; +} + + +sub getBackendtoImportTo { + my $bindDN = shift; + my $sizeOfSuffix = 0; + my $NULL = ""; + my $suffixArg = "nsslapd-suffix"; + my $bindDN_backend = $NULL; + open( DSELDIF, "< $DSEldif" ) || die "Can't open $DSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "LDBM_BACKEND_INSTANCE"){ + my $suffix = $entry->{$suffixArg}[0]; + if ((belongsSuffix($suffix,$bindDN)) && (length($suffix) > $sizeOfSuffix)) { + $sizeOfSuffix = length($suffix); + $bindDN_backend = $entry->{cn}[0]; + } + } + } + close(DSELDIF); + return $bindDN_backend ; +} + + +sub ExportAndAddEntry { + my $DN = shift; + my $backendtoExportFrom = shift; + my $ldif_dir = shift; + my $ldif = "$ldif_dir${PATHSEP}$backendtoExportFrom.ldif" ; + # first: export entry pointed out by the $DN to $ldif file + $ENV{"$LIB_PATH"}=$old_libpath; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "\ncan't create $ldif_dir to store temporary ldif files\n"; + } + chdir($oldSlapdExecDir) or die "\nCould not change directory to $oldSlapdExecDir: $!\n"; + &db2Ldif($ldif, $backendtoExportFrom, $oldHome, $DN); + chdir($curdir) or die "\nCould not change directory to $curdir: $!\n"; + + # then: Add it to new + if (! $conn) { + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + } + open( BINDDNLDIF, "< $ldif" ) || die "\nCan't open $ldif: $!: \n"; + my $in = new Mozilla::LDAP::LDIF(*BINDDNLDIF) ; + while ($entry = readOneEntry $in) { + my $entryDN = $entry->getDN(1); + if ($DN eq $entryDN) { + addEntryToNew($entry, "nsds5ReplicaBindDN", 0); + } + } + close(BINDDNLDIF); + # remove the ldif file after the import + unlink($ldif) ; +} + +############################################################################# +sub MigrateNSDS_replication_agreement { + foreach $replicationAgreement (@replicationAgreements) { + my $DN = $replicationAgreement->getDN(1); + if (alreadyExistsInNew($replicationAgreement)){ + # replication agreement already exists + printMsg("\n\n*** NSDS_REPLICATION_AGREEMENT - $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + &migrate_credential($replicationAgreement, "nsDS5ReplicaCredentials"); + addEntryToNew($replicationAgreement, "NSDS_REPLICATION_AGREEMENT", 1); + } + } +} + + +sub parseNSDS_replication_agreement{ + my $replicationAgreement = shift; + push @replicationAgreements, $replicationAgreement ; +} + +############################################################################# + +sub migrateChangelog5{ + my $changelog = shift; + my $DN = $changelog->getDN(1); + my $changelogdir = "nsslapd-changelogdir"; + if (alreadyExistsInNew($changelog)){ + # cn=changelog5,cn=config already exists in new + my $newChangelog = searchEntry($DN); + my @newChangelogdir = $newChangelog->getValues($changelogdir); + $changelog->setValues($changelogdir, @newChangelogdir); + updateEntry($changelog, "CHANGELOG5", 0, 1); + } + else { + # cn=changelog5,cn=config need to be created in new. + # the changelogdir value must be setup to <new_root_server>/slapd-instance/changelogdb + $changelog->setValues($changelogdir,"${serverHome}${PATHSEP}changelogdb"); + addEntryToNew($changelog, "CHANGELOG5", 1); + } +} + + +sub migrateChangelog { + my $oldchangelogdir = ""; + my $newchangelogdir = ""; + my $changelogdir = "nsslapd-changelogdir"; + my $CL5DN = "cn=changelog5,cn=config"; + printTrace("\n\n***** Migrate Changelog...",0,1); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF); + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "CHANGELOG5"){ + $oldchangelogdir = ($entry->getValues($changelogdir))[0]; + } + } + close(DSELDIF); + if ($oldchangelogdir) { + # If using olddatadir to migrate from, the path of the changelogdb + # from the dse.ldif may not match the path where the old server + # root was archived. We may need to modify oldchangelogdir so the + # copy of the changelog files succeeds. + unless(-e $oldchangelogdir) { + if($olddatadir) { + my @cldbpath = split(/\//,$oldchangelogdir); + until($cldbpath[0] =~/^slapd-/) { + shift(@cldbpath); + } + my $tmpcldbpath = join(${PATHSEP}, @cldbpath); + $oldchangelogdir = "$oldDir${PATHSEP}$tmpcldbpath"; + } + # If oldchangelogdir still looks to be wrong, prompt for the + # location instead of just failing on the copydir operation + # and bombing out of the migration. + unless(-e $oldchangelogdir) { + print("\n\nThe old changelog directory \"$oldchangelogdir\" doesn't exist. Please enter the correct path: "); + $oldchangelogdir = <STDIN>; + } + } + &startServer() unless (isDirectoryAlive()); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + my $newChangelog = searchEntry($CL5DN); + $newchangelogdir = ($newChangelog->getValues($changelogdir))[0]; + stopServer($root,'slapd-'.$newname); + printTrace("\ncopying $oldchangelogdir${PATHSEP}* to $newchangelogdir",3); + copyDir("$oldchangelogdir","$newchangelogdir"); + + # We need to modify the DBVERSION file for a new verision of the db + open(DBVERSION,">$newchangelogdir${PATHSEP}DBVERSION") || die "Can't overwrite $newchangelogdir${PATHSEP}DBVERSION: $! "; + print DBVERSION "Changelog5/NSMMReplicationPlugin/3.0"; + close(DBVERSION); + + &startServer() unless (isDirectoryAlive()); + } +} + +############################################################################# + +sub migrateReplication{ + my $replication = shift; + my $DN = $replication->getDN(1); + if (alreadyExistsInNew($replication)){ + # replication agreement already exists + printMsg("\n\n*** $DN already exists"); + printMsg("\n*** Migration will not update it"); + } + else { + addEntryToNew($replication, "REPLICATION", 1); + } +} + +############################################################################# + +sub migrateSecurity{ + my $security = shift; + if ($entry->hasValue("objectClass", "nsEncryptionConfig")) { + my $certfile = "alias/slapd-" . $newname . "-cert8.db"; + my $keyfile = "alias/slapd-" . $newname. "-key3.db"; + $entry->setValues("nsCertfile",$certfile) if ! $entry->hasValue("nsCertfile",$certfile); + $entry->setValues("nsKeyfile",$keyfile) if ! $entry->hasValue("nsKeyfile",$keyfile); + } + if (alreadyExistsInNew($security)){ + # already exists in new + updateEntry($security, "SECURITY", 0, 1); + } + else { + addEntryToNew($security, "SECURITY", 1); + } +} + +############################################################################# + +sub migrateSNMP{ + my $snmp = shift; + if (alreadyExistsInNew($snmp)){ + # already exists in new + updateEntry($snmp, "SNMP", 0, 1); + } + else { + addEntryToNew($snmp, "SNMP", 1); + } +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + my $sep = shift ; + + if ($sep) { + print "\n-------------------------------------------------------------------------"; + print LOGFILE "\n-------------------------------------------------------------------------"; + } + + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg ; + } +} + +############################################################################# +# this subroutine implements a very stupid version of diff + +sub diff { + my $f1 = shift; + my $f2 = shift; + my $lineToBeginWith = shift; + my $NULL = "" ; + my $diff_f1 = $NULL ; + my $diff_f2 = $NULL ; + my $retval = $NULL ; + my $ret; + open(F1, "$f1") or die "Could not open file $f1"; + open(F2, "$f2") or close(F1), die "Could not open file $f2"; + + while (defined($l1 = <F1>)) { + if ($lineToBeginWith){ + $lineToBeginWith -- ; + next ; + } + next if ($l1 =~ /^\#/); + $ret = defined($l2 = <F2>); + if ($ret) { + $ret = defined($l2 = <F2>) while ($ret && ($l2 =~ /^\#/)) ; + if ($ret) { + if (!($l1 eq $l2)) { + + # ignore whitespace + $l1_clean = $l1 ; + $l2_clean = $l2 ; + $l1_clean =~ s/\s//g; + $l2_clean =~ s/\s//g; + + if (!($l1_clean eq $l2_clean)) { + $diff_f1 .= "${l1}" unless ($l1_clean eq $NULL); + $diff_f2 .= "${l2}" unless ($l2_clean eq $NULL); + } + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + + while (defined($l2 = <F2>)) { + if (($l2 =~ /^\#/) || ($l2 =~ /^\s*$/)) { + next ; + } + else { + $diff_f2 .= "${l2}" ; + } + } + + close(F1); + close(F2); + + $retval .= "- differences present in your config file but not in standard file:\n\n". "$diff_f1\n" if ($diff_f1) ; + $retval .= "- differences present in standard file but not in your config file:\n\n" . "$diff_f2" if ($diff_f2) ; + return $retval ; +} + +sub CompareStdConfigFiles { + # Compare each configuration file against its default version. If it has changed, + # notify the user that the file has changed and will need to be checked by the + # user. This should be safe to do because there should be no path information + # stored in these conf files, which are just schema stuff. + # printTrace("\nCheck if standard configuration files have changed",3); + + # get the version of the DS to migrate + ($oldVersion, $oldMinor) = &getVersion($oldDir, $oldversionstr); + # get the version of the new DS + ($Version, $Minor) = &getVersion($root); + + # get old LIB_PATH + $old_libpath = &getLibPath($oldDir, $oldVersion, $oldMinor); + # get new LIB_PATH + $new_libpath = &getLibPath($root, $Version, $Minor); + + my $origFilePath = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}install${PATHSEP}schema${PATHSEP}" ; + my $FilesChanged = ""; + my $AllDiffs = "***********************************************************************"; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + opendir(CONFDIR, $oldSchemaDir) or + die "Error: could not open migrated config dir $oldConfDir: $!"; + + foreach $file (readdir(CONFDIR)) { + $origFile = $origFilePath . $file ; + $configFile = $oldSchemaDir . $file ; + if (( exists($stdIncludes{lc($file)})) && (-f $origFile)) { + $diffs = &diff($configFile, $origFile, $lineToBegin); + $lineToBegin = 0 if $lineToBegin ; + if ($diffs) { + $FilesChanged .= "\n$configFile"; + $AllDiffs .= "\n$configFile is different than the standard configuration file" ; + $AllDiffs .= "\nYou will need to check this file and make sure its changes are compatible "; + $AllDiffs .= "with the new directory server\nHere are the differences:\n"; + $AllDiffs .= "$diffs \n\n"; + $AllDiffs .= "***********************************************************************"; + } + else { + $NoChanges .= "\n$configFile"; + } + } + } + closedir(CONFDIR); + +if ($FilesChanged) { + printTrace("\nNo changes to old configuration files:$NoChanges",3) ; + printTrace("\n***********************************************************************",3) ; + printMsg("\nThe following standard files have been modified: $FilesChanged"); + if ($NO_INPUT_USER) { + # do nothing + } + else { + printMsg("\nDo you want to see the differences Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("$AllDiffs"); + } + printMsg("\nDo you want to continue the migration Yes/No [No] ?"); + $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + } + } +} + + + +############################################################################# + +# this is used to run the system() call, capture exit and signal codes, +# and die() upon badness; the first argument is a directory to change +# dir to, if any, and the rest are passed to system() +sub mySystem { + my $rc = &mySystemNoDie(@_); + my ($dir, @args) = @_; + if ($rc == 0) { +# success + } elsif ($rc == 0xff00) { + die "Error executing @args: error code $rc: $!"; + } elsif ($rc > 0x80) { + $rc >>= 8; + die "Error executing @args: error code $rc: $!"; + } else { + if ($rc & 0x80) { + $rc &= ~0x80; + } + die "Error executing @args: received signal $rc: $!"; + } + + # usually won't get return value + return $rc; +} + +# This version does not die but just returns the error code +sub mySystemNoDie { + my ($dir, @args) = @_; + if ($dir && ($dir ne "")) { + chdir($dir) or die "Could not change directory to $dir: $!"; + } + my $cmd = $args[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = 0xffff & system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd @fixargs\n"; + $rc = 0xffff & system {$cmd} @fixargs; + } + chdir(${curdir}) or die "Could not change directory to $curdir: $!"; + return $rc; +} + +########################################################################################### +# # +# Export/Import of the backends in @BACKENDS # +# # +########################################################################################### + +sub manydb2Ldif { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}=$old_libpath; + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "can't create $ldif_dir to store temporary ldif files"; + } + chdir($oldSlapdExecDir) or die "Could not change directory to $oldSlapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &db2Ldif($ldif, $backend, $oldHome); + } + print " Done.\n"; + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + +sub db2Ldif { + my $ldif = shift ; + my $backend = shift ; + my $home = shift ; + my $include_suffix = shift ; + my $db2ldif_param ; + if ($include_suffix) { + $db2ldif_param = "db2ldif -r -D $home -n $backend -a $ldif -s \"$include_suffix\""; + } + else { + $db2ldif_param = "db2ldif -r -D $home -n $backend -a $ldif"; + } + open(DB2LDIF, "${quote}${quote}$slapdExecName${quote} $db2ldif_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + my $ii = 0; + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + printMsg($_); + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the 6.x slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif) { + chown( $newuid, $newgid, $ldif) or printMsg("\nUnable to change the ownership of $ldif to $localuser") ; + } + } +} + +sub manyLdif2db { + my $ldif_dir = shift; + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($slapdExecDir) or die "Could not change directory to $slapdExecDir: $!"; + foreach $backend (@BACKENDS) { + my $ldif = "${ldif_dir}$backend.ldif" ; + &Ldif2db($ldif, $backend); + } + # remove the empty ldif directory + # but not if using the data dir + if (!$olddatadir) { + rmdir($ldif_dir); + } + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + + +sub Ldif2db { + my $ldif = shift ; + my $backend = shift ; + my $ldif2db_param = "ldif2db -D $serverHome -n $backend -i $ldif"; + open(LDIF2DB, "${quote}${quote}$slapdExecName${quote} $ldif2db_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + while (<LDIF2DB>) { + printMsg($_); + } + close(LDIF2DB); + # remove the ldif file after the import + # but not if using the data dir + if (!$olddatadir) { + unlink($ldif) ; + } +} + + +########################################################################################### +# # +# Running/Stopping the Server # +# # +########################################################################################### + + + +sub isDirectoryAlive { + die "\n Migration aborted. Make sure your old and new Directory Servers are installed on the same machine \n" if ( $LDAPservername == -1 ); + my $test_conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd); + if ($test_conn) { + $test_conn->close(); + return 1; + } + else { + return 0 ; + } +} + + +sub startServer { + my $instanceDir = ${serverHome} ; + my $errLog = $instanceDir . $PATHSEP . 'logs' . $PATHSEP . 'errors'; + # emulate tail -f + # if the last line we see does not contain "slapd started", try again + my $done = 0; + my $started = 0; + my $code = 0; + my $lastLine = ""; + my $timeout = time + 240; # 4 minutes + $ENV{"$LIB_PATH"}=$new_libpath; + + my $startCmd = $instanceDir . $PATHSEP . 'start' . $script_suffix; + if (! -f $startCmd) { + $startCmd = $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix; + } + $code = &mySystem($instanceDir,$startCmd); + open(IN, $errLog) or die "Could not open error log $errLog: $!"; + my $pos = tell(IN); + while (($done == 0) && (time < $timeout)) { + for (; ($done == 0) && ($_ = <IN>); $pos = tell(IN)) { + $lastLine = $_; + # print; + # the server has already been started and shutdown once . . . + if (/slapd started\./) { + $started++; + if ($started == 2) { + $done = 1; + } + # sometimes the server will fail to come up; in that case, restart it + } elsif (/Initialization Failed/) { + # print "Server failed to start: $_"; + $code = &mySystem($instanceDir, $startCmd); + # sometimes the server will fail to come up; in that case, restart it + } elsif (/exiting\./) { + # print "Server failed to start: $_"; + #$code = &mySystem($startCmd); + $code = &mySystem($instanceDir, $startCmd); + } + } + if ($lastLine =~ /PR_Bind/) { + # server port conflicts with another one, just report and punt + print $lastLine; + print "This server cannot be started until the other server on this\n"; + print "port is shutdown.\n"; + $done = 1; + } + if ($done == 0) { + # rest a bit, then . . . + sleep(2); + # . . . reset the EOF status of the file desc + seek(IN, $pos, 0); + } + } + close(IN); + + sleep(5); + die "\nUnable to start the $Version.$Minor Directory Server\n" unless (isDirectoryAlive()); + + return 0; +} + +sub stopServer { + my $root = shift; + my $name = shift; + $maxStopIterations = 5; + print "\nShutting down server $name . . .\n"; + $ENV{"$LIB_PATH"}=$new_libpath; + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote; + if (! -f $stopCmd) { + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote; + } + + if (! -f $stopCmd) { + # no stop command, probably a 1.X system; for NT, we'll try net stop + # for unix, we'll get the pid and kill it + if ($isNT) { + $stopCmd = 'net stop ' . $name; + } else { + # see if there is a pid file + $pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' . + $PATHSEP . 'pid'; + if (open(PIDFILE, $pidfile)) { + chomp($pid = <PIDFILE>); + close(PIDFILE); + while ($maxStopIterations-- && !$exitCode) { + $exitCode = kill(15, $pid); + } + $stopCmd = undef; + } + } + } + + # keep looping until the stop cmd returns an error code, which usually + # means that what ever we want to stop is stopped, or some other error + # occurred e.g. permission, or no such service + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + while ($stopCmd && $maxStopIterations-- && $exitCode) { + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + } + + if (!$maxStopIterations) { + print "Warning: could not shutdown the server: $!\n"; + } + sleep(10) ; + $exitCode = 0; +} + + +sub runAndIgnoreOutput { + my $cmd = shift; + printMsg("."); + open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!"; + printMsg("."); + sleep(1); # allow pipe to fill with data + printMsg("."); + while (<RUNCMD>) { +# print; + } + my $code = close(RUNCMD); +# print "runAndIgnore: code=$code status=$?\n"; + return $?; +} + +############################################################################# +# migrate SSL info + +sub MigrateSSL { + my $secPwd = 'bidon' ; + # copy the SSL directory + ©Dir("$oldHome${PATHSEP}ssl","$serverHome${PATHSEP}ssl") if (-d "$oldHome${PATHSEP}ssl"); + # copy the cert db and key files + if ( -d "$oldDir${PATHSEP}alias") { + $aliasDir = "$root${PATHSEP}alias"; + if (! -d $aliasDir) { + mkdir($aliasDir, 0750); + } + &stopServer($root,'slapd-'.$newname); + my $keydb = "$aliasDir${PATHSEP}slapd-$newname-key3.db" ; + my $certdb = "$aliasDir${PATHSEP}slapd-$newname-cert8.db" ; + my $certdb7 = "$aliasDir${PATHSEP}slapd-$newname-cert7.db" ; + my $old_keydb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ; + my $old_certdb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db"; + my $keydb_backup = "$aliasDir${PATHSEP}slapd-$newname-key3.db_backup" ; + my $certdb_backup = "$aliasDir${PATHSEP}slapd-$newname-cert7.db_backup" ; + if (-f $old_keydb) { + if (-f $keydb) { + if ($NO_INPUT_USER) { + printMsg("\n$keydb already exists. backup in $keydb_backup ..."); + ©BinFile($keydb,$keydb_backup); + ©BinFile($old_keydb,$keydb); + } + else { + print("\n\n$keydb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_keydb,$keydb); + } + } + } + else { + ©BinFile($old_keydb,$keydb); + } + } + if (-f $old_certdb) { + $mode = (stat($old_certdb))[2] if $PRESERVE; + if (-f $certdb) { + if ($NO_INPUT_USER) { + printMsg("\n$certdb already exists. backup in $certdb_backup ..."); + ©BinFile($certdb,$certdb_backup); + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + else { + print("\n\n$certdb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + } + } + else { + ©BinFile($old_certdb,$certdb7); + } + } + # copy the old password file + if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") { + ©BinFile( + "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt", + "$aliasDir${PATHSEP}$type-$newname-pin.txt" + ); + } + &startServer(); + if ($PRESERVE) { + chown($newuid,$newgid,$certdb) || print "Failed to set uid $newuid gid $newgid on $certdb : $!\n"; + chmod($mode,$certdb) || print "Failed to set mode $mode on $certdb : $!\n"; + } + } + +} + +sub DisableSSL { + my $entry = $conn->search("cn=config","base","objectclass=*"); + my $LDAPparam = "nsslapd-security" ; + my $Value = "off" ; + if ($entry->{$LDAPparam}[0] ne $Value) { + printTrace("\nDisable SSL...",1); + $entry->setValues($LDAPparam, $Value); + } + my $res = $conn->update($entry); + if ($res) { + printTrace("\nSSL disabled",2); + } + else { + printMsg("\nCan't disabled SSL. The server may have problems to start"); + } +} + +# enable the migration of client authentication informations +sub MigrateCertmap { + # backup the old certmap.conf and replace it with the new one + my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"; + my $newCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf" ; + my $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + if (-f $oldCertmap) { + if ($NO_INPUT_USER) { + printMsg("\n$newCertmap has been backup in $backupCertmap"); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up the file $newCertmap [$backupCertmap] ?") ; + my $Answer = <STDIN> ; + $backupCertmap = $Answer if ($Answer ne "\n"); + chomp($backupCertmap); + printTrace("\nDest: .$backupCertmap.",4); + if (-e $backupCertmap) { + printMsg("\n\n$backupCertmap already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup file: $newCertmap in $backupCertmap",4); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + } + else { + } +} + +sub hasChangedoldCertmap { + my $certmapfile = shift ; + my @reference = ("certmap default default", + "default:DNComps", + "default:FilterComps e") ; + my $cpt = 0 ; + printTrace("\nhasChangedoldCertmap",3); + open(CERTMAP,"< $certmapfile"); + while (<CERTMAP>) { + if ((! /^\s*#/) && (! /^\s*$/)) { + my $ref = $reference[$cpt] ; + printTrace("\nValue: $_, ref: $ref",4); + if (! /^\s*$ref\s*$/) { + return 1 ; + } + else { + $cpt++ ; + } + } + } + close (CERTMAP); + printTrace("\ncpt: $cpt",4); + if ($cpt < $#reference) { + return 1 ; + } + else { + return 0 ; + } +} + + +########################################################################################### +# # +# Copy directory and files functions # +# # +########################################################################################### + + +sub copyDir { + my $src = shift; + my $dest = shift; + my $exclude = shift; + + opendir( SRC, $src ) or die "Can't open directory $src: $!: "; + my $mode; + my $uid; + my $gid; + mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest ); + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + local ( @files ) = readdir ( SRC ); + closedir( SRC ); + for ( @files ) { + if ( $_ eq "." || $_ eq ".." ) { + next; + } elsif ( $exclude && /$exclude/ ) { + next; + } elsif ( $_ =~ /^__/ ) { + # region files are incompatible between 32 + # and 64 bit servers + next; + } elsif( -d "$src${PATHSEP}$_") { + ©Dir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); + } else { + ©BinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_"); + } + } +} + +sub copyBinFile { + my $src = shift; + my $dest = shift; + my $buf = ""; + my $bufsize = 8192; + + open( SRC, $src ) || die "Can't open $src: $!\n"; + # if we are given a directory destination instead of a file, extract the + # filename portion of the source to use as the destination filename + if (-d $dest) { + $dest = $dest . $PATHSEP . &basename($src); + } + open( DEST, ">$dest" ) || die "Can't create $dest: $!\n"; + binmode SRC; + binmode DEST; + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + while (read(SRC, $buf, $bufsize)) { + print DEST $buf; + } + close( SRC ); + close( DEST ); +} + +############################################################################################################# +# backup 6.x configuration files # +# backup the directory <root_server5>/slapd-instance/config dans <root_server5>/slapd-instance/BackupConfig # # +# # +############################################################################################################# + + +sub backupConfigFiles { + # backup the 6.x config files + my $src = "$serverHome${PATHSEP}config" ; + my $dest = "$serverHome${PATHSEP}config_backup" ; + if ($NO_INPUT_USER) { + printMsg("\n$src has been backup in $dest"); + ©Dir($src,$dest); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up your configuration directory [$dest] ?") ; + my $Answer = <STDIN> ; + $dest = $Answer if ($Answer ne "\n"); + chomp($dest); + printTrace("\nDest: .$dest.",4); + if (-e $dest) { + printMsg("\n\n$dest already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $dest = "$serverHome${PATHSEP}config_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup Directory: $src in $dest",4); + ©Dir($src,$dest); + } +} +############################################################################# + +sub getLDAPservername { + my $oldLDAPservername; + my $LDAPservername; + my $localhost = "nsslapd-localhost"; + open(OLDDSELDIF, "< $oldDSEldif") or die "\nError: could not open old config file $oldDSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*OLDDSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $oldLDAPservername = $values[0]; + printTrace("\nName of the old LDAP server: $oldLDAPservername",3); + } + break; + } + } + close(OLDSELDIF); + + open( DSELDIF, "< $DSEldif" ) || die "\nCan't open $DSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN(1) ; + if ($DN =~ /^cn=config$/i) { + my @values = $entry->getValues($localhost); + if ($entry->size($localhost)) { + $LDAPservername = $values[0]; + printTrace("\nName of the new LDAP server: $LDAPservername",3); + } + break; + } + } + close(DSELDIF); + # check ol and new Directory Instance are installed on the same physical machine. + if (lc($oldLDAPservername) ne lc($LDAPservername)) { + # warn the user he tries to migrate a 4.x server installed on a different machine from the 6.x one + printMsg("\n\nYour old instance is on $oldLDAPservername, whereas your new instance is on $LDAPservername. Migration on different machines is not supported. Do you want to continue ? Yes/No [No]:") ; + if (! (<STDIN> =~ /yes|y/i)) { + return -1; + } + } + return $LDAPservername ; +} + +############################################################################# + +sub getLibPath { + my $myDir = shift; + my $myVersion = shift; + my $myMinor = shift; + + if ($isNT) { + return $ENV{"$LIB_PATH"}; + } + if (($myVersion >= 6) && ($myMinor >= 2)) { + return + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + } else { + return "$myDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + } +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $versionstr = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + if (!$versionstr) { # version not specified on cmd line - find it + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $cur_libpath=$ENV{"$LIB_PATH"}; + $ENV{"$LIB_PATH"}= + "$dir${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory/ || /^iPlanet-Directory/i) { + $versionstr = $_; + last; + } + } + $code = close(F); + # print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}=$cur_libpath; + } + + if ($versionstr =~ /^Netscape-Directory\/(\d+)\.(\d+)(?:b\d)*\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } elsif ($versionstr =~ /(\d+)\.(\d+)/) { + $version = $1; + $minor = $2; + } + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################################### +sub normalizeDir { + my $dir = shift ; + my $dir_prec = "" ; + while ($dir_prec ne $dir) { + $dir_prec = $dir ; + if ($isNT) { + grep { s@\\\\@\\@g } $dir ; + } + else { + grep { s@//@/@g } $dir ; + } + } + return $dir ; +} + + +############################################################################################### + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################################### +# get uid and group id of the 6.x slapd server. +# The uid is done through the nsslapd-localuser attribute + +sub getuid_gid { + my $newuid ; + my $newgid ; + my $localuser ; + my $localuser_attr = "nsslapd-localuser" ; + if (! $isNT) { + &startServer() unless (isDirectoryAlive()); + my $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; + my $entry = $conn->search("cn=config ", "base","objectclass=*", 0, ($localuser_attr)) ; + # Tests wether we succeed to get the entry cn=config + die "\nCan't get the entry cn=config \n" unless ($entry); + my @values = $entry->getValues($localuser_attr); + $conn->close(); + if ($#values == -1 || ($values[0] eq "") ) { # tests wether the nsslapd-localuser attribute has a value + printMsg("\nNo localuser has been found in the configuration of the directory. "); + if ($NO_INPUT_USER) { + printMsg("\nWe considered nobody as the localuser"); + $localuser = "nobody" ; + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\nUnder what user does your $Version.$Minor directory server run [nobody] ? ") ; + $localuser = <STDIN> ; + chomp($localuser); + $localuser = "nobody" if ($localuser eq ""); + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + if ($newuid) { + $Ask = 0 ; + } + else { + printMsg("\nError: $localuser is unknown from the system "); + } + } + } + } + else { + $localuser = $values[0]; # returns the first value (we should only have one localuser) + my $size = $#values ; + } + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + return ($localuser, $newuid, $newgid) ; + } + else { + return () ; + } +} + +sub getolduid_gid { + my $oldlocaluser ; + my $localuserAttr = "nsslapd-localuser"; + my $entry ; + if (! $isNT) { + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + $typeOfEntry = getTypeOfEntry($entry); + if ($typeOfEntry eq "CONFIG_NODE") { + $oldlocaluser = $entry->{$localuserAttr}[0] if ($entry->exists($localuserAttr)); + break ; + } + } + close(DSE); + ($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ; + return ($oldlocaluser, $olduid, $oldgid) ; + } + else { + return (); + } +} +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} + +################################ +# Need to migrate the credential. +# If the credential can not be migrated, leave it at it is +################################ +sub migrate_credential{ + my $entry_to_modify = shift; + my $credentials_attr = shift; + my @old_value = $entry_to_modify->getValues($credentials_attr); + my $migratecredExecName = 'migratecred'; + my $credOldHome = $oldHome; + my $credServerHome = $serverHome; + + if ($isNT) { + # oldHome may be pointing to the archived copy of the + # instance dir which may be different than the path that + # the instance was originally installed as on Windows. If + # this path is not the original install path, then the + # credential will not be migrated correctly. We should + # prompt the user on Windows for the correct path. + + print "\n\nThe old instance path must be the same as where it was"; + print "\ninitially installed, not where it was archived in order"; + print "\nfor this step to succeed. Please verify that the path"; + print "\nis correct. Note that case sensitivity is important here."; + print "\n\nOld Instance Directory: $credOldHome"; + print "\nIs this correct? (y/n): "; + chomp(my $answer = <STDIN>); + if (!($answer =~ /y|yes/i)) { + print "\nPlease enter the correct path for the old instance directory: "; + chomp($credOldHome = <STDIN>); + } + + print "\n\nThe new instance path must also be correct for this step"; + print "\nto succeed. Please verify that the path is correct. Note"; + print "\nthat case sensitivity is important here."; + print "\n\nNew Instance Directory: $credServerHome"; + print "\nIs this correct? (y/n): "; + chomp(my $answer = <STDIN>); + if (!($answer =~ /y|yes/i)) { + print "\nPlease enter the correct path for the new instance directory: "; + chomp($credServerHome = <STDIN>); + } + } +# print "\nMigratecred command is: ${quote}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}$migratecredExecName${quote} -o $credOldHome -n $credServerHome -c @old_value\n"; + + my @new_cred = `${quote}$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}$migratecredExecName${quote} -o $credOldHome -n $credServerHome -c @old_value`; + + if ( $? == 0 ) + { + $entry_to_modify->setValues($credentials_attr, @new_cred); + } +} + diff --git a/ldap/admin/src/scripts/template-migrateInstance5 b/ldap/admin/src/scripts/template-migrateInstance5 new file mode 100644 index 00000000..3d3396c2 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrateInstance5 @@ -0,0 +1,518 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +use Time::localtime; + +BEGIN { + require 'uname.lib'; + $| = 1; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + $SEP = $isNT ? ";" : ":" ; + $exitCode = 0; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + $os = "WINNT"; + } else { + $os = &uname("-s"); + if ($os eq "SunOS") { + $isSolaris9 = ( &uname("-r") eq "5.9" ); + } + } + + if ( ($os eq "AIX") || ($os eq "HP-UX") ) { + $sigChildHandler = 'sigChildHandler'; + } + SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + $slapdExecName = $isNT ? 'slapd.exe' : 'ns-slapd'; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; +} + + + +$TRACELEVEL = 0; +${root} = "{{DS-ROOT}}" ; +${type} = "" ; +${newname} = "" ; +${newport} = "" ; +${rootDN} = "" ; +${rootpwd} = "" ; +${localhost} = "" ; +${LogFileReport} = "" ; + +# get input users +&getParameters() ; + + +${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; +${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; +${oldSlapdConf} = "${oldConfDir}slapd.conf" ; +${serverHome} = "${root}${PATHSEP}$type-$newname" ; +${ldif_rep} = "${oldConfDir}${PATHSEP}ldif${PATHSEP}" ; +${curdir} = getCwd(); + + +if (!(-d $serverHome)) { + print("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + print("\n$oldHome doesn't exist\n"); + exit(1); + } +$ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"} ; +if ($isSolaris9) { + $ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.005_03${PATHSEP}lib${PATHSEP}sun4-solaris${PATHSEP}CORE${SEP}".$ENV{"$LIB_PATH"} ; +} + +if ($isNT) { + $ENV{"PERL5LIB"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.005_03${PATHSEP}site${PATHSEP}lib${SEP}".$ENV{"PERL5LIB"} ; +} +else { + $ENV{"PERL5LIB"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.005_03${PATHSEP}lib${PATHSEP}site${SEP}".$ENV{"PERL5LIB"} ; +} + +# get the version of the DS to migrate +($oldVersion, $oldMinor) = &getVersion($oldDir); +# get the version of the new DS +($Version, $Minor) = &getVersion($root); + +if ($Version >= 5) { + if ($oldVersion == 4) { + $myscript = "migrateTo5" ; + printMsg("\n******* Migration from $oldVersion.$oldMinor Netscape Directory Server to $Version.$Minor iPlanet Directory Server *********\n"); + } + elsif ($oldVersion == 5 ) { + printMsg("\nWarning. You may experiment some problems considering the version of directory server you want to migrate is not a 5.0") if ($oldMinor != 0); + if ($oldMinor > $Minor) { + die "The migration from a version to an oldest one is not supported\nMigration aborted\n"; + } + $myscript = "migrate50to51" ; + printMsg("\n******* Migration from $oldVersion.$oldMinor to $Version.$Minor iPlanet Directory Server *********\n"); + } + else { + die "We don't support the version of directory server you want to migrate"; + } +} +else { + die "\n\nThe version of directory you want to upgrade is not a 5.x product\nMigration aborted\n"; +} + + + +my $start_time = gmtime ; +@args = ($, $myscript, @ARGV, '-L', $LogFileReport); +$exitCode = &mySystem(@args); +#die "Error: @args: $!" if ($exitCode != 0); + +open(LOGFILE,">> $LogFileReport") or die "\nCan't write to $LogFileReport\n$!\n"; +if (! $exitCode) { + my $end_time = gmtime ; + printMsg("-> Migration started at $start_time\n"); + printMsg("-> Migration ended at $end_time\n\n"); +} +printMsg("***********************************************\n\n"); +print("-> The migration report file is available at: $LogFileReport\n\n"); + +close(LOGFILE); + +####################################################################################################################### +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o oldInstancePath -n newInstancePath [-t tracelevel] \n"); + print(STDERR " [-L logfile] [-noinput]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - new 5.x Directory Manager\n"); + print(STDERR " : -w password - new 5.x Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for new 5.x Directory Manager's password\n"); + print(STDERR " : -j filename - Read new 5.x Directory Manager's password from file\n"); + print(STDERR " : -p port - new 5.x Directory Server port\n"); + print(STDERR " : -o oldInstancePath - Path of the old instance to migrate \n"); + print(STDERR " : -n newInstancePath - Path of the new 5.x instance\n"); + print(STDERR " : [-t tracelevel] - specify the level of trace (0..3) by default setup to 1\n"); + print(STDERR " : [-L logfile] - specify the file to log the migration report \n"); + print(STDERR " : [-noinput] - no user interventions during migration processing to solve conflicts\n"); + + } + + +####################################################################################################################### +# get input users + +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # 5.x instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe 5.x instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # 5.x DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + } elsif ("$ARGV[$i]" eq "-L") { # user defined logfile for the migration + $LogFileReport = $ARGV[++$i]; + } + else { + &usage() ; + exit(1); + } + $i++; + } + + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + if ($exit) { + &usage() ; + exit(1); + } + +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg; + } + +} + +############################################################################# +sub mySystem { + my $cmd = $_[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @_; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd \"@fixargs\"\n"; + $rc = system {$cmd} @fixargs; + } + + return $rc; +} + +############################################################################# + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $ENV{"$LIB_PATH"}="$dir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory\/(\d+)\.(\d+)\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + elsif (/^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + elsif (/^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + } + $code = close(F); +# print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} diff --git a/ldap/admin/src/scripts/template-migrateInstance6 b/ldap/admin/src/scripts/template-migrateInstance6 new file mode 100644 index 00000000..faaf6363 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrateInstance6 @@ -0,0 +1,550 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +use Time::localtime; + +BEGIN { + require 'uname.lib'; + $| = 1; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + $SEP = $isNT ? ";" : ":" ; + $exitCode = 0; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + $os = "WINNT"; + } else { + $os = &uname("-s"); + if ($os eq "SunOS") { + $isSolaris9 = ( &uname("-r") eq "5.9" ); + } + } + + if ( ($os eq "AIX") || ($os eq "HP-UX") ) { + $sigChildHandler = 'sigChildHandler'; + } + SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + $slapdExecName = $isNT ? 'slapd.exe' : 'ns-slapd'; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; +} + + + +$TRACELEVEL = 0; +${root} = "{{DS-ROOT}}" ; +${type} = "" ; +${newname} = "" ; +${newport} = "" ; +${rootDN} = "" ; +${rootpwd} = "" ; +${localhost} = "" ; +${LogFileReport} = "" ; + +# get input users +&getParameters() ; + + +${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; +${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; +${oldSlapdConf} = "${oldConfDir}slapd.conf" ; +${serverHome} = "${root}${PATHSEP}$type-$newname" ; +${ldif_rep} = "${oldConfDir}ldif${PATHSEP}" ; +${curdir} = getCwd(); + + +if (!(-d $serverHome)) { + print("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + print("\n$oldHome doesn't exist\n"); + exit(1); + } +if ($olddatadir && !(-d $olddatadir)) { + print("\n$olddatadir doesn't exist\n"); + exit(1); + } +$ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"} ; +if ($isSolaris9) { + $ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.6.1${PATHSEP}lib${PATHSEP}sun4-solaris${PATHSEP}CORE${SEP}".$ENV{"$LIB_PATH"} ; +} + +if ($isNT) { + $ENV{"PERL5LIB"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.6.1${PATHSEP}site${PATHSEP}lib${SEP}".$ENV{"PERL5LIB"} ; +} +else { + $ENV{"PERL5LIB"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.6.1${PATHSEP}lib${PATHSEP}site${SEP}".$ENV{"PERL5LIB"} ; +} + +# get the version of the DS to migrate +($oldVersion, $oldMinor) = &getVersion($oldDir, $oldversionstr); +# get the version of the new DS +($Version, $Minor) = &getVersion($root); + +if ($Version >= 6) { + if ($oldVersion == 4) { + $myscript = "migrateTo6" ; + printMsg("\n******* Migration from $oldVersion.$oldMinor to $Version.$Minor Directory Server *********\n"); + } + elsif ($oldVersion == 5 ) { + printMsg("\nWarning. You may experience some problems if the version of directory server you want to migrate is not a 5.0 or 5.1") if ($oldMinor > 1); + $myscript = "migrate5to6" ; + printMsg("\n******* Migration from $oldVersion.$oldMinor to $Version.$Minor Directory Server *********\n"); + } else { + die "We don't support the version of directory server you want to migrate"; + } +} +else { + die "\n\nThe version of directory you want to upgrade is not a 6.x product\nMigration aborted\n"; +} + +my $start_time = gmtime ; +@args = ($^X, $myscript, @ARGV, '-L', $LogFileReport); +$exitCode = &mySystem(@args); +#die "Error: @args: $!" if ($exitCode != 0); + +open(LOGFILE,">> $LogFileReport") or die "\nCan't write to $LogFileReport\n$!\n"; +if (! $exitCode) { + my $end_time = gmtime ; + printMsg("-> Migration started at $start_time\n"); + printMsg("-> Migration ended at $end_time\n\n"); +} +printMsg("***********************************************\n\n"); +print("-> The migration report file is available at: $LogFileReport\n\n"); + +close(LOGFILE); + +####################################################################################################################### +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o oldInstancePath -n newInstancePath [-t tracelevel] \n"); + print(STDERR " [-L logfile] [-noinput]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - new Directory Manager\n"); + print(STDERR " : -w password - new Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for new Directory Manager's password\n"); + print(STDERR " : -j filename - Read new Directory Manager's password from file\n"); + print(STDERR " : -p port - new Directory Server port\n"); + print(STDERR " : -o oldInstancePath - Path of the old instance to migrate \n"); + print(STDERR " : -n newInstancePath - Path of the new instance\n"); + print(STDERR " : [-d dataPath] - Path to directory containing data files to import into new instance\n"); + print(STDERR " : [-v oldVersion] - Version of old instance (obtained by running $slapdExecName -v\n"); + print(STDERR " : [-t tracelevel] - specify the level of trace (0..3) by default setup to 1\n"); + print(STDERR " : [-L logfile] - specify the file to log the migration report \n"); + print(STDERR " : [-noinput] - no user interventions during migration processing to solve conflicts\n"); + + } + + +####################################################################################################################### +# get input users + +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # 5.x instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe new instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # 5.x DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-d") { # old instance LDIF data dir + if (! $olddatadir ) { + $olddatadir = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-v") { # old version + if (! $oldversionstr ) { + $oldversionstr = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + } elsif ("$ARGV[$i]" eq "-L") { # user defined logfile for the migration + $LogFileReport = $ARGV[++$i]; + } + else { + &usage() ; + exit(1); + } + $i++; + } + + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + if ($exit) { + &usage() ; + exit(1); + } + +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg; + } + +} + +############################################################################# +sub mySystem { + my $cmd = $_[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @_; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd \"@fixargs\"\n"; + if($isNT) { + $rc = system "\"@fixargs\""; + } else { + $rc = system @fixargs; + } + } + + return $rc; +} + +############################################################################# + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $versionstr = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + if (!$versionstr) { # version not specified on cmd line - find it + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $ENV{"$LIB_PATH"}= + "$dir${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory/ || /^iPlanet-Directory/i) { + $versionstr = $_; + last; + } + } + $code = close(F); + # print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + } + + if ($versionstr =~ /^Netscape-Directory\/(\d+)\.(\d+)(?:b\d)*\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } elsif ($versionstr =~ /(\d+)\.(\d+)/) { + $version = $1; + $minor = $2; + } + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} diff --git a/ldap/admin/src/scripts/template-migrateInstance7 b/ldap/admin/src/scripts/template-migrateInstance7 new file mode 100644 index 00000000..2a754b10 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrateInstance7 @@ -0,0 +1,559 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +use Time::localtime; + +BEGIN { + require 'uname.lib'; + $| = 1; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + $SEP = $isNT ? ";" : ":" ; + $exitCode = 0; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + $os = "WINNT"; + } else { + $os = &uname("-s"); + if ($os eq "SunOS") { + $isSolaris9 = ( &uname("-r") eq "5.9" ); + } + } + + if ( ($os eq "AIX") || ($os eq "HP-UX") ) { + $sigChildHandler = 'sigChildHandler'; + } + SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + $slapdExecName = $isNT ? 'slapd.exe' : 'ns-slapd'; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; +} + + + +$TRACELEVEL = 0; +${root} = "{{DS-ROOT}}" ; +${type} = "" ; +${newname} = "" ; +${newport} = "" ; +${rootDN} = "" ; +${rootpwd} = "" ; +${localhost} = "" ; +${LogFileReport} = "" ; + +# get input users +&getParameters() ; + + +${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; +${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; +${oldSlapdConf} = "${oldConfDir}slapd.conf" ; +${serverHome} = "${root}${PATHSEP}$type-$newname" ; +${ldif_rep} = "${oldConfDir}ldif${PATHSEP}" ; +${curdir} = getCwd(); + + +if (!(-d $serverHome)) { + print("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + print("\n$oldHome doesn't exist\n"); + exit(1); + } +if ($olddatadir && !(-d $olddatadir)) { + print("\n$olddatadir doesn't exist\n"); + exit(1); + } +$ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"} ; +if ($isSolaris9) { + $ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.6.1${PATHSEP}lib${PATHSEP}sun4-solaris${PATHSEP}CORE${SEP}".$ENV{"$LIB_PATH"} ; +} + +if ($isNT) { + $ENV{"PERL5LIB"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.6.1${PATHSEP}site${PATHSEP}lib${SEP}".$ENV{"PERL5LIB"} ; +} +else { + $ENV{"PERL5LIB"} = "$root${PATHSEP}lib${PATHSEP}nsPerl5.6.1${PATHSEP}lib${PATHSEP}site${SEP}".$ENV{"PERL5LIB"} ; +} + +# get the version of the DS to migrate +($oldVersion, $oldMinor) = &getVersion($oldDir, $oldversionstr); +# get the version of the new DS +($Version, $Minor) = &getVersion($root); + +if ($Version >= 7) { + if ($oldVersion == 4) { + $myscript = "migrateTo7" ; + printMsg("\n******* Migration from $oldVersion.$oldMinor to $Version.$Minor Directory Server *********\n"); + } + elsif ($oldVersion == 5 ) { + printMsg("\nWarning. You may experience some problems if the version of directory server you want to migrate is not a 5.0 or 5.1") if ($oldMinor > 1); + $myscript = "migrate5to7" ; + printMsg("\n******* Migration from $oldVersion.$oldMinor to $Version.$Minor Directory Server *********\n"); + } + elsif ($oldVersion == 6 ) { + $myscript = "migrate6to7" ; + printMsg("\n******* Migration from $oldVersion.$oldMinor to $Version.$Minor Directory Server *********\n"); + } + else { + + die "We don't support the version of directory server you want to migrate"; + } +} +else { + die "\n\nThe version of directory you want to upgrade is not a 7.x product\nMigration aborted\n"; +} + +my $start_time = gmtime ; +@args = ($^X, $myscript, @ARGV, '-L', $LogFileReport); +$exitCode = &mySystem(@args); +#die "Error: @args: $!" if ($exitCode != 0); + +open(LOGFILE,">> $LogFileReport") or die "\nCan't write to $LogFileReport\n$!\n"; +if (! $exitCode) { + my $end_time = gmtime ; + printMsg("-> Migration started at $start_time\n"); + printMsg("-> Migration ended at $end_time\n\n"); +} +printMsg("***********************************************\n\n"); +print("-> The migration report file is available at: $LogFileReport\n\n"); + +close(LOGFILE); + +####################################################################################################################### +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o oldInstancePath -n newInstancePath [-t tracelevel] \n"); + print(STDERR " [-L logfile] [-noinput]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - new Directory Manager\n"); + print(STDERR " : -w password - new Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for new Directory Manager's password\n"); + print(STDERR " : -j filename - Read new Directory Manager's password from file\n"); + print(STDERR " : -p port - new Directory Server port\n"); + print(STDERR " : -o oldInstancePath - Path of the old instance to migrate \n"); + print(STDERR " : -n newInstancePath - Path of the new instance\n"); + print(STDERR " : [-d dataPath] - Path to directory containing data files to import into new instance\n"); + print(STDERR " : [-v oldVersion] - Version of old instance (obtained by running $slapdExecName -v\n"); + print(STDERR " : [-t tracelevel] - specify the level of trace (0..3) by default setup to 1\n"); + print(STDERR " : [-L logfile] - specify the file to log the migration report \n"); + print(STDERR " : [-noinput] - no user interventions during migration processing to solve conflicts\n"); + + } + + +####################################################################################################################### +# get input users + +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # 5.x instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe new instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # 5.x DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-d") { # old instance LDIF data dir + if (! $olddatadir ) { + $olddatadir = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-v") { # old version + if (! $oldversionstr ) { + $oldversionstr = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + } elsif ("$ARGV[$i]" eq "-L") { # user defined logfile for the migration + $LogFileReport = $ARGV[++$i]; + } + else { + &usage() ; + exit(1); + } + $i++; + } + + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + if ($exit) { + &usage() ; + exit(1); + } + +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg; + } + +} + +############################################################################# +sub mySystem { + my $cmd = $_[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @_; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd \"@fixargs\"\n"; + if($isNT) { + $rc = system "\"@fixargs\""; + } else { + $rc = system @fixargs; + } + } + + return $rc; +} + +############################################################################# + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $versionstr = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + if (!$versionstr) { # version not specified on cmd line - find it + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $preserve_lib_path = $ENV{"$LIB_PATH"}; + $ENV{"$LIB_PATH"}= + "$dir${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory/ || /^iPlanet-Directory/i) { + $versionstr = $_; + last; + } + } + $code = close(F); + # print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + } + + if ($versionstr =~ /^Netscape-Directory\/(\d+)\.(\d+)(?:b\d)*\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } elsif ($versionstr =~ /(\d+)\.(\d+)/) { + $version = $1; + $minor = $2; + } + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + # Restore the original library path + $ENV{"$LIB_PATH"} = $preserve_lib_path; + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} diff --git a/ldap/admin/src/scripts/template-migrateTo5 b/ldap/admin/src/scripts/template-migrateTo5 new file mode 100755 index 00000000..f02a6279 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrateTo5 @@ -0,0 +1,3094 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Migrate a 4.0 directory server to a 5.x directory server + +######################################################################################################## +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Entry; +use Mozilla::LDAP::LDIF; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::localtime; + +######################################################################################################## +use Class::Struct ; # load struct-building module + +struct S_index => { + names => '@' , + types => '@' , + oids => '@' , + specific => '$' + }; + + +struct S_plugin => { + name => '$' , + type => '$' , + enable => '$' , + args => '@' + }; +##################################################################################################### + +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o 4.xInstancePath -n 5.xInstancePath [-t tracelevel] [-L logfile]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - 5.x Directory Manager\n"); + print(STDERR " : -w password - 5.x Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for 5.x Directory Manager's password\n"); + print(STDERR " : -j filename - Read 5.x Directory Manager's password from file\n"); + print(STDERR " : -p port - 5.x Directory Server port\n"); + print(STDERR " : -o 4.xInstancePath - Path of the 4.x instance to migrate \n"); + print(STDERR " : -n 5.xInstancePath - Path of the new 5.x instance\n"); + print(STDERR " : [-t tracelevel] - specify the level of trace (0..3)\n"); + print(STDERR " : [-L logfile] - specify the file to log the migration report \n"); + + + } + + + +############# +BEGIN { + + require 'uname.lib' ; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + ${SEP} = $isNT ? ";" : ":" ; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + + # If this variable is set, all file/directory creation will make sure the mode + # and ownership of the destination is the same as the source + $PRESERVE = 1 if (!$isNT); + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + if ($isNT) { + $os = "WINNT"; + } else { + $os = &uname("-s"); + } + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + } + if ( $os eq "AIX" ) { + $dll_suffix = "_shr.a"; + } + elsif ( $os eq "HP-UX" ) { + $dll_suffix = ".sl"; + } + elsif ( $os eq "WINNT" ) { + $dll_suffix = ".dll"; + } + else { + $dll_suffix = ".so"; + } + $slapdExecName = $isNT ? 'slapd.exe' : 'ns-slapd'; + # if this flag is set, we will migrate the 4.x database + # by doing a db2ldif -> ldif2db; + $convertToLDIF = 1; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; + # if the old value for dbcachesize is less than this, make it this + $MIN_DBCACHESIZE = '500000'; +} + +SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + + # 4.x parameters + ${oldDir} = "" ; + ${oldname} = "" ; + ${oldHome} = "" ; + ${oldConfDir} = "" ; + ${oldlocaluser} ; + ${olduid} ; + ${oldgid} ; + + # 5.x parameters + ${root} = "{{DS-ROOT}}" ; + ${type} = "" ; + ${newname} = "" ; + ${newport} = "" ; + ${rootDN} = "" ; + ${rootpwd} = "" ; + ${localhost} = "" ; + ${LogFileReport} = "" ; + ${newuid} ; + ${localuser} ; + ${newgid} ; + $NO_INPUT_USER = 0 ; # by default user can give inputs during the migration process + ${curdir} = getCwd(); + ${slapdExecDir} = "${root}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + # specify the level of trace + $TRACELEVEL=1; + + # get input users + &getParameters() ; + ${oldDir} = &normalizeDir("${oldDir}"); + ${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; + ${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; + ${oldSlapdConf} = "${oldConfDir}slapd.conf" ; + ${oldDSEldif} = "${oldConfDir}dse.ldif" ; + ${serverHome} = "${root}${PATHSEP}$type-$newname" ; + ${DSEldif} = "$serverHome${PATHSEP}config${PATHSEP}dse.ldif"; + ${ldif_rep} = "${oldConfDir}${PATHSEP}ldif${PATHSEP}" ; + ${oldSlapdExecDir} = "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + + + + open(LOGFILE, ">> $LogFileReport"); + + printTrace("\noldDir: $oldDir, oldHome: $oldHome, \noldConfDir: $oldConfDir, \noldSlapdConf: $oldSlapdConf, \nldif_rep: $ldif_rep, \nrootDN: $rootDN, \nPwd: ******, \nPort: $newport, \nNewname: $newname\n",3); + printTrace("\nLIB_PATH: $LIB_PATH",4); + + if (!(-d $serverHome)) { + printMsg("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + printMsg("\n$oldHome doesn't exist\n"); + exit(1); + } + +#define CONFIG_DATABASE_DIRECTIVE "database" +#define CONFIG_DATABASE_ATTRIBUTE "nsslapd-database" +#define CONFIG_PLUGIN_DIRECTIVE "plugin" +#define CONFIG_PLUGIN_ATTRIBUTE "nsslapd-plugin" +#define CONFIG_SIZELIMIT_DIRECTIVE "sizelimit" +#define CONFIG_SIZELIMIT_ATTRIBUTE "nsslapd-sizelimit" +#define CONFIG_ORCAUTO_DIRECTIVE "orcauto" +#define CONFIG_ORCAUTO_ATTRIBUTE "nsslapd-orcauto" +#define CONFIG_TIMELIMIT_DIRECTIVE "timelimit" +#define CONFIG_TIMELIMIT_ATTRIBUTE "nsslapd-timelimit" +#define CONFIG_SUFFIX_DIRECTIVE "suffix" +#define CONFIG_SUFFIX_ATTRIBUTE "nsslapd-suffix" +#define CONFIG_READONLY_DIRECTIVE "readonly" +#define CONFIG_READONLY_ATTRIBUTE "nsslapd-readonly" +#define CONFIG_REFERRAL_DIRECTIVE "referral" +#define CONFIG_REFERRAL_ATTRIBUTE "nsslapd-referral" +#define CONFIG_OBJECTCLASS_DIRECTIVE "objectclass" +#define CONFIG_OBJECTCLASS_ATTRIBUTE "nsslapd-objectclass" +#define CONFIG_ATTRIBUTE_DIRECTIVE "attribute" +#define CONFIG_ATTRIBUTE_ATTRIBUTE "nsslapd-attribute" +#define CONFIG_SCHEMACHECK_DIRECTIVE "schemacheck" +#define CONFIG_SCHEMACHECK_ATTRIBUTE "nsslapd-schemacheck" +#define CONFIG_LOGLEVEL_DIRECTIVE "loglevel" +#define CONFIG_LOGLEVEL_ATTRIBUTE "nsslapd-errorlog-level" +#define CONFIG_ACCESSLOGLEVEL_DIRECTIVE "accessloglevel" +#define CONFIG_ACCESSLOGLEVEL_ATTRIBUTE "nsslapd-accesslog-level" +#define CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "accesslog-maxNumOfLogsPerDir" +#define CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-accesslog-maxlogsperdir" +#define CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "errorlog-maxNumOfLogsPerDir" +#define CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-errorlog-maxlogsperdir" +#define CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "auditlog-maxNumOfLogsPerDir" +#define CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-auditlog-maxlogsperdir" +#define CONFIG_ACCESSLOG_MAXLOGSIZE_DIRECTIVE "accesslog-maxlogsize" +#define CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-accesslog-maxlogsize" +#define CONFIG_ERRORLOG_MAXLOGSIZE_DIRECTIVE "errorlog-maxlogsize" +#define CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-errorlog-maxlogsize" +#define CONFIG_AUDITLOG_MAXLOGSIZE_DIRECTIVE "auditlog-maxlogsize" +#define CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-auditlog-maxlogsize" +#define CONFIG_ACCESSLOG_LOGROTATIONTIME_DIRECTIVE "accesslog-logrotationtime" +#define CONFIG_ACCESSLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-accesslog-logrotationtime" +#define CONFIG_ERRORLOG_LOGROTATIONTIME_DIRECTIVE "errorlog-logrotationtime" +#define CONFIG_ERRORLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-errorlog-logrotationtime" +#define CONFIG_AUDITLOG_LOGROTATIONTIME_DIRECTIVE "auditlog-logrotationtime" +#define CONFIG_AUDITLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-auditlog-logrotationtime" +#define CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "accesslog-logrotationtimeunit" +#define CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logrotationtimeunit" +#define CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "errorlog-logrotationtimeunit" +#define CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logrotationtimeunit" +#define CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "auditlog-logrotationtimeunit" +#define CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logrotationtimeunit" +#define CONFIG_ACCESSLOG_MAXLOGDISKSPACE_DIRECTIVE "accesslog-maxlogdiskspace" +#define CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logmaxdiskspace" +#define CONFIG_ERRORLOG_MAXLOGDISKSPACE_DIRECTIVE "errorlog-maxlogdiskspace" +#define CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logmaxdiskspace" +#define CONFIG_AUDITLOG_MAXLOGDISKSPACE_DIRECTIVE "auditlog-maxlogdiskspace" +#define CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logmaxdiskspace" +#define CONFIG_ACCESSLOG_MINFREEDISKSPACE_DIRECTIVE "accesslog-minfreediskspace" +#define CONFIG_ACCESSLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logminfreediskspace" +#define CONFIG_ERRORLOG_MINFREEDISKSPACE_DIRECTIVE "errorlog-minfreediskspace" +#define CONFIG_ERRORLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logminfreediskspace" +#define CONFIG_AUDITLOG_MINFREEDISKSPACE_DIRECTIVE "auditlog-minfreediskspace" +#define CONFIG_AUDITLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logminfreediskspace" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_DIRECTIVE "accesslog-logexpirationtime" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-accesslog-logexpirationtime" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIME_DIRECTIVE "errorlog-logexpirationtime" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-errorlog-logexpirationtime" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIME_DIRECTIVE "auditlog-logexpirationtime" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-auditlog-logexpirationtime" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "accesslog-logexpirationtimeunit" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logexpirationtimeunit" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "errorlog-logexpirationtimeunit" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logexpirationtimeunit" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "auditlog-logexpirationtimeunit" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logexpirationtimeunit" +#define CONFIG_ACCESSLOG_LOGGING_ENABLED_DIRECTIVE "accesslog-logging-enabled" +#define CONFIG_ACCESSLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-accesslog-logging-enabled" +#define CONFIG_ERRORLOG_LOGGING_ENABLED_DIRECTIVE "errorlog-logging-enabled" +#define CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-errorlog-logging-enabled" +#define CONFIG_AUDITLOG_LOGGING_ENABLED_DIRECTIVE "auditlog-logging-enabled" +#define CONFIG_AUDITLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-auditlog-logging-enabled" +#define CONFIG_ROOTDN_DIRECTIVE "rootdn" +#define CONFIG_ROOTDN_ATTRIBUTE "nsslapd-rootdn" +#define CONFIG_ROOTPW_DIRECTIVE "rootpw" +#define CONFIG_ROOTPW_ATTRIBUTE "nsslapd-rootpw" +#define CONFIG_ROOTPWSTORAGESCHEME_DIRECTIVE "rootpwstoragescheme" +#define CONFIG_ROOTPWSTORAGESCHEME_ATTRIBUTE "nsslapd-rootpwstoragescheme" +#define CONFIG_UPDATEDN_DIRECTIVE "updatedn" +#define CONFIG_UPDATEDN_ATTRIBUTE "nsslapd-updatedn" +#define CONFIG_UPDATEPW_DIRECTIVE "updatepw" +#define CONFIG_UPDATEPW_ATTRIBUTE "nsslapd-updatepw" +#define CONFIG_UPDATESSLCLIENT_DIRECTIVE "updateSSLclient" +#define CONFIG_UPDATESSLCLIENT_ATTRIBUTE "nsslapd-updateSSLclient" +#define CONFIG_AUDITFILE_DIRECTIVE "auditfile" +#define CONFIG_AUDITFILE_ATTRIBUTE "nsslapd-auditlog" +#define CONFIG_LASTMOD_DIRECTIVE "lastmod" +#define CONFIG_LASTMOD_ATTRIBUTE "nsslapd-lastmod" +#define CONFIG_INCLUDE_DIRECTIVE "include" +#define CONFIG_INCLUDE_ATTRIBUTE "nsslapd-include" +#define CONFIG_DYNAMICCONF_DIRECTIVE "dynamicconf" +#define CONFIG_DYNAMICCONF_ATTRIBUTE "nsslapd-dynamicconf" +#define CONFIG_USEROC_DIRECTIVE "useroc" +#define CONFIG_USEROC_ATTRIBUTE "nsslapd-useroc" +#define CONFIG_USERAT_DIRECTIVE "userat" +#define CONFIG_USERAT_ATTRIBUTE "nsslapd-userat" +#define CONFIG_SVRTAB_DIRECTIVE "svrtab" +#define CONFIG_SVRTAB_ATTRIBUTE "nsslapd-svrtab" +#ifndef _WIN32 +#define CONFIG_LOCALUSER_DIRECTIVE "localuser" +#define CONFIG_LOCALUSER_ATTRIBUTE "nsslapd-localuser" +#endif /* !_WIN32 */ +#define CONFIG_LOCALHOST_DIRECTIVE "localhost" +#define CONFIG_LOCALHOST_ATTRIBUTE "nsslapd-localhost" +#define CONFIG_PORT_DIRECTIVE "port" +#define CONFIG_PORT_ATTRIBUTE "nsslapd-port" +#define CONFIG_LISTENHOST_DIRECTIVE "listenhost" +#define CONFIG_LISTENHOST_ATTRIBUTE "nsslapd-listenhost" +#define CONFIG_SECURITY_DIRECTIVE "security" +#define CONFIG_SECURITY_ATTRIBUTE "nsslapd-security" +#define CONFIG_SSL3CIPHERS_DIRECTIVE "SSL3ciphers" +#define CONFIG_SSL3CIPHERS_ATTRIBUTE "nsslapd-SSL3ciphers" +#define CONFIG_ACCESSLOG_DIRECTIVE "accesslog" +#define CONFIG_ACCESSLOG_ATTRIBUTE "nsslapd-accesslog" +#define CONFIG_ERRORLOG_DIRECTIVE "errorlog" +#define CONFIG_ERRORLOG_ATTRIBUTE "nsslapd-errorlog" +#define CONFIG_INSTANCEDIR_DIRECTIVE "instancedir" +#define CONFIG_INSTANCEDIR_ATTRIBUTE "nsslapd-instancedir" +#define CONFIG_SECUREPORT_DIRECTIVE "secure-port" +#define CONFIG_SECUREPORT_ATTRIBUTE "nsslapd-securePort" +#define CONFIG_SECURELISTENHOST_DIRECTIVE "secure-listenhost" +#define CONFIG_SECURELISTENHOST_ATTRIBUTE "nsslapd-securelistenhost" +#define CONFIG_THREADNUMBER_DIRECTIVE "threadnumber" +#define CONFIG_THREADNUMBER_ATTRIBUTE "nsslapd-threadnumber" +#define CONFIG_MAXTHREADSPERCONN_DIRECTIVE "maxthreadsperconn" +#define CONFIG_MAXTHREADSPERCONN_ATTRIBUTE "nsslapd-maxthreadsperconn" +#if !defined(_WIN32) && !defined(AIX) +#define CONFIG_MAXDESCRIPTORS_DIRECTIVE "maxdescriptors" +#define CONFIG_MAXDESCRIPTORS_ATTRIBUTE "nsslapd-maxdescriptors" +#endif /* !_WIN32 && ! AIX */ +#define CONFIG_RESERVEDESCRIPTORS_DIRECTIVE "reservedescriptors" +#define CONFIG_RESERVEDESCRIPTORS_ATTRIBUTE "nsslapd-reservedescriptors" +#define CONFIG_IDLETIMEOUT_DIRECTIVE "idletimeout" +#define CONFIG_IDLETIMEOUT_ATTRIBUTE "nsslapd-idletimeout" +#define CONFIG_IOBLOCKTIMEOUT_DIRECTIVE "ioblocktimeout" +#define CONFIG_IOBLOCKTIMEOUT_ATTRIBUTE "nsslapd-ioblocktimeout" +#define CONFIG_NTSYNCH_DIRECTIVE "ntsynch" +#define CONFIG_NTSYNCH_ATTRIBUTE "nsslapd-NTSynch" +#define CONFIG_NTSYNCHUSESSL_DIRECTIVE "ntsynchusessl" +#define CONFIG_NTSYNCHUSESSL_ATTRIBUTE "nsslapd-NTSynch-SSL" +#define CONFIG_NTSYNCHPORT_DIRECTIVE "ntsynch-port" +#define CONFIG_NTSYNCHPORT_ATTRIBUTE "nsslapd-NTSynch-port" +#define CONFIG_ACCESSCONTROL_DIRECTIVE "accesscontrol" +#define CONFIG_ACCESSCONTROL_ATTRIBUTE "nsslapd-accesscontrol" +#define CONFIG_GROUPEVALNESTLEVEL_DIRECTIVE "groupevalnestlevel" +#define CONFIG_GROUPEVALNESTLEVEL_ATTRIBUTE "nsslapd-groupevalnestlevel" +#define CONFIG_NAGLE_DIRECTIVE "nagle" +#define CONFIG_NAGLE_ATTRIBUTE "nsslapd-nagle" +#define CONFIG_PW_CHANGE_DIRECTIVE "pw_change" +#define CONFIG_PW_CHANGE_ATTRIBUTE "passwordChange" +#define CONFIG_PW_MUSTCHANGE_DIRECTIVE "pw_must_change" +#define CONFIG_PW_MUSTCHANGE_ATTRIBUTE "passwordMustChange" +#define CONFIG_PW_SYNTAX_DIRECTIVE "pw_syntax" +#define CONFIG_PW_SYNTAX_ATTRIBUTE "passwordCheckSyntax" +#define CONFIG_PW_MINLENGTH_DIRECTIVE "pw_minlength" +#define CONFIG_PW_MINLENGTH_ATTRIBUTE "passwordMinLength" +#define CONFIG_PW_EXP_DIRECTIVE "pw_exp" +#define CONFIG_PW_EXP_ATTRIBUTE "passwordExp" +#define CONFIG_PW_MAXAGE_DIRECTIVE "pw_maxage" +#define CONFIG_PW_MAXAGE_ATTRIBUTE "passwordMaxAge" +#define CONFIG_PW_MINAGE_DIRECTIVE "pw_minage" +#define CONFIG_PW_MINAGE_ATTRIBUTE "passwordMinAge" +#define CONFIG_PW_WARNING_DIRECTIVE "pw_warning" +#define CONFIG_PW_WARNING_ATTRIBUTE "passwordWarning" +#define CONFIG_PW_HISTORY_DIRECTIVE "pw_history" +#define CONFIG_PW_HISTORY_ATTRIBUTE "passwordHistory" +#define CONFIG_PW_INHISTORY_DIRECTIVE "pw_inhistory" +#define CONFIG_PW_INHISTORY_ATTRIBUTE "passwordInHistory" +#define CONFIG_PW_LOCKOUT_DIRECTIVE "pw_lockout" +#define CONFIG_PW_LOCKOUT_ATTRIBUTE "passwordLockout" +#define CONFIG_PW_STORAGESCHEME_DIRECTIVE "pw_storagescheme" +#define CONFIG_PW_STORAGESCHEME_ATTRIBUTE "passwordStorageScheme" +#define CONFIG_PW_MAXFAILURE_DIRECTIVE "pw_maxfailure" +#define CONFIG_PW_MAXFAILURE_ATTRIBUTE "passwordMaxFailure" +#define CONFIG_PW_UNLOCK_DIRECTIVE "pw_unlock" +#define CONFIG_PW_UNLOCK_ATTRIBUTE "passwordUnlock" +#define CONFIG_PW_LOCKDURATION_DIRECTIVE "pw_lockduration" +#define CONFIG_PW_LOCKDURATION_ATTRIBUTE "passwordLockoutDuration" +#define CONFIG_PW_RESETFAILURECOUNT_DIRECTIVE "pw_resetfailurecount" +#define CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE "passwordResetFailureCount" +#define CONFIG_ACCESSLOG_BUFFERING_DIRECTIVE "logbuffering" +#define CONFIG_ACCESSLOG_BUFFERING_ATTRIBUTE "nsslapd-accesslog-logbuffering" +#define CONFIG_CHANGELOG_DIR_DIRECTIVE "changelogdir" +#define CONFIG_CHANGELOG_DIR_ATTRIBUTE "nsslapd-changelogdir" +#define CONFIG_CHANGELOG_SUFFIX_DIRECTIVE "changelogsuffix" +#define CONFIG_CHANGELOG_SUFFIX_ATTRIBUTE "nsslapd-changelogsuffix" +#define CONFIG_CHANGELOG_MAXENTRIES_DIRECTIVE "changelogmaxextries" +#define CONFIG_CHANGELOG_MAXENTRIES_ATTRIBUTE "nsslapd-changelogmaxentries" +#define CONFIG_CHANGELOG_MAXAGE_DIRECTIVE "changelogmaxage" +#define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE "nsslapd-changelogmaxage" +#define CONFIG_RETURN_EXACT_CASE_DIRECTIVE "return_exact_case" +#define CONFIG_RESULT_TWEAK_DIRECTIVE "result_tweak" +#define CONFIG_REFERRAL_MODE_DIRECTIVE "referralmode" +#define CONFIG_ATTRIBUTE_NAME_EXCEPTION_DIRECTIVE "attribute_name_exceptions" +#define CONFIG_MAXBERSIZE_DIRECTIVE "maxbersize" +#define CONFIG_VERSIONSTRING_DIRECTIVE "versionstring" +#define CONFIG_ENQUOTE_SUP_OC_DIRECTIVE "enquote_sup_oc" +#define CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE "nsslapd-enquote_sup_oc" +#define CONFIG_BASEDN_DIRECTIVE "certmap-basedn" +#define CONFIG_BASEDN_ATTRIBUTE "nsslapd-certmap-basedn" + +%HashParametersName = (); + +# The following hash displays only general server parameters to migrate under cn=config +%GeneralSrvParamToMigrate = ( + 'accesscontrol' => 'nsslapd-accesscontrol', + 'errorlog-logging-enabled' => 'nsslapd-errorlog-logging-enabled', + 'accesslog-logging-enabled' => 'nsslapd-accesslog-logging-enabled', + 'auditlog-logging-enabled' => 'nsslapd-auditlog-logging-enabled', + 'logbuffering' => 'nsslapd-accesslog-logbuffering', + 'accesslog-logexpirationtime' => 'nsslapd-accesslog-logexpirationtime', + 'accesslog-logexpirationtimeunit' => 'nsslapd-accesslog-logexpirationtimeunit', + 'accesslog-maxlogdiskspace' => 'nsslapd-accesslog-logmaxdiskspace', + 'accesslog-minfreediskspace' => 'nsslapd-accesslog-logminfreediskspace', + 'accesslog-logrotationtime' => 'nsslapd-accesslog-logrotationtime', + 'accesslog-logrotationtimeunit' => 'nsslapd-accesslog-logrotationtimeunit', + 'accesslog-maxlogsize' => 'nsslapd-accesslog-maxlogsize', + 'accesslog-maxnumoflogsperdir' => 'nsslapd-accesslog-maxLogsPerDir', + 'auditlog-logexpirationtime' => 'nsslapd-auditlog-logexpirationtime', + 'auditlog-logexpirationtimeunit' => 'nsslapd-auditlog-logexpirationtimeunit', + 'auditlog-maxlogdiskspace' => 'nsslapd-auditlog-logmaxdiskspace', + 'auditlog-minfreediskspace' => 'nsslapd-auditlog-logminfreediskspace', + 'auditlog-logrotationtime' => 'nsslapd-auditlog-logrotationtime', + 'auditlog-logrotationtimeunit' => 'nsslapd-auditlog-logrotationtimeunit', + 'auditlog-maxlogsize' => 'nsslapd-auditlog-maxlogsize', + 'auditlog-maxnumoflogsperdir' => 'nsslapd-auditlog-maxLogsPerDir', + 'certmap-basedn' => 'nsslapd-certmap-basedn', + 'enquote_sup_oc' => 'nsslapd-enquote-sup-oc', + 'loglevel' => 'nsslapd-errorlog-level', + 'errorlog-logexpirationtime' => 'nsslapd-errorlog-logexpirationtime', + 'errorlog-logexpirationtimeunit' => 'nsslapd-errorlog-logexpirationtimeunit', + 'errorlog-maxlogdiskspace' => 'nsslapd-errorlog-logmaxdiskspace', + 'errorlog-minfreediskspace' => 'nsslapd-errorlog-logminfreediskspace', + 'errorlog-logrotationtime' => 'nsslapd-errorlog-logrotationtime', + 'errorlog-logrotationtimeunit' => 'nsslapd-errorlog-logrotationtimeunit', + 'errorlog-maxlogsize' => 'nsslapd-errorlog-maxlogsize', + 'errorlog-maxnumoflogsperdir' => 'nsslapd-errorlog-maxlogsperdir', + 'idletimeout' => 'nsslapd-idletimeout', + 'ioblocktimeout' => 'nsslapd-ioblocktimeout', + 'lastmod' => 'nsslapd-lastmod', + 'listenhost' => 'nsslapd-listenhost', + 'maxdescriptors' => 'nsslapd-maxdescriptors', + 'referral' => 'nsslapd-referral', + 'reservedescriptors' => 'nsslapd-reservedescriptors', + 'rootpwstoragescheme' => 'nsslapd-rootpwstoragescheme', + 'schemacheck' => 'nsslapd-schemacheck', + 'secure-port' => 'nsslapd-securePort', + 'security' => 'nsslapd-security', + 'sizelimit' => 'nsslapd-sizelimit', + 'SSL3ciphers' => 'nsslapd-SSL3ciphers', + 'timelimit' => 'nsslapd-timelimit', + 'pw_change' => 'passwordChange', + 'pw_syntax' => 'passwordCheckSyntax', + 'pw_exp' => 'passwordExp', + 'pw_history' => 'passwordHistory', + 'pw_inhistory' => 'passwordInHistory', + 'pw_lockout' => 'passwordLockout', + 'pw_lockduration' => 'passwordLockoutDuration', + 'pw_maxage' => 'passwordMaxAge', + 'pw_maxfailure' => 'passwordMaxFailure', + 'pw_minage' => 'passwordMinAge', + 'pw_minlength' => 'passwordMinLength', + 'pw_must_change' => 'passwordMustChange', + 'pw_resetfailurecount' => 'passwordResetFailureCount', + 'pw_storagescheme' => 'passwordStorageScheme', + 'pw_unlock' => 'passwordUnlock', + 'pw_warning' => 'passwordWarning' +); + +# the following hash displays global parameters related to database stored under cn=config,cn=ldbm database,cn=plugins,cn=config +%GlobalConfigLDBMparamToMigrate = ( + 'allidsthreshold' => 'nsslapd-allidsthreshold', + 'lookthroughlimit' => 'nsslapd-lookthroughlimit', + 'mode' => 'nsslapd-mode', + 'dbcachesize' => 'nsslapd-dbcachesize' +); + +# the following hash displays specific parameters to each backends and stored under cn=DBname,cn=ldbm database,cn=plugins,cn=config +%LDBMparamToMigrate = ( + 'cachesize' => 'nsslapd-cachesize', + 'readonly' => 'nsslapd-readonly' +); + +%stdIncludes = ( + "${oldConfDir}slapd.at.conf", "\n", + "${oldConfDir}slapd.oc.conf", "\n", + "${oldConfDir}java-object-schema.conf", "\n", + "${oldConfDir}ns-admin-schema.conf", "\n", + "${oldConfDir}ns-calendar-schema.conf", "\n", + "${oldConfDir}ns-certificate-schema.conf", "\n", + "${oldConfDir}ns-common-schema.conf", "\n", + "${oldConfDir}ns-compass-schema.conf", "\n", + "${oldConfDir}ns-delegated-admin-schema.conf", "\n", + "${oldConfDir}ns-directory-schema.conf", "\n", + "${oldConfDir}ns-legacy-schema.conf", "\n", + "${oldConfDir}ns-mail-schema.conf", "\n", + "${oldConfDir}ns-mcd-browser-schema.conf", "\n", + "${oldConfDir}ns-mcd-config-schema.conf", "\n", + "${oldConfDir}ns-mcd-li-schema.conf", "\n", + "${oldConfDir}ns-mcd-mail-schema.conf", "\n", + "${oldConfDir}ns-media-schema.conf", "\n", + "${oldConfDir}ns-mlm-schema.conf", "\n", + "${oldConfDir}ns-msg-schema.conf", "\n", + "${oldConfDir}ns-netshare-schema.conf", "\n", + "${oldConfDir}ns-news-schema.conf", "\n", + "${oldConfDir}ns-proxy-schema.conf", "\n", + "${oldConfDir}ns-value-schema.conf", "\n", + "${oldConfDir}ns-wcal-schema.conf", "\n", + "${oldConfDir}ns-cos-schema.conf", "\n", + "${oldConfDir}ns-web-schema.conf", "\n" +); + +%userDefinedConfigFiles = ( + "slapd.conf", "\n", + "slapd.ldbm.conf", "\n", + "slapd.user_at.conf", "\n", + "slapd.user_oc.conf", "\n", + "ns-schema.conf", "\n" + ); + +$CIS_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.15" ; +$TELEPHONE_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.50" ; +$DN_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.12" ; +$CES_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.26" ; +$INT_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.27" ; +$BIN_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.5" ; + +%allowedPlugins = ( + "cis", $CIS_SYNTAX_OID, + "tel", $TELEPHONE_SYNTAX_OID, + "dn", $DN_SYNTAX_OID, + "ces", $CES_SYNTAX_OID, + "int", $INT_SYNTAX_OID, + "bin", $BIN_SYNTAX_OID + ); + +%allowedModifiers = ( + "single", "SINGLE-VALUE" + ); +# "override" is not supported anymore and "operational" cannot be used in user defined attribute. + +@oldSuffixes = () ; # array of 4.x suffixes (with "o=netscaperoot" if presents) + +# link beetwen the name of the suffix and its associated DBname +%DBNAMES = () ; +%DBDirectory = () ; + +%oldhash = () ; + +# list of standard plugin's in version 4 +%stdPlugins = ( + "7-bit check" => "\n", + "binary syntax" => "\n", + "case exact string syntax" => "\n", + "case ignore string syntax" => "\n", + "distinguished name syntax" => "\n", + "integer syntax" => "\n", + "internationalization plugin" => "\n", + "referential integrity postoperation" => "\n", + "telephone syntax" => "\n", + "uid uniqueness" => "\n" + + ); + +# list of standard indexes configured out of the box in version 4 +%stdIndex = ( + 'aci', "\n", + 'changenumber', "\n", + 'copiedfrom', "\n", + 'dncomp', "\n", + 'entrydn', "\n", + 'numsubordinates', "\n", + 'objectclass', "\n", + 'parentid', "\n" +); + +# list of user added Plugin's. In 5.x, they 'll need to be recompiled +@badPlugins = () ; + +%newIndex = () ; + +%User_oc = () ; +# push objectnames as they are encountered in config files. +@User_oc_names = () ; + +%User_at = () ; + + + +#Usage parameters +$USER_OC_FILE_MODIFIED = 0 ; # 0 if user don't want to modify LDIF objectclasses before processing, 1 else +$USER_AT_FILE_MODIFIED = 0 ; +$INDEX_FILE_MODIFIED = 0 ; + +# Shutdown the legacy Directory instance +printTrace("\nShutdown the legacy Directory Server instance: ${oldHome}",0); +&stopServer($oldDir, 'slapd-'.$oldname); + +# compare configuration files with the standard ones +CompareStdConfigFiles() ; +die "\n\n The version of the product you want to migrate is not a 4.x Netscape Directory Server\n" unless ($oldVersion == 4) ; + +FillHashParametersName() ; + +############### Connect to the 5.x LDAP Directory Server ###################### +$ENV{"$LIB_PATH"} = "$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"} ; +my $LDAPservername = &getLDAPservername(); +die "\n Migration aborted. Check your 4.x and 5.x Netscape Directory Server are installed on the same machine \n" if ( $LDAPservername == -1 ); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; + +# continue if the connection to 5.x LDAP server is successful ! +printTrace("\nConnected to $Version.$Minor LDAP server\n",0) ; + +# get the uid and gid of the 5.x slapd user +($localuser, $newuid, $newgid) = getuid_gid(); +# get the uid and gid of the 4.x slapd user +($oldlocaluser, $olduid, $oldgid) = getolduid_gid(); + +# backup 5.x configuration files in <root_server5>/slapd-instancename/config +printTrace("\nBackup $serverHome${PATHSEP}config on $serverHome${PATHSEP}config_backup ...",0); +&backupConfigFiles(); + +# Parse the main configuration file: slapd.conf +printTrace("\nParse the configuration file: $oldSlapdConf...",0); +ParseSlapdConf("< ${oldSlapdConf}"); + +#migrate key/cert databases +printTrace("\nMigrate key/cert databases...",0); +&MigrateSSL(); + +# Update parameters : general server parameters, global LDBM parameter, specific backend parameters +printTrace("\nUpdate general server parameters...",0); +AddGeneralParameters(); +printTrace("\nUpdate global LDBM parameters...",0); +AddGeneralLDBMParameters(); +printTrace("\nUpdate specific backend parameters...",0); +AddSpecificLDBMParameters(); + +##### FOR TESTING PURPOSE ONLY ######## +# +#testIndexUpdating(); +# +####################################### + +# Migrate some entries contained in the old dse.ldif, and migrate certmap.conf +&MigrateDSE() ; +&MigrateCertmap() ; + +# update 5.x attribute definitions +LDAPmodify_User_at(); + +# update 5.x object classes definitions +LDAPmodify_User_oc(); + +# add new indexes to each backends +LDAPmodify_Indexes(); + +# migrate Plug'ins parameters (enable attribute, and arguments) +LDAPmodify_stdPlugin(); + +################## Close the connection to 5.x LDAP Server ##################### +$conn->close; + + +################## stop the 5.x instance and Export/Import the data, restart the server ################## +if (%DBNAMES) { + &stopServer($root,'slapd-'.$newname); + printTrace("\ndata processing...",0) ; + # migrate data for each suffix: 4.x -> LDIF files + &db2ldif($oldSlapdConf); + + # migrate LDIF data to the 5.x database: LDIF -> 5.x + &manyLdif2db(); + &startServer(); +} +else { + printTrace("\nThere no 4.x non-standard suffixes to migrate",0); +} + +printMsg("\n\n ****** End of migration ******\n\n"); + +close(LOGFILE); + + +########################################################################################### +# get input users +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # 4.x instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe 4.x instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # 5.x instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe 5.x instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # 5.x DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + $NO_INPUT_USER = 1 ; + } elsif ("$ARGV[$i]" eq "-L") { # migration logfile + $LogFileReport = $ARGV[++$i] ; + } + else { + print("\nThe option $ARGV[$i] is not recognized"); + &usage() ; + exit(1); + } + $i++; + } + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe 5.x instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe 4.x instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + + if ($exit) { + &usage() ; + exit(1); + } + +} + + +############################################################################### +# This subroutine is used to parse the slapd.conf configuration file and migrate specific parameters contained in it + + +sub ParseSlapdConf { + my $oldsrc = shift; + my $NumLine = 0 ; + # read the old conf file into a hash table + open( OLDSRC, $oldsrc ) || die "Can't open $oldsrc: $!: "; + LINE: while ( <OLDSRC> ) { + $NumLine++ ; + printTrace("\nLine: $_",4) ; + if (/^\s*\#/) { # skip comments + printTrace("\n# ",4) ; + next LINE; + } + if (/^\s*$/) { # skip blank lines + printTrace("\nBLANK LINE",4); + next LINE; + } elsif (/^suffix\s+/i) { + chomp($_) ; + CheckSuffix($_); + } elsif (/^plugin/i) { + printTrace("\nPLUGIN",4); + chomp($_); + if (! &isAStandardPlugin($_)) { + push @badPlugins, $_; + } + else { + my $Plugin = $_ ; + if (! &ParsePlugin($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of the plugin:\n$Plugin"); + } + } + } elsif (/^include\s+[\"]?(.*?)[\"]?\s*$/i) { + # strip leading and trailing " + my $include_file = $1 ; + grep { s@/@\\@g } $include_file if $isNT; + if (! &isAStandardInclude($include_file)) { + printTrace("\nFILE: $1 NOT STANDARD",4) ; + &ParseConfigurationFile($include_file); + printTrace("\nEXIT ParseConfigurationFile: $include_file",4) ; + } + } elsif (/^userat\s+[\"]?(.*?)[\"]?\s*$/i) { + printTrace("\nuserat: $1",4); + my $at_file = $1 ; + grep { s@/@\\@g } $at_file if $isNT; + # Parse user defined attributes + &ParseAttributesFile($at_file); + } elsif (/^useroc\s+[\"]?(.*?)[\"]?\s*$/i) { + printTrace("\nuseroc: $1",4); + my $oc_file = $1 ; + grep { s@/@\\@g } $oc_file if $isNT; + # Parse user defined object classes + &ParseObjectClassesFile($oc_file); + } elsif (/^dynamicconf\s+[\"]?(.*?)[\"]?\s*$/i){ + printTrace("\ndynamicconf: $1",4); + my $dynamiconf_file = $1 ; + grep { s@/@\\@g } $dynamiconf_file if $isNT; + # Parse dynamic configuration file (e-g slapd.ldbm.conf) + &ParseConfigurationFile($dynamiconf_file); + } elsif (/^\s*(\S+)\s+[\"]?(.*?)[\"]?\s*$/) { + printTrace("\nParseParameters: $1",4); + # Parse parameters and record the associated value in %oldhash + &ParseParameters($1,$2,$NumLine); + } else { + printTrace("\nUnknown format of configuration data: $_",0); } + } + close(OLDSRC); + + } + + + +############################################################################# +# return 1 if the suffix already exists, 0 else +sub existSuffix { + my $suffixname = shift ; + my $entry = $conn->search("cn=\"$suffixname\",cn=mapping tree,cn=config ", "base","objectclass=*") ; + return 1 if ($entry) ; + return 0 ; +} + +# return the name of the backend if it has been successfully created, 0 else +sub createBackend { + my $suffixname = shift ; + my $backend = "MigratedDB_0" ; + my $NbRetry = 1 ; + my $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + while ($entry) { + # try to find another name for the backend + $backend = "MigratedDB_$NbRetry" ; + $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + $NbRetry++; + } + # normally I should have a unique name for the backend + my $suffixarg = "nsslapd-suffix" ; + $entry = $conn->newEntry() ; + $entry->setDN("cn=$backend,cn=ldbm database,cn=plugins,cn=config"); + $entry->setValues("objectclass", "top", "extensibleObject", "nsBackendInstance" ); + $entry->setValues("cn", $backend ); + $entry->setValues($suffixarg, $suffixname ); + my $res = $conn->add($entry) ; + if ($res) { + return $backend ; + } + else { + return 0 ; + } +} + +# return 1, if add the new entry in the mapping tree, else 0 +sub AddEntryInMappingTree { + my $backend = shift ; + my $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + if ($entry) { + printTrace("\nAddEntry in progress ...",4) ; + my $suffixarg = "nsslapd-suffix" ; + my $statearg = "nsslapd-state" ; + my $backendarg= "nsslapd-backend"; + my $suffixname = $entry->{$suffixarg}[0]; + $entry = $conn->newEntry() ; + $entry->setDN("cn=\"$suffixname\",cn=mapping tree,cn=config") ; + $entry->setValues("objectclass", "top", "extensibleObject", "nsMappingTree" ); + $entry->setValues("cn", "\"$suffixname\""); + $entry->setValues($statearg, "backend"); + $entry->setValues($backendarg, $backend); + return $conn->add($entry); + } + else { + printTrace("\nNo AddEntry processed for $backend",4); + return 0 ; + } +} + + +# Treat the case where the suffix is "o=NetscapeRoot" +sub CheckSuffix { + my $suffix = shift ; + my $suffixname ; + if (!(/^suffix\s+\"?(.*?)\"?\s*$/i)) { + printMsg("Syntax error of the suffix: $suffix"); + return 0 ; + } + else { + $suffixname = $1 ; + } + if (/^suffix\s+\"?\s*o=netscaperoot\s*\"?\s*$/i) { + printTrace("\nFor the suffix o=NetscapeRoot, we do nothing",1); + # treat the case where the suffix is "o=NetscapeRoot" + } + else { + push @oldSuffixes, $_; + # check if the suffix already exists in the 5.x DS target + if (! existSuffix($suffixname)) { + printTrace("\nSuffix $suffixname doesn't exist",1) ; + # create a new backend with the name of the suffix preceded by MigratedDB_ + my $backend = createBackend($suffixname) ; + if ($backend) { + printTrace("\nBackend: $backend has been created !!!",1); + # if the creation of the backend is ok, we add a new entry in the mapping tree + if (AddEntryInMappingTree($backend)) { + # We add the association dbname->suffix in the hash %DBNAMES + $DBNAMES{$suffixname} = $backend ; + # get the db filename + $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + my $dirarg = "nsslapd-directory"; + $DBDirectory{$backend} = $entry->{$dirarg}[0]; + printTrace("\nThe relation $backend->$suffixname has been added to the mapping tree",2); + } + else { + printMsg("\nCOULD NOT ADD ENTRY: $backend->$suffixname IN MAPPINGTREE"); + } + } + else { + printMsg("\nCOULD NOT CREATE BACKEND: $backend"); + } + } + else { + printTrace("\nSuffix: $suffixname already exists",1); + # the suffix already exists in the 5.x DS + } + } + return 1 ; +} + +############################################################################# +# Usefull to know the standard configuration +sub isAStandardPlugin { + my $line = shift; + chomp($line); + printTrace("\nStdPlugin?: $line",4); + if ($line =~ /^plugin\s+(database|extendop|preoperation|postoperation|matchingrule|syntax)\s+(on|off)\s+\"(.*?)\"\s+\"(.*?)\"\s+(\S+)(.*)$/i) { + # $1 = <type>, $2 = <on|off>, $3 = <name>, $4 = <pathname>, $5 = <init_function>, $6 = [<arg>]* + printTrace("\nName: $3, pathname: $4, init_function: $5",4); + + my $LC_line = lc($3); + my $Value = $stdPlugins{$LC_line} ; + if ($Value) { + printTrace("\nIS A STANDARD PLUGIN",4); + } + else { + printTrace("\nNOT A STANDARD PLUGIN",4); + } + return $stdPlugins{$LC_line} ; + } + else { + printTrace("\nSYNTAX ERROR PLUGIN",4); + return 0 ; + } +} + +sub isAStandardIndex { + my $line = shift ; + chomp($line); + if ($line =~ /^index\s+(\S+).*/i) { + my $LC_line = lc($1); + my $Value = $stdIndex{$LC_line} ; + printTrace("\nInclude: $LC_line \nValue: $Value", 4); + return $stdIndex{$LC_line}; + } + else { + return 0 ; + } +} + + +sub isAStandardInclude { + my $line = shift; + + chomp($line); + if ($isNT){ + return $stdIncludes{lc($line)}; + } + else { + return $stdIncludes{$line} ; + } +} + +############################################################################# +# +# Execute a Perldap command to update plugins definition in the 5.x schema + +sub LDAPmodify_stdPlugin { + my $Filename = shift ; + my @pluginames = keys(%stdPlugins); + if (! $STDPLUGINS_FILE_MODIFIED) { + printTrace("\nLDAPmodify_plugin",4); + printTrace("\nMigrate plugin's...",1); + foreach $pluginame ( @pluginames ) { + my $update_plugin = 0 ; + my $ref_plugin = $stdPlugins{$pluginame}; + if ($ref_plugin ne "\n") { + my $name = $ref_plugin->name ; + my $entry = $conn->search("cn=$name,cn=plugins,cn=config", "base","objectclass=nsSlapdPlugin") ; + if ($entry) { + my $pluginenabled="nsslapd-pluginenabled" ; + if (($entry->{$pluginenabled}[0]) ne $ref_plugin->enable) { + $update_plugin = 1 ; + my $enable = $ref_plugin->enable ; + printTrace("\n$pluginame, plugin-enable: $enable",3) ; + $entry->setValues($pluginenabled, $enable ); + } + my $ArgNum = 0 ; + foreach $ArgValue (@{$ref_plugin->args}) { + my $Arg="nsslapd-pluginarg$ArgNum"; + printTrace("\n$Arg: $ArgValue",3) ; + if ($entry->{$Arg}[0] ne $ArgValue) { + printTrace("\n$pluginame, $Arg: $ArgValue",3) ; + $update_plugin = 1 ; + $entry->setValues($Arg, $ArgValue) ; + } + $ArgNum++ ; + } + if ($update_plugin) { + printTrace("\n$pluginame is being updated...",2); + my $res = $conn->update($entry) ; + if ($res) { + printTrace("\nupdated !",2); + } + else { + printMsg("\nError during update of plugin: $pluginame") ; + $MigrationErrors .= "\nError during update of plugin: $pluginame"; + } + } + else { + printTrace("\n$pluginame has not changed",4); + } + } + else { + printMsg("\ncan't access the plugin: cn=$name,cn=plugins,cn=config"); + } + } + else { + printTrace("\nPLUGIN NOT RECORDED: $pluginame",4) ; + } + } + } + else { + # treat the case where the user wants to look at these plugins before processing + } +} + +############################################################################# +# Execute Perldap command to add new indexes to the migrated instances + +sub LDAPmodify_Indexes { + my $Filename = shift ; + my @indexnames = keys(%newIndex); + my @suffixnames = keys(%DBNAMES); + if ((! $INDEX_FILE_MODIFIED) && (%DBNAMES)) { + # we update indexes only if there is at least one backend to migrate + printTrace("\nLDAPmodify_indexes",4); + printTrace("\nMigrate indexes...",1); + foreach $indexname ( @indexnames ) { + printTrace("\nIndexName: $indexname",4); + printTrace("\nIndexTypes: .@{$newIndex{$indexname}->types}.", 4) ; + printTrace("\nIndexOIDS: .@{$newIndex{$indexname}->oids}.", 4) ; + foreach $suffixname ( @suffixnames ) { + # check if the index already exists ! + printTrace("\nsearch for cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...", 3); + my $entry = $conn->search("cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + if (! $entry) { + # create a new index + printTrace("index $indexname is being created under cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...",2); + my $entry = $conn->newEntry(); + $entry->setDN("cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config"); + $entry->setValues("objectclass", "top", "nsIndex" ) ; + $entry->setValues("cn", $indexname) ; + $entry->setValues("nssystemindex", "false") ; + my @types = @{$newIndex{$indexname}->types} ; + my @oids = @{$newIndex{$indexname}->oids} ; + $entry->setValues("nsindextype", @types) if (@types) ; + $entry->setValues("nsmatchingrule", @oids ) if (@oids); + my $res = $conn->add($entry) ; + if ($res) { + printTrace("\nAdd index successfully: $indexname for backend: $DBNAMES{$suffixname}",2); + } + else { + printMsg("\n Failed to add the index: $indexname to backend: $DBNAMES{$suffixname}"); + $MigrationErrors .= "\n Failed to add the index: $indexname to backend: $DBNAMES{$suffixname}" ; + } + } + elsif ($entry->{nssystemindex}[0] eq "false") { + # if the index is not a system index, we update it + printTrace("\nindex $indexname is being processed under cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...",2); + my @types = @{$newIndex{$indexname}->types} ; printTrace("\ntypes: .@types.",2) ; + my @oids = @{$newIndex{$indexname}->oids} ; printTrace("\noids: .@oids.",2) ; + my @existing_types = $entry->getValues("nsindextype"); + my @existing_oids = $entry->getValues("nsmatchingrule"); + # get the elements present in @types and not present in @existing_types + my @typesToAdd = &getDiff(\@types, \@existing_types); + # same for matchingrules + my @oidsToAdd = &getDiff(\@oids, \@existing_oids); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + foreach $newoid (@oidsToAdd) { + $entry->addValue("nsmatchingrule", $newoid); + } + if (@typesToAdd || @oidsToAdd) { + my $res = $conn->update($entry) ; + if ($res) { + printTrace("\nUpdate index successfully: $indexname for backend: $DBNAMES{$suffixname}",2); + } + else { + printMsg("\n Failed to update the index: $indexname to backend: $DBNAMES{$suffixname}"); + $MigrationErrors .= "\n Failed to update the index: $indexname to backend: $DBNAMES{$suffixname}" ; + } + } + else { + printTrace("\nNothing to update",2); + } + } + else { + printTrace("\nThe index: $indexname is a system index. It can't be updated",2); + } + } + } + + } + else { + # treat the case where the user wants to look at these indexes before processing + } + +} + +############################################################################# +# +# Execute a Perldap command to add all user defined object classes in the 5.x schema + +sub LDAPmodify_User_oc { + my $Filename = shift ; + if (! $USER_OC_FILE_MODIFIED) { + printTrace("\nLDAPmodify_User_oc",4); + printTrace("\nMigrate objectclasses...",1); + foreach $objectname ( @User_oc_names ) { + my $entry = $conn->search("cn=schema", "base","objectclass=*") ; + die "\ncan't connect to object: cn=schema\n" unless ($entry); + printTrace("\nObjectName: $objectname\nValue: $User_oc{$objectname}",3); + next if ($entry->hasValue("objectclasses",$User_oc{$objectname},1)) ; + $entry->addValue("objectclasses",$User_oc{$objectname},"1") ; + my $res = $conn->update($entry) ; + if (! $res) { + printMsg("\nCan\'t add objectclass to the schema: $User_oc{$objectname}"); + my $msg = $conn->getErrorString(); + printMsg("\nMsg: $msg"); + $MigrationErrors .= "\nCan\'t add objectclass to the schema: $User_oc{$objectname}" ; + } + else { + printTrace("\nobjectclass: $User_oc{$objectname} added",2); + } + } + } + else { + # treat the case where the user wants to look at these objectclasses before processing + } +} + +############################################################################# +# +# Execute a Perldap command to add all user defined attributes in the 5.x schema + +sub LDAPmodify_User_at { + my $Filename = shift ; + my @attributenames = keys(%User_at); + if (! $USER_AT_FILE_MODIFIED) { + + printTrace("\nLDAPmodify_User_at",4); + printTrace("\nMigrate attributes...",1); + foreach $attributename ( @attributenames ) { + my $entry = $conn->search("cn=schema", "base","objectclass=*") ; + printTrace("\nAtributeName: $attributename, Value: $User_at{$attributename}",3); + die "\nentry not found cn=schema\n" unless $entry ; + next if ($entry->hasValue("attributetypes",$User_at{$attributename},1) ) ; + my $res = $entry->addValue("attributetypes",$User_at{$attributename},"1") ; + if (! $res) { + printMsg("\nCan\'t add attribute to the schema: $User_at{$attributename}"); + $MigrationErrors .= "\nCan\'t add attribute to the schema: $User_at{$attributename}" ; + } + my $res = $conn->update($entry) ; + if ($res) { + printTrace("\nattribute: $attributename added",2); + } + else { + printMsg("\nCan\'t add attribute to the schema: $User_at{$attributename}"); + my $msg = $conn->getErrorString(); + printMsg("\nMsg: $msg"); + $MigrationErrors .= "\nCan\'t add attribute to the schema: $User_at{$attributename}" ; + } + } + } + else { + # treat the case where the user wants to look at these attributes before processing + } +} + +############################################################################# +# Add an object class to the user_oc hash and reset the object !!! +sub AddObjectClass { + my $ObjectClass = shift ; + my $ObjectName = $ObjectClass->{'ObjectName'} ; + my $Object_oid = $ObjectClass->{'Object_oid'} ; + my $Object_superior = $ObjectClass->{'Object_superior'} ; + my $Object_requires = $ObjectClass->{'Object_requires'} ; + my $Object_allows = $ObjectClass->{'Object_allows'} ; + my $ObjectClassDef = "( $Object_oid NAME \'$ObjectName\' DESC \'\' SUP $Object_superior STRUCTURAL MUST ($Object_requires) MAY ($Object_allows) X-ORIGIN \'user defined\' )"; + if ( (!($ObjectName =~ /^top$/i)) && ( ! $User_oc{$ObjectName} )) { + $User_oc{$ObjectName} = $ObjectClassDef ; + push @User_oc_names, $ObjectName ; + printTrace("ObjectName: $ObjectName \nObject_oid: $Object_oid \nObject_superior:$Object_superior \nObject_requires: $Object_requires \nObject_allows: $Object_allows \nObjectClassDefinition: $User_oc{$ObjectName}\n",4); + } + elsif ( ($User_oc{$ObjectName}) && ($User_oc{$ObjectName} ne $ObjectClassDef) ) { + printMsg("\nAttempt to redifine the objectclass: $ObjectName previously defined in your configuration file. Operation not allowed "); + } + else { + printMsg("\nAttempt to redifine the objectclass: top. Operation not allowed"); + } + resetObjectClass($ObjectClass); +} + +############################################################################# +# Build an LDIF attribute and add it to the user_at hash +sub AddAttribute { + my $Attr = shift ; + my $AttributeName = $Attr->{'AttributeName'}; + my $Attribute_oid = $Attr->{'Attribute_oid'}; + my $Attribute_aliases = $Attr->{'Attribute_aliases'}; + my $Attribute_syntax = $Attr->{'Attribute_syntax'}; + my $Attribute_single = $Attr->{'Attribute_single'}; + my $AttributeDef = "( $Attribute_oid NAME ( \'$AttributeName\' $Attribute_aliases) DESC \'User Defined Attribute\' SYNTAX $Attribute_syntax $Attribute_single X-ORIGIN 'user defined' )" ; + printTrace("\nAttributeDef: $AttributeDef",4); + $User_at{$AttributeName} = $AttributeDef ; +} +############################################################################# +# add the index structure to the newIndex hash +sub AddIndex { + my $ref_index = shift ; + my $state = shift ; + printTrace("\nAddIndex, last state: $state",4) ; + if ($state == 1) { + $ref_index->specific("ALL") ; + return 1 ; + } + elsif ($state == 6) { + $ref_index->specific("NONE") ; + return 1 ; + } + if (($state == 1) || ($state == 3) || ($state == 5) || ($state == 6)) { + foreach $name (@{$ref_index->names}) { + $newIndex{$name} = $ref_index ; # record the ref to the index struct in the newIndex hash + } + return 1 ; + } + else { + return 0 ; + } +} + +############################################################################# +# add the plugin structure to the stdPlugin hash + +sub AddPlugin { + my $ref_plugin = shift ; + printTrace("\nAddPlugin",4) ; + $stdPlugins{lc($ref_plugin->name)} = $ref_plugin ; + my $name = $ref_plugin->name ; + my $type = $ref_plugin->type ; + my $enable = $ref_plugin->enable ; + + printTrace("\nPluginName: $name",4); + printTrace("\nPluginType: $type",4); + printTrace("\nPluginEnable: $enable",4); + printTrace("\nPluginArgs: @{$ref_plugin->args}",4); + return 1 ; +} + + +############################################################################# +# parse a plugin definition and call the addindex + +sub ParsePlugin { + my $Plugin = shift ; + my $NumLine = shift ; + my $state = 0 ; + my $ErrorMsg = "Syntax error of a plugin definition. \n line parsed:"; + my $ref_plugin = S_plugin->new(); + printTrace("\nParsePlugin: $_",4); + if (/^plugin\s+(database|extendop|preoperation|postoperation|matchingrule|syntax)\s+(on|off)\s+\"(.*?)\"\s+\"(.*?)\"\s+(\S+)(.*)$/i) { + # $1 = <type>, $2 = <on|off>, $3 = <name>, $4 = <pathname>, $5 = <init_function>, $6 = [<arg>]* + $ref_plugin->name($3); + $ref_plugin->type($1); + $ref_plugin->enable($2); + $_ = $6 ; + my $ArgNb = 0 ; + my $prec ; + my $arg ; + my $Unix_oldDir = $oldDir ; + my $Unix_root = $root ; + grep { s@\\@/@g } $Unix_oldDir if $isNT; + grep { s@\\@/@g } $Unix_root if $isNT; + while (!(/^\s*$/)) { + if (/^\s*\".*?\"/) { + s/^\s*\"(.*?)\"(.*)/$2/i ; + $arg = $1 ; + } + elsif (/^\s*[^\"\s]+/) { + s/^\s*([^\"\s]+)(.*)/$2/i ; + $arg = $1 ; + } + $prec = $_ ; + $_ = $arg ; + + s@$Unix_oldDir@$Unix_root@ig ; + s/$type-$oldname/$type-$newname/ig ; + @{$ref_plugin->args}[$ArgNb++] = $_ ; + $_ = $prec ; + } + if (/^\s*$/) { + return AddPlugin($ref_plugin); + } + else { + return 0 ; + } + } + return 0 ; +} + +############################################################################# +# parse an index definition and call the addindex + +sub ParseIndex { + my $index = shift ; + my $NumLine = shift ; + my $ref_index = S_index->new() ; + my $Value ; + my $state = 0 ; + my $ErrorMsg = "Syntax error of an index definition.\nline parsed:"; + printTrace("\nParseIndex: $_",4) ; + s/,/, /g ; + s/\s+,/,/g ; + s/^index\s+//i ; # substitute the token index + while (!(/^\s*$/)) { + s/^\s*(\S+)(.*)$/$2/ ; + $Value = $1 ; + printTrace("\nValue: $Value",4); + printTrace("\nState: $state",4) ; + SWITCH: { + if ($state == 0) { + if ($Value =~ /[^\.]/) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->names}, $1 ; + } + else { + $state = 1 ; + push @{$ref_index->names}, $Value ; + } + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 1) { + if ($Value =~ /^none$/i) { + $state = 6 ; # end of the index definition + } + elsif ($Value =~ /^\"\"$/) { + $state = 4 ; # we expect to have at least one OID + } + elsif ($Value =~ /(\S+),$/) { + $state = 2 ; + push @{$ref_index->types}, $1 ; + } + else { + $state = 3 ; + push @{$ref_index->types}, $Value ; + } + last SWITCH ; + } + if ($state == 2) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->types}, $1 ; + } + else { + $state = 3 ; + push @{$ref_index->types}, $Value ; + } + last SWITCH ; + } + if ($state == 3) { + if ($Value =~ /(\S+),$/) { + $state = 4 ; + push @{$ref_index->oids}, $1 ; + } + else { + $state = 5 ; + push @{$ref_index->oids}, $Value ; + } + last SWITCH ; + } + if ($state == 4) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->oids}, $1 ; + } + else { + $state = 5 ; + push @{$ref_index->oids}, $Value ; + } + last SWITCH ; + } + } + } +return AddIndex($ref_index,$state) ; + +} + +############################################################################# + +sub ParseAttribute { + + + my $Attr = shift ; + my $NumLine = shift ; + my $state = 1 ; + my $ErrorMsg = "Syntax error of an attribute definition.\nline parsed:"; + my %Attribute = ( + 'AttributeName' => "", + 'Attribute_oid' => "", + 'Attribute_aliases' => "", + 'Attribute_syntax' => "", + 'Attribute_single' => "" + ); + my $AttributeName = " "; + printTrace("\nParseAttribute",4); + while (!(/^\s*$/)) { + s/^(.*?)(\S+)\s*$/$1/ ; + printTrace("\nValue: $2",4); + printTrace("\nState: $state",4) ; + my $Value = $2 ; + SWITCH: { + if ($state == 1) { + if (isAllowedModifier($Value)) { + $state = 1 ; + $modifier = lc($Value); + $AttrVar = 'Attribute_' . $modifier ; + $Attribute{$AttrVar} = &getModifierValue($Value) ; + } + elsif (&isAllowedPlugin($Value)) { + $state = 2 ; + $Attribute{'Attribute_syntax'} = &getSyntaxOid($Value) ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 2) { + if ($Value =~ /[\.]|-oid$/) { + $Attribute{'Attribute_oid'} = "$Value" ; + printTrace("\nAttribute-oid: $Attribute{'Attribute_oid'}",3); + $state = 3 ; + } + elsif ($Value =~ /[^\.]/) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + $state = 4 ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 3) { + if ($Value =~ /[^\.]/) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + $state = 4 ; } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 4) { + if ($Value =~/^attribute$/i){ + $state = 5; + } + elsif ($Value =~/[^\.]/i) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 5) { + return 0 ; + last SWITCH ; + } + } + } + $Attribute{'Attribute_oid'} = $Attribute{'AttributeName'} . '-oid' unless ($Attribute{'Attribute_oid'}) ; + return AddAttribute(\%Attribute) ; +} + + +############################################################################# +# fill in the hash HashParametersName + +sub FillHashParametersName { + my @paramnames = ( keys(%GeneralSrvParamToMigrate), keys(%GlobalConfigLDBMparamToMigrate), keys(%LDBMparamToMigrate)); + foreach $param (@paramnames) { + $HashParametersName{$param} = '\n'; + } +} + + +# Parse parameters +sub ParseParameters { + my $param = shift ; + my $value = shift ; + my $NumLine = shift ; + my $ErrorMsg = "parameter unknown, or not to be migrated: "; + if ($HashParametersName{lc($param)} && ($value !~ /^\s*$/)) { + $HashParametersName{lc($param)} = $value ; + printTrace("\nParam: $param is present",4); + } + else { + printTrace("\n$NumLine, $ErrorMsg,$param",4); + } + +} + +# add general server parameters +sub AddGeneralParameters { + my @paramnames = keys(%GeneralSrvParamToMigrate); + my $entry = $conn->search("cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=config. \nMigration stopped\n" unless ($entry); + printTrace("\nAddGeneralParameters",4); + foreach $param (@paramnames) { + my $LDAPparam = $GeneralSrvParamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam ",0); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } +} + + +# add general LDBM parameters +sub AddGeneralLDBMParameters { + my @paramnames = keys(%GlobalConfigLDBMparamToMigrate); + my $entry = $conn->search("cn=config,cn=ldbm database,cn=plugins,cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=config,cn=ldbm database,cn=plugins,cn=config. \nMigration stopped\n" unless ($entry); + printTrace("\nAddGeneralLDBMParameters",4); + foreach $param (@paramnames) { + my $LDAPparam = $GlobalConfigLDBMparamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam ",0); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } +} + +# add specific LDBM parameters +sub AddSpecificLDBMParameters { + my @paramnames = keys(%LDBMparamToMigrate); + my %REV_DBNAMES = reverse %DBNAMES ; + my @dbnames = keys(%REV_DBNAMES); + printTrace("\nAddSpecificLDBMParameters",4); + foreach $dbname (@dbnames) { + my $entry = $conn->search("cn=$dbname,cn=ldbm database,cn=plugins,cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=$dbname,cn=ldbm database,cn=plugins,cn=config. \nMigration stopped\n" unless ($entry); + foreach $param (@paramnames) { + my $LDAPparam = $LDBMparamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam",2); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } + } +} + +############################################################################# +# Parse a configuration file potentialy tuned by the user (different from slapd.user_oc.conf and slapd.user_at.conf) + +sub ParseConfigurationFile { + + my $FileToParse = shift; + my $NumLine = 0; + my $PARSE_OBJECTCLASSES = 0 ; # 1 if there are objectclass definitions in the file + printTrace("\nParseConfigurationFile: $FileToParse",4) ; + printTrace("\nParse $FileToParse",2); + # read each line of the configuration file + my $CONFIGFILE = "CONFIGFILE.$FileToParse" ; + open( $CONFIGFILE, $FileToParse ) || die "Can't open $FileToParsec: $!: "; + LINE: while ( <$CONFIGFILE> ) { + $NumLine++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } elsif (/^suffix\s+/i) { + chomp($_) ; + CheckSuffix($_) ; + } elsif (/^plugin/i) { + chomp($_); + if (! &isAStandardPlugin($_)) { + push @badPlugins, $_; + } + else { + my $Plugin = $_ ; + if (! &ParsePlugin($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of the plugin:\n$Plugin"); + } + } + } elsif (/^index/i) { + chomp($_); + if (! &isAStandardIndex($_)) { + my $Index = $_ ; + if (! &ParseIndex($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of index:\n$Index"); + } + } + } elsif (/^include\s+[\"]?(.*?)[\"]?\s*$/i) { + # strip leading and trailing " + my $include_file = $1 ; + grep { s@/@\\@g } $include_file if $isNT; + if (! &isAStandardInclude($include_file)) { + &ParseConfigurationFile($include_file); + } + } elsif (/^attribute\s+\S+/i) { + chomp($_); + my $Attrib = $_ ; + if (! &ParseAttribute($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of attribute:\n$Attrib"); + } + } elsif (/^objectclass\s+(\S+)\s*$/i) { + # At least one objectclass is present in the file + $PARSE_OBJECTCLASSES = 1; + } elsif (/^\s*(\S+)\s+[\"]?(.*?)[\"]?\s*$/) { + # Parse parameters and record the associated value in %Oldhash + &ParseParameters($1,$2,$NumLine); + } + } + close($CONFIGFILE); + ParseObjectClassesFile($FileToParse) if ($PARSE_OBJECTCLASSES); # parse objectclass definition + +} + +############################################################################# +# Parse the file specified in the userat attribute + +sub ParseAttributesFile { + my $userat_file=shift ; + my $NumLine = 0; + printTrace("\nParseAttributesFile: $userat_file",4); + printTrace("\nParse user defined attributes file: $userat_file",2); + # read each line of the configuration file + open( ATTRFILE, $userat_file ) || die "Can't open $FileToParsec: $!: "; + LINE: while ( <ATTRFILE> ) { + $NumLine++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } elsif (/^attribute\s+\S+/i) { + chomp($_); + my $Attrib = $_ ; + if (! &ParseAttribute($_, $NumLine)) { + printMsg("\nLine $NumLine, syntax error of attribute:\n$Attrib"); + } + } + } + close(ATTRFILE); +} + +############################################################################# +# Parse the file specified in the useroc token + +sub ParseObjectClassesFile { + my $useroc_file = shift ; + my %ObjectClass = ( + 'ObjectName' => " ", + 'Object_oid' => " ", + 'Object_superior' => "top", + 'Object_requires' => " ", + 'Object_allows' => " " + ); + + my $state = 0; + my $ErrorMsg = "Syntax error of an object class definition.\nline parsed:"; + my $LineNb = 0 ; # Number of the current line parsed in the file + printTrace("ParseObjectClassesFile: $useroc_file\n",4) ; + # read each line of the configuration file + open( OBJCLASSFILE, $useroc_file ) || die "Can't open $FileToParsec: $!: "; + printTrace("Begin the parsing of the file: $useroc_file",4); + LINE: while ( <OBJCLASSFILE> ) { + printTrace("Current Line: $_",4); + $LineNb++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } + SWITCH: { + if ($state == 0) { resetObjectClass(\%ObjectClass); + if (/^objectclass\s+(\S+)\s*$/i) { + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + else {} # printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 1) {if (/^\s+oid\s+(\S+)\s*$/i) { + $ObjectClass{'Object_oid'} = $1; + $state = 2 ;} + elsif (/^\s+superior\s+(\S+)\s*$/i) { + $ObjectClass{'Object_superior'} = $1; + $state = 3 ; + } + elsif (/^\s+requires\s*$/i) { + $state = 4; + } + elsif (/^\s+allows\s*$/i) { + $state = 5; + } + else {$state=0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 2) {if (/^\s+superior\s+(\S+)\s*$/i) { + $ObjectClass{'Object_superior'} = $1; + $state = 3 ;} + elsif (/^\s+requires\s*$/i) { + $state = 4; + } + elsif (/^\s+allows\s*$/i) { + $state = 5; + } + else { $state=0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 3) {if (/^\s+requires\s*$/i) + { $state = 4; } + elsif (/^objectclass\s+(\S+)\s*$/i) { + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + elsif (/^\s+allows\s*$/i) + { $state = 5; } + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 4) {if (/^\s+([^,\s]+),\s*$/i) { + $ObjectClass{'Object_requires'}.=$1." \$ "; } + elsif (/^\s+([^,\s]+)\s*$/i) { + $ObjectClass{'Object_requires'}.=$1." "; + $state = 6; } + else {$state = 0;printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 5) {if (/^\s+([^,\s]+),\s*$/i) { + $ObjectClass{'Object_allows'}.=$1." \$ "; } + elsif (/^\s+([^,\s]+)\s*$/i) { + $ObjectClass{'Object_allows'}.=$1." "; + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $state = 0; } + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 6) {if (/^objectclass\s+(\S+)\s*$/i) { + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + elsif (/^\s+allows\s*$/i) { + $state = 5;} + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + } + } + close(OBJCLASSFILE); + if (($state == 3) || ($state == 4) || ($state == 5) || ($state == 6)) { + &AddObjectClass(\%ObjectClass); + } + printTrace("state: $state",4); +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg ; + } +} + +############################################################################# +# reset an objectclass structure + +sub resetObjectClass { + my $ObjectClass = shift; + $ObjectClass->{'ObjectName'} = " " ; + $ObjectClass->{'Object_oid'} = " " ; + $ObjectClass->{'Object_superior'} = "top" ; + $ObjectClass->{'Object_requires'} = " " ; + $ObjectClass->{'Object_allows'} = " " ; +} + +############################################################################# +# this subroutine implements a very stupid version of diff + +sub diff { + my $f1 = shift; + my $f2 = shift; + my $lineToBeginWith = shift; + my $NULL = "" ; + my $diff_f1 = $NULL ; + my $diff_f2 = $NULL ; + my $retval = $NULL ; + my $ret; + open(F1, "$f1") or die "Could not open file $f1"; + open(F2, "$f2") or close(F1), die "Could not open file $f2"; + + while (defined($l1 = <F1>)) { + if ($lineToBeginWith){ + $lineToBeginWith -- ; + next ; + } + next if ($l1 =~ /^\#/); + $ret = defined($l2 = <F2>); + if ($ret) { + $ret = defined($l2 = <F2>) while ($ret && ($l2 =~ /^\#/)) ; + if ($ret) { + if (!($l1 eq $l2)) { + + # ignore whitespace + $l1_clean = $l1 ; + $l2_clean = $l2 ; + $l1_clean =~ s/\s//g; + $l2_clean =~ s/\s//g; + + if (!($l1_clean eq $l2_clean)) { + $diff_f1 .= "${l1}" unless ($l1_clean eq $NULL); + $diff_f2 .= "${l2}" unless ($l2_clean eq $NULL); + } + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + + while (defined($l2 = <F2>)) { + if (($l2 =~ /^\#/) || ($l2 =~ /^\s*$/)) { + next ; + } + else { + $diff_f2 .= "${l2}" ; + } + } + + close(F1); + close(F2); + + $retval .= "- differences present in your config file but not in standard file:\n\n". "$diff_f1\n" if ($diff_f1) ; + $retval .= "- differences present in standard file but not in your config file:\n\n" . "$diff_f2" if ($diff_f2) ; + return $retval ; +} + +sub CompareStdConfigFiles { + # Compare each configuration file against its default version. If it has changed, + # notify the user that the file has changed and will need to be checked by the + # user. This should be safe to do because there should be no path information + # stored in these conf files, which are just schema stuff. + # printTrace("\nCheck if standard configuration files have changed",3); + + # get the version of the DS to migrate + ($oldVersion, $oldMinor) = &getVersion($oldDir); + # get the version of the new DS + ($Version, $Minor) = &getVersion($root); + + my $origFilePath = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}install${PATHSEP}config${PATHSEP}" ; + my $FilesChanged = ""; + my $AllDiffs = "***********************************************************************"; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + printTrace("\nVersion of the old directory server: $oldVersion.$oldMinor",0); + opendir(CONFDIR, $oldConfDir) or + die "Error: could not open migrated config dir $oldConfDir: $!"; + + foreach $file (readdir(CONFDIR)) { + $origFile = $origFilePath . $file ; + $configFile = $oldConfDir . $file ; + if ((! exists($userDefinedConfigFiles{lc($file)})) && (-f $origFile)) { + my $lineToBegin = 1 if (lc($file) eq "slapd-collations.conf"); # we ignore the first line of slapd-collations + $diffs = &diff($configFile, $origFile, $lineToBegin); + $lineToBegin = 0 if $lineToBegin ; + if ($diffs) { + $FilesChanged .= "\n$configFile"; + $AllDiffs .= "\n$configFile is different than the standard configuration file" ; + $AllDiffs .= "\nYou will need to check this file and make sure its changes are compatible "; + $AllDiffs .= "with the new directory server\nHere are the differences:\n"; + $AllDiffs .= "$diffs \n\n"; + $AllDiffs .= "***********************************************************************"; + } + else { + $NoChanges .= "\n$configFile"; + } + } + } + closedir(CONFDIR); + +if ($FilesChanged) { + printTrace("\nNo changes to old configuration files:$NoChanges",3) ; + printTrace("\n***********************************************************************",3) ; + printMsg("\nThe following standard files have been modified: $FilesChanged"); + if ($NO_INPUT_USER) { + # do nothing + } + else { + printMsg("\nDo you want to see the differences Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("$AllDiffs"); + } + printMsg("\nDo you want to continue the migration Yes/No [No] ?"); + $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + } + } +} + + +############################################################################# + +sub db2ldif { + my ($conf, $ldif_dir) = @_; + $ENV{"$LIB_PATH"}="$oldDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + if (!$conf) { + $conf = "$oldHome${PATHSEP}config${PATHSEP}slapd.conf"; + } + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "can't create $ldif_rep to store temporary ldif files"; + } + my $dir = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + chdir($dir) or + die "Error: could not change directory to $dir: $!"; + my @suffixnames = keys(%DBNAMES) ; + foreach $suffixname (@suffixnames) { + my $ldif_file = $ldif_dir.$DBNAMES{$suffixname}.".ldif" ; + # If we are on NT, ${quote} is setup to "\"", else it's setup to "" + # As the suffix can contain some space characters, I write the suffix parameter: "\"$suffixname\"" rather than "${quote}$suffixname${quote}" + my @cmd = + ( "${quote}$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server" . + "${PATHSEP}$slapdExecName${quote}", "db2ldif", '-n', '-f', + "${quote}$conf${quote}", '-a', "${quote}$ldif_file${quote}", + '-d', '1','-s',"\"$suffixname\"" ); + open(DB2LDIF, "${quote}@cmd${quote} 2>&1|") or + die "Error: could not execute @cmd: $!"; + sleep(1); # allow pipe to fill with data + $ii = 0; # counter + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the 5.x slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif_file) { + chown( $newuid, $newgid, $ldif_file) or printMsg("\nUnable to change the ownership of $ldif_file to $localuser") ; + } + } + } + print " Done.\n"; + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + +############################################################################# + +# this is used to run the system() call, capture exit and signal codes, +# and die() upon badness; the first argument is a directory to change +# dir to, if any, and the rest are passed to system() +sub mySystem { + my $rc = &mySystemNoDie(@_); + my ($dir, @args) = @_; + if ($rc == 0) { +# success + } elsif ($rc == 0xff00) { + die "Error executing @args: error code $rc: $!"; + } elsif ($rc > 0x80) { + $rc >>= 8; + die "Error executing @args: error code $rc: $!"; + } else { + if ($rc & 0x80) { + $rc &= ~0x80; + } + die "Error executing @args: received signal $rc: $!"; + } + + # usually won't get return value + return $rc; +} + +# This version does not die but just returns the error code +sub mySystemNoDie { + my ($dir, @args) = @_; + if ($dir && ($dir ne "")) { + chdir($dir) or die "Could not change directory to $dir: $!"; + } + my $cmd = $args[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = 0xffff & system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd @fixargs\n"; + $rc = 0xffff & system {$cmd} @fixargs; + } + chdir(${curdir}) or die "Could not change directory to $curdir: $!"; + return $rc; +} + +############################################################################# +sub manyLdif2db { + my %rev_dbnames = reverse(%DBNAMES); + @backends = keys(%rev_dbnames); + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + chdir($slapdExecDir) or die "Could not change directory to $slapdExecDir: $!"; + foreach $backend (@backends) { + my $ldif = "${ldif_rep}$backend.ldif" ; + &Ldif2db($ldif, $backend); + } + # remove the empty ldif directory + rmdir($ldif_rep); + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + + +sub Ldif2db { + my $ldif = shift ; + my $backend = shift ; + my $ldif2db_param = "ldif2db -D $serverHome -n $backend -i $ldif"; + open(LDIF2DB, "${quote}${quote}$slapdExecName${quote} $ldif2db_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + while (<LDIF2DB>) { + printMsg($_); + } + close(LDIF2DB); + # remove the ldif file after the import + unlink($ldif) ; +} + +############################################################################# + +#sub copyBak { +# opendir( OLDBAK, "$oldHome${PATHSEP}bak" ) || +# die "Can't open directory $oldHome${PATHSEP}bak: $!: "; +# local ( @dirs ) = readdir( OLDBAK ); +# closedir ( OLDBAK ); +# for ( @dirs ) { +# if ( $_ eq "." || $_ eq ".." ) { +# next; +# } elsif ( -d "$oldHome${PATHSEP}bak${PATHSEP}$_" ) { +# $srcDir = "$oldHome${PATHSEP}bak${PATHSEP}$_"; +# $destDir = "$serverHome${PATHSEP}bak${PATHSEP}$_"; +# $srcLDIF = "$oldHome${PATHSEP}ldif${PATHSEP}bak.ldif"; +# $destLDIF = "$serverHome${PATHSEP}ldif${PATHSEP}bak.ldif"; +# mkdir( $destDir , 0755 ) if !( -e $destDir); +# # Converting database +# if ( !$isNT && $newuser ) { +# chown($newuid, $newgid, +# "$serverHome${PATHSEP}bak", $destDir); +# } +# &other_db2ldif($srcDir, $srcLDIF); +# if ($needAclUpg) { +# &mySystem("$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server", +# "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server" . +# "${PATHSEP}aclupg$exe_suffix", '-d', '-i', +# $srcLDIF, '-o', $destLDIF); +# } else { +# ©BinFile($srcLDIF, $destLDIF); +# } +# &other_ldif2db($destLDIF, $destDir); +# } +# } +#} +############################################################################# + +sub startServer { + my $instanceDir = ${serverHome} ; + my $errLog = $instanceDir . $PATHSEP . 'logs' . $PATHSEP . 'errors'; + # emulate tail -f + # if the last line we see does not contain "slapd started", try again + my $done = 0; + my $started = 0; + my $code = 0; + my $lastLine = ""; + my $timeout = time + 240; # 4 minutes + $ENV{"$LIB_PATH"}="${root}${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + + my $startCmd = $instanceDir . $PATHSEP . 'start' . $script_suffix; + if (! -f $startCmd) { + $startCmd = $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix; + } + printTrace("\nInstanceDir: $instanceDir\n",4); + $code = &mySystem($instanceDir,$startCmd); + open(IN, $errLog) or die "Could not open error log $errLog: $!"; + my $pos = tell(IN); + while (($done == 0) && (time < $timeout)) { + for (; ($done == 0) && ($_ = <IN>); $pos = tell(IN)) { + $lastLine = $_; + # print; + # the server has already been started and shutdown once . . . + if (/slapd started\./) { + $started++; + if ($started == 2) { + $done = 1; + } + # sometimes the server will fail to come up; in that case, restart it + } elsif (/Initialization Failed/) { + # print "Server failed to start: $_"; + $code = &mySystem($instanceDir, $startCmd); + # sometimes the server will fail to come up; in that case, restart it + } elsif (/exiting\./) { + # print "Server failed to start: $_"; + #$code = &mySystem($startCmd); + + $code = &mySystem($instanceDir, $startCmd); + } + } + if ($lastLine =~ /PR_Bind/) { + # server port conflicts with another one, just report and punt + print $lastLine; + print "This server cannot be started until the other server on this\n"; + print "port is shutdown.\n"; + $done = 1; + } + if ($done == 0) { + # rest a bit, then . . . + sleep(2); + # . . . reset the EOF status of the file desc + seek(IN, $pos, 0); + } + } + close(IN); + + if ($started < 2) { + $! = $code; + # $now = time; + # if ($now > $timeout) { + # print "Possible timeout: timeout=$timeout now=$now\n"; + # } + die "Error: could not start server: $!"; + } + + return 0; +} + +sub stopServer { + my $root = shift; + my $name = shift; + $maxStopIterations = 5; + print "\nShutting down server $name . . .\n"; + + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote; + if (! -f $stopCmd) { + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote; + } + + if (! -f $stopCmd) { + # no stop command, probably a 1.X system; for NT, we'll try net stop + # for unix, we'll get the pid and kill it + if ($isNT) { + $stopCmd = 'net stop ' . $name; + } else { + # see if there is a pid file + $pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' . + $PATHSEP . 'pid'; + if (open(PIDFILE, $pidfile)) { + chomp($pid = <PIDFILE>); + close(PIDFILE); + while ($maxStopIterations-- && !$exitCode) { + $exitCode = kill(15, $pid); + } + $stopCmd = undef; + } + } + } + + # keep looping until the stop cmd returns an error code, which usually + # means that what ever we want to stop is stopped, or some other error + # occurred e.g. permission, or no such service + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + while ($stopCmd && $maxStopIterations-- && $exitCode) { + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + } + + if (!$maxStopIterations) { + print "Warning: could not shutdown the server: $!\n"; + } + + sleep(10) ; + + $exitCode = 0; + +} + + +sub runAndIgnoreOutput { + my $cmd = shift; + printMsg("."); + open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!"; + printMsg("."); + sleep(1); # allow pipe to fill with data + printMsg("."); + while (<RUNCMD>) { +# print; + } + my $code = close(RUNCMD); +# print "runAndIgnore: code=$code status=$?\n"; + return $?; +} +############################################################################# +# migrate some of entries present in the old DSE.ldif like +# cn=snmp,cn=config +# cn=encryption,cn=config +# all the aci's + +sub MigrateDSE { + printTrace("\nMigrate DSE entries...",1); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($old_entry = readOneEntry $in) { + my $DN = $old_entry->getDN() ; + SWITCH: { + # migrate the entrie: cn=snmp,cn=config + if ($DN =~ /^cn=SNMP,cn=config$/i) { + my $entry = $conn->search("$DN","base","objectclass=nsSNMP"); + if ($entry) { + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + printMsg("\nUnable to get info under $DN"); + } + last SWITCH; + } + # migrate the entrie: cn=encryption,cn=config + if ($DN =~ /cn=encryption,cn=config$/i) { + if ($conn->search("$DN","base","objectclass=*")) { + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + my $res = $conn->add($old_entry); + if ($res) { + printTrace("\n$DN added !",2); + } + else { + printMsg("\nFailed to add $DN"); + } + } + last SWITCH; + } + if (@{$old_entry->{aci}} && (! ($DN =~ /^cn=monitor$/i)) && (! ($DN =~ /^cn=schema$/i))) { + # migrate aci's + my $entry = $conn->search("$DN","base","objectclass=*"); + if ($entry) { + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + my $res = $conn->add($old_entry); + if ($res) { + printTrace("\n$DN added !",2); + } + else { + printMsg("\nFailed to add $DN"); + } + } + last SWITCH; + } + } + } + close(DSELDIF); +} +############################################################################# +# migrate SSL info + +sub MigrateSSL { + my $secPwd = 'bidon' ; + # copy the SSL directory + ©Dir("$oldHome${PATHSEP}ssl","$serverHome${PATHSEP}ssl"); + # copy the cert db and key files + if ( -d "$oldDir${PATHSEP}alias") { + $aliasDir = "$root${PATHSEP}alias"; + if (! -d $aliasDir) { + mkdir($aliasDir, 0750); + } + my $keydb = "$aliasDir${PATHSEP}slapd-$newname-key3.db" ; + my $certdb = "$aliasDir${PATHSEP}slapd-$newname-cert7.db" ; + my $old_keydb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ; + my $old_certdb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db"; + my $keydb_backup = "$aliasDir${PATHSEP}slapd-$newname-key3.db_backup" ; + my $certdb_backup = "$aliasDir${PATHSEP}slapd-$newname-cert7.db_backup" ; + if (-f $old_keydb) { + if (-f $keydb) { + if ($NO_INPUT_USER) { + printMsg("\n$keydb already exists. backup in $keydb_backup ..."); + ©BinFile($keydb,$keydb_backup); + ©BinFile($old_keydb,$keydb); + } + else { + print("\n\n$keydb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_keydb,$keydb); + } + } + } + else { + ©BinFile($old_keydb,$keydb); + } + } + if (-f $old_certdb) { + if (-f $certdb) { + if ($NO_INPUT_USER) { + printMsg("\n$certdb already exists. backup in $certdb_backup ..."); + ©BinFile($certdb,$certdb_backup); + ©BinFile($old_certdb,$certdb); + } + else { + print("\n\n$certdb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_certdb,$certdb); + } + } + } + else { + ©BinFile($old_certdb,$certdb); + } + } + # copy the old password file + if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") { + ©BinFile( + "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt", + "$aliasDir${PATHSEP}$type-$newname-pin.txt" + ); + } + } + +} + +sub DisableSSL { + my $entry = $conn->search("cn=config","base","objectclass=*"); + my $LDAPparam = "nsslapd-security" ; + my $Value = "off" ; + if ($entry->{$LDAPparam}[0] ne $Value) { + printTrace("\nDisable SSL...",1); + $entry->setValues($LDAPparam, $Value); + } + my $res = $conn->update($entry); + if ($res) { + printTrace("\nSSL disabled",2); + } + else { + printMsg("\nCan't disabled SSL. The server may have problems to start"); + } +} + +# enable the migration of client authentication informations +sub MigrateCertmap { + # backup the old 5.x certmap.conf and replace it with the 4.x certmap.conf file + my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"; + my $newCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf" ; + my $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + if (&hasChangedoldCertmap($oldCertmap)) { + if ($NO_INPUT_USER) { + printMsg("\n$newCertmap has been backup in $backupCertmap"); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up the file $newCertmap [$backupCertmap] ?") ; + my $Answer = <STDIN> ; + $backupCertmap = $Answer if ($Answer ne "\n"); + chomp($backupCertmap); + printTrace("\nDest: .$backupCertmap.",4); + if (-e $backupCertmap) { + printMsg("\n\n$backupCertmap already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup file: $newCertmap in $backupCertmap",4); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + } + else { + } +} + +sub hasChangedoldCertmap { + my $certmapfile = shift ; + my @reference = ("certmap default default", + "default:DNComps", + "default:FilterComps e") ; + my $cpt = 0 ; + printTrace("\nhasChangedoldCertmap",3); + open(CERTMAP,"< $certmapfile"); + while (<CERTMAP>) { + if ((! /^\s*#/) && (! /^\s*$/)) { + my $ref = $reference[$cpt] ; + printTrace("\nValue: $_, ref: $ref",4); + if (! /^\s*$ref\s*$/) { + return 1 ; + } + else { + $cpt++ ; + } + } + } + close (CERTMAP); + printTrace("\ncpt: $cpt",4); + if ($cpt < $#reference) { + return 1 ; + } + else { + return 0 ; + } +} +############################################################################# +# copy a directory to another + +sub copyDir { + my $src = shift; + my $dest = shift; + my $exclude = shift; + + opendir( SRC, $src ) or die "Can't open directory $src: $!: "; + my $mode; + my $uid; + my $gid; + mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest ); + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + local ( @files ) = readdir ( SRC ); + closedir( SRC ); + for ( @files ) { + if ( $_ eq "." || $_ eq ".." ) { + next; + } elsif ( $exclude && /$exclude/ ) { + next; + } elsif( -d "$src${PATHSEP}$_") { + ©Dir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); + } else { + ©BinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_"); + } + } +} + +sub copyBinFile { + my $src = shift; + my $dest = shift; + my $buf = ""; + my $bufsize = 8192; + + open( SRC, $src ) || die "Can't open $src: $!\n"; + # if we are given a directory destination instead of a file, extract the + # filename portion of the source to use as the destination filename + if (-d $dest) { + $dest = $dest . $PATHSEP . &basename($src); + } + open( DEST, ">$dest" ) || die "Can't create $dest: $!\n"; + binmode SRC; + binmode DEST; + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + while (read(SRC, $buf, $bufsize)) { + print DEST $buf; + } + close( SRC ); + close( DEST ); +} +############################################################################# +# backup 5.x configuration files +# backup the directory <root_server5>/slapd-instance/config dans <root_server5>/slapd-instance/BackupConfig + +sub backupConfigFiles { + # backup the 5.x config files + my $src = "$serverHome${PATHSEP}config" ; + my $dest = "$serverHome${PATHSEP}config_backup" ; + if ($NO_INPUT_USER) { + printMsg("\n$src has been backup in $dest"); + ©Dir($src,$dest); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up your configuration directory [$dest] ?") ; + my $Answer = <STDIN> ; + $dest = $Answer if ($Answer ne "\n"); + chomp($dest); + printTrace("\nDest: .$dest.",4); + if (-e $dest) { + printMsg("\n\n$dest already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $dest = "$serverHome${PATHSEP}config_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup Directory: $src in $dest",4); + ©Dir($src,$dest); + } +} +############################################################################# + +sub getLDAPservername { + my $oldLDAPservername; + my $LDAPservername; + open(OLDSLAPDCONF, $oldSlapdConf) or + die "\nError: could not open old config file $oldSlapdConf \n"; + while(<OLDSLAPDCONF>) { + chop; + if (/^localhost\s+/i) { + ($oldLDAPservername = $') =~ s/^[\"]//;; + $oldLDAPservername =~ s/[\"]$//; + printTrace("\nName of the old LDAP server: $oldLDAPservername",3); + } + } + close(OLDSLAPDCONF); + + open( DSELDIF, "< $DSEldif" ) || die "\nCan't open $DSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN() ; + if ($DN =~ /^cn=config$/i) { + my $localhost = "nsslapd-localhost"; + my @values = $entry->getValues($localhost); + if ($#values != -1) { + $LDAPservername = $values[0]; + } + break; + } + } + close(DSELDIF); + # check 4.x and 5.x are installed on the same physical machine. + if (lc($oldLDAPservername) ne lc($LDAPservername)) { + # warn the user he tries to migrate a 4.x server installed on a different machine from the 5.x one + printMsg("\n\nYour 4.x server is on $oldLDAPservername, and your 5.x server is on $LDAPservername. We don't support migration on different machines. Do you want to continue ? Yes/No [No]:") ; + if (! (<STDIN> =~ /yes|y/i)) { + return -1; + } + } + return $LDAPservername ; +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $ENV{"$LIB_PATH"}="$dir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + print; + if (/^Netscape-Directory\/(\d+)\.(\d+)\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + elsif (/^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + elsif (/^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + last; + } + } + $code = close(F); +# print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}="$root${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + + if ($version == 0) { + printMsg("\nCould not determine version of the directory server in $dir: "); + printMsg("\nBe careful, this script is designed to migrate only from 4.x Netscape Directory Server to 5.x. Do you want to continue ? [no]: "); + my $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + $version = 4 ; # setup to 4 and pray ... + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + return ( $version, $minor ); +} + +############################################################################# + +sub getDiff { + # we get references to arrays + my $elements = shift ; + my $existing_elements = shift ; + my %count = () ; + my %countEE = () ; + @diff = () ; + foreach $e (@{$elements}, @{$existing_elements}) { $count{$e}++ ;} + foreach $e (@{existing_elements}) { $countEE{$e}++ ;} + foreach $e (@{$elements}) { + # if $e is only present in @$elements, we push it to the diff array + if (($count{$e} == 1) && ($countEE{$e} == 0)) { + push @diff, $e ; + } + } + return @diff ; +} + +############################################################################################### +sub testIndexUpdating { + #my $entry = $conn->newEntry(); + #$entry->setDN("cn=djeattribute,cn=index,cn=MigratedDB_5,cn=ldbm database,cn=plugins,cn=config"); + my $entry = $conn->search("cn=mail,cn=index,cn=MigratedDB_2,cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + my @types = ("pres", "sub", "eq") ; + my @existing_types = $entry->getValues("nsindextype"); + my @typesToAdd = &getDiff(\@types, \@existing_types); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + my $res = $conn->update($entry) ; + if ($res) {print("\nUpdate index mail\n");} + else { print("\ncan't update index mail");} + + $entry = $conn->search("cn=givenName,cn=index,cn=MigratedDB_2,cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + @types = ("pres", "sub", "eq") ; + @existing_types = $entry->getValues("nsindextype"); print("\ngivenName, existing_types: @existing_types"); + @typesToAdd = &getDiff(\@types, \@existing_types); print("\nTypesToAdd: @typesToAdd"); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + my $res = $conn->update($entry) ; + if ($res) {print("\nUpdate index givenName\n");} + else { print("\ncan't update index givenName");} + } + + +############################################################################################### +sub normalizeDir { + my $dir = shift ; + my $dir_prec = "" ; + while ($dir_prec ne $dir) { + $dir_prec = $dir ; + if ($isNT) { + grep { s@\\\\@\\@g } $dir ; + } + else { + grep { s@//@/@g } $dir ; + } + } + return $dir ; +} + + +############################################################################################### +# return 1 if the value parameters is +sub isAllowedPlugin { + my $Value = lc(shift) ; + if ($allowedPlugins{$Value}) { + return 1 ; + } + else { + return 0 ; + } + +} + + +sub getSyntaxOid { + my $Value = lc(shift) ; + return $allowedPlugins{$Value} ; +} + +############################################################################################### +# return 1 if the value given in parameters is an allowed modifier +sub isAllowedModifier { + my $Value = lc(shift) ; + if ($allowedModifiers{$Value}) { + return 1 ; + } + else { + return 0 ; + } +} + +sub getModifierValue { + my $Value = lc(shift) ; + return $allowedModifiers{$Value} ; +} + +############################################################################################### + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################################### +# get uid and group id of the 5.x slapd server. +# The uid is done through the nsslapd-localuser attribute + +sub getuid_gid { + my $newuid ; + my $newgid ; + my $localuser ; + my $localuser_attr = "nsslapd-localuser" ; + if (! $isNT) { + my $entry = $conn->search("cn=config ", "base","objectclass=*", 0, ($localuser_attr)) ; + # Tests wether we succeed to get the entry cn=config + die "\nCan't get the entry cn=config \n" unless ($entry); + my @values = $entry->getValues($localuser_attr); + if ($#values == -1 || ($values[0] eq "") ) { # tests wether the nsslapd-localuser attribute has a value + printMsg("\nNo localuser has been found in the configuration of the directory. "); + if ($NO_INPUT_USER) { + printMsg("\nWe considered nobody as the localuser"); + $localuser = "nobody" ; + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\nUnder what user does your $Version.$Minor directory server run [nobody] ? ") ; + $localuser = <STDIN> ; + chomp($localuser); + $localuser = "nobody" if ($localuser eq ""); + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + if ($newuid) { + $Ask = 0 ; + } + else { + printMsg("\nError: $localuser is unknown from the system "); + } + } + } + } + else { + $localuser = $values[0]; # returns the first value (we should only have one localuser) + my $size = $#values ; + } + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + return ($localuser, $newuid, $newgid) ; + } + else { + return () ; + } +} + + +############################################################################################### +# get uid and group id of the 4.x slapd server. + +sub getolduid_gid { + my $oldlocaluser ; + if (! $isNT) { + open(CONF, $oldSlapdConf) or die "\nError: cannot open $oldSlapdConf: $!\n"; + while (<CONF>) { + if (/^localuser\s+/i) { + chomp($oldlocaluser = $'); + last; + } + } + close(CONF); + ($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ; + return ($oldlocaluser, $olduid, $oldgid) ; + } + else { + return (); + } +} + +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} diff --git a/ldap/admin/src/scripts/template-migrateTo6 b/ldap/admin/src/scripts/template-migrateTo6 new file mode 100644 index 00000000..91174b41 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrateTo6 @@ -0,0 +1,3268 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Migrate a old directory server to a 6.2 directory server + +######################################################################################################## +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Entry; +use Mozilla::LDAP::LDIF; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::localtime; + +######################################################################################################## +use Class::Struct ; # load struct-building module + +struct S_index => { + names => '@' , + types => '@' , + oids => '@' , + specific => '$' + }; + + +struct S_plugin => { + name => '$' , + type => '$' , + enable => '$' , + args => '@' + }; +##################################################################################################### + +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o OldInstancePath -n NewInstancePath [-t tracelevel] [-L logfile]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - New Directory Manager\n"); + print(STDERR " : -w password - New Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for New Directory Manager's password\n"); + print(STDERR " : -j filename - Read New Directory Manager's password from file\n"); + print(STDERR " : -p port - New Directory Server port\n"); + print(STDERR " : -o OldInstancePath - Path of the Old instance to migrate \n"); + print(STDERR " : -n NewInstancePath - Path of the new instance\n"); + print(STDERR " : [-d dataPath] - Path to directory containing data files to import into new instance\n"); + print(STDERR " : [-v oldVersion] - Old version (obtained by running $slapdExecName -v\n"); + print(STDERR " : [-t tracelevel] - specify the level of trace (0..3)\n"); + print(STDERR " : [-L logfile] - specify the file to log the migration report \n"); + + + } + + + +############# +BEGIN { + + require 'uname.lib' ; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + ${SEP} = $isNT ? ";" : ":" ; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + + # If this variable is set, all file/directory creation will make sure the mode + # and ownership of the destination is the same as the source + $PRESERVE = 1 if (!$isNT); + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + if ($isNT) { + $os = "WINNT"; + } else { + $os = &uname("-s"); + } + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + } + if ( $os eq "AIX" ) { + $dll_suffix = "_shr.a"; + } + elsif ( $os eq "HP-UX" ) { + $dll_suffix = ".sl"; + } + elsif ( $os eq "WINNT" ) { + $dll_suffix = ".dll"; + } + else { + $dll_suffix = ".so"; + } + $slapdExecName = $isNT ? 'slapd.exe' : './ns-slapd'; + # if this flag is set, we will migrate the old database + # by doing a db2ldif -> ldif2db; + $convertToLDIF = 1; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; + # if the old value for dbcachesize is less than this, make it this + $MIN_DBCACHESIZE = '500000'; +} + +SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + + # Old parameters + ${oldDir} = "" ; + ${oldname} = "" ; + ${oldHome} = "" ; + ${oldConfDir} = "" ; + ${oldlocaluser} ; + ${olduid} ; + ${oldgid} ; + + # New parameters + ${root} = "{{DS-ROOT}}" ; + ${type} = "" ; + ${newname} = "" ; + ${newport} = "" ; + ${rootDN} = "" ; + ${rootpwd} = "" ; + ${localhost} = "" ; + ${LogFileReport} = "" ; + ${newuid} ; + ${localuser} ; + ${newgid} ; + $NO_INPUT_USER = 0 ; # by default user can give inputs during the migration process + ${curdir} = getCwd(); + ${slapdExecDir} = "${root}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + # specify the level of trace + $TRACELEVEL=1; + + $LDAP_SERVER_UNREACHABLE = 81; + + # get input users + &getParameters() ; + ${oldDir} = &normalizeDir("${oldDir}"); + ${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; + ${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; + ${oldSlapdConf} = "${oldConfDir}slapd.conf" ; + ${oldDSEldif} = "${oldConfDir}dse.ldif" ; + ${serverHome} = "${root}${PATHSEP}$type-$newname" ; + ${DSEldif} = "$serverHome${PATHSEP}config${PATHSEP}dse.ldif"; + ${ldif_rep} = "${oldConfDir}${PATHSEP}ldif${PATHSEP}" ; + ${oldSlapdExecDir} = "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + + + + open(LOGFILE, ">> $LogFileReport"); + + printTrace("\noldDir: $oldDir, oldHome: $oldHome, \noldConfDir: $oldConfDir, \noldSlapdConf: $oldSlapdConf, \nldif_rep: $ldif_rep, \nrootDN: $rootDN, \nPwd: ******, \nPort: $newport, \nNewname: $newname\n",3); + printTrace("\nLIB_PATH: $LIB_PATH",4); + + if (!(-d $serverHome)) { + printMsg("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + printMsg("\n$oldHome doesn't exist\n"); + exit(1); + } + + if ($olddatadir && !(-d $olddatadir)) { + print("\n$olddatadir doesn't exist\n"); + exit(1); + } + +#define CONFIG_DATABASE_DIRECTIVE "database" +#define CONFIG_DATABASE_ATTRIBUTE "nsslapd-database" +#define CONFIG_PLUGIN_DIRECTIVE "plugin" +#define CONFIG_PLUGIN_ATTRIBUTE "nsslapd-plugin" +#define CONFIG_SIZELIMIT_DIRECTIVE "sizelimit" +#define CONFIG_SIZELIMIT_ATTRIBUTE "nsslapd-sizelimit" +#define CONFIG_ORCAUTO_DIRECTIVE "orcauto" +#define CONFIG_ORCAUTO_ATTRIBUTE "nsslapd-orcauto" +#define CONFIG_TIMELIMIT_DIRECTIVE "timelimit" +#define CONFIG_TIMELIMIT_ATTRIBUTE "nsslapd-timelimit" +#define CONFIG_SUFFIX_DIRECTIVE "suffix" +#define CONFIG_SUFFIX_ATTRIBUTE "nsslapd-suffix" +#define CONFIG_READONLY_DIRECTIVE "readonly" +#define CONFIG_READONLY_ATTRIBUTE "nsslapd-readonly" +#define CONFIG_REFERRAL_DIRECTIVE "referral" +#define CONFIG_REFERRAL_ATTRIBUTE "nsslapd-referral" +#define CONFIG_OBJECTCLASS_DIRECTIVE "objectclass" +#define CONFIG_OBJECTCLASS_ATTRIBUTE "nsslapd-objectclass" +#define CONFIG_ATTRIBUTE_DIRECTIVE "attribute" +#define CONFIG_ATTRIBUTE_ATTRIBUTE "nsslapd-attribute" +#define CONFIG_SCHEMACHECK_DIRECTIVE "schemacheck" +#define CONFIG_SCHEMACHECK_ATTRIBUTE "nsslapd-schemacheck" +#define CONFIG_LOGLEVEL_DIRECTIVE "loglevel" +#define CONFIG_LOGLEVEL_ATTRIBUTE "nsslapd-errorlog-level" +#define CONFIG_ACCESSLOGLEVEL_DIRECTIVE "accessloglevel" +#define CONFIG_ACCESSLOGLEVEL_ATTRIBUTE "nsslapd-accesslog-level" +#define CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "accesslog-maxNumOfLogsPerDir" +#define CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-accesslog-maxlogsperdir" +#define CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "errorlog-maxNumOfLogsPerDir" +#define CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-errorlog-maxlogsperdir" +#define CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "auditlog-maxNumOfLogsPerDir" +#define CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-auditlog-maxlogsperdir" +#define CONFIG_ACCESSLOG_MAXLOGSIZE_DIRECTIVE "accesslog-maxlogsize" +#define CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-accesslog-maxlogsize" +#define CONFIG_ERRORLOG_MAXLOGSIZE_DIRECTIVE "errorlog-maxlogsize" +#define CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-errorlog-maxlogsize" +#define CONFIG_AUDITLOG_MAXLOGSIZE_DIRECTIVE "auditlog-maxlogsize" +#define CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-auditlog-maxlogsize" +#define CONFIG_ACCESSLOG_LOGROTATIONTIME_DIRECTIVE "accesslog-logrotationtime" +#define CONFIG_ACCESSLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-accesslog-logrotationtime" +#define CONFIG_ERRORLOG_LOGROTATIONTIME_DIRECTIVE "errorlog-logrotationtime" +#define CONFIG_ERRORLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-errorlog-logrotationtime" +#define CONFIG_AUDITLOG_LOGROTATIONTIME_DIRECTIVE "auditlog-logrotationtime" +#define CONFIG_AUDITLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-auditlog-logrotationtime" +#define CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "accesslog-logrotationtimeunit" +#define CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logrotationtimeunit" +#define CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "errorlog-logrotationtimeunit" +#define CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logrotationtimeunit" +#define CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "auditlog-logrotationtimeunit" +#define CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logrotationtimeunit" +#define CONFIG_ACCESSLOG_MAXLOGDISKSPACE_DIRECTIVE "accesslog-maxlogdiskspace" +#define CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logmaxdiskspace" +#define CONFIG_ERRORLOG_MAXLOGDISKSPACE_DIRECTIVE "errorlog-maxlogdiskspace" +#define CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logmaxdiskspace" +#define CONFIG_AUDITLOG_MAXLOGDISKSPACE_DIRECTIVE "auditlog-maxlogdiskspace" +#define CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logmaxdiskspace" +#define CONFIG_ACCESSLOG_MINFREEDISKSPACE_DIRECTIVE "accesslog-minfreediskspace" +#define CONFIG_ACCESSLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logminfreediskspace" +#define CONFIG_ERRORLOG_MINFREEDISKSPACE_DIRECTIVE "errorlog-minfreediskspace" +#define CONFIG_ERRORLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logminfreediskspace" +#define CONFIG_AUDITLOG_MINFREEDISKSPACE_DIRECTIVE "auditlog-minfreediskspace" +#define CONFIG_AUDITLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logminfreediskspace" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_DIRECTIVE "accesslog-logexpirationtime" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-accesslog-logexpirationtime" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIME_DIRECTIVE "errorlog-logexpirationtime" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-errorlog-logexpirationtime" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIME_DIRECTIVE "auditlog-logexpirationtime" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-auditlog-logexpirationtime" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "accesslog-logexpirationtimeunit" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logexpirationtimeunit" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "errorlog-logexpirationtimeunit" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logexpirationtimeunit" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "auditlog-logexpirationtimeunit" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logexpirationtimeunit" +#define CONFIG_ACCESSLOG_LOGGING_ENABLED_DIRECTIVE "accesslog-logging-enabled" +#define CONFIG_ACCESSLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-accesslog-logging-enabled" +#define CONFIG_ERRORLOG_LOGGING_ENABLED_DIRECTIVE "errorlog-logging-enabled" +#define CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-errorlog-logging-enabled" +#define CONFIG_AUDITLOG_LOGGING_ENABLED_DIRECTIVE "auditlog-logging-enabled" +#define CONFIG_AUDITLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-auditlog-logging-enabled" +#define CONFIG_ROOTDN_DIRECTIVE "rootdn" +#define CONFIG_ROOTDN_ATTRIBUTE "nsslapd-rootdn" +#define CONFIG_ROOTPW_DIRECTIVE "rootpw" +#define CONFIG_ROOTPW_ATTRIBUTE "nsslapd-rootpw" +#define CONFIG_ROOTPWSTORAGESCHEME_DIRECTIVE "rootpwstoragescheme" +#define CONFIG_ROOTPWSTORAGESCHEME_ATTRIBUTE "nsslapd-rootpwstoragescheme" +#define CONFIG_UPDATEDN_DIRECTIVE "updatedn" +#define CONFIG_UPDATEDN_ATTRIBUTE "nsslapd-updatedn" +#define CONFIG_UPDATEPW_DIRECTIVE "updatepw" +#define CONFIG_UPDATEPW_ATTRIBUTE "nsslapd-updatepw" +#define CONFIG_UPDATESSLCLIENT_DIRECTIVE "updateSSLclient" +#define CONFIG_UPDATESSLCLIENT_ATTRIBUTE "nsslapd-updateSSLclient" +#define CONFIG_AUDITFILE_DIRECTIVE "auditfile" +#define CONFIG_AUDITFILE_ATTRIBUTE "nsslapd-auditlog" +#define CONFIG_LASTMOD_DIRECTIVE "lastmod" +#define CONFIG_LASTMOD_ATTRIBUTE "nsslapd-lastmod" +#define CONFIG_INCLUDE_DIRECTIVE "include" +#define CONFIG_INCLUDE_ATTRIBUTE "nsslapd-include" +#define CONFIG_DYNAMICCONF_DIRECTIVE "dynamicconf" +#define CONFIG_DYNAMICCONF_ATTRIBUTE "nsslapd-dynamicconf" +#define CONFIG_USEROC_DIRECTIVE "useroc" +#define CONFIG_USEROC_ATTRIBUTE "nsslapd-useroc" +#define CONFIG_USERAT_DIRECTIVE "userat" +#define CONFIG_USERAT_ATTRIBUTE "nsslapd-userat" +#define CONFIG_SVRTAB_DIRECTIVE "svrtab" +#define CONFIG_SVRTAB_ATTRIBUTE "nsslapd-svrtab" +#ifndef _WIN32 +#define CONFIG_LOCALUSER_DIRECTIVE "localuser" +#define CONFIG_LOCALUSER_ATTRIBUTE "nsslapd-localuser" +#endif /* !_WIN32 */ +#define CONFIG_LOCALHOST_DIRECTIVE "localhost" +#define CONFIG_LOCALHOST_ATTRIBUTE "nsslapd-localhost" +#define CONFIG_PORT_DIRECTIVE "port" +#define CONFIG_PORT_ATTRIBUTE "nsslapd-port" +#define CONFIG_LISTENHOST_DIRECTIVE "listenhost" +#define CONFIG_LISTENHOST_ATTRIBUTE "nsslapd-listenhost" +#define CONFIG_SECURITY_DIRECTIVE "security" +#define CONFIG_SECURITY_ATTRIBUTE "nsslapd-security" +#define CONFIG_SSL3CIPHERS_DIRECTIVE "SSL3ciphers" +#define CONFIG_SSL3CIPHERS_ATTRIBUTE "nsslapd-SSL3ciphers" +#define CONFIG_ACCESSLOG_DIRECTIVE "accesslog" +#define CONFIG_ACCESSLOG_ATTRIBUTE "nsslapd-accesslog" +#define CONFIG_ERRORLOG_DIRECTIVE "errorlog" +#define CONFIG_ERRORLOG_ATTRIBUTE "nsslapd-errorlog" +#define CONFIG_INSTANCEDIR_DIRECTIVE "instancedir" +#define CONFIG_INSTANCEDIR_ATTRIBUTE "nsslapd-instancedir" +#define CONFIG_SECUREPORT_DIRECTIVE "secure-port" +#define CONFIG_SECUREPORT_ATTRIBUTE "nsslapd-securePort" +#define CONFIG_SECURELISTENHOST_DIRECTIVE "secure-listenhost" +#define CONFIG_SECURELISTENHOST_ATTRIBUTE "nsslapd-securelistenhost" +#define CONFIG_THREADNUMBER_DIRECTIVE "threadnumber" +#define CONFIG_THREADNUMBER_ATTRIBUTE "nsslapd-threadnumber" +#define CONFIG_MAXTHREADSPERCONN_DIRECTIVE "maxthreadsperconn" +#define CONFIG_MAXTHREADSPERCONN_ATTRIBUTE "nsslapd-maxthreadsperconn" +#if !defined(_WIN32) && !defined(AIX) +#define CONFIG_MAXDESCRIPTORS_DIRECTIVE "maxdescriptors" +#define CONFIG_MAXDESCRIPTORS_ATTRIBUTE "nsslapd-maxdescriptors" +#endif /* !_WIN32 && ! AIX */ +#define CONFIG_RESERVEDESCRIPTORS_DIRECTIVE "reservedescriptors" +#define CONFIG_RESERVEDESCRIPTORS_ATTRIBUTE "nsslapd-reservedescriptors" +#define CONFIG_IDLETIMEOUT_DIRECTIVE "idletimeout" +#define CONFIG_IDLETIMEOUT_ATTRIBUTE "nsslapd-idletimeout" +#define CONFIG_IOBLOCKTIMEOUT_DIRECTIVE "ioblocktimeout" +#define CONFIG_IOBLOCKTIMEOUT_ATTRIBUTE "nsslapd-ioblocktimeout" +#define CONFIG_NTSYNCH_DIRECTIVE "ntsynch" +#define CONFIG_NTSYNCH_ATTRIBUTE "nsslapd-NTSynch" +#define CONFIG_NTSYNCHUSESSL_DIRECTIVE "ntsynchusessl" +#define CONFIG_NTSYNCHUSESSL_ATTRIBUTE "nsslapd-NTSynch-SSL" +#define CONFIG_NTSYNCHPORT_DIRECTIVE "ntsynch-port" +#define CONFIG_NTSYNCHPORT_ATTRIBUTE "nsslapd-NTSynch-port" +#define CONFIG_ACCESSCONTROL_DIRECTIVE "accesscontrol" +#define CONFIG_ACCESSCONTROL_ATTRIBUTE "nsslapd-accesscontrol" +#define CONFIG_GROUPEVALNESTLEVEL_DIRECTIVE "groupevalnestlevel" +#define CONFIG_GROUPEVALNESTLEVEL_ATTRIBUTE "nsslapd-groupevalnestlevel" +#define CONFIG_NAGLE_DIRECTIVE "nagle" +#define CONFIG_NAGLE_ATTRIBUTE "nsslapd-nagle" +#define CONFIG_PW_CHANGE_DIRECTIVE "pw_change" +#define CONFIG_PW_CHANGE_ATTRIBUTE "passwordChange" +#define CONFIG_PW_MUSTCHANGE_DIRECTIVE "pw_must_change" +#define CONFIG_PW_MUSTCHANGE_ATTRIBUTE "passwordMustChange" +#define CONFIG_PW_SYNTAX_DIRECTIVE "pw_syntax" +#define CONFIG_PW_SYNTAX_ATTRIBUTE "passwordCheckSyntax" +#define CONFIG_PW_MINLENGTH_DIRECTIVE "pw_minlength" +#define CONFIG_PW_MINLENGTH_ATTRIBUTE "passwordMinLength" +#define CONFIG_PW_EXP_DIRECTIVE "pw_exp" +#define CONFIG_PW_EXP_ATTRIBUTE "passwordExp" +#define CONFIG_PW_MAXAGE_DIRECTIVE "pw_maxage" +#define CONFIG_PW_MAXAGE_ATTRIBUTE "passwordMaxAge" +#define CONFIG_PW_MINAGE_DIRECTIVE "pw_minage" +#define CONFIG_PW_MINAGE_ATTRIBUTE "passwordMinAge" +#define CONFIG_PW_WARNING_DIRECTIVE "pw_warning" +#define CONFIG_PW_WARNING_ATTRIBUTE "passwordWarning" +#define CONFIG_PW_HISTORY_DIRECTIVE "pw_history" +#define CONFIG_PW_HISTORY_ATTRIBUTE "passwordHistory" +#define CONFIG_PW_INHISTORY_DIRECTIVE "pw_inhistory" +#define CONFIG_PW_INHISTORY_ATTRIBUTE "passwordInHistory" +#define CONFIG_PW_LOCKOUT_DIRECTIVE "pw_lockout" +#define CONFIG_PW_LOCKOUT_ATTRIBUTE "passwordLockout" +#define CONFIG_PW_STORAGESCHEME_DIRECTIVE "pw_storagescheme" +#define CONFIG_PW_STORAGESCHEME_ATTRIBUTE "passwordStorageScheme" +#define CONFIG_PW_MAXFAILURE_DIRECTIVE "pw_maxfailure" +#define CONFIG_PW_MAXFAILURE_ATTRIBUTE "passwordMaxFailure" +#define CONFIG_PW_UNLOCK_DIRECTIVE "pw_unlock" +#define CONFIG_PW_UNLOCK_ATTRIBUTE "passwordUnlock" +#define CONFIG_PW_LOCKDURATION_DIRECTIVE "pw_lockduration" +#define CONFIG_PW_LOCKDURATION_ATTRIBUTE "passwordLockoutDuration" +#define CONFIG_PW_RESETFAILURECOUNT_DIRECTIVE "pw_resetfailurecount" +#define CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE "passwordResetFailureCount" +#define CONFIG_ACCESSLOG_BUFFERING_DIRECTIVE "logbuffering" +#define CONFIG_ACCESSLOG_BUFFERING_ATTRIBUTE "nsslapd-accesslog-logbuffering" +#define CONFIG_CHANGELOG_DIR_DIRECTIVE "changelogdir" +#define CONFIG_CHANGELOG_DIR_ATTRIBUTE "nsslapd-changelogdir" +#define CONFIG_CHANGELOG_SUFFIX_DIRECTIVE "changelogsuffix" +#define CONFIG_CHANGELOG_SUFFIX_ATTRIBUTE "nsslapd-changelogsuffix" +#define CONFIG_CHANGELOG_MAXENTRIES_DIRECTIVE "changelogmaxextries" +#define CONFIG_CHANGELOG_MAXENTRIES_ATTRIBUTE "nsslapd-changelogmaxentries" +#define CONFIG_CHANGELOG_MAXAGE_DIRECTIVE "changelogmaxage" +#define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE "nsslapd-changelogmaxage" +#define CONFIG_RETURN_EXACT_CASE_DIRECTIVE "return_exact_case" +#define CONFIG_RESULT_TWEAK_DIRECTIVE "result_tweak" +#define CONFIG_REFERRAL_MODE_DIRECTIVE "referralmode" +#define CONFIG_ATTRIBUTE_NAME_EXCEPTION_DIRECTIVE "attribute_name_exceptions" +#define CONFIG_MAXBERSIZE_DIRECTIVE "maxbersize" +#define CONFIG_VERSIONSTRING_DIRECTIVE "versionstring" +#define CONFIG_ENQUOTE_SUP_OC_DIRECTIVE "enquote_sup_oc" +#define CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE "nsslapd-enquote_sup_oc" +#define CONFIG_BASEDN_DIRECTIVE "certmap-basedn" +#define CONFIG_BASEDN_ATTRIBUTE "nsslapd-certmap-basedn" + +%HashParametersName = (); + +# The following hash displays only general server parameters to migrate under cn=config +%GeneralSrvParamToMigrate = ( + 'accesscontrol' => 'nsslapd-accesscontrol', + 'errorlog-logging-enabled' => 'nsslapd-errorlog-logging-enabled', + 'accesslog-logging-enabled' => 'nsslapd-accesslog-logging-enabled', + 'auditlog-logging-enabled' => 'nsslapd-auditlog-logging-enabled', + 'logbuffering' => 'nsslapd-accesslog-logbuffering', + 'accesslog-logexpirationtime' => 'nsslapd-accesslog-logexpirationtime', + 'accesslog-logexpirationtimeunit' => 'nsslapd-accesslog-logexpirationtimeunit', + 'accesslog-maxlogdiskspace' => 'nsslapd-accesslog-logmaxdiskspace', + 'accesslog-minfreediskspace' => 'nsslapd-accesslog-logminfreediskspace', + 'accesslog-logrotationtime' => 'nsslapd-accesslog-logrotationtime', + 'accesslog-logrotationtimeunit' => 'nsslapd-accesslog-logrotationtimeunit', + 'accesslog-maxlogsize' => 'nsslapd-accesslog-maxlogsize', + 'accesslog-maxnumoflogsperdir' => 'nsslapd-accesslog-maxLogsPerDir', + 'auditlog-logexpirationtime' => 'nsslapd-auditlog-logexpirationtime', + 'auditlog-logexpirationtimeunit' => 'nsslapd-auditlog-logexpirationtimeunit', + 'auditlog-maxlogdiskspace' => 'nsslapd-auditlog-logmaxdiskspace', + 'auditlog-minfreediskspace' => 'nsslapd-auditlog-logminfreediskspace', + 'auditlog-logrotationtime' => 'nsslapd-auditlog-logrotationtime', + 'auditlog-logrotationtimeunit' => 'nsslapd-auditlog-logrotationtimeunit', + 'auditlog-maxlogsize' => 'nsslapd-auditlog-maxlogsize', + 'auditlog-maxnumoflogsperdir' => 'nsslapd-auditlog-maxLogsPerDir', + 'certmap-basedn' => 'nsslapd-certmap-basedn', + 'enquote_sup_oc' => 'nsslapd-enquote-sup-oc', + 'loglevel' => 'nsslapd-errorlog-level', + 'errorlog-logexpirationtime' => 'nsslapd-errorlog-logexpirationtime', + 'errorlog-logexpirationtimeunit' => 'nsslapd-errorlog-logexpirationtimeunit', + 'errorlog-maxlogdiskspace' => 'nsslapd-errorlog-logmaxdiskspace', + 'errorlog-minfreediskspace' => 'nsslapd-errorlog-logminfreediskspace', + 'errorlog-logrotationtime' => 'nsslapd-errorlog-logrotationtime', + 'errorlog-logrotationtimeunit' => 'nsslapd-errorlog-logrotationtimeunit', + 'errorlog-maxlogsize' => 'nsslapd-errorlog-maxlogsize', + 'errorlog-maxnumoflogsperdir' => 'nsslapd-errorlog-maxlogsperdir', + 'idletimeout' => 'nsslapd-idletimeout', + 'ioblocktimeout' => 'nsslapd-ioblocktimeout', + 'lastmod' => 'nsslapd-lastmod', + 'listenhost' => 'nsslapd-listenhost', + 'maxdescriptors' => 'nsslapd-maxdescriptors', + 'referral' => 'nsslapd-referral', + 'reservedescriptors' => 'nsslapd-reservedescriptors', + 'rootpwstoragescheme' => 'nsslapd-rootpwstoragescheme', + 'schemacheck' => 'nsslapd-schemacheck', + 'secure-port' => 'nsslapd-securePort', + 'security' => 'nsslapd-security', + 'sizelimit' => 'nsslapd-sizelimit', + 'SSL3ciphers' => 'nsslapd-SSL3ciphers', + 'timelimit' => 'nsslapd-timelimit', + 'pw_change' => 'passwordChange', + 'pw_syntax' => 'passwordCheckSyntax', + 'pw_exp' => 'passwordExp', + 'pw_history' => 'passwordHistory', + 'pw_inhistory' => 'passwordInHistory', + 'pw_lockout' => 'passwordLockout', + 'pw_lockduration' => 'passwordLockoutDuration', + 'pw_maxage' => 'passwordMaxAge', + 'pw_maxfailure' => 'passwordMaxFailure', + 'pw_minage' => 'passwordMinAge', + 'pw_minlength' => 'passwordMinLength', + 'pw_must_change' => 'passwordMustChange', + 'pw_resetfailurecount' => 'passwordResetFailureCount', + 'pw_storagescheme' => 'passwordStorageScheme', + 'pw_unlock' => 'passwordUnlock', + 'pw_warning' => 'passwordWarning' +); + +# the following hash displays global parameters related to database stored under cn=config,cn=ldbm database,cn=plugins,cn=config +%GlobalConfigLDBMparamToMigrate = ( + 'allidsthreshold' => 'nsslapd-allidsthreshold', + 'lookthroughlimit' => 'nsslapd-lookthroughlimit', + 'mode' => 'nsslapd-mode', + 'dbcachesize' => 'nsslapd-dbcachesize' +); + +# the following hash displays specific parameters to each backends and stored under cn=DBname,cn=ldbm database,cn=plugins,cn=config +%LDBMparamToMigrate = ( + 'cachesize' => 'nsslapd-cachesize', + 'readonly' => 'nsslapd-readonly' +); + +%stdIncludes = ( + "${oldConfDir}slapd.at.conf", "\n", + "${oldConfDir}slapd.oc.conf", "\n", + "${oldConfDir}java-object-schema.conf", "\n", + "${oldConfDir}ns-admin-schema.conf", "\n", + "${oldConfDir}ns-calendar-schema.conf", "\n", + "${oldConfDir}ns-certificate-schema.conf", "\n", + "${oldConfDir}ns-common-schema.conf", "\n", + "${oldConfDir}ns-compass-schema.conf", "\n", + "${oldConfDir}ns-delegated-admin-schema.conf", "\n", + "${oldConfDir}ns-directory-schema.conf", "\n", + "${oldConfDir}ns-legacy-schema.conf", "\n", + "${oldConfDir}ns-mail-schema.conf", "\n", + "${oldConfDir}ns-mcd-browser-schema.conf", "\n", + "${oldConfDir}ns-mcd-config-schema.conf", "\n", + "${oldConfDir}ns-mcd-li-schema.conf", "\n", + "${oldConfDir}ns-mcd-mail-schema.conf", "\n", + "${oldConfDir}ns-media-schema.conf", "\n", + "${oldConfDir}ns-mlm-schema.conf", "\n", + "${oldConfDir}ns-msg-schema.conf", "\n", + "${oldConfDir}ns-netshare-schema.conf", "\n", + "${oldConfDir}ns-news-schema.conf", "\n", + "${oldConfDir}ns-proxy-schema.conf", "\n", + "${oldConfDir}ns-value-schema.conf", "\n", + "${oldConfDir}ns-wcal-schema.conf", "\n", + "${oldConfDir}ns-cos-schema.conf", "\n", + "${oldConfDir}ns-web-schema.conf", "\n" +); + +%userDefinedConfigFiles = ( + "slapd.conf", "\n", + "slapd.ldbm.conf", "\n", + "slapd.user_at.conf", "\n", + "slapd.user_oc.conf", "\n", + "ns-schema.conf", "\n" + ); + +$CIS_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.15" ; +$TELEPHONE_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.50" ; +$DN_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.12" ; +$CES_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.26" ; +$INT_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.27" ; +$BIN_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.5" ; + +%allowedPlugins = ( + "cis", $CIS_SYNTAX_OID, + "tel", $TELEPHONE_SYNTAX_OID, + "dn", $DN_SYNTAX_OID, + "ces", $CES_SYNTAX_OID, + "int", $INT_SYNTAX_OID, + "bin", $BIN_SYNTAX_OID + ); + +%allowedModifiers = ( + "single", "SINGLE-VALUE" + ); +# "override" is not supported anymore and "operational" cannot be used in user defined attribute. + +@oldSuffixes = () ; # array of old suffixes (with "o=netscaperoot" if presents) + +# link beetwen the name of the suffix and its associated DBname +%DBNAMES = () ; +%DBDirectory = () ; + +%oldhash = () ; + +# list of standard plugin's in version 4 +%stdPlugins = ( + "7-bit check" => "\n", + "binary syntax" => "\n", + "case exact string syntax" => "\n", + "case ignore string syntax" => "\n", + "distinguished name syntax" => "\n", + "integer syntax" => "\n", + "internationalization plugin" => "\n", + "referential integrity postoperation" => "\n", + "telephone syntax" => "\n", + "uid uniqueness" => "\n" + + ); + +# list of standard indexes configured out of the box in version 4 +%stdIndex = ( + 'aci', "\n", + 'changenumber', "\n", + 'copiedfrom', "\n", + 'dncomp', "\n", + 'entrydn', "\n", + 'numsubordinates', "\n", + 'objectclass', "\n", + 'parentid', "\n" +); + +# list of user added Plugin's. In the new version, they 'll need to be recompiled +@badPlugins = () ; + +%newIndex = () ; + +%User_oc = () ; +# push objectnames as they are encountered in config files. +@User_oc_names = () ; + +%User_at = () ; + + + +#Usage parameters +$USER_OC_FILE_MODIFIED = 0 ; # 0 if user don't want to modify LDIF objectclasses before processing, 1 else +$USER_AT_FILE_MODIFIED = 0 ; +$INDEX_FILE_MODIFIED = 0 ; + +# get the version of the DS to migrate +($oldVersion, $oldMinor) = &getVersion($oldDir, $oldversionstr); +# get the version of the new DS +($Version, $Minor) = &getVersion($root); + +# get old LIB_PATH +$old_libpath = &getLibPath($oldDir, $oldVersion, $oldMinor); +# get new LIB_PATH +$new_libpath = &getLibPath($root, $Version, $Minor); + +# Shutdown the legacy Directory instance +printTrace("\nShutdown the legacy Directory Server instance: ${oldHome}",0); +&stopServer($oldDir, 'slapd-'.$oldname); + +# compare configuration files with the standard ones +CompareStdConfigFiles() ; +die "\n\n The version of the product you want to migrate is not a 4.x Netscape Directory Server\n" unless ($oldVersion == 4) ; + +FillHashParametersName() ; + +############### Connect to the New LDAP Directory Server ###################### +$ENV{"$LIB_PATH"} = $new_libpath; +my $LDAPservername = &getLDAPservername(); +die "\n Migration aborted. Make sure your Old and New Directory Servers are installed on the same machine \n" if ( $LDAPservername == -1 ); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; + +# continue if the connection to new LDAP server is successful ! +printTrace("\nConnected to $Version.$Minor LDAP server\n",0) ; + +# get the uid and gid of the new slapd user +($localuser, $newuid, $newgid) = getuid_gid(); +# get the uid and gid of the old slapd user +($oldlocaluser, $olduid, $oldgid) = getolduid_gid(); + +# backup new configuration files in <new_root_server>/slapd-instancename/config +printTrace("\nBackup $serverHome${PATHSEP}config on $serverHome${PATHSEP}config_backup ...",0); +&backupConfigFiles(); + +# Parse the main configuration file: slapd.conf +printTrace("\nParse the configuration file: $oldSlapdConf...",0); +ParseSlapdConf("< ${oldSlapdConf}"); + +#migrate key/cert databases +printTrace("\nMigrate key/cert databases...",0); +&MigrateSSL(); + +# Update parameters : general server parameters, global LDBM parameter, specific backend parameters +printTrace("\nUpdate general server parameters...",0); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; +AddGeneralParameters(); +printTrace("\nUpdate global LDBM parameters...",0); +AddGeneralLDBMParameters(); +printTrace("\nUpdate specific backend parameters...",0); +AddSpecificLDBMParameters(); + +##### FOR TESTING PURPOSE ONLY ######## +# +#testIndexUpdating(); +# +####################################### + +# Migrate some entries contained in the old dse.ldif, and migrate certmap.conf +&MigrateDSE() ; +&MigrateCertmap() ; + +# update new attribute definitions +LDAPmodify_User_at(); + +# update new object classes definitions +LDAPmodify_User_oc(); + +# add new indexes to each backends +LDAPmodify_Indexes(); + +# migrate Plug'ins parameters (enable attribute, and arguments) +LDAPmodify_stdPlugin(); + +################## Close the connection to new LDAP Server ##################### +$conn->close; + + +################## stop the new instance and Export/Import the data, restart the server ################## +if (%DBNAMES) { + &stopServer($root,'slapd-'.$newname); + if ($olddatadir) { + printTrace("\nold data directory $olddatadir...",0) ; + $ldif_rep = "$olddatadir${PATHSEP}"; + } else { + printTrace("\ndata processing...",0) ; + # migrate data for each suffix: old -> LDIF files + &db2ldif($oldSlapdConf); + } + + # migrate LDIF data to the new database: LDIF -> new + &manyLdif2db(); + &startServer(); +} +else { + printTrace("\nThere no old non-standard suffixes to migrate",0); +} + +printMsg("\n\n ****** End of migration ******\n\n"); + +close(LOGFILE); + + +########################################################################################### +# get input users +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # new instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe new instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # new DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-d") { # old instance LDIF data dir + if (! $olddatadir ) { + $olddatadir = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-v") { # old version + if (! $oldversionstr ) { + $oldversionstr = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + $NO_INPUT_USER = 1 ; + } elsif ("$ARGV[$i]" eq "-L") { # migration logfile + $LogFileReport = $ARGV[++$i] ; + } + else { + print("\nThe option $ARGV[$i] is not recognized"); + &usage() ; + exit(1); + } + $i++; + } + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + + if ($exit) { + &usage() ; + exit(1); + } + +} + + +############################################################################### +# This subroutine is used to parse the slapd.conf configuration file and migrate specific parameters contained in it + + +sub ParseSlapdConf { + my $oldsrc = shift; + my $NumLine = 0 ; + # read the old conf file into a hash table + open( OLDSRC, $oldsrc ) || die "Can't open $oldsrc: $!: "; + LINE: while ( <OLDSRC> ) { + $NumLine++ ; + printTrace("\nLine: $_",4) ; + if (/^\s*\#/) { # skip comments + printTrace("\n# ",4) ; + next LINE; + } + if (/^\s*$/) { # skip blank lines + printTrace("\nBLANK LINE",4); + next LINE; + } elsif (/^suffix\s+/i) { + chomp($_) ; + CheckSuffix($_); + } elsif (/^plugin/i) { + printTrace("\nPLUGIN",4); + chomp($_); + if (! &isAStandardPlugin($_)) { + push @badPlugins, $_; + } + else { + my $Plugin = $_ ; + if (! &ParsePlugin($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of the plugin:\n$Plugin"); + } + } + } elsif (/^include\s+[\"]?(.*?)[\"]?\s*$/i) { + # strip leading and trailing " + my $include_file = $1 ; + grep { s@/@\\@g } $include_file if $isNT; + if (! &isAStandardInclude($include_file)) { + printTrace("\nFILE: $1 NOT STANDARD",4) ; + &ParseConfigurationFile($include_file); + printTrace("\nEXIT ParseConfigurationFile: $include_file",4) ; + } + } elsif (/^userat\s+[\"]?(.*?)[\"]?\s*$/i) { + printTrace("\nuserat: $1",4); + my $at_file = $1 ; + grep { s@/@\\@g } $at_file if $isNT; + # Parse user defined attributes + &ParseAttributesFile($at_file); + } elsif (/^useroc\s+[\"]?(.*?)[\"]?\s*$/i) { + printTrace("\nuseroc: $1",4); + my $oc_file = $1 ; + grep { s@/@\\@g } $oc_file if $isNT; + # Parse user defined object classes + &ParseObjectClassesFile($oc_file); + } elsif (/^dynamicconf\s+[\"]?(.*?)[\"]?\s*$/i){ + printTrace("\ndynamicconf: $1",4); + my $dynamiconf_file = $1 ; + grep { s@/@\\@g } $dynamiconf_file if $isNT; + # Parse dynamic configuration file (e-g slapd.ldbm.conf) + &ParseConfigurationFile($dynamiconf_file); + } elsif (/^\s*(\S+)\s+[\"]?(.*?)[\"]?\s*$/) { + printTrace("\nParseParameters: $1",4); + # Parse parameters and record the associated value in %oldhash + &ParseParameters($1,$2,$NumLine); + } else { + printTrace("\nUnknown format of configuration data: $_",0); } + } + close(OLDSRC); + + } + + + +############################################################################# +# return 1 if the suffix already exists, 0 else +sub existSuffix { + my $suffixname = shift ; + my $nsuffix = normalizeDN($suffixname); + my $entry = $conn->search("cn=mapping tree,cn=config", "one", "(|(cn=\"$suffixname\")(cn=\"$nsuffix\"))"); + return 1 if ($entry) ; + my $cpt = 5; + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && $cpt && (! $entry)) { + printTrace("\ntry to reconnect to search cn=\"$suffixname\",cn=mapping tree,cn=config", 1); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $entry = $conn->search("cn=mapping tree,cn=config", "one", "(|(cn=\"$suffixname\")(cn=\"$nsuffix\"))"); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + return 1 if ($entry) ; + return 0 ; +} + +# return the name of the backend if it has been successfully created, 0 else +sub createBackend { + my $suffixname = shift ; + my $backend = "MigratedDB_0" ; + my $NbRetry = 1 ; + my $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + while ($entry) { + # try to find another name for the backend + $backend = "MigratedDB_$NbRetry" ; + $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + $NbRetry++; + } + # normally I should have a unique name for the backend + my $suffixarg = "nsslapd-suffix" ; + $entry = $conn->newEntry() ; + $entry->setDN("cn=$backend,cn=ldbm database,cn=plugins,cn=config"); + $entry->setValues("objectclass", "top", "extensibleObject", "nsBackendInstance" ); + $entry->setValues("cn", $backend ); + $entry->setValues($suffixarg, $suffixname ); + my $res = $conn->add($entry) ; + if ($res) { + return $backend ; + } + else { + return 0 ; + } +} + +# return 1, if add the new entry in the mapping tree, else 0 +sub AddEntryInMappingTree { + my $backend = shift ; + my $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + if ($entry) { + printTrace("\nAddEntry in progress ...",4) ; + my $suffixarg = "nsslapd-suffix" ; + my $statearg = "nsslapd-state" ; + my $backendarg= "nsslapd-backend"; + my $suffixname = $entry->{$suffixarg}[0]; + $entry = $conn->newEntry() ; + $entry->setDN("cn=\"$suffixname\",cn=mapping tree,cn=config") ; + $entry->setValues("objectclass", "top", "extensibleObject", "nsMappingTree" ); + $entry->setValues("cn", "\"$suffixname\""); + $entry->setValues($statearg, "backend"); + $entry->setValues($backendarg, $backend); + return $conn->add($entry); + } + else { + printTrace("\nNo AddEntry processed for $backend",4); + return 0 ; + } +} + + +# Treat the case where the suffix is "o=NetscapeRoot" +sub CheckSuffix { + my $suffix = shift ; + my $suffixname ; + my $expLdif; + my $confirm = "No"; + my $dest = "$serverHome${PATHSEP}db_backup" ; + my $newSlapdExecDir = "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + + if (!(/^suffix\s+\"?(.*?)\"?\s*$/i)) { + printMsg("Syntax error of the suffix: $suffix"); + return 0 ; + } + else { + $suffixname = $1 ; + } + if (/^suffix\s+\"?\s*o=netscaperoot\s*\"?\s*$/i) { + printTrace("\nFor the suffix o=NetscapeRoot, we do nothing",1); + # treat the case where the suffix is "o=NetscapeRoot" + } + else { + push @oldSuffixes, $_; + # check if the suffix already exists in the new DS target + if (! existSuffix($suffixname)) { + printTrace("\n\nSuffix $suffixname doesn't exist",1) ; + # create a new backend with the name of the suffix preceded by MigratedDB_ + my $backend = createBackend($suffixname) ; + if ($backend) { + printTrace("\nBackend: $backend has been created !!!",1); + # if the creation of the backend is ok, we add a new entry in the mapping tree + if (AddEntryInMappingTree($backend)) { + # We add the association dbname->suffix in the hash %DBNAMES + $DBNAMES{$suffixname} = $backend ; + # get the db filename + $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + my $dirarg = "nsslapd-directory"; + $DBDirectory{$backend} = $entry->{$dirarg}[0]; + printTrace("\nThe relation $backend->$suffixname has been added to the mapping tree",2); + } + else { + printMsg("\nCOULD NOT ADD ENTRY: $backend->$suffixname IN MAPPINGTREE"); + } + } + else { + printMsg("\nCOULD NOT CREATE BACKEND: $backend"); + } + } + else { + printMsg("\n\nSuffix: $suffixname already exists"); + # the suffix already exists in the new DS + printMsg("\nMigration will overwrite existing database"); + printMsg("\nDo you want to continue Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + my $nsuffix = normalizeDN($suffixname); + my $my_entry = $conn->search("cn=mapping tree,cn=config", "one", "(|(cn=\"$suffixname\")(cn=\"$nsuffix\"))"); + my $backend = $my_entry->{"nsslapd-backend"}[0]; + my $backend_entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + printMsg("Do you want to export the existing data Yes/No [Yes] ?"); + my $answer = <STDIN> ; + if (!($answer =~ /n|no/i)) { + mkdir $dest, 0700 unless (-d $dest); + $expLdif = "$dest${PATHSEP}$backend.ldif"; + while (!($confirm =~ /y|yes/i)) { + printMsg("\nEnter the full pathname of the file [$expLdif]:") ; + $answer = <STDIN> ; + chomp($expLdif = $answer) unless ($answer eq "\n"); + printMsg("\nExisting data will be exported under $expLdif"); + printMsg("\nContinue Yes/No [No] ?"); + $confirm = <STDIN>; + } + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($newSlapdExecDir) or die "\nCould not change directory to $newSlapdExecDir: $!\n"; + printTrace("\nNow backing up database $CN in $expLdif\n",0); + &stopServer($root,'slapd-'.$newname); + &newinst_db2ldif($expLdif, $suffixname, $serverHome); + &startServer(); + } + # We add the association dbname->suffix in the hash %DBNAMES + $DBNAMES{$suffixname} = $backend ; + my $dirarg = "nsslapd-directory"; + $DBDirectory{$backend} = $backend_entry->{$dirarg}[0]; + printTrace("\nThe relation $backend->$suffixname has been added to the mapping tree",2); + } + } + return 1 ; +} +} + +############################################################################# +# Usefull to know the standard configuration +sub isAStandardPlugin { + my $line = shift; + chomp($line); + printTrace("\nStdPlugin?: $line",4); + if ($line =~ /^plugin\s+(database|extendop|preoperation|postoperation|matchingrule|syntax)\s+(on|off)\s+\"(.*?)\"\s+\"(.*?)\"\s+(\S+)(.*)$/i) { + # $1 = <type>, $2 = <on|off>, $3 = <name>, $4 = <pathname>, $5 = <init_function>, $6 = [<arg>]* + printTrace("\nName: $3, pathname: $4, init_function: $5",4); + + my $LC_line = lc($3); + my $Value = $stdPlugins{$LC_line} ; + if ($Value) { + printTrace("\nIS A STANDARD PLUGIN",4); + } + else { + printTrace("\nNOT A STANDARD PLUGIN",4); + } + return $stdPlugins{$LC_line} ; + } + else { + printTrace("\nSYNTAX ERROR PLUGIN",4); + return 0 ; + } +} + +sub isAStandardIndex { + my $line = shift ; + chomp($line); + if ($line =~ /^index\s+(\S+).*/i) { + my $LC_line = lc($1); + my $Value = $stdIndex{$LC_line} ; + printTrace("\nInclude: $LC_line \nValue: $Value", 4); + return $stdIndex{$LC_line}; + } + else { + return 0 ; + } +} + + +sub isAStandardInclude { + my $line = shift; + + chomp($line); + if ($isNT){ + return $stdIncludes{lc($line)}; + } + else { + return $stdIncludes{$line} ; + } +} + +############################################################################# +# +# Execute a Perldap command to update plugins definition in the new schema + +sub LDAPmodify_stdPlugin { + my $Filename = shift ; + my @pluginames = keys(%stdPlugins); + if (! $STDPLUGINS_FILE_MODIFIED) { + printTrace("\nLDAPmodify_plugin",4); + printTrace("\nMigrate plugin's...",1); + foreach $pluginame ( @pluginames ) { + my $update_plugin = 0 ; + my $ref_plugin = $stdPlugins{$pluginame}; + if ($ref_plugin ne "\n") { + my $name = $ref_plugin->name ; + # We have a name change of "uid uniqueness plugin" in DS6.x + # to "attribute uniqueness" + $name = "attribute uniqueness" if ($name eq "uid uniqueness"); + my $entry = $conn->search("cn=$name,cn=plugins,cn=config", "base","objectclass=nsSlapdPlugin") ; + if ($entry) { + my $pluginenabled="nsslapd-pluginenabled" ; + if (($entry->{$pluginenabled}[0]) ne $ref_plugin->enable) { + $update_plugin = 1 ; + my $enable = $ref_plugin->enable ; + printTrace("\n$pluginame, plugin-enable: $enable",3) ; + $entry->setValues($pluginenabled, $enable ); + } + my $ArgNum = 0 ; + foreach $ArgValue (@{$ref_plugin->args}) { + my $Arg="nsslapd-pluginarg$ArgNum"; + printTrace("\n$Arg: $ArgValue",3) ; + if ($entry->{$Arg}[0] ne $ArgValue) { + printTrace("\n$pluginame, $Arg: $ArgValue",3) ; + $update_plugin = 1 ; + $entry->setValues($Arg, $ArgValue) ; + } + $ArgNum++ ; + } + if ($update_plugin) { + printTrace("\n$pluginame is being updated...",2); + my $res = $conn->update($entry) ; + if ($res) { + printTrace("\nupdated !",2); + } + else { + printMsg("\nError during update of plugin: $pluginame") ; + $MigrationErrors .= "\nError during update of plugin: $pluginame"; + } + } + else { + printTrace("\n$pluginame has not changed",4); + } + } + else { + printMsg("\ncan't access the plugin: cn=$name,cn=plugins,cn=config"); + } + } + else { + printTrace("\nPLUGIN NOT RECORDED: $pluginame",4) ; + } + } + } + else { + # treat the case where the user wants to look at these plugins before processing + } +} + +############################################################################# +# Execute Perldap command to add new indexes to the migrated instances + +sub LDAPmodify_Indexes { + my $Filename = shift ; + my @indexnames = keys(%newIndex); + my @suffixnames = keys(%DBNAMES); + if ((! $INDEX_FILE_MODIFIED) && (%DBNAMES)) { + # we update indexes only if there is at least one backend to migrate + printTrace("\nLDAPmodify_indexes",4); + printTrace("\nMigrate indexes...",1); + foreach $indexname ( @indexnames ) { + printTrace("\nIndexName: $indexname",4); + printTrace("\nIndexTypes: .@{$newIndex{$indexname}->types}.", 4) ; + printTrace("\nIndexOIDS: .@{$newIndex{$indexname}->oids}.", 4) ; + foreach $suffixname ( @suffixnames ) { + # check if the index already exists ! + printTrace("\nsearch for cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...", 3); + my $entry = $conn->search("cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + if (! $entry) { + # create a new index + printTrace("index $indexname is being created under cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...",2); + my $entry = $conn->newEntry(); + $entry->setDN("cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config"); + $entry->setValues("objectclass", "top", "nsIndex" ) ; + $entry->setValues("cn", $indexname) ; + $entry->setValues("nssystemindex", "false") ; + my @types = @{$newIndex{$indexname}->types} ; + my @oids = @{$newIndex{$indexname}->oids} ; + $entry->setValues("nsindextype", @types) if (@types) ; + $entry->setValues("nsmatchingrule", @oids ) if (@oids); + my $res = $conn->add($entry) ; + if ($res) { + printTrace("\nAdd index successfully: $indexname for backend: $DBNAMES{$suffixname}",2); + } + else { + printMsg("\n Failed to add the index: $indexname to backend: $DBNAMES{$suffixname}"); + $MigrationErrors .= "\n Failed to add the index: $indexname to backend: $DBNAMES{$suffixname}" ; + } + } + elsif ($entry->{nssystemindex}[0] eq "false") { + # if the index is not a system index, we update it + printTrace("\nindex $indexname is being processed under cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...",2); + my @types = @{$newIndex{$indexname}->types} ; printTrace("\ntypes: .@types.",2) ; + my @oids = @{$newIndex{$indexname}->oids} ; printTrace("\noids: .@oids.",2) ; + my @existing_types = $entry->getValues("nsindextype"); + my @existing_oids = $entry->getValues("nsmatchingrule"); + # get the elements present in @types and not present in @existing_types + my @typesToAdd = &getDiff(\@types, \@existing_types); + # same for matchingrules + my @oidsToAdd = &getDiff(\@oids, \@existing_oids); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + foreach $newoid (@oidsToAdd) { + $entry->addValue("nsmatchingrule", $newoid); + } + if (@typesToAdd || @oidsToAdd) { + my $res = $conn->update($entry) ; + if ($res) { + printTrace("\nUpdate index successfully: $indexname for backend: $DBNAMES{$suffixname}",2); + } + else { + printMsg("\n Failed to update the index: $indexname to backend: $DBNAMES{$suffixname}"); + $MigrationErrors .= "\n Failed to update the index: $indexname to backend: $DBNAMES{$suffixname}" ; + } + } + else { + printTrace("\nNothing to update",2); + } + } + else { + printTrace("\nThe index: $indexname is a system index. It can't be updated",2); + } + } + } + + } + else { + # treat the case where the user wants to look at these indexes before processing + } + +} + +############################################################################# +# +# Execute a Perldap command to add all user defined object classes in the new schema + +sub LDAPmodify_User_oc { + my $Filename = shift ; + if (! $USER_OC_FILE_MODIFIED) { + printTrace("\nLDAPmodify_User_oc",4); + printTrace("\nMigrate objectclasses...",1); + foreach $objectname ( @User_oc_names ) { + my $entry = $conn->search("cn=schema", "base","objectclass=*") ; + die "\ncan't connect to object: cn=schema\n" unless ($entry); + printTrace("\nObjectName: $objectname\nValue: $User_oc{$objectname}",3); + next if ($entry->hasValue("objectclasses",$User_oc{$objectname},1)) ; + $entry->addValue("objectclasses",$User_oc{$objectname},"1") ; + my $res = $conn->update($entry) ; + my $err = $conn->getErrorCode(); + if ($res) { + printTrace("\nobjectclass: $User_oc{$objectname} added",2); + } elsif ($err == 20) { # already exists + printTrace("\nobjectclass: $User_oc{$objectname} already exists",1); + } else { + printMsg("\nCan\'t add objectclass to the schema: $User_oc{$objectname}"); + my $msg = $conn->getErrorString(); + printMsg("\nMsg: $msg"); + $MigrationErrors .= "\nCan\'t add objectclass to the schema: $User_oc{$objectname}" ; + } + } + } + else { + # treat the case where the user wants to look at these objectclasses before processing + } +} + +############################################################################# +# +# Execute a Perldap command to add all user defined attributes in the new schema + +sub LDAPmodify_User_at { + my $Filename = shift ; + my @attributenames = keys(%User_at); + if (! $USER_AT_FILE_MODIFIED) { + + printTrace("\nLDAPmodify_User_at",4); + printTrace("\nMigrate attributes...",1); + foreach $attributename ( @attributenames ) { + my $entry = $conn->search("cn=schema", "base","objectclass=*") ; + printTrace("\nAtributeName: $attributename, Value: $User_at{$attributename}",3); + die "\nentry not found cn=schema\n" unless $entry ; + next if ($entry->hasValue("attributetypes",$User_at{$attributename},1) ) ; + my $res = $entry->addValue("attributetypes",$User_at{$attributename},"1") ; + if (! $res) { + printMsg("\nCan\'t add attribute to the schema: $User_at{$attributename}"); + $MigrationErrors .= "\nCan\'t add attribute to the schema: $User_at{$attributename}" ; + } + my $res = $conn->update($entry) ; + my $err = $conn->getErrorCode(); + if ($res) { + printTrace("\nattribute: $attributename added",2); + } elsif ($err == 20) { # already exists + printTrace("\nattribute: $attributename already exists",1); + } + else { + printMsg("\nCan\'t add attribute to the schema: $User_at{$attributename}"); + my $msg = $conn->getErrorString(); + printMsg("\nMsg: $msg"); + $MigrationErrors .= "\nCan\'t add attribute to the schema: $User_at{$attributename}" ; + } + } + } + else { + # treat the case where the user wants to look at these attributes before processing + } +} + +############################################################################# +# Add an object class to the user_oc hash and reset the object !!! +sub AddObjectClass { + my $ObjectClass = shift ; + my $ObjectName = $ObjectClass->{'ObjectName'} ; + my $Object_oid = $ObjectClass->{'Object_oid'} ; + my $Object_superior = $ObjectClass->{'Object_superior'} ; + my $Object_requires = $ObjectClass->{'Object_requires'} ; + my $Object_allows = $ObjectClass->{'Object_allows'} ; + my $ObjectClassDef = "( $Object_oid NAME \'$ObjectName\' DESC \'\' SUP $Object_superior STRUCTURAL MUST ($Object_requires) MAY ($Object_allows) X-ORIGIN \'user defined\' )"; + if ( (!($ObjectName =~ /^top$/i)) && ( ! $User_oc{$ObjectName} )) { + $User_oc{$ObjectName} = $ObjectClassDef ; + push @User_oc_names, $ObjectName ; + printTrace("ObjectName: $ObjectName \nObject_oid: $Object_oid \nObject_superior:$Object_superior \nObject_requires: $Object_requires \nObject_allows: $Object_allows \nObjectClassDefinition: $User_oc{$ObjectName}\n",4); + } + elsif ( ($User_oc{$ObjectName}) && ($User_oc{$ObjectName} ne $ObjectClassDef) ) { + printMsg("\nAttempt to redifine the objectclass: $ObjectName previously defined in your configuration file. Operation not allowed "); + } + else { + printMsg("\nAttempt to redifine the objectclass: top. Operation not allowed"); + } + resetObjectClass($ObjectClass); +} + +############################################################################# +# Build an LDIF attribute and add it to the user_at hash +sub AddAttribute { + my $Attr = shift ; + my $AttributeName = $Attr->{'AttributeName'}; + my $Attribute_oid = $Attr->{'Attribute_oid'}; + my $Attribute_aliases = $Attr->{'Attribute_aliases'}; + my $Attribute_syntax = $Attr->{'Attribute_syntax'}; + my $Attribute_single = $Attr->{'Attribute_single'}; + my $AttributeDef = "( $Attribute_oid NAME ( \'$AttributeName\' $Attribute_aliases) DESC \'User Defined Attribute\' SYNTAX $Attribute_syntax $Attribute_single X-ORIGIN 'user defined' )" ; + printTrace("\nAttributeDef: $AttributeDef",4); + $User_at{$AttributeName} = $AttributeDef ; +} +############################################################################# +# add the index structure to the newIndex hash +sub AddIndex { + my $ref_index = shift ; + my $state = shift ; + printTrace("\nAddIndex, last state: $state",4) ; + if ($state == 1) { + $ref_index->specific("ALL") ; + return 1 ; + } + elsif ($state == 6) { + $ref_index->specific("NONE") ; + return 1 ; + } + if (($state == 1) || ($state == 3) || ($state == 5) || ($state == 6)) { + foreach $name (@{$ref_index->names}) { + $newIndex{$name} = $ref_index ; # record the ref to the index struct in the newIndex hash + } + return 1 ; + } + else { + return 0 ; + } +} + +############################################################################# +# add the plugin structure to the stdPlugin hash + +sub AddPlugin { + my $ref_plugin = shift ; + printTrace("\nAddPlugin",4) ; + $stdPlugins{lc($ref_plugin->name)} = $ref_plugin ; + my $name = $ref_plugin->name ; + my $type = $ref_plugin->type ; + my $enable = $ref_plugin->enable ; + + printTrace("\nPluginName: $name",4); + printTrace("\nPluginType: $type",4); + printTrace("\nPluginEnable: $enable",4); + printTrace("\nPluginArgs: @{$ref_plugin->args}",4); + return 1 ; +} + + +############################################################################# +# parse a plugin definition and call the addindex + +sub ParsePlugin { + my $Plugin = shift ; + my $NumLine = shift ; + my $state = 0 ; + my $ErrorMsg = "Syntax error of a plugin definition. \n line parsed:"; + my $ref_plugin = S_plugin->new(); + printTrace("\nParsePlugin: $_",4); + if (/^plugin\s+(database|extendop|preoperation|postoperation|matchingrule|syntax)\s+(on|off)\s+\"(.*?)\"\s+\"(.*?)\"\s+(\S+)(.*)$/i) { + # $1 = <type>, $2 = <on|off>, $3 = <name>, $4 = <pathname>, $5 = <init_function>, $6 = [<arg>]* + $ref_plugin->name($3); + $ref_plugin->type($1); + $ref_plugin->enable($2); + $_ = $6 ; + my $ArgNb = 0 ; + my $prec ; + my $arg ; + my $Unix_oldDir = $oldDir ; + my $Unix_root = $root ; + grep { s@\\@/@g } $Unix_oldDir if $isNT; + grep { s@\\@/@g } $Unix_root if $isNT; + while (!(/^\s*$/)) { + if (/^\s*\".*?\"/) { + s/^\s*\"(.*?)\"(.*)/$2/i ; + $arg = $1 ; + } + elsif (/^\s*[^\"\s]+/) { + s/^\s*([^\"\s]+)(.*)/$2/i ; + $arg = $1 ; + } + $prec = $_ ; + $_ = $arg ; + + s@$Unix_oldDir@$Unix_root@ig ; + s/$type-$oldname/$type-$newname/ig ; + @{$ref_plugin->args}[$ArgNb++] = $_ ; + $_ = $prec ; + } + if (/^\s*$/) { + return AddPlugin($ref_plugin); + } + else { + return 0 ; + } + } + return 0 ; +} + +############################################################################# +# parse an index definition and call the addindex + +sub ParseIndex { + my $index = shift ; + my $NumLine = shift ; + my $ref_index = S_index->new() ; + my $Value ; + my $state = 0 ; + my $ErrorMsg = "Syntax error of an index definition.\nline parsed:"; + printTrace("\nParseIndex: $_",4) ; + s/,/, /g ; + s/\s+,/,/g ; + s/^index\s+//i ; # substitute the token index + while (!(/^\s*$/)) { + s/^\s*(\S+)(.*)$/$2/ ; + $Value = $1 ; + printTrace("\nValue: $Value",4); + printTrace("\nState: $state",4) ; + SWITCH: { + if ($state == 0) { + if ($Value =~ /[^\.]/) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->names}, $1 ; + } + else { + $state = 1 ; + push @{$ref_index->names}, $Value ; + } + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 1) { + if ($Value =~ /^none$/i) { + $state = 6 ; # end of the index definition + } + elsif ($Value =~ /^\"\"$/) { + $state = 4 ; # we expect to have at least one OID + } + elsif ($Value =~ /(\S+),$/) { + $state = 2 ; + push @{$ref_index->types}, $1 ; + } + else { + $state = 3 ; + push @{$ref_index->types}, $Value ; + } + last SWITCH ; + } + if ($state == 2) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->types}, $1 ; + } + else { + $state = 3 ; + push @{$ref_index->types}, $Value ; + } + last SWITCH ; + } + if ($state == 3) { + if ($Value =~ /(\S+),$/) { + $state = 4 ; + push @{$ref_index->oids}, $1 ; + } + else { + $state = 5 ; + push @{$ref_index->oids}, $Value ; + } + last SWITCH ; + } + if ($state == 4) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->oids}, $1 ; + } + else { + $state = 5 ; + push @{$ref_index->oids}, $Value ; + } + last SWITCH ; + } + } + } +return AddIndex($ref_index,$state) ; + +} + +############################################################################# + +sub ParseAttribute { + + + my $Attr = shift ; + my $NumLine = shift ; + my $state = 1 ; + my $ErrorMsg = "Syntax error of an attribute definition.\nline parsed:"; + my %Attribute = ( + 'AttributeName' => "", + 'Attribute_oid' => "", + 'Attribute_aliases' => "", + 'Attribute_syntax' => "", + 'Attribute_single' => "" + ); + my $AttributeName = " "; + printTrace("\nParseAttribute",4); + while (!(/^\s*$/)) { + s/^(.*?)(\S+)\s*$/$1/ ; + printTrace("\nValue: $2",4); + printTrace("\nState: $state",4) ; + my $Value = $2 ; + SWITCH: { + if ($state == 1) { + if (isAllowedModifier($Value)) { + $state = 1 ; + $modifier = lc($Value); + $AttrVar = 'Attribute_' . $modifier ; + $Attribute{$AttrVar} = &getModifierValue($Value) ; + } + elsif (&isAllowedPlugin($Value)) { + $state = 2 ; + $Attribute{'Attribute_syntax'} = &getSyntaxOid($Value) ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 2) { + if ($Value =~ /[\.]|-oid$/) { + $Attribute{'Attribute_oid'} = "$Value" ; + printTrace("\nAttribute-oid: $Attribute{'Attribute_oid'}",3); + $state = 3 ; + } + elsif ($Value =~ /[^\.]/) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + $state = 4 ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 3) { + if ($Value =~ /[^\.]/) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + $state = 4 ; } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 4) { + if ($Value =~/^attribute$/i){ + $state = 5; + } + elsif ($Value =~/[^\.]/i) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 5) { + return 0 ; + last SWITCH ; + } + } + } + $Attribute{'Attribute_oid'} = $Attribute{'AttributeName'} . '-oid' unless ($Attribute{'Attribute_oid'}) ; + return AddAttribute(\%Attribute) ; +} + + +############################################################################# +# fill in the hash HashParametersName + +sub FillHashParametersName { + my @paramnames = ( keys(%GeneralSrvParamToMigrate), keys(%GlobalConfigLDBMparamToMigrate), keys(%LDBMparamToMigrate)); + foreach $param (@paramnames) { + $HashParametersName{$param} = '\n'; + } +} + + +# Parse parameters +sub ParseParameters { + my $param = shift ; + my $value = shift ; + my $NumLine = shift ; + my $ErrorMsg = "parameter unknown, or not to be migrated: "; + if ($HashParametersName{lc($param)} && ($value !~ /^\s*$/)) { + $HashParametersName{lc($param)} = $value ; + printTrace("\nParam: $param is present",4); + } + else { + printTrace("\n$NumLine, $ErrorMsg,$param",4); + } + +} + +# add general server parameters +sub AddGeneralParameters { + my @paramnames = keys(%GeneralSrvParamToMigrate); + my $entry = $conn->search("cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=config. \nMigration stopped\n" unless ($entry); + printTrace("\nAddGeneralParameters",4); + foreach $param (@paramnames) { + my $LDAPparam = $GeneralSrvParamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam ",0); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } +} + + +# add general LDBM parameters +sub AddGeneralLDBMParameters { + my @paramnames = keys(%GlobalConfigLDBMparamToMigrate); + my $entry = $conn->search("cn=config,cn=ldbm database,cn=plugins,cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=config,cn=ldbm database,cn=plugins,cn=config. \nMigration stopped\n" unless ($entry); + printTrace("\nAddGeneralLDBMParameters",4); + foreach $param (@paramnames) { + my $LDAPparam = $GlobalConfigLDBMparamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam ",0); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } +} + +# add specific LDBM parameters +sub AddSpecificLDBMParameters { + my @paramnames = keys(%LDBMparamToMigrate); + my %REV_DBNAMES = reverse %DBNAMES ; + my @dbnames = keys(%REV_DBNAMES); + printTrace("\nAddSpecificLDBMParameters",4); + foreach $dbname (@dbnames) { + my $entry = $conn->search("cn=$dbname,cn=ldbm database,cn=plugins,cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=$dbname,cn=ldbm database,cn=plugins,cn=config. \nMigration stopped\n" unless ($entry); + foreach $param (@paramnames) { + my $LDAPparam = $LDBMparamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam",2); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } + } +} + +############################################################################# +# Parse a configuration file potentialy tuned by the user (different from slapd.user_oc.conf and slapd.user_at.conf) + +sub ParseConfigurationFile { + + my $FileToParse = shift; + my $NumLine = 0; + my $PARSE_OBJECTCLASSES = 0 ; # 1 if there are objectclass definitions in the file + printTrace("\nParseConfigurationFile: $FileToParse",4) ; + printTrace("\nParse $FileToParse",2); + # read each line of the configuration file + my $CONFIGFILE = "CONFIGFILE.$FileToParse" ; + open( $CONFIGFILE, $FileToParse ) || die "Can't open $FileToParsec: $!: "; + LINE: while ( <$CONFIGFILE> ) { + $NumLine++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } elsif (/^suffix\s+/i) { + chomp($_) ; + CheckSuffix($_) ; + } elsif (/^plugin/i) { + chomp($_); + if (! &isAStandardPlugin($_)) { + push @badPlugins, $_; + } + else { + my $Plugin = $_ ; + if (! &ParsePlugin($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of the plugin:\n$Plugin"); + } + } + } elsif (/^index/i) { + chomp($_); + if (! &isAStandardIndex($_)) { + my $Index = $_ ; + if (! &ParseIndex($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of index:\n$Index"); + } + } + } elsif (/^include\s+[\"]?(.*?)[\"]?\s*$/i) { + # strip leading and trailing " + my $include_file = $1 ; + grep { s@/@\\@g } $include_file if $isNT; + if (! &isAStandardInclude($include_file)) { + &ParseConfigurationFile($include_file); + } + } elsif (/^attribute\s+\S+/i) { + chomp($_); + my $Attrib = $_ ; + if (! &ParseAttribute($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of attribute:\n$Attrib"); + } + } elsif (/^objectclass\s+(\S+)\s*$/i) { + # At least one objectclass is present in the file + $PARSE_OBJECTCLASSES = 1; + } elsif (/^\s*(\S+)\s+[\"]?(.*?)[\"]?\s*$/) { + # Parse parameters and record the associated value in %Oldhash + &ParseParameters($1,$2,$NumLine); + } + } + close($CONFIGFILE); + ParseObjectClassesFile($FileToParse) if ($PARSE_OBJECTCLASSES); # parse objectclass definition + +} + +############################################################################# +# Parse the file specified in the userat attribute + +sub ParseAttributesFile { + my $userat_file=shift ; + my $NumLine = 0; + printTrace("\nParseAttributesFile: $userat_file",4); + printTrace("\nParse user defined attributes file: $userat_file",2); + # read each line of the configuration file + open( ATTRFILE, $userat_file ) || die "Can't open $FileToParsec: $!: "; + LINE: while ( <ATTRFILE> ) { + $NumLine++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } elsif (/^attribute\s+\S+/i) { + chomp($_); + my $Attrib = $_ ; + if (! &ParseAttribute($_, $NumLine)) { + printMsg("\nLine $NumLine, syntax error of attribute:\n$Attrib"); + } + } + } + close(ATTRFILE); +} + +############################################################################# +# Parse the file specified in the useroc token + +sub ParseObjectClassesFile { + my $useroc_file = shift ; + my %ObjectClass = ( + 'ObjectName' => " ", + 'Object_oid' => " ", + 'Object_superior' => "top", + 'Object_requires' => " ", + 'Object_allows' => " " + ); + + my $state = 0; + my $ErrorMsg = "Syntax error of an object class definition.\nline parsed:"; + my $LineNb = 0 ; # Number of the current line parsed in the file + printTrace("ParseObjectClassesFile: $useroc_file\n",4) ; + # read each line of the configuration file + open( OBJCLASSFILE, $useroc_file ) || die "Can't open $FileToParsec: $!: "; + printTrace("Begin the parsing of the file: $useroc_file",4); + LINE: while ( <OBJCLASSFILE> ) { + printTrace("Current Line: $_",4); + $LineNb++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } + SWITCH: { + if ($state == 0) { resetObjectClass(\%ObjectClass); + if (/^objectclass\s+(\S+)\s*$/i) { + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + else {} # printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 1) {if (/^\s+oid\s+(\S+)\s*$/i) { + $ObjectClass{'Object_oid'} = $1; + $state = 2 ;} + elsif (/^\s+superior\s+(\S+)\s*$/i) { + $ObjectClass{'Object_superior'} = $1; + $state = 3 ; + } + elsif (/^\s+requires\s*$/i) { + $state = 4; + } + elsif (/^\s+allows\s*$/i) { + $state = 5; + } + else {$state=0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 2) {if (/^\s+superior\s+(\S+)\s*$/i) { + $ObjectClass{'Object_superior'} = $1; + $state = 3 ;} + elsif (/^\s+requires\s*$/i) { + $state = 4; + } + elsif (/^\s+allows\s*$/i) { + $state = 5; + } + else { $state=0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 3) {if (/^\s+requires\s*$/i) + { $state = 4; } + elsif (/^objectclass\s+(\S+)\s*$/i) { + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + elsif (/^\s+allows\s*$/i) + { $state = 5; } + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 4) {if (/^\s+([^,\s]+),\s*$/i) { + $ObjectClass{'Object_requires'}.=$1." \$ "; } + elsif (/^\s+([^,\s]+)\s*$/i) { + $ObjectClass{'Object_requires'}.=$1." "; + $state = 6; } + else {$state = 0;printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 5) {if (/^\s+([^,\s]+),\s*$/i) { + $ObjectClass{'Object_allows'}.=$1." \$ "; } + elsif (/^\s+([^,\s]+)\s*$/i) { + $ObjectClass{'Object_allows'}.=$1." "; + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $state = 0; } + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 6) {if (/^objectclass\s+(\S+)\s*$/i) { + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + elsif (/^\s+allows\s*$/i) { + $state = 5;} + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + } + } + close(OBJCLASSFILE); + if (($state == 3) || ($state == 4) || ($state == 5) || ($state == 6)) { + &AddObjectClass(\%ObjectClass); + } + printTrace("state: $state",4); +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg ; + } +} + +############################################################################# +# reset an objectclass structure + +sub resetObjectClass { + my $ObjectClass = shift; + $ObjectClass->{'ObjectName'} = " " ; + $ObjectClass->{'Object_oid'} = " " ; + $ObjectClass->{'Object_superior'} = "top" ; + $ObjectClass->{'Object_requires'} = " " ; + $ObjectClass->{'Object_allows'} = " " ; +} + +############################################################################# +# this subroutine implements a very stupid version of diff + +sub diff { + my $f1 = shift; + my $f2 = shift; + my $lineToBeginWith = shift; + my $NULL = "" ; + my $diff_f1 = $NULL ; + my $diff_f2 = $NULL ; + my $retval = $NULL ; + my $ret; + open(F1, "$f1") or die "Could not open file $f1"; + open(F2, "$f2") or close(F1), die "Could not open file $f2"; + + while (defined($l1 = <F1>)) { + if ($lineToBeginWith){ + $lineToBeginWith -- ; + next ; + } + next if ($l1 =~ /^\#/); + $ret = defined($l2 = <F2>); + if ($ret) { + $ret = defined($l2 = <F2>) while ($ret && ($l2 =~ /^\#/)) ; + if ($ret) { + if (!($l1 eq $l2)) { + + # ignore whitespace + $l1_clean = $l1 ; + $l2_clean = $l2 ; + $l1_clean =~ s/\s//g; + $l2_clean =~ s/\s//g; + + if (!($l1_clean eq $l2_clean)) { + $diff_f1 .= "${l1}" unless ($l1_clean eq $NULL); + $diff_f2 .= "${l2}" unless ($l2_clean eq $NULL); + } + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + + while (defined($l2 = <F2>)) { + if (($l2 =~ /^\#/) || ($l2 =~ /^\s*$/)) { + next ; + } + else { + $diff_f2 .= "${l2}" ; + } + } + + close(F1); + close(F2); + + $retval .= "- differences present in your config file but not in standard file:\n\n". "$diff_f1\n" if ($diff_f1) ; + $retval .= "- differences present in standard file but not in your config file:\n\n" . "$diff_f2" if ($diff_f2) ; + return $retval ; +} + +sub CompareStdConfigFiles { + # Compare each configuration file against its default version. If it has changed, + # notify the user that the file has changed and will need to be checked by the + # user. This should be safe to do because there should be no path information + # stored in these conf files, which are just schema stuff. + # printTrace("\nCheck if standard configuration files have changed",3); + + my $origFilePath = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}install${PATHSEP}config${PATHSEP}" ; + my $FilesChanged = ""; + my $AllDiffs = "***********************************************************************"; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + printTrace("\nVersion of the old directory server: $oldVersion.$oldMinor",0); + opendir(CONFDIR, $oldConfDir) or + die "Error: could not open migrated config dir $oldConfDir: $!"; + + foreach $file (readdir(CONFDIR)) { + $origFile = $origFilePath . $file ; + $configFile = $oldConfDir . $file ; + if ((! exists($userDefinedConfigFiles{lc($file)})) && (-f $origFile)) { + my $lineToBegin = 1 if (lc($file) eq "slapd-collations.conf"); # we ignore the first line of slapd-collations + $diffs = &diff($configFile, $origFile, $lineToBegin); + $lineToBegin = 0 if $lineToBegin ; + if ($diffs) { + $FilesChanged .= "\n$configFile"; + $AllDiffs .= "\n$configFile is different than the standard configuration file" ; + $AllDiffs .= "\nYou will need to check this file and make sure its changes are compatible "; + $AllDiffs .= "with the new directory server\nHere are the differences:\n"; + $AllDiffs .= "$diffs \n\n"; + $AllDiffs .= "***********************************************************************"; + } + else { + $NoChanges .= "\n$configFile"; + } + } + } + closedir(CONFDIR); + +if ($FilesChanged) { + printTrace("\nNo changes to old configuration files:$NoChanges",3) ; + printTrace("\n***********************************************************************",3) ; + printMsg("\nThe following standard files have been modified: $FilesChanged"); + if ($NO_INPUT_USER) { + # do nothing + } + else { + printMsg("\nDo you want to see the differences Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("$AllDiffs"); + } + printMsg("\nDo you want to continue the migration Yes/No [No] ?"); + $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + } + } +} + + +############################################################################# + +sub db2ldif { + my ($conf, $ldif_dir) = @_; + $ENV{"$LIB_PATH"}=$old_libpath; + if (!$conf) { + $conf = "$oldHome${PATHSEP}config${PATHSEP}slapd.conf"; + } + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "can't create $ldif_rep to store temporary ldif files"; + } + my $dir = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + chdir($dir) or + die "Error: could not change directory to $dir: $!"; + my @suffixnames = keys(%DBNAMES) ; + foreach $suffixname (@suffixnames) { + my $ldif_file = $ldif_dir.$DBNAMES{$suffixname}.".ldif" ; + # If we are on NT, ${quote} is setup to "\"", else it's setup to "" + # As the suffix can contain some space characters, I write the suffix parameter: "\"$suffixname\"" rather than "${quote}$suffixname${quote}" + my @cmd = + ( "${quote}$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server" . + "${PATHSEP}$slapdExecName${quote}", "db2ldif", '-n', '-f', + "${quote}$conf${quote}", '-a', "${quote}$ldif_file${quote}", + '-d', '1','-s',"\"$suffixname\"" ); + open(DB2LDIF, "${quote}@cmd${quote} 2>&1|") or + die "Error: could not execute @cmd: $!"; + sleep(1); # allow pipe to fill with data + $ii = 0; # counter + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the new slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif_file) { + chown( $newuid, $newgid, $ldif_file) or printMsg("\nUnable to change the ownership of $ldif_file to $localuser") ; + } + } + } + print " Done.\n"; + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + +############################################################################# +# This db2ldif is used to export database of the new instance + +sub newinst_db2ldif { + my $ldif = shift ; + my $include_suffix = shift ; + my $home = shift ; + my $db2ldif_param = "db2ldif -r -D $home -a $ldif -s \"$include_suffix\""; + + open(DB2LDIF, "${quote}${quote}$slapdExecName${quote} $db2ldif_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + my $ii = 0; + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + printMsg($_); + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the 5.x slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif) { + chown( $newuid, $newgid, $ldif) or printMsg("\nUnable to change the ownership of $ldif to $localuser") ; + } + } +} + +############################################################################# + +# this is used to run the system() call, capture exit and signal codes, +# and die() upon badness; the first argument is a directory to change +# dir to, if any, and the rest are passed to system() +sub mySystem { + my $rc = &mySystemNoDie(@_); + my ($dir, @args) = @_; + if ($rc == 0) { +# success + } elsif ($rc == 0xff00) { + die "Error executing @args: error code $rc: $!"; + } elsif ($rc > 0x80) { + $rc >>= 8; + die "Error executing @args: error code $rc: $!"; + } else { + if ($rc & 0x80) { + $rc &= ~0x80; + } + die "Error executing @args: received signal $rc: $!"; + } + + # usually won't get return value + return $rc; +} + +# This version does not die but just returns the error code +sub mySystemNoDie { + my ($dir, @args) = @_; + if ($dir && ($dir ne "")) { + chdir($dir) or die "Could not change directory to $dir: $!"; + } + my $cmd = $args[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = 0xffff & system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd @fixargs\n"; + $rc = 0xffff & system {$cmd} @fixargs; + } + chdir(${curdir}) or die "Could not change directory to $curdir: $!"; + return $rc; +} + +############################################################################# +sub manyLdif2db { + my %rev_dbnames = reverse(%DBNAMES); + @backends = keys(%rev_dbnames); + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($slapdExecDir) or die "Could not change directory to $slapdExecDir: $!"; + foreach $backend (@backends) { + my $ldif = "${ldif_rep}$backend.ldif" ; + if (! -f $ldif) { + $ldif = ${ldif_rep}."data.ldif"; + } + &Ldif2db($ldif, $backend); + } + # remove the empty ldif directory + # but not if using the data dir + if (!$olddatadir) { + rmdir($ldif_rep); + } + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + + +sub Ldif2db { + my $ldif = shift ; + my $backend = shift ; + my $ldif2db_param = "ldif2db -D $serverHome -n $backend -i $ldif"; + open(LDIF2DB, "${quote}${quote}$slapdExecName${quote} $ldif2db_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + while (<LDIF2DB>) { + printMsg($_); + } + close(LDIF2DB); + # remove the ldif file after the import + # but not if using the data dir + if (!$olddatadir) { + unlink($ldif) ; + } +} + +############################################################################# + +#sub copyBak { +# opendir( OLDBAK, "$oldHome${PATHSEP}bak" ) || +# die "Can't open directory $oldHome${PATHSEP}bak: $!: "; +# local ( @dirs ) = readdir( OLDBAK ); +# closedir ( OLDBAK ); +# for ( @dirs ) { +# if ( $_ eq "." || $_ eq ".." ) { +# next; +# } elsif ( -d "$oldHome${PATHSEP}bak${PATHSEP}$_" ) { +# $srcDir = "$oldHome${PATHSEP}bak${PATHSEP}$_"; +# $destDir = "$serverHome${PATHSEP}bak${PATHSEP}$_"; +# $srcLDIF = "$oldHome${PATHSEP}ldif${PATHSEP}bak.ldif"; +# $destLDIF = "$serverHome${PATHSEP}ldif${PATHSEP}bak.ldif"; +# mkdir( $destDir , 0755 ) if !( -e $destDir); +# # Converting database +# if ( !$isNT && $newuser ) { +# chown($newuid, $newgid, +# "$serverHome${PATHSEP}bak", $destDir); +# } +# &other_db2ldif($srcDir, $srcLDIF); +# if ($needAclUpg) { +# &mySystem("$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server", +# "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server" . +# "${PATHSEP}aclupg$exe_suffix", '-d', '-i', +# $srcLDIF, '-o', $destLDIF); +# } else { +# ©BinFile($srcLDIF, $destLDIF); +# } +# &other_ldif2db($destLDIF, $destDir); +# } +# } +#} +############################################################################# + +sub startServer { + my $instanceDir = ${serverHome} ; + my $errLog = $instanceDir . $PATHSEP . 'logs' . $PATHSEP . 'errors'; + # emulate tail -f + # if the last line we see does not contain "slapd started", try again + my $done = 0; + my $started = 0; + my $code = 0; + my $lastLine = ""; + my $timeout = time + 240; # 4 minutes + $ENV{"$LIB_PATH"}=$new_libpath; + + my $startCmd = $instanceDir . $PATHSEP . 'start' . $script_suffix; + if (! -f $startCmd) { + $startCmd = $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix; + } + printTrace("\nInstanceDir: $instanceDir\n",4); + $code = &mySystem($instanceDir,$startCmd); + open(IN, $errLog) or die "Could not open error log $errLog: $!"; + my $pos = tell(IN); + while (($done == 0) && (time < $timeout)) { + for (; ($done == 0) && ($_ = <IN>); $pos = tell(IN)) { + $lastLine = $_; + # print; + # the server has already been started and shutdown once . . . + if (/slapd started\./) { + $started++; + if ($started == 2) { + $done = 1; + } + # sometimes the server will fail to come up; in that case, restart it + } elsif (/Initialization Failed/) { + # print "Server failed to start: $_"; + $code = &mySystem($instanceDir, $startCmd); + # sometimes the server will fail to come up; in that case, restart it + } elsif (/exiting\./) { + # print "Server failed to start: $_"; + #$code = &mySystem($startCmd); + + $code = &mySystem($instanceDir, $startCmd); + } + } + if ($lastLine =~ /PR_Bind/) { + # server port conflicts with another one, just report and punt + print $lastLine; + print "This server cannot be started until the other server on this\n"; + print "port is shutdown.\n"; + $done = 1; + } + if ($done == 0) { + # rest a bit, then . . . + sleep(2); + # . . . reset the EOF status of the file desc + seek(IN, $pos, 0); + } + } + close(IN); + + if ($started < 2) { + $! = $code; + # $now = time; + # if ($now > $timeout) { + # print "Possible timeout: timeout=$timeout now=$now\n"; + # } + die "Error: could not start server: $!"; + } + + return 0; +} + +sub stopServer { + my $root = shift; + my $name = shift; + $maxStopIterations = 5; + print "\nShutting down server $name . . .\n"; + + $ENV{"$LIB_PATH"}=$new_libpath; + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote; + if (! -f $stopCmd) { + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote; + } + + if (! -f $stopCmd) { + # no stop command, probably a 1.X system; for NT, we'll try net stop + # for unix, we'll get the pid and kill it + if ($isNT) { + $stopCmd = 'net stop ' . $name; + } else { + # see if there is a pid file + $pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' . + $PATHSEP . 'pid'; + if (open(PIDFILE, $pidfile)) { + chomp($pid = <PIDFILE>); + close(PIDFILE); + while ($maxStopIterations-- && !$exitCode) { + $exitCode = kill(15, $pid); + } + $stopCmd = undef; + } + } + } + + # keep looping until the stop cmd returns an error code, which usually + # means that what ever we want to stop is stopped, or some other error + # occurred e.g. permission, or no such service + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + while ($stopCmd && $maxStopIterations-- && $exitCode) { + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + } + + if (!$maxStopIterations) { + print "Warning: could not shutdown the server: $!\n"; + } + + sleep(10) ; + + $exitCode = 0; + +} + + +sub runAndIgnoreOutput { + my $cmd = shift; + printMsg("."); + open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!"; + printMsg("."); + sleep(1); # allow pipe to fill with data + printMsg("."); + while (<RUNCMD>) { +# print; + } + my $code = close(RUNCMD); +# print "runAndIgnore: code=$code status=$?\n"; + return $?; +} +############################################################################# +# migrate some of entries present in the old DSE.ldif like +# cn=snmp,cn=config +# cn=encryption,cn=config +# all the aci's + +sub MigrateDSE { + printTrace("\nMigrate DSE entries...",1); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($old_entry = readOneEntry $in) { + my $DN = $old_entry->getDN() ; + SWITCH: { + # migrate the entrie: cn=snmp,cn=config + if ($DN =~ /^cn=SNMP,cn=config$/i) { + my $entry = $conn->search("$DN","base","objectclass=nsSNMP"); + if ($entry) { + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + printMsg("\nUnable to get info under $DN"); + } + last SWITCH; + } + # migrate the entrie: cn=encryption,cn=config + if ($DN =~ /cn=encryption,cn=config$/i) { + if ($conn->search("$DN","base","objectclass=*")) { + if ($old_entry->hasValue("objectClass", "nsEncryptionConfig")) { + my $certfile = "alias/slapd-" . $newname . "-cert8.db"; + my $keyfile = "alias/slapd-" . $newname. "-key3.db"; + $old_entry->setValues("nsCertfile",$certfile) if ! $old_entry->hasValue("nsCertfile",$certfile); + $old_entry->setValues("nsKeyfile",$keyfile) if ! $old_entry->hasValue("nsKeyfile",$keyfile); + } + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + my $res = $conn->add($old_entry); + if ($res) { + printTrace("\n$DN added !",2); + } + else { + printMsg("\nFailed to add $DN"); + } + } + last SWITCH; + } + if (@{$old_entry->{aci}} && (! ($DN =~ /^cn=monitor$/i)) && (! ($DN =~ /^cn=schema$/i))) { + # migrate aci's + my $entry = $conn->search("$DN","base","objectclass=*"); + if ($entry) { + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + my $res = $conn->add($old_entry); + if ($res) { + printTrace("\n$DN added !",2); + } + else { + printMsg("\nFailed to add $DN"); + } + } + last SWITCH; + } + } + } + close(DSELDIF); +} +############################################################################# +# migrate SSL info + +sub MigrateSSL { + my $secPwd = 'bidon' ; + # copy the SSL directory + ©Dir("$oldHome${PATHSEP}ssl","$serverHome${PATHSEP}ssl"); + # copy the cert db and key files + if ( -d "$oldDir${PATHSEP}alias") { + $aliasDir = "$root${PATHSEP}alias"; + if (! -d $aliasDir) { + mkdir($aliasDir, 0750); + } + &stopServer($root,'slapd-'.$newname); + my $keydb = "$aliasDir${PATHSEP}slapd-$newname-key3.db" ; + my $certdb = "$aliasDir${PATHSEP}slapd-$newname-cert8.db" ; + my $certdb7 = "$aliasDir${PATHSEP}slapd-$newname-cert7.db" ; + my $old_keydb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ; + my $old_certdb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db"; + my $keydb_backup = "$aliasDir${PATHSEP}slapd-$newname-key3.db_backup" ; + my $certdb_backup = "$aliasDir${PATHSEP}slapd-$newname-cert7.db_backup" ; + if (-f $old_keydb) { + if (-f $keydb) { + if ($NO_INPUT_USER) { + printMsg("\n$keydb already exists. backup in $keydb_backup ..."); + ©BinFile($keydb,$keydb_backup); + ©BinFile($old_keydb,$keydb); + } + else { + print("\n\n$keydb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_keydb,$keydb); + } + } + } + else { + ©BinFile($old_keydb,$keydb); + } + } + if (-f $old_certdb) { + $mode = (stat($old_certdb))[2] if $PRESERVE; + if (-f $certdb) { + if ($NO_INPUT_USER) { + printMsg("\n$certdb already exists. backup in $certdb_backup ..."); + ©BinFile($certdb,$certdb_backup); + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + else { + print("\n\n$certdb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + } + } + else { + ©BinFile($old_certdb,$certdb7); + } + } + # copy the old password file + if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") { + ©BinFile( + "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt", + "$aliasDir${PATHSEP}$type-$newname-pin.txt" + ); + } + &startServer(); + if ($PRESERVE) { + chown($newuid,$newgid,$certdb) || print "Failed to set uid $newuid gid $newgid on $certdb : $!\n"; + chmod($mode,$certdb) || print "Failed to set mode $mode on $certdb : $!\n"; + } + } + +} + +sub DisableSSL { + my $entry = $conn->search("cn=config","base","objectclass=*"); + my $LDAPparam = "nsslapd-security" ; + my $Value = "off" ; + if ($entry->{$LDAPparam}[0] ne $Value) { + printTrace("\nDisable SSL...",1); + $entry->setValues($LDAPparam, $Value); + } + my $res = $conn->update($entry); + if ($res) { + printTrace("\nSSL disabled",2); + } + else { + printMsg("\nCan't disable SSL, the server may have problems starting"); + } +} + +# enable the migration of client authentication informations +sub MigrateCertmap { + # backup the old new certmap.conf and replace it with the old certmap.conf file + my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"; + my $newCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf" ; + my $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + if (&hasChangedoldCertmap($oldCertmap)) { + if ($NO_INPUT_USER) { + printMsg("\n$newCertmap has been backup in $backupCertmap"); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up the file $newCertmap [$backupCertmap] ?") ; + my $Answer = <STDIN> ; + $backupCertmap = $Answer if ($Answer ne "\n"); + chomp($backupCertmap); + printTrace("\nDest: .$backupCertmap.",4); + if (-e $backupCertmap) { + printMsg("\n\n$backupCertmap already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup file: $newCertmap in $backupCertmap",4); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + } + else { + } +} + +sub hasChangedoldCertmap { + my $certmapfile = shift ; + my @reference = ("certmap default default", + "default:DNComps", + "default:FilterComps e") ; + my $cpt = 0 ; + printTrace("\nhasChangedoldCertmap",3); + open(CERTMAP,"< $certmapfile"); + while (<CERTMAP>) { + if ((! /^\s*#/) && (! /^\s*$/)) { + my $ref = $reference[$cpt] ; + printTrace("\nValue: $_, ref: $ref",4); + if (! /^\s*$ref\s*$/) { + return 1 ; + } + else { + $cpt++ ; + } + } + } + close (CERTMAP); + printTrace("\ncpt: $cpt",4); + if ($cpt < $#reference) { + return 1 ; + } + else { + return 0 ; + } +} +############################################################################# +# copy a directory to another + +sub copyDir { + my $src = shift; + my $dest = shift; + my $exclude = shift; + + opendir( SRC, $src ) or die "Can't open directory $src: $!: "; + my $mode; + my $uid; + my $gid; + mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest ); + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + local ( @files ) = readdir ( SRC ); + closedir( SRC ); + for ( @files ) { + if ( $_ eq "." || $_ eq ".." ) { + next; + } elsif ( $exclude && /$exclude/ ) { + next; + } elsif( -d "$src${PATHSEP}$_") { + ©Dir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); + } else { + ©BinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_"); + } + } +} + +sub copyBinFile { + my $src = shift; + my $dest = shift; + my $buf = ""; + my $bufsize = 8192; + + open( SRC, $src ) || die "Can't open $src: $!\n"; + # if we are given a directory destination instead of a file, extract the + # filename portion of the source to use as the destination filename + if (-d $dest) { + $dest = $dest . $PATHSEP . &basename($src); + } + open( DEST, ">$dest" ) || die "Can't create $dest: $!\n"; + binmode SRC; + binmode DEST; + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + while (read(SRC, $buf, $bufsize)) { + print DEST $buf; + } + close( SRC ); + close( DEST ); +} +############################################################################# +# backup new configuration files +# backup the directory <new_root_server>/slapd-instance/config in <new_root_server>/slapd-instance/BackupConfig + +sub backupConfigFiles { + # backup the new config files + my $src = "$serverHome${PATHSEP}config" ; + my $dest = "$serverHome${PATHSEP}config_backup" ; + if ($NO_INPUT_USER) { + printMsg("\n$src has been backup in $dest"); + ©Dir($src,$dest); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up your configuration directory [$dest] ?") ; + my $Answer = <STDIN> ; + $dest = $Answer if ($Answer ne "\n"); + chomp($dest); + printTrace("\nDest: .$dest.",4); + if (-e $dest) { + printMsg("\n\n$dest already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $dest = "$serverHome${PATHSEP}config_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup Directory: $src in $dest",4); + ©Dir($src,$dest); + } +} +############################################################################# + +sub getLDAPservername { + my $oldLDAPservername; + my $LDAPservername; + open(OLDSLAPDCONF, $oldSlapdConf) or + die "\nError: could not open old config file $oldSlapdConf \n"; + while(<OLDSLAPDCONF>) { + chop; + if (/^localhost\s+/i) { + ($oldLDAPservername = $') =~ s/^[\"]//;; + $oldLDAPservername =~ s/[\"]$//; + printTrace("\nName of the old LDAP server: $oldLDAPservername",3); + } + } + close(OLDSLAPDCONF); + + open( DSELDIF, "< $DSEldif" ) || die "\nCan't open $DSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN() ; + if ($DN =~ /^cn=config$/i) { + my $localhost = "nsslapd-localhost"; + my @values = $entry->getValues($localhost); + if ($#values != -1) { + $LDAPservername = $values[0]; + } + break; + } + } + close(DSELDIF); + # check old and new are installed on the same physical machine. + if (lc($oldLDAPservername) ne lc($LDAPservername)) { + # warn the user he tries to migrate a old server installed on a different machine from the new one + printMsg("\n\nYour old server is on $oldLDAPservername, and your new server is on $LDAPservername. We don't support migration on different machines. Do you want to continue ? Yes/No [No]:") ; + if (! (<STDIN> =~ /yes|y/i)) { + return -1; + } + } + return $LDAPservername ; +} + +############################################################################# + +sub getLibPath { + my $myDir = shift; + my $myVersion = shift; + my $myMinor = shift; + + if ($isNT) { + return $ENV{"$LIB_PATH"}; + } + if (($myVersion >= 6) && ($myMinor >= 2)) { + return + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + } else { + return "$myDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + } +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $versionstr = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + if (!$versionstr) { # version not specified on cmd line - find it + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $cur_libpath=$ENV{"$LIB_PATH"}; + $ENV{"$LIB_PATH"}= + "$dir${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory/ || /^iPlanet-Directory/i) { + $versionstr = $_; + last; + } + } + $code = close(F); + # print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}=$cur_libpath; + } + + if ($versionstr =~ /^Netscape-Directory\/(\d+)\.(\d+)(?:b\d)*\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } elsif ($versionstr =~ /(\d+)\.(\d+)/) { + $version = $1; + $minor = $2; + } + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################# + +sub getDiff { + # we get references to arrays + my $elements = shift ; + my $existing_elements = shift ; + my %count = () ; + my %countEE = () ; + @diff = () ; + foreach $e (@{$elements}, @{$existing_elements}) { $count{$e}++ ;} + foreach $e (@{existing_elements}) { $countEE{$e}++ ;} + foreach $e (@{$elements}) { + # if $e is only present in @$elements, we push it to the diff array + if (($count{$e} == 1) && ($countEE{$e} == 0)) { + push @diff, $e ; + } + } + return @diff ; +} + +############################################################################################### +sub testIndexUpdating { + #my $entry = $conn->newEntry(); + #$entry->setDN("cn=djeattribute,cn=index,cn=MigratedDB_5,cn=ldbm database,cn=plugins,cn=config"); + my $entry = $conn->search("cn=mail,cn=index,cn=MigratedDB_2,cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + my @types = ("pres", "sub", "eq") ; + my @existing_types = $entry->getValues("nsindextype"); + my @typesToAdd = &getDiff(\@types, \@existing_types); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + my $res = $conn->update($entry) ; + if ($res) {print("\nUpdate index mail\n");} + else { print("\ncan't update index mail");} + + $entry = $conn->search("cn=givenName,cn=index,cn=MigratedDB_2,cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + @types = ("pres", "sub", "eq") ; + @existing_types = $entry->getValues("nsindextype"); print("\ngivenName, existing_types: @existing_types"); + @typesToAdd = &getDiff(\@types, \@existing_types); print("\nTypesToAdd: @typesToAdd"); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + my $res = $conn->update($entry) ; + if ($res) {print("\nUpdate index givenName\n");} + else { print("\ncan't update index givenName");} + } + + +############################################################################################### +sub normalizeDir { + my $dir = shift ; + my $dir_prec = "" ; + while ($dir_prec ne $dir) { + $dir_prec = $dir ; + if ($isNT) { + grep { s@\\\\@\\@g } $dir ; + } + else { + grep { s@//@/@g } $dir ; + } + } + return $dir ; +} + + +############################################################################################### +# return 1 if the value parameters is +sub isAllowedPlugin { + my $Value = lc(shift) ; + if ($allowedPlugins{$Value}) { + return 1 ; + } + else { + return 0 ; + } + +} + + +sub getSyntaxOid { + my $Value = lc(shift) ; + return $allowedPlugins{$Value} ; +} + +############################################################################################### +# return 1 if the value given in parameters is an allowed modifier +sub isAllowedModifier { + my $Value = lc(shift) ; + if ($allowedModifiers{$Value}) { + return 1 ; + } + else { + return 0 ; + } +} + +sub getModifierValue { + my $Value = lc(shift) ; + return $allowedModifiers{$Value} ; +} + +############################################################################################### + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################################### +# get uid and group id of the new slapd server. +# The uid is done through the nsslapd-localuser attribute + +sub getuid_gid { + my $newuid ; + my $newgid ; + my $localuser ; + my $localuser_attr = "nsslapd-localuser" ; + if (! $isNT) { + my $entry = $conn->search("cn=config ", "base","objectclass=*", 0, ($localuser_attr)) ; + # Tests wether we succeed to get the entry cn=config + die "\nCan't get the entry cn=config \n" unless ($entry); + my @values = $entry->getValues($localuser_attr); + if ($#values == -1 || ($values[0] eq "") ) { # tests wether the nsslapd-localuser attribute has a value + printMsg("\nNo localuser has been found in the configuration of the directory. "); + if ($NO_INPUT_USER) { + printMsg("\nWe considered nobody as the localuser"); + $localuser = "nobody" ; + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\nUnder what user does your $Version.$Minor directory server run [nobody] ? ") ; + $localuser = <STDIN> ; + chomp($localuser); + $localuser = "nobody" if ($localuser eq ""); + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + if ($newuid) { + $Ask = 0 ; + } + else { + printMsg("\nError: $localuser is unknown from the system "); + } + } + } + } + else { + $localuser = $values[0]; # returns the first value (we should only have one localuser) + my $size = $#values ; + } + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + return ($localuser, $newuid, $newgid) ; + } + else { + return () ; + } +} + + +############################################################################################### +# get uid and group id of the old slapd server. + +sub getolduid_gid { + my $oldlocaluser ; + if (! $isNT) { + open(CONF, $oldSlapdConf) or die "\nError: cannot open $oldSlapdConf: $!\n"; + while (<CONF>) { + if (/^localuser\s+/i) { + chomp($oldlocaluser = $'); + last; + } + } + close(CONF); + ($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ; + return ($oldlocaluser, $olduid, $oldgid) ; + } + else { + return (); + } +} + +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} diff --git a/ldap/admin/src/scripts/template-migrateTo7 b/ldap/admin/src/scripts/template-migrateTo7 new file mode 100644 index 00000000..73f71ab9 --- /dev/null +++ b/ldap/admin/src/scripts/template-migrateTo7 @@ -0,0 +1,3268 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Migrate a old directory server to a 7.0 directory server + +######################################################################################################## +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Entry; +use Mozilla::LDAP::LDIF; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::localtime; + +######################################################################################################## +use Class::Struct ; # load struct-building module + +struct S_index => { + names => '@' , + types => '@' , + oids => '@' , + specific => '$' + }; + + +struct S_plugin => { + name => '$' , + type => '$' , + enable => '$' , + args => '@' + }; +##################################################################################################### + +sub usage { + print(STDERR "\nUsage: $0 -D rootdn { -w password | -w - | -j filename } -p port \n"); + print(STDERR " -o OldInstancePath -n NewInstancePath [-t tracelevel] [-L logfile]\n"); + print(STDERR "************** parameters in brackets are optionals, others are required **************\n"); + print(STDERR " Opts: -D rootdn - New Directory Manager\n"); + print(STDERR " : -w password - New Directory Manager's password\n"); + print(STDERR " : -w - - Prompt for New Directory Manager's password\n"); + print(STDERR " : -j filename - Read New Directory Manager's password from file\n"); + print(STDERR " : -p port - New Directory Server port\n"); + print(STDERR " : -o OldInstancePath - Path of the Old instance to migrate \n"); + print(STDERR " : -n NewInstancePath - Path of the new instance\n"); + print(STDERR " : [-d dataPath] - Path to directory containing data files to import into new instance\n"); + print(STDERR " : [-v oldVersion] - Old version (obtained by running $slapdExecName -v\n"); + print(STDERR " : [-t tracelevel] - specify the level of trace (0..3)\n"); + print(STDERR " : [-L logfile] - specify the file to log the migration report \n"); + + + } + + + +############# +BEGIN { + + require 'uname.lib' ; + $isNT = -d '\\'; + $PATHSEP = $isNT ? "\\" : "/"; + ${SEP} = $isNT ? ";" : ":" ; + @INC = ( '.', '../../../admin/admin/bin'); + grep { s@/@\\@g } @INC if $isNT; + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + # NT needs quotes around some things unix doesn't + $quote = $isNT ? "\"" : ""; + + # If this variable is set, all file/directory creation will make sure the mode + # and ownership of the destination is the same as the source + $PRESERVE = 1 if (!$isNT); + $script_suffix = $isNT ? ".bat" : ""; + $exe_suffix = $isNT ? ".exe" : ""; + if ($isNT) { + $os = "WINNT"; + } else { + $os = &uname("-s"); + } + if ($isNT) { + # we have to pass batch files directly to the NT command interpreter + $com_spec = $ENV{ComSpec}; + if (!$com_spec) { + $com_spec = $ENV{COMSPEC}; + } + if (!$com_spec || ! -f $com_spec) { + # find the first available command interpreter + foreach $drive (c..z) { + $com_spec = "$drive:\\winnt\\system32\\cmd.exe"; + last if (-f $com_spec); + $com_spec = undef; + } + if (! $com_spec) { + # punt and pray + $com_spec = 'c:\winnt\system32\cmd.exe'; + } + } + } + if ( $os eq "AIX" ) { + $dll_suffix = "_shr.a"; + } + elsif ( $os eq "HP-UX" ) { + $dll_suffix = ".sl"; + } + elsif ( $os eq "WINNT" ) { + $dll_suffix = ".dll"; + } + else { + $dll_suffix = ".so"; + } + $slapdExecName = $isNT ? 'slapd.exe' : './ns-slapd'; + # if this flag is set, we will migrate the old database + # by doing a db2ldif -> ldif2db; + $convertToLDIF = 1; + select STDERR; + $| = 1; + select STDOUT; + $| = 1; + # if the old value for dbcachesize is less than this, make it this + $MIN_DBCACHESIZE = '500000'; +} + +SWITCH: { + if ($os eq "AIX") { + $LIB_PATH = "LIBPATH" ; + last SWITCH ; + } + if ($os eq "HP-UX") { + $LIB_PATH = "SHLIB_PATH" ; + last SWITCH ; + } + if ($isNT) { + $LIB_PATH = "PATH" ; + last SWITCH ; + } + else { + $LIB_PATH = "LD_LIBRARY_PATH" ; + last SWITCH ; + } + } + + # Old parameters + ${oldDir} = "" ; + ${oldname} = "" ; + ${oldHome} = "" ; + ${oldConfDir} = "" ; + ${oldlocaluser} ; + ${olduid} ; + ${oldgid} ; + + # New parameters + ${root} = "{{DS-ROOT}}" ; + ${type} = "" ; + ${newname} = "" ; + ${newport} = "" ; + ${rootDN} = "" ; + ${rootpwd} = "" ; + ${localhost} = "" ; + ${LogFileReport} = "" ; + ${newuid} ; + ${localuser} ; + ${newgid} ; + $NO_INPUT_USER = 0 ; # by default user can give inputs during the migration process + ${curdir} = getCwd(); + ${slapdExecDir} = "${root}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + # specify the level of trace + $TRACELEVEL=1; + + $LDAP_SERVER_UNREACHABLE = 81; + + # get input users + &getParameters() ; + ${oldDir} = &normalizeDir("${oldDir}"); + ${oldHome} = "${oldDir}${PATHSEP}$type-$oldname" ; + ${oldConfDir} = "${oldHome}${PATHSEP}config${PATHSEP}" ; + ${oldSlapdConf} = "${oldConfDir}slapd.conf" ; + ${oldDSEldif} = "${oldConfDir}dse.ldif" ; + ${serverHome} = "${root}${PATHSEP}$type-$newname" ; + ${DSEldif} = "$serverHome${PATHSEP}config${PATHSEP}dse.ldif"; + ${ldif_rep} = "${oldConfDir}${PATHSEP}ldif${PATHSEP}" ; + ${oldSlapdExecDir} = "${oldDir}${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + + + + + open(LOGFILE, ">> $LogFileReport"); + + printTrace("\noldDir: $oldDir, oldHome: $oldHome, \noldConfDir: $oldConfDir, \noldSlapdConf: $oldSlapdConf, \nldif_rep: $ldif_rep, \nrootDN: $rootDN, \nPwd: ******, \nPort: $newport, \nNewname: $newname\n",3); + printTrace("\nLIB_PATH: $LIB_PATH",4); + + if (!(-d $serverHome)) { + printMsg("\n$serverHome doesn't exist\n"); + exit(1); + } + if (!(-d $oldHome)) { + printMsg("\n$oldHome doesn't exist\n"); + exit(1); + } + + if ($olddatadir && !(-d $olddatadir)) { + print("\n$olddatadir doesn't exist\n"); + exit(1); + } + +#define CONFIG_DATABASE_DIRECTIVE "database" +#define CONFIG_DATABASE_ATTRIBUTE "nsslapd-database" +#define CONFIG_PLUGIN_DIRECTIVE "plugin" +#define CONFIG_PLUGIN_ATTRIBUTE "nsslapd-plugin" +#define CONFIG_SIZELIMIT_DIRECTIVE "sizelimit" +#define CONFIG_SIZELIMIT_ATTRIBUTE "nsslapd-sizelimit" +#define CONFIG_ORCAUTO_DIRECTIVE "orcauto" +#define CONFIG_ORCAUTO_ATTRIBUTE "nsslapd-orcauto" +#define CONFIG_TIMELIMIT_DIRECTIVE "timelimit" +#define CONFIG_TIMELIMIT_ATTRIBUTE "nsslapd-timelimit" +#define CONFIG_SUFFIX_DIRECTIVE "suffix" +#define CONFIG_SUFFIX_ATTRIBUTE "nsslapd-suffix" +#define CONFIG_READONLY_DIRECTIVE "readonly" +#define CONFIG_READONLY_ATTRIBUTE "nsslapd-readonly" +#define CONFIG_REFERRAL_DIRECTIVE "referral" +#define CONFIG_REFERRAL_ATTRIBUTE "nsslapd-referral" +#define CONFIG_OBJECTCLASS_DIRECTIVE "objectclass" +#define CONFIG_OBJECTCLASS_ATTRIBUTE "nsslapd-objectclass" +#define CONFIG_ATTRIBUTE_DIRECTIVE "attribute" +#define CONFIG_ATTRIBUTE_ATTRIBUTE "nsslapd-attribute" +#define CONFIG_SCHEMACHECK_DIRECTIVE "schemacheck" +#define CONFIG_SCHEMACHECK_ATTRIBUTE "nsslapd-schemacheck" +#define CONFIG_LOGLEVEL_DIRECTIVE "loglevel" +#define CONFIG_LOGLEVEL_ATTRIBUTE "nsslapd-errorlog-level" +#define CONFIG_ACCESSLOGLEVEL_DIRECTIVE "accessloglevel" +#define CONFIG_ACCESSLOGLEVEL_ATTRIBUTE "nsslapd-accesslog-level" +#define CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "accesslog-maxNumOfLogsPerDir" +#define CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-accesslog-maxlogsperdir" +#define CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "errorlog-maxNumOfLogsPerDir" +#define CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-errorlog-maxlogsperdir" +#define CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_DIRECTIVE "auditlog-maxNumOfLogsPerDir" +#define CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-auditlog-maxlogsperdir" +#define CONFIG_ACCESSLOG_MAXLOGSIZE_DIRECTIVE "accesslog-maxlogsize" +#define CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-accesslog-maxlogsize" +#define CONFIG_ERRORLOG_MAXLOGSIZE_DIRECTIVE "errorlog-maxlogsize" +#define CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-errorlog-maxlogsize" +#define CONFIG_AUDITLOG_MAXLOGSIZE_DIRECTIVE "auditlog-maxlogsize" +#define CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-auditlog-maxlogsize" +#define CONFIG_ACCESSLOG_LOGROTATIONTIME_DIRECTIVE "accesslog-logrotationtime" +#define CONFIG_ACCESSLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-accesslog-logrotationtime" +#define CONFIG_ERRORLOG_LOGROTATIONTIME_DIRECTIVE "errorlog-logrotationtime" +#define CONFIG_ERRORLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-errorlog-logrotationtime" +#define CONFIG_AUDITLOG_LOGROTATIONTIME_DIRECTIVE "auditlog-logrotationtime" +#define CONFIG_AUDITLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-auditlog-logrotationtime" +#define CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "accesslog-logrotationtimeunit" +#define CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logrotationtimeunit" +#define CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "errorlog-logrotationtimeunit" +#define CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logrotationtimeunit" +#define CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_DIRECTIVE "auditlog-logrotationtimeunit" +#define CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logrotationtimeunit" +#define CONFIG_ACCESSLOG_MAXLOGDISKSPACE_DIRECTIVE "accesslog-maxlogdiskspace" +#define CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logmaxdiskspace" +#define CONFIG_ERRORLOG_MAXLOGDISKSPACE_DIRECTIVE "errorlog-maxlogdiskspace" +#define CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logmaxdiskspace" +#define CONFIG_AUDITLOG_MAXLOGDISKSPACE_DIRECTIVE "auditlog-maxlogdiskspace" +#define CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logmaxdiskspace" +#define CONFIG_ACCESSLOG_MINFREEDISKSPACE_DIRECTIVE "accesslog-minfreediskspace" +#define CONFIG_ACCESSLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logminfreediskspace" +#define CONFIG_ERRORLOG_MINFREEDISKSPACE_DIRECTIVE "errorlog-minfreediskspace" +#define CONFIG_ERRORLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logminfreediskspace" +#define CONFIG_AUDITLOG_MINFREEDISKSPACE_DIRECTIVE "auditlog-minfreediskspace" +#define CONFIG_AUDITLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logminfreediskspace" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_DIRECTIVE "accesslog-logexpirationtime" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-accesslog-logexpirationtime" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIME_DIRECTIVE "errorlog-logexpirationtime" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-errorlog-logexpirationtime" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIME_DIRECTIVE "auditlog-logexpirationtime" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-auditlog-logexpirationtime" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "accesslog-logexpirationtimeunit" +#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logexpirationtimeunit" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "errorlog-logexpirationtimeunit" +#define CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logexpirationtimeunit" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_DIRECTIVE "auditlog-logexpirationtimeunit" +#define CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logexpirationtimeunit" +#define CONFIG_ACCESSLOG_LOGGING_ENABLED_DIRECTIVE "accesslog-logging-enabled" +#define CONFIG_ACCESSLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-accesslog-logging-enabled" +#define CONFIG_ERRORLOG_LOGGING_ENABLED_DIRECTIVE "errorlog-logging-enabled" +#define CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-errorlog-logging-enabled" +#define CONFIG_AUDITLOG_LOGGING_ENABLED_DIRECTIVE "auditlog-logging-enabled" +#define CONFIG_AUDITLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-auditlog-logging-enabled" +#define CONFIG_ROOTDN_DIRECTIVE "rootdn" +#define CONFIG_ROOTDN_ATTRIBUTE "nsslapd-rootdn" +#define CONFIG_ROOTPW_DIRECTIVE "rootpw" +#define CONFIG_ROOTPW_ATTRIBUTE "nsslapd-rootpw" +#define CONFIG_ROOTPWSTORAGESCHEME_DIRECTIVE "rootpwstoragescheme" +#define CONFIG_ROOTPWSTORAGESCHEME_ATTRIBUTE "nsslapd-rootpwstoragescheme" +#define CONFIG_UPDATEDN_DIRECTIVE "updatedn" +#define CONFIG_UPDATEDN_ATTRIBUTE "nsslapd-updatedn" +#define CONFIG_UPDATEPW_DIRECTIVE "updatepw" +#define CONFIG_UPDATEPW_ATTRIBUTE "nsslapd-updatepw" +#define CONFIG_UPDATESSLCLIENT_DIRECTIVE "updateSSLclient" +#define CONFIG_UPDATESSLCLIENT_ATTRIBUTE "nsslapd-updateSSLclient" +#define CONFIG_AUDITFILE_DIRECTIVE "auditfile" +#define CONFIG_AUDITFILE_ATTRIBUTE "nsslapd-auditlog" +#define CONFIG_LASTMOD_DIRECTIVE "lastmod" +#define CONFIG_LASTMOD_ATTRIBUTE "nsslapd-lastmod" +#define CONFIG_INCLUDE_DIRECTIVE "include" +#define CONFIG_INCLUDE_ATTRIBUTE "nsslapd-include" +#define CONFIG_DYNAMICCONF_DIRECTIVE "dynamicconf" +#define CONFIG_DYNAMICCONF_ATTRIBUTE "nsslapd-dynamicconf" +#define CONFIG_USEROC_DIRECTIVE "useroc" +#define CONFIG_USEROC_ATTRIBUTE "nsslapd-useroc" +#define CONFIG_USERAT_DIRECTIVE "userat" +#define CONFIG_USERAT_ATTRIBUTE "nsslapd-userat" +#define CONFIG_SVRTAB_DIRECTIVE "svrtab" +#define CONFIG_SVRTAB_ATTRIBUTE "nsslapd-svrtab" +#ifndef _WIN32 +#define CONFIG_LOCALUSER_DIRECTIVE "localuser" +#define CONFIG_LOCALUSER_ATTRIBUTE "nsslapd-localuser" +#endif /* !_WIN32 */ +#define CONFIG_LOCALHOST_DIRECTIVE "localhost" +#define CONFIG_LOCALHOST_ATTRIBUTE "nsslapd-localhost" +#define CONFIG_PORT_DIRECTIVE "port" +#define CONFIG_PORT_ATTRIBUTE "nsslapd-port" +#define CONFIG_LISTENHOST_DIRECTIVE "listenhost" +#define CONFIG_LISTENHOST_ATTRIBUTE "nsslapd-listenhost" +#define CONFIG_SECURITY_DIRECTIVE "security" +#define CONFIG_SECURITY_ATTRIBUTE "nsslapd-security" +#define CONFIG_SSL3CIPHERS_DIRECTIVE "SSL3ciphers" +#define CONFIG_SSL3CIPHERS_ATTRIBUTE "nsslapd-SSL3ciphers" +#define CONFIG_ACCESSLOG_DIRECTIVE "accesslog" +#define CONFIG_ACCESSLOG_ATTRIBUTE "nsslapd-accesslog" +#define CONFIG_ERRORLOG_DIRECTIVE "errorlog" +#define CONFIG_ERRORLOG_ATTRIBUTE "nsslapd-errorlog" +#define CONFIG_INSTANCEDIR_DIRECTIVE "instancedir" +#define CONFIG_INSTANCEDIR_ATTRIBUTE "nsslapd-instancedir" +#define CONFIG_SECUREPORT_DIRECTIVE "secure-port" +#define CONFIG_SECUREPORT_ATTRIBUTE "nsslapd-securePort" +#define CONFIG_SECURELISTENHOST_DIRECTIVE "secure-listenhost" +#define CONFIG_SECURELISTENHOST_ATTRIBUTE "nsslapd-securelistenhost" +#define CONFIG_THREADNUMBER_DIRECTIVE "threadnumber" +#define CONFIG_THREADNUMBER_ATTRIBUTE "nsslapd-threadnumber" +#define CONFIG_MAXTHREADSPERCONN_DIRECTIVE "maxthreadsperconn" +#define CONFIG_MAXTHREADSPERCONN_ATTRIBUTE "nsslapd-maxthreadsperconn" +#if !defined(_WIN32) && !defined(AIX) +#define CONFIG_MAXDESCRIPTORS_DIRECTIVE "maxdescriptors" +#define CONFIG_MAXDESCRIPTORS_ATTRIBUTE "nsslapd-maxdescriptors" +#endif /* !_WIN32 && ! AIX */ +#define CONFIG_RESERVEDESCRIPTORS_DIRECTIVE "reservedescriptors" +#define CONFIG_RESERVEDESCRIPTORS_ATTRIBUTE "nsslapd-reservedescriptors" +#define CONFIG_IDLETIMEOUT_DIRECTIVE "idletimeout" +#define CONFIG_IDLETIMEOUT_ATTRIBUTE "nsslapd-idletimeout" +#define CONFIG_IOBLOCKTIMEOUT_DIRECTIVE "ioblocktimeout" +#define CONFIG_IOBLOCKTIMEOUT_ATTRIBUTE "nsslapd-ioblocktimeout" +#define CONFIG_NTSYNCH_DIRECTIVE "ntsynch" +#define CONFIG_NTSYNCH_ATTRIBUTE "nsslapd-NTSynch" +#define CONFIG_NTSYNCHUSESSL_DIRECTIVE "ntsynchusessl" +#define CONFIG_NTSYNCHUSESSL_ATTRIBUTE "nsslapd-NTSynch-SSL" +#define CONFIG_NTSYNCHPORT_DIRECTIVE "ntsynch-port" +#define CONFIG_NTSYNCHPORT_ATTRIBUTE "nsslapd-NTSynch-port" +#define CONFIG_ACCESSCONTROL_DIRECTIVE "accesscontrol" +#define CONFIG_ACCESSCONTROL_ATTRIBUTE "nsslapd-accesscontrol" +#define CONFIG_GROUPEVALNESTLEVEL_DIRECTIVE "groupevalnestlevel" +#define CONFIG_GROUPEVALNESTLEVEL_ATTRIBUTE "nsslapd-groupevalnestlevel" +#define CONFIG_NAGLE_DIRECTIVE "nagle" +#define CONFIG_NAGLE_ATTRIBUTE "nsslapd-nagle" +#define CONFIG_PW_CHANGE_DIRECTIVE "pw_change" +#define CONFIG_PW_CHANGE_ATTRIBUTE "passwordChange" +#define CONFIG_PW_MUSTCHANGE_DIRECTIVE "pw_must_change" +#define CONFIG_PW_MUSTCHANGE_ATTRIBUTE "passwordMustChange" +#define CONFIG_PW_SYNTAX_DIRECTIVE "pw_syntax" +#define CONFIG_PW_SYNTAX_ATTRIBUTE "passwordCheckSyntax" +#define CONFIG_PW_MINLENGTH_DIRECTIVE "pw_minlength" +#define CONFIG_PW_MINLENGTH_ATTRIBUTE "passwordMinLength" +#define CONFIG_PW_EXP_DIRECTIVE "pw_exp" +#define CONFIG_PW_EXP_ATTRIBUTE "passwordExp" +#define CONFIG_PW_MAXAGE_DIRECTIVE "pw_maxage" +#define CONFIG_PW_MAXAGE_ATTRIBUTE "passwordMaxAge" +#define CONFIG_PW_MINAGE_DIRECTIVE "pw_minage" +#define CONFIG_PW_MINAGE_ATTRIBUTE "passwordMinAge" +#define CONFIG_PW_WARNING_DIRECTIVE "pw_warning" +#define CONFIG_PW_WARNING_ATTRIBUTE "passwordWarning" +#define CONFIG_PW_HISTORY_DIRECTIVE "pw_history" +#define CONFIG_PW_HISTORY_ATTRIBUTE "passwordHistory" +#define CONFIG_PW_INHISTORY_DIRECTIVE "pw_inhistory" +#define CONFIG_PW_INHISTORY_ATTRIBUTE "passwordInHistory" +#define CONFIG_PW_LOCKOUT_DIRECTIVE "pw_lockout" +#define CONFIG_PW_LOCKOUT_ATTRIBUTE "passwordLockout" +#define CONFIG_PW_STORAGESCHEME_DIRECTIVE "pw_storagescheme" +#define CONFIG_PW_STORAGESCHEME_ATTRIBUTE "passwordStorageScheme" +#define CONFIG_PW_MAXFAILURE_DIRECTIVE "pw_maxfailure" +#define CONFIG_PW_MAXFAILURE_ATTRIBUTE "passwordMaxFailure" +#define CONFIG_PW_UNLOCK_DIRECTIVE "pw_unlock" +#define CONFIG_PW_UNLOCK_ATTRIBUTE "passwordUnlock" +#define CONFIG_PW_LOCKDURATION_DIRECTIVE "pw_lockduration" +#define CONFIG_PW_LOCKDURATION_ATTRIBUTE "passwordLockoutDuration" +#define CONFIG_PW_RESETFAILURECOUNT_DIRECTIVE "pw_resetfailurecount" +#define CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE "passwordResetFailureCount" +#define CONFIG_ACCESSLOG_BUFFERING_DIRECTIVE "logbuffering" +#define CONFIG_ACCESSLOG_BUFFERING_ATTRIBUTE "nsslapd-accesslog-logbuffering" +#define CONFIG_CHANGELOG_DIR_DIRECTIVE "changelogdir" +#define CONFIG_CHANGELOG_DIR_ATTRIBUTE "nsslapd-changelogdir" +#define CONFIG_CHANGELOG_SUFFIX_DIRECTIVE "changelogsuffix" +#define CONFIG_CHANGELOG_SUFFIX_ATTRIBUTE "nsslapd-changelogsuffix" +#define CONFIG_CHANGELOG_MAXENTRIES_DIRECTIVE "changelogmaxextries" +#define CONFIG_CHANGELOG_MAXENTRIES_ATTRIBUTE "nsslapd-changelogmaxentries" +#define CONFIG_CHANGELOG_MAXAGE_DIRECTIVE "changelogmaxage" +#define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE "nsslapd-changelogmaxage" +#define CONFIG_RETURN_EXACT_CASE_DIRECTIVE "return_exact_case" +#define CONFIG_RESULT_TWEAK_DIRECTIVE "result_tweak" +#define CONFIG_REFERRAL_MODE_DIRECTIVE "referralmode" +#define CONFIG_ATTRIBUTE_NAME_EXCEPTION_DIRECTIVE "attribute_name_exceptions" +#define CONFIG_MAXBERSIZE_DIRECTIVE "maxbersize" +#define CONFIG_VERSIONSTRING_DIRECTIVE "versionstring" +#define CONFIG_ENQUOTE_SUP_OC_DIRECTIVE "enquote_sup_oc" +#define CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE "nsslapd-enquote_sup_oc" +#define CONFIG_BASEDN_DIRECTIVE "certmap-basedn" +#define CONFIG_BASEDN_ATTRIBUTE "nsslapd-certmap-basedn" + +%HashParametersName = (); + +# The following hash displays only general server parameters to migrate under cn=config +%GeneralSrvParamToMigrate = ( + 'accesscontrol' => 'nsslapd-accesscontrol', + 'errorlog-logging-enabled' => 'nsslapd-errorlog-logging-enabled', + 'accesslog-logging-enabled' => 'nsslapd-accesslog-logging-enabled', + 'auditlog-logging-enabled' => 'nsslapd-auditlog-logging-enabled', + 'logbuffering' => 'nsslapd-accesslog-logbuffering', + 'accesslog-logexpirationtime' => 'nsslapd-accesslog-logexpirationtime', + 'accesslog-logexpirationtimeunit' => 'nsslapd-accesslog-logexpirationtimeunit', + 'accesslog-maxlogdiskspace' => 'nsslapd-accesslog-logmaxdiskspace', + 'accesslog-minfreediskspace' => 'nsslapd-accesslog-logminfreediskspace', + 'accesslog-logrotationtime' => 'nsslapd-accesslog-logrotationtime', + 'accesslog-logrotationtimeunit' => 'nsslapd-accesslog-logrotationtimeunit', + 'accesslog-maxlogsize' => 'nsslapd-accesslog-maxlogsize', + 'accesslog-maxnumoflogsperdir' => 'nsslapd-accesslog-maxLogsPerDir', + 'auditlog-logexpirationtime' => 'nsslapd-auditlog-logexpirationtime', + 'auditlog-logexpirationtimeunit' => 'nsslapd-auditlog-logexpirationtimeunit', + 'auditlog-maxlogdiskspace' => 'nsslapd-auditlog-logmaxdiskspace', + 'auditlog-minfreediskspace' => 'nsslapd-auditlog-logminfreediskspace', + 'auditlog-logrotationtime' => 'nsslapd-auditlog-logrotationtime', + 'auditlog-logrotationtimeunit' => 'nsslapd-auditlog-logrotationtimeunit', + 'auditlog-maxlogsize' => 'nsslapd-auditlog-maxlogsize', + 'auditlog-maxnumoflogsperdir' => 'nsslapd-auditlog-maxLogsPerDir', + 'certmap-basedn' => 'nsslapd-certmap-basedn', + 'enquote_sup_oc' => 'nsslapd-enquote-sup-oc', + 'loglevel' => 'nsslapd-errorlog-level', + 'errorlog-logexpirationtime' => 'nsslapd-errorlog-logexpirationtime', + 'errorlog-logexpirationtimeunit' => 'nsslapd-errorlog-logexpirationtimeunit', + 'errorlog-maxlogdiskspace' => 'nsslapd-errorlog-logmaxdiskspace', + 'errorlog-minfreediskspace' => 'nsslapd-errorlog-logminfreediskspace', + 'errorlog-logrotationtime' => 'nsslapd-errorlog-logrotationtime', + 'errorlog-logrotationtimeunit' => 'nsslapd-errorlog-logrotationtimeunit', + 'errorlog-maxlogsize' => 'nsslapd-errorlog-maxlogsize', + 'errorlog-maxnumoflogsperdir' => 'nsslapd-errorlog-maxlogsperdir', + 'idletimeout' => 'nsslapd-idletimeout', + 'ioblocktimeout' => 'nsslapd-ioblocktimeout', + 'lastmod' => 'nsslapd-lastmod', + 'listenhost' => 'nsslapd-listenhost', + 'maxdescriptors' => 'nsslapd-maxdescriptors', + 'referral' => 'nsslapd-referral', + 'reservedescriptors' => 'nsslapd-reservedescriptors', + 'rootpwstoragescheme' => 'nsslapd-rootpwstoragescheme', + 'schemacheck' => 'nsslapd-schemacheck', + 'secure-port' => 'nsslapd-securePort', + 'security' => 'nsslapd-security', + 'sizelimit' => 'nsslapd-sizelimit', + 'SSL3ciphers' => 'nsslapd-SSL3ciphers', + 'timelimit' => 'nsslapd-timelimit', + 'pw_change' => 'passwordChange', + 'pw_syntax' => 'passwordCheckSyntax', + 'pw_exp' => 'passwordExp', + 'pw_history' => 'passwordHistory', + 'pw_inhistory' => 'passwordInHistory', + 'pw_lockout' => 'passwordLockout', + 'pw_lockduration' => 'passwordLockoutDuration', + 'pw_maxage' => 'passwordMaxAge', + 'pw_maxfailure' => 'passwordMaxFailure', + 'pw_minage' => 'passwordMinAge', + 'pw_minlength' => 'passwordMinLength', + 'pw_must_change' => 'passwordMustChange', + 'pw_resetfailurecount' => 'passwordResetFailureCount', + 'pw_storagescheme' => 'passwordStorageScheme', + 'pw_unlock' => 'passwordUnlock', + 'pw_warning' => 'passwordWarning' +); + +# the following hash displays global parameters related to database stored under cn=config,cn=ldbm database,cn=plugins,cn=config +%GlobalConfigLDBMparamToMigrate = ( + 'allidsthreshold' => 'nsslapd-allidsthreshold', + 'lookthroughlimit' => 'nsslapd-lookthroughlimit', + 'mode' => 'nsslapd-mode', + 'dbcachesize' => 'nsslapd-dbcachesize' +); + +# the following hash displays specific parameters to each backends and stored under cn=DBname,cn=ldbm database,cn=plugins,cn=config +%LDBMparamToMigrate = ( + 'cachesize' => 'nsslapd-cachesize', + 'readonly' => 'nsslapd-readonly' +); + +%stdIncludes = ( + "${oldConfDir}slapd.at.conf", "\n", + "${oldConfDir}slapd.oc.conf", "\n", + "${oldConfDir}java-object-schema.conf", "\n", + "${oldConfDir}ns-admin-schema.conf", "\n", + "${oldConfDir}ns-calendar-schema.conf", "\n", + "${oldConfDir}ns-certificate-schema.conf", "\n", + "${oldConfDir}ns-common-schema.conf", "\n", + "${oldConfDir}ns-compass-schema.conf", "\n", + "${oldConfDir}ns-delegated-admin-schema.conf", "\n", + "${oldConfDir}ns-directory-schema.conf", "\n", + "${oldConfDir}ns-legacy-schema.conf", "\n", + "${oldConfDir}ns-mail-schema.conf", "\n", + "${oldConfDir}ns-mcd-browser-schema.conf", "\n", + "${oldConfDir}ns-mcd-config-schema.conf", "\n", + "${oldConfDir}ns-mcd-li-schema.conf", "\n", + "${oldConfDir}ns-mcd-mail-schema.conf", "\n", + "${oldConfDir}ns-media-schema.conf", "\n", + "${oldConfDir}ns-mlm-schema.conf", "\n", + "${oldConfDir}ns-msg-schema.conf", "\n", + "${oldConfDir}ns-netshare-schema.conf", "\n", + "${oldConfDir}ns-news-schema.conf", "\n", + "${oldConfDir}ns-proxy-schema.conf", "\n", + "${oldConfDir}ns-value-schema.conf", "\n", + "${oldConfDir}ns-wcal-schema.conf", "\n", + "${oldConfDir}ns-cos-schema.conf", "\n", + "${oldConfDir}ns-web-schema.conf", "\n" +); + +%userDefinedConfigFiles = ( + "slapd.conf", "\n", + "slapd.ldbm.conf", "\n", + "slapd.user_at.conf", "\n", + "slapd.user_oc.conf", "\n", + "ns-schema.conf", "\n" + ); + +$CIS_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.15" ; +$TELEPHONE_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.50" ; +$DN_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.12" ; +$CES_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.26" ; +$INT_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.27" ; +$BIN_SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.5" ; + +%allowedPlugins = ( + "cis", $CIS_SYNTAX_OID, + "tel", $TELEPHONE_SYNTAX_OID, + "dn", $DN_SYNTAX_OID, + "ces", $CES_SYNTAX_OID, + "int", $INT_SYNTAX_OID, + "bin", $BIN_SYNTAX_OID + ); + +%allowedModifiers = ( + "single", "SINGLE-VALUE" + ); +# "override" is not supported anymore and "operational" cannot be used in user defined attribute. + +@oldSuffixes = () ; # array of old suffixes (with "o=netscaperoot" if presents) + +# link beetwen the name of the suffix and its associated DBname +%DBNAMES = () ; +%DBDirectory = () ; + +%oldhash = () ; + +# list of standard plugin's in version 4 +%stdPlugins = ( + "7-bit check" => "\n", + "binary syntax" => "\n", + "case exact string syntax" => "\n", + "case ignore string syntax" => "\n", + "distinguished name syntax" => "\n", + "integer syntax" => "\n", + "internationalization plugin" => "\n", + "referential integrity postoperation" => "\n", + "telephone syntax" => "\n", + "uid uniqueness" => "\n" + + ); + +# list of standard indexes configured out of the box in version 4 +%stdIndex = ( + 'aci', "\n", + 'changenumber', "\n", + 'copiedfrom', "\n", + 'dncomp', "\n", + 'entrydn', "\n", + 'numsubordinates', "\n", + 'objectclass', "\n", + 'parentid', "\n" +); + +# list of user added Plugin's. In the new version, they 'll need to be recompiled +@badPlugins = () ; + +%newIndex = () ; + +%User_oc = () ; +# push objectnames as they are encountered in config files. +@User_oc_names = () ; + +%User_at = () ; + + + +#Usage parameters +$USER_OC_FILE_MODIFIED = 0 ; # 0 if user don't want to modify LDIF objectclasses before processing, 1 else +$USER_AT_FILE_MODIFIED = 0 ; +$INDEX_FILE_MODIFIED = 0 ; + +# get the version of the DS to migrate +($oldVersion, $oldMinor) = &getVersion($oldDir, $oldversionstr); +# get the version of the new DS +($Version, $Minor) = &getVersion($root); + +# get old LIB_PATH +$old_libpath = &getLibPath($oldDir, $oldVersion, $oldMinor); +# get new LIB_PATH +$new_libpath = &getLibPath($root, $Version, $Minor); + +# Shutdown the legacy Directory instance +printTrace("\nShutdown the legacy Directory Server instance: ${oldHome}",0); +&stopServer($oldDir, 'slapd-'.$oldname); + +# compare configuration files with the standard ones +CompareStdConfigFiles() ; +die "\n\n The version of the product you want to migrate is not a 4.x Netscape Directory Server\n" unless ($oldVersion == 4) ; + +FillHashParametersName() ; + +############### Connect to the New LDAP Directory Server ###################### +$ENV{"$LIB_PATH"} = $new_libpath; +my $LDAPservername = &getLDAPservername(); +die "\n Migration aborted. Make sure your Old and New Directory Servers are installed on the same machine \n" if ( $LDAPservername == -1 ); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; + +# continue if the connection to new LDAP server is successful ! +printTrace("\nConnected to $Version.$Minor LDAP server\n",0) ; + +# get the uid and gid of the new slapd user +($localuser, $newuid, $newgid) = getuid_gid(); +# get the uid and gid of the old slapd user +($oldlocaluser, $olduid, $oldgid) = getolduid_gid(); + +# backup new configuration files in <new_root_server>/slapd-instancename/config +printTrace("\nBackup $serverHome${PATHSEP}config on $serverHome${PATHSEP}config_backup ...",0); +&backupConfigFiles(); + +# Parse the main configuration file: slapd.conf +printTrace("\nParse the configuration file: $oldSlapdConf...",0); +ParseSlapdConf("< ${oldSlapdConf}"); + +#migrate key/cert databases +printTrace("\nMigrate key/cert databases...",0); +&MigrateSSL(); + +# Update parameters : general server parameters, global LDBM parameter, specific backend parameters +printTrace("\nUpdate general server parameters...",0); +$conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Can't contact the $Version.$Minor LDAP server: $LDAPservername\n"; +AddGeneralParameters(); +printTrace("\nUpdate global LDBM parameters...",0); +AddGeneralLDBMParameters(); +printTrace("\nUpdate specific backend parameters...",0); +AddSpecificLDBMParameters(); + +##### FOR TESTING PURPOSE ONLY ######## +# +#testIndexUpdating(); +# +####################################### + +# Migrate some entries contained in the old dse.ldif, and migrate certmap.conf +&MigrateDSE() ; +&MigrateCertmap() ; + +# update new attribute definitions +LDAPmodify_User_at(); + +# update new object classes definitions +LDAPmodify_User_oc(); + +# add new indexes to each backends +LDAPmodify_Indexes(); + +# migrate Plug'ins parameters (enable attribute, and arguments) +LDAPmodify_stdPlugin(); + +################## Close the connection to new LDAP Server ##################### +$conn->close; + + +################## stop the new instance and Export/Import the data, restart the server ################## +if (%DBNAMES) { + &stopServer($root,'slapd-'.$newname); + if ($olddatadir) { + printTrace("\nold data directory $olddatadir...",0) ; + $ldif_rep = "$olddatadir${PATHSEP}"; + } else { + printTrace("\ndata processing...",0) ; + # migrate data for each suffix: old -> LDIF files + &db2ldif($oldSlapdConf); + } + + # migrate LDIF data to the new database: LDIF -> new + &manyLdif2db(); + &startServer(); +} +else { + printTrace("\nThere no old non-standard suffixes to migrate",0); +} + +printMsg("\n\n ****** End of migration ******\n\n"); + +close(LOGFILE); + + +########################################################################################### +# get input users +sub getParameters { + my $exit = 0 ; + my $i = 0; + my $pwdfile= ""; + while ($i <= $#ARGV) { + if ( "$ARGV[$i]" eq "-D" ) { # directory manager + if (! $rootDN) { + $rootDN = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-w") { # password + if (! $rootpwd) { + $rootpwd = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-j") { # password file + if (! $pwdfile) { + $pwdfile = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-o") { # old instance path + if (! $oldHome ) { + $oldHome = $ARGV[++$i] ; + grep { s@\\@/@g } $oldHome if $isNT ; + if ($oldHome =~ /[\"]?(.*)?[\"]?/) { $oldHome = $1 ; } + if ($oldHome =~ m@^(.*)/([^-/]*)-([^/]*)[/]?$@) { + $oldDir = $1 ; + $type = $2 ; + $oldname = $3 ; + if ($isNT) { + $oldDir = lc($oldDir) ; + $type = lc($type) ; + $oldname = lc($oldname) ; + $oldHome = lc($oldHome) ; + grep { s@/@\\@g } $oldDir ; + grep { s@/@\\@g } $oldHome ; + } + } + else { + print("\nThe old instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-n") { # new instance path + if (! $serverHome ) { + $serverHome = $ARGV[++$i] ; + grep { s@\\@/@g } $root if $isNT ; + grep { s@\\@/@g } $serverHome if $isNT ; + if ($serverHome =~ /[\"]?(.*)?[\"]?/) { $serverHome = $1 ; } + if ($serverHome =~ m@^(.*?)/?([^/-]*)-([^/]*)[/]?$@) { + $root = $1 if ($1); + $type = $2 ; + $newname = $3 ; + if ($isNT) { + $root = lc($root) ; + $type = lc($type) ; + $newname = lc($newname) ; + $serverHome = lc($serverHome) ; + grep { s@/@\\@g } $root ; + grep { s@/@\\@g } $serverHome ; + } + } + else { + print("\nThe new instance path is not correct. It must be like slapd-instancename"); + &usage(); + exit(1); + } + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-p") { # new DS port + if (! $newport ) { + $newport = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-d") { # old instance LDIF data dir + if (! $olddatadir ) { + $olddatadir = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-v") { # old version + if (! $oldversionstr ) { + $oldversionstr = $ARGV[++$i] ; + } + else { + &usage() ; + exit(1); + } + } elsif ("$ARGV[$i]" eq "-t") { # TRACELEVEL + my $value = $ARGV[++$i] ; + if ($value =~ /[0-3]/) { + $TRACELEVEL = $value ; + } + else { + print("\nThe tracelevel must belong to 0..3 interval"); + &usage(); + exit(); + } + } elsif ("$ARGV[$i]" eq "-noinput") { # no user interventions during processing + $NO_INPUT_USER = 1 ; + } elsif ("$ARGV[$i]" eq "-L") { # migration logfile + $LogFileReport = $ARGV[++$i] ; + } + else { + print("\nThe option $ARGV[$i] is not recognized"); + &usage() ; + exit(1); + } + $i++; + } + if (! $rootDN) { + print("\nThe rootDN is missing"); + $exit = 1; + } + if ($pwdfile ne "") { + # Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpwd = <RPASS>; + chomp($rootpwd); + close(RPASS); + } elsif ($rootpwd eq "-"){ + # Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; + # Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpwd = ReadLine(0); +# chomp($rootpwd); +# ReadMode('normal'); + } + if (! $rootpwd) { + print("\nThe rootpwd is missing"); + $exit = 1 ; + } + if (! $newport) { + print("\nThe port is missing"); + $exit = 1; + } + if (! $serverHome) { + print("\nThe new instance path is missing"); + $exit = 1; + } + if (! $oldHome) { + print("\nThe old instance path is missing"); + $exit = 1; + } + if ((! $LogFileReport) && $serverHome) { + ($sec, $min, $hour, $dd, $mm, $yy) = &GetTime(); + $LogFileReport = "${serverHome}${PATHSEP}logs${PATHSEP}Migration_${dd}${mm}${yy}_${hour}${min}${sec}.log"; + } + + if ($exit) { + &usage() ; + exit(1); + } + +} + + +############################################################################### +# This subroutine is used to parse the slapd.conf configuration file and migrate specific parameters contained in it + + +sub ParseSlapdConf { + my $oldsrc = shift; + my $NumLine = 0 ; + # read the old conf file into a hash table + open( OLDSRC, $oldsrc ) || die "Can't open $oldsrc: $!: "; + LINE: while ( <OLDSRC> ) { + $NumLine++ ; + printTrace("\nLine: $_",4) ; + if (/^\s*\#/) { # skip comments + printTrace("\n# ",4) ; + next LINE; + } + if (/^\s*$/) { # skip blank lines + printTrace("\nBLANK LINE",4); + next LINE; + } elsif (/^suffix\s+/i) { + chomp($_) ; + CheckSuffix($_); + } elsif (/^plugin/i) { + printTrace("\nPLUGIN",4); + chomp($_); + if (! &isAStandardPlugin($_)) { + push @badPlugins, $_; + } + else { + my $Plugin = $_ ; + if (! &ParsePlugin($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of the plugin:\n$Plugin"); + } + } + } elsif (/^include\s+[\"]?(.*?)[\"]?\s*$/i) { + # strip leading and trailing " + my $include_file = $1 ; + grep { s@/@\\@g } $include_file if $isNT; + if (! &isAStandardInclude($include_file)) { + printTrace("\nFILE: $1 NOT STANDARD",4) ; + &ParseConfigurationFile($include_file); + printTrace("\nEXIT ParseConfigurationFile: $include_file",4) ; + } + } elsif (/^userat\s+[\"]?(.*?)[\"]?\s*$/i) { + printTrace("\nuserat: $1",4); + my $at_file = $1 ; + grep { s@/@\\@g } $at_file if $isNT; + # Parse user defined attributes + &ParseAttributesFile($at_file); + } elsif (/^useroc\s+[\"]?(.*?)[\"]?\s*$/i) { + printTrace("\nuseroc: $1",4); + my $oc_file = $1 ; + grep { s@/@\\@g } $oc_file if $isNT; + # Parse user defined object classes + &ParseObjectClassesFile($oc_file); + } elsif (/^dynamicconf\s+[\"]?(.*?)[\"]?\s*$/i){ + printTrace("\ndynamicconf: $1",4); + my $dynamiconf_file = $1 ; + grep { s@/@\\@g } $dynamiconf_file if $isNT; + # Parse dynamic configuration file (e-g slapd.ldbm.conf) + &ParseConfigurationFile($dynamiconf_file); + } elsif (/^\s*(\S+)\s+[\"]?(.*?)[\"]?\s*$/) { + printTrace("\nParseParameters: $1",4); + # Parse parameters and record the associated value in %oldhash + &ParseParameters($1,$2,$NumLine); + } else { + printTrace("\nUnknown format of configuration data: $_",0); } + } + close(OLDSRC); + + } + + + +############################################################################# +# return 1 if the suffix already exists, 0 else +sub existSuffix { + my $suffixname = shift ; + my $nsuffix = normalizeDN($suffixname); + my $entry = $conn->search("cn=mapping tree,cn=config", "one", "(|(cn=\"$suffixname\")(cn=\"$nsuffix\"))"); + return 1 if ($entry) ; + my $cpt = 5; + my $errorCode = $conn->getErrorCode(); + while (($errorCode eq $LDAP_SERVER_UNREACHABLE) && $cpt && (! $entry)) { + printTrace("\ntry to reconnect to search cn=\"$suffixname\",cn=mapping tree,cn=config", 1); + $conn = new Mozilla::LDAP::Conn($LDAPservername,$newport,$rootDN,$rootpwd) or die "\n Unable to contact the $Version.$Minor LDAP server: $LDAPservername\n"; + $entry = $conn->search("cn=mapping tree,cn=config", "one", "(|(cn=\"$suffixname\")(cn=\"$nsuffix\"))"); + $errorCode = $conn->getErrorCode(); + $cpt--; + } + return 1 if ($entry) ; + return 0 ; +} + +# return the name of the backend if it has been successfully created, 0 else +sub createBackend { + my $suffixname = shift ; + my $backend = "MigratedDB_0" ; + my $NbRetry = 1 ; + my $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + while ($entry) { + # try to find another name for the backend + $backend = "MigratedDB_$NbRetry" ; + $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + $NbRetry++; + } + # normally I should have a unique name for the backend + my $suffixarg = "nsslapd-suffix" ; + $entry = $conn->newEntry() ; + $entry->setDN("cn=$backend,cn=ldbm database,cn=plugins,cn=config"); + $entry->setValues("objectclass", "top", "extensibleObject", "nsBackendInstance" ); + $entry->setValues("cn", $backend ); + $entry->setValues($suffixarg, $suffixname ); + my $res = $conn->add($entry) ; + if ($res) { + return $backend ; + } + else { + return 0 ; + } +} + +# return 1, if add the new entry in the mapping tree, else 0 +sub AddEntryInMappingTree { + my $backend = shift ; + my $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + if ($entry) { + printTrace("\nAddEntry in progress ...",4) ; + my $suffixarg = "nsslapd-suffix" ; + my $statearg = "nsslapd-state" ; + my $backendarg= "nsslapd-backend"; + my $suffixname = $entry->{$suffixarg}[0]; + $entry = $conn->newEntry() ; + $entry->setDN("cn=\"$suffixname\",cn=mapping tree,cn=config") ; + $entry->setValues("objectclass", "top", "extensibleObject", "nsMappingTree" ); + $entry->setValues("cn", "\"$suffixname\""); + $entry->setValues($statearg, "backend"); + $entry->setValues($backendarg, $backend); + return $conn->add($entry); + } + else { + printTrace("\nNo AddEntry processed for $backend",4); + return 0 ; + } +} + + +# Treat the case where the suffix is "o=NetscapeRoot" +sub CheckSuffix { + my $suffix = shift ; + my $suffixname ; + my $expLdif; + my $confirm = "No"; + my $dest = "$serverHome${PATHSEP}db_backup" ; + my $newSlapdExecDir = "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + + if (!(/^suffix\s+\"?(.*?)\"?\s*$/i)) { + printMsg("Syntax error of the suffix: $suffix"); + return 0 ; + } + else { + $suffixname = $1 ; + } + if (/^suffix\s+\"?\s*o=netscaperoot\s*\"?\s*$/i) { + printTrace("\nFor the suffix o=NetscapeRoot, we do nothing",1); + # treat the case where the suffix is "o=NetscapeRoot" + } + else { + push @oldSuffixes, $_; + # check if the suffix already exists in the new DS target + if (! existSuffix($suffixname)) { + printTrace("\n\nSuffix $suffixname doesn't exist",1) ; + # create a new backend with the name of the suffix preceded by MigratedDB_ + my $backend = createBackend($suffixname) ; + if ($backend) { + printTrace("\nBackend: $backend has been created !!!",1); + # if the creation of the backend is ok, we add a new entry in the mapping tree + if (AddEntryInMappingTree($backend)) { + # We add the association dbname->suffix in the hash %DBNAMES + $DBNAMES{$suffixname} = $backend ; + # get the db filename + $entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + my $dirarg = "nsslapd-directory"; + $DBDirectory{$backend} = $entry->{$dirarg}[0]; + printTrace("\nThe relation $backend->$suffixname has been added to the mapping tree",2); + } + else { + printMsg("\nCOULD NOT ADD ENTRY: $backend->$suffixname IN MAPPINGTREE"); + } + } + else { + printMsg("\nCOULD NOT CREATE BACKEND: $backend"); + } + } + else { + printMsg("\n\nSuffix: $suffixname already exists"); + # the suffix already exists in the new DS + printMsg("\nMigration will overwrite existing database"); + printMsg("\nDo you want to continue Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + my $nsuffix = normalizeDN($suffixname); + my $my_entry = $conn->search("cn=mapping tree,cn=config", "one", "(|(cn=\"$suffixname\")(cn=\"$nsuffix\"))"); + my $backend = $my_entry->{"nsslapd-backend"}[0]; + my $backend_entry = $conn->search("cn=$backend,cn=ldbm database,cn=plugins,cn=config ", "base","objectclass=*") ; + printMsg("Do you want to export the existing data Yes/No [Yes] ?"); + my $answer = <STDIN> ; + if (!($answer =~ /n|no/i)) { + mkdir $dest, 0700 unless (-d $dest); + $expLdif = "$dest${PATHSEP}$backend.ldif"; + while (!($confirm =~ /y|yes/i)) { + printMsg("\nEnter the full pathname of the file [$expLdif]:") ; + $answer = <STDIN> ; + chomp($expLdif = $answer) unless ($answer eq "\n"); + printMsg("\nExisting data will be exported under $expLdif"); + printMsg("\nContinue Yes/No [No] ?"); + $confirm = <STDIN>; + } + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($newSlapdExecDir) or die "\nCould not change directory to $newSlapdExecDir: $!\n"; + printTrace("\nNow backing up database $CN in $expLdif\n",0); + &stopServer($root,'slapd-'.$newname); + &newinst_db2ldif($expLdif, $suffixname, $serverHome); + &startServer(); + } + # We add the association dbname->suffix in the hash %DBNAMES + $DBNAMES{$suffixname} = $backend ; + my $dirarg = "nsslapd-directory"; + $DBDirectory{$backend} = $backend_entry->{$dirarg}[0]; + printTrace("\nThe relation $backend->$suffixname has been added to the mapping tree",2); + } + } + return 1 ; +} +} + +############################################################################# +# Usefull to know the standard configuration +sub isAStandardPlugin { + my $line = shift; + chomp($line); + printTrace("\nStdPlugin?: $line",4); + if ($line =~ /^plugin\s+(database|extendop|preoperation|postoperation|matchingrule|syntax)\s+(on|off)\s+\"(.*?)\"\s+\"(.*?)\"\s+(\S+)(.*)$/i) { + # $1 = <type>, $2 = <on|off>, $3 = <name>, $4 = <pathname>, $5 = <init_function>, $6 = [<arg>]* + printTrace("\nName: $3, pathname: $4, init_function: $5",4); + + my $LC_line = lc($3); + my $Value = $stdPlugins{$LC_line} ; + if ($Value) { + printTrace("\nIS A STANDARD PLUGIN",4); + } + else { + printTrace("\nNOT A STANDARD PLUGIN",4); + } + return $stdPlugins{$LC_line} ; + } + else { + printTrace("\nSYNTAX ERROR PLUGIN",4); + return 0 ; + } +} + +sub isAStandardIndex { + my $line = shift ; + chomp($line); + if ($line =~ /^index\s+(\S+).*/i) { + my $LC_line = lc($1); + my $Value = $stdIndex{$LC_line} ; + printTrace("\nInclude: $LC_line \nValue: $Value", 4); + return $stdIndex{$LC_line}; + } + else { + return 0 ; + } +} + + +sub isAStandardInclude { + my $line = shift; + + chomp($line); + if ($isNT){ + return $stdIncludes{lc($line)}; + } + else { + return $stdIncludes{$line} ; + } +} + +############################################################################# +# +# Execute a Perldap command to update plugins definition in the new schema + +sub LDAPmodify_stdPlugin { + my $Filename = shift ; + my @pluginames = keys(%stdPlugins); + if (! $STDPLUGINS_FILE_MODIFIED) { + printTrace("\nLDAPmodify_plugin",4); + printTrace("\nMigrate plugin's...",1); + foreach $pluginame ( @pluginames ) { + my $update_plugin = 0 ; + my $ref_plugin = $stdPlugins{$pluginame}; + if ($ref_plugin ne "\n") { + my $name = $ref_plugin->name ; + # We have a name change of "uid uniqueness plugin" in DS7.0 + # to "attribute uniqueness" + $name = "attribute uniqueness" if ($name eq "uid uniqueness"); + my $entry = $conn->search("cn=$name,cn=plugins,cn=config", "base","objectclass=nsSlapdPlugin") ; + if ($entry) { + my $pluginenabled="nsslapd-pluginenabled" ; + if (($entry->{$pluginenabled}[0]) ne $ref_plugin->enable) { + $update_plugin = 1 ; + my $enable = $ref_plugin->enable ; + printTrace("\n$pluginame, plugin-enable: $enable",3) ; + $entry->setValues($pluginenabled, $enable ); + } + my $ArgNum = 0 ; + foreach $ArgValue (@{$ref_plugin->args}) { + my $Arg="nsslapd-pluginarg$ArgNum"; + printTrace("\n$Arg: $ArgValue",3) ; + if ($entry->{$Arg}[0] ne $ArgValue) { + printTrace("\n$pluginame, $Arg: $ArgValue",3) ; + $update_plugin = 1 ; + $entry->setValues($Arg, $ArgValue) ; + } + $ArgNum++ ; + } + if ($update_plugin) { + printTrace("\n$pluginame is being updated...",2); + my $res = $conn->update($entry) ; + if ($res) { + printTrace("\nupdated !",2); + } + else { + printMsg("\nError during update of plugin: $pluginame") ; + $MigrationErrors .= "\nError during update of plugin: $pluginame"; + } + } + else { + printTrace("\n$pluginame has not changed",4); + } + } + else { + printMsg("\ncan't access the plugin: cn=$name,cn=plugins,cn=config"); + } + } + else { + printTrace("\nPLUGIN NOT RECORDED: $pluginame",4) ; + } + } + } + else { + # treat the case where the user wants to look at these plugins before processing + } +} + +############################################################################# +# Execute Perldap command to add new indexes to the migrated instances + +sub LDAPmodify_Indexes { + my $Filename = shift ; + my @indexnames = keys(%newIndex); + my @suffixnames = keys(%DBNAMES); + if ((! $INDEX_FILE_MODIFIED) && (%DBNAMES)) { + # we update indexes only if there is at least one backend to migrate + printTrace("\nLDAPmodify_indexes",4); + printTrace("\nMigrate indexes...",1); + foreach $indexname ( @indexnames ) { + printTrace("\nIndexName: $indexname",4); + printTrace("\nIndexTypes: .@{$newIndex{$indexname}->types}.", 4) ; + printTrace("\nIndexOIDS: .@{$newIndex{$indexname}->oids}.", 4) ; + foreach $suffixname ( @suffixnames ) { + # check if the index already exists ! + printTrace("\nsearch for cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...", 3); + my $entry = $conn->search("cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + if (! $entry) { + # create a new index + printTrace("index $indexname is being created under cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...",2); + my $entry = $conn->newEntry(); + $entry->setDN("cn=$indexname,cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config"); + $entry->setValues("objectclass", "top", "nsIndex" ) ; + $entry->setValues("cn", $indexname) ; + $entry->setValues("nssystemindex", "false") ; + my @types = @{$newIndex{$indexname}->types} ; + my @oids = @{$newIndex{$indexname}->oids} ; + $entry->setValues("nsindextype", @types) if (@types) ; + $entry->setValues("nsmatchingrule", @oids ) if (@oids); + my $res = $conn->add($entry) ; + if ($res) { + printTrace("\nAdd index successfully: $indexname for backend: $DBNAMES{$suffixname}",2); + } + else { + printMsg("\n Failed to add the index: $indexname to backend: $DBNAMES{$suffixname}"); + $MigrationErrors .= "\n Failed to add the index: $indexname to backend: $DBNAMES{$suffixname}" ; + } + } + elsif ($entry->{nssystemindex}[0] eq "false") { + # if the index is not a system index, we update it + printTrace("\nindex $indexname is being processed under cn=index,cn=$DBNAMES{$suffixname},cn=ldbm database,cn=plugins,cn=config...",2); + my @types = @{$newIndex{$indexname}->types} ; printTrace("\ntypes: .@types.",2) ; + my @oids = @{$newIndex{$indexname}->oids} ; printTrace("\noids: .@oids.",2) ; + my @existing_types = $entry->getValues("nsindextype"); + my @existing_oids = $entry->getValues("nsmatchingrule"); + # get the elements present in @types and not present in @existing_types + my @typesToAdd = &getDiff(\@types, \@existing_types); + # same for matchingrules + my @oidsToAdd = &getDiff(\@oids, \@existing_oids); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + foreach $newoid (@oidsToAdd) { + $entry->addValue("nsmatchingrule", $newoid); + } + if (@typesToAdd || @oidsToAdd) { + my $res = $conn->update($entry) ; + if ($res) { + printTrace("\nUpdate index successfully: $indexname for backend: $DBNAMES{$suffixname}",2); + } + else { + printMsg("\n Failed to update the index: $indexname to backend: $DBNAMES{$suffixname}"); + $MigrationErrors .= "\n Failed to update the index: $indexname to backend: $DBNAMES{$suffixname}" ; + } + } + else { + printTrace("\nNothing to update",2); + } + } + else { + printTrace("\nThe index: $indexname is a system index. It can't be updated",2); + } + } + } + + } + else { + # treat the case where the user wants to look at these indexes before processing + } + +} + +############################################################################# +# +# Execute a Perldap command to add all user defined object classes in the new schema + +sub LDAPmodify_User_oc { + my $Filename = shift ; + if (! $USER_OC_FILE_MODIFIED) { + printTrace("\nLDAPmodify_User_oc",4); + printTrace("\nMigrate objectclasses...",1); + foreach $objectname ( @User_oc_names ) { + my $entry = $conn->search("cn=schema", "base","objectclass=*") ; + die "\ncan't connect to object: cn=schema\n" unless ($entry); + printTrace("\nObjectName: $objectname\nValue: $User_oc{$objectname}",3); + next if ($entry->hasValue("objectclasses",$User_oc{$objectname},1)) ; + $entry->addValue("objectclasses",$User_oc{$objectname},"1") ; + my $res = $conn->update($entry) ; + my $err = $conn->getErrorCode(); + if ($res) { + printTrace("\nobjectclass: $User_oc{$objectname} added",2); + } elsif ($err == 20) { # already exists + printTrace("\nobjectclass: $User_oc{$objectname} already exists",1); + } else { + printMsg("\nCan\'t add objectclass to the schema: $User_oc{$objectname}"); + my $msg = $conn->getErrorString(); + printMsg("\nMsg: $msg"); + $MigrationErrors .= "\nCan\'t add objectclass to the schema: $User_oc{$objectname}" ; + } + } + } + else { + # treat the case where the user wants to look at these objectclasses before processing + } +} + +############################################################################# +# +# Execute a Perldap command to add all user defined attributes in the new schema + +sub LDAPmodify_User_at { + my $Filename = shift ; + my @attributenames = keys(%User_at); + if (! $USER_AT_FILE_MODIFIED) { + + printTrace("\nLDAPmodify_User_at",4); + printTrace("\nMigrate attributes...",1); + foreach $attributename ( @attributenames ) { + my $entry = $conn->search("cn=schema", "base","objectclass=*") ; + printTrace("\nAtributeName: $attributename, Value: $User_at{$attributename}",3); + die "\nentry not found cn=schema\n" unless $entry ; + next if ($entry->hasValue("attributetypes",$User_at{$attributename},1) ) ; + my $res = $entry->addValue("attributetypes",$User_at{$attributename},"1") ; + if (! $res) { + printMsg("\nCan\'t add attribute to the schema: $User_at{$attributename}"); + $MigrationErrors .= "\nCan\'t add attribute to the schema: $User_at{$attributename}" ; + } + my $res = $conn->update($entry) ; + my $err = $conn->getErrorCode(); + if ($res) { + printTrace("\nattribute: $attributename added",2); + } elsif ($err == 20) { # already exists + printTrace("\nattribute: $attributename already exists",1); + } + else { + printMsg("\nCan\'t add attribute to the schema: $User_at{$attributename}"); + my $msg = $conn->getErrorString(); + printMsg("\nMsg: $msg"); + $MigrationErrors .= "\nCan\'t add attribute to the schema: $User_at{$attributename}" ; + } + } + } + else { + # treat the case where the user wants to look at these attributes before processing + } +} + +############################################################################# +# Add an object class to the user_oc hash and reset the object !!! +sub AddObjectClass { + my $ObjectClass = shift ; + my $ObjectName = $ObjectClass->{'ObjectName'} ; + my $Object_oid = $ObjectClass->{'Object_oid'} ; + my $Object_superior = $ObjectClass->{'Object_superior'} ; + my $Object_requires = $ObjectClass->{'Object_requires'} ; + my $Object_allows = $ObjectClass->{'Object_allows'} ; + my $ObjectClassDef = "( $Object_oid NAME \'$ObjectName\' DESC \'\' SUP $Object_superior STRUCTURAL MUST ($Object_requires) MAY ($Object_allows) X-ORIGIN \'user defined\' )"; + if ( (!($ObjectName =~ /^top$/i)) && ( ! $User_oc{$ObjectName} )) { + $User_oc{$ObjectName} = $ObjectClassDef ; + push @User_oc_names, $ObjectName ; + printTrace("ObjectName: $ObjectName \nObject_oid: $Object_oid \nObject_superior:$Object_superior \nObject_requires: $Object_requires \nObject_allows: $Object_allows \nObjectClassDefinition: $User_oc{$ObjectName}\n",4); + } + elsif ( ($User_oc{$ObjectName}) && ($User_oc{$ObjectName} ne $ObjectClassDef) ) { + printMsg("\nAttempt to redifine the objectclass: $ObjectName previously defined in your configuration file. Operation not allowed "); + } + else { + printMsg("\nAttempt to redifine the objectclass: top. Operation not allowed"); + } + resetObjectClass($ObjectClass); +} + +############################################################################# +# Build an LDIF attribute and add it to the user_at hash +sub AddAttribute { + my $Attr = shift ; + my $AttributeName = $Attr->{'AttributeName'}; + my $Attribute_oid = $Attr->{'Attribute_oid'}; + my $Attribute_aliases = $Attr->{'Attribute_aliases'}; + my $Attribute_syntax = $Attr->{'Attribute_syntax'}; + my $Attribute_single = $Attr->{'Attribute_single'}; + my $AttributeDef = "( $Attribute_oid NAME ( \'$AttributeName\' $Attribute_aliases) DESC \'User Defined Attribute\' SYNTAX $Attribute_syntax $Attribute_single X-ORIGIN 'user defined' )" ; + printTrace("\nAttributeDef: $AttributeDef",4); + $User_at{$AttributeName} = $AttributeDef ; +} +############################################################################# +# add the index structure to the newIndex hash +sub AddIndex { + my $ref_index = shift ; + my $state = shift ; + printTrace("\nAddIndex, last state: $state",4) ; + if ($state == 1) { + $ref_index->specific("ALL") ; + return 1 ; + } + elsif ($state == 6) { + $ref_index->specific("NONE") ; + return 1 ; + } + if (($state == 1) || ($state == 3) || ($state == 5) || ($state == 6)) { + foreach $name (@{$ref_index->names}) { + $newIndex{$name} = $ref_index ; # record the ref to the index struct in the newIndex hash + } + return 1 ; + } + else { + return 0 ; + } +} + +############################################################################# +# add the plugin structure to the stdPlugin hash + +sub AddPlugin { + my $ref_plugin = shift ; + printTrace("\nAddPlugin",4) ; + $stdPlugins{lc($ref_plugin->name)} = $ref_plugin ; + my $name = $ref_plugin->name ; + my $type = $ref_plugin->type ; + my $enable = $ref_plugin->enable ; + + printTrace("\nPluginName: $name",4); + printTrace("\nPluginType: $type",4); + printTrace("\nPluginEnable: $enable",4); + printTrace("\nPluginArgs: @{$ref_plugin->args}",4); + return 1 ; +} + + +############################################################################# +# parse a plugin definition and call the addindex + +sub ParsePlugin { + my $Plugin = shift ; + my $NumLine = shift ; + my $state = 0 ; + my $ErrorMsg = "Syntax error of a plugin definition. \n line parsed:"; + my $ref_plugin = S_plugin->new(); + printTrace("\nParsePlugin: $_",4); + if (/^plugin\s+(database|extendop|preoperation|postoperation|matchingrule|syntax)\s+(on|off)\s+\"(.*?)\"\s+\"(.*?)\"\s+(\S+)(.*)$/i) { + # $1 = <type>, $2 = <on|off>, $3 = <name>, $4 = <pathname>, $5 = <init_function>, $6 = [<arg>]* + $ref_plugin->name($3); + $ref_plugin->type($1); + $ref_plugin->enable($2); + $_ = $6 ; + my $ArgNb = 0 ; + my $prec ; + my $arg ; + my $Unix_oldDir = $oldDir ; + my $Unix_root = $root ; + grep { s@\\@/@g } $Unix_oldDir if $isNT; + grep { s@\\@/@g } $Unix_root if $isNT; + while (!(/^\s*$/)) { + if (/^\s*\".*?\"/) { + s/^\s*\"(.*?)\"(.*)/$2/i ; + $arg = $1 ; + } + elsif (/^\s*[^\"\s]+/) { + s/^\s*([^\"\s]+)(.*)/$2/i ; + $arg = $1 ; + } + $prec = $_ ; + $_ = $arg ; + + s@$Unix_oldDir@$Unix_root@ig ; + s/$type-$oldname/$type-$newname/ig ; + @{$ref_plugin->args}[$ArgNb++] = $_ ; + $_ = $prec ; + } + if (/^\s*$/) { + return AddPlugin($ref_plugin); + } + else { + return 0 ; + } + } + return 0 ; +} + +############################################################################# +# parse an index definition and call the addindex + +sub ParseIndex { + my $index = shift ; + my $NumLine = shift ; + my $ref_index = S_index->new() ; + my $Value ; + my $state = 0 ; + my $ErrorMsg = "Syntax error of an index definition.\nline parsed:"; + printTrace("\nParseIndex: $_",4) ; + s/,/, /g ; + s/\s+,/,/g ; + s/^index\s+//i ; # substitute the token index + while (!(/^\s*$/)) { + s/^\s*(\S+)(.*)$/$2/ ; + $Value = $1 ; + printTrace("\nValue: $Value",4); + printTrace("\nState: $state",4) ; + SWITCH: { + if ($state == 0) { + if ($Value =~ /[^\.]/) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->names}, $1 ; + } + else { + $state = 1 ; + push @{$ref_index->names}, $Value ; + } + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 1) { + if ($Value =~ /^none$/i) { + $state = 6 ; # end of the index definition + } + elsif ($Value =~ /^\"\"$/) { + $state = 4 ; # we expect to have at least one OID + } + elsif ($Value =~ /(\S+),$/) { + $state = 2 ; + push @{$ref_index->types}, $1 ; + } + else { + $state = 3 ; + push @{$ref_index->types}, $Value ; + } + last SWITCH ; + } + if ($state == 2) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->types}, $1 ; + } + else { + $state = 3 ; + push @{$ref_index->types}, $Value ; + } + last SWITCH ; + } + if ($state == 3) { + if ($Value =~ /(\S+),$/) { + $state = 4 ; + push @{$ref_index->oids}, $1 ; + } + else { + $state = 5 ; + push @{$ref_index->oids}, $Value ; + } + last SWITCH ; + } + if ($state == 4) { + if ($Value =~ /(\S+),$/) { + push @{$ref_index->oids}, $1 ; + } + else { + $state = 5 ; + push @{$ref_index->oids}, $Value ; + } + last SWITCH ; + } + } + } +return AddIndex($ref_index,$state) ; + +} + +############################################################################# + +sub ParseAttribute { + + + my $Attr = shift ; + my $NumLine = shift ; + my $state = 1 ; + my $ErrorMsg = "Syntax error of an attribute definition.\nline parsed:"; + my %Attribute = ( + 'AttributeName' => "", + 'Attribute_oid' => "", + 'Attribute_aliases' => "", + 'Attribute_syntax' => "", + 'Attribute_single' => "" + ); + my $AttributeName = " "; + printTrace("\nParseAttribute",4); + while (!(/^\s*$/)) { + s/^(.*?)(\S+)\s*$/$1/ ; + printTrace("\nValue: $2",4); + printTrace("\nState: $state",4) ; + my $Value = $2 ; + SWITCH: { + if ($state == 1) { + if (isAllowedModifier($Value)) { + $state = 1 ; + $modifier = lc($Value); + $AttrVar = 'Attribute_' . $modifier ; + $Attribute{$AttrVar} = &getModifierValue($Value) ; + } + elsif (&isAllowedPlugin($Value)) { + $state = 2 ; + $Attribute{'Attribute_syntax'} = &getSyntaxOid($Value) ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 2) { + if ($Value =~ /[\.]|-oid$/) { + $Attribute{'Attribute_oid'} = "$Value" ; + printTrace("\nAttribute-oid: $Attribute{'Attribute_oid'}",3); + $state = 3 ; + } + elsif ($Value =~ /[^\.]/) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + $state = 4 ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 3) { + if ($Value =~ /[^\.]/) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + $state = 4 ; } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 4) { + if ($Value =~/^attribute$/i){ + $state = 5; + } + elsif ($Value =~/[^\.]/i) { + $AttributeName = $Attribute{'AttributeName'} ; + if ($AttributeName) { $Attribute{'Attribute_aliases'} .= "\'$AttributeName\' " ;} + $Attribute{'AttributeName'} = $Value ; + } + else { + return 0 ; + } + last SWITCH ; + } + if ($state == 5) { + return 0 ; + last SWITCH ; + } + } + } + $Attribute{'Attribute_oid'} = $Attribute{'AttributeName'} . '-oid' unless ($Attribute{'Attribute_oid'}) ; + return AddAttribute(\%Attribute) ; +} + + +############################################################################# +# fill in the hash HashParametersName + +sub FillHashParametersName { + my @paramnames = ( keys(%GeneralSrvParamToMigrate), keys(%GlobalConfigLDBMparamToMigrate), keys(%LDBMparamToMigrate)); + foreach $param (@paramnames) { + $HashParametersName{$param} = '\n'; + } +} + + +# Parse parameters +sub ParseParameters { + my $param = shift ; + my $value = shift ; + my $NumLine = shift ; + my $ErrorMsg = "parameter unknown, or not to be migrated: "; + if ($HashParametersName{lc($param)} && ($value !~ /^\s*$/)) { + $HashParametersName{lc($param)} = $value ; + printTrace("\nParam: $param is present",4); + } + else { + printTrace("\n$NumLine, $ErrorMsg,$param",4); + } + +} + +# add general server parameters +sub AddGeneralParameters { + my @paramnames = keys(%GeneralSrvParamToMigrate); + my $entry = $conn->search("cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=config. \nMigration stopped\n" unless ($entry); + printTrace("\nAddGeneralParameters",4); + foreach $param (@paramnames) { + my $LDAPparam = $GeneralSrvParamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam ",0); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } +} + + +# add general LDBM parameters +sub AddGeneralLDBMParameters { + my @paramnames = keys(%GlobalConfigLDBMparamToMigrate); + my $entry = $conn->search("cn=config,cn=ldbm database,cn=plugins,cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=config,cn=ldbm database,cn=plugins,cn=config. \nMigration stopped\n" unless ($entry); + printTrace("\nAddGeneralLDBMParameters",4); + foreach $param (@paramnames) { + my $LDAPparam = $GlobalConfigLDBMparamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam ",0); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } +} + +# add specific LDBM parameters +sub AddSpecificLDBMParameters { + my @paramnames = keys(%LDBMparamToMigrate); + my %REV_DBNAMES = reverse %DBNAMES ; + my @dbnames = keys(%REV_DBNAMES); + printTrace("\nAddSpecificLDBMParameters",4); + foreach $dbname (@dbnames) { + my $entry = $conn->search("cn=$dbname,cn=ldbm database,cn=plugins,cn=config","base","objectclass=*"); + die "\ncan't access to object: cn=$dbname,cn=ldbm database,cn=plugins,cn=config. \nMigration stopped\n" unless ($entry); + foreach $param (@paramnames) { + my $LDAPparam = $LDBMparamToMigrate{$param} ; + my $Value = $HashParametersName{$param} ; + if (($Value ne '\n') && ($entry->{$LDAPparam}[0] ne $Value)) { + printTrace("\nLDAPparam: $LDAPparam, Value: $Value",4); + $entry->setValues($LDAPparam, $Value); + my $res = $conn->update($entry); + if ($res) { + printTrace("\nUpdate successfully $LDAPparam",2); + } + else { + printMsg("\nCan't update parameter: $LDAPparam"); + } + } + } + } +} + +############################################################################# +# Parse a configuration file potentialy tuned by the user (different from slapd.user_oc.conf and slapd.user_at.conf) + +sub ParseConfigurationFile { + + my $FileToParse = shift; + my $NumLine = 0; + my $PARSE_OBJECTCLASSES = 0 ; # 1 if there are objectclass definitions in the file + printTrace("\nParseConfigurationFile: $FileToParse",4) ; + printTrace("\nParse $FileToParse",2); + # read each line of the configuration file + my $CONFIGFILE = "CONFIGFILE.$FileToParse" ; + open( $CONFIGFILE, $FileToParse ) || die "Can't open $FileToParsec: $!: "; + LINE: while ( <$CONFIGFILE> ) { + $NumLine++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } elsif (/^suffix\s+/i) { + chomp($_) ; + CheckSuffix($_) ; + } elsif (/^plugin/i) { + chomp($_); + if (! &isAStandardPlugin($_)) { + push @badPlugins, $_; + } + else { + my $Plugin = $_ ; + if (! &ParsePlugin($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of the plugin:\n$Plugin"); + } + } + } elsif (/^index/i) { + chomp($_); + if (! &isAStandardIndex($_)) { + my $Index = $_ ; + if (! &ParseIndex($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of index:\n$Index"); + } + } + } elsif (/^include\s+[\"]?(.*?)[\"]?\s*$/i) { + # strip leading and trailing " + my $include_file = $1 ; + grep { s@/@\\@g } $include_file if $isNT; + if (! &isAStandardInclude($include_file)) { + &ParseConfigurationFile($include_file); + } + } elsif (/^attribute\s+\S+/i) { + chomp($_); + my $Attrib = $_ ; + if (! &ParseAttribute($_,$NumLine)) { + printMsg("\nLine $NumLine, syntax error of attribute:\n$Attrib"); + } + } elsif (/^objectclass\s+(\S+)\s*$/i) { + # At least one objectclass is present in the file + $PARSE_OBJECTCLASSES = 1; + } elsif (/^\s*(\S+)\s+[\"]?(.*?)[\"]?\s*$/) { + # Parse parameters and record the associated value in %Oldhash + &ParseParameters($1,$2,$NumLine); + } + } + close($CONFIGFILE); + ParseObjectClassesFile($FileToParse) if ($PARSE_OBJECTCLASSES); # parse objectclass definition + +} + +############################################################################# +# Parse the file specified in the userat attribute + +sub ParseAttributesFile { + my $userat_file=shift ; + my $NumLine = 0; + printTrace("\nParseAttributesFile: $userat_file",4); + printTrace("\nParse user defined attributes file: $userat_file",2); + # read each line of the configuration file + open( ATTRFILE, $userat_file ) || die "Can't open $FileToParsec: $!: "; + LINE: while ( <ATTRFILE> ) { + $NumLine++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } elsif (/^attribute\s+\S+/i) { + chomp($_); + my $Attrib = $_ ; + if (! &ParseAttribute($_, $NumLine)) { + printMsg("\nLine $NumLine, syntax error of attribute:\n$Attrib"); + } + } + } + close(ATTRFILE); +} + +############################################################################# +# Parse the file specified in the useroc token + +sub ParseObjectClassesFile { + my $useroc_file = shift ; + my %ObjectClass = ( + 'ObjectName' => " ", + 'Object_oid' => " ", + 'Object_superior' => "top", + 'Object_requires' => " ", + 'Object_allows' => " " + ); + + my $state = 0; + my $ErrorMsg = "Syntax error of an object class definition.\nline parsed:"; + my $LineNb = 0 ; # Number of the current line parsed in the file + printTrace("ParseObjectClassesFile: $useroc_file\n",4) ; + # read each line of the configuration file + open( OBJCLASSFILE, $useroc_file ) || die "Can't open $FileToParsec: $!: "; + printTrace("Begin the parsing of the file: $useroc_file",4); + LINE: while ( <OBJCLASSFILE> ) { + printTrace("Current Line: $_",4); + $LineNb++ ; + if (/^\s*\#/) { # skip comments + next LINE; + } + if (/^\s*$/) { # skip blank lines + next LINE; + } + SWITCH: { + if ($state == 0) { resetObjectClass(\%ObjectClass); + if (/^objectclass\s+(\S+)\s*$/i) { + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + else {} # printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 1) {if (/^\s+oid\s+(\S+)\s*$/i) { + $ObjectClass{'Object_oid'} = $1; + $state = 2 ;} + elsif (/^\s+superior\s+(\S+)\s*$/i) { + $ObjectClass{'Object_superior'} = $1; + $state = 3 ; + } + elsif (/^\s+requires\s*$/i) { + $state = 4; + } + elsif (/^\s+allows\s*$/i) { + $state = 5; + } + else {$state=0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 2) {if (/^\s+superior\s+(\S+)\s*$/i) { + $ObjectClass{'Object_superior'} = $1; + $state = 3 ;} + elsif (/^\s+requires\s*$/i) { + $state = 4; + } + elsif (/^\s+allows\s*$/i) { + $state = 5; + } + else { $state=0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 3) {if (/^\s+requires\s*$/i) + { $state = 4; } + elsif (/^objectclass\s+(\S+)\s*$/i) { + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + elsif (/^\s+allows\s*$/i) + { $state = 5; } + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 4) {if (/^\s+([^,\s]+),\s*$/i) { + $ObjectClass{'Object_requires'}.=$1." \$ "; } + elsif (/^\s+([^,\s]+)\s*$/i) { + $ObjectClass{'Object_requires'}.=$1." "; + $state = 6; } + else {$state = 0;printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 5) {if (/^\s+([^,\s]+),\s*$/i) { + $ObjectClass{'Object_allows'}.=$1." \$ "; } + elsif (/^\s+([^,\s]+)\s*$/i) { + $ObjectClass{'Object_allows'}.=$1." "; + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $state = 0; } + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + if ($state == 6) {if (/^objectclass\s+(\S+)\s*$/i) { + # run an ldap add before to continue + &AddObjectClass(\%ObjectClass); + $ObjectClass{'ObjectName'} = $1; + $state = 1 ;} + elsif (/^\s+allows\s*$/i) { + $state = 5;} + else {$state = 0; printMsg($ErrorMsg,$_,$LineNb);} + last SWITCH;} + } + } + close(OBJCLASSFILE); + if (($state == 3) || ($state == 4) || ($state == 5) || ($state == 6)) { + &AddObjectClass(\%ObjectClass); + } + printTrace("state: $state",4); +} + +############################################################################# +# printMsg print message to the user standard output. + +sub printMsg { + + my $TypeMsg = shift ; + my $Msg = shift ; + my $LineNb = shift ; + if ($LineNb) { + printTrace("Line: $LineNb, $TypeMsg, $Msg"); + } + else { + printTrace("$TypeMsg $Msg"); + } +} + +############################################################################# +# print message error to the user standard output. + +sub printTrace { + + my $Msg = shift ; + my $level = shift ; + if ($level <= $TRACELEVEL) { + print($Msg); + print LOGFILE $Msg ; + } +} + +############################################################################# +# reset an objectclass structure + +sub resetObjectClass { + my $ObjectClass = shift; + $ObjectClass->{'ObjectName'} = " " ; + $ObjectClass->{'Object_oid'} = " " ; + $ObjectClass->{'Object_superior'} = "top" ; + $ObjectClass->{'Object_requires'} = " " ; + $ObjectClass->{'Object_allows'} = " " ; +} + +############################################################################# +# this subroutine implements a very stupid version of diff + +sub diff { + my $f1 = shift; + my $f2 = shift; + my $lineToBeginWith = shift; + my $NULL = "" ; + my $diff_f1 = $NULL ; + my $diff_f2 = $NULL ; + my $retval = $NULL ; + my $ret; + open(F1, "$f1") or die "Could not open file $f1"; + open(F2, "$f2") or close(F1), die "Could not open file $f2"; + + while (defined($l1 = <F1>)) { + if ($lineToBeginWith){ + $lineToBeginWith -- ; + next ; + } + next if ($l1 =~ /^\#/); + $ret = defined($l2 = <F2>); + if ($ret) { + $ret = defined($l2 = <F2>) while ($ret && ($l2 =~ /^\#/)) ; + if ($ret) { + if (!($l1 eq $l2)) { + + # ignore whitespace + $l1_clean = $l1 ; + $l2_clean = $l2 ; + $l1_clean =~ s/\s//g; + $l2_clean =~ s/\s//g; + + if (!($l1_clean eq $l2_clean)) { + $diff_f1 .= "${l1}" unless ($l1_clean eq $NULL); + $diff_f2 .= "${l2}" unless ($l2_clean eq $NULL); + } + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + else { + next if ($l1 =~ /^\s*$/) ; + $diff_f1 .= "${l1}"; + } + } + + while (defined($l2 = <F2>)) { + if (($l2 =~ /^\#/) || ($l2 =~ /^\s*$/)) { + next ; + } + else { + $diff_f2 .= "${l2}" ; + } + } + + close(F1); + close(F2); + + $retval .= "- differences present in your config file but not in standard file:\n\n". "$diff_f1\n" if ($diff_f1) ; + $retval .= "- differences present in standard file but not in your config file:\n\n" . "$diff_f2" if ($diff_f2) ; + return $retval ; +} + +sub CompareStdConfigFiles { + # Compare each configuration file against its default version. If it has changed, + # notify the user that the file has changed and will need to be checked by the + # user. This should be safe to do because there should be no path information + # stored in these conf files, which are just schema stuff. + # printTrace("\nCheck if standard configuration files have changed",3); + + my $origFilePath = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}install${PATHSEP}config${PATHSEP}" ; + my $FilesChanged = ""; + my $AllDiffs = "***********************************************************************"; + my $NoChanges = "" ; + my $lineToBegin = 0 ; + printTrace("\nVersion of the old directory server: $oldVersion.$oldMinor",0); + opendir(CONFDIR, $oldConfDir) or + die "Error: could not open migrated config dir $oldConfDir: $!"; + + foreach $file (readdir(CONFDIR)) { + $origFile = $origFilePath . $file ; + $configFile = $oldConfDir . $file ; + if ((! exists($userDefinedConfigFiles{lc($file)})) && (-f $origFile)) { + my $lineToBegin = 1 if (lc($file) eq "slapd-collations.conf"); # we ignore the first line of slapd-collations + $diffs = &diff($configFile, $origFile, $lineToBegin); + $lineToBegin = 0 if $lineToBegin ; + if ($diffs) { + $FilesChanged .= "\n$configFile"; + $AllDiffs .= "\n$configFile is different than the standard configuration file" ; + $AllDiffs .= "\nYou will need to check this file and make sure its changes are compatible "; + $AllDiffs .= "with the new directory server\nHere are the differences:\n"; + $AllDiffs .= "$diffs \n\n"; + $AllDiffs .= "***********************************************************************"; + } + else { + $NoChanges .= "\n$configFile"; + } + } + } + closedir(CONFDIR); + +if ($FilesChanged) { + printTrace("\nNo changes to old configuration files:$NoChanges",3) ; + printTrace("\n***********************************************************************",3) ; + printMsg("\nThe following standard files have been modified: $FilesChanged"); + if ($NO_INPUT_USER) { + # do nothing + } + else { + printMsg("\nDo you want to see the differences Yes/No [No] ?") ; + my $answer = <STDIN> ; + if ($answer =~ /y|yes/i) { + printMsg("$AllDiffs"); + } + printMsg("\nDo you want to continue the migration Yes/No [No] ?"); + $answer = <STDIN> ; + if (! ($answer =~ /y|yes/i)) { + exit(1); + } + } + } +} + + +############################################################################# + +sub db2ldif { + my ($conf, $ldif_dir) = @_; + $ENV{"$LIB_PATH"}=$old_libpath; + if (!$conf) { + $conf = "$oldHome${PATHSEP}config${PATHSEP}slapd.conf"; + } + if (! $ldif_dir) { $ldif_dir = $ldif_rep ;} + if (!(-d $ldif_dir)) { + mkdir($ldif_dir,0777) or die "can't create $ldif_rep to store temporary ldif files"; + } + my $dir = "$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server"; + chdir($dir) or + die "Error: could not change directory to $dir: $!"; + my @suffixnames = keys(%DBNAMES) ; + foreach $suffixname (@suffixnames) { + my $ldif_file = $ldif_dir.$DBNAMES{$suffixname}.".ldif" ; + # If we are on NT, ${quote} is setup to "\"", else it's setup to "" + # As the suffix can contain some space characters, I write the suffix parameter: "\"$suffixname\"" rather than "${quote}$suffixname${quote}" + my @cmd = + ( "${quote}$oldDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server" . + "${PATHSEP}$slapdExecName${quote}", "db2ldif", '-n', '-f', + "${quote}$conf${quote}", '-a', "${quote}$ldif_file${quote}", + '-d', '1','-s',"\"$suffixname\"" ); + open(DB2LDIF, "${quote}@cmd${quote} 2>&1|") or + die "Error: could not execute @cmd: $!"; + sleep(1); # allow pipe to fill with data + $ii = 0; # counter + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the new slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif_file) { + chown( $newuid, $newgid, $ldif_file) or printMsg("\nUnable to change the ownership of $ldif_file to $localuser") ; + } + } + } + print " Done.\n"; + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + +############################################################################# +# This db2ldif is used to export database of the new instance + +sub newinst_db2ldif { + my $ldif = shift ; + my $include_suffix = shift ; + my $home = shift ; + my $db2ldif_param = "db2ldif -r -D $home -a $ldif -s \"$include_suffix\""; + + open(DB2LDIF, "${quote}${quote}$slapdExecName${quote} $db2ldif_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + my $ii = 0; + while (<DB2LDIF>) { + ++$ii; + if (($ii % 250) == 0) { + printMsg(" Processing...\n"); + } + printMsg($_); + } + close(DB2LDIF); + # set the ownership of the ldif file; should be the same as the 5.x slapd user id + if ((! $isNt) && ($oldlocaluser ne $localuser)) { + if (-f $ldif) { + chown( $newuid, $newgid, $ldif) or printMsg("\nUnable to change the ownership of $ldif to $localuser") ; + } + } +} + +############################################################################# + +# this is used to run the system() call, capture exit and signal codes, +# and die() upon badness; the first argument is a directory to change +# dir to, if any, and the rest are passed to system() +sub mySystem { + my $rc = &mySystemNoDie(@_); + my ($dir, @args) = @_; + if ($rc == 0) { +# success + } elsif ($rc == 0xff00) { + die "Error executing @args: error code $rc: $!"; + } elsif ($rc > 0x80) { + $rc >>= 8; + die "Error executing @args: error code $rc: $!"; + } else { + if ($rc & 0x80) { + $rc &= ~0x80; + } + die "Error executing @args: received signal $rc: $!"; + } + + # usually won't get return value + return $rc; +} + +# This version does not die but just returns the error code +sub mySystemNoDie { + my ($dir, @args) = @_; + if ($dir && ($dir ne "")) { + chdir($dir) or die "Could not change directory to $dir: $!"; + } + my $cmd = $args[0]; + # the system {$cmd} avoids some NT shell quoting problems if the $cmd + # needs to be quoted e.g. contains spaces; the map puts double quotes + # around the arguments on NT which are stripped by the command + # interpreter cmd.exe; but don't quote things which are already quoted + my @fixargs = map { /^[\"].*[\"]$/ ? $_ : $quote . $_ . $quote } @args; + my $rc = 0; + if ($cmd =~ /[.](bat|cmd)$/) { + # we have to pass batch files directly to the NT command interpreter + $cmd = $com_spec; +# print "system $cmd /c \"@fixargs\"\n"; + $rc = 0xffff & system {$cmd} '/c', "\"@fixargs\""; + } else { +# print "system $cmd @fixargs\n"; + $rc = 0xffff & system {$cmd} @fixargs; + } + chdir(${curdir}) or die "Could not change directory to $curdir: $!"; + return $rc; +} + +############################################################################# +sub manyLdif2db { + my %rev_dbnames = reverse(%DBNAMES); + @backends = keys(%rev_dbnames); + $ENV{"$LIB_PATH"}=$new_libpath; + chdir($slapdExecDir) or die "Could not change directory to $slapdExecDir: $!"; + foreach $backend (@backends) { + my $ldif = "${ldif_rep}$backend.ldif" ; + if (! -f $ldif) { + $ldif = ${ldif_rep}."data.ldif"; + } + &Ldif2db($ldif, $backend); + } + # remove the empty ldif directory + # but not if using the data dir + if (!$olddatadir) { + rmdir($ldif_rep); + } + chdir($curdir) or die "Could not change directory to $curdir: $!"; +} + + +sub Ldif2db { + my $ldif = shift ; + my $backend = shift ; + my $ldif2db_param = "ldif2db -D $serverHome -n $backend -i $ldif"; + open(LDIF2DB, "${quote}${quote}$slapdExecName${quote} $ldif2db_param${quote} 2>&1 |") or die "Could not run ns-slapd program $ldif2db_exe\n"; + sleep(1); # allow some data to accumulate in the pipe + while (<LDIF2DB>) { + printMsg($_); + } + close(LDIF2DB); + # remove the ldif file after the import + # but not if using the data dir + if (!$olddatadir) { + unlink($ldif) ; + } +} + +############################################################################# + +#sub copyBak { +# opendir( OLDBAK, "$oldHome${PATHSEP}bak" ) || +# die "Can't open directory $oldHome${PATHSEP}bak: $!: "; +# local ( @dirs ) = readdir( OLDBAK ); +# closedir ( OLDBAK ); +# for ( @dirs ) { +# if ( $_ eq "." || $_ eq ".." ) { +# next; +# } elsif ( -d "$oldHome${PATHSEP}bak${PATHSEP}$_" ) { +# $srcDir = "$oldHome${PATHSEP}bak${PATHSEP}$_"; +# $destDir = "$serverHome${PATHSEP}bak${PATHSEP}$_"; +# $srcLDIF = "$oldHome${PATHSEP}ldif${PATHSEP}bak.ldif"; +# $destLDIF = "$serverHome${PATHSEP}ldif${PATHSEP}bak.ldif"; +# mkdir( $destDir , 0755 ) if !( -e $destDir); +# # Converting database +# if ( !$isNT && $newuser ) { +# chown($newuid, $newgid, +# "$serverHome${PATHSEP}bak", $destDir); +# } +# &other_db2ldif($srcDir, $srcLDIF); +# if ($needAclUpg) { +# &mySystem("$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server", +# "$root${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server" . +# "${PATHSEP}aclupg$exe_suffix", '-d', '-i', +# $srcLDIF, '-o', $destLDIF); +# } else { +# ©BinFile($srcLDIF, $destLDIF); +# } +# &other_ldif2db($destLDIF, $destDir); +# } +# } +#} +############################################################################# + +sub startServer { + my $instanceDir = ${serverHome} ; + my $errLog = $instanceDir . $PATHSEP . 'logs' . $PATHSEP . 'errors'; + # emulate tail -f + # if the last line we see does not contain "slapd started", try again + my $done = 0; + my $started = 0; + my $code = 0; + my $lastLine = ""; + my $timeout = time + 240; # 4 minutes + $ENV{"$LIB_PATH"}=$new_libpath; + + my $startCmd = $instanceDir . $PATHSEP . 'start' . $script_suffix; + if (! -f $startCmd) { + $startCmd = $instanceDir . $PATHSEP . 'start-slapd' . $script_suffix; + } + printTrace("\nInstanceDir: $instanceDir\n",4); + $code = &mySystem($instanceDir,$startCmd); + open(IN, $errLog) or die "Could not open error log $errLog: $!"; + my $pos = tell(IN); + while (($done == 0) && (time < $timeout)) { + for (; ($done == 0) && ($_ = <IN>); $pos = tell(IN)) { + $lastLine = $_; + # print; + # the server has already been started and shutdown once . . . + if (/slapd started\./) { + $started++; + if ($started == 2) { + $done = 1; + } + # sometimes the server will fail to come up; in that case, restart it + } elsif (/Initialization Failed/) { + # print "Server failed to start: $_"; + $code = &mySystem($instanceDir, $startCmd); + # sometimes the server will fail to come up; in that case, restart it + } elsif (/exiting\./) { + # print "Server failed to start: $_"; + #$code = &mySystem($startCmd); + + $code = &mySystem($instanceDir, $startCmd); + } + } + if ($lastLine =~ /PR_Bind/) { + # server port conflicts with another one, just report and punt + print $lastLine; + print "This server cannot be started until the other server on this\n"; + print "port is shutdown.\n"; + $done = 1; + } + if ($done == 0) { + # rest a bit, then . . . + sleep(2); + # . . . reset the EOF status of the file desc + seek(IN, $pos, 0); + } + } + close(IN); + + if ($started < 2) { + $! = $code; + # $now = time; + # if ($now > $timeout) { + # print "Possible timeout: timeout=$timeout now=$now\n"; + # } + die "Error: could not start server: $!"; + } + + return 0; +} + +sub stopServer { + my $root = shift; + my $name = shift; + $maxStopIterations = 5; + print "\nShutting down server $name . . .\n"; + + $ENV{"$LIB_PATH"}=$new_libpath; + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop' . $script_suffix . $quote; + if (! -f $stopCmd) { + $stopCmd = $quote . $root . $PATHSEP . $name . $PATHSEP . 'stop-slapd' . $script_suffix . $quote; + } + + if (! -f $stopCmd) { + # no stop command, probably a 1.X system; for NT, we'll try net stop + # for unix, we'll get the pid and kill it + if ($isNT) { + $stopCmd = 'net stop ' . $name; + } else { + # see if there is a pid file + $pidfile = $root . $PATHSEP . $name . $PATHSEP . 'logs' . + $PATHSEP . 'pid'; + if (open(PIDFILE, $pidfile)) { + chomp($pid = <PIDFILE>); + close(PIDFILE); + while ($maxStopIterations-- && !$exitCode) { + $exitCode = kill(15, $pid); + } + $stopCmd = undef; + } + } + } + + # keep looping until the stop cmd returns an error code, which usually + # means that what ever we want to stop is stopped, or some other error + # occurred e.g. permission, or no such service + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + while ($stopCmd && $maxStopIterations-- && $exitCode) { + $exitCode = &runAndIgnoreOutput($stopCmd); +# print "stopServer: exitCode=$exitCode\n"; + } + + if (!$maxStopIterations) { + print "Warning: could not shutdown the server: $!\n"; + } + + sleep(10) ; + + $exitCode = 0; + +} + + +sub runAndIgnoreOutput { + my $cmd = shift; + printMsg("."); + open(RUNCMD, "${quote}$cmd${quote} 2>&1 |") or die "Error: could not run $cmd: $!"; + printMsg("."); + sleep(1); # allow pipe to fill with data + printMsg("."); + while (<RUNCMD>) { +# print; + } + my $code = close(RUNCMD); +# print "runAndIgnore: code=$code status=$?\n"; + return $?; +} +############################################################################# +# migrate some of entries present in the old DSE.ldif like +# cn=snmp,cn=config +# cn=encryption,cn=config +# all the aci's + +sub MigrateDSE { + printTrace("\nMigrate DSE entries...",1); + open( DSELDIF, "< $oldDSEldif" ) || die "Can't open $oldDSEldif: $!: "; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($old_entry = readOneEntry $in) { + my $DN = $old_entry->getDN() ; + SWITCH: { + # migrate the entrie: cn=snmp,cn=config + if ($DN =~ /^cn=SNMP,cn=config$/i) { + my $entry = $conn->search("$DN","base","objectclass=nsSNMP"); + if ($entry) { + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + printMsg("\nUnable to get info under $DN"); + } + last SWITCH; + } + # migrate the entrie: cn=encryption,cn=config + if ($DN =~ /cn=encryption,cn=config$/i) { + if ($conn->search("$DN","base","objectclass=*")) { + if ($old_entry->hasValue("objectClass", "nsEncryptionConfig")) { + my $certfile = "alias/slapd-" . $newname . "-cert8.db"; + my $keyfile = "alias/slapd-" . $newname. "-key3.db"; + $old_entry->setValues("nsCertfile",$certfile) if ! $old_entry->hasValue("nsCertfile",$certfile); + $old_entry->setValues("nsKeyfile",$keyfile) if ! $old_entry->hasValue("nsKeyfile",$keyfile); + } + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + my $res = $conn->add($old_entry); + if ($res) { + printTrace("\n$DN added !",2); + } + else { + printMsg("\nFailed to add $DN"); + } + } + last SWITCH; + } + if (@{$old_entry->{aci}} && (! ($DN =~ /^cn=monitor$/i)) && (! ($DN =~ /^cn=schema$/i))) { + # migrate aci's + my $entry = $conn->search("$DN","base","objectclass=*"); + if ($entry) { + my $res = $conn->update($old_entry); + if ($res) { + printTrace("\n$DN updated !",2); + } + else { + printMsg("\nFailed to update $DN"); + } + } + else { + my $res = $conn->add($old_entry); + if ($res) { + printTrace("\n$DN added !",2); + } + else { + printMsg("\nFailed to add $DN"); + } + } + last SWITCH; + } + } + } + close(DSELDIF); +} +############################################################################# +# migrate SSL info + +sub MigrateSSL { + my $secPwd = 'bidon' ; + # copy the SSL directory + ©Dir("$oldHome${PATHSEP}ssl","$serverHome${PATHSEP}ssl"); + # copy the cert db and key files + if ( -d "$oldDir${PATHSEP}alias") { + $aliasDir = "$root${PATHSEP}alias"; + if (! -d $aliasDir) { + mkdir($aliasDir, 0750); + } + &stopServer($root,'slapd-'.$newname); + my $keydb = "$aliasDir${PATHSEP}slapd-$newname-key3.db" ; + my $certdb = "$aliasDir${PATHSEP}slapd-$newname-cert8.db" ; + my $certdb7 = "$aliasDir${PATHSEP}slapd-$newname-cert7.db" ; + my $old_keydb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-key3.db" ; + my $old_certdb = "$oldDir${PATHSEP}alias${PATHSEP}slapd-$oldname-cert7.db"; + my $keydb_backup = "$aliasDir${PATHSEP}slapd-$newname-key3.db_backup" ; + my $certdb_backup = "$aliasDir${PATHSEP}slapd-$newname-cert7.db_backup" ; + if (-f $old_keydb) { + if (-f $keydb) { + if ($NO_INPUT_USER) { + printMsg("\n$keydb already exists. backup in $keydb_backup ..."); + ©BinFile($keydb,$keydb_backup); + ©BinFile($old_keydb,$keydb); + } + else { + print("\n\n$keydb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + ©BinFile($old_keydb,$keydb); + } + } + } + else { + ©BinFile($old_keydb,$keydb); + } + } + if (-f $old_certdb) { + $mode = (stat($old_certdb))[2] if $PRESERVE; + if (-f $certdb) { + if ($NO_INPUT_USER) { + printMsg("\n$certdb already exists. backup in $certdb_backup ..."); + ©BinFile($certdb,$certdb_backup); + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + else { + print("\n\n$certdb already exists. Do you want to overwrite it ? [no]: "); + my $answer = <STDIN> ; + if ($answer =~ /^y|yes$/i) { + unlink($certdb) || print "Couldn't delete $certdb : $!\n"; + ©BinFile($old_certdb,$certdb7); + } + } + } + else { + ©BinFile($old_certdb,$certdb7); + } + } + # copy the old password file + if (-f "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt") { + ©BinFile( + "$oldDir${PATHSEP}alias${PATHSEP}$type-$oldname-pin.txt", + "$aliasDir${PATHSEP}$type-$newname-pin.txt" + ); + } + &startServer(); + if ($PRESERVE) { + chown($newuid,$newgid,$certdb) || print "Failed to set uid $newuid gid $newgid on $certdb : $!\n"; + chmod($mode,$certdb) || print "Failed to set mode $mode on $certdb : $!\n"; + } + } + +} + +sub DisableSSL { + my $entry = $conn->search("cn=config","base","objectclass=*"); + my $LDAPparam = "nsslapd-security" ; + my $Value = "off" ; + if ($entry->{$LDAPparam}[0] ne $Value) { + printTrace("\nDisable SSL...",1); + $entry->setValues($LDAPparam, $Value); + } + my $res = $conn->update($entry); + if ($res) { + printTrace("\nSSL disabled",2); + } + else { + printMsg("\nCan't disable SSL, the server may have problems starting"); + } +} + +# enable the migration of client authentication informations +sub MigrateCertmap { + # backup the old new certmap.conf and replace it with the old certmap.conf file + my $oldCertmap = "$oldDir${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf"; + my $newCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf" ; + my $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + if (&hasChangedoldCertmap($oldCertmap)) { + if ($NO_INPUT_USER) { + printMsg("\n$newCertmap has been backup in $backupCertmap"); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up the file $newCertmap [$backupCertmap] ?") ; + my $Answer = <STDIN> ; + $backupCertmap = $Answer if ($Answer ne "\n"); + chomp($backupCertmap); + printTrace("\nDest: .$backupCertmap.",4); + if (-e $backupCertmap) { + printMsg("\n\n$backupCertmap already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $backupCertmap = "$root${PATHSEP}shared${PATHSEP}config${PATHSEP}certmap.conf_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup file: $newCertmap in $backupCertmap",4); + ©BinFile($newCertmap,$backupCertmap); + ©BinFile($oldCertmap,$newCertmap); + } + } + else { + } +} + +sub hasChangedoldCertmap { + my $certmapfile = shift ; + my @reference = ("certmap default default", + "default:DNComps", + "default:FilterComps e") ; + my $cpt = 0 ; + printTrace("\nhasChangedoldCertmap",3); + open(CERTMAP,"< $certmapfile"); + while (<CERTMAP>) { + if ((! /^\s*#/) && (! /^\s*$/)) { + my $ref = $reference[$cpt] ; + printTrace("\nValue: $_, ref: $ref",4); + if (! /^\s*$ref\s*$/) { + return 1 ; + } + else { + $cpt++ ; + } + } + } + close (CERTMAP); + printTrace("\ncpt: $cpt",4); + if ($cpt < $#reference) { + return 1 ; + } + else { + return 0 ; + } +} +############################################################################# +# copy a directory to another + +sub copyDir { + my $src = shift; + my $dest = shift; + my $exclude = shift; + + opendir( SRC, $src ) or die "Can't open directory $src: $!: "; + my $mode; + my $uid; + my $gid; + mkdir ( $dest , 0755 ) or die "\nCan't create directory $dest. \nPlease check you have enough rights to create it and/or check that your parent directory exists.\n" if !( -e $dest ); + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + local ( @files ) = readdir ( SRC ); + closedir( SRC ); + for ( @files ) { + if ( $_ eq "." || $_ eq ".." ) { + next; + } elsif ( $exclude && /$exclude/ ) { + next; + } elsif( -d "$src${PATHSEP}$_") { + ©Dir ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_" ); + } else { + ©BinFile ( "$src${PATHSEP}$_", "$dest${PATHSEP}$_"); + } + } +} + +sub copyBinFile { + my $src = shift; + my $dest = shift; + my $buf = ""; + my $bufsize = 8192; + + open( SRC, $src ) || die "Can't open $src: $!\n"; + # if we are given a directory destination instead of a file, extract the + # filename portion of the source to use as the destination filename + if (-d $dest) { + $dest = $dest . $PATHSEP . &basename($src); + } + open( DEST, ">$dest" ) || die "Can't create $dest: $!\n"; + binmode SRC; + binmode DEST; + if ($PRESERVE) { + $mode = (stat($src))[2]; + ($uid, $gid) = (stat(_))[4..5]; + # Make sure files owned by the old user are owned by the + # new user + if ($uid == $olduid) { + $uid = $newuid; + $gid = $newgid; + } + chown $uid, $gid, $dest; + chmod $mode, $dest; + } + while (read(SRC, $buf, $bufsize)) { + print DEST $buf; + } + close( SRC ); + close( DEST ); +} +############################################################################# +# backup new configuration files +# backup the directory <new_root_server>/slapd-instance/config in <new_root_server>/slapd-instance/BackupConfig + +sub backupConfigFiles { + # backup the new config files + my $src = "$serverHome${PATHSEP}config" ; + my $dest = "$serverHome${PATHSEP}config_backup" ; + if ($NO_INPUT_USER) { + printMsg("\n$src has been backup in $dest"); + ©Dir($src,$dest); + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\n\nWhere do you want to back up your configuration directory [$dest] ?") ; + my $Answer = <STDIN> ; + $dest = $Answer if ($Answer ne "\n"); + chomp($dest); + printTrace("\nDest: .$dest.",4); + if (-e $dest) { + printMsg("\n\n$dest already exists. Do you want to overwrite it Yes/No [No] ?") ; + if (<STDIN> =~ /yes|y/i) { + $Ask = 0 ; + } + else { + $dest = "$serverHome${PATHSEP}config_backup" ; + } + } + else { + $Ask = 0 ; + } + } + printTrace("\nBackup Directory: $src in $dest",4); + ©Dir($src,$dest); + } +} +############################################################################# + +sub getLDAPservername { + my $oldLDAPservername; + my $LDAPservername; + open(OLDSLAPDCONF, $oldSlapdConf) or + die "\nError: could not open old config file $oldSlapdConf \n"; + while(<OLDSLAPDCONF>) { + chop; + if (/^localhost\s+/i) { + ($oldLDAPservername = $') =~ s/^[\"]//;; + $oldLDAPservername =~ s/[\"]$//; + printTrace("\nName of the old LDAP server: $oldLDAPservername",3); + } + } + close(OLDSLAPDCONF); + + open( DSELDIF, "< $DSEldif" ) || die "\nCan't open $DSEldif \n"; + my $in = new Mozilla::LDAP::LDIF(*DSELDIF) ; + while ($entry = readOneEntry $in) { + my $DN = $entry->getDN() ; + if ($DN =~ /^cn=config$/i) { + my $localhost = "nsslapd-localhost"; + my @values = $entry->getValues($localhost); + if ($#values != -1) { + $LDAPservername = $values[0]; + } + break; + } + } + close(DSELDIF); + # check old and new are installed on the same physical machine. + if (lc($oldLDAPservername) ne lc($LDAPservername)) { + # warn the user he tries to migrate a old server installed on a different machine from the new one + printMsg("\n\nYour old server is on $oldLDAPservername, and your new server is on $LDAPservername. We don't support migration on different machines. Do you want to continue ? Yes/No [No]:") ; + if (! (<STDIN> =~ /yes|y/i)) { + return -1; + } + } + return $LDAPservername ; +} + +############################################################################# + +sub getLibPath { + my $myDir = shift; + my $myVersion = shift; + my $myMinor = shift; + + if ($isNT) { + return $ENV{"$LIB_PATH"}; + } + if (($myVersion >= 6) && ($myMinor >= 2)) { + return + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$myDir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + } else { + return "$myDir${PATHSEP}lib${SEP}".$ENV{"$LIB_PATH"}; + } +} + +############################################################################# + +sub getVersion { + my $dir = shift; + my $versionstr = shift; + my $version = 0; + my $minor = 0; + my $buildNumber = 0; + my $progDir = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${PATHSEP}"; + my $progDir2 = "${PATHSEP}bin${PATHSEP}slapd${PATHSEP}"; + + # find the slapd executable + if (!$versionstr) { # version not specified on cmd line - find it + $prog = $dir . $progDir . $slapdExecName; + if (! -f $prog) { + $prog = $dir . $progDir2 . $slapdExecName; + if (-f $prog && $isNT) { + # if slapd is in bin/slapd and we're on NT, just assume version 1; + # apparently, slapd.exe doesn't like the -v argument . . . + return ( '1', $minor ); + } + else{ + die "Could not run slapd program $prog: $!"; + } + } + else { + chdir($dir . $progDir); + } + $cur_libpath=$ENV{"$LIB_PATH"}; + $ENV{"$LIB_PATH"}= + "$dir${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}lib${SEP}". + "$dir${PATHSEP}bin${PATHSEP}slapd${PATHSEP}server${SEP}". + $ENV{"$LIB_PATH"}; + # read the old version from the old slapd program + + open(F, "${quote}${quote}$prog${quote} -v${quote} 2>&1 |") or + die "Could not run slapd program $prog: $!"; + sleep(1); # allow some data to accumulate in the pipe +# print "Output from $prog -v:\n"; + while (<F>) { + if (/^Netscape-Directory/ || /^iPlanet-Directory/i) { + $versionstr = $_; + last; + } + } + $code = close(F); + # print "$prog returned code=$code status=$?\n"; + $ENV{"$LIB_PATH"}=$cur_libpath; + } + + if ($versionstr =~ /^Netscape-Directory\/(\d+)\.(\d+)(?:b\d)*\s+(\S+)/) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^Netscape-Directory\(restrict?ed-mode\)\/(\d+)\.(\d+)\s+(\S+)/) { # we can have restricted-mode or restriced-mode ... + $version = $1; + $minor = $2; + $buildNumber = $3; + } + elsif ($versionstr =~ /^iPlanet-Directory\/(\d+)\.(\d+)\s+(\S+)/i) { + $version = $1; + $minor = $2; + $buildNumber = $3; + } elsif ($versionstr =~ /(\d+)\.(\d+)/) { + $version = $1; + $minor = $2; + } + + if ($version == 0) { + die "\nCould not determine version of the directory server in $dir: \n"; + } + + # distinguish the 4.1 and the 4.11 thanks to the buildNumber + if (($version == 4) && ($minor == 1)){ + if (! ($buildNumber =~ /^B99\.16/)) { + # it's not a 4.1 Netscape Directory Server => it's a 4.11 + $minor = 11 ; + } + } + chdir($curdir) or die "Could not change directory to $curdir: $!" ; + return ( $version, $minor ); +} + +############################################################################# + +sub getDiff { + # we get references to arrays + my $elements = shift ; + my $existing_elements = shift ; + my %count = () ; + my %countEE = () ; + @diff = () ; + foreach $e (@{$elements}, @{$existing_elements}) { $count{$e}++ ;} + foreach $e (@{existing_elements}) { $countEE{$e}++ ;} + foreach $e (@{$elements}) { + # if $e is only present in @$elements, we push it to the diff array + if (($count{$e} == 1) && ($countEE{$e} == 0)) { + push @diff, $e ; + } + } + return @diff ; +} + +############################################################################################### +sub testIndexUpdating { + #my $entry = $conn->newEntry(); + #$entry->setDN("cn=djeattribute,cn=index,cn=MigratedDB_5,cn=ldbm database,cn=plugins,cn=config"); + my $entry = $conn->search("cn=mail,cn=index,cn=MigratedDB_2,cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + my @types = ("pres", "sub", "eq") ; + my @existing_types = $entry->getValues("nsindextype"); + my @typesToAdd = &getDiff(\@types, \@existing_types); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + my $res = $conn->update($entry) ; + if ($res) {print("\nUpdate index mail\n");} + else { print("\ncan't update index mail");} + + $entry = $conn->search("cn=givenName,cn=index,cn=MigratedDB_2,cn=ldbm database,cn=plugins,cn=config","base","objectclass=nsIndex"); + @types = ("pres", "sub", "eq") ; + @existing_types = $entry->getValues("nsindextype"); print("\ngivenName, existing_types: @existing_types"); + @typesToAdd = &getDiff(\@types, \@existing_types); print("\nTypesToAdd: @typesToAdd"); + foreach $newtype (@typesToAdd) { + $entry->addValue("nsindextype", $newtype); printTrace("\nnewtype: $newtype",2); + } + my $res = $conn->update($entry) ; + if ($res) {print("\nUpdate index givenName\n");} + else { print("\ncan't update index givenName");} + } + + +############################################################################################### +sub normalizeDir { + my $dir = shift ; + my $dir_prec = "" ; + while ($dir_prec ne $dir) { + $dir_prec = $dir ; + if ($isNT) { + grep { s@\\\\@\\@g } $dir ; + } + else { + grep { s@//@/@g } $dir ; + } + } + return $dir ; +} + + +############################################################################################### +# return 1 if the value parameters is +sub isAllowedPlugin { + my $Value = lc(shift) ; + if ($allowedPlugins{$Value}) { + return 1 ; + } + else { + return 0 ; + } + +} + + +sub getSyntaxOid { + my $Value = lc(shift) ; + return $allowedPlugins{$Value} ; +} + +############################################################################################### +# return 1 if the value given in parameters is an allowed modifier +sub isAllowedModifier { + my $Value = lc(shift) ; + if ($allowedModifiers{$Value}) { + return 1 ; + } + else { + return 0 ; + } +} + +sub getModifierValue { + my $Value = lc(shift) ; + return $allowedModifiers{$Value} ; +} + +############################################################################################### + +sub GetTime { + my $tm = localtime; + (my $sec, my $min, my $hour, my $dd, my $mm, my $yy) = ($tm->sec, $tm->min, $tm->hour, $tm->mday, ($tm->mon)+1, ($tm->year)+1900); + $sec = "0$sec" unless $sec > 9 ; + $min = "0$min" unless $min > 9 ; + $hour = "0$hour" unless $hour > 9 ; + $dd = "0$dd" unless $dd > 9 ; + $mm = "0$mm" unless $mm > 9 ; + return ($sec, $min, $hour, $dd, $mm, $yy); +} + +############################################################################################### +# get uid and group id of the new slapd server. +# The uid is done through the nsslapd-localuser attribute + +sub getuid_gid { + my $newuid ; + my $newgid ; + my $localuser ; + my $localuser_attr = "nsslapd-localuser" ; + if (! $isNT) { + my $entry = $conn->search("cn=config ", "base","objectclass=*", 0, ($localuser_attr)) ; + # Tests wether we succeed to get the entry cn=config + die "\nCan't get the entry cn=config \n" unless ($entry); + my @values = $entry->getValues($localuser_attr); + if ($#values == -1 || ($values[0] eq "") ) { # tests wether the nsslapd-localuser attribute has a value + printMsg("\nNo localuser has been found in the configuration of the directory. "); + if ($NO_INPUT_USER) { + printMsg("\nWe considered nobody as the localuser"); + $localuser = "nobody" ; + } + else { + my $Ask = 1 ; + while ($Ask) { + printMsg("\nUnder what user does your $Version.$Minor directory server run [nobody] ? ") ; + $localuser = <STDIN> ; + chomp($localuser); + $localuser = "nobody" if ($localuser eq ""); + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + if ($newuid) { + $Ask = 0 ; + } + else { + printMsg("\nError: $localuser is unknown from the system "); + } + } + } + } + else { + $localuser = $values[0]; # returns the first value (we should only have one localuser) + my $size = $#values ; + } + ($newuid, $newgid) = (getpwnam("$localuser"))[2..3] ; + return ($localuser, $newuid, $newgid) ; + } + else { + return () ; + } +} + + +############################################################################################### +# get uid and group id of the old slapd server. + +sub getolduid_gid { + my $oldlocaluser ; + if (! $isNT) { + open(CONF, $oldSlapdConf) or die "\nError: cannot open $oldSlapdConf: $!\n"; + while (<CONF>) { + if (/^localuser\s+/i) { + chomp($oldlocaluser = $'); + last; + } + } + close(CONF); + ($olduid, $oldgid) = (getpwnam("$oldlocaluser"))[2..3] ; + return ($oldlocaluser, $olduid, $oldgid) ; + } + else { + return (); + } +} + +############################################################################################### +# get current directory + +sub getCwd { + my $command = $isNT ? "cd" : "/bin/pwd"; + open(PWDCMD, "$command 2>&1 |") or + die "Error: could not execute $command: $!"; + # without the following sleep, reading from the pipe will + # return nothing; I guess it gives the pwd command time + # to get some data to read . . . + sleep(1); + my $currentdir; + while (<PWDCMD>) { + if (!$currentdir) { + chomp($currentdir = $_); + } + } + my $code = close(PWDCMD); +# if ($code || $?) { +# print "$command returned code=$code status=$? dir=$curdir\n"; +# } +# print "getCwd curdir=\[$curdir\]\n"; + return $currentdir; +} diff --git a/ldap/admin/src/scripts/template-ns-accountstatus.pl b/ldap/admin/src/scripts/template-ns-accountstatus.pl new file mode 100644 index 00000000..5913f7ad --- /dev/null +++ b/ldap/admin/src/scripts/template-ns-accountstatus.pl @@ -0,0 +1,813 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +############################### +# SUB-ROUTINES +############################### + +sub usage_and_exit +{ + print (STDERR "$cmd [-D rootdn] { -w password | -w - | -j filename } \n"); + print (STDERR " [-p port] [-h host] -I DN-to-$operation\n\n"); + print (STDERR "May be used to $operation a user or a domain of users\n\n"); + print (STDERR "Arguments:\n"); + print (STDERR " -? - help\n"); + print (STDERR " -D rootdn - Provide a Directory Manager DN. Default= '$defrootdn'\n"); + print (STDERR " -w password - Provide a password for the Directory Manager DN\n"); + print (STDERR " -w - - Prompt for the Directory Manager's password\n"); + print (STDERR " -j filename - Read the Directory Manager's password from file\n"); + print (STDERR " -p port - Provide a port. Default= '$defport'\n"); + print (STDERR " -h host - Provide a host name. Default= '$defhost'\n"); + print (STDERR " -I DN-to-$operation - Single entry DN or role DN to $operation\n"); + exit 100; +} + +sub debug +{ +# print " ==> @_"; +} + +sub out +{ + print "@_"; +} + +# -------------------------- +# Check if the entry is part of a locked role: +# i.e.: for each role member (nsroledn) of nsdisabledrole, check if +# * it is the same as the entry +# * the entry is member of role (==has nsroledn attributes), compare each of +# them with the nsroledn of nsdisabledrole +# * if nsroledn of nsdisabledrole are complex, go through each of them +# argv[0] is the local file handler +# argv[1] is the entry (may be a single entry DN or a role DN) +# argv[2] is the base for the search +# -------------------------- + +$throughRole=""; + +sub indirectLock +{ + # For recursivity, file handler must be local + my $L_filehandle=$_[0]; + $L_filehandle++; + + my $L_entry=$_[1]; + # Remove useless space + my @L_intern=split /([,])/,$L_entry; + my $L_result=""; + foreach $L_part (@L_intern) + { + $L_part=~s/^ +//; + $L_part=~ tr/A-Z/a-z/; + $L_result="$L_result$L_part"; + } + $L_entry=$L_result; + + my $L_base=$_[2]; + + my $L_search; + my $L_currentrole; + my $L_retCode; + + my $L_local; + +`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsroledn >> {{DEV-NULL}} 2>&1 `; +$retCode=$?; +if ( $retCode != 0 ) +{ + $retCode=$?>>8; + return 1; +} + + # Check if the role is a nested role + @L_Nested="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=nsNestedRoleDefinition)(objectclass=ldapsubentry))\" "; + # L_isNested == 1 means that we are going through a nested role, so for each member of that + # nested role, check that the member is below the scope of the nested + $L_isNested=@L_Nested; + + # Not Direct Lock, Go through roles if any + $L_search="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsroledn "; + + debug("\t-->indirectLock: check if $L_entry is part of a locked role from base $L_base\n\n"); + + unless (open ($L_filehandle, "$L_search |")) + { + out("Can't open file $L_filehandle\n"); + exit; + } + while (<$L_filehandle>) { + + s/\n //g; + if (/^nsroledn: (.*)\n/) { + $L_currentrole = $1; + + # Remove useless space + my @L_intern=split /([,])/,$L_currentrole; + my $L_result=""; + foreach $L_part (@L_intern) + { + $L_part=~s/^ +//; + $L_part=~ tr/A-Z/a-z/; + $L_result="$L_result$L_part"; + } + $L_currentrole=$L_result; + + debug("\t-- indirectLock loop: current nsroledn $L_currentrole of base $L_base\n"); + if ( $L_isNested == 1 ) + { + if ( checkScope($L_currentrole, $L_base) == 0 ) + { + # Scope problem probably a bad conf, skip the currentrole + next; + } + } + + if ( $L_currentrole eq $L_entry ) + { + # the entry is a role that is directly locked + # i.e, nsroledn of nsdisabledrole contains the entry + $throughRole=$L_base; + $throughRole=~ tr/A-Z/a-z/; + + # skipDisabled means that we've just found that the entry (which is a role) + # is locked directly (==its DN is part of nsroledn attributes) + # we just want to know now, if it is locked through another role + # at least, one + if ( $skipDisabled == 1 ) + { + # direct inactivation + $directLocked=1; + # just go through that test once + $skipDisabled=0; + next; + } + debug("\t-- 1 indirectLock: $L_currentrole locked throughRole == $throughRole\n"); + return 0; + } + + $L_retCode=memberOf($L_currentrole, $L_entry); + if ( $L_retCode == 0 && $single == 1 ) + { + $throughRole=$L_currentrole; + $throughRole=~ tr/A-Z/a-z/; + if ( $skipManaged == 1 ) + { + if ( $L_currentrole eq $nsManagedDisabledRole) + { + # Try next nsroledn + $directLocked=1; + $skipManaged=0; + next; + } + } + debug("\t-- 2 indirectLock: $L_currentrole locked throughRole == $throughRole\n"); + return 0; + } + + # Only for the first iteration + # the first iteration is with nsdisabledrole as base, other + # loops are deeper + $L_local=$skipDisabled; + $skipDisabled=0; + + # the current nsroledn may be a complex role, just go through + # its won nsroledn + $L_retCode=indirectLock($L_filehandle,$L_entry, $L_currentrole); + + # Because of recursivity, to keep the initial value for the first level + $skipDisabled=$L_local; + + if ( $L_retCode == 0 ) + { + $throughRole=$L_currentrole; + $throughRole=~ tr/A-Z/a-z/; + debug("\t-- 3 indirectLock: $L_entry locked throughRole == $throughRole\n"); + return 0; + } + } + } + + close($L_filehandle); + + debug("\t<--indirectLock: no more nsroledn to process\n"); + return 1; +} + +# -------------------------- +# Check if nsroledn is part of the entry attributes +# argv[0] is a role DN (nsroledn attribute) +# argv[1] is the entry +# -------------------------- +sub memberOf +{ + my $L_nsroledn=$_[0]; + $L_nsroledn=~ tr/A-Z/a-z/; + + my $L_entry=$_[1]; + + my $L_search; + my $L_currentrole; + + $L_search="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_entry\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsrole"; + + debug("\t\t-->memberOf: $L_search: check if $L_entry has $L_nsroledn as nsroledn attribute\n"); + + open (LDAP2, "$L_search |"); + while (<LDAP2>) { + s/\n //g; + if (/^nsrole: (.*)\n/) { + $L_currentrole = $1; + $L_currentrole=~ tr/A-Z/a-z/; + if ( $L_currentrole eq $L_nsroledn ) + { + # the parm is part of the $L_entry nsroledn + debug("\t\t<--memberOf: $L_entry locked through $L_nsroledn\n"); + return 0; + } + } + } + close(LDAP2); + + # the parm is not part of the $L_entry nsroledn + debug("\t\t<--memberOf: $L_entry not locked through $L_nsroledn\n"); + return 1; +} + + +# -------------------------- +# Remove the rdn of a DN +# argv[0] is a DN +# -------------------------- +sub removeRdn +{ + $L_entry=$_[0]; + + @L_entryToTest=split /([,])/,$L_entry; + debug("removeRdn: entry to split: $L_entry**@L_entryToTest\n"); + + $newDN=""; + $removeRDN=1; + foreach $part (@L_entryToTest) + { + $part=~ s/^ +//; + $part=~ tr/A-Z/a-z/; + if ( $removeRDN <= 2 ) + { + $removeRDN=$removeRDN+1; + } + else + { + $newDN="$newDN$part"; + } + } + + debug("removeRdn: new DN **$newDN**\n"); +} + +# -------------------------- +# Check if L_current is below the scope of +# L_nestedRole +# argv[0] is a role +# argv[1] is the nested role +# -------------------------- +sub checkScope +{ + $L_current=$_[0]; + $L_nestedRole=$_[1]; + + debug("checkScope: check if $L_current is below $L_nestedRole\n"); + + removeRdn($L_nestedRole); + $L_nestedRoleSuffix=$newDN; + debug("checkScope: nested role based: $L_nestedRoleSuffix\n"); + + $cont=1; + while ( ($cont == 1) && ($L_current ne "") ) + { + removeRdn($L_current); + $currentDn=$newDN; + debug("checkScope: current DN to check: $currentDn\n"); + + if ( $currentDn eq $L_nestedRoleSuffix ) + { + debug("checkScope: DN match!!!\n"); + $cont = 0; + } + else + { + $L_current=$currentDn; + } + } + + if ( $cont == 1 ) + { + debug("checkScope: $_[0] and $_[1] are not compatible\n"); + return 0; + } + else + { + debug("checkScope: $_[0] and $_[1] are compatible\n"); + return 1; + } +} + + +############################### +# MAIN ROUTINE +############################### + +# Generated variable +$dsroot="{{DS-ROOT}}"; + +# Determine which command we are running +if ( $0 =~ /ns-inactivate(.pl)?$/ ) +{ + $cmd="ns-inactivate.pl"; + $operation="inactivate"; + $state="inactivated"; + $modrole="add"; + $already="already"; +} +elsif ( $0 =~ /ns-activate(.pl)?$/ ) +{ + $cmd="ns-activate.pl"; + $operation="activate"; + $state="activated"; + $modrole="delete"; + $already="already"; +} +elsif ( $0 =~ /ns-accountstatus(.pl)?$/ ) +{ + $cmd="ns-accountstatus.pl"; + $operation="get status of"; + $state="activated"; + # no need for $modrole as no operation is performed + $already=""; + +} +else +{ + out("$0: unknown command\n"); + exit 100; +} + +debug("Running ** $cmd ** $operation\n"); + +$dsbinroot="$dsroot{{SEP}}shared{{SEP}}bin"; +$ldapsearch="$dsbinroot{{SEP}}ldapsearch -1"; +$ldapmodify="$dsbinroot{{SEP}}ldapmodify"; + +# Default values +$defrootdn= "{{ROOT-DN}}"; +$defhost= "{{SERVER-NAME}}"; +$defport= "{{SERVER-PORT}}"; + +# User values +$rootdn= "{{ROOT-DN}}"; +$rootpw= ""; +$pwfile= ""; +$host= "{{SERVER-NAME}}"; +$port= "{{SERVER-PORT}}"; +$entry= ""; + +$single=0; +$role=0; + +chdir("$dsbinroot"); + +# Process the command line arguments +while( $arg = shift) +{ + if($arg eq "-?") + { + usage_and_exit(); + } + elsif($arg eq "-D") + { + $rootdn= shift @ARGV; + } + elsif($arg eq "-w") + { + $rootpw= shift @ARGV; + } + elsif($arg eq "-j") + { + $pwfile= shift @ARGV; + } + elsif($arg eq "-p") + { + $port= shift @ARGV; + } + elsif($arg eq "-h") + { + $host= shift @ARGV; + } + elsif($arg eq "-I") + { + $entry= shift @ARGV; + } + else + { + print "$arg: Unknown command line argument.\n"; + usage_and_exit(); + } +} + +if ($pwfile ne ""){ +# Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpw = <RPASS>; + chomp($rootpw); + close(RPASS); +} elsif ($rootpw eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpw = ReadLine(0); +# chomp($rootpw); +# ReadMode('normal'); +} + +if( $rootpw eq "" ) +{ + usage_and_exit(); +} + +if( $entry eq "" ) +{ + usage_and_exit(); +} + +# +# Check the actual existence of the entry to inactivate/activate +# and at the same time, validate the various parm: port, host, rootdn, rootpw +# +@exist=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(objectclass=*)\" dn`; +$retCode1=$?; +if ( $retCode1 != 0 ) +{ + $retCode1=$?>>8; + exit $retCode1; +} + +@isRole=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(&(objectclass=LDAPsubentry)(objectclass=nsRoleDefinition))\" dn`; +$nbLineRole=@isRole; +$retCode2=$?; +if ( $retCode2 != 0 ) +{ + $retCode2=$?>>8; + exit $retCode2; +} + +if ( $nbLineRole == 1 ) +{ + debug("Groups of users\n"); + $role=1; +} +else +{ + debug("Single user\n"); + $single=1; +} + +# +# First of all, check the existence of the nsaccountlock attribute in the entry +# +$isLocked=0; +if ( $single == 1 ) +{ + $searchAccountLock="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(objectclass=*)\" nsaccountlock"; + open (LDAP1, "$searchAccountLock |"); + while (<LDAP1>) { + s/\n //g; + if (/^nsaccountlock: (.*)\n/) { + $L_currentvalue = $1; + $L_currentvalue=~ tr/A-Z/a-z/; + if ( $L_currentvalue eq "true") + { + $isLocked=1; + } + elsif ( $L_currentvalue eq "false" ) + { + $isLocked=0; + } + } + } + close(LDAP1); +} +debug("Is the entry already locked? ==> $isLocked\n"); + +# +# Get the suffix name of that entry +# + +# Remove the space at the beginning (just in case...) +# -I "uid=jvedder , ou=People , o=sun.com" +@suffix=split /([,])/,$entry; +$result=""; +foreach $part (@suffix) +{ + $part=~s/^ +//; + $part=~ tr/A-Z/a-z/; + $result="$result$part"; +} +@suffixN=$result; + +debug("Entry to $operation: #@suffix#\n"); +debug("Entry to $operation: #@suffixN#\n"); + +# Get the suffix +$cont=0; +while ($cont == 0) +{ + # Look if suffix is the suffix of the entry + # ldapsearch -s one -b "cn=mapping tree,cn=config" "cn=\"uid=jvedder,ou=People,o=sun.com\"" + # + debug("\tSuffix from the entry: #@suffixN#\n"); + @mapping=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s one -b \"cn=mapping tree, cn=config\" \"cn=\\"@suffixN\\"\" cn `; + + $retCode=$?; + if ( $retCode != 0 ) + { + $retCode=$?>>8; + exit $retCode; + } + + # If we get a result, remove the dn: + # dn: cn="o=sun.com",cn=mapping tree,cn=config + # cn: "o=sun.com" + # + shift @mapping; + + foreach $res (@mapping) + { + # Break the string cn: "o=sun.com" into pieces + @cn= split(/ /,$res); + + # And remove the cn: part + shift @cn; + + # Now compare the suffix we extract from the mapping tree + # with the suffix derived from the entry + debug("\tSuffix from mapping tree: #@cn#\n"); + if ( @cn eq @suffixN ) { + debug("Found matching suffix\n"); + $cont=1; + } + } + + if ( $cont == 0 ) + { + # Remove the current rdn to try another suffix + shift @suffix; + + $result=""; + foreach $part (@suffix) + { + $part=~ s/^ +//; + $part=~ tr/A-Z/a-z/; + $result="$result$part"; + } + @suffixN=$result; + + debug("\t\tNothing found => go up one level in rdn #@suffix#\n"); + $len=@suffix; + if ( $len == 0 ) + { + debug("Can not find suffix. Problem\n"); + $cont=2; + } + } +} +if ( $cont == 2) +{ + out("Can not find suffix for entry $entry\n"); + exit 100; +} + +if ( $operation eq "inactivate" ) +{ + # + # Now that we have the suffix and we know if we deal with a single entry or + # a role, just try to create the COS and roles associated. + # + @base=( + "cn=nsManagedDisabledRole,@suffixN", + "cn=nsDisabledRole,@suffixN", + "cn=nsAccountInactivationTmp,@suffixN", + "\'cn=\"cn=nsDisabledRole,@suffixN\",cn=nsAccountInactivationTmp,@suffixN\'", + "cn=nsAccountInactivation_cos,@suffixN" ); + + $addrolescos="$ldapmodify -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -c -a >> {{DEV-NULL}} 2>&1 "; + @role1=( + "dn: cn=nsManagedDisabledRole,@suffixN\n", + "objectclass: LDAPsubentry\n", + "objectclass: nsRoleDefinition\n", + "objectclass: nsSimpleRoleDefinition\n", + "objectclass: nsManagedRoleDefinition\n", + "cn: nsManagedDisabledRole\n\n" ); + @role2=( + "dn: cn=nsDisabledRole,@suffixN\n", + "objectclass: top\n", + "objectclass: LDAPsubentry\n", + "objectclass: nsRoleDefinition\n", + "objectclass: nsComplexRoleDefinition\n", + "objectclass: nsNestedRoleDefinition\n", + "nsRoleDN: cn=nsManagedDisabledRole,@suffixN\n", + "cn: nsDisabledRole\n\n" ); + @cos1=( + "dn: cn=nsAccountInactivationTmp,@suffixN\n", + "objectclass: top\n", + "objectclass: nsContainer\n\n" ); + @cos2=( + "dn: cn=\"cn=nsDisabledRole,@suffixN\",cn=nsAccountInactivationTmp,@suffixN\n", + "objectclass: top\n", + "objectclass: extensibleObject\n", + "objectclass: costemplate\n", + "objectclass: ldapsubentry\n", + "cosPriority: 1\n", + "nsAccountLock: true\n\n" ); + @cos3=( + "dn: cn=nsAccountInactivation_cos,@suffixN\n", + "objectclass: top\n", + "objectclass: LDAPsubentry\n", + "objectclass: cosSuperDefinition\n", + "objectclass: cosClassicDefinition\n", + "cosTemplateDn: cn=nsAccountInactivationTmp,@suffixN\n", + "cosSpecifier: nsRole\n", + "cosAttribute: nsAccountLock operational\n\n" ); + + @all=(\@role1, \@role2, \@cos1, \@cos2, \@cos3); + + $i=0; + + foreach $current (@base) + { + debug("Creating $current ??\n"); + open(FD,"| $addrolescos "); + print FD @{$all[$i]}; + close(FD); + if ( $? != 0 ) + { + $retCode=$?>>8; + if ( $retCode == 68 ) + { + debug("Entry $current already exists, ignore error\n"); + } + else + { + # Probably a more serious problem. + # Exit with LDAP error + exit $retCode; + } + } + else + { + debug("Entry $current created\n"); + } + $i=$i+1; + } +} + +$skipManaged=0; +$skipDisabled=0; +$directLocked=0; + +$nsDisabledRole="cn=nsDisabledRole,@suffixN"; +$nsDisabledRole=~ tr/A-Z/a-z/; + +$nsManagedDisabledRole="cn=nsManagedDisabledRole,@suffixN"; +$nsManagedDisabledRole=~ tr/A-Z/a-z/; + +if ( $operation eq "inactivate" ) +{ + # Go through all the roles part of nsdisabledrole to check if the entry + # is a member of one of those roles + $ret=indirectLock("LDAP00", $entry, $nsDisabledRole); + if ( $ret == 0 ) + { + if ( $throughRole ne $nsDisabledRole && $throughRole ne $nsManagedDisabledRole ) + { + # indirect lock + out("$entry already $state through $throughRole.\n"); + } + else + { + # direct lock + out("$entry already $state.\n"); + } + exit 100; + } + elsif ( $isLocked == 1 ) + { + # the entry is not locked through a role, may be nsaccountlock is "hardcoded" ? + out("$entry already $state (probably directly).\n"); + exit 103; + } +} +elsif ( $operation eq "activate" || $operation eq "get status of" ) +{ + $skipManaged=$single; + $skipDisabled=$role; + + $ret=indirectLock("LDAP00",$entry, $nsDisabledRole); + + if ( $ret == 0 ) + { + # undirectly locked + if ( $throughRole ne $nsDisabledRole && $throughRole ne $nsManagedDisabledRole ) + { + if ( $operation eq "activate" ) + { + out("$entry inactivated through $throughRole. Can not activate it individually.\n"); + exit 100; + } + else + { + out("$entry inactivated through $throughRole.\n"); + exit 104; + } + } + debug("$entry locked individually\n"); + + if ( $operation ne "activate" ) + { + out("$entry inactivated.\n"); + exit 103; + } + } + elsif ( $directLocked == 0 ) + { + if ( $operation eq "activate" && $isLocked != 1 ) + { + out("$entry $already $state.\n"); + exit 100; + } + elsif ( $isLocked != 1 ) + { + out("$entry $already $state.\n"); + exit 102; + } + else + { + # not locked using our schema, but nsaccountlock is probably present + out("$entry inactivated (probably directly).\n"); + exit 103; + } + } + elsif ( $operation ne "activate" ) + { + out("$entry inactivated.\n"); + exit 103; + } + # else Locked directly, juste unlock it! + debug("$entry locked individually\n"); +} + +# +# Inactivate/activate the entry +# +$action="$ldapmodify -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -c >> {{DEV-NULL}} 2>&1"; +if ( $single == 1 ) +{ + @record=( + "dn: $entry\n", + "changetype: modify\n", + "$modrole: nsRoleDN\n", + "nsRoleDN: cn=nsManagedDisabledRole,@suffixN\n\n" ); +} +else +{ + @record=( + "dn: cn=nsDisabledRole,@suffixN\n", + "changetype: modify\n", + "$modrole: nsRoleDN\n", + "nsRoleDN: $entry\n\n" ); +} +open(FD,"| $action "); +print FD @record; +close(FD); +if ( $? != 0 ) +{ +debug("$modrole, $entry\n"); + $retCode=$?>>8; + exit $retCode; +} + +out("$entry $state.\n"); +exit 0; diff --git a/ldap/admin/src/scripts/template-ns-activate.pl b/ldap/admin/src/scripts/template-ns-activate.pl new file mode 100644 index 00000000..5913f7ad --- /dev/null +++ b/ldap/admin/src/scripts/template-ns-activate.pl @@ -0,0 +1,813 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +############################### +# SUB-ROUTINES +############################### + +sub usage_and_exit +{ + print (STDERR "$cmd [-D rootdn] { -w password | -w - | -j filename } \n"); + print (STDERR " [-p port] [-h host] -I DN-to-$operation\n\n"); + print (STDERR "May be used to $operation a user or a domain of users\n\n"); + print (STDERR "Arguments:\n"); + print (STDERR " -? - help\n"); + print (STDERR " -D rootdn - Provide a Directory Manager DN. Default= '$defrootdn'\n"); + print (STDERR " -w password - Provide a password for the Directory Manager DN\n"); + print (STDERR " -w - - Prompt for the Directory Manager's password\n"); + print (STDERR " -j filename - Read the Directory Manager's password from file\n"); + print (STDERR " -p port - Provide a port. Default= '$defport'\n"); + print (STDERR " -h host - Provide a host name. Default= '$defhost'\n"); + print (STDERR " -I DN-to-$operation - Single entry DN or role DN to $operation\n"); + exit 100; +} + +sub debug +{ +# print " ==> @_"; +} + +sub out +{ + print "@_"; +} + +# -------------------------- +# Check if the entry is part of a locked role: +# i.e.: for each role member (nsroledn) of nsdisabledrole, check if +# * it is the same as the entry +# * the entry is member of role (==has nsroledn attributes), compare each of +# them with the nsroledn of nsdisabledrole +# * if nsroledn of nsdisabledrole are complex, go through each of them +# argv[0] is the local file handler +# argv[1] is the entry (may be a single entry DN or a role DN) +# argv[2] is the base for the search +# -------------------------- + +$throughRole=""; + +sub indirectLock +{ + # For recursivity, file handler must be local + my $L_filehandle=$_[0]; + $L_filehandle++; + + my $L_entry=$_[1]; + # Remove useless space + my @L_intern=split /([,])/,$L_entry; + my $L_result=""; + foreach $L_part (@L_intern) + { + $L_part=~s/^ +//; + $L_part=~ tr/A-Z/a-z/; + $L_result="$L_result$L_part"; + } + $L_entry=$L_result; + + my $L_base=$_[2]; + + my $L_search; + my $L_currentrole; + my $L_retCode; + + my $L_local; + +`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsroledn >> {{DEV-NULL}} 2>&1 `; +$retCode=$?; +if ( $retCode != 0 ) +{ + $retCode=$?>>8; + return 1; +} + + # Check if the role is a nested role + @L_Nested="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=nsNestedRoleDefinition)(objectclass=ldapsubentry))\" "; + # L_isNested == 1 means that we are going through a nested role, so for each member of that + # nested role, check that the member is below the scope of the nested + $L_isNested=@L_Nested; + + # Not Direct Lock, Go through roles if any + $L_search="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsroledn "; + + debug("\t-->indirectLock: check if $L_entry is part of a locked role from base $L_base\n\n"); + + unless (open ($L_filehandle, "$L_search |")) + { + out("Can't open file $L_filehandle\n"); + exit; + } + while (<$L_filehandle>) { + + s/\n //g; + if (/^nsroledn: (.*)\n/) { + $L_currentrole = $1; + + # Remove useless space + my @L_intern=split /([,])/,$L_currentrole; + my $L_result=""; + foreach $L_part (@L_intern) + { + $L_part=~s/^ +//; + $L_part=~ tr/A-Z/a-z/; + $L_result="$L_result$L_part"; + } + $L_currentrole=$L_result; + + debug("\t-- indirectLock loop: current nsroledn $L_currentrole of base $L_base\n"); + if ( $L_isNested == 1 ) + { + if ( checkScope($L_currentrole, $L_base) == 0 ) + { + # Scope problem probably a bad conf, skip the currentrole + next; + } + } + + if ( $L_currentrole eq $L_entry ) + { + # the entry is a role that is directly locked + # i.e, nsroledn of nsdisabledrole contains the entry + $throughRole=$L_base; + $throughRole=~ tr/A-Z/a-z/; + + # skipDisabled means that we've just found that the entry (which is a role) + # is locked directly (==its DN is part of nsroledn attributes) + # we just want to know now, if it is locked through another role + # at least, one + if ( $skipDisabled == 1 ) + { + # direct inactivation + $directLocked=1; + # just go through that test once + $skipDisabled=0; + next; + } + debug("\t-- 1 indirectLock: $L_currentrole locked throughRole == $throughRole\n"); + return 0; + } + + $L_retCode=memberOf($L_currentrole, $L_entry); + if ( $L_retCode == 0 && $single == 1 ) + { + $throughRole=$L_currentrole; + $throughRole=~ tr/A-Z/a-z/; + if ( $skipManaged == 1 ) + { + if ( $L_currentrole eq $nsManagedDisabledRole) + { + # Try next nsroledn + $directLocked=1; + $skipManaged=0; + next; + } + } + debug("\t-- 2 indirectLock: $L_currentrole locked throughRole == $throughRole\n"); + return 0; + } + + # Only for the first iteration + # the first iteration is with nsdisabledrole as base, other + # loops are deeper + $L_local=$skipDisabled; + $skipDisabled=0; + + # the current nsroledn may be a complex role, just go through + # its won nsroledn + $L_retCode=indirectLock($L_filehandle,$L_entry, $L_currentrole); + + # Because of recursivity, to keep the initial value for the first level + $skipDisabled=$L_local; + + if ( $L_retCode == 0 ) + { + $throughRole=$L_currentrole; + $throughRole=~ tr/A-Z/a-z/; + debug("\t-- 3 indirectLock: $L_entry locked throughRole == $throughRole\n"); + return 0; + } + } + } + + close($L_filehandle); + + debug("\t<--indirectLock: no more nsroledn to process\n"); + return 1; +} + +# -------------------------- +# Check if nsroledn is part of the entry attributes +# argv[0] is a role DN (nsroledn attribute) +# argv[1] is the entry +# -------------------------- +sub memberOf +{ + my $L_nsroledn=$_[0]; + $L_nsroledn=~ tr/A-Z/a-z/; + + my $L_entry=$_[1]; + + my $L_search; + my $L_currentrole; + + $L_search="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_entry\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsrole"; + + debug("\t\t-->memberOf: $L_search: check if $L_entry has $L_nsroledn as nsroledn attribute\n"); + + open (LDAP2, "$L_search |"); + while (<LDAP2>) { + s/\n //g; + if (/^nsrole: (.*)\n/) { + $L_currentrole = $1; + $L_currentrole=~ tr/A-Z/a-z/; + if ( $L_currentrole eq $L_nsroledn ) + { + # the parm is part of the $L_entry nsroledn + debug("\t\t<--memberOf: $L_entry locked through $L_nsroledn\n"); + return 0; + } + } + } + close(LDAP2); + + # the parm is not part of the $L_entry nsroledn + debug("\t\t<--memberOf: $L_entry not locked through $L_nsroledn\n"); + return 1; +} + + +# -------------------------- +# Remove the rdn of a DN +# argv[0] is a DN +# -------------------------- +sub removeRdn +{ + $L_entry=$_[0]; + + @L_entryToTest=split /([,])/,$L_entry; + debug("removeRdn: entry to split: $L_entry**@L_entryToTest\n"); + + $newDN=""; + $removeRDN=1; + foreach $part (@L_entryToTest) + { + $part=~ s/^ +//; + $part=~ tr/A-Z/a-z/; + if ( $removeRDN <= 2 ) + { + $removeRDN=$removeRDN+1; + } + else + { + $newDN="$newDN$part"; + } + } + + debug("removeRdn: new DN **$newDN**\n"); +} + +# -------------------------- +# Check if L_current is below the scope of +# L_nestedRole +# argv[0] is a role +# argv[1] is the nested role +# -------------------------- +sub checkScope +{ + $L_current=$_[0]; + $L_nestedRole=$_[1]; + + debug("checkScope: check if $L_current is below $L_nestedRole\n"); + + removeRdn($L_nestedRole); + $L_nestedRoleSuffix=$newDN; + debug("checkScope: nested role based: $L_nestedRoleSuffix\n"); + + $cont=1; + while ( ($cont == 1) && ($L_current ne "") ) + { + removeRdn($L_current); + $currentDn=$newDN; + debug("checkScope: current DN to check: $currentDn\n"); + + if ( $currentDn eq $L_nestedRoleSuffix ) + { + debug("checkScope: DN match!!!\n"); + $cont = 0; + } + else + { + $L_current=$currentDn; + } + } + + if ( $cont == 1 ) + { + debug("checkScope: $_[0] and $_[1] are not compatible\n"); + return 0; + } + else + { + debug("checkScope: $_[0] and $_[1] are compatible\n"); + return 1; + } +} + + +############################### +# MAIN ROUTINE +############################### + +# Generated variable +$dsroot="{{DS-ROOT}}"; + +# Determine which command we are running +if ( $0 =~ /ns-inactivate(.pl)?$/ ) +{ + $cmd="ns-inactivate.pl"; + $operation="inactivate"; + $state="inactivated"; + $modrole="add"; + $already="already"; +} +elsif ( $0 =~ /ns-activate(.pl)?$/ ) +{ + $cmd="ns-activate.pl"; + $operation="activate"; + $state="activated"; + $modrole="delete"; + $already="already"; +} +elsif ( $0 =~ /ns-accountstatus(.pl)?$/ ) +{ + $cmd="ns-accountstatus.pl"; + $operation="get status of"; + $state="activated"; + # no need for $modrole as no operation is performed + $already=""; + +} +else +{ + out("$0: unknown command\n"); + exit 100; +} + +debug("Running ** $cmd ** $operation\n"); + +$dsbinroot="$dsroot{{SEP}}shared{{SEP}}bin"; +$ldapsearch="$dsbinroot{{SEP}}ldapsearch -1"; +$ldapmodify="$dsbinroot{{SEP}}ldapmodify"; + +# Default values +$defrootdn= "{{ROOT-DN}}"; +$defhost= "{{SERVER-NAME}}"; +$defport= "{{SERVER-PORT}}"; + +# User values +$rootdn= "{{ROOT-DN}}"; +$rootpw= ""; +$pwfile= ""; +$host= "{{SERVER-NAME}}"; +$port= "{{SERVER-PORT}}"; +$entry= ""; + +$single=0; +$role=0; + +chdir("$dsbinroot"); + +# Process the command line arguments +while( $arg = shift) +{ + if($arg eq "-?") + { + usage_and_exit(); + } + elsif($arg eq "-D") + { + $rootdn= shift @ARGV; + } + elsif($arg eq "-w") + { + $rootpw= shift @ARGV; + } + elsif($arg eq "-j") + { + $pwfile= shift @ARGV; + } + elsif($arg eq "-p") + { + $port= shift @ARGV; + } + elsif($arg eq "-h") + { + $host= shift @ARGV; + } + elsif($arg eq "-I") + { + $entry= shift @ARGV; + } + else + { + print "$arg: Unknown command line argument.\n"; + usage_and_exit(); + } +} + +if ($pwfile ne ""){ +# Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpw = <RPASS>; + chomp($rootpw); + close(RPASS); +} elsif ($rootpw eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpw = ReadLine(0); +# chomp($rootpw); +# ReadMode('normal'); +} + +if( $rootpw eq "" ) +{ + usage_and_exit(); +} + +if( $entry eq "" ) +{ + usage_and_exit(); +} + +# +# Check the actual existence of the entry to inactivate/activate +# and at the same time, validate the various parm: port, host, rootdn, rootpw +# +@exist=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(objectclass=*)\" dn`; +$retCode1=$?; +if ( $retCode1 != 0 ) +{ + $retCode1=$?>>8; + exit $retCode1; +} + +@isRole=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(&(objectclass=LDAPsubentry)(objectclass=nsRoleDefinition))\" dn`; +$nbLineRole=@isRole; +$retCode2=$?; +if ( $retCode2 != 0 ) +{ + $retCode2=$?>>8; + exit $retCode2; +} + +if ( $nbLineRole == 1 ) +{ + debug("Groups of users\n"); + $role=1; +} +else +{ + debug("Single user\n"); + $single=1; +} + +# +# First of all, check the existence of the nsaccountlock attribute in the entry +# +$isLocked=0; +if ( $single == 1 ) +{ + $searchAccountLock="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(objectclass=*)\" nsaccountlock"; + open (LDAP1, "$searchAccountLock |"); + while (<LDAP1>) { + s/\n //g; + if (/^nsaccountlock: (.*)\n/) { + $L_currentvalue = $1; + $L_currentvalue=~ tr/A-Z/a-z/; + if ( $L_currentvalue eq "true") + { + $isLocked=1; + } + elsif ( $L_currentvalue eq "false" ) + { + $isLocked=0; + } + } + } + close(LDAP1); +} +debug("Is the entry already locked? ==> $isLocked\n"); + +# +# Get the suffix name of that entry +# + +# Remove the space at the beginning (just in case...) +# -I "uid=jvedder , ou=People , o=sun.com" +@suffix=split /([,])/,$entry; +$result=""; +foreach $part (@suffix) +{ + $part=~s/^ +//; + $part=~ tr/A-Z/a-z/; + $result="$result$part"; +} +@suffixN=$result; + +debug("Entry to $operation: #@suffix#\n"); +debug("Entry to $operation: #@suffixN#\n"); + +# Get the suffix +$cont=0; +while ($cont == 0) +{ + # Look if suffix is the suffix of the entry + # ldapsearch -s one -b "cn=mapping tree,cn=config" "cn=\"uid=jvedder,ou=People,o=sun.com\"" + # + debug("\tSuffix from the entry: #@suffixN#\n"); + @mapping=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s one -b \"cn=mapping tree, cn=config\" \"cn=\\"@suffixN\\"\" cn `; + + $retCode=$?; + if ( $retCode != 0 ) + { + $retCode=$?>>8; + exit $retCode; + } + + # If we get a result, remove the dn: + # dn: cn="o=sun.com",cn=mapping tree,cn=config + # cn: "o=sun.com" + # + shift @mapping; + + foreach $res (@mapping) + { + # Break the string cn: "o=sun.com" into pieces + @cn= split(/ /,$res); + + # And remove the cn: part + shift @cn; + + # Now compare the suffix we extract from the mapping tree + # with the suffix derived from the entry + debug("\tSuffix from mapping tree: #@cn#\n"); + if ( @cn eq @suffixN ) { + debug("Found matching suffix\n"); + $cont=1; + } + } + + if ( $cont == 0 ) + { + # Remove the current rdn to try another suffix + shift @suffix; + + $result=""; + foreach $part (@suffix) + { + $part=~ s/^ +//; + $part=~ tr/A-Z/a-z/; + $result="$result$part"; + } + @suffixN=$result; + + debug("\t\tNothing found => go up one level in rdn #@suffix#\n"); + $len=@suffix; + if ( $len == 0 ) + { + debug("Can not find suffix. Problem\n"); + $cont=2; + } + } +} +if ( $cont == 2) +{ + out("Can not find suffix for entry $entry\n"); + exit 100; +} + +if ( $operation eq "inactivate" ) +{ + # + # Now that we have the suffix and we know if we deal with a single entry or + # a role, just try to create the COS and roles associated. + # + @base=( + "cn=nsManagedDisabledRole,@suffixN", + "cn=nsDisabledRole,@suffixN", + "cn=nsAccountInactivationTmp,@suffixN", + "\'cn=\"cn=nsDisabledRole,@suffixN\",cn=nsAccountInactivationTmp,@suffixN\'", + "cn=nsAccountInactivation_cos,@suffixN" ); + + $addrolescos="$ldapmodify -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -c -a >> {{DEV-NULL}} 2>&1 "; + @role1=( + "dn: cn=nsManagedDisabledRole,@suffixN\n", + "objectclass: LDAPsubentry\n", + "objectclass: nsRoleDefinition\n", + "objectclass: nsSimpleRoleDefinition\n", + "objectclass: nsManagedRoleDefinition\n", + "cn: nsManagedDisabledRole\n\n" ); + @role2=( + "dn: cn=nsDisabledRole,@suffixN\n", + "objectclass: top\n", + "objectclass: LDAPsubentry\n", + "objectclass: nsRoleDefinition\n", + "objectclass: nsComplexRoleDefinition\n", + "objectclass: nsNestedRoleDefinition\n", + "nsRoleDN: cn=nsManagedDisabledRole,@suffixN\n", + "cn: nsDisabledRole\n\n" ); + @cos1=( + "dn: cn=nsAccountInactivationTmp,@suffixN\n", + "objectclass: top\n", + "objectclass: nsContainer\n\n" ); + @cos2=( + "dn: cn=\"cn=nsDisabledRole,@suffixN\",cn=nsAccountInactivationTmp,@suffixN\n", + "objectclass: top\n", + "objectclass: extensibleObject\n", + "objectclass: costemplate\n", + "objectclass: ldapsubentry\n", + "cosPriority: 1\n", + "nsAccountLock: true\n\n" ); + @cos3=( + "dn: cn=nsAccountInactivation_cos,@suffixN\n", + "objectclass: top\n", + "objectclass: LDAPsubentry\n", + "objectclass: cosSuperDefinition\n", + "objectclass: cosClassicDefinition\n", + "cosTemplateDn: cn=nsAccountInactivationTmp,@suffixN\n", + "cosSpecifier: nsRole\n", + "cosAttribute: nsAccountLock operational\n\n" ); + + @all=(\@role1, \@role2, \@cos1, \@cos2, \@cos3); + + $i=0; + + foreach $current (@base) + { + debug("Creating $current ??\n"); + open(FD,"| $addrolescos "); + print FD @{$all[$i]}; + close(FD); + if ( $? != 0 ) + { + $retCode=$?>>8; + if ( $retCode == 68 ) + { + debug("Entry $current already exists, ignore error\n"); + } + else + { + # Probably a more serious problem. + # Exit with LDAP error + exit $retCode; + } + } + else + { + debug("Entry $current created\n"); + } + $i=$i+1; + } +} + +$skipManaged=0; +$skipDisabled=0; +$directLocked=0; + +$nsDisabledRole="cn=nsDisabledRole,@suffixN"; +$nsDisabledRole=~ tr/A-Z/a-z/; + +$nsManagedDisabledRole="cn=nsManagedDisabledRole,@suffixN"; +$nsManagedDisabledRole=~ tr/A-Z/a-z/; + +if ( $operation eq "inactivate" ) +{ + # Go through all the roles part of nsdisabledrole to check if the entry + # is a member of one of those roles + $ret=indirectLock("LDAP00", $entry, $nsDisabledRole); + if ( $ret == 0 ) + { + if ( $throughRole ne $nsDisabledRole && $throughRole ne $nsManagedDisabledRole ) + { + # indirect lock + out("$entry already $state through $throughRole.\n"); + } + else + { + # direct lock + out("$entry already $state.\n"); + } + exit 100; + } + elsif ( $isLocked == 1 ) + { + # the entry is not locked through a role, may be nsaccountlock is "hardcoded" ? + out("$entry already $state (probably directly).\n"); + exit 103; + } +} +elsif ( $operation eq "activate" || $operation eq "get status of" ) +{ + $skipManaged=$single; + $skipDisabled=$role; + + $ret=indirectLock("LDAP00",$entry, $nsDisabledRole); + + if ( $ret == 0 ) + { + # undirectly locked + if ( $throughRole ne $nsDisabledRole && $throughRole ne $nsManagedDisabledRole ) + { + if ( $operation eq "activate" ) + { + out("$entry inactivated through $throughRole. Can not activate it individually.\n"); + exit 100; + } + else + { + out("$entry inactivated through $throughRole.\n"); + exit 104; + } + } + debug("$entry locked individually\n"); + + if ( $operation ne "activate" ) + { + out("$entry inactivated.\n"); + exit 103; + } + } + elsif ( $directLocked == 0 ) + { + if ( $operation eq "activate" && $isLocked != 1 ) + { + out("$entry $already $state.\n"); + exit 100; + } + elsif ( $isLocked != 1 ) + { + out("$entry $already $state.\n"); + exit 102; + } + else + { + # not locked using our schema, but nsaccountlock is probably present + out("$entry inactivated (probably directly).\n"); + exit 103; + } + } + elsif ( $operation ne "activate" ) + { + out("$entry inactivated.\n"); + exit 103; + } + # else Locked directly, juste unlock it! + debug("$entry locked individually\n"); +} + +# +# Inactivate/activate the entry +# +$action="$ldapmodify -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -c >> {{DEV-NULL}} 2>&1"; +if ( $single == 1 ) +{ + @record=( + "dn: $entry\n", + "changetype: modify\n", + "$modrole: nsRoleDN\n", + "nsRoleDN: cn=nsManagedDisabledRole,@suffixN\n\n" ); +} +else +{ + @record=( + "dn: cn=nsDisabledRole,@suffixN\n", + "changetype: modify\n", + "$modrole: nsRoleDN\n", + "nsRoleDN: $entry\n\n" ); +} +open(FD,"| $action "); +print FD @record; +close(FD); +if ( $? != 0 ) +{ +debug("$modrole, $entry\n"); + $retCode=$?>>8; + exit $retCode; +} + +out("$entry $state.\n"); +exit 0; diff --git a/ldap/admin/src/scripts/template-ns-inactivate.pl b/ldap/admin/src/scripts/template-ns-inactivate.pl new file mode 100644 index 00000000..5913f7ad --- /dev/null +++ b/ldap/admin/src/scripts/template-ns-inactivate.pl @@ -0,0 +1,813 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +############################### +# SUB-ROUTINES +############################### + +sub usage_and_exit +{ + print (STDERR "$cmd [-D rootdn] { -w password | -w - | -j filename } \n"); + print (STDERR " [-p port] [-h host] -I DN-to-$operation\n\n"); + print (STDERR "May be used to $operation a user or a domain of users\n\n"); + print (STDERR "Arguments:\n"); + print (STDERR " -? - help\n"); + print (STDERR " -D rootdn - Provide a Directory Manager DN. Default= '$defrootdn'\n"); + print (STDERR " -w password - Provide a password for the Directory Manager DN\n"); + print (STDERR " -w - - Prompt for the Directory Manager's password\n"); + print (STDERR " -j filename - Read the Directory Manager's password from file\n"); + print (STDERR " -p port - Provide a port. Default= '$defport'\n"); + print (STDERR " -h host - Provide a host name. Default= '$defhost'\n"); + print (STDERR " -I DN-to-$operation - Single entry DN or role DN to $operation\n"); + exit 100; +} + +sub debug +{ +# print " ==> @_"; +} + +sub out +{ + print "@_"; +} + +# -------------------------- +# Check if the entry is part of a locked role: +# i.e.: for each role member (nsroledn) of nsdisabledrole, check if +# * it is the same as the entry +# * the entry is member of role (==has nsroledn attributes), compare each of +# them with the nsroledn of nsdisabledrole +# * if nsroledn of nsdisabledrole are complex, go through each of them +# argv[0] is the local file handler +# argv[1] is the entry (may be a single entry DN or a role DN) +# argv[2] is the base for the search +# -------------------------- + +$throughRole=""; + +sub indirectLock +{ + # For recursivity, file handler must be local + my $L_filehandle=$_[0]; + $L_filehandle++; + + my $L_entry=$_[1]; + # Remove useless space + my @L_intern=split /([,])/,$L_entry; + my $L_result=""; + foreach $L_part (@L_intern) + { + $L_part=~s/^ +//; + $L_part=~ tr/A-Z/a-z/; + $L_result="$L_result$L_part"; + } + $L_entry=$L_result; + + my $L_base=$_[2]; + + my $L_search; + my $L_currentrole; + my $L_retCode; + + my $L_local; + +`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsroledn >> {{DEV-NULL}} 2>&1 `; +$retCode=$?; +if ( $retCode != 0 ) +{ + $retCode=$?>>8; + return 1; +} + + # Check if the role is a nested role + @L_Nested="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=nsNestedRoleDefinition)(objectclass=ldapsubentry))\" "; + # L_isNested == 1 means that we are going through a nested role, so for each member of that + # nested role, check that the member is below the scope of the nested + $L_isNested=@L_Nested; + + # Not Direct Lock, Go through roles if any + $L_search="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_base\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsroledn "; + + debug("\t-->indirectLock: check if $L_entry is part of a locked role from base $L_base\n\n"); + + unless (open ($L_filehandle, "$L_search |")) + { + out("Can't open file $L_filehandle\n"); + exit; + } + while (<$L_filehandle>) { + + s/\n //g; + if (/^nsroledn: (.*)\n/) { + $L_currentrole = $1; + + # Remove useless space + my @L_intern=split /([,])/,$L_currentrole; + my $L_result=""; + foreach $L_part (@L_intern) + { + $L_part=~s/^ +//; + $L_part=~ tr/A-Z/a-z/; + $L_result="$L_result$L_part"; + } + $L_currentrole=$L_result; + + debug("\t-- indirectLock loop: current nsroledn $L_currentrole of base $L_base\n"); + if ( $L_isNested == 1 ) + { + if ( checkScope($L_currentrole, $L_base) == 0 ) + { + # Scope problem probably a bad conf, skip the currentrole + next; + } + } + + if ( $L_currentrole eq $L_entry ) + { + # the entry is a role that is directly locked + # i.e, nsroledn of nsdisabledrole contains the entry + $throughRole=$L_base; + $throughRole=~ tr/A-Z/a-z/; + + # skipDisabled means that we've just found that the entry (which is a role) + # is locked directly (==its DN is part of nsroledn attributes) + # we just want to know now, if it is locked through another role + # at least, one + if ( $skipDisabled == 1 ) + { + # direct inactivation + $directLocked=1; + # just go through that test once + $skipDisabled=0; + next; + } + debug("\t-- 1 indirectLock: $L_currentrole locked throughRole == $throughRole\n"); + return 0; + } + + $L_retCode=memberOf($L_currentrole, $L_entry); + if ( $L_retCode == 0 && $single == 1 ) + { + $throughRole=$L_currentrole; + $throughRole=~ tr/A-Z/a-z/; + if ( $skipManaged == 1 ) + { + if ( $L_currentrole eq $nsManagedDisabledRole) + { + # Try next nsroledn + $directLocked=1; + $skipManaged=0; + next; + } + } + debug("\t-- 2 indirectLock: $L_currentrole locked throughRole == $throughRole\n"); + return 0; + } + + # Only for the first iteration + # the first iteration is with nsdisabledrole as base, other + # loops are deeper + $L_local=$skipDisabled; + $skipDisabled=0; + + # the current nsroledn may be a complex role, just go through + # its won nsroledn + $L_retCode=indirectLock($L_filehandle,$L_entry, $L_currentrole); + + # Because of recursivity, to keep the initial value for the first level + $skipDisabled=$L_local; + + if ( $L_retCode == 0 ) + { + $throughRole=$L_currentrole; + $throughRole=~ tr/A-Z/a-z/; + debug("\t-- 3 indirectLock: $L_entry locked throughRole == $throughRole\n"); + return 0; + } + } + } + + close($L_filehandle); + + debug("\t<--indirectLock: no more nsroledn to process\n"); + return 1; +} + +# -------------------------- +# Check if nsroledn is part of the entry attributes +# argv[0] is a role DN (nsroledn attribute) +# argv[1] is the entry +# -------------------------- +sub memberOf +{ + my $L_nsroledn=$_[0]; + $L_nsroledn=~ tr/A-Z/a-z/; + + my $L_entry=$_[1]; + + my $L_search; + my $L_currentrole; + + $L_search="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$L_entry\" \"(|(objectclass=*)(objectclass=ldapsubentry))\" nsrole"; + + debug("\t\t-->memberOf: $L_search: check if $L_entry has $L_nsroledn as nsroledn attribute\n"); + + open (LDAP2, "$L_search |"); + while (<LDAP2>) { + s/\n //g; + if (/^nsrole: (.*)\n/) { + $L_currentrole = $1; + $L_currentrole=~ tr/A-Z/a-z/; + if ( $L_currentrole eq $L_nsroledn ) + { + # the parm is part of the $L_entry nsroledn + debug("\t\t<--memberOf: $L_entry locked through $L_nsroledn\n"); + return 0; + } + } + } + close(LDAP2); + + # the parm is not part of the $L_entry nsroledn + debug("\t\t<--memberOf: $L_entry not locked through $L_nsroledn\n"); + return 1; +} + + +# -------------------------- +# Remove the rdn of a DN +# argv[0] is a DN +# -------------------------- +sub removeRdn +{ + $L_entry=$_[0]; + + @L_entryToTest=split /([,])/,$L_entry; + debug("removeRdn: entry to split: $L_entry**@L_entryToTest\n"); + + $newDN=""; + $removeRDN=1; + foreach $part (@L_entryToTest) + { + $part=~ s/^ +//; + $part=~ tr/A-Z/a-z/; + if ( $removeRDN <= 2 ) + { + $removeRDN=$removeRDN+1; + } + else + { + $newDN="$newDN$part"; + } + } + + debug("removeRdn: new DN **$newDN**\n"); +} + +# -------------------------- +# Check if L_current is below the scope of +# L_nestedRole +# argv[0] is a role +# argv[1] is the nested role +# -------------------------- +sub checkScope +{ + $L_current=$_[0]; + $L_nestedRole=$_[1]; + + debug("checkScope: check if $L_current is below $L_nestedRole\n"); + + removeRdn($L_nestedRole); + $L_nestedRoleSuffix=$newDN; + debug("checkScope: nested role based: $L_nestedRoleSuffix\n"); + + $cont=1; + while ( ($cont == 1) && ($L_current ne "") ) + { + removeRdn($L_current); + $currentDn=$newDN; + debug("checkScope: current DN to check: $currentDn\n"); + + if ( $currentDn eq $L_nestedRoleSuffix ) + { + debug("checkScope: DN match!!!\n"); + $cont = 0; + } + else + { + $L_current=$currentDn; + } + } + + if ( $cont == 1 ) + { + debug("checkScope: $_[0] and $_[1] are not compatible\n"); + return 0; + } + else + { + debug("checkScope: $_[0] and $_[1] are compatible\n"); + return 1; + } +} + + +############################### +# MAIN ROUTINE +############################### + +# Generated variable +$dsroot="{{DS-ROOT}}"; + +# Determine which command we are running +if ( $0 =~ /ns-inactivate(.pl)?$/ ) +{ + $cmd="ns-inactivate.pl"; + $operation="inactivate"; + $state="inactivated"; + $modrole="add"; + $already="already"; +} +elsif ( $0 =~ /ns-activate(.pl)?$/ ) +{ + $cmd="ns-activate.pl"; + $operation="activate"; + $state="activated"; + $modrole="delete"; + $already="already"; +} +elsif ( $0 =~ /ns-accountstatus(.pl)?$/ ) +{ + $cmd="ns-accountstatus.pl"; + $operation="get status of"; + $state="activated"; + # no need for $modrole as no operation is performed + $already=""; + +} +else +{ + out("$0: unknown command\n"); + exit 100; +} + +debug("Running ** $cmd ** $operation\n"); + +$dsbinroot="$dsroot{{SEP}}shared{{SEP}}bin"; +$ldapsearch="$dsbinroot{{SEP}}ldapsearch -1"; +$ldapmodify="$dsbinroot{{SEP}}ldapmodify"; + +# Default values +$defrootdn= "{{ROOT-DN}}"; +$defhost= "{{SERVER-NAME}}"; +$defport= "{{SERVER-PORT}}"; + +# User values +$rootdn= "{{ROOT-DN}}"; +$rootpw= ""; +$pwfile= ""; +$host= "{{SERVER-NAME}}"; +$port= "{{SERVER-PORT}}"; +$entry= ""; + +$single=0; +$role=0; + +chdir("$dsbinroot"); + +# Process the command line arguments +while( $arg = shift) +{ + if($arg eq "-?") + { + usage_and_exit(); + } + elsif($arg eq "-D") + { + $rootdn= shift @ARGV; + } + elsif($arg eq "-w") + { + $rootpw= shift @ARGV; + } + elsif($arg eq "-j") + { + $pwfile= shift @ARGV; + } + elsif($arg eq "-p") + { + $port= shift @ARGV; + } + elsif($arg eq "-h") + { + $host= shift @ARGV; + } + elsif($arg eq "-I") + { + $entry= shift @ARGV; + } + else + { + print "$arg: Unknown command line argument.\n"; + usage_and_exit(); + } +} + +if ($pwfile ne ""){ +# Open file and get the password + unless (open (RPASS, $pwfile)) { + die "Error, cannot open password file $passwdfile\n"; + } + $rootpw = <RPASS>; + chomp($rootpw); + close(RPASS); +} elsif ($rootpw eq "-"){ +# Read the password from terminal + die "The '-w -' option requires an extension library (Term::ReadKey) which is not\n", + "part of the standard perl distribution. If you want to use it, you must\n", + "download and install the module. You can find it at\n", + "http://www.perl.com/CPAN/CPAN.html\n"; +# Remove the previous line and uncomment the following 6 lines once you have installed Term::ReadKey module. +# use Term::ReadKey; +# print "Bind Password: "; +# ReadMode('noecho'); +# $rootpw = ReadLine(0); +# chomp($rootpw); +# ReadMode('normal'); +} + +if( $rootpw eq "" ) +{ + usage_and_exit(); +} + +if( $entry eq "" ) +{ + usage_and_exit(); +} + +# +# Check the actual existence of the entry to inactivate/activate +# and at the same time, validate the various parm: port, host, rootdn, rootpw +# +@exist=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(objectclass=*)\" dn`; +$retCode1=$?; +if ( $retCode1 != 0 ) +{ + $retCode1=$?>>8; + exit $retCode1; +} + +@isRole=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(&(objectclass=LDAPsubentry)(objectclass=nsRoleDefinition))\" dn`; +$nbLineRole=@isRole; +$retCode2=$?; +if ( $retCode2 != 0 ) +{ + $retCode2=$?>>8; + exit $retCode2; +} + +if ( $nbLineRole == 1 ) +{ + debug("Groups of users\n"); + $role=1; +} +else +{ + debug("Single user\n"); + $single=1; +} + +# +# First of all, check the existence of the nsaccountlock attribute in the entry +# +$isLocked=0; +if ( $single == 1 ) +{ + $searchAccountLock="$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s base -b \"$entry\" \"(objectclass=*)\" nsaccountlock"; + open (LDAP1, "$searchAccountLock |"); + while (<LDAP1>) { + s/\n //g; + if (/^nsaccountlock: (.*)\n/) { + $L_currentvalue = $1; + $L_currentvalue=~ tr/A-Z/a-z/; + if ( $L_currentvalue eq "true") + { + $isLocked=1; + } + elsif ( $L_currentvalue eq "false" ) + { + $isLocked=0; + } + } + } + close(LDAP1); +} +debug("Is the entry already locked? ==> $isLocked\n"); + +# +# Get the suffix name of that entry +# + +# Remove the space at the beginning (just in case...) +# -I "uid=jvedder , ou=People , o=sun.com" +@suffix=split /([,])/,$entry; +$result=""; +foreach $part (@suffix) +{ + $part=~s/^ +//; + $part=~ tr/A-Z/a-z/; + $result="$result$part"; +} +@suffixN=$result; + +debug("Entry to $operation: #@suffix#\n"); +debug("Entry to $operation: #@suffixN#\n"); + +# Get the suffix +$cont=0; +while ($cont == 0) +{ + # Look if suffix is the suffix of the entry + # ldapsearch -s one -b "cn=mapping tree,cn=config" "cn=\"uid=jvedder,ou=People,o=sun.com\"" + # + debug("\tSuffix from the entry: #@suffixN#\n"); + @mapping=`$ldapsearch -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -s one -b \"cn=mapping tree, cn=config\" \"cn=\\"@suffixN\\"\" cn `; + + $retCode=$?; + if ( $retCode != 0 ) + { + $retCode=$?>>8; + exit $retCode; + } + + # If we get a result, remove the dn: + # dn: cn="o=sun.com",cn=mapping tree,cn=config + # cn: "o=sun.com" + # + shift @mapping; + + foreach $res (@mapping) + { + # Break the string cn: "o=sun.com" into pieces + @cn= split(/ /,$res); + + # And remove the cn: part + shift @cn; + + # Now compare the suffix we extract from the mapping tree + # with the suffix derived from the entry + debug("\tSuffix from mapping tree: #@cn#\n"); + if ( @cn eq @suffixN ) { + debug("Found matching suffix\n"); + $cont=1; + } + } + + if ( $cont == 0 ) + { + # Remove the current rdn to try another suffix + shift @suffix; + + $result=""; + foreach $part (@suffix) + { + $part=~ s/^ +//; + $part=~ tr/A-Z/a-z/; + $result="$result$part"; + } + @suffixN=$result; + + debug("\t\tNothing found => go up one level in rdn #@suffix#\n"); + $len=@suffix; + if ( $len == 0 ) + { + debug("Can not find suffix. Problem\n"); + $cont=2; + } + } +} +if ( $cont == 2) +{ + out("Can not find suffix for entry $entry\n"); + exit 100; +} + +if ( $operation eq "inactivate" ) +{ + # + # Now that we have the suffix and we know if we deal with a single entry or + # a role, just try to create the COS and roles associated. + # + @base=( + "cn=nsManagedDisabledRole,@suffixN", + "cn=nsDisabledRole,@suffixN", + "cn=nsAccountInactivationTmp,@suffixN", + "\'cn=\"cn=nsDisabledRole,@suffixN\",cn=nsAccountInactivationTmp,@suffixN\'", + "cn=nsAccountInactivation_cos,@suffixN" ); + + $addrolescos="$ldapmodify -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -c -a >> {{DEV-NULL}} 2>&1 "; + @role1=( + "dn: cn=nsManagedDisabledRole,@suffixN\n", + "objectclass: LDAPsubentry\n", + "objectclass: nsRoleDefinition\n", + "objectclass: nsSimpleRoleDefinition\n", + "objectclass: nsManagedRoleDefinition\n", + "cn: nsManagedDisabledRole\n\n" ); + @role2=( + "dn: cn=nsDisabledRole,@suffixN\n", + "objectclass: top\n", + "objectclass: LDAPsubentry\n", + "objectclass: nsRoleDefinition\n", + "objectclass: nsComplexRoleDefinition\n", + "objectclass: nsNestedRoleDefinition\n", + "nsRoleDN: cn=nsManagedDisabledRole,@suffixN\n", + "cn: nsDisabledRole\n\n" ); + @cos1=( + "dn: cn=nsAccountInactivationTmp,@suffixN\n", + "objectclass: top\n", + "objectclass: nsContainer\n\n" ); + @cos2=( + "dn: cn=\"cn=nsDisabledRole,@suffixN\",cn=nsAccountInactivationTmp,@suffixN\n", + "objectclass: top\n", + "objectclass: extensibleObject\n", + "objectclass: costemplate\n", + "objectclass: ldapsubentry\n", + "cosPriority: 1\n", + "nsAccountLock: true\n\n" ); + @cos3=( + "dn: cn=nsAccountInactivation_cos,@suffixN\n", + "objectclass: top\n", + "objectclass: LDAPsubentry\n", + "objectclass: cosSuperDefinition\n", + "objectclass: cosClassicDefinition\n", + "cosTemplateDn: cn=nsAccountInactivationTmp,@suffixN\n", + "cosSpecifier: nsRole\n", + "cosAttribute: nsAccountLock operational\n\n" ); + + @all=(\@role1, \@role2, \@cos1, \@cos2, \@cos3); + + $i=0; + + foreach $current (@base) + { + debug("Creating $current ??\n"); + open(FD,"| $addrolescos "); + print FD @{$all[$i]}; + close(FD); + if ( $? != 0 ) + { + $retCode=$?>>8; + if ( $retCode == 68 ) + { + debug("Entry $current already exists, ignore error\n"); + } + else + { + # Probably a more serious problem. + # Exit with LDAP error + exit $retCode; + } + } + else + { + debug("Entry $current created\n"); + } + $i=$i+1; + } +} + +$skipManaged=0; +$skipDisabled=0; +$directLocked=0; + +$nsDisabledRole="cn=nsDisabledRole,@suffixN"; +$nsDisabledRole=~ tr/A-Z/a-z/; + +$nsManagedDisabledRole="cn=nsManagedDisabledRole,@suffixN"; +$nsManagedDisabledRole=~ tr/A-Z/a-z/; + +if ( $operation eq "inactivate" ) +{ + # Go through all the roles part of nsdisabledrole to check if the entry + # is a member of one of those roles + $ret=indirectLock("LDAP00", $entry, $nsDisabledRole); + if ( $ret == 0 ) + { + if ( $throughRole ne $nsDisabledRole && $throughRole ne $nsManagedDisabledRole ) + { + # indirect lock + out("$entry already $state through $throughRole.\n"); + } + else + { + # direct lock + out("$entry already $state.\n"); + } + exit 100; + } + elsif ( $isLocked == 1 ) + { + # the entry is not locked through a role, may be nsaccountlock is "hardcoded" ? + out("$entry already $state (probably directly).\n"); + exit 103; + } +} +elsif ( $operation eq "activate" || $operation eq "get status of" ) +{ + $skipManaged=$single; + $skipDisabled=$role; + + $ret=indirectLock("LDAP00",$entry, $nsDisabledRole); + + if ( $ret == 0 ) + { + # undirectly locked + if ( $throughRole ne $nsDisabledRole && $throughRole ne $nsManagedDisabledRole ) + { + if ( $operation eq "activate" ) + { + out("$entry inactivated through $throughRole. Can not activate it individually.\n"); + exit 100; + } + else + { + out("$entry inactivated through $throughRole.\n"); + exit 104; + } + } + debug("$entry locked individually\n"); + + if ( $operation ne "activate" ) + { + out("$entry inactivated.\n"); + exit 103; + } + } + elsif ( $directLocked == 0 ) + { + if ( $operation eq "activate" && $isLocked != 1 ) + { + out("$entry $already $state.\n"); + exit 100; + } + elsif ( $isLocked != 1 ) + { + out("$entry $already $state.\n"); + exit 102; + } + else + { + # not locked using our schema, but nsaccountlock is probably present + out("$entry inactivated (probably directly).\n"); + exit 103; + } + } + elsif ( $operation ne "activate" ) + { + out("$entry inactivated.\n"); + exit 103; + } + # else Locked directly, juste unlock it! + debug("$entry locked individually\n"); +} + +# +# Inactivate/activate the entry +# +$action="$ldapmodify -p $port -h $host -D \"$rootdn\" -w \"$rootpw\" -c >> {{DEV-NULL}} 2>&1"; +if ( $single == 1 ) +{ + @record=( + "dn: $entry\n", + "changetype: modify\n", + "$modrole: nsRoleDN\n", + "nsRoleDN: cn=nsManagedDisabledRole,@suffixN\n\n" ); +} +else +{ + @record=( + "dn: cn=nsDisabledRole,@suffixN\n", + "changetype: modify\n", + "$modrole: nsRoleDN\n", + "nsRoleDN: $entry\n\n" ); +} +open(FD,"| $action "); +print FD @record; +close(FD); +if ( $? != 0 ) +{ +debug("$modrole, $entry\n"); + $retCode=$?>>8; + exit $retCode; +} + +out("$entry $state.\n"); +exit 0; diff --git a/ldap/admin/src/scripts/template-ns-newpwpolicy.pl b/ldap/admin/src/scripts/template-ns-newpwpolicy.pl new file mode 100755 index 00000000..dd57c944 --- /dev/null +++ b/ldap/admin/src/scripts/template-ns-newpwpolicy.pl @@ -0,0 +1,241 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +# Add new password policy specific entries + +############################################################################# +# enable the use of Perldap functions +require DynaLoader; + +use Getopt::Std; +use Mozilla::LDAP::Conn; +use Mozilla::LDAP::Utils qw(:all); +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API + +############################################################################# +# Default values of the variables + +$opt_D = "{{ROOT-DN}}"; +$opt_p = "{{SERVER-PORT}}"; +$opt_h = "{{SERVER-NAME}}"; +$opt_v = 0; + +# Variables +$ldapsearch="{{DS-ROOT}}{{SEP}}shared{{SEP}}bin{{SEP}}ldapsearch"; +$ldapmodify="{{DS-ROOT}}{{SEP}}shared{{SEP}}bin{{SEP}}ldapmodify"; + +chdir("{{DS-ROOT}}{{SEP}}shared{{SEP}}bin"); + +############################################################################# + +sub usage { + print (STDERR "ns-newpwpolicy.pl [-v] [-D rootdn] { -w password | -j filename } \n"); + print (STDERR " [-p port] [-h host] -U UserDN -S SuffixDN\n\n"); + + print (STDERR "Arguments:\n"); + print (STDERR " -? - help\n"); + print (STDERR " -v - verbose output\n"); + print (STDERR " -D rootdn - Directory Manager DN. Default= '$opt_D'\n"); + print (STDERR " -w rootpw - password for the Directory Manager DN\n"); + print (STDERR " -j filename - Read the Directory Manager's password from file\n"); + print (STDERR " -p port - port. Default= $opt_p\n"); + print (STDERR " -h host - host name. Default= '$opt_h'\n"); + print (STDERR " -U userDN - User entry DN\n"); + print (STDERR " -S suffixDN - Suffix entry DN\n"); + exit 100; +} + +# Process the command line arguments +{ + usage() if (!getopts('vD:w:j:p:h:U:S:')); + + if ($opt_j ne ""){ + die "Error, cannot open password file $opt_j\n" unless (open (RPASS, $opt_j)); + $opt_w = <RPASS>; + chomp($opt_w); + close(RPASS); + } + + usage() if( $opt_w eq "" ); + if ($opt_U eq "" && $opt_S eq "") { + print (STDERR "Please provide at least -S or -U option.\n\n"); + } + + # Now, check if the user/group exists + + if ($opt_S) { + print (STDERR "host = $opt_h, port = $opt_p, suffixDN = \"$opt_S\"\n\n") if $opt_v; + @base=( + "cn=nsPwPolicyContainer,$opt_S", + "cn=\"cn=nsPwPolicyEntry,$opt_S\",cn=nsPwPolicyContainer,$opt_S", + "cn=\"cn=nsPwTemplateEntry,$opt_S\",cn=nsPwPolicyContainer,$opt_S", + "cn=nsPwPolicy_cos,$opt_S" + ); + + $ldapadd="$ldapmodify -p $opt_p -h $opt_h -D \"$opt_D\" -w \"$opt_w\" -c -a 2>&1"; + $modifyCfg="$ldapmodify -p $opt_p -h $opt_h -D \"$opt_D\" -w \"$opt_w\" -c 2>&1"; + + @container=( + "dn: cn=nsPwPolicyContainer,$opt_S\n", + "objectclass: top\n", + "objectclass: nsContainer\n\n" ); + @pwpolicy=( + "dn: cn=\"cn=nsPwPolicyEntry,$opt_S\",cn=nsPwPolicyContainer,$opt_S\n", + "objectclass: top\n", + "objectclass: ldapsubentry\n", + "objectclass: passwordpolicy\n\n" ); + @template=( + "dn: cn=\"cn=nsPwTemplateEntry,$opt_S\",cn=nsPwPolicyContainer,$opt_S\n", + "objectclass: top\n", + "objectclass: extensibleObject\n", + "objectclass: costemplate\n", + "objectclass: ldapsubentry\n", + "cosPriority: 1\n", + "pwdpolicysubentry: cn=\"cn=nsPwPolicyEntry,$opt_S\",cn=nsPwPolicyContainer,$opt_S\n\n" ); + @cos=( + "dn: cn=nsPwPolicy_cos,$opt_S\n", + "objectclass: top\n", + "objectclass: LDAPsubentry\n", + "objectclass: cosSuperDefinition\n", + "objectclass: cosPointerDefinition\n", + "cosTemplateDn: cn=\"cn=nsPwTemplateEntry,$opt_S\",cn=nsPwPolicyContainer,$opt_S\n", + "cosAttribute: pwdpolicysubentry default operational-default\n\n" ); + + @all=(\@container, \@pwpolicy, \@template, \@cos); + + $i=0; + + foreach $current (@base) + { + open(FD,"| $ldapadd"); + print FD @{$all[$i]}; + close(FD); + if ( $? != 0 ) { + $retCode=$?>>8; + if ( $retCode == 68 ) { + print( STDERR "Entry \"$current\" already exists. Please ignore the error\n\n"); + } + else { + # Probably a more serious problem. + # Exit with LDAP error + print(STDERR "Error $retcode while adding \"$current\". Exiting.\n"); + exit $retCode; + } + } + else { + print( STDERR "Entry \"$current\" created\n\n") if $opt_v; + } + $i=$i+1; + } + + $modConfig = "dn:cn=config\nchangetype: modify\nreplace:nsslapd-pwpolicy-local\nnsslapd-pwpolicy-local: on\n\n"; + open(FD,"| $modifyCfg "); + print(FD $modConfig); + close(FD); + $retcode = $?; + if ( $retcode != 0 ) { + print( STDERR "Error $retcode while modifing \"cn=config\". Exiting.\n" ); + exit ($retcode); + } + else { + print( STDERR "Entry \"cn=config\" modified\n\n") if $opt_v; + } + } # end of $opt_S + + if ($opt_U) { + my $norm_opt_U = normalizeDN($opt_U); + print (STDERR "host = $opt_h, port = $opt_p, userDN = \"$norm_opt_U\"\n\n") if $opt_v; + $retcode = `$ldapsearch -h $opt_h -p $opt_p -b \"$norm_opt_U\" -s base \"\"`; + if ($retcode != 0 ) { + print( STDERR "the user entry $norm_opt_U does not exist. Exiting.\n"); + exit ($retcode); + } + + print( STDERR "the user entry $norm_opt_U found..\n\n") if $opt_v; + + # Now, get the parentDN + @rdns = ldap_explode_dn($norm_opt_U, 0); + shift @rdns; + $parentDN = join(',', @rdns); + + print (STDERR "parentDN is $parentDN\n\n") if $opt_v; + + @base=( + "cn=nsPwPolicyContainer,$parentDN", + "cn=\"cn=nsPwPolicyEntry,$norm_opt_U\",cn=nsPwPolicyContainer,$parentDN" + ); + + $ldapadd="$ldapmodify -p $opt_p -h $opt_h -D \"$opt_D\" -w \"$opt_w\" -c -a 2>&1"; + $modifyCfg="$ldapmodify -p $opt_p -h $opt_h -D \"$opt_D\" -w \"$opt_w\" -c 2>&1"; + + @container=( + "dn: cn=nsPwPolicyContainer,$parentDN\n", + "objectclass: top\n", + "objectclass: nsContainer\n\n" ); + @pwpolicy=( + "dn: cn=\"cn=nsPwPolicyEntry,$norm_opt_U\",cn=nsPwPolicyContainer,$parentDN\n", + "objectclass: top\n", + "objectclass: ldapsubentry\n", + "objectclass: passwordpolicy\n\n" ); + + @all=(\@container, \@pwpolicy); + + $i=0; + + foreach $current (@base) + { + open(FD,"| $ldapadd "); + print FD @{$all[$i]}; + close(FD); + if ( $? != 0 ) { + $retCode=$?>>8; + if ( $retCode == 68 ) { + print( STDERR "Entry $current already exists. Please ignore the error\n\n"); + } + else { + # Probably a more serious problem. + # Exit with LDAP error + print(STDERR "Error $retcode while adding \"$current\". Exiting.\n"); + exit $retCode; + } + } + else { + print( STDERR "Entry $current created\n\n") if $opt_v; + } + $i=$i+1; + } + + $target = "cn=\"cn=nsPwPolicyEntry,$norm_opt_U\",cn=nsPwPolicyContainer,$parentDN"; + $modConfig = "dn: $norm_opt_U\nchangetype: modify\nreplace:pwdpolicysubentry\npwdpolicysubentry: $target\n\n"; + open(FD,"| $modifyCfg "); + print(FD $modConfig); + close(FD); + $retcode = $?; + if ( $retcode != 0 ) { + print( STDERR "Error $retcode while modifing $norm_opt_U. Exiting.\n" ); + exit ($retcode); + } + else { + print( STDERR "Entry \"$norm_opt_U\" modified\n\n") if $opt_v; + } + + $modConfig = "dn:cn=config\nchangetype: modify\nreplace:nsslapd-pwpolicy-local\nnsslapd-pwpolicy-local: on\n\n"; + open(FD,"| $modifyCfg "); + print(FD $modConfig); + close(FD); + $retcode = $?; + if ( $retcode != 0 ) { + print( STDERR "Error $retcode while modifing \"cn=config\". Exiting.\n" ); + exit ($retcode); + } + else { + print( STDERR "Entry \"cn=config\" modified\n\n") if $opt_v; + } + } # end of $opt_U +} diff --git a/ldap/admin/src/scripts/template-repl-monitor-cgi.pl b/ldap/admin/src/scripts/template-repl-monitor-cgi.pl new file mode 100755 index 00000000..b9494d0d --- /dev/null +++ b/ldap/admin/src/scripts/template-repl-monitor-cgi.pl @@ -0,0 +1,40 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright (C) 2002-2004 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +use Cgi; + +$params = ""; +$params .= " -h $cgiVars{'servhost'}" if $cgiVars{'servhost'}; +$params .= " -p $cgiVars{'servport'}" if $cgiVars{'servport'}; +$params .= " -f $cgiVars{'configfile'}" if $cgiVars{'configfile'}; +$params .= " -t $cgiVars{'refreshinterval'}" if $cgiVars{'refreshinterval'}; +if ($cgiVars{'admurl'}) { + $admurl = "$cgiVars{'admurl'}"; + if ( $ENV{'QUERY_STRING'} ) { + $admurl .= "?$ENV{'QUERY_STRING'}"; + } + elsif ( $ENV{'CONTENT_LENGTH'} ) { + $admurl .= "?$Cgi::CONTENT"; + } + $params .= " -u \"$admurl\""; +} +$siteroot = $cgiVars{'siteroot'}; +$perl = "$siteroot/bin/slapd/admin/bin/perl"; +$ENV{'LD_LIBRARY_PATH'} = "$siteroot/lib:$siteroot/lib/nsPerl5.005_03/lib"; + +# Save user-specified parameters as cookies in monreplication.properties. +# Sync up with the property file so that monreplication2 is interval, and +# monreplication3 the config file pathname. +$propertyfile = "$siteroot/bin/admin/admin/bin/property/monreplication.properties"; +$edit1 = "s#monreplication2=.*#monreplication2=$cgiVars{'refreshinterval'}#;"; +$edit2 = "s#^monreplication3=.*#monreplication3=$cgiVars{'configfile'}#;"; +system("$perl -p -i.bak -e \"$edit1\" -e \"$edit2\" $propertyfile"); + +# Now the real work +$replmon = "$siteroot/bin/slapd/admin/scripts/template-repl-monitor.pl"; +system("$perl $replmon $params"); diff --git a/ldap/admin/src/scripts/template-repl-monitor.pl b/ldap/admin/src/scripts/template-repl-monitor.pl new file mode 100755 index 00000000..78e3aa85 --- /dev/null +++ b/ldap/admin/src/scripts/template-repl-monitor.pl @@ -0,0 +1,956 @@ +#{{PERL-EXEC}} + +############################################################################## +# +# Copyright (C) 2002-2004 Netscape Communications Corporation. +# All Rights Reserved. +# +# FILE: repl-monitor.pl +# +# SYNOPSIS: +# repl-monitor.pl -f configuration-file [-h host] [-p port] [-r] \ +# [-u refresh-url] [-t refresh-interval] +# +# repl-monitor.pl -v +# +# DESCRIPTION: +# Given an LDAP replication "supplier" server, crawl over all the ldap +# servers via direct or indirect replication agreements. +# For each master replica discovered, display the maxcsn of the master +# and the replication status of all its lower level replicas. +# All output is in HTML. +# +# OPTIONS: +# +# -f configuration-file +# The configuration file contains the sections for the connection +# parameters, the server alias, and the thresholds for different colors +# when display the time lags between consumers and master. +# If the Admin Server is running on Windows, the configuration-file +# name may have format "D:/Netscape/replmon.conf". +# +# The connection parameter section consists of the section name +# followed by one of more connection parameter entries: +# +# [connection] +# host:port:binddn:bindpwd:bindcert +# host:port=shadowport:binddn:bindpwd:bindcert +# ... +# +# where host:port default (*:*) to that in a replication agreement, +# binddn default (*) to "cn=Directory Manager", and bindcert is the +# pathname of cert db if you want the script to connect to the server +# via SSL. If bindcert is omitted, the connection will be simple +# bind. +# "port=shadowport" means to use shadowport instead of port if port +# is specified in the replication agreement. This is useful when +# for example, ssl port is specified in a replication agreement, +# but you can't access the cert db from the machine where this +# script is running. So you could let the script to map the ssl +# port to a non-ssl port and use the simple bind. +# +# A server may have a dedicated or a share entry in the connection +# section. The script will find out the most matched entry for a given +# server. For example, if all the ldap servers except host1 share the +# same binddn and bindpassword, the connection section then just need +# two entries: +# +# [connection] +# *:*:binddn:bindpassword: +# host1:*:binddn:bindpassword: +# +# If a host:port is assigned an alias, then the alias instead of +# host:port will be displayed in The output file. Each host:port +# can have only one alias. But each alias may be used by more than +# one host:port. +# +# [alias] +# alias = host:port +# ... +# +# CSN time lags between masters and consumers might be displayed in +# different colors based on their range. The thresholds for different +# colors may be specified in color section: +# +# [color] +# lowmark (in minutes) = color +# ... +# If the color section or color entry is missing, the default color +# set is: green for [0-5) minutes lag, yellow [5-60), and red 60 and more. +# +# -h host +# Initial replication supplier's host. Default to the current host. +# +# -p port +# Initial replication supplier's port. Default to 389. +# +# -r If specified, -r causes the routine to be entered without printing +# HTML header information. This is suitable when making multiple calls +# to this routine (e.g. when specifying multiple, different, "unrelated" +# supplier servers) and expecting a single HTML output. +# +# -t refresh-interval +# Specify the refresh interval in seconds. This option has to be +# jointly used with option -u. +# +# -u refresh-url +# The output HTML file may invoke a CGI program periodically. If +# this CGI program in turn calls this script, the effect is that +# the output HTML file would automatically refresh itself. This +# is useful for continuing monitoring. See also option -t. +# +# -v Print out the version of this script +# +# DIAGNOSTICS: +# There are several ways to invoke this script if you got error +# "Can't locate Mozilla/LDAP/Conn.pm in @INC", or +# "usage: Undefined variable": +# +# 1. Set the first line of the script to #!<DSHOME>/bin/slapd/admin/bin/perl +# and run this script directly. +# +# 2. Run +# <DSHOME>/bin/slapd/admin/bin/perl repl-monitor.pl +# +# 3. Set environment variable PERL5LIB to your Perl lib dirs where +# Mozilla::LDAP module can be located. +# +# 4. Invoke the script as follows if <MYPERLDIR>/lib/site contains +# Mozilla/LDAP: +# <MYPERLDIR>/bin/perl -I <MYPERLDIR>/lib/site repl-monitor.pl +# +# If you get error "Can't load ...", try to set environment variable +# for library path to <DSHOME>/lib:<DSHOME>/lib/nsPerl5.005_03/lib +# +############################################################################# +$usage = "\nusage: $0 -f configuration-file [-h host] [-p port] [-r] [-u refresh-url] [-t refresh-interval]\n\nor : $0 -v\n"; + +use Getopt::Std; # parse command line arguments +use Mozilla::LDAP::Conn; # LDAP module for Perl +use Mozilla::LDAP::Utils qw(normalizeDN); # LULU, utilities. +use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API +use Time::Local; # to convert GMT Z strings to localtime + +# +# Global variables +# +$product = "Netscape Directory Server Replication Monitor"; +$version = "Version 1.0"; +# +# ldap servers given or discovered from the replication agreements: +# @servers = (host:port=shadowport:binddn:password:cert_db) +# +# entries read from the connection section of the configuration file: +# @allconnections = (host:port=shadowport:binddn:password:cert_db) +# +# aliases of ldap servers read from the configuration file: +# %allaliases{$host:$port}= (alias) +# +# replicas discovered on all ldap servers +# @allreplicas = (server#:replicaroot:replicatype:serverid:replicadn) +# +# ruvs retrieved from all replicas +# @allruvs{replica#:masterid} = (rawcsn:decimalcsn;mon/day/year hh:mi:ss) +# +# agreements discovered on all ldap supplier servers: +# @allagreements = (supplier_replica#:consumer#:conntype:schedule:status) +# the array may take another format after the consumer replicas are located: +# @allagreements = (supplier_replica#:consumer_replica#:conntype:schedule:status) +# + +#main +{ + # turn off buffered I/O + $| = 1; + + # Check for legal options + if (!getopts('h:p:f:ru:t:v')) { + print $usage; + exit -1; + } + + if ($opt_v) { + print "$product - $version\n"; + exit; + } + + $interval = $opt_t; + $interval = 300 if ( !$interval || $interval <= 0 ); + + # Get current date/time + $nowraw = localtime(); + ($wday, $mm, $dd, $tt, $yy) = split(/ /, $nowraw); + $now = "$wday $mm $dd $yy $tt"; + + # if no -r (Reenter and skip html header), print html header + if (!$opt_r) { + # print the HTML header + &print_html_header; + } else { + # print separator for new replication set + print "<hr width=90% size=3><br>\n"; + } + + exit -1 if &validateArgs < 0; + exit if &read_cfg_file ($opt_f) < 0; + + # Start with the given host and port + # The index names in %ld are defined in Mozilla::LDAP::Utils::ldapArgs() + &add_server ("$ld{host}:$ld{port}:$ld{bind}:$ld{pswd}:$ld{cert}"); + + $serveridx = 0; + while ($serveridx <= $#servers) { + if (&get_replicas ($serveridx) != 0 && $serveridx == 0) { + my ($host, $port, $binddn) = split (/:/, $servers[0]); + print("Login to $host:$port as \"$binddn\" failed\n"); + exit; + } + $serveridx++; + } + + &find_consumer_replicas; + &process_suppliers; + + # All done! - well, for the current invokation only + # print "</body></html>\n"; + exit; +} + +sub validateArgs +{ + my ($rc) = 0; + + %ld = Mozilla::LDAP::Utils::ldapArgs(); + + if (!$opt_v && !$opt_f) { + print "<p>Error: Missing configuration file.\n"; + print "<p>If you need help on the configuration file, Please go back and click the Help button.\n"; + #print $usage; # Don't show usage in CGI + $rc = -1; + } + elsif (!$opt_h) { + chop ($ld{"host"} = `hostname`); + } + + return $rc; +} + +sub read_cfg_file +{ + my ($fn) = @_; + unless (open(CFGFILEHANDLE, $fn)) { + print "<p>Error: Can't open \"$fn\": $!.\n"; + print "<p>If you need help on the configuration file, Please go back and click the Help button.\n"; + return -1; + } + $section = 0; + while (<CFGFILEHANDLE>) { + next if (/^\s*\#/ || /^\s*$/); + chop ($_); + if (m/^\[(.*)\]/) { + $section = $1; + } + else { + if ( $section =~ /conn/i ) { + push (@allconnections, $_); + } + elsif ( $section =~ /alias/i ) { + m/^\s*(\S.*)\s*=\s*(\S+)/; + $allaliases {$2} = $1; + } + elsif ( $section =~ /color/i ) { + m/^\s*(-?\d+)\s*=\s*(\S+)/; + $allcolors {$1} = $2; + } + } + } + if ( ! keys (%allcolors) ) { + $allcolors {0} = "#ccffcc"; #apple green + $allcolors {5} = "#ffffcc"; #cream yellow + $allcolors {60} = "#ffcccc"; #pale pink + } + @colorkeys = sort (keys (%allcolors)); + close (CFGFILEHANDLE); + return 0; +} + +sub get_replicas +{ + my ($serveridx) = @_; + my ($conn, $host, $port, $shadowport, $binddn, $bindpwd, $bindcert); + my ($others); + my ($replica, $replicadn); + my ($ruv, $replicaroot, $replicatype, $serverid, $masterid, $maxcsn); + my ($type, $flag, $i); + my ($myridx, $ridx, $cidx); + + # + # Bind to the server + # + ($host, $port, $binddn, $bindpwd, $bindcert) = split (/:/, "$servers[$serveridx]", 5); + + ($port, $shadowport) = split (/=/, $port); + $shadowport = $port if !$shadowport; + + $conn = new Mozilla::LDAP::Conn ($host, $shadowport, "$binddn", $bindpwd, $bindcert); + + return -1 if (!$conn); + + # + # Get all the replica on the server + # + $myridx = $#allreplicas + 1; + $replica = $conn->search ("cn=mapping tree,cn=config", + "sub", + "(objectClass=nsDS5Replica)", 0, + qw(nsDS5ReplicaRoot nsDS5ReplicaType nsDS5Flags nsDS5ReplicaId)); + while ($replica) { + $replicadn = $replica->getDN; + $replicaroot = normalizeDN ($replica->{nsDS5ReplicaRoot}[0]); + $type = $replica->{nsDS5ReplicaType}[0]; + $flag = $replica->{nsDS5Flags}[0]; + $serverid = $replica->{nsDS5ReplicaId}[0]; + + # flag = 0: change log is not created + # type = 2: read only replica + # type = 3: updatable replica + $replicatype = $flag == 0 ? "consumer" : ($type == 2 ? "hub" : "master"); + + push (@allreplicas, "$serveridx:$replicaroot:$replicatype:$serverid:$replicadn"); + + $replica = $conn->nextEntry (); + } + + # + # Get ruv for each replica + # + for ($ridx = $myridx; $ridx <= $#allreplicas; $ridx++) { + + $replicaroot = $1 if ($allreplicas[$ridx] =~ /^\d+:([^:]*)/); + # do a one level search with nsuniqueid in the filter - this will force the use of the + # nsuniqueid index instead of the entry dn index, which seems to be unreliable in + # heavily loaded servers + $ruv = $conn->search($replicaroot, "one", + "(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectClass=nsTombstone))", + 0, qw(nsds50ruv nsruvReplicaLastModified)); + next if !$ruv; # this should be an error case . . . + + for ($ruv->getValues('nsds50ruv')) { + if (m/\{replica\s+(\d+).+?\}\s*\S+\s*(\S+)/i) { + $masterid = $1; + $maxcsn = &to_decimal_csn ($2); + $allruvs {"$ridx:$masterid"} = "$2:$maxcsn"; + } + } + + for ($ruv->getValues('nsruvReplicaLastModified')) { + if (m/\{replica\s+(\d+).+?\}\s*(\S+)/i) { + $masterid = $1; + $lastmodifiedat = hex($2); + my ($sec, $min, $hour, $mday, $mon, $year) = localtime ($lastmodifiedat); + $mon++; + $year += 1900; + $hour = "0".$hour if ($hour < 10); + $min = "0".$min if ($min < 10); + $sec = "0".$sec if ($sec < 10); + $allruvs {"$ridx:$masterid"} .= ";$mon/$mday/$year $hour:$min:$sec"; + } + } + } + + # + # Get all agreements for each supplier replica + # + for ($ridx = $myridx; $ridx <= $#allreplicas; $ridx++) { + $_ = $allreplicas[$ridx]; + + # Skip consumers + next if m/:consumer:/i; + + m/:([^:]*)$/; + $replicadn = $1; + my @attrlist = qw(cn nsds5BeginReplicaRefresh nsds5replicaUpdateInProgress + nsds5ReplicaLastInitStatus nsds5ReplicaLastInitStart + nsds5ReplicaLastInitEnd nsds5replicaReapActive + nsds5replicaLastUpdateStart nsds5replicaLastUpdateEnd + nsds5replicaChangesSentSinceStartup nsds5replicaLastUpdateStatus + nsds5ReplicaHost + nsds5ReplicaPort nsDS5ReplicaBindMethod nsds5ReplicaUpdateSchedule); + $agreement = $conn->search("$replicadn", "sub", "(objectClass=nsDS5ReplicationAgreement)", + 0, @attrlist); + while ($agreement) { + + my %agmt = (); + # Push consumer to server stack if we have not already + $host = ($agreement->getValues('nsDS5ReplicaHost'))[0]; + $port = ($agreement->getValues('nsDS5ReplicaPort'))[0]; + $cidx = &add_server ("$host:$port"); + + for (@attrlist) { + $agmt{$_} = ($agreement->getValues($_))[0]; + } + if ($agmt{nsDS5ReplicaBindMethod} =~ /simple/i) { + $agmt{nsDS5ReplicaBindMethod} = 'n'; + } + if (!$agmt{nsds5ReplicaUpdateSchedule} || + ($agmt{nsds5ReplicaUpdateSchedule} eq '0000-2359 0123456') || + ($agmt{nsds5ReplicaUpdateSchedule} eq '*') || + ($agmt{nsds5ReplicaUpdateSchedule} eq '* *')) { + $agmt{nsds5ReplicaUpdateSchedule} = 'always in sync'; + } + + $agmt{ridx} = $ridx; + $agmt{cidx} = $cidx; + push @allagreements, \%agmt; + + $agreement = $conn->nextEntry (); + } + } + + $conn->close; +} + +# +# Initially, the agreements have consumer host:port info instead of +# replica info. This routine will find the consumer replica info +# +sub find_consumer_replicas +{ + my ($m_ridx); # index of master's replica + my ($s_ridx); # index of supplier's replica + my ($c_ridx); # index of consumer's replica + my ($c_sidx); # index of consumer server + my ($remainder); # + my ($s_replicaroot); # supplier replica root + my ($c_replicaroot); # consumer replica root + my ($j, $val); + + # + # Loop through every agreement defined on the current supplier replica + # + foreach (@allagreements) { + $s_ridx = $_->{ridx}; + $c_sidx = $_->{cidx}; + $s_replicaroot = $1 if ($allreplicas[$s_ridx] =~ /^\d+:([^:]*)/); + $c_replicaroot = ""; + + # $c_ridx will be assigned to -$c_sidx + # if the condumer is not accessible + # $c_sidx will not be zero since it's + # not the first server. + $c_ridx = -$c_sidx; # $c_sidx will not be zero + + # Loop through consumer's replicas and find + # the counter part for the current supplier + # replica + for ($j = 0; $j <= $#allreplicas; $j++) { + + # Get a replica on consumer + # I'm not sure what's going on here, but possibly could be made + # much simpler with normalizeDN and/or ldap_explode_dn + if ($allreplicas[$j] =~ /^$c_sidx:([^:]*)/) { + $val = $1; + + # We need to find out the consumer + # replica that matches the supplier + # replicaroot most. + if ($s_replicaroot =~ /^.*$val$/i && + length ($val) >= length ($c_replicaroot)) { + $c_ridx = $j; + + # Avoid case-sensitive comparison + last if (length($s_replicaroot) == length($val)); + $c_replicaroot = $val; + } + } + } + $_->{ridx} = $s_ridx; + $_->{cidx} = $c_ridx; + } +} + +sub process_suppliers +{ + my ($ridx, $mid, $maxcsn); + + $mid = ""; + + $last_sidx = -1; # global variable for print html page + + for ($ridx = 0; $ridx <= $#allreplicas; $ridx++) { + + # Skip consumers and hubs + next if $allreplicas[$ridx] !~ /:master:(\d+):/i; + $mid = $1; + + # Skip replicas without agreements defined yet + next if (! grep {$_->{ridx} == $ridx} @allagreements); + + $maxcsn = &print_master_header ($ridx, $mid); + if ( "$maxcsn" != "none" ) { + &print_consumer_header (); + &print_consumers ($ridx, $mid); + } + &print_supplier_end; + } + + if ($mid eq "") { + print "<p>The server is not a master or it has no replication agreement\n"; + } +} + +sub print_master_header +{ + my ($ridx, $mid) = @_; + my ($myruv) = $allruvs {"$ridx:$mid"}; + my ($maxcsnval) = split ( /;/, "$myruv" ); + my ($maxcsn) = &to_string_csn ($maxcsnval); + my ($sidx, $replicaroot, $replicatype, $serverid) = split (/:/, $allreplicas[$ridx]); + + # Print the master name + if ( $last_sidx != $sidx ) { + my ($ldapurl) = &get_ldap_url ($sidx, $sidx); + &print_legend if ( $last_sidx < 0); + print "<p><p><hr><p>\n"; + print "\n<p><center class=page-subtitle><font color=#0099cc>\n"; + print "Master:  $ldapurl</center>\n"; + $last_sidx = $sidx; + } + + # Print the current replica info onthe master + print "\n<p><table border=0 cellspacing=1 cellpadding=6 cols=10 width=100% class=bgColor9>\n"; + + print "\n<tr><td colspan=10><center>\n"; + print "<font class=areatitle>Replica ID: </font>"; + print "<font class=text28>$serverid</font>\n"; + + print "<font class=areatitle>Replica Root: </font>"; + print "<font class=text28>$replicaroot</font>\n"; + + print "<font class=areatitle>Max CSN: </font>"; + print "<font class=text28>$maxcsn</font>\n"; + + return $maxcsn; +} + +sub print_consumer_header +{ + #Print the header of consumer + print "\n<tr class=bgColor16>\n"; + print "<th nowrap>Receiver</th>\n"; + print "<th nowrap>Time Lag</th>\n"; + print "<th nowrap>Max CSN</th>\n"; + print "<th nowrap>Last Modify Time</th>\n"; + print "<th nowrap>Supplier</th>\n"; + print "<th nowrap>Sent/Skipped</th>\n"; + print "<th nowrap>Update Status</th>\n"; + print "<th nowrap>Update Started</th>\n"; + print "<th nowrap>Update Ended</th>\n"; + print "<th nowrap colspan=2>Schedule</th>\n"; + print "<th nowrap>SSL?</th>\n"; + print "</tr>\n"; +} + +sub print_consumers +{ + my ($m_ridx, $mid) = @_; + my ($ignore, $m_replicaroot) = split (/:/, $allreplicas[$m_ridx]); + my (@consumers, @ouragreements, @myagreements); + my ($s_ridx, $c_ridx, $conntype, $schedule, $status); + my ($c_maxcsn_str, $lag, $markcolor); + my ($c_replicaroot, $c_replicatype); + my ($first_entry); + my ($nrows); + my ($found); + + undef @ouragreements; + + # Collect all the consumer replicas for the current master replica + push (@consumers, $m_ridx); + foreach (@consumers) { + $s_ridx = $_; + for (@allagreements) { + next if ($_->{ridx} != $s_ridx); + $c_ridx = $_->{cidx}; + next if $c_ridx == $m_ridx; + push @ouragreements, $_; + $found = 0; + foreach (@consumers) { + if ($_ == $c_ridx) { + $found = 1; + last; + } + } + push (@consumers, $c_ridx) if !$found; + } + } + + # Print each consumer replica + my ($myruv) = $allruvs {"$m_ridx:$mid"}; + my ($m_maxcsn) = split ( /;/, "$myruv" ); + foreach (@consumers) { + $c_ridx = $_; + next if $c_ridx == $m_ridx; + + if ($c_ridx >= 0) { + $myruv = $allruvs {"$c_ridx:$mid"}; + ($c_maxcsn, $c_lastmodified) = split ( /;/, "$myruv" ); + ($c_maxcsn_str, $lag, $markcolor) = &cacl_time_lag ($m_maxcsn, $c_maxcsn); + $c_maxcsn_str =~ s/ /\<br\>/; + ($c_sidx, $c_replicaroot, $c_replicatype) = split (/:/, $allreplicas[$c_ridx]); + $c_replicaroot = "same as master" if $m_replicaroot eq $c_replicaroot; + } + else { + # $c_ridx is actually -$c_sidx when c is not available + $c_sidx = -$c_ridx; + $c_maxcsn_str = "_"; + $lag = "n/a"; + $markcolor = red; + $c_replicaroot = "_"; + $c_replicatype = "_"; + } + + $nrows = 0; + foreach (@ouragreements) { + next if ($_->{cidx} != $c_ridx); + $nrows++; + } + + $first_entry = 1; + foreach (@ouragreements) { + next if ($_->{cidx} != $c_ridx); + $s_ridx = $_->{ridx}; + $conntype = $_->{nsDS5ReplicaBindMethod}; + $status = $_->{nsds5replicaLastUpdateStatus}; + $schedule = $_->{nsds5ReplicaUpdateSchedule}; + $s_sidx = $1 if $allreplicas [$s_ridx] =~ /^(\d+):/; + $s_ldapurl = &get_ldap_url ($s_sidx, "n/a"); + + # Print out the consumer's replica and ruvs + print "\n<tr class=bgColor13>\n"; + if ($first_entry) { + $first_entry = 0; + $c_ldapurl = &get_ldap_url ($c_sidx, $conntype); + print "<td rowspan=$nrows width=5% class=bgColor5>$c_ldapurl<BR>Type: $c_replicatype</td>\n"; + print "<td rowspan=$nrows width=5% nowrap bgcolor=$markcolor><center>$lag</center></td>\n"; + print "<td rowspan=$nrows width=15% nowrap>$c_maxcsn_str</td>\n"; + print "<td rowspan=$nrows width=15% nowrap>$c_lastmodified</td>\n"; + } + print "<td width=5% nowrap><center>$s_ldapurl</center></td>\n"; + my $changecount = $_->{nsds5replicaChangesSentSinceStartup}; + if ( $changecount =~ /^$mid:(\d+)\/(\d+) / || $changecount =~ / $mid:(\d+)\/(\d+) / ) { + $changecount = "$1 / $2"; + } + elsif ( $changecount =~ /^(\d+)$/ ) { + $changecount = $changecount . " / " . "$_->{nsds5replicaChangesSkippedSinceStartup}"; + } + else { + $changecount = "0 / 0"; + } + print "<td width=3% nowrap>$changecount</td>\n"; + my $redfontstart = ""; + my $redfontend = ""; + if ($status =~ /error/i) { + $redfontstart = "<font color='red'>"; + $redfontend = "</font>"; + } + elsif ($status =~ /^(\d+) /) { + if ( $1 != 0 ) { + # warning + $redfontstart = "<font color='#FF7777'>"; + $redfontend = "</font>"; + } + } + print "<td width=20% nowrap>$redfontstart$status$redfontend</td>\n"; + print "<td nowrap>", &format_z_time($_->{nsds5replicaLastUpdateStart}), "</td>\n"; + print "<td nowrap>", &format_z_time($_->{nsds5replicaLastUpdateEnd}), "</td>\n"; + if ( $schedule =~ /always/i ) { + print "<td colspan=2 width=10% nowrap>$schedule</td>\n"; + } + else { + my ($ndays, @days); + $schedule =~ /(\d\d)(\d\d)-(\d\d)(\d\d) (\d+)/; + print "<td width=10% nowrap>$1:$2-$3:$4</td>\n"; + $ndays = $5; + $ndays =~ s/(\d)/$1,/g; + @days = (Sun,Mon,Tue,Wed,Thu,Fri,Sat)[eval $ndays]; + print "<td width=10% nowrap>@days</td>\n"; + } + print "<td width=3% nowrap class=bgColor5>$conntype</td>\n"; + } + } +} + +sub cacl_time_lag +{ + my ($s_maxcsn, $c_maxcsn) = @_; + my ($markcolor); + my ($csn_str); + my ($s_tm, $c_tm, $lag_tm, $lag_str, $hours, $minute); + + $csn_str = &to_string_csn ($c_maxcsn); + + if ($s_maxcsn && !$c_maxcsn) { + $lag_str = "- ?:??:??"; + $markcolor = &get_color (36000); # assume consumer has big latency + } + elsif (!$s_maxcsn && $c_maxcsn) { + $lag_str = "+ ?:??:??"; + $markcolor = &get_color (1); # consumer is ahead of supplier + } + elsif ($s_maxcsn le $c_maxcsn) { + $lag_str = "0:00:00"; + $markcolor = &get_color (0); + } + else { + my ($rawcsn, $decimalcsn) = split (/:/, $s_maxcsn); + ($s_tm) = split(/ /, $decimalcsn); + + ($rawcsn, $decimalcsn) = split (/:/, $c_maxcsn); + ($c_tm) = split(/ /, $decimalcsn); + if ($s_tm > $c_tm) { + $lag_tm = $s_tm - $c_tm; + $lag_str = "- "; + $markcolor = &get_color ($lag_tm); + } + else { + $lag_tm = $c_tm - $s_tm; + $lag_str = "+ "; + $markcolor = $allcolors{ $colorkeys[0] }; # no delay + } + $hours = int ($lag_tm / 3600); + $lag_str .= "$hours:"; + + $lag_tm = $lag_tm % 3600; + $minutes = int ($lag_tm / 60); + $minutes = "0".$minutes if ($minutes < 10); + $lag_str .= "$minutes:"; + + $lag_tm = $lag_tm % 60; + $lag_tm = "0".$lag_tm if ($lag_tm < 10); + $lag_str .= "$lag_tm"; + } + return ($csn_str, $lag_str, $markcolor); +} + +# +# The subroutine would append a new entry to the end of +# @servers if the host and port are new to @servers. +# +sub add_server +{ + my ($host, $port, $binddn, $bindpwd, $bindcert) = split (/:/, "@_"); + my ($shadowport) = $port; + my ($domainpattern) = '\.[^:]+'; + my ($i); + + # Remove the domain name from the host name + my ($hostnode) = $host; + $hostnode = $1 if $host =~ /^(\w+)\./; + + # new host:port + if ($binddn eq "" || $bindpwd eq "" && $bindcert eq "") { + # + # Look up connection parameter in the order of + # host:port + # host:* + # *:port + # *:* + # + my (@myconfig, $h, $p, $d, $w, $c); + (@myconfig = grep (/^$hostnode($domainpattern)*:$port\D/i, @allconnections)) || + (@myconfig = grep (/^$hostnode($domainpattern)*:\*:/i, @allconnections)) || + (@myconfig = grep (/^\*:$port\D/, @allconnections)) || + (@myconfig = grep (/^\*:\*\D/, @allconnections)); + if ($#myconfig >= 0) { + ($h, $p, $d, $w, $c) = split (/:/, $myconfig[0]); + ($p, $shadowport) = split (/=/, $p); + $p = "" if $p eq "*"; + $c = "" if $c eq "*"; + } + if ($binddn eq "" || $binddn eq "*") { + if ($d eq "" || $d eq "*") { + $binddn = "cn=Directory Manager"; + } + else { + $binddn = $d; + } + } + $bindpwd = $w if ($bindpwd eq "" || $bindpwd eq "*"); + $bindcert = $c if ($bindcert eq "" || $bindcert eq "*"); + } + + for ($i = 0; $i <= $#servers; $i++) { + return $i if ($servers[$i] =~ /$hostnode($domainpattern)*:\d*=$shadowport\D/i); + } + + push (@servers, "$host:$port=$shadowport:$binddn:$bindpwd:$bindcert"); + return $i; +} + +sub get_ldap_url +{ + my ($sidx, $conntype) = @_; + my ($host, $port) = split(/:/, $servers[$sidx]); + my ($shadowport); + ($port, $shadowport) = split (/=/, $port); + my ($protocol, $ldapurl); + + if ($port eq 636 && $conntype eq "0" || $conntype =~ /SSL/i) { + $protocol = ldaps; + } + else { + $protocol = ldap; + } + my ($instance) = $allaliases { "$host:$port" }; + $instance = "$host:$port" if !$instance; + if ($conntype eq "n/a") { + $ldapurl = $instance; + } + else { + $ldapurl = "<a href=\"$protocol://$host:$port/\">$instance</a>"; + } + return $ldapurl; +} + +sub to_decimal_csn +{ + my ($maxcsn) = @_; + if (!$maxcsn || $maxcsn eq "") { + return "none"; + } + + my ($tm, $seq, $masterid, $subseq) = unpack("a8 a4 a4 a4", $maxcsn); + + $tm = hex($tm); + $seq = hex($seq); + $masterid = hex($masterid); + $subseq = hex($subseq); + + return "$tm $seq $masterid $subseq"; +} + +sub to_string_csn +{ + my ($rawcsn, $decimalcsn) = split(/:/, "@_"); + if (!$rawcsn || $rawcsn eq "") { + return "none"; + } + my ($tm, $seq, $masterid, $subseq) = split(/ /, $decimalcsn); + my ($sec, $min, $hour, $mday, $mon, $year) = localtime($tm); + $mon++; + $year += 1900; + foreach ($sec, $min, $hour, $mday, $mon) { + $_ = "0".$_ if ($_ < 10); + } + my ($csnstr) = "$mon/$mday/$year $hour:$min:$sec"; + $csnstr .= " $seq $subseq" if ( $seq != 0 || $subseq != 0 ); + return "$rawcsn ($csnstr)"; +} + +sub get_color +{ + my ($lag_minute) = @_; + $lag_minute /= 60; + my ($color) = $allcolors { $colorkeys[0] }; + foreach (@colorkeys) { + last if ($lag_minute < $_); + $color = $allcolors {$_}; + } + return $color; +} + +# subroutine to remove escaped encoding + +sub unescape +{ + #my ($_) = @_; + tr/+/ /; + s/%(..)/pack("c",hex($1))/ge; + $_; +} + +sub print_html_header +{ + # print the HTML header + + print "Content-type: text/html\n\n"; + print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\"><html>\n"; + print "<head><title>Replication Status</title>\n"; + # print "<link type=text/css rel=stylesheet href=\"master-style.css\">\n"; + print "<style text/css>\n"; + print "Body, p, table, td, ul, li {color: #000000; font-family: Arial, Helvetica, sans-serif; font-size: 12px;}\n"; + print "A {color:blue; text-decoration: none;}\n"; + print "BODY {font-family: arial, helvetica, sans-serif}\n"; + print "P {font-family: arial, helvetica, sans-serif}\n"; + print "TH {font-weight: bold; font-family: arial, helvetica, sans-serif}\n"; + print "TD {font-family: arial, helvetica, sans-serif}\n"; + print ".bgColor1 {background-color: #003366;}\n"; + print ".bgColor4 {background-color: #cccccc;}\n"; + print ".bgColor5 {background-color: #999999;}\n"; + print ".bgColor9 {background-color: #336699;}\n"; + print ".bgColor13 {background-color: #ffffff;}\n"; + print ".bgColor16 {background-color: #6699cc;}\n"; + print ".text8 {color: #0099cc; font-size: 11px; font-weight: bold;}\n"; + print ".text28 {color: #ffcc33; font-size: 12px; font-weight: bold;}\n"; + print ".areatitle {font-weight: bold; color: #ffffff; font-family: arial, helvetica, sans-serif}\n"; + print ".page-title {font-weight: bold; font-size: larger; font-family: arial, helvetica, sans-serif}\n"; + print ".page-subtitle {font-weight: bold; font-family: arial, helvetica, sans-serif}\n"; + + print "</style></head>\n<body class=bgColor4>\n"; + + if ($opt_u) { + print "<meta http-equiv=refresh content=$interval; URL=$opt_u>\n"; + } + + print "<table border=0 cellspacing=0 cellpadding=10 width=100% class=bgColor1>\n"; + print "<tr><td><font class=text8>$now</font></td>\n"; + print "<td align=center class=page-title><font color=#0099CC>"; + print "Netscape Directory Server Replication Status</font>\n"; + + if ($opt_u) { + print "<br><font class=text8>(This page updates every $interval seconds)</font>\n"; + } + + print "</td><td align=right valign=center width=25%><font class=text8>$version"; + print "</font></td></table>\n"; +} + +sub print_legend +{ + my ($nlegends) = $#colorkeys + 1; + print "\n<center><p><font class=page-subtitle color=#0099cc>Time Lag Legend:</font><p>\n"; + print "<table cellpadding=6 cols=$nlegends width=40%>\n<tr>\n"; + my ($i, $j); + for ($i = 0; $i < $nlegends - 1; $i++) { + $j = $colorkeys[$i]; + print "\n<td bgcolor=$allcolors{$j}><center>within $colorkeys[$i+1] min</center></td>\n"; + } + $j = $colorkeys[$i]; + print "\n<td bgcolor=$allcolors{$j}><center>over $colorkeys[$i] min</center></td>\n"; + print "\n<td bgcolor=red><center>server n/a</center></td>\n"; + print "</table></center>\n"; +} + +sub print_supplier_end +{ + print "</table>\n"; +} + +# given a string in generalized time format, convert to ascii time +sub format_z_time +{ + my $zstr = shift; + return "n/a" if (! $zstr); + my ($year, $mon, $day, $hour, $min, $sec) = + ($zstr =~ /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/); + my $time = timegm($sec, $min, $hour, $day, ($mon-1), $year); + ($sec, $min, $hour, $day, $mon, $year) = localtime($time); + $mon++; + $year += 1900; + foreach ($sec, $min, $hour, $day, $mon) { + $_ = "0".$_ if ($_ < 10); + } + + return "$mon/$day/$year $hour:$min:$sec"; +} diff --git a/ldap/admin/src/scripts/template-verify-db.pl b/ldap/admin/src/scripts/template-verify-db.pl new file mode 100644 index 00000000..a6cd98ca --- /dev/null +++ b/ldap/admin/src/scripts/template-verify-db.pl @@ -0,0 +1,196 @@ +#{{PERL-EXEC}} +# +# BEGIN COPYRIGHT BLOCK +# Copyright (C) 2003-2004 AOL, Inc. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +sub getDbDir +{ + (my $here) = @_; + my @dbdirs = (); + + opendir(DIR, $here) or die "can't opendir $here : $!"; + while (defined($dir = readdir(DIR))) + { + my $thisdir; + if ("$here" eq ".") + { + $thisdir = $dir; + } + else + { + $thisdir = $here . "{{SEP}}" . $dir; + } + if (-d $thisdir) + { + if (!($thisdir =~ /\./)) + { + opendir(SUBDIR, "$thisdir") or die "can't opendir $thisdir : $!"; + while (defined($file = readdir(SUBDIR))) + { + if ($file eq "DBVERSION") + { + $#dbdirs++; + $dbdirs[$#dbdirs] = $thisdir; + } + } + closedir(SUBDIR); + } + } + } + closedir(DIR); + + return \@dbdirs; +} + +sub getLastLogfile +{ + (my $here) = @_; + my $logfile = ""; + + opendir(DIR, $here) or die "can't opendir $here : $!"; + while (defined($file = readdir(DIR))) + { + if ($file =~ /log./) + { + $logfile = $file; + } + } + closedir(DIR); + + return \$logfile; +} + +print("*****************************************************************\n"); +print("verify-db: This tool should only be run if recovery start fails\n" . + "and the server is down. If you run this tool while the server is\n" . + "running, you may get false reports of corrupted files or other\n" . + "false errors.\n"); +print("*****************************************************************\n"); + +# get dirs having DBVERSION +my $dbdirs = getDbDir("."); + +for (my $i = 0; $i < @$dbdirs; $i++) +{ + # run ../bin/slapd/server/db_printlog -h <dbdir> for each <dbdir> + print "Verify log files in $$dbdirs[$i] ... "; + open(PRINTLOG, "..{{SEP}}bin{{SEP}}slapd{{SEP}}server{{SEP}}db_printlog -h $$dbdirs[$i] 2>&1 1> nul |"); + sleep 1; + my $haserr = 0; + while ($l = <PRINTLOG>) + { + if ("$l" ne "") + { + if ($haserr == 0) + { + print "\n"; + } + print "LOG ERROR: $l"; + $haserr++; + } + } + close(PRINTLOG); + if ($haserr == 0 && $? == 0) + { + print "Good\n"; + } + else + { + my $logfile = getLastLogfile($$dbdirs[$i]); + print "Log file(s) in $$dbdirs[$i] could be corrupted.\n"; + print "Please delete a log file $$logfile, and try restarting the server.\n"; + } +} + +for (my $i = 0; $i < @$dbdirs; $i++) +{ + # changelog + opendir(DB, $$dbdirs[$i]) or die "can't opendir $$dbdirs[$i] : $!"; + while (defined($db = readdir(DB))) + { + if ($db =~ /\.db/) + { + my $thisdb = $$dbdirs[$i] . "{{SEP}}" . $db; + print "Verify $thisdb ... "; + open(DBVERIFY, "..{{SEP}}bin{{SEP}}slapd{{SEP}}server{{SEP}}db_verify $thisdb 2>&1 1> nul |"); + sleep 1; + my $haserr = 0; + while ($l = <DBVERIFY>) + { + if ($haserr == 0) + { + print "\n"; + } + if ("$l" ne "") + { + $haserr++; + print "DB ERROR: $l"; + } + } + close(DBVERIFY); + if ($haserr == 0 && $? == 0) + { + print "Good\n"; + } + else + { + print "changelog file $db in $$dbdirs[$i] is corrupted.\n"; + print "Please restore your backup and recover the database.\n"; + } + } + } + closedir(DB); + + # backend: get instance dirs under <dbdir> + my $instdirs = getDbDir($$dbdirs[$i]); + + for (my $j = 0; $j < @$instdirs; $j++) + { + opendir(DIR, $$instdirs[$j]) or die "can't opendir $here : $!"; + while (defined($db = readdir(DIR))) + { + if ($db =~ /\.db/) + { + my $thisdb = $$instdirs[$j] . "{{SEP}}" . $db; + print "Verify $thisdb ... "; + open(DBVERIFY, "..{{SEP}}bin{{SEP}}slapd{{SEP}}server{{SEP}}db_verify $thisdb 2>&1 1> null |"); + sleep 1; + my $haserr = 0; + while ($l = <DBVERIFY>) + { + if ($haserr == 0) + { + print "\n"; + } + if ("$l" ne "") + { + $haserr++; + print "DB ERROR: $l"; + } + } + close(DBVERIFY); + if ($haserr == 0 && $? == 0) + { + print "Good\n"; + } + else + { + if ("$db" =~ /id2entry.db/) + { + print "Primary db file $db in $$instdirs[$j] is corrupted.\n"; + print "Please restore your backup and recover the database.\n"; + } + else + { + print "Secondary index file $db in $$instdirs[$j] is corrupted.\n"; + print "Please run db2index(.pl) for reindexing.\n"; + } + } + } + } + closedir(DIR); + } +} |