DSCreate.pm.in 50 KB

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