]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-dump.c
Merge pull request #33154 from yuwata/test-async
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-dump.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/time.h>
4
5 #include "alloc-util.h"
6 #include "bus-dump.h"
7 #include "bus-internal.h"
8 #include "bus-message.h"
9 #include "bus-type.h"
10 #include "cap-list.h"
11 #include "capability-util.h"
12 #include "fileio.h"
13 #include "format-util.h"
14 #include "glyph-util.h"
15 #include "macro.h"
16 #include "pcapng.h"
17 #include "string-util.h"
18 #include "strv.h"
19 #include "terminal-util.h"
20
21 static char *indent(unsigned level, uint64_t flags) {
22 char *p;
23 unsigned n, i = 0;
24
25 n = 0;
26
27 if (flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0)
28 level -= 1;
29
30 if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER)
31 n += 2;
32
33 p = new(char, n + level*8 + 1);
34 if (!p)
35 return NULL;
36
37 if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER) {
38 p[i++] = ' ';
39 p[i++] = ' ';
40 }
41
42 memset(p + i, ' ', level*8);
43 p[i + level*8] = 0;
44
45 return p;
46 }
47
48 _public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) {
49 unsigned level = 1;
50 int r;
51
52 assert_return(m, -EINVAL);
53 assert_return((flags & ~_SD_BUS_MESSAGE_DUMP_KNOWN_FLAGS) == 0, -EINVAL);
54
55 if (!f)
56 f = stdout;
57
58 if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER) {
59 usec_t ts = m->realtime;
60
61 if (ts == 0)
62 ts = now(CLOCK_REALTIME);
63
64 fprintf(f,
65 "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u",
66 m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
67 m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() :
68 m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "",
69 special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET),
70 ansi_normal(),
71
72 ansi_highlight(),
73 bus_message_type_to_string(m->header->type) ?: "(unknown)",
74 ansi_normal(),
75
76 m->header->endian,
77 m->header->flags,
78 m->header->version);
79
80 /* Display synthetic message serial number in a more readable
81 * format than UINT32_MAX */
82 if (BUS_MESSAGE_COOKIE(m) == UINT32_MAX)
83 fprintf(f, " Cookie=-1");
84 else
85 fprintf(f, " Cookie=%" PRIu64, BUS_MESSAGE_COOKIE(m));
86
87 if (m->reply_cookie != 0)
88 fprintf(f, " ReplyCookie=%" PRIu64, m->reply_cookie);
89
90 fprintf(f, " Timestamp=\"%s\"\n", strna(FORMAT_TIMESTAMP_STYLE(ts, TIMESTAMP_US_UTC)));
91
92 if (m->sender)
93 fprintf(f, " Sender=%s%s%s", ansi_highlight(), m->sender, ansi_normal());
94 if (m->destination)
95 fprintf(f, " Destination=%s%s%s", ansi_highlight(), m->destination, ansi_normal());
96 if (m->path)
97 fprintf(f, " Path=%s%s%s", ansi_highlight(), m->path, ansi_normal());
98 if (m->interface)
99 fprintf(f, " Interface=%s%s%s", ansi_highlight(), m->interface, ansi_normal());
100 if (m->member)
101 fprintf(f, " Member=%s%s%s", ansi_highlight(), m->member, ansi_normal());
102
103 if (m->sender || m->destination || m->path || m->interface || m->member)
104 fputs("\n", f);
105
106 if (sd_bus_error_is_set(&m->error))
107 fprintf(f,
108 " ErrorName=%s%s%s"
109 " ErrorMessage=%s\"%s\"%s\n",
110 ansi_highlight_red(), strna(m->error.name), ansi_normal(),
111 ansi_highlight_red(), strna(m->error.message), ansi_normal());
112
113 if (m->monotonic != 0)
114 fprintf(f, " Monotonic="USEC_FMT, m->monotonic);
115 if (m->realtime != 0)
116 fprintf(f, " Realtime="USEC_FMT, m->realtime);
117 if (m->seqnum != 0)
118 fprintf(f, " SequenceNumber=%"PRIu64, m->seqnum);
119
120 if (m->monotonic != 0 || m->realtime != 0 || m->seqnum != 0)
121 fputs("\n", f);
122
123 bus_creds_dump(&m->creds, f, true);
124 }
125
126 r = sd_bus_message_rewind(m, !(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY));
127 if (r < 0)
128 return log_error_errno(r, "Failed to rewind: %m");
129
130 if (!(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
131 _cleanup_free_ char *prefix = NULL;
132
133 prefix = indent(0, flags);
134 if (!prefix)
135 return log_oom();
136
137 fprintf(f, "%sMESSAGE \"%s\" {\n", prefix, strempty(m->root_container.signature));
138 }
139
140 for (;;) {
141 _cleanup_free_ char *prefix = NULL;
142 const char *contents = NULL;
143 char type;
144 union {
145 uint8_t u8;
146 uint16_t u16;
147 int16_t s16;
148 uint32_t u32;
149 int32_t s32;
150 uint64_t u64;
151 int64_t s64;
152 double d64;
153 const char *string;
154 int i;
155 } basic;
156
157 r = sd_bus_message_peek_type(m, &type, &contents);
158 if (r < 0)
159 return log_error_errno(r, "Failed to peek type: %m");
160
161 if (r == 0) {
162 if (level <= 1)
163 break;
164
165 r = sd_bus_message_exit_container(m);
166 if (r < 0)
167 return log_error_errno(r, "Failed to exit container: %m");
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 return log_error_errno(r, "Failed to enter container: %m");
187
188 if (type == SD_BUS_TYPE_ARRAY)
189 fprintf(f, "%sARRAY \"%s\" {\n", prefix, contents);
190 else if (type == SD_BUS_TYPE_VARIANT)
191 fprintf(f, "%sVARIANT \"%s\" {\n", prefix, contents);
192 else if (type == SD_BUS_TYPE_STRUCT)
193 fprintf(f, "%sSTRUCT \"%s\" {\n", prefix, contents);
194 else if (type == SD_BUS_TYPE_DICT_ENTRY)
195 fprintf(f, "%sDICT_ENTRY \"%s\" {\n", prefix, contents);
196
197 level++;
198
199 continue;
200 }
201
202 r = sd_bus_message_read_basic(m, type, &basic);
203 if (r < 0)
204 return log_error_errno(r, "Failed to get basic: %m");
205
206 assert(r > 0);
207
208 switch (type) {
209
210 case SD_BUS_TYPE_BYTE:
211 fprintf(f, "%sBYTE %s%u%s;\n", prefix, ansi_highlight(), basic.u8, ansi_normal());
212 break;
213
214 case SD_BUS_TYPE_BOOLEAN:
215 fprintf(f, "%sBOOLEAN %s%s%s;\n", prefix, ansi_highlight(), true_false(basic.i), ansi_normal());
216 break;
217
218 case SD_BUS_TYPE_INT16:
219 fprintf(f, "%sINT16 %s%i%s;\n", prefix, ansi_highlight(), basic.s16, ansi_normal());
220 break;
221
222 case SD_BUS_TYPE_UINT16:
223 fprintf(f, "%sUINT16 %s%u%s;\n", prefix, ansi_highlight(), basic.u16, ansi_normal());
224 break;
225
226 case SD_BUS_TYPE_INT32:
227 fprintf(f, "%sINT32 %s%i%s;\n", prefix, ansi_highlight(), basic.s32, ansi_normal());
228 break;
229
230 case SD_BUS_TYPE_UINT32:
231 fprintf(f, "%sUINT32 %s%u%s;\n", prefix, ansi_highlight(), basic.u32, ansi_normal());
232 break;
233
234 case SD_BUS_TYPE_INT64:
235 fprintf(f, "%sINT64 %s%"PRIi64"%s;\n", prefix, ansi_highlight(), basic.s64, ansi_normal());
236 break;
237
238 case SD_BUS_TYPE_UINT64:
239 fprintf(f, "%sUINT64 %s%"PRIu64"%s;\n", prefix, ansi_highlight(), basic.u64, ansi_normal());
240 break;
241
242 case SD_BUS_TYPE_DOUBLE:
243 fprintf(f, "%sDOUBLE %s%g%s;\n", prefix, ansi_highlight(), basic.d64, ansi_normal());
244 break;
245
246 case SD_BUS_TYPE_STRING:
247 fprintf(f, "%sSTRING \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal());
248 break;
249
250 case SD_BUS_TYPE_OBJECT_PATH:
251 fprintf(f, "%sOBJECT_PATH \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal());
252 break;
253
254 case SD_BUS_TYPE_SIGNATURE:
255 fprintf(f, "%sSIGNATURE \"%s%s%s\";\n", prefix, ansi_highlight(), basic.string, ansi_normal());
256 break;
257
258 case SD_BUS_TYPE_UNIX_FD:
259 fprintf(f, "%sUNIX_FD %s%i%s;\n", prefix, ansi_highlight(), basic.i, ansi_normal());
260 break;
261
262 default:
263 assert_not_reached();
264 }
265 }
266
267 if (!(flags & SD_BUS_MESSAGE_DUMP_SUBTREE_ONLY)) {
268 _cleanup_free_ char *prefix = NULL;
269
270 prefix = indent(0, flags);
271 if (!prefix)
272 return log_oom();
273
274 fprintf(f, "%s};\n\n", prefix);
275 }
276
277 return 0;
278 }
279
280 static void dump_capabilities(
281 sd_bus_creds *c,
282 FILE *f,
283 const char *name,
284 bool terse,
285 int (*has)(sd_bus_creds *c, int capability)) {
286
287 unsigned long i, last_cap;
288 unsigned n = 0;
289 int r;
290
291 assert(c);
292 assert(f);
293 assert(name);
294 assert(has);
295
296 i = 0;
297 r = has(c, i);
298 if (r < 0)
299 return;
300
301 fprintf(f, "%s%s=%s", terse ? " " : "", name, terse ? "" : ansi_highlight());
302 last_cap = cap_last_cap();
303
304 for (;;) {
305 if (r > 0) {
306
307 if (n > 0)
308 fputc(' ', f);
309 if (n % 4 == 3)
310 fprintf(f, terse ? "\n " : "\n ");
311
312 fprintf(f, "%s", strna(capability_to_name(i)));
313 n++;
314 }
315
316 i++;
317
318 if (i > last_cap)
319 break;
320
321 r = has(c, i);
322 }
323
324 fputs("\n", f);
325
326 if (!terse)
327 fputs(ansi_normal(), f);
328 }
329
330 int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
331 uid_t owner, audit_loginuid;
332 uint32_t audit_sessionid;
333 char **cmdline = NULL, **well_known = NULL;
334 const char *prefix, *color, *suffix, *s;
335 int r, q, v, w, z;
336
337 assert(c);
338
339 if (!f)
340 f = stdout;
341
342 if (terse) {
343 prefix = " ";
344 suffix = "";
345 color = "";
346 } else {
347 const char *off;
348
349 prefix = "";
350 color = ansi_highlight();
351
352 off = ansi_normal();
353 suffix = strjoina(off, "\n");
354 }
355
356 if (c->mask & SD_BUS_CREDS_PID)
357 fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix);
358 if (c->mask & SD_BUS_CREDS_PIDFD)
359 fprintf(f, "%sPIDFD=%syes%s", prefix, color, suffix);
360 if (c->mask & SD_BUS_CREDS_TID)
361 fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix);
362 if (c->mask & SD_BUS_CREDS_PPID) {
363 if (c->ppid == 0)
364 fprintf(f, "%sPPID=%sn/a%s", prefix, color, suffix);
365 else
366 fprintf(f, "%sPPID=%s"PID_FMT"%s", prefix, color, c->ppid, suffix);
367 }
368 if (c->mask & SD_BUS_CREDS_TTY)
369 fprintf(f, "%sTTY=%s%s%s", prefix, color, strna(c->tty), suffix);
370
371 if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID|SD_BUS_CREDS_TTY))))
372 fputs("\n", f);
373
374 if (c->mask & SD_BUS_CREDS_UID)
375 fprintf(f, "%sUID=%s"UID_FMT"%s", prefix, color, c->uid, suffix);
376 if (c->mask & SD_BUS_CREDS_EUID)
377 fprintf(f, "%sEUID=%s"UID_FMT"%s", prefix, color, c->euid, suffix);
378 if (c->mask & SD_BUS_CREDS_SUID)
379 fprintf(f, "%sSUID=%s"UID_FMT"%s", prefix, color, c->suid, suffix);
380 if (c->mask & SD_BUS_CREDS_FSUID)
381 fprintf(f, "%sFSUID=%s"UID_FMT"%s", prefix, color, c->fsuid, suffix);
382 r = sd_bus_creds_get_owner_uid(c, &owner);
383 if (r >= 0)
384 fprintf(f, "%sOwnerUID=%s"UID_FMT"%s", prefix, color, owner, suffix);
385 if (c->mask & SD_BUS_CREDS_GID)
386 fprintf(f, "%sGID=%s"GID_FMT"%s", prefix, color, c->gid, suffix);
387 if (c->mask & SD_BUS_CREDS_EGID)
388 fprintf(f, "%sEGID=%s"GID_FMT"%s", prefix, color, c->egid, suffix);
389 if (c->mask & SD_BUS_CREDS_SGID)
390 fprintf(f, "%sSGID=%s"GID_FMT"%s", prefix, color, c->sgid, suffix);
391 if (c->mask & SD_BUS_CREDS_FSGID)
392 fprintf(f, "%sFSGID=%s"GID_FMT"%s", prefix, color, c->fsgid, suffix);
393
394 if (c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) {
395 fprintf(f, "%sSupplementaryGIDs=%s", prefix, color);
396 for (unsigned i = 0; i < c->n_supplementary_gids; i++)
397 fprintf(f, "%s" GID_FMT, i > 0 ? " " : "", c->supplementary_gids[i]);
398 fprintf(f, "%s", suffix);
399 }
400
401 if (terse && ((c->mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
402 SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
403 SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) || r >= 0))
404 fputs("\n", f);
405
406 if (c->mask & SD_BUS_CREDS_COMM)
407 fprintf(f, "%sComm=%s%s%s", prefix, color, c->comm, suffix);
408 if (c->mask & SD_BUS_CREDS_TID_COMM)
409 fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix);
410 if (c->mask & SD_BUS_CREDS_EXE)
411 fprintf(f, "%sExe=%s%s%s", prefix, color, strna(c->exe), suffix);
412
413 if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM)))
414 fputs("\n", f);
415
416 r = sd_bus_creds_get_cmdline(c, &cmdline);
417 if (r >= 0) {
418 fprintf(f, "%sCommandLine=%s", prefix, color);
419 STRV_FOREACH(i, cmdline) {
420 if (i != cmdline)
421 fputc(' ', f);
422
423 fputs(*i, f);
424 }
425
426 fprintf(f, "%s", suffix);
427 } else if (r != -ENODATA)
428 fprintf(f, "%sCommandLine=%sn/a%s", prefix, color, suffix);
429
430 if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)
431 fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix);
432 if (c->mask & SD_BUS_CREDS_DESCRIPTION)
433 fprintf(f, "%sDescription=%s%s%s", prefix, color, c->description, suffix);
434
435 if (terse && (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_DESCRIPTION)))
436 fputs("\n", f);
437
438 if (c->mask & SD_BUS_CREDS_CGROUP)
439 fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix);
440 s = NULL;
441 r = sd_bus_creds_get_unit(c, &s);
442 if (r != -ENODATA)
443 fprintf(f, "%sUnit=%s%s%s", prefix, color, strna(s), suffix);
444 s = NULL;
445 v = sd_bus_creds_get_slice(c, &s);
446 if (v != -ENODATA)
447 fprintf(f, "%sSlice=%s%s%s", prefix, color, strna(s), suffix);
448 s = NULL;
449 q = sd_bus_creds_get_user_unit(c, &s);
450 if (q != -ENODATA)
451 fprintf(f, "%sUserUnit=%s%s%s", prefix, color, strna(s), suffix);
452 s = NULL;
453 w = sd_bus_creds_get_user_slice(c, &s);
454 if (w != -ENODATA)
455 fprintf(f, "%sUserSlice=%s%s%s", prefix, color, strna(s), suffix);
456 s = NULL;
457 z = sd_bus_creds_get_session(c, &s);
458 if (z != -ENODATA)
459 fprintf(f, "%sSession=%s%s%s", prefix, color, strna(s), suffix);
460
461 if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || r != -ENODATA || q != -ENODATA || v != -ENODATA || w != -ENODATA || z != -ENODATA))
462 fputs("\n", f);
463
464 r = sd_bus_creds_get_audit_login_uid(c, &audit_loginuid);
465 if (r >= 0)
466 fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix);
467 else if (r != -ENODATA)
468 fprintf(f, "%sAuditLoginUID=%sn/a%s", prefix, color, suffix);
469 q = sd_bus_creds_get_audit_session_id(c, &audit_sessionid);
470 if (q >= 0)
471 fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix);
472 else if (q != -ENODATA)
473 fprintf(f, "%sAuditSessionID=%sn/a%s", prefix, color, suffix);
474
475 if (terse && (r != -ENODATA || q != -ENODATA))
476 fputs("\n", f);
477
478 if (c->mask & SD_BUS_CREDS_UNIQUE_NAME)
479 fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix);
480
481 if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) {
482 fprintf(f, "%sWellKnownNames=%s", prefix, color);
483 STRV_FOREACH(i, well_known) {
484 if (i != well_known)
485 fputc(' ', f);
486
487 fputs(*i, f);
488 }
489
490 fprintf(f, "%s", suffix);
491 }
492
493 if (terse && (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known))
494 fputc('\n', f);
495
496 dump_capabilities(c, f, "EffectiveCapabilities", terse, sd_bus_creds_has_effective_cap);
497 dump_capabilities(c, f, "PermittedCapabilities", terse, sd_bus_creds_has_permitted_cap);
498 dump_capabilities(c, f, "InheritableCapabilities", terse, sd_bus_creds_has_inheritable_cap);
499 dump_capabilities(c, f, "BoundingCapabilities", terse, sd_bus_creds_has_bounding_cap);
500
501 return 0;
502 }
503
504 static uint16_t pcapng_optlen(size_t len) {
505 return ALIGN4(len + sizeof(struct pcapng_option));
506 }
507
508 static void pcapng_putopt(FILE *f, uint16_t code, const void *data, size_t len) {
509 struct pcapng_option opt = {
510 .code = code,
511 .length = len,
512 };
513
514 assert(f);
515 assert((uint16_t) len == len);
516 assert(data || len == 0);
517
518 fwrite(&opt, 1, sizeof(opt), f);
519 if (len > 0) {
520 size_t pad = ALIGN4(len) - len;
521
522 fwrite(data, 1, len, f);
523
524 assert(pad < sizeof(uint32_t));
525 while (pad-- > 0)
526 fputc('\0', f);
527 }
528 }
529
530 static void pcapng_section_header(FILE *f, const char *os, const char *app) {
531 uint32_t len;
532
533 assert(f);
534
535 /* determine length of section header and options */
536 len = sizeof(struct pcapng_section);
537 if (os)
538 len += pcapng_optlen(strlen(os));
539 if (app)
540 len += pcapng_optlen(strlen(app));
541 len += pcapng_optlen(0); /* OPT_END */
542 len += sizeof(uint32_t); /* trailer length */
543
544 struct pcapng_section hdr = {
545 .block_type = PCAPNG_SECTION_BLOCK,
546 .block_length = len,
547 .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
548 .major_version = PCAPNG_MAJOR_VERS,
549 .minor_version = PCAPNG_MINOR_VERS,
550 .section_length = UINT64_MAX,
551 };
552
553 fwrite(&hdr, 1, sizeof(hdr), f);
554 if (os)
555 pcapng_putopt(f, PCAPNG_SHB_OS, os, strlen(os));
556 if (app)
557 pcapng_putopt(f, PCAPNG_SHB_USERAPPL, app, strlen(app));
558 pcapng_putopt(f, PCAPNG_OPT_END, NULL, 0);
559 fwrite(&len, 1, sizeof(uint32_t), f);
560 }
561
562 /* Only have a single instance of dbus pseudo interface */
563 static void pcapng_interface_header(FILE *f, size_t snaplen) {
564 uint32_t len;
565
566 assert(f);
567 assert(snaplen > 0);
568 assert((size_t) (uint32_t) snaplen == snaplen);
569
570 /* no options (yet) */
571 len = sizeof(struct pcapng_interface_block) + sizeof(uint32_t);
572 struct pcapng_interface_block hdr = {
573 .block_type = PCAPNG_INTERFACE_BLOCK,
574 .block_length = len,
575 .link_type = 231, /* D-Bus */
576 .snap_len = snaplen,
577 };
578
579 fwrite(&hdr, 1, sizeof(hdr), f);
580 fwrite(&len, 1, sizeof(uint32_t), f);
581 }
582
583 int bus_pcap_header(size_t snaplen, const char *os, const char *info, FILE *f) {
584 if (!f)
585 f = stdout;
586
587 pcapng_section_header(f, os, info);
588 pcapng_interface_header(f, snaplen);
589 return fflush_and_check(f);
590 }
591
592 int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
593 struct bus_body_part *part;
594 size_t msglen, caplen, pad;
595 uint32_t length;
596 uint64_t ts;
597 unsigned i;
598 size_t w;
599
600 if (!f)
601 f = stdout;
602
603 assert(m);
604 assert(snaplen > 0);
605 assert((size_t) (uint32_t) snaplen == snaplen);
606
607 ts = m->realtime ?: now(CLOCK_REALTIME);
608 msglen = BUS_MESSAGE_SIZE(m);
609 caplen = MIN(msglen, snaplen);
610 pad = ALIGN4(caplen) - caplen;
611
612 /* packet block has no options */
613 length = sizeof(struct pcapng_enhance_packet_block)
614 + caplen + pad + sizeof(uint32_t);
615
616 struct pcapng_enhance_packet_block epb = {
617 .block_type = PCAPNG_ENHANCED_PACKET_BLOCK,
618 .block_length = length,
619 .interface_id = 0,
620 .timestamp_hi = (uint32_t)(ts >> 32),
621 .timestamp_lo = (uint32_t)ts,
622 .original_length = msglen,
623 .capture_length = caplen,
624 };
625
626 /* write the pcapng enhanced packet block header */
627 fwrite(&epb, 1, sizeof(epb), f);
628
629 /* write the dbus header */
630 w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen);
631 fwrite(m->header, 1, w, f);
632 snaplen -= w;
633
634 /* write the dbus body */
635 MESSAGE_FOREACH_PART(part, i, m) {
636 if (snaplen <= 0)
637 break;
638
639 w = MIN(part->size, snaplen);
640 fwrite(part->data, 1, w, f);
641 snaplen -= w;
642 }
643
644 while (pad-- > 0)
645 fputc('\0', f);
646
647 /* trailing block length */
648 fwrite(&length, 1, sizeof(uint32_t), f);
649
650 return fflush_and_check(f);
651 }