DSCreate.pm.in 53 KB

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