#{{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 = ; 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 = ; if ($answer =~ /y|yes/i) { printMsg("Do you want to export the existing data Yes/No [Yes] ?"); my $answer = ; 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 = ; chomp($expLdif = $answer) unless ($answer eq "\n"); printMsg("\nExisting data will be exported under $expLdif"); printMsg("\nContinue Yes/No [No] ?"); $confirm = ; } $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 /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 = ; } } &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 = )) { if ($lineToBeginWith){ $lineToBeginWith -- ; next ; } next if ($l1 =~ /^\#/); $ret = defined($l2 = ); if ($ret) { $ret = defined($l2 = ) 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 = )) { 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 = ; if ($answer =~ /y|yes/i) { printMsg("$AllDiffs"); } printMsg("\nDo you want to continue the migration Yes/No [No] ?"); $answer = ; 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 () { ++$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 () { 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) && ($_ = ); $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 = ); 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 () { # 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 = ; 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 = ; 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 = ; $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 ( =~ /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 () { 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 /slapd-instance/config dans /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 = ; $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 ( =~ /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 (! ( =~ /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 () { 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 = ; 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 () { 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 = ); if (!($answer =~ /y|yes/i)) { print "\nPlease enter the correct path for the old instance directory: "; chomp($credOldHome = ); } 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 = ); if (!($answer =~ /y|yes/i)) { print "\nPlease enter the correct path for the new instance directory: "; chomp($credServerHome = ); } } # 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); } }