dbus-common.c revision c516c8d17f77a1c761447f4c40c8dfffeda2e06d
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
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 <assert.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "log.h"
#include "dbus-common.h"
#include "util.h"
#include "def.h"
#include "strv.h"
int bus_check_peercred(DBusConnection *c) {
int fd;
socklen_t l;
assert(c);
l = sizeof(struct ucred);
log_error("SO_PEERCRED failed: %m");
return -errno;
}
if (l != sizeof(struct ucred)) {
log_error("SO_PEERCRED returned wrong size.");
return -E2BIG;
}
return -EPERM;
return 1;
}
/* This complexity should probably move into D-Bus itself:
*
for (;;) {
break;
break;
if (!dbus_connection_read_write_dispatch(bus, ((begin + DEFAULT_TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC))
break;
}
if (!dbus_connection_get_is_connected(bus)) {
return -ECONNREFUSED;
}
if (!dbus_connection_get_is_authenticated(bus)) {
return -EACCES;
}
return 0;
}
int r;
bool private = true;
if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) {
/* If we are root, then let's talk directly to the
* system instance, instead of going via the bus */
if (!bus)
return -EIO;
} else {
if (t == DBUS_BUS_SESSION) {
const char *e;
/* If we are supposed to talk to the instance,
* try via XDG_RUNTIME_DIR first, then
* fallback to normal bus access */
e = getenv("XDG_RUNTIME_DIR");
if (e) {
char *p;
return -ENOMEM;
free(p);
}
}
if (!bus) {
if (!bus)
return -EIO;
private = false;
}
}
if (private) {
if (bus_check_peercred(bus) < 0) {
return -EACCES;
}
}
if (r < 0) {
return r;
}
if (_private)
return 0;
}
int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error) {
char *p = NULL;
int r;
else if (user)
else if (host)
if (!p) {
return -ENOMEM;
}
free(p);
if (!bus)
return -EIO;
return r;
}
return r;
}
return 0;
}
int r;
/* Don't bother with PolicyKit if we are root */
if (geteuid() == 0)
bus = dbus_connection_open_private("unixexec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error);
if (!bus)
return -EIO;
return r;
}
return r;
}
return 0;
}
if (!error)
return NULL;
/* Sometimes the D-Bus server is a little bit too verbose with
* its error messages, so let's override them here */
return "Access denied";
}
return bus_error_message(error);
}
DBusConnection *c,
const char *introspection,
const char *interfaces,
const BusBoundProperties *bound_properties) {
int r;
assert(c);
if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) {
goto oom;
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) {
const BusBoundProperties *bp;
const BusProperty *p;
void *data;
if (!dbus_message_get_args(
&error,
continue;
goto get_prop;
}
/* no match */
else
if (!reply)
goto oom;
goto oom;
if (p->indirect)
if (r < 0) {
if (r == -ENOMEM)
goto oom;
}
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && bound_properties) {
const char *interface;
const BusBoundProperties *bp;
const BusProperty *p;
if (!dbus_message_get_args(
&error,
}
goto oom;
goto oom;
continue;
void *data;
goto oom;
if (p->indirect)
if (r < 0) {
if (r == -ENOMEM)
goto oom;
}
goto oom;
}
}
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) {
const BusBoundProperties *bp;
const BusProperty *p;
char *sig;
void *data;
if (!dbus_message_iter_next(&iter) ||
if (!dbus_message_iter_next(&iter) ||
continue;
goto set_prop;
}
/* no match */
else
if (!p->set) {
}
if (!sig)
goto oom;
}
if (p->indirect)
if (r == -ENOMEM)
goto oom;
else if (r < 0)
if (!reply)
goto oom;
/* Send out a signal about this, but it doesn't really
* matter if this fails, so eat all errors */
property);
if (changed) {
}
} else {
}
}
if (reply) {
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
}
oom:
if (reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
const char *t = data;
assert(i);
if (!t)
t = "";
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
return -ENOMEM;
return 0;
}
char **t = data;
assert(i);
return bus_append_strv_iter(i, t);
}
bool *b = data;
assert(i);
assert(b);
db = *b;
return -ENOMEM;
return 0;
}
int *b = data;
assert(i);
assert(b);
db = *b > 0;
return -ENOMEM;
return 0;
}
assert(i);
/* Let's ensure that usec_t is actually 64bit, and hence this
* function can be used for usec_t */
return -ENOMEM;
return 0;
}
assert(i);
/* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually
* 32bit, and hence this function can be used for
return -ENOMEM;
return 0;
}
assert(i);
return -ENOMEM;
return 0;
}
uint64_t u;
assert(i);
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
return -ENOMEM;
return 0;
}
uint64_t u;
assert(i);
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
return -ENOMEM;
return 0;
}
int64_t l;
assert(i);
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l))
return -ENOMEM;
return 0;
}
assert(i);
dbus_message_iter_get_basic(i, t);
return 0;
}
const char *bus_errno_to_dbus(int error) {
switch(error) {
case -EINVAL:
return DBUS_ERROR_INVALID_ARGS;
case -ENOMEM:
return DBUS_ERROR_NO_MEMORY;
case -EPERM:
case -EACCES:
return DBUS_ERROR_ACCESS_DENIED;
case -ESRCH:
case -ENOENT:
return DBUS_ERROR_FILE_NOT_FOUND;
case -EEXIST:
return DBUS_ERROR_FILE_EXISTS;
case -ETIMEDOUT:
case -ETIME:
return DBUS_ERROR_TIMEOUT;
case -EIO:
return DBUS_ERROR_IO_ERROR;
case -ENETRESET:
case -ECONNABORTED:
case -ECONNRESET:
return DBUS_ERROR_DISCONNECTED;
}
return DBUS_ERROR_FAILED;
}
DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
} else {
}
goto oom;
goto oom;
if (berror)
return DBUS_HANDLER_RESULT_HANDLED;
oom:
if (reply)
if (berror)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
DBusMessage *m;
const char *i;
if (!m)
goto oom;
/* We won't send any property values, since they might be
* large and sometimes not cheap to generated */
goto oom;
goto oom;
goto oom;
return m;
oom:
if (m)
return NULL;
}
DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property) {
DBusMessage *m;
if (!m)
goto oom;
/* We won't send any property values, since they might be
* large and sometimes not cheap to generated */
goto oom;
goto oom;
goto oom;
return m;
oom:
if (m)
return NULL;
}
unsigned flags;
/* no watch flags for disabled watches */
if (!dbus_watch_get_enabled(bus_watch))
return 0;
if (flags & DBUS_WATCH_READABLE)
if (flags & DBUS_WATCH_WRITABLE)
}
unsigned flags = 0;
return flags;
}
assert(m);
if (!dbus_message_iter_init(m, &iter))
return -EINVAL;
}
unsigned n = 0, i = 0;
char **l;
return -EINVAL;
n++;
}
if (!(l = new(char*, n+1)))
return -ENOMEM;
const char *s;
dbus_message_iter_get_basic(&sub, &s);
if (!(l[i++] = strdup(s))) {
strv_free(l);
return -ENOMEM;
}
}
assert(i == n);
l[i] = NULL;
if (_l)
*_l = l;
return 0;
}
return -ENOMEM;
STRV_FOREACH(l, l)
return -ENOMEM;
return -ENOMEM;
return 0;
}
return -EIO;
return -EIO;
return 0;
}
switch (dbus_message_iter_get_arg_type(iter)) {
case DBUS_TYPE_STRING: {
const char *s;
return 1;
}
case DBUS_TYPE_BOOLEAN: {
dbus_bool_t b;
return 1;
}
case DBUS_TYPE_UINT64: {
uint64_t u;
/* Yes, heuristics! But we can change this check
* should it turn out to not be sufficient */
char timestamp[FORMAT_TIMESTAMP_MAX], *t;
if (t || all)
char timespan[FORMAT_TIMESPAN_MAX];
} else
return 1;
}
case DBUS_TYPE_UINT32: {
uint32_t u;
else
return 1;
}
case DBUS_TYPE_INT32: {
int32_t i;
return 1;
}
case DBUS_TYPE_DOUBLE: {
double d;
return 1;
}
case DBUS_TYPE_ARRAY:
bool space = false;
if (all ||
const char *s;
dbus_message_iter_get_basic(&sub, &s);
space = true;
}
puts("");
}
return 1;
if (all ||
uint8_t u;
dbus_message_iter_get_basic(&sub, &u);
printf("%02x", u);
}
puts("");
}
return 1;
if (all ||
uint32_t u;
dbus_message_iter_get_basic(&sub, &u);
printf("%08x", u);
}
puts("");
}
return 1;
}
break;
}
return 0;
}
}
DBusMessage *m = NULL;
/* We unregister the name here, but we continue to process
* requests, until we get the response for it, so that all
* requests are guaranteed to be processed. */
"ReleaseName");
if (!m)
goto oom;
if (!dbus_message_append_args(
m,
&name,
goto oom;
goto oom;
goto oom;
return;
oom:
log_oom();
if (pending) {
}
if (m)
}
assert(m);
/* Every time we get a new message we reset out timeout */
}
/* This mimics dbus_bus_get_unix_user() */
const char *name,
"GetConnectionUnixProcessID");
if (!m) {
goto finish;
}
if (!dbus_message_append_args(
m,
goto finish;
}
if (!reply)
goto finish;
goto finish;
if (!dbus_message_get_args(
goto finish;
if (m)
if (reply)
}
if (!dbus_error_is_set(error))
return false;
return true;
return true;
}
const char *destination,
const char *path,
const char *interface,
const char *method,
int first_arg_type, ...) {
DBusMessage *m, *reply;
int r = 0;
if (!m) {
r = log_oom();
goto finish;
}
r = log_oom();
goto finish;
}
if (!reply) {
if (!return_error)
if (bus_error_is_no_service(&error))
r = -ENOENT;
r = -EACCES;
r = -ETIMEDOUT;
else
r = -EIO;
goto finish;
}
if (return_reply)
*return_reply = reply;
else
if(return_error)
else
return r;
}