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