environment: allow control of the environment block via D-Bus

This commit is contained in:
Lennart Poettering 2010-05-09 23:53:52 +02:00
parent 6e620becc8
commit 1137a57c26
15 changed files with 225 additions and 17 deletions

View 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 \
@ -59,6 +60,12 @@
" <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);

49
dbus.c
View File

@ -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;
}

2
dbus.h
View File

@ -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

View File

@ -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;
}

View File

@ -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,

View File

@ -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);

View File

@ -177,6 +177,8 @@ struct Manager {
char **sysvinit_path;
char **sysvrcnd_path;
char **environment;
usec_t boot_timestamp;
/* Data specific to the device subsystem */

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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,

67
strv.c
View File

@ -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;
}

1
strv.h
View File

@ -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)++)

View 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;

View 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);