]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-convenience.c
tmpfiles: accurately report creation results
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-convenience.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 "bus-internal.h"
23 #include "bus-message.h"
24 #include "bus-signature.h"
25 #include "bus-util.h"
26 #include "bus-type.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_bus_message_unref_ 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 = bus_message_append_ap(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(
62 sd_bus *bus,
63 const char *destination,
64 const char *path,
65 const char *interface,
66 const char *member,
67 sd_bus_error *error,
68 sd_bus_message **reply,
69 const char *types, ...) {
70
71 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
72 int r;
73
74 assert_return(bus, -EINVAL);
75 assert_return(!bus_pid_changed(bus), -ECHILD);
76
77 if (!BUS_IS_OPEN(bus->state))
78 return -ENOTCONN;
79
80 r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
81 if (r < 0)
82 return r;
83
84 if (!isempty(types)) {
85 va_list ap;
86
87 va_start(ap, types);
88 r = bus_message_append_ap(m, types, ap);
89 va_end(ap);
90 if (r < 0)
91 return r;
92 }
93
94 return sd_bus_call(bus, m, 0, error, reply);
95 }
96
97 _public_ int sd_bus_reply_method_return(
98 sd_bus_message *call,
99 const char *types, ...) {
100
101 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
102 int r;
103
104 assert_return(call, -EINVAL);
105 assert_return(call->sealed, -EPERM);
106 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
107 assert_return(call->bus, -EINVAL);
108 assert_return(!bus_pid_changed(call->bus), -ECHILD);
109
110 if (!BUS_IS_OPEN(call->bus->state))
111 return -ENOTCONN;
112
113 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
114 return 0;
115
116 r = sd_bus_message_new_method_return(call, &m);
117 if (r < 0)
118 return r;
119
120 if (!isempty(types)) {
121 va_list ap;
122
123 va_start(ap, types);
124 r = bus_message_append_ap(m, types, ap);
125 va_end(ap);
126 if (r < 0)
127 return r;
128 }
129
130 return sd_bus_send(call->bus, m, NULL);
131 }
132
133 _public_ int sd_bus_reply_method_error(
134 sd_bus_message *call,
135 const sd_bus_error *e) {
136
137 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
138 int r;
139
140 assert_return(call, -EINVAL);
141 assert_return(call->sealed, -EPERM);
142 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
143 assert_return(sd_bus_error_is_set(e), -EINVAL);
144 assert_return(call->bus, -EINVAL);
145 assert_return(!bus_pid_changed(call->bus), -ECHILD);
146
147 if (!BUS_IS_OPEN(call->bus->state))
148 return -ENOTCONN;
149
150 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
151 return 0;
152
153 r = sd_bus_message_new_method_error(call, &m, e);
154 if (r < 0)
155 return r;
156
157 return sd_bus_send(call->bus, m, NULL);
158 }
159
160 _public_ int sd_bus_reply_method_errorf(
161 sd_bus_message *call,
162 const char *name,
163 const char *format,
164 ...) {
165
166 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
167 va_list ap;
168
169 assert_return(call, -EINVAL);
170 assert_return(call->sealed, -EPERM);
171 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
172 assert_return(call->bus, -EINVAL);
173 assert_return(!bus_pid_changed(call->bus), -ECHILD);
174
175 if (!BUS_IS_OPEN(call->bus->state))
176 return -ENOTCONN;
177
178 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
179 return 0;
180
181 va_start(ap, format);
182 bus_error_setfv(&error, name, format, ap);
183 va_end(ap);
184
185 return sd_bus_reply_method_error(call, &error);
186 }
187
188 _public_ int sd_bus_reply_method_errno(
189 sd_bus_message *call,
190 int error,
191 const sd_bus_error *p) {
192
193 _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
194
195 assert_return(call, -EINVAL);
196 assert_return(call->sealed, -EPERM);
197 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
198 assert_return(call->bus, -EINVAL);
199 assert_return(!bus_pid_changed(call->bus), -ECHILD);
200
201 if (!BUS_IS_OPEN(call->bus->state))
202 return -ENOTCONN;
203
204 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
205 return 0;
206
207 if (sd_bus_error_is_set(p))
208 return sd_bus_reply_method_error(call, p);
209
210 sd_bus_error_set_errno(&berror, error);
211
212 return sd_bus_reply_method_error(call, &berror);
213 }
214
215 _public_ int sd_bus_reply_method_errnof(
216 sd_bus_message *call,
217 int error,
218 const char *format,
219 ...) {
220
221 _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
222 va_list ap;
223
224 assert_return(call, -EINVAL);
225 assert_return(call->sealed, -EPERM);
226 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
227 assert_return(call->bus, -EINVAL);
228 assert_return(!bus_pid_changed(call->bus), -ECHILD);
229
230 if (!BUS_IS_OPEN(call->bus->state))
231 return -ENOTCONN;
232
233 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
234 return 0;
235
236 va_start(ap, format);
237 bus_error_set_errnofv(&berror, error, format, ap);
238 va_end(ap);
239
240 return sd_bus_reply_method_error(call, &berror);
241 }
242
243 _public_ int sd_bus_get_property(
244 sd_bus *bus,
245 const char *destination,
246 const char *path,
247 const char *interface,
248 const char *member,
249 sd_bus_error *error,
250 sd_bus_message **reply,
251 const char *type) {
252
253 sd_bus_message *rep = NULL;
254 int r;
255
256 assert_return(bus, -EINVAL);
257 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
258 assert_return(member_name_is_valid(member), -EINVAL);
259 assert_return(reply, -EINVAL);
260 assert_return(signature_is_single(type, false), -EINVAL);
261 assert_return(!bus_pid_changed(bus), -ECHILD);
262
263 if (!BUS_IS_OPEN(bus->state))
264 return -ENOTCONN;
265
266 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
267 if (r < 0)
268 return r;
269
270 r = sd_bus_message_enter_container(rep, 'v', type);
271 if (r < 0) {
272 sd_bus_message_unref(rep);
273 return r;
274 }
275
276 *reply = rep;
277 return 0;
278 }
279
280 _public_ int sd_bus_get_property_trivial(
281 sd_bus *bus,
282 const char *destination,
283 const char *path,
284 const char *interface,
285 const char *member,
286 sd_bus_error *error,
287 char type, void *ptr) {
288
289 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
290 int r;
291
292 assert_return(bus, -EINVAL);
293 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
294 assert_return(member_name_is_valid(member), -EINVAL);
295 assert_return(bus_type_is_trivial(type), -EINVAL);
296 assert_return(ptr, -EINVAL);
297 assert_return(!bus_pid_changed(bus), -ECHILD);
298
299 if (!BUS_IS_OPEN(bus->state))
300 return -ENOTCONN;
301
302 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
303 if (r < 0)
304 return r;
305
306 r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
307 if (r < 0)
308 return r;
309
310 r = sd_bus_message_read_basic(reply, type, ptr);
311 if (r < 0)
312 return r;
313
314 return 0;
315 }
316
317 _public_ int sd_bus_get_property_string(
318 sd_bus *bus,
319 const char *destination,
320 const char *path,
321 const char *interface,
322 const char *member,
323 sd_bus_error *error,
324 char **ret) {
325
326 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
327 const char *s;
328 char *n;
329 int r;
330
331 assert_return(bus, -EINVAL);
332 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
333 assert_return(member_name_is_valid(member), -EINVAL);
334 assert_return(ret, -EINVAL);
335 assert_return(!bus_pid_changed(bus), -ECHILD);
336
337 if (!BUS_IS_OPEN(bus->state))
338 return -ENOTCONN;
339
340 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
341 if (r < 0)
342 return r;
343
344 r = sd_bus_message_enter_container(reply, 'v', "s");
345 if (r < 0)
346 return r;
347
348 r = sd_bus_message_read_basic(reply, 's', &s);
349 if (r < 0)
350 return r;
351
352 n = strdup(s);
353 if (!n)
354 return -ENOMEM;
355
356 *ret = n;
357 return 0;
358 }
359
360 _public_ int sd_bus_get_property_strv(
361 sd_bus *bus,
362 const char *destination,
363 const char *path,
364 const char *interface,
365 const char *member,
366 sd_bus_error *error,
367 char ***ret) {
368
369 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
370 int r;
371
372 assert_return(bus, -EINVAL);
373 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
374 assert_return(member_name_is_valid(member), -EINVAL);
375 assert_return(ret, -EINVAL);
376 assert_return(!bus_pid_changed(bus), -ECHILD);
377
378 if (!BUS_IS_OPEN(bus->state))
379 return -ENOTCONN;
380
381 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
382 if (r < 0)
383 return r;
384
385 r = sd_bus_message_enter_container(reply, 'v', NULL);
386 if (r < 0)
387 return r;
388
389 r = sd_bus_message_read_strv(reply, ret);
390 if (r < 0)
391 return r;
392
393 return 0;
394 }
395
396 _public_ int sd_bus_set_property(
397 sd_bus *bus,
398 const char *destination,
399 const char *path,
400 const char *interface,
401 const char *member,
402 sd_bus_error *error,
403 const char *type, ...) {
404
405 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
406 va_list ap;
407 int r;
408
409 assert_return(bus, -EINVAL);
410 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
411 assert_return(member_name_is_valid(member), -EINVAL);
412 assert_return(signature_is_single(type, false), -EINVAL);
413 assert_return(!bus_pid_changed(bus), -ECHILD);
414
415 if (!BUS_IS_OPEN(bus->state))
416 return -ENOTCONN;
417
418 r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
419 if (r < 0)
420 return r;
421
422 r = sd_bus_message_append(m, "ss", strempty(interface), member);
423 if (r < 0)
424 return r;
425
426 r = sd_bus_message_open_container(m, 'v', type);
427 if (r < 0)
428 return r;
429
430 va_start(ap, type);
431 r = bus_message_append_ap(m, type, ap);
432 va_end(ap);
433 if (r < 0)
434 return r;
435
436 r = sd_bus_message_close_container(m);
437 if (r < 0)
438 return r;
439
440 return sd_bus_call(bus, m, 0, error, NULL);
441 }
442
443 _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
444 sd_bus_creds *c;
445
446 assert_return(call, -EINVAL);
447 assert_return(call->sealed, -EPERM);
448 assert_return(call->bus, -EINVAL);
449 assert_return(!bus_pid_changed(call->bus), -ECHILD);
450
451 if (!BUS_IS_OPEN(call->bus->state))
452 return -ENOTCONN;
453
454 c = sd_bus_message_get_creds(call);
455
456 /* All data we need? */
457 if (c && (mask & ~c->mask) == 0) {
458 *creds = sd_bus_creds_ref(c);
459 return 0;
460 }
461
462 /* No data passed? Or not enough data passed to retrieve the missing bits? */
463 if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
464 /* We couldn't read anything from the call, let's try
465 * to get it from the sender or peer */
466
467 if (call->sender)
468 return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
469 else
470 return sd_bus_get_owner_creds(call->bus, mask, creds);
471 }
472
473 return bus_creds_extend_by_pid(c, mask, creds);
474 }
475
476 _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
477 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
478 uid_t our_uid;
479 bool know_caps = false;
480 int r;
481
482 assert_return(call, -EINVAL);
483 assert_return(call->sealed, -EPERM);
484 assert_return(call->bus, -EINVAL);
485 assert_return(!bus_pid_changed(call->bus), -ECHILD);
486
487 if (!BUS_IS_OPEN(call->bus->state))
488 return -ENOTCONN;
489
490 if (capability >= 0) {
491 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
492 if (r < 0)
493 return r;
494
495 /* Note that not even on kdbus we might have the caps
496 * field, due to faked identities, or namespace
497 * translation issues. */
498 r = sd_bus_creds_has_effective_cap(creds, capability);
499 if (r > 0)
500 return 1;
501 if (r == 0)
502 know_caps = true;
503 } else {
504 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
505 if (r < 0)
506 return r;
507 }
508
509 /* Now, check the UID, but only if the capability check wasn't
510 * sufficient */
511 our_uid = getuid();
512 if (our_uid != 0 || !know_caps || capability < 0) {
513 uid_t sender_uid;
514
515 /* Try to use the EUID, if we have it. */
516 r = sd_bus_creds_get_euid(creds, &sender_uid);
517 if (r < 0)
518 r = sd_bus_creds_get_uid(creds, &sender_uid);
519
520 if (r >= 0) {
521 /* Sender has same UID as us, then let's grant access */
522 if (sender_uid == our_uid)
523 return 1;
524
525 /* Sender is root, we are not root. */
526 if (our_uid != 0 && sender_uid == 0)
527 return 1;
528 }
529 }
530
531 return 0;
532 }