10 #include <sys/resource.h>
14 #include "dinit-utmp.h"
15 #include "dinit-util.h"
17 struct service_flags_t
20 bool rw_ready : 1; // file system should be writable once this service starts
21 bool log_ready : 1; // syslog should be available once this service starts
23 // Other service options flags:
24 bool no_sigterm : 1; // do not send SIGTERM
25 bool runs_on_console : 1; // run "in the foreground"
26 bool starts_on_console : 1; // starts in the foreground
27 bool shares_console : 1; // run on console, but not exclusively
28 bool pass_cs_fd : 1; // pass this service a control socket connection via fd
29 bool start_interruptible : 1; // the startup of this service process is ok to interrupt with SIGINT
30 bool skippable : 1; // if interrupted the service is skipped (scripted services)
31 bool signal_process_only : 1; // signal the session process, not the whole group
33 service_flags_t() noexcept : rw_ready(false), log_ready(false), no_sigterm(false),
34 runs_on_console(false), starts_on_console(false), shares_console(false),
35 pass_cs_fd(false), start_interruptible(false), skippable(false), signal_process_only(false)
40 // Resource limits for a particular service & particular resource
41 struct service_rlimits
43 int resource_id; // RLIMIT_xxx identifying resource
48 service_rlimits(int id) : resource_id(id), soft_set(0), hard_set(0), limits({0,0}) { }
51 // Exception while loading a service
52 class service_load_exc
55 std::string service_name;
56 std::string exc_description;
59 service_load_exc(const std::string &serviceName, std::string &&desc) noexcept
60 : service_name(serviceName), exc_description(std::move(desc))
65 class service_not_found : public service_load_exc
68 service_not_found(const std::string &serviceName) noexcept
69 : service_load_exc(serviceName, "Service description not found.")
74 class service_cyclic_dependency : public service_load_exc
77 service_cyclic_dependency(const std::string &serviceName) noexcept
78 : service_load_exc(serviceName, "Has cyclic dependency.")
83 class service_description_exc : public service_load_exc
86 service_description_exc(const std::string &serviceName, std::string &&extraInfo) noexcept
87 : service_load_exc(serviceName, std::move(extraInfo))
92 namespace dinit_load {
94 using string = std::string;
95 using string_iterator = std::string::iterator;
97 // exception thrown when encountering a syntax issue when reading a setting value
98 class setting_exception
103 setting_exception(const std::string &&exc_info) : info(std::move(exc_info))
107 std::string &get_info()
114 // Utility function to skip white space. Returns an iterator at the
115 // first non-white-space position (or at end).
116 inline string_iterator skipws(string_iterator i, string_iterator end)
122 if (! isspace(*i, locale::classic())) {
130 // Convert a signal name to the corresponding signal number
131 inline int signal_name_to_number(std::string &signame)
133 if (signame == "HUP") return SIGHUP;
134 if (signame == "INT") return SIGINT;
135 if (signame == "QUIT") return SIGQUIT;
136 if (signame == "USR1") return SIGUSR1;
137 if (signame == "USR2") return SIGUSR2;
138 if (signame == "KILL") return SIGKILL;
142 // Read a setting name.
143 inline string read_setting_name(string_iterator & i, string_iterator end)
147 using std::use_facet;
149 const ctype<char> & facet = use_facet<ctype<char> >(locale::classic());
152 // Allow alphabetical characters, and dash (-) in setting name
153 while (i != end && (*i == '-' || *i == '.' || facet.is(ctype<char>::alpha, *i))) {
160 // Read a setting value.
162 // In general a setting value is a single-line string. It may contain multiple parts
163 // separated by white space (which is normally collapsed). A hash mark - # - denotes
164 // the end of the value and the beginning of a comment (it should be preceded by
167 // Part of a value may be quoted using double quote marks, which prevents collapse
168 // of whitespace and interpretation of most special characters (the quote marks will
169 // not be considered part of the value). A backslash can precede a character (such
170 // as '#' or '"' or another backslash) to remove its special meaning. Newline
171 // characters are not allowed in values and cannot be quoted.
173 // This function expects the string to be in an ASCII-compatible encoding (the "classic" locale).
175 // Throws setting_exception on error.
178 // service_name - the name of the service to which the setting applies
179 // i - reference to string iterator through the line
180 // end - iterator at end of line (not including newline character if any)
181 // part_positions - list of <int,int> to which the position of each setting value
182 // part will be added as [start,end). May be null.
183 inline string read_setting_value(string_iterator & i, string_iterator end,
184 std::list<std::pair<unsigned,unsigned>> * part_positions = nullptr)
192 bool new_part = true;
199 part_start = rval.length();
206 if (c == '\"') break;
207 else if (c == '\\') {
208 // A backslash escapes the following character.
215 throw setting_exception("Line end follows backslash escape character (`\\')");
224 // String wasn't terminated
225 throw setting_exception("Unterminated quoted string");
228 else if (c == '\\') {
230 part_start = rval.length();
233 // A backslash escapes the next character
239 throw setting_exception("Backslash escape (`\\') not followed by character");
242 else if (isspace(c, locale::classic())) {
243 if (! new_part && part_positions != nullptr) {
244 part_positions->emplace_back(part_start, rval.length());
249 if (*i == '#') break; // comment
250 rval += ' '; // collapse ws to a single space
254 // Possibly intended a comment; we require leading whitespace to reduce occurrence of accidental
255 // comments in setting values.
256 throw setting_exception("hashmark (`#') comment must be separated from setting value by whitespace");
260 part_start = rval.length();
269 if (part_positions != nullptr) {
270 part_positions->emplace_back(part_start, rval.length());
276 // Parse a userid parameter which may be a numeric user ID or a username. If a name, the
277 // userid is looked up via the system user database (getpwnam() function). In this case,
278 // the associated group is stored in the location specified by the group_p parameter if
280 inline uid_t parse_uid_param(const std::string ¶m, const std::string &service_name, const char *setting_name, gid_t *group_p)
282 const char * uid_err_msg = "Specified user id contains invalid numeric characters "
283 "or is outside allowed range.";
285 // Could be a name or a numeric id. But we should assume numeric first, just in case
286 // a user manages to give themselves a username that parses as a number.
289 // POSIX does not specify whether uid_t is a signed or unsigned type, but regardless
290 // is is probably safe to assume that valid values are positive. We'll also assert
291 // that the value range fits within "unsigned long long" since it seems unlikely
292 // that would ever not be the case.
293 static_assert((uintmax_t)std::numeric_limits<uid_t>::max()
294 <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "uid_t is too large");
295 unsigned long long v = std::stoull(param, &ind, 0);
296 if (v > static_cast<unsigned long long>(std::numeric_limits<uid_t>::max())
297 || ind != param.length()) {
298 throw service_description_exc(service_name, std::string(setting_name) + ": " + uid_err_msg);
302 catch (std::out_of_range &exc) {
303 throw service_description_exc(service_name, uid_err_msg);
305 catch (std::invalid_argument &exc) {
306 // Ok, so it doesn't look like a number: proceed...
310 struct passwd * pwent = getpwnam(param.c_str());
311 if (pwent == nullptr) {
312 // Maybe an error, maybe just no entry.
314 throw service_description_exc(service_name, std::string(setting_name) + ": Specified user \"" + param
315 + "\" does not exist in system database.");
318 throw service_description_exc(service_name, std::string("Error accessing user database: ")
324 *group_p = pwent->pw_gid;
327 return pwent->pw_uid;
330 inline gid_t parse_gid_param(const std::string ¶m, const char *setting_name, const std::string &service_name)
332 const char * gid_err_msg = "Specified group id contains invalid numeric characters or is "
333 "outside allowed range.";
335 // Could be a name or a numeric id. But we should assume numeric first, just in case
336 // a user manages to give themselves a username that parses as a number.
339 // POSIX does not specify whether uid_t is an signed or unsigned, but regardless
340 // is is probably safe to assume that valid values are positive. We'll also assume
341 // that the value range fits with "unsigned long long" since it seems unlikely
342 // that would ever not be the case.
343 static_assert((uintmax_t)std::numeric_limits<gid_t>::max()
344 <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "gid_t is too large");
345 unsigned long long v = std::stoull(param, &ind, 0);
346 if (v > static_cast<unsigned long long>(std::numeric_limits<gid_t>::max())
347 || ind != param.length()) {
348 throw service_description_exc(service_name, std::string(setting_name) + ": " + gid_err_msg);
352 catch (std::out_of_range &exc) {
353 throw service_description_exc(service_name, std::string(setting_name) + ": " + gid_err_msg);
355 catch (std::invalid_argument &exc) {
356 // Ok, so it doesn't look like a number: proceed...
360 struct group * grent = getgrnam(param.c_str());
361 if (grent == nullptr) {
362 // Maybe an error, maybe just no entry.
364 throw service_description_exc(service_name, std::string(setting_name) + ": Specified group \"" + param
365 + "\" does not exist in system database.");
368 throw service_description_exc(service_name, std::string("Error accessing group database: ")
373 return grent->gr_gid;
376 // Parse a time, specified as a decimal number of seconds (with optional fractional component after decimal
377 // point or decimal comma).
378 inline void parse_timespec(const std::string ¶mval, const std::string &servicename,
379 const char * paramname, timespec &ts)
381 decltype(ts.tv_sec) isec = 0;
382 decltype(ts.tv_nsec) insec = 0;
383 auto max_secs = std::numeric_limits<decltype(isec)>::max() / 10;
384 auto len = paramval.length();
386 for (i = 0; i < len; i++) {
387 char ch = paramval[i];
388 if (ch == '.' || ch == ',') {
392 if (ch < '0' || ch > '9') {
393 throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
395 // check for overflow
396 if (isec >= max_secs) {
397 throw service_description_exc(servicename, std::string("Too-large value for ") + paramname);
402 decltype(insec) insec_m = 100000000; // 10^8
403 for ( ; i < len; i++) {
404 char ch = paramval[i];
405 if (ch < '0' || ch > '9') {
406 throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
408 insec += (ch - '0') * insec_m;
415 // Parse an unsigned numeric parameter value
416 inline unsigned long long parse_unum_param(const std::string ¶m, const std::string &service_name,
417 unsigned long long max = std::numeric_limits<unsigned long long>::max())
419 const char * num_err_msg = "Specified value contains invalid numeric characters or is outside "
424 unsigned long long v = std::stoull(param, &ind, 0);
425 if (v > max || ind != param.length()) {
426 throw service_description_exc(service_name, num_err_msg);
430 catch (std::out_of_range &exc) {
431 throw service_description_exc(service_name, num_err_msg);
433 catch (std::invalid_argument &exc) {
434 throw service_description_exc(service_name, num_err_msg);
438 // In a vector, find or create rlimits for a particular resource type.
439 inline service_rlimits &find_rlimits(std::vector<service_rlimits> &all_rlimits, int resource_id)
441 for (service_rlimits &limits : all_rlimits) {
442 if (limits.resource_id == resource_id) {
447 all_rlimits.emplace_back(resource_id);
448 return all_rlimits.back();
451 // Parse resource limits setting (can specify both hard and soft limit).
452 inline void parse_rlimit(const std::string &line, const std::string &service_name, const char *param_name,
453 service_rlimits &rlimit)
456 // 4:5 - soft:hard limits both set
457 // 4:- soft set, hard set to unlimited
458 // 4: soft set, hard limit unchanged
459 // 4 soft and hard limit set to same limit
462 throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
465 const char *cline = line.c_str();
466 rlimit.hard_set = rlimit.soft_set = false;
469 const char * index = cline;
471 if (cline[0] != ':') {
472 rlimit.soft_set = true;
473 if (cline[0] == '-') {
474 rlimit.limits.rlim_cur = RLIM_INFINITY;
479 unsigned long long limit = std::strtoull(cline, &nindex, 0);
481 if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
482 if (index == cline) throw std::invalid_argument("");
483 rlimit.limits.rlim_cur = limit;
487 rlimit.hard_set = true;
488 rlimit.limits.rlim_max = rlimit.limits.rlim_cur;
493 throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
498 if (*index == 0) return;
501 rlimit.limits.rlim_max = RLIM_INFINITY;
503 throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
507 const char *hard_start = index;
509 unsigned long long limit = std::strtoull(cline, &nindex, 0);
511 if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
512 if (index == hard_start) throw std::invalid_argument("");
513 rlimit.limits.rlim_max = limit;
516 catch (std::invalid_argument &exc) {
517 throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
519 catch (std::out_of_range &exc) {
520 throw service_description_exc(service_name, std::string(param_name) + ": Too-large value.");
524 // Process an opened service file, line by line.
525 // name - the service name
526 // service_file - the service file input stream
527 // func - a function of the form:
528 // void(string &line, string &setting, string_iterator i, string_iterator end)
530 // line - the complete line (excluding newline character)
531 // setting - the setting name, from the beginning of the line
532 // i - iterator at the beginning of the setting value
533 // end - iterator marking the end of the line
535 // May throw service load exceptions or I/O exceptions if enabled on stream.
536 template <typename T>
537 void process_service_file(string name, std::istream &service_file, T func)
541 while (getline(service_file, line)) {
542 string::iterator i = line.begin();
543 string::iterator end = line.end();
548 continue; // comment line
550 string setting = read_setting_name(i, end);
552 if (i == end || (*i != '=' && *i != ':')) {
553 throw service_description_exc(name, "Badly formed line.");
555 i = skipws(++i, end);
557 func(line, setting, i, end);
562 // A wrapper type for service parameters. It is parameterised by dependency type.
563 template <class dep_type>
564 class service_settings_wrapper
566 template <typename A, typename B> using pair = std::pair<A,B>;
567 template <typename A> using list = std::list<A>;
572 list<pair<unsigned,unsigned>> command_offsets;
574 list<pair<unsigned,unsigned>> stop_command_offsets;
579 bool do_sub_vars = false;
581 service_type_t service_type = service_type_t::PROCESS;
582 std::list<dep_type> depends;
584 service_flags_t onstart_flags;
585 int term_signal = -1; // additional termination signal
586 bool auto_restart = false;
587 bool smooth_recovery = false;
589 int socket_perms = 0666;
590 // Note: Posix allows that uid_t and gid_t may be unsigned types, but eg chown uses -1 as an
591 // invalid value, so it's safe to assume that we can do the same:
592 uid_t socket_uid = -1;
593 gid_t socket_uid_gid = -1; // primary group of socket user if known
594 gid_t socket_gid = -1;
595 // Restart limit interval / count; default is 10 seconds, 3 restarts:
596 timespec restart_interval = { .tv_sec = 10, .tv_nsec = 0 };
597 int max_restarts = 3;
598 timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
599 timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
600 timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
601 std::vector<service_rlimits> rlimits;
603 int readiness_fd = -1; // readiness fd in service process
604 std::string readiness_var; // environment var to hold readiness fd
606 uid_t run_as_uid = -1;
607 gid_t run_as_uid_gid = -1; // primary group of "run as" uid if known
608 gid_t run_as_gid = -1;
610 string chain_to_name;
613 char inittab_id[sizeof(utmpx().ut_id)] = {0};
614 char inittab_line[sizeof(utmpx().ut_line)] = {0};
617 // Finalise settings (after processing all setting lines)
620 // If socket_gid hasn't been explicitly set, but the socket_uid was specified as a name (and
621 // we therefore recovered the primary group), use the primary group of the specified user.
622 if (socket_gid == (gid_t)-1) socket_gid = socket_uid_gid;
623 // likewise for "run as" gid/uid.
624 if (run_as_gid == (gid_t)-1) run_as_gid = run_as_uid_gid;
628 // Process a service description line. In general, parse the setting value and record the parsed value
629 // in a service settings wrapper object. Errors will be reported via service_description_exc exception.
632 // settings_wrapper : wrapper for service settings
633 // load_service_t : type of load service function/lambda (see below)
634 // process_dep_dir_t : type of process_dep_dir funciton/lambda (see below)
637 // settings : wrapper object for service settings
638 // name : name of the service being processed
639 // line : the current line of the service description file
640 // setting : the name of the setting (from the beginning of line)
641 // i : iterator at beginning of setting value (including whitespace)
642 // end : iterator at end of line
643 // load_service : function to load a service
644 // arguments: const char *service_name
645 // return: a value that can be used (with a dependency type) to construct a dependency
646 // in the 'depends' vector within the 'settings' object
647 // process_dep_dir : function to process a dependency directory
648 // arguments: decltype(settings.depends) &dependencies
649 // const string &waitsford - directory as specified in parameter
650 // dependency_type dep_type - type of dependency to add
651 template <typename settings_wrapper,
652 typename load_service_t,
653 typename process_dep_dir_t>
654 void process_service_line(settings_wrapper &settings, const char *name, string &line, string &setting,
655 string::iterator &i, string::iterator &end, load_service_t load_service,
656 process_dep_dir_t process_dep_dir)
658 if (setting == "command") {
659 settings.command = read_setting_value(i, end, &settings.command_offsets);
661 else if (setting == "working-dir") {
662 settings.working_dir = read_setting_value(i, end, nullptr);
664 else if (setting == "env-file") {
665 settings.env_file = read_setting_value(i, end, nullptr);
667 else if (setting == "socket-listen") {
668 settings.socket_path = read_setting_value(i, end, nullptr);
670 else if (setting == "socket-permissions") {
671 string sock_perm_str = read_setting_value(i, end, nullptr);
674 settings.socket_perms = std::stoi(sock_perm_str, &ind, 8);
675 if (ind != sock_perm_str.length()) {
676 throw std::logic_error("");
679 catch (std::logic_error &exc) {
680 throw service_description_exc(name, "socket-permissions: Badly-formed or "
681 "out-of-range numeric value");
684 else if (setting == "socket-uid") {
685 string sock_uid_s = read_setting_value(i, end, nullptr);
686 settings.socket_uid = parse_uid_param(sock_uid_s, name, "socket-uid", &settings.socket_uid_gid);
688 else if (setting == "socket-gid") {
689 string sock_gid_s = read_setting_value(i, end, nullptr);
690 settings.socket_gid = parse_gid_param(sock_gid_s, "socket-gid", name);
692 else if (setting == "stop-command") {
693 settings.stop_command = read_setting_value(i, end, &settings.stop_command_offsets);
695 else if (setting == "pid-file") {
696 settings.pid_file = read_setting_value(i, end);
698 else if (setting == "depends-on") {
699 string dependency_name = read_setting_value(i, end);
700 settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::REGULAR);
702 else if (setting == "depends-ms") {
703 string dependency_name = read_setting_value(i, end);
704 settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::MILESTONE);
706 else if (setting == "waits-for") {
707 string dependency_name = read_setting_value(i, end);
708 settings.depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::WAITS_FOR);
710 else if (setting == "waits-for.d") {
711 string waitsford = read_setting_value(i, end);
712 process_dep_dir(settings.depends, waitsford, dependency_type::WAITS_FOR);
714 else if (setting == "logfile") {
715 settings.logfile = read_setting_value(i, end);
717 else if (setting == "restart") {
718 string restart = read_setting_value(i, end);
719 settings.auto_restart = (restart == "yes" || restart == "true");
721 else if (setting == "smooth-recovery") {
722 string recovery = read_setting_value(i, end);
723 settings.smooth_recovery = (recovery == "yes" || recovery == "true");
725 else if (setting == "type") {
726 string type_str = read_setting_value(i, end);
727 if (type_str == "scripted") {
728 settings.service_type = service_type_t::SCRIPTED;
730 else if (type_str == "process") {
731 settings.service_type = service_type_t::PROCESS;
733 else if (type_str == "bgprocess") {
734 settings.service_type = service_type_t::BGPROCESS;
736 else if (type_str == "internal") {
737 settings.service_type = service_type_t::INTERNAL;
740 throw service_description_exc(name, "Service type must be one of: \"scripted\","
741 " \"process\", \"bgprocess\" or \"internal\"");
744 else if (setting == "options") {
745 std::list<std::pair<unsigned,unsigned>> indices;
746 string onstart_cmds = read_setting_value(i, end, &indices);
747 for (auto indexpair : indices) {
748 string option_txt = onstart_cmds.substr(indexpair.first,
749 indexpair.second - indexpair.first);
750 if (option_txt == "starts-rwfs") {
751 settings.onstart_flags.rw_ready = true;
753 else if (option_txt == "starts-log") {
754 settings.onstart_flags.log_ready = true;
756 else if (option_txt == "no-sigterm") {
757 settings.onstart_flags.no_sigterm = true;
759 else if (option_txt == "runs-on-console") {
760 settings.onstart_flags.runs_on_console = true;
761 // A service that runs on the console necessarily starts on console:
762 settings.onstart_flags.starts_on_console = true;
763 settings.onstart_flags.shares_console = false;
765 else if (option_txt == "starts-on-console") {
766 settings.onstart_flags.starts_on_console = true;
767 settings.onstart_flags.shares_console = false;
769 else if (option_txt == "shares-console") {
770 settings.onstart_flags.shares_console = true;
771 settings.onstart_flags.runs_on_console = false;
772 settings.onstart_flags.starts_on_console = false;
774 else if (option_txt == "pass-cs-fd") {
775 settings.onstart_flags.pass_cs_fd = true;
777 else if (option_txt == "start-interruptible") {
778 settings.onstart_flags.start_interruptible = true;
780 else if (option_txt == "skippable") {
781 settings.onstart_flags.skippable = true;
783 else if (option_txt == "signal-process-only") {
784 settings.onstart_flags.signal_process_only = true;
787 throw service_description_exc(name, "Unknown option: " + option_txt);
791 else if (setting == "load-options") {
792 std::list<std::pair<unsigned,unsigned>> indices;
793 string load_opts = read_setting_value(i, end, &indices);
794 for (auto indexpair : indices) {
795 string option_txt = load_opts.substr(indexpair.first,
796 indexpair.second - indexpair.first);
797 if (option_txt == "sub-vars") {
798 // substitute environment variables in command line
799 settings.do_sub_vars = true;
801 else if (option_txt == "no-sub-vars") {
802 settings.do_sub_vars = false;
805 throw service_description_exc(name, "Unknown load option: " + option_txt);
809 else if (setting == "term-signal" || setting == "termsignal") {
810 // Note: "termsignal" supported for legacy reasons.
811 string signame = read_setting_value(i, end, nullptr);
812 int signo = signal_name_to_number(signame);
814 throw service_description_exc(name, "Unknown/unsupported termination signal: "
818 settings.term_signal = signo;
821 else if (setting == "restart-limit-interval") {
822 string interval_str = read_setting_value(i, end, nullptr);
823 parse_timespec(interval_str, name, "restart-limit-interval", settings.restart_interval);
825 else if (setting == "restart-delay") {
826 string rsdelay_str = read_setting_value(i, end, nullptr);
827 parse_timespec(rsdelay_str, name, "restart-delay", settings.restart_delay);
829 else if (setting == "restart-limit-count") {
830 string limit_str = read_setting_value(i, end, nullptr);
831 settings.max_restarts = parse_unum_param(limit_str, name, std::numeric_limits<int>::max());
833 else if (setting == "stop-timeout") {
834 string stoptimeout_str = read_setting_value(i, end, nullptr);
835 parse_timespec(stoptimeout_str, name, "stop-timeout", settings.stop_timeout);
837 else if (setting == "start-timeout") {
838 string starttimeout_str = read_setting_value(i, end, nullptr);
839 parse_timespec(starttimeout_str, name, "start-timeout", settings.start_timeout);
841 else if (setting == "run-as") {
842 string run_as_str = read_setting_value(i, end, nullptr);
843 settings.run_as_uid = parse_uid_param(run_as_str, name, "run-as", &settings.run_as_uid_gid);
845 else if (setting == "chain-to") {
846 settings.chain_to_name = read_setting_value(i, end, nullptr);
848 else if (setting == "ready-notification") {
849 string notify_setting = read_setting_value(i, end, nullptr);
850 if (starts_with(notify_setting, "pipefd:")) {
851 settings.readiness_fd = parse_unum_param(notify_setting.substr(7 /* len 'pipefd:' */),
852 name, std::numeric_limits<int>::max());
854 else if (starts_with(notify_setting, "pipevar:")) {
855 settings.readiness_var = notify_setting.substr(8 /* len 'pipevar:' */);
856 if (settings.readiness_var.empty()) {
857 throw service_description_exc(name, "Invalid pipevar variable name "
858 "in ready-notification");
862 throw service_description_exc(name, "Unknown ready-notification setting: "
866 else if (setting == "inittab-id") {
867 string inittab_setting = read_setting_value(i, end, nullptr);
869 if (inittab_setting.length() > sizeof(settings.inittab_id)) {
870 throw service_description_exc(name, "inittab-id setting is too long");
872 strncpy(settings.inittab_id, inittab_setting.c_str(), sizeof(settings.inittab_id));
875 else if (setting == "inittab-line") {
876 string inittab_setting = read_setting_value(i, end, nullptr);
878 if (inittab_setting.length() > sizeof(settings.inittab_line)) {
879 throw service_description_exc(name, "inittab-line setting is too long");
881 strncpy(settings.inittab_line, inittab_setting.c_str(), sizeof(settings.inittab_line));
884 else if (setting == "rlimit-nofile") {
885 string nofile_setting = read_setting_value(i, end, nullptr);
886 service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_NOFILE);
887 parse_rlimit(line, name, "rlimit-nofile", nofile_limits);
889 else if (setting == "rlimit-core") {
890 string nofile_setting = read_setting_value(i, end, nullptr);
891 service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_CORE);
892 parse_rlimit(line, name, "rlimit-core", nofile_limits);
894 else if (setting == "rlimit-data") {
895 string nofile_setting = read_setting_value(i, end, nullptr);
896 service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_DATA);
897 parse_rlimit(line, name, "rlimit-data", nofile_limits);
899 else if (setting == "rlimit-addrspace") {
900 #if defined(RLIMIT_AS)
901 string nofile_setting = read_setting_value(i, end, nullptr);
902 service_rlimits &nofile_limits = find_rlimits(settings.rlimits, RLIMIT_AS);
903 parse_rlimit(line, name, "rlimit-addrspace", nofile_limits);
907 throw service_description_exc(name, "Unknown setting: '" + setting + "'.");
911 } // namespace dinit_load
913 using dinit_load::process_service_file;