1 /* Combine dump files, either by appending or by merging by timestamp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
9 * Mergecap written by Scott Renfro <scott@renfro.org> based on
10 * editcap by Richard Sharpe and Guy Harris
15 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
21 #include <wsutil/ws_getopt.h>
25 #include <wiretap/wtap.h>
27 #include <wsutil/clopts_common.h>
28 #include <wsutil/cmdarg_err.h>
29 #include <wsutil/filesystem.h>
30 #include <wsutil/file_util.h>
31 #include <wsutil/privileges.h>
32 #include <wsutil/strnatcmp.h>
33 #include <wsutil/ws_assert.h>
34 #include <wsutil/wslog.h>
37 #include <wsutil/version_info.h>
40 #include <wsutil/plugins.h>
43 #include <wsutil/report_message.h>
45 #include <wiretap/merge.h>
47 #include "ui/failure_message.h"
53 print_usage(FILE *output
)
55 fprintf(output
, "\n");
56 fprintf(output
, "Usage: mergecap [options] -w <outfile>|- <infile> [<infile> ...]\n");
57 fprintf(output
, "\n");
58 fprintf(output
, "Output:\n");
59 fprintf(output
, " -a concatenate rather than merge files.\n");
60 fprintf(output
, " default is to merge based on frame timestamps.\n");
61 fprintf(output
, " -s <snaplen> truncate packets to <snaplen> bytes of data.\n");
62 fprintf(output
, " -w <outfile>|- set the output filename to <outfile> or '-' for stdout.\n");
63 fprintf(output
, " -F <capture type> set the output file type; default is pcapng.\n");
64 fprintf(output
, " an empty \"-F\" option will list the file types.\n");
65 fprintf(output
, " -I <IDB merge mode> set the merge mode for Interface Description Blocks; default is 'all'.\n");
66 fprintf(output
, " an empty \"-I\" option will list the merge modes.\n");
67 fprintf(output
, "\n");
68 fprintf(output
, "Miscellaneous:\n");
69 fprintf(output
, " -h, --help display this help and exit.\n");
70 fprintf(output
, " -V verbose output.\n");
71 fprintf(output
, " -v, --version print version information and exit.\n");
75 * Report an error in command-line arguments.
78 mergecap_cmdarg_err(const char *fmt
, va_list ap
)
80 fprintf(stderr
, "mergecap: ");
81 vfprintf(stderr
, fmt
, ap
);
82 fprintf(stderr
, "\n");
86 * Report additional information for an error in command-line arguments.
89 mergecap_cmdarg_err_cont(const char *fmt
, va_list ap
)
91 vfprintf(stderr
, fmt
, ap
);
92 fprintf(stderr
, "\n");
96 list_capture_types(void) {
97 GArray
*writable_type_subtypes
;
99 fprintf(stderr
, "mergecap: The available capture file types for the \"-F\" flag are:\n");
100 writable_type_subtypes
= wtap_get_writable_file_types_subtypes(FT_SORT_BY_NAME
);
101 for (guint i
= 0; i
< writable_type_subtypes
->len
; i
++) {
102 int ft
= g_array_index(writable_type_subtypes
, int, i
);
103 fprintf(stderr
, " %s - %s\n", wtap_file_type_subtype_name(ft
),
104 wtap_file_type_subtype_description(ft
));
106 g_array_free(writable_type_subtypes
, TRUE
);
110 list_idb_merge_modes(void) {
113 fprintf(stderr
, "mergecap: The available IDB merge modes for the \"-I\" flag are:\n");
114 for (i
= 0; i
< IDB_MERGE_MODE_MAX
; i
++) {
115 fprintf(stderr
, " %s\n", merge_idb_merge_mode_to_string(i
));
120 merge_callback(merge_event event
, int num
,
121 const merge_in_file_t in_files
[], const guint in_file_count
,
128 case MERGE_EVENT_INPUT_FILES_OPENED
:
129 for (i
= 0; i
< in_file_count
; i
++) {
130 fprintf(stderr
, "mergecap: %s is type %s.\n", in_files
[i
].filename
,
131 wtap_file_type_subtype_description(wtap_file_type_subtype(in_files
[i
].wth
)));
135 case MERGE_EVENT_FRAME_TYPE_SELECTED
:
136 /* for this event, num = frame_type */
137 if (num
== WTAP_ENCAP_PER_PACKET
) {
139 * Find out why we had to choose WTAP_ENCAP_PER_PACKET.
141 int first_frame_type
, this_frame_type
;
143 first_frame_type
= wtap_file_encap(in_files
[0].wth
);
144 for (i
= 1; i
< in_file_count
; i
++) {
145 this_frame_type
= wtap_file_encap(in_files
[i
].wth
);
146 if (first_frame_type
!= this_frame_type
) {
147 fprintf(stderr
, "mergecap: multiple frame encapsulation types detected\n");
148 fprintf(stderr
, " defaulting to WTAP_ENCAP_PER_PACKET\n");
149 fprintf(stderr
, " %s had type %s (%s)\n",
150 in_files
[0].filename
,
151 wtap_encap_description(first_frame_type
),
152 wtap_encap_name(first_frame_type
));
153 fprintf(stderr
, " %s had type %s (%s)\n",
154 in_files
[i
].filename
,
155 wtap_encap_description(this_frame_type
),
156 wtap_encap_name(this_frame_type
));
161 fprintf(stderr
, "mergecap: selected frame_type %s (%s)\n",
162 wtap_encap_description(num
),
163 wtap_encap_name(num
));
166 case MERGE_EVENT_READY_TO_MERGE
:
167 fprintf(stderr
, "mergecap: ready to merge records\n");
170 case MERGE_EVENT_RECORD_WAS_READ
:
171 /* for this event, num = count */
172 fprintf(stderr
, "Record: %d\n", num
);
175 case MERGE_EVENT_DONE
:
176 fprintf(stderr
, "mergecap: merging complete\n");
180 /* false = do not stop merging */
185 main(int argc
, char *argv
[])
187 char *configuration_init_error
;
188 static const struct report_message_routines mergecap_report_routines
= {
191 open_failure_message
,
192 read_failure_message
,
193 write_failure_message
,
194 cfile_open_failure_message
,
195 cfile_dump_open_failure_message
,
196 cfile_read_failure_message
,
197 cfile_write_failure_message
,
198 cfile_close_failure_message
201 static const struct ws_option long_options
[] = {
202 {"help", ws_no_argument
, NULL
, 'h'},
203 {"version", ws_no_argument
, NULL
, 'v'},
206 gboolean do_append
= FALSE
;
207 gboolean verbose
= FALSE
;
208 int in_file_count
= 0;
210 int file_type
= WTAP_FILE_TYPE_SUBTYPE_UNKNOWN
;
212 gchar
*err_info
= NULL
;
214 guint32 err_framenum
;
215 char *out_filename
= NULL
;
216 merge_result status
= MERGE_OK
;
217 idb_merge_mode mode
= IDB_MERGE_MODE_MAX
;
218 merge_progress_callback_t cb
;
220 cmdarg_err_init(mergecap_cmdarg_err
, mergecap_cmdarg_err_cont
);
222 /* Initialize log handler early so we can have proper logging during startup. */
223 ws_log_init("mergecap", vcmdarg_err
);
225 /* Early logging command-line initialization. */
226 ws_log_parse_args(&argc
, argv
, vcmdarg_err
, 1);
228 ws_noisy("Finished log init and parsing command line log arguments");
231 create_app_running_mutex();
234 /* Initialize the version information. */
235 ws_init_version_info("Mergecap", NULL
, NULL
);
238 * Get credential information for later use.
240 init_process_policies();
243 * Attempt to get the pathname of the directory containing the
246 configuration_init_error
= configuration_init(argv
[0], NULL
);
247 if (configuration_init_error
!= NULL
) {
249 "mergecap: Can't get pathname of directory containing the mergecap program: %s.\n",
250 configuration_init_error
);
251 g_free(configuration_init_error
);
254 init_report_message("mergecap", &mergecap_report_routines
);
258 /* Process the options first */
259 while ((opt
= ws_getopt_long(argc
, argv
, "aF:hI:s:vVw:", long_options
, NULL
)) != -1) {
263 do_append
= !do_append
;
267 file_type
= wtap_name_to_file_type_subtype(ws_optarg
);
269 fprintf(stderr
, "mergecap: \"%s\" isn't a valid capture file type\n",
271 list_capture_types();
272 status
= MERGE_ERR_INVALID_OPTION
;
278 show_help_header("Merge two or more capture files into one.");
284 mode
= merge_string_to_idb_merge_mode(ws_optarg
);
285 if (mode
== IDB_MERGE_MODE_MAX
) {
286 fprintf(stderr
, "mergecap: \"%s\" isn't a valid IDB merge mode\n",
288 list_idb_merge_modes();
289 status
= MERGE_ERR_INVALID_OPTION
;
295 snaplen
= get_nonzero_guint32(ws_optarg
, "snapshot length");
308 out_filename
= ws_optarg
;
311 case '?': /* Bad options if GNU getopt */
314 list_capture_types();
317 list_idb_merge_modes();
322 status
= MERGE_ERR_INVALID_OPTION
;
328 /* Default to pcapng when writing. */
329 if (file_type
== WTAP_FILE_TYPE_SUBTYPE_UNKNOWN
)
330 file_type
= wtap_pcapng_file_type_subtype();
332 cb
.callback_func
= merge_callback
;
335 /* check for proper args; at a minimum, must have an output
336 * filename and one input file
338 in_file_count
= argc
- ws_optind
;
340 fprintf(stderr
, "mergecap: an output filename must be set with -w\n");
341 fprintf(stderr
, " run with -h for help\n");
342 status
= MERGE_ERR_INVALID_OPTION
;
345 if (in_file_count
< 1) {
346 fprintf(stderr
, "mergecap: No input files were specified\n");
351 * Setting IDB merge mode must use a file format that supports
352 * (and thus requires) interface ID and information blocks.
354 if (mode
!= IDB_MERGE_MODE_MAX
&&
355 wtap_file_type_subtype_supports_block(file_type
, WTAP_BLOCK_IF_ID_AND_INFO
) == BLOCK_NOT_SUPPORTED
) {
356 fprintf(stderr
, "The IDB merge mode can only be used with an output format that identifies interfaces\n");
357 status
= MERGE_ERR_INVALID_OPTION
;
361 /* if they didn't set IDB merge mode, set it to our default */
362 if (mode
== IDB_MERGE_MODE_MAX
) {
363 mode
= IDB_MERGE_MODE_ALL_SAME
;
366 /* open the outfile */
367 if (strcmp(out_filename
, "-") == 0) {
368 /* merge the files to the standard output */
369 status
= merge_files_to_stdout(file_type
,
370 (const char *const *) &argv
[ws_optind
],
371 in_file_count
, do_append
, mode
, snaplen
,
372 get_appname_and_version(),
373 verbose
? &cb
: NULL
,
374 &err
, &err_info
, &err_fileno
, &err_framenum
);
376 /* merge the files to the outfile */
377 status
= merge_files(out_filename
, file_type
,
378 (const char *const *) &argv
[ws_optind
], in_file_count
,
379 do_append
, mode
, snaplen
, get_appname_and_version(),
380 verbose
? &cb
: NULL
,
381 &err
, &err_info
, &err_fileno
, &err_framenum
);
388 case MERGE_USER_ABORTED
:
389 /* we don't catch SIGINT/SIGTERM (yet?), so we couldn't have aborted */
390 ws_assert_not_reached();
393 case MERGE_ERR_CANT_OPEN_INFILE
:
394 cfile_open_failure_message(argv
[ws_optind
+ err_fileno
], err
, err_info
);
397 case MERGE_ERR_CANT_OPEN_OUTFILE
:
398 cfile_dump_open_failure_message(out_filename
, err
, err_info
, file_type
);
401 case MERGE_ERR_CANT_READ_INFILE
:
402 cfile_read_failure_message(argv
[ws_optind
+ err_fileno
], err
, err_info
);
405 case MERGE_ERR_BAD_PHDR_INTERFACE_ID
:
406 cmdarg_err("Record %u of \"%s\" has an interface ID that does not match any IDB in its file.",
407 err_framenum
, argv
[ws_optind
+ err_fileno
]);
410 case MERGE_ERR_CANT_WRITE_OUTFILE
:
411 cfile_write_failure_message(argv
[ws_optind
+ err_fileno
], out_filename
,
412 err
, err_info
, err_framenum
, file_type
);
415 case MERGE_ERR_CANT_CLOSE_OUTFILE
:
416 cfile_close_failure_message(out_filename
, err
, err_info
);
419 case MERGE_ERR_INVALID_OPTION
:
421 cmdarg_err("%s", err_info
);
424 cmdarg_err("Unspecified error with merge option");
429 cmdarg_err("Unknown merge_files error %d", status
);
436 return (status
== MERGE_OK
) ? 0 : 2;