chiark / gitweb /
busctl: introduce busctl "get-property" command for reading and dumping object properties
[elogind.git] / src / libsystemd / sd-bus / bus-dump.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 "util.h"
23 #include "capability.h"
24 #include "strv.h"
25 #include "audit.h"
26 #include "macro.h"
27
28 #include "bus-message.h"
29 #include "bus-internal.h"
30 #include "bus-type.h"
31 #include "bus-dump.h"
32
33 static char *indent(unsigned level, unsigned flags) {
34         char *p;
35         unsigned n, i = 0;
36
37         n = 0;
38
39         if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0)
40                 level -= 1;
41
42         if (flags & BUS_MESSAGE_DUMP_WITH_HEADER)
43                 n += 2;
44
45         p = new(char, n + level*8 + 1);
46         if (!p)
47                 return NULL;
48
49         if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
50                 p[i++] = ' ';
51                 p[i++] = ' ';
52         }
53
54         memset(p + i, ' ', level*8);
55         p[i + level*8] = 0;
56
57         return p;
58 }
59
60 int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) {
61         unsigned level = 1;
62         int r;
63
64         assert(m);
65
66         if (!f)
67                 f = stdout;
68
69         if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) {
70                 fprintf(f,
71                         "%s%s%s Type=%s%s%s  Endian=%c  Flags=%u  Version=%u  Priority=%lli",
72                         m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
73                         m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() :
74                         m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", draw_special_char(DRAW_TRIANGULAR_BULLET), ansi_highlight_off(),
75                         ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_highlight_off(),
76                         m->header->endian,
77                         m->header->flags,
78                         m->header->version,
79                         (long long) m->priority);
80
81                 /* Display synthetic message serial number in a more readable
82                  * format than (uint32_t) -1 */
83                 if (BUS_MESSAGE_COOKIE(m) == 0xFFFFFFFFULL)
84                         fprintf(f, " Cookie=-1");
85                 else
86                         fprintf(f, " Cookie=%" PRIu64, BUS_MESSAGE_COOKIE(m));
87
88                 if (m->reply_cookie != 0)
89                         fprintf(f, "  ReplyCookie=%" PRIu64, m->reply_cookie);
90
91                 fputs("\n", f);
92
93                 if (m->sender)
94                         fprintf(f, "  Sender=%s%s%s", ansi_highlight(), m->sender, ansi_highlight_off());
95                 if (m->destination)
96                         fprintf(f, "  Destination=%s%s%s", ansi_highlight(), m->destination, ansi_highlight_off());
97                 if (m->path)
98                         fprintf(f, "  Path=%s%s%s", ansi_highlight(), m->path, ansi_highlight_off());
99                 if (m->interface)
100                         fprintf(f, "  Interface=%s%s%s", ansi_highlight(), m->interface, ansi_highlight_off());
101                 if (m->member)
102                         fprintf(f, "  Member=%s%s%s", ansi_highlight(), m->member, ansi_highlight_off());
103
104                 if (m->sender || m->destination || m->path || m->interface || m->member)
105                         fputs("\n", f);
106
107                 if (sd_bus_error_is_set(&m->error))
108                         fprintf(f,
109                                 "  ErrorName=%s%s%s"
110                                 "  ErrorMessage=%s\"%s\"%s\n",
111                                 ansi_highlight_red(), strna(m->error.name), ansi_highlight_off(),
112                                 ansi_highlight_red(), strna(m->error.message), ansi_highlight_off());
113
114                 if (m->monotonic != 0)
115                         fprintf(f, "  Monotonic="USEC_FMT, m->monotonic);
116                 if (m->realtime != 0)
117                         fprintf(f, "  Realtime="USEC_FMT, m->realtime);
118                 if (m->seqnum != 0)
119                         fprintf(f, "  SequenceNumber=%"PRIu64, m->seqnum);
120
121                 if (m->monotonic != 0 || m->realtime != 0 || m->seqnum != 0)
122                         fputs("\n", f);
123
124                 bus_creds_dump(&m->creds, f);
125         }
126
127         r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY));
128         if (r < 0) {
129                 log_error("Failed to rewind: %s", strerror(-r));
130                 return r;
131         }
132
133         if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY))
134                 fprintf(f, "%sMESSAGE \"%s\" {\n", indent(0, flags), strempty(m->root_container.signature));
135
136         for (;;) {
137                 _cleanup_free_ char *prefix = NULL;
138                 const char *contents = NULL;
139                 char type;
140                 union {
141                         uint8_t u8;
142                         uint16_t u16;
143                         int16_t s16;
144                         uint32_t u32;
145                         int32_t s32;
146                         uint64_t u64;
147                         int64_t s64;
148                         double d64;
149                         const char *string;
150                         int i;
151                 } basic;
152
153                 r = sd_bus_message_peek_type(m, &type, &contents);
154                 if (r < 0) {
155                         log_error("Failed to peek type: %s", strerror(-r));
156                         return r;
157                 }
158
159                 if (r == 0) {
160                         if (level <= 1)
161                                 break;
162
163                         r = sd_bus_message_exit_container(m);
164                         if (r < 0) {
165                                 log_error("Failed to exit container: %s", strerror(-r));
166                                 return r;
167                         }
168
169                         level--;
170
171                         prefix = indent(level, flags);
172                         if (!prefix)
173                                 return log_oom();
174
175                         fprintf(f, "%s};\n", prefix);
176                         continue;
177                 }
178
179                 prefix = indent(level, flags);
180                 if (!prefix)
181                         return log_oom();
182
183                 if (bus_type_is_container(type) > 0) {
184                         r = sd_bus_message_enter_container(m, type, contents);
185                         if (r < 0) {
186                                 log_error("Failed to enter container: %s", strerror(-r));
187                                 return r;
188                         }
189
190                         if (type == SD_BUS_TYPE_ARRAY)
191                                 fprintf(f, "%sARRAY \"%s\" {\n", prefix, contents);
192                         else if (type == SD_BUS_TYPE_VARIANT)
193                                 fprintf(f, "%sVARIANT \"%s\" {\n", prefix, contents);
194                         else if (type == SD_BUS_TYPE_STRUCT)
195                                 fprintf(f, "%sSTRUCT \"%s\" {\n", prefix, contents);
196                         else if (type == SD_BUS_TYPE_DICT_ENTRY)
197                                 fprintf(f, "%sDICT_ENTRY \"%s\" {\n", prefix, contents);
198
199                         level ++;
200
201                         continue;
202                 }
203
204                 r = sd_bus_message_read_basic(m, type, &basic);
205                 if (r < 0) {
206                         log_error("Failed to get basic: %s", strerror(-r));
207                         return r;
208                 }
209
210                 assert(r > 0);
211
212                 switch (type) {
213
214                 case SD_BUS_TYPE_BYTE:
215                         fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_highlight_off());
216                         break;
217
218                 case SD_BUS_TYPE_BOOLEAN:
219                         fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_highlight_off());
220                         break;
221
222                 case SD_BUS_TYPE_INT16:
223                         fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_highlight_off());
224                         break;
225
226                 case SD_BUS_TYPE_UINT16:
227                         fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_highlight_off());
228                         break;
229
230                 case SD_BUS_TYPE_INT32:
231                         fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_highlight_off());
232                         break;
233
234                 case SD_BUS_TYPE_UINT32:
235                         fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_highlight_off());
236                         break;
237
238                 case SD_BUS_TYPE_INT64:
239                         fprintf(f, "%sINT64 %s%"PRIi64"%s;\n", prefix, ansi_highlight(), basic.s64, ansi_highlight_off());
240                         break;
241
242                 case SD_BUS_TYPE_UINT64:
243                         fprintf(f, "%sUINT64 %s%"PRIu64"%s;\n", prefix, ansi_highlight(), basic.u64, ansi_highlight_off());
244                         break;
245
246                 case SD_BUS_TYPE_DOUBLE:
247                         fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_highlight_off());
248                         break;
249
250                 case SD_BUS_TYPE_STRING:
251                         fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
252                         break;
253
254                 case SD_BUS_TYPE_OBJECT_PATH:
255                         fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
256                         break;
257
258                 case SD_BUS_TYPE_SIGNATURE:
259                         fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_highlight_off());
260                         break;
261
262                 case SD_BUS_TYPE_UNIX_FD:
263                         fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_highlight_off());
264                         break;
265
266                 default:
267                         assert_not_reached("Unknown basic type.");
268                 }
269         }
270
271         if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY))
272                 fprintf(f, "%s};\n\n", indent(0, flags));
273
274         return 0;
275 }
276
277 static void dump_capabilities(
278                 sd_bus_creds *c,
279                 FILE *f,
280                 const char *name,
281                 int (*has)(sd_bus_creds *c, int capability)) {
282
283         unsigned long i, last_cap;
284         unsigned n = 0;
285         int r;
286
287         assert(c);
288         assert(f);
289         assert(name);
290         assert(has);
291
292         i = 0;
293         r = has(c, i);
294         if (r < 0)
295                 return;
296
297         fprintf(f, "  %s=", name);
298         last_cap = cap_last_cap();
299
300         for (;;) {
301                 if (r > 0) {
302                         _cleanup_cap_free_charp_ char *t;
303
304                         if (n > 0)
305                                 fputc(' ', f);
306                         if (n % 4 == 3)
307                                 fputs("\n          ", f);
308
309                         t = cap_to_name(i);
310                         fprintf(f, "%s", t);
311                         n++;
312                 }
313
314                 i++;
315
316                 if (i > last_cap)
317                         break;
318
319                 r = has(c, i);
320         }
321
322         fputs("\n", f);
323 }
324
325 int bus_creds_dump(sd_bus_creds *c, FILE *f) {
326         bool audit_sessionid_is_set = false, audit_loginuid_is_set = false;
327         const char *u = NULL, *uu = NULL, *s = NULL, *sl = NULL;
328         uid_t owner, audit_loginuid;
329         uint32_t audit_sessionid;
330         char **cmdline = NULL, **well_known = NULL;
331         int r;
332
333         assert(c);
334
335         if (!f)
336                 f = stdout;
337
338         if (c->mask & SD_BUS_CREDS_PID)
339                 fprintf(f, "  PID="PID_FMT, c->pid);
340         if (c->mask & SD_BUS_CREDS_PID_STARTTIME)
341                 fprintf(f, "  PIDStartTime="USEC_FMT, c->pid_starttime);
342         if (c->mask & SD_BUS_CREDS_TID)
343                 fprintf(f, "  TID="PID_FMT, c->tid);
344         if (c->mask & SD_BUS_CREDS_UID)
345                 fprintf(f, "  UID="UID_FMT, c->uid);
346         r = sd_bus_creds_get_owner_uid(c, &owner);
347         if (r >= 0)
348                 fprintf(f, "  OwnerUID="UID_FMT, owner);
349         if (c->mask & SD_BUS_CREDS_GID)
350                 fprintf(f, "  GID="GID_FMT, c->gid);
351
352         if ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID|SD_BUS_CREDS_UID|SD_BUS_CREDS_GID)) || r >= 0)
353                 fputs("\n", f);
354
355         if (c->mask & SD_BUS_CREDS_EXE)
356                 fprintf(f, "  Exe=%s", c->exe);
357         if (c->mask & SD_BUS_CREDS_COMM)
358                 fprintf(f, "  Comm=%s", c->comm);
359         if (c->mask & SD_BUS_CREDS_TID_COMM)
360                 fprintf(f, "  TIDComm=%s", c->tid_comm);
361
362         if (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))
363                 fputs("\n", f);
364
365         if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)
366                 fprintf(f, "  Label=%s", c->label);
367         if (c->mask & SD_BUS_CREDS_DESCRIPTION)
368                 fprintf(f, "  Description=%s", c->description);
369
370         if (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_DESCRIPTION))
371                 fputs("\n", f);
372
373         if (sd_bus_creds_get_cmdline(c, &cmdline) >= 0) {
374                 char **i;
375
376                 fputs("  CommandLine={", f);
377                 STRV_FOREACH(i, cmdline) {
378                         if (i != cmdline)
379                                 fputc(' ', f);
380
381                         fputs(*i, f);
382                 }
383
384                 fputs("}\n", f);
385         }
386
387         if (c->mask & SD_BUS_CREDS_CGROUP)
388                 fprintf(f, "  CGroup=%s", c->cgroup);
389         sd_bus_creds_get_unit(c, &u);
390         if (u)
391                 fprintf(f, "  Unit=%s", u);
392         sd_bus_creds_get_user_unit(c, &uu);
393         if (uu)
394                 fprintf(f, "  UserUnit=%s", uu);
395         sd_bus_creds_get_slice(c, &sl);
396         if (sl)
397                 fprintf(f, "  Slice=%s", sl);
398         sd_bus_creds_get_session(c, &s);
399         if (s)
400                 fprintf(f, "  Session=%s", s);
401
402         if ((c->mask & SD_BUS_CREDS_CGROUP) || u || uu || sl || s)
403                 fputs("\n", f);
404
405         if (sd_bus_creds_get_audit_login_uid(c, &audit_loginuid) >= 0) {
406                 audit_loginuid_is_set = true;
407                 fprintf(f, "  AuditLoginUID="UID_FMT, audit_loginuid);
408         }
409         if (sd_bus_creds_get_audit_session_id(c, &audit_sessionid) >= 0) {
410                 audit_sessionid_is_set = true;
411                 fprintf(f, "  AuditSessionID=%"PRIu32, audit_sessionid);
412         }
413
414         if (audit_loginuid_is_set || audit_sessionid_is_set)
415                 fputs("\n", f);
416
417         if (c->mask & SD_BUS_CREDS_UNIQUE_NAME)
418                 fprintf(f, "  UniqueName=%s", c->unique_name);
419
420         if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) {
421                 char **i;
422
423                 fputs("  WellKnownNames={", f);
424                 STRV_FOREACH(i, well_known) {
425                         if (i != well_known)
426                                 fputc(' ', f);
427
428                         fputs(*i, f);
429                 }
430
431                 fputc('}', f);
432         }
433
434         if (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)
435                 fputc('\n', f);
436
437         dump_capabilities(c, f, "EffectiveCapabilities", sd_bus_creds_has_effective_cap);
438         dump_capabilities(c, f, "PermittedCapabilities", sd_bus_creds_has_permitted_cap);
439         dump_capabilities(c, f, "InheritableCapabilities", sd_bus_creds_has_inheritable_cap);
440         dump_capabilities(c, f, "BoundingCapabilities", sd_bus_creds_has_bounding_cap);
441
442         return 0;
443 }
444
445 /*
446  * For details about the file format, see:
447  *
448  * http://wiki.wireshark.org/Development/LibpcapFileFormat
449  */
450
451 typedef struct _packed_ pcap_hdr_s {
452         uint32_t magic_number;   /* magic number */
453         uint16_t version_major;  /* major version number */
454         uint16_t version_minor;  /* minor version number */
455         int32_t  thiszone;       /* GMT to local correction */
456         uint32_t sigfigs;        /* accuracy of timestamps */
457         uint32_t snaplen;        /* max length of captured packets, in octets */
458         uint32_t network;        /* data link type */
459 } pcap_hdr_t ;
460
461 typedef struct  _packed_ pcaprec_hdr_s {
462         uint32_t ts_sec;         /* timestamp seconds */
463         uint32_t ts_usec;        /* timestamp microseconds */
464         uint32_t incl_len;       /* number of octets of packet saved in file */
465         uint32_t orig_len;       /* actual length of packet */
466 } pcaprec_hdr_t;
467
468 int bus_pcap_header(size_t snaplen, FILE *f) {
469
470         pcap_hdr_t hdr = {
471                 .magic_number = 0xa1b2c3d4U,
472                 .version_major = 2,
473                 .version_minor = 4,
474                 .thiszone = 0, /* UTC */
475                 .sigfigs = 0,
476                 .network = 231, /* D-Bus */
477         };
478
479         if (!f)
480                 f = stdout;
481
482         assert(snaplen > 0);
483         assert((size_t) (uint32_t) snaplen == snaplen);
484
485         hdr.snaplen = (uint32_t) snaplen;
486
487         fwrite(&hdr, 1, sizeof(hdr), f);
488         fflush(f);
489
490         return 0;
491 }
492
493 int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
494         struct bus_body_part *part;
495         pcaprec_hdr_t hdr = {};
496         struct timeval tv;
497         unsigned i;
498         size_t w;
499
500         if (!f)
501                 f = stdout;
502
503         assert(m);
504         assert(snaplen > 0);
505         assert((size_t) (uint32_t) snaplen == snaplen);
506
507         if (m->realtime != 0)
508                 timeval_store(&tv, m->realtime);
509         else
510                 assert_se(gettimeofday(&tv, NULL) >= 0);
511
512         hdr.ts_sec = tv.tv_sec;
513         hdr.ts_usec = tv.tv_usec;
514         hdr.orig_len = BUS_MESSAGE_SIZE(m);
515         hdr.incl_len = MIN(hdr.orig_len, snaplen);
516
517         /* write the pcap header */
518         fwrite(&hdr, 1, sizeof(hdr), f);
519
520         /* write the dbus header */
521         w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen);
522         fwrite(m->header, 1, w, f);
523         snaplen -= w;
524
525         /* write the dbus body */
526         MESSAGE_FOREACH_PART(part, i, m) {
527                 if (snaplen <= 0)
528                         break;
529
530                 w = MIN(part->size, snaplen);
531                 fwrite(part->data, 1, w, f);
532                 snaplen -= w;
533         }
534
535         fflush(f);
536
537         return 0;
538 }