1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "bus-print-properties.h"
4 #include "cap-list.h"
5 #include "cgroup-util.h"
6 #include "escape.h"
7 #include "mountpoint-util.h"
8 #include "nsflags.h"
9 #include "parse-util.h"
10 #include "stdio-util.h"
11 #include "string-util.h"
12 #include "strv.h"
13 #include "time-util.h"
14 #include "user-util.h"
15
bus_print_property_value(const char * name,const char * expected_value,BusPrintPropertyFlags flags,const char * value)16 int bus_print_property_value(const char *name, const char *expected_value, BusPrintPropertyFlags flags, const char *value) {
17 assert(name);
18
19 if (expected_value && !streq_ptr(expected_value, value))
20 return 0;
21
22 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) && isempty(value))
23 return 0;
24
25 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
26 puts(strempty(value));
27 else
28 printf("%s=%s\n", name, strempty(value));
29
30 return 0;
31 }
32
bus_print_property_valuef(const char * name,const char * expected_value,BusPrintPropertyFlags flags,const char * fmt,...)33 int bus_print_property_valuef(const char *name, const char *expected_value, BusPrintPropertyFlags flags, const char *fmt, ...) {
34 _cleanup_free_ char *s = NULL;
35 va_list ap;
36 int r;
37
38 assert(name);
39 assert(fmt);
40
41 va_start(ap, fmt);
42 r = vasprintf(&s, fmt, ap);
43 va_end(ap);
44 if (r < 0)
45 return -ENOMEM;
46
47 return bus_print_property_value(name, expected_value, flags, s);
48 }
49
bus_print_property(const char * name,const char * expected_value,sd_bus_message * m,BusPrintPropertyFlags flags)50 static int bus_print_property(const char *name, const char *expected_value, sd_bus_message *m, BusPrintPropertyFlags flags) {
51 char type;
52 const char *contents;
53 int r;
54
55 assert(name);
56 assert(m);
57
58 r = sd_bus_message_peek_type(m, &type, &contents);
59 if (r < 0)
60 return r;
61
62 switch (type) {
63
64 case SD_BUS_TYPE_STRING: {
65 const char *s;
66
67 r = sd_bus_message_read_basic(m, type, &s);
68 if (r < 0)
69 return r;
70
71 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) || !isempty(s)) {
72 bool good;
73
74 /* This property has a single value, so we need to take
75 * care not to print a new line, everything else is OK. */
76 good = !strchr(s, '\n');
77 bus_print_property_value(name, expected_value, flags, good ? s : "[unprintable]");
78 }
79
80 return 1;
81 }
82
83 case SD_BUS_TYPE_BOOLEAN: {
84 int b;
85
86 r = sd_bus_message_read_basic(m, type, &b);
87 if (r < 0)
88 return r;
89
90 if (expected_value && parse_boolean(expected_value) != b)
91 return 1;
92
93 bus_print_property_value(name, NULL, flags, yes_no(b));
94 return 1;
95 }
96
97 case SD_BUS_TYPE_UINT64: {
98 uint64_t u;
99
100 r = sd_bus_message_read_basic(m, type, &u);
101 if (r < 0)
102 return r;
103
104 /* Yes, heuristics! But we can change this check
105 * should it turn out to not be sufficient */
106
107 if (endswith(name, "Timestamp") ||
108 STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec", "TimeUSec", "RTCTimeUSec"))
109
110 bus_print_property_value(name, expected_value, flags, FORMAT_TIMESTAMP(u));
111
112 else if (strstr(name, "USec"))
113 bus_print_property_value(name, expected_value, flags, FORMAT_TIMESPAN(u, 0));
114
115 else if (streq(name, "CoredumpFilter"))
116 bus_print_property_valuef(name, expected_value, flags, "0x%"PRIx64, u);
117
118 else if (streq(name, "RestrictNamespaces")) {
119 _cleanup_free_ char *s = NULL;
120 const char *result;
121
122 if ((u & NAMESPACE_FLAGS_ALL) == 0)
123 result = "yes";
124 else if (FLAGS_SET(u, NAMESPACE_FLAGS_ALL))
125 result = "no";
126 else {
127 r = namespace_flags_to_string(u, &s);
128 if (r < 0)
129 return r;
130
131 result = s;
132 }
133
134 bus_print_property_value(name, expected_value, flags, result);
135
136 } else if (streq(name, "MountFlags")) {
137 const char *result;
138
139 result = mount_propagation_flags_to_string(u);
140 if (!result)
141 return -EINVAL;
142
143 bus_print_property_value(name, expected_value, flags, result);
144
145 } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
146 _cleanup_free_ char *s = NULL;
147
148 r = capability_set_to_string_alloc(u, &s);
149 if (r < 0)
150 return r;
151
152 bus_print_property_value(name, expected_value, flags, s);
153
154 } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
155 (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
156 (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
157 (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == UINT64_MAX) ||
158 (endswith(name, "NSec") && u == UINT64_MAX))
159
160 bus_print_property_value(name, expected_value, flags, "[not set]");
161
162 else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "MemoryAvailable") && u == CGROUP_LIMIT_MAX) ||
163 (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
164 (startswith(name, "Limit") && u == UINT64_MAX) ||
165 (startswith(name, "DefaultLimit") && u == UINT64_MAX))
166
167 bus_print_property_value(name, expected_value, flags, "infinity");
168 else if (STR_IN_SET(name, "IPIngressBytes", "IPIngressPackets", "IPEgressBytes", "IPEgressPackets") && u == UINT64_MAX)
169 bus_print_property_value(name, expected_value, flags, "[no data]");
170 else
171 bus_print_property_valuef(name, expected_value, flags, "%"PRIu64, u);
172
173 return 1;
174 }
175
176 case SD_BUS_TYPE_INT64: {
177 int64_t i;
178
179 r = sd_bus_message_read_basic(m, type, &i);
180 if (r < 0)
181 return r;
182
183 bus_print_property_valuef(name, expected_value, flags, "%"PRIi64, i);
184 return 1;
185 }
186
187 case SD_BUS_TYPE_UINT32: {
188 uint32_t u;
189
190 r = sd_bus_message_read_basic(m, type, &u);
191 if (r < 0)
192 return r;
193
194 if (strstr(name, "UMask") || strstr(name, "Mode"))
195 bus_print_property_valuef(name, expected_value, flags, "%04o", u);
196
197 else if (streq(name, "UID")) {
198 if (u == UID_INVALID)
199 bus_print_property_value(name, expected_value, flags, "[not set]");
200 else
201 bus_print_property_valuef(name, expected_value, flags, "%"PRIu32, u);
202 } else if (streq(name, "GID")) {
203 if (u == GID_INVALID)
204 bus_print_property_value(name, expected_value, flags, "[not set]");
205 else
206 bus_print_property_valuef(name, expected_value, flags, "%"PRIu32, u);
207 } else
208 bus_print_property_valuef(name, expected_value, flags, "%"PRIu32, u);
209
210 return 1;
211 }
212
213 case SD_BUS_TYPE_INT32: {
214 int32_t i;
215
216 r = sd_bus_message_read_basic(m, type, &i);
217 if (r < 0)
218 return r;
219
220 bus_print_property_valuef(name, expected_value, flags, "%"PRIi32, i);
221 return 1;
222 }
223
224 case SD_BUS_TYPE_DOUBLE: {
225 double d;
226
227 r = sd_bus_message_read_basic(m, type, &d);
228 if (r < 0)
229 return r;
230
231 bus_print_property_valuef(name, expected_value, flags, "%g", d);
232 return 1;
233 }
234
235 case SD_BUS_TYPE_ARRAY:
236 if (streq(contents, "s")) {
237 bool first = true;
238 const char *str;
239
240 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents);
241 if (r < 0)
242 return r;
243
244 while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) {
245 _cleanup_free_ char *e = NULL;
246
247 e = shell_maybe_quote(str, 0);
248 if (!e)
249 return -ENOMEM;
250
251 if (first) {
252 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
253 printf("%s=", name);
254 first = false;
255 } else
256 fputs(" ", stdout);
257
258 fputs(e, stdout);
259 }
260 if (r < 0)
261 return r;
262
263 if (first && FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) && !FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
264 printf("%s=", name);
265 if (!first || FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY))
266 puts("");
267
268 r = sd_bus_message_exit_container(m);
269 if (r < 0)
270 return r;
271
272 return 1;
273
274 } else if (streq(contents, "y")) {
275 const uint8_t *u;
276 size_t n;
277
278 r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
279 if (r < 0)
280 return r;
281
282 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) || n > 0) {
283 unsigned i;
284
285 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
286 printf("%s=", name);
287
288 for (i = 0; i < n; i++)
289 printf("%02x", u[i]);
290
291 puts("");
292 }
293
294 return 1;
295
296 } else if (streq(contents, "u")) {
297 uint32_t *u;
298 size_t n;
299
300 r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
301 if (r < 0)
302 return r;
303
304 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) || n > 0) {
305 unsigned i;
306
307 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
308 printf("%s=", name);
309
310 for (i = 0; i < n; i++)
311 printf("%08x", u[i]);
312
313 puts("");
314 }
315
316 return 1;
317 }
318
319 break;
320 }
321
322 return 0;
323 }
324
bus_message_print_all_properties(sd_bus_message * m,bus_message_print_t func,char ** filter,BusPrintPropertyFlags flags,Set ** found_properties)325 int bus_message_print_all_properties(
326 sd_bus_message *m,
327 bus_message_print_t func,
328 char **filter,
329 BusPrintPropertyFlags flags,
330 Set **found_properties) {
331
332 int r;
333
334 assert(m);
335
336 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
337 if (r < 0)
338 return r;
339
340 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
341 _cleanup_free_ char *name_with_equal = NULL;
342 const char *name, *contents, *expected_value = NULL;
343
344 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
345 if (r < 0)
346 return r;
347
348 if (found_properties) {
349 r = set_ensure_put(found_properties, &string_hash_ops, name);
350 if (r < 0)
351 return log_oom();
352 }
353
354 name_with_equal = strjoin(name, "=");
355 if (!name_with_equal)
356 return log_oom();
357
358 if (!filter || strv_contains(filter, name) ||
359 (expected_value = strv_find_startswith(filter, name_with_equal))) {
360 r = sd_bus_message_peek_type(m, NULL, &contents);
361 if (r < 0)
362 return r;
363
364 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
365 if (r < 0)
366 return r;
367
368 if (func)
369 r = func(name, expected_value, m, flags);
370 if (!func || r == 0)
371 r = bus_print_property(name, expected_value, m, flags);
372 if (r < 0)
373 return r;
374 if (r == 0) {
375 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) && !expected_value)
376 printf("%s=[unprintable]\n", name);
377 /* skip what we didn't read */
378 r = sd_bus_message_skip(m, contents);
379 if (r < 0)
380 return r;
381 }
382
383 r = sd_bus_message_exit_container(m);
384 if (r < 0)
385 return r;
386 } else {
387 r = sd_bus_message_skip(m, "v");
388 if (r < 0)
389 return r;
390 }
391
392 r = sd_bus_message_exit_container(m);
393 if (r < 0)
394 return r;
395 }
396 if (r < 0)
397 return r;
398
399 r = sd_bus_message_exit_container(m);
400 if (r < 0)
401 return r;
402
403 return 0;
404 }
405
bus_print_all_properties(sd_bus * bus,const char * dest,const char * path,bus_message_print_t func,char ** filter,BusPrintPropertyFlags flags,Set ** found_properties)406 int bus_print_all_properties(
407 sd_bus *bus,
408 const char *dest,
409 const char *path,
410 bus_message_print_t func,
411 char **filter,
412 BusPrintPropertyFlags flags,
413 Set **found_properties) {
414
415 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
416 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
417 int r;
418
419 assert(bus);
420 assert(path);
421
422 r = sd_bus_call_method(bus,
423 dest,
424 path,
425 "org.freedesktop.DBus.Properties",
426 "GetAll",
427 &error,
428 &reply,
429 "s", "");
430 if (r < 0)
431 return r;
432
433 return bus_message_print_all_properties(reply, func, filter, flags, found_properties);
434 }
435