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