30ce03abf24eb4f1e228d8638696b3b0215ace85
[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
16 #include "proc-service.h"
17
18 using string = std::string;
19 using string_iterator = std::string::iterator;
20
21 // Utility function to skip white space. Returns an iterator at the
22 // first non-white-space position (or at end).
23 static string_iterator skipws(string_iterator i, string_iterator end)
24 {
25     using std::locale;
26     using std::isspace;
27     
28     while (i != end) {
29       if (! isspace(*i, locale::classic())) {
30         break;
31       }
32       ++i;
33     }
34     return i;
35 }
36
37 // Read a setting name.
38 static string read_setting_name(string_iterator & i, string_iterator end)
39 {
40     using std::locale;
41     using std::ctype;
42     using std::use_facet;
43     
44     const ctype<char> & facet = use_facet<ctype<char> >(locale::classic());
45
46     string rval;
47     // Allow alphabetical characters, and dash (-) in setting name
48     while (i != end && (*i == '-' || facet.is(ctype<char>::alpha, *i))) {
49         rval += *i;
50         ++i;
51     }
52     return rval;
53 }
54
55 namespace {
56     class setting_exception
57     {
58         std::string info;
59         
60         public:
61         setting_exception(const std::string &&exc_info) : info(std::move(exc_info))
62         {
63         }
64         
65         std::string &get_info()
66         {
67             return info;
68         }
69     };
70 }
71
72
73 // Read a setting value
74 //
75 // In general a setting value is a single-line string. It may contain multiple parts
76 // separated by white space (which is normally collapsed). A hash mark - # - denotes
77 // the end of the value and the beginning of a comment (it should be preceded by
78 // whitespace).
79 //
80 // Part of a value may be quoted using double quote marks, which prevents collapse
81 // of whitespace and interpretation of most special characters (the quote marks will
82 // not be considered part of the value). A backslash can precede a character (such
83 // as '#' or '"' or another backslash) to remove its special meaning. Newline
84 // characters are not allowed in values and cannot be quoted.
85 //
86 // This function expects the string to be in an ASCII-compatible, single byte
87 // encoding (the "classic" locale).
88 //
89 // Params:
90 //    service_name - the name of the service to which the setting applies
91 //    i  -  reference to string iterator through the line
92 //    end -   iterator at end of line
93 //    part_positions -  list of <int,int> to which the position of each setting value
94 //                      part will be added as [start,end). May be null.
95 static string read_setting_value(string_iterator & i, string_iterator end,
96         std::list<std::pair<unsigned,unsigned>> * part_positions = nullptr)
97 {
98     using std::locale;
99     using std::isspace;
100
101     i = skipws(i, end);
102     
103     string rval;
104     bool new_part = true;
105     int part_start;
106     
107     while (i != end) {
108         char c = *i;
109         if (c == '\"') {
110             if (new_part) {
111                 part_start = rval.length();
112                 new_part = false;
113             }
114             // quoted string
115             ++i;
116             while (i != end) {
117                 c = *i;
118                 if (c == '\"') break;
119                 if (c == '\n') {
120                     throw setting_exception("Line end inside quoted string");
121                 }
122                 else if (c == '\\') {
123                     // A backslash escapes the following character.
124                     ++i;
125                     if (i != end) {
126                         c = *i;
127                         if (c == '\n') {
128                             throw setting_exception("Line end follows backslash escape character (`\\')");
129                         }
130                         rval += c;
131                     }
132                 }
133                 else {
134                     rval += c;
135                 }
136                 ++i;
137             }
138             if (i == end) {
139                 // String wasn't terminated
140                 throw setting_exception("Unterminated quoted string");
141             }
142         }
143         else if (c == '\\') {
144             if (new_part) {
145                 part_start = rval.length();
146                 new_part = false;
147             }
148             // A backslash escapes the next character
149             ++i;
150             if (i != end) {
151                 rval += *i;
152             }
153             else {
154                 throw setting_exception("Backslash escape (`\\') not followed by character");
155             }
156         }
157         else if (isspace(c, locale::classic())) {
158             if (! new_part && part_positions != nullptr) {
159                 part_positions->emplace_back(part_start, rval.length());
160                 new_part = true;
161             }
162             i = skipws(i, end);
163             if (i == end) break;
164             if (*i == '#') break; // comment
165             rval += ' ';  // collapse ws to a single space
166             continue;
167         }
168         else if (c == '#') {
169             // Possibly intended a comment; we require leading whitespace to reduce occurrence of accidental
170             // comments in setting values.
171             throw setting_exception("hashmark (`#') comment must be separated from setting value by whitespace");
172         }
173         else {
174             if (new_part) {
175                 part_start = rval.length();
176                 new_part = false;
177             }
178             rval += c;
179         }
180         ++i;
181     }
182
183     // Got to end:
184     if (part_positions != nullptr) {
185         part_positions->emplace_back(part_start, rval.length());
186     }
187
188     return rval;
189 }
190
191 static int signal_name_to_number(std::string &signame)
192 {
193     if (signame == "HUP") return SIGHUP;
194     if (signame == "INT") return SIGINT;
195     if (signame == "QUIT") return SIGQUIT;
196     if (signame == "USR1") return SIGUSR1;
197     if (signame == "USR2") return SIGUSR2;
198     return -1;
199 }
200
201 static const char * uid_err_msg = "Specified user id contains invalid numeric characters or is outside allowed range.";
202
203 // Parse a userid parameter which may be a numeric user ID or a username. If a name, the
204 // userid is looked up via the system user database (getpwnam() function). In this case,
205 // the associated group is stored in the location specified by the group_p parameter iff
206 // it is not null and iff it contains the value -1.
207 static uid_t parse_uid_param(const std::string &param, const std::string &service_name, gid_t *group_p)
208 {
209     // Could be a name or a numeric id. But we should assume numeric first, just in case
210     // a user manages to give themselves a username that parses as a number.
211     std::size_t ind = 0;
212     try {
213         // POSIX does not specify whether uid_t is an signed or unsigned, but regardless
214         // is is probably safe to assume that valid values are positive. We'll also assert
215         // that the value range fits within "unsigned long long" since it seems unlikely
216         // that would ever not be the case.
217         static_assert((uintmax_t)std::numeric_limits<uid_t>::max() <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "uid_t is too large");
218         unsigned long long v = std::stoull(param, &ind, 0);
219         if (v > static_cast<unsigned long long>(std::numeric_limits<uid_t>::max()) || ind != param.length()) {
220             throw service_description_exc(service_name, uid_err_msg);
221         }
222         return v;
223     }
224     catch (std::out_of_range &exc) {
225         throw service_description_exc(service_name, uid_err_msg);
226     }
227     catch (std::invalid_argument &exc) {
228         // Ok, so it doesn't look like a number: proceed...
229     }
230
231     errno = 0;
232     struct passwd * pwent = getpwnam(param.c_str());
233     if (pwent == nullptr) {
234         // Maybe an error, maybe just no entry.
235         if (errno == 0) {
236             throw service_description_exc(service_name, "Specified user \"" + param + "\" does not exist in system database.");
237         }
238         else {
239             throw service_description_exc(service_name, std::string("Error accessing user database: ") + strerror(errno));
240         }
241     }
242     
243     if (group_p && *group_p != (gid_t)-1) {
244         *group_p = pwent->pw_gid;
245     }
246     
247     return pwent->pw_uid;
248 }
249
250 static const char * num_err_msg = "Specified value contains invalid numeric characters or is outside allowed range.";
251
252 // Parse an unsigned numeric parameter value
253 static unsigned long long parse_unum_param(const std::string &param, const std::string &service_name,
254         unsigned long long max = std::numeric_limits<unsigned long long>::max())
255 {
256     std::size_t ind = 0;
257     try {
258         unsigned long long v = std::stoull(param, &ind, 0);
259         if (v > max || ind != param.length()) {
260             throw service_description_exc(service_name, num_err_msg);
261         }
262         return v;
263     }
264     catch (std::out_of_range &exc) {
265         throw service_description_exc(service_name, num_err_msg);
266     }
267     catch (std::invalid_argument &exc) {
268         throw service_description_exc(service_name, num_err_msg);
269     }
270 }
271
272 static const char * gid_err_msg = "Specified group id contains invalid numeric characters or is outside allowed range.";
273
274 static gid_t parse_gid_param(const std::string &param, const std::string &service_name)
275 {
276     // Could be a name or a numeric id. But we should assume numeric first, just in case
277     // a user manages to give themselves a username that parses as a number.
278     std::size_t ind = 0;
279     try {
280         // POSIX does not specify whether uid_t is an signed or unsigned, but regardless
281         // is is probably safe to assume that valid values are positive. We'll also assume
282         // that the value range fits with "unsigned long long" since it seems unlikely
283         // that would ever not be the case.
284         static_assert((uintmax_t)std::numeric_limits<gid_t>::max() <= (uintmax_t)std::numeric_limits<unsigned long long>::max(), "gid_t is too large");
285         unsigned long long v = std::stoull(param, &ind, 0);
286         if (v > static_cast<unsigned long long>(std::numeric_limits<gid_t>::max()) || ind != param.length()) {
287             throw service_description_exc(service_name, gid_err_msg);
288         }
289         return v;
290     }
291     catch (std::out_of_range &exc) {
292         throw service_description_exc(service_name, gid_err_msg);
293     }
294     catch (std::invalid_argument &exc) {
295         // Ok, so it doesn't look like a number: proceed...
296     }
297
298     errno = 0;
299     struct group * grent = getgrnam(param.c_str());
300     if (grent == nullptr) {
301         // Maybe an error, maybe just no entry.
302         if (errno == 0) {
303             throw service_description_exc(service_name, "Specified group \"" + param + "\" does not exist in system database.");
304         }
305         else {
306             throw service_description_exc(service_name, std::string("Error accessing group database: ") + strerror(errno));
307         }
308     }
309     
310     return grent->gr_gid;
311 }
312
313 // Parse a time, specified as a decimal number of seconds (with optional fractional component after decimal
314 // point or decimal comma).
315 //
316 static void parse_timespec(const std::string &paramval, const std::string &servicename,
317         const char * paramname, timespec &ts)
318 {
319     decltype(ts.tv_sec) isec = 0;
320     decltype(ts.tv_nsec) insec = 0;
321     auto max_secs = std::numeric_limits<decltype(isec)>::max() / 10;
322     auto len = paramval.length();
323     decltype(len) i;
324     for (i = 0; i < len; i++) {
325         char ch = paramval[i];
326         if (ch == '.' || ch == ',') {
327             i++;
328             break;
329         }
330         if (ch < '0' || ch > '9') {
331             throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
332         }
333         // check for overflow
334         if (isec >= max_secs) {
335            throw service_description_exc(servicename, std::string("Too-large value for ") + paramname);
336         }
337         isec *= 10;
338         isec += ch - '0';
339     }
340     decltype(insec) insec_m = 100000000; // 10^8
341     for ( ; i < len; i++) {
342         char ch = paramval[i];
343         if (ch < '0' || ch > '9') {
344             throw service_description_exc(servicename, std::string("Bad value for ") + paramname);
345         }
346         insec += (ch - '0') * insec_m;
347         insec_m /= 10;
348     }
349     ts.tv_sec = isec;
350     ts.tv_nsec = insec;
351 }
352
353 // Perform environment variable substitution on a command line, if specified.
354 //   line -  the string storing the command and arguments
355 //   offsets - the [start,end) pair of offsets of the command and each argument within the string
356 //
357 static void do_env_subst(std::string &line, std::list<std::pair<unsigned,unsigned>> &offsets, bool do_sub_vars)
358 {
359     if (do_sub_vars) {
360         auto i = offsets.begin();
361         std::string r_line = line.substr(i->first, i->second - i->first); // copy command part
362         for (++i; i != offsets.end(); ++i) {
363             auto &offset_pair = *i;
364             if (line[offset_pair.first] == '$') {
365                 // Do subsitution for this part:
366                 auto env_name = line.substr(offset_pair.first + 1, offset_pair.second - offset_pair.first - 1);
367                 char *env_val = getenv(env_name.c_str());
368                 if (env_val != nullptr) {
369                     auto val_len = strlen(env_val);
370                     r_line += " ";
371                     offset_pair.first = r_line.length();
372                     offset_pair.second = offset_pair.first + val_len;
373                     r_line += env_val;
374                 }
375                 else {
376                     // specified enironment variable not set: treat as an empty string
377                     offset_pair.first = r_line.length();
378                     offset_pair.second = offset_pair.first;
379                 }
380             }
381             else {
382                 // No subsitution for this part:
383                 r_line += " ";
384                 auto new_offs = r_line.length();
385                 auto len = offset_pair.second - offset_pair.first;
386                 r_line += line.substr(offset_pair.first, len);
387                 offset_pair.first = new_offs;
388                 offset_pair.second = new_offs + len;
389             }
390         }
391         line = std::move(r_line);
392     }
393 }
394
395 // Find a service record, or load it from file. If the service has
396 // dependencies, load those also.
397 //
398 // Might throw a ServiceLoadExc exception if a dependency cycle is found or if another
399 // problem occurs (I/O error, service description not found etc). Throws std::bad_alloc
400 // if a memory allocation failure occurs.
401 //
402 service_record * dirload_service_set::load_service(const char * name)
403 {
404     using std::string;
405     using std::ifstream;
406     using std::ios;
407     using std::ios_base;
408     using std::locale;
409     using std::isspace;
410     
411     using std::list;
412     using std::pair;
413     
414     // First try and find an existing record...
415     service_record * rval = find_service(string(name));
416     if (rval != 0) {
417         if (rval->is_dummy()) {
418             throw service_cyclic_dependency(name);
419         }
420         return rval;
421     }
422
423     ifstream service_file;
424
425     // Couldn't find one. Have to load it.
426     for (auto &service_dir : service_dirs) {
427         string service_filename = service_dir.get_dir();
428         if (*(service_filename.rbegin()) != '/') {
429             service_filename += '/';
430         }
431         service_filename += name;
432
433         service_file.open(service_filename.c_str(), ios::in);
434         if (service_file) break;
435     }
436     
437     if (! service_file) {
438         throw service_not_found(string(name));
439     }
440
441     string command;
442     list<pair<unsigned,unsigned>> command_offsets;
443     string stop_command;
444     list<pair<unsigned,unsigned>> stop_command_offsets;
445     string working_dir;
446     string pid_file;
447
448     bool do_sub_vars = false;
449
450     service_type_t service_type = service_type_t::PROCESS;
451     std::list<prelim_dep> depends;
452     string logfile;
453     service_flags_t onstart_flags;
454     int term_signal = -1;  // additional termination signal
455     bool auto_restart = false;
456     bool smooth_recovery = false;
457     string socket_path;
458     int socket_perms = 0666;
459     // Note: Posix allows that uid_t and gid_t may be unsigned types, but eg chown uses -1 as an
460     // invalid value, so it's safe to assume that we can do the same:
461     uid_t socket_uid = -1;
462     gid_t socket_gid = -1;
463     // Restart limit interval / count; default is 10 seconds, 3 restarts:
464     timespec restart_interval = { .tv_sec = 10, .tv_nsec = 0 };
465     int max_restarts = 3;
466     timespec restart_delay = { .tv_sec = 0, .tv_nsec = 200000000 };
467     timespec stop_timeout = { .tv_sec = 10, .tv_nsec = 0 };
468     timespec start_timeout = { .tv_sec = 60, .tv_nsec = 0 };
469     
470     uid_t run_as_uid = -1;
471     gid_t run_as_gid = -1;
472
473     string line;
474     service_file.exceptions(ios::badbit | ios::failbit);
475     
476     // Add a dummy service record now to prevent infinite recursion in case of cyclic dependency.
477     // We replace this with the real service later (or remove it if we find a configuration error).
478     rval = new service_record(this, string(name));
479     add_service(rval);
480     
481     try {
482         // getline can set failbit if it reaches end-of-file, we don't want an exception in that case:
483         service_file.exceptions(ios::badbit);
484         
485         while (getline(service_file, line)) {
486             string::iterator i = line.begin();
487             string::iterator end = line.end();
488           
489             i = skipws(i, end);
490             if (i != end) {
491                 if (*i == '#') {
492                     continue;  // comment line
493                 }
494                 string setting = read_setting_name(i, end);
495                 i = skipws(i, end);
496                 if (i == end || (*i != '=' && *i != ':')) {
497                     throw service_description_exc(name, "Badly formed line.");
498                 }
499                 i = skipws(++i, end);
500                 
501                 if (setting == "command") {
502                     command = read_setting_value(i, end, &command_offsets);
503                 }
504                 else if (setting == "working-dir") {
505                     working_dir = read_setting_value(i, end, nullptr);
506                 }
507                 else if (setting == "socket-listen") {
508                     socket_path = read_setting_value(i, end, nullptr);
509                 }
510                 else if (setting == "socket-permissions") {
511                     string sock_perm_str = read_setting_value(i, end, nullptr);
512                     std::size_t ind = 0;
513                     try {
514                         socket_perms = std::stoi(sock_perm_str, &ind, 8);
515                         if (ind != sock_perm_str.length()) {
516                             throw std::logic_error("");
517                         }
518                     }
519                     catch (std::logic_error &exc) {
520                         throw service_description_exc(name, "socket-permissions: Badly-formed or out-of-range numeric value");
521                     }
522                 }
523                 else if (setting == "socket-uid") {
524                     string sock_uid_s = read_setting_value(i, end, nullptr);
525                     socket_uid = parse_uid_param(sock_uid_s, name, &socket_gid);
526                 }
527                 else if (setting == "socket-gid") {
528                     string sock_gid_s = read_setting_value(i, end, nullptr);
529                     socket_gid = parse_gid_param(sock_gid_s, name);
530                 }
531                 else if (setting == "stop-command") {
532                     stop_command = read_setting_value(i, end, &stop_command_offsets);
533                 }
534                 else if (setting == "pid-file") {
535                     pid_file = read_setting_value(i, end);
536                 }
537                 else if (setting == "depends-on") {
538                     string dependency_name = read_setting_value(i, end);
539                     depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::REGULAR);
540                 }
541                 else if (setting == "depends-ms") {
542                     string dependency_name = read_setting_value(i, end);
543                     depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::MILESTONE);
544                 }
545                 else if (setting == "waits-for") {
546                     string dependency_name = read_setting_value(i, end);
547                     depends.emplace_back(load_service(dependency_name.c_str()), dependency_type::WAITS_FOR);
548                 }
549                 else if (setting == "logfile") {
550                     logfile = read_setting_value(i, end);
551                 }
552                 else if (setting == "restart") {
553                     string restart = read_setting_value(i, end);
554                     auto_restart = (restart == "yes" || restart == "true");
555                 }
556                 else if (setting == "smooth-recovery") {
557                     string recovery = read_setting_value(i, end);
558                     smooth_recovery = (recovery == "yes" || recovery == "true");
559                 }
560                 else if (setting == "type") {
561                     string type_str = read_setting_value(i, end);
562                     if (type_str == "scripted") {
563                         service_type = service_type_t::SCRIPTED;
564                     }
565                     else if (type_str == "process") {
566                         service_type = service_type_t::PROCESS;
567                     }
568                     else if (type_str == "bgprocess") {
569                         service_type = service_type_t::BGPROCESS;
570                     }
571                     else if (type_str == "internal") {
572                         service_type = service_type_t::INTERNAL;
573                     }
574                     else {
575                         throw service_description_exc(name, "Service type must be one of: \"scripted\","
576                             " \"process\", \"bgprocess\" or \"internal\"");
577                     }
578                 }
579                 else if (setting == "options") {
580                     std::list<std::pair<unsigned,unsigned>> indices;
581                     string onstart_cmds = read_setting_value(i, end, &indices);
582                     for (auto indexpair : indices) {
583                         string option_txt = onstart_cmds.substr(indexpair.first, indexpair.second - indexpair.first);
584                         if (option_txt == "starts-rwfs") {
585                             onstart_flags.rw_ready = true;
586                         }
587                         else if (option_txt == "starts-log") {
588                             onstart_flags.log_ready = true;
589                         }
590                         else if (option_txt == "no-sigterm") {
591                             onstart_flags.no_sigterm = true;
592                         }
593                         else if (option_txt == "runs-on-console") {
594                             onstart_flags.runs_on_console = true;
595                             // A service that runs on the console necessarily starts on console:
596                             onstart_flags.starts_on_console = true;
597                         }
598                         else if (option_txt == "starts-on-console") {
599                             onstart_flags.starts_on_console = true;
600                         }
601                         else if (option_txt == "pass-cs-fd") {
602                             onstart_flags.pass_cs_fd = true;
603                         }
604                         else if (option_txt == "start-interruptible") {
605                             onstart_flags.start_interruptible = true;
606                         }
607                         else if (option_txt == "skippable") {
608                             onstart_flags.skippable = true;
609                         }
610                         else if (option_txt == "signal-process-only") {
611                             onstart_flags.signal_process_only = true;
612                         }
613                         else {
614                             throw service_description_exc(name, "Unknown option: " + option_txt);
615                         }
616                     }
617                 }
618                 else if (setting == "load-options") {
619                     std::list<std::pair<unsigned,unsigned>> indices;
620                     string load_opts = read_setting_value(i, end, &indices);
621                     for (auto indexpair : indices) {
622                         string option_txt = load_opts.substr(indexpair.first, indexpair.second - indexpair.first);
623                         if (option_txt == "sub-vars") {
624                             // substitute environment variables in command line
625                             do_sub_vars = true;
626                         }
627                         else if (option_txt == "no-sub-vars") {
628                             do_sub_vars = false;
629                         }
630                         else {
631                             throw service_description_exc(name, "Unknown load option: " + option_txt);
632                         }
633                     }
634                 }
635                 else if (setting == "termsignal") {
636                     string signame = read_setting_value(i, end, nullptr);
637                     int signo = signal_name_to_number(signame);
638                     if (signo == -1) {
639                         throw service_description_exc(name, "Unknown/unsupported termination signal: " + signame);
640                     }
641                     else {
642                         term_signal = signo;
643                     }
644                 }
645                 else if (setting == "restart-limit-interval") {
646                     string interval_str = read_setting_value(i, end, nullptr);
647                     parse_timespec(interval_str, name, "restart-limit-interval", restart_interval);
648                 }
649                 else if (setting == "restart-delay") {
650                     string rsdelay_str = read_setting_value(i, end, nullptr);
651                     parse_timespec(rsdelay_str, name, "restart-delay", restart_delay);
652                 }
653                 else if (setting == "restart-limit-count") {
654                     string limit_str = read_setting_value(i, end, nullptr);
655                     max_restarts = parse_unum_param(limit_str, name, std::numeric_limits<int>::max());
656                 }
657                 else if (setting == "stop-timeout") {
658                     string stoptimeout_str = read_setting_value(i, end, nullptr);
659                     parse_timespec(stoptimeout_str, name, "stop-timeout", stop_timeout);
660                 }
661                 else if (setting == "start-timeout") {
662                     string starttimeout_str = read_setting_value(i, end, nullptr);
663                     parse_timespec(starttimeout_str, name, "start-timeout", start_timeout);
664                 }
665                 else if (setting == "run-as") {
666                     string run_as_str = read_setting_value(i, end, nullptr);
667                     run_as_uid = parse_uid_param(run_as_str, name, &run_as_gid);
668                 }
669                 else {
670                     throw service_description_exc(name, "Unknown setting: " + setting);
671                 }
672             }
673         }
674         
675         service_file.close();
676         
677         if (service_type == service_type_t::PROCESS || service_type == service_type_t::BGPROCESS || service_type == service_type_t::SCRIPTED) {
678             if (command.length() == 0) {
679                 throw service_description_exc(name, "Service command not specified");
680             }
681         }
682         
683         // Now replace the dummy service record with a real record:
684         for (auto iter = records.begin(); iter != records.end(); iter++) {
685             if (*iter == rval) {
686                 // We've found the dummy record
687                 delete rval;
688                 if (service_type == service_type_t::PROCESS) {
689                     do_env_subst(command, command_offsets, do_sub_vars);
690                     auto rvalps = new process_service(this, string(name), std::move(command),
691                             command_offsets, depends);
692                     rvalps->set_workding_dir(working_dir);
693                     rvalps->set_restart_interval(restart_interval, max_restarts);
694                     rvalps->set_restart_delay(restart_delay);
695                     rvalps->set_stop_timeout(stop_timeout);
696                     rvalps->set_start_timeout(start_timeout);
697                     rvalps->set_extra_termination_signal(term_signal);
698                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
699                     rvalps->set_workding_dir(working_dir);
700                     // process service start / run on console must be the same:
701                     onstart_flags.starts_on_console = onstart_flags.runs_on_console;
702                     rval = rvalps;
703                 }
704                 else if (service_type == service_type_t::BGPROCESS) {
705                     do_env_subst(command, command_offsets, do_sub_vars);
706                     auto rvalps = new bgproc_service(this, string(name), std::move(command),
707                             command_offsets, depends);
708                     rvalps->set_workding_dir(working_dir);
709                     rvalps->set_pid_file(std::move(pid_file));
710                     rvalps->set_restart_interval(restart_interval, max_restarts);
711                     rvalps->set_restart_delay(restart_delay);
712                     rvalps->set_stop_timeout(stop_timeout);
713                     rvalps->set_start_timeout(start_timeout);
714                     rvalps->set_extra_termination_signal(term_signal);
715                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
716                     onstart_flags.runs_on_console = false;
717                     rval = rvalps;
718                 }
719                 else if (service_type == service_type_t::SCRIPTED) {
720                     do_env_subst(command, command_offsets, do_sub_vars);
721                     auto rvalps = new scripted_service(this, string(name), std::move(command),
722                             command_offsets, depends);
723                     rvalps->set_stop_command(stop_command, stop_command_offsets);
724                     rvalps->set_workding_dir(working_dir);
725                     rvalps->set_stop_timeout(stop_timeout);
726                     rvalps->set_start_timeout(start_timeout);
727                     rvalps->set_extra_termination_signal(term_signal);
728                     rvalps->set_run_as_uid_gid(run_as_uid, run_as_gid);
729                     rval = rvalps;
730                 }
731                 else {
732                     rval = new service_record(this, string(name), service_type, depends);
733                 }
734                 rval->set_log_file(logfile);
735                 rval->set_auto_restart(auto_restart);
736                 rval->set_smooth_recovery(smooth_recovery);
737                 rval->set_flags(onstart_flags);
738                 rval->set_socket_details(std::move(socket_path), socket_perms, socket_uid, socket_gid);
739                 *iter = rval;
740                 break;
741             }
742         }
743         
744         return rval;
745     }
746     catch (setting_exception &setting_exc)
747     {
748         // Must remove the dummy service record.
749         std::remove(records.begin(), records.end(), rval);
750         delete rval;
751         throw service_description_exc(name, std::move(setting_exc.get_info()));
752     }
753     catch (...) {
754         // Must remove the dummy service record.
755         std::remove(records.begin(), records.end(), rval);
756         delete rval;
757         throw;
758     }
759 }