load-service.h 51 KB

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