load-service.h 59 KB

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