1 /* Combine multiple dump files, either by appending or by merging by timestamp
3 * Written by Scott Renfro <scott@renfro.org> based on
4 * editcap by Richard Sharpe and Guy Harris
6 * Copyright 2013, Scott Renfro <scott[AT]renfro.org>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
17 #define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
24 #include <sys/resource.h>
31 #if defined(__APPLE__)
32 #include <sys/sysctl.h>
36 #include "wtap_opttypes.h"
39 #include <wsutil/filesystem.h>
40 #include "wsutil/os_version_info.h"
41 #include <wsutil/report_message.h>
42 #include <wsutil/wslog.h>
43 #include <wsutil/ws_assert.h>
46 static const char* idb_merge_mode_strings
[] = {
47 /* IDB_MERGE_MODE_NONE */
49 /* IDB_MERGE_MODE_ALL_SAME */
51 /* IDB_MERGE_MODE_ANY_SAME */
53 /* IDB_MERGE_MODE_MAX */
58 merge_string_to_idb_merge_mode(const char *name
)
61 for (i
= 0; i
< IDB_MERGE_MODE_MAX
; i
++) {
62 if (g_strcmp0(name
, idb_merge_mode_strings
[i
]) == 0) {
63 return (idb_merge_mode
) i
;
66 return IDB_MERGE_MODE_MAX
;
70 merge_idb_merge_mode_to_string(const int mode
)
72 if (mode
>= 0 && mode
< IDB_MERGE_MODE_MAX
) {
73 return idb_merge_mode_strings
[mode
];
75 return idb_merge_mode_strings
[(int)IDB_MERGE_MODE_MAX
];
80 cleanup_in_file(merge_in_file_t
*in_file
)
82 ws_assert(in_file
!= NULL
);
84 wtap_close(in_file
->wth
);
87 g_array_free(in_file
->idb_index_map
, true);
88 in_file
->idb_index_map
= NULL
;
90 wtap_rec_cleanup(&in_file
->rec
);
91 ws_buffer_free(&in_file
->frame_buffer
);
95 add_idb_index_map(merge_in_file_t
*in_file
, const unsigned orig_index _U_
, const unsigned found_index
)
97 ws_assert(in_file
!= NULL
);
98 ws_assert(in_file
->idb_index_map
!= NULL
);
101 * we didn't really need the orig_index, since just appending to the array
102 * should result in the orig_index being its location in the array; but we
103 * pass it into this function to do a sanity check here
105 ws_assert(orig_index
== in_file
->idb_index_map
->len
);
107 g_array_append_val(in_file
->idb_index_map
, found_index
);
112 raise_limit(int resource
, unsigned add
)
115 /* For now don't try to raise the hard limit; we could if we
116 * have the appropriate privileges (CAP_SYS_RESOURCE on Linux).
118 if ((getrlimit(resource
, &rl
) == 0) && rl
.rlim_cur
< rl
.rlim_max
) {
119 rlim_t old_cur
= rl
.rlim_cur
;
120 /* What open file descriptor limit do we need? */
121 rl
.rlim_cur
= rl
.rlim_cur
+ add
;
122 /* Check for overflow (unlikely). */
123 rl
.rlim_cur
= MAX(old_cur
, rl
.rlim_cur
);
124 rl
.rlim_cur
= MIN(rl
.rlim_cur
, rl
.rlim_max
);
125 if (setrlimit(resource
, &rl
) == 0) {
128 #if defined(__APPLE__)
129 /* On Leopard, setrlimit(RLIMIT_NOFILE, ...) fails
130 * on attempts to set rlim_cur above OPEN_MAX, even
131 * if rlim_max > OPEN_MAX. OPEN_MAX is 10240.
133 * Starting with some later version (at least Mojave,
134 * possibly earlier), it can be set to kern.maxfilesperproc
135 * from sysctl, which is _usually_ higher than 10240.
137 * In Big Sur and later, it can always be set to rlim_max.
138 * (That is, setrlimit() will return 0 and getrlimit() will
139 * subsequently return the set value; the enforced limit
140 * is the lesser of that and kern.maxfilesperproc.)
142 if (resource
== RLIMIT_NOFILE
) {
143 unsigned int nlimit
= 0;
144 size_t limit_len
= sizeof(nlimit
);
145 if (sysctlbyname("kern.maxfilesperproc", &nlimit
, &limit_len
, NULL
, 0) != 0 || nlimit
< OPEN_MAX
) {
146 rl
.rlim_cur
= OPEN_MAX
;
148 rl
.rlim_cur
= nlimit
;
150 if (setrlimit(RLIMIT_NOFILE
, &rl
) == 0) {
153 if (rl
.rlim_cur
> OPEN_MAX
) {
154 rl
.rlim_cur
= OPEN_MAX
;
155 if (setrlimit(RLIMIT_NOFILE
, &rl
) == 0) {
166 /** Open a number of input files to merge.
168 * @param in_file_count number of entries in in_file_names
169 * @param in_file_names filenames of the input files
170 * @param out_files output pointer with filled file array, or NULL
171 * @param err wiretap error, if failed
172 * @param err_info wiretap error string, if failed
173 * @param err_fileno file on which open failed, if failed
174 * @return The number of input files opened, which can be less than
175 * the number requested if the limit of open file descriptors is reached.
178 merge_open_in_files(unsigned in_file_count
, const char *const *in_file_names
,
179 merge_in_file_t
**out_files
, merge_progress_callback_t
* cb
,
180 int *err
, char **err_info
, unsigned *err_fileno
)
184 size_t files_size
= in_file_count
* sizeof(merge_in_file_t
);
185 merge_in_file_t
*files
;
188 bool try_raise_nofile
= false;
191 files
= (merge_in_file_t
*)g_malloc0(files_size
);
194 while (i
< in_file_count
) {
195 files
[i
].filename
= in_file_names
[i
];
196 files
[i
].wth
= wtap_open_offline(in_file_names
[i
], WTAP_TYPE_AUTO
, err
, err_info
, false);
197 files
[i
].state
= RECORD_NOT_PRESENT
;
198 files
[i
].packet_num
= 0;
201 if (*err
== EMFILE
&& i
> 2) {
202 /* We need at least two opened files to merge things if we
203 * are batch processing. (If there was only one file to open
204 * then we can "merge" a single file so long as we don't get
205 * EMFILE, even though that's pointless.)
208 report_warning("Requested opening %u files but could only open %u: %s\nUsing temporary files to batch process.", in_file_count
, i
, g_strerror(*err
));
210 if (!try_raise_nofile
) {
211 try_raise_nofile
= true;
212 if (raise_limit(RLIMIT_NOFILE
, in_file_count
- i
)) {
216 report_warning("Requested opening %u files but could only open %u: %s\nUsing temporary files to batch process (try ulimit -n to adjust the limit).", in_file_count
, i
, g_strerror(*err
));
219 files_size
= in_file_count
* sizeof(merge_in_file_t
);
220 files
= (merge_in_file_t
*)g_realloc(files
, files_size
);
224 /* Close the files we've already opened. */
225 for (j
= 0; j
< i
; j
++)
226 cleanup_in_file(&files
[j
]);
232 size
= wtap_file_size(files
[i
].wth
, err
);
234 for (j
= 0; j
!= UINT_MAX
&& j
<= i
; j
++)
235 cleanup_in_file(&files
[j
]);
240 wtap_rec_init(&files
[i
].rec
);
241 ws_buffer_init(&files
[i
].frame_buffer
, 1514);
242 files
[i
].size
= size
;
243 files
[i
].idb_index_map
= g_array_new(false, false, sizeof(unsigned));
249 cb
->callback_func(MERGE_EVENT_INPUT_FILES_OPENED
, 0, files
, in_file_count
, cb
->data
);
252 return in_file_count
;
255 /** Close the input files again.
257 * @param in_file_count number of entries in in_files
258 * @param in_files input file array to be closed
261 merge_close_in_files(int in_file_count
, merge_in_file_t in_files
[])
264 for (i
= 0; i
< in_file_count
; i
++) {
265 cleanup_in_file(&in_files
[i
]);
269 /** Select an output frame type based on the input files
271 * If all files have the same frame type, then use that.
272 * Otherwise select WTAP_ENCAP_PER_PACKET. If the selected
273 * output file type doesn't support per packet frame types,
274 * then the wtap_dump_open call will fail with a reasonable
277 * @param file_type output file type
278 * @param in_file_count number of entries in in_files
279 * @param in_files input file array
280 * @return the frame type
283 merge_select_frame_type(const int file_type
, int in_file_count
, merge_in_file_t in_files
[])
286 int selected_frame_type
;
288 selected_frame_type
= wtap_file_encap(in_files
[0].wth
);
289 if (!wtap_dump_can_write_encap(file_type
, selected_frame_type
)) {
290 return WTAP_ENCAP_UNKNOWN
;
293 for (i
= 1; i
< in_file_count
; i
++) {
294 int this_frame_type
= wtap_file_encap(in_files
[i
].wth
);
295 if (!wtap_dump_can_write_encap(file_type
, this_frame_type
)) {
296 return WTAP_ENCAP_UNKNOWN
;
298 if (selected_frame_type
!= this_frame_type
) {
299 selected_frame_type
= WTAP_ENCAP_PER_PACKET
;
304 return selected_frame_type
;
308 * returns true if first argument is earlier than second
311 is_earlier(nstime_t
*l
, nstime_t
*r
) /* XXX, move to nstime.c */
313 if (l
->secs
> r
->secs
) { /* left is later */
315 } else if (l
->secs
< r
->secs
) { /* left is earlier */
317 } else if (l
->nsecs
> r
->nsecs
) { /* tv_sec equal, l.usec later */
320 /* either one < two or one == two
321 * either way, return one
326 /** Read the next packet, in chronological order, from the set of files to
329 * On success, set *err to 0 and return a pointer to the merge_in_file_t
330 * for the file from which the packet was read.
332 * On a read error, set *err to the error and return a pointer to the
333 * merge_in_file_t for the file on which we got an error.
335 * On an EOF (meaning all the files are at EOF), set *err to 0 and return
338 * @param in_file_count number of entries in in_files
339 * @param in_files input file array
340 * @param err wiretap error, if failed
341 * @param err_info wiretap error string, if failed
342 * @return pointer to merge_in_file_t for file from which that packet
343 * came or on which we got a read error, or NULL if we're at EOF on
346 static merge_in_file_t
*
347 merge_read_packet(int in_file_count
, merge_in_file_t in_files
[],
348 int *err
, char **err_info
)
352 nstime_t tv
= NSTIME_INIT_MAX
;
356 * Make sure we have a record available from each file that's not at
357 * EOF, and search for the record with the earliest time stamp or
358 * with no time stamp (those records are treated as earlier than
359 * all other records). Yes, this means you won't get a chronological
360 * merge of those records, but you obviously *can't* get that.
362 for (i
= 0; i
< in_file_count
; i
++) {
365 if (in_files
[i
].state
== RECORD_NOT_PRESENT
) {
367 * No packet available, and we haven't seen an error or EOF yet,
368 * so try to read the next packet.
370 if (!wtap_read(in_files
[i
].wth
, &in_files
[i
].rec
,
371 &in_files
[i
].frame_buffer
, err
, err_info
,
374 in_files
[i
].state
= GOT_ERROR
;
377 in_files
[i
].state
= AT_EOF
;
379 in_files
[i
].state
= RECORD_PRESENT
;
382 if (in_files
[i
].state
== RECORD_PRESENT
) {
383 rec
= &in_files
[i
].rec
;
384 if (!(rec
->presence_flags
& WTAP_HAS_TS
)) {
386 * No time stamp. Pick this record, and stop looking.
391 if (is_earlier(&rec
->ts
, &tv
)) {
393 * This record's time stamp is earlier than any of the
394 * records we've seen so far. Pick it, for now, but
404 /* All the streams are at EOF. Return an EOF indication. */
409 /* We'll need to read another packet from this file. */
410 in_files
[ei
].state
= RECORD_NOT_PRESENT
;
412 /* Count this packet. */
413 in_files
[ei
].packet_num
++;
416 * Return a pointer to the merge_in_file_t of the file from which the
420 return &in_files
[ei
];
423 /** Read the next packet, in file sequence order, from the set of files
426 * On success, set *err to 0 and return a pointer to the merge_in_file_t
427 * for the file from which the packet was read.
429 * On a read error, set *err to the error and return a pointer to the
430 * merge_in_file_t for the file on which we got an error.
432 * On an EOF (meaning all the files are at EOF), set *err to 0 and return
435 * @param in_file_count number of entries in in_files
436 * @param in_files input file array
437 * @param err wiretap error, if failed
438 * @param err_info wiretap error string, if failed
439 * @return pointer to merge_in_file_t for file from which that packet
440 * came or on which we got a read error, or NULL if we're at EOF on
443 static merge_in_file_t
*
444 merge_append_read_packet(int in_file_count
, merge_in_file_t in_files
[],
445 int *err
, char **err_info
)
451 * Find the first file not at EOF, and read the next packet from it.
453 for (i
= 0; i
< in_file_count
; i
++) {
454 if (in_files
[i
].state
== AT_EOF
)
455 continue; /* This file is already at EOF */
456 if (wtap_read(in_files
[i
].wth
, &in_files
[i
].rec
,
457 &in_files
[i
].frame_buffer
, err
, err_info
,
459 break; /* We have a packet */
461 /* Read error - quit immediately. */
462 in_files
[i
].state
= GOT_ERROR
;
465 /* EOF - flag this file as being at EOF, and try the next one. */
466 in_files
[i
].state
= AT_EOF
;
468 if (i
== in_file_count
) {
469 /* All the streams are at EOF. Return an EOF indication. */
475 * Return a pointer to the merge_in_file_t of the file from which the
483 /* creates a section header block for the new output file */
485 create_shb_header(const merge_in_file_t
*in_files
, const unsigned in_file_count
,
486 const char *app_name
)
489 wtap_block_t shb_hdr
;
490 GString
*comment_gstr
;
491 GString
*os_info_str
;
493 wtapng_section_mandatory_t
* shb_data
;
497 shb_hdrs
= wtap_file_get_shb_for_new_file(in_files
[0].wth
);
498 shb_hdr
= g_array_index(shb_hdrs
, wtap_block_t
, 0);
500 comment_gstr
= g_string_new("");
503 * TODO: merge comments from all files
505 * XXX - do we want some way to record which comments, hardware/OS/app
506 * descriptions, IDBs, etc.? came from which files?
509 g_string_append_printf(comment_gstr
, "File created by merging: \n");
511 for (i
= 0; i
< in_file_count
; i
++) {
512 g_string_append_printf(comment_gstr
, "File%d: %s \n",i
+1,in_files
[i
].filename
);
515 os_info_str
= g_string_new("");
516 get_os_version_info(os_info_str
);
518 shb_data
= (wtapng_section_mandatory_t
*)wtap_block_get_mandatory_data(shb_hdr
);
519 shb_data
->section_length
= -1;
520 /* TODO: handle comments from each file being merged */
521 /* XXX: 65535 is the maximum size for an option (hence comment) in pcapng.
522 * Truncate it? Let wiretap/pcapng.c decide what to do? (Currently it
523 * writes nothing without reporting an error.) What if we support other
524 * output file formats later?
526 opt_str
= g_string_free(comment_gstr
, FALSE
);
527 /* XXX: We probably want to prepend (insert at index 0) instead? */
528 wtap_block_add_string_option_owned(shb_hdr
, OPT_COMMENT
, opt_str
);
530 * XXX - and how do we preserve all the OPT_SHB_HARDWARE, OPT_SHB_OS,
531 * and OPT_SHB_USERAPPL values from all the previous files?
533 wtap_block_remove_option(shb_hdr
, OPT_SHB_HARDWARE
);
534 opt_len
= os_info_str
->len
;
535 opt_str
= g_string_free(os_info_str
, FALSE
);
537 wtap_block_set_string_option_value(shb_hdr
, OPT_SHB_OS
, opt_str
, opt_len
); /* UTF-8 string containing the name */
538 /* of the operating system used to create this section. */
542 * No OS information; remove the old version.
544 wtap_block_remove_option(shb_hdr
, OPT_SHB_OS
);
546 wtap_block_set_string_option_value(shb_hdr
, OPT_SHB_USERAPPL
, app_name
, app_name
? strlen(app_name
): 0 ); /* NULL if not available, UTF-8 string containing the name */
547 /* of the application used to create this section. */
553 is_duplicate_idb(const wtap_block_t idb1
, const wtap_block_t idb2
)
555 wtapng_if_descr_mandatory_t
*idb1_mand
, *idb2_mand
;
556 bool have_idb1_value
, have_idb2_value
;
557 uint64_t idb1_if_speed
, idb2_if_speed
;
558 uint8_t idb1_if_tsresol
, idb2_if_tsresol
;
559 uint8_t idb1_if_fcslen
, idb2_if_fcslen
;
560 char *idb1_opt_comment
, *idb2_opt_comment
;
561 char *idb1_if_name
, *idb2_if_name
;
562 char *idb1_if_description
, *idb2_if_description
;
563 char *idb1_if_hardware
, *idb2_if_hardware
;
564 char *idb1_if_os
, *idb2_if_os
;
566 ws_assert(idb1
&& idb2
);
567 idb1_mand
= (wtapng_if_descr_mandatory_t
*)wtap_block_get_mandatory_data(idb1
);
568 idb2_mand
= (wtapng_if_descr_mandatory_t
*)wtap_block_get_mandatory_data(idb2
);
570 ws_debug("merge::is_duplicate_idb() called");
571 ws_debug("idb1_mand->wtap_encap == idb2_mand->wtap_encap: %s",
572 (idb1_mand
->wtap_encap
== idb2_mand
->wtap_encap
) ? "true":"false");
573 if (idb1_mand
->wtap_encap
!= idb2_mand
->wtap_encap
) {
574 /* Clearly not the same interface. */
575 ws_debug("returning false");
579 ws_debug("idb1_mand->time_units_per_second == idb2_mand->time_units_per_second: %s",
580 (idb1_mand
->time_units_per_second
== idb2_mand
->time_units_per_second
) ? "true":"false");
581 if (idb1_mand
->time_units_per_second
!= idb2_mand
->time_units_per_second
) {
583 * Probably not the same interface, and we can't combine them
586 ws_debug("returning false");
590 ws_debug("idb1_mand->tsprecision == idb2_mand->tsprecision: %s",
591 (idb1_mand
->tsprecision
== idb2_mand
->tsprecision
) ? "true":"false");
592 if (idb1_mand
->tsprecision
!= idb2_mand
->tsprecision
) {
594 * Probably not the same interface, and we can't combine them
597 ws_debug("returning false");
601 /* XXX: should snaplen not be compared? */
602 ws_debug("idb1_mand->snap_len == idb2_mand->snap_len: %s",
603 (idb1_mand
->snap_len
== idb2_mand
->snap_len
) ? "true":"false");
604 if (idb1_mand
->snap_len
!= idb2_mand
->snap_len
) {
605 ws_debug("returning false");
609 /* XXX - what do to if we have only one value? */
610 have_idb1_value
= (wtap_block_get_uint64_option_value(idb1
, OPT_IDB_SPEED
, &idb1_if_speed
) == WTAP_OPTTYPE_SUCCESS
);
611 have_idb2_value
= (wtap_block_get_uint64_option_value(idb2
, OPT_IDB_SPEED
, &idb2_if_speed
) == WTAP_OPTTYPE_SUCCESS
);
612 if (have_idb1_value
&& have_idb2_value
) {
613 ws_debug("idb1_if_speed == idb2_if_speed: %s",
614 (idb1_if_speed
== idb2_if_speed
) ? "true":"false");
615 if (idb1_if_speed
!= idb2_if_speed
) {
616 ws_debug("returning false");
621 /* XXX - what do to if we have only one value? */
622 have_idb1_value
= (wtap_block_get_uint8_option_value(idb1
, OPT_IDB_TSRESOL
, &idb1_if_tsresol
) == WTAP_OPTTYPE_SUCCESS
);
623 have_idb2_value
= (wtap_block_get_uint8_option_value(idb2
, OPT_IDB_TSRESOL
, &idb2_if_tsresol
) == WTAP_OPTTYPE_SUCCESS
);
624 if (have_idb1_value
&& have_idb2_value
) {
625 ws_debug("idb1_if_tsresol == idb2_if_tsresol: %s",
626 (idb1_if_tsresol
== idb2_if_tsresol
) ? "true":"false");
627 if (idb1_if_tsresol
!= idb2_if_tsresol
) {
628 ws_debug("returning false");
633 /* XXX - what do to if we have only one value? */
634 have_idb1_value
= (wtap_block_get_uint8_option_value(idb1
, OPT_IDB_FCSLEN
, &idb1_if_fcslen
) == WTAP_OPTTYPE_SUCCESS
);
635 have_idb2_value
= (wtap_block_get_uint8_option_value(idb2
, OPT_IDB_FCSLEN
, &idb2_if_fcslen
) == WTAP_OPTTYPE_SUCCESS
);
636 if (have_idb1_value
&& have_idb2_value
) {
637 ws_debug("idb1_if_fcslen == idb2_if_fcslen: %s",
638 (idb1_if_fcslen
== idb2_if_fcslen
) ? "true":"false");
639 if (idb1_if_fcslen
== idb2_if_fcslen
) {
640 ws_debug("returning false");
646 * XXX - handle multiple comments?
647 * XXX - if the comments are different, just combine them if we
648 * decide the two interfaces are really the same? As comments
649 * can be arbitrary strings added by people, the fact that they're
650 * different doesn't necessarily mean the interfaces are different.
652 have_idb1_value
= (wtap_block_get_nth_string_option_value(idb1
, OPT_COMMENT
, 0, &idb1_opt_comment
) == WTAP_OPTTYPE_SUCCESS
);
653 have_idb2_value
= (wtap_block_get_nth_string_option_value(idb2
, OPT_COMMENT
, 0, &idb2_opt_comment
) == WTAP_OPTTYPE_SUCCESS
);
654 if (have_idb1_value
&& have_idb2_value
) {
655 ws_debug("g_strcmp0(idb1_opt_comment, idb2_opt_comment) == 0: %s",
656 (g_strcmp0(idb1_opt_comment
, idb2_opt_comment
) == 0) ? "true":"false");
657 if (g_strcmp0(idb1_opt_comment
, idb2_opt_comment
) != 0) {
658 ws_debug("returning false");
663 /* XXX - what do to if we have only one value? */
664 have_idb1_value
= (wtap_block_get_string_option_value(idb1
, OPT_IDB_NAME
, &idb1_if_name
) == WTAP_OPTTYPE_SUCCESS
);
665 have_idb2_value
= (wtap_block_get_string_option_value(idb2
, OPT_IDB_NAME
, &idb2_if_name
) == WTAP_OPTTYPE_SUCCESS
);
666 if (have_idb1_value
&& have_idb2_value
) {
667 ws_debug("g_strcmp0(idb1_if_name, idb2_if_name) == 0: %s",
668 (g_strcmp0(idb1_if_name
, idb2_if_name
) == 0) ? "true":"false");
669 if (g_strcmp0(idb1_if_name
, idb2_if_name
) != 0) {
670 ws_debug("returning false");
675 /* XXX - what do to if we have only one value? */
676 have_idb1_value
= (wtap_block_get_string_option_value(idb1
, OPT_IDB_DESCRIPTION
, &idb1_if_description
) == WTAP_OPTTYPE_SUCCESS
);
677 have_idb2_value
= (wtap_block_get_string_option_value(idb2
, OPT_IDB_DESCRIPTION
, &idb2_if_description
) == WTAP_OPTTYPE_SUCCESS
);
678 if (have_idb1_value
&& have_idb2_value
) {
679 ws_debug("g_strcmp0(idb1_if_description, idb2_if_description) == 0: %s",
680 (g_strcmp0(idb1_if_description
, idb2_if_description
) == 0) ? "true":"false");
681 if (g_strcmp0(idb1_if_description
, idb2_if_description
) != 0) {
682 ws_debug("returning false");
687 /* XXX - what do to if we have only one value? */
688 have_idb1_value
= (wtap_block_get_string_option_value(idb1
, OPT_IDB_HARDWARE
, &idb1_if_hardware
) == WTAP_OPTTYPE_SUCCESS
);
689 have_idb2_value
= (wtap_block_get_string_option_value(idb2
, OPT_IDB_HARDWARE
, &idb2_if_hardware
) == WTAP_OPTTYPE_SUCCESS
);
690 if (have_idb1_value
&& have_idb2_value
) {
691 ws_debug("g_strcmp0(idb1_if_hardware, idb2_if_hardware) == 0: %s",
692 (g_strcmp0(idb1_if_hardware
, idb2_if_hardware
) == 0) ? "true":"false");
693 if (g_strcmp0(idb1_if_hardware
, idb2_if_hardware
) != 0) {
694 ws_debug("returning false");
699 /* XXX - what do to if we have only one value? */
700 have_idb1_value
= (wtap_block_get_string_option_value(idb1
, OPT_IDB_OS
, &idb1_if_os
) == WTAP_OPTTYPE_SUCCESS
);
701 have_idb2_value
= (wtap_block_get_string_option_value(idb2
, OPT_IDB_OS
, &idb2_if_os
) == WTAP_OPTTYPE_SUCCESS
);
702 if (have_idb1_value
&& have_idb2_value
) {
703 ws_debug("g_strcmp0(idb1_if_os, idb2_if_os) == 0: %s",
704 (g_strcmp0(idb1_if_os
, idb2_if_os
) == 0) ? "true":"false");
705 if (g_strcmp0(idb1_if_os
, idb2_if_os
) != 0) {
706 ws_debug("returning false");
711 /* does not compare filters nor interface statistics */
712 ws_debug("returning true");
717 * Returns true if all of the input files have duplicate IDBs to the other files.
720 all_idbs_are_duplicates(const merge_in_file_t
*in_files
, const unsigned in_file_count
)
722 wtapng_iface_descriptions_t
*first_idb_list
= NULL
;
723 wtapng_iface_descriptions_t
*other_idb_list
= NULL
;
724 unsigned first_idb_list_size
, other_idb_list_size
;
725 wtap_block_t first_file_idb
, other_file_idb
;
728 ws_assert(in_files
!= NULL
);
730 /* get the first file's info */
731 first_idb_list
= wtap_file_get_idb_info(in_files
[0].wth
);
732 ws_assert(first_idb_list
->interface_data
);
734 first_idb_list_size
= first_idb_list
->interface_data
->len
;
736 /* now compare the other input files with that */
737 for (i
= 1; i
< in_file_count
; i
++) {
738 other_idb_list
= wtap_file_get_idb_info(in_files
[i
].wth
);
739 ws_assert(other_idb_list
->interface_data
);
740 other_idb_list_size
= other_idb_list
->interface_data
->len
;
742 if (other_idb_list_size
!= first_idb_list_size
) {
743 ws_debug("sizes of IDB lists don't match: first=%u, other=%u",
744 first_idb_list_size
, other_idb_list_size
);
745 g_free(other_idb_list
);
746 g_free(first_idb_list
);
750 for (j
= 0; j
< other_idb_list_size
; j
++) {
751 first_file_idb
= g_array_index(first_idb_list
->interface_data
, wtap_block_t
, j
);
752 other_file_idb
= g_array_index(other_idb_list
->interface_data
, wtap_block_t
, j
);
754 if (!is_duplicate_idb(first_file_idb
, other_file_idb
)) {
755 ws_debug("IDBs at index %d do not match, returning false", j
);
756 g_free(other_idb_list
);
757 g_free(first_idb_list
);
761 g_free(other_idb_list
);
764 ws_debug("returning true");
766 g_free(first_idb_list
);
772 * Returns true if the given input_file_idb is a duplicate of an existing one
773 * in the merged_idb_list; it's a duplicate if the interface description data
774 * is all identical to a previous one in another input file. For this
775 * function, the input file IDB's index does NOT need to match the index
776 * location of a previous one to be considered a duplicate; any match is
777 * considered a success. That means it will even match another IDB from its
778 * own (same) input file.
781 find_duplicate_idb(const wtap_block_t input_file_idb
,
782 const wtapng_iface_descriptions_t
*merged_idb_list
,
783 unsigned *found_index
)
785 wtap_block_t merged_idb
;
788 ws_assert(input_file_idb
!= NULL
);
789 ws_assert(merged_idb_list
!= NULL
);
790 ws_assert(merged_idb_list
->interface_data
!= NULL
);
791 ws_assert(found_index
!= NULL
);
793 for (i
= 0; i
< merged_idb_list
->interface_data
->len
; i
++) {
794 merged_idb
= g_array_index(merged_idb_list
->interface_data
, wtap_block_t
, i
);
796 if (is_duplicate_idb(input_file_idb
, merged_idb
)) {
805 /* Adds IDB to merged file info. If pdh is not NULL, also tries to
806 * add the IDB to the file (if the file type supports writing IDBs).
807 * returns true on success
808 * (merged_idb_list->interface_data->len - 1 is the new index) */
810 add_idb_to_merged_file(wtapng_iface_descriptions_t
*merged_idb_list
,
811 const wtap_block_t input_file_idb
, wtap_dumper
*pdh
,
812 int *err
, char **err_info
)
815 wtapng_if_descr_mandatory_t
* idb_mand
;
817 ws_assert(merged_idb_list
!= NULL
);
818 ws_assert(merged_idb_list
->interface_data
!= NULL
);
819 ws_assert(input_file_idb
!= NULL
);
821 idb
= wtap_block_make_copy(input_file_idb
);
822 idb_mand
= (wtapng_if_descr_mandatory_t
*)wtap_block_get_mandatory_data(idb
);
824 /* Don't copy filter or stat information */
825 idb_mand
->num_stat_entries
= 0; /* Number of ISB:s */
826 idb_mand
->interface_statistics
= NULL
;
829 if (wtap_file_type_subtype_supports_block(wtap_dump_file_type_subtype(pdh
), WTAP_BLOCK_IF_ID_AND_INFO
) != BLOCK_NOT_SUPPORTED
) {
830 if (!wtap_dump_add_idb(pdh
, input_file_idb
, err
, err_info
)) {
835 g_array_append_val(merged_idb_list
->interface_data
, idb
);
841 * Create clone IDBs for the merge file for IDBs found in the middle of
842 * input files while processing.
845 process_new_idbs(wtap_dumper
*pdh
, merge_in_file_t
*in_files
, const unsigned in_file_count
, const idb_merge_mode mode
, wtapng_iface_descriptions_t
*merged_idb_list
, int *err
, char **err_info
)
847 wtap_block_t input_file_idb
;
848 unsigned itf_count
, merged_index
;
851 for (i
= 0; i
< in_file_count
; i
++) {
854 * The number below is the global interface number within wth,
855 * not the number within the section. We will do both mappings
856 * in map_rec_interface_id().
858 itf_count
= in_files
[i
].wth
->next_interface_data
;
859 while ((input_file_idb
= wtap_get_next_interface_description(in_files
[i
].wth
)) != NULL
) {
861 /* If we were initially in ALL mode and all the interfaces
862 * did match, then we set the mode to ANY (merge duplicates).
863 * If the interfaces didn't match, then we are still in ALL
864 * mode, but treat that as NONE (write out all IDBs.)
865 * XXX: Should there be separate modes for "match ALL at the start
866 * and ANY later" vs "match ALL at the beginning and NONE later"?
867 * Should there be a two-pass mode for people who want ALL mode to
868 * work for IDBs in the middle of the file? (See #16542)
871 if (mode
== IDB_MERGE_MODE_ANY_SAME
&&
872 find_duplicate_idb(input_file_idb
, merged_idb_list
, &merged_index
))
874 ws_debug("mode ANY set and found a duplicate");
876 * It's the same as a previous IDB, so we're going to "merge"
877 * them into one by adding a map from its old IDB index to the
878 * new one. This will be used later to change the rec
881 add_idb_index_map(&in_files
[i
], itf_count
, merged_index
);
884 ws_debug("mode NONE or ALL set or did not find a duplicate");
886 * This IDB does not match a previous (or we want to save all
887 * IDBs), so add the IDB to the merge file, and add a map of
890 if (add_idb_to_merged_file(merged_idb_list
, input_file_idb
, pdh
, err
, err_info
)) {
891 merged_index
= merged_idb_list
->interface_data
->len
- 1;
892 add_idb_index_map(&in_files
[i
], itf_count
, merged_index
);
897 itf_count
= in_files
[i
].wth
->next_interface_data
;
905 * Create clone IDBs for the merge file, based on the input files and mode.
907 static wtapng_iface_descriptions_t
*
908 generate_merged_idbs(merge_in_file_t
*in_files
, const unsigned in_file_count
, idb_merge_mode
* const mode
)
910 wtapng_iface_descriptions_t
*merged_idb_list
= NULL
;
911 wtap_block_t input_file_idb
;
912 unsigned itf_count
, merged_index
;
915 /* create new IDB info */
916 merged_idb_list
= g_new(wtapng_iface_descriptions_t
,1);
917 merged_idb_list
->interface_data
= g_array_new(false, false, sizeof(wtap_block_t
));
919 if (*mode
== IDB_MERGE_MODE_ALL_SAME
&& all_idbs_are_duplicates(in_files
, in_file_count
)) {
920 ws_debug("mode ALL set and all IDBs are duplicates");
922 /* All files have the same interfaces are the same, so merge any
923 * IDBs found later in the files together with duplicates.
924 * (Note this is also the right thing to do if we have some kind
925 * of two-pass mode and all_idbs_are_duplicates actually did
926 * compare all the IDBs instead of just the ones before any packets.)
928 *mode
= IDB_MERGE_MODE_ANY_SAME
;
930 /* they're all the same, so just get the first file's IDBs */
931 itf_count
= in_files
[0].wth
->next_interface_data
;
932 /* put them in the merged file */
933 while ((input_file_idb
= wtap_get_next_interface_description(in_files
[0].wth
)) != NULL
) {
934 add_idb_to_merged_file(merged_idb_list
, input_file_idb
, NULL
, NULL
, NULL
);
935 merged_index
= merged_idb_list
->interface_data
->len
- 1;
936 add_idb_index_map(&in_files
[0], itf_count
, merged_index
);
937 /* and set all the other file index maps the same way */
938 for (i
= 1; i
< in_file_count
; i
++) {
939 if (wtap_get_next_interface_description(in_files
[i
].wth
) != NULL
) {
940 add_idb_index_map(&in_files
[i
], itf_count
, merged_index
);
942 ws_assert_not_reached();
945 itf_count
= in_files
[0].wth
->next_interface_data
;
949 for (i
= 0; i
< in_file_count
; i
++) {
951 itf_count
= in_files
[i
].wth
->next_interface_data
;
952 while ((input_file_idb
= wtap_get_next_interface_description(in_files
[i
].wth
)) != NULL
) {
954 if (*mode
== IDB_MERGE_MODE_ANY_SAME
&&
955 find_duplicate_idb(input_file_idb
, merged_idb_list
, &merged_index
))
957 ws_debug("mode ANY set and found a duplicate");
959 * It's the same as a previous IDB, so we're going to "merge"
960 * them into one by adding a map from its old IDB index to the new
961 * one. This will be used later to change the rec interface_id.
963 add_idb_index_map(&in_files
[i
], itf_count
, merged_index
);
966 ws_debug("mode NONE set or did not find a duplicate");
968 * This IDB does not match a previous (or we want to save all IDBs),
969 * so add the IDB to the merge file, and add a map of the indices.
971 add_idb_to_merged_file(merged_idb_list
, input_file_idb
, NULL
, NULL
, NULL
);
972 merged_index
= merged_idb_list
->interface_data
->len
- 1;
973 add_idb_index_map(&in_files
[i
], itf_count
, merged_index
);
975 itf_count
= in_files
[i
].wth
->next_interface_data
;
980 return merged_idb_list
;
984 map_rec_interface_id(wtap_rec
*rec
, const merge_in_file_t
*in_file
)
986 unsigned current_interface_id
= 0;
987 ws_assert(rec
!= NULL
);
988 ws_assert(in_file
!= NULL
);
989 ws_assert(in_file
->idb_index_map
!= NULL
);
991 if (rec
->presence_flags
& WTAP_HAS_INTERFACE_ID
) {
992 unsigned section_num
= (rec
->presence_flags
& WTAP_HAS_SECTION_NUMBER
) ? rec
->section_number
: 0;
993 current_interface_id
= wtap_file_get_shb_global_interface_id(in_file
->wth
, section_num
, rec
->rec_header
.packet_header
.interface_id
);
996 if (current_interface_id
>= in_file
->idb_index_map
->len
) {
997 /* this shouldn't happen, but in a malformed input file it could */
998 ws_debug("current_interface_id (%u) >= in_file->idb_index_map->len (%u) (ERROR?)",
999 current_interface_id
, in_file
->idb_index_map
->len
);
1003 rec
->rec_header
.packet_header
.interface_id
= g_array_index(in_file
->idb_index_map
, unsigned, current_interface_id
);
1004 rec
->presence_flags
|= WTAP_HAS_INTERFACE_ID
;
1009 /** Return values from internal merge routines. */
1013 /* below here are true errors */
1014 MERGE_ERR_CANT_OPEN_INFILE
,
1015 MERGE_ERR_CANT_OPEN_OUTFILE
,
1016 MERGE_ERR_CANT_READ_INFILE
,
1017 MERGE_ERR_BAD_PHDR_INTERFACE_ID
,
1018 MERGE_ERR_CANT_WRITE_OUTFILE
,
1019 MERGE_ERR_CANT_CLOSE_OUTFILE
1023 merge_process_packets(wtap_dumper
*pdh
, const int file_type
,
1024 merge_in_file_t
*in_files
, const unsigned in_file_count
,
1025 const bool do_append
,
1026 const idb_merge_mode mode
, unsigned snaplen
,
1027 merge_progress_callback_t
* cb
,
1028 wtapng_iface_descriptions_t
*idb_inf
,
1029 GArray
*nrb_combined
, GArray
*dsb_combined
,
1030 int *err
, char **err_info
, unsigned *err_fileno
,
1031 uint32_t *err_framenum
)
1033 merge_result status
= MERGE_OK
;
1034 merge_in_file_t
*in_file
;
1036 bool stop_flag
= false;
1037 wtap_rec
*rec
, snap_rec
;
1043 in_file
= merge_append_read_packet(in_file_count
, in_files
, err
,
1047 in_file
= merge_read_packet(in_file_count
, in_files
, err
,
1051 if (in_file
== NULL
) {
1052 /* We're at EOF on all input files */
1057 /* I/O error reading from in_file */
1058 if (*err
== WTAP_ERR_SHORT_READ
) {
1060 * A truncated file is not a fatal error, just stop reading
1061 * from that file, report it, and keep going.
1062 * XXX - What about WTAP_ERR_BAD_FILE? Are there *any*
1063 * read errors, as opposed to not being able to open the file
1064 * or write a record, that make us want to abort the entire
1067 report_cfile_read_failure(in_file
->filename
, *err
, *err_info
);
1073 status
= MERGE_ERR_CANT_READ_INFILE
;
1080 stop_flag
= cb
->callback_func(MERGE_EVENT_RECORD_WAS_READ
, count
, in_files
, in_file_count
, cb
->data
);
1083 /* The user decided to abort the merge. */
1084 status
= MERGE_USER_ABORTED
;
1088 rec
= &in_file
->rec
;
1090 if (wtap_file_type_subtype_supports_block(file_type
,
1091 WTAP_BLOCK_IF_ID_AND_INFO
) != BLOCK_NOT_SUPPORTED
) {
1092 if (!process_new_idbs(pdh
, in_files
, in_file_count
, mode
, idb_inf
, err
, err_info
)) {
1093 status
= MERGE_ERR_CANT_WRITE_OUTFILE
;
1098 switch (rec
->rec_type
) {
1100 case REC_TYPE_PACKET
:
1101 if (rec
->presence_flags
& WTAP_HAS_CAP_LEN
) {
1103 rec
->rec_header
.packet_header
.caplen
> snaplen
) {
1105 * The dumper will only write up to caplen bytes out,
1106 * so we only need to change that value, instead of
1107 * cloning the whole packet with fewer bytes.
1109 * XXX: but do we need to change the IDBs' snap_len?
1112 snap_rec
.rec_header
.packet_header
.caplen
= snaplen
;
1120 * Does this file type support identifying the interfaces on
1121 * which packets arrive?
1123 * That mean that the abstract interface provided by libwiretap
1124 * involves WTAP_BLOCK_IF_ID_AND_INFO blocks.
1126 if (wtap_file_type_subtype_supports_block(file_type
,
1127 WTAP_BLOCK_IF_ID_AND_INFO
) != BLOCK_NOT_SUPPORTED
) {
1129 * XXX - We should do this only for record types
1130 * that pertain to a particular interface; for
1131 * now, we hardcode that, but we need to figure
1132 * out a more general way to handle this.
1134 if (rec
->rec_type
== REC_TYPE_PACKET
) {
1135 if (!map_rec_interface_id(rec
, in_file
)) {
1136 status
= MERGE_ERR_BAD_PHDR_INTERFACE_ID
;
1142 * If any DSBs were read before this record, be sure to pass those now
1143 * such that wtap_dump can pick it up.
1145 if (nrb_combined
&& in_file
->wth
->nrbs
) {
1146 GArray
*in_nrb
= in_file
->wth
->nrbs
;
1147 for (unsigned i
= in_file
->nrbs_seen
; i
< in_nrb
->len
; i
++) {
1148 wtap_block_t wblock
= g_array_index(in_nrb
, wtap_block_t
, i
);
1149 g_array_append_val(nrb_combined
, wblock
);
1150 in_file
->nrbs_seen
++;
1153 if (dsb_combined
&& in_file
->wth
->dsbs
) {
1154 GArray
*in_dsb
= in_file
->wth
->dsbs
;
1155 for (unsigned i
= in_file
->dsbs_seen
; i
< in_dsb
->len
; i
++) {
1156 wtap_block_t wblock
= g_array_index(in_dsb
, wtap_block_t
, i
);
1157 g_array_append_val(dsb_combined
, wblock
);
1158 in_file
->dsbs_seen
++;
1162 if (!wtap_dump(pdh
, rec
, ws_buffer_start_ptr(&in_file
->frame_buffer
),
1164 status
= MERGE_ERR_CANT_WRITE_OUTFILE
;
1167 wtap_rec_reset(rec
);
1171 cb
->callback_func(MERGE_EVENT_DONE
, count
, in_files
, in_file_count
, cb
->data
);
1173 if (status
== MERGE_OK
|| status
== MERGE_USER_ABORTED
) {
1174 /* Check for IDBs, NRBs, or DSBs read after the last packet records. */
1175 if (wtap_file_type_subtype_supports_block(file_type
,
1176 WTAP_BLOCK_IF_ID_AND_INFO
) != BLOCK_NOT_SUPPORTED
) {
1177 if (!process_new_idbs(pdh
, in_files
, in_file_count
, mode
, idb_inf
, err
, err_info
)) {
1178 status
= MERGE_ERR_CANT_WRITE_OUTFILE
;
1182 for (unsigned j
= 0; j
< in_file_count
; j
++) {
1183 in_file
= &in_files
[j
];
1184 GArray
*in_nrb
= in_file
->wth
->nrbs
;
1186 for (unsigned i
= in_file
->nrbs_seen
; i
< in_nrb
->len
; i
++) {
1187 wtap_block_t wblock
= g_array_index(in_nrb
, wtap_block_t
, i
);
1188 g_array_append_val(nrb_combined
, wblock
);
1189 in_file
->nrbs_seen
++;
1195 for (unsigned j
= 0; j
< in_file_count
; j
++) {
1196 in_file
= &in_files
[j
];
1197 GArray
*in_dsb
= in_file
->wth
->dsbs
;
1199 for (unsigned i
= in_file
->dsbs_seen
; i
< in_dsb
->len
; i
++) {
1200 wtap_block_t wblock
= g_array_index(in_dsb
, wtap_block_t
, i
);
1201 g_array_append_val(dsb_combined
, wblock
);
1202 in_file
->dsbs_seen
++;
1208 if (status
== MERGE_OK
|| status
== MERGE_USER_ABORTED
) {
1209 if (!wtap_dump_close(pdh
, NULL
, err
, err_info
))
1210 status
= MERGE_ERR_CANT_CLOSE_OUTFILE
;
1213 * We already got some error; no need to report another error on
1216 * Don't overwrite the earlier error.
1219 char *close_err_info
= NULL
;
1220 (void)wtap_dump_close(pdh
, NULL
, &close_err
, &close_err_info
);
1221 g_free(close_err_info
);
1224 /* Close the input files after the output file in case the latter still
1225 * holds references to blocks in the input file (such as the DSB). Even if
1226 * those DSBs are only written when wtap_dump is called and nothing bad will
1227 * happen now, let's keep all pointers in pdh valid for correctness sake. */
1228 merge_close_in_files(in_file_count
, in_files
);
1230 if (status
== MERGE_OK
|| in_file
== NULL
) {
1234 *err_fileno
= (unsigned)(in_file
- in_files
);
1235 *err_framenum
= in_file
->packet_num
;
1242 tempfile_free(void *data
) {
1243 char *filename
= (char*)data
;
1244 ws_unlink(filename
);
1248 #define MAX_MERGE_FILES 10000 // Arbitrary
1250 // NOLINTNEXTLINE(misc-no-recursion)
1251 merge_files_common(const char* out_filename
, /* filename in normal output mode,
1252 optional tempdir in tempfile mode (NULL for OS default) */
1253 char **out_filenamep
, const char *pfx
, /* tempfile mode */
1254 const int file_type
, const char *const *in_filenames
,
1255 const unsigned in_file_count
, const bool do_append
,
1256 idb_merge_mode mode
, unsigned snaplen
,
1257 const char *app_name
, merge_progress_callback_t
* cb
, wtap_compression_type compression_type
)
1259 merge_in_file_t
*in_files
= NULL
;
1260 int frame_type
= WTAP_ENCAP_PER_PACKET
;
1261 unsigned open_file_count
;
1263 char *err_info
= NULL
;
1264 unsigned err_fileno
= 0;
1265 uint32_t err_framenum
= 0;
1266 merge_result status
= MERGE_OK
;
1268 GArray
*shb_hdrs
= NULL
;
1269 wtapng_iface_descriptions_t
*idb_inf
= NULL
;
1270 GArray
*nrb_combined
= NULL
;
1271 GArray
*dsb_combined
= NULL
;
1272 GPtrArray
*temp_files
= NULL
;
1275 ws_assert(in_file_count
> 0);
1276 ws_assert(in_file_count
< MAX_MERGE_FILES
);
1277 ws_assert(in_filenames
!= NULL
);
1279 /* if a callback was given, it has to have a callback function ptr */
1280 ws_assert((cb
!= NULL
) ? (cb
->callback_func
!= NULL
) : true);
1282 ws_debug("merge_files: begin");
1284 for (unsigned total_file_count
= 0; total_file_count
< in_file_count
&& status
== MERGE_OK
; total_file_count
+= open_file_count
) {
1286 /* Reserve a file descriptor for the output; if we run out of file
1287 * descriptors we will end up writing to a temp file instead of the
1288 * file or stdout originally requested, but this simplifies EMFILE
1293 report_cfile_dump_open_failure(out_filename
, errno
, NULL
, file_type
);
1297 /* open the input files */
1298 open_file_count
= merge_open_in_files(in_file_count
- total_file_count
, &in_filenames
[total_file_count
], &in_files
, cb
, &err
, &err_info
, &err_fileno
);
1299 if (open_file_count
== 0) {
1300 ws_debug("merge_open_in_files() failed with err=%d", err
);
1301 report_cfile_open_failure(in_filenames
[err_fileno
], err
, err_info
);
1306 /* Snapshot length not specified - default to the maximum. */
1307 snaplen
= WTAP_MAX_PACKET_SIZE_STANDARD
;
1311 * This doesn't tell us that much. It tells us what to set the outfile's
1312 * encap type to, but that's all - for example, it does *not* tell us
1313 * whether the input files had the same number of IDBs, for the same exact
1314 * interfaces, and only one IDB each, so it doesn't actually tell us
1315 * whether we can merge IDBs into one or not.
1317 * XXX: If an input file is WTAP_ENCAP_PER_PACKET, just because the
1318 * output file format (e.g. pcapng) can write WTAP_ENCAP_PER_PACKET,
1319 * that doesn't mean that the format can actually write all the IDBs.
1321 frame_type
= merge_select_frame_type(file_type
, open_file_count
, in_files
);
1322 ws_debug("got frame_type=%d", frame_type
);
1325 cb
->callback_func(MERGE_EVENT_FRAME_TYPE_SELECTED
, frame_type
, in_files
, open_file_count
, cb
->data
);
1327 /* prepare the outfile */
1328 wtap_dump_params params
= WTAP_DUMP_PARAMS_INIT
;
1329 params
.encap
= frame_type
;
1330 params
.snaplen
= snaplen
;
1332 * Does this file type support identifying the interfaces on
1333 * which packets arrive?
1335 * That mean that the abstract interface provided by libwiretap
1336 * involves WTAP_BLOCK_IF_ID_AND_INFO blocks.
1338 if (wtap_file_type_subtype_supports_block(file_type
,
1339 WTAP_BLOCK_IF_ID_AND_INFO
) != BLOCK_NOT_SUPPORTED
) {
1340 shb_hdrs
= create_shb_header(in_files
, open_file_count
, app_name
);
1341 ws_debug("SHB created");
1343 idb_inf
= generate_merged_idbs(in_files
, open_file_count
, &mode
);
1344 ws_debug("IDB merge operation complete, got %u IDBs", idb_inf
? idb_inf
->interface_data
->len
: 0);
1346 /* We do our own mapping of interface numbers */
1347 params
.shb_iface_to_global
= NULL
;
1348 /* XXX other blocks like ISB are now discarded. */
1349 params
.shb_hdrs
= shb_hdrs
;
1350 params
.idb_inf
= idb_inf
;
1352 if (wtap_file_type_subtype_supports_block(file_type
,
1353 WTAP_BLOCK_NAME_RESOLUTION
) != BLOCK_NOT_SUPPORTED
) {
1354 nrb_combined
= g_array_new(false, false, sizeof(wtap_block_t
));
1355 params
.nrbs_growing
= nrb_combined
;
1357 if (wtap_file_type_subtype_supports_block(file_type
,
1358 WTAP_BLOCK_DECRYPTION_SECRETS
) != BLOCK_NOT_SUPPORTED
) {
1359 dsb_combined
= g_array_new(false, false, sizeof(wtap_block_t
));
1360 params
.dsbs_growing
= dsb_combined
;
1363 if (open_file_count
< in_file_count
) {
1364 if (temp_files
== NULL
) {
1365 temp_files
= g_ptr_array_new_with_free_func(tempfile_free
);
1367 char* temp_filename
;
1368 /* If out_filenamep is not null, then out_filename is the
1369 * desired tempdir, so let's use that.
1371 pdh
= wtap_dump_open_tempfile(out_filenamep
? out_filename
: NULL
,
1373 pfx
? pfx
: "mergecap", file_type
,
1374 compression_type
, ¶ms
, &err
,
1377 g_ptr_array_add(temp_files
, temp_filename
);
1379 } else if (out_filenamep
) {
1380 pdh
= wtap_dump_open_tempfile(out_filename
, out_filenamep
, pfx
, file_type
,
1381 compression_type
, ¶ms
, &err
,
1383 } else if (out_filename
) {
1384 pdh
= wtap_dump_open(out_filename
, file_type
, compression_type
,
1385 ¶ms
, &err
, &err_info
);
1387 pdh
= wtap_dump_open_stdout(file_type
, compression_type
, ¶ms
,
1391 merge_close_in_files(open_file_count
, in_files
);
1393 wtap_block_array_free(shb_hdrs
);
1394 wtap_free_idb_info(idb_inf
);
1396 g_array_free(nrb_combined
, true);
1399 g_array_free(dsb_combined
, true);
1402 g_ptr_array_free(temp_files
, true);
1404 report_cfile_dump_open_failure(out_filename
, err
, err_info
, file_type
);
1409 cb
->callback_func(MERGE_EVENT_READY_TO_MERGE
, 0, in_files
, open_file_count
, cb
->data
);
1411 status
= merge_process_packets(pdh
, file_type
, in_files
, open_file_count
,
1412 do_append
, mode
, snaplen
, cb
,
1413 idb_inf
, nrb_combined
, dsb_combined
,
1415 &err_fileno
, &err_framenum
);
1418 wtap_block_array_free(shb_hdrs
);
1419 wtap_free_idb_info(idb_inf
);
1421 g_array_free(nrb_combined
, true);
1422 nrb_combined
= NULL
;
1425 g_array_free(dsb_combined
, true);
1426 dsb_combined
= NULL
;
1431 if (status
!= MERGE_OK
) {
1433 * Failed. Clean up and return false.
1436 case MERGE_USER_ABORTED
:
1437 /* This isn't an error, so no need to report anything */
1440 case MERGE_ERR_CANT_OPEN_INFILE
:
1441 report_cfile_open_failure(in_filenames
[err_fileno
], err
, err_info
);
1444 case MERGE_ERR_CANT_OPEN_OUTFILE
:
1445 report_cfile_dump_open_failure(out_filename
, err
, err_info
, file_type
);
1448 case MERGE_ERR_CANT_READ_INFILE
:
1449 report_cfile_read_failure(in_filenames
[err_fileno
], err
, err_info
);
1452 case MERGE_ERR_BAD_PHDR_INTERFACE_ID
:
1453 report_failure("Record %u of \"%s\" has an interface ID that does not match any IDB in its file.",
1454 err_framenum
, in_filenames
[err_fileno
]);
1457 case MERGE_ERR_CANT_WRITE_OUTFILE
:
1458 report_cfile_write_failure(in_filenames
[err_fileno
], out_filename
,
1459 err
, err_info
, err_framenum
, file_type
);
1462 case MERGE_ERR_CANT_CLOSE_OUTFILE
:
1463 report_cfile_close_failure(out_filename
, err
, err_info
);
1467 report_failure("Unknown merge_files error %d", status
);
1470 if (temp_files
!= NULL
)
1471 g_ptr_array_free(temp_files
, true);
1475 if (temp_files
!= NULL
) {
1476 // We recurse here, but we're limited by MAX_MERGE_FILES
1477 status
= merge_files_common(out_filename
, out_filenamep
, pfx
,
1478 file_type
, (const char**)temp_files
->pdata
,
1479 temp_files
->len
, do_append
, mode
, snaplen
, app_name
, cb
, compression_type
);
1480 /* If that failed, it has already reported an error */
1481 g_ptr_array_free(temp_files
, true);
1484 return status
== MERGE_OK
;
1488 * Merges the files to an output file whose name is supplied as an argument,
1489 * based on given input, and invokes callback during execution. Returns
1490 * MERGE_OK on success, or a MERGE_ERR_XXX on failure.
1493 merge_files(const char* out_filename
, const int file_type
,
1494 const char *const *in_filenames
, const unsigned in_file_count
,
1495 const bool do_append
, const idb_merge_mode mode
,
1496 unsigned snaplen
, const char *app_name
, merge_progress_callback_t
* cb
, const wtap_compression_type compression_type
)
1498 ws_assert(out_filename
!= NULL
);
1499 ws_assert(in_file_count
> 0);
1500 ws_assert(in_filenames
!= NULL
);
1502 /* #19402: ensure we aren't appending to one of our inputs */
1505 for (i
= 0; i
< in_file_count
; i
++) {
1506 if (files_identical(out_filename
, in_filenames
[i
])) {
1507 report_failure("Output file %s is same as input file %s; "
1508 "appending would create infinite loop",
1509 out_filename
, in_filenames
[i
]);
1515 return merge_files_common(out_filename
, NULL
, NULL
,
1516 file_type
, in_filenames
, in_file_count
,
1517 do_append
, mode
, snaplen
, app_name
, cb
, compression_type
);
1521 * Merges the files to a temporary file based on given input, and invokes
1522 * callback during execution. Returns MERGE_OK on success, or a MERGE_ERR_XXX
1526 merge_files_to_tempfile(const char *tmpdir
, char **out_filenamep
, const char *pfx
,
1527 const int file_type
, const char *const *in_filenames
,
1528 const unsigned in_file_count
, const bool do_append
,
1529 const idb_merge_mode mode
, unsigned snaplen
,
1530 const char *app_name
, merge_progress_callback_t
* cb
)
1532 ws_assert(out_filenamep
!= NULL
);
1534 /* no temporary file name yet */
1535 *out_filenamep
= NULL
;
1537 return merge_files_common(tmpdir
, out_filenamep
, pfx
,
1538 file_type
, in_filenames
, in_file_count
,
1539 do_append
, mode
, snaplen
, app_name
, cb
, WTAP_UNCOMPRESSED
);
1543 * Merges the files to the standard output based on given input, and invokes
1544 * callback during execution. Returns MERGE_OK on success, or a MERGE_ERR_XXX
1548 merge_files_to_stdout(const int file_type
, const char *const *in_filenames
,
1549 const unsigned in_file_count
, const bool do_append
,
1550 const idb_merge_mode mode
, unsigned snaplen
,
1551 const char *app_name
, merge_progress_callback_t
* cb
,
1552 wtap_compression_type compression_type
)
1554 return merge_files_common(NULL
, NULL
, NULL
,
1555 file_type
, in_filenames
, in_file_count
,
1556 do_append
, mode
, snaplen
, app_name
, cb
, compression_type
);
1560 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1565 * indent-tabs-mode: nil
1568 * vi: set shiftwidth=4 tabstop=8 expandtab:
1569 * :indentSize=4:tabSize=8:noTabs=true: