DSCreate.pm.in 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459
  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 create a new instance of
  41. # directory server.
  42. #
  43. ##########################
  44. package DSCreate;
  45. use DSUtil;
  46. use Inf;
  47. use FileConn;
  48. use Net::Domain qw(hostfqdn);
  49. # tempfiles
  50. use File::Temp qw(tempfile tempdir);
  51. use File::Path;
  52. use File::Copy;
  53. use File::Basename qw(basename dirname);
  54. use POSIX qw(:errno_h);
  55. # load perldap
  56. use Mozilla::LDAP::Conn;
  57. use Mozilla::LDAP::Utils qw(normalizeDN);
  58. use Mozilla::LDAP::API qw(ldap_explode_dn);
  59. use Mozilla::LDAP::LDIF;
  60. use Exporter;
  61. @ISA = qw(Exporter);
  62. @EXPORT = qw(createDSInstance removeDSInstance setDefaults createInstanceScripts
  63. makeOtherConfigFiles installSchema updateSelinuxPolicy updateTmpfilesDotD
  64. get_initconfigdir updateSystemD makeDSDirs);
  65. @EXPORT_OK = qw(createDSInstance removeDSInstance setDefaults createInstanceScripts
  66. makeOtherConfigFiles installSchema updateSelinuxPolicy updateTmpfilesDotD
  67. get_initconfigdir updateSystemD makeDSDirs);
  68. use strict;
  69. use SetupLog;
  70. sub get_initconfigdir {
  71. # determine initconfig_dir
  72. if (getLogin eq 'root') {
  73. return "@initconfigdir@";
  74. } else {
  75. return "$ENV{HOME}/.@package_name@";
  76. }
  77. }
  78. sub checkPort {
  79. my $inf = shift;
  80. # allow port 0 if ldapi is used
  81. if ("@enable_ldapi@") {
  82. if ($inf->{slapd}->{ldapifilepath} &&
  83. ($inf->{slapd}->{ServerPort} == 0)) {
  84. return ();
  85. }
  86. }
  87. if ($inf->{slapd}->{ServerPort} !~ /^\d+$/) {
  88. return ('error_port_invalid', $inf->{slapd}->{ServerPort});
  89. }
  90. if (!portAvailable($inf->{slapd}->{ServerPort})) {
  91. return ('error_port_available', $inf->{slapd}->{ServerPort}, $!);
  92. }
  93. return ();
  94. }
  95. # checks the parameters in $inf to make sure the supplied values
  96. # are valid
  97. # returns null if successful, or an error string for use with getText()
  98. sub sanityCheckParams {
  99. my $inf = shift;
  100. my @errs = ();
  101. # if we don't need to start the server right away, we can skip the
  102. # port number checks
  103. if (!defined($inf->{slapd}->{start_server}) or
  104. ($inf->{slapd}->{start_server} == 1)) {
  105. if (@errs = checkPort($inf)) {
  106. return @errs;
  107. }
  108. }
  109. if (!isValidServerID($inf->{slapd}->{ServerIdentifier})) {
  110. return ('error_invalid_serverid', $inf->{slapd}->{ServerIdentifier});
  111. } elsif (-d $inf->{slapd}->{config_dir}) {
  112. return ('error_server_already_exists', $inf->{slapd}->{config_dir});
  113. }
  114. if (@errs = isValidUser($inf->{General}->{SuiteSpotUserID})) {
  115. return @errs;
  116. }
  117. if (@errs = isValidGroup($inf->{General}->{SuiteSpotGroup})) {
  118. return @errs;
  119. }
  120. if (!isValidDN($inf->{slapd}->{Suffix})) {
  121. return ('dialog_dssuffix_error', $inf->{slapd}->{Suffix});
  122. }
  123. if (!isValidDN($inf->{slapd}->{RootDN})) {
  124. return ('dialog_dsrootdn_error', $inf->{slapd}->{RootDN});
  125. }
  126. if ($inf->{slapd}->{RootDNPwd} =~ /^\{\w+\}.+/) {
  127. debug(1, "The root password is already hashed - no checking will be performed\n");
  128. } elsif (length($inf->{slapd}->{RootDNPwd}) < 8) {
  129. debug(0, "WARNING: The root password is less than 8 characters long. You should choose a longer one.\n");
  130. }
  131. if (@errs = checkHostname($inf->{General}->{FullMachineName}, 0)) {
  132. debug(1, @errs);
  133. return @errs;
  134. }
  135. return ();
  136. }
  137. sub getMode {
  138. my $inf = shift;
  139. my $mode = shift;
  140. my $rest = shift;
  141. if (!$rest) {
  142. $rest = "0";
  143. }
  144. if (defined($inf->{General}->{SuiteSpotGroup})) {
  145. $mode = "0" . $mode . $mode . $rest;
  146. } else {
  147. $mode = "0" . $mode . $rest . $rest;
  148. }
  149. return oct($mode);
  150. }
  151. # This is used to change the ownership and permissions of files and directories
  152. # The mode is just a single digit octal number (e.g. 4 6 7)
  153. # If there is a group, the ownership and permissions will allow group access
  154. # otherwise, only the owner will be allowed access
  155. sub changeOwnerMode {
  156. my $inf = shift;
  157. my $mode = shift;
  158. my $it = shift;
  159. my $gidonly = shift;
  160. my $othermode = shift;
  161. my $uid = getpwnam $inf->{General}->{SuiteSpotUserID};
  162. my $gid = -1; # default to leave it alone
  163. my $mode_string = "";
  164. if (defined($inf->{General}->{SuiteSpotGroup})) {
  165. $gid = getgrnam $inf->{General}->{SuiteSpotGroup};
  166. }
  167. $mode = getMode($inf, $mode, $othermode);
  168. $! = 0; # clear errno
  169. chmod $mode, $it;
  170. if ($!) {
  171. return ('error_chmoding_file', $it, $!);
  172. }
  173. $mode_string = sprintf "%lo", $mode;
  174. debug(1, "changeOwnerMode: changed mode of $it to $mode_string\n");
  175. $! = 0; # clear errno
  176. if ( $gidonly ) {
  177. chown -1, $gid, $it;
  178. } else {
  179. chown $uid, $gid, $it;
  180. }
  181. if ($!) {
  182. return ('error_chowning_file', $it, $inf->{General}->{SuiteSpotUserID}, $!);
  183. }
  184. if ( $gidonly ) {
  185. debug(1, "changeOwnerMode: changed group ownership of $it to group $gid\n");
  186. } else {
  187. debug(1, "changeOwnerMode: changed ownership of $it to user $uid group $gid\n");
  188. }
  189. return ();
  190. }
  191. sub makeDSDirs {
  192. my $inf = shift;
  193. my $verbose = ($DSUtil::debuglevel > 0);
  194. my $mode = getMode($inf, 7);
  195. my @errs;
  196. # These paths are owned by the SuiteSpotGroup
  197. # This allows the admin server to run as a different,
  198. # more privileged user than the directory server, but
  199. # still allows the admin server to manage directory
  200. # server files/dirs without being root
  201. for my $kw (qw(inst_dir config_dir schema_dir log_dir lock_dir run_dir tmp_dir
  202. cert_dir db_dir ldif_dir bak_dir)) {
  203. my $dir = $inf->{slapd}->{$kw};
  204. @errs = makePaths($dir, $mode, $inf->{General}->{SuiteSpotUserID},
  205. $inf->{General}->{SuiteSpotGroup});
  206. if (@errs) {
  207. return @errs;
  208. }
  209. }
  210. # run_dir is a special case because it is usually shared among
  211. # all instances and the admin server
  212. # all instances must be able to write to it
  213. # if the SuiteSpotUserID is root or 0, we can just skip
  214. # this because root will have access to it - we really
  215. # shouldn't be using root anyway, primarily just for
  216. # legacy migration support
  217. # if there are two different user IDs that need access
  218. # to this directory, then SuiteSpotGroup must be defined,
  219. # and both users must be members of the SuiteSpotGroup
  220. if (($inf->{General}->{SuiteSpotUserID} eq 'root') ||
  221. (defined($inf->{General}->{SuiteSpotUserID}) &&
  222. ($inf->{General}->{SuiteSpotUserID} =~ /^0$/))) {
  223. # skip
  224. debug(3, "Root user " . $inf->{General}->{SuiteSpotUserID} . " already has access to $inf->{slapd}->{run_dir} - skipping\n");
  225. } else {
  226. my $dir = $inf->{slapd}->{run_dir};
  227. # rwx by user only, or by user & group if a group is defined. Also only change the group ownership.
  228. @errs = changeOwnerMode($inf, 7, $dir, 1);
  229. debug(3, "\t" . `/bin/ls -ld $dir`);
  230. }
  231. # set the group of the parent dir of config_dir and inst_dir
  232. if (defined($inf->{General}->{SuiteSpotGroup})) {
  233. for my $kw (qw(inst_dir config_dir)) {
  234. my $dir = $inf->{slapd}->{$kw};
  235. my $parent = dirname($dir);
  236. # changeOwnerMode(inf, mode, file, gidonly, othermode);
  237. @errs = changeOwnerMode($inf, 7, $parent, 1, 5);
  238. if (@errs) {
  239. return @errs;
  240. }
  241. }
  242. }
  243. return @errs;
  244. }
  245. sub createInstanceScripts {
  246. my $inf = shift;
  247. my $skip = shift;
  248. my $perlexec = "@perlexec@" || "/usr/bin/env perl";
  249. my $myperl = "!$perlexec";
  250. my $mydevnull = (-f "/dev/null" ? " /dev/null " : " NUL ");
  251. # determine initconfig_dir
  252. my $initconfig_dir = $inf->{slapd}->{initconfig_dir} || get_initconfigdir();
  253. my %maptable = (
  254. "DS-ROOT" => $inf->{General}->{prefix},
  255. "SEP" => "/", # works on all platforms
  256. "SERVER-NAME" => $inf->{General}->{FullMachineName},
  257. "SERVER-PORT" => $inf->{slapd}->{ServerPort},
  258. "PERL-EXEC" => $myperl,
  259. "DEV-NULL" => $mydevnull,
  260. "ROOT-DN" => $inf->{slapd}->{RootDN},
  261. "LDIF-DIR" => $inf->{slapd}->{ldif_dir},
  262. "SERV-ID" => $inf->{slapd}->{ServerIdentifier},
  263. "BAK-DIR" => $inf->{slapd}->{bak_dir},
  264. "SERVER-DIR" => $inf->{General}->{ServerRoot},
  265. "CONFIG-DIR" => $inf->{slapd}->{config_dir},
  266. "INITCONFIG-DIR" => $initconfig_dir,
  267. "INST-DIR" => $inf->{slapd}->{inst_dir},
  268. "RUN-DIR" => $inf->{slapd}->{run_dir},
  269. "PRODUCT-NAME" => "slapd",
  270. "SERVERBIN-DIR" => $inf->{slapd}->{sbindir},
  271. "DB-DIR" => $inf->{slapd}->{db_dir}
  272. );
  273. my $dir = "$inf->{General}->{prefix}@taskdir@";
  274. for my $file (glob("$dir/template-*")) {
  275. my $basename = $file;
  276. $basename =~ s/^.*template-//;
  277. my $destfile = "$inf->{slapd}->{inst_dir}/$basename";
  278. next if ($skip and -f $destfile); # in skip mode, skip files that already exist
  279. if (!open(SRC, "< $file")) {
  280. return ("error_opening_scripttmpl", $file, $!);
  281. }
  282. if (!open(DEST, "> $destfile")) {
  283. return ("error_opening_scripttmpl", $destfile, $!);
  284. }
  285. my $contents; # slurp entire file into memory
  286. read SRC, $contents, int(-s $file);
  287. close(SRC);
  288. while (my ($key, $val) = each %maptable) {
  289. $contents =~ s/\{\{$key\}\}/$val/g;
  290. }
  291. print DEST $contents;
  292. close(DEST);
  293. my @errs = changeOwnerMode($inf, 5, $destfile);
  294. if (@errs) {
  295. return @errs;
  296. }
  297. }
  298. return ();
  299. }
  300. sub createConfigFile {
  301. my $inf = shift;
  302. my $conffile = "$inf->{slapd}->{config_dir}/dse.ldif";
  303. my $conn = new FileConn;
  304. my @errs;
  305. # first, create the basic config
  306. my $mapper = new Inf("$inf->{General}->{prefix}@infdir@/dscreate.map");
  307. my $dsinf = new Inf("$inf->{General}->{prefix}@infdir@/slapd.inf");
  308. if (!$inf->{slapd}->{ds_bename}) {
  309. $inf->{slapd}->{ds_bename} = "userRoot"; # for suffix-db
  310. }
  311. $mapper = process_maptbl($mapper, \@errs, $inf, $dsinf);
  312. if (!$mapper or @errs) {
  313. $conn->close();
  314. if (!@errs) {
  315. @errs = ('error_creating_file', $conffile, $!);
  316. }
  317. return @errs;
  318. }
  319. my @ldiffiles = ("$inf->{General}->{prefix}@templatedir@/template-dse.ldif",
  320. "$inf->{General}->{prefix}@templatedir@/template-suffix-db.ldif",
  321. "$inf->{General}->{prefix}@templatedir@/template-sasl.ldif");
  322. if ("@enable_pam_passthru@") {
  323. push @ldiffiles, "$inf->{General}->{prefix}@templatedir@/template-pampta.ldif";
  324. }
  325. if ("@enable_bitwise@") {
  326. push @ldiffiles, "$inf->{General}->{prefix}@templatedir@/template-bitwise.ldif";
  327. }
  328. if ("@enable_dna@") {
  329. push @ldiffiles, "$inf->{General}->{prefix}@templatedir@/template-dnaplugin.ldif";
  330. }
  331. if (-f "$inf->{General}->{prefix}@updatedir@/50replication-plugins.ldif") {
  332. push @ldiffiles, "$inf->{General}->{prefix}@updatedir@/50replication-plugins.ldif";
  333. }
  334. if (-f "$inf->{General}->{prefix}@updatedir@/50posix-winsync-plugin.ldif") {
  335. push @ldiffiles, "$inf->{General}->{prefix}@updatedir@/50posix-winsync-plugin.ldif";
  336. }
  337. if (-f "$inf->{General}->{prefix}@templatedir@/90betxn-plugins.ldif") {
  338. push @ldiffiles, "$inf->{General}->{prefix}@templatedir@/90betxn-plugins.ldif";
  339. }
  340. # additional configuration LDIF files
  341. if (exists($inf->{slapd}->{ConfigFile})) {
  342. if (ref($inf->{slapd}->{ConfigFile})) {
  343. push @ldiffiles, @{$inf->{slapd}->{ConfigFile}};
  344. } else {
  345. push @ldiffiles, $inf->{slapd}->{ConfigFile};
  346. }
  347. }
  348. getMappedEntries($mapper, \@ldiffiles, \@errs, \&check_and_add_entry,
  349. [$conn]);
  350. if (@errs) {
  351. $conn->close();
  352. return @errs;
  353. }
  354. if ("@enable_ldapi@") {
  355. my $ent = $conn->search("cn=config", "base", "(objectclass=*)");
  356. if (defined($inf->{slapd}->{ldapifilepath})) {
  357. $ent->setValues("nsslapd-ldapifilepath", $inf->{slapd}->{ldapifilepath});
  358. $ent->setValues("nsslapd-ldapilisten", "on");
  359. } else {
  360. my $parent = dirname($inf->{slapd}->{run_dir});
  361. $ent->setValues("nsslapd-ldapifilepath",
  362. "$parent/slapd-$inf->{slapd}->{ServerIdentifier}.socket");
  363. $ent->setValues("nsslapd-ldapilisten", "off");
  364. }
  365. if ("@enable_autobind@") {
  366. $ent->setValues("nsslapd-ldapiautobind", "off");
  367. $ent->setValues("nsslapd-ldapimaprootdn", $inf->{slapd}->{RootDN});
  368. $ent->setValues("nsslapd-ldapimaptoentries", "off");
  369. $ent->setValues("nsslapd-ldapiuidnumbertype", "uidNumber");
  370. $ent->setValues("nsslapd-ldapigidnumbertype", "gidNumber");
  371. $ent->setValues("nsslapd-ldapientrysearchbase", $inf->{slapd}->{Suffix});
  372. if ("@enable_auto_dn_suffix@") {
  373. $ent->setValues("nsslapd-ldapiautodnsuffix", "cn=peercred,cn=external,cn=auth");
  374. }
  375. }
  376. $ent->setValues("nsslapd-defaultNamingContext", $inf->{slapd}->{Suffix});
  377. if (!$conn->update($ent)) {
  378. $conn->close();
  379. return ("error_enabling_feature", "ldapi", $conn->getErrorString());
  380. }
  381. }
  382. if ($inf->{slapd}->{sasl_path}) {
  383. my $ent = $conn->search("cn=config", "base", "(objectclass=*)");
  384. $ent->setValues("nsslapd-saslpath", $inf->{slapd}->{sasl_path});
  385. if (!$conn->update($ent)) {
  386. $conn->close();
  387. return ("error_enabling_feature", "sasl_path", $conn->getErrorString());
  388. }
  389. }
  390. if (!$conn->write($conffile)) {
  391. $conn->close();
  392. return ("error_writing_ldif", $conffile, $!);
  393. }
  394. $conn->close();
  395. if (@errs = changeOwnerMode($inf, 6, $conffile)) {
  396. return @errs;
  397. }
  398. # make a copy
  399. my $origconf = "$inf->{slapd}->{config_dir}/dse_original.ldif";
  400. $! = 0; # clear errno
  401. copy($conffile, $origconf);
  402. if ($!) {
  403. return ('error_copying_file', $conffile, $origconf, $!);
  404. }
  405. if (@errs = changeOwnerMode($inf, 4, $origconf)) {
  406. return @errs;
  407. }
  408. return @errs;
  409. }
  410. sub makeOtherConfigFiles {
  411. my $inf = shift;
  412. my $skip = shift;
  413. my @errs;
  414. my %maptable = (
  415. "DS-ROOT" => $inf->{General}->{prefix},
  416. "SERVER-DIR" => $inf->{General}->{ServerRoot},
  417. "CONFIG-DIR" => $inf->{slapd}->{config_dir},
  418. "INST-DIR" => $inf->{slapd}->{inst_dir},
  419. "RUN-DIR" => $inf->{slapd}->{run_dir},
  420. "PRODUCT-NAME" => "slapd",
  421. "SERVERBIN-DIR" => $inf->{slapd}->{sbindir},
  422. );
  423. # install certmap.conf at <configdir>
  424. my $src = "$inf->{General}->{prefix}@configdir@/certmap.conf";
  425. my $dest = "$inf->{slapd}->{config_dir}/certmap.conf";
  426. $! = 0; # clear errno
  427. #in skip mode, skip files that already exist
  428. unless ($skip and -f $dest) {
  429. copy($src, $dest);
  430. if ($!) {
  431. return ('error_copying_file', $src, $dest, $!);
  432. }
  433. if (@errs = changeOwnerMode($inf, 4, $dest)) {
  434. return @errs;
  435. }
  436. }
  437. $src = "$inf->{General}->{prefix}@configdir@/slapd-collations.conf";
  438. $dest = "$inf->{slapd}->{config_dir}/slapd-collations.conf";
  439. $! = 0; # clear errno
  440. #in skip mode, skip files that already exist
  441. unless ($skip and -f $dest) {
  442. copy($src, $dest);
  443. if ($!) {
  444. return ('error_copying_file', $src, $dest, $!);
  445. }
  446. if (@errs = changeOwnerMode($inf, 4, $dest)) {
  447. return @errs;
  448. }
  449. }
  450. # determine initconfig_dir
  451. my $initconfig_dir = $inf->{slapd}->{initconfig_dir} || get_initconfigdir();
  452. # install instance specific initconfig script
  453. $src = "$inf->{General}->{prefix}@configdir@/template-initconfig";
  454. $dest = "$initconfig_dir/@package_name@-$inf->{slapd}->{ServerIdentifier}";
  455. $! = 0; # clear errno
  456. # in skip mode, skip files that already exist
  457. unless ($skip and -f $dest) {
  458. if (!open(SRC, "< $src")) {
  459. return ("error_opening_scripttmpl", $src, $!);
  460. }
  461. if (!open(DEST, "> $dest")) {
  462. return ("error_opening_scripttmpl", $dest, $!);
  463. }
  464. my $contents; # slurp entire file into memory
  465. read SRC, $contents, int(-s $src);
  466. close(SRC);
  467. while (my ($key, $val) = each %maptable) {
  468. $contents =~ s/\{\{$key\}\}/$val/g;
  469. }
  470. print DEST $contents;
  471. close(DEST);
  472. if (@errs = changeOwnerMode($inf, 4, $dest)) {
  473. return @errs;
  474. }
  475. }
  476. return ();
  477. }
  478. sub installSchema {
  479. my $inf = shift;
  480. my $skip = shift;
  481. my @errs;
  482. my @schemafiles = ();
  483. if (!defined($inf->{slapd}->{install_full_schema}) or
  484. $inf->{slapd}->{install_full_schema}) {
  485. push @schemafiles, glob("$inf->{General}->{prefix}@schemadir@/*");
  486. } else {
  487. push @schemafiles, "$inf->{General}->{prefix}@schemadir@/00core.ldif",
  488. "$inf->{General}->{prefix}@schemadir@/01core389.ldif";
  489. }
  490. # additional schema files
  491. if (exists($inf->{slapd}->{SchemaFile})) {
  492. if (ref($inf->{slapd}->{SchemaFile})) {
  493. push @schemafiles, @{$inf->{slapd}->{SchemaFile}};
  494. } else {
  495. push @schemafiles, $inf->{slapd}->{SchemaFile};
  496. }
  497. }
  498. for my $file (@schemafiles) {
  499. my $src = $file;
  500. my $basename = basename($src);
  501. my $dest = "$inf->{slapd}->{schema_dir}/$basename";
  502. next if ($skip and -f $dest); # skip files that already exist
  503. $! = 0; # clear errno
  504. copy($src, $dest);
  505. if ($!) {
  506. return ('error_copying_file', $src, $dest, $!);
  507. }
  508. my $mode = 4; # default read only
  509. if ($basename eq "99user.ldif") {
  510. $mode = 6; # read write
  511. }
  512. if (@errs = changeOwnerMode($inf, $mode, $dest)) {
  513. return @errs;
  514. }
  515. }
  516. return ();
  517. }
  518. # maps the suffix attr to the filename to use
  519. my %suffixTable = (
  520. 'o' => "@templatedir@/template-org.ldif",
  521. 'dc' => "@templatedir@/template-domain.ldif",
  522. 'ou' => "@templatedir@/template-orgunit.ldif",
  523. 'st' => "@templatedir@/template-state.ldif",
  524. 'l' => "@templatedir@/template-locality.ldif",
  525. 'c' => "@templatedir@/template-country.ldif"
  526. );
  527. sub initDatabase {
  528. my $inf = shift;
  529. my $istempldif = 0;
  530. # If the user has specified an LDIF file to use to initialize the database,
  531. # load it now
  532. my $ldiffile = $inf->{slapd}->{InstallLdifFile};
  533. if ($ldiffile =~ /none/i) {
  534. debug(1, "No ldif file or org entries specified - no initial database will be created\n");
  535. return ();
  536. } elsif ($ldiffile && ($ldiffile !~ /suggest/i)) {
  537. debug(1, "Loading initial ldif file $ldiffile\n");
  538. if (! -r $ldiffile) {
  539. return ('error_opening_init_ldif', $ldiffile);
  540. }
  541. } elsif (($inf->{slapd}->{Suffix} =~ /^(.*?)=/) && $suffixTable{$1}) {
  542. my @errs;
  543. my $template = $inf->{General}->{prefix} . $suffixTable{$1};
  544. my $mapper = new Inf("$inf->{General}->{prefix}@infdir@/dsorgentries.map");
  545. my $dsinf = new Inf("$inf->{General}->{prefix}@infdir@/slapd.inf");
  546. my @rdns = ldap_explode_dn($inf->{slapd}->{Suffix}, 1);
  547. $inf->{slapd}->{naming_value} = $rdns[0];
  548. $mapper = process_maptbl($mapper, \@errs, $inf, $dsinf);
  549. if (!$mapper or @errs) {
  550. return @errs;
  551. }
  552. my @ldiffiles = ($template, "$inf->{General}->{prefix}@templatedir@/template-baseacis.ldif");
  553. # default is to create org entries unless explicitly set to none
  554. if (!exists($inf->{slapd}->{InstallLdifFile}) or
  555. ($inf->{slapd}->{InstallLdifFile} =~ /suggest/i)) {
  556. push @ldiffiles, "$inf->{General}->{prefix}@templatedir@/template.ldif";
  557. }
  558. my ($fh, $templdif) = tempfile("ldifXXXXXX", SUFFIX => ".ldif", OPEN => 0,
  559. DIR => File::Spec->tmpdir);
  560. if (!$templdif) {
  561. return ('error_creating_templdif', $!);
  562. }
  563. my $conn = new FileConn;
  564. $conn->setNamingContext($inf->{slapd}->{Suffix});
  565. getMappedEntries($mapper, \@ldiffiles, \@errs, \&check_and_add_entry,
  566. [$conn]);
  567. if (@errs) {
  568. $conn->close();
  569. return @errs;
  570. }
  571. if (!$conn->write($templdif)) {
  572. $conn->close();
  573. return ('error_writing_ldif', $templdif, $!);
  574. }
  575. $conn->close();
  576. if (@errs) {
  577. return @errs;
  578. }
  579. if (@errs = changeOwnerMode($inf, 4, $templdif)) {
  580. unlink($ldiffile);
  581. return @errs;
  582. }
  583. # $templdif now contains the ldif to import
  584. $ldiffile = $templdif;
  585. $istempldif = 1;
  586. }
  587. if (!$ldiffile) {
  588. return ();
  589. }
  590. my $cmd = "$inf->{slapd}->{inst_dir}/ldif2db -n $inf->{slapd}->{ds_bename} -i \'$ldiffile\'";
  591. $? = 0; # clear error condition
  592. my $output = `$cmd 2>&1`;
  593. my $result = $?;
  594. if ($istempldif) {
  595. unlink($ldiffile);
  596. }
  597. if ($result) {
  598. return ('error_importing_ldif', $ldiffile, $result, $output);
  599. }
  600. debug(1, $output);
  601. return ();
  602. }
  603. sub startServer {
  604. my $inf = shift;
  605. return () if (defined($inf->{slapd}->{start_server}) && !$inf->{slapd}->{start_server});
  606. my @errs;
  607. # get error log
  608. my $errLog = "$inf->{slapd}->{log_dir}/errors";
  609. my $startcmd = "$inf->{slapd}->{inst_dir}/start-slapd";
  610. if ("@systemdsystemunitdir@" and (getLogin() eq 'root')) {
  611. $startcmd = "/bin/systemctl start @package_name@\@$inf->{slapd}->{ServerIdentifier}.service";
  612. }
  613. # emulate tail -f
  614. # if the last line we see does not contain "slapd started", try again
  615. my $done = 0;
  616. my $started = 0;
  617. my $code = 0;
  618. my $lastLine = "";
  619. my $cmdPat = 'slapd started\.';
  620. my $timeout = $inf->{slapd}->{startup_timeout};
  621. $timeout = $timeout?$timeout:600; # default is 10 minutes
  622. $timeout = time + $timeout;
  623. debug(1, "Starting the server: $startcmd\n");
  624. $? = 0; # clear error condition
  625. my $output = `$startcmd 2>&1`;
  626. $code = $?;
  627. debug(1, "Started the server: code $code\n");
  628. if ($code) {
  629. debug(0, $output);
  630. } else {
  631. debug(1, $output);
  632. }
  633. # try to open the server error log
  634. my $ii = 0;
  635. while (time < $timeout) {
  636. if (open(IN, $errLog)) {
  637. last;
  638. }
  639. sleep(1);
  640. if (!($ii % 10)) {
  641. debug(0, "Attempting to obtain server status . . .\n");
  642. }
  643. ++$ii;
  644. }
  645. if (! -f $errLog) {
  646. debug(0, "Error: Could not read error log $errLog to get server startup status. Error: $!\n");
  647. return ('error_starting_server', $startcmd, "no status", $!);
  648. }
  649. if (time >= $timeout) {
  650. debug(0, "Error: timed out waiting for the server to start and write to $errLog");
  651. return ('error_starting_server', $startcmd, "timeout", 0);
  652. }
  653. my $pos = tell(IN);
  654. my $line;
  655. while (($done == 0) && (time < $timeout)) {
  656. for (; ($done == 0) && ($line = <IN>); $pos = tell(IN)) {
  657. $lastLine = $line;
  658. debug(1, $line);
  659. if ($line =~ /$cmdPat/) {
  660. $done = 1;
  661. $started = 1;
  662. } elsif ($line =~ /Initialization Failed/) {
  663. debug(1, "Server failed to start, retrying . . .\n");
  664. $code = system($startcmd);
  665. } elsif ($line =~ /exiting\./) {
  666. debug(1, "Server failed to start, retrying . . .\n");
  667. $code = system($startcmd);
  668. }
  669. }
  670. if ($lastLine =~ /PR_Bind/) {
  671. # server port conflicts with another one, just report and punt
  672. debug(0, $lastLine);
  673. @errs = ('error_port_available', $inf->{slapd}->{ServerPort}, $!);
  674. $done = 1;
  675. }
  676. if ($done == 0) {
  677. # rest a bit, then . . .
  678. sleep(2);
  679. # . . . reset the EOF status of the file desc
  680. seek(IN, $pos, 0);
  681. }
  682. }
  683. close(IN);
  684. if (!$started) {
  685. $! = $code;
  686. my $now = time;
  687. if ($now > $timeout) {
  688. debug(0, "Possible timeout starting server: timeout=$timeout now=$now\n");
  689. }
  690. @errs = ('error_starting_server', $startcmd, $lastLine, $!);
  691. } else {
  692. debug(1, "Your new directory server has been started.\n");
  693. }
  694. return @errs;
  695. }
  696. sub set_path_attribute {
  697. my $val = shift;
  698. my $defaultval = shift;
  699. my $prefix = shift;
  700. if ($val) {
  701. return "$prefix" . "$val";
  702. } else {
  703. return "$prefix" . "$defaultval";
  704. }
  705. }
  706. sub setDefaults {
  707. my $inf = shift;
  708. # set default values
  709. # this turns off the warnings
  710. if (!defined($inf->{General}->{prefix})) {
  711. $inf->{General}->{prefix} = "";
  712. }
  713. if (!$inf->{General}->{FullMachineName}) {
  714. $inf->{General}->{FullMachineName} = hostfqdn;
  715. }
  716. if (!$inf->{General}->{SuiteSpotUserID}) {
  717. if ($> != 0) { # if not root, use the user's uid
  718. $inf->{General}->{SuiteSpotUserID} = getLogin;
  719. }
  720. # otherwise, the uid must be specified
  721. }
  722. if (!$inf->{General}->{SuiteSpotGroup}) {
  723. # If the group wasn't specified, use the primary group
  724. # of the SuiteSpot user
  725. $inf->{General}->{SuiteSpotGroup} = getGroup($inf->{General}->{SuiteSpotUserID});
  726. }
  727. if (!$inf->{slapd}->{RootDN}) {
  728. $inf->{slapd}->{RootDN} = "cn=Directory Manager";
  729. }
  730. if (!$inf->{slapd}->{Suffix}) {
  731. my $suffix = $inf->{General}->{FullMachineName};
  732. # convert fqdn to dc= domain components
  733. $suffix =~ s/^[^\.]*\.//; # just the domain part
  734. $suffix = "dc=$suffix";
  735. $suffix =~ s/\./,dc=/g;
  736. $inf->{slapd}->{Suffix} = $suffix;
  737. }
  738. $inf->{slapd}->{Suffix} = normalizeDN($inf->{slapd}->{Suffix});
  739. if (!$inf->{slapd}->{ServerIdentifier}) {
  740. my $servid = $inf->{General}->{FullMachineName};
  741. # strip out the leftmost domain component
  742. $servid =~ s/\..*$//;
  743. $inf->{slapd}->{ServerIdentifier} = $servid;
  744. }
  745. if ("@with_fhs_opt@") {
  746. $inf->{General}->{ServerRoot} = "$inf->{General}->{prefix}/opt/@PACKAGE_NAME@";
  747. } else {
  748. $inf->{General}->{ServerRoot} = "$inf->{General}->{prefix}@serverdir@";
  749. }
  750. if (!defined($inf->{slapd}->{sasl_path})) {
  751. if ($ ne "linux") {
  752. $inf->{slapd}->{sasl_path} = "$inf->{General}->{prefix}@libdir@/sasl2";
  753. }
  754. }
  755. if (!defined($inf->{slapd}->{ServerPort}) and
  756. !defined($inf->{slapd}->{ldapifilepath})) {
  757. if ("@enable_ldapi@") {
  758. return ('error_missing_port_and_ldapi');
  759. } else {
  760. return ('error_missing_port');
  761. }
  762. }
  763. if (!defined($inf->{slapd}->{ServerPort})) {
  764. $inf->{slapd}->{ServerPort} = 0;
  765. }
  766. $inf->{slapd}->{HashedRootDNPwd} = getHashedPassword($inf->{slapd}->{RootDNPwd});
  767. $inf->{slapd}->{localstatedir} = set_path_attribute($inf->{slapd}->{localstatedir},
  768. "@localstatedir@",
  769. $inf->{General}->{prefix});
  770. my $localstatedir = $inf->{slapd}->{localstatedir};
  771. my $servid = $inf->{slapd}->{ServerIdentifier};
  772. $inf->{slapd}->{sysconfdir} = set_path_attribute($inf->{slapd}->{sysconfdir},
  773. "@sysconfdir@",
  774. $inf->{General}->{prefix});
  775. my $sysconfdir = $inf->{slapd}->{sysconfdir};
  776. $inf->{slapd}->{bindir} = set_path_attribute($inf->{slapd}->{bindir},
  777. "@bindir@",
  778. $inf->{General}->{prefix});
  779. $inf->{slapd}->{sbindir} = set_path_attribute($inf->{slapd}->{sbindir},
  780. "@sbindir@",
  781. $inf->{General}->{prefix});
  782. $inf->{slapd}->{datadir} = set_path_attribute($inf->{slapd}->{datadir},
  783. "@datadir@",
  784. $inf->{General}->{prefix});
  785. if (!defined($inf->{slapd}->{inst_dir})) {
  786. $inf->{slapd}->{inst_dir} = "$inf->{General}->{ServerRoot}/slapd-$servid";
  787. }
  788. if (!defined($inf->{slapd}->{config_dir})) {
  789. $inf->{slapd}->{config_dir} = "$inf->{General}->{prefix}@instconfigdir@/slapd-$servid";
  790. }
  791. $ENV{DS_CONFIG_DIR} = $inf->{slapd}->{config_dir};
  792. if (!defined($inf->{slapd}->{schema_dir})) {
  793. $inf->{slapd}->{schema_dir} = "$sysconfdir/@PACKAGE_NAME@/slapd-$servid/schema";
  794. }
  795. if (!defined($inf->{slapd}->{lock_dir})) {
  796. if ("@with_fhs_opt@") {
  797. $inf->{slapd}->{lock_dir} = "$localstatedir/@PACKAGE_NAME@/slapd-$servid/lock";
  798. } else {
  799. $inf->{slapd}->{lock_dir} = "$localstatedir/lock/@PACKAGE_NAME@/slapd-$servid";
  800. }
  801. }
  802. if (!defined($inf->{slapd}->{log_dir})) {
  803. if ("@with_fhs_opt@") {
  804. $inf->{slapd}->{log_dir} = "$localstatedir/@PACKAGE_NAME@/slapd-$servid/log";
  805. } else {
  806. $inf->{slapd}->{log_dir} = "$localstatedir/log/@PACKAGE_NAME@/slapd-$servid";
  807. }
  808. }
  809. if (!defined($inf->{slapd}->{run_dir})) {
  810. if ("@with_fhs_opt@") {
  811. $inf->{slapd}->{run_dir} = "$localstatedir/@PACKAGE_NAME@/slapd-$servid/run";
  812. } else {
  813. $inf->{slapd}->{run_dir} = "$localstatedir/run/@PACKAGE_NAME@";
  814. }
  815. }
  816. $ENV{DS_RUN_DIR} = $inf->{slapd}->{run_dir};
  817. if (!defined($inf->{slapd}->{db_dir})) {
  818. if ("@with_fhs_opt@") {
  819. $inf->{slapd}->{db_dir} = "$localstatedir/@PACKAGE_NAME@/slapd-$servid/db";
  820. } else {
  821. $inf->{slapd}->{db_dir} = "$localstatedir/lib/@PACKAGE_NAME@/slapd-$servid/db";
  822. }
  823. }
  824. if (!defined($inf->{slapd}->{bak_dir})) {
  825. if ("@with_fhs_opt@") {
  826. $inf->{slapd}->{bak_dir} = "$localstatedir/@PACKAGE_NAME@/slapd-$servid/bak";
  827. } else {
  828. $inf->{slapd}->{bak_dir} = "$localstatedir/lib/@PACKAGE_NAME@/slapd-$servid/bak";
  829. }
  830. }
  831. $ENV{DS_BAK_DIR} = $inf->{slapd}->{bak_dir};
  832. if (!defined($inf->{slapd}->{ldif_dir})) {
  833. if ("@with_fhs_opt@") {
  834. $inf->{slapd}->{ldif_dir} = "$localstatedir/@PACKAGE_NAME@/slapd-$servid/ldif";
  835. } else {
  836. $inf->{slapd}->{ldif_dir} = "$localstatedir/lib/@PACKAGE_NAME@/slapd-$servid/ldif";
  837. }
  838. }
  839. if (!defined($inf->{slapd}->{tmp_dir})) {
  840. if ("@with_fhs_opt@") {
  841. $inf->{slapd}->{tmp_dir} = "/tmp";
  842. } else {
  843. $inf->{slapd}->{tmp_dir} = "/tmp";
  844. }
  845. }
  846. $ENV{DS_TMP_DIR} = $inf->{slapd}->{tmp_dir};
  847. if (!defined($inf->{slapd}->{cert_dir})) {
  848. $inf->{slapd}->{cert_dir} = $inf->{slapd}->{config_dir};
  849. }
  850. return ();
  851. }
  852. sub updateSelinuxPolicy {
  853. my $inf = shift;
  854. # if selinux is not available, do nothing
  855. if ("@with_selinux@") {
  856. my $localstatedir = $inf->{slapd}->{localstatedir};
  857. # run restorecon on all of the parent directories we
  858. # may have created (this only happens if this is the
  859. # first instance created).
  860. if ("@with_fhs_opt@") {
  861. system("restorecon -R $localstatedir/@PACKAGE_NAME@");
  862. } else {
  863. system("restorecon -R $localstatedir/lock/@PACKAGE_NAME@");
  864. system("restorecon -R $localstatedir/log/@PACKAGE_NAME@");
  865. system("restorecon -R $localstatedir/run/@PACKAGE_NAME@");
  866. system("restorecon -R $localstatedir/lib/@PACKAGE_NAME@");
  867. }
  868. # run restorecon on all instance directories we created
  869. for my $kw (qw(inst_dir config_dir schema_dir log_dir lock_dir run_dir tmp_dir
  870. cert_dir db_dir ldif_dir bak_dir)) {
  871. my $dir = $inf->{slapd}->{$kw};
  872. system("restorecon -R $dir");
  873. }
  874. # label the selected port as ldap_port_t
  875. if ($inf->{slapd}->{ServerPort} != 0) {
  876. my $need_label = 1;
  877. # check if the port is already labeled properly
  878. my $portline = `semanage port -l | grep ldap_port_t | grep tcp`;
  879. chomp($portline);
  880. $portline =~ s/ldap_port_t\s+tcp\s+//g;
  881. my @labeledports = split(/,\s+/, $portline);
  882. foreach my $labeledport (@labeledports) {
  883. if ($inf->{slapd}->{ServerPort} == $labeledport) {
  884. $need_label = 0;
  885. last;
  886. }
  887. }
  888. if ($need_label == 1) {
  889. my $semanage_err;
  890. my $rc;
  891. my $retry = 60;
  892. $ENV{LANG} = "C";
  893. while (($retry > 0) && ($semanage_err = `semanage port -a -t ldap_port_t -p tcp $inf->{slapd}->{ServerPort} 2>&1`) && ($rc = $?)) {
  894. debug(1, "Adding port $inf->{slapd}->{ServerPort} to selinux policy failed - $semanage_err (return code: $rc).\n");
  895. debug(1, "Retrying in 5 seconds\n");
  896. sleep(5);
  897. $retry--;
  898. }
  899. if (0 == $retry) {
  900. debug(1, "Adding port $inf->{slapd}->{ServerPort} to selinux policy failed - $semanage_err (return code: $rc).\n");
  901. debug(1, "Reached time limit.\n");
  902. }
  903. }
  904. }
  905. }
  906. }
  907. sub updateTmpfilesDotD {
  908. my $inf = shift;
  909. my $dir = "@with_tmpfiles_d@";
  910. # if tmpfiles.d is not available, do nothing
  911. if ($dir and -d $dir) {
  912. my $filename = "$dir/@package_name@-$inf->{slapd}->{ServerIdentifier}.conf";
  913. if (! -f $filename) {
  914. debug(3, "Creating $filename\n");
  915. my $username = "";
  916. my $groupname = "";
  917. my $conffile = "$inf->{slapd}->{config_dir}/dse.ldif";
  918. # use the owner:group from the dse.ldif for the instance
  919. if (-f $conffile) {
  920. my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
  921. $atime,$mtime,$ctime,$blksize,$blocks)
  922. = stat(_);
  923. $username = getpwuid($uid);
  924. if (!$username) {
  925. debug(1, "Error: could not get username from uid $uid\n");
  926. }
  927. $groupname = getgrgid($gid);
  928. }
  929. # else, see if we were passed in values to use
  930. if (!$username) {
  931. $username = $inf->{General}->{SuiteSpotUserID};
  932. }
  933. if (!$groupname) {
  934. if (defined($inf->{General}->{SuiteSpotGroup})) {
  935. $groupname = $inf->{General}->{SuiteSpotGroup};
  936. } else { # $groupname
  937. $groupname = "-"; # use default
  938. }
  939. }
  940. my $parent = dirname($inf->{slapd}->{lock_dir});
  941. if (!open(DOTDFILE, ">$filename")) {
  942. return ( [ 'error_creating_file', $filename, $! ] );
  943. }
  944. # Type Path Mode UID GID Age
  945. # d /var/run/user 0755 root root 10d
  946. # we don't use age
  947. print DOTDFILE "d $inf->{slapd}->{run_dir} 0770 $username $groupname\n";
  948. print DOTDFILE "d $parent 0770 $username $groupname\n";
  949. print DOTDFILE "d $inf->{slapd}->{lock_dir} 0770 $username $groupname\n";
  950. close DOTDFILE;
  951. } else {
  952. debug(3, "$filename exists - skipping\n");
  953. }
  954. } else {
  955. debug(3, "no tmpfiles.d - skipping\n");
  956. }
  957. return ();
  958. }
  959. sub updateSystemD {
  960. my $inf = shift;
  961. my $unitdir = "@systemdsystemunitdir@";
  962. my $confbasedir = "@systemdsystemconfdir@";
  963. my $confdir = "$confbasedir/@[email protected]";
  964. if (!$unitdir or !$confdir or ! -d $unitdir or ! -d $confdir) {
  965. debug(3, "no systemd - skipping\n");
  966. return ();
  967. }
  968. my @errs = ();
  969. my $initconfigdir = $inf->{slapd}->{initconfigdir} || get_initconfigdir();
  970. debug(1, "updating systemd files in $unitdir and $confdir for all directory server instances in $initconfigdir\n");
  971. my $pkgname = "@package_name@";
  972. my $changes = 0;
  973. # installation should already have put down the files and
  974. # directories - we just need to update the symlinks
  975. my $servicefile = "$unitdir/$pkgname\@.service";
  976. # first, look for new instances
  977. for my $file (glob("$initconfigdir/$pkgname-*")) {
  978. my $inst = $file;
  979. $inst =~ s/^.*$pkgname-//;
  980. # see if this is the admin or snmp or some other service
  981. if (-f "$unitdir/$pkgname-$inst.service") {
  982. debug(1, "$unitdir/$pkgname-$inst.service already exists - skipping\n");
  983. next;
  984. } elsif (-f "$confbasedir/$pkgname-$inst.service") {
  985. debug(1, "$confbasedir/$pkgname-$inst.service already exists - skipping\n");
  986. next;
  987. } else {
  988. my $servicelink = "$confdir/$pkgname\@$inst.service";
  989. if (! -l $servicelink) {
  990. if (!symlink($servicefile, $servicelink)) {
  991. debug(1, "error updating link $servicelink to $servicefile - $!\n");
  992. push @errs, [ 'error_linking_file', $servicefile, $servicelink, $! ];
  993. } else {
  994. debug(2, "updated link $servicelink to $servicefile\n");
  995. }
  996. $changes++;
  997. }
  998. }
  999. }
  1000. # next, look for instances that have been removed
  1001. for my $file (glob("$confdir/$pkgname\@*.service")) {
  1002. my $inst = $file;
  1003. $inst =~ s/^.*$pkgname\@(.*?).service$/$1/;
  1004. if (! -f "$initconfigdir/$pkgname-$inst") {
  1005. if (!unlink($file)) {
  1006. debug(1, "error removing $file - $!\n");
  1007. push @errs, [ 'error_removing_path', $file, $! ];
  1008. } else {
  1009. debug(2, "removed systemd file $file for removed instance $inst\n");
  1010. }
  1011. $changes++;
  1012. }
  1013. }
  1014. if ($changes > 0) {
  1015. $? = 0;
  1016. my $cmd = '/bin/systemctl --system daemon-reload';
  1017. # run the reload command
  1018. my $output = `$cmd 2>&1`;
  1019. my $status = $?;
  1020. if ($status) {
  1021. debug(1, "Error: $cmd failed - output $output: $!\n");
  1022. push @errs, [ 'error_running_command', $cmd, $output, $! ];
  1023. } else {
  1024. debug(2, "$cmd succeeded\n");
  1025. }
  1026. } else {
  1027. debug(1, "No changes to $unitdir or $confdir\n");
  1028. }
  1029. return @errs;
  1030. }
  1031. sub createDSInstance {
  1032. my $inf = shift;
  1033. my @errs;
  1034. if (@errs = setDefaults($inf)) {
  1035. return @errs;
  1036. }
  1037. if (@errs = sanityCheckParams($inf)) {
  1038. return @errs;
  1039. }
  1040. if (@errs = makeDSDirs($inf)) {
  1041. return @errs;
  1042. }
  1043. if (@errs = createConfigFile($inf)) {
  1044. return @errs;
  1045. }
  1046. if (@errs = makeOtherConfigFiles($inf)) {
  1047. return @errs;
  1048. }
  1049. if (@errs = createInstanceScripts($inf)) {
  1050. return @errs;
  1051. }
  1052. if (@errs = installSchema($inf)) {
  1053. return @errs;
  1054. }
  1055. if (@errs = initDatabase($inf)) {
  1056. return @errs;
  1057. }
  1058. updateSelinuxPolicy($inf);
  1059. if (@errs = updateTmpfilesDotD($inf)) {
  1060. return @errs;
  1061. }
  1062. if (@errs = updateSystemD($inf)) {
  1063. return @errs;
  1064. }
  1065. if (@errs = startServer($inf)) {
  1066. return @errs;
  1067. }
  1068. return @errs;
  1069. }
  1070. sub stopServer {
  1071. my $instancedir = shift;
  1072. my $prog = $instancedir . "/stop-slapd";
  1073. if (-x $prog) {
  1074. $? = 0;
  1075. # run the stop command
  1076. my $output = `$prog 2>&1`;
  1077. my $status = $?;
  1078. debug(3, "stopping server $instancedir returns status $status: output $output\n");
  1079. if ($status) {
  1080. debug(1,"Warning: Could not stop directory server: status $status: output $output\n");
  1081. # if the server is not running, that's ok
  1082. if ($output =~ /not running/) {
  1083. $! = ENOENT;
  1084. return 1;
  1085. }
  1086. # else, some other error (e.g. permission) - return false for error
  1087. return;
  1088. }
  1089. } else {
  1090. debug(1, "stopping server: no such program $prog: cannot stop server\n");
  1091. return;
  1092. }
  1093. debug(1, "Successfully stopped server $instancedir\n");
  1094. return 1;
  1095. }
  1096. # NOTE: Returns a list of array ref - each array ref is suitable for passing
  1097. # to Resource::getText
  1098. sub removeDSInstance {
  1099. my $inst = shift;
  1100. my $force = shift;
  1101. my $all = shift;
  1102. my $initconfig_dir = shift || get_initconfigdir();
  1103. my $baseconfigdir = $ENV{DS_CONFIG_DIR} || "@instconfigdir@";
  1104. my $instname = "slapd-$inst";
  1105. my $configdir;
  1106. my $rundir;
  1107. my $product_name;
  1108. my @errs;
  1109. my $initconfig = "$initconfig_dir/@package_name@-$inst";
  1110. # Get the configdir, rundir and product_name from the instance initconfig script.
  1111. unless(open(INFILE, $initconfig)) {
  1112. return ( [ 'error_no_such_instance', $instname, $! ] );
  1113. }
  1114. my $line;
  1115. while($line = <INFILE>) {
  1116. if ($line =~ /CONFIG_DIR=(.*) ; export CONFIG_DIR/) {
  1117. $configdir = $1;
  1118. } elsif ($line =~ /CONFIG_DIR=(.*)$/) {
  1119. $configdir = $1;
  1120. } elsif ($line =~ /RUN_DIR=(.*) ; export RUN_DIR/) {
  1121. $rundir = $1;
  1122. } elsif ($line =~ /RUN_DIR=(.*)$/) {
  1123. $rundir = $1;
  1124. } elsif ($line =~ /PRODUCT_NAME=(.*) ; export PRODUCT_NAME/) {
  1125. $product_name = $1;
  1126. } elsif ($line =~ /PRODUCT_NAME=(.*)$/) {
  1127. $product_name = $1;
  1128. }
  1129. }
  1130. close(INFILE);
  1131. if ( ! -d $configdir )
  1132. {
  1133. debug(1, "Error: $configdir does not exist: $!\n");
  1134. return ( [ 'error_no_such_instance', $configdir, $! ] );
  1135. }
  1136. # read the config file to find out the paths
  1137. my $dseldif = "$configdir/dse.ldif";
  1138. my $conn = new FileConn($dseldif, 1);
  1139. if (!$conn) {
  1140. debug(1, "Error: Could not open config file $dseldif: Error $!\n");
  1141. return ( [ 'error_opening_dseldif', $dseldif, $! ] );
  1142. }
  1143. my $dn = "cn=config";
  1144. my $entry = $conn->search($dn, "base", "(cn=*)", 0);
  1145. if (!$entry)
  1146. {
  1147. debug(1, "Error: Search $dn in $dseldif failed: $entry\n");
  1148. push @errs, [ 'error_finding_config_entry', $dn, $dseldif, $conn->getErrorString() ];
  1149. }
  1150. $dn = "cn=config,cn=ldbm database,cn=plugins,cn=config";
  1151. my $dbentry = $conn->search($dn, "base", "(cn=*)", 0);
  1152. if (!$dbentry)
  1153. {
  1154. debug(1, "Error: Search $dn in $dseldif failed: $dbentry\n");
  1155. push @errs, [ 'error_finding_config_entry', $dn, $dseldif, $conn->getErrorString() ];
  1156. }
  1157. $conn->close();
  1158. # stop the server
  1159. my $instdir = "";
  1160. if ($entry) {
  1161. foreach my $path ( @{$entry->{"nsslapd-instancedir"}} )
  1162. {
  1163. if (!stopServer($path)) {
  1164. if ($force) {
  1165. debug(1, "Warning: Could not stop directory server - Error: $! - forcing continue\n");
  1166. } elsif ($! == ENOENT) { # stop script not found or server not running
  1167. debug(1, "Warning: Could not stop directory server: already removed or not running\n");
  1168. push @errs, [ 'error_stopping_server', $path, $! ];
  1169. } else { # real error
  1170. debug(1, "Error: Could not stop directory server - aborting - use -f flag to force removal\n");
  1171. push @errs, [ 'error_stopping_server', $path, $! ];
  1172. return @errs;
  1173. }
  1174. }
  1175. $instdir = $path;
  1176. }
  1177. }
  1178. # remove physical dirs/files
  1179. if ($dbentry) {
  1180. push @errs, remove_tree($dbentry, "nsslapd-directory", $instname, 1);
  1181. push @errs, remove_tree($dbentry, "nsslapd-db-logdirectory", $instname, 1);
  1182. }
  1183. if ($entry) {
  1184. push @errs, remove_tree($entry, "nsslapd-lockdir", $instname, 0);
  1185. push @errs, remove_tree($entry, "nsslapd-tmpdir", $instname, 0);
  1186. push @errs, remove_tree($entry, "nsslapd-bakdir", $instname, 1);
  1187. push @errs, remove_tree($entry, "nsslapd-errorlog", $instname, 1);
  1188. }
  1189. # instance dir
  1190. if ( -d $instdir && $instdir =~ /$instname/ )
  1191. {
  1192. # clean up pid files (if any)
  1193. remove_pidfile("STARTPIDFILE", $inst, $instdir, $instname, $rundir, $product_name);
  1194. remove_pidfile("PIDFILE", $inst, $instdir, $instname, $rundir, $product_name);
  1195. my $rc = rmtree($instdir);
  1196. if ( 0 == $rc )
  1197. {
  1198. push @errs, [ 'error_removing_path', $instdir, $! ];
  1199. debug(1, "Warning: $instdir was not removed. Error: $!\n");
  1200. }
  1201. }
  1202. # Finally, config dir
  1203. if ($all) {
  1204. push @errs, remove_tree($entry, "nsslapd-schemadir", $instname, 1);
  1205. } else {
  1206. push @errs, remove_tree($entry, "nsslapd-schemadir", $instname, 1, "\.db\$");
  1207. }
  1208. # Remove the instance specific initconfig script
  1209. if ( -f $initconfig ) {
  1210. my $rc = unlink($initconfig);
  1211. if ( 0 == $rc )
  1212. {
  1213. push @errs, [ 'error_removing_path', $initconfig, $! ];
  1214. debug(1, "Warning: $initconfig was not removed. Error: $!\n");
  1215. }
  1216. }
  1217. my $tmpfilesdir = "@with_tmpfiles_d@";
  1218. my $tmpfilesname = "$tmpfilesdir/@package_name@-$inst";
  1219. if ($tmpfilesdir and -d $tmpfilesdir and -f $tmpfilesname) {
  1220. my $rc = unlink($tmpfilesname);
  1221. if ( 0 == $rc )
  1222. {
  1223. push @errs, [ 'error_removing_path', $tmpfilesname, $! ];
  1224. debug(1, "Warning: $tmpfilesname was not removed. Error: $!\n");
  1225. }
  1226. }
  1227. # remove the selinux label from the ports if needed
  1228. if ("@with_selinux@") {
  1229. foreach my $port (@{$entry->{"nsslapd-port"}})
  1230. {
  1231. my $semanage_err;
  1232. my $rc;
  1233. my $retry = 60;
  1234. $ENV{LANG} = "C";
  1235. while (($retry > 0) && ($semanage_err = `semanage port -d -t ldap_port_t -p tcp $port 2>&1`) && ($rc = $?)) {
  1236. if (($semanage_err =~ /defined in policy, cannot be deleted/) || ($semanage_err =~ /is not defined/)) {
  1237. $retry = -1;
  1238. } else {
  1239. debug(1, "Warning: Port $port not removed from selinux policy correctly. Error: $semanage_err\n");
  1240. debug(1, "Retrying in 5 seconds\n");
  1241. sleep(5);
  1242. $retry--;
  1243. }
  1244. }
  1245. if (0 == $retry) {
  1246. push @errs, [ 'error_removing_port_label', $port, $semanage_err];
  1247. debug(1, "Warning: Port $port not removed from selinux policy correctly. Error: $semanage_err\n");
  1248. debug(1, "Reached time limit.\n");
  1249. }
  1250. }
  1251. foreach my $secureport (@{$entry->{"nsslapd-secureport"}})
  1252. {
  1253. my $semanage_err;
  1254. my $rc;
  1255. my $retry = 60;
  1256. $ENV{LANG} = "C";
  1257. while (($retry > 0) && ($semanage_err = `semanage port -d -t ldap_port_t -p tcp $secureport 2>&1`) && ($rc = $?)) {
  1258. if (($semanage_err =~ /defined in policy, cannot be deleted/) || ($semanage_err =~ /is not defined/)) {
  1259. $retry = -1;
  1260. } else {
  1261. debug(1, "Warning: Port $secureport not removed from selinux policy correctly. Error: $semanage_err\n");
  1262. debug(1, "Retrying in 5 seconds\n");
  1263. sleep(5);
  1264. $retry--;
  1265. }
  1266. }
  1267. if (0 == $retry) {
  1268. push @errs, [ 'error_removing_port_label', $secureport, $semanage_err];
  1269. debug(1, "Warning: Port $secureport not removed from selinux policy correctly. Error: $semanage_err\n");
  1270. debug(1, "Reached time limit.\n");
  1271. }
  1272. }
  1273. }
  1274. # update systemd files
  1275. push @errs, updateSystemD();
  1276. # if we got here, report success
  1277. if (@errs) {
  1278. debug(1, "Could not successfully remove $instname\n");
  1279. } else {
  1280. debug(1, "Instance $instname removed.\n");
  1281. }
  1282. return @errs;
  1283. }
  1284. 1;
  1285. # emacs settings
  1286. # Local Variables:
  1287. # mode:perl
  1288. # indent-tabs-mode: nil
  1289. # tab-width: 4
  1290. # End: