Don't abuse std::find to find end of special message.
[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 //
163 static void parse_timespec(const std::string &paramval, const std::string &servicename,
164         const char * paramname, timespec &ts)
165 {
166     decltype(ts.tv_sec) isec = 0;
167     decltype(ts.tv_nsec) insec = 0;
168     auto max_secs = std::numeric_limits<decltype(isec)>::max() / 10;
169     auto len = paramval.length();
170     decltype(len) i;
171     for (i = 0; i < len; i++) {
172         char ch = paramval[i];
173         if (ch == '.' || ch == ',') {
174             i++;
175             break;
176         }
177         if (ch < '0' || ch > '9') {
178             throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
179         }
180         // check for overflow
181         if (isec >= max_secs) {
182            throw service_description_exc(servicename, std::string("Too-large value for ") + paramname);
183         }
184         isec *= 10;
185         isec += ch - '0';
186     }
187     decltype(insec) insec_m = 100000000; // 10^8
188     for ( ; i < len; i++) {
189         char ch = paramval[i];
190         if (ch < '0' || ch > '9') {
191             throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
192         }
193         insec += (ch - '0') * insec_m;
194         insec_m /= 10;
195     }
196     ts.tv_sec = isec;
197     ts.tv_nsec = insec;
198 }
199
200 // Perform environment variable substitution on a command line, if specified.
201 //   line -  the string storing the command and arguments
202 //   offsets - the [start,end) pair of offsets of the command and each argument within the string
203 //
204 static void do_env_subst(std::string &line, std::list<std::pair<unsigned,unsigned>> &offsets,
205         bool do_sub_vars)
206 {
207     if (do_sub_vars) {
208         auto i = offsets.begin();
209         std::string r_line = line.substr(i->first, i->second - i->first); // copy command part
210         for (++i; i != offsets.end(); ++i) {
211             auto &offset_pair = *i;
212             if (line[offset_pair.first] == '$') {
213                 // Do subsitution for this part:
214                 auto env_name = line.substr(offset_pair.first + 1,
215                         offset_pair.second - offset_pair.first - 1);
216                 char *env_val = getenv(env_name.c_str());
217                 if (env_val != nullptr) {
218                     auto val_len = strlen(env_val);
219                     r_line += " ";
220                     offset_pair.first = r_line.length();
221                     offset_pair.second = offset_pair.first + val_len;
222                     r_line += env_val;
223                 }
224                 else {
225                     // specified enironment variable not set: treat as an empty string
226                     offset_pair.first = r_line.length();
227                     offset_pair.second = offset_pair.first;
228                 }
229             }
230             else {
231                 // No subsitution for this part:
232                 r_line += " ";
233                 auto new_offs = r_line.length();
234                 auto len = offset_pair.second - offset_pair.first;
235                 r_line += line.substr(offset_pair.first, len);
236                 offset_pair.first = new_offs;
237                 offset_pair.second = new_offs + len;
238             }
239         }
240         line = std::move(r_line);
241     }
242 }
243
244 // Process a dependency directory - filenames contained within correspond to service names which
245 // are loaded and added as a dependency of the given type. Expected use is with a directory
246 // containing symbolic links to other service descriptions, but this isn't required.
247 // Failure to read the directory contents, or to find a service listed within, is not considered
248 // a fatal error.
249 static void process_dep_dir(dirload_service_set &sset,
250         const char *servicename,
251         const string &service_filename,
252         std::list<prelim_dep> &deplist, const std::string &depdirpath,
253         dependency_type dep_type)
254 {
255     std::string depdir_fname = combine_paths(parent_path(service_filename), depdirpath.c_str());
256
257     DIR *depdir = opendir(depdir_fname.c_str());
258     if (depdir == nullptr) {
259         log(loglevel_t::WARN, "Could not open dependency directory '", depdir_fname,
260                 "' for ", servicename, " service.");
261         return;
262     }
263
264     errno = 0;
265     dirent * dent = readdir(depdir);
266     while (dent != nullptr) {
267         char * name =  dent->d_name;
268         if (name[0] != '.') {
269             try {
270                 service_record * sr = sset.load_service(name);
271                 deplist.emplace_back(sr, dep_type);
272             }
273             catch (service_not_found &) {
274                 log(loglevel_t::WARN, "Ignoring unresolved dependency '", name,
275                         "' in dependency directory '", depdirpath,
276                         "' for ", servicename, " service.");
277             }
278         }
279         dent = readdir(depdir);
280     }
281
282     if (errno != 0) {
283         log(loglevel_t::WARN, "Error reading dependency directory '", depdirpath,
284                 "' for ", servicename, " service.");
285     }
286 }
287
288 // Check if one string starts with another
289 static bool starts_with(string s, const char *prefix)
290 {
291     const char * sp = s.c_str();
292     while (*sp != 0 && *prefix != 0) {
293         if (*sp != *prefix) return false;
294         sp++; prefix++;
295     }
296     return *prefix == 0;
297 }
298
299 // Find a service record, or load it from file. If the service has
300 // dependencies, load those also.
301 //
302 // Might throw a ServiceLoadExc exception if a dependency cycle is found or if another
303 // problem occurs (I/O error, service description not found etc). Throws std::bad_alloc
304 // if a memory allocation failure occurs.
305 //
306 service_record * dirload_service_set::load_service(const char * name)
307 {
308     using std::string;
309     using std::ifstream;
310     using std::ios;
311     using std::ios_base;
312     using std::locale;
313     using std::isspace;
314     
315     using std::list;
316     using std::pair;
317     
318     using namespace dinit_load;
319
320     // First try and find an existing record...
321     service_record * rval = find_service(string(name));
322     if (rval != 0) {
323         if (rval->is_dummy()) {
324             throw service_cyclic_dependency(name);
325         }
326         return rval;
327     }
328
329     ifstream service_file;
330     string service_filename;
331
332     // Couldn't find one. Have to load it.
333     for (auto &service_dir : service_dirs) {
334         service_filename = service_dir.get_dir();
335         if (*(service_filename.rbegin()) != '/') {
336             service_filename += '/';
337         }
338         service_filename += name;
339
340         service_file.open(service_filename.c_str(), ios::in);
341         if (service_file) break;
342     }
343     
344     if (! service_file) {
345         throw service_not_found(string(name));
346     }
347
348     string command;
349     list<pair<unsigned,unsigned>> command_offsets;
350     string stop_command;
351     list<pair<unsigned,unsigned>> stop_command_offsets;
352     string working_dir;
353     string pid_file;
354
355     bool do_sub_vars = false;
356
357     service_type_t service_type = service_type_t::PROCESS;
358     std::list<prelim_dep> depends;
359     string logfile;
360     service_flags_t onstart_flags;
361     int term_signal = -1;  // additional termination signal
362     bool auto_restart = false;
363     bool smooth_recovery = false;
364     string socket_path;
365     int socket_perms = 0666;
366     // Note: Posix allows that uid_t and gid_t may be unsigned types, but eg chown uses -1 as an
367     // invalid value, so it's safe to assume that we can do the same:
368     uid_t socket_uid = -1;
369     gid_t socket_gid = -1;
370     // Restart limit interval / count; default is 10 seconds, 3 restarts:
371     timespec restart_interval = { .tv_sec = 10, .tv_nsec = 0 };
372     int max_restarts = 3;
373     timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
374     timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
375     timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
376     
377     int readiness_fd = -1;      // readiness fd in service process
378     std::string readiness_var;  // environment var to hold readiness fd
379
380     uid_t run_as_uid = -1;
381     gid_t run_as_gid = -1;
382
383     string chain_to_name;
384
385     #if USE_UTMPX
386     char inittab_id[sizeof(utmpx().ut_id)] = {0};
387     char inittab_line[sizeof(utmpx().ut_line)] = {0};
388     #endif
389
390     string line;
391     service_file.exceptions(ios::badbit | ios::failbit);
392     
393     // Add a dummy service record now to prevent infinite recursion in case of cyclic dependency.
394     // We replace this with the real service later (or remove it if we find a configuration error).
395     rval = new service_record(this, string(name));
396     add_service(rval);
397     
398     try {
399         // getline can set failbit if it reaches end-of-file, we don't want an exception in that case:
400         service_file.exceptions(ios::badbit);
401         
402         process_service_file(name, service_file,
403                 [&](string &line, string &setting, string_iterator &i, string_iterator &end) -> void {
404             if (setting == "command") {
405                 command = read_setting_value(i, end, &command_offsets);
406             }
407             else if (setting == "working-dir") {
408                 working_dir = read_setting_value(i, end, nullptr);
409             }
410             else if (setting == "socket-listen") {
411                 socket_path = read_setting_value(i, end, nullptr);
412             }
413             else if (setting == "socket-permissions") {
414                 string sock_perm_str = read_setting_value(i, end, nullptr);
415                 std::size_t ind = 0;
416                 try {
417                     socket_perms = std::stoi(sock_perm_str, &ind, 8);
418                     if (ind != sock_perm_str.length()) {
419                         throw std::logic_error("");
420                     }
421                 }
422                 catch (std::logic_error &exc) {
423                     throw service_description_exc(name, "socket-permissions: Badly-formed or "
424                             "out-of-range numeric value");
425                 }
426             }
427             else if (setting == "socket-uid") {
428                 string sock_uid_s = read_setting_value(i, end, nullptr);
429                 socket_uid = parse_uid_param(sock_uid_s, name, &socket_gid);
430             }
431             else if (setting == "socket-gid") {
432                 string sock_gid_s = read_setting_value(i, end, nullptr);
433                 socket_gid = parse_gid_param(sock_gid_s, name);
434             }
435             else if (setting == "stop-command") {
436                 stop_command = read_setting_value(i, end, &stop_command_offsets);
437             }
438             else if (setting == "pid-file") {
439                 pid_file = read_setting_value(i, end);
440             }
441             else if (setting == "depends-on") {
442                 string dependency_name = read_setting_value(i, end);
443                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::REGULAR);
444             }
445             else if (setting == "depends-ms") {
446                 string dependency_name = read_setting_value(i, end);
447                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::MILESTONE);
448             }
449             else if (setting == "waits-for") {
450                 string dependency_name = read_setting_value(i, end);
451                 depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::WAITS_FOR);
452             }
453             else if (setting == "waits-for.d") {
454                 string waitsford = read_setting_value(i, end);
455                 process_dep_dir(*this, name, service_filename, depends, waitsford,
456                         dependency_type::WAITS_FOR);
457             }
458             else if (setting == "logfile") {
459                 logfile = read_setting_value(i, end);
460             }
461             else if (setting == "restart") {
462                 string restart = read_setting_value(i, end);
463                 auto_restart = (restart == "yes" || restart == "true");
464             }
465             else if (setting == "smooth-recovery") {
466                 string recovery = read_setting_value(i, end);
467                 smooth_recovery = (recovery == "yes" || recovery == "true");
468             }
469             else if (setting == "type") {
470                 string type_str = read_setting_value(i, end);
471                 if (type_str == "scripted") {
472                     service_type = service_type_t::SCRIPTED;
473                 }
474                 else if (type_str == "process") {
475                     service_type = service_type_t::PROCESS;
476                 }
477                 else if (type_str == "bgprocess") {
478                     service_type = service_type_t::BGPROCESS;
479                 }
480                 else if (type_str == "internal") {
481                     service_type = service_type_t::INTERNAL;
482                 }
483                 else {
484                     throw service_description_exc(name, "Service type must be one of: \"scripted\","
485                         " \"process\", \"bgprocess\" or \"internal\"");
486                 }
487             }
488             else if (setting == "options") {
489                 std::list<std::pair<unsigned,unsigned>> indices;
490                 string onstart_cmds = read_setting_value(i, end, &indices);
491                 for (auto indexpair : indices) {
492                     string option_txt = onstart_cmds.substr(indexpair.first,
493                             indexpair.second - indexpair.first);
494                     if (option_txt == "starts-rwfs") {
495                         onstart_flags.rw_ready = true;
496                     }
497                     else if (option_txt == "starts-log") {
498                         onstart_flags.log_ready = true;
499                     }
500                     else if (option_txt == "no-sigterm") {
501                         onstart_flags.no_sigterm = true;
502                     }
503                     else if (option_txt == "runs-on-console") {
504                         onstart_flags.runs_on_console = true;
505                         // A service that runs on the console necessarily starts on console:
506                         onstart_flags.starts_on_console = true;
507                         onstart_flags.shares_console = false;
508                     }
509                     else if (option_txt == "starts-on-console") {
510                         onstart_flags.starts_on_console = true;
511                         onstart_flags.shares_console = false;
512                     }
513                     else if (option_txt == "shares-console") {
514                         onstart_flags.shares_console = true;
515                         onstart_flags.runs_on_console = false;
516                         onstart_flags.starts_on_console = false;
517                     }
518                     else if (option_txt == "pass-cs-fd") {
519                         onstart_flags.pass_cs_fd = true;
520                     }
521                     else if (option_txt == "start-interruptible") {
522                         onstart_flags.start_interruptible = true;
523                     }
524                     else if (option_txt == "skippable") {
525                         onstart_flags.skippable = true;
526                     }
527                     else if (option_txt == "signal-process-only") {
528                         onstart_flags.signal_process_only = true;
529                     }
530                     else {
531                         throw service_description_exc(name, "Unknown option: " + option_txt);
532                     }
533                 }
534             }
535             else if (setting == "load-options") {
536                 std::list<std::pair<unsigned,unsigned>> indices;
537                 string load_opts = read_setting_value(i, end, &indices);
538                 for (auto indexpair : indices) {
539                     string option_txt = load_opts.substr(indexpair.first,
540                             indexpair.second - indexpair.first);
541                     if (option_txt == "sub-vars") {
542                         // substitute environment variables in command line
543                         do_sub_vars = true;
544                     }
545                     else if (option_txt == "no-sub-vars") {
546                         do_sub_vars = false;
547                     }
548                     else {
549                         throw service_description_exc(name, "Unknown load option: " + option_txt);
550                     }
551                 }
552             }
553             else if (setting == "term-signal" || setting == "termsignal") {
554                 // Note: "termsignal" supported for legacy reasons.
555                 string signame = read_setting_value(i, end, nullptr);
556                 int signo = signal_name_to_number(signame);
557                 if (signo == -1) {
558                     throw service_description_exc(name, "Unknown/unsupported termination signal: "
559                             + signame);
560                 }
561                 else {
562                     term_signal = signo;
563                 }
564             }
565             else if (setting == "restart-limit-interval") {
566                 string interval_str = read_setting_value(i, end, nullptr);
567                 parse_timespec(interval_str, name, "restart-limit-interval", restart_interval);
568             }
569             else if (setting == "restart-delay") {
570                 string rsdelay_str = read_setting_value(i, end, nullptr);
571                 parse_timespec(rsdelay_str, name, "restart-delay", restart_delay);
572             }
573             else if (setting == "restart-limit-count") {
574                 string limit_str = read_setting_value(i, end, nullptr);
575                 max_restarts = parse_unum_param(limit_str, name, std::numeric_limits<int>::max());
576             }
577             else if (setting == "stop-timeout") {
578                 string stoptimeout_str = read_setting_value(i, end, nullptr);
579                 parse_timespec(stoptimeout_str, name, "stop-timeout", stop_timeout);
580             }
581             else if (setting == "start-timeout") {
582                 string starttimeout_str = read_setting_value(i, end, nullptr);
583                 parse_timespec(starttimeout_str, name, "start-timeout", start_timeout);
584             }
585             else if (setting == "run-as") {
586                 string run_as_str = read_setting_value(i, end, nullptr);
587                 run_as_uid = parse_uid_param(run_as_str, name, &run_as_gid);
588             }
589             else if (setting == "chain-to") {
590                 chain_to_name = read_setting_value(i, end, nullptr);
591             }
592             else if (setting == "ready-notification") {
593                 string notify_setting = read_setting_value(i, end, nullptr);
594                 if (starts_with(notify_setting, "pipefd:")) {
595                     readiness_fd = parse_unum_param(notify_setting.substr(7 /* len 'pipefd:' */),
596                             name, std::numeric_limits<int>::max());
597                 }
598                 else if (starts_with(notify_setting, "pipevar:")) {
599                     readiness_var = notify_setting.substr(8 /* len 'pipevar:' */);
600                     if (readiness_var.empty()) {
601                         throw service_description_exc(name, "Invalid pipevar variable name "
602                                 "in ready-notification");
603                     }
604                 }
605                 else {
606                     throw service_description_exc(name, "Unknown ready-notification setting: "
607                             + notify_setting);
608                 }
609             }
610             else if (setting == "inittab-id") {
611                 string inittab_setting = read_setting_value(i, end, nullptr);
612                 #if USE_UTMPX
613                 if (inittab_setting.length() > sizeof(inittab_id)) {
614                     throw service_description_exc(name, "inittab-id setting is too long");
615                 }
616                 strncpy(inittab_id, inittab_setting.c_str(), sizeof(inittab_id));
617                 #endif
618             }
619             else if (setting == "inittab-line") {
620                 string inittab_setting = read_setting_value(i, end, nullptr);
621                 #if USE_UTMPX
622                 if (inittab_setting.length() > sizeof(inittab_line)) {
623                     throw service_description_exc(name, "inittab-line setting is too long");
624                 }
625                 strncpy(inittab_line, inittab_setting.c_str(), sizeof(inittab_line));
626                 #endif
627             }
628             else {
629                 throw service_description_exc(name, "Unknown setting: " + setting);
630             }
631         });
632
633         service_file.close();
634         
635         if (service_type == service_type_t::PROCESS || service_type == service_type_t::BGPROCESS
636                 || service_type == service_type_t::SCRIPTED) {
637             if (command.length() == 0) {
638                 throw service_description_exc(name, "Service command not specified");
639             }
640         }
641         
642         // Now replace the dummy service record with a real record:
643         for (auto iter = records.begin(); iter != records.end(); iter++) {
644             if (*iter == rval) {
645                 // We've found the dummy record
646                 delete rval;
647                 if (service_type == service_type_t::PROCESS) {
648                     do_env_subst(command, command_offsets, do_sub_vars);
649                     auto rvalps = new process_service(this, string(name), std::move(command),
650                             command_offsets, depends);
651                     rvalps->set_workding_dir(working_dir);
652                     rvalps->set_restart_interval(restart_interval, max_restarts);
653                     rvalps->set_restart_delay(restart_delay);
654                     rvalps->set_stop_timeout(stop_timeout);
655                     rvalps->set_start_timeout(start_timeout);
656                     rvalps->set_extra_termination_signal(term_signal);
657                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
658                     rvalps->set_workding_dir(working_dir);
659                     rvalps->set_notification_fd(readiness_fd);
660                     rvalps->set_notification_var(std::move(readiness_var));
661                     #if USE_UTMPX
662                     rvalps->set_utmp_id(inittab_id);
663                     rvalps->set_utmp_line(inittab_line);
664                     #endif
665                     rval = rvalps;
666                 }
667                 else if (service_type == service_type_t::BGPROCESS) {
668                     do_env_subst(command, command_offsets, do_sub_vars);
669                     auto rvalps = new bgproc_service(this, string(name), std::move(command),
670                             command_offsets, depends);
671                     rvalps->set_workding_dir(working_dir);
672                     rvalps->set_pid_file(std::move(pid_file));
673                     rvalps->set_restart_interval(restart_interval, max_restarts);
674                     rvalps->set_restart_delay(restart_delay);
675                     rvalps->set_stop_timeout(stop_timeout);
676                     rvalps->set_start_timeout(start_timeout);
677                     rvalps->set_extra_termination_signal(term_signal);
678                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
679                     onstart_flags.runs_on_console = false;
680                     rval = rvalps;
681                 }
682                 else if (service_type == service_type_t::SCRIPTED) {
683                     do_env_subst(command, command_offsets, do_sub_vars);
684                     auto rvalps = new scripted_service(this, string(name), std::move(command),
685                             command_offsets, depends);
686                     rvalps->set_stop_command(stop_command, stop_command_offsets);
687                     rvalps->set_workding_dir(working_dir);
688                     rvalps->set_stop_timeout(stop_timeout);
689                     rvalps->set_start_timeout(start_timeout);
690                     rvalps->set_extra_termination_signal(term_signal);
691                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
692                     rval = rvalps;
693                 }
694                 else {
695                     rval = new service_record(this, string(name), service_type, depends);
696                 }
697                 rval->set_log_file(logfile);
698                 rval->set_auto_restart(auto_restart);
699                 rval->set_smooth_recovery(smooth_recovery);
700                 rval->set_flags(onstart_flags);
701                 rval->set_socket_details(std::move(socket_path), socket_perms, socket_uid, socket_gid);
702                 rval->set_chain_to(std::move(chain_to_name));
703                 *iter = rval;
704                 break;
705             }
706         }
707         
708         return rval;
709     }
710     catch (setting_exception &setting_exc)
711     {
712         // Must remove the dummy service record.
713         records.erase(std::find(records.begin(), records.end(), rval));
714         delete rval;
715         throw service_description_exc(name, std::move(setting_exc.get_info()));
716     }
717     catch (...) {
718         // Must remove the dummy service record.
719         records.erase(std::find(records.begin(), records.end(), rval));
720         delete rval;
721         throw;
722     }
723 }