chiark / gitweb /
environment: allow control of the environment block via D-Bus
authorLennart Poettering <lennart@poettering.net>
Sun, 9 May 2010 21:53:52 +0000 (23:53 +0200)
committerLennart Poettering <lennart@poettering.net>
Sun, 9 May 2010 21:53:52 +0000 (23:53 +0200)
15 files changed:
dbus-manager.c
dbus.c
dbus.h
execute.c
execute.h
manager.c
manager.h
missing.h
mount.c
service.c
socket.c
strv.c
strv.h
systemctl.vala
systemd-interfaces.vala

index 47fb403..4b3b45e 100644 (file)
@@ -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                       \
         "  <method name=\"Reload\"/>"                                   \
         "  <method name=\"Reexecute\"/>"                                \
         "  <method name=\"Exit\"/>"                                     \
+        "  <method name=\"SetEnvironment\">"                            \
+        "   <arg name=\"names\" type=\"as\" direction=\"in\"/>"         \
+        "  </method>"                                                   \
+        "  <method name=\"UnsetEnvironment\">"                          \
+        "   <arg name=\"names\" type=\"as\" direction=\"in\"/>"        \
+        "  </method>"                                                   \
         "  <signal name=\"UnitNew\">"                                   \
         "   <arg name=\"id\" type=\"s\"/>"                              \
         "   <arg name=\"unit\" type=\"o\"/>"                            \
@@ -82,6 +89,7 @@
         "  <property name=\"LogTarget\" type=\"s\" access=\"read\"/>"   \
         "  <property name=\"NNames\" type=\"u\" access=\"read\"/>"      \
         "  <property name=\"NJobs\" type=\"u\" access=\"read\"/>"       \
+        "  <property name=\"Environment\" type=\"as\" access=\"read\"/>" \
         " </interface>"                                                 \
         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 0054d15..6ed659a 100644 (file)
--- 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 47a6639..51b71ea 100644 (file)
--- 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
index 7192abf..a7775a4 100644 (file)
--- 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;
                 }
index f820d56..5c2d157 100644 (file)
--- 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,
index 1691719..a4696d6 100644 (file)
--- 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);
index b6b6926..a6500ac 100644 (file)
--- 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 */
index a8b5e80..7db7d7d 100644 (file)
--- 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 c989cdd..ce99af0 100644 (file)
--- 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,
index 45f7110..b208e9e 100644 (file)
--- 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,
index 755bc59..402eeaf 100644 (file)
--- 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 ed5755a..a749096 100644 (file)
--- 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 9cd8732..f0be83d 100644 (file)
--- 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)++)
index 388ab7d..ddbede9 100644 (file)
@@ -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;
index 045bb55..a95dc01 100644 (file)
@@ -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);