DSMigration.pm.in 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  1. # BEGIN COPYRIGHT BLOCK
  2. # This Program is free software; you can redistribute it and/or modify it under
  3. # the terms of the GNU General Public License as published by the Free Software
  4. # Foundation; version 2 of the License.
  5. #
  6. # This Program is distributed in the hope that it will be useful, but WITHOUT
  7. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. #
  10. # You should have received a copy of the GNU General Public License along with
  11. # this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. # Place, Suite 330, Boston, MA 02111-1307 USA.
  13. #
  14. # In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. # right to link the code of this Program with code not covered under the GNU
  16. # General Public License ("Non-GPL Code") and to distribute linked combinations
  17. # including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. # permitted under this exception must only link to the code of this Program
  19. # through those well defined interfaces identified in the file named EXCEPTION
  20. # found in the source code files (the "Approved Interfaces"). The files of
  21. # Non-GPL Code may instantiate templates or use macros or inline functions from
  22. # the Approved Interfaces without causing the resulting work to be covered by
  23. # the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. # additions to the list of Approved Interfaces. You must obey the GNU General
  25. # Public License in all respects for all of the Program code and other code used
  26. # in conjunction with the Program except the Non-GPL Code covered by this
  27. # exception. If you modify this file, you may extend this exception to your
  28. # version of the file, but you are not obligated to do so. If you do not wish to
  29. # provide this exception without modification, you must delete this exception
  30. # statement from your version and license this file solely under the GPL without
  31. # exception.
  32. #
  33. #
  34. # Copyright (C) 2007 Red Hat, Inc.
  35. # All rights reserved.
  36. # END COPYRIGHT BLOCK
  37. #
  38. ###########################
  39. #
  40. # This perl module provides a way to set up a new installation after
  41. # the binaries have already been extracted. This is typically after
  42. # using native packaging support to install the package e.g. RPM,
  43. # pkgadd, depot, etc. This script will show the license, readme,
  44. # dsktune, then run the usual setup pre and post installers.
  45. #
  46. ##########################
  47. package DSMigration;
  48. use Migration;
  49. use DSUtil;
  50. use Inf;
  51. use DSCreate;
  52. # tempfiles
  53. use File::Temp qw(tempfile tempdir);
  54. use File::Basename qw(basename);
  55. # absolute path handling
  56. use Cwd qw(realpath);
  57. # load perldap
  58. use Mozilla::LDAP::Conn;
  59. use Mozilla::LDAP::Utils qw(normalizeDN);
  60. use Mozilla::LDAP::API qw(ldap_explode_dn);
  61. use Mozilla::LDAP::LDIF;
  62. use Carp;
  63. use Exporter;
  64. @ISA = qw(Exporter);
  65. @EXPORT = qw(migrateDS);
  66. @EXPORT_OK = qw(migrateDS);
  67. use strict;
  68. use SetupLog;
  69. # these are the attributes for which we will always use
  70. # the new value, or which do not apply anymore
  71. # for the next major release e.g. when we support migration from the
  72. # current release 1.1.x to 1.2 or 2.0, the old version number will
  73. # become quite important for migration - for example, when migrating
  74. # from older than 1.1 to 1.1.x, we need to add the attributes in the
  75. # table below to the new entry because the attribute didn't exist
  76. # at all in the old server version - however, when migrating from
  77. # e.g. 1.1.x to 2.0, we must preserve the old value - this means
  78. # if the user has deleted the attribute from the entry, we must
  79. # "migrate" that deletion by removing the attribute from the new
  80. # entry
  81. my %ignoreOld =
  82. (
  83. 'nsslapd-errorlog' => 'nsslapd-errorlog',
  84. 'nsslapd-accesslog' => 'nsslapd-accesslog',
  85. 'nsslapd-auditlog' => 'nsslapd-auditlog',
  86. 'nskeyfile' => 'nsKeyfile',
  87. 'nscertfile' => 'nsCertfile',
  88. 'nsslapd-pluginpath' => 'nsslapd-pluginPath',
  89. 'nsslapd-plugintype' => 'nsslapd-pluginType',
  90. 'nsslapd-pluginversion' => 'nsslapd-pluginVersion',
  91. 'nsslapd-plugin-depends-on-named' => 'nsslapd-plugin-depends-on-named',
  92. # these are new attrs that we should just pass through
  93. 'nsslapd-allow-unauthenticated-binds' => 'nsslapd-allow-unauthenticated-binds',
  94. 'nsslapd-allow-anonymous-access' => 'nsslapd-allow-anonymous-access',
  95. 'nsslapd-saslpath' => 'nsslapd-saslpath',
  96. 'nsslapd-rundir' => 'nsslapd-rundir',
  97. 'nsslapd-schemadir' => 'nsslapd-schemadir',
  98. 'nsslapd-lockdir' => 'nsslapd-lockdir',
  99. 'nsslapd-tmpdir' => 'nsslapd-tmpdir',
  100. 'nsslapd-certdir' => 'nsslapd-certdir',
  101. 'nsslapd-ldifdir' => 'nsslapd-ldifdir',
  102. 'nsslapd-bakdir' => 'nsslapd-bakdir',
  103. 'nsslapd-instancedir' => 'nsslapd-instancedir',
  104. 'nsslapd-ldapifilepath' => 'nsslapd-ldapifilepath',
  105. 'nsslapd-ldapilisten' => 'nsslapd-ldapilisten',
  106. 'nsslapd-ldapiautobind' => 'nsslapd-ldapiautobind',
  107. 'nsslapd-ldapimaprootdn' => 'nsslapd-ldapimaprootdn',
  108. 'nsslapd-ldapimaptoentries' => 'nsslapd-ldapimaptoentries',
  109. 'nsslapd-ldapiuidnumbertype' => 'nsslapd-ldapiuidnumbertype',
  110. 'nsslapd-ldapigidnumbertype' => 'nsslapd-ldapigidnumbertype',
  111. 'nsslapd-ldapientrysearchbase' => 'nsslapd-ldapientrysearchbase',
  112. 'nsslapd-ldapiautodnsuffix' => 'nsslapd-ldapiautodnsuffix',
  113. 'numsubordinates' => 'numSubordinates',
  114. # for these, we just want to use the default values, even if they were
  115. # set in 7.1 or later
  116. 'nsslapd-db-private-import-mem' => 'nsslapd-db-private-import-mem',
  117. 'nsslapd-import-cache-autosize' => 'nsslapd-import-cache-autosize',
  118. # nsslapd-allidsthreshold does not exist anymore
  119. # the analogous concept is nsslapd-idlistscanlimit for searches
  120. 'nsslapd-allidsthreshold' => 'nsslapd-allidsthreshold'
  121. );
  122. # these are the obsolete entries we do not migrate
  123. my %ignoreOldEntries =
  124. (
  125. 'cn=presence,cn=plugins,cn=config' => 'cn=presence,cn=plugins,cn=config',
  126. 'cn=aim presence,cn=presence,cn=plugins,cn=config' => 'cn=aim presence,cn=presence,cn=plugins,cn=config',
  127. 'cn=icq presence,cn=presence,cn=plugins,cn=config' => 'cn=icq presence,cn=presence,cn=plugins,cn=config',
  128. 'cn=yahoo presence,cn=presence,cn=plugins,cn=config' => 'cn=yahoo presence,cn=presence,cn=plugins,cn=config'
  129. );
  130. # these are the attributes for which we will always use
  131. # the old value
  132. my %alwaysUseOld =
  133. (
  134. 'aci' => 'aci'
  135. );
  136. sub getDBVERSION {
  137. my $olddbdir = shift;
  138. my $data = shift;
  139. open DBVERSION, "$olddbdir/DBVERSION" or
  140. return ('error_reading_dbversion', $olddbdir, $!);
  141. my $line = <DBVERSION>;
  142. close DBVERSION;
  143. chomp($line);
  144. @{$data} = split("/", $line);
  145. return ();
  146. }
  147. sub isOldDatabase {
  148. my $olddbdir = shift;
  149. my $errs = shift; # array ref
  150. # check old DBVERSION file
  151. my @verinfo;
  152. if (@{$errs} = getDBVERSION($olddbdir, \@verinfo)) {
  153. return 0;
  154. }
  155. if ((($verinfo[0] =~ /^netscape/i) or ($verinfo[0] =~ /^iplanet/i)) and
  156. (($verinfo[1] =~ /^6/) or ($verinfo[1] =~ /^5/) or ($verinfo[1] =~ /^4/))) {
  157. return 1;
  158. }
  159. return 0;
  160. }
  161. sub getNewDbDir {
  162. my ($ent, $attr, $mig, $inst) = @_;
  163. my $newval;
  164. my %objclasses = map { lc($_) => $_ } $ent->getValues('objectclass');
  165. my $cn = $ent->getValues('cn');
  166. # there is one case where we want to just use the existing db directory
  167. # that's the case where the user has moved the indexes and/or the
  168. # transaction logs to different partitions for performance
  169. # in that case, the old directory will not be the same as the default,
  170. # and the directory will exist
  171. # for cross platform, we should just use the new default location
  172. if (!$mig->{crossplatform}) {
  173. my $oldval = $ent->getValues($attr);
  174. my $absoldval = realpath($oldval) || $oldval;
  175. my $olddefault = "$mig->{actualsroot}/$inst";
  176. if (-d $absoldval and ($absoldval !~ /^$olddefault/)) {
  177. debug(2, "Keeping old value [$absoldval] for attr $attr in entry ", $ent->getDN(), "\n");
  178. return $oldval;
  179. }
  180. }
  181. # otherwise, just use the new default locations
  182. if ("@with_fhs_opt@") {
  183. if ($objclasses{nsbackendinstance}) {
  184. $newval = "@localstatedir@/$mig->{pkgname}/$inst/db/$cn";
  185. } elsif (lc $cn eq 'config') {
  186. $newval = "@localstatedir@/$mig->{pkgname}/$inst/db";
  187. } elsif (lc $cn eq 'changelog5') {
  188. $newval = "@localstatedir@/$mig->{pkgname}/$inst/changelogdb";
  189. }
  190. } else {
  191. if ($objclasses{nsbackendinstance}) {
  192. $newval = "@localstatedir@/lib/$mig->{pkgname}/$inst/db/$cn";
  193. } elsif (lc $cn eq 'config') {
  194. $newval = "@localstatedir@/lib/$mig->{pkgname}/$inst/db";
  195. } elsif (lc $cn eq 'changelog5') {
  196. $newval = "@localstatedir@/lib/$mig->{pkgname}/$inst/changelogdb";
  197. }
  198. }
  199. debug(2, "New value [$newval] for attr $attr in entry ", $ent->getDN(), "\n");
  200. return $newval;
  201. }
  202. sub migrateCredentials {
  203. my ($ent, $attr, $mig, $inst) = @_;
  204. my $oldval = $ent->getValues($attr);
  205. my $qoldval = shellEscape($oldval);
  206. # Older versions of the server on x86 systems and other systems that do not use network byte order
  207. # stored the credentials incorrectly. The first step is to determine if this is the case. We
  208. # migrate using the same server root to see if we get the same output as we input.
  209. debug(3, "In migrateCredentials - see how old credentials were encoded.\n");
  210. my $testval = `@bindir@/migratecred -o $mig->{actualsroot}/$inst -n $mig->{actualsroot}/$inst -c $qoldval`;
  211. chomp($testval);
  212. if ($testval ne $oldval) { # need to turn on the special flag
  213. debug(3, "Credentials not encoded correctly. oldval $oldval not equal to testval $testval. The value will be re-encoded correctly.\n");
  214. $ENV{MIGRATE_BROKEN_PWD} = "1"; # decode and re-encode correctly
  215. }
  216. debug(3, "Executing @bindir@/migratecred -o $mig->{actualsroot}/$inst -n @instconfigdir@/$inst -c $qoldval . . .\n");
  217. my $newval = `@bindir@/migratecred -o $mig->{actualsroot}/$inst -n @instconfigdir@/$inst -c $qoldval`;
  218. chomp($newval);
  219. delete $ENV{MIGRATE_BROKEN_PWD}; # clear the flag, if set
  220. debug(3, "Converted old value [$oldval] to new value [$newval] for attr $attr in entry ", $ent->getDN(), "\n");
  221. return $newval;
  222. }
  223. sub removensState {
  224. my ($ent, $attr, $mig, $inst) = @_;
  225. my $newval;
  226. # nsstate is binary and cannot be migrated cross platform
  227. if (!$mig->{crossplatform}) {
  228. $newval = $ent->getValues($attr);
  229. }
  230. return $newval;
  231. }
  232. sub migIdlSwitch {
  233. my ($ent, $attr, $mig, $inst) = @_;
  234. my $newval;
  235. # if doing cross platform migration, just use the default value for
  236. # nsslapd-idl-switch
  237. # if not doing cross platform, meaning we just use the existing
  238. # database binaries, we must preserve whatever the old value is
  239. # unless migrating from 6.21 or earlier, in which case we must
  240. # be migrating from LDIF, and must use the new idl switch
  241. if (!$mig->{crossplatform}) {
  242. # the given entry is the old entry - see if it has the nsslapd-directory
  243. my $olddbdir = $ent->getValues('nsslapd-db-home-directory') ||
  244. $ent->getValues('nsslapd-directory') ||
  245. "$mig->{actualsroot}/$inst/db"; # old default db home directory
  246. # replace the old sroot value with the actual physical location on the target/dest
  247. $olddbdir =~ s/^$mig->{actualsroot}/$mig->{oldsroot}/;
  248. my @errs;
  249. my $isold = isOldDatabase($olddbdir, \@errs);
  250. if (@errs) {
  251. $mig->msg($FATAL, @errs);
  252. return $newval; # use default new value
  253. } elsif ($isold) {
  254. debug(3, "The database in $olddbdir is too old to migrate the idl switch setting\n");
  255. return $newval; # use default new value
  256. }
  257. # else the database could be in the new format already - preserve
  258. # the user's old value
  259. $newval = $ent->getValues($attr);
  260. }
  261. return $newval;
  262. }
  263. # these are attributes that we have to transform from
  264. # the old value to the new value (e.g. a pathname)
  265. # The key of this hash is the attribute name. The value
  266. # is an anonymous sub which takes two arguments - the entry
  267. # and the old value. The return value of the sub is
  268. # the new value
  269. my %transformAttr =
  270. (
  271. 'nsslapd-directory' => \&getNewDbDir,
  272. 'nsslapd-db-logdirectory' => \&getNewDbDir,
  273. 'nsslapd-changelogdir' => \&getNewDbDir,
  274. 'nsds5replicacredentials' => \&migrateCredentials,
  275. 'nsmultiplexorcredentials' => \&migrateCredentials,
  276. 'nsstate' => \&removensState,
  277. 'nsslapd-idl-switch' => \&migIdlSwitch
  278. );
  279. sub copyDatabaseDirs {
  280. my $srcdir = shift;
  281. my $destdir = shift;
  282. my $filesonly = shift;
  283. my @errs;
  284. my $isold = isOldDatabase($srcdir, \@errs);
  285. if (@errs) {
  286. return @errs;
  287. } elsif ($isold) {
  288. return ('error_database_too_old', $srcdir);
  289. }
  290. if (-d $srcdir && ! -d $destdir && !$filesonly) {
  291. debug(1, "Copying database directory $srcdir to $destdir\n");
  292. if (system ("cp -p -r $srcdir $destdir")) {
  293. return ('error_copying_dbdir', $srcdir, $destdir, $?);
  294. }
  295. } elsif (! -d $srcdir) {
  296. return ("error_dbsrcdir_not_exist", $srcdir);
  297. } else {
  298. debug(1, "The destination directory $destdir already exists, copying files/dirs individually\n");
  299. $! = 0;
  300. debug(1, "Removing any existing db files in $destdir\n");
  301. foreach my $file (glob("$destdir/*")) {
  302. next if (! -f $file);
  303. unlink($file);
  304. if ($!) {
  305. return ("error_removing_temp_db_files", $destdir, $!);
  306. }
  307. }
  308. foreach my $file (glob("$srcdir/*")) {
  309. if (-f $file) {
  310. debug(3, "Copying $file to $destdir\n");
  311. if (system ("cp -p $file $destdir")) {
  312. return ('error_copying_dbfile', $file, $destdir, $?);
  313. }
  314. } elsif (-d $file && !$filesonly) {
  315. debug(3, "Copying $file to $destdir\n");
  316. if (system ("cp -p -r $file $destdir")) {
  317. return ('error_copying_dbdir', $file, $destdir, $?);
  318. }
  319. }
  320. }
  321. }
  322. return ();
  323. }
  324. # older versions may use the old Netscape names e.g. Netscape Administration Server
  325. # we have to convert these to the new names e.g. @capbrand@ Administration Server
  326. sub migrateNetscapeRoot {
  327. my $ldiffile = shift;
  328. my ($fh, $tmpldiffile);
  329. # create a temp inf file for writing for other processes
  330. # never overwrite the user supplied inf file
  331. ($fh, $tmpldiffile) = tempfile("nsrootXXXXXX", UNLINK => 0,
  332. SUFFIX => ".ldif", OPEN => 1,
  333. DIR => File::Spec->tmpdir);
  334. if (!open( MYLDIF, "$ldiffile" )) {
  335. debug(1, "Error: Can't open $ldiffile: $!");
  336. return;
  337. }
  338. my $in = new Mozilla::LDAP::LDIF(*MYLDIF);
  339. while (my $ent = readOneEntry $in) {
  340. my $dn = $ent->getDN();
  341. next if (!$dn); # netscaperoot should not have the empty dn
  342. $dn =~ s/\bNetscape\b/@capbrand@/g;
  343. $ent->setDN($dn);
  344. foreach my $attr (keys %{$ent}) {
  345. my @vals = $ent->getValues($attr);
  346. map { s/\bNetscape\b/@capbrand@/g } @vals;
  347. $ent->setValues($attr, @vals);
  348. }
  349. Mozilla::LDAP::LDIF::put_LDIF($fh, 78, $ent);
  350. }
  351. close( MYLDIF );
  352. close( $fh );
  353. return $tmpldiffile;
  354. }
  355. sub fixIntegerIndexes {
  356. my $mig = shift;
  357. my $inst_dir = shift;
  358. my $newdbdir = shift;
  359. if (!$mig->{integerattrs}) {
  360. debug(1, "No integer syntax attributes, no indexes fixed\n");
  361. return ();
  362. }
  363. # look at each index file in the db dir
  364. # if it is on our list of integer attributes,
  365. # remove it and re-create it
  366. my $dbname = basename($newdbdir);
  367. for (glob("$newdbdir/*.db4")) {
  368. my $indexname = basename($_, '.db4');
  369. if ($mig->{integerattrs}->{lc $indexname}) {
  370. $mig->msg($INFO, 'fixing_integer_attr_index', $indexname, $newdbdir);
  371. debug(1, "Removing file $_\n");
  372. if (! unlink $_) {
  373. debug(1, "Error: could not remove file $_: $!\n");
  374. return ('error_removing_index_file', $_, $!);
  375. }
  376. my $cmd = "$inst_dir/db2index -n \"$dbname\" -t \"$indexname\"";
  377. debug(1, "Re-creating index file $_: $cmd\n");
  378. $? = 0; # clear error condition
  379. my $output = `$cmd 2>&1`;
  380. if ($?) {
  381. return ('error_recreating_index_file', $_, $output);
  382. }
  383. debug(1, $output);
  384. } else {
  385. debug(3, "Index $indexname is not for an integer syntax attribute - skipping\n");
  386. }
  387. }
  388. return ();
  389. }
  390. # migrate all of the databases in an instance
  391. sub migrateDatabases {
  392. my $mig = shift; # the Migration object
  393. my $inst = shift; # the instance name (e.g. slapd-instance)
  394. my $src = shift; # a Conn to the source
  395. my $dest = shift; # a Conn to the dest
  396. my $olddefault = "$mig->{actualsroot}/$inst/db"; # old default db home directory
  397. my @errs;
  398. # the ldif2db command will be in nsslapd-instancedir
  399. my $cfgent = $dest->search("cn=config", "base", "(objectclass=*)");
  400. my $inst_dir = $cfgent->getValues('nsslapd-instancedir');
  401. # first, look for an LDIF file in that directory with the same name as the
  402. # database
  403. my $foundldif;
  404. for (glob("$mig->{oldsroot}/$inst/db/*.ldif")) {
  405. my $fname = $_;
  406. my $dbname = basename($fname, '.ldif');
  407. my $deleteflag = 0;
  408. if ($fname =~ /NetscapeRoot.ldif$/) {
  409. $fname = migrateNetscapeRoot($fname);
  410. if ($fname) {
  411. # make sure $fname is owned by the server user
  412. my $cfgent = $dest->search("cn=config", "base", "(objectclass=*)");
  413. my $user = $cfgent->getValues('nsslapd-localuser');
  414. my $uid = getpwnam $user;
  415. chown $uid, -1, $fname;
  416. $deleteflag = 1;
  417. } else {
  418. return ("error_creating_templdif", $!);
  419. }
  420. }
  421. my $cmd = "$inst_dir/ldif2db -n \"$dbname\" -i \"$fname\"";
  422. debug(1, "migrateDatabases: executing command $cmd\n");
  423. $? = 0; # clear error condition
  424. my $output = `$cmd 2>&1`;
  425. if ($deleteflag) {
  426. unlink($fname);
  427. }
  428. if ($?) {
  429. return ('error_importing_migrated_db', $fname, $?, $output);
  430. }
  431. debug(1, $output);
  432. $foundldif = 1;
  433. }
  434. if ($foundldif) {
  435. return (); # done - can do nothing else for cross-platform
  436. } elsif ($mig->{crossplatform}) { # cross platform requires LDIF files
  437. return ('ldif_required_for_cross_platform', "$mig->{oldsroot}/$inst/db");
  438. }
  439. # if no LDIF files, just copy over the database directories
  440. my $ent = $src->search("cn=ldbm database,cn=plugins,cn=config", "one",
  441. "(objectclass=*)");
  442. if (!$ent) {
  443. return ("error_reading_olddbconfig", $src->getErrorString());
  444. }
  445. # there is one case where we want to just use the existing db directory
  446. # that's the case where the user has moved the indexes and/or the
  447. # transaction logs to different partitions for performance
  448. # in that case, the old directory will not be the same as the default,
  449. # and the directory will exist
  450. my $olddefault = "$mig->{actualsroot}/$inst";
  451. do {
  452. my $cn = $ent->getValues('cn');
  453. my %objclasses = map { lc($_) => $_ } $ent->getValues('objectclass');
  454. if ($cn eq 'config') { # global config
  455. my $newent = $dest->search($ent->getDN(), "base", "(objectclass=*)");
  456. my $newdbdir = "";
  457. if ("@with_fhs_opt@") {
  458. $newdbdir = $newent->getValues('nsslapd-directory') ||
  459. "@localstatedir@/$mig->{pkgname}/$inst/db";
  460. } else {
  461. $newdbdir = $newent->getValues('nsslapd-directory') ||
  462. "@localstatedir@/lib/$mig->{pkgname}/$inst/db";
  463. }
  464. debug(1, "Found ldbm database plugin config entry ", $ent->getDN(), "\n");
  465. my $dir = $ent->getValues('nsslapd-directory');
  466. my $homedir = $ent->getValues('nsslapd-db-home-directory');
  467. my $logdir = $ent->getValues('nsslapd-db-logdirectory');
  468. debug(1, "old db dir = $dir homedir = $homedir logdir = $logdir\n");
  469. my $srcdir = $homedir || $dir || "$olddefault/db";
  470. if (-d $srcdir and ($srcdir !~ /^$olddefault/)) {
  471. debug(2, "Not copying database files from [$srcdir]\n");
  472. } else {
  473. # replace the old sroot value with the actual physical location on the target/dest
  474. $srcdir =~ s/^$mig->{actualsroot}/$mig->{oldsroot}/;
  475. if (@errs = copyDatabaseDirs($srcdir, $newdbdir, 1)) {
  476. return @errs;
  477. }
  478. }
  479. if ($logdir && ($logdir ne $srcdir)) {
  480. if (-d $logdir and ($logdir !~ /^$olddefault/)) {
  481. debug(2, "Not copying transaction logs from [$logdir]\n");
  482. } else {
  483. # replace the old sroot value with the actual physical location on the target/dest
  484. $newdbdir = $newent->getValues('nsslapd-db-logdirectory') ||
  485. $newdbdir;
  486. $logdir =~ s/^$mig->{actualsroot}/$mig->{oldsroot}/;
  487. if (@errs = copyDatabaseDirs($logdir, $newdbdir, 1)) {
  488. return @errs;
  489. }
  490. }
  491. }
  492. } elsif ($objclasses{nsbackendinstance}) {
  493. debug(1, "Found ldbm database instance entry ", $ent->getDN(), "\n");
  494. my $dir = $ent->getValues('nsslapd-directory');
  495. # the default db instance directory is
  496. # $oldroot/$inst/$cn
  497. debug(1, "old instance $cn dbdir $dir\n");
  498. my $srcdir = $dir || "$olddefault/db/$cn";
  499. my $newent = $dest->search($ent->getDN(), "base", "(objectclass=*)");
  500. my $newdbdir = "";
  501. if ("@with_fhs_opt@") {
  502. $newdbdir = $newent->getValues('nsslapd-directory') ||
  503. "@localstatedir@/$mig->{pkgname}/$inst/db/$cn";
  504. } else {
  505. $newdbdir = $newent->getValues('nsslapd-directory') ||
  506. "@localstatedir@/lib/$mig->{pkgname}/$inst/db/$cn";
  507. }
  508. if (-d $srcdir and ($srcdir !~ /^$olddefault/)) {
  509. debug(2, "Not copying database indexes from [$srcdir]\n");
  510. } else {
  511. # replace the old sroot value with the actual physical location on the target/dest
  512. $srcdir =~ s/^$mig->{actualsroot}/$mig->{oldsroot}/;
  513. if (@errs = copyDatabaseDirs($srcdir, "$newdbdir")) {
  514. return @errs;
  515. }
  516. # fix up the integer indexes
  517. if ($mig->{integerattrs}) {
  518. debug(3, "The schema has some integer attributes\n");
  519. if (@errs = fixIntegerIndexes($mig, $inst_dir, $newdbdir)) {
  520. return @errs;
  521. }
  522. } else {
  523. debug(3, "No integer attributes to fix for $newdbdir\n");
  524. }
  525. }
  526. }
  527. } while ($ent = $src->nextEntry());
  528. return ();
  529. }
  530. sub migrateChangelogs {
  531. my $mig = shift; # the Migration object
  532. my $inst = shift; # the instance name (e.g. slapd-instance)
  533. my $src = shift; # a Conn to the source
  534. my $dest = shift; # a Conn to the dest
  535. my $olddefault = "$mig->{actualsroot}/$inst"; # old default db home directory
  536. # changelog config entry
  537. my $oldent = $src->search("cn=changelog5, cn=config", "base", "(objectclass=*)");
  538. my $newent = $dest->search("cn=changelog5, cn=config", "base", "(objectclass=*)");
  539. if ($oldent and $newent) { # changelog configured
  540. my $oldcldir = $oldent->getValues('nsslapd-changelogdir');
  541. if (-d $oldcldir and ($oldcldir !~ /^$olddefault/)) {
  542. debug(2, "Not copying changelogdb from [$oldcldir]\n");
  543. } else {
  544. # replace the old sroot value with the actual physical location on the target/dest
  545. $oldcldir =~ s/^$mig->{actualsroot}/$mig->{oldsroot}/;
  546. my $newcldir = $newent->getValues('nsslapd-changelogdir');
  547. my @errs = copyDatabaseDirs($oldcldir, $newcldir);
  548. if (@errs) {
  549. return @errs;
  550. }
  551. }
  552. }
  553. return ();
  554. }
  555. sub fixAttrsInEntry {
  556. my ($ent, $mig, $inst) = @_;
  557. for my $attr (keys %{$ent}) {
  558. my $lcattr = lc $attr;
  559. if ($ignoreOld{$lcattr}) {
  560. debug(3, "fixAttrsInEntry: ignoring old invalid or obsolete attr $attr\n");
  561. $ent->remove($attr);
  562. next;
  563. } elsif ($transformAttr{$lcattr}) {
  564. my $newval = &{$transformAttr{$lcattr}}($ent, $attr, $mig, $inst);
  565. if (!$newval) {
  566. debug(2, "Removing attribute $attr from entry ", $ent->getDN(), "\n");
  567. $ent->remove($attr);
  568. } else {
  569. debug(2, "Setting new value $newval for attribute $attr in entry ", $ent->getDN(), "\n");
  570. $ent->setValues($attr, $newval);
  571. }
  572. } # else just keep as is
  573. }
  574. }
  575. sub mergeEntries {
  576. my ($old, $new, $mig, $inst) = @_;
  577. my %inoldonly; # attrs in old entry but not new one
  578. my %innewonly; # attrs in new entry but not old one
  579. my @attrs; # attrs common to old and new
  580. # if the attribute exists in the old entry but not the new one
  581. # we should probably add it (checking for special cases first)
  582. # if the attribute exists in the new entry but not the old one
  583. # we might have to delete it from the new entry
  584. # first, get a list of all attributes
  585. foreach my $attr (keys %{$old}) {
  586. if (! $new->exists($attr)) {
  587. $inoldonly{$attr} = $attr;
  588. } else {
  589. push @attrs, $attr;
  590. }
  591. }
  592. foreach my $attr (keys %{$new}) {
  593. if (! $old->exists($attr)) {
  594. $innewonly{$attr} = $attr;
  595. }
  596. }
  597. # iterate through the attr lists
  598. my $cn = lc $new->getValues("cn");
  599. foreach my $attr (keys %inoldonly, keys %innewonly, @attrs) {
  600. debug(3, "mergeEntries: merging entry ", $old->getDN(), " attr $attr\n");
  601. my $lcattr = lc $attr;
  602. if ($ignoreOld{$lcattr}) {
  603. debug(3, "mergeEntries: ignoring old invalid or obsolete attr $attr\n");
  604. next; # use new value or just omit if attr is obsolete
  605. } elsif ($transformAttr{$lcattr}) {
  606. # only transform if the value is in the old entry
  607. if (!$innewonly{$attr}) {
  608. my $oldval = $old->getValues($attr);
  609. my $newval = &{$transformAttr{$lcattr}}($old, $attr, $mig, $inst);
  610. if (!$newval) {
  611. debug(3, "Removing attribute $attr from entry ", $new->getDN(), "\n");
  612. $new->remove($attr);
  613. } else {
  614. debug(3, "Setting new value $newval for attribute $attr in entry ", $new->getDN(), "\n");
  615. $new->setValues($attr, $newval);
  616. }
  617. }
  618. } elsif ($cn eq "internationalization plugin" and $lcattr eq "nsslapd-pluginarg0") {
  619. debug(3, "mergeEntries: using new value of internationalization plugin nsslapd-pluginarg0\n");
  620. next; # use the new value of this path name
  621. } elsif ($cn eq "referential integrity postoperation" and $lcattr eq "nsslapd-pluginarg1") {
  622. debug(3, "mergeEntries: using new value of referential integrity postoperation nsslapd-pluginarg1\n");
  623. next; # use the new value of this path name
  624. } elsif ($innewonly{$attr}) {
  625. debug(3, "mergeEntries: removing attr $attr from new entry\n");
  626. $new->remove($attr); # in new but not old - just remove it
  627. } else {
  628. my $oldval = $old->getValues($attr);
  629. my $newval = $new->getValues($attr);
  630. $new->setValues($attr, $old->getValues($attr)); # use old value
  631. debug(3, "mergeEntries: using old val $oldval instead of new val $newval\n");
  632. }
  633. }
  634. }
  635. my @allattrlist = ('*', 'aci', 'createTimestamp', 'creatorsName',
  636. 'modifyTimestamp', 'modifiersName');
  637. sub getAllEntries {
  638. my $conn = shift;
  639. my $href = shift;
  640. my $aref = shift;
  641. # these are the special DSEs for which we only need ACIs
  642. for my $dn ("", "cn=monitor", "cn=config") {
  643. my $scope = $dn ? "sub" : "base";
  644. my @attrlist;
  645. if ($dn eq "cn=config") {
  646. @attrlist = @allattrlist;
  647. } else {
  648. @attrlist = qw(aci);
  649. }
  650. my $ent = $conn->search($dn, $scope, "(objectclass=*)", 0, @attrlist);
  651. next if (!$ent or ($conn->getErrorCode() eq 32));
  652. if ($conn->getErrorCode()) {
  653. return ('error_reading_entry', $dn, $conn->getErrorString());
  654. }
  655. do {
  656. my $ndn = normalizeDN($ent->getDN());
  657. $href->{$ndn} = $ent;
  658. push @{$aref}, $ndn;
  659. } while ($ent = $conn->nextEntry());
  660. }
  661. return ();
  662. }
  663. # these entries cannot be migrated if doing cross platform
  664. my %noCrossPlatformDN = (
  665. 'cn=uniqueid generator,cn=config' => 'cn=uniqueid generator,cn=config'
  666. );
  667. sub mergeConfigEntries {
  668. my $mig = shift; # the Migration object
  669. my $inst = shift; # the instance name (e.g. slapd-instance)
  670. my $src = shift; # a Conn to the source
  671. my $dest = shift; # a Conn to the dest
  672. # first, read in old file
  673. my %olddse; # map of normalized DN to Entry
  674. my @olddns; # the DNs in their original order
  675. my @errs;
  676. if (@errs = getAllEntries($src, \%olddse, \@olddns)) {
  677. return @errs;
  678. }
  679. # next, read in new file
  680. my %newdse; # map of normalized DN to Entry
  681. my @allnewdns;
  682. my @newdns; # the DNs in their original order that are not in olddns
  683. if (@errs = getAllEntries($dest, \%newdse, \@allnewdns)) {
  684. return @errs;
  685. }
  686. for my $ndn (@allnewdns) {
  687. if (! exists $olddse{$ndn}) {
  688. push @newdns, $ndn;
  689. }
  690. }
  691. # now, compare entries
  692. # if the entry exists in the old tree but not the new, add it
  693. # if the entry exists in the new tree but not the old, delete it
  694. # otherwise, merge the entries
  695. # @olddns contains the dns in the old dse.ldif, including ones that
  696. # may also be in the new dse.ldif
  697. # @newdns contains dns that are only in the new dse.ldif
  698. for my $dn (@olddns, @newdns) {
  699. my $oldent = $olddse{$dn};
  700. my $newent = $newdse{$dn};
  701. my $op;
  702. my $rc = 1;
  703. if ($mig->{crossplatform} && $noCrossPlatformDN{$dn}) {
  704. debug(1, "Cannot migrate the entry $dn - skipping\n");
  705. next;
  706. } elsif ($oldent && !$newent) {
  707. if (!$ignoreOldEntries{$dn}) { # make sure it's not obsolete
  708. # may have to fix up some values in the old entry
  709. fixAttrsInEntry($oldent, $mig, $inst);
  710. $rc = $dest->add($oldent);
  711. $op = "add";
  712. } else {
  713. debug(2, "Ignoring entry $dn - configuration not supported\n");
  714. }
  715. } elsif (!$oldent && $newent) {
  716. if ($dn =~ /o=deleteAfterMigration/i) {
  717. $rc = $dest->delete($dn);
  718. $op = "delete";
  719. } else {
  720. # do nothing - no change to entry
  721. }
  722. } else { #merge
  723. # $newent will contain the merged entry
  724. mergeEntries($oldent, $newent, $mig, $inst);
  725. $rc = $dest->update($newent);
  726. $op = "update";
  727. }
  728. if (!$rc) {
  729. return ('error_updating_merge_entry', $op, $dn, $dest->getErrorString());
  730. }
  731. }
  732. return ();
  733. }
  734. my %deletedschema = (
  735. '50ns-calendar' => '50ns-calendar.ldif',
  736. '50ns-compass' => '50ns-compass.ldif',
  737. '50ns-delegated-admin' => '50ns-delegated-admin.ldif',
  738. '50ns-legacy' => '50ns-legacy.ldif',
  739. '50ns-mail' => '50ns-mail.ldif',
  740. '50ns-mcd-browser' => '50ns-mcd-browser.ldif',
  741. '50ns-mcd-config' => '50ns-mcd-config.ldif',
  742. '50ns-mcd-li' => '50ns-mcd-li.ldif',
  743. '50ns-mcd-mail' => '50ns-mcd-mail.ldif',
  744. '50ns-media' => '50ns-media.ldif',
  745. '50ns-mlm' => '50ns-mlm.ldif',
  746. '50ns-msg' => '50ns-msg.ldif',
  747. '50ns-netshare' => '50ns-netshare.ldif',
  748. '50ns-news' => '50ns-news.ldif',
  749. '50ns-proxy' => '50ns-proxy.ldif',
  750. '50ns-wcal' => '50ns-wcal.ldif',
  751. '51ns-calendar' => '51ns-calendar.ldif'
  752. );
  753. # these indexes are handled specially by the db code
  754. my %intattrstoskip = (
  755. 'numsubordinates' => 'numSubordinates',
  756. 'hassubordinates' => 'hasSubordinates'
  757. );
  758. sub fixup99user {
  759. my $mig = shift; # the Migration object
  760. my $inst = shift; # The name of the instance
  761. my $newschemadir = shift; # the new instance's schema path
  762. my %attrstoskip = ();
  763. my %objclassestoskip = ();
  764. my $uid;
  765. my $gid;
  766. my $mode;
  767. # Read every schema file in the legacy server's schema directory
  768. for (glob("$mig->{oldsroot}/$inst/config/schema/*.ldif")) {
  769. if (!open( OLDSCHEMA, $_ )) {
  770. debug(0, "Can't open schema file $_: $!\n");
  771. next;
  772. }
  773. # Read attributes from each file, looking for ones that contain
  774. # the string "DESC ''".
  775. my $in = new Mozilla::LDAP::LDIF(*OLDSCHEMA);
  776. while (my $ent = readOneEntry $in) {
  777. my @attrs = $ent->getValues('attributeTypes');
  778. my @objclasses = $ent->getValues('objectClasses');
  779. foreach my $attr (@attrs) {
  780. debug(4, "Checking if attribute should be added to skip list ($attr)\n");
  781. if ($attr =~ /\(\s*(\S*)\s*NAME .* DESC \'\'/) {
  782. # Store the OID of those in an associative array for
  783. # quick lookups later.
  784. debug(3, "Adding attribute to list to skip (OID $1)\n");
  785. $attrstoskip{"$1"} = 1;
  786. }
  787. }
  788. foreach my $objclass (@objclasses) {
  789. debug(4, "Checking if objectclass should be added to skip list ($objclass)\n");
  790. if ($objclass =~ /\(\s*(\S*)\s*NAME .* DESC \'\'/) {
  791. # Store the OID of those in an associative array for
  792. # quick lookups later.
  793. debug(3, "Adding objectclass to list to skip (OID $1)\n");
  794. $objclassestoskip{"$1"} = 1;
  795. }
  796. }
  797. }
  798. close(OLDSCHEMA);
  799. }
  800. # Open the 99user.ldif file in the new server schema directory, which is a
  801. # copy of the one in the legacy server. Also open a tempfile.
  802. if (!open(USERSCHEMA, "$newschemadir/99user.ldif")) {
  803. return ("error_opening_schema", "$newschemadir/99user.ldif", $!);
  804. }
  805. # Open a tempfile to write the cleaned 99user.ldif to
  806. if (!open(TMPSCHEMA, ">$newschemadir/99user.ldif.tmp")) {
  807. close(USERSCHEMA);
  808. return ("error_opening_schema", "$newschemadir/99user.ldif.tmp", $!);
  809. }
  810. # Iterate through every attribute in the 99user.ldif file and write them to the
  811. # tempfile if their OID doesn't exist in the "bad schema" array.
  812. my $in = new Mozilla::LDAP::LDIF(*USERSCHEMA);
  813. while (my $ent = readOneEntry $in) {
  814. my @attrs = $ent->getValues('attributeTypes');
  815. my @objclasses = $ent->getValues('objectClasses');
  816. my @keepattrs;
  817. my @keepobjclasses;
  818. foreach my $attr (@attrs) {
  819. if ($attr =~ /\(\s*(\S*)\s*NAME/) {
  820. debug(3, "Checking if attribute should be trimmed (OID $1)\n");
  821. # See if this OID is in our list of attrs to skip
  822. if ($attrstoskip{"$1"}) {
  823. debug(2, "Trimming attribute from 99user.ldif (OID $1)\n");
  824. next;
  825. }
  826. }
  827. # Keep this value
  828. debug(3, "Keeping attribute in 99user.ldif (OID $1)\n");
  829. push @keepattrs, $attr;
  830. }
  831. foreach my $objclass (@objclasses) {
  832. if ($objclass =~ /\(\s*(\S*)\s*NAME/) {
  833. debug(3, "Checking if objectclass should be trimmed (OID $1)\n");
  834. # See if this OID is in our list of objectclasses to skip
  835. if ($objclassestoskip{"$1"}) {
  836. debug(2, "Trimming objectclass from 99user.ldif (OID $1)\n");
  837. next;
  838. }
  839. }
  840. # Keep this value
  841. debug(3, "Keeping objectclass in 99user.ldif (OID $1)\n");
  842. push @keepobjclasses, $objclass;
  843. }
  844. # Update the entry with the values we want to keep
  845. if ($#keepattrs >= $[) {
  846. $ent->setValues("attributetypes", @keepattrs);
  847. } else {
  848. $ent->remove("attributetypes");
  849. }
  850. if ($#keepobjclasses >= $[) {
  851. $ent->setValues("objectclasses", @keepobjclasses);
  852. } else {
  853. $ent->remove("objectclasses");
  854. }
  855. # Write the entry to temp schema file
  856. my $oldfh = select(TMPSCHEMA);
  857. $ent->printLDIF();
  858. select($oldfh);
  859. }
  860. close(USERSCHEMA);
  861. close(TMPSCHEMA);
  862. # Make the ownership and permissions on the temp schema file
  863. # the same as the copied 99user.ldif.
  864. ($mode, $uid, $gid) = (stat("$newschemadir/99user.ldif"))[2,4,5];
  865. if ((chown $uid, $gid, "$newschemadir/99user.ldif.tmp") != 1) {
  866. return ("error_schema_permissions", "$newschemadir/99user.ldif.tmp", $!);
  867. }
  868. if ((chmod $mode, "$newschemadir/99user.ldif.tmp") != 1) {
  869. return ("error_schema_permissions", "$newschemadir/99user.ldif.tmp", $!);
  870. }
  871. # Replace the copied 99user.ldif with the trimmed file.
  872. if ((rename "$newschemadir/99user.ldif.tmp", "$newschemadir/99user.ldif") != 1) {
  873. return ("error_renaming_schema", "$newschemadir/99user.ldif.tmp", "$newschemadir/99user.ldif", $!);
  874. }
  875. return();
  876. }
  877. sub migrateSchema {
  878. my $mig = shift; # the Migration object
  879. my $inst = shift; # the instance name (e.g. slapd-instance)
  880. my $src = shift; # a Conn to the source
  881. my $dest = shift; # a Conn to the dest
  882. my @errs;
  883. my $cfgent = $dest->search("cn=config", "base", "(objectclass=*)");
  884. my $newschemadir = $cfgent->getValues('nsslapd-schemadir') ||
  885. "$mig->{configdir}/$inst/schema";
  886. my %newschema = map {basename($_, '.ldif') => $_} glob("$newschemadir/*.ldif");
  887. delete $newschema{"99user"}; # always copy this one
  888. for (glob("$mig->{oldsroot}/$inst/config/schema/*.ldif")) {
  889. my $fname = basename($_, '.ldif');
  890. next if ($deletedschema{$fname}); # don't copy deleted schema
  891. next if ($newschema{$fname}); # use new version
  892. if (system("cp -p $_ $newschemadir")) {
  893. return ("error_migrating_schema", $_, $!);
  894. }
  895. }
  896. # fixup any attributes with missing descriptions in 99user.ldif
  897. if (@errs = fixup99user($mig, $inst, $newschemadir)) {
  898. return @errs;
  899. }
  900. if (!$mig->{crossplatform}) {
  901. # now, for all of the new schema, we need to get the list of attribute
  902. # types with INTEGER syntax, including derived types (e.g. SUP 'attr')
  903. # not required for cross platform because import of the old ldif file
  904. # will automatically recreate all indexes
  905. my %intattrs = ();
  906. for (glob("$newschemadir/*.ldif")) {
  907. # read in schema entry from LDIF
  908. if (!open( MYSCHEMA, $_ )) {
  909. debug(0, "Can't open schema file $_: $!\n");
  910. next;
  911. }
  912. my $in = new Mozilla::LDAP::LDIF(*MYSCHEMA);
  913. while (my $ent = readOneEntry $in) {
  914. my @attrs = $ent->getValues('attributeTypes');
  915. foreach my $attr (@attrs) {
  916. # first see if the attribute definition uses INTEGER syntax
  917. # else see if the super uses INTEGER - note this assumes the attributes
  918. # are listed in the files in SUP order - that is, an attribute does
  919. # not reference a SUP before it is defined
  920. if ($attr =~ / NAME (?:\(\s)?[\']?(\w+)[\']?.* SYNTAX 1.3.6.1.4.1.1466.115.121.1.27[\{\s]/) {
  921. next if ($intattrstoskip{lc $1});
  922. $intattrs{lc $1} = $1;
  923. } elsif (($attr =~ / NAME (?:\(\s)?[\']?(\w+)[\']?.*SUP [\']?(\w+)[\']?/) &&
  924. $intattrs{lc $2}) {
  925. next if ($intattrstoskip{lc $1});
  926. $intattrs{lc $1} = $1;
  927. }
  928. }
  929. }
  930. close MYSCHEMA;
  931. }
  932. # %intattrs now contains all of the integer valued attributes
  933. $mig->{integerattrs} = \%intattrs; # hashref
  934. }
  935. return ();
  936. }
  937. sub migrateDSInstance {
  938. my $mig = shift; # the Migration object
  939. my $inst = shift; # the instance name (e.g. slapd-instance)
  940. my $src = shift; # a Conn to the source
  941. my $dest = shift; # a Conn to the dest
  942. my @errs;
  943. # first, merge dse ldif
  944. if (@errs = mergeConfigEntries($mig, $inst, $src, $dest)) {
  945. return @errs;
  946. }
  947. # next, grab the old schema
  948. if (@errs = migrateSchema($mig, $inst, $src, $dest)) {
  949. return @errs;
  950. }
  951. # next, the databases
  952. if (@errs = migrateDatabases($mig, $inst, $src, $dest)) {
  953. return @errs;
  954. }
  955. # next, the changelogs
  956. if (!$mig->{crossplatform}) {
  957. if (@errs = migrateChangelogs($mig, $inst, $src, $dest)) {
  958. return @errs;
  959. }
  960. }
  961. # next, the security files
  962. my $cfgent = $dest->search("cn=config", "base", "(objectclass=*)");
  963. my $newcertdir = $cfgent->getValues("nsslapd-certdir") ||
  964. "@instconfigdir@/$inst";
  965. $mig->migrateSecurityFiles($inst, $newcertdir);
  966. return @errs;
  967. }
  968. sub migrateDS {
  969. my $mig = shift;
  970. my @errs;
  971. # migration needs to know the instance directory for the directory
  972. # servers - this assumes they are all in the same place
  973. if (!$mig->{ServerRoot}) {
  974. if ("@with_fhs_opt@") {
  975. $mig->{ServerRoot} = "$mig->{inf}->{General}->{prefix}/opt/@PACKAGE_NAME@";
  976. } else {
  977. $mig->{ServerRoot} = "$mig->{inf}->{General}->{prefix}@serverdir@";
  978. }
  979. }
  980. # for each instance
  981. foreach my $inst (@{$mig->{instances}}) {
  982. if (-f "$mig->{configdir}/$inst/dse.ldif") {
  983. $mig->msg($WARN, 'instance_already_exists', "$mig->{configdir}/$inst/dse.ldif");
  984. next;
  985. }
  986. # you could theoretically make this work with either a remote source or
  987. # remote dest
  988. # $mig->{inf} would contain an entry for each instance e.g.
  989. # $mig->{inf}->{$inst}
  990. # each instance specific entry would contain a {General} and a {slapd}
  991. # all the information necessary to open an LDAP::Conn to the server
  992. # if the source, you could also change createInfFromConfig to read
  993. # the info from the Conn (or FileConn) that's needed to create the
  994. # instance on the dest
  995. # extract the information needed for ds_newinst.pl
  996. my $oldconfigdir = "$mig->{oldsroot}/$inst/config";
  997. my $inf = createInfFromConfig($oldconfigdir, $inst, \@errs);
  998. if (@errs) {
  999. $mig->msg(@errs);
  1000. return 0;
  1001. }
  1002. if (!$inf) {
  1003. $mig->msg($FATAL, 'error_opening_dseldif', "$oldconfigdir/dse.ldif", $!);
  1004. return 0;
  1005. }
  1006. debug(2, "Using inffile $inf->{filename} created from $oldconfigdir\n");
  1007. # create servers but do not start them until after databases
  1008. # have been migrated
  1009. $inf->{slapd}->{start_server} = 0;
  1010. # create the new instance
  1011. @errs = createDSInstance($inf);
  1012. unlink($inf->{filename});
  1013. if (@errs) {
  1014. $mig->msg(@errs);
  1015. $mig->msg($FATAL, 'error_creating_dsinstance', $inst);
  1016. return 0;
  1017. } else {
  1018. $mig->msg('created_dsinstance', $inst);
  1019. }
  1020. my $src = new FileConn("$oldconfigdir/dse.ldif", 1); # read-only
  1021. if (!$src) {
  1022. $mig->msg($FATAL, 'error_opening_dseldif', "$oldconfigdir/dse.ldif", $!);
  1023. return 0;
  1024. }
  1025. my $dest = new FileConn("$mig->{configdir}/$inst/dse.ldif");
  1026. if (!$dest) {
  1027. $src->close();
  1028. $mig->msg($FATAL, 'error_opening_dseldif', "$mig->{configdir}/$inst/dse.ldif", $!);
  1029. return 0;
  1030. }
  1031. @errs = migrateDSInstance($mig, $inst, $src, $dest);
  1032. $src->close();
  1033. $dest->close();
  1034. if (@errs) {
  1035. $mig->msg(@errs);
  1036. return 0;
  1037. }
  1038. # ensure any selinux relabeling gets done if needed
  1039. DSCreate::updateSelinuxPolicy($inf);
  1040. # finally, start the server
  1041. if ($mig->{start_servers}) {
  1042. $inf->{slapd}->{start_server} = 1;
  1043. if (@errs = DSCreate::startServer($inf)) {
  1044. $mig->msg(@errs);
  1045. return 0;
  1046. }
  1047. }
  1048. }
  1049. return 1;
  1050. }
  1051. #############################################################################
  1052. # Mandatory TRUE return value.
  1053. #
  1054. 1;
  1055. # emacs settings
  1056. # Local Variables:
  1057. # mode:perl
  1058. # indent-tabs-mode: nil
  1059. # tab-width: 4
  1060. # End: