Fix setting group when only uid specified
[oweals/dinit.git] / src / includes / load-service.h
1 #include <iostream>
2 #include <list>
3 #include <limits>
4 #include <csignal>
5 #include <cstring>
6 #include <utility>
7
8 #include <sys/types.h>
9 #include <sys/time.h>
10 #include <sys/resource.h>
11 #include <grp.h>
12 #include <pwd.h>
13
14 #include "dinit-utmp.h"
15 #include "dinit-util.h"
16
17 struct service_flags_t
18 {
19     // on-start flags:
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
22
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
32
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)
36     {
37     }
38 };
39
40 // Resource limits for a particular service & particular resource
41 struct service_rlimits
42 {
43     int resource_id; // RLIMIT_xxx identifying resource
44     bool soft_set : 1;
45     bool hard_set : 1;
46     struct rlimit limits;
47
48     service_rlimits(int id) : resource_id(id), soft_set(0), hard_set(0), limits({0,0}) { }
49 };
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
58     protected:
59     service_load_exc(const std::string &serviceName, std::string &&desc) noexcept
60         : service_name(serviceName), exc_description(std::move(desc))
61     {
62     }
63 };
64
65 class service_not_found : public service_load_exc
66 {
67     public:
68     service_not_found(const std::string &serviceName) noexcept
69         : service_load_exc(serviceName, "Service description not found.")
70     {
71     }
72 };
73
74 class service_cyclic_dependency : public service_load_exc
75 {
76     public:
77     service_cyclic_dependency(const std::string &serviceName) noexcept
78         : service_load_exc(serviceName, "Has cyclic dependency.")
79     {
80     }
81 };
82
83 class service_description_exc : public service_load_exc
84 {
85     public:
86     service_description_exc(const std::string &serviceName, std::string &&extraInfo) noexcept
87         : service_load_exc(serviceName, std::move(extraInfo))
88     {
89     }
90 };
91
92 namespace dinit_load {
93
94 using string = std::string;
95 using string_iterator = std::string::iterator;
96
97 // exception thrown when encountering a syntax issue when reading a setting value
98 class setting_exception
99 {
100     std::string info;
101
102     public:
103     setting_exception(const std::string &&exc_info) : info(std::move(exc_info))
104     {
105     }
106
107     std::string &get_info()
108     {
109         return info;
110     }
111 };
112
113
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)
117 {
118     using std::locale;
119     using std::isspace;
120
121     while (i != end) {
122       if (! isspace(*i, locale::classic())) {
123         break;
124       }
125       ++i;
126     }
127     return i;
128 }
129
130 // Convert a signal name to the corresponding signal number
131 inline int signal_name_to_number(std::string &signame)
132 {
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;
139     return -1;
140 }
141
142 // Read a setting name.
143 inline string read_setting_name(string_iterator & i, string_iterator end)
144 {
145     using std::locale;
146     using std::ctype;
147     using std::use_facet;
148
149     const ctype<char> & facet = use_facet<ctype<char> >(locale::classic());
150
151     string rval;
152     // Allow alphabetical characters, and dash (-) in setting name
153     while (i != end && (*i == '-' || *i == '.' || facet.is(ctype<char>::alpha, *i))) {
154         rval += *i;
155         ++i;
156     }
157     return rval;
158 }
159
160 // Read a setting value.
161 //
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
165 // whitespace).
166 //
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.
172 //
173 // This function expects the string to be in an ASCII-compatible encoding (the "classic" locale).
174 //
175 // Throws setting_exception on error.
176 //
177 // Params:
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)
185 {
186     using std::locale;
187     using std::isspace;
188
189     i = skipws(i, end);
190
191     string rval;
192     bool new_part = true;
193     int part_start;
194
195     while (i != end) {
196         char c = *i;
197         if (c == '\"') {
198             if (new_part) {
199                 part_start = rval.length();
200                 new_part = false;
201             }
202             // quoted string
203             ++i;
204             while (i != end) {
205                 c = *i;
206                 if (c == '\"') break;
207                 else if (c == '\\') {
208                     // A backslash escapes the following character.
209                     ++i;
210                     if (i != end) {
211                         c = *i;
212                         rval += c;
213                     }
214                     else {
215                         throw setting_exception("Line end follows backslash escape character (`\\')");
216                     }
217                 }
218                 else {
219                     rval += c;
220                 }
221                 ++i;
222             }
223             if (i == end) {
224                 // String wasn't terminated
225                 throw setting_exception("Unterminated quoted string");
226             }
227         }
228         else if (c == '\\') {
229             if (new_part) {
230                 part_start = rval.length();
231                 new_part = false;
232             }
233             // A backslash escapes the next character
234             ++i;
235             if (i != end) {
236                 rval += *i;
237             }
238             else {
239                 throw setting_exception("Backslash escape (`\\') not followed by character");
240             }
241         }
242         else if (isspace(c, locale::classic())) {
243             if (! new_part && part_positions != nullptr) {
244                 part_positions->emplace_back(part_start, rval.length());
245                 new_part = true;
246             }
247             i = skipws(i, end);
248             if (i == end) break;
249             if (*i == '#') break; // comment
250             rval += ' ';  // collapse ws to a single space
251             continue;
252         }
253         else if (c == '#') {
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");
257         }
258         else {
259             if (new_part) {
260                 part_start = rval.length();
261                 new_part = false;
262             }
263             rval += c;
264         }
265         ++i;
266     }
267
268     // Got to end:
269     if (part_positions != nullptr) {
270         part_positions->emplace_back(part_start, rval.length());
271     }
272
273     return rval;
274 }
275
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
279 // it is not null.
280 inline uid_t parse_uid_param(const std::string &param, const std::string &service_name, const char *setting_name, gid_t *group_p)
281 {
282     const char * uid_err_msg = "Specified user id contains invalid numeric characters "
283             "or is outside allowed range.";
284
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.
287     std::size_t ind = 0;
288     try {
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);
299         }
300         return v;
301     }
302     catch (std::out_of_range &exc) {
303         throw service_description_exc(service_name, uid_err_msg);
304     }
305     catch (std::invalid_argument &exc) {
306         // Ok, so it doesn't look like a number: proceed...
307     }
308
309     errno = 0;
310     struct passwd * pwent = getpwnam(param.c_str());
311     if (pwent == nullptr) {
312         // Maybe an error, maybe just no entry.
313         if (errno == 0) {
314             throw service_description_exc(service_name, std::string(setting_name) + ": Specified user \"" + param
315                     + "\" does not exist in system database.");
316         }
317         else {
318             throw service_description_exc(service_name, std::string("Error accessing user database: ")
319                     + strerror(errno));
320         }
321     }
322
323     if (group_p) {
324         *group_p = pwent->pw_gid;
325     }
326
327     return pwent->pw_uid;
328 }
329
330 inline gid_t parse_gid_param(const std::string &param, const char *setting_name, const std::string &service_name)
331 {
332     const char * gid_err_msg = "Specified group id contains invalid numeric characters or is "
333             "outside allowed range.";
334
335     // Could be a name or a numeric id. But we should assume numeric first, just in case
336     // a user manages to give themselves a username that parses as a number.
337     std::size_t ind = 0;
338     try {
339         // POSIX does not specify whether uid_t is 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);
349         }
350         return v;
351     }
352     catch (std::out_of_range &exc) {
353         throw service_description_exc(service_name, std::string(setting_name) + ": " + gid_err_msg);
354     }
355     catch (std::invalid_argument &exc) {
356         // Ok, so it doesn't look like a number: proceed...
357     }
358
359     errno = 0;
360     struct group * grent = getgrnam(param.c_str());
361     if (grent == nullptr) {
362         // Maybe an error, maybe just no entry.
363         if (errno == 0) {
364             throw service_description_exc(service_name, std::string(setting_name) + ": Specified group \"" + param
365                     + "\" does not exist in system database.");
366         }
367         else {
368             throw service_description_exc(service_name, std::string("Error accessing group database: ")
369                     + strerror(errno));
370         }
371     }
372
373     return grent->gr_gid;
374 }
375
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 &paramval, const std::string &servicename,
379         const char * paramname, timespec &ts)
380 {
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();
385     decltype(len) i;
386     for (i = 0; i < len; i++) {
387         char ch = paramval[i];
388         if (ch == '.' || ch == ',') {
389             i++;
390             break;
391         }
392         if (ch < '0' || ch > '9') {
393             throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
394         }
395         // check for overflow
396         if (isec >= max_secs) {
397            throw service_description_exc(servicename, std::string("Too-large value for ") + paramname);
398         }
399         isec *= 10;
400         isec += ch - '0';
401     }
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);
407         }
408         insec += (ch - '0') * insec_m;
409         insec_m /= 10;
410     }
411     ts.tv_sec = isec;
412     ts.tv_nsec = insec;
413 }
414
415 // Parse an unsigned numeric parameter value
416 inline unsigned long long parse_unum_param(const std::string &param, const std::string &service_name,
417         unsigned long long max = std::numeric_limits<unsigned long long>::max())
418 {
419     const char * num_err_msg = "Specified value contains invalid numeric characters or is outside "
420             "allowed range.";
421
422     std::size_t ind = 0;
423     try {
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);
427         }
428         return v;
429     }
430     catch (std::out_of_range &exc) {
431         throw service_description_exc(service_name, num_err_msg);
432     }
433     catch (std::invalid_argument &exc) {
434         throw service_description_exc(service_name, num_err_msg);
435     }
436 }
437
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)
440 {
441     for (service_rlimits &limits : all_rlimits) {
442         if (limits.resource_id == resource_id) {
443             return limits;
444         }
445     }
446
447     all_rlimits.emplace_back(resource_id);
448     return all_rlimits.back();
449 }
450
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)
454 {
455     // Examples:
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
460
461     if (line.empty()) {
462         throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
463     }
464
465     const char *cline = line.c_str();
466     rlimit.hard_set = rlimit.soft_set = false;
467
468     try {
469         const char * index = cline;
470         errno = 0;
471         if (cline[0] != ':') {
472             rlimit.soft_set = true;
473             if (cline[0] == '-') {
474                 rlimit.limits.rlim_cur = RLIM_INFINITY;
475                 index = cline + 1;
476             }
477             else {
478                 char *nindex;
479                 unsigned long long limit = std::strtoull(cline, &nindex, 0);
480                 index = nindex;
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;
484             }
485
486             if (*index == 0) {
487                 rlimit.hard_set = true;
488                 rlimit.limits.rlim_max = rlimit.limits.rlim_cur;
489                 return;
490             }
491
492             if (*index != ':') {
493                 throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
494             }
495         }
496
497         index++;
498         if (*index == 0) return;
499
500         if (*index == '-') {
501             rlimit.limits.rlim_max = RLIM_INFINITY;
502             if (index[1] != 0) {
503                 throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
504             }
505         }
506         else {
507             const char *hard_start = index;
508             char *nindex;
509             unsigned long long limit = std::strtoull(cline, &nindex, 0);
510             index = nindex;
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;
514         }
515     }
516     catch (std::invalid_argument &exc) {
517         throw service_description_exc(service_name, std::string(param_name) + ": Bad value.");
518     }
519     catch (std::out_of_range &exc) {
520         throw service_description_exc(service_name, std::string(param_name) + ": Too-large value.");
521     }
522 }
523
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)
529 //           Called with:
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
534 //
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)
538 {
539     string line;
540
541     while (getline(service_file, line)) {
542         string::iterator i = line.begin();
543         string::iterator end = line.end();
544
545         i = skipws(i, end);
546         if (i != end) {
547             if (*i == '#') {
548                 continue;  // comment line
549             }
550             string setting = read_setting_name(i, end);
551             i = skipws(i, end);
552             if (i == end || (*i != '=' && *i != ':')) {
553                 throw service_description_exc(name, "Badly formed line.");
554             }
555             i = skipws(++i, end);
556
557             func(line, setting, i, end);
558         }
559     }
560 }
561
562 // A wrapper type for service parameters. It is parameterised by dependency type.
563 template <class dep_type>
564 class service_settings_wrapper
565 {
566     template <typename A, typename B> using pair = std::pair<A,B>;
567     template <typename A> using list = std::list<A>;
568
569     public:
570
571     string command;
572     list<pair<unsigned,unsigned>> command_offsets;
573     string stop_command;
574     list<pair<unsigned,unsigned>> stop_command_offsets;
575     string working_dir;
576     string pid_file;
577     string env_file;
578
579     bool do_sub_vars = false;
580
581     service_type_t service_type = service_type_t::PROCESS;
582     std::list<dep_type> depends;
583     string logfile;
584     service_flags_t onstart_flags;
585     int term_signal = -1;  // additional termination signal
586     bool auto_restart = false;
587     bool smooth_recovery = false;
588     string socket_path;
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;
602
603     int readiness_fd = -1;      // readiness fd in service process
604     std::string readiness_var;  // environment var to hold readiness fd
605
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;
609
610     string chain_to_name;
611
612     #if USE_UTMPX
613     char inittab_id[sizeof(utmpx().ut_id)] = {0};
614     char inittab_line[sizeof(utmpx().ut_line)] = {0};
615     #endif
616
617     // Finalise settings (after processing all setting lines)
618     void finalise()
619     {
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;
625     }
626 };
627
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.
630 //
631 // type parameters:
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)
635 //
636 // parameters:
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)
657 {
658     if (setting == "command") {
659         settings.command = read_setting_value(i, end, &settings.command_offsets);
660     }
661     else if (setting == "working-dir") {
662         settings.working_dir = read_setting_value(i, end, nullptr);
663     }
664     else if (setting == "env-file") {
665         settings.env_file = read_setting_value(i, end, nullptr);
666     }
667     else if (setting == "socket-listen") {
668         settings.socket_path = read_setting_value(i, end, nullptr);
669     }
670     else if (setting == "socket-permissions") {
671         string sock_perm_str = read_setting_value(i, end, nullptr);
672         std::size_t ind = 0;
673         try {
674             settings.socket_perms = std::stoi(sock_perm_str, &ind, 8);
675             if (ind != sock_perm_str.length()) {
676                 throw std::logic_error("");
677             }
678         }
679         catch (std::logic_error &exc) {
680             throw service_description_exc(name, "socket-permissions: Badly-formed or "
681                     "out-of-range numeric value");
682         }
683     }
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);
687     }
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);
691     }
692     else if (setting == "stop-command") {
693         settings.stop_command = read_setting_value(i, end, &settings.stop_command_offsets);
694     }
695     else if (setting == "pid-file") {
696         settings.pid_file = read_setting_value(i, end);
697     }
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);
701     }
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);
705     }
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);
709     }
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);
713     }
714     else if (setting == "logfile") {
715         settings.logfile = read_setting_value(i, end);
716     }
717     else if (setting == "restart") {
718         string restart = read_setting_value(i, end);
719         settings.auto_restart = (restart == "yes" || restart == "true");
720     }
721     else if (setting == "smooth-recovery") {
722         string recovery = read_setting_value(i, end);
723         settings.smooth_recovery = (recovery == "yes" || recovery == "true");
724     }
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;
729         }
730         else if (type_str == "process") {
731             settings.service_type = service_type_t::PROCESS;
732         }
733         else if (type_str == "bgprocess") {
734             settings.service_type = service_type_t::BGPROCESS;
735         }
736         else if (type_str == "internal") {
737             settings.service_type = service_type_t::INTERNAL;
738         }
739         else {
740             throw service_description_exc(name, "Service type must be one of: \"scripted\","
741                 " \"process\", \"bgprocess\" or \"internal\"");
742         }
743     }
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;
752             }
753             else if (option_txt == "starts-log") {
754                 settings.onstart_flags.log_ready = true;
755             }
756             else if (option_txt == "no-sigterm") {
757                 settings.onstart_flags.no_sigterm = true;
758             }
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;
764             }
765             else if (option_txt == "starts-on-console") {
766                 settings.onstart_flags.starts_on_console = true;
767                 settings.onstart_flags.shares_console = false;
768             }
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;
773             }
774             else if (option_txt == "pass-cs-fd") {
775                 settings.onstart_flags.pass_cs_fd = true;
776             }
777             else if (option_txt == "start-interruptible") {
778                 settings.onstart_flags.start_interruptible = true;
779             }
780             else if (option_txt == "skippable") {
781                 settings.onstart_flags.skippable = true;
782             }
783             else if (option_txt == "signal-process-only") {
784                 settings.onstart_flags.signal_process_only = true;
785             }
786             else {
787                 throw service_description_exc(name, "Unknown option: " + option_txt);
788             }
789         }
790     }
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;
800             }
801             else if (option_txt == "no-sub-vars") {
802                 settings.do_sub_vars = false;
803             }
804             else {
805                 throw service_description_exc(name, "Unknown load option: " + option_txt);
806             }
807         }
808     }
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);
813         if (signo == -1) {
814             throw service_description_exc(name, "Unknown/unsupported termination signal: "
815                     + signame);
816         }
817         else {
818             settings.term_signal = signo;
819         }
820     }
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);
824     }
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);
828     }
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());
832     }
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);
836     }
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);
840     }
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);
844     }
845     else if (setting == "chain-to") {
846         settings.chain_to_name = read_setting_value(i, end, nullptr);
847     }
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());
853         }
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");
859             }
860         }
861         else {
862             throw service_description_exc(name, "Unknown ready-notification setting: "
863                     + notify_setting);
864         }
865     }
866     else if (setting == "inittab-id") {
867         string inittab_setting = read_setting_value(i, end, nullptr);
868         #if USE_UTMPX
869         if (inittab_setting.length() > sizeof(settings.inittab_id)) {
870             throw service_description_exc(name, "inittab-id setting is too long");
871         }
872         strncpy(settings.inittab_id, inittab_setting.c_str(), sizeof(settings.inittab_id));
873         #endif
874     }
875     else if (setting == "inittab-line") {
876         string inittab_setting = read_setting_value(i, end, nullptr);
877         #if USE_UTMPX
878         if (inittab_setting.length() > sizeof(settings.inittab_line)) {
879             throw service_description_exc(name, "inittab-line setting is too long");
880         }
881         strncpy(settings.inittab_line, inittab_setting.c_str(), sizeof(settings.inittab_line));
882         #endif
883     }
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);
888     }
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);
893     }
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);
898     }
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);
904         #endif
905     }
906     else {
907         throw service_description_exc(name, "Unknown setting: '" + setting + "'.");
908     }
909 }
910
911 } // namespace dinit_load
912
913 using dinit_load::process_service_file;