]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/bus-util.c
timedated: use libsystemd-bus instead of libdbus for bus communication
[thirdparty/systemd.git] / src / libsystemd-bus / bus-util.c
CommitLineData
40ca29a1
LP
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
31static 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
42int 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
68int 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
100int 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
119int 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
195typedef 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
202static 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
227static 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
242int 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
369void 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}