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