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