From: Davin McCall Date: Mon, 4 Jun 2018 09:43:09 +0000 (+0100) Subject: Introduce "load-options" setting and "sub-vars" option. X-Git-Tag: v0.2.0~12 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=aa81da01009fe9327b29399e1f294df8b698fed0;p=oweals%2Fdinit.git Introduce "load-options" setting and "sub-vars" option. This allows for specifying a service command line with parameters in the form of $NAME, where NAME is an environment variable. The parameter is replaced with the contents of the environment variable as a single value. --- diff --git a/doc/manpages/dinit-service.5 b/doc/manpages/dinit-service.5 index c3b4c26..06c8e78 100644 --- a/doc/manpages/dinit-service.5 +++ b/doc/manpages/dinit-service.5 @@ -201,6 +201,15 @@ will go this file. .TP \fBoptions\fR = \fIoption\fR... Specifies various options for this service. See the \fBOPTIONS\fR section. +.TP +\fBload-options\fR = \fIload_option\fR... +Specifies options for interpreting other settings when loading this service +description. Currently there is only one available option, \fBsub-vars\fR, +which specifies that command line arguments in the form of \fB$NAME\fR should +be replaced with the contents of the environment variable with the specified +name. Note that no word-splitting is performed and the variable value always +becomes a single argument; if the variable is not defined, it is replaced with +an empty (zero-length) argument. .\" .SS OPTIONS .\" diff --git a/src/load-service.cc b/src/load-service.cc index 5a76a54..15d882a 100644 --- a/src/load-service.cc +++ b/src/load-service.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -349,6 +350,48 @@ static void parse_timespec(const std::string ¶mval, const std::string &servi ts.tv_nsec = insec; } +// Perform environment variable substitution on a command line, if specified. +// line - the string storing the command and arguments +// offsets - the [start,end) pair of offsets of the command and each argument within the string +// +static void do_env_subst(std::string &line, std::list> &offsets, bool do_sub_vars) +{ + if (do_sub_vars) { + auto i = offsets.begin(); + std::string r_line = line.substr(i->first, i->second - i->first); // copy command part + for (++i; i != offsets.end(); ++i) { + auto &offset_pair = *i; + if (line[offset_pair.first] == '$') { + // Do subsitution for this part: + auto env_name = line.substr(offset_pair.first + 1, offset_pair.second - offset_pair.first - 1); + char *env_val = getenv(env_name.c_str()); + if (env_val != nullptr) { + auto val_len = strlen(env_val); + r_line += " "; + offset_pair.first = r_line.length(); + offset_pair.second = offset_pair.first + val_len; + r_line += env_val; + } + else { + // specified enironment variable not set: treat as an empty string + offset_pair.first = r_line.length(); + offset_pair.second = offset_pair.first; + } + } + else { + // No subsitution for this part: + r_line += " "; + auto new_offs = r_line.length(); + auto len = offset_pair.second - offset_pair.first; + r_line += line.substr(offset_pair.first, len); + offset_pair.first = new_offs; + offset_pair.second = new_offs + len; + } + } + line = std::move(r_line); + } +} + // Find a service record, or load it from file. If the service has // dependencies, load those also. // @@ -398,6 +441,8 @@ service_record * dirload_service_set::load_service(const char * name) string working_dir; string pid_file; + bool do_sub_vars = false; + service_type_t service_type = service_type_t::PROCESS; std::list depends; string logfile; @@ -561,6 +606,23 @@ service_record * dirload_service_set::load_service(const char * name) } } } + else if (setting == "load-options") { + std::list> indices; + string load_opts = read_setting_value(i, end, &indices); + for (auto indexpair : indices) { + string option_txt = load_opts.substr(indexpair.first, indexpair.second - indexpair.first); + if (option_txt == "sub-vars") { + // substitute environment variables in command line + do_sub_vars = true; + } + else if (option_txt == "no-sub-vars") { + do_sub_vars = false; + } + else { + throw service_description_exc(name, "Unknown load option: " + option_txt); + } + } + } else if (setting == "termsignal") { string signame = read_setting_value(i, end, nullptr); int signo = signal_name_to_number(signame); @@ -615,6 +677,7 @@ service_record * dirload_service_set::load_service(const char * name) // We've found the dummy record delete rval; if (service_type == service_type_t::PROCESS) { + do_env_subst(command, command_offsets, do_sub_vars); auto rvalps = new process_service(this, string(name), std::move(command), command_offsets, depends); rvalps->set_workding_dir(working_dir); @@ -631,6 +694,7 @@ service_record * dirload_service_set::load_service(const char * name) rval = rvalps; } else if (service_type == service_type_t::BGPROCESS) { + do_env_subst(command, command_offsets, do_sub_vars); auto rvalps = new bgproc_service(this, string(name), std::move(command), command_offsets, depends); rvalps->set_workding_dir(working_dir); @@ -646,6 +710,7 @@ service_record * dirload_service_set::load_service(const char * name) rval = rvalps; } else if (service_type == service_type_t::SCRIPTED) { + do_env_subst(command, command_offsets, do_sub_vars); auto rvalps = new scripted_service(this, string(name), std::move(command), command_offsets, depends); rvalps->set_stop_command(stop_command, stop_command_offsets);