From c7040b5d1c2c148f12b6a5eef3dfce1661805131 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Feb 2014 02:02:00 +0100 Subject: [PATCH] core: allow User=, Group=, Nice=, Environment=, Type= to be passed when creating a transient service --- src/core/dbus-execute.c | 124 ++++++++++++++++++++++++++++++++++ src/core/dbus-execute.h | 2 + src/core/dbus-kill.c | 18 +++++ src/core/dbus-service.c | 24 +++++++ src/run/run.c | 146 +++++++++++++++++++++++++++++++++------- 5 files changed, 288 insertions(+), 26 deletions(-) diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index edf84f89f..d2bbda2db 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -30,6 +30,7 @@ #include "execute.h" #include "dbus-execute.h" #include "capability.h" +#include "env-util.h" BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput); @@ -513,3 +514,126 @@ int bus_property_get_exec_command_list( return sd_bus_message_close_container(reply); } + +int bus_exec_context_set_transient_property( + Unit *u, + ExecContext *c, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(u); + assert(c); + assert(name); + assert(message); + + if (streq(name, "User")) { + const char *uu; + + r = sd_bus_message_read(message, "s", &uu); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + + if (isempty(uu)) { + free(c->user); + c->user = NULL; + } else { + char *t; + + t = strdup(uu); + if (!t) + return -ENOMEM; + + free(c->user); + c->user = t; + } + + unit_write_drop_in_private_format(u, mode, name, "User=%s\n", uu); + } + + return 1; + + } else if (streq(name, "Group")) { + const char *gg; + + r = sd_bus_message_read(message, "s", &gg); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + + if (isempty(gg)) { + free(c->group); + c->group = NULL; + } else { + char *t; + + t = strdup(gg); + if (!t) + return -ENOMEM; + + free(c->group); + c->group = t; + } + + unit_write_drop_in_private_format(u, mode, name, "Group=%s\n", gg); + } + + return 1; + + } else if (streq(name, "Nice")) { + int n; + + r = sd_bus_message_read(message, "i", &n); + if (r < 0) + return r; + + if (n < PRIO_MIN || n >= PRIO_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range"); + + if (mode != UNIT_CHECK) { + c->nice = n; + unit_write_drop_in_private_format(u, mode, name, "Nice=%i\n", n); + } + + return 1; + + } else if (streq(name, "Environment")) { + + _cleanup_strv_free_ char **l = NULL; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + if (!strv_env_is_valid(l)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block."); + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined; + char **e; + + e = strv_env_merge(2, c->environment, l); + if (!e) + return -ENOMEM; + + strv_free(c->environment); + c->environment = e; + + joined = strv_join(c->environment, " "); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "Environment=%s\n", joined); + } + + return 1; + } + + return 0; +} diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h index 5c8bc3063..e4c2d5ddf 100644 --- a/src/core/dbus-execute.h +++ b/src/core/dbus-execute.h @@ -42,3 +42,5 @@ extern const sd_bus_vtable bus_exec_vtable[]; int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); + +int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index 8d3e149f3..f3cfb226e 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -70,6 +70,24 @@ int bus_kill_context_set_transient_property( return 1; + } else if (streq(name, "KillSignal")) { + int sig; + + r = sd_bus_message_read(message, "i", &sig); + if (r < 0) + return r; + + if (sig <= 0 || sig >= _NSIG) + return sd_bus_error_setf(error, "Signal %i out of range", sig); + + if (mode != UNIT_CHECK) { + c->kill_signal = sig; + + unit_write_drop_in_private_format(u, mode, name, "KillSignal=%s\n", signal_to_string(sig)); + } + + return 1; + } else if (streq(name, "SendSIGHUP")) { int b; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 3db9339d4..73cf17541 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -96,6 +96,25 @@ static int bus_service_set_transient_property( return 1; + } else if (streq(name, "Type")) { + const char *t; + ServiceType k; + + r = sd_bus_message_read(message, "s", &t); + if (r < 0) + return r; + + k = service_type_from_string(t); + if (k < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service type %s", t); + + if (mode != UNIT_CHECK) { + s->type = k; + unit_write_drop_in_private_format(UNIT(s), mode, name, "Type=%s\n", service_type_to_string(s->type)); + } + + return 1; + } else if (streq(name, "ExecStart")) { unsigned n = 0; @@ -151,6 +170,7 @@ static int bus_service_set_transient_property( n++; } + if (r < 0) return r; @@ -223,6 +243,10 @@ int bus_service_set_property( if (r != 0) return r; + r = bus_exec_context_set_transient_property(u, &s->exec_context, name, message, mode, error); + if (r != 0) + return r; + r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, mode, error); if (r != 0) return r; diff --git a/src/run/run.c b/src/run/run.c index 1b14e40e4..f9c6c292e 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -39,6 +39,12 @@ static bool arg_send_sighup = false; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_user = false; +static const char *arg_service_type = NULL; +static const char *arg_exec_user = NULL; +static const char *arg_exec_group = NULL; +static int arg_nice = 0; +static bool arg_nice_set = false; +static char **arg_environment = NULL; static int help(void) { @@ -54,7 +60,12 @@ static int help(void) { " --description=TEXT Description for unit\n" " --slice=SLICE Run in the specified slice\n" " -r --remain-after-exit Leave service around until explicitly stopped\n" - " --send-sighup Send SIGHUP when terminating\n", + " --send-sighup Send SIGHUP when terminating\n" + " --service-type=TYPE Service type\n" + " --uid=USER Run as system user\n" + " --gid=GROUP Run as system group\n" + " --nice=NICE Nice level\n" + " --setenv=ENV Set environment\n", program_invocation_short_name); return 0; @@ -71,25 +82,35 @@ static int parse_argv(int argc, char *argv[]) { ARG_DESCRIPTION, ARG_SLICE, ARG_SEND_SIGHUP, + ARG_EXEC_USER, + ARG_EXEC_GROUP, + ARG_SERVICE_TYPE, + ARG_NICE, + ARG_SETENV }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "scope", no_argument, NULL, ARG_SCOPE }, - { "unit", required_argument, NULL, ARG_UNIT }, - { "description", required_argument, NULL, ARG_DESCRIPTION }, - { "slice", required_argument, NULL, ARG_SLICE }, - { "remain-after-exit", no_argument, NULL, 'r' }, - { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "scope", no_argument, NULL, ARG_SCOPE }, + { "unit", required_argument, NULL, ARG_UNIT }, + { "description", required_argument, NULL, ARG_DESCRIPTION }, + { "slice", required_argument, NULL, ARG_SLICE }, + { "remain-after-exit", no_argument, NULL, 'r' }, + { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "service-type", required_argument, NULL, ARG_SERVICE_TYPE }, + { "uid", required_argument, NULL, ARG_EXEC_USER }, + { "gid", required_argument, NULL, ARG_EXEC_GROUP }, + { "nice", required_argument, NULL, ARG_NICE }, + { "setenv", required_argument, NULL, ARG_SETENV }, {}, }; - int c; + int r, c; assert(argc >= 0); assert(argv); @@ -148,6 +169,35 @@ static int parse_argv(int argc, char *argv[]) { arg_host = optarg; break; + case ARG_SERVICE_TYPE: + arg_service_type = optarg; + break; + + case ARG_EXEC_USER: + arg_exec_user = optarg; + break; + + case ARG_EXEC_GROUP: + arg_exec_group = optarg; + break; + + case ARG_NICE: + r = safe_atoi(optarg, &arg_nice); + if (r < 0) { + log_error("Failed to parse nice value"); + return -EINVAL; + } + + arg_nice_set = true; + break; + + case ARG_SETENV: + + if (strv_extend(&arg_environment, optarg) < 0) + return log_oom(); + + break; + case '?': return -EINVAL; @@ -171,6 +221,11 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_scope && (arg_remain_after_exit || arg_service_type || arg_exec_user || arg_exec_group || arg_nice_set || arg_environment)) { + log_error("--remain-after-exit, --service-type=, --user=, --group=, --nice= and --setenv= are not supported in --scope mode."); + return -EINVAL; + } + return 1; } @@ -251,7 +306,6 @@ static int start_transient_service( _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_free_ char *name = NULL; - char **i; int r; if (arg_unit) @@ -269,6 +323,56 @@ static int start_transient_service( if (r < 0) return r; + if (arg_service_type) { + r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type); + if (r < 0) + return r; + } + + if (arg_exec_user) { + r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user); + if (r < 0) + return r; + } + + if (arg_exec_group) { + r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group); + if (r < 0) + return r; + } + + if (arg_nice_set) { + r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice); + if (r < 0) + return r; + } + + if (!strv_isempty(arg_environment)) { + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", "Environment"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, arg_environment); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) return r; @@ -293,17 +397,7 @@ static int start_transient_service( if (r < 0) return r; - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return r; - - STRV_FOREACH(i, argv) { - r = sd_bus_message_append(m, "s", *i); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(m); + r = sd_bus_message_append_strv(m, argv); if (r < 0) return r; -- 2.30.2