load-service.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. #include <algorithm>
  2. #include <string>
  3. #include <fstream>
  4. #include <locale>
  5. #include <limits>
  6. #include <list>
  7. #include <cstring>
  8. #include <cstdlib>
  9. #include <sys/stat.h>
  10. #include <sys/types.h>
  11. #include <pwd.h>
  12. #include <grp.h>
  13. #include <dirent.h>
  14. #include "proc-service.h"
  15. #include "dinit-log.h"
  16. #include "dinit-util.h"
  17. #include "dinit-utmp.h"
  18. using string = std::string;
  19. using string_iterator = std::string::iterator;
  20. // Perform environment variable substitution on a command line, if specified.
  21. // line - the string storing the command and arguments
  22. // offsets - the [start,end) pair of offsets of the command and each argument within the string
  23. //
  24. static void do_env_subst(std::string &line, std::list<std::pair<unsigned,unsigned>> &offsets,
  25. bool do_sub_vars)
  26. {
  27. if (do_sub_vars) {
  28. auto i = offsets.begin();
  29. std::string r_line = line.substr(i->first, i->second - i->first); // copy command part
  30. for (++i; i != offsets.end(); ++i) {
  31. auto &offset_pair = *i;
  32. if (line[offset_pair.first] == '$') {
  33. // Do subsitution for this part:
  34. auto env_name = line.substr(offset_pair.first + 1,
  35. offset_pair.second - offset_pair.first - 1);
  36. char *env_val = getenv(env_name.c_str());
  37. if (env_val != nullptr) {
  38. auto val_len = strlen(env_val);
  39. r_line += " ";
  40. offset_pair.first = r_line.length();
  41. offset_pair.second = offset_pair.first + val_len;
  42. r_line += env_val;
  43. }
  44. else {
  45. // specified enironment variable not set: treat as an empty string
  46. offset_pair.first = r_line.length();
  47. offset_pair.second = offset_pair.first;
  48. }
  49. }
  50. else {
  51. // No subsitution for this part:
  52. r_line += " ";
  53. auto new_offs = r_line.length();
  54. auto len = offset_pair.second - offset_pair.first;
  55. r_line += line.substr(offset_pair.first, len);
  56. offset_pair.first = new_offs;
  57. offset_pair.second = new_offs + len;
  58. }
  59. }
  60. line = std::move(r_line);
  61. }
  62. }
  63. // Process a dependency directory - filenames contained within correspond to service names which
  64. // are loaded and added as a dependency of the given type. Expected use is with a directory
  65. // containing symbolic links to other service descriptions, but this isn't required.
  66. // Failure to read the directory contents, or to find a service listed within, is not considered
  67. // a fatal error.
  68. static void process_dep_dir(dirload_service_set &sset,
  69. const char *servicename,
  70. const string &service_filename,
  71. std::list<prelim_dep> &deplist, const std::string &depdirpath,
  72. dependency_type dep_type,
  73. const service_record *avoid_circular)
  74. {
  75. std::string depdir_fname = combine_paths(parent_path(service_filename), depdirpath.c_str());
  76. DIR *depdir = opendir(depdir_fname.c_str());
  77. if (depdir == nullptr) {
  78. log(loglevel_t::WARN, "Could not open dependency directory '", depdir_fname,
  79. "' for ", servicename, " service.");
  80. return;
  81. }
  82. errno = 0;
  83. dirent * dent = readdir(depdir);
  84. while (dent != nullptr) {
  85. char * name = dent->d_name;
  86. if (name[0] != '.') {
  87. try {
  88. service_record * sr = sset.load_service(name);
  89. deplist.emplace_back(sr, dep_type);
  90. }
  91. catch (service_not_found &) {
  92. log(loglevel_t::WARN, "Ignoring unresolved dependency '", name,
  93. "' in dependency directory '", depdirpath,
  94. "' for ", servicename, " service.");
  95. }
  96. }
  97. dent = readdir(depdir);
  98. }
  99. if (errno != 0) {
  100. log(loglevel_t::WARN, "Error reading dependency directory '", depdirpath,
  101. "' for ", servicename, " service.");
  102. }
  103. closedir(depdir);
  104. }
  105. service_record * dirload_service_set::load_service(const char * name, const service_record *avoid_circular)
  106. {
  107. return load_reload_service(name, nullptr, avoid_circular);
  108. }
  109. service_record * dirload_service_set::reload_service(service_record * service)
  110. {
  111. return load_reload_service(service->get_name().c_str(), service, service);
  112. }
  113. // Update the dependencies of the specified service atomically. May fail with bad_alloc.
  114. static void update_depenencies(service_record *service,
  115. dinit_load::service_settings_wrapper<prelim_dep> &settings)
  116. {
  117. std::list<service_dep> &deps = service->get_dependencies();
  118. auto first_preexisting = deps.begin();
  119. // build a set of services currently issuing acquisition
  120. std::unordered_set<service_record *> deps_with_acqs;
  121. for (auto i = deps.begin(), e = deps.end(); i != e; ++i) {
  122. if (i->holding_acq) {
  123. deps_with_acqs.insert(i->get_to());
  124. }
  125. }
  126. try {
  127. // Insert all the new dependencies before the first pre-existing dependency
  128. for (auto &new_dep : settings.depends) {
  129. bool has_acq = deps_with_acqs.count(new_dep.to);
  130. service->add_dep(new_dep.to, new_dep.dep_type, first_preexisting, has_acq);
  131. }
  132. }
  133. catch (...) {
  134. // remove the inserted dependencies
  135. for (auto i = deps.begin(); i != first_preexisting; ++i) {
  136. i = service->rm_dep(i);
  137. }
  138. // re-throw the exception
  139. throw;
  140. }
  141. // Now remove all pre-existing dependencies (no exceptions possible from here).
  142. for( ; first_preexisting != deps.end(); ) {
  143. first_preexisting = service->rm_dep(first_preexisting);
  144. }
  145. }
  146. // Update the command, and dependencies, of the specified service atomically. May fail with bad_alloc.
  147. static void update_command_and_dependencies(base_process_service *service,
  148. dinit_load::service_settings_wrapper<prelim_dep> &settings)
  149. {
  150. // Get the current command parts
  151. std::string orig_cmd; std::vector<const char *> orig_arg_parts;
  152. service->get_command(orig_cmd, orig_arg_parts);
  153. // Separate the new command parts and set
  154. std::vector<const char *> cmd_arg_parts = separate_args(settings.command, settings.command_offsets);
  155. service->set_command(std::move(settings.command), std::move(cmd_arg_parts));
  156. try {
  157. update_depenencies(service, settings);
  158. }
  159. catch (...) {
  160. // restore original command
  161. service->set_command(std::move(orig_cmd), std::move(orig_arg_parts));
  162. // re-throw the exception
  163. throw;
  164. }
  165. }
  166. service_record * dirload_service_set::load_reload_service(const char *name, service_record *reload_svc,
  167. const service_record *avoid_circular)
  168. {
  169. // For reload, we have the following problems:
  170. // - ideally want to allow changing service type, at least for stopped services. That implies creating
  171. // a new (replacement) service_record object, at least in cases where the type does change.
  172. // - dependencies may change (including addition of new dependencies which aren't yet loaded). We need
  173. // to prevent cyclic dependencies forming.
  174. // - We want atomicity. If any new settings are not valid/alterable, or if a cyclic dependency is
  175. // created, nothing should change. Ideally this would extend to unloading any dependencies which were
  176. // loaded as part of the reload attempt.
  177. // - We need to either transfer handles referring to the old service (so that they refer to the new
  178. // service), or make them invalid. Or, we alter the original service without creating a new one
  179. // (which we can only do if the type doesn't change).
  180. // Approach:
  181. // - remember the initial service count, so we can remove services loaded as part of the reload
  182. // operation if we want to abort it later (i.e. if service count changed from N to N+X, remove the
  183. // last X services)
  184. // - check that the new settings are valid (if the service is running, check if the settings can be
  185. // altered, though we may just defer some changes until service is restarted)
  186. // - check all dependencies of the newly created service record for cyclic dependencies, via depth-first
  187. // traversal.
  188. // - If changing type:
  189. // - create the service initially just as if loading a new service (but with no dummy placeholder,
  190. // use the original service for that).
  191. // - switch all dependents to depend on the new record. Copy necessary runtime data from the original
  192. // to the new service record. Remove dependencies from the old record, and release any dependency
  193. // services as appropriate (so they stop if no longer needed). Finally, remove the old service
  194. // record and delete it.
  195. // Otherwise:
  196. // - copy the new settings to the existing service
  197. // - fix dependencies
  198. //
  199. // Limitations:
  200. // - caller must check there are no handles (or only a single requesting handle) to the service before
  201. // calling
  202. // - cannot change the type of a non-stopped service
  203. using std::string;
  204. using std::ifstream;
  205. using std::ios;
  206. using std::ios_base;
  207. using std::locale;
  208. using std::isspace;
  209. using std::list;
  210. using std::pair;
  211. using namespace dinit_load;
  212. if (reload_svc == nullptr) {
  213. // First try and find an existing record...
  214. service_record * rval = find_service(string(name));
  215. if (rval != nullptr) {
  216. if (rval == avoid_circular || rval->is_dummy()) {
  217. throw service_cyclic_dependency(name);
  218. }
  219. return rval;
  220. }
  221. }
  222. service_record *rval = nullptr;
  223. service_record *dummy = nullptr;
  224. ifstream service_file;
  225. string service_filename;
  226. // Couldn't find one. Have to load it.
  227. for (auto &service_dir : service_dirs) {
  228. service_filename = service_dir.get_dir();
  229. if (*(service_filename.rbegin()) != '/') {
  230. service_filename += '/';
  231. }
  232. service_filename += name;
  233. service_file.open(service_filename.c_str(), ios::in);
  234. if (service_file) break;
  235. }
  236. if (! service_file) {
  237. throw service_not_found(string(name));
  238. }
  239. service_settings_wrapper<prelim_dep> settings;
  240. string line;
  241. // getline can set failbit if it reaches end-of-file, we don't want an exception in that case. There's
  242. // no good way to handle an I/O error however, so we'll have exceptions thrown on badbit:
  243. service_file.exceptions(ios::badbit);
  244. bool create_new_record = true;
  245. try {
  246. if (reload_svc == nullptr) {
  247. // Add a dummy service record now to prevent infinite recursion in case of cyclic dependency.
  248. // We replace this with the real service later (or remove it if we find a configuration error).
  249. dummy = new service_record(this, string(name));
  250. add_service(dummy);
  251. }
  252. process_service_file(name, service_file,
  253. [&](string &line, string &setting, string_iterator &i, string_iterator &end) -> void {
  254. auto process_dep_dir_n = [&](std::list<prelim_dep> &deplist, const std::string &waitsford,
  255. dependency_type dep_type) -> void {
  256. process_dep_dir(*this, name, service_filename, deplist, waitsford, dep_type, reload_svc);
  257. };
  258. auto load_service_n = [&](const string &dep_name) -> service_record * {
  259. return load_service(dep_name.c_str(), reload_svc);
  260. };
  261. process_service_line(settings, name, line, setting, i, end, load_service_n, process_dep_dir_n);
  262. });
  263. service_file.close();
  264. auto report_err = [&](const char *msg){
  265. throw service_description_exc(name, msg);
  266. };
  267. settings.finalise(report_err);
  268. auto service_type = settings.service_type;
  269. if (reload_svc != nullptr) {
  270. // Make sure settings are able to be changed/are compatible
  271. service_record *service = reload_svc;
  272. if (service->get_state() != service_state_t::STOPPED) {
  273. // Can not change type of a running service.
  274. if (service_type != service->get_type()) {
  275. throw service_description_exc(name, "cannot change type of non-stopped service.");
  276. }
  277. // Can not alter a starting/stopping service, at least for now.
  278. if (service->get_state() != service_state_t::STARTED) {
  279. throw service_description_exc(name,
  280. "cannot alter settings for service which is currently starting/stopping.");
  281. }
  282. // Check validity of dependencies (if started, regular deps must be started)
  283. for (auto &new_dep : settings.depends) {
  284. if (new_dep.dep_type == dependency_type::REGULAR) {
  285. if (new_dep.to->get_state() != service_state_t::STARTED) {
  286. throw service_description_exc(name,
  287. std::string("Cannot add non-started dependency '")
  288. + new_dep.to->get_name() + "'.");
  289. }
  290. }
  291. }
  292. // Cannot change certain flags
  293. auto current_flags = service->get_flags();
  294. if (current_flags.starts_on_console != settings.onstart_flags.starts_on_console
  295. || current_flags.shares_console != settings.onstart_flags.shares_console) {
  296. throw service_description_exc(name, "cannot change starts_on_console/"
  297. "shares_console flags for a running service.");
  298. }
  299. // Cannot change pid file
  300. if (service->get_type() == service_type_t::BGPROCESS) {
  301. auto *bgp_service = static_cast<bgproc_service *>(service);
  302. if (bgp_service->get_pid_file() != settings.pid_file) {
  303. throw service_description_exc(name, "cannot change pid_file for running service.");
  304. }
  305. }
  306. // Cannot change inittab_id/inittab_line
  307. #if USE_UTMPX
  308. if (service->get_type() == service_type_t::PROCESS) {
  309. auto *proc_service = static_cast<process_service *>(service);
  310. auto *svc_utmp_id = proc_service->get_utmp_id();
  311. auto *svc_utmp_ln = proc_service->get_utmp_line();
  312. if (strncmp(svc_utmp_id, settings.inittab_id, proc_service->get_utmp_id_size()) != 0
  313. || strncmp(svc_utmp_ln, settings.inittab_line,
  314. proc_service->get_utmp_line_size()) != 0) {
  315. throw service_description_exc(name, "cannot change inittab-id or inittab-line "
  316. "settings for running service.");
  317. }
  318. }
  319. #endif
  320. // Already started; we must replace settings on existing service record
  321. create_new_record = false;
  322. }
  323. }
  324. // Note, we need to be very careful to handle exceptions properly and roll back any changes that
  325. // we've made before the exception occurred.
  326. if (service_type == service_type_t::PROCESS) {
  327. do_env_subst(settings.command, settings.command_offsets, settings.do_sub_vars);
  328. process_service *rvalps;
  329. if (create_new_record) {
  330. rvalps = new process_service(this, string(name), std::move(settings.command),
  331. settings.command_offsets, settings.depends);
  332. }
  333. else {
  334. rvalps = static_cast<process_service *>(reload_svc);
  335. update_command_and_dependencies(rvalps, settings);
  336. }
  337. rval = rvalps;
  338. // All of the following should be noexcept or must perform rollback on exception
  339. rvalps->set_working_dir(std::move(settings.working_dir));
  340. rvalps->set_env_file(std::move(settings.env_file));
  341. rvalps->set_rlimits(std::move(settings.rlimits));
  342. rvalps->set_restart_interval(settings.restart_interval, settings.max_restarts);
  343. rvalps->set_restart_delay(settings.restart_delay);
  344. rvalps->set_stop_timeout(settings.stop_timeout);
  345. rvalps->set_start_timeout(settings.start_timeout);
  346. rvalps->set_extra_termination_signal(settings.term_signal);
  347. rvalps->set_run_as_uid_gid(settings.run_as_uid, settings.run_as_gid);
  348. rvalps->set_notification_fd(settings.readiness_fd);
  349. rvalps->set_notification_var(std::move(settings.readiness_var));
  350. #if USE_UTMPX
  351. rvalps->set_utmp_id(settings.inittab_id);
  352. rvalps->set_utmp_line(settings.inittab_line);
  353. #endif
  354. }
  355. else if (service_type == service_type_t::BGPROCESS) {
  356. do_env_subst(settings.command, settings.command_offsets, settings.do_sub_vars);
  357. bgproc_service *rvalps;
  358. if (create_new_record) {
  359. rvalps = new bgproc_service(this, string(name), std::move(settings.command),
  360. settings.command_offsets, settings.depends);
  361. }
  362. else {
  363. rvalps = static_cast<bgproc_service *>(reload_svc);
  364. update_command_and_dependencies(rvalps, settings);
  365. }
  366. rval = rvalps;
  367. // All of the following should be noexcept or must perform rollback on exception
  368. rvalps->set_working_dir(std::move(settings.working_dir));
  369. rvalps->set_env_file(std::move(settings.env_file));
  370. rvalps->set_rlimits(std::move(settings.rlimits));
  371. rvalps->set_pid_file(std::move(settings.pid_file));
  372. rvalps->set_restart_interval(settings.restart_interval, settings.max_restarts);
  373. rvalps->set_restart_delay(settings.restart_delay);
  374. rvalps->set_stop_timeout(settings.stop_timeout);
  375. rvalps->set_start_timeout(settings.start_timeout);
  376. rvalps->set_extra_termination_signal(settings.term_signal);
  377. rvalps->set_run_as_uid_gid(settings.run_as_uid, settings.run_as_gid);
  378. settings.onstart_flags.runs_on_console = false;
  379. }
  380. else if (service_type == service_type_t::SCRIPTED) {
  381. do_env_subst(settings.command, settings.command_offsets, settings.do_sub_vars);
  382. std::vector<const char *> stop_arg_parts = separate_args(settings.stop_command, settings.stop_command_offsets);
  383. scripted_service *rvalps;
  384. if (create_new_record) {
  385. rvalps = new scripted_service(this, string(name), std::move(settings.command),
  386. settings.command_offsets, settings.depends);
  387. }
  388. else {
  389. rvalps = static_cast<scripted_service *>(reload_svc);
  390. update_command_and_dependencies(rvalps, settings);
  391. }
  392. rval = rvalps;
  393. // All of the following should be noexcept or must perform rollback on exception
  394. rvalps->set_stop_command(std::move(settings.stop_command), std::move(stop_arg_parts));
  395. rvalps->set_working_dir(std::move(settings.working_dir));
  396. rvalps->set_env_file(std::move(settings.env_file));
  397. rvalps->set_rlimits(std::move(settings.rlimits));
  398. rvalps->set_stop_timeout(settings.stop_timeout);
  399. rvalps->set_start_timeout(settings.start_timeout);
  400. rvalps->set_extra_termination_signal(settings.term_signal);
  401. rvalps->set_run_as_uid_gid(settings.run_as_uid, settings.run_as_gid);
  402. }
  403. else {
  404. if (create_new_record) {
  405. rval = new service_record(this, string(name), service_type, settings.depends);
  406. }
  407. else {
  408. rval = reload_svc;
  409. update_depenencies(rval, settings);
  410. }
  411. }
  412. rval->set_log_file(std::move(settings.logfile));
  413. rval->set_auto_restart(settings.auto_restart);
  414. rval->set_smooth_recovery(settings.smooth_recovery);
  415. rval->set_flags(settings.onstart_flags);
  416. rval->set_socket_details(std::move(settings.socket_path), settings.socket_perms,
  417. settings.socket_uid, settings.socket_gid);
  418. rval->set_chain_to(std::move(settings.chain_to_name));
  419. if (create_new_record && reload_svc != nullptr) {
  420. // switch dependencies to old record so that they refer to the new record
  421. // Add dependent-link for all dependencies. Add to the new service first, so we can rollback
  422. // on failure:
  423. int added_dep_links = 0;
  424. try {
  425. for (auto &dep : rval->get_dependencies()) {
  426. dep.get_to()->get_dependents().push_back(&dep);
  427. added_dep_links++;
  428. }
  429. }
  430. catch (...) {
  431. for (auto &dep : rval->get_dependencies()) {
  432. if (added_dep_links-- == 0) break;
  433. dep.get_to()->get_dependents().pop_back();
  434. }
  435. throw;
  436. }
  437. // Remove dependent-link for all dependencies from the original:
  438. reload_svc->prepare_for_unload();
  439. // Set links in all dependents to the original to point to the new service:
  440. rval->get_dependents() = std::move(reload_svc->get_dependents());
  441. for (auto n : rval->get_dependents()) {
  442. n->set_to(rval);
  443. }
  444. }
  445. if (dummy != nullptr) {
  446. auto iter = std::find(records.begin(), records.end(), dummy);
  447. *iter = rval;
  448. delete dummy;
  449. }
  450. return rval;
  451. }
  452. catch (setting_exception &setting_exc)
  453. {
  454. // Must remove the dummy service record.
  455. if (dummy != nullptr) {
  456. records.erase(std::find(records.begin(), records.end(), dummy));
  457. delete dummy;
  458. }
  459. if (create_new_record) delete rval;
  460. throw service_description_exc(name, std::move(setting_exc.get_info()));
  461. }
  462. catch (std::system_error &sys_err)
  463. {
  464. if (dummy != nullptr) {
  465. records.erase(std::find(records.begin(), records.end(), dummy));
  466. delete dummy;
  467. }
  468. if (create_new_record) delete rval;
  469. throw service_description_exc(name, sys_err.what());
  470. }
  471. catch (...) // (should only be std::bad_alloc / service_description_exc)
  472. {
  473. if (dummy != nullptr) {
  474. records.erase(std::find(records.begin(), records.end(), dummy));
  475. delete dummy;
  476. }
  477. if (create_new_record) delete rval;
  478. throw;
  479. }
  480. }