Systemd/src/shared/bus-util.c

1721 lines
51 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
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 <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
2013-08-12 00:35:49 +02:00
#include <sys/socket.h>
#include <unistd.h>
2013-08-12 00:35:49 +02:00
#include "sd-bus-protocol.h"
#include "sd-bus.h"
#include "sd-daemon.h"
#include "sd-event.h"
#include "sd-id128.h"
#include "alloc-util.h"
#include "bus-internal.h"
#include "bus-label.h"
#include "bus-message.h"
#include "bus-util.h"
#include "cap-list.h"
#include "cgroup-util.h"
#include "def.h"
#include "escape.h"
#include "fd-util.h"
2013-11-10 02:31:12 +01:00
#include "missing.h"
#include "mount-util.h"
#include "nsflags.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "rlimit-util.h"
#include "stdio-util.h"
#include "strv.h"
#include "user-util.h"
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
assert(m);
assert(e);
sd_bus_close(sd_bus_message_get_bus(m));
sd_event_exit(e, 0);
return 1;
}
int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) {
const char *match;
const char *unique;
int r;
assert(e);
assert(bus);
assert(name);
/* We unregister the name here and then wait for the
* NameOwnerChanged signal for this event to arrive before we
* quit. We do this in order to make sure that any queued
* requests are still processed before we really exit. */
r = sd_bus_get_unique_name(bus, &unique);
if (r < 0)
return r;
match = strjoina(
"sender='org.freedesktop.DBus',"
"type='signal',"
"interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',"
"path='/org/freedesktop/DBus',"
"arg0='", name, "',",
"arg1='", unique, "',",
"arg2=''");
r = sd_bus_add_match_async(bus, NULL, match, name_owner_change_callback, NULL, e);
if (r < 0)
return r;
r = sd_bus_release_name_async(bus, NULL, name, NULL, NULL);
if (r < 0)
return r;
return 0;
}
2013-12-19 21:14:52 +01:00
int bus_event_loop_with_idle(
sd_event *e,
sd_bus *bus,
const char *name,
usec_t timeout,
check_idle_t check_idle,
void *userdata) {
bool exiting = false;
int r, code;
assert(e);
assert(bus);
assert(name);
for (;;) {
2013-12-19 21:14:52 +01:00
bool idle;
r = sd_event_get_state(e);
if (r < 0)
return r;
if (r == SD_EVENT_FINISHED)
break;
2013-12-19 21:14:52 +01:00
if (check_idle)
idle = check_idle(userdata);
else
idle = true;
r = sd_event_run(e, exiting || !idle ? (uint64_t) -1 : timeout);
if (r < 0)
return r;
if (r == 0 && !exiting && idle) {
r = sd_bus_try_close(bus);
if (r == -EBUSY)
continue;
/* Fallback for dbus1 connections: we
* unregister the name and wait for the
* response to come through for it */
if (r == -EOPNOTSUPP) {
/* Inform the service manager that we
* are going down, so that it will
* queue all further start requests,
* instead of assuming we are already
* running. */
sd_notify(false, "STOPPING=1");
r = bus_async_unregister_and_exit(e, bus, name);
if (r < 0)
return r;
exiting = true;
continue;
}
if (r < 0)
return r;
sd_event_exit(e, 0);
break;
}
}
r = sd_event_get_exit_code(e, &code);
if (r < 0)
return r;
return code;
}
int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *rep = NULL;
int r, has_owner = 0;
assert(c);
assert(name);
r = sd_bus_call_method(c,
"org.freedesktop.DBus",
"/org/freedesktop/dbus",
"org.freedesktop.DBus",
"NameHasOwner",
error,
&rep,
"s",
name);
if (r < 0)
return r;
r = sd_bus_message_read_basic(rep, 'b', &has_owner);
if (r < 0)
return sd_bus_error_set_errno(error, r);
return has_owner;
}
static int check_good_user(sd_bus_message *m, uid_t good_user) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t sender_uid;
int r;
assert(m);
if (good_user == UID_INVALID)
return 0;
r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
return r;
/* Don't trust augmented credentials for authorization */
assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM);
r = sd_bus_creds_get_euid(creds, &sender_uid);
if (r < 0)
return r;
return sender_uid == good_user;
}
int bus_test_polkit(
sd_bus_message *call,
int capability,
const char *action,
const char **details,
uid_t good_user,
bool *_challenge,
sd_bus_error *e) {
int r;
assert(call);
assert(action);
/* Tests non-interactively! */
r = check_good_user(call, good_user);
if (r != 0)
return r;
r = sd_bus_query_sender_privilege(call, capability);
if (r < 0)
return r;
else if (r > 0)
return 1;
#if ENABLE_POLKIT
else {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
int authorized = false, challenge = false;
const char *sender, **k, **v;
sender = sd_bus_message_get_sender(call);
if (!sender)
return -EBADMSG;
r = sd_bus_message_new_method_call(
call->bus,
&request,
"org.freedesktop.PolicyKit1",
"/org/freedesktop/PolicyKit1/Authority",
"org.freedesktop.PolicyKit1.Authority",
"CheckAuthorization");
if (r < 0)
return r;
r = sd_bus_message_append(
request,
"(sa{sv})s",
"system-bus-name", 1, "name", "s", sender,
action);
if (r < 0)
return r;
r = sd_bus_message_open_container(request, 'a', "{ss}");
if (r < 0)
return r;
STRV_FOREACH_PAIR(k, v, details) {
r = sd_bus_message_append(request, "{ss}", *k, *v);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(request);
if (r < 0)
return r;
r = sd_bus_message_append(request, "us", 0, NULL);
if (r < 0)
return r;
r = sd_bus_call(call->bus, request, 0, e, &reply);
if (r < 0) {
/* Treat no PK available as access denied */
if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
sd_bus_error_free(e);
return -EACCES;
}
return r;
}
r = sd_bus_message_enter_container(reply, 'r', "bba{ss}");
if (r < 0)
return r;
r = sd_bus_message_read(reply, "bb", &authorized, &challenge);
if (r < 0)
return r;
if (authorized)
return 1;
if (_challenge) {
*_challenge = challenge;
return 0;
}
}
#endif
return -EACCES;
}
#if ENABLE_POLKIT
typedef struct AsyncPolkitQuery {
sd_bus_message *request, *reply;
sd_bus_message_handler_t callback;
void *userdata;
sd_bus_slot *slot;
Hashmap *registry;
} AsyncPolkitQuery;
static void async_polkit_query_free(AsyncPolkitQuery *q) {
if (!q)
return;
sd_bus_slot_unref(q->slot);
if (q->registry && q->request)
hashmap_remove(q->registry, q->request);
sd_bus_message_unref(q->request);
sd_bus_message_unref(q->reply);
free(q);
}
static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) {
_cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
AsyncPolkitQuery *q = userdata;
int r;
assert(reply);
assert(q);
q->slot = sd_bus_slot_unref(q->slot);
q->reply = sd_bus_message_ref(reply);
r = sd_bus_message_rewind(q->request, true);
if (r < 0) {
r = sd_bus_reply_method_errno(q->request, r, NULL);
goto finish;
}
r = q->callback(q->request, q->userdata, &error_buffer);
r = bus_maybe_reply_error(q->request, r, &error_buffer);
finish:
async_polkit_query_free(q);
return r;
}
#endif
int bus_verify_polkit_async(
sd_bus_message *call,
int capability,
const char *action,
const char **details,
bool interactive,
uid_t good_user,
Hashmap **registry,
sd_bus_error *error) {
#if ENABLE_POLKIT
_cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
AsyncPolkitQuery *q;
const char *sender, **k, **v;
sd_bus_message_handler_t callback;
void *userdata;
int c;
#endif
int r;
assert(call);
assert(action);
assert(registry);
r = check_good_user(call, good_user);
if (r != 0)
return r;
#if ENABLE_POLKIT
q = hashmap_get(*registry, call);
if (q) {
int authorized, challenge;
/* This is the second invocation of this function, and
* there's already a response from polkit, let's
* process it */
assert(q->reply);
if (sd_bus_message_is_method_error(q->reply, NULL)) {
const sd_bus_error *e;
/* Copy error from polkit reply */
e = sd_bus_message_get_error(q->reply);
sd_bus_error_copy(error, e);
/* Treat no PK available as access denied */
if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN))
return -EACCES;
return -sd_bus_error_get_errno(e);
}
r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
if (r >= 0)
r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
if (r < 0)
return r;
if (authorized)
return 1;
if (challenge)
return sd_bus_error_set(error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, "Interactive authentication required.");
return -EACCES;
}
#endif
r = sd_bus_query_sender_privilege(call, capability);
if (r < 0)
return r;
else if (r > 0)
return 1;
#if ENABLE_POLKIT
if (sd_bus_get_current_message(call->bus) != call)
return -EINVAL;
callback = sd_bus_get_current_handler(call->bus);
if (!callback)
return -EINVAL;
userdata = sd_bus_get_current_userdata(call->bus);
sender = sd_bus_message_get_sender(call);
if (!sender)
return -EBADMSG;
c = sd_bus_message_get_allow_interactive_authorization(call);
if (c < 0)
return c;
if (c > 0)
interactive = true;
r = hashmap_ensure_allocated(registry, NULL);
if (r < 0)
return r;
r = sd_bus_message_new_method_call(
call->bus,
&pk,
"org.freedesktop.PolicyKit1",
"/org/freedesktop/PolicyKit1/Authority",
"org.freedesktop.PolicyKit1.Authority",
"CheckAuthorization");
if (r < 0)
return r;
r = sd_bus_message_append(
pk,
"(sa{sv})s",
"system-bus-name", 1, "name", "s", sender,
action);
if (r < 0)
return r;
r = sd_bus_message_open_container(pk, 'a', "{ss}");
if (r < 0)
return r;
STRV_FOREACH_PAIR(k, v, details) {
r = sd_bus_message_append(pk, "{ss}", *k, *v);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(pk);
if (r < 0)
return r;
r = sd_bus_message_append(pk, "us", !!interactive, NULL);
if (r < 0)
return r;
q = new0(AsyncPolkitQuery, 1);
if (!q)
return -ENOMEM;
q->request = sd_bus_message_ref(call);
q->callback = callback;
q->userdata = userdata;
r = hashmap_put(*registry, call, q);
if (r < 0) {
async_polkit_query_free(q);
return r;
}
q->registry = *registry;
r = sd_bus_call_async(call->bus, &q->slot, pk, async_polkit_callback, q, 0);
if (r < 0) {
async_polkit_query_free(q);
return r;
}
return 0;
#endif
return -EACCES;
}
void bus_verify_polkit_async_registry_free(Hashmap *registry) {
#if ENABLE_POLKIT
hashmap_free_with_destructor(registry, async_polkit_query_free);
#endif
}
2013-08-12 00:35:49 +02:00
int bus_check_peercred(sd_bus *c) {
2013-08-12 00:35:49 +02:00
struct ucred ucred;
int fd, r;
2013-08-12 00:35:49 +02:00
assert(c);
fd = sd_bus_get_fd(c);
if (fd < 0)
return fd;
2013-08-12 00:35:49 +02:00
r = getpeercred(fd, &ucred);
if (r < 0)
return r;
2013-08-12 00:35:49 +02:00
if (ucred.uid != 0 && ucred.uid != geteuid())
return -EPERM;
return 1;
}
int bus_connect_system_systemd(sd_bus **_bus) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
2013-08-12 00:35:49 +02:00
int r;
assert(_bus);
if (geteuid() != 0)
return sd_bus_default_system(_bus);
2013-08-12 14:19:22 +02:00
/* If we are root then let's talk directly to the system
* instance, instead of going via the bus */
r = sd_bus_new(&bus);
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
r = sd_bus_set_address(bus, "unix:path=/run/systemd/private");
if (r < 0)
return r;
r = sd_bus_start(bus);
if (r < 0)
return sd_bus_default_system(_bus);
r = bus_check_peercred(bus);
2013-08-12 14:19:22 +02:00
if (r < 0)
return r;
*_bus = bus;
bus = NULL;
2013-08-12 14:19:22 +02:00
return 0;
}
int bus_connect_user_systemd(sd_bus **_bus) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
_cleanup_free_ char *ee = NULL;
const char *e;
int r;
assert(_bus);
e = secure_getenv("XDG_RUNTIME_DIR");
if (!e)
return sd_bus_default_user(_bus);
ee = bus_address_escape(e);
if (!ee)
return -ENOMEM;
r = sd_bus_new(&bus);
if (r < 0)
return r;
bus->address = strjoin("unix:path=", ee, "/systemd/private");
if (!bus->address)
return -ENOMEM;
r = sd_bus_start(bus);
if (r < 0)
return sd_bus_default_user(_bus);
r = bus_check_peercred(bus);
if (r < 0)
return r;
*_bus = bus;
bus = NULL;
return 0;
}
#define print_property(name, fmt, ...) \
do { \
if (value) \
printf(fmt "\n", __VA_ARGS__); \
else \
printf("%s=" fmt "\n", name, __VA_ARGS__); \
} while (0)
int bus_print_property(const char *name, sd_bus_message *m, bool value, bool all) {
2013-08-12 14:19:22 +02:00
char type;
const char *contents;
2013-11-05 02:57:49 +01:00
int r;
2013-08-12 14:19:22 +02:00
assert(name);
assert(m);
2013-08-12 14:19:22 +02:00
r = sd_bus_message_peek_type(m, &type, &contents);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
switch (type) {
case SD_BUS_TYPE_STRING: {
const char *s;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_read_basic(m, type, &s);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
if (all || !isempty(s)) {
bool good;
/* This property has a single value, so we need to take
* care not to print a new line, everything else is OK. */
good = !strchr(s, '\n');
print_property(name, "%s", good ? s : "[unprintable]");
}
2013-08-12 14:19:22 +02:00
return 1;
}
case SD_BUS_TYPE_BOOLEAN: {
int b;
2013-08-12 14:19:22 +02:00
r = sd_bus_message_read_basic(m, type, &b);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
print_property(name, "%s", yes_no(b));
2013-08-12 14:19:22 +02:00
return 1;
}
case SD_BUS_TYPE_UINT64: {
uint64_t u;
r = sd_bus_message_read_basic(m, type, &u);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
/* Yes, heuristics! But we can change this check
* should it turn out to not be sufficient */
if (endswith(name, "Timestamp") || STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec")) {
2013-08-12 14:19:22 +02:00
char timestamp[FORMAT_TIMESTAMP_MAX], *t;
t = format_timestamp(timestamp, sizeof(timestamp), u);
if (t || all)
print_property(name, "%s", strempty(t));
2013-08-12 14:19:22 +02:00
} else if (strstr(name, "USec")) {
char timespan[FORMAT_TIMESPAN_MAX];
print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0));
} else if (streq(name, "RestrictNamespaces")) {
_cleanup_free_ char *s = NULL;
const char *result;
if ((u & NAMESPACE_FLAGS_ALL) == 0)
result = "yes";
else if ((u & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL)
result = "no";
else {
r = namespace_flag_to_string_many(u, &s);
if (r < 0)
return r;
result = s;
}
print_property(name, "%s", result);
} else if (streq(name, "MountFlags")) {
const char *result;
result = mount_propagation_flags_to_string(u);
if (!result)
return -EINVAL;
print_property(name, "%s", result);
} else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
_cleanup_free_ char *s = NULL;
r = capability_set_to_string_alloc(u, &s);
if (r < 0)
return r;
print_property(name, "%s", s);
} else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
(STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
(STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
(STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) ||
(endswith(name, "NSec") && u == (uint64_t) -1))
print_property(name, "%s", "[not set]");
else if ((STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
(STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) ||
(startswith(name, "Limit") && u == (uint64_t) -1) ||
(startswith(name, "DefaultLimit") && u == (uint64_t) -1))
print_property(name, "%s", "infinity");
else
print_property(name, "%"PRIu64, u);
2013-08-12 14:19:22 +02:00
return 1;
}
case SD_BUS_TYPE_INT64: {
int64_t i;
r = sd_bus_message_read_basic(m, type, &i);
if (r < 0)
return r;
print_property(name, "%"PRIi64, i);
return 1;
}
2013-08-12 14:19:22 +02:00
case SD_BUS_TYPE_UINT32: {
uint32_t u;
r = sd_bus_message_read_basic(m, type, &u);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
if (strstr(name, "UMask") || strstr(name, "Mode"))
print_property(name, "%04o", u);
else if (streq(name, "UID")) {
if (u == UID_INVALID)
print_property(name, "%s", "[not set]");
else
print_property(name, "%"PRIu32, u);
} else if (streq(name, "GID")) {
if (u == GID_INVALID)
print_property(name, "%s", "[not set]");
else
print_property(name, "%"PRIu32, u);
} else
print_property(name, "%"PRIu32, u);
2013-08-12 14:19:22 +02:00
return 1;
}
case SD_BUS_TYPE_INT32: {
int32_t i;
r = sd_bus_message_read_basic(m, type, &i);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
print_property(name, "%"PRIi32, i);
2013-08-12 14:19:22 +02:00
return 1;
}
case SD_BUS_TYPE_DOUBLE: {
double d;
r = sd_bus_message_read_basic(m, type, &d);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
print_property(name, "%g", d);
2013-08-12 14:19:22 +02:00
return 1;
}
case SD_BUS_TYPE_ARRAY:
if (streq(contents, "s")) {
2013-11-07 05:49:00 +01:00
bool first = true;
const char *str;
2013-08-12 14:19:22 +02:00
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) {
bool good;
if (first && !value)
2013-11-07 05:49:00 +01:00
printf("%s=", name);
2017-08-08 16:05:29 +02:00
/* This property has multiple space-separated values, so
* neither spaces not newlines can be allowed in a value. */
good = str[strcspn(str, " \n")] == '\0';
printf("%s%s", first ? "" : " ", good ? str : "[unprintable]");
2013-11-07 05:49:00 +01:00
first = false;
}
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
if (first && all && !value)
2013-08-12 14:19:22 +02:00
printf("%s=", name);
2013-11-07 05:49:00 +01:00
if (!first || all)
2013-08-12 14:19:22 +02:00
puts("");
r = sd_bus_message_exit_container(m);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
return 1;
} else if (streq(contents, "y")) {
const uint8_t *u;
size_t n;
r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
if (all || n > 0) {
unsigned int i;
if (!value)
printf("%s=", name);
2013-08-12 14:19:22 +02:00
for (i = 0; i < n; i++)
printf("%02x", u[i]);
puts("");
}
return 1;
} else if (streq(contents, "u")) {
uint32_t *u;
size_t n;
r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-08-12 14:19:22 +02:00
if (all || n > 0) {
unsigned int i;
if (!value)
printf("%s=", name);
2013-08-12 14:19:22 +02:00
for (i = 0; i < n; i++)
printf("%08x", u[i]);
puts("");
}
return 1;
}
break;
}
return 0;
}
int bus_message_print_all_properties(
sd_bus_message *m,
bus_message_print_t func,
char **filter,
bool value,
bool all,
Set **found_properties) {
int r;
2013-11-05 02:57:49 +01:00
assert(m);
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
if (r < 0)
return r;
while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
const char *name;
const char *contents;
r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
if (r < 0)
return r;
if (found_properties) {
r = set_ensure_allocated(found_properties, &string_hash_ops);
if (r < 0)
return log_oom();
r = set_put(*found_properties, name);
if (r < 0 && r != EEXIST)
return log_oom();
}
2013-11-05 02:57:49 +01:00
if (!filter || strv_find(filter, name)) {
r = sd_bus_message_peek_type(m, NULL, &contents);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
if (func)
r = func(name, m, value, all);
if (!func || r == 0)
r = bus_print_property(name, m, value, all);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
if (r == 0) {
if (all)
printf("%s=[unprintable]\n", name);
/* skip what we didn't read */
r = sd_bus_message_skip(m, contents);
if (r < 0)
return r;
}
2013-11-05 02:57:49 +01:00
r = sd_bus_message_exit_container(m);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
} else {
r = sd_bus_message_skip(m, "v");
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
2013-11-05 02:57:49 +01:00
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
return 0;
}
int bus_print_all_properties(
sd_bus *bus,
const char *dest,
const char *path,
bus_message_print_t func,
char **filter,
bool value,
bool all,
Set **found_properties) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(bus);
assert(path);
r = sd_bus_call_method(bus,
dest,
path,
"org.freedesktop.DBus.Properties",
"GetAll",
&error,
&reply,
"s", "");
if (r < 0)
return r;
return bus_message_print_all_properties(reply, func, filter, value, all, found_properties);
}
2013-11-05 02:57:49 +01:00
int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
sd_id128_t *p = userdata;
const void *v;
size_t n;
int r;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
if (n == 0)
*p = SD_ID128_NULL;
else if (n == 16)
memcpy((*p).bytes, v, n);
else
return -EINVAL;
2013-11-05 02:57:49 +01:00
return 0;
}
static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool copy_string) {
2013-11-05 02:57:49 +01:00
char type;
int r;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_peek_type(m, &type, NULL);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
switch (type) {
2013-11-05 02:57:49 +01:00
case SD_BUS_TYPE_STRING: {
const char **p = userdata;
const char *s;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_read_basic(m, type, &s);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
if (isempty(s))
s = NULL;
if (copy_string)
return free_and_strdup((char **) userdata, s);
*p = s;
return 0;
2013-11-05 02:57:49 +01:00
}
2013-11-05 02:57:49 +01:00
case SD_BUS_TYPE_ARRAY: {
2015-08-06 00:31:09 +02:00
_cleanup_strv_free_ char **l = NULL;
char ***p = userdata;
2013-11-05 02:57:49 +01:00
r = bus_message_read_strv_extend(m, &l);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
strv_free(*p);
*p = TAKE_PTR(l);
return 0;
2013-11-05 02:57:49 +01:00
}
2013-11-05 02:57:49 +01:00
case SD_BUS_TYPE_BOOLEAN: {
unsigned b;
bool *p = userdata;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_read_basic(m, type, &b);
if (r < 0)
return r;
*p = !!b;
return 0;
2013-11-05 02:57:49 +01:00
}
case SD_BUS_TYPE_INT32:
2013-11-05 02:57:49 +01:00
case SD_BUS_TYPE_UINT32: {
uint32_t u, *p = userdata;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_read_basic(m, type, &u);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
*p = u;
return 0;
2013-11-05 02:57:49 +01:00
}
case SD_BUS_TYPE_INT64:
2013-11-05 02:57:49 +01:00
case SD_BUS_TYPE_UINT64: {
uint64_t t, *p = userdata;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_read_basic(m, type, &t);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
*p = t;
return 0;
2013-11-05 02:57:49 +01:00
}
case SD_BUS_TYPE_DOUBLE: {
double d, *p = userdata;
r = sd_bus_message_read_basic(m, type, &d);
if (r < 0)
return r;
*p = d;
return 0;
}}
return -EOPNOTSUPP;
2013-11-05 02:57:49 +01:00
}
int bus_message_map_all_properties(
sd_bus_message *m,
const struct bus_properties_map *map,
bool copy_string,
sd_bus_error *error,
void *userdata) {
2013-11-05 02:57:49 +01:00
int r;
assert(m);
2013-11-05 02:57:49 +01:00
assert(map);
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
if (r < 0)
return r;
while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
const struct bus_properties_map *prop;
const char *member;
const char *contents;
void *v;
unsigned i;
r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
for (i = 0, prop = NULL; map[i].member; i++)
if (streq(map[i].member, member)) {
prop = &map[i];
break;
}
if (prop) {
r = sd_bus_message_peek_type(m, NULL, &contents);
if (r < 0)
return r;
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
if (r < 0)
return r;
v = (uint8_t *)userdata + prop->offset;
if (map[i].set)
r = prop->set(sd_bus_message_get_bus(m), member, m, error, v);
2013-11-05 02:57:49 +01:00
else
r = map_basic(sd_bus_message_get_bus(m), member, m, error, v, copy_string);
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
} else {
r = sd_bus_message_skip(m, "v");
if (r < 0)
return r;
2013-11-05 02:57:49 +01:00
}
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
}
if (r < 0)
return r;
return sd_bus_message_exit_container(m);
}
int bus_message_map_properties_changed(
sd_bus_message *m,
const struct bus_properties_map *map,
bool copy_string,
sd_bus_error *error,
void *userdata) {
const char *member;
int r, invalidated, i;
assert(m);
assert(map);
r = bus_message_map_all_properties(m, map, copy_string, error, userdata);
if (r < 0)
return r;
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
if (r < 0)
return r;
invalidated = 0;
while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member)) > 0)
for (i = 0; map[i].member; i++)
if (streq(map[i].member, member)) {
++invalidated;
break;
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
return invalidated;
}
int bus_map_all_properties(
sd_bus *bus,
const char *destination,
const char *path,
const struct bus_properties_map *map,
sd_bus_error *error,
sd_bus_message **reply,
void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
int r;
assert(bus);
assert(destination);
assert(path);
assert(map);
r = sd_bus_call_method(
bus,
destination,
path,
"org.freedesktop.DBus.Properties",
"GetAll",
error,
&m,
"s", "");
if (r < 0)
return r;
r = bus_message_map_all_properties(m, map, !reply, error, userdata);
if (r < 0)
return r;
if (reply)
*reply = sd_bus_message_ref(m);
return r;
}
int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
assert(ret);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
switch (transport) {
case BUS_TRANSPORT_LOCAL:
if (user)
r = sd_bus_default_user(&bus);
else
r = sd_bus_default_system(&bus);
break;
case BUS_TRANSPORT_REMOTE:
r = sd_bus_open_system_remote(&bus, host);
break;
case BUS_TRANSPORT_MACHINE:
r = sd_bus_open_system_machine(&bus, host);
break;
default:
assert_not_reached("Hmm, unknown transport type.");
}
if (r < 0)
return r;
r = sd_bus_set_exit_on_disconnect(bus, true);
if (r < 0)
return r;
*ret = bus;
bus = NULL;
return 0;
}
int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) {
int r;
assert(transport >= 0);
assert(transport < _BUS_TRANSPORT_MAX);
assert(bus);
assert_return((transport == BUS_TRANSPORT_LOCAL) == !host, -EINVAL);
assert_return(transport == BUS_TRANSPORT_LOCAL || !user, -EOPNOTSUPP);
switch (transport) {
case BUS_TRANSPORT_LOCAL:
if (user)
r = bus_connect_user_systemd(bus);
else
r = bus_connect_system_systemd(bus);
break;
case BUS_TRANSPORT_REMOTE:
r = sd_bus_open_system_remote(bus, host);
break;
case BUS_TRANSPORT_MACHINE:
r = sd_bus_open_system_machine(bus, host);
break;
default:
assert_not_reached("Hmm, unknown transport type.");
}
return r;
}
int bus_property_get_bool(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
int b = *(bool*) userdata;
return sd_bus_message_append_basic(reply, 'b', &b);
}
2018-01-11 10:42:27 +01:00
int bus_property_set_bool(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *value,
void *userdata,
sd_bus_error *error) {
int b, r;
r = sd_bus_message_read(value, "b", &b);
if (r < 0)
return r;
*(bool *) userdata = !!b;
return 0;
}
int bus_property_get_id128(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
sd_id128_t *id = userdata;
if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */
return sd_bus_message_append(reply, "ay", 0);
else
core: add "invocation ID" concept to service manager This adds a new invocation ID concept to the service manager. The invocation ID identifies each runtime cycle of a unit uniquely. A new randomized 128bit ID is generated each time a unit moves from and inactive to an activating or active state. The primary usecase for this concept is to connect the runtime data PID 1 maintains about a service with the offline data the journal stores about it. Previously we'd use the unit name plus start/stop times, which however is highly racy since the journal will generally process log data after the service already ended. The "invocation ID" kinda matches the "boot ID" concept of the Linux kernel, except that it applies to an individual unit instead of the whole system. The invocation ID is passed to the activated processes as environment variable. It is additionally stored as extended attribute on the cgroup of the unit. The latter is used by journald to automatically retrieve it for each log logged message and attach it to the log entry. The environment variable is very easily accessible, even for unprivileged services. OTOH the extended attribute is only accessible to privileged processes (this is because cgroupfs only supports the "trusted." xattr namespace, not "user."). The environment variable may be altered by services, the extended attribute may not be, hence is the better choice for the journal. Note that reading the invocation ID off the extended attribute from journald is racy, similar to the way reading the unit name for a logging process is. This patch adds APIs to read the invocation ID to sd-id128: sd_id128_get_invocation() may be used in a similar fashion to sd_id128_get_boot(). PID1's own logging is updated to always include the invocation ID when it logs information about a unit. A new bus call GetUnitByInvocationID() is added that allows retrieving a bus path to a unit by its invocation ID. The bus path is built using the invocation ID, thus providing a path for referring to a unit that is valid only for the current runtime cycleof it. Outlook for the future: should the kernel eventually allow passing of cgroup information along AF_UNIX/SOCK_DGRAM messages via a unique cgroup id, then we can alter the invocation ID to be generated as hash from that rather than entirely randomly. This way we can derive the invocation race-freely from the messages.
2016-08-30 23:18:46 +02:00
return sd_bus_message_append_array(reply, 'y', id->bytes, 16);
}
#if __SIZEOF_SIZE_T__ != 8
int bus_property_get_size(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
uint64_t sz = *(size_t*) userdata;
return sd_bus_message_append_basic(reply, 't', &sz);
}
#endif
#if __SIZEOF_LONG__ != 8
int bus_property_get_long(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
int64_t l = *(long*) userdata;
return sd_bus_message_append_basic(reply, 'x', &l);
}
int bus_property_get_ulong(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
uint64_t ul = *(unsigned long*) userdata;
return sd_bus_message_append_basic(reply, 't', &ul);
}
#endif
int bus_log_parse_error(int r) {
return log_error_errno(r, "Failed to parse bus message: %m");
}
2013-11-07 05:49:04 +01:00
int bus_log_create_error(int r) {
return log_error_errno(r, "Failed to create bus message: %m");
2013-11-07 05:49:04 +01:00
}
bus: implement bus_path_{en,de}code_unique() Whenever we provide a bus API that allows clients to create and manage server-side objects, we need to provide a unique name for these objects. There are two ways to provide them: 1) Let the server choose a name and return it as method reply. 2) Let the client pass its name of choice in the method arguments. The first method is the easiest one to implement. However, it suffers from a race condition: If a client creates an object asynchronously, it cannot destroy that object until it received the method reply. It cannot know the name of the new object, thus, it cannot destroy it. Furthermore, this method enforces a round-trip. If the client _depends_ on the method call to succeed (eg., it would close() the connection if it failed), the client usually has no reason to wait for the method reply. Instead, the client can immediately schedule further method calls on the newly created object (in case the API guarantees in-order method-call handling). The second method fixes both problems: The client passes an object name with the method-call. The server uses it to create the object. Therefore, the client can schedule object destruction even if the object-creation hasn't finished, yet (again, requiring in-order method-call handling). Furthermore, the client can schedule further method calls on the newly created object, before the constructor returned. There're two problems to solve, though: 1) Object names are usually defined via dbus object paths, which are usually globally namespaced. Therefore, multiple clients must be able to choose unique object names without interference. 2) If multiple libraries share the same bus connection, they must be able to choose unique object names without interference. The first problem is solved easily by prefixing a name with the unique-bus-name of a connection. The server side must enforce this and reject any other name. The second problem is solved by providing unique suffixes from within sd-bus. As long as sd-bus always returns a fresh new ID, if requested, multiple libraries will never interfere. This implementation re-uses bus->cookie as ID generator, which already provides unique IDs for each bus connection. This patch introduces two new helpers: bus_path_encode_unique(sd_bus *bus, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); This creates a new object-path via the template '/prefix/sender_id/external_id'. That is, it appends two new labels to the given prefix. If 'sender_id' is NULL, it will use bus->unique_name, if 'external_id' is NULL, it will allocate a fresh, unique cookie from bus->cookie. bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); This reverses what bus_path_encode_unique() did. It parses 'path' from the template '/prefix/sender/external' and returns both suffix-labels in 'ret_sender' and 'ret_external'. In case the template does not match, 0 is returned and both output arguments are set to NULL. Otherwise, 1 is returned and the output arguments contain the decoded labels. Note: Client-side allocated IDs are inspired by the Wayland protocol (which itself was inspired by X11). Wayland uses those IDs heavily to avoid round-trips. Clients can create server-side objects and send method calls without any round-trip and waiting for any object IDs to be returned. But unlike Wayland, DBus uses gobally namespaced object names. Therefore, we have to add the extra step by adding the unique-name of the bus connection.
2015-04-10 17:44:30 +02:00
/**
* bus_path_encode_unique() - encode unique object path
* @b: bus connection or NULL
* @prefix: object path prefix
* @sender_id: unique-name of client, or NULL
* @external_id: external ID to be chosen by client, or NULL
* @ret_path: storage for encoded object path pointer
*
* Whenever we provide a bus API that allows clients to create and manage
* server-side objects, we need to provide a unique name for these objects. If
* we let the server choose the name, we suffer from a race condition: If a
* client creates an object asynchronously, it cannot destroy that object until
* it received the method reply. It cannot know the name of the new object,
* thus, it cannot destroy it. Furthermore, it enforces a round-trip.
*
* Therefore, many APIs allow the client to choose the unique name for newly
* created objects. There're two problems to solve, though:
* 1) Object names are usually defined via dbus object paths, which are
* usually globally namespaced. Therefore, multiple clients must be able
* to choose unique object names without interference.
* 2) If multiple libraries share the same bus connection, they must be
* able to choose unique object names without interference.
* The first problem is solved easily by prefixing a name with the
* unique-bus-name of a connection. The server side must enforce this and
* reject any other name. The second problem is solved by providing unique
* suffixes from within sd-bus.
*
* This helper allows clients to create unique object-paths. It uses the
* template '/prefix/sender_id/external_id' and returns the new path in
* @ret_path (must be freed by the caller).
* If @sender_id is NULL, the unique-name of @b is used. If @external_id is
* NULL, this function allocates a unique suffix via @b (by requesting a new
* cookie). If both @sender_id and @external_id are given, @b can be passed as
* NULL.
*
* Returns: 0 on success, negative error code on failure.
*/
int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) {
_cleanup_free_ char *sender_label = NULL, *external_label = NULL;
char external_buf[DECIMAL_STR_MAX(uint64_t)], *p;
int r;
assert_return(b || (sender_id && external_id), -EINVAL);
assert_return(object_path_is_valid(prefix), -EINVAL);
assert_return(ret_path, -EINVAL);
if (!sender_id) {
r = sd_bus_get_unique_name(b, &sender_id);
if (r < 0)
return r;
}
if (!external_id) {
xsprintf(external_buf, "%"PRIu64, ++b->cookie);
external_id = external_buf;
}
sender_label = bus_label_escape(sender_id);
if (!sender_label)
return -ENOMEM;
external_label = bus_label_escape(external_id);
if (!external_label)
return -ENOMEM;
p = strjoin(prefix, "/", sender_label, "/", external_label);
bus: implement bus_path_{en,de}code_unique() Whenever we provide a bus API that allows clients to create and manage server-side objects, we need to provide a unique name for these objects. There are two ways to provide them: 1) Let the server choose a name and return it as method reply. 2) Let the client pass its name of choice in the method arguments. The first method is the easiest one to implement. However, it suffers from a race condition: If a client creates an object asynchronously, it cannot destroy that object until it received the method reply. It cannot know the name of the new object, thus, it cannot destroy it. Furthermore, this method enforces a round-trip. If the client _depends_ on the method call to succeed (eg., it would close() the connection if it failed), the client usually has no reason to wait for the method reply. Instead, the client can immediately schedule further method calls on the newly created object (in case the API guarantees in-order method-call handling). The second method fixes both problems: The client passes an object name with the method-call. The server uses it to create the object. Therefore, the client can schedule object destruction even if the object-creation hasn't finished, yet (again, requiring in-order method-call handling). Furthermore, the client can schedule further method calls on the newly created object, before the constructor returned. There're two problems to solve, though: 1) Object names are usually defined via dbus object paths, which are usually globally namespaced. Therefore, multiple clients must be able to choose unique object names without interference. 2) If multiple libraries share the same bus connection, they must be able to choose unique object names without interference. The first problem is solved easily by prefixing a name with the unique-bus-name of a connection. The server side must enforce this and reject any other name. The second problem is solved by providing unique suffixes from within sd-bus. As long as sd-bus always returns a fresh new ID, if requested, multiple libraries will never interfere. This implementation re-uses bus->cookie as ID generator, which already provides unique IDs for each bus connection. This patch introduces two new helpers: bus_path_encode_unique(sd_bus *bus, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); This creates a new object-path via the template '/prefix/sender_id/external_id'. That is, it appends two new labels to the given prefix. If 'sender_id' is NULL, it will use bus->unique_name, if 'external_id' is NULL, it will allocate a fresh, unique cookie from bus->cookie. bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); This reverses what bus_path_encode_unique() did. It parses 'path' from the template '/prefix/sender/external' and returns both suffix-labels in 'ret_sender' and 'ret_external'. In case the template does not match, 0 is returned and both output arguments are set to NULL. Otherwise, 1 is returned and the output arguments contain the decoded labels. Note: Client-side allocated IDs are inspired by the Wayland protocol (which itself was inspired by X11). Wayland uses those IDs heavily to avoid round-trips. Clients can create server-side objects and send method calls without any round-trip and waiting for any object IDs to be returned. But unlike Wayland, DBus uses gobally namespaced object names. Therefore, we have to add the extra step by adding the unique-name of the bus connection.
2015-04-10 17:44:30 +02:00
if (!p)
return -ENOMEM;
*ret_path = p;
return 0;
}
/**
* bus_path_decode_unique() - decode unique object path
* @path: object path to decode
* @prefix: object path prefix
* @ret_sender: output parameter for sender-id label
* @ret_external: output parameter for external-id label
*
* This does the reverse of bus_path_encode_unique() (see its description for
* details). Both trailing labels, sender-id and external-id, are unescaped and
* returned in the given output parameters (the caller must free them).
*
* Note that this function returns 0 if the path does not match the template
* (see bus_path_encode_unique()), 1 if it matched.
*
* Returns: Negative error code on failure, 0 if the given object path does not
* match the template (return parameters are set to NULL), 1 if it was
* parsed successfully (return parameters contain allocated labels).
*/
int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) {
const char *p, *q;
char *sender, *external;
assert(object_path_is_valid(path));
assert(object_path_is_valid(prefix));
assert(ret_sender);
assert(ret_external);
p = object_path_startswith(path, prefix);
if (!p) {
*ret_sender = NULL;
*ret_external = NULL;
return 0;
}
q = strchr(p, '/');
if (!q) {
*ret_sender = NULL;
*ret_external = NULL;
return 0;
}
sender = bus_label_unescape_n(p, q - p);
external = bus_label_unescape(q + 1);
if (!sender || !external) {
free(sender);
free(external);
return -ENOMEM;
}
*ret_sender = sender;
*ret_external = external;
return 1;
}
int bus_property_get_rlimit(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
struct rlimit *rl;
uint64_t u;
rlim_t x;
const char *is_soft;
assert(bus);
assert(reply);
assert(userdata);
is_soft = endswith(property, "Soft");
rl = *(struct rlimit**) userdata;
if (rl)
x = is_soft ? rl->rlim_cur : rl->rlim_max;
else {
struct rlimit buf = {};
int z;
const char *s;
s = is_soft ? strndupa(property, is_soft - property) : property;
z = rlimit_from_string(strstr(s, "Limit"));
assert(z >= 0);
getrlimit(z, &buf);
x = is_soft ? buf.rlim_cur : buf.rlim_max;
}
/* rlim_t might have different sizes, let's map
* RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
* all archs */
u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
return sd_bus_message_append(reply, "t", u);
}
int bus_track_add_name_many(sd_bus_track *t, char **l) {
int r = 0;
char **i;
assert(t);
/* Continues adding after failure, and returns the first failure. */
STRV_FOREACH(i, l) {
int k;
k = sd_bus_track_add_name(t, *i);
if (k < 0 && r >= 0)
r = k;
}
return r;
}
int bus_open_system_watch_bind(sd_bus **ret) {
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
const char *e;
int r;
assert(ret);
/* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal turned on. */
r = sd_bus_new(&bus);
if (r < 0)
return r;
e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
if (!e)
e = DEFAULT_SYSTEM_BUS_ADDRESS;
r = sd_bus_set_address(bus, e);
if (r < 0)
return r;
r = sd_bus_set_bus_client(bus, true);
if (r < 0)
return r;
r = sd_bus_set_trusted(bus, true);
if (r < 0)
return r;
r = sd_bus_negotiate_creds(bus, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS);
if (r < 0)
return r;
r = sd_bus_set_watch_bind(bus, true);
if (r < 0)
return r;
r = sd_bus_set_connected_signal(bus, true);
if (r < 0)
return r;
r = sd_bus_start(bus);
if (r < 0)
return r;
*ret = bus;
bus = NULL;
return 0;
}