]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/bus-print-properties.c
Merge pull request #18863 from keszybz/cmdline-escaping
[thirdparty/systemd.git] / src / shared / bus-print-properties.c
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
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
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
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 char timestamp[FORMAT_TIMESTAMP_MAX];
110 const char *t;
111
112 t = format_timestamp(timestamp, sizeof(timestamp), u);
113 bus_print_property_value(name, expected_value, flags, t);
114
115 } else if (strstr(name, "USec")) {
116 char timespan[FORMAT_TIMESPAN_MAX];
117
118 (void) format_timespan(timespan, sizeof(timespan), u, 0);
119 bus_print_property_value(name, expected_value, flags, timespan);
120
121 } else if (streq(name, "CoredumpFilter"))
122 bus_print_property_valuef(name, expected_value, flags, "0x%"PRIx64, u);
123
124 else if (streq(name, "RestrictNamespaces")) {
125 _cleanup_free_ char *s = NULL;
126 const char *result;
127
128 if ((u & NAMESPACE_FLAGS_ALL) == 0)
129 result = "yes";
130 else if (FLAGS_SET(u, NAMESPACE_FLAGS_ALL))
131 result = "no";
132 else {
133 r = namespace_flags_to_string(u, &s);
134 if (r < 0)
135 return r;
136
137 result = s;
138 }
139
140 bus_print_property_value(name, expected_value, flags, result);
141
142 } else if (streq(name, "MountFlags")) {
143 const char *result;
144
145 result = mount_propagation_flags_to_string(u);
146 if (!result)
147 return -EINVAL;
148
149 bus_print_property_value(name, expected_value, flags, result);
150
151 } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
152 _cleanup_free_ char *s = NULL;
153
154 r = capability_set_to_string_alloc(u, &s);
155 if (r < 0)
156 return r;
157
158 bus_print_property_value(name, expected_value, flags, s);
159
160 } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
161 (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
162 (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
163 (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == UINT64_MAX) ||
164 (endswith(name, "NSec") && u == UINT64_MAX))
165
166 bus_print_property_value(name, expected_value, flags, "[not set]");
167
168 else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
169 (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
170 (startswith(name, "Limit") && u == UINT64_MAX) ||
171 (startswith(name, "DefaultLimit") && u == UINT64_MAX))
172
173 bus_print_property_value(name, expected_value, flags, "infinity");
174 else if (STR_IN_SET(name, "IPIngressBytes", "IPIngressPackets", "IPEgressBytes", "IPEgressPackets") && u == UINT64_MAX)
175 bus_print_property_value(name, expected_value, flags, "[no data]");
176 else
177 bus_print_property_valuef(name, expected_value, flags, "%"PRIu64, u);
178
179 return 1;
180 }
181
182 case SD_BUS_TYPE_INT64: {
183 int64_t i;
184
185 r = sd_bus_message_read_basic(m, type, &i);
186 if (r < 0)
187 return r;
188
189 bus_print_property_valuef(name, expected_value, flags, "%"PRIi64, i);
190 return 1;
191 }
192
193 case SD_BUS_TYPE_UINT32: {
194 uint32_t u;
195
196 r = sd_bus_message_read_basic(m, type, &u);
197 if (r < 0)
198 return r;
199
200 if (strstr(name, "UMask") || strstr(name, "Mode"))
201 bus_print_property_valuef(name, expected_value, flags, "%04o", u);
202
203 else if (streq(name, "UID")) {
204 if (u == UID_INVALID)
205 bus_print_property_value(name, expected_value, flags, "[not set]");
206 else
207 bus_print_property_valuef(name, expected_value, flags, "%"PRIu32, u);
208 } else if (streq(name, "GID")) {
209 if (u == GID_INVALID)
210 bus_print_property_value(name, expected_value, flags, "[not set]");
211 else
212 bus_print_property_valuef(name, expected_value, flags, "%"PRIu32, u);
213 } else
214 bus_print_property_valuef(name, expected_value, flags, "%"PRIu32, u);
215
216 return 1;
217 }
218
219 case SD_BUS_TYPE_INT32: {
220 int32_t i;
221
222 r = sd_bus_message_read_basic(m, type, &i);
223 if (r < 0)
224 return r;
225
226 bus_print_property_valuef(name, expected_value, flags, "%"PRIi32, i);
227 return 1;
228 }
229
230 case SD_BUS_TYPE_DOUBLE: {
231 double d;
232
233 r = sd_bus_message_read_basic(m, type, &d);
234 if (r < 0)
235 return r;
236
237 bus_print_property_valuef(name, expected_value, flags, "%g", d);
238 return 1;
239 }
240
241 case SD_BUS_TYPE_ARRAY:
242 if (streq(contents, "s")) {
243 bool first = true;
244 const char *str;
245
246 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents);
247 if (r < 0)
248 return r;
249
250 while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) {
251 _cleanup_free_ char *e = NULL;
252
253 e = shell_maybe_quote(str, 0);
254 if (!e)
255 return -ENOMEM;
256
257 if (first) {
258 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
259 printf("%s=", name);
260 first = false;
261 } else
262 fputs(" ", stdout);
263
264 fputs(e, stdout);
265 }
266 if (r < 0)
267 return r;
268
269 if (first && FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) && !FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
270 printf("%s=", name);
271 if (!first || FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY))
272 puts("");
273
274 r = sd_bus_message_exit_container(m);
275 if (r < 0)
276 return r;
277
278 return 1;
279
280 } else if (streq(contents, "y")) {
281 const uint8_t *u;
282 size_t n;
283
284 r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
285 if (r < 0)
286 return r;
287
288 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) || n > 0) {
289 unsigned i;
290
291 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
292 printf("%s=", name);
293
294 for (i = 0; i < n; i++)
295 printf("%02x", u[i]);
296
297 puts("");
298 }
299
300 return 1;
301
302 } else if (streq(contents, "u")) {
303 uint32_t *u;
304 size_t n;
305
306 r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
307 if (r < 0)
308 return r;
309
310 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) || n > 0) {
311 unsigned i;
312
313 if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE))
314 printf("%s=", name);
315
316 for (i = 0; i < n; i++)
317 printf("%08x", u[i]);
318
319 puts("");
320 }
321
322 return 1;
323 }
324
325 break;
326 }
327
328 return 0;
329 }
330
331 int bus_message_print_all_properties(
332 sd_bus_message *m,
333 bus_message_print_t func,
334 char **filter,
335 BusPrintPropertyFlags flags,
336 Set **found_properties) {
337
338 int r;
339
340 assert(m);
341
342 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
343 if (r < 0)
344 return r;
345
346 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
347 _cleanup_free_ char *name_with_equal = NULL;
348 const char *name, *contents, *expected_value = NULL;
349
350 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
351 if (r < 0)
352 return r;
353
354 if (found_properties) {
355 r = set_ensure_put(found_properties, &string_hash_ops, name);
356 if (r < 0)
357 return log_oom();
358 }
359
360 name_with_equal = strjoin(name, "=");
361 if (!name_with_equal)
362 return log_oom();
363
364 if (!filter || strv_find(filter, name) ||
365 (expected_value = strv_find_startswith(filter, name_with_equal))) {
366 r = sd_bus_message_peek_type(m, NULL, &contents);
367 if (r < 0)
368 return r;
369
370 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
371 if (r < 0)
372 return r;
373
374 if (func)
375 r = func(name, expected_value, m, flags);
376 if (!func || r == 0)
377 r = bus_print_property(name, expected_value, m, flags);
378 if (r < 0)
379 return r;
380 if (r == 0) {
381 if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) && !expected_value)
382 printf("%s=[unprintable]\n", name);
383 /* skip what we didn't read */
384 r = sd_bus_message_skip(m, contents);
385 if (r < 0)
386 return r;
387 }
388
389 r = sd_bus_message_exit_container(m);
390 if (r < 0)
391 return r;
392 } else {
393 r = sd_bus_message_skip(m, "v");
394 if (r < 0)
395 return r;
396 }
397
398 r = sd_bus_message_exit_container(m);
399 if (r < 0)
400 return r;
401 }
402 if (r < 0)
403 return r;
404
405 r = sd_bus_message_exit_container(m);
406 if (r < 0)
407 return r;
408
409 return 0;
410 }
411
412 int bus_print_all_properties(
413 sd_bus *bus,
414 const char *dest,
415 const char *path,
416 bus_message_print_t func,
417 char **filter,
418 BusPrintPropertyFlags flags,
419 Set **found_properties) {
420
421 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
422 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
423 int r;
424
425 assert(bus);
426 assert(path);
427
428 r = sd_bus_call_method(bus,
429 dest,
430 path,
431 "org.freedesktop.DBus.Properties",
432 "GetAll",
433 &error,
434 &reply,
435 "s", "");
436 if (r < 0)
437 return r;
438
439 return bus_message_print_all_properties(reply, func, filter, flags, found_properties);
440 }