Some documentation updates.
[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         const char * index = cline;
231         errno = 0;
232         if (cline[0] != ':') {
233             rlimit.soft_set = true;
234             if (cline[0] == '-') {
235                 rlimit.limits.rlim_cur = RLIM_INFINITY;
236                 index = cline + 1;
237             }
238             else {
239                 char *nindex;
240                 unsigned long long limit = std::strtoull(cline, &nindex, 0);
241                 index = nindex;
242                 if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
243                 if (index == cline) throw std::invalid_argument("");
244                 rlimit.limits.rlim_cur = limit;
245             }
246
247             if (*index == 0) {
248                 rlimit.hard_set = true;
249                 rlimit.limits.rlim_max = rlimit.limits.rlim_cur;
250                 return;
251             }
252
253             if (*index != ':') {
254                 throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
255             }
256         }
257
258         index++;
259         if (*index == 0) return;
260
261         if (*index == '-') {
262             rlimit.limits.rlim_max = RLIM_INFINITY;
263             if (index[1] != 0) {
264                 throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
265             }
266         }
267         else {
268             const char *hard_start = index;
269             char *nindex;
270             unsigned long long limit = std::strtoull(cline, &nindex, 0);
271             index = nindex;
272             if (errno == ERANGE || limit > std::numeric_limits<rlim_t>::max()) throw std::out_of_range("");
273             if (index == hard_start) throw std::invalid_argument("");
274             rlimit.limits.rlim_max = limit;
275         }
276     }
277     catch (std::invalid_argument &exc) {
278         throw service_description_exc(service_name, std::string("Bad value for ") + param_name);
279     }
280     catch (std::out_of_range &exc) {
281         throw service_description_exc(service_name, std::string("Too-large value for ") + param_name);
282     }
283 }
284
285 // Perform environment variable substitution on a command line, if specified.
286 //   line -  the string storing the command and arguments
287 //   offsets - the [start,end) pair of offsets of the command and each argument within the string
288 //
289 static void do_env_subst(std::string &line, std::list<std::pair<unsigned,unsigned>> &offsets,
290         bool do_sub_vars)
291 {
292     if (do_sub_vars) {
293         auto i = offsets.begin();
294         std::string r_line = line.substr(i->first, i->second - i->first); // copy command part
295         for (++i; i != offsets.end(); ++i) {
296             auto &offset_pair = *i;
297             if (line[offset_pair.first] == '$') {
298                 // Do subsitution for this part:
299                 auto env_name = line.substr(offset_pair.first + 1,
300                         offset_pair.second - offset_pair.first - 1);
301                 char *env_val = getenv(env_name.c_str());
302                 if (env_val != nullptr) {
303                     auto val_len = strlen(env_val);
304                     r_line += " ";
305                     offset_pair.first = r_line.length();
306                     offset_pair.second = offset_pair.first + val_len;
307                     r_line += env_val;
308                 }
309                 else {
310                     // specified enironment variable not set: treat as an empty string
311                     offset_pair.first = r_line.length();
312                     offset_pair.second = offset_pair.first;
313                 }
314             }
315             else {
316                 // No subsitution for this part:
317                 r_line += " ";
318                 auto new_offs = r_line.length();
319                 auto len = offset_pair.second - offset_pair.first;
320                 r_line += line.substr(offset_pair.first, len);
321                 offset_pair.first = new_offs;
322                 offset_pair.second = new_offs + len;
323             }
324         }
325         line = std::move(r_line);
326     }
327 }
328
329 // Process a dependency directory - filenames contained within correspond to service names which
330 // are loaded and added as a dependency of the given type. Expected use is with a directory
331 // containing symbolic links to other service descriptions, but this isn't required.
332 // Failure to read the directory contents, or to find a service listed within, is not considered
333 // a fatal error.
334 static void process_dep_dir(dirload_service_set &sset,
335         const char *servicename,
336         const string &service_filename,
337         std::list<prelim_dep> &deplist, const std::string &depdirpath,
338         dependency_type dep_type)
339 {
340     std::string depdir_fname = combine_paths(parent_path(service_filename), depdirpath.c_str());
341
342     DIR *depdir = opendir(depdir_fname.c_str());
343     if (depdir == nullptr) {
344         log(loglevel_t::WARN, "Could not open dependency directory '", depdir_fname,
345                 "' for ", servicename, " service.");
346         return;
347     }
348
349     errno = 0;
350     dirent * dent = readdir(depdir);
351     while (dent != nullptr) {
352         char * name =  dent->d_name;
353         if (name[0] != '.') {
354             try {
355                 service_record * sr = sset.load_service(name);
356                 deplist.emplace_back(sr, dep_type);
357             }
358             catch (service_not_found &) {
359                 log(loglevel_t::WARN, "Ignoring unresolved dependency '", name,
360                         "' in dependency directory '", depdirpath,
361                         "' for ", servicename, " service.");
362             }
363         }
364         dent = readdir(depdir);
365     }
366
367     if (errno != 0) {
368         log(loglevel_t::WARN, "Error reading dependency directory '", depdirpath,
369                 "' for ", servicename, " service.");
370     }
371
372     closedir(depdir);
373 }
374
375 // Check if one string starts with another
376 static bool starts_with(string s, const char *prefix)
377 {
378     const char * sp = s.c_str();
379     while (*sp != 0 && *prefix != 0) {
380         if (*sp != *prefix) return false;
381         sp++; prefix++;
382     }
383     return *prefix == 0;
384 }
385
386 // Find a service record, or load it from file. If the service has
387 // dependencies, load those also.
388 //
389 // Might throw a ServiceLoadExc exception if a dependency cycle is found or if another
390 // problem occurs (I/O error, service description not found etc). Throws std::bad_alloc
391 // if a memory allocation failure occurs.
392 //
393 service_record * dirload_service_set::load_service(const char * name)
394 {
395     using std::string;
396     using std::ifstream;
397     using std::ios;
398     using std::ios_base;
399     using std::locale;
400     using std::isspace;
401     
402     using std::list;
403     using std::pair;
404     
405     using namespace dinit_load;
406
407     // First try and find an existing record...
408     service_record * rval = find_service(string(name));
409     if (rval != 0) {
410         if (rval->is_dummy()) {
411             throw service_cyclic_dependency(name);
412         }
413         return rval;
414     }
415
416     ifstream service_file;
417     string service_filename;
418
419     // Couldn't find one. Have to load it.
420     for (auto &service_dir : service_dirs) {
421         service_filename = service_dir.get_dir();
422         if (*(service_filename.rbegin()) != '/') {
423             service_filename += '/';
424         }
425         service_filename += name;
426
427         service_file.open(service_filename.c_str(), ios::in);
428         if (service_file) break;
429     }
430     
431     if (! service_file) {
432         throw service_not_found(string(name));
433     }
434
435     string command;
436     list<pair<unsigned,unsigned>> command_offsets;
437     string stop_command;
438     list<pair<unsigned,unsigned>> stop_command_offsets;
439     string working_dir;
440     string pid_file;
441     string env_file;
442
443     bool do_sub_vars = false;
444
445     service_type_t service_type = service_type_t::PROCESS;
446     std::list<prelim_dep> depends;
447     string logfile;
448     service_flags_t onstart_flags;
449     int term_signal = -1;  // additional termination signal
450     bool auto_restart = false;
451     bool smooth_recovery = false;
452     string socket_path;
453     int socket_perms = 0666;
454     // Note: Posix allows that uid_t and gid_t may be unsigned types, but eg chown uses -1 as an
455     // invalid value, so it's safe to assume that we can do the same:
456     uid_t socket_uid = -1;
457     gid_t socket_gid = -1;
458     // Restart limit interval / count; default is 10 seconds, 3 restarts:
459     timespec restart_interval = { .tv_sec = 10, .tv_nsec = 0 };
460     int max_restarts = 3;
461     timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
462     timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
463     timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
464     std::vector<service_rlimits> rlimits;
465     
466     int readiness_fd = -1;      // readiness fd in service process
467     std::string readiness_var;  // environment var to hold readiness fd
468
469     uid_t run_as_uid = -1;
470     gid_t run_as_gid = -1;
471
472     string chain_to_name;
473
474     #if USE_UTMPX
475     char inittab_id[sizeof(utmpx().ut_id)] = {0};
476     char inittab_line[sizeof(utmpx().ut_line)] = {0};
477     #endif
478
479     string line;
480     service_file.exceptions(ios::badbit | ios::failbit);
481     
482     // Add a dummy service record now to prevent infinite recursion in case of cyclic dependency.
483     // We replace this with the real service later (or remove it if we find a configuration error).
484     rval = new service_record(this, string(name));
485     add_service(rval);
486     
487     try {
488         // getline can set failbit if it reaches end-of-file, we don't want an exception in that case:
489         service_file.exceptions(ios::badbit);
490         
491         process_service_file(name, service_file,
492                 [&](string &line, string &setting, string_iterator &i, string_iterator &end) -> void {
493             if (setting == "command") {
494                 command = read_setting_value(i, end, &command_offsets);
495             }
496             else if (setting == "working-dir") {
497                 working_dir = read_setting_value(i, end, nullptr);
498             }
499             else if (setting == "env-file") {
500                 env_file = read_setting_value(i, end, nullptr);
501             }
502             else if (setting == "socket-listen") {
503                 socket_path = read_setting_value(i, end, nullptr);
504             }
505             else if (setting == "socket-permissions") {
506                 string sock_perm_str = read_setting_value(i, end, nullptr);
507                 std::size_t ind = 0;
508                 try {
509                     socket_perms = std::stoi(sock_perm_str, &ind, 8);
510                     if (ind != sock_perm_str.length()) {
511                         throw std::logic_error("");
512                     }
513                 }
514                 catch (std::logic_error &exc) {
515                     throw service_description_exc(name, "socket-permissions: Badly-formed or "
516                             "out-of-range numeric value");
517                 }
518             }
519             else if (setting == "socket-uid") {
520                 string sock_uid_s = read_setting_value(i, end, nullptr);
521                 socket_uid = parse_uid_param(sock_uid_s, name, &socket_gid);
522             }
523             else if (setting == "socket-gid") {
524                 string sock_gid_s = read_setting_value(i, end, nullptr);
525                 socket_gid = parse_gid_param(sock_gid_s, name);
526             }
527             else if (setting == "stop-command") {
528                 stop_command = read_setting_value(i, end, &stop_command_offsets);
529             }
530             else if (setting == "pid-file") {
531                 pid_file = read_setting_value(i, end);
532             }
533             else if (setting == "depends-on") {
534                 string dependency_name = read_setting_value(i, end);
535                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::REGULAR);
536             }
537             else if (setting == "depends-ms") {
538                 string dependency_name = read_setting_value(i, end);
539                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::MILESTONE);
540             }
541             else if (setting == "waits-for") {
542                 string dependency_name = read_setting_value(i, end);
543                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::WAITS_FOR);
544             }
545             else if (setting == "waits-for.d") {
546                 string waitsford = read_setting_value(i, end);
547                 process_dep_dir(*this, name, service_filename, depends, waitsford,
548                         dependency_type::WAITS_FOR);
549             }
550             else if (setting == "logfile") {
551                 logfile = read_setting_value(i, end);
552             }
553             else if (setting == "restart") {
554                 string restart = read_setting_value(i, end);
555                 auto_restart = (restart == "yes" || restart == "true");
556             }
557             else if (setting == "smooth-recovery") {
558                 string recovery = read_setting_value(i, end);
559                 smooth_recovery = (recovery == "yes" || recovery == "true");
560             }
561             else if (setting == "type") {
562                 string type_str = read_setting_value(i, end);
563                 if (type_str == "scripted") {
564                     service_type = service_type_t::SCRIPTED;
565                 }
566                 else if (type_str == "process") {
567                     service_type = service_type_t::PROCESS;
568                 }
569                 else if (type_str == "bgprocess") {
570                     service_type = service_type_t::BGPROCESS;
571                 }
572                 else if (type_str == "internal") {
573                     service_type = service_type_t::INTERNAL;
574                 }
575                 else {
576                     throw service_description_exc(name, "Service type must be one of: \"scripted\","
577                         " \"process\", \"bgprocess\" or \"internal\"");
578                 }
579             }
580             else if (setting == "options") {
581                 std::list<std::pair<unsigned,unsigned>> indices;
582                 string onstart_cmds = read_setting_value(i, end, &indices);
583                 for (auto indexpair : indices) {
584                     string option_txt = onstart_cmds.substr(indexpair.first,
585                             indexpair.second - indexpair.first);
586                     if (option_txt == "starts-rwfs") {
587                         onstart_flags.rw_ready = true;
588                     }
589                     else if (option_txt == "starts-log") {
590                         onstart_flags.log_ready = true;
591                     }
592                     else if (option_txt == "no-sigterm") {
593                         onstart_flags.no_sigterm = true;
594                     }
595                     else if (option_txt == "runs-on-console") {
596                         onstart_flags.runs_on_console = true;
597                         // A service that runs on the console necessarily starts on console:
598                         onstart_flags.starts_on_console = true;
599                         onstart_flags.shares_console = false;
600                     }
601                     else if (option_txt == "starts-on-console") {
602                         onstart_flags.starts_on_console = true;
603                         onstart_flags.shares_console = false;
604                     }
605                     else if (option_txt == "shares-console") {
606                         onstart_flags.shares_console = true;
607                         onstart_flags.runs_on_console = false;
608                         onstart_flags.starts_on_console = false;
609                     }
610                     else if (option_txt == "pass-cs-fd") {
611                         onstart_flags.pass_cs_fd = true;
612                     }
613                     else if (option_txt == "start-interruptible") {
614                         onstart_flags.start_interruptible = true;
615                     }
616                     else if (option_txt == "skippable") {
617                         onstart_flags.skippable = true;
618                     }
619                     else if (option_txt == "signal-process-only") {
620                         onstart_flags.signal_process_only = true;
621                     }
622                     else {
623                         throw service_description_exc(name, "Unknown option: " + option_txt);
624                     }
625                 }
626             }
627             else if (setting == "load-options") {
628                 std::list<std::pair<unsigned,unsigned>> indices;
629                 string load_opts = read_setting_value(i, end, &indices);
630                 for (auto indexpair : indices) {
631                     string option_txt = load_opts.substr(indexpair.first,
632                             indexpair.second - indexpair.first);
633                     if (option_txt == "sub-vars") {
634                         // substitute environment variables in command line
635                         do_sub_vars = true;
636                     }
637                     else if (option_txt == "no-sub-vars") {
638                         do_sub_vars = false;
639                     }
640                     else {
641                         throw service_description_exc(name, "Unknown load option: " + option_txt);
642                     }
643                 }
644             }
645             else if (setting == "term-signal" || setting == "termsignal") {
646                 // Note: "termsignal" supported for legacy reasons.
647                 string signame = read_setting_value(i, end, nullptr);
648                 int signo = signal_name_to_number(signame);
649                 if (signo == -1) {
650                     throw service_description_exc(name, "Unknown/unsupported termination signal: "
651                             + signame);
652                 }
653                 else {
654                     term_signal = signo;
655                 }
656             }
657             else if (setting == "restart-limit-interval") {
658                 string interval_str = read_setting_value(i, end, nullptr);
659                 parse_timespec(interval_str, name, "restart-limit-interval", restart_interval);
660             }
661             else if (setting == "restart-delay") {
662                 string rsdelay_str = read_setting_value(i, end, nullptr);
663                 parse_timespec(rsdelay_str, name, "restart-delay", restart_delay);
664             }
665             else if (setting == "restart-limit-count") {
666                 string limit_str = read_setting_value(i, end, nullptr);
667                 max_restarts = parse_unum_param(limit_str, name, std::numeric_limits<int>::max());
668             }
669             else if (setting == "stop-timeout") {
670                 string stoptimeout_str = read_setting_value(i, end, nullptr);
671                 parse_timespec(stoptimeout_str, name, "stop-timeout", stop_timeout);
672             }
673             else if (setting == "start-timeout") {
674                 string starttimeout_str = read_setting_value(i, end, nullptr);
675                 parse_timespec(starttimeout_str, name, "start-timeout", start_timeout);
676             }
677             else if (setting == "run-as") {
678                 string run_as_str = read_setting_value(i, end, nullptr);
679                 run_as_uid = parse_uid_param(run_as_str, name, &run_as_gid);
680             }
681             else if (setting == "chain-to") {
682                 chain_to_name = read_setting_value(i, end, nullptr);
683             }
684             else if (setting == "ready-notification") {
685                 string notify_setting = read_setting_value(i, end, nullptr);
686                 if (starts_with(notify_setting, "pipefd:")) {
687                     readiness_fd = parse_unum_param(notify_setting.substr(7 /* len 'pipefd:' */),
688                             name, std::numeric_limits<int>::max());
689                 }
690                 else if (starts_with(notify_setting, "pipevar:")) {
691                     readiness_var = notify_setting.substr(8 /* len 'pipevar:' */);
692                     if (readiness_var.empty()) {
693                         throw service_description_exc(name, "Invalid pipevar variable name "
694                                 "in ready-notification");
695                     }
696                 }
697                 else {
698                     throw service_description_exc(name, "Unknown ready-notification setting: "
699                             + notify_setting);
700                 }
701             }
702             else if (setting == "inittab-id") {
703                 string inittab_setting = read_setting_value(i, end, nullptr);
704                 #if USE_UTMPX
705                 if (inittab_setting.length() > sizeof(inittab_id)) {
706                     throw service_description_exc(name, "inittab-id setting is too long");
707                 }
708                 strncpy(inittab_id, inittab_setting.c_str(), sizeof(inittab_id));
709                 #endif
710             }
711             else if (setting == "inittab-line") {
712                 string inittab_setting = read_setting_value(i, end, nullptr);
713                 #if USE_UTMPX
714                 if (inittab_setting.length() > sizeof(inittab_line)) {
715                     throw service_description_exc(name, "inittab-line setting is too long");
716                 }
717                 strncpy(inittab_line, inittab_setting.c_str(), sizeof(inittab_line));
718                 #endif
719             }
720             else if (setting == "rlimit-nofile") {
721                 string nofile_setting = read_setting_value(i, end, nullptr);
722                 service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_NOFILE);
723                 parse_rlimit(line, name, "rlimit-nofile", nofile_limits);
724             }
725             else if (setting == "rlimit-core") {
726                 string nofile_setting = read_setting_value(i, end, nullptr);
727                 service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_CORE);
728                 parse_rlimit(line, name, "rlimit-core", nofile_limits);
729             }
730             else if (setting == "rlimit-data") {
731                 string nofile_setting = read_setting_value(i, end, nullptr);
732                 service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_DATA);
733                 parse_rlimit(line, name, "rlimit-data", nofile_limits);
734             }
735             else if (setting == "rlimit-addrspace") {
736                 #if defined(RLIMIT_AS)
737                     string nofile_setting = read_setting_value(i, end, nullptr);
738                     service_rlimits &nofile_limits = find_rlimits(rlimits, RLIMIT_AS);
739                     parse_rlimit(line, name, "rlimit-addrspace", nofile_limits);
740                 #endif
741             }
742             else {
743                 throw service_description_exc(name, "Unknown setting: " + setting);
744             }
745         });
746
747         service_file.close();
748         
749         if (service_type == service_type_t::PROCESS || service_type == service_type_t::BGPROCESS
750                 || service_type == service_type_t::SCRIPTED) {
751             if (command.length() == 0) {
752                 throw service_description_exc(name, "Service command not specified");
753             }
754         }
755         
756         // Now replace the dummy service record with a real record:
757         for (auto iter = records.begin(); iter != records.end(); iter++) {
758             if (*iter == rval) {
759                 // We've found the dummy record
760                 delete rval;
761                 if (service_type == service_type_t::PROCESS) {
762                     do_env_subst(command, command_offsets, do_sub_vars);
763                     auto rvalps = new process_service(this, string(name), std::move(command),
764                             command_offsets, depends);
765                     rvalps->set_working_dir(working_dir);
766                     rvalps->set_env_file(env_file);
767                     rvalps->set_rlimits(std::move(rlimits));
768                     rvalps->set_restart_interval(restart_interval, max_restarts);
769                     rvalps->set_restart_delay(restart_delay);
770                     rvalps->set_stop_timeout(stop_timeout);
771                     rvalps->set_start_timeout(start_timeout);
772                     rvalps->set_extra_termination_signal(term_signal);
773                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
774                     rvalps->set_notification_fd(readiness_fd);
775                     rvalps->set_notification_var(std::move(readiness_var));
776                     #if USE_UTMPX
777                     rvalps->set_utmp_id(inittab_id);
778                     rvalps->set_utmp_line(inittab_line);
779                     #endif
780                     rval = rvalps;
781                 }
782                 else if (service_type == service_type_t::BGPROCESS) {
783                     do_env_subst(command, command_offsets, do_sub_vars);
784                     auto rvalps = new bgproc_service(this, string(name), std::move(command),
785                             command_offsets, depends);
786                     rvalps->set_working_dir(working_dir);
787                     rvalps->set_env_file(env_file);
788                     rvalps->set_rlimits(std::move(rlimits));
789                     rvalps->set_pid_file(std::move(pid_file));
790                     rvalps->set_restart_interval(restart_interval, max_restarts);
791                     rvalps->set_restart_delay(restart_delay);
792                     rvalps->set_stop_timeout(stop_timeout);
793                     rvalps->set_start_timeout(start_timeout);
794                     rvalps->set_extra_termination_signal(term_signal);
795                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
796                     onstart_flags.runs_on_console = false;
797                     rval = rvalps;
798                 }
799                 else if (service_type == service_type_t::SCRIPTED) {
800                     do_env_subst(command, command_offsets, do_sub_vars);
801                     auto rvalps = new scripted_service(this, string(name), std::move(command),
802                             command_offsets, depends);
803                     rvalps->set_stop_command(stop_command, stop_command_offsets);
804                     rvalps->set_working_dir(working_dir);
805                     rvalps->set_env_file(env_file);
806                     rvalps->set_rlimits(std::move(rlimits));
807                     rvalps->set_stop_timeout(stop_timeout);
808                     rvalps->set_start_timeout(start_timeout);
809                     rvalps->set_extra_termination_signal(term_signal);
810                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
811                     rval = rvalps;
812                 }
813                 else {
814                     rval = new service_record(this, string(name), service_type, depends);
815                 }
816                 rval->set_log_file(logfile);
817                 rval->set_auto_restart(auto_restart);
818                 rval->set_smooth_recovery(smooth_recovery);
819                 rval->set_flags(onstart_flags);
820                 rval->set_socket_details(std::move(socket_path), socket_perms, socket_uid, socket_gid);
821                 rval->set_chain_to(std::move(chain_to_name));
822                 *iter = rval;
823                 break;
824             }
825         }
826         
827         return rval;
828     }
829     catch (setting_exception &setting_exc)
830     {
831         // Must remove the dummy service record.
832         records.erase(std::find(records.begin(), records.end(), rval));
833         delete rval;
834         throw service_description_exc(name, std::move(setting_exc.get_info()));
835     }
836     catch (...) {
837         // Must remove the dummy service record.
838         records.erase(std::find(records.begin(), records.end(), rval));
839         delete rval;
840         throw;
841     }
842 }