load-service.h 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. #include <iostream>
  2. #include <list>
  3. #include <limits>
  4. #include <csignal>
  5. #include <cstring>
  6. #include <utility>
  7. #include <sys/types.h>
  8. #include <sys/time.h>
  9. #include <sys/resource.h>
  10. #include <grp.h>
  11. #include <pwd.h>
  12. #include "dinit-utmp.h"
  13. #include "dinit-util.h"
  14. struct service_flags_t
  15. {
  16. // on-start flags:
  17. bool rw_ready : 1; // file system should be writable once this service starts
  18. bool log_ready : 1; // syslog should be available once this service starts
  19. // Other service options flags:
  20. bool no_sigterm : 1; // do not send SIGTERM
  21. bool runs_on_console : 1; // run "in the foreground"
  22. bool starts_on_console : 1; // starts in the foreground
  23. bool shares_console : 1; // run on console, but not exclusively
  24. bool pass_cs_fd : 1; // pass this service a control socket connection via fd
  25. bool start_interruptible : 1; // the startup of this service process is ok to interrupt with SIGINT
  26. bool skippable : 1; // if interrupted the service is skipped (scripted services)
  27. bool signal_process_only : 1; // signal the session process, not the whole group
  28. service_flags_t() noexcept : rw_ready(false), log_ready(false), no_sigterm(false),
  29. runs_on_console(false), starts_on_console(false), shares_console(false),
  30. pass_cs_fd(false), start_interruptible(false), skippable(false), signal_process_only(false)
  31. {
  32. }
  33. };
  34. // Resource limits for a particular service & particular resource
  35. struct service_rlimits
  36. {
  37. int resource_id; // RLIMIT_xxx identifying resource
  38. bool soft_set : 1;
  39. bool hard_set : 1;
  40. struct rlimit limits;
  41. service_rlimits(int id) : resource_id(id), soft_set(0), hard_set(0), limits({0,0}) { }
  42. };
  43. // Exception while loading a service
  44. class service_load_exc
  45. {
  46. public:
  47. std::string service_name;
  48. std::string exc_description;
  49. protected:
  50. service_load_exc(const std::string &serviceName, std::string &&desc) noexcept
  51. : service_name(serviceName), exc_description(std::move(desc))
  52. {
  53. }
  54. };
  55. class service_not_found : public service_load_exc
  56. {
  57. public:
  58. service_not_found(const std::string &serviceName) noexcept
  59. : service_load_exc(serviceName, "Service description not found.")
  60. {
  61. }
  62. };
  63. class service_cyclic_dependency : public service_load_exc
  64. {
  65. public:
  66. service_cyclic_dependency(const std::string &serviceName) noexcept
  67. : service_load_exc(serviceName, "Has cyclic dependency.")
  68. {
  69. }
  70. };
  71. class service_description_exc : public service_load_exc
  72. {
  73. public:
  74. service_description_exc(const std::string &serviceName, std::string &&extraInfo) noexcept
  75. : service_load_exc(serviceName, std::move(extraInfo))
  76. {
  77. }
  78. };
  79. namespace dinit_load {
  80. using string = std::string;
  81. using string_iterator = std::string::iterator;
  82. // exception thrown when encountering a syntax issue when reading a setting value
  83. class setting_exception
  84. {
  85. std::string info;
  86. public:
  87. setting_exception(const std::string &&exc_info) : info(std::move(exc_info))
  88. {
  89. }
  90. std::string &get_info()
  91. {
  92. return info;
  93. }
  94. };
  95. // Utility function to skip white space. Returns an iterator at the
  96. // first non-white-space position (or at end).
  97. inline string_iterator skipws(string_iterator i, string_iterator end)
  98. {
  99. using std::locale;
  100. using std::isspace;
  101. while (i != end) {
  102. if (! isspace(*i, locale::classic())) {
  103. break;
  104. }
  105. ++i;
  106. }
  107. return i;
  108. }
  109. // Convert a signal name to the corresponding signal number
  110. inline int signal_name_to_number(std::string &signame)
  111. {
  112. if (signame == "HUP") return SIGHUP;
  113. if (signame == "INT") return SIGINT;
  114. if (signame == "QUIT") return SIGQUIT;
  115. if (signame == "USR1") return SIGUSR1;
  116. if (signame == "USR2") return SIGUSR2;
  117. if (signame == "KILL") return SIGKILL;
  118. return -1;
  119. }
  120. // Read a setting name.
  121. inline string read_setting_name(string_iterator & i, string_iterator end)
  122. {
  123. using std::locale;
  124. using std::ctype;
  125. using std::use_facet;
  126. const ctype<char> & facet = use_facet<ctype<char> >(locale::classic());
  127. string rval;
  128. // Allow alphabetical characters, and dash (-) in setting name
  129. while (i != end && (*i == '-' || *i == '.' || facet.is(ctype<char>::alpha, *i))) {
  130. rval += *i;
  131. ++i;
  132. }
  133. return rval;
  134. }
  135. // Read a setting value.
  136. //
  137. // In general a setting value is a single-line string. It may contain multiple parts
  138. // separated by white space (which is normally collapsed). A hash mark - # - denotes
  139. // the end of the value and the beginning of a comment (it should be preceded by
  140. // whitespace).
  141. //
  142. // Part of a value may be quoted using double quote marks, which prevents collapse
  143. // of whitespace and interpretation of most special characters (the quote marks will
  144. // not be considered part of the value). A backslash can precede a character (such
  145. // as '#' or '"' or another backslash) to remove its special meaning. Newline
  146. // characters are not allowed in values and cannot be quoted.
  147. //
  148. // This function expects the string to be in an ASCII-compatible encoding (the "classic" locale).
  149. //
  150. // Throws setting_exception on error.
  151. //
  152. // Params:
  153. // service_name - the name of the service to which the setting applies
  154. // i - reference to string iterator through the line
  155. // end - iterator at end of line (not including newline character if any)
  156. // part_positions - list of <int,int> to which the position of each setting value
  157. // part will be added as [start,end). May be null.
  158. inline string read_setting_value(string_iterator & i, string_iterator end,
  159. std::list<std::pair<unsigned,unsigned>> * part_positions = nullptr)
  160. {
  161. using std::locale;
  162. using std::isspace;
  163. i = skipws(i, end);
  164. string rval;
  165. bool new_part = true;
  166. int part_start;
  167. while (i != end) {
  168. char c = *i;
  169. if (c == '\"') {
  170. if (new_part) {
  171. part_start = rval.length();
  172. new_part = false;
  173. }
  174. // quoted string
  175. ++i;
  176. while (i != end) {
  177. c = *i;
  178. if (c == '\"') break;
  179. else if (c == '\\') {
  180. // A backslash escapes the following character.
  181. ++i;
  182. if (i != end) {
  183. c = *i;
  184. rval += c;
  185. }
  186. else {
  187. throw setting_exception("Line end follows backslash escape character (`\\')");
  188. }
  189. }
  190. else {
  191. rval += c;
  192. }
  193. ++i;
  194. }
  195. if (i == end) {
  196. // String wasn't terminated
  197. throw setting_exception("Unterminated quoted string");
  198. }
  199. }
  200. else if (c == '\\') {
  201. if (new_part) {
  202. part_start = rval.length();
  203. new_part = false;
  204. }
  205. // A backslash escapes the next character
  206. ++i;
  207. if (i != end) {
  208. rval += *i;
  209. }
  210. else {
  211. throw setting_exception("Backslash escape (`\\') not followed by character");
  212. }
  213. }
  214. else if (isspace(c, locale::classic())) {
  215. if (! new_part && part_positions != nullptr) {
  216. part_positions->emplace_back(part_start, rval.length());
  217. new_part = true;
  218. }
  219. i = skipws(i, end);
  220. if (i == end) break;
  221. if (*i == '#') break; // comment
  222. rval += ' '; // collapse ws to a single space
  223. continue;
  224. }
  225. else if (c == '#') {
  226. // Possibly intended a comment; we require leading whitespace to reduce occurrence of accidental
  227. // comments in setting values.
  228. throw setting_exception("hashmark (`#') comment must be separated from setting value by whitespace");
  229. }
  230. else {
  231. if (new_part) {
  232. part_start = rval.length();
  233. new_part = false;
  234. }
  235. rval += c;
  236. }
  237. ++i;
  238. }
  239. // Got to end:
  240. if (part_positions != nullptr) {
  241. part_positions->emplace_back(part_start, rval.length());
  242. }
  243. return rval;
  244. }
  245. // Parse a userid parameter which may be a numeric user ID or a username. If a name, the
  246. // userid is looked up via the system user database (getpwnam() function). In this case,
  247. // the associated group is stored in the location specified by the group_p parameter if
  248. // it is not null.
  249. inline uid_t parse_uid_param(const std::string &param, const std::string &service_name, const char *setting_name, gid_t *group_p)
  250. {
  251. const char * uid_err_msg = "Specified user id contains invalid numeric characters "
  252. "or is outside allowed range.";
  253. // Could be a name or a numeric id. But we should assume numeric first, just in case
  254. // a user manages to give themselves a username that parses as a number.
  255. std::size_t ind = 0;
  256. try {
  257. // POSIX does not specify whether uid_t is a signed or unsigned type, but regardless
  258. // is is probably safe to assume that valid values are positive. We'll also assert
  259. // that the value range fits within "unsigned long long" since it seems unlikely
  260. // that would ever not be the case.
  261. static_assert((uintmax_t)std::numeric_limits<uid_t>::max()
  262. <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "uid_t is too large");
  263. unsigned long long v = std::stoull(param, &ind, 0);
  264. if (v > static_cast<unsigned long long>(std::numeric_limits<uid_t>::max())
  265. || ind != param.length()) {
  266. throw service_description_exc(service_name, std::string(setting_name) + ": " + uid_err_msg);
  267. }
  268. return v;
  269. }
  270. catch (std::out_of_range &exc) {
  271. throw service_description_exc(service_name, uid_err_msg);
  272. }
  273. catch (std::invalid_argument &exc) {
  274. // Ok, so it doesn't look like a number: proceed...
  275. }
  276. errno = 0;
  277. struct passwd * pwent = getpwnam(param.c_str());
  278. if (pwent == nullptr) {
  279. // Maybe an error, maybe just no entry.
  280. if (errno == 0) {
  281. throw service_description_exc(service_name, std::string(setting_name) + ": Specified user \"" + param
  282. + "\" does not exist in system database.");
  283. }
  284. else {
  285. throw service_description_exc(service_name, std::string("Error accessing user database: ")
  286. + strerror(errno));
  287. }
  288. }
  289. if (group_p) {
  290. *group_p = pwent->pw_gid;
  291. }
  292. return pwent->pw_uid;
  293. }
  294. inline gid_t parse_gid_param(const std::string &param, const char *setting_name, const std::string &service_name)
  295. {
  296. const char * gid_err_msg = "Specified group id contains invalid numeric characters or is "
  297. "outside allowed range.";
  298. // Could be a name or a numeric id. But we should assume numeric first, just in case
  299. // a user manages to give themselves a username that parses as a number.
  300. std::size_t ind = 0;
  301. try {
  302. // POSIX does not specify whether uid_t is an signed or unsigned, but regardless
  303. // is is probably safe to assume that valid values are positive. We'll also assume
  304. // that the value range fits with "unsigned long long" since it seems unlikely
  305. // that would ever not be the case.
  306. static_assert((uintmax_t)std::numeric_limits<gid_t>::max()
  307. <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "gid_t is too large");
  308. unsigned long long v = std::stoull(param, &ind, 0);
  309. if (v > static_cast<unsigned long long>(std::numeric_limits<gid_t>::max())
  310. || ind != param.length()) {
  311. throw service_description_exc(service_name, std::string(setting_name) + ": " + gid_err_msg);
  312. }
  313. return v;
  314. }
  315. catch (std::out_of_range &exc) {
  316. throw service_description_exc(service_name, std::string(setting_name) + ": " + gid_err_msg);
  317. }
  318. catch (std::invalid_argument &exc) {
  319. // Ok, so it doesn't look like a number: proceed...
  320. }
  321. errno = 0;
  322. struct group * grent = getgrnam(param.c_str());
  323. if (grent == nullptr) {
  324. // Maybe an error, maybe just no entry.
  325. if (errno == 0) {
  326. throw service_description_exc(service_name, std::string(setting_name) + ": Specified group \"" + param
  327. + "\" does not exist in system database.");
  328. }
  329. else {
  330. throw service_description_exc(service_name, std::string("Error accessing group database: ")
  331. + strerror(errno));
  332. }
  333. }
  334. return grent->gr_gid;
  335. }
  336. // Parse a time, specified as a decimal number of seconds (with optional fractional component after decimal
  337. // point or decimal comma).
  338. inline void parse_timespec(const std::string &paramval, const std::string &servicename,
  339. const char * paramname, timespec &ts)
  340. {
  341. decltype(ts.tv_sec) isec = 0;
  342. decltype(ts.tv_nsec) insec = 0;
  343. auto max_secs = std::numeric_limits<decltype(isec)>::max() / 10;
  344. auto len = paramval.length();
  345. decltype(len) i;
  346. for (i = 0; i < len; i++) {
  347. char ch = paramval[i];
  348. if (ch == '.' || ch == ',') {
  349. i++;
  350. break;
  351. }
  352. if (ch < '0' || ch > '9') {
  353. throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
  354. }
  355. // check for overflow
  356. if (isec >= max_secs) {
  357. throw service_description_exc(servicename, std::string("Too-large value for ") + paramname);
  358. }
  359. isec *= 10;
  360. isec += ch - '0';
  361. }
  362. decltype(insec) insec_m = 100000000; // 10^8
  363. for ( ; i < len; i++) {
  364. char ch = paramval[i];
  365. if (ch < '0' || ch > '9') {
  366. throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
  367. }
  368. insec += (ch - '0') * insec_m;
  369. insec_m /= 10;
  370. }
  371. ts.tv_sec = isec;
  372. ts.tv_nsec = insec;
  373. }
  374. // Parse an unsigned numeric parameter value
  375. inline unsigned long long parse_unum_param(const std::string &param, const std::string &service_name,
  376. unsigned long long max = std::numeric_limits<unsigned long long>::max())
  377. {
  378. const char * num_err_msg = "Specified value contains invalid numeric characters or is outside "
  379. "allowed range.";
  380. std::size_t ind = 0;
  381. try {
  382. unsigned long long v = std::stoull(param, &ind, 0);
  383. if (v > max || ind != param.length()) {
  384. throw service_description_exc(service_name, num_err_msg);
  385. }
  386. return v;
  387. }
  388. catch (std::out_of_range &exc) {
  389. throw service_description_exc(service_name, num_err_msg);
  390. }
  391. catch (std::invalid_argument &exc) {
  392. throw service_description_exc(service_name, num_err_msg);
  393. }
  394. }
  395. // In a vector, find or create rlimits for a particular resource type.
  396. inline service_rlimits &find_rlimits(std::vector<service_rlimits> &all_rlimits, int resource_id)
  397. {
  398. for (service_rlimits &limits : all_rlimits) {
  399. if (limits.resource_id == resource_id) {
  400. return limits;
  401. }
  402. }
  403. all_rlimits.emplace_back(resource_id);
  404. return all_rlimits.back();
  405. }
  406. // Parse resource limits setting (can specify both hard and soft limit).
  407. inline void parse_rlimit(const std::string &line, const std::string &service_name, const char *param_name,
  408. service_rlimits &rlimit)
  409. {
  410. // Examples:
  411. // 4:5 - soft:hard limits both set
  412. // 4:- soft set, hard set to unlimited
  413. // 4: soft set, hard limit unchanged
  414. // 4 soft and hard limit set to same limit
  415. if (line.empty()) {
  416. throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
  417. }
  418. const char *cline = line.c_str();
  419. rlimit.hard_set = rlimit.soft_set = false;
  420. try {
  421. const char * index = cline;
  422. errno = 0;
  423. if (cline[0] != ':') {
  424. rlimit.soft_set = true;
  425. if (cline[0] == '-') {
  426. rlimit.limits.rlim_cur = RLIM_INFINITY;
  427. index = cline + 1;
  428. }
  429. else {
  430. char *nindex;
  431. unsigned long long limit = std::strtoull(cline, &nindex, 0);
  432. index = nindex;
  433. if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
  434. if (index == cline) throw std::invalid_argument("");
  435. rlimit.limits.rlim_cur = limit;
  436. }
  437. if (*index == 0) {
  438. rlimit.hard_set = true;
  439. rlimit.limits.rlim_max = rlimit.limits.rlim_cur;
  440. return;
  441. }
  442. if (*index != ':') {
  443. throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
  444. }
  445. }
  446. index++;
  447. if (*index == 0) return;
  448. if (*index == '-') {
  449. rlimit.limits.rlim_max = RLIM_INFINITY;
  450. if (index[1] != 0) {
  451. throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
  452. }
  453. }
  454. else {
  455. const char *hard_start = index;
  456. char *nindex;
  457. unsigned long long limit = std::strtoull(cline, &nindex, 0);
  458. index = nindex;
  459. if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
  460. if (index == hard_start) throw std::invalid_argument("");
  461. rlimit.limits.rlim_max = limit;
  462. }
  463. }
  464. catch (std::invalid_argument &exc) {
  465. throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
  466. }
  467. catch (std::out_of_range &exc) {
  468. throw service_description_exc(service_name, std::string(param_name) + ": Too-large value.");
  469. }
  470. }
  471. // Process an opened service file, line by line.
  472. // name - the service name
  473. // service_file - the service file input stream
  474. // func - a function of the form:
  475. // void(string &line, string &setting, string_iterator i, string_iterator end)
  476. // Called with:
  477. // line - the complete line (excluding newline character)
  478. // setting - the setting name, from the beginning of the line
  479. // i - iterator at the beginning of the setting value
  480. // end - iterator marking the end of the line
  481. //
  482. // May throw service load exceptions or I/O exceptions if enabled on stream.
  483. template <typename T>
  484. void process_service_file(string name, std::istream &service_file, T func)
  485. {
  486. string line;
  487. while (getline(service_file, line)) {
  488. string::iterator i = line.begin();
  489. string::iterator end = line.end();
  490. i = skipws(i, end);
  491. if (i != end) {
  492. if (*i == '#') {
  493. continue; // comment line
  494. }
  495. string setting = read_setting_name(i, end);
  496. i = skipws(i, end);
  497. if (i == end || (*i != '=' && *i != ':')) {
  498. throw service_description_exc(name, "Badly formed line.");
  499. }
  500. i = skipws(++i, end);
  501. func(line, setting, i, end);
  502. }
  503. }
  504. }
  505. // A wrapper type for service parameters. It is parameterised by dependency type.
  506. template <class dep_type>
  507. class service_settings_wrapper
  508. {
  509. template <typename A, typename B> using pair = std::pair<A,B>;
  510. template <typename A> using list = std::list<A>;
  511. public:
  512. string command;
  513. list<pair<unsigned,unsigned>> command_offsets;
  514. string stop_command;
  515. list<pair<unsigned,unsigned>> stop_command_offsets;
  516. string working_dir;
  517. string pid_file;
  518. string env_file;
  519. bool do_sub_vars = false;
  520. service_type_t service_type = service_type_t::PROCESS;
  521. std::list<dep_type> depends;
  522. string logfile;
  523. service_flags_t onstart_flags;
  524. int term_signal = -1; // additional termination signal
  525. bool auto_restart = false;
  526. bool smooth_recovery = false;
  527. string socket_path;
  528. int socket_perms = 0666;
  529. // Note: Posix allows that uid_t and gid_t may be unsigned types, but eg chown uses -1 as an
  530. // invalid value, so it's safe to assume that we can do the same:
  531. uid_t socket_uid = -1;
  532. gid_t socket_uid_gid = -1; // primary group of socket user if known
  533. gid_t socket_gid = -1;
  534. // Restart limit interval / count; default is 10 seconds, 3 restarts:
  535. timespec restart_interval = { .tv_sec = 10, .tv_nsec = 0 };
  536. int max_restarts = 3;
  537. timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
  538. timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
  539. timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
  540. std::vector<service_rlimits> rlimits;
  541. int readiness_fd = -1; // readiness fd in service process
  542. std::string readiness_var; // environment var to hold readiness fd
  543. uid_t run_as_uid = -1;
  544. gid_t run_as_uid_gid = -1; // primary group of "run as" uid if known
  545. gid_t run_as_gid = -1;
  546. string chain_to_name;
  547. #if USE_UTMPX
  548. char inittab_id[sizeof(utmpx().ut_id)] = {0};
  549. char inittab_line[sizeof(utmpx().ut_line)] = {0};
  550. #endif
  551. // Finalise settings (after processing all setting lines)
  552. void finalise()
  553. {
  554. // If socket_gid hasn't been explicitly set, but the socket_uid was specified as a name (and
  555. // we therefore recovered the primary group), use the primary group of the specified user.
  556. if (socket_gid == (gid_t)-1) socket_gid = socket_uid_gid;
  557. // likewise for "run as" gid/uid.
  558. if (run_as_gid == (gid_t)-1) run_as_gid = run_as_uid_gid;
  559. }
  560. };
  561. // Process a service description line. In general, parse the setting value and record the parsed value
  562. // in a service settings wrapper object. Errors will be reported via service_description_exc exception.
  563. //
  564. // type parameters:
  565. // settings_wrapper : wrapper for service settings
  566. // load_service_t : type of load service function/lambda (see below)
  567. // process_dep_dir_t : type of process_dep_dir funciton/lambda (see below)
  568. //
  569. // parameters:
  570. // settings : wrapper object for service settings
  571. // name : name of the service being processed
  572. // line : the current line of the service description file
  573. // setting : the name of the setting (from the beginning of line)
  574. // i : iterator at beginning of setting value (including whitespace)
  575. // end : iterator at end of line
  576. // load_service : function to load a service
  577. // arguments: const char *service_name
  578. // return: a value that can be used (with a dependency type) to construct a dependency
  579. // in the 'depends' vector within the 'settings' object
  580. // process_dep_dir : function to process a dependency directory
  581. // arguments: decltype(settings.depends) &dependencies
  582. // const string &waitsford - directory as specified in parameter
  583. // dependency_type dep_type - type of dependency to add
  584. template <typename settings_wrapper,
  585. typename load_service_t,
  586. typename process_dep_dir_t>
  587. void process_service_line(settings_wrapper &settings, const char *name, string &line, string &setting,
  588. string::iterator &i, string::iterator &end, load_service_t load_service,
  589. process_dep_dir_t process_dep_dir)
  590. {
  591. if (setting == "command") {
  592. settings.command = read_setting_value(i, end, &settings.command_offsets);
  593. }
  594. else if (setting == "working-dir") {
  595. settings.working_dir = read_setting_value(i, end, nullptr);
  596. }
  597. else if (setting == "env-file") {
  598. settings.env_file = read_setting_value(i, end, nullptr);
  599. }
  600. else if (setting == "socket-listen") {
  601. settings.socket_path = read_setting_value(i, end, nullptr);
  602. }
  603. else if (setting == "socket-permissions") {
  604. string sock_perm_str = read_setting_value(i, end, nullptr);
  605. std::size_t ind = 0;
  606. try {
  607. settings.socket_perms = std::stoi(sock_perm_str, &ind, 8);
  608. if (ind != sock_perm_str.length()) {
  609. throw std::logic_error("");
  610. }
  611. }
  612. catch (std::logic_error &exc) {
  613. throw service_description_exc(name, "socket-permissions: Badly-formed or "
  614. "out-of-range numeric value");
  615. }
  616. }
  617. else if (setting == "socket-uid") {
  618. string sock_uid_s = read_setting_value(i, end, nullptr);
  619. settings.socket_uid = parse_uid_param(sock_uid_s, name, "socket-uid", &settings.socket_uid_gid);
  620. }
  621. else if (setting == "socket-gid") {
  622. string sock_gid_s = read_setting_value(i, end, nullptr);
  623. settings.socket_gid = parse_gid_param(sock_gid_s, "socket-gid", name);
  624. }
  625. else if (setting == "stop-command") {
  626. settings.stop_command = read_setting_value(i, end, &settings.stop_command_offsets);
  627. }
  628. else if (setting == "pid-file") {
  629. settings.pid_file = read_setting_value(i, end);
  630. }
  631. else if (setting == "depends-on") {
  632. string dependency_name = read_setting_value(i, end);
  633. settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::REGULAR);
  634. }
  635. else if (setting == "depends-ms") {
  636. string dependency_name = read_setting_value(i, end);
  637. settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::MILESTONE);
  638. }
  639. else if (setting == "waits-for") {
  640. string dependency_name = read_setting_value(i, end);
  641. settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::WAITS_FOR);
  642. }
  643. else if (setting == "waits-for.d") {
  644. string waitsford = read_setting_value(i, end);
  645. process_dep_dir(settings.depends, waitsford, dependency_type::WAITS_FOR);
  646. }
  647. else if (setting == "logfile") {
  648. settings.logfile = read_setting_value(i, end);
  649. }
  650. else if (setting == "restart") {
  651. string restart = read_setting_value(i, end);
  652. settings.auto_restart = (restart == "yes" || restart == "true");
  653. }
  654. else if (setting == "smooth-recovery") {
  655. string recovery = read_setting_value(i, end);
  656. settings.smooth_recovery = (recovery == "yes" || recovery == "true");
  657. }
  658. else if (setting == "type") {
  659. string type_str = read_setting_value(i, end);
  660. if (type_str == "scripted") {
  661. settings.service_type = service_type_t::SCRIPTED;
  662. }
  663. else if (type_str == "process") {
  664. settings.service_type = service_type_t::PROCESS;
  665. }
  666. else if (type_str == "bgprocess") {
  667. settings.service_type = service_type_t::BGPROCESS;
  668. }
  669. else if (type_str == "internal") {
  670. settings.service_type = service_type_t::INTERNAL;
  671. }
  672. else {
  673. throw service_description_exc(name, "Service type must be one of: \"scripted\","
  674. " \"process\", \"bgprocess\" or \"internal\"");
  675. }
  676. }
  677. else if (setting == "options") {
  678. std::list<std::pair<unsigned,unsigned>> indices;
  679. string onstart_cmds = read_setting_value(i, end, &indices);
  680. for (auto indexpair : indices) {
  681. string option_txt = onstart_cmds.substr(indexpair.first,
  682. indexpair.second - indexpair.first);
  683. if (option_txt == "starts-rwfs") {
  684. settings.onstart_flags.rw_ready = true;
  685. }
  686. else if (option_txt == "starts-log") {
  687. settings.onstart_flags.log_ready = true;
  688. }
  689. else if (option_txt == "no-sigterm") {
  690. settings.onstart_flags.no_sigterm = true;
  691. }
  692. else if (option_txt == "runs-on-console") {
  693. settings.onstart_flags.runs_on_console = true;
  694. // A service that runs on the console necessarily starts on console:
  695. settings.onstart_flags.starts_on_console = true;
  696. settings.onstart_flags.shares_console = false;
  697. }
  698. else if (option_txt == "starts-on-console") {
  699. settings.onstart_flags.starts_on_console = true;
  700. settings.onstart_flags.shares_console = false;
  701. }
  702. else if (option_txt == "shares-console") {
  703. settings.onstart_flags.shares_console = true;
  704. settings.onstart_flags.runs_on_console = false;
  705. settings.onstart_flags.starts_on_console = false;
  706. }
  707. else if (option_txt == "pass-cs-fd") {
  708. settings.onstart_flags.pass_cs_fd = true;
  709. }
  710. else if (option_txt == "start-interruptible") {
  711. settings.onstart_flags.start_interruptible = true;
  712. }
  713. else if (option_txt == "skippable") {
  714. settings.onstart_flags.skippable = true;
  715. }
  716. else if (option_txt == "signal-process-only") {
  717. settings.onstart_flags.signal_process_only = true;
  718. }
  719. else {
  720. throw service_description_exc(name, "Unknown option: " + option_txt);
  721. }
  722. }
  723. }
  724. else if (setting == "load-options") {
  725. std::list<std::pair<unsigned,unsigned>> indices;
  726. string load_opts = read_setting_value(i, end, &indices);
  727. for (auto indexpair : indices) {
  728. string option_txt = load_opts.substr(indexpair.first,
  729. indexpair.second - indexpair.first);
  730. if (option_txt == "sub-vars") {
  731. // substitute environment variables in command line
  732. settings.do_sub_vars = true;
  733. }
  734. else if (option_txt == "no-sub-vars") {
  735. settings.do_sub_vars = false;
  736. }
  737. else {
  738. throw service_description_exc(name, "Unknown load option: " + option_txt);
  739. }
  740. }
  741. }
  742. else if (setting == "term-signal" || setting == "termsignal") {
  743. // Note: "termsignal" supported for legacy reasons.
  744. string signame = read_setting_value(i, end, nullptr);
  745. int signo = signal_name_to_number(signame);
  746. if (signo == -1) {
  747. throw service_description_exc(name, "Unknown/unsupported termination signal: "
  748. + signame);
  749. }
  750. else {
  751. settings.term_signal = signo;
  752. }
  753. }
  754. else if (setting == "restart-limit-interval") {
  755. string interval_str = read_setting_value(i, end, nullptr);
  756. parse_timespec(interval_str, name, "restart-limit-interval", settings.restart_interval);
  757. }
  758. else if (setting == "restart-delay") {
  759. string rsdelay_str = read_setting_value(i, end, nullptr);
  760. parse_timespec(rsdelay_str, name, "restart-delay", settings.restart_delay);
  761. }
  762. else if (setting == "restart-limit-count") {
  763. string limit_str = read_setting_value(i, end, nullptr);
  764. settings.max_restarts = parse_unum_param(limit_str, name, std::numeric_limits<int>::max());
  765. }
  766. else if (setting == "stop-timeout") {
  767. string stoptimeout_str = read_setting_value(i, end, nullptr);
  768. parse_timespec(stoptimeout_str, name, "stop-timeout", settings.stop_timeout);
  769. }
  770. else if (setting == "start-timeout") {
  771. string starttimeout_str = read_setting_value(i, end, nullptr);
  772. parse_timespec(starttimeout_str, name, "start-timeout", settings.start_timeout);
  773. }
  774. else if (setting == "run-as") {
  775. string run_as_str = read_setting_value(i, end, nullptr);
  776. settings.run_as_uid = parse_uid_param(run_as_str, name, "run-as", &settings.run_as_uid_gid);
  777. }
  778. else if (setting == "chain-to") {
  779. settings.chain_to_name = read_setting_value(i, end, nullptr);
  780. }
  781. else if (setting == "ready-notification") {
  782. string notify_setting = read_setting_value(i, end, nullptr);
  783. if (starts_with(notify_setting, "pipefd:")) {
  784. settings.readiness_fd = parse_unum_param(notify_setting.substr(7 /* len 'pipefd:' */),
  785. name, std::numeric_limits<int>::max());
  786. }
  787. else if (starts_with(notify_setting, "pipevar:")) {
  788. settings.readiness_var = notify_setting.substr(8 /* len 'pipevar:' */);
  789. if (settings.readiness_var.empty()) {
  790. throw service_description_exc(name, "Invalid pipevar variable name "
  791. "in ready-notification");
  792. }
  793. }
  794. else {
  795. throw service_description_exc(name, "Unknown ready-notification setting: "
  796. + notify_setting);
  797. }
  798. }
  799. else if (setting == "inittab-id") {
  800. string inittab_setting = read_setting_value(i, end, nullptr);
  801. #if USE_UTMPX
  802. if (inittab_setting.length() > sizeof(settings.inittab_id)) {
  803. throw service_description_exc(name, "inittab-id setting is too long");
  804. }
  805. strncpy(settings.inittab_id, inittab_setting.c_str(), sizeof(settings.inittab_id));
  806. #endif
  807. }
  808. else if (setting == "inittab-line") {
  809. string inittab_setting = read_setting_value(i, end, nullptr);
  810. #if USE_UTMPX
  811. if (inittab_setting.length() > sizeof(settings.inittab_line)) {
  812. throw service_description_exc(name, "inittab-line setting is too long");
  813. }
  814. strncpy(settings.inittab_line, inittab_setting.c_str(), sizeof(settings.inittab_line));
  815. #endif
  816. }
  817. else if (setting == "rlimit-nofile") {
  818. string nofile_setting = read_setting_value(i, end, nullptr);
  819. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_NOFILE);
  820. parse_rlimit(line, name, "rlimit-nofile", nofile_limits);
  821. }
  822. else if (setting == "rlimit-core") {
  823. string nofile_setting = read_setting_value(i, end, nullptr);
  824. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_CORE);
  825. parse_rlimit(line, name, "rlimit-core", nofile_limits);
  826. }
  827. else if (setting == "rlimit-data") {
  828. string nofile_setting = read_setting_value(i, end, nullptr);
  829. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_DATA);
  830. parse_rlimit(line, name, "rlimit-data", nofile_limits);
  831. }
  832. else if (setting == "rlimit-addrspace") {
  833. #if defined(RLIMIT_AS)
  834. string nofile_setting = read_setting_value(i, end, nullptr);
  835. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_AS);
  836. parse_rlimit(line, name, "rlimit-addrspace", nofile_limits);
  837. #endif
  838. }
  839. else {
  840. throw service_description_exc(name, "Unknown setting: '" + setting + "'.");
  841. }
  842. }
  843. } // namespace dinit_load
  844. using dinit_load::process_service_file;