25e2b4a192038051347fe74679098eac8ac5304b
[oweals/dinit.git] / src / load-service.cc
1 #include <algorithm>
2 #include <string>
3 #include <fstream>
4 #include <locale>
5 #include <iostream>
6 #include <limits>
7
8 #include <cstring>
9 #include <cstdlib>
10
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <pwd.h>
14 #include <grp.h>
15 #include <dirent.h>
16
17 #include "proc-service.h"
18 #include "dinit-log.h"
19 #include "dinit-util.h"
20 #include "dinit-utmp.h"
21
22 using string = std::string;
23 using string_iterator = std::string::iterator;
24
25 // Convert a signal name to the corresponding signal number
26 static int signal_name_to_number(std::string &signame)
27 {
28     if (signame == "HUP") return SIGHUP;
29     if (signame == "INT") return SIGINT;
30     if (signame == "QUIT") return SIGQUIT;
31     if (signame == "USR1") return SIGUSR1;
32     if (signame == "USR2") return SIGUSR2;
33     if (signame == "KILL") return SIGKILL;
34     return -1;
35 }
36
37 static const char * uid_err_msg = "Specified user id contains invalid numeric characters "
38         "or is outside allowed range.";
39
40 // Parse a userid parameter which may be a numeric user ID or a username. If a name, the
41 // userid is looked up via the system user database (getpwnam() function). In this case,
42 // the associated group is stored in the location specified by the group_p parameter iff
43 // it is not null and iff it contains the value -1.
44 static uid_t parse_uid_param(const std::string &param, const std::string &service_name, gid_t *group_p)
45 {
46     // Could be a name or a numeric id. But we should assume numeric first, just in case
47     // a user manages to give themselves a username that parses as a number.
48     std::size_t ind = 0;
49     try {
50         // POSIX does not specify whether uid_t is an signed or unsigned, but regardless
51         // is is probably safe to assume that valid values are positive. We'll also assert
52         // that the value range fits within "unsigned long long" since it seems unlikely
53         // that would ever not be the case.
54         static_assert((uintmax_t)std::numeric_limits<uid_t>::max()
55                 <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "uid_t is too large");
56         unsigned long long v = std::stoull(param, &ind, 0);
57         if (v > static_cast<unsigned long long>(std::numeric_limits<uid_t>::max())
58                 || ind != param.length()) {
59             throw service_description_exc(service_name, uid_err_msg);
60         }
61         return v;
62     }
63     catch (std::out_of_range &exc) {
64         throw service_description_exc(service_name, uid_err_msg);
65     }
66     catch (std::invalid_argument &exc) {
67         // Ok, so it doesn't look like a number: proceed...
68     }
69
70     errno = 0;
71     struct passwd * pwent = getpwnam(param.c_str());
72     if (pwent == nullptr) {
73         // Maybe an error, maybe just no entry.
74         if (errno == 0) {
75             throw service_description_exc(service_name, "Specified user \"" + param
76                     + "\" does not exist in system database.");
77         }
78         else {
79             throw service_description_exc(service_name, std::string("Error accessing user database: ")
80                     + strerror(errno));
81         }
82     }
83     
84     if (group_p && *group_p != (gid_t)-1) {
85         *group_p = pwent->pw_gid;
86     }
87     
88     return pwent->pw_uid;
89 }
90
91 static const char * num_err_msg = "Specified value contains invalid numeric characters or is outside "
92         "allowed range.";
93
94 // Parse an unsigned numeric parameter value
95 static unsigned long long parse_unum_param(const std::string &param, const std::string &service_name,
96         unsigned long long max = std::numeric_limits<unsigned long long>::max())
97 {
98     std::size_t ind = 0;
99     try {
100         unsigned long long v = std::stoull(param, &ind, 0);
101         if (v > max || ind != param.length()) {
102             throw service_description_exc(service_name, num_err_msg);
103         }
104         return v;
105     }
106     catch (std::out_of_range &exc) {
107         throw service_description_exc(service_name, num_err_msg);
108     }
109     catch (std::invalid_argument &exc) {
110         throw service_description_exc(service_name, num_err_msg);
111     }
112 }
113
114 static const char * gid_err_msg = "Specified group id contains invalid numeric characters or is "
115         "outside allowed range.";
116
117 static gid_t parse_gid_param(const std::string &param, const std::string &service_name)
118 {
119     // Could be a name or a numeric id. But we should assume numeric first, just in case
120     // a user manages to give themselves a username that parses as a number.
121     std::size_t ind = 0;
122     try {
123         // POSIX does not specify whether uid_t is an signed or unsigned, but regardless
124         // is is probably safe to assume that valid values are positive. We'll also assume
125         // that the value range fits with "unsigned long long" since it seems unlikely
126         // that would ever not be the case.
127         static_assert((uintmax_t)std::numeric_limits<gid_t>::max()
128                 <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "gid_t is too large");
129         unsigned long long v = std::stoull(param, &ind, 0);
130         if (v > static_cast<unsigned long long>(std::numeric_limits<gid_t>::max())
131                 || ind != param.length()) {
132             throw service_description_exc(service_name, gid_err_msg);
133         }
134         return v;
135     }
136     catch (std::out_of_range &exc) {
137         throw service_description_exc(service_name, gid_err_msg);
138     }
139     catch (std::invalid_argument &exc) {
140         // Ok, so it doesn't look like a number: proceed...
141     }
142
143     errno = 0;
144     struct group * grent = getgrnam(param.c_str());
145     if (grent == nullptr) {
146         // Maybe an error, maybe just no entry.
147         if (errno == 0) {
148             throw service_description_exc(service_name, "Specified group \"" + param
149                     + "\" does not exist in system database.");
150         }
151         else {
152             throw service_description_exc(service_name, std::string("Error accessing group database: ")
153                     + strerror(errno));
154         }
155     }
156     
157     return grent->gr_gid;
158 }
159
160 // Parse a time, specified as a decimal number of seconds (with optional fractional component after decimal
161 // point or decimal comma).
162 static void parse_timespec(const std::string &paramval, const std::string &servicename,
163         const char * paramname, timespec &ts)
164 {
165     decltype(ts.tv_sec) isec = 0;
166     decltype(ts.tv_nsec) insec = 0;
167     auto max_secs = std::numeric_limits<decltype(isec)>::max() / 10;
168     auto len = paramval.length();
169     decltype(len) i;
170     for (i = 0; i < len; i++) {
171         char ch = paramval[i];
172         if (ch == '.' || ch == ',') {
173             i++;
174             break;
175         }
176         if (ch < '0' || ch > '9') {
177             throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
178         }
179         // check for overflow
180         if (isec >= max_secs) {
181            throw service_description_exc(servicename, std::string("Too-large value for ") + paramname);
182         }
183         isec *= 10;
184         isec += ch - '0';
185     }
186     decltype(insec) insec_m = 100000000; // 10^8
187     for ( ; i < len; i++) {
188         char ch = paramval[i];
189         if (ch < '0' || ch > '9') {
190             throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
191         }
192         insec += (ch - '0') * insec_m;
193         insec_m /= 10;
194     }
195     ts.tv_sec = isec;
196     ts.tv_nsec = insec;
197 }
198
199 // In a vector, find or create rlimits for a particular resource type.
200 static service_rlimits &find_rlimits(std::vector<service_rlimits> all_rlimits, int resource_id)
201 {
202     for (service_rlimits &limits : all_rlimits) {
203         if (limits.resource_id == resource_id) {
204             return limits;
205         }
206     }
207
208     all_rlimits.emplace_back(resource_id);
209     return all_rlimits.back();
210 }
211
212 // Parse resource limits setting (can specify both hard and soft limit).
213 static void parse_rlimit(const std::string &line, const std::string &service_name, const char *param_name,
214         service_rlimits &rlimit)
215 {
216     // Examples:
217     // 4:5 - soft:hard limits both set
218     // 4:-   soft set, hard set to unlimited
219     // 4:    soft set, hard limit unchanged
220     // 4     soft and hard limit set to same limit
221
222     if (line.empty()) {
223         throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
224     }
225
226     const char *cline = line.c_str();
227     rlimit.hard_set = rlimit.soft_set = false;
228
229     try {
230         char * index;
231         errno = 0;
232         if (cline[0] != ':') {
233             rlimit.soft_set = true;
234             if (cline[0] == '-') {
235                 rlimit.limits.rlim_cur = RLIM_INFINITY;
236             }
237             else {
238                 unsigned long long limit = std::strtoull(cline, &index, 0);
239                 if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
240                 if (index == cline) throw std::invalid_argument("");
241                 rlimit.limits.rlim_cur = limit;
242             }
243
244             if (*index == 0) {
245                 rlimit.hard_set = true;
246                 rlimit.limits.rlim_max = rlimit.limits.rlim_cur;
247                 return;
248             }
249         }
250
251         if (*index != ':') {
252             throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
253         }
254
255         index++;
256         if (*index == 0) return;
257
258         if (*index == '-') {
259             rlimit.limits.rlim_max = RLIM_INFINITY;
260         }
261         else {
262             char *hard_start = index;
263             unsigned long long limit = std::strtoull(cline, &index, 0);
264             if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
265             if (index == hard_start) throw std::invalid_argument("");
266             rlimit.limits.rlim_max = limit;
267         }
268     }
269     catch (std::invalid_argument &exc) {
270         throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
271     }
272     catch (std::out_of_range &exc) {
273         throw service_description_exc(service_name, std::string("Too-large value for ") + param_name);
274     }
275 }
276
277 // Perform environment variable substitution on a command line, if specified.
278 //   line -  the string storing the command and arguments
279 //   offsets - the [start,end) pair of offsets of the command and each argument within the string
280 //
281 static void do_env_subst(std::string &line, std::list<std::pair<unsigned,unsigned>> &offsets,
282         bool do_sub_vars)
283 {
284     if (do_sub_vars) {
285         auto i = offsets.begin();
286         std::string r_line = line.substr(i->first, i->second - i->first); // copy command part
287         for (++i; i != offsets.end(); ++i) {
288             auto &offset_pair = *i;
289             if (line[offset_pair.first] == '$') {
290                 // Do subsitution for this part:
291                 auto env_name = line.substr(offset_pair.first + 1,
292                         offset_pair.second - offset_pair.first - 1);
293                 char *env_val = getenv(env_name.c_str());
294                 if (env_val != nullptr) {
295                     auto val_len = strlen(env_val);
296                     r_line += " ";
297                     offset_pair.first = r_line.length();
298                     offset_pair.second = offset_pair.first + val_len;
299                     r_line += env_val;
300                 }
301                 else {
302                     // specified enironment variable not set: treat as an empty string
303                     offset_pair.first = r_line.length();
304                     offset_pair.second = offset_pair.first;
305                 }
306             }
307             else {
308                 // No subsitution for this part:
309                 r_line += " ";
310                 auto new_offs = r_line.length();
311                 auto len = offset_pair.second - offset_pair.first;
312                 r_line += line.substr(offset_pair.first, len);
313                 offset_pair.first = new_offs;
314                 offset_pair.second = new_offs + len;
315             }
316         }
317         line = std::move(r_line);
318     }
319 }
320
321 // Process a dependency directory - filenames contained within correspond to service names which
322 // are loaded and added as a dependency of the given type. Expected use is with a directory
323 // containing symbolic links to other service descriptions, but this isn't required.
324 // Failure to read the directory contents, or to find a service listed within, is not considered
325 // a fatal error.
326 static void process_dep_dir(dirload_service_set &sset,
327         const char *servicename,
328         const string &service_filename,
329         std::list<prelim_dep> &deplist, const std::string &depdirpath,
330         dependency_type dep_type)
331 {
332     std::string depdir_fname = combine_paths(parent_path(service_filename), depdirpath.c_str());
333
334     DIR *depdir = opendir(depdir_fname.c_str());
335     if (depdir == nullptr) {
336         log(loglevel_t::WARN, "Could not open dependency directory '", depdir_fname,
337                 "' for ", servicename, " service.");
338         return;
339     }
340
341     errno = 0;
342     dirent * dent = readdir(depdir);
343     while (dent != nullptr) {
344         char * name =  dent->d_name;
345         if (name[0] != '.') {
346             try {
347                 service_record * sr = sset.load_service(name);
348                 deplist.emplace_back(sr, dep_type);
349             }
350             catch (service_not_found &) {
351                 log(loglevel_t::WARN, "Ignoring unresolved dependency '", name,
352                         "' in dependency directory '", depdirpath,
353                         "' for ", servicename, " service.");
354             }
355         }
356         dent = readdir(depdir);
357     }
358
359     if (errno != 0) {
360         log(loglevel_t::WARN, "Error reading dependency directory '", depdirpath,
361                 "' for ", servicename, " service.");
362     }
363 }
364
365 // Check if one string starts with another
366 static bool starts_with(string s, const char *prefix)
367 {
368     const char * sp = s.c_str();
369     while (*sp != 0 && *prefix != 0) {
370         if (*sp != *prefix) return false;
371         sp++; prefix++;
372     }
373     return *prefix == 0;
374 }
375
376 // Find a service record, or load it from file. If the service has
377 // dependencies, load those also.
378 //
379 // Might throw a ServiceLoadExc exception if a dependency cycle is found or if another
380 // problem occurs (I/O error, service description not found etc). Throws std::bad_alloc
381 // if a memory allocation failure occurs.
382 //
383 service_record * dirload_service_set::load_service(const char * name)
384 {
385     using std::string;
386     using std::ifstream;
387     using std::ios;
388     using std::ios_base;
389     using std::locale;
390     using std::isspace;
391     
392     using std::list;
393     using std::pair;
394     
395     using namespace dinit_load;
396
397     // First try and find an existing record...
398     service_record * rval = find_service(string(name));
399     if (rval != 0) {
400         if (rval->is_dummy()) {
401             throw service_cyclic_dependency(name);
402         }
403         return rval;
404     }
405
406     ifstream service_file;
407     string service_filename;
408
409     // Couldn't find one. Have to load it.
410     for (auto &service_dir : service_dirs) {
411         service_filename = service_dir.get_dir();
412         if (*(service_filename.rbegin()) != '/') {
413             service_filename += '/';
414         }
415         service_filename += name;
416
417         service_file.open(service_filename.c_str(), ios::in);
418         if (service_file) break;
419     }
420     
421     if (! service_file) {
422         throw service_not_found(string(name));
423     }
424
425     string command;
426     list<pair<unsigned,unsigned>> command_offsets;
427     string stop_command;
428     list<pair<unsigned,unsigned>> stop_command_offsets;
429     string working_dir;
430     string pid_file;
431
432     bool do_sub_vars = false;
433
434     service_type_t service_type = service_type_t::PROCESS;
435     std::list<prelim_dep> depends;
436     string logfile;
437     service_flags_t onstart_flags;
438     int term_signal = -1;  // additional termination signal
439     bool auto_restart = false;
440     bool smooth_recovery = false;
441     string socket_path;
442     int socket_perms = 0666;
443     // Note: Posix allows that uid_t and gid_t may be unsigned types, but eg chown uses -1 as an
444     // invalid value, so it's safe to assume that we can do the same:
445     uid_t socket_uid = -1;
446     gid_t socket_gid = -1;
447     // Restart limit interval / count; default is 10 seconds, 3 restarts:
448     timespec restart_interval = { .tv_sec = 10, .tv_nsec = 0 };
449     int max_restarts = 3;
450     timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
451     timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
452     timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
453     std::vector<service_rlimits> rlimits;
454     
455     int readiness_fd = -1;      // readiness fd in service process
456     std::string readiness_var;  // environment var to hold readiness fd
457
458     uid_t run_as_uid = -1;
459     gid_t run_as_gid = -1;
460
461     string chain_to_name;
462
463     #if USE_UTMPX
464     char inittab_id[sizeof(utmpx().ut_id)] = {0};
465     char inittab_line[sizeof(utmpx().ut_line)] = {0};
466     #endif
467
468     string line;
469     service_file.exceptions(ios::badbit | ios::failbit);
470     
471     // Add a dummy service record now to prevent infinite recursion in case of cyclic dependency.
472     // We replace this with the real service later (or remove it if we find a configuration error).
473     rval = new service_record(this, string(name));
474     add_service(rval);
475     
476     try {
477         // getline can set failbit if it reaches end-of-file, we don't want an exception in that case:
478         service_file.exceptions(ios::badbit);
479         
480         process_service_file(name, service_file,
481                 [&](string &line, string &setting, string_iterator &i, string_iterator &end) -> void {
482             if (setting == "command") {
483                 command = read_setting_value(i, end, &command_offsets);
484             }
485             else if (setting == "working-dir") {
486                 working_dir = read_setting_value(i, end, nullptr);
487             }
488             else if (setting == "socket-listen") {
489                 socket_path = read_setting_value(i, end, nullptr);
490             }
491             else if (setting == "socket-permissions") {
492                 string sock_perm_str = read_setting_value(i, end, nullptr);
493                 std::size_t ind = 0;
494                 try {
495                     socket_perms = std::stoi(sock_perm_str, &ind, 8);
496                     if (ind != sock_perm_str.length()) {
497                         throw std::logic_error("");
498                     }
499                 }
500                 catch (std::logic_error &exc) {
501                     throw service_description_exc(name, "socket-permissions: Badly-formed or "
502                             "out-of-range numeric value");
503                 }
504             }
505             else if (setting == "socket-uid") {
506                 string sock_uid_s = read_setting_value(i, end, nullptr);
507                 socket_uid = parse_uid_param(sock_uid_s, name, &socket_gid);
508             }
509             else if (setting == "socket-gid") {
510                 string sock_gid_s = read_setting_value(i, end, nullptr);
511                 socket_gid = parse_gid_param(sock_gid_s, name);
512             }
513             else if (setting == "stop-command") {
514                 stop_command = read_setting_value(i, end, &stop_command_offsets);
515             }
516             else if (setting == "pid-file") {
517                 pid_file = read_setting_value(i, end);
518             }
519             else if (setting == "depends-on") {
520                 string dependency_name = read_setting_value(i, end);
521                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::REGULAR);
522             }
523             else if (setting == "depends-ms") {
524                 string dependency_name = read_setting_value(i, end);
525                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::MILESTONE);
526             }
527             else if (setting == "waits-for") {
528                 string dependency_name = read_setting_value(i, end);
529                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::WAITS_FOR);
530             }
531             else if (setting == "waits-for.d") {
532                 string waitsford = read_setting_value(i, end);
533                 process_dep_dir(*this, name, service_filename, depends, waitsford,
534                         dependency_type::WAITS_FOR);
535             }
536             else if (setting == "logfile") {
537                 logfile = read_setting_value(i, end);
538             }
539             else if (setting == "restart") {
540                 string restart = read_setting_value(i, end);
541                 auto_restart = (restart == "yes" || restart == "true");
542             }
543             else if (setting == "smooth-recovery") {
544                 string recovery = read_setting_value(i, end);
545                 smooth_recovery = (recovery == "yes" || recovery == "true");
546             }
547             else if (setting == "type") {
548                 string type_str = read_setting_value(i, end);
549                 if (type_str == "scripted") {
550                     service_type = service_type_t::SCRIPTED;
551                 }
552                 else if (type_str == "process") {
553                     service_type = service_type_t::PROCESS;
554                 }
555                 else if (type_str == "bgprocess") {
556                     service_type = service_type_t::BGPROCESS;
557                 }
558                 else if (type_str == "internal") {
559                     service_type = service_type_t::INTERNAL;
560                 }
561                 else {
562                     throw service_description_exc(name, "Service type must be one of: \"scripted\","
563                         " \"process\", \"bgprocess\" or \"internal\"");
564                 }
565             }
566             else if (setting == "options") {
567                 std::list<std::pair<unsigned,unsigned>> indices;
568                 string onstart_cmds = read_setting_value(i, end, &indices);
569                 for (auto indexpair : indices) {
570                     string option_txt = onstart_cmds.substr(indexpair.first,
571                             indexpair.second - indexpair.first);
572                     if (option_txt == "starts-rwfs") {
573                         onstart_flags.rw_ready = true;
574                     }
575                     else if (option_txt == "starts-log") {
576                         onstart_flags.log_ready = true;
577                     }
578                     else if (option_txt == "no-sigterm") {
579                         onstart_flags.no_sigterm = true;
580                     }
581                     else if (option_txt == "runs-on-console") {
582                         onstart_flags.runs_on_console = true;
583                         // A service that runs on the console necessarily starts on console:
584                         onstart_flags.starts_on_console = true;
585                         onstart_flags.shares_console = false;
586                     }
587                     else if (option_txt == "starts-on-console") {
588                         onstart_flags.starts_on_console = true;
589                         onstart_flags.shares_console = false;
590                     }
591                     else if (option_txt == "shares-console") {
592                         onstart_flags.shares_console = true;
593                         onstart_flags.runs_on_console = false;
594                         onstart_flags.starts_on_console = false;
595                     }
596                     else if (option_txt == "pass-cs-fd") {
597                         onstart_flags.pass_cs_fd = true;
598                     }
599                     else if (option_txt == "start-interruptible") {
600                         onstart_flags.start_interruptible = true;
601                     }
602                     else if (option_txt == "skippable") {
603                         onstart_flags.skippable = true;
604                     }
605                     else if (option_txt == "signal-process-only") {
606                         onstart_flags.signal_process_only = true;
607                     }
608                     else {
609                         throw service_description_exc(name, "Unknown option: " + option_txt);
610                     }
611                 }
612             }
613             else if (setting == "load-options") {
614                 std::list<std::pair<unsigned,unsigned>> indices;
615                 string load_opts = read_setting_value(i, end, &indices);
616                 for (auto indexpair : indices) {
617                     string option_txt = load_opts.substr(indexpair.first,
618                             indexpair.second - indexpair.first);
619                     if (option_txt == "sub-vars") {
620                         // substitute environment variables in command line
621                         do_sub_vars = true;
622                     }
623                     else if (option_txt == "no-sub-vars") {
624                         do_sub_vars = false;
625                     }
626                     else {
627                         throw service_description_exc(name, "Unknown load option: " + option_txt);
628                     }
629                 }
630             }
631             else if (setting == "term-signal" || setting == "termsignal") {
632                 // Note: "termsignal" supported for legacy reasons.
633                 string signame = read_setting_value(i, end, nullptr);
634                 int signo = signal_name_to_number(signame);
635                 if (signo == -1) {
636                     throw service_description_exc(name, "Unknown/unsupported termination signal: "
637                             + signame);
638                 }
639                 else {
640                     term_signal = signo;
641                 }
642             }
643             else if (setting == "restart-limit-interval") {
644                 string interval_str = read_setting_value(i, end, nullptr);
645                 parse_timespec(interval_str, name, "restart-limit-interval", restart_interval);
646             }
647             else if (setting == "restart-delay") {
648                 string rsdelay_str = read_setting_value(i, end, nullptr);
649                 parse_timespec(rsdelay_str, name, "restart-delay", restart_delay);
650             }
651             else if (setting == "restart-limit-count") {
652                 string limit_str = read_setting_value(i, end, nullptr);
653                 max_restarts = parse_unum_param(limit_str, name, std::numeric_limits<int>::max());
654             }
655             else if (setting == "stop-timeout") {
656                 string stoptimeout_str = read_setting_value(i, end, nullptr);
657                 parse_timespec(stoptimeout_str, name, "stop-timeout", stop_timeout);
658             }
659             else if (setting == "start-timeout") {
660                 string starttimeout_str = read_setting_value(i, end, nullptr);
661                 parse_timespec(starttimeout_str, name, "start-timeout", start_timeout);
662             }
663             else if (setting == "run-as") {
664                 string run_as_str = read_setting_value(i, end, nullptr);
665                 run_as_uid = parse_uid_param(run_as_str, name, &run_as_gid);
666             }
667             else if (setting == "chain-to") {
668                 chain_to_name = read_setting_value(i, end, nullptr);
669             }
670             else if (setting == "ready-notification") {
671                 string notify_setting = read_setting_value(i, end, nullptr);
672                 if (starts_with(notify_setting, "pipefd:")) {
673                     readiness_fd = parse_unum_param(notify_setting.substr(7 /* len 'pipefd:' */),
674                             name, std::numeric_limits<int>::max());
675                 }
676                 else if (starts_with(notify_setting, "pipevar:")) {
677                     readiness_var = notify_setting.substr(8 /* len 'pipevar:' */);
678                     if (readiness_var.empty()) {
679                         throw service_description_exc(name, "Invalid pipevar variable name "
680                                 "in ready-notification");
681                     }
682                 }
683                 else {
684                     throw service_description_exc(name, "Unknown ready-notification setting: "
685                             + notify_setting);
686                 }
687             }
688             else if (setting == "inittab-id") {
689                 string inittab_setting = read_setting_value(i, end, nullptr);
690                 #if USE_UTMPX
691                 if (inittab_setting.length() > sizeof(inittab_id)) {
692                     throw service_description_exc(name, "inittab-id setting is too long");
693                 }
694                 strncpy(inittab_id, inittab_setting.c_str(), sizeof(inittab_id));
695                 #endif
696             }
697             else if (setting == "inittab-line") {
698                 string inittab_setting = read_setting_value(i, end, nullptr);
699                 #if USE_UTMPX
700                 if (inittab_setting.length() > sizeof(inittab_line)) {
701                     throw service_description_exc(name, "inittab-line setting is too long");
702                 }
703                 strncpy(inittab_line, inittab_setting.c_str(), sizeof(inittab_line));
704                 #endif
705             }
706             else if (setting == "rlimit-nofile") {
707                 string nofile_setting = read_setting_value(i, end, nullptr);
708                 service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_NOFILE);
709                 parse_rlimit(line, name, "rlimit-nofile", nofile_limits);
710             }
711             else if (setting == "rlimit-core") {
712                 string nofile_setting = read_setting_value(i, end, nullptr);
713                 service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_CORE);
714                 parse_rlimit(line, name, "rlimit-core", nofile_limits);
715             }
716             else if (setting == "rlimit-data") {
717                 string nofile_setting = read_setting_value(i, end, nullptr);
718                 service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_DATA);
719                 parse_rlimit(line, name, "rlimit-data", nofile_limits);
720             }
721             else if (setting == "rlimit-addrspace") {
722                 string nofile_setting = read_setting_value(i, end, nullptr);
723                 service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_AS);
724                 parse_rlimit(line, name, "rlimit-addrspace", nofile_limits);
725             }
726             else {
727                 throw service_description_exc(name, "Unknown setting: " + setting);
728             }
729         });
730
731         service_file.close();
732         
733         if (service_type == service_type_t::PROCESS || service_type == service_type_t::BGPROCESS
734                 || service_type == service_type_t::SCRIPTED) {
735             if (command.length() == 0) {
736                 throw service_description_exc(name, "Service command not specified");
737             }
738         }
739         
740         // Now replace the dummy service record with a real record:
741         for (auto iter = records.begin(); iter != records.end(); iter++) {
742             if (*iter == rval) {
743                 // We've found the dummy record
744                 delete rval;
745                 if (service_type == service_type_t::PROCESS) {
746                     do_env_subst(command, command_offsets, do_sub_vars);
747                     auto rvalps = new process_service(this, string(name), std::move(command),
748                             command_offsets, depends);
749                     rvalps->set_working_dir(working_dir);
750                     rvalps->set_rlimits(std::move(rlimits));
751                     rvalps->set_restart_interval(restart_interval, max_restarts);
752                     rvalps->set_restart_delay(restart_delay);
753                     rvalps->set_stop_timeout(stop_timeout);
754                     rvalps->set_start_timeout(start_timeout);
755                     rvalps->set_extra_termination_signal(term_signal);
756                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
757                     rvalps->set_notification_fd(readiness_fd);
758                     rvalps->set_notification_var(std::move(readiness_var));
759                     #if USE_UTMPX
760                     rvalps->set_utmp_id(inittab_id);
761                     rvalps->set_utmp_line(inittab_line);
762                     #endif
763                     rval = rvalps;
764                 }
765                 else if (service_type == service_type_t::BGPROCESS) {
766                     do_env_subst(command, command_offsets, do_sub_vars);
767                     auto rvalps = new bgproc_service(this, string(name), std::move(command),
768                             command_offsets, depends);
769                     rvalps->set_working_dir(working_dir);
770                     rvalps->set_rlimits(std::move(rlimits));
771                     rvalps->set_pid_file(std::move(pid_file));
772                     rvalps->set_restart_interval(restart_interval, max_restarts);
773                     rvalps->set_restart_delay(restart_delay);
774                     rvalps->set_stop_timeout(stop_timeout);
775                     rvalps->set_start_timeout(start_timeout);
776                     rvalps->set_extra_termination_signal(term_signal);
777                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
778                     onstart_flags.runs_on_console = false;
779                     rval = rvalps;
780                 }
781                 else if (service_type == service_type_t::SCRIPTED) {
782                     do_env_subst(command, command_offsets, do_sub_vars);
783                     auto rvalps = new scripted_service(this, string(name), std::move(command),
784                             command_offsets, depends);
785                     rvalps->set_stop_command(stop_command, stop_command_offsets);
786                     rvalps->set_working_dir(working_dir);
787                     rvalps->set_rlimits(std::move(rlimits));
788                     rvalps->set_stop_timeout(stop_timeout);
789                     rvalps->set_start_timeout(start_timeout);
790                     rvalps->set_extra_termination_signal(term_signal);
791                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
792                     rval = rvalps;
793                 }
794                 else {
795                     rval = new service_record(this, string(name), service_type, depends);
796                 }
797                 rval->set_log_file(logfile);
798                 rval->set_auto_restart(auto_restart);
799                 rval->set_smooth_recovery(smooth_recovery);
800                 rval->set_flags(onstart_flags);
801                 rval->set_socket_details(std::move(socket_path), socket_perms, socket_uid, socket_gid);
802                 rval->set_chain_to(std::move(chain_to_name));
803                 *iter = rval;
804                 break;
805             }
806         }
807         
808         return rval;
809     }
810     catch (setting_exception &setting_exc)
811     {
812         // Must remove the dummy service record.
813         records.erase(std::find(records.begin(), records.end(), rval));
814         delete rval;
815         throw service_description_exc(name, std::move(setting_exc.get_info()));
816     }
817     catch (...) {
818         // Must remove the dummy service record.
819         records.erase(std::find(records.begin(), records.end(), rval));
820         delete rval;
821         throw;
822     }
823 }