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