]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-convenience.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-convenience.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "bus-internal.h"
22 #include "bus-message.h"
23 #include "bus-signature.h"
24 #include "bus-type.h"
25 #include "bus-util.h"
26 #include "string-util.h"
27
28 _public_ int sd_bus_emit_signal(
29 sd_bus *bus,
30 const char *path,
31 const char *interface,
32 const char *member,
33 const char *types, ...) {
34
35 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
36 int r;
37
38 assert_return(bus, -EINVAL);
39 assert_return(!bus_pid_changed(bus), -ECHILD);
40
41 if (!BUS_IS_OPEN(bus->state))
42 return -ENOTCONN;
43
44 r = sd_bus_message_new_signal(bus, &m, path, interface, member);
45 if (r < 0)
46 return r;
47
48 if (!isempty(types)) {
49 va_list ap;
50
51 va_start(ap, types);
52 r = sd_bus_message_appendv(m, types, ap);
53 va_end(ap);
54 if (r < 0)
55 return r;
56 }
57
58 return sd_bus_send(bus, m, NULL);
59 }
60
61 _public_ int sd_bus_call_method_async(
62 sd_bus *bus,
63 sd_bus_slot **slot,
64 const char *destination,
65 const char *path,
66 const char *interface,
67 const char *member,
68 sd_bus_message_handler_t callback,
69 void *userdata,
70 const char *types, ...) {
71
72 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
73 int r;
74
75 assert_return(bus, -EINVAL);
76 assert_return(!bus_pid_changed(bus), -ECHILD);
77
78 if (!BUS_IS_OPEN(bus->state))
79 return -ENOTCONN;
80
81 r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
82 if (r < 0)
83 return r;
84
85 if (!isempty(types)) {
86 va_list ap;
87
88 va_start(ap, types);
89 r = sd_bus_message_appendv(m, types, ap);
90 va_end(ap);
91 if (r < 0)
92 return r;
93 }
94
95 return sd_bus_call_async(bus, slot, m, callback, userdata, 0);
96 }
97
98 _public_ int sd_bus_call_method(
99 sd_bus *bus,
100 const char *destination,
101 const char *path,
102 const char *interface,
103 const char *member,
104 sd_bus_error *error,
105 sd_bus_message **reply,
106 const char *types, ...) {
107
108 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
109 int r;
110
111 bus_assert_return(bus, -EINVAL, error);
112 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
113
114 if (!BUS_IS_OPEN(bus->state)) {
115 r = -ENOTCONN;
116 goto fail;
117 }
118
119 r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
120 if (r < 0)
121 goto fail;
122
123 if (!isempty(types)) {
124 va_list ap;
125
126 va_start(ap, types);
127 r = sd_bus_message_appendv(m, types, ap);
128 va_end(ap);
129 if (r < 0)
130 goto fail;
131 }
132
133 return sd_bus_call(bus, m, 0, error, reply);
134
135 fail:
136 return sd_bus_error_set_errno(error, r);
137 }
138
139 _public_ int sd_bus_reply_method_return(
140 sd_bus_message *call,
141 const char *types, ...) {
142
143 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
144 int r;
145
146 assert_return(call, -EINVAL);
147 assert_return(call->sealed, -EPERM);
148 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
149 assert_return(call->bus, -EINVAL);
150 assert_return(!bus_pid_changed(call->bus), -ECHILD);
151
152 if (!BUS_IS_OPEN(call->bus->state))
153 return -ENOTCONN;
154
155 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
156 return 0;
157
158 r = sd_bus_message_new_method_return(call, &m);
159 if (r < 0)
160 return r;
161
162 if (!isempty(types)) {
163 va_list ap;
164
165 va_start(ap, types);
166 r = sd_bus_message_appendv(m, types, ap);
167 va_end(ap);
168 if (r < 0)
169 return r;
170 }
171
172 return sd_bus_send(call->bus, m, NULL);
173 }
174
175 _public_ int sd_bus_reply_method_error(
176 sd_bus_message *call,
177 const sd_bus_error *e) {
178
179 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
180 int r;
181
182 assert_return(call, -EINVAL);
183 assert_return(call->sealed, -EPERM);
184 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
185 assert_return(sd_bus_error_is_set(e), -EINVAL);
186 assert_return(call->bus, -EINVAL);
187 assert_return(!bus_pid_changed(call->bus), -ECHILD);
188
189 if (!BUS_IS_OPEN(call->bus->state))
190 return -ENOTCONN;
191
192 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
193 return 0;
194
195 r = sd_bus_message_new_method_error(call, &m, e);
196 if (r < 0)
197 return r;
198
199 return sd_bus_send(call->bus, m, NULL);
200 }
201
202 _public_ int sd_bus_reply_method_errorf(
203 sd_bus_message *call,
204 const char *name,
205 const char *format,
206 ...) {
207
208 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
209 va_list ap;
210
211 assert_return(call, -EINVAL);
212 assert_return(call->sealed, -EPERM);
213 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
214 assert_return(call->bus, -EINVAL);
215 assert_return(!bus_pid_changed(call->bus), -ECHILD);
216
217 if (!BUS_IS_OPEN(call->bus->state))
218 return -ENOTCONN;
219
220 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
221 return 0;
222
223 va_start(ap, format);
224 bus_error_setfv(&error, name, format, ap);
225 va_end(ap);
226
227 return sd_bus_reply_method_error(call, &error);
228 }
229
230 _public_ int sd_bus_reply_method_errno(
231 sd_bus_message *call,
232 int error,
233 const sd_bus_error *p) {
234
235 _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
236
237 assert_return(call, -EINVAL);
238 assert_return(call->sealed, -EPERM);
239 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
240 assert_return(call->bus, -EINVAL);
241 assert_return(!bus_pid_changed(call->bus), -ECHILD);
242
243 if (!BUS_IS_OPEN(call->bus->state))
244 return -ENOTCONN;
245
246 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
247 return 0;
248
249 if (sd_bus_error_is_set(p))
250 return sd_bus_reply_method_error(call, p);
251
252 sd_bus_error_set_errno(&berror, error);
253
254 return sd_bus_reply_method_error(call, &berror);
255 }
256
257 _public_ int sd_bus_reply_method_errnof(
258 sd_bus_message *call,
259 int error,
260 const char *format,
261 ...) {
262
263 _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
264 va_list ap;
265
266 assert_return(call, -EINVAL);
267 assert_return(call->sealed, -EPERM);
268 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
269 assert_return(call->bus, -EINVAL);
270 assert_return(!bus_pid_changed(call->bus), -ECHILD);
271
272 if (!BUS_IS_OPEN(call->bus->state))
273 return -ENOTCONN;
274
275 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
276 return 0;
277
278 va_start(ap, format);
279 sd_bus_error_set_errnofv(&berror, error, format, ap);
280 va_end(ap);
281
282 return sd_bus_reply_method_error(call, &berror);
283 }
284
285 _public_ int sd_bus_get_property(
286 sd_bus *bus,
287 const char *destination,
288 const char *path,
289 const char *interface,
290 const char *member,
291 sd_bus_error *error,
292 sd_bus_message **reply,
293 const char *type) {
294
295 sd_bus_message *rep = NULL;
296 int r;
297
298 bus_assert_return(bus, -EINVAL, error);
299 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
300 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
301 bus_assert_return(reply, -EINVAL, error);
302 bus_assert_return(signature_is_single(type, false), -EINVAL, error);
303 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
304
305 if (!BUS_IS_OPEN(bus->state)) {
306 r = -ENOTCONN;
307 goto fail;
308 }
309
310 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
311 if (r < 0)
312 return r;
313
314 r = sd_bus_message_enter_container(rep, 'v', type);
315 if (r < 0) {
316 sd_bus_message_unref(rep);
317 goto fail;
318 }
319
320 *reply = rep;
321 return 0;
322
323 fail:
324 return sd_bus_error_set_errno(error, r);
325 }
326
327 _public_ int sd_bus_get_property_trivial(
328 sd_bus *bus,
329 const char *destination,
330 const char *path,
331 const char *interface,
332 const char *member,
333 sd_bus_error *error,
334 char type, void *ptr) {
335
336 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
337 int r;
338
339 bus_assert_return(bus, -EINVAL, error);
340 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
341 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
342 bus_assert_return(bus_type_is_trivial(type), -EINVAL, error);
343 bus_assert_return(ptr, -EINVAL, error);
344 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
345
346 if (!BUS_IS_OPEN(bus->state)) {
347 r = -ENOTCONN;
348 goto fail;
349 }
350
351 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
352 if (r < 0)
353 return r;
354
355 r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
356 if (r < 0)
357 goto fail;
358
359 r = sd_bus_message_read_basic(reply, type, ptr);
360 if (r < 0)
361 goto fail;
362
363 return 0;
364
365 fail:
366 return sd_bus_error_set_errno(error, r);
367 }
368
369 _public_ int sd_bus_get_property_string(
370 sd_bus *bus,
371 const char *destination,
372 const char *path,
373 const char *interface,
374 const char *member,
375 sd_bus_error *error,
376 char **ret) {
377
378 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
379 const char *s;
380 char *n;
381 int r;
382
383 bus_assert_return(bus, -EINVAL, error);
384 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
385 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
386 bus_assert_return(ret, -EINVAL, error);
387 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
388
389 if (!BUS_IS_OPEN(bus->state)) {
390 r = -ENOTCONN;
391 goto fail;
392 }
393
394 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
395 if (r < 0)
396 return r;
397
398 r = sd_bus_message_enter_container(reply, 'v', "s");
399 if (r < 0)
400 goto fail;
401
402 r = sd_bus_message_read_basic(reply, 's', &s);
403 if (r < 0)
404 goto fail;
405
406 n = strdup(s);
407 if (!n) {
408 r = -ENOMEM;
409 goto fail;
410 }
411
412 *ret = n;
413 return 0;
414
415 fail:
416 return sd_bus_error_set_errno(error, r);
417 }
418
419 _public_ int sd_bus_get_property_strv(
420 sd_bus *bus,
421 const char *destination,
422 const char *path,
423 const char *interface,
424 const char *member,
425 sd_bus_error *error,
426 char ***ret) {
427
428 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
429 int r;
430
431 bus_assert_return(bus, -EINVAL, error);
432 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
433 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
434 bus_assert_return(ret, -EINVAL, error);
435 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
436
437 if (!BUS_IS_OPEN(bus->state)) {
438 r = -ENOTCONN;
439 goto fail;
440 }
441
442 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
443 if (r < 0)
444 return r;
445
446 r = sd_bus_message_enter_container(reply, 'v', NULL);
447 if (r < 0)
448 goto fail;
449
450 r = sd_bus_message_read_strv(reply, ret);
451 if (r < 0)
452 goto fail;
453
454 return 0;
455
456 fail:
457 return sd_bus_error_set_errno(error, r);
458 }
459
460 _public_ int sd_bus_set_property(
461 sd_bus *bus,
462 const char *destination,
463 const char *path,
464 const char *interface,
465 const char *member,
466 sd_bus_error *error,
467 const char *type, ...) {
468
469 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
470 va_list ap;
471 int r;
472
473 bus_assert_return(bus, -EINVAL, error);
474 bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
475 bus_assert_return(member_name_is_valid(member), -EINVAL, error);
476 bus_assert_return(signature_is_single(type, false), -EINVAL, error);
477 bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
478
479 if (!BUS_IS_OPEN(bus->state)) {
480 r = -ENOTCONN;
481 goto fail;
482 }
483
484 r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
485 if (r < 0)
486 goto fail;
487
488 r = sd_bus_message_append(m, "ss", strempty(interface), member);
489 if (r < 0)
490 goto fail;
491
492 r = sd_bus_message_open_container(m, 'v', type);
493 if (r < 0)
494 goto fail;
495
496 va_start(ap, type);
497 r = sd_bus_message_appendv(m, type, ap);
498 va_end(ap);
499 if (r < 0)
500 goto fail;
501
502 r = sd_bus_message_close_container(m);
503 if (r < 0)
504 goto fail;
505
506 return sd_bus_call(bus, m, 0, error, NULL);
507
508 fail:
509 return sd_bus_error_set_errno(error, r);
510 }
511
512 _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
513 sd_bus_creds *c;
514
515 assert_return(call, -EINVAL);
516 assert_return(call->sealed, -EPERM);
517 assert_return(call->bus, -EINVAL);
518 assert_return(!bus_pid_changed(call->bus), -ECHILD);
519
520 if (!BUS_IS_OPEN(call->bus->state))
521 return -ENOTCONN;
522
523 c = sd_bus_message_get_creds(call);
524
525 /* All data we need? */
526 if (c && (mask & ~c->mask) == 0) {
527 *creds = sd_bus_creds_ref(c);
528 return 0;
529 }
530
531 /* No data passed? Or not enough data passed to retrieve the missing bits? */
532 if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
533 /* We couldn't read anything from the call, let's try
534 * to get it from the sender or peer. */
535
536 if (call->sender)
537 /* There's a sender, but the creds are missing. */
538 return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
539 else
540 /* There's no sender. For direct connections
541 * the credentials of the AF_UNIX peer matter,
542 * which may be queried via sd_bus_get_owner_creds(). */
543 return sd_bus_get_owner_creds(call->bus, mask, creds);
544 }
545
546 return bus_creds_extend_by_pid(c, mask, creds);
547 }
548
549 _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
550 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
551 uid_t our_uid;
552 bool know_caps = false;
553 int r;
554
555 assert_return(call, -EINVAL);
556 assert_return(call->sealed, -EPERM);
557 assert_return(call->bus, -EINVAL);
558 assert_return(!bus_pid_changed(call->bus), -ECHILD);
559
560 if (!BUS_IS_OPEN(call->bus->state))
561 return -ENOTCONN;
562
563 if (capability >= 0) {
564
565 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
566 if (r < 0)
567 return r;
568
569 /* We cannot use augmented caps for authorization,
570 * since then data is acquired raceful from
571 * /proc. This can never actually happen, but let's
572 * better be safe than sorry, and do an extra check
573 * here. */
574 assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EFFECTIVE_CAPS) == 0, -EPERM);
575
576 r = sd_bus_creds_has_effective_cap(creds, capability);
577 if (r > 0)
578 return 1;
579 if (r == 0)
580 know_caps = true;
581 } else {
582 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
583 if (r < 0)
584 return r;
585 }
586
587 /* Now, check the UID, but only if the capability check wasn't
588 * sufficient */
589 our_uid = getuid();
590 if (our_uid != 0 || !know_caps || capability < 0) {
591 uid_t sender_uid;
592
593 /* We cannot use augmented uid/euid for authorization,
594 * since then data is acquired raceful from
595 * /proc. This can never actually happen, but let's
596 * better be safe than sorry, and do an extra check
597 * here. */
598 assert_return((sd_bus_creds_get_augmented_mask(creds) & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID)) == 0, -EPERM);
599
600 /* Try to use the EUID, if we have it. */
601 r = sd_bus_creds_get_euid(creds, &sender_uid);
602 if (r < 0)
603 r = sd_bus_creds_get_uid(creds, &sender_uid);
604
605 if (r >= 0) {
606 /* Sender has same UID as us, then let's grant access */
607 if (sender_uid == our_uid)
608 return 1;
609
610 /* Sender is root, we are not root. */
611 if (our_uid != 0 && sender_uid == 0)
612 return 1;
613 }
614 }
615
616 return 0;
617 }