#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\"/>" \
" <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
{ "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 }
};
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);
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;
+}
return 0; \
}
+int bus_parse_strv(DBusMessage *m, char ***_l);
+
#endif
char **argv,
const ExecContext *context,
int fds[], unsigned n_fds,
+ char **environment,
bool apply_permissions,
bool apply_chroot,
bool confirm_spawn,
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;
}
char **argv,
const ExecContext *context,
int fds[], unsigned n_fds,
+ char **environment,
bool apply_permissions,
bool apply_chroot,
bool confirm_spawn,
#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
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;
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);
char **sysvinit_path;
char **sysvrcnd_path;
+ char **environment;
+
usec_t boot_timestamp;
/* Data specific to the device subsystem */
return syscall(SYS_pivot_root, new_root, put_old);
}
-
#endif
NULL,
&m->exec_context,
NULL, 0,
+ m->meta.manager->environment,
true,
true,
UNIT(m)->meta.manager->confirm_spawn,
argv,
&s->exec_context,
fds, n_fds,
+ s->meta.manager->environment,
apply_permissions,
apply_chroot,
UNIT(s)->meta.manager->confirm_spawn,
argv,
&s->exec_context,
NULL, 0,
+ s->meta.manager->environment,
true,
true,
UNIT(s)->meta.manager->confirm_spawn,
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;
+}
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)++)
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);
} else if (args[1] == "dump")
stdout.puts(manager.dump());
+
else if (args[1] == "snapshot") {
ObjectPath p = manager.create_snapshot(args.length > 2 ? args[2] : "");
"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;
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;
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);