Systemd/src/bus-proxyd/driver.c

748 lines
34 KiB
C
Raw Normal View History

/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2013 Daniel Mack
Copyright 2014 Kay Sievers
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include "sd-bus.h"
#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-message.h"
#include "bus-util.h"
#include "driver.h"
#include "env-util.h"
#include "proxy.h"
#include "set.h"
#include "strv.h"
#include "synthesize.h"
#include "util.h"
static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
int r;
assert(bus);
assert(name);
assert(_creds);
r = sd_bus_get_name_creds(bus, name, mask, &c);
if (r == -ESRCH || r == -ENXIO)
return sd_bus_error_setf(error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", name);
if (r < 0)
return r;
*_creds = c;
c = NULL;
return 0;
}
static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
const char *name;
int r;
assert(bus);
assert(m);
assert(_creds);
r = sd_bus_message_read(m, "s", &name);
if (r < 0)
return r;
return get_creds_by_name(bus, name, mask, _creds, error);
}
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
static int driver_activation(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
ProxyActivation *activation = userdata;
/*
* The org.freedesktop.DBus.Peer.Ping() call returned. We don't care
* whether this succeeded, failed, was not implemented or timed out. We
* cannot assume that the target reacts to this properly. Hence, just
* send the reply to the activation request and be done.
*/
m = activation->request; /* claim reference */
--activation->proxy->n_activations;
LIST_REMOVE(activations_by_proxy, activation->proxy->activations, activation);
sd_bus_slot_unref(activation->slot);
free(activation);
return synthetic_reply_method_return(m, "u", BUS_START_REPLY_SUCCESS);
}
int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPolicy *sp, const struct ucred *ucred, Set *owned_names) {
int r;
assert(a);
assert(b);
assert(m);
if (!a->is_kernel)
return 0;
if (!streq_ptr(sd_bus_message_get_destination(m), "org.freedesktop.DBus"))
return 0;
/* The "Hello()" call is is handled in process_hello() */
if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
if (!sd_bus_message_has_signature(m, ""))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
return synthetic_reply_method_return(m, "s",
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
"<node>\n"
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
" <method name=\"Introspect\">\n"
" <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" </interface>\n"
" <interface name=\"org.freedesktop.DBus\">\n"
" <method name=\"AddMatch\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" </method>\n"
" <method name=\"RemoveMatch\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" </method>\n"
" <method name=\"GetConnectionCredentials\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"a{sv}\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetConnectionSELinuxSecurityContext\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"ay\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetConnectionUnixProcessID\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetConnectionUnixUser\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetId\">\n"
" <arg type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetNameOwner\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"Hello\">\n"
" <arg type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ListActivatableNames\">\n"
" <arg type=\"as\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ListNames\">\n"
" <arg type=\"as\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ListQueuedOwners\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"as\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"NameHasOwner\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"b\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ReleaseName\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"ReloadConfig\">\n"
" </method>\n"
" <method name=\"RequestName\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"StartServiceByName\">\n"
" <arg type=\"s\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"in\"/>\n"
" <arg type=\"u\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"UpdateActivationEnvironment\">\n"
" <arg type=\"a{ss}\" direction=\"in\"/>\n"
" </method>\n"
" <signal name=\"NameAcquired\">\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"NameLost\">\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"NameOwnerChanged\">\n"
" <arg type=\"s\"/>\n"
" <arg type=\"s\"/>\n"
" <arg type=\"s\"/>\n"
" </signal>\n"
" </interface>\n"
"</node>\n");
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "AddMatch")) {
const char *match;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "s", &match);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_add_match(a, NULL, match, proxy_match, p);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, NULL);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RemoveMatch")) {
const char *match;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "s", &match);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = bus_remove_match_by_string(a, match, NULL, NULL);
if (r == 0)
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_MATCH_RULE_NOT_FOUND, "Match rule not found"));
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, NULL);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionCredentials")) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = get_creds_by_message(a, m, SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SELINUX_CONTEXT, &creds, &error);
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
r = sd_bus_message_new_method_return(m, &reply);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_open_container(reply, 'a', "{sv}");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
/* Due to i.e. namespace translations some data might be missing */
if (creds->mask & SD_BUS_CREDS_PID) {
r = sd_bus_message_append(reply, "{sv}", "ProcessID", "u", (uint32_t) creds->pid);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
}
if (creds->mask & SD_BUS_CREDS_EUID) {
r = sd_bus_message_append(reply, "{sv}", "UnixUserID", "u", (uint32_t) creds->euid);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
}
if (creds->mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
r = sd_bus_message_open_container(reply, 'e', "sv");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_append(reply, "s", "LinuxSecurityLabel");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_open_container(reply, 'v', "ay");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_append_array(reply, 'y', creds->label, strlen(creds->label));
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_close_container(reply);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_close_container(reply);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_driver_send(m->bus, reply);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext")) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = get_creds_by_message(a, m, SD_BUS_CREDS_SELINUX_CONTEXT, &creds, &error);
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
if (!(creds->mask & SD_BUS_CREDS_SELINUX_CONTEXT))
return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
r = sd_bus_message_new_method_return(m, &reply);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_append_array(reply, 'y', creds->label, strlen(creds->label));
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_driver_send(m->bus, reply);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixProcessID")) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = get_creds_by_message(a, m, SD_BUS_CREDS_PID, &creds, &error);
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
if (!(creds->mask & SD_BUS_CREDS_PID))
return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
return synthetic_reply_method_return(m, "u", (uint32_t) creds->pid);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixUser")) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = get_creds_by_message(a, m, SD_BUS_CREDS_EUID, &creds, &error);
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
if (!(creds->mask & SD_BUS_CREDS_EUID))
return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
return synthetic_reply_method_return(m, "u", (uint32_t) creds->euid);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetId")) {
sd_id128_t server_id;
char buf[SD_ID128_STRING_MAX];
if (!sd_bus_message_has_signature(m, ""))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_get_bus_id(a, &server_id);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, "s", sd_id128_to_string(server_id, buf));
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetNameOwner")) {
const char *name;
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "s", &name);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (streq(name, "org.freedesktop.DBus"))
return synthetic_reply_method_return(m, "s", "org.freedesktop.DBus");
r = get_creds_by_name(a, name, SD_BUS_CREDS_UNIQUE_NAME, &creds, &error);
if (r < 0)
return synthetic_reply_method_errno(m, r, &error);
if (!(creds->mask & SD_BUS_CREDS_UNIQUE_NAME))
return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
return synthetic_reply_method_return(m, "s", creds->unique_name);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListActivatableNames")) {
_cleanup_strv_free_ char **names = NULL;
if (!sd_bus_message_has_signature(m, ""))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_list_names(a, NULL, &names);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
/* Let's sort the names list to make it stable */
strv_sort(names);
return synthetic_reply_method_return_strv(m, names);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListNames")) {
_cleanup_strv_free_ char **names = NULL;
if (!sd_bus_message_has_signature(m, ""))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_list_names(a, &names, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = strv_extend(&names, "org.freedesktop.DBus");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
/* Let's sort the names list to make it stable */
strv_sort(names);
return synthetic_reply_method_return_strv(m, names);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListQueuedOwners")) {
struct kdbus_cmd_list cmd = {
.flags = KDBUS_LIST_QUEUED,
.size = sizeof(cmd),
};
2015-02-04 16:21:27 +01:00
struct kdbus_info *name_list, *name;
_cleanup_strv_free_ char **owners = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
char *arg0;
int err = 0;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "s", &arg0);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_get_name_creds(a, arg0, 0, NULL);
if (r == -ESRCH || r == -ENXIO) {
sd_bus_error_setf(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Could not get owners of name '%s': no such name.", arg0);
return synthetic_reply_method_errno(m, r, &error);
}
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
2015-02-04 16:21:27 +01:00
r = ioctl(a->input_fd, KDBUS_CMD_LIST, &cmd);
if (r < 0)
return synthetic_reply_method_errno(m, -errno, NULL);
2015-02-04 16:21:27 +01:00
name_list = (struct kdbus_info *) ((uint8_t *) a->kdbus_buffer + cmd.offset);
2015-02-04 16:21:27 +01:00
KDBUS_FOREACH(name, name_list, cmd.list_size) {
struct kdbus_item *item;
char *n;
KDBUS_ITEM_FOREACH(item, name, items) {
if (item->type == KDBUS_ITEM_OWNED_NAME) {
if (!streq_ptr(item->name.name, arg0))
continue;
if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) {
err = -ENOMEM;
break;
}
r = strv_consume(&owners, n);
if (r < 0) {
err = r;
break;
}
}
}
if (err < 0)
break;
}
r = bus_kernel_cmd_free(a, cmd.offset);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (err < 0)
return synthetic_reply_method_errno(m, err, NULL);
return synthetic_reply_method_return_strv(m, owners);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "NameHasOwner")) {
const char *name;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "s", &name);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (streq(name, "org.freedesktop.DBus"))
return synthetic_reply_method_return(m, "b", true);
r = sd_bus_get_name_creds(a, name, 0, NULL);
if (r < 0 && r != -ESRCH && r != -ENXIO)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, "b", r >= 0);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReleaseName")) {
const char *name;
if (!sd_bus_message_has_signature(m, "s"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "s", &name);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_release_name(a, name);
if (r < 0) {
if (r == -ESRCH)
return synthetic_reply_method_return(m, "u", BUS_NAME_NON_EXISTENT);
if (r == -EADDRINUSE)
return synthetic_reply_method_return(m, "u", BUS_NAME_NOT_OWNER);
return synthetic_reply_method_errno(m, r, NULL);
}
set_remove(owned_names, (char*) name);
return synthetic_reply_method_return(m, "u", BUS_NAME_RELEASED);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReloadConfig")) {
if (!sd_bus_message_has_signature(m, ""))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = shared_policy_reload(sp);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, NULL);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RequestName")) {
const char *name;
uint32_t flags, param;
bool in_queue;
if (!sd_bus_message_has_signature(m, "su"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "su", &name, &flags);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (sp) {
Policy *policy;
bool denied;
policy = shared_policy_acquire(sp);
denied = !policy_check_own(policy, ucred->uid, ucred->gid, name);
shared_policy_release(sp, policy);
if (denied)
return synthetic_reply_method_errno(m, -EPERM, NULL);
}
if ((flags & ~(BUS_NAME_ALLOW_REPLACEMENT|BUS_NAME_REPLACE_EXISTING|BUS_NAME_DO_NOT_QUEUE)) != 0)
return synthetic_reply_method_errno(m, -EINVAL, NULL);
param = 0;
if (flags & BUS_NAME_ALLOW_REPLACEMENT)
param |= SD_BUS_NAME_ALLOW_REPLACEMENT;
if (flags & BUS_NAME_REPLACE_EXISTING)
param |= SD_BUS_NAME_REPLACE_EXISTING;
if (!(flags & BUS_NAME_DO_NOT_QUEUE))
param |= SD_BUS_NAME_QUEUE;
r = set_put_strdup(owned_names, name);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_request_name(a, name, param);
if (r < 0) {
if (r == -EALREADY)
return synthetic_reply_method_return(m, "u", BUS_NAME_ALREADY_OWNER);
set_remove(owned_names, (char*) name);
if (r == -EEXIST)
return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS);
return synthetic_reply_method_errno(m, r, NULL);
}
in_queue = (r == 0);
if (in_queue)
return synthetic_reply_method_return(m, "u", BUS_NAME_IN_QUEUE);
return synthetic_reply_method_return(m, "u", BUS_NAME_PRIMARY_OWNER);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "StartServiceByName")) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = NULL;
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
ProxyActivation *activation;
const char *name;
uint64_t cookie;
uint32_t flags;
if (!sd_bus_message_has_signature(m, "su"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_read(m, "su", &name, &flags);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (flags != 0)
return synthetic_reply_method_errno(m, -EINVAL, NULL);
r = sd_bus_get_name_creds(a, name, 0, NULL);
if (r >= 0 || streq(name, "org.freedesktop.DBus"))
return synthetic_reply_method_return(m, "u", BUS_START_REPLY_ALREADY_RUNNING);
if (r != -ESRCH)
return synthetic_reply_method_errno(m, r, NULL);
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
if (p->n_activations >= PROXY_ACTIVATIONS_MAX)
return synthetic_reply_method_errno(m, -EMFILE, NULL);
r = sd_bus_message_get_cookie(m, &cookie);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_new_method_call(a,
&msg,
name,
"/",
"org.freedesktop.DBus.Peer",
"Ping");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = bus_message_seal(msg, cookie, BUS_DEFAULT_TIMEOUT);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
activation = new0(ProxyActivation, 1);
if (!activation)
return synthetic_reply_method_errno(m, -ENOMEM, NULL);
r = sd_bus_call_async(a,
&activation->slot,
msg,
driver_activation,
activation,
0);
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
if (r < 0) {
free(activation);
return synthetic_reply_method_errno(m, r, NULL);
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
}
bus-proxy: make StartServiceByName synchronous The StartServiceByName() call was provided by dbus-daemon to activate a service without sending a message. On receiption, dbus-daemon schedules an activation request (different modes are supported) and sends back the reply once activation is done. With kdbus, we marked StartServiceByName() as deprecated. There is no real reason to start services explicitly. Instead, applications should just *use* the service and rely on it being activated implicitly. However, we provide compatibility with dbus-daemon and implement StartServiceByName() on the proxy via a call to org.freedesktop.DBus.Peer.Ping() on the destination. This will activate the peer implicitly as part of the no-op Ping() method call (regardless whether the peer actually implements that call). Now, the problem is, StartServiceByName() was synchronous on dbus-daemon but isn't on bus-proxy. Hence, on return, there is no guarantee that ListNames includes the activated name. As this is required by some applications, we need to make this synchronous. This patch makes the proxy track the Ping() method call and send the reply of StartServiceByName() only once Ping() returned. We do not look at possible errors of Ping(), as there is no strict requirement for the peer to implement org.freedesktop.DBus.Peer. Furthermore, any interesting error should have already been caught by sd_bus_send() before. Note: This race was triggered by gdbus. The gdbus-proxy implementation relies on a name to be available after StartServiceByName() returns. This is highly fragile and should be dropped by gdbus. Even if the call is synchronous, there is no reason whatsoever to assume the service did not exit-on-idle before ListNames() returns. However, this race is much less likely than the startup race, so we try to be compatible to dbus-daemon now.
2015-07-31 13:25:04 +02:00
activation->proxy = p;
activation->request = sd_bus_message_ref(m);
LIST_PREPEND(activations_by_proxy, p->activations, activation);
++p->n_activations;
return 1;
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "UpdateActivationEnvironment")) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = NULL;
_cleanup_strv_free_ char **args = NULL;
if (!sd_bus_message_has_signature(m, "a{ss}"))
return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{ss}");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "ss")) > 0) {
_cleanup_free_ char *s = NULL;
const char *key;
const char *value;
r = sd_bus_message_read(m, "ss", &key, &value);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
s = strjoin(key, "=", value, NULL);
if (!s)
return synthetic_reply_method_errno(m, -ENOMEM, NULL);
if (!env_assignment_is_valid(s)) {
log_warning("UpdateActivationEnvironment() called with invalid assignment, discarding: %s", s);
} else {
r = strv_extend(&args, s);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
if (strv_isempty(args)) /* nothing to do? */
return synthetic_reply_method_return(m, NULL);
r = sd_bus_message_new_method_call(
a,
&msg,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"SetEnvironment");
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_message_append_strv(msg, args);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
r = sd_bus_call(a, msg, 0, NULL, NULL);
if (r < 0)
return synthetic_reply_method_errno(m, r, NULL);
return synthetic_reply_method_return(m, NULL);
} else {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = sd_bus_error_setf(&error, SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s'.", m->member);
return synthetic_reply_method_errno(m, r, &error);
}
}