1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
22 #include <sys/socket.h>
25 #include "sd-daemon.h"
28 #include "alloc-util.h"
29 #include "bus-error.h"
30 #include "bus-internal.h"
31 #include "bus-label.h"
32 #include "bus-message.h"
34 #include "cgroup-util.h"
41 #include "parse-util.h"
42 #include "path-util.h"
43 #include "proc-cmdline.h"
44 #include "rlimit-util.h"
46 #include "signal-util.h"
47 #include "stdio-util.h"
48 #include "string-util.h"
50 #include "syslog-util.h"
51 #include "unit-name.h"
52 #include "user-util.h"
56 static int name_owner_change_callback(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
57 sd_event
*e
= userdata
;
62 sd_bus_close(sd_bus_message_get_bus(m
));
68 int bus_async_unregister_and_exit(sd_event
*e
, sd_bus
*bus
, const char *name
) {
69 _cleanup_free_
char *match
= NULL
;
77 /* We unregister the name here and then wait for the
78 * NameOwnerChanged signal for this event to arrive before we
79 * quit. We do this in order to make sure that any queued
80 * requests are still processed before we really exit. */
82 r
= sd_bus_get_unique_name(bus
, &unique
);
87 "sender='org.freedesktop.DBus',"
89 "interface='org.freedesktop.DBus',"
90 "member='NameOwnerChanged',"
91 "path='/org/freedesktop/DBus',"
94 "arg2=''", name
, unique
);
98 r
= sd_bus_add_match(bus
, NULL
, match
, name_owner_change_callback
, e
);
102 r
= sd_bus_release_name(bus
, name
);
109 int bus_event_loop_with_idle(
114 check_idle_t check_idle
,
116 bool exiting
= false;
126 r
= sd_event_get_state(e
);
129 if (r
== SD_EVENT_FINISHED
)
133 idle
= check_idle(userdata
);
137 r
= sd_event_run(e
, exiting
|| !idle
? (uint64_t) -1 : timeout
);
141 if (r
== 0 && !exiting
&& idle
) {
143 r
= sd_bus_try_close(bus
);
147 /* Fallback for dbus1 connections: we
148 * unregister the name and wait for the
149 * response to come through for it */
150 if (r
== -EOPNOTSUPP
) {
152 /* Inform the service manager that we
153 * are going down, so that it will
154 * queue all further start requests,
155 * instead of assuming we are already
157 sd_notify(false, "STOPPING=1");
159 r
= bus_async_unregister_and_exit(e
, bus
, name
);
175 r
= sd_event_get_exit_code(e
, &code
);
182 int bus_name_has_owner(sd_bus
*c
, const char *name
, sd_bus_error
*error
) {
183 _cleanup_bus_message_unref_ sd_bus_message
*rep
= NULL
;
184 int r
, has_owner
= 0;
189 r
= sd_bus_call_method(c
,
190 "org.freedesktop.DBus",
191 "/org/freedesktop/dbus",
192 "org.freedesktop.DBus",
201 r
= sd_bus_message_read_basic(rep
, 'b', &has_owner
);
203 return sd_bus_error_set_errno(error
, r
);
208 static int check_good_user(sd_bus_message
*m
, uid_t good_user
) {
209 _cleanup_bus_creds_unref_ sd_bus_creds
*creds
= NULL
;
215 if (good_user
== UID_INVALID
)
218 r
= sd_bus_query_sender_creds(m
, SD_BUS_CREDS_EUID
, &creds
);
222 /* Don't trust augmented credentials for authorization */
223 assert_return((sd_bus_creds_get_augmented_mask(creds
) & SD_BUS_CREDS_EUID
) == 0, -EPERM
);
225 r
= sd_bus_creds_get_euid(creds
, &sender_uid
);
229 return sender_uid
== good_user
;
233 sd_bus_message
*call
,
236 const char **details
,
246 /* Tests non-interactively! */
248 r
= check_good_user(call
, good_user
);
252 r
= sd_bus_query_sender_privilege(call
, capability
);
259 _cleanup_bus_message_unref_ sd_bus_message
*request
= NULL
;
260 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
261 int authorized
= false, challenge
= false;
262 const char *sender
, **k
, **v
;
264 sender
= sd_bus_message_get_sender(call
);
268 r
= sd_bus_message_new_method_call(
271 "org.freedesktop.PolicyKit1",
272 "/org/freedesktop/PolicyKit1/Authority",
273 "org.freedesktop.PolicyKit1.Authority",
274 "CheckAuthorization");
278 r
= sd_bus_message_append(
281 "system-bus-name", 1, "name", "s", sender
,
286 r
= sd_bus_message_open_container(request
, 'a', "{ss}");
290 STRV_FOREACH_PAIR(k
, v
, details
) {
291 r
= sd_bus_message_append(request
, "{ss}", *k
, *v
);
296 r
= sd_bus_message_close_container(request
);
300 r
= sd_bus_message_append(request
, "us", 0, NULL
);
304 r
= sd_bus_call(call
->bus
, request
, 0, e
, &reply
);
306 /* Treat no PK available as access denied */
307 if (sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
)) {
308 sd_bus_error_free(e
);
315 r
= sd_bus_message_enter_container(reply
, 'r', "bba{ss}");
319 r
= sd_bus_message_read(reply
, "bb", &authorized
, &challenge
);
327 *_challenge
= challenge
;
338 typedef struct AsyncPolkitQuery
{
339 sd_bus_message
*request
, *reply
;
340 sd_bus_message_handler_t callback
;
346 static void async_polkit_query_free(AsyncPolkitQuery
*q
) {
351 sd_bus_slot_unref(q
->slot
);
353 if (q
->registry
&& q
->request
)
354 hashmap_remove(q
->registry
, q
->request
);
356 sd_bus_message_unref(q
->request
);
357 sd_bus_message_unref(q
->reply
);
362 static int async_polkit_callback(sd_bus_message
*reply
, void *userdata
, sd_bus_error
*error
) {
363 _cleanup_bus_error_free_ sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
364 AsyncPolkitQuery
*q
= userdata
;
370 q
->slot
= sd_bus_slot_unref(q
->slot
);
371 q
->reply
= sd_bus_message_ref(reply
);
373 r
= sd_bus_message_rewind(q
->request
, true);
375 r
= sd_bus_reply_method_errno(q
->request
, r
, NULL
);
379 r
= q
->callback(q
->request
, q
->userdata
, &error_buffer
);
380 r
= bus_maybe_reply_error(q
->request
, r
, &error_buffer
);
383 async_polkit_query_free(q
);
390 int bus_verify_polkit_async(
391 sd_bus_message
*call
,
394 const char **details
,
398 sd_bus_error
*error
) {
401 _cleanup_bus_message_unref_ sd_bus_message
*pk
= NULL
;
403 const char *sender
, **k
, **v
;
404 sd_bus_message_handler_t callback
;
414 r
= check_good_user(call
, good_user
);
419 q
= hashmap_get(*registry
, call
);
421 int authorized
, challenge
;
423 /* This is the second invocation of this function, and
424 * there's already a response from polkit, let's
428 if (sd_bus_message_is_method_error(q
->reply
, NULL
)) {
429 const sd_bus_error
*e
;
431 /* Copy error from polkit reply */
432 e
= sd_bus_message_get_error(q
->reply
);
433 sd_bus_error_copy(error
, e
);
435 /* Treat no PK available as access denied */
436 if (sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
))
439 return -sd_bus_error_get_errno(e
);
442 r
= sd_bus_message_enter_container(q
->reply
, 'r', "bba{ss}");
444 r
= sd_bus_message_read(q
->reply
, "bb", &authorized
, &challenge
);
453 return sd_bus_error_set(error
, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED
, "Interactive authentication required.");
459 r
= sd_bus_query_sender_privilege(call
, capability
);
466 if (sd_bus_get_current_message(call
->bus
) != call
)
469 callback
= sd_bus_get_current_handler(call
->bus
);
473 userdata
= sd_bus_get_current_userdata(call
->bus
);
475 sender
= sd_bus_message_get_sender(call
);
479 c
= sd_bus_message_get_allow_interactive_authorization(call
);
485 r
= hashmap_ensure_allocated(registry
, NULL
);
489 r
= sd_bus_message_new_method_call(
492 "org.freedesktop.PolicyKit1",
493 "/org/freedesktop/PolicyKit1/Authority",
494 "org.freedesktop.PolicyKit1.Authority",
495 "CheckAuthorization");
499 r
= sd_bus_message_append(
502 "system-bus-name", 1, "name", "s", sender
,
507 r
= sd_bus_message_open_container(pk
, 'a', "{ss}");
511 STRV_FOREACH_PAIR(k
, v
, details
) {
512 r
= sd_bus_message_append(pk
, "{ss}", *k
, *v
);
517 r
= sd_bus_message_close_container(pk
);
521 r
= sd_bus_message_append(pk
, "us", !!interactive
, NULL
);
525 q
= new0(AsyncPolkitQuery
, 1);
529 q
->request
= sd_bus_message_ref(call
);
530 q
->callback
= callback
;
531 q
->userdata
= userdata
;
533 r
= hashmap_put(*registry
, call
, q
);
535 async_polkit_query_free(q
);
539 q
->registry
= *registry
;
541 r
= sd_bus_call_async(call
->bus
, &q
->slot
, pk
, async_polkit_callback
, q
, 0);
543 async_polkit_query_free(q
);
553 void bus_verify_polkit_async_registry_free(Hashmap
*registry
) {
557 while ((q
= hashmap_steal_first(registry
)))
558 async_polkit_query_free(q
);
560 hashmap_free(registry
);
564 int bus_check_peercred(sd_bus
*c
) {
571 fd
= sd_bus_get_fd(c
);
575 l
= sizeof(struct ucred
);
576 if (getsockopt(fd
, SOL_SOCKET
, SO_PEERCRED
, &ucred
, &l
) < 0)
579 if (l
!= sizeof(struct ucred
))
582 if (ucred
.uid
!= 0 && ucred
.uid
!= geteuid())
588 int bus_connect_system_systemd(sd_bus
**_bus
) {
589 _cleanup_bus_unref_ sd_bus
*bus
= NULL
;
595 return sd_bus_default_system(_bus
);
597 /* If we are root and kdbus is not available, then let's talk
598 * directly to the system instance, instead of going via the
601 r
= sd_bus_new(&bus
);
605 r
= sd_bus_set_address(bus
, KERNEL_SYSTEM_BUS_ADDRESS
);
609 bus
->bus_client
= true;
611 r
= sd_bus_start(bus
);
618 bus
= sd_bus_unref(bus
);
620 r
= sd_bus_new(&bus
);
624 r
= sd_bus_set_address(bus
, "unix:path=/run/systemd/private");
628 r
= sd_bus_start(bus
);
630 return sd_bus_default_system(_bus
);
632 r
= bus_check_peercred(bus
);
642 int bus_connect_user_systemd(sd_bus
**_bus
) {
643 _cleanup_bus_unref_ sd_bus
*bus
= NULL
;
644 _cleanup_free_
char *ee
= NULL
;
648 /* Try via kdbus first, and then directly */
652 r
= sd_bus_new(&bus
);
656 if (asprintf(&bus
->address
, KERNEL_USER_BUS_ADDRESS_FMT
, getuid()) < 0)
659 bus
->bus_client
= true;
661 r
= sd_bus_start(bus
);
668 bus
= sd_bus_unref(bus
);
670 e
= secure_getenv("XDG_RUNTIME_DIR");
672 return sd_bus_default_user(_bus
);
674 ee
= bus_address_escape(e
);
678 r
= sd_bus_new(&bus
);
682 bus
->address
= strjoin("unix:path=", ee
, "/systemd/private", NULL
);
686 r
= sd_bus_start(bus
);
688 return sd_bus_default_user(_bus
);
690 r
= bus_check_peercred(bus
);
700 int bus_print_property(const char *name
, sd_bus_message
*property
, bool all
) {
702 const char *contents
;
708 r
= sd_bus_message_peek_type(property
, &type
, &contents
);
714 case SD_BUS_TYPE_STRING
: {
717 r
= sd_bus_message_read_basic(property
, type
, &s
);
721 if (all
|| !isempty(s
)) {
722 _cleanup_free_
char *escaped
= NULL
;
724 escaped
= xescape(s
, "\n");
728 printf("%s=%s\n", name
, escaped
);
734 case SD_BUS_TYPE_BOOLEAN
: {
737 r
= sd_bus_message_read_basic(property
, type
, &b
);
741 printf("%s=%s\n", name
, yes_no(b
));
746 case SD_BUS_TYPE_UINT64
: {
749 r
= sd_bus_message_read_basic(property
, type
, &u
);
753 /* Yes, heuristics! But we can change this check
754 * should it turn out to not be sufficient */
756 if (endswith(name
, "Timestamp")) {
757 char timestamp
[FORMAT_TIMESTAMP_MAX
], *t
;
759 t
= format_timestamp(timestamp
, sizeof(timestamp
), u
);
761 printf("%s=%s\n", name
, strempty(t
));
763 } else if (strstr(name
, "USec")) {
764 char timespan
[FORMAT_TIMESPAN_MAX
];
766 printf("%s=%s\n", name
, format_timespan(timespan
, sizeof(timespan
), u
, 0));
768 printf("%s=%llu\n", name
, (unsigned long long) u
);
773 case SD_BUS_TYPE_INT64
: {
776 r
= sd_bus_message_read_basic(property
, type
, &i
);
780 printf("%s=%lld\n", name
, (long long) i
);
785 case SD_BUS_TYPE_UINT32
: {
788 r
= sd_bus_message_read_basic(property
, type
, &u
);
792 if (strstr(name
, "UMask") || strstr(name
, "Mode"))
793 printf("%s=%04o\n", name
, u
);
795 printf("%s=%u\n", name
, (unsigned) u
);
800 case SD_BUS_TYPE_INT32
: {
803 r
= sd_bus_message_read_basic(property
, type
, &i
);
807 printf("%s=%i\n", name
, (int) i
);
811 case SD_BUS_TYPE_DOUBLE
: {
814 r
= sd_bus_message_read_basic(property
, type
, &d
);
818 printf("%s=%g\n", name
, d
);
822 case SD_BUS_TYPE_ARRAY
:
823 if (streq(contents
, "s")) {
827 r
= sd_bus_message_enter_container(property
, SD_BUS_TYPE_ARRAY
, contents
);
831 while((r
= sd_bus_message_read_basic(property
, SD_BUS_TYPE_STRING
, &str
)) > 0) {
832 _cleanup_free_
char *escaped
= NULL
;
837 escaped
= xescape(str
, "\n ");
841 printf("%s%s", first
? "" : " ", escaped
);
853 r
= sd_bus_message_exit_container(property
);
859 } else if (streq(contents
, "y")) {
863 r
= sd_bus_message_read_array(property
, SD_BUS_TYPE_BYTE
, (const void**) &u
, &n
);
872 for (i
= 0; i
< n
; i
++)
873 printf("%02x", u
[i
]);
880 } else if (streq(contents
, "u")) {
884 r
= sd_bus_message_read_array(property
, SD_BUS_TYPE_UINT32
, (const void**) &u
, &n
);
893 for (i
= 0; i
< n
; i
++)
894 printf("%08x", u
[i
]);
908 int bus_print_all_properties(sd_bus
*bus
, const char *dest
, const char *path
, char **filter
, bool all
) {
909 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
910 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
916 r
= sd_bus_call_method(bus
,
919 "org.freedesktop.DBus.Properties",
927 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "{sv}");
931 while ((r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_DICT_ENTRY
, "sv")) > 0) {
933 const char *contents
;
935 r
= sd_bus_message_read_basic(reply
, SD_BUS_TYPE_STRING
, &name
);
939 if (!filter
|| strv_find(filter
, name
)) {
940 r
= sd_bus_message_peek_type(reply
, NULL
, &contents
);
944 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_VARIANT
, contents
);
948 r
= bus_print_property(name
, reply
, all
);
953 printf("%s=[unprintable]\n", name
);
954 /* skip what we didn't read */
955 r
= sd_bus_message_skip(reply
, contents
);
960 r
= sd_bus_message_exit_container(reply
);
964 r
= sd_bus_message_skip(reply
, "v");
969 r
= sd_bus_message_exit_container(reply
);
976 r
= sd_bus_message_exit_container(reply
);
983 int bus_map_id128(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
984 sd_id128_t
*p
= userdata
;
989 r
= sd_bus_message_read_array(m
, SD_BUS_TYPE_BYTE
, &v
, &n
);
996 memcpy((*p
).bytes
, v
, n
);
1003 static int map_basic(sd_bus
*bus
, const char *member
, sd_bus_message
*m
, sd_bus_error
*error
, void *userdata
) {
1007 r
= sd_bus_message_peek_type(m
, &type
, NULL
);
1012 case SD_BUS_TYPE_STRING
: {
1014 char **p
= userdata
;
1016 r
= sd_bus_message_read_basic(m
, type
, &s
);
1023 r
= free_and_strdup(p
, s
);
1027 case SD_BUS_TYPE_ARRAY
: {
1028 _cleanup_strv_free_
char **l
= NULL
;
1029 char ***p
= userdata
;
1031 r
= bus_message_read_strv_extend(m
, &l
);
1042 case SD_BUS_TYPE_BOOLEAN
: {
1046 r
= sd_bus_message_read_basic(m
, type
, &b
);
1055 case SD_BUS_TYPE_UINT32
: {
1057 uint32_t *p
= userdata
;
1059 r
= sd_bus_message_read_basic(m
, type
, &u
);
1068 case SD_BUS_TYPE_UINT64
: {
1070 uint64_t *p
= userdata
;
1072 r
= sd_bus_message_read_basic(m
, type
, &t
);
1088 int bus_message_map_all_properties(
1090 const struct bus_properties_map
*map
,
1093 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1099 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "{sv}");
1103 while ((r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_DICT_ENTRY
, "sv")) > 0) {
1104 const struct bus_properties_map
*prop
;
1106 const char *contents
;
1110 r
= sd_bus_message_read_basic(m
, SD_BUS_TYPE_STRING
, &member
);
1114 for (i
= 0, prop
= NULL
; map
[i
].member
; i
++)
1115 if (streq(map
[i
].member
, member
)) {
1121 r
= sd_bus_message_peek_type(m
, NULL
, &contents
);
1125 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_VARIANT
, contents
);
1129 v
= (uint8_t *)userdata
+ prop
->offset
;
1131 r
= prop
->set(sd_bus_message_get_bus(m
), member
, m
, &error
, v
);
1133 r
= map_basic(sd_bus_message_get_bus(m
), member
, m
, &error
, v
);
1137 r
= sd_bus_message_exit_container(m
);
1141 r
= sd_bus_message_skip(m
, "v");
1146 r
= sd_bus_message_exit_container(m
);
1153 return sd_bus_message_exit_container(m
);
1156 int bus_message_map_properties_changed(
1158 const struct bus_properties_map
*map
,
1162 int r
, invalidated
, i
;
1167 r
= bus_message_map_all_properties(m
, map
, userdata
);
1171 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "s");
1176 while ((r
= sd_bus_message_read_basic(m
, SD_BUS_TYPE_STRING
, &member
)) > 0)
1177 for (i
= 0; map
[i
].member
; i
++)
1178 if (streq(map
[i
].member
, member
)) {
1185 r
= sd_bus_message_exit_container(m
);
1192 int bus_map_all_properties(
1194 const char *destination
,
1196 const struct bus_properties_map
*map
,
1199 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1200 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1204 assert(destination
);
1208 r
= sd_bus_call_method(
1212 "org.freedesktop.DBus.Properties",
1220 return bus_message_map_all_properties(m
, map
, userdata
);
1223 int bus_connect_transport(BusTransport transport
, const char *host
, bool user
, sd_bus
**bus
) {
1226 assert(transport
>= 0);
1227 assert(transport
< _BUS_TRANSPORT_MAX
);
1230 assert_return((transport
== BUS_TRANSPORT_LOCAL
) == !host
, -EINVAL
);
1231 assert_return(transport
== BUS_TRANSPORT_LOCAL
|| !user
, -EOPNOTSUPP
);
1233 switch (transport
) {
1235 case BUS_TRANSPORT_LOCAL
:
1237 r
= sd_bus_default_user(bus
);
1239 r
= sd_bus_default_system(bus
);
1243 case BUS_TRANSPORT_REMOTE
:
1244 r
= sd_bus_open_system_remote(bus
, host
);
1247 case BUS_TRANSPORT_MACHINE
:
1248 r
= sd_bus_open_system_machine(bus
, host
);
1252 assert_not_reached("Hmm, unknown transport type.");
1258 int bus_connect_transport_systemd(BusTransport transport
, const char *host
, bool user
, sd_bus
**bus
) {
1261 assert(transport
>= 0);
1262 assert(transport
< _BUS_TRANSPORT_MAX
);
1265 assert_return((transport
== BUS_TRANSPORT_LOCAL
) == !host
, -EINVAL
);
1266 assert_return(transport
== BUS_TRANSPORT_LOCAL
|| !user
, -EOPNOTSUPP
);
1268 switch (transport
) {
1270 case BUS_TRANSPORT_LOCAL
:
1272 r
= bus_connect_user_systemd(bus
);
1274 r
= bus_connect_system_systemd(bus
);
1278 case BUS_TRANSPORT_REMOTE
:
1279 r
= sd_bus_open_system_remote(bus
, host
);
1282 case BUS_TRANSPORT_MACHINE
:
1283 r
= sd_bus_open_system_machine(bus
, host
);
1287 assert_not_reached("Hmm, unknown transport type.");
1293 int bus_property_get_bool(
1296 const char *interface
,
1297 const char *property
,
1298 sd_bus_message
*reply
,
1300 sd_bus_error
*error
) {
1302 int b
= *(bool*) userdata
;
1304 return sd_bus_message_append_basic(reply
, 'b', &b
);
1307 #if __SIZEOF_SIZE_T__ != 8
1308 int bus_property_get_size(
1311 const char *interface
,
1312 const char *property
,
1313 sd_bus_message
*reply
,
1315 sd_bus_error
*error
) {
1317 uint64_t sz
= *(size_t*) userdata
;
1319 return sd_bus_message_append_basic(reply
, 't', &sz
);
1323 #if __SIZEOF_LONG__ != 8
1324 int bus_property_get_long(
1327 const char *interface
,
1328 const char *property
,
1329 sd_bus_message
*reply
,
1331 sd_bus_error
*error
) {
1333 int64_t l
= *(long*) userdata
;
1335 return sd_bus_message_append_basic(reply
, 'x', &l
);
1338 int bus_property_get_ulong(
1341 const char *interface
,
1342 const char *property
,
1343 sd_bus_message
*reply
,
1345 sd_bus_error
*error
) {
1347 uint64_t ul
= *(unsigned long*) userdata
;
1349 return sd_bus_message_append_basic(reply
, 't', &ul
);
1353 int bus_log_parse_error(int r
) {
1354 return log_error_errno(r
, "Failed to parse bus message: %m");
1357 int bus_log_create_error(int r
) {
1358 return log_error_errno(r
, "Failed to create bus message: %m");
1361 int bus_parse_unit_info(sd_bus_message
*message
, UnitInfo
*u
) {
1367 return sd_bus_message_read(
1382 int bus_append_unit_property_assignment(sd_bus_message
*m
, const char *assignment
) {
1383 const char *eq
, *field
;
1389 eq
= strchr(assignment
, '=');
1391 log_error("Not an assignment: %s", assignment
);
1395 field
= strndupa(assignment
, eq
- assignment
);
1398 if (streq(field
, "CPUQuota")) {
1402 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, "CPUQuotaPerSecUSec");
1404 return bus_log_create_error(r
);
1406 r
= sd_bus_message_append(m
, "v", "t", USEC_INFINITY
);
1408 } else if (endswith(eq
, "%")) {
1411 if (sscanf(eq
, "%lf%%", &percent
) != 1 || percent
<= 0) {
1412 log_error("CPU quota '%s' invalid.", eq
);
1416 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, "CPUQuotaPerSecUSec");
1418 return bus_log_create_error(r
);
1420 r
= sd_bus_message_append(m
, "v", "t", (usec_t
) percent
* USEC_PER_SEC
/ 100);
1422 log_error("CPU quota needs to be in percent.");
1427 return bus_log_create_error(r
);
1430 } else if (streq(field
, "EnvironmentFile")) {
1431 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, "EnvironmentFiles");
1435 r
= sd_bus_message_append(m
, "v", "a(sb)", 1,
1436 eq
[0] == '-' ? eq
+ 1 : eq
,
1443 r
= sd_bus_message_append_basic(m
, SD_BUS_TYPE_STRING
, field
);
1445 return bus_log_create_error(r
);
1447 if (STR_IN_SET(field
,
1448 "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
1449 "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
1450 "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
1451 "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
1452 "SyslogLevelPrefix")) {
1454 r
= parse_boolean(eq
);
1456 log_error("Failed to parse boolean assignment %s.", assignment
);
1460 r
= sd_bus_message_append(m
, "v", "b", r
);
1462 } else if (streq(field
, "MemoryLimit")) {
1465 if (isempty(eq
) || streq(eq
, "infinity"))
1466 bytes
= (uint64_t) -1;
1468 r
= parse_size(eq
, 1024, &bytes
);
1470 log_error("Failed to parse bytes specification %s", assignment
);
1475 r
= sd_bus_message_append(m
, "v", "t", bytes
);
1477 } else if (streq(field
, "TasksMax")) {
1480 if (isempty(eq
) || streq(eq
, "infinity"))
1483 r
= safe_atou64(eq
, &n
);
1485 log_error("Failed to parse maximum tasks specification %s", assignment
);
1490 r
= sd_bus_message_append(m
, "v", "t", n
);
1492 } else if (STR_IN_SET(field
, "CPUShares", "StartupCPUShares")) {
1495 r
= cg_cpu_shares_parse(eq
, &u
);
1497 log_error("Failed to parse %s value %s.", field
, eq
);
1501 r
= sd_bus_message_append(m
, "v", "t", u
);
1503 } else if (STR_IN_SET(field
, "BlockIOWeight", "StartupBlockIOWeight")) {
1506 r
= cg_cpu_shares_parse(eq
, &u
);
1508 log_error("Failed to parse %s value %s.", field
, eq
);
1512 r
= sd_bus_message_append(m
, "v", "t", u
);
1514 } else if (STR_IN_SET(field
,
1515 "User", "Group", "DevicePolicy", "KillMode",
1516 "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
1517 "StandardInput", "StandardOutput", "StandardError",
1518 "Description", "Slice", "Type", "WorkingDirectory",
1519 "RootDirectory", "SyslogIdentifier", "ProtectSystem",
1521 r
= sd_bus_message_append(m
, "v", "s", eq
);
1523 else if (streq(field
, "SyslogLevel")) {
1526 level
= log_level_from_string(eq
);
1528 log_error("Failed to parse %s value %s.", field
, eq
);
1532 r
= sd_bus_message_append(m
, "v", "i", level
);
1534 } else if (streq(field
, "SyslogFacility")) {
1537 facility
= log_facility_unshifted_from_string(eq
);
1539 log_error("Failed to parse %s value %s.", field
, eq
);
1543 r
= sd_bus_message_append(m
, "v", "i", facility
);
1545 } else if (streq(field
, "DeviceAllow")) {
1548 r
= sd_bus_message_append(m
, "v", "a(ss)", 0);
1550 const char *path
, *rwm
, *e
;
1552 e
= strchr(eq
, ' ');
1554 path
= strndupa(eq
, e
- eq
);
1561 if (!path_startswith(path
, "/dev")) {
1562 log_error("%s is not a device file in /dev.", path
);
1566 r
= sd_bus_message_append(m
, "v", "a(ss)", 1, path
, rwm
);
1569 } else if (STR_IN_SET(field
, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
1572 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
1574 const char *path
, *bandwidth
, *e
;
1577 e
= strchr(eq
, ' ');
1579 path
= strndupa(eq
, e
- eq
);
1582 log_error("Failed to parse %s value %s.", field
, eq
);
1586 if (!path_startswith(path
, "/dev")) {
1587 log_error("%s is not a device file in /dev.", path
);
1591 r
= parse_size(bandwidth
, 1000, &bytes
);
1593 log_error("Failed to parse byte value %s.", bandwidth
);
1597 r
= sd_bus_message_append(m
, "v", "a(st)", 1, path
, bytes
);
1600 } else if (streq(field
, "BlockIODeviceWeight")) {
1603 r
= sd_bus_message_append(m
, "v", "a(st)", 0);
1605 const char *path
, *weight
, *e
;
1608 e
= strchr(eq
, ' ');
1610 path
= strndupa(eq
, e
- eq
);
1613 log_error("Failed to parse %s value %s.", field
, eq
);
1617 if (!path_startswith(path
, "/dev")) {
1618 log_error("%s is not a device file in /dev.", path
);
1622 r
= safe_atou64(weight
, &u
);
1624 log_error("Failed to parse %s value %s.", field
, weight
);
1627 r
= sd_bus_message_append(m
, "v", "a(st)", path
, u
);
1630 } else if (rlimit_from_string(field
) >= 0) {
1633 if (streq(eq
, "infinity"))
1636 r
= safe_atou64(eq
, &rl
);
1638 log_error("Invalid resource limit: %s", eq
);
1643 r
= sd_bus_message_append(m
, "v", "t", rl
);
1645 } else if (streq(field
, "Nice")) {
1648 r
= safe_atoi32(eq
, &i
);
1650 log_error("Failed to parse %s value %s.", field
, eq
);
1654 r
= sd_bus_message_append(m
, "v", "i", i
);
1656 } else if (streq(field
, "Environment")) {
1659 r
= sd_bus_message_open_container(m
, 'v', "as");
1661 return bus_log_create_error(r
);
1663 r
= sd_bus_message_open_container(m
, 'a', "s");
1665 return bus_log_create_error(r
);
1670 _cleanup_free_
char *word
= NULL
;
1672 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
|EXTRACT_CUNESCAPE
);
1674 log_error("Failed to parse Environment value %s", eq
);
1680 if (!env_assignment_is_valid(word
)) {
1681 log_error("Invalid environment assignment: %s", eq
);
1685 r
= sd_bus_message_append_basic(m
, 's', word
);
1687 return bus_log_create_error(r
);
1690 r
= sd_bus_message_close_container(m
);
1692 return bus_log_create_error(r
);
1694 r
= sd_bus_message_close_container(m
);
1696 } else if (streq(field
, "KillSignal")) {
1699 sig
= signal_from_string_try_harder(eq
);
1701 log_error("Failed to parse %s value %s.", field
, eq
);
1705 r
= sd_bus_message_append(m
, "v", "i", sig
);
1707 } else if (streq(field
, "AccuracySec")) {
1710 r
= parse_sec(eq
, &u
);
1712 log_error("Failed to parse %s value %s", field
, eq
);
1716 r
= sd_bus_message_append(m
, "v", "t", u
);
1717 } else if (streq(field
, "TimerSlackNSec")) {
1720 r
= parse_nsec(eq
, &n
);
1722 log_error("Failed to parse %s value %s", field
, eq
);
1726 r
= sd_bus_message_append(m
, "v", "t", n
);
1727 } else if (streq(field
, "OOMScoreAdjust")) {
1730 r
= safe_atoi(eq
, &oa
);
1732 log_error("Failed to parse %s value %s", field
, eq
);
1736 if (!oom_score_adjust_is_valid(oa
)) {
1737 log_error("OOM score adjust value out of range");
1741 r
= sd_bus_message_append(m
, "v", "i", oa
);
1742 } else if (STR_IN_SET(field
, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) {
1745 r
= sd_bus_message_open_container(m
, 'v', "as");
1747 return bus_log_create_error(r
);
1749 r
= sd_bus_message_open_container(m
, 'a', "s");
1751 return bus_log_create_error(r
);
1756 _cleanup_free_
char *word
= NULL
;
1759 r
= extract_first_word(&p
, &word
, NULL
, EXTRACT_QUOTES
);
1761 log_error("Failed to parse %s value %s", field
, eq
);
1767 if (!utf8_is_valid(word
)) {
1768 log_error("Failed to parse %s value %s", field
, eq
);
1772 offset
= word
[0] == '-';
1773 if (!path_is_absolute(word
+ offset
)) {
1774 log_error("Failed to parse %s value %s", field
, eq
);
1778 path_kill_slashes(word
+ offset
);
1780 r
= sd_bus_message_append_basic(m
, 's', word
);
1782 return bus_log_create_error(r
);
1785 r
= sd_bus_message_close_container(m
);
1787 return bus_log_create_error(r
);
1789 r
= sd_bus_message_close_container(m
);
1792 log_error("Unknown assignment %s.", assignment
);
1797 return bus_log_create_error(r
);
1802 typedef struct BusWaitForJobs
{
1809 sd_bus_slot
*slot_job_removed
;
1810 sd_bus_slot
*slot_disconnected
;
1813 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1816 log_error("Warning! D-Bus connection terminated.");
1817 sd_bus_close(sd_bus_message_get_bus(m
));
1822 static int match_job_removed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
1823 const char *path
, *unit
, *result
;
1824 BusWaitForJobs
*d
= userdata
;
1832 r
= sd_bus_message_read(m
, "uoss", &id
, &path
, &unit
, &result
);
1834 bus_log_parse_error(r
);
1838 found
= set_remove(d
->jobs
, (char*) path
);
1844 if (!isempty(result
))
1845 d
->result
= strdup(result
);
1848 d
->name
= strdup(unit
);
1853 void bus_wait_for_jobs_free(BusWaitForJobs
*d
) {
1857 set_free_free(d
->jobs
);
1859 sd_bus_slot_unref(d
->slot_disconnected
);
1860 sd_bus_slot_unref(d
->slot_job_removed
);
1862 sd_bus_unref(d
->bus
);
1870 int bus_wait_for_jobs_new(sd_bus
*bus
, BusWaitForJobs
**ret
) {
1871 _cleanup_(bus_wait_for_jobs_freep
) BusWaitForJobs
*d
= NULL
;
1877 d
= new0(BusWaitForJobs
, 1);
1881 d
->bus
= sd_bus_ref(bus
);
1883 /* When we are a bus client we match by sender. Direct
1884 * connections OTOH have no initialized sender field, and
1885 * hence we ignore the sender then */
1886 r
= sd_bus_add_match(
1888 &d
->slot_job_removed
,
1891 "sender='org.freedesktop.systemd1',"
1892 "interface='org.freedesktop.systemd1.Manager',"
1893 "member='JobRemoved',"
1894 "path='/org/freedesktop/systemd1'" :
1896 "interface='org.freedesktop.systemd1.Manager',"
1897 "member='JobRemoved',"
1898 "path='/org/freedesktop/systemd1'",
1899 match_job_removed
, d
);
1903 r
= sd_bus_add_match(
1905 &d
->slot_disconnected
,
1907 "sender='org.freedesktop.DBus.Local',"
1908 "interface='org.freedesktop.DBus.Local',"
1909 "member='Disconnected'",
1910 match_disconnected
, d
);
1920 static int bus_process_wait(sd_bus
*bus
) {
1924 r
= sd_bus_process(bus
, NULL
);
1930 r
= sd_bus_wait(bus
, (uint64_t) -1);
1936 static int bus_job_get_service_result(BusWaitForJobs
*d
, char **result
) {
1937 _cleanup_free_
char *dbus_path
= NULL
;
1943 dbus_path
= unit_dbus_path_from_name(d
->name
);
1947 return sd_bus_get_property_string(d
->bus
,
1948 "org.freedesktop.systemd1",
1950 "org.freedesktop.systemd1.Service",
1956 static const struct {
1957 const char *result
, *explanation
;
1958 } explanations
[] = {
1959 { "resources", "a configured resource limit was exceeded" },
1960 { "timeout", "a timeout was exceeded" },
1961 { "exit-code", "the control process exited with error code" },
1962 { "signal", "a fatal signal was delivered to the control process" },
1963 { "core-dump", "a fatal signal was delivered causing the control process to dump core" },
1964 { "watchdog", "the service failed to send watchdog ping" },
1965 { "start-limit", "start of the service was attempted too often" }
1968 static void log_job_error_with_service_result(const char* service
, const char *result
) {
1969 _cleanup_free_
char *service_shell_quoted
= NULL
;
1973 service_shell_quoted
= shell_maybe_quote(service
);
1975 if (!isempty(result
)) {
1978 for (i
= 0; i
< ELEMENTSOF(explanations
); ++i
)
1979 if (streq(result
, explanations
[i
].result
))
1982 if (i
< ELEMENTSOF(explanations
)) {
1983 log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
1985 explanations
[i
].explanation
,
1986 strna(service_shell_quoted
));
1992 log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n",
1994 strna(service_shell_quoted
));
1997 /* For some results maybe additional explanation is required */
1998 if (streq_ptr(result
, "start-limit"))
1999 log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.",
2000 strna(service_shell_quoted
));
2003 static int check_wait_response(BusWaitForJobs
*d
, bool quiet
) {
2009 if (streq(d
->result
, "canceled"))
2010 log_error("Job for %s canceled.", strna(d
->name
));
2011 else if (streq(d
->result
, "timeout"))
2012 log_error("Job for %s timed out.", strna(d
->name
));
2013 else if (streq(d
->result
, "dependency"))
2014 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d
->name
));
2015 else if (streq(d
->result
, "invalid"))
2016 log_error("Job for %s invalid.", strna(d
->name
));
2017 else if (streq(d
->result
, "assert"))
2018 log_error("Assertion failed on job for %s.", strna(d
->name
));
2019 else if (streq(d
->result
, "unsupported"))
2020 log_error("Operation on or unit type of %s not supported on this system.", strna(d
->name
));
2021 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped")) {
2024 _cleanup_free_
char *result
= NULL
;
2026 q
= bus_job_get_service_result(d
, &result
);
2028 log_debug_errno(q
, "Failed to get Result property of service %s: %m", d
->name
);
2030 log_job_error_with_service_result(d
->name
, result
);
2032 log_error("Job failed. See \"journalctl -xe\" for details.");
2036 if (streq(d
->result
, "canceled"))
2038 else if (streq(d
->result
, "timeout"))
2040 else if (streq(d
->result
, "dependency"))
2042 else if (streq(d
->result
, "invalid"))
2044 else if (streq(d
->result
, "assert"))
2046 else if (streq(d
->result
, "unsupported"))
2048 else if (!streq(d
->result
, "done") && !streq(d
->result
, "skipped"))
2054 int bus_wait_for_jobs(BusWaitForJobs
*d
, bool quiet
) {
2059 while (!set_isempty(d
->jobs
)) {
2062 q
= bus_process_wait(d
->bus
);
2064 return log_error_errno(q
, "Failed to wait for response: %m");
2067 q
= check_wait_response(d
, quiet
);
2068 /* Return the first error as it is most likely to be
2070 if (q
< 0 && r
== 0)
2073 log_debug_errno(q
, "Got result %s/%m for job %s", strna(d
->result
), strna(d
->name
));
2076 d
->name
= mfree(d
->name
);
2077 d
->result
= mfree(d
->result
);
2083 int bus_wait_for_jobs_add(BusWaitForJobs
*d
, const char *path
) {
2088 r
= set_ensure_allocated(&d
->jobs
, &string_hash_ops
);
2092 return set_put_strdup(d
->jobs
, path
);
2095 int bus_wait_for_jobs_one(BusWaitForJobs
*d
, const char *path
, bool quiet
) {
2098 r
= bus_wait_for_jobs_add(d
, path
);
2102 return bus_wait_for_jobs(d
, quiet
);
2105 int bus_deserialize_and_dump_unit_file_changes(sd_bus_message
*m
, bool quiet
, UnitFileChange
**changes
, unsigned *n_changes
) {
2106 const char *type
, *path
, *source
;
2109 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "(sss)");
2111 return bus_log_parse_error(r
);
2113 while ((r
= sd_bus_message_read(m
, "(sss)", &type
, &path
, &source
)) > 0) {
2115 if (streq(type
, "symlink"))
2116 log_info("Created symlink from %s to %s.", path
, source
);
2118 log_info("Removed symlink %s.", path
);
2121 r
= unit_file_changes_add(changes
, n_changes
, streq(type
, "symlink") ? UNIT_FILE_SYMLINK
: UNIT_FILE_UNLINK
, path
, source
);
2126 return bus_log_parse_error(r
);
2128 r
= sd_bus_message_exit_container(m
);
2130 return bus_log_parse_error(r
);
2136 * bus_path_encode_unique() - encode unique object path
2137 * @b: bus connection or NULL
2138 * @prefix: object path prefix
2139 * @sender_id: unique-name of client, or NULL
2140 * @external_id: external ID to be chosen by client, or NULL
2141 * @ret_path: storage for encoded object path pointer
2143 * Whenever we provide a bus API that allows clients to create and manage
2144 * server-side objects, we need to provide a unique name for these objects. If
2145 * we let the server choose the name, we suffer from a race condition: If a
2146 * client creates an object asynchronously, it cannot destroy that object until
2147 * it received the method reply. It cannot know the name of the new object,
2148 * thus, it cannot destroy it. Furthermore, it enforces a round-trip.
2150 * Therefore, many APIs allow the client to choose the unique name for newly
2151 * created objects. There're two problems to solve, though:
2152 * 1) Object names are usually defined via dbus object paths, which are
2153 * usually globally namespaced. Therefore, multiple clients must be able
2154 * to choose unique object names without interference.
2155 * 2) If multiple libraries share the same bus connection, they must be
2156 * able to choose unique object names without interference.
2157 * The first problem is solved easily by prefixing a name with the
2158 * unique-bus-name of a connection. The server side must enforce this and
2159 * reject any other name. The second problem is solved by providing unique
2160 * suffixes from within sd-bus.
2162 * This helper allows clients to create unique object-paths. It uses the
2163 * template '/prefix/sender_id/external_id' and returns the new path in
2164 * @ret_path (must be freed by the caller).
2165 * If @sender_id is NULL, the unique-name of @b is used. If @external_id is
2166 * NULL, this function allocates a unique suffix via @b (by requesting a new
2167 * cookie). If both @sender_id and @external_id are given, @b can be passed as
2170 * Returns: 0 on success, negative error code on failure.
2172 int bus_path_encode_unique(sd_bus
*b
, const char *prefix
, const char *sender_id
, const char *external_id
, char **ret_path
) {
2173 _cleanup_free_
char *sender_label
= NULL
, *external_label
= NULL
;
2174 char external_buf
[DECIMAL_STR_MAX(uint64_t)], *p
;
2177 assert_return(b
|| (sender_id
&& external_id
), -EINVAL
);
2178 assert_return(object_path_is_valid(prefix
), -EINVAL
);
2179 assert_return(ret_path
, -EINVAL
);
2182 r
= sd_bus_get_unique_name(b
, &sender_id
);
2188 xsprintf(external_buf
, "%"PRIu64
, ++b
->cookie
);
2189 external_id
= external_buf
;
2192 sender_label
= bus_label_escape(sender_id
);
2196 external_label
= bus_label_escape(external_id
);
2197 if (!external_label
)
2200 p
= strjoin(prefix
, "/", sender_label
, "/", external_label
, NULL
);
2209 * bus_path_decode_unique() - decode unique object path
2210 * @path: object path to decode
2211 * @prefix: object path prefix
2212 * @ret_sender: output parameter for sender-id label
2213 * @ret_external: output parameter for external-id label
2215 * This does the reverse of bus_path_encode_unique() (see its description for
2216 * details). Both trailing labels, sender-id and external-id, are unescaped and
2217 * returned in the given output parameters (the caller must free them).
2219 * Note that this function returns 0 if the path does not match the template
2220 * (see bus_path_encode_unique()), 1 if it matched.
2222 * Returns: Negative error code on failure, 0 if the given object path does not
2223 * match the template (return parameters are set to NULL), 1 if it was
2224 * parsed successfully (return parameters contain allocated labels).
2226 int bus_path_decode_unique(const char *path
, const char *prefix
, char **ret_sender
, char **ret_external
) {
2228 char *sender
, *external
;
2230 assert(object_path_is_valid(path
));
2231 assert(object_path_is_valid(prefix
));
2233 assert(ret_external
);
2235 p
= object_path_startswith(path
, prefix
);
2238 *ret_external
= NULL
;
2245 *ret_external
= NULL
;
2249 sender
= bus_label_unescape_n(p
, q
- p
);
2250 external
= bus_label_unescape(q
+ 1);
2251 if (!sender
|| !external
) {
2257 *ret_sender
= sender
;
2258 *ret_external
= external
;
2262 bool is_kdbus_wanted(void) {
2263 _cleanup_free_
char *value
= NULL
;
2265 const bool configured
= true;
2267 const bool configured
= false;
2272 if (get_proc_cmdline_key("kdbus", NULL
) > 0)
2275 r
= get_proc_cmdline_key("kdbus=", &value
);
2279 return parse_boolean(value
) == 1;
2282 bool is_kdbus_available(void) {
2283 _cleanup_close_
int fd
= -1;
2284 struct kdbus_cmd cmd
= { .size
= sizeof(cmd
), .flags
= KDBUS_FLAG_NEGOTIATE
};
2286 if (!is_kdbus_wanted())
2289 fd
= open("/sys/fs/kdbus/control", O_RDWR
| O_CLOEXEC
| O_NONBLOCK
| O_NOCTTY
);
2293 return ioctl(fd
, KDBUS_CMD_BUS_MAKE
, &cmd
) >= 0;
2296 int bus_property_get_rlimit(
2299 const char *interface
,
2300 const char *property
,
2301 sd_bus_message
*reply
,
2303 sd_bus_error
*error
) {
2313 rl
= *(struct rlimit
**) userdata
;
2317 struct rlimit buf
= {};
2320 z
= rlimit_from_string(strstr(property
, "Limit"));
2327 /* rlim_t might have different sizes, let's map
2328 * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
2330 u
= x
== RLIM_INFINITY
? (uint64_t) -1 : (uint64_t) x
;
2332 return sd_bus_message_append(reply
, "t", u
);