chiark / gitweb /
timedated: use libsystemd-bus instead of libdbus for bus communication
[elogind.git] / src / libsystemd-bus / bus-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
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
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   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "sd-event.h"
23 #include "sd-bus.h"
24
25 #include "util.h"
26 #include "macro.h"
27 #include "def.h"
28
29 #include "bus-util.h"
30
31 static int quit_callback(sd_bus *bus, sd_bus_message *m, void *userdata) {
32         sd_event *e = userdata;
33
34         assert(bus);
35         assert(m);
36         assert(e);
37
38         sd_event_request_quit(e);
39         return 1;
40 }
41
42 int bus_async_unregister_and_quit(sd_event *e, sd_bus *bus, const char *name) {
43         _cleanup_free_ char *match = NULL;
44         int r;
45
46         assert(e);
47         assert(bus);
48         assert(name);
49
50         r = asprintf(&match, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameLost',arg0='%s'", name);
51         if (r < 0)
52                 return r;
53
54         r = sd_bus_add_match(bus, match, quit_callback, e);
55         if (r < 0)
56                 return r;
57
58         r = sd_bus_release_name(bus, name);
59         if (r < 0)
60                 return r;
61
62         if (r != SD_BUS_NAME_RELEASED)
63                 return -EIO;
64
65         return 0;
66 }
67
68 int bus_event_loop_with_idle(sd_event *e, sd_bus *bus, const char *name, usec_t timeout) {
69         bool exiting = false;
70         int r;
71
72         assert(e);
73         assert(bus);
74         assert(name);
75
76         for (;;) {
77                 r = sd_event_get_state(e);
78                 if (r < 0)
79                         return r;
80
81                 if (r == SD_EVENT_FINISHED)
82                         break;
83
84                 r = sd_event_run(e, exiting ? (uint64_t) -1 : 5 * USEC_PER_SEC /* DEFAULT_EXIT_USEC */);
85                 if (r < 0)
86                         return r;
87
88                 if (r == 0 && !exiting) {
89                         r = bus_async_unregister_and_quit(e, bus, name);
90                         if (r < 0)
91                                 return r;
92
93                         exiting = true;
94                 }
95         }
96
97         return 0;
98 }
99
100 int bus_property_get_tristate(
101                 sd_bus *bus,
102                 const char *path,
103                 const char *interface,
104                 const char *property,
105                 sd_bus_message *reply,
106                 sd_bus_error *error,
107                 void *userdata) {
108
109         int *tristate = userdata;
110         int r;
111
112         r = sd_bus_message_append(reply, "b", *tristate > 0);
113         if (r < 0)
114                 return r;
115
116         return 1;
117 }
118
119 int bus_verify_polkit(
120                 sd_bus *bus,
121                 sd_bus_message *m,
122                 const char *action,
123                 bool interactive,
124                 bool *_challenge,
125                 sd_bus_error *e) {
126
127         const char *sender;
128         uid_t uid;
129         int r;
130
131         assert(bus);
132         assert(m);
133         assert(action);
134
135         sender = sd_bus_message_get_sender(m);
136         if (!sender)
137                 return -EBADMSG;
138
139         r = sd_bus_get_owner_uid(bus, sender, &uid);
140         if (r < 0)
141                 return r;
142
143         if (uid == 0)
144                 return 1;
145
146 #ifdef ENABLE_POLKIT
147         else {
148                 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
149                 bool authorized = false, challenge = false;
150
151                 r = sd_bus_call_method(
152                                 bus,
153                                 "org.freedesktop.PolicyKit1",
154                                 "/org/freedesktop/PolicyKit1/Authority",
155                                 "org.freedesktop.PolicyKit1.Authority",
156                                 "CheckAuthorization",
157                                 e,
158                                 &reply,
159                                 "(sa{sv})sa{ss}us",
160                                 "system-bus-name", 1, "name", "s", sender,
161                                 action,
162                                 0,
163                                 interactive ? 1 : 0,
164                                 "");
165
166                 if (r < 0) {
167                         /* Treat no PK available as access denied */
168                         if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
169                                 sd_bus_error_free(e);
170                                 return -EACCES;
171                         }
172
173                         return r;
174                 }
175
176                 r = sd_bus_message_read(reply, "(bb)", &authorized, &challenge);
177                 if (r < 0)
178                         return r;
179
180                 if (authorized)
181                         return 1;
182
183                 if (_challenge) {
184                         *_challenge = challenge;
185                         return 0;
186                 }
187         }
188 #endif
189
190         return -EACCES;
191 }
192
193 #ifdef ENABLE_POLKIT
194
195 typedef struct AsyncPolkitQuery {
196         sd_bus_message *request, *reply;
197         sd_bus_message_handler_t callback;
198         void *userdata;
199         uint64_t serial;
200 } AsyncPolkitQuery;
201
202 static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata) {
203         AsyncPolkitQuery *q = userdata;
204         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
205         int r;
206
207         assert(bus);
208         assert(reply);
209         assert(q);
210
211         q->reply = sd_bus_message_ref(reply);
212         q->serial = 0;
213
214         m = sd_bus_message_ref(q->request);
215
216         r = sd_bus_message_rewind(m, true);
217         if (r < 0)
218                 return r;
219
220         r = q->callback(bus, m, q->userdata);
221         if (r < 0)
222                 return r;
223
224         return 1;
225 }
226
227 static void async_polkit_query_free(sd_bus *b, AsyncPolkitQuery *q) {
228
229         if (!q)
230                 return;
231
232         if (q->serial >  0 && b)
233                 sd_bus_send_with_reply_cancel(b, q->serial);
234
235         sd_bus_message_unref(q->request);
236         sd_bus_message_unref(q->reply);
237         free(q);
238 }
239
240 #endif
241
242 int bus_verify_polkit_async(
243                 sd_bus *bus,
244                 Hashmap **registry,
245                 sd_bus_message *m,
246                 const char *action,
247                 bool interactive,
248                 sd_bus_error *error,
249                 sd_bus_message_handler_t callback,
250                 void *userdata) {
251
252 #ifdef ENABLE_POLKIT
253         _cleanup_bus_message_unref_ sd_bus_message *pk = NULL;
254         AsyncPolkitQuery *q;
255 #endif
256         const char *sender;
257         uid_t uid;
258         int r;
259
260         assert(bus);
261         assert(registry);
262         assert(m);
263         assert(action);
264
265 #ifdef ENABLE_POLKIT
266         q = hashmap_remove(*registry, m);
267         if (q) {
268                 bool authorized, challenge;
269
270                 /* This is the second invocation of this function, and
271                  * there's already a response from polkit, let's
272                  * process it */
273                 assert(q->reply);
274
275                 if (sd_bus_message_is_method_error(q->reply, NULL)) {
276                         const sd_bus_error *e;
277
278                         /* Treat no PK available as access denied */
279                         if (sd_bus_message_is_method_error(q->reply, SD_BUS_ERROR_SERVICE_UNKNOWN)) {
280                                 async_polkit_query_free(bus, q);
281                                 return -EACCES;
282                         }
283
284                         e = sd_bus_message_get_error(q->reply);
285                         sd_bus_error_copy(error, e);
286                         r = sd_bus_error_get_errno(e);
287
288                         async_polkit_query_free(bus, q);
289                         return r;
290                 }
291
292                 r = sd_bus_message_enter_container(q->reply, 'r', "bba{ss}");
293                 if (r >= 0)
294                         r = sd_bus_message_read(q->reply, "bb", &authorized, &challenge);
295
296                 async_polkit_query_free(bus, q);
297
298                 if (r < 0)
299                         return r;
300
301                 if (authorized)
302                         return 1;
303
304                 return -EACCES;
305         }
306 #endif
307
308         sender = sd_bus_message_get_sender(m);
309         if (!sender)
310                 return -EBADMSG;
311
312         r = sd_bus_get_owner_uid(bus, sender, &uid);
313         if (r < 0)
314                 return r;
315
316         if (uid == 0)
317                 return 1;
318 #ifdef ENABLE_POLKIT
319
320         r = hashmap_ensure_allocated(registry, trivial_hash_func, trivial_compare_func);
321         if (r < 0)
322                 return r;
323
324         r = sd_bus_message_new_method_call(
325                         bus,
326                         "org.freedesktop.PolicyKit1",
327                         "/org/freedesktop/PolicyKit1/Authority",
328                         "org.freedesktop.PolicyKit1.Authority",
329                         "CheckAuthorization",
330                         &pk);
331         if (r < 0)
332                 return r;
333
334         r = sd_bus_message_append(
335                         pk,
336                         "(sa{sv})sa{ss}us",
337                         "system-bus-name", 1, "name", "s", sender,
338                         action,
339                         0,
340                         interactive ? 1 : 0,
341                         "");
342         if (r < 0)
343                 return r;
344
345         q = new0(AsyncPolkitQuery, 1);
346         if (!q)
347                 return -ENOMEM;
348
349         q->request = sd_bus_message_ref(m);
350         q->callback = callback;
351         q->userdata = userdata;
352
353         r = hashmap_put(*registry, m, q);
354         if (r < 0) {
355                 async_polkit_query_free(bus, q);
356                 return r;
357         }
358
359         r = sd_bus_send_with_reply(bus, pk, async_polkit_callback, q, 0, &q->serial);
360         if (r < 0)
361                 return r;
362
363         return 0;
364 #endif
365
366         return -EACCES;
367 }
368
369 void bus_verify_polkit_async_registry_free(sd_bus *bus, Hashmap *registry) {
370 #ifdef ENABLE_POLKIT
371         AsyncPolkitQuery *q;
372
373         while ((q = hashmap_steal_first(registry)))
374                 async_polkit_query_free(bus, q);
375
376         hashmap_free(registry);
377 #endif
378 }