load-service.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. #include <algorithm>
  2. #include <string>
  3. #include <fstream>
  4. #include <locale>
  5. #include <limits>
  6. #include <list>
  7. #include <utility>
  8. #include <iterator>
  9. #include <cstring>
  10. #include <cstdlib>
  11. #include <sys/stat.h>
  12. #include <sys/types.h>
  13. #include <pwd.h>
  14. #include <grp.h>
  15. #include <dirent.h>
  16. #include "proc-service.h"
  17. #include "dinit-log.h"
  18. #include "dinit-util.h"
  19. #include "dinit-utmp.h"
  20. using string = std::string;
  21. using string_iterator = std::string::iterator;
  22. // Perform environment variable substitution on a command line, if specified.
  23. // line - the string storing the command and arguments
  24. // offsets - the [start,end) pair of offsets of the command and each argument within the string
  25. //
  26. static void do_env_subst(const char *setting_name, ha_string &line,
  27. std::list<std::pair<unsigned,unsigned>> &offsets, bool do_sub_vars)
  28. {
  29. using namespace dinit_load;
  30. if (do_sub_vars) {
  31. std::string line_s = std::string(line.c_str(), line.length());
  32. cmdline_var_subst(setting_name, line_s, offsets, resolve_env_var);
  33. line = line_s;
  34. }
  35. }
  36. // Process a dependency directory - filenames contained within correspond to service names which
  37. // are loaded and added as a dependency of the given type. Expected use is with a directory
  38. // containing symbolic links to other service descriptions, but this isn't required.
  39. // Failure to read the directory contents, or to find a service listed within, is not considered
  40. // a fatal error.
  41. static void process_dep_dir(dirload_service_set &sset,
  42. const char *servicename,
  43. const string &service_filename,
  44. std::list<prelim_dep> &deplist, const std::string &depdirpath,
  45. dependency_type dep_type,
  46. const service_record *avoid_circular)
  47. {
  48. std::string depdir_fname = combine_paths(parent_path(service_filename), depdirpath.c_str());
  49. DIR *depdir = opendir(depdir_fname.c_str());
  50. if (depdir == nullptr) {
  51. log(loglevel_t::WARN, "Could not open dependency directory '", depdir_fname,
  52. "' for ", servicename, " service.");
  53. return;
  54. }
  55. errno = 0;
  56. dirent * dent = readdir(depdir);
  57. while (dent != nullptr) {
  58. char * name = dent->d_name;
  59. if (name[0] != '.') {
  60. try {
  61. service_record * sr = sset.load_service(name);
  62. deplist.emplace_back(sr, dep_type);
  63. }
  64. catch (service_not_found &) {
  65. log(loglevel_t::WARN, "Ignoring unresolved dependency '", name,
  66. "' in dependency directory '", depdirpath,
  67. "' for ", servicename, " service.");
  68. }
  69. }
  70. errno = 0; // errno may have changed in the meantime
  71. dent = readdir(depdir);
  72. }
  73. if (errno != 0) {
  74. log(loglevel_t::WARN, "Error reading dependency directory '", depdirpath,
  75. "' for ", servicename, " service.");
  76. }
  77. closedir(depdir);
  78. }
  79. service_record * dirload_service_set::load_service(const char * name, const service_record *avoid_circular)
  80. {
  81. return load_reload_service(name, nullptr, avoid_circular);
  82. }
  83. service_record * dirload_service_set::reload_service(service_record * service)
  84. {
  85. return load_reload_service(service->get_name().c_str(), service, service);
  86. }
  87. using service_dep_list = decltype(std::declval<dinit_load::service_settings_wrapper<prelim_dep>>().depends);
  88. // Check for dependency cycles for the specified service (orig) with the given set of dependencies
  89. static void check_cycle(service_dep_list &deps, service_record *orig)
  90. {
  91. linked_uo_set<service_record *> pending;
  92. for (auto &new_dep : deps) {
  93. if (new_dep.to == orig) {
  94. throw service_cyclic_dependency(orig->get_name());
  95. }
  96. pending.add_back(new_dep.to);
  97. }
  98. for (auto i = pending.begin(); i != pending.end(); ++i) {
  99. auto &dep_list = (*i)->get_dependencies();
  100. for (auto &dep : dep_list) {
  101. if (dep.get_to() == orig) {
  102. throw service_cyclic_dependency(orig->get_name());
  103. }
  104. pending.add_back(dep.get_to());
  105. }
  106. }
  107. }
  108. // Update the dependencies of the specified service atomically.
  109. // May fail with bad_alloc, service_cyclic_dependency.
  110. static void update_depenencies(service_record *service,
  111. dinit_load::service_settings_wrapper<prelim_dep> &settings,
  112. std::list<service_dep> &before_deps)
  113. {
  114. check_cycle(settings.depends, service);
  115. std::list<service_dep> &deps = service->get_dependencies();
  116. auto first_preexisting = deps.begin();
  117. auto &depts = service->get_dependents();
  118. auto first_pre_dept = depts.begin();
  119. try {
  120. // Insert all new dependents (from "before" relationships) before the first pre-existing dependent
  121. for (auto new_dept_i = before_deps.begin(); new_dept_i != before_deps.end(); ) {
  122. auto &new_dept = *new_dept_i;
  123. depts.insert(depts.begin(), &new_dept);
  124. // splice the dependency into the dependent:
  125. auto next_dept_i = std::next(new_dept_i);
  126. auto &from_deps = new_dept.get_from()->get_dependencies();
  127. from_deps.splice(from_deps.begin(), before_deps, new_dept_i);
  128. new_dept_i = next_dept_i;
  129. }
  130. // Insert all the new dependencies before the first pre-existing dependency
  131. for (auto &new_dep : settings.depends) {
  132. service->add_dep(new_dep.to, new_dep.dep_type, first_preexisting);
  133. }
  134. }
  135. catch (...) {
  136. // remove "before" dependencies from dependents
  137. for (auto i = depts.begin(); i != first_pre_dept; ) {
  138. auto next_i = std::next(i);
  139. (*i)->get_from()->rm_dep(**i);
  140. i = next_i;
  141. }
  142. // remove the inserted dependencies
  143. for (auto i = deps.begin(); i != first_preexisting; ) {
  144. i = service->rm_dep(i);
  145. }
  146. // re-throw the exception
  147. throw;
  148. }
  149. // Now remove all pre-existing dependencies (no exceptions possible from here).
  150. for( ; first_preexisting != deps.end(); ) {
  151. first_preexisting = service->rm_dep(first_preexisting);
  152. }
  153. // Also remove pre-existing "before" dependents
  154. for( ; first_pre_dept != depts.end(); ) {
  155. auto next_pre_dept = std::next(first_pre_dept);
  156. if ((*first_pre_dept)->dep_type == dependency_type::BEFORE) {
  157. (*first_pre_dept)->get_from()->rm_dep(**first_pre_dept);
  158. }
  159. first_pre_dept = next_pre_dept;
  160. }
  161. }
  162. // Update the command, and dependencies, of the specified service atomically.
  163. // May fail with bad_alloc, service_cyclic_dependency.
  164. static void update_command_and_dependencies(base_process_service *service,
  165. dinit_load::service_settings_wrapper<prelim_dep> &settings,
  166. std::list<service_dep> &before_deps)
  167. {
  168. // Get the current command parts
  169. ha_string orig_cmd; std::vector<const char *> orig_arg_parts;
  170. service->get_command(orig_cmd, orig_arg_parts);
  171. // Separate the new command parts and set
  172. std::vector<const char *> cmd_arg_parts = separate_args(settings.command, settings.command_offsets);
  173. service->set_command(std::move(settings.command), std::move(cmd_arg_parts));
  174. try {
  175. update_depenencies(service, settings, before_deps);
  176. }
  177. catch (...) {
  178. // restore original command
  179. service->set_command(std::move(orig_cmd), std::move(orig_arg_parts));
  180. // re-throw the exception
  181. throw;
  182. }
  183. }
  184. service_record * dirload_service_set::load_reload_service(const char *name, service_record *reload_svc,
  185. const service_record *avoid_circular)
  186. {
  187. // For reload, we have the following problems:
  188. // - ideally want to allow changing service type, at least for stopped services. That implies creating
  189. // a new (replacement) service_record object, at least in cases where the type does change.
  190. // - dependencies may change (including addition of new dependencies which aren't yet loaded). We need
  191. // to prevent cyclic dependencies forming.
  192. // - We want atomicity. If any new settings are not valid/alterable, or if a cyclic dependency is
  193. // created, nothing should change. Ideally this would extend to unloading any dependencies which were
  194. // loaded as part of the reload attempt.
  195. // - We need to either transfer handles referring to the old service (so that they refer to the new
  196. // service), or make them invalid. Or, we alter the original service without creating a new one
  197. // (which we can only do if the type doesn't change).
  198. // Approach:
  199. // - remember the initial service count, so we can remove services loaded as part of the reload
  200. // operation if we want to abort it later (i.e. if service count changed from N to N+X, remove the
  201. // last X services)
  202. // - check that the new settings are valid (if the service is running, check if the settings can be
  203. // altered, though we may just defer some changes until service is restarted)
  204. // - check all dependencies of the newly created service record for cyclic dependencies, via depth-first
  205. // traversal.
  206. // - If changing type:
  207. // - create the service initially just as if loading a new service (but with no dummy placeholder,
  208. // use the original service for that).
  209. // - switch all dependents to depend on the new record. Copy necessary runtime data from the original
  210. // to the new service record. Remove dependencies from the old record, and release any dependency
  211. // services as appropriate (so they stop if no longer needed). Finally, remove the old service
  212. // record and delete it.
  213. // Otherwise:
  214. // - copy the new settings to the existing service
  215. // - fix dependencies
  216. //
  217. // Limitations:
  218. // - caller must check there are no handles (or only a single requesting handle) to the service before
  219. // calling
  220. // - cannot change the type of a non-stopped service
  221. using std::string;
  222. using std::ifstream;
  223. using std::ios;
  224. using std::ios_base;
  225. using std::locale;
  226. using std::isspace;
  227. using std::list;
  228. using std::pair;
  229. using namespace dinit_load;
  230. if (reload_svc == nullptr) {
  231. // First try and find an existing record...
  232. service_record * rval = find_service(string(name));
  233. if (rval != nullptr) {
  234. if (rval == avoid_circular || rval->is_dummy()) {
  235. throw service_cyclic_dependency(name);
  236. }
  237. return rval;
  238. }
  239. }
  240. service_record *rval = nullptr;
  241. service_record *dummy = nullptr;
  242. ifstream service_file;
  243. string service_filename;
  244. int fail_load_errno = 0;
  245. std::string fail_load_path;
  246. // Couldn't find one. Have to load it.
  247. for (auto &service_dir : service_dirs) {
  248. service_filename = service_dir.get_dir();
  249. if (*(service_filename.rbegin()) != '/') {
  250. service_filename += '/';
  251. }
  252. service_filename += name;
  253. service_file.open(service_filename.c_str(), ios::in);
  254. if (service_file) break;
  255. if (errno != ENOENT && fail_load_errno == 0) {
  256. fail_load_errno = errno;
  257. fail_load_path = std::move(service_filename);
  258. }
  259. }
  260. if (!service_file) {
  261. if (fail_load_errno == 0) {
  262. throw service_not_found(string(name));
  263. }
  264. else {
  265. throw service_load_error(name, std::move(fail_load_path), fail_load_errno);
  266. }
  267. }
  268. service_settings_wrapper<prelim_dep> settings;
  269. string line;
  270. // getline can set failbit if it reaches end-of-file, we don't want an exception in that case. There's
  271. // no good way to handle an I/O error however, so we'll have exceptions thrown on badbit:
  272. service_file.exceptions(ios::badbit);
  273. bool create_new_record = true;
  274. try {
  275. if (reload_svc == nullptr) {
  276. // Add a dummy service record now to prevent infinite recursion in case of cyclic dependency.
  277. // We replace this with the real service later (or remove it if we find a configuration error).
  278. dummy = new service_record(this, string(name));
  279. add_service(dummy);
  280. }
  281. process_service_file(name, service_file,
  282. [&](string &line, unsigned line_num, string &setting,
  283. string_iterator &i, string_iterator &end) -> void {
  284. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  285. dependency_type dep_type) -> void {
  286. process_dep_dir(*this, name, service_filename, deplist, waitsford, dep_type, reload_svc);
  287. };
  288. auto load_service_n = [&](const string &dep_name) -> service_record * {
  289. try {
  290. return load_service(dep_name.c_str(), reload_svc);
  291. }
  292. catch (service_description_exc &sle) {
  293. log_service_load_failure(sle);
  294. throw service_load_exc(name, "could not load dependency.");
  295. }
  296. catch (service_load_exc &sle) {
  297. log(loglevel_t::ERROR, "Could not load service ", sle.service_name, ": ",
  298. sle.exc_description);
  299. throw service_load_exc(name, "could not load dependency.");
  300. }
  301. };
  302. process_service_line(settings, name, line, line_num, setting, i, end, load_service_n,
  303. process_dep_dir_n);
  304. });
  305. service_file.close();
  306. auto report_err = [&](const char *msg){
  307. throw service_load_exc(name, msg);
  308. };
  309. settings.finalise(report_err);
  310. auto service_type = settings.service_type;
  311. if (reload_svc != nullptr) {
  312. // Make sure settings are able to be changed/are compatible
  313. service_record *service = reload_svc;
  314. if (service->get_state() != service_state_t::STOPPED) {
  315. // Can not change type of a running service.
  316. if (service_type != service->get_type()) {
  317. throw service_load_exc(name, "cannot change type of non-stopped service.");
  318. }
  319. // Can not alter a starting/stopping service, at least for now.
  320. if (service->get_state() != service_state_t::STARTED) {
  321. throw service_load_exc(name,
  322. "cannot alter settings for service which is currently starting/stopping.");
  323. }
  324. // Check validity of dependencies (if started, regular deps must be started)
  325. for (auto &new_dep : settings.depends) {
  326. if (new_dep.dep_type == dependency_type::REGULAR) {
  327. if (new_dep.to->get_state() != service_state_t::STARTED) {
  328. throw service_load_exc(name,
  329. std::string("cannot add non-started dependency '")
  330. + new_dep.to->get_name() + "'.");
  331. }
  332. }
  333. }
  334. // Cannot change certain flags
  335. auto current_flags = service->get_flags();
  336. if (current_flags.starts_on_console != settings.onstart_flags.starts_on_console
  337. || current_flags.shares_console != settings.onstart_flags.shares_console) {
  338. throw service_load_exc(name, "cannot change starts_on_console/"
  339. "shares_console flags for a running service.");
  340. }
  341. // Cannot change pid file
  342. if (service->get_type() == service_type_t::BGPROCESS) {
  343. auto *bgp_service = static_cast<bgproc_service *>(service);
  344. if (bgp_service->get_pid_file() != settings.pid_file) {
  345. throw service_load_exc(name, "cannot change pid_file for running service.");
  346. }
  347. }
  348. // Cannot change inittab_id/inittab_line
  349. #if USE_UTMPX
  350. if (service->get_type() == service_type_t::PROCESS) {
  351. auto *proc_service = static_cast<process_service *>(service);
  352. auto *svc_utmp_id = proc_service->get_utmp_id();
  353. auto *svc_utmp_ln = proc_service->get_utmp_line();
  354. if (strncmp(svc_utmp_id, settings.inittab_id, proc_service->get_utmp_id_size()) != 0
  355. || strncmp(svc_utmp_ln, settings.inittab_line,
  356. proc_service->get_utmp_line_size()) != 0) {
  357. throw service_load_exc(name, "cannot change inittab-id or inittab-line "
  358. "settings for running service.");
  359. }
  360. }
  361. #endif
  362. // Already started; we must replace settings on existing service record
  363. create_new_record = false;
  364. }
  365. else if (service_type != service->get_type()) {
  366. // No need to create a new record if the type hasn't changed
  367. create_new_record = false;
  368. }
  369. }
  370. // Note, we need to be very careful to handle exceptions properly and roll back any changes that
  371. // we've made before the exception occurred.
  372. // if we have "before" constraints, check them now, before we potentially do irreversible changes
  373. // to an existing service.
  374. std::list<service_dep> before_deps;
  375. if (dummy == nullptr) {
  376. for (const std::string &before_ent : settings.before_svcs) {
  377. service_record *before_svc;
  378. try {
  379. before_svc = load_service(before_ent.c_str());
  380. }
  381. catch (service_description_exc &sle) {
  382. log_service_load_failure(sle);
  383. throw service_load_exc(name, "could not load dependency.");
  384. }
  385. catch (service_load_exc &sle) {
  386. log(loglevel_t::ERROR, "Could not load service ", sle.service_name, ": ",
  387. sle.exc_description);
  388. throw service_load_exc(name, "could not load dependency.");
  389. }
  390. before_deps.emplace_back(before_svc, reload_svc, dependency_type::BEFORE);
  391. // (note, we may need to adjust the to-service if we create a new service record object)
  392. check_cycle(settings.depends, before_svc);
  393. if (before_svc == reload_svc) {
  394. throw service_cyclic_dependency(before_svc->get_name());
  395. }
  396. }
  397. }
  398. else {
  399. // If we have a dummy service in place, we can't load "before" services since they
  400. // may depend on *this* service which is currently represented as a dummy, which would
  401. // trigger cycle detection.
  402. // So, we'll do it later in this case. We can also postpone if we'll be creating a
  403. // replacement service record rather than modifying the original.
  404. }
  405. if (service_type == service_type_t::PROCESS) {
  406. do_env_subst("command", settings.command, settings.command_offsets, settings.do_sub_vars);
  407. do_env_subst("stop-command", settings.stop_command, settings.stop_command_offsets, settings.do_sub_vars);
  408. std::vector<const char *> stop_arg_parts = separate_args(settings.stop_command, settings.stop_command_offsets);
  409. process_service *rvalps;
  410. if (create_new_record) {
  411. rvalps = new process_service(this, string(name), std::move(settings.command),
  412. settings.command_offsets, settings.depends);
  413. if (reload_svc != nullptr) {
  414. check_cycle(settings.depends, reload_svc);
  415. }
  416. }
  417. else {
  418. rvalps = static_cast<process_service *>(reload_svc);
  419. update_command_and_dependencies(rvalps, settings, before_deps);
  420. }
  421. rval = rvalps;
  422. // All of the following should be noexcept or must perform rollback on exception
  423. rvalps->set_stop_command(std::move(settings.stop_command), std::move(stop_arg_parts));
  424. rvalps->set_working_dir(std::move(settings.working_dir));
  425. rvalps->set_env_file(std::move(settings.env_file));
  426. #if SUPPORT_CGROUPS
  427. rvalps->set_cgroup(std::move(settings.run_in_cgroup));
  428. #endif
  429. rvalps->set_rlimits(std::move(settings.rlimits));
  430. rvalps->set_restart_interval(settings.restart_interval, settings.max_restarts);
  431. rvalps->set_restart_delay(settings.restart_delay);
  432. rvalps->set_stop_timeout(settings.stop_timeout);
  433. rvalps->set_start_timeout(settings.start_timeout);
  434. rvalps->set_extra_termination_signal(settings.term_signal);
  435. rvalps->set_run_as_uid_gid(settings.run_as_uid, settings.run_as_gid);
  436. rvalps->set_notification_fd(settings.readiness_fd);
  437. rvalps->set_notification_var(std::move(settings.readiness_var));
  438. rvalps->set_log_file(std::move(settings.logfile));
  439. rvalps->set_log_buf_max(settings.max_log_buffer_sz);
  440. rvalps->set_log_mode(settings.log_type);
  441. #if USE_UTMPX
  442. rvalps->set_utmp_id(settings.inittab_id);
  443. rvalps->set_utmp_line(settings.inittab_line);
  444. #endif
  445. }
  446. else if (service_type == service_type_t::BGPROCESS) {
  447. do_env_subst("command", settings.command, settings.command_offsets, settings.do_sub_vars);
  448. do_env_subst("stop-command", settings.stop_command, settings.stop_command_offsets, settings.do_sub_vars);
  449. std::vector<const char *> stop_arg_parts = separate_args(settings.stop_command, settings.stop_command_offsets);
  450. bgproc_service *rvalps;
  451. if (create_new_record) {
  452. rvalps = new bgproc_service(this, string(name), std::move(settings.command),
  453. settings.command_offsets, settings.depends);
  454. if (reload_svc != nullptr) {
  455. check_cycle(settings.depends, reload_svc);
  456. }
  457. }
  458. else {
  459. rvalps = static_cast<bgproc_service *>(reload_svc);
  460. update_command_and_dependencies(rvalps, settings, before_deps);
  461. }
  462. rval = rvalps;
  463. // All of the following should be noexcept or must perform rollback on exception
  464. rvalps->set_stop_command(std::move(settings.stop_command), std::move(stop_arg_parts));
  465. rvalps->set_working_dir(std::move(settings.working_dir));
  466. rvalps->set_env_file(std::move(settings.env_file));
  467. #if SUPPORT_CGROUPS
  468. rvalps->set_cgroup(std::move(settings.run_in_cgroup));
  469. #endif
  470. rvalps->set_rlimits(std::move(settings.rlimits));
  471. rvalps->set_pid_file(std::move(settings.pid_file));
  472. rvalps->set_restart_interval(settings.restart_interval, settings.max_restarts);
  473. rvalps->set_restart_delay(settings.restart_delay);
  474. rvalps->set_stop_timeout(settings.stop_timeout);
  475. rvalps->set_start_timeout(settings.start_timeout);
  476. rvalps->set_extra_termination_signal(settings.term_signal);
  477. rvalps->set_run_as_uid_gid(settings.run_as_uid, settings.run_as_gid);
  478. rvalps->set_log_file(std::move(settings.logfile));
  479. rvalps->set_log_buf_max(settings.max_log_buffer_sz);
  480. rvalps->set_log_mode(settings.log_type);
  481. settings.onstart_flags.runs_on_console = false;
  482. }
  483. else if (service_type == service_type_t::SCRIPTED) {
  484. do_env_subst("command", settings.command, settings.command_offsets, settings.do_sub_vars);
  485. do_env_subst("stop-command", settings.stop_command, settings.stop_command_offsets, settings.do_sub_vars);
  486. std::vector<const char *> stop_arg_parts = separate_args(settings.stop_command, settings.stop_command_offsets);
  487. scripted_service *rvalps;
  488. if (create_new_record) {
  489. rvalps = new scripted_service(this, string(name), std::move(settings.command),
  490. settings.command_offsets, settings.depends);
  491. if (reload_svc != nullptr) {
  492. check_cycle(settings.depends, reload_svc);
  493. }
  494. }
  495. else {
  496. rvalps = static_cast<scripted_service *>(reload_svc);
  497. update_command_and_dependencies(rvalps, settings, before_deps);
  498. }
  499. rval = rvalps;
  500. // All of the following should be noexcept or must perform rollback on exception
  501. rvalps->set_stop_command(std::move(settings.stop_command), std::move(stop_arg_parts));
  502. rvalps->set_working_dir(std::move(settings.working_dir));
  503. rvalps->set_env_file(std::move(settings.env_file));
  504. #if SUPPORT_CGROUPS
  505. rvalps->set_cgroup(std::move(settings.run_in_cgroup));
  506. #endif
  507. rvalps->set_rlimits(std::move(settings.rlimits));
  508. rvalps->set_stop_timeout(settings.stop_timeout);
  509. rvalps->set_start_timeout(settings.start_timeout);
  510. rvalps->set_extra_termination_signal(settings.term_signal);
  511. rvalps->set_run_as_uid_gid(settings.run_as_uid, settings.run_as_gid);
  512. rvalps->set_log_file(std::move(settings.logfile));
  513. rvalps->set_log_buf_max(settings.max_log_buffer_sz);
  514. rvalps->set_log_mode(settings.log_type);
  515. }
  516. else {
  517. if (create_new_record) {
  518. if (service_type == service_type_t::INTERNAL) {
  519. rval = new service_record(this, string(name), service_type, settings.depends);
  520. }
  521. else {
  522. /* TRIGGERED */
  523. rval = new triggered_service(this, string(name), service_type, settings.depends);
  524. }
  525. if (reload_svc != nullptr) {
  526. check_cycle(settings.depends, reload_svc);
  527. }
  528. }
  529. else {
  530. rval = reload_svc;
  531. update_depenencies(rval, settings, before_deps);
  532. }
  533. }
  534. rval->set_auto_restart(settings.auto_restart);
  535. rval->set_smooth_recovery(settings.smooth_recovery);
  536. rval->set_flags(settings.onstart_flags);
  537. rval->set_socket_details(std::move(settings.socket_path), settings.socket_perms,
  538. settings.socket_uid, settings.socket_gid);
  539. rval->set_chain_to(std::move(settings.chain_to_name));
  540. if (create_new_record && reload_svc != nullptr) {
  541. // switch dependencies on old record so that they refer to the new record
  542. auto &dept_list = rval->get_dependents();
  543. for (auto &dept : before_deps) {
  544. dept_list.push_back(&dept);
  545. }
  546. // Add dependent-link for all dependencies. Add to the new service first, so we can rollback
  547. // on failure:
  548. int added_dep_links = 0;
  549. try {
  550. for (auto &dep : rval->get_dependencies()) {
  551. dep.get_to()->get_dependents().push_back(&dep);
  552. added_dep_links++;
  553. }
  554. }
  555. catch (...) {
  556. // exception caught; roll back any added dependencies and re-throw
  557. for (auto &dep : rval->get_dependencies()) {
  558. if (added_dep_links-- == 0) break;
  559. dep.get_to()->get_dependents().pop_back();
  560. }
  561. throw;
  562. }
  563. // --- Point of no return: mustn't fail from here ---
  564. // Remove all "before" dependents from the original service
  565. auto &reload_depts = reload_svc->get_dependents();
  566. for (auto i = reload_depts.begin(); i != reload_depts.end(); ) {
  567. auto next_i = std::next(i);
  568. if ((*i)->dep_type == dependency_type::BEFORE) {
  569. (*i)->get_from()->rm_dep(**i);
  570. }
  571. i = next_i;
  572. }
  573. // Transfer dependents from the original service record to the new record;
  574. // set links in all dependents on the original to point to the new service:
  575. auto first_new_before = dept_list.begin();
  576. dept_list.splice(first_new_before, reload_depts);
  577. for (auto &dept : dept_list) {
  578. dept->set_to(rval);
  579. }
  580. // Remove dependent-link for all dependencies from the original:
  581. reload_svc->prepare_for_unload();
  582. // Splice in the "before" dependencies
  583. auto i = before_deps.begin();
  584. decltype(i) j;
  585. while (i != before_deps.end()) {
  586. j = std::next(i);
  587. i->set_to(rval);
  588. auto &from_deps = i->get_from()->get_dependencies();
  589. from_deps.splice(from_deps.end(), before_deps, i);
  590. i = j;
  591. }
  592. }
  593. if (dummy != nullptr) {
  594. auto iter = std::find(records.begin(), records.end(), dummy);
  595. *iter = rval;
  596. delete dummy;
  597. // process before entries now. We must do it after "installing" the newly loaded service
  598. // in the service set (which we do just above) in order to avoid triggering the cycle
  599. // detection (in case the "before" service depends directly on this one) due to the dummy
  600. // service.
  601. auto ii = std::prev(rval->get_dependents().end());
  602. auto i = settings.before_svcs.begin();
  603. try {
  604. for ( ; i != settings.before_svcs.end(); ++i) {
  605. const std::string &before_ent = *i;
  606. service_record *before_svc = load_service(before_ent.c_str());
  607. before_svc->add_dep(rval, dependency_type::BEFORE);
  608. }
  609. }
  610. catch (...) {
  611. // undo if unsuccessful:
  612. for (auto j = std::next(ii); j != rval->get_dependents().end(); j = std::next(ii)) {
  613. (*j)->get_to()->rm_dep(**j);
  614. }
  615. dummy = nullptr;
  616. rval->prepare_for_unload();
  617. records.erase(std::find(records.begin(), records.end(), rval));
  618. throw;
  619. }
  620. }
  621. return rval;
  622. }
  623. catch (service_description_exc &setting_exc)
  624. {
  625. // Must remove the dummy service record.
  626. if (dummy != nullptr) {
  627. records.erase(std::find(records.begin(), records.end(), dummy));
  628. delete dummy;
  629. }
  630. if (create_new_record) delete rval;
  631. if (setting_exc.service_name.empty()) {
  632. setting_exc.service_name = name;
  633. }
  634. throw;
  635. }
  636. catch (std::system_error &sys_err)
  637. {
  638. if (dummy != nullptr) {
  639. records.erase(std::find(records.begin(), records.end(), dummy));
  640. delete dummy;
  641. }
  642. if (create_new_record) delete rval;
  643. throw service_load_exc(name, sys_err.what());
  644. }
  645. catch (...) // (should only be std::bad_alloc or service_load_exc)
  646. {
  647. if (dummy != nullptr) {
  648. records.erase(std::find(records.begin(), records.end(), dummy));
  649. delete dummy;
  650. }
  651. if (create_new_record) delete rval;
  652. throw;
  653. }
  654. }