]> git.ipfire.org Git - people/ms/systemd.git/blame - dbus.c
socket: optionally call accept() for incoming connections and spawn one service insta...
[people/ms/systemd.git] / dbus.c
CommitLineData
ea430986
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
2
a7334b09
LP
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
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
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
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
ea430986
LP
22#include <dbus/dbus.h>
23
24#include <sys/epoll.h>
25#include <sys/timerfd.h>
26#include <errno.h>
27#include <unistd.h>
28
29#include "dbus.h"
30#include "log.h"
31#include "strv.h"
8e274523 32#include "cgroup.h"
ea430986 33
f278026d 34static void api_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) {
ea430986
LP
35 Manager *m = data;
36
37 assert(bus);
38 assert(m);
2e317f52
LP
39
40 if (!m->api_bus)
41 return;
42
f278026d 43 assert(m->api_bus == bus);
ea430986 44
f278026d 45 m->request_api_bus_dispatch = status != DBUS_DISPATCH_COMPLETE;
ea430986
LP
46}
47
8e274523
LP
48static void system_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) {
49 Manager *m = data;
50
51 assert(bus);
52 assert(m);
2e317f52
LP
53
54 if (!m->system_bus)
55 return;
56
8e274523
LP
57 assert(m->system_bus == bus);
58
59 m->request_system_bus_dispatch = status != DBUS_DISPATCH_COMPLETE;
60}
61
ea430986
LP
62static uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
63 unsigned flags;
64 uint32_t events = 0;
65
66 assert(bus_watch);
67
68 /* no watch flags for disabled watches */
69 if (!dbus_watch_get_enabled(bus_watch))
70 return 0;
71
72 flags = dbus_watch_get_flags(bus_watch);
73
74 if (flags & DBUS_WATCH_READABLE)
75 events |= EPOLLIN;
76 if (flags & DBUS_WATCH_WRITABLE)
77 events |= EPOLLOUT;
78
79 return events | EPOLLHUP | EPOLLERR;
80}
81
82static unsigned events_to_bus_flags(uint32_t events) {
83 unsigned flags = 0;
84
85 if (events & EPOLLIN)
86 flags |= DBUS_WATCH_READABLE;
87 if (events & EPOLLOUT)
88 flags |= DBUS_WATCH_WRITABLE;
89 if (events & EPOLLHUP)
90 flags |= DBUS_WATCH_HANGUP;
91 if (events & EPOLLERR)
92 flags |= DBUS_WATCH_ERROR;
93
94 return flags;
95}
96
97void bus_watch_event(Manager *m, Watch *w, int events) {
98 assert(m);
99 assert(w);
100
101 /* This is called by the event loop whenever there is
102 * something happening on D-Bus' file handles. */
103
8e274523 104 if (!dbus_watch_get_enabled(w->data.bus_watch))
ea430986
LP
105 return;
106
107 dbus_watch_handle(w->data.bus_watch, events_to_bus_flags(events));
108}
109
110static dbus_bool_t bus_add_watch(DBusWatch *bus_watch, void *data) {
111 Manager *m = data;
112 Watch *w;
113 struct epoll_event ev;
114
115 assert(bus_watch);
116 assert(m);
117
118 if (!(w = new0(Watch, 1)))
119 return FALSE;
120
121 w->fd = dbus_watch_get_unix_fd(bus_watch);
122 w->type = WATCH_DBUS_WATCH;
123 w->data.bus_watch = bus_watch;
124
125 zero(ev);
126 ev.events = bus_flags_to_events(bus_watch);
127 ev.data.ptr = w;
128
129 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
130
131 if (errno != EEXIST) {
132 free(w);
133 return FALSE;
134 }
135
136 /* Hmm, bloody D-Bus creates multiple watches on the
137 * same fd. epoll() does not like that. As a dirty
138 * hack we simply dup() the fd and hence get a second
139 * one we can safely add to the epoll(). */
140
141 if ((w->fd = dup(w->fd)) < 0) {
142 free(w);
143 return FALSE;
144 }
145
146 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
147 free(w);
148 close_nointr_nofail(w->fd);
149 return FALSE;
150 }
151
152 w->fd_is_dupped = true;
153 }
154
155 dbus_watch_set_data(bus_watch, w, NULL);
156
157 return TRUE;
158}
159
160static void bus_remove_watch(DBusWatch *bus_watch, void *data) {
161 Manager *m = data;
162 Watch *w;
163
164 assert(bus_watch);
165 assert(m);
166
167 if (!(w = dbus_watch_get_data(bus_watch)))
168 return;
169
170 assert(w->type == WATCH_DBUS_WATCH);
171 assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
172
173 if (w->fd_is_dupped)
174 close_nointr_nofail(w->fd);
175
176 free(w);
177}
178
179static void bus_toggle_watch(DBusWatch *bus_watch, void *data) {
180 Manager *m = data;
181 Watch *w;
182 struct epoll_event ev;
183
184 assert(bus_watch);
185 assert(m);
186
187 assert_se(w = dbus_watch_get_data(bus_watch));
188 assert(w->type == WATCH_DBUS_WATCH);
189
190 zero(ev);
191 ev.events = bus_flags_to_events(bus_watch);
192 ev.data.ptr = w;
193
194 assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_MOD, w->fd, &ev) == 0);
195}
196
197static int bus_timeout_arm(Manager *m, Watch *w) {
198 struct itimerspec its;
199
200 assert(m);
201 assert(w);
202
203 zero(its);
204
205 if (dbus_timeout_get_enabled(w->data.bus_timeout)) {
206 timespec_store(&its.it_value, dbus_timeout_get_interval(w->data.bus_timeout) * USEC_PER_MSEC);
207 its.it_interval = its.it_interval;
208 }
209
210 if (timerfd_settime(w->fd, 0, &its, NULL) < 0)
211 return -errno;
212
213 return 0;
214}
215
216void bus_timeout_event(Manager *m, Watch *w, int events) {
217 assert(m);
218 assert(w);
219
220 /* This is called by the event loop whenever there is
221 * something happening on D-Bus' file handles. */
222
223 if (!(dbus_timeout_get_enabled(w->data.bus_timeout)))
224 return;
225
226 dbus_timeout_handle(w->data.bus_timeout);
227}
228
229static dbus_bool_t bus_add_timeout(DBusTimeout *timeout, void *data) {
230 Manager *m = data;
231 Watch *w;
232 struct epoll_event ev;
233
234 assert(timeout);
235 assert(m);
236
237 if (!(w = new0(Watch, 1)))
238 return FALSE;
239
240 if (!(w->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
241 goto fail;
242
243 w->type = WATCH_DBUS_TIMEOUT;
244 w->data.bus_timeout = timeout;
245
246 if (bus_timeout_arm(m, w) < 0)
247 goto fail;
248
249 zero(ev);
250 ev.events = EPOLLIN;
251 ev.data.ptr = w;
252
253 if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
254 goto fail;
255
256 dbus_timeout_set_data(timeout, w, NULL);
257
258 return TRUE;
259
260fail:
261 if (w->fd >= 0)
262 close_nointr_nofail(w->fd);
263
264 free(w);
265 return FALSE;
266}
267
268static void bus_remove_timeout(DBusTimeout *timeout, void *data) {
269 Manager *m = data;
270 Watch *w;
271
272 assert(timeout);
273 assert(m);
274
275 if (!(w = dbus_timeout_get_data(timeout)))
276 return;
277
278 assert(w->type == WATCH_DBUS_TIMEOUT);
279 assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
280 close_nointr_nofail(w->fd);
281 free(w);
282}
283
284static void bus_toggle_timeout(DBusTimeout *timeout, void *data) {
285 Manager *m = data;
286 Watch *w;
287 int r;
288
289 assert(timeout);
290 assert(m);
291
292 assert_se(w = dbus_timeout_get_data(timeout));
293 assert(w->type == WATCH_DBUS_TIMEOUT);
294
295 if ((r = bus_timeout_arm(m, w)) < 0)
296 log_error("Failed to rearm timer: %s", strerror(-r));
297}
298
f278026d 299static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
c1e1601e
LP
300 Manager *m = data;
301 DBusError error;
302
303 assert(connection);
304 assert(message);
305 assert(m);
306
307 dbus_error_init(&error);
308
309 /* log_debug("Got D-Bus request: %s.%s() on %s", */
310 /* dbus_message_get_interface(message), */
311 /* dbus_message_get_member(message), */
312 /* dbus_message_get_path(message)); */
313
314 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
2e317f52 315 log_error("Warning! API D-Bus connection terminated.");
f278026d 316 bus_done_api(m);
c1e1601e
LP
317
318 } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
319 const char *name, *old, *new;
320
321 if (!dbus_message_get_args(message, &error,
322 DBUS_TYPE_STRING, &name,
323 DBUS_TYPE_STRING, &old,
324 DBUS_TYPE_STRING, &new,
325 DBUS_TYPE_INVALID))
326 log_error("Failed to parse NameOwnerChanged message: %s", error.message);
327 else {
328 if (set_remove(m->subscribed, (char*) name))
329 log_debug("Subscription client vanished: %s (left: %u)", name, set_size(m->subscribed));
330 }
331 }
332
333 dbus_error_free(&error);
334 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
335}
336
8e274523
LP
337static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, DBusMessage *message, void *data) {
338 Manager *m = data;
339 DBusError error;
340
341 assert(connection);
342 assert(message);
343 assert(m);
344
345 dbus_error_init(&error);
346
347 /* log_debug("Got D-Bus request: %s.%s() on %s", */
348 /* dbus_message_get_interface(message), */
349 /* dbus_message_get_member(message), */
350 /* dbus_message_get_path(message)); */
351
352 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
2e317f52 353 log_error("Warning! System D-Bus connection terminated.");
f278026d 354 bus_done_system(m);
8e274523
LP
355
356 } if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
357 const char *cgroup;
358
359 if (!dbus_message_get_args(message, &error,
360 DBUS_TYPE_STRING, &cgroup,
361 DBUS_TYPE_INVALID))
362 log_error("Failed to parse Released message: %s", error.message);
363 else
364 cgroup_notify_empty(m, cgroup);
365 }
366
367 dbus_error_free(&error);
368 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
369}
370
c1e1601e 371unsigned bus_dispatch(Manager *m) {
ea430986
LP
372 assert(m);
373
fd18e1f4
LP
374 if (m->request_api_bus_dispatch) {
375 if (dbus_connection_dispatch(m->api_bus) == DBUS_DISPATCH_COMPLETE)
f278026d 376 m->request_api_bus_dispatch = false;
c1e1601e 377
fd18e1f4
LP
378 return 1;
379 }
380
381 if (m->request_system_bus_dispatch) {
382 if (dbus_connection_dispatch(m->system_bus) == DBUS_DISPATCH_COMPLETE)
8e274523 383 m->request_system_bus_dispatch = false;
fd18e1f4
LP
384
385 return 1;
386 }
c1e1601e 387
8e274523 388 return 0;
ea430986
LP
389}
390
61902ea3
LP
391static void pending_cb(DBusPendingCall *pending, void *userdata) {
392 DBusMessage *reply;
393 DBusError error;
394
395 dbus_error_init(&error);
396
397 assert_se(reply = dbus_pending_call_steal_reply(pending));
398
399 switch (dbus_message_get_type(reply)) {
400
401 case DBUS_MESSAGE_TYPE_ERROR:
402
403 assert_se(dbus_set_error_from_message(&error, reply));
404 log_warning("RequestName() failed: %s", error.message);
405 break;
406
407 case DBUS_MESSAGE_TYPE_METHOD_RETURN: {
408 uint32_t r;
409
410 if (!dbus_message_get_args(reply,
411 &error,
412 DBUS_TYPE_UINT32, &r,
413 DBUS_TYPE_INVALID)) {
414 log_error("Failed to parse RequestName() reply: %s", error.message);
415 break;
416 }
417
418 if (r == 1)
419 log_debug("Successfully acquired name.");
420 else
421 log_error("Name already owned.");
422
423 break;
424 }
425
426 default:
427 assert_not_reached("Invalid reply message");
428 }
429
430 dbus_message_unref(reply);
431 dbus_error_free(&error);
432}
433
ea430986
LP
434static int request_name(Manager *m) {
435 DBusMessage *message;
436 const char *name = "org.freedesktop.systemd1";
437 uint32_t flags = 0;
61902ea3 438 DBusPendingCall *pending;
ea430986
LP
439
440 if (!(message = dbus_message_new_method_call(
441 DBUS_SERVICE_DBUS,
442 DBUS_PATH_DBUS,
443 DBUS_INTERFACE_DBUS,
444 "RequestName")))
445 return -ENOMEM;
446
447 if (!dbus_message_append_args(
448 message,
449 DBUS_TYPE_STRING, &name,
450 DBUS_TYPE_UINT32, &flags,
451 DBUS_TYPE_INVALID)) {
452 dbus_message_unref(message);
453 return -ENOMEM;
454 }
455
f278026d 456 if (!dbus_connection_send_with_reply(m->api_bus, message, &pending, -1)) {
ea430986
LP
457 dbus_message_unref(message);
458 return -ENOMEM;
459 }
460
ea430986
LP
461
462 dbus_message_unref(message);
463
61902ea3
LP
464 if (!dbus_pending_call_set_notify(pending, pending_cb, NULL, NULL)) {
465 dbus_pending_call_cancel(pending);
466 dbus_pending_call_unref(pending);
467 return -ENOMEM;
468 }
469
470
471 dbus_pending_call_unref(pending);
472
473 /* We simple ask for the name and don't wait for it. Sooner or
474 * later we'll have it. */
475
ea430986
LP
476 return 0;
477}
478
8e274523
LP
479static int bus_setup_loop(Manager *m, DBusConnection *bus) {
480 assert(m);
481 assert(bus);
482
483 dbus_connection_set_exit_on_disconnect(bus, FALSE);
fd18e1f4 484
8e274523
LP
485 if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
486 !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL))
487 return -ENOMEM;
488
489 return 0;
490}
491
f278026d 492int bus_init_system(Manager *m) {
ea430986
LP
493 DBusError error;
494 char *id;
495 int r;
496
497 assert(m);
498
ea430986 499 dbus_error_init(&error);
ea430986 500
f278026d
LP
501 if (m->system_bus)
502 return 0;
8e274523 503
f278026d
LP
504 if (m->running_as != MANAGER_SESSION && m->api_bus)
505 m->system_bus = m->api_bus;
506 else {
8e274523 507 if (!(m->system_bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
266e525c 508 log_debug("Failed to get system D-Bus connection, retrying later: %s", error.message);
8e274523 509 dbus_error_free(&error);
f278026d 510 return 0;
8e274523
LP
511 }
512
fd18e1f4
LP
513 dbus_connection_set_dispatch_status_function(m->system_bus, system_bus_dispatch_status, m, NULL);
514 m->request_system_bus_dispatch = true;
515
8e274523 516 if ((r = bus_setup_loop(m, m->system_bus)) < 0) {
f278026d 517 bus_done_system(m);
8e274523
LP
518 return r;
519 }
f278026d
LP
520 }
521
522 if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) {
523 bus_done_system(m);
c1e1601e
LP
524 return -ENOMEM;
525 }
526
f278026d 527 dbus_bus_add_match(m->system_bus,
c1e1601e 528 "type='signal',"
f278026d
LP
529 "interface='org.freedesktop.systemd1.Agent',"
530 "path='/org/freedesktop/systemd1/agent'",
c1e1601e
LP
531 &error);
532
533 if (dbus_error_is_set(&error)) {
534 log_error("Failed to register match: %s", error.message);
535 dbus_error_free(&error);
f278026d 536 bus_done_system(m);
ea430986
LP
537 return -ENOMEM;
538 }
539
f278026d
LP
540 log_debug("Successfully connected to system D-Bus bus %s as %s",
541 strnull((id = dbus_connection_get_server_id(m->system_bus))),
542 strnull(dbus_bus_get_unique_name(m->system_bus)));
543 dbus_free(id);
544
f278026d
LP
545 return 0;
546}
547
548int bus_init_api(Manager *m) {
549 DBusError error;
550 char *id;
551 int r;
552
553 assert(m);
554
555 dbus_error_init(&error);
556
557 if (m->api_bus)
558 return 0;
559
560 if (m->running_as != MANAGER_SESSION && m->system_bus)
561 m->api_bus = m->system_bus;
562 else {
563 if (!(m->api_bus = dbus_bus_get_private(m->running_as == MANAGER_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) {
266e525c 564 log_debug("Failed to get API D-Bus connection, retrying later: %s", error.message);
f278026d
LP
565 dbus_error_free(&error);
566 return 0;
567 }
568
fd18e1f4
LP
569 dbus_connection_set_dispatch_status_function(m->api_bus, api_bus_dispatch_status, m, NULL);
570 m->request_api_bus_dispatch = true;
571
f278026d
LP
572 if ((r = bus_setup_loop(m, m->api_bus)) < 0) {
573 bus_done_api(m);
574 return r;
575 }
ea430986
LP
576 }
577
f278026d
LP
578 if (!dbus_connection_register_object_path(m->api_bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
579 !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) ||
580 !dbus_connection_register_fallback(m->api_bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) ||
581 !dbus_connection_add_filter(m->api_bus, api_bus_message_filter, m, NULL)) {
582 bus_done_api(m);
583 return -ENOMEM;
584 }
585
586 dbus_bus_add_match(m->api_bus,
8e274523 587 "type='signal',"
f278026d
LP
588 "sender='"DBUS_SERVICE_DBUS"',"
589 "interface='"DBUS_INTERFACE_DBUS"',"
590 "path='"DBUS_PATH_DBUS"'",
8e274523
LP
591 &error);
592
593 if (dbus_error_is_set(&error)) {
594 log_error("Failed to register match: %s", error.message);
595 dbus_error_free(&error);
f278026d 596 bus_done_api(m);
8e274523
LP
597 return -ENOMEM;
598 }
599
f278026d
LP
600 if ((r = request_name(m)) < 0) {
601 bus_done_api(m);
602 return r;
603 }
ea430986 604
f278026d
LP
605 log_debug("Successfully connected to API D-Bus bus %s as %s",
606 strnull((id = dbus_connection_get_server_id(m->api_bus))),
607 strnull(dbus_bus_get_unique_name(m->api_bus)));
8e274523
LP
608 dbus_free(id);
609
f278026d
LP
610 if (!m->subscribed)
611 if (!(m->subscribed = set_new(string_hash_func, string_compare_func)))
612 return -ENOMEM;
613
ea430986
LP
614 return 0;
615}
616
f278026d 617void bus_done_api(Manager *m) {
ea430986
LP
618 assert(m);
619
f278026d
LP
620 if (m->api_bus) {
621 if (m->system_bus == m->api_bus)
622 m->system_bus = NULL;
623
90d3f30b 624 dbus_connection_set_dispatch_status_function(m->api_bus, NULL, NULL, NULL);
2e317f52 625 dbus_connection_close(m->api_bus);
f278026d
LP
626 dbus_connection_unref(m->api_bus);
627 m->api_bus = NULL;
ea430986 628 }
c1e1601e
LP
629
630 if (m->subscribed) {
631 char *c;
632
633 while ((c = set_steal_first(m->subscribed)))
634 free(c);
635
636 set_free(m->subscribed);
637 m->subscribed = NULL;
638 }
ea430986
LP
639}
640
f278026d
LP
641void bus_done_system(Manager *m) {
642 assert(m);
643
644 if (m->system_bus == m->api_bus)
645 bus_done_api(m);
646
647 if (m->system_bus) {
90d3f30b 648 dbus_connection_set_dispatch_status_function(m->system_bus, NULL, NULL, NULL);
2e317f52 649 dbus_connection_close(m->system_bus);
f278026d
LP
650 dbus_connection_unref(m->system_bus);
651 m->system_bus = NULL;
652 }
653}
654
ea430986
LP
655DBusHandlerResult bus_default_message_handler(Manager *m, DBusMessage *message, const char*introspection, const BusProperty *properties) {
656 DBusError error;
657 DBusMessage *reply = NULL;
658 int r;
659
660 assert(m);
661 assert(message);
662
663 dbus_error_init(&error);
664
665 if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") && introspection) {
666
667 if (!(reply = dbus_message_new_method_return(message)))
668 goto oom;
669
670 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID))
671 goto oom;
672
673 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "Get") && properties) {
674 const char *interface, *property;
675 const BusProperty *p;
676
677 if (!dbus_message_get_args(
678 message,
679 &error,
680 DBUS_TYPE_STRING, &interface,
681 DBUS_TYPE_STRING, &property,
682 DBUS_TYPE_INVALID))
683 return bus_send_error_reply(m, message, &error, -EINVAL);
684
685 for (p = properties; p->property; p++)
686 if (streq(p->interface, interface) && streq(p->property, property))
687 break;
688
689 if (p->property) {
690 DBusMessageIter iter, sub;
691
692 if (!(reply = dbus_message_new_method_return(message)))
693 goto oom;
694
695 dbus_message_iter_init_append(reply, &iter);
696
697 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, p->signature, &sub))
698 goto oom;
699
b9f49ee4 700 if ((r = p->append(m, &sub, property, (void*) p->data)) < 0) {
ea430986
LP
701
702 if (r == -ENOMEM)
703 goto oom;
704
705 dbus_message_unref(reply);
706 return bus_send_error_reply(m, message, NULL, r);
707 }
708
709 if (!dbus_message_iter_close_container(&iter, &sub))
710 goto oom;
711 }
712 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Properties", "GetAll") && properties) {
713 const char *interface;
714 const BusProperty *p;
715 DBusMessageIter iter, sub, sub2, sub3;
716 bool any = false;
717
718 if (!dbus_message_get_args(
719 message,
720 &error,
721 DBUS_TYPE_STRING, &interface,
722 DBUS_TYPE_INVALID))
723 return bus_send_error_reply(m, message, &error, -EINVAL);
724
725 if (!(reply = dbus_message_new_method_return(message)))
726 goto oom;
727
728 dbus_message_iter_init_append(reply, &iter);
729
730 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &sub))
731 goto oom;
732
733 for (p = properties; p->property; p++) {
734 if (!streq(p->interface, interface))
735 continue;
736
c401a1e0 737 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, NULL, &sub2) ||
ea430986
LP
738 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &p->property) ||
739 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, p->signature, &sub3))
740 goto oom;
741
b9f49ee4 742 if ((r = p->append(m, &sub3, p->property, (void*) p->data)) < 0) {
ea430986
LP
743
744 if (r == -ENOMEM)
745 goto oom;
746
747 dbus_message_unref(reply);
748 return bus_send_error_reply(m, message, NULL, r);
749 }
750
751 if (!dbus_message_iter_close_container(&sub2, &sub3) ||
752 !dbus_message_iter_close_container(&sub, &sub2))
753 goto oom;
754
755 any = true;
756 }
757
758 if (!dbus_message_iter_close_container(&iter, &sub))
759 goto oom;
760 }
761
762 if (reply) {
f278026d 763 if (!dbus_connection_send(m->api_bus, reply, NULL))
ea430986
LP
764 goto oom;
765
766 dbus_message_unref(reply);
767 return DBUS_HANDLER_RESULT_HANDLED;
768 }
769
770 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
771
772oom:
773 if (reply)
774 dbus_message_unref(reply);
775
776 dbus_error_free(&error);
777
778 return DBUS_HANDLER_RESULT_NEED_MEMORY;
779}
780
ea430986
LP
781static const char *error_to_dbus(int error) {
782
783 switch(error) {
784
785 case -EINVAL:
786 return DBUS_ERROR_INVALID_ARGS;
787
788 case -ENOMEM:
789 return DBUS_ERROR_NO_MEMORY;
790
791 case -EPERM:
792 case -EACCES:
793 return DBUS_ERROR_ACCESS_DENIED;
794
795 case -ESRCH:
796 return DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN;
797
798 case -ENOENT:
799 return DBUS_ERROR_FILE_NOT_FOUND;
800
801 case -EEXIST:
802 return DBUS_ERROR_FILE_EXISTS;
803
804 case -ETIMEDOUT:
805 return DBUS_ERROR_TIMEOUT;
806
807 case -EIO:
808 return DBUS_ERROR_IO_ERROR;
809
810 case -ENETRESET:
811 case -ECONNABORTED:
812 case -ECONNRESET:
813 return DBUS_ERROR_DISCONNECTED;
814 }
815
816 return DBUS_ERROR_FAILED;
817}
818
819DBusHandlerResult bus_send_error_reply(Manager *m, DBusMessage *message, DBusError *bus_error, int error) {
820 DBusMessage *reply = NULL;
821 const char *name, *text;
822
823 if (bus_error && dbus_error_is_set(bus_error)) {
824 name = bus_error->name;
825 text = bus_error->message;
826 } else {
827 name = error_to_dbus(error);
828 text = strerror(-error);
829 }
830
831 if (!(reply = dbus_message_new_error(message, name, text)))
832 goto oom;
833
f278026d 834 if (!dbus_connection_send(m->api_bus, reply, NULL))
ea430986
LP
835 goto oom;
836
837 dbus_message_unref(reply);
838
839 if (bus_error)
840 dbus_error_free(bus_error);
841
842 return DBUS_HANDLER_RESULT_HANDLED;
843
844oom:
845 if (reply)
846 dbus_message_unref(reply);
847
848 if (bus_error)
849 dbus_error_free(bus_error);
850
851 return DBUS_HANDLER_RESULT_NEED_MEMORY;
852}
853
854int bus_property_append_string(Manager *m, DBusMessageIter *i, const char *property, void *data) {
855 const char *t = data;
856
857 assert(m);
858 assert(i);
859 assert(property);
38131695
LP
860
861 if (!t)
862 t = "";
ea430986
LP
863
864 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
865 return -ENOMEM;
866
867 return 0;
868}
869
870int bus_property_append_strv(Manager *m, DBusMessageIter *i, const char *property, void *data) {
871 DBusMessageIter sub;
872 char **t = data;
873
874 assert(m);
875 assert(i);
876 assert(property);
877
878 if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
879 return -ENOMEM;
880
881 STRV_FOREACH(t, t)
882 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, t))
883 return -ENOMEM;
884
885 if (!dbus_message_iter_close_container(i, &sub))
886 return -ENOMEM;
887
888 return 0;
889}
890
891int bus_property_append_bool(Manager *m, DBusMessageIter *i, const char *property, void *data) {
892 bool *b = data;
893 dbus_bool_t db;
894
895 assert(m);
896 assert(i);
897 assert(property);
898 assert(b);
899
900 db = *b;
901
902 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &db))
903 return -ENOMEM;
904
905 return 0;
906}
38131695
LP
907
908int bus_property_append_uint64(Manager *m, DBusMessageIter *i, const char *property, void *data) {
909 assert(m);
910 assert(i);
911 assert(property);
912 assert(data);
913
914 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, data))
915 return -ENOMEM;
916
917 return 0;
918}
919
920int bus_property_append_uint32(Manager *m, DBusMessageIter *i, const char *property, void *data) {
921 assert(m);
922 assert(i);
923 assert(property);
924 assert(data);
925
926 if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, data))
927 return -ENOMEM;
928
929 return 0;
930}