Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service argument enhancements #394

Merged
merged 3 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion doc/manpages/dinit-service.5.m4
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ Specifies a file containing value assignments for environment variables, in the
format recognised by the \fBdinit\fR command's \fB\-\-env\-file\fR option (see \fBdinit\fR(8)).
The file is read when the service is loaded, therefore values from it can be used in variable
substitutions (see \fBVARIABLE SUBSTITUTION\fR).
Variable substitution is not performed on the \fBenv\-file\fR property value itself.
Minimal variable substitution is performed on the \fBenv\-file\fR property value itself.
If the path is not absolute, it is resolved relative to the directory containing the service
description.
.TP
Expand Down Expand Up @@ -784,6 +784,8 @@ The following commands are available:
\fB@include\fR \fIpath\fR
Include the contents of another file, specified via its full path.
If the specified file does not exist, an error is produced.
The \fIpath\fR is subject to minimal variable substitution
(see \fBVARIABLE SUBSTITUTION\fR).
.TP
\fB@include\-opt\fR \fIpath\fR
As for \fB@include\fR, but produces no error if the named file does not exist.
Expand Down
3 changes: 2 additions & 1 deletion src/igr-tests/igr-runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -995,5 +995,6 @@ void svc_arg_test()
"test-hello\n" +
"hello\n" +
"foo\n" +
"foo\n");
"foo\n" +
"bar\n");
}
4 changes: 4 additions & 0 deletions src/igr-tests/svc-arg/checkarg.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#!/bin/sh

echo "$1" >> "$OUTPUT"

if [ -n "$FOOVAR" ]; then
echo "$FOOVAR" >> "$OUTPUT"
fi
3 changes: 3 additions & 0 deletions src/igr-tests/svc-arg/sd/checkarg
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ type = process
waits-for = checkarg2@$1
command = ../checkarg.sh $1
restart = false
# this normally only supports absolute paths,
# but here we control it so just for the test...
@include-opt sd/inc-$1
1 change: 1 addition & 0 deletions src/igr-tests/svc-arg/sd/env-foo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FOOVAR=bar
1 change: 1 addition & 0 deletions src/igr-tests/svc-arg/sd/inc-foo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
env-file = env-foo
59 changes: 41 additions & 18 deletions src/includes/load-service.h
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,9 @@ inline void parse_rlimit(const std::string &line, file_pos_ref input_pos,
}
}

inline string read_include_path(string const &svcname, string const &meta_cmd, file_pos_ref input_pos,
string_iterator &i, string_iterator end, const char *argval);

// Process an opened service file, line by line.
// name - the service name
// service_input - the service file input stream (stack)
Expand All @@ -899,7 +902,7 @@ inline void parse_rlimit(const std::string &line, file_pos_ref input_pos,
//
// May throw service load exceptions or I/O exceptions if enabled on stream.
template <typename T>
void process_service_file(string name, file_input_stack &service_input, T process_line_func)
void process_service_file(string name, file_input_stack &service_input, T process_line_func, const char *argval = nullptr)
{
string line;

Expand Down Expand Up @@ -957,12 +960,8 @@ void process_service_file(string name, file_input_stack &service_input, T proces
bool is_include_opt = (meta_cmd == "include-opt");
if (is_include_opt || meta_cmd == "include") {
// @include-opt or @include
std::list<std::pair<unsigned,unsigned>> part_positions;
file_pos_ref input_pos { service_input.current_file_name(), line_num };
std::string include_name = read_setting_value(input_pos, i, end, &part_positions);
if (part_positions.size() != 1) {
throw service_description_exc(name, "'@" + meta_cmd + "' requires a single argument", input_pos);
}
std::string include_name = read_include_path(name, meta_cmd, input_pos, i, end, argval);

std::ifstream file(include_name);
file.exceptions(std::ios::badbit);
Expand Down Expand Up @@ -1093,9 +1092,9 @@ static void value_var_subst(const char *setting_name, std::string &line,
if (name.empty()) {
throw service_description_exc(setting_name, "invalid/missing variable name after '$'");
}
else if (is_arg && (name != "1" || !argval)) {
// only one arg is supported and it must be present
throw service_description_exc(setting_name, "missing value in argument substitution");
else if (is_arg && name != "1") {
// only one arg is supported
throw service_description_exc(setting_name, "only one service argument may be present");
}
char altmode = '\0';
bool colon = false;
Expand Down Expand Up @@ -1133,11 +1132,17 @@ static void value_var_subst(const char *setting_name, std::string &line,
if (!resolved || (colon && !*resolved)) {
resolved_vw = {line.c_str() + (altbeg - line.begin()), (size_t)(altend - altbeg)};
}
} else if (altmode == '+') {
}
else if (altmode == '+') {
if (resolved && (!colon || *resolved)) {
resolved_vw = {line.c_str() + (altbeg - line.begin()), (size_t)(altend - altbeg)};
}
}
else if (is_arg && !argval) {
// $1 and ${1} is special in that it must be set or it is an error
// however, we want the more complex syntaxes for conditional substitution
throw service_description_exc(setting_name, "missing value in argument substitution");
}

xpos = j - line.begin();
int name_len = xpos - dindx;
Expand Down Expand Up @@ -1233,8 +1238,8 @@ static void value_var_subst(const char *setting_name, std::string &line,
line = std::move(r_line);
}

// Reads a dependency name while performing minimal argument expansion in it.
inline string read_dependency_value(const char *setting_name, file_pos_ref input_pos, string_iterator &i,
// Reads a value while performing minimal argument expansion in it.
inline string read_value_with_arg(const char *setting_name, file_pos_ref input_pos, string_iterator &i,
string_iterator end, const char *argval)
{
string rval;
Expand All @@ -1246,6 +1251,24 @@ inline string read_dependency_value(const char *setting_name, file_pos_ref input
return rval;
}

// Reads an include path while performing minimal argument expansion in it.
inline string read_include_path(string const &svcname, string const &meta_cmd, file_pos_ref input_pos,
string_iterator &i, string_iterator end, const char *argval)
{
string rval;
std::list<std::pair<unsigned,unsigned>> parts;

read_setting_value(rval, setting_op_t::ASSIGN, input_pos, i, end, &parts);
if (parts.size() != 1) {
throw service_description_exc(svcname, "'@" + meta_cmd + "' requires a single argument", input_pos);
}

std::list<std::pair<unsigned,unsigned>> offsets;
offsets.emplace_back(0, rval.size());
value_var_subst(meta_cmd.c_str(), rval, offsets, resolve_env_var, nullptr, argval);
return rval;
}

// A wrapper type for service parameters. It is parameterised by dependency type.
template <class dep_type>
class service_settings_wrapper
Expand Down Expand Up @@ -1514,7 +1537,7 @@ void process_service_line(settings_wrapper &settings, const char *name, const ch
settings.working_dir = read_setting_value(input_pos, i, end, nullptr);
break;
case setting_id_t::ENV_FILE:
settings.env_file = read_setting_value(input_pos, i, end, nullptr);
settings.env_file = read_value_with_arg(setting.c_str(), input_pos, i, end, arg);
break;
#if SUPPORT_CGROUPS
case setting_id_t::RUN_IN_CGROUP:
Expand Down Expand Up @@ -1552,21 +1575,21 @@ void process_service_line(settings_wrapper &settings, const char *name, const ch
break;
case setting_id_t::DEPENDS_ON:
{
string dependency_name = read_dependency_value(setting.c_str(), input_pos, i, end, arg);
string dependency_name = read_value_with_arg(setting.c_str(), input_pos, i, end, arg);
settings.depends.emplace_back(load_service(dependency_name.c_str()),
dependency_type::REGULAR);
break;
}
case setting_id_t::DEPENDS_MS:
{
string dependency_name = read_dependency_value(setting.c_str(), input_pos, i, end, arg);
string dependency_name = read_value_with_arg(setting.c_str(), input_pos, i, end, arg);
settings.depends.emplace_back(load_service(dependency_name.c_str()),
dependency_type::MILESTONE);
break;
}
case setting_id_t::WAITS_FOR:
{
string dependency_name = read_dependency_value(setting.c_str(), input_pos, i, end, arg);
string dependency_name = read_value_with_arg(setting.c_str(), input_pos, i, end, arg);
settings.depends.emplace_back(load_service(dependency_name.c_str()),
dependency_type::WAITS_FOR);
break;
Expand All @@ -1591,13 +1614,13 @@ void process_service_line(settings_wrapper &settings, const char *name, const ch
}
case setting_id_t::AFTER:
{
string after_name = read_dependency_value(setting.c_str(), input_pos, i, end, arg);
string after_name = read_value_with_arg(setting.c_str(), input_pos, i, end, arg);
settings.after_svcs.emplace_back(std::move(after_name));
break;
}
case setting_id_t::BEFORE:
{
string before_name = read_dependency_value(setting.c_str(), input_pos, i, end, arg);
string before_name = read_value_with_arg(setting.c_str(), input_pos, i, end, arg);
settings.before_svcs.emplace_back(std::move(before_name));
break;
}
Expand Down
2 changes: 1 addition & 1 deletion src/load-service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ service_record * dirload_service_set::load_reload_service(const char *fullname,

process_service_line(settings, name.c_str(), argval, line, fpr, setting,
op, i, end, load_service_n, process_dep_dir_n);
});
}, argval);

auto report_err = [&](const char *msg){
throw service_load_exc(name, msg);
Expand Down