]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dbus-common.c
build-sys: __secure_getenv lost dunder in libc 2.17
[thirdparty/systemd.git] / src / shared / dbus-common.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
9a1ac7b9
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
9a1ac7b9
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
9a1ac7b9 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
9a1ac7b9
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <assert.h>
23#include <sys/socket.h>
24#include <errno.h>
25#include <unistd.h>
a8f11321
LP
26#include <stdio.h>
27#include <stdlib.h>
9a1ac7b9 28#include <dbus/dbus.h>
bfebab7f 29#include <string.h>
3df5bf61 30#include <sys/epoll.h>
9a1ac7b9
LP
31
32#include "log.h"
33#include "dbus-common.h"
b9978121 34#include "util.h"
4db17f29 35#include "missing.h"
f6a6225e 36#include "def.h"
bfebab7f 37#include "strv.h"
9a1ac7b9
LP
38
39int bus_check_peercred(DBusConnection *c) {
40 int fd;
41 struct ucred ucred;
42 socklen_t l;
43
44 assert(c);
45
46 assert_se(dbus_connection_get_unix_fd(c, &fd));
47
48 l = sizeof(struct ucred);
49 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) {
50 log_error("SO_PEERCRED failed: %m");
51 return -errno;
52 }
53
54 if (l != sizeof(struct ucred)) {
55 log_error("SO_PEERCRED returned wrong size.");
56 return -E2BIG;
57 }
58
be81bfc4 59 if (ucred.uid != 0 && ucred.uid != geteuid())
9a1ac7b9
LP
60 return -EPERM;
61
62 return 1;
63}
64
a8f11321
LP
65static int sync_auth(DBusConnection *bus, DBusError *error) {
66 usec_t begin, tstamp;
9a1ac7b9 67
a8f11321 68 assert(bus);
b9978121 69
a8f11321
LP
70 /* This complexity should probably move into D-Bus itself:
71 *
72 * https://bugs.freedesktop.org/show_bug.cgi?id=35189 */
9a1ac7b9 73
a8f11321
LP
74 begin = tstamp = now(CLOCK_MONOTONIC);
75 for (;;) {
9a1ac7b9 76
f6a6225e 77 if (tstamp > begin + DEFAULT_TIMEOUT_USEC)
a8f11321 78 break;
9a1ac7b9 79
a8f11321
LP
80 if (dbus_connection_get_is_authenticated(bus))
81 break;
f4579ce7 82
f6a6225e 83 if (!dbus_connection_read_write_dispatch(bus, ((begin + DEFAULT_TIMEOUT_USEC - tstamp) + USEC_PER_MSEC - 1) / USEC_PER_MSEC))
a8f11321 84 break;
b9978121 85
a8f11321
LP
86 tstamp = now(CLOCK_MONOTONIC);
87 }
b9978121 88
a8f11321
LP
89 if (!dbus_connection_get_is_connected(bus)) {
90 dbus_set_error_const(error, DBUS_ERROR_NO_SERVER, "Connection terminated during authentication.");
91 return -ECONNREFUSED;
92 }
b9978121 93
a8f11321
LP
94 if (!dbus_connection_get_is_authenticated(bus)) {
95 dbus_set_error_const(error, DBUS_ERROR_TIMEOUT, "Failed to authenticate in time.");
96 return -EACCES;
97 }
b9978121 98
a8f11321
LP
99 return 0;
100}
b9978121 101
be81bfc4
LP
102int bus_connect(DBusBusType t, DBusConnection **_bus, bool *_private, DBusError *error) {
103 DBusConnection *bus = NULL;
a8f11321 104 int r;
be81bfc4 105 bool private = true;
b9978121 106
a8f11321 107 assert(_bus);
b9978121 108
a8f11321 109 if (geteuid() == 0 && t == DBUS_BUS_SYSTEM) {
be81bfc4
LP
110 /* If we are root, then let's talk directly to the
111 * system instance, instead of going via the bus */
112
113 bus = dbus_connection_open_private("unix:path=/run/systemd/private", error);
114 if (!bus)
115 return -EIO;
116
117 } else {
118 if (t == DBUS_BUS_SESSION) {
119 const char *e;
91b22f21 120
be81bfc4
LP
121 /* If we are supposed to talk to the instance,
122 * try via XDG_RUNTIME_DIR first, then
123 * fallback to normal bus access */
124
4db17f29 125 e = secure_getenv("XDG_RUNTIME_DIR");
be81bfc4
LP
126 if (e) {
127 char *p;
128
129 if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0)
130 return -ENOMEM;
131
132 bus = dbus_connection_open_private(p, NULL);
133 free(p);
134 }
135 }
91b22f21 136
be81bfc4
LP
137 if (!bus) {
138 bus = dbus_bus_get_private(t, error);
139 if (!bus)
91b22f21 140 return -EIO;
be81bfc4
LP
141
142 private = false;
91b22f21 143 }
be81bfc4 144 }
a8f11321 145
be81bfc4 146 dbus_connection_set_exit_on_disconnect(bus, FALSE);
a8f11321 147
be81bfc4 148 if (private) {
a8f11321 149 if (bus_check_peercred(bus) < 0) {
b9978121
LP
150 dbus_connection_close(bus);
151 dbus_connection_unref(bus);
152
a8f11321 153 dbus_set_error_const(error, DBUS_ERROR_ACCESS_DENIED, "Failed to verify owner of bus.");
b9978121
LP
154 return -EACCES;
155 }
9a1ac7b9
LP
156 }
157
be81bfc4
LP
158 r = sync_auth(bus, error);
159 if (r < 0) {
a8f11321
LP
160 dbus_connection_close(bus);
161 dbus_connection_unref(bus);
162 return r;
163 }
164
be81bfc4
LP
165 if (_private)
166 *_private = private;
167
a8f11321
LP
168 *_bus = bus;
169 return 0;
170}
171
172int bus_connect_system_ssh(const char *user, const char *host, DBusConnection **_bus, DBusError *error) {
173 DBusConnection *bus;
174 char *p = NULL;
175 int r;
176
177 assert(_bus);
178 assert(user || host);
179
180 if (user && host)
72e764a6 181 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@%s,argv3=systemd-stdio-bridge", user, host);
a8f11321 182 else if (user)
72e764a6 183 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s@localhost,argv3=systemd-stdio-bridge", user);
a8f11321 184 else if (host)
72e764a6 185 asprintf(&p, "unixexec:path=ssh,argv1=-xT,argv2=%s,argv3=systemd-stdio-bridge", host);
a8f11321
LP
186
187 if (!p) {
188 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
189 return -ENOMEM;
190 }
191
192 bus = dbus_connection_open_private(p, error);
193 free(p);
194
195 if (!bus)
196 return -EIO;
197
198 dbus_connection_set_exit_on_disconnect(bus, FALSE);
199
200 if ((r = sync_auth(bus, error)) < 0) {
201 dbus_connection_close(bus);
202 dbus_connection_unref(bus);
203 return r;
204 }
205
206 if (!dbus_bus_register(bus, error)) {
207 dbus_connection_close(bus);
208 dbus_connection_unref(bus);
209 return r;
210 }
211
212 *_bus = bus;
213 return 0;
214}
215
216int bus_connect_system_polkit(DBusConnection **_bus, DBusError *error) {
217 DBusConnection *bus;
218 int r;
219
220 assert(_bus);
221
222 /* Don't bother with PolicyKit if we are root */
223 if (geteuid() == 0)
224 return bus_connect(DBUS_BUS_SYSTEM, _bus, NULL, error);
225
72e764a6
LP
226 bus = dbus_connection_open_private("unixexec:path=pkexec,argv1=" SYSTEMD_STDIO_BRIDGE_BINARY_PATH, error);
227 if (!bus)
a8f11321
LP
228 return -EIO;
229
9a1ac7b9
LP
230 dbus_connection_set_exit_on_disconnect(bus, FALSE);
231
a8f11321
LP
232 if ((r = sync_auth(bus, error)) < 0) {
233 dbus_connection_close(bus);
234 dbus_connection_unref(bus);
235 return r;
236 }
237
238 if (!dbus_bus_register(bus, error)) {
239 dbus_connection_close(bus);
240 dbus_connection_unref(bus);
241 return r;
242 }
243
9a1ac7b9
LP
244 *_bus = bus;
245 return 0;
246}
4cf5d675
LP
247
248const char *bus_error_message(const DBusError *error) {
eecd1362
LP
249 if (!error)
250 return NULL;
4cf5d675
LP
251
252 /* Sometimes the D-Bus server is a little bit too verbose with
253 * its error messages, so let's override them here */
254 if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED))
255 return "Access denied";
256
257 return error->message;
258}
bfebab7f 259
eecd1362
LP
260const char *bus_error_message_or_strerror(const DBusError *error, int err) {
261
262 if (error && dbus_error_is_set(error))
263 return bus_error_message(error);
264
265 return strerror(err);
266}
267
bfebab7f
LP
268DBusHandlerResult bus_default_message_handler(
269 DBusConnection *c,
270 DBusMessage *message,
271 const char *introspection,
272 const char *interfaces,
d200735e 273 const BusBoundProperties *bound_properties) {
bfebab7f
LP
274
275 DBusError error;
276 DBusMessage *reply = NULL;
277 int r;
278
279 assert(c);
280 assert(message);
281
282 dbus_error_init(&error);
283
284 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) {
285
286 if (!(reply = dbus_message_new_method_return(message)))
287 goto oom;
288
289 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID))
290 goto oom;
291
d200735e 292 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && bound_properties) {
bfebab7f 293 const char *interface, *property;
d200735e 294 const BusBoundProperties *bp;
bfebab7f 295 const BusProperty *p;
d200735e
MS
296 void *data;
297 DBusMessageIter iter, sub;
bfebab7f
LP
298
299 if (!dbus_message_get_args(
300 message,
301 &error,
302 DBUS_TYPE_STRING, &interface,
303 DBUS_TYPE_STRING, &property,
304 DBUS_TYPE_INVALID))
305 return bus_send_error_reply(c, message, &error, -EINVAL);
306
d200735e
MS
307 for (bp = bound_properties; bp->interface; bp++) {
308 if (!streq(bp->interface, interface))
309 continue;
bfebab7f 310
d200735e
MS
311 for (p = bp->properties; p->property; p++)
312 if (streq(p->property, property))
313 goto get_prop;
314 }
bfebab7f 315
d200735e
MS
316 /* no match */
317 if (!nulstr_contains(interfaces, interface))
318 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
319 else
320 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
bfebab7f 321
d200735e 322 return bus_send_error_reply(c, message, &error, -EINVAL);
bfebab7f 323
d200735e
MS
324get_prop:
325 reply = dbus_message_new_method_return(message);
326 if (!reply)
327 goto oom;
bfebab7f 328
d200735e 329 dbus_message_iter_init_append(reply, &iter);
bfebab7f 330
d200735e
MS
331 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub))
332 goto oom;
bfebab7f 333
d200735e
MS
334 data = (char*)bp->base + p->offset;
335 if (p->indirect)
336 data = *(void**)data;
337 r = p->append(&sub, property, data);
338 if (r < 0) {
339 if (r == -ENOMEM)
bfebab7f 340 goto oom;
bfebab7f 341
d200735e
MS
342 dbus_message_unref(reply);
343 return bus_send_error_reply(c, message, NULL, r);
bfebab7f
LP
344 }
345
d200735e
MS
346 if (!dbus_message_iter_close_container(&iter, &sub))
347 goto oom;
348
349 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && bound_properties) {
bfebab7f 350 const char *interface;
d200735e 351 const BusBoundProperties *bp;
bfebab7f
LP
352 const BusProperty *p;
353 DBusMessageIter iter, sub, sub2, sub3;
354
355 if (!dbus_message_get_args(
356 message,
357 &error,
358 DBUS_TYPE_STRING, &interface,
359 DBUS_TYPE_INVALID))
360 return bus_send_error_reply(c, message, &error, -EINVAL);
361
362 if (interface[0] && !nulstr_contains(interfaces, interface)) {
363 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
364 return bus_send_error_reply(c, message, &error, -EINVAL);
365 }
366
367 if (!(reply = dbus_message_new_method_return(message)))
368 goto oom;
369
370 dbus_message_iter_init_append(reply, &iter);
371
372 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub))
373 goto oom;
374
d200735e
MS
375 for (bp = bound_properties; bp->interface; bp++) {
376 if (interface[0] && !streq(bp->interface, interface))
bfebab7f
LP
377 continue;
378
d200735e
MS
379 for (p = bp->properties; p->property; p++) {
380 void *data;
bfebab7f 381
d200735e
MS
382 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) ||
383 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) ||
384 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3))
bfebab7f
LP
385 goto oom;
386
d200735e
MS
387 data = (char*)bp->base + p->offset;
388 if (p->indirect)
389 data = *(void**)data;
390 r = p->append(&sub3, p->property, data);
391 if (r < 0) {
392 if (r == -ENOMEM)
393 goto oom;
bfebab7f 394
d200735e
MS
395 dbus_message_unref(reply);
396 return bus_send_error_reply(c, message, NULL, r);
397 }
398
399 if (!dbus_message_iter_close_container(&sub2, &sub3) ||
400 !dbus_message_iter_close_container(&sub, &sub2))
401 goto oom;
402 }
bfebab7f
LP
403 }
404
405 if (!dbus_message_iter_close_container(&iter, &sub))
406 goto oom;
407
d200735e 408 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Set") && bound_properties) {
bfebab7f
LP
409 const char *interface, *property;
410 DBusMessageIter iter;
d200735e 411 const BusBoundProperties *bp;
bfebab7f 412 const BusProperty *p;
d200735e
MS
413 DBusMessageIter sub;
414 char *sig;
9612f07c 415 void *data;
d4e7373b 416 DBusMessage *changed;
bfebab7f
LP
417
418 if (!dbus_message_iter_init(message, &iter) ||
419 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
420 return bus_send_error_reply(c, message, NULL, -EINVAL);
421
422 dbus_message_iter_get_basic(&iter, &interface);
423
424 if (!dbus_message_iter_next(&iter) ||
425 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
426 return bus_send_error_reply(c, message, NULL, -EINVAL);
427
428 dbus_message_iter_get_basic(&iter, &property);
429
430 if (!dbus_message_iter_next(&iter) ||
431 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT ||
432 dbus_message_iter_has_next(&iter))
433 return bus_send_error_reply(c, message, NULL, -EINVAL);
434
d200735e
MS
435 for (bp = bound_properties; bp->interface; bp++) {
436 if (!streq(bp->interface, interface))
437 continue;
438
439 for (p = bp->properties; p->property; p++)
440 if (streq(p->property, property))
441 goto set_prop;
442 }
bfebab7f 443
d200735e
MS
444 /* no match */
445 if (!nulstr_contains(interfaces, interface))
446 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
447 else
448 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
bfebab7f 449
d200735e 450 return bus_send_error_reply(c, message, &error, -EINVAL);
bfebab7f 451
d200735e
MS
452set_prop:
453 if (!p->set) {
454 dbus_set_error_const(&error, DBUS_ERROR_PROPERTY_READ_ONLY, "Property read-only");
455 return bus_send_error_reply(c, message, &error, -EINVAL);
456 }
bfebab7f 457
d200735e 458 dbus_message_iter_recurse(&iter, &sub);
bfebab7f 459
d200735e
MS
460 sig = dbus_message_iter_get_signature(&sub);
461 if (!sig)
462 goto oom;
463
464 if (!streq(sig, p->signature)) {
bfebab7f 465 dbus_free(sig);
d200735e
MS
466 return bus_send_error_reply(c, message, NULL, -EINVAL);
467 }
d200735e 468 dbus_free(sig);
bfebab7f 469
d4e7373b 470 data = (uint8_t*) bp->base + p->offset;
9612f07c
MO
471 if (p->indirect)
472 data = *(void**)data;
d4e7373b 473
9612f07c 474 r = p->set(&sub, property, data);
d4e7373b
LP
475 if (r == -ENOMEM)
476 goto oom;
477 else if (r < 0)
d200735e 478 return bus_send_error_reply(c, message, NULL, r);
bfebab7f 479
d200735e
MS
480 reply = dbus_message_new_method_return(message);
481 if (!reply)
482 goto oom;
d4e7373b
LP
483
484 /* Send out a signal about this, but it doesn't really
485 * matter if this fails, so eat all errors */
486 changed = bus_properties_changed_one_new(
487 dbus_message_get_path(message),
488 interface,
489 property);
490 if (changed) {
491 dbus_connection_send(c, changed, NULL);
492 dbus_message_unref(changed);
493 }
494
495
cda2b84a
MS
496 } else {
497 const char *interface = dbus_message_get_interface(message);
498
499 if (!interface || !nulstr_contains(interfaces, interface)) {
500 dbus_set_error_const(&error, DBUS_ERROR_UNKNOWN_INTERFACE, "Unknown interface");
501 return bus_send_error_reply(c, message, &error, -EINVAL);
502 }
bfebab7f
LP
503 }
504
505 if (reply) {
506 if (!dbus_connection_send(c, reply, NULL))
507 goto oom;
508
509 dbus_message_unref(reply);
510 return DBUS_HANDLER_RESULT_HANDLED;
511 }
512
513 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
514
515oom:
516 if (reply)
517 dbus_message_unref(reply);
518
519 dbus_error_free(&error);
520
521 return DBUS_HANDLER_RESULT_NEED_MEMORY;
522}
523
524int bus_property_append_string(DBusMessageIter *i, const char *property, void *data) {
525 const char *t = data;
526
527 assert(i);
528 assert(property);
529
530 if (!t)
531 t = "";
532
533 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
534 return -ENOMEM;
535
536 return 0;
537}
538
539int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data) {
bfebab7f
LP
540 char **t = data;
541
542 assert(i);
543 assert(property);
544
98a28fef 545 return bus_append_strv_iter(i, t);
bfebab7f
LP
546}
547
548int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data) {
549 bool *b = data;
550 dbus_bool_t db;
551
552 assert(i);
553 assert(property);
554 assert(b);
555
556 db = *b;
557
558 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
559 return -ENOMEM;
560
561 return 0;
562}
563
aa001cd6
LP
564int bus_property_append_tristate_false(DBusMessageIter *i, const char *property, void *data) {
565 int *b = data;
566 dbus_bool_t db;
567
568 assert(i);
569 assert(property);
570 assert(b);
571
572 db = *b > 0;
573
574 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
575 return -ENOMEM;
576
577 return 0;
578}
579
bfebab7f
LP
580int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data) {
581 assert(i);
582 assert(property);
583 assert(data);
584
bd253d1b 585 /* Let's ensure that usec_t is actually 64bit, and hence this
bfebab7f
LP
586 * function can be used for usec_t */
587 assert_cc(sizeof(uint64_t) == sizeof(usec_t));
588
589 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data))
590 return -ENOMEM;
591
592 return 0;
593}
594
595int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data) {
596 assert(i);
597 assert(property);
598 assert(data);
599
bd253d1b
LP
600 /* Let's ensure that pid_t, mode_t, uid_t, gid_t are actually
601 * 32bit, and hence this function can be used for
602 * pid_t/mode_t/uid_t/gid_t */
bfebab7f
LP
603 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
604 assert_cc(sizeof(uint32_t) == sizeof(mode_t));
605 assert_cc(sizeof(uint32_t) == sizeof(unsigned));
bd253d1b
LP
606 assert_cc(sizeof(uint32_t) == sizeof(uid_t));
607 assert_cc(sizeof(uint32_t) == sizeof(gid_t));
bfebab7f
LP
608
609 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
610 return -ENOMEM;
611
612 return 0;
613}
614
615int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data) {
616 assert(i);
617 assert(property);
618 assert(data);
619
620 assert_cc(sizeof(int32_t) == sizeof(int));
621
622 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, data))
623 return -ENOMEM;
624
625 return 0;
626}
627
628int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) {
629 uint64_t u;
630
631 assert(i);
632 assert(property);
633 assert(data);
634
635 u = (uint64_t) *(size_t*) data;
636
637 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
638 return -ENOMEM;
639
640 return 0;
641}
642
643int bus_property_append_ul(DBusMessageIter *i, const char *property, void *data) {
644 uint64_t u;
645
646 assert(i);
647 assert(property);
648 assert(data);
649
650 u = (uint64_t) *(unsigned long*) data;
651
652 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
653 return -ENOMEM;
916abb21
LP
654
655 return 0;
656}
657
658int bus_property_append_long(DBusMessageIter *i, const char *property, void *data) {
572481b9 659 int64_t l;
916abb21
LP
660
661 assert(i);
662 assert(property);
663 assert(data);
664
572481b9 665 l = (int64_t) *(long*) data;
916abb21 666
572481b9 667 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, &l))
916abb21 668 return -ENOMEM;
bfebab7f
LP
669
670 return 0;
671}
672
c757a65b
LP
673int bus_property_set_uint64(DBusMessageIter *i, const char *property, void *data) {
674 uint64_t *t = data;
675
676 assert(i);
677 assert(property);
678
679 dbus_message_iter_get_basic(i, t);
680 return 0;
681}
682
bfebab7f
LP
683const char *bus_errno_to_dbus(int error) {
684
685 switch(error) {
686
687 case -EINVAL:
688 return DBUS_ERROR_INVALID_ARGS;
689
690 case -ENOMEM:
691 return DBUS_ERROR_NO_MEMORY;
692
693 case -EPERM:
694 case -EACCES:
695 return DBUS_ERROR_ACCESS_DENIED;
696
697 case -ESRCH:
698 return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN;
699
700 case -ENOENT:
701 return DBUS_ERROR_FILE_NOT_FOUND;
702
703 case -EEXIST:
704 return DBUS_ERROR_FILE_EXISTS;
705
706 case -ETIMEDOUT:
707 case -ETIME:
708 return DBUS_ERROR_TIMEOUT;
709
710 case -EIO:
711 return DBUS_ERROR_IO_ERROR;
712
713 case -ENETRESET:
714 case -ECONNABORTED:
715 case -ECONNRESET:
716 return DBUS_ERROR_DISCONNECTED;
717 }
718
719 return DBUS_ERROR_FAILED;
720}
721
722DBusHandlerResult bus_send_error_reply(DBusConnection *c, DBusMessage *message, DBusError *berror, int error) {
723 DBusMessage *reply = NULL;
724 const char *name, *text;
725
726 if (berror && dbus_error_is_set(berror)) {
727 name = berror->name;
728 text = berror->message;
729 } else {
730 name = bus_errno_to_dbus(error);
731 text = strerror(-error);
732 }
733
734 if (!(reply = dbus_message_new_error(message, name, text)))
735 goto oom;
736
737 if (!dbus_connection_send(c, reply, NULL))
738 goto oom;
739
740 dbus_message_unref(reply);
741
742 if (berror)
743 dbus_error_free(berror);
744
745 return DBUS_HANDLER_RESULT_HANDLED;
746
747oom:
748 if (reply)
749 dbus_message_unref(reply);
750
751 if (berror)
752 dbus_error_free(berror);
753
754 return DBUS_HANDLER_RESULT_NEED_MEMORY;
755}
756
757DBusMessage* bus_properties_changed_new(const char *path, const char *interface, const char *properties) {
758 DBusMessage *m;
759 DBusMessageIter iter, sub;
760 const char *i;
761
762 assert(interface);
763 assert(properties);
764
d4e7373b
LP
765 m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
766 if (!m)
bfebab7f
LP
767 goto oom;
768
769 dbus_message_iter_init_append(m, &iter);
770
771 /* We won't send any property values, since they might be
772 * large and sometimes not cheap to generated */
773
774 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
775 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
776 !dbus_message_iter_close_container(&iter, &sub) ||
777 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
778 goto oom;
779
780 NULSTR_FOREACH(i, properties)
781 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &i))
782 goto oom;
783
784 if (!dbus_message_iter_close_container(&iter, &sub))
785 goto oom;
786
787 return m;
788
789oom:
790 if (m)
791 dbus_message_unref(m);
792
793 return NULL;
794}
3df5bf61 795
d4e7373b
LP
796DBusMessage* bus_properties_changed_one_new(const char *path, const char *interface, const char *property) {
797 DBusMessage *m;
798 DBusMessageIter iter, sub;
799
800 assert(interface);
801 assert(property);
802
803 m = dbus_message_new_signal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
804 if (!m)
805 goto oom;
806
807 dbus_message_iter_init_append(m, &iter);
808
809 /* We won't send any property values, since they might be
810 * large and sometimes not cheap to generated */
811
812 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface) ||
813 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub) ||
814 !dbus_message_iter_close_container(&iter, &sub) ||
815 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub))
816 goto oom;
817
818 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &property))
819 goto oom;
820
821 if (!dbus_message_iter_close_container(&iter, &sub))
822 goto oom;
823
824 return m;
825
826oom:
827 if (m)
828 dbus_message_unref(m);
829
830 return NULL;
831}
832
3df5bf61
LP
833uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
834 unsigned flags;
835 uint32_t events = 0;
836
837 assert(bus_watch);
838
839 /* no watch flags for disabled watches */
840 if (!dbus_watch_get_enabled(bus_watch))
841 return 0;
842
843 flags = dbus_watch_get_flags(bus_watch);
844
845 if (flags & DBUS_WATCH_READABLE)
846 events |= EPOLLIN;
847 if (flags & DBUS_WATCH_WRITABLE)
848 events |= EPOLLOUT;
849
850 return events | EPOLLHUP | EPOLLERR;
851}
852
853unsigned bus_events_to_flags(uint32_t events) {
854 unsigned flags = 0;
855
856 if (events & EPOLLIN)
857 flags |= DBUS_WATCH_READABLE;
858 if (events & EPOLLOUT)
859 flags |= DBUS_WATCH_WRITABLE;
860 if (events & EPOLLHUP)
861 flags |= DBUS_WATCH_HANGUP;
862 if (events & EPOLLERR)
863 flags |= DBUS_WATCH_ERROR;
864
865 return flags;
866}
8d0e38a2
LP
867
868int bus_parse_strv(DBusMessage *m, char ***_l) {
869 DBusMessageIter iter;
870
871 assert(m);
872 assert(_l);
873
874 if (!dbus_message_iter_init(m, &iter))
875 return -EINVAL;
876
877 return bus_parse_strv_iter(&iter, _l);
878}
879
880int bus_parse_strv_iter(DBusMessageIter *iter, char ***_l) {
881 DBusMessageIter sub;
882 unsigned n = 0, i = 0;
883 char **l;
884
885 assert(iter);
886 assert(_l);
887
888 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
889 dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRING)
890 return -EINVAL;
891
892 dbus_message_iter_recurse(iter, &sub);
893
894 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
895 n++;
896 dbus_message_iter_next(&sub);
897 }
898
899 if (!(l = new(char*, n+1)))
900 return -ENOMEM;
901
902 dbus_message_iter_recurse(iter, &sub);
903
904 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
905 const char *s;
906
907 assert_se(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
908 dbus_message_iter_get_basic(&sub, &s);
909
910 if (!(l[i++] = strdup(s))) {
911 strv_free(l);
912 return -ENOMEM;
913 }
914
915 dbus_message_iter_next(&sub);
916 }
917
918 assert(i == n);
919 l[i] = NULL;
920
921 if (_l)
922 *_l = l;
923
924 return 0;
925}
98a28fef
LP
926
927int bus_append_strv_iter(DBusMessageIter *iter, char **l) {
928 DBusMessageIter sub;
929
930 assert(iter);
931
932 if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &sub))
933 return -ENOMEM;
934
935 STRV_FOREACH(l, l)
936 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, l))
937 return -ENOMEM;
938
939 if (!dbus_message_iter_close_container(iter, &sub))
940 return -ENOMEM;
941
942 return 0;
943}
abca4822
LP
944
945int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
946
947 assert(iter);
948 assert(data);
949
950 if (dbus_message_iter_get_arg_type(iter) != type)
951 return -EIO;
952
953 dbus_message_iter_get_basic(iter, data);
954
955 if (!dbus_message_iter_next(iter) != !next)
956 return -EIO;
957
958 return 0;
959}
a4c279f8
LP
960
961int generic_print_property(const char *name, DBusMessageIter *iter, bool all) {
962 assert(name);
963 assert(iter);
964
965 switch (dbus_message_iter_get_arg_type(iter)) {
966
967 case DBUS_TYPE_STRING: {
968 const char *s;
969 dbus_message_iter_get_basic(iter, &s);
970
971 if (all || !isempty(s))
972 printf("%s=%s\n", name, s);
973
974 return 1;
975 }
976
977 case DBUS_TYPE_BOOLEAN: {
978 dbus_bool_t b;
979
980 dbus_message_iter_get_basic(iter, &b);
981 printf("%s=%s\n", name, yes_no(b));
982
983 return 1;
984 }
985
986 case DBUS_TYPE_UINT64: {
987 uint64_t u;
988 dbus_message_iter_get_basic(iter, &u);
989
990 /* Yes, heuristics! But we can change this check
991 * should it turn out to not be sufficient */
992
993 if (endswith(name, "Timestamp")) {
994 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
995
996 t = format_timestamp(timestamp, sizeof(timestamp), u);
997 if (t || all)
998 printf("%s=%s\n", name, strempty(t));
999
1000 } else if (strstr(name, "USec")) {
1001 char timespan[FORMAT_TIMESPAN_MAX];
1002
1003 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
1004 } else
1005 printf("%s=%llu\n", name, (unsigned long long) u);
1006
1007 return 1;
1008 }
1009
1010 case DBUS_TYPE_UINT32: {
1011 uint32_t u;
1012 dbus_message_iter_get_basic(iter, &u);
1013
1014 if (strstr(name, "UMask") || strstr(name, "Mode"))
1015 printf("%s=%04o\n", name, u);
1016 else
1017 printf("%s=%u\n", name, (unsigned) u);
1018
1019 return 1;
1020 }
1021
1022 case DBUS_TYPE_INT32: {
1023 int32_t i;
1024 dbus_message_iter_get_basic(iter, &i);
1025
1026 printf("%s=%i\n", name, (int) i);
1027 return 1;
1028 }
1029
1030 case DBUS_TYPE_DOUBLE: {
1031 double d;
1032 dbus_message_iter_get_basic(iter, &d);
1033
1034 printf("%s=%g\n", name, d);
1035 return 1;
1036 }
1037
1038 case DBUS_TYPE_ARRAY:
1039
1040 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1041 DBusMessageIter sub;
1042 bool space = false;
1043
1044 dbus_message_iter_recurse(iter, &sub);
1045 if (all ||
1046 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1047 printf("%s=", name);
1048
1049 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1050 const char *s;
1051
1052 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1053 dbus_message_iter_get_basic(&sub, &s);
1054 printf("%s%s", space ? " " : "", s);
1055
1056 space = true;
1057 dbus_message_iter_next(&sub);
1058 }
1059
1060 puts("");
1061 }
1062
1063 return 1;
1064
1065 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1066 DBusMessageIter sub;
1067
1068 dbus_message_iter_recurse(iter, &sub);
1069 if (all ||
1070 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1071 printf("%s=", name);
1072
1073 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1074 uint8_t u;
1075
1076 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1077 dbus_message_iter_get_basic(&sub, &u);
1078 printf("%02x", u);
1079
1080 dbus_message_iter_next(&sub);
1081 }
1082
1083 puts("");
1084 }
1085
8351ceae
LP
1086 return 1;
1087
1088 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_UINT32) {
1089 DBusMessageIter sub;
1090
1091 dbus_message_iter_recurse(iter, &sub);
1092 if (all ||
1093 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1094 printf("%s=", name);
1095
1096 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1097 uint32_t u;
1098
1099 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32);
1100 dbus_message_iter_get_basic(&sub, &u);
1101 printf("%08x", u);
1102
1103 dbus_message_iter_next(&sub);
1104 }
1105
1106 puts("");
1107 }
1108
a4c279f8
LP
1109 return 1;
1110 }
1111
1112 break;
1113 }
1114
1115 return 0;
1116}
ad740100
LP
1117
1118static void release_name_pending_cb(DBusPendingCall *pending, void *userdata) {
1119 DBusMessage *reply;
1120 DBusConnection *bus = userdata;
1121
1122 assert_se(reply = dbus_pending_call_steal_reply(pending));
1123 dbus_message_unref(reply);
1124
1125 dbus_connection_close(bus);
1126}
1127
1128void bus_async_unregister_and_exit(DBusConnection *bus, const char *name) {
1129 DBusMessage *m = NULL;
1130 DBusPendingCall *pending = NULL;
1131
1132 assert(bus);
1133
1134 /* We unregister the name here, but we continue to process
1135 * requests, until we get the response for it, so that all
1136 * requests are guaranteed to be processed. */
1137
1138 m = dbus_message_new_method_call(
1139 DBUS_SERVICE_DBUS,
1140 DBUS_PATH_DBUS,
1141 DBUS_INTERFACE_DBUS,
1142 "ReleaseName");
1143 if (!m)
1144 goto oom;
1145
1146 if (!dbus_message_append_args(
1147 m,
1148 DBUS_TYPE_STRING,
1149 &name,
1150 DBUS_TYPE_INVALID))
1151 goto oom;
1152
1153 if (!dbus_connection_send_with_reply(bus, m, &pending, -1))
1154 goto oom;
1155
1156 if (!dbus_pending_call_set_notify(pending, release_name_pending_cb, bus, NULL))
1157 goto oom;
1158
1159 dbus_message_unref(m);
1160 dbus_pending_call_unref(pending);
1161
1162 return;
1163
1164oom:
0d0f0c50 1165 log_oom();
ad740100
LP
1166
1167 if (pending) {
1168 dbus_pending_call_cancel(pending);
1169 dbus_pending_call_unref(pending);
1170 }
1171
1172 if (m)
1173 dbus_message_unref(m);
1174}
1175
1176DBusHandlerResult bus_exit_idle_filter(DBusConnection *bus, DBusMessage *m, void *userdata) {
1177 usec_t *remain_until = userdata;
1178
1179 assert(bus);
1180 assert(m);
1181 assert(remain_until);
1182
49f43d5f 1183 /* Every time we get a new message we reset out timeout */
ad740100
LP
1184 *remain_until = now(CLOCK_MONOTONIC) + DEFAULT_EXIT_USEC;
1185
1186 if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected"))
1187 dbus_connection_close(bus);
1188
1189 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1190}
f8e2fb7b
LP
1191
1192/* This mimics dbus_bus_get_unix_user() */
1193pid_t bus_get_unix_process_id(
1194 DBusConnection *connection,
1195 const char *name,
1196 DBusError *error) {
1197
1198 DBusMessage *m = NULL, *reply = NULL;
1199 uint32_t pid = 0;
1200
1201 m = dbus_message_new_method_call(
1202 DBUS_SERVICE_DBUS,
1203 DBUS_PATH_DBUS,
1204 DBUS_INTERFACE_DBUS,
1205 "GetConnectionUnixProcessID");
1206 if (!m) {
1207 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1208 goto finish;
1209 }
1210
1211 if (!dbus_message_append_args(
1212 m,
1213 DBUS_TYPE_STRING, &name,
1214 DBUS_TYPE_INVALID)) {
1215 dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
1216 goto finish;
1217 }
1218
1219 reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
1220 if (!reply)
1221 goto finish;
1222
1223 if (dbus_set_error_from_message(error, reply))
1224 goto finish;
1225
1226 if (!dbus_message_get_args(
1227 reply, error,
1228 DBUS_TYPE_UINT32, &pid,
1229 DBUS_TYPE_INVALID))
1230 goto finish;
1231
1232finish:
1233 if (m)
1234 dbus_message_unref(m);
1235
1236 if (reply)
1237 dbus_message_unref(reply);
1238
1239 return (pid_t) pid;
1240}
c67de56f
SP
1241
1242bool bus_error_is_no_service(const DBusError *error) {
1243 assert(error);
1244
1245 if (!dbus_error_is_set(error))
1246 return false;
1247
1248 if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
1249 return true;
1250
1251 if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
1252 return true;
1253
1254 return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
1255}
1256
67f3c402
LP
1257int bus_method_call_with_reply(
1258 DBusConnection *bus,
1259 const char *destination,
1260 const char *path,
1261 const char *interface,
1262 const char *method,
1263 DBusMessage **return_reply,
1264 DBusError *return_error,
1265 int first_arg_type, ...) {
1266
c67de56f
SP
1267 DBusError error;
1268 DBusMessage *m, *reply;
1269 va_list ap;
1270 int r = 0;
1271
1272 dbus_error_init(&error);
1273 assert(bus);
1274
1275 m = dbus_message_new_method_call(destination, path, interface, method);
1276 if (!m) {
1277 r = log_oom();
1278 goto finish;
1279 }
1280
1281 va_start(ap, first_arg_type);
1282 if (!dbus_message_append_args_valist(m, first_arg_type, ap)) {
1283 va_end(ap);
c67de56f
SP
1284 r = log_oom();
1285 goto finish;
1286 }
1287 va_end(ap);
1288
1289 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
c67de56f 1290 if (!reply) {
c516c8d1
SP
1291 if (!return_error)
1292 log_error("Failed to issue method call: %s", bus_error_message(&error));
67f3c402 1293
c67de56f
SP
1294 if (bus_error_is_no_service(&error))
1295 r = -ENOENT;
1296 else if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED))
1297 r = -EACCES;
1298 else if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY))
1299 r = -ETIMEDOUT;
1300 else
1301 r = -EIO;
1302 goto finish;
1303 }
3137e0bd 1304
c67de56f
SP
1305 if (return_reply)
1306 *return_reply = reply;
1307 else
1308 dbus_message_unref(reply);
3137e0bd 1309
c67de56f 1310finish:
3137e0bd
LP
1311 if (m)
1312 dbus_message_unref(m);
1313
1314 if (return_error)
1315 *return_error = error;
c67de56f
SP
1316 else
1317 dbus_error_free(&error);
1318
1319 return r;
1320}