load-service.h 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  1. #include <iostream>
  2. #include <list>
  3. #include <limits>
  4. #include <utility>
  5. #include <vector>
  6. #include <iterator>
  7. #include <csignal>
  8. #include <cstring>
  9. #include <cstdlib>
  10. #include <sys/types.h>
  11. #include <sys/time.h>
  12. #include <sys/resource.h>
  13. #include <grp.h>
  14. #include <pwd.h>
  15. #include "dinit-env.h"
  16. #include "dinit-utmp.h"
  17. #include "dinit-util.h"
  18. #include "service-constants.h"
  19. #include "mconfig.h"
  20. struct service_flags_t
  21. {
  22. // on-start flags:
  23. bool rw_ready : 1; // file system should be writable once this service starts
  24. bool log_ready : 1; // syslog should be available once this service starts
  25. // Other service options flags:
  26. bool runs_on_console : 1; // run "in the foreground"
  27. bool starts_on_console : 1; // starts in the foreground
  28. bool shares_console : 1; // run on console, but not exclusively
  29. bool pass_cs_fd : 1; // pass this service a control socket connection via fd
  30. bool start_interruptible : 1; // the startup of this service process is ok to interrupt with SIGINT
  31. bool skippable : 1; // if interrupted the service is skipped (scripted services)
  32. bool signal_process_only : 1; // signal the session process, not the whole group
  33. bool always_chain : 1; // always start chain-to service on exit
  34. bool kill_all_on_stop : 1; // kill all other processes before stopping this service
  35. service_flags_t() noexcept : rw_ready(false), log_ready(false),
  36. runs_on_console(false), starts_on_console(false), shares_console(false),
  37. pass_cs_fd(false), start_interruptible(false), skippable(false), signal_process_only(false),
  38. always_chain(false), kill_all_on_stop(false)
  39. {
  40. }
  41. };
  42. // Resource limits for a particular service & particular resource
  43. struct service_rlimits
  44. {
  45. int resource_id; // RLIMIT_xxx identifying resource
  46. bool soft_set : 1;
  47. bool hard_set : 1;
  48. struct rlimit limits;
  49. service_rlimits(int id) : resource_id(id), soft_set(0), hard_set(0), limits({0,0}) { }
  50. };
  51. // Exception while loading a service
  52. class service_load_exc
  53. {
  54. public:
  55. std::string service_name;
  56. std::string exc_description;
  57. service_load_exc(const std::string &serviceName, std::string &&desc)
  58. : service_name(serviceName), exc_description(std::move(desc))
  59. {
  60. }
  61. protected:
  62. service_load_exc(std::string &&desc) : exc_description(std::move(desc))
  63. {
  64. }
  65. };
  66. class service_not_found : public service_load_exc
  67. {
  68. public:
  69. service_not_found(const std::string &serviceName)
  70. : service_load_exc(serviceName, "service description not found.")
  71. {
  72. }
  73. };
  74. class service_load_error : public service_load_exc
  75. {
  76. public:
  77. service_load_error(const std::string &serviceName, std::string &&path, int fail_errno)
  78. : service_load_exc(serviceName, path + ": " + strerror(fail_errno))
  79. {
  80. }
  81. };
  82. class service_cyclic_dependency : public service_load_exc
  83. {
  84. public:
  85. service_cyclic_dependency(const std::string &serviceName)
  86. : service_load_exc(serviceName, "has cyclic dependency.")
  87. {
  88. }
  89. };
  90. class service_description_exc : public service_load_exc
  91. {
  92. public:
  93. const unsigned line_num = -1;
  94. const char * const setting_name = nullptr;
  95. service_description_exc(unsigned line_num, std::string &&exc_info)
  96. : service_load_exc(std::move(exc_info)), line_num(line_num)
  97. {
  98. }
  99. service_description_exc(const char *setting_name, std::string &&exc_info)
  100. : service_load_exc(std::move(exc_info)), setting_name(setting_name)
  101. {
  102. }
  103. service_description_exc(const std::string &serviceName, std::string &&extraInfo, unsigned line_num)
  104. : service_load_exc(serviceName, std::move(extraInfo)), line_num(line_num)
  105. {
  106. }
  107. service_description_exc(const std::string &serviceName, std::string &&extraInfo, const char *setting_name)
  108. : service_load_exc(serviceName, std::move(extraInfo)), setting_name(setting_name)
  109. {
  110. }
  111. };
  112. namespace dinit_load {
  113. using string = std::string;
  114. using string_iterator = std::string::iterator;
  115. // Utility function to skip white space. Returns an iterator at the
  116. // first non-white-space position (or at end).
  117. inline string_iterator skipws(string_iterator i, string_iterator end) noexcept
  118. {
  119. using std::locale;
  120. using std::isspace;
  121. while (i != end) {
  122. if (! isspace(*i, locale::classic())) {
  123. break;
  124. }
  125. ++i;
  126. }
  127. return i;
  128. }
  129. // Convert a signal name to the corresponding signal number
  130. inline int signal_name_to_number(std::string &signame) noexcept
  131. {
  132. if (signame == "none" || signame == "NONE") return 0;
  133. if (signame == "HUP") return SIGHUP;
  134. if (signame == "INT") return SIGINT;
  135. if (signame == "TERM") return SIGTERM;
  136. if (signame == "QUIT") return SIGQUIT;
  137. if (signame == "USR1") return SIGUSR1;
  138. if (signame == "USR2") return SIGUSR2;
  139. if (signame == "KILL") return SIGKILL;
  140. return -1;
  141. }
  142. // Read a setting/variable name; return empty string if no valid name
  143. //
  144. // If env is set, dashes/dots are not allowed within names. They are not typically allowed by shells
  145. // and they interfere with substitution patterns.
  146. inline string read_config_name(string_iterator & i, string_iterator end, bool env = false) noexcept
  147. {
  148. using std::locale;
  149. using std::ctype;
  150. using std::use_facet;
  151. // To avoid the horror of locales, we'll use the classic facet only, to identify digits, control
  152. // characters and punctuation. (Unless something is totally crazy, we are talking about ASCII or
  153. // a superset of it, but using the facet allows us to avoid that assumption). However, we're only
  154. // working with "narrow" char type so accuracy is limited. In general, that's not going to matter
  155. // much, but may allow certain unicode punctuation characters to be used as part of a name for example.
  156. const ctype<char> & facet = use_facet<ctype<char>>(locale::classic());
  157. string rval;
  158. // Don't allow empty name, numeric digit, or dash/dot at start of setting name
  159. if (i == end || (*i == '-' || *i == '.' || facet.is(ctype<char>::digit, *i))) {
  160. return {};
  161. }
  162. // Within the setting name, allow dash and dot unless parsing envvar name
  163. // also allow any non-control, non-punctuation non-space character.
  164. while (i != end && (((*i == '-' || *i == '.') && !env) || *i == '_'
  165. || (!facet.is(ctype<char>::cntrl, *i) && !facet.is(ctype<char>::punct, *i)
  166. && !facet.is(ctype<char>::space, *i)))) {
  167. rval += *i;
  168. ++i;
  169. }
  170. return rval;
  171. }
  172. // Read a setting value.
  173. //
  174. // In general a setting value is a single-line string. It may contain multiple parts
  175. // separated by white space (which is normally collapsed). A hash mark - # - denotes
  176. // the end of the value and the beginning of a comment (it should be preceded by
  177. // whitespace).
  178. //
  179. // Part of a value may be quoted using double quote marks, which prevents collapse
  180. // of whitespace and interpretation of most special characters (the quote marks will
  181. // not be considered part of the value). A backslash can precede a character (such
  182. // as '#' or '"' or another backslash) to remove its special meaning. Newline
  183. // characters are not allowed in values and cannot be quoted.
  184. //
  185. // This function expects the string to be in an ASCII-compatible encoding (the "classic" locale).
  186. //
  187. // Throws service_description_exc (with service name unset) on error.
  188. //
  189. // Params:
  190. // service_name - the name of the service to which the setting applies
  191. // i - reference to string iterator through the line
  192. // end - iterator at end of line (not including newline character if any)
  193. // part_positions - list of <int,int> to which the position of each setting value
  194. // part will be added as [start,end). May be null.
  195. inline string read_setting_value(unsigned line_num, string_iterator & i, string_iterator end,
  196. std::list<std::pair<unsigned,unsigned>> * part_positions = nullptr)
  197. {
  198. using std::locale;
  199. using std::isspace;
  200. i = skipws(i, end);
  201. string rval;
  202. bool new_part = true;
  203. int part_start;
  204. while (i != end) {
  205. char c = *i;
  206. if (c == '\"') {
  207. if (new_part) {
  208. part_start = rval.length();
  209. new_part = false;
  210. }
  211. // quoted string
  212. ++i;
  213. while (i != end) {
  214. c = *i;
  215. if (c == '\"') break;
  216. else if (c == '\\') {
  217. // A backslash escapes the following character.
  218. ++i;
  219. if (i != end) {
  220. c = *i;
  221. rval += c;
  222. }
  223. else {
  224. throw service_description_exc(line_num, "line end follows backslash escape character (`\\')");
  225. }
  226. }
  227. else {
  228. rval += c;
  229. }
  230. ++i;
  231. }
  232. if (i == end) {
  233. // String wasn't terminated
  234. throw service_description_exc(line_num, "unterminated quoted string");
  235. }
  236. }
  237. else if (c == '\\') {
  238. if (new_part) {
  239. part_start = rval.length();
  240. new_part = false;
  241. }
  242. // A backslash escapes the next character
  243. ++i;
  244. if (i != end) {
  245. rval += *i;
  246. }
  247. else {
  248. throw service_description_exc(line_num, "backslash escape (`\\') not followed by character");
  249. }
  250. }
  251. else if (isspace(c, locale::classic())) {
  252. if (! new_part && part_positions != nullptr) {
  253. part_positions->emplace_back(part_start, rval.length());
  254. new_part = true;
  255. }
  256. i = skipws(i, end);
  257. if (i == end) break;
  258. if (*i == '#') break; // comment
  259. rval += ' '; // collapse ws to a single space
  260. continue;
  261. }
  262. else if (c == '#') {
  263. // Possibly intended a comment; we require leading whitespace to reduce occurrence of accidental
  264. // comments in setting values.
  265. throw service_description_exc(line_num, "hashmark (`#') comment must be separated from setting value by whitespace");
  266. }
  267. else {
  268. if (new_part) {
  269. part_start = rval.length();
  270. new_part = false;
  271. }
  272. rval += c;
  273. }
  274. ++i;
  275. }
  276. // Got to end:
  277. if (part_positions != nullptr) {
  278. part_positions->emplace_back(part_start, rval.length());
  279. }
  280. return rval;
  281. }
  282. inline void fill_environment_userinfo(uid_t uid, const std::string &service_name, environment &env)
  283. {
  284. if (uid == (uid_t)-1) {
  285. uid = geteuid();
  286. }
  287. char buf[std::numeric_limits<unsigned long long>::digits10 + 1];
  288. snprintf(buf, sizeof(buf), "%llu", (unsigned long long)uid);
  289. errno = 0;
  290. struct passwd *pwent = getpwuid(uid);
  291. if (!pwent) {
  292. if (!errno) {
  293. throw service_load_exc(service_name, std::string("user id '") + buf + "' does not exist in system database");
  294. } else {
  295. throw service_load_exc(service_name, std::string("error accessing user database: ") + strerror(errno));
  296. }
  297. }
  298. std::string enval;
  299. // USER
  300. enval = "USER=";
  301. enval += pwent->pw_name;
  302. env.set_var(std::move(enval));
  303. // LOGNAME
  304. enval = "LOGNAME=";
  305. enval += pwent->pw_name;
  306. env.set_var(std::move(enval));
  307. // HOME
  308. enval = "HOME=";
  309. enval += pwent->pw_dir;
  310. env.set_var(std::move(enval));
  311. // SHELL
  312. enval = "SHELL=";
  313. enval += pwent->pw_shell;
  314. env.set_var(std::move(enval));
  315. // UID (non-standard, but useful)
  316. enval = "UID=";
  317. snprintf(buf, sizeof(buf), "%llu", (unsigned long long)pwent->pw_uid);
  318. enval += buf;
  319. env.set_var(std::move(enval));
  320. // GID (non-standard, but useful)
  321. enval = "GID=";
  322. snprintf(buf, sizeof(buf), "%llu", (unsigned long long)pwent->pw_gid);
  323. enval += buf;
  324. env.set_var(std::move(enval));
  325. }
  326. // Parse a userid parameter which may be a numeric user ID or a username. If a name, the
  327. // userid is looked up via the system user database (getpwnam() function). In this case,
  328. // the associated group is stored in the location specified by the group_p parameter if
  329. // it is not null.
  330. inline uid_t parse_uid_param(unsigned line_num, const std::string &param, const std::string &service_name,
  331. const char *setting_name, gid_t *group_p)
  332. {
  333. const char * uid_err_msg = "specified user id contains invalid numeric characters "
  334. "or is outside allowed range.";
  335. // Could be a name or a numeric id. But we should assume numeric first, just in case
  336. // a user manages to give themselves a username that parses as a number.
  337. std::size_t ind = 0;
  338. try {
  339. // POSIX does not specify whether uid_t is a signed or unsigned type, but regardless
  340. // is is probably safe to assume that valid values are positive. We'll also assert
  341. // that the value range fits within "unsigned long long" since it seems unlikely
  342. // that would ever not be the case.
  343. static_assert((uintmax_t)std::numeric_limits<uid_t>::max()
  344. <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "uid_t is too large");
  345. unsigned long long v = std::stoull(param, &ind, 0);
  346. if (v > static_cast<unsigned long long>(std::numeric_limits<uid_t>::max())
  347. || ind != param.length()) {
  348. throw service_description_exc(service_name, std::string(setting_name) + ": " + uid_err_msg, line_num);
  349. }
  350. return v;
  351. }
  352. catch (std::out_of_range &exc) {
  353. throw service_description_exc(service_name, uid_err_msg, line_num);
  354. }
  355. catch (std::invalid_argument &exc) {
  356. // Ok, so it doesn't look like a number: proceed...
  357. }
  358. errno = 0;
  359. struct passwd * pwent = getpwnam(param.c_str());
  360. if (pwent == nullptr) {
  361. // Maybe an error, maybe just no entry.
  362. if (errno == 0) {
  363. throw service_description_exc(service_name, std::string(setting_name) + ": specified user \"" + param
  364. + "\" does not exist in system database.", line_num);
  365. }
  366. else {
  367. throw service_description_exc(service_name, std::string("error accessing user database: ")
  368. + strerror(errno), line_num);
  369. }
  370. }
  371. if (group_p) {
  372. *group_p = pwent->pw_gid;
  373. }
  374. return pwent->pw_uid;
  375. }
  376. inline gid_t parse_gid_param(unsigned line_num, const std::string &param, const char *setting_name,
  377. const std::string &service_name)
  378. {
  379. const char * gid_err_msg = "specified group id contains invalid numeric characters or is "
  380. "outside allowed range.";
  381. // Could be a name or a numeric id. But we should assume numeric first, just in case
  382. // a user manages to give themselves a username that parses as a number.
  383. std::size_t ind = 0;
  384. try {
  385. // POSIX does not specify whether uid_t is an signed or unsigned, but regardless
  386. // is is probably safe to assume that valid values are positive. We'll also assume
  387. // that the value range fits with "unsigned long long" since it seems unlikely
  388. // that would ever not be the case.
  389. static_assert((uintmax_t)std::numeric_limits<gid_t>::max()
  390. <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "gid_t is too large");
  391. unsigned long long v = std::stoull(param, &ind, 0);
  392. if (v > static_cast<unsigned long long>(std::numeric_limits<gid_t>::max())
  393. || ind != param.length()) {
  394. throw service_description_exc(service_name, std::string(setting_name) + ": " + gid_err_msg, line_num);
  395. }
  396. return v;
  397. }
  398. catch (std::out_of_range &exc) {
  399. throw service_description_exc(service_name, std::string(setting_name) + ": " + gid_err_msg, line_num);
  400. }
  401. catch (std::invalid_argument &exc) {
  402. // Ok, so it doesn't look like a number: proceed...
  403. }
  404. errno = 0;
  405. struct group * grent = getgrnam(param.c_str());
  406. if (grent == nullptr) {
  407. // Maybe an error, maybe just no entry.
  408. if (errno == 0) {
  409. throw service_description_exc(service_name, std::string(setting_name) + ": specified group \"" + param
  410. + "\" does not exist in system database.", line_num);
  411. }
  412. else {
  413. throw service_description_exc(service_name, std::string("error accessing group database: ")
  414. + strerror(errno), line_num);
  415. }
  416. }
  417. return grent->gr_gid;
  418. }
  419. // Parse a time, specified as a decimal number of seconds (with optional fractional component after decimal
  420. // point or decimal comma).
  421. inline void parse_timespec(unsigned line_num, const std::string &paramval, const std::string &servicename,
  422. const char * paramname, timespec &ts)
  423. {
  424. decltype(ts.tv_sec) isec = 0;
  425. decltype(ts.tv_nsec) insec = 0;
  426. auto max_secs = std::numeric_limits<decltype(isec)>::max() / 10;
  427. auto len = paramval.length();
  428. decltype(len) i;
  429. for (i = 0; i < len; i++) {
  430. char ch = paramval[i];
  431. if (ch == '.' || ch == ',') {
  432. i++;
  433. break;
  434. }
  435. if (ch < '0' || ch > '9') {
  436. throw service_description_exc(servicename, std::string("bad value for ") + paramname, line_num);
  437. }
  438. // check for overflow
  439. if (isec >= max_secs) {
  440. throw service_description_exc(servicename, std::string("too-large value for ") + paramname, line_num);
  441. }
  442. isec *= 10;
  443. isec += ch - '0';
  444. }
  445. decltype(insec) insec_m = 100000000; // 10^8
  446. for ( ; i < len; i++) {
  447. char ch = paramval[i];
  448. if (ch < '0' || ch > '9') {
  449. throw service_description_exc(servicename, std::string("bad value for ") + paramname, line_num);
  450. }
  451. insec += (ch - '0') * insec_m;
  452. insec_m /= 10;
  453. }
  454. ts.tv_sec = isec;
  455. ts.tv_nsec = insec;
  456. }
  457. // Parse an unsigned numeric parameter value
  458. inline unsigned long long parse_unum_param(unsigned line_num, const std::string &param,
  459. const std::string &service_name, unsigned long long max = std::numeric_limits<unsigned long long>::max())
  460. {
  461. const char * num_err_msg = "specified value contains invalid numeric characters or is outside "
  462. "allowed range.";
  463. std::size_t ind = 0;
  464. try {
  465. unsigned long long v = std::stoull(param, &ind, 0);
  466. if (v > max || ind != param.length()) {
  467. throw service_description_exc(service_name, num_err_msg, line_num);
  468. }
  469. return v;
  470. }
  471. catch (std::out_of_range &exc) {
  472. throw service_description_exc(service_name, num_err_msg, line_num);
  473. }
  474. catch (std::invalid_argument &exc) {
  475. throw service_description_exc(service_name, num_err_msg, line_num);
  476. }
  477. }
  478. // In a vector, find or create rlimits for a particular resource type.
  479. inline service_rlimits &find_rlimits(std::vector<service_rlimits> &all_rlimits, int resource_id)
  480. {
  481. for (service_rlimits &limits : all_rlimits) {
  482. if (limits.resource_id == resource_id) {
  483. return limits;
  484. }
  485. }
  486. all_rlimits.emplace_back(resource_id);
  487. return all_rlimits.back();
  488. }
  489. // Parse resource limits setting (can specify both hard and soft limit).
  490. inline void parse_rlimit(const std::string &line, unsigned line_num, const std::string &service_name,
  491. const char *param_name, service_rlimits &rlimit)
  492. {
  493. // Examples:
  494. // 4:5 - soft:hard limits both set
  495. // 4:- soft set, hard set to unlimited
  496. // 4: soft set, hard limit unchanged
  497. // 4 soft and hard limit set to same limit
  498. if (line.empty()) {
  499. throw service_description_exc(service_name, std::string(param_name) + ": bad value.", line_num);
  500. }
  501. const char *cline = line.c_str();
  502. rlimit.hard_set = rlimit.soft_set = false;
  503. try {
  504. const char * index = cline;
  505. errno = 0;
  506. if (cline[0] != ':') {
  507. rlimit.soft_set = true;
  508. if (cline[0] == '-') {
  509. rlimit.limits.rlim_cur = RLIM_INFINITY;
  510. index = cline + 1;
  511. }
  512. else {
  513. errno = 0;
  514. char *nindex;
  515. unsigned long long limit = std::strtoull(cline, &nindex, 0);
  516. index = nindex;
  517. if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
  518. if (index == cline) throw std::invalid_argument("");
  519. rlimit.limits.rlim_cur = limit;
  520. }
  521. if (*index == 0) {
  522. rlimit.hard_set = true;
  523. rlimit.limits.rlim_max = rlimit.limits.rlim_cur;
  524. return;
  525. }
  526. if (*index != ':') {
  527. throw service_description_exc(service_name, std::string(param_name) + ": bad value.", line_num);
  528. }
  529. }
  530. index++;
  531. if (*index == 0) return;
  532. rlimit.hard_set = true;
  533. if (*index == '-') {
  534. rlimit.limits.rlim_max = RLIM_INFINITY;
  535. if (index[1] != 0) {
  536. throw service_description_exc(service_name, std::string(param_name) + ": bad value.", line_num);
  537. }
  538. }
  539. else {
  540. const char *hard_start = index;
  541. char *nindex;
  542. errno = 0;
  543. unsigned long long limit = std::strtoull(hard_start, &nindex, 0);
  544. index = nindex;
  545. if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
  546. if (index == hard_start) throw std::invalid_argument("");
  547. rlimit.limits.rlim_max = limit;
  548. }
  549. }
  550. catch (std::invalid_argument &exc) {
  551. throw service_description_exc(service_name, std::string(param_name) + ": bad value.", line_num);
  552. }
  553. catch (std::out_of_range &exc) {
  554. throw service_description_exc(service_name, std::string(param_name) + ": too-large value.", line_num);
  555. }
  556. }
  557. // Process an opened service file, line by line.
  558. // name - the service name
  559. // service_file - the service file input stream
  560. // func - a function of the form:
  561. // void(string &line, string &setting, string_iterator i, string_iterator end)
  562. // Called with:
  563. // line - the complete line (excluding newline character)
  564. // setting - the setting name, from the beginning of the line
  565. // i - iterator at the beginning of the setting value
  566. // end - iterator marking the end of the line
  567. //
  568. // May throw service load exceptions or I/O exceptions if enabled on stream.
  569. template <typename T>
  570. void process_service_file(string name, std::istream &service_file, T func)
  571. {
  572. string line;
  573. unsigned line_num = 0;
  574. while (getline(service_file, line)) {
  575. ++line_num;
  576. string::iterator i = line.begin();
  577. string::iterator end = line.end();
  578. i = skipws(i, end);
  579. if (i != end) {
  580. if (*i == '#') {
  581. continue; // comment line
  582. }
  583. string setting = read_config_name(i, end);
  584. i = skipws(i, end);
  585. if (setting.empty() || i == end || (*i != '=' && *i != ':')) {
  586. throw service_description_exc(name, "badly formed line.", line_num);
  587. }
  588. i = skipws(++i, end);
  589. func(line, line_num, setting, i, end);
  590. }
  591. }
  592. }
  593. // A dummy lint-reporting "function".
  594. static auto dummy_lint = [](const char *){};
  595. // Resolve leading variables in paths using the environment
  596. static auto resolve_env_var = [](const string &name, environment::env_map const &envmap){
  597. return envmap.lookup(name);
  598. };
  599. // Substitute variable references in a value with their values. Specified offsets must give
  600. // the location of separate arguments after word splitting and are adjusted appropriately.
  601. // If you simply wish to substitute all variables in the given string, pass an offsets list
  602. // containing one pair with the string's bounds (0, size). '$$' resolves to a single '$'.
  603. //
  604. // throws: setting_exception if a $-substitution is ill-formed, or if the command line is too long;
  605. // bad_alloc on allocation failure
  606. template <typename T>
  607. static void value_var_subst(const char *setting_name, std::string &line,
  608. std::list<std::pair<unsigned,unsigned>> &offsets, T &var_resolve,
  609. environment::env_map const &envmap)
  610. {
  611. auto dindx = line.find('$');
  612. if (dindx == string::npos) {
  613. return;
  614. }
  615. if (line.length() > (size_t)std::numeric_limits<int>::max()) {
  616. // (avoid potential for overflow later)
  617. throw service_description_exc(setting_name, "value too long");
  618. }
  619. auto i = offsets.begin();
  620. unsigned xpos = 0; // position to copy from
  621. std::string r_line;
  622. int offadj = 0;
  623. while (i != offsets.end()) {
  624. i->first += offadj; // don't adjust end yet
  625. while (i->second > dindx) {
  626. r_line.append(line, xpos, dindx - xpos); // copy unmatched part
  627. if (line[dindx + 1] == '$') {
  628. // double dollar, collapse to single
  629. r_line += '$';
  630. xpos = dindx + 2;
  631. --offadj;
  632. }
  633. else {
  634. // variable
  635. auto token_end = std::next(line.begin(), i->second);
  636. bool brace = line[dindx + 1] == '{';
  637. auto i = std::next(line.begin(), dindx + 1 + int(brace));
  638. // read environment variable name
  639. string name = read_config_name(i, token_end, true);
  640. if (name.empty()) {
  641. throw service_description_exc(setting_name, "invalid/missing variable name after '$'");
  642. }
  643. char altmode = '\0';
  644. bool colon = false;
  645. auto altbeg = i, altend = i;
  646. if (brace) {
  647. /* ${foo+val}, ${foo-val}, ${foo:+val}, ${foo:-val} */
  648. if (*i == ':') {
  649. colon = true;
  650. ++i;
  651. if (*i != '+' && *i != '-') {
  652. throw service_description_exc(setting_name, "invalid syntax in variable substitution");
  653. }
  654. }
  655. if (*i == '+' || *i == '-') {
  656. altmode = *i;
  657. altbeg = ++i;
  658. while (i != token_end && *i != '}') {
  659. ++i;
  660. }
  661. altend = i;
  662. }
  663. if (*i != '}') {
  664. throw service_description_exc(setting_name, "unmatched '{' in variable substitution");
  665. }
  666. ++i;
  667. }
  668. size_t line_len_before = r_line.size();
  669. auto *resolved = var_resolve(name, envmap);
  670. /* apply shell-like substitutions */
  671. if (altmode == '-') {
  672. if (!resolved || (colon && !*resolved)) {
  673. r_line.append(altbeg, altend);
  674. } else if (resolved) {
  675. r_line.append(resolved);
  676. }
  677. } else if (altmode == '+') {
  678. if (resolved && (!colon || *resolved)) {
  679. r_line.append(altbeg, altend);
  680. }
  681. } else if (resolved) {
  682. r_line.append(resolved);
  683. }
  684. size_t line_len_after = r_line.size();
  685. if (line_len_after > (size_t)std::numeric_limits<int>::max()) {
  686. // (avoid potential overflow)
  687. throw service_description_exc(setting_name, "value too long (after substitution)");
  688. }
  689. xpos = i - line.begin();
  690. int name_len = xpos - dindx;
  691. offadj += (int)line_len_after - (int)line_len_before - name_len;
  692. }
  693. dindx = line.find('$', xpos);
  694. }
  695. i->second += offadj;
  696. ++i;
  697. while (i != offsets.end() && i->second < dindx) {
  698. i->first += offadj;
  699. i->second += offadj;
  700. ++i;
  701. }
  702. }
  703. r_line.append(line, xpos); // copy final unmatched part
  704. line = std::move(r_line);
  705. }
  706. // A wrapper type for service parameters. It is parameterised by dependency type.
  707. template <class dep_type>
  708. class service_settings_wrapper
  709. {
  710. template <typename A, typename B> using pair = std::pair<A,B>;
  711. template <typename A> using list = std::list<A>;
  712. public:
  713. ha_string command;
  714. list<pair<unsigned,unsigned>> command_offsets; // [start,end) offset of each arg (inc. executable)
  715. ha_string stop_command;
  716. list<pair<unsigned,unsigned>> stop_command_offsets;
  717. string working_dir;
  718. string pid_file;
  719. string env_file;
  720. bool export_passwd_vars = false;
  721. bool export_service_name = false;
  722. service_type_t service_type = service_type_t::INTERNAL;
  723. list<dep_type> depends;
  724. list<std::string> before_svcs;
  725. list<std::string> after_svcs;
  726. log_type_id log_type = log_type_id::NONE;
  727. string logfile;
  728. unsigned max_log_buffer_sz = 4096;
  729. service_flags_t onstart_flags;
  730. int term_signal = SIGTERM; // termination signal
  731. bool auto_restart = DEFAULT_AUTO_RESTART;
  732. bool smooth_recovery = false;
  733. string socket_path;
  734. int socket_perms = 0666;
  735. // Note: Posix allows that uid_t and gid_t may be unsigned types, but eg chown uses -1 as an
  736. // invalid value, so it's safe to assume that we can do the same:
  737. uid_t socket_uid = -1;
  738. gid_t socket_uid_gid = -1; // primary group of socket user if known
  739. gid_t socket_gid = -1;
  740. // Restart limit interval / count; default is 10 seconds, 3 restarts:
  741. timespec restart_interval = { .tv_sec = 10, .tv_nsec = 0 };
  742. int max_restarts = 3;
  743. timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
  744. timespec stop_timeout = { .tv_sec = DEFAULT_STOP_TIMEOUT, .tv_nsec = 0 };
  745. timespec start_timeout = { .tv_sec = DEFAULT_START_TIMEOUT, .tv_nsec = 0 };
  746. std::vector<service_rlimits> rlimits;
  747. int readiness_fd = -1; // readiness fd in service process
  748. string readiness_var; // environment var to hold readiness fd
  749. uid_t run_as_uid = -1;
  750. gid_t run_as_uid_gid = -1; // primary group of "run as" uid if known
  751. gid_t run_as_gid = -1;
  752. string chain_to_name;
  753. #if SUPPORT_CGROUPS
  754. string run_in_cgroup;
  755. #endif
  756. #if USE_UTMPX
  757. char inittab_id[sizeof(utmpx().ut_id)] = {0};
  758. char inittab_line[sizeof(utmpx().ut_line)] = {0};
  759. #endif
  760. // Finalise settings (after processing all setting lines), perform some basic sanity checks and
  761. // optionally some additional lint checks. May throw service_description_exc
  762. //
  763. // Note: we have the do_report_lint parameter to prevent code (and strings) being emitted for lint
  764. // checks even when the dummy_lint function is used. (Ideally the compiler would optimise them away).
  765. template <typename T, typename U = decltype(dummy_lint), typename V = decltype(resolve_env_var),
  766. bool do_report_lint = !std::is_same<U, decltype(dummy_lint)>::value>
  767. void finalise(T &report_error, environment::env_map const &envmap, U &report_lint = dummy_lint, V &var_subst = resolve_env_var)
  768. {
  769. if (service_type == service_type_t::PROCESS || service_type == service_type_t::BGPROCESS
  770. || service_type == service_type_t::SCRIPTED) {
  771. if (command.empty()) {
  772. report_error("'command' setting not specified.");
  773. }
  774. }
  775. if (do_report_lint && (service_type == service_type_t::INTERNAL
  776. || service_type == service_type_t::TRIGGERED)) {
  777. if (!command.empty()) {
  778. report_lint("'command' specified, but ignored for the specified (or default) service type.");
  779. }
  780. if (!stop_command.empty()) {
  781. report_lint("'stop-command' specified, but ignored for the specified (or default) service type.");
  782. }
  783. if (!working_dir.empty()) {
  784. report_lint("'working-dir' specified, but ignored for the specified (or default) service type.");
  785. }
  786. #if SUPPORT_CGROUPS
  787. if (!run_in_cgroup.empty()) {
  788. report_lint("'run-in-cgroup' specified, but ignored for the specified (or default) service type.");
  789. }
  790. #endif
  791. if (run_as_uid != (uid_t)-1) {
  792. report_lint("'run-as' specified, but ignored for the specified (or default) service type.");
  793. }
  794. if (!socket_path.empty()) {
  795. report_lint("'socket-listen' specified, but ignored for the specified (or default) service type'.");
  796. }
  797. #if USE_UTMPX
  798. if (inittab_id[0] != 0 || inittab_line[0] != 0) {
  799. report_lint("'inittab_line' or 'inittab_id' specified, but ignored for the specified (or default) service type.");
  800. }
  801. #endif
  802. if (onstart_flags.signal_process_only || onstart_flags.start_interruptible) {
  803. report_lint("signal options were specified, but ignored for the specified (or default) service type.");
  804. }
  805. if (onstart_flags.pass_cs_fd) {
  806. report_lint("option 'pass_cs_fd' was specified, but ignored for the specified (or default) service type.");
  807. }
  808. if (onstart_flags.skippable) {
  809. report_lint("option 'skippable' was specified, but ignored for the specified (or default) service type.");
  810. }
  811. if (log_type != log_type_id::NONE) {
  812. report_lint("option 'log_type' was specified, but ignored for the specified (or default) service type.");
  813. }
  814. }
  815. if (do_report_lint) {
  816. if (log_type != log_type_id::LOGFILE && !logfile.empty()) {
  817. report_lint("option 'logfile' was specified, but selected log type is not 'file'");
  818. }
  819. if (log_type == log_type_id::LOGFILE && logfile.empty()) {
  820. report_lint("option 'logfile' not set, but selected log type is 'file'");
  821. }
  822. }
  823. if (service_type == service_type_t::BGPROCESS) {
  824. if (pid_file.empty()) {
  825. report_error("process ID file ('pid-file') not specified for bgprocess service.");
  826. }
  827. if (readiness_fd != -1 || !readiness_var.empty()) {
  828. report_error("readiness notification ('ready-notification') is not supported "
  829. "for bgprocess services.");
  830. }
  831. }
  832. if (onstart_flags.kill_all_on_stop && service_type != service_type_t::INTERNAL
  833. && service_type != service_type_t::SCRIPTED) {
  834. report_error("kill-all-on-stop can only be set on scripted or internal services.");
  835. }
  836. // Resolve paths via variable substitution
  837. {
  838. std::list<std::pair<unsigned, unsigned>> offsets;
  839. /* reserve item */
  840. offsets.emplace_back(0, 0);
  841. auto do_resolve = [&](const char *setting_name, string &setting_value) {
  842. try {
  843. offsets.front().first = 0;
  844. offsets.front().second = setting_value.size();
  845. value_var_subst(setting_name, setting_value, offsets, var_subst, envmap);
  846. }
  847. catch (service_description_exc &exc) {
  848. report_error((string() + setting_name + ": " + exc.exc_description).c_str());
  849. }
  850. };
  851. do_resolve("socket-listen", socket_path);
  852. do_resolve("logfile", logfile);
  853. do_resolve("working-dir", working_dir);
  854. do_resolve("pid-file", pid_file);
  855. }
  856. // If socket_gid hasn't been explicitly set, but the socket_uid was specified as a name (and
  857. // we therefore recovered the primary group), use the primary group of the specified user.
  858. if (socket_gid == (gid_t)-1) socket_gid = socket_uid_gid;
  859. // likewise for "run as" gid/uid, but only if we aren't supporting supplementary group initialisation
  860. // (if we do support supplementary groups, run_as_gid==-1 means "use the user groups including
  861. // supplementary groups" whereas run_as_gid==X means "use group X with no supplementary groups").
  862. #if !USE_INITGROUPS
  863. if (run_as_gid == (gid_t)-1) run_as_gid = run_as_uid_gid;
  864. #endif
  865. if (log_type != log_type_id::LOGFILE) {
  866. logfile.clear();
  867. }
  868. }
  869. };
  870. // Process a service description line. In general, parse the setting value and record the parsed value
  871. // in a service settings wrapper object. Errors will be reported via service_description_exc exception.
  872. //
  873. // type parameters:
  874. // settings_wrapper : wrapper for service settings
  875. // load_service_t : type of load service function/lambda (see below)
  876. // process_dep_dir_t : type of process_dep_dir funciton/lambda (see below)
  877. //
  878. // parameters:
  879. // settings : wrapper object for service settings
  880. // name : name of the service being processed
  881. // line : the current line of the service description file
  882. // setting : the name of the setting (from the beginning of line)
  883. // i : iterator at beginning of setting value (including whitespace)
  884. // end : iterator at end of line
  885. // load_service : function to load a service
  886. // arguments: const char *service_name
  887. // return: a value that can be used (with a dependency type) to construct a dependency
  888. // in the 'depends' vector within the 'settings' object
  889. // process_dep_dir : function to process a dependency directory
  890. // arguments: decltype(settings.depends) &dependencies
  891. // const string &waitsford - directory as specified in parameter
  892. // dependency_type dep_type - type of dependency to add
  893. template <typename settings_wrapper,
  894. typename load_service_t,
  895. typename process_dep_dir_t>
  896. void process_service_line(settings_wrapper &settings, const char *name, string &line, unsigned line_num,
  897. string &setting, string::iterator &i, string::iterator &end, load_service_t load_service,
  898. process_dep_dir_t process_dep_dir)
  899. {
  900. if (setting == "command") {
  901. settings.command = read_setting_value(line_num, i, end, &settings.command_offsets);
  902. }
  903. else if (setting == "working-dir") {
  904. settings.working_dir = read_setting_value(line_num, i, end, nullptr);
  905. }
  906. else if (setting == "env-file") {
  907. settings.env_file = read_setting_value(line_num, i, end, nullptr);
  908. }
  909. #if SUPPORT_CGROUPS
  910. else if (setting == "run-in-cgroup") {
  911. settings.run_in_cgroup = read_setting_value(line_num, i, end, nullptr);
  912. }
  913. #endif
  914. else if (setting == "socket-listen") {
  915. settings.socket_path = read_setting_value(line_num, i, end, nullptr);
  916. }
  917. else if (setting == "socket-permissions") {
  918. string sock_perm_str = read_setting_value(line_num, i, end, nullptr);
  919. std::size_t ind = 0;
  920. try {
  921. settings.socket_perms = std::stoi(sock_perm_str, &ind, 8);
  922. if (ind != sock_perm_str.length()) {
  923. throw std::logic_error("");
  924. }
  925. }
  926. catch (std::logic_error &exc) {
  927. throw service_description_exc(name, "socket-permissions: badly-formed or "
  928. "out-of-range numeric value", line_num);
  929. }
  930. }
  931. else if (setting == "socket-uid") {
  932. string sock_uid_s = read_setting_value(line_num, i, end, nullptr);
  933. settings.socket_uid = parse_uid_param(line_num, sock_uid_s, name, "socket-uid", &settings.socket_uid_gid);
  934. }
  935. else if (setting == "socket-gid") {
  936. string sock_gid_s = read_setting_value(line_num, i, end, nullptr);
  937. settings.socket_gid = parse_gid_param(line_num, sock_gid_s, "socket-gid", name);
  938. }
  939. else if (setting == "stop-command") {
  940. settings.stop_command = read_setting_value(line_num, i, end, &settings.stop_command_offsets);
  941. }
  942. else if (setting == "pid-file") {
  943. settings.pid_file = read_setting_value(line_num, i, end);
  944. }
  945. else if (setting == "depends-on") {
  946. string dependency_name = read_setting_value(line_num, i, end);
  947. settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::REGULAR);
  948. }
  949. else if (setting == "depends-ms") {
  950. string dependency_name = read_setting_value(line_num, i, end);
  951. settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::MILESTONE);
  952. }
  953. else if (setting == "waits-for") {
  954. string dependency_name = read_setting_value(line_num, i, end);
  955. settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::WAITS_FOR);
  956. }
  957. else if (setting == "waits-for.d") {
  958. string waitsford = read_setting_value(line_num, i, end);
  959. process_dep_dir(settings.depends, waitsford, dependency_type::WAITS_FOR);
  960. }
  961. else if (setting == "after") {
  962. string after_name = read_setting_value(line_num, i, end);
  963. settings.after_svcs.emplace_back(std::move(after_name));
  964. }
  965. else if (setting == "before") {
  966. string before_name = read_setting_value(line_num, i, end);
  967. settings.before_svcs.emplace_back(std::move(before_name));
  968. }
  969. else if (setting == "logfile") {
  970. settings.logfile = read_setting_value(line_num, i, end);
  971. if (!settings.logfile.empty() && settings.log_type == log_type_id::NONE) {
  972. settings.log_type = log_type_id::LOGFILE;
  973. }
  974. }
  975. else if (setting == "log-type") {
  976. string log_type_str = read_setting_value(line_num, i, end);
  977. if (log_type_str == "file") {
  978. settings.log_type = log_type_id::LOGFILE;
  979. }
  980. else if (log_type_str == "buffer") {
  981. settings.log_type = log_type_id::BUFFER;
  982. }
  983. else if (log_type_str == "none") {
  984. settings.log_type = log_type_id::NONE;
  985. }
  986. else {
  987. throw service_description_exc(name, "log type must be one of: \"file\", \"buffer\" or \"none\"",
  988. line_num);
  989. }
  990. }
  991. else if (setting == "log-buffer-size") {
  992. string log_buffer_size_str = read_setting_value(line_num, i, end);
  993. unsigned bufsize = (unsigned)parse_unum_param(line_num, log_buffer_size_str, name,
  994. std::numeric_limits<unsigned>::max() / 2);
  995. settings.max_log_buffer_sz = bufsize;
  996. }
  997. else if (setting == "restart") {
  998. string restart = read_setting_value(line_num, i, end);
  999. settings.auto_restart = (restart == "yes" || restart == "true");
  1000. }
  1001. else if (setting == "smooth-recovery") {
  1002. string recovery = read_setting_value(line_num, i, end);
  1003. settings.smooth_recovery = (recovery == "yes" || recovery == "true");
  1004. }
  1005. else if (setting == "type") {
  1006. string type_str = read_setting_value(line_num, i, end);
  1007. if (type_str == "scripted") {
  1008. settings.service_type = service_type_t::SCRIPTED;
  1009. }
  1010. else if (type_str == "process") {
  1011. settings.service_type = service_type_t::PROCESS;
  1012. }
  1013. else if (type_str == "bgprocess") {
  1014. settings.service_type = service_type_t::BGPROCESS;
  1015. }
  1016. else if (type_str == "internal") {
  1017. settings.service_type = service_type_t::INTERNAL;
  1018. }
  1019. else if (type_str == "triggered") {
  1020. settings.service_type = service_type_t::TRIGGERED;
  1021. }
  1022. else {
  1023. throw service_description_exc(name, "service type must be one of: \"scripted\","
  1024. " \"process\", \"bgprocess\", \"internal\" or \"triggered\"", line_num);
  1025. }
  1026. }
  1027. else if (setting == "options") {
  1028. std::list<std::pair<unsigned,unsigned>> indices;
  1029. string onstart_cmds = read_setting_value(line_num, i, end, &indices);
  1030. for (auto indexpair : indices) {
  1031. string option_txt = onstart_cmds.substr(indexpair.first,
  1032. indexpair.second - indexpair.first);
  1033. if (option_txt == "starts-rwfs") {
  1034. settings.onstart_flags.rw_ready = true;
  1035. }
  1036. else if (option_txt == "starts-log") {
  1037. settings.onstart_flags.log_ready = true;
  1038. }
  1039. else if (option_txt == "runs-on-console") {
  1040. settings.onstart_flags.runs_on_console = true;
  1041. // A service that runs on the console necessarily starts on console:
  1042. settings.onstart_flags.starts_on_console = true;
  1043. settings.onstart_flags.shares_console = false;
  1044. }
  1045. else if (option_txt == "starts-on-console") {
  1046. settings.onstart_flags.starts_on_console = true;
  1047. settings.onstart_flags.shares_console = false;
  1048. }
  1049. else if (option_txt == "shares-console") {
  1050. settings.onstart_flags.shares_console = true;
  1051. settings.onstart_flags.runs_on_console = false;
  1052. settings.onstart_flags.starts_on_console = false;
  1053. }
  1054. else if (option_txt == "pass-cs-fd") {
  1055. settings.onstart_flags.pass_cs_fd = true;
  1056. }
  1057. else if (option_txt == "start-interruptible") {
  1058. settings.onstart_flags.start_interruptible = true;
  1059. }
  1060. else if (option_txt == "skippable") {
  1061. settings.onstart_flags.skippable = true;
  1062. }
  1063. else if (option_txt == "signal-process-only") {
  1064. settings.onstart_flags.signal_process_only = true;
  1065. }
  1066. else if (option_txt == "always-chain") {
  1067. settings.onstart_flags.always_chain = true;
  1068. }
  1069. else if (option_txt == "kill-all-on-stop") {
  1070. settings.onstart_flags.kill_all_on_stop = true;
  1071. }
  1072. else {
  1073. throw service_description_exc(name, "Unknown option: " + option_txt, line_num);
  1074. }
  1075. }
  1076. }
  1077. else if (setting == "load-options") {
  1078. std::list<std::pair<unsigned,unsigned>> indices;
  1079. string load_opts = read_setting_value(line_num, i, end, &indices);
  1080. for (auto indexpair : indices) {
  1081. string option_txt = load_opts.substr(indexpair.first,
  1082. indexpair.second - indexpair.first);
  1083. if (option_txt == "export-passwd-vars") {
  1084. settings.export_passwd_vars = true;
  1085. }
  1086. else if (option_txt == "export-service-name") {
  1087. settings.export_service_name = true;
  1088. }
  1089. else if (option_txt == "sub-vars") {
  1090. // noop: for backwards compatibility only
  1091. // we don't support no-sub-vars anymore, however
  1092. }
  1093. else {
  1094. throw service_description_exc(name, "unknown load option: " + option_txt, line_num);
  1095. }
  1096. }
  1097. }
  1098. else if (setting == "term-signal" || setting == "termsignal") {
  1099. // Note: "termsignal" supported for legacy reasons.
  1100. string signame = read_setting_value(line_num, i, end, nullptr);
  1101. int signo = signal_name_to_number(signame);
  1102. if (signo == -1) {
  1103. throw service_description_exc(name, "unknown/unsupported termination signal: "
  1104. + signame, line_num);
  1105. }
  1106. else {
  1107. settings.term_signal = signo;
  1108. }
  1109. }
  1110. else if (setting == "restart-limit-interval") {
  1111. string interval_str = read_setting_value(line_num, i, end, nullptr);
  1112. parse_timespec(line_num, interval_str, name, "restart-limit-interval", settings.restart_interval);
  1113. }
  1114. else if (setting == "restart-delay") {
  1115. string rsdelay_str = read_setting_value(line_num, i, end, nullptr);
  1116. parse_timespec(line_num, rsdelay_str, name, "restart-delay", settings.restart_delay);
  1117. }
  1118. else if (setting == "restart-limit-count") {
  1119. string limit_str = read_setting_value(line_num, i, end, nullptr);
  1120. settings.max_restarts = parse_unum_param(line_num, limit_str, name, std::numeric_limits<int>::max());
  1121. }
  1122. else if (setting == "stop-timeout") {
  1123. string stoptimeout_str = read_setting_value(line_num, i, end, nullptr);
  1124. parse_timespec(line_num, stoptimeout_str, name, "stop-timeout", settings.stop_timeout);
  1125. }
  1126. else if (setting == "start-timeout") {
  1127. string starttimeout_str = read_setting_value(line_num, i, end, nullptr);
  1128. parse_timespec(line_num, starttimeout_str, name, "start-timeout", settings.start_timeout);
  1129. }
  1130. else if (setting == "run-as") {
  1131. string run_as_str = read_setting_value(line_num, i, end, nullptr);
  1132. settings.run_as_uid = parse_uid_param(line_num, run_as_str, name, "run-as", &settings.run_as_uid_gid);
  1133. }
  1134. else if (setting == "chain-to") {
  1135. settings.chain_to_name = read_setting_value(line_num, i, end, nullptr);
  1136. }
  1137. else if (setting == "ready-notification") {
  1138. string notify_setting = read_setting_value(line_num, i, end, nullptr);
  1139. if (starts_with(notify_setting, "pipefd:")) {
  1140. settings.readiness_fd = parse_unum_param(line_num, notify_setting.substr(7 /* len 'pipefd:' */),
  1141. name, std::numeric_limits<int>::max());
  1142. }
  1143. else if (starts_with(notify_setting, "pipevar:")) {
  1144. settings.readiness_var = notify_setting.substr(8 /* len 'pipevar:' */);
  1145. if (settings.readiness_var.empty()) {
  1146. throw service_description_exc(name, "invalid pipevar variable name "
  1147. "in ready-notification", line_num);
  1148. }
  1149. }
  1150. else {
  1151. throw service_description_exc(name, "unknown ready-notification setting: "
  1152. + notify_setting, line_num);
  1153. }
  1154. }
  1155. else if (setting == "inittab-id") {
  1156. string inittab_setting = read_setting_value(line_num, i, end, nullptr);
  1157. #if USE_UTMPX
  1158. if (inittab_setting.length() > sizeof(settings.inittab_id)) {
  1159. throw service_description_exc(name, "inittab-id setting is too long", line_num);
  1160. }
  1161. strncpy(settings.inittab_id, inittab_setting.c_str(), sizeof(settings.inittab_id));
  1162. #endif
  1163. }
  1164. else if (setting == "inittab-line") {
  1165. string inittab_setting = read_setting_value(line_num, i, end, nullptr);
  1166. #if USE_UTMPX
  1167. if (inittab_setting.length() > sizeof(settings.inittab_line)) {
  1168. throw service_description_exc(name, "inittab-line setting is too long", line_num);
  1169. }
  1170. strncpy(settings.inittab_line, inittab_setting.c_str(), sizeof(settings.inittab_line));
  1171. #endif
  1172. }
  1173. else if (setting == "rlimit-nofile") {
  1174. string nofile_setting = read_setting_value(line_num, i, end, nullptr);
  1175. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_NOFILE);
  1176. parse_rlimit(nofile_setting, line_num, name, "rlimit-nofile", nofile_limits);
  1177. }
  1178. else if (setting == "rlimit-core") {
  1179. string core_setting = read_setting_value(line_num, i, end, nullptr);
  1180. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_CORE);
  1181. parse_rlimit(core_setting, line_num, name, "rlimit-core", nofile_limits);
  1182. }
  1183. else if (setting == "rlimit-data") {
  1184. string data_setting = read_setting_value(line_num, i, end, nullptr);
  1185. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_DATA);
  1186. parse_rlimit(data_setting, line_num, name, "rlimit-data", nofile_limits);
  1187. }
  1188. else if (setting == "rlimit-addrspace") {
  1189. #if defined(RLIMIT_AS)
  1190. string addrspace_setting = read_setting_value(line_num, i, end, nullptr);
  1191. service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_AS);
  1192. parse_rlimit(addrspace_setting, line_num, name, "rlimit-addrspace", nofile_limits);
  1193. #endif
  1194. }
  1195. else {
  1196. throw service_description_exc(name, "unknown setting: '" + setting + "'.", line_num);
  1197. }
  1198. }
  1199. } // namespace dinit_load
  1200. using dinit_load::process_service_file;