From 1137a57c2677936bab56c26591a42c93a5e670a8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 9 May 2010 23:53:52 +0200 Subject: [PATCH] environment: allow control of the environment block via D-Bus --- dbus-manager.c | 55 +++++++++++++++++++++++++++++++++ dbus.c | 49 ++++++++++++++++++++++++++++++ dbus.h | 2 ++ execute.c | 3 +- execute.h | 1 + manager.c | 5 +++ manager.h | 2 ++ missing.h | 1 - mount.c | 1 + service.c | 1 + socket.c | 1 + strv.c | 67 +++++++++++++++++++++++++++++++++++++++++ strv.h | 1 + systemctl.vala | 48 ++++++++++++++++++++--------- systemd-interfaces.vala | 5 +++ 15 files changed, 225 insertions(+), 17 deletions(-) diff --git a/dbus-manager.c b/dbus-manager.c index 47fb403f1..4b3b45e60 100644 --- a/dbus-manager.c +++ b/dbus-manager.c @@ -24,6 +24,7 @@ #include "dbus.h" #include "log.h" #include "dbus-manager.h" +#include "strv.h" #define INTROSPECTION_BEGIN \ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ @@ -59,6 +60,12 @@ " " \ " " \ " " \ + " " \ + " " \ + " " \ + " " \ + " " \ + " " \ " " \ " " \ " " \ @@ -82,6 +89,7 @@ " " \ " " \ " " \ + " " \ " " \ BUS_PROPERTIES_INTERFACE \ BUS_INTROSPECTABLE_INTERFACE @@ -162,6 +170,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection { "org.freedesktop.systemd1.Manager", "LogTarget", bus_manager_append_log_target, "s", NULL }, { "org.freedesktop.systemd1.Manager", "NNames", bus_manager_append_n_names, "u", NULL }, { "org.freedesktop.systemd1.Manager", "NJobs", bus_manager_append_n_jobs, "u", NULL }, + { "org.freedesktop.systemd1.Manager", "Environment", bus_property_append_strv, "as", m->environment }, { NULL, NULL, NULL, NULL, NULL } }; @@ -572,6 +581,52 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection m->exit_code = MANAGER_EXIT; + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) { + char **l = NULL, **e = NULL; + + if ((r = bus_parse_strv(message, &l)) < 0) { + if (r == -ENOMEM) + goto oom; + + return bus_send_error_reply(m, message, NULL, r); + } + + e = strv_env_merge(m->environment, l, NULL); + strv_free(l); + + if (!e) + goto oom; + + if (!(reply = dbus_message_new_method_return(message))) { + strv_free(e); + goto oom; + } + + strv_free(m->environment); + m->environment = e; + + } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) { + char **l = NULL, **e = NULL; + + if ((r = bus_parse_strv(message, &l)) < 0) { + if (r == -ENOMEM) + goto oom; + + return bus_send_error_reply(m, message, NULL, r); + } + + e = strv_env_delete(m->environment, l, NULL); + strv_free(l); + + if (!e) + goto oom; + + if (!(reply = dbus_message_new_method_return(message))) + goto oom; + + strv_free(m->environment); + m->environment = e; + } else return bus_default_message_handler(m, message, NULL, properties); diff --git a/dbus.c b/dbus.c index 0054d1519..6ed659a23 100644 --- a/dbus.c +++ b/dbus.c @@ -1085,3 +1085,52 @@ int bus_property_append_int32(Manager *m, DBusMessageIter *i, const char *proper return 0; } + +int bus_parse_strv(DBusMessage *m, char ***_l) { + DBusMessageIter iter, sub; + unsigned n = 0, i = 0; + char **l; + + assert(m); + assert(_l); + + if (!dbus_message_iter_init(m, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) + return -EINVAL; + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + n++; + dbus_message_iter_next(&sub); + } + + if (!(l = new(char*, n+1))) + return -ENOMEM; + + assert_se(dbus_message_iter_init(m, &iter)); + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *s; + + assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub, &s); + + if (!(l[i++] = strdup(s))) { + strv_free(l); + return -ENOMEM; + } + + dbus_message_iter_next(&sub); + } + + assert(i == n); + l[i] = NULL; + + if (_l) + *_l = l; + + return 0; +} diff --git a/dbus.h b/dbus.h index 47a6639b4..51b71eac6 100644 --- a/dbus.h +++ b/dbus.h @@ -102,4 +102,6 @@ int bus_property_append_uint64(Manager *m, DBusMessageIter *i, const char *prope return 0; \ } +int bus_parse_strv(DBusMessage *m, char ***_l); + #endif diff --git a/execute.c b/execute.c index 7192abfff..a7775a4ea 100644 --- a/execute.c +++ b/execute.c @@ -724,6 +724,7 @@ int exec_spawn(ExecCommand *command, char **argv, const ExecContext *context, int fds[], unsigned n_fds, + char **environment, bool apply_permissions, bool apply_chroot, bool confirm_spawn, @@ -1034,7 +1035,7 @@ int exec_spawn(ExecCommand *command, goto fail; } - if (!(final_env = strv_env_merge(environ, our_env, context->environment, NULL))) { + if (!(final_env = strv_env_merge(environment, our_env, context->environment, NULL))) { r = EXIT_MEMORY; goto fail; } diff --git a/execute.h b/execute.h index f820d56cb..5c2d15787 100644 --- a/execute.h +++ b/execute.h @@ -182,6 +182,7 @@ int exec_spawn(ExecCommand *command, char **argv, const ExecContext *context, int fds[], unsigned n_fds, + char **environment, bool apply_permissions, bool apply_chroot, bool confirm_spawn, diff --git a/manager.c b/manager.c index 1691719a6..a4696d6e9 100644 --- a/manager.c +++ b/manager.c @@ -51,6 +51,7 @@ #include "unit-name.h" #include "dbus-unit.h" #include "dbus-job.h" +#include "missing.h" /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */ #define GC_QUEUE_ENTRIES_MAX 16 @@ -336,6 +337,9 @@ int manager_new(ManagerRunningAs running_as, bool confirm_spawn, Manager **_m) { m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ + if (!(m->environment = strv_copy(environ))) + goto fail; + if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) goto fail; @@ -544,6 +548,7 @@ void manager_free(Manager *m) { strv_free(m->unit_path); strv_free(m->sysvinit_path); strv_free(m->sysvrcnd_path); + strv_free(m->environment); free(m->cgroup_controller); free(m->cgroup_hierarchy); diff --git a/manager.h b/manager.h index b6b6926bd..a6500ac60 100644 --- a/manager.h +++ b/manager.h @@ -177,6 +177,8 @@ struct Manager { char **sysvinit_path; char **sysvrcnd_path; + char **environment; + usec_t boot_timestamp; /* Data specific to the device subsystem */ diff --git a/missing.h b/missing.h index a8b5e80b0..7db7d7d2e 100644 --- a/missing.h +++ b/missing.h @@ -35,5 +35,4 @@ static inline int pivot_root(const char *new_root, const char *put_old) { return syscall(SYS_pivot_root, new_root, put_old); } - #endif diff --git a/mount.c b/mount.c index c989cddab..ce99af077 100644 --- a/mount.c +++ b/mount.c @@ -490,6 +490,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { NULL, &m->exec_context, NULL, 0, + m->meta.manager->environment, true, true, UNIT(m)->meta.manager->confirm_spawn, diff --git a/service.c b/service.c index 45f7110bd..b208e9e0b 100644 --- a/service.c +++ b/service.c @@ -1187,6 +1187,7 @@ static int service_spawn( argv, &s->exec_context, fds, n_fds, + s->meta.manager->environment, apply_permissions, apply_chroot, UNIT(s)->meta.manager->confirm_spawn, diff --git a/socket.c b/socket.c index 755bc598d..402eeaf62 100644 --- a/socket.c +++ b/socket.c @@ -578,6 +578,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { argv, &s->exec_context, NULL, 0, + s->meta.manager->environment, true, true, UNIT(s)->meta.manager->confirm_spawn, diff --git a/strv.c b/strv.c index ed5755a0d..a749096f9 100644 --- a/strv.c +++ b/strv.c @@ -434,3 +434,70 @@ fail: return NULL; } + +static bool env_match(const char *t, const char *pattern) { + assert(t); + assert(pattern); + + /* pattern a matches string a + * a matches a= + * a matches a=b + * a= matches a= + * a=b matches a=b + * a= does not match a + * a=b does not match a= + * a=b does not match a + * a=b does not match a=c */ + + if (streq(t, pattern)) + return true; + + if (!strchr(pattern, '=')) { + size_t l = strlen(pattern); + + return strncmp(t, pattern, l) == 0 && t[l] == '='; + } + + return false; +} + +char **strv_env_delete(char **x, ...) { + size_t n = 0, i = 0; + char **l, **k, **r, **j; + va_list ap; + + /* Deletes every entry fromx that is mentioned in the other + * string lists */ + + n = strv_length(x); + + if (!(r = new(char*, n+1))) + return NULL; + + STRV_FOREACH(k, x) { + va_start(ap, x); + + while ((l = va_arg(ap, char**))) + STRV_FOREACH(j, l) + if (env_match(*k, *j)) + goto delete; + + va_end(ap); + + if (!(r[i++] = strdup(*k))) { + strv_free(r); + return NULL; + } + + continue; + + delete: + va_end(ap); + } + + r[i] = NULL; + + assert(i <= n); + + return r; +} diff --git a/strv.h b/strv.h index 9cd8732b4..f0be83dd5 100644 --- a/strv.h +++ b/strv.h @@ -54,6 +54,7 @@ char **strv_split_quoted(const char *s) _malloc; char *strv_join(char **l, const char *separator) _malloc; char **strv_env_merge(char **x, ...) _sentinel; +char **strv_env_delete(char **x, ...) _sentinel; #define STRV_FOREACH(s, l) \ for ((s) = (l); (s) && *(s); (s)++) diff --git a/systemctl.vala b/systemctl.vala index 388ab7d50..ddbede94b 100644 --- a/systemctl.vala +++ b/systemctl.vala @@ -74,21 +74,24 @@ int main (string[] args) { context.add_main_entries(entries, null); context.set_description( "Commands:\n" + - " list-units List units\n" + - " list-jobs List jobs\n" + - " clear-jobs Cancel all jobs\n" + - " load [NAME...] Load one or more units\n" + - " cancel [JOB...] Cancel one or more jobs\n" + - " start [NAME...] Start on or more units\n" + - " stop [NAME...] Stop on or more units\n" + - " enter [NAME] Start one unit and stop all others\n" + - " restart [NAME...] Restart on or more units\n" + - " reload [NAME...] Reload on or more units\n" + - " monitor Monitor unit/job changes\n" + - " dump Dump server status\n" + - " snapshot [NAME] Create a snapshot\n" + - " daemon-reload Reload daemon configuration\n" + - " daemon-reexecute Reexecute daemon\n"); + " list-units List units\n" + + " list-jobs List jobs\n" + + " clear-jobs Cancel all jobs\n" + + " load [NAME...] Load one or more units\n" + + " cancel [JOB...] Cancel one or more jobs\n" + + " start [NAME...] Start on or more units\n" + + " stop [NAME...] Stop on or more units\n" + + " enter [NAME] Start one unit and stop all others\n" + + " restart [NAME...] Restart on or more units\n" + + " reload [NAME...] Reload on or more units\n" + + " monitor Monitor unit/job changes\n" + + " dump Dump server status\n" + + " snapshot [NAME] Create a snapshot\n" + + " daemon-reload Reload daemon configuration\n" + + " daemon-reexecute Reexecute daemon\n" + + " show-environment Dump environment\n" + + " set-environment [NAME=VALUE...] Set one or more environment variables\n" + + " unset-environment [NAME...] Unset one or more environment variables\n"); try { context.parse(ref args); @@ -245,6 +248,7 @@ int main (string[] args) { } else if (args[1] == "dump") stdout.puts(manager.dump()); + else if (args[1] == "snapshot") { ObjectPath p = manager.create_snapshot(args.length > 2 ? args[2] : ""); @@ -255,12 +259,26 @@ int main (string[] args) { "org.freedesktop.systemd1.Unit") as Unit; stdout.printf("%s\n", u.id); + } else if (args[1] == "daemon-reload") manager.reload(); + else if (args[1] == "daemon-reexecute" || args[1] == "daemon-reexec") manager.reexecute(); + else if (args[1] == "daemon-exit") manager.exit(); + + else if (args[1] == "show-environment") { + foreach(var x in manager.environment) + stderr.printf("%s\n", x); + + } else if (args[1] == "set-environment") + manager.set_environment(args[2:args.length]); + + else if (args[1] == "unset-environment") + manager.unset_environment(args[2:args.length]); + else { stderr.printf("Unknown command %s.\n", args[1]); return 1; diff --git a/systemd-interfaces.vala b/systemd-interfaces.vala index 045bb5504..a95dc01f5 100644 --- a/systemd-interfaces.vala +++ b/systemd-interfaces.vala @@ -43,6 +43,8 @@ public interface Manager : DBus.Object { ObjectPath unit_path; } + public abstract string[] environment { owned get; } + public abstract UnitInfo[] list_units() throws DBus.Error; public abstract JobInfo[] list_jobs() throws DBus.Error; @@ -63,6 +65,9 @@ public interface Manager : DBus.Object { public abstract ObjectPath create_snapshot(string name = "", bool cleanup = false) throws DBus.Error; + public abstract void set_environment(string[] names) throws DBus.Error; + public abstract void unset_environment(string[] names) throws DBus.Error; + public abstract signal void unit_new(string id, ObjectPath path); public abstract signal void unit_removed(string id, ObjectPath path); public abstract signal void job_new(uint32 id, ObjectPath path); -- 2.30.2