CMake: API reference fixes.
[metze/wireshark/wip.git] / mergecap.c
1 /* Combine dump files, either by appending or by merging by timestamp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Mergecap written by Scott Renfro <scott@renfro.org> based on
10  * editcap by Richard Sharpe and Guy Harris
11  *
12  */
13
14 #include <config.h>
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <errno.h>
19 #include <glib.h>
20
21 #ifdef HAVE_GETOPT_H
22 #include <getopt.h>
23 #endif
24
25 #include <string.h>
26
27 #include <wiretap/wtap.h>
28
29 #ifndef HAVE_GETOPT_LONG
30 #include <wsutil/wsgetopt.h>
31 #endif
32
33 #include <wsutil/clopts_common.h>
34 #include <wsutil/cmdarg_err.h>
35 #include <wsutil/crash_info.h>
36 #include <wsutil/filesystem.h>
37 #include <wsutil/file_util.h>
38 #include <wsutil/privileges.h>
39 #include <wsutil/strnatcmp.h>
40 #include <version_info.h>
41
42 #ifdef HAVE_PLUGINS
43 #include <wsutil/plugins.h>
44 #endif
45
46 #include <wsutil/report_message.h>
47
48 #include <wiretap/merge.h>
49
50 #ifdef _WIN32
51 #include <wsutil/unicode-utils.h>
52 #endif /* _WIN32 */
53
54 #include "ui/failure_message.h"
55
56 /*
57  * Show the usage
58  */
59 static void
60 print_usage(FILE *output)
61 {
62   fprintf(output, "\n");
63   fprintf(output, "Usage: mergecap [options] -w <outfile>|- <infile> [<infile> ...]\n");
64   fprintf(output, "\n");
65   fprintf(output, "Output:\n");
66   fprintf(output, "  -a                concatenate rather than merge files.\n");
67   fprintf(output, "                    default is to merge based on frame timestamps.\n");
68   fprintf(output, "  -s <snaplen>      truncate packets to <snaplen> bytes of data.\n");
69   fprintf(output, "  -w <outfile>|-    set the output filename to <outfile> or '-' for stdout.\n");
70   fprintf(output, "  -F <capture type> set the output file type; default is pcapng.\n");
71   fprintf(output, "                    an empty \"-F\" option will list the file types.\n");
72   fprintf(output, "  -I <IDB merge mode> set the merge mode for Interface Description Blocks; default is 'all'.\n");
73   fprintf(output, "                    an empty \"-I\" option will list the merge modes.\n");
74   fprintf(output, "\n");
75   fprintf(output, "Miscellaneous:\n");
76   fprintf(output, "  -h                display this help and exit.\n");
77   fprintf(output, "  -v                verbose output.\n");
78 }
79
80 /*
81  * Report an error in command-line arguments.
82  */
83 static void
84 mergecap_cmdarg_err(const char *fmt, va_list ap)
85 {
86   fprintf(stderr, "mergecap: ");
87   vfprintf(stderr, fmt, ap);
88   fprintf(stderr, "\n");
89 }
90
91 /*
92  * Report additional information for an error in command-line arguments.
93  */
94 static void
95 mergecap_cmdarg_err_cont(const char *fmt, va_list ap)
96 {
97   vfprintf(stderr, fmt, ap);
98   fprintf(stderr, "\n");
99 }
100
101 struct string_elem {
102   const char *sstr;     /* The short string */
103   const char *lstr;     /* The long string */
104 };
105
106 static gint
107 string_compare(gconstpointer a, gconstpointer b)
108 {
109   return strcmp(((const struct string_elem *)a)->sstr,
110                 ((const struct string_elem *)b)->sstr);
111 }
112
113 static void
114 string_elem_print(gpointer data, gpointer not_used _U_)
115 {
116   fprintf(stderr, "    %s - %s\n", ((struct string_elem *)data)->sstr,
117           ((struct string_elem *)data)->lstr);
118 }
119
120 /*
121  * General errors and warnings are reported with an console message
122  * in mergecap.
123  */
124 static void
125 failure_warning_message(const char *msg_format, va_list ap)
126 {
127   fprintf(stderr, "mergecap: ");
128   vfprintf(stderr, msg_format, ap);
129   fprintf(stderr, "\n");
130 }
131
132 static void
133 list_capture_types(void) {
134   int i;
135   struct string_elem *captypes;
136   GSList *list = NULL;
137
138   captypes = g_new(struct string_elem,WTAP_NUM_FILE_TYPES_SUBTYPES);
139
140   fprintf(stderr, "mergecap: The available capture file types for the \"-F\" flag are:\n");
141   for (i = 0; i < WTAP_NUM_FILE_TYPES_SUBTYPES; i++) {
142     if (wtap_dump_can_open(i)) {
143       captypes[i].sstr = wtap_file_type_subtype_short_string(i);
144       captypes[i].lstr = wtap_file_type_subtype_string(i);
145       list = g_slist_insert_sorted(list, &captypes[i], string_compare);
146     }
147   }
148   g_slist_foreach(list, string_elem_print, NULL);
149   g_slist_free(list);
150   g_free(captypes);
151 }
152
153 static void
154 list_idb_merge_modes(void) {
155   int i;
156
157   fprintf(stderr, "mergecap: The available IDB merge modes for the \"-I\" flag are:\n");
158   for (i = 0; i < IDB_MERGE_MODE_MAX; i++) {
159     fprintf(stderr, "    %s\n", merge_idb_merge_mode_to_string(i));
160   }
161 }
162
163 static gboolean
164 merge_callback(merge_event event, int num,
165                const merge_in_file_t in_files[], const guint in_file_count,
166                void *data _U_)
167 {
168   guint i;
169
170   switch (event) {
171
172     case MERGE_EVENT_INPUT_FILES_OPENED:
173       for (i = 0; i < in_file_count; i++) {
174         fprintf(stderr, "mergecap: %s is type %s.\n", in_files[i].filename,
175                 wtap_file_type_subtype_string(wtap_file_type_subtype(in_files[i].wth)));
176       }
177       break;
178
179     case MERGE_EVENT_FRAME_TYPE_SELECTED:
180       /* for this event, num = frame_type */
181       if (num == WTAP_ENCAP_PER_PACKET) {
182         /*
183          * Find out why we had to choose WTAP_ENCAP_PER_PACKET.
184          */
185         int first_frame_type, this_frame_type;
186
187         first_frame_type = wtap_file_encap(in_files[0].wth);
188         for (i = 1; i < in_file_count; i++) {
189           this_frame_type = wtap_file_encap(in_files[i].wth);
190           if (first_frame_type != this_frame_type) {
191             fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
192             fprintf(stderr, "          defaulting to WTAP_ENCAP_PER_PACKET\n");
193             fprintf(stderr, "          %s had type %s (%s)\n",
194                     in_files[0].filename,
195                     wtap_encap_string(first_frame_type),
196                     wtap_encap_short_string(first_frame_type));
197             fprintf(stderr, "          %s had type %s (%s)\n",
198                     in_files[i].filename,
199                     wtap_encap_string(this_frame_type),
200                     wtap_encap_short_string(this_frame_type));
201             break;
202           }
203         }
204       }
205       fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
206               wtap_encap_string(num),
207               wtap_encap_short_string(num));
208       break;
209
210     case MERGE_EVENT_READY_TO_MERGE:
211       fprintf(stderr, "mergecap: ready to merge records\n");
212       break;
213
214     case MERGE_EVENT_RECORD_WAS_READ:
215       /* for this event, num = count */
216       fprintf(stderr, "Record: %d\n", num);
217       break;
218
219     case MERGE_EVENT_DONE:
220       fprintf(stderr, "mergecap: merging complete\n");
221       break;
222   }
223
224   /* false = do not stop merging */
225   return FALSE;
226 }
227
228
229 int
230 main(int argc, char *argv[])
231 {
232   GString            *comp_info_str;
233   GString            *runtime_info_str;
234   char               *init_progfile_dir_error;
235   int                 opt;
236   static const struct option long_options[] = {
237       {"help", no_argument, NULL, 'h'},
238       {"version", no_argument, NULL, 'V'},
239       {0, 0, 0, 0 }
240   };
241   gboolean            do_append          = FALSE;
242   gboolean            verbose            = FALSE;
243   int                 in_file_count      = 0;
244   guint32             snaplen            = 0;
245 #ifdef PCAP_NG_DEFAULT
246   int                 file_type          = WTAP_FILE_TYPE_SUBTYPE_PCAPNG; /* default to pcap format */
247 #else
248   int                 file_type          = WTAP_FILE_TYPE_SUBTYPE_PCAP; /* default to pcapng format */
249 #endif
250   int                 err                = 0;
251   gchar              *err_info           = NULL;
252   int                 err_fileno;
253   guint32             err_framenum;
254   char               *out_filename       = NULL;
255   merge_result        status             = MERGE_OK;
256   idb_merge_mode      mode               = IDB_MERGE_MODE_MAX;
257   merge_progress_callback_t cb;
258
259   cmdarg_err_init(mergecap_cmdarg_err, mergecap_cmdarg_err_cont);
260
261 #ifdef _WIN32
262   arg_list_utf_16to8(argc, argv);
263   create_app_running_mutex();
264 #endif /* _WIN32 */
265
266   /* Get the compile-time version information string */
267   comp_info_str = get_compiled_version_info(NULL, NULL);
268
269   /* Get the run-time version information string */
270   runtime_info_str = get_runtime_version_info(NULL);
271
272   /* Add it to the information to be reported on a crash. */
273   ws_add_crash_info("Mergecap (Wireshark) %s\n"
274        "\n"
275        "%s"
276        "\n"
277        "%s",
278     get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
279   g_string_free(comp_info_str, TRUE);
280   g_string_free(runtime_info_str, TRUE);
281
282   /*
283    * Get credential information for later use.
284    */
285   init_process_policies();
286
287   /*
288    * Attempt to get the pathname of the directory containing the
289    * executable file.
290    */
291   init_progfile_dir_error = init_progfile_dir(argv[0], main);
292   if (init_progfile_dir_error != NULL) {
293     fprintf(stderr,
294             "mergecap: Can't get pathname of directory containing the mergecap program: %s.\n",
295             init_progfile_dir_error);
296     g_free(init_progfile_dir_error);
297   }
298
299   init_report_message(failure_warning_message, failure_warning_message,
300                       NULL, NULL, NULL);
301
302   wtap_init(TRUE);
303
304   /* Process the options first */
305   while ((opt = getopt_long(argc, argv, "aF:hI:s:vVw:", long_options, NULL)) != -1) {
306
307     switch (opt) {
308     case 'a':
309       do_append = !do_append;
310       break;
311
312     case 'F':
313       file_type = wtap_short_string_to_file_type_subtype(optarg);
314       if (file_type < 0) {
315         fprintf(stderr, "mergecap: \"%s\" isn't a valid capture file type\n",
316                 optarg);
317         list_capture_types();
318         status = MERGE_ERR_INVALID_OPTION;
319         goto clean_exit;
320       }
321       break;
322
323     case 'h':
324       printf("Mergecap (Wireshark) %s\n"
325              "Merge two or more capture files into one.\n"
326              "See https://www.wireshark.org for more information.\n",
327              get_ws_vcs_version_info());
328       print_usage(stdout);
329       goto clean_exit;
330       break;
331
332     case 'I':
333       mode = merge_string_to_idb_merge_mode(optarg);
334       if (mode == IDB_MERGE_MODE_MAX) {
335         fprintf(stderr, "mergecap: \"%s\" isn't a valid IDB merge mode\n",
336                 optarg);
337         list_idb_merge_modes();
338         status = MERGE_ERR_INVALID_OPTION;
339         goto clean_exit;
340       }
341       break;
342
343     case 's':
344       snaplen = get_nonzero_guint32(optarg, "snapshot length");
345       break;
346
347     case 'v':
348       verbose = TRUE;
349       break;
350
351     case 'V':
352       comp_info_str = get_compiled_version_info(NULL, NULL);
353       runtime_info_str = get_runtime_version_info(NULL);
354       show_version("Mergecap (Wireshark)", comp_info_str, runtime_info_str);
355       g_string_free(comp_info_str, TRUE);
356       g_string_free(runtime_info_str, TRUE);
357       goto clean_exit;
358       break;
359
360     case 'w':
361       out_filename = optarg;
362       break;
363
364     case '?':              /* Bad options if GNU getopt */
365       switch(optopt) {
366       case'F':
367         list_capture_types();
368         break;
369       case'I':
370         list_idb_merge_modes();
371         break;
372       default:
373         print_usage(stderr);
374       }
375       status = MERGE_ERR_INVALID_OPTION;
376       goto clean_exit;
377       break;
378     }
379   }
380
381   cb.callback_func = merge_callback;
382   cb.data = NULL;
383
384   /* check for proper args; at a minimum, must have an output
385    * filename and one input file
386    */
387   in_file_count = argc - optind;
388   if (!out_filename) {
389     fprintf(stderr, "mergecap: an output filename must be set with -w\n");
390     fprintf(stderr, "          run with -h for help\n");
391     status = MERGE_ERR_INVALID_OPTION;
392     goto clean_exit;
393   }
394   if (in_file_count < 1) {
395     fprintf(stderr, "mergecap: No input files were specified\n");
396     return 1;
397   }
398
399   /* setting IDB merge mode must use PCAPNG output */
400   if (mode != IDB_MERGE_MODE_MAX && file_type != WTAP_FILE_TYPE_SUBTYPE_PCAPNG) {
401     fprintf(stderr, "The IDB merge mode can only be used with PCAPNG output format\n");
402     status = MERGE_ERR_INVALID_OPTION;
403     goto clean_exit;
404   }
405
406   /* if they didn't set IDB merge mode, set it to our default */
407   if (mode == IDB_MERGE_MODE_MAX) {
408     mode = IDB_MERGE_MODE_ALL_SAME;
409   }
410
411   /* open the outfile */
412   if (strcmp(out_filename, "-") == 0) {
413     /* merge the files to the standard output */
414     status = merge_files_to_stdout(file_type,
415                                    (const char *const *) &argv[optind],
416                                    in_file_count, do_append, mode, snaplen,
417                                    "mergecap", verbose ? &cb : NULL,
418                                    &err, &err_info, &err_fileno, &err_framenum);
419   } else {
420     /* merge the files to the outfile */
421     status = merge_files(out_filename, file_type,
422                          (const char *const *) &argv[optind], in_file_count,
423                          do_append, mode, snaplen, "mergecap", verbose ? &cb : NULL,
424                          &err, &err_info, &err_fileno, &err_framenum);
425   }
426
427   switch (status) {
428     case MERGE_OK:
429       break;
430
431     case MERGE_USER_ABORTED:
432       /* we don't catch SIGINT/SIGTERM (yet?), so we couldn't have aborted */
433       g_assert(FALSE);
434       break;
435
436     case MERGE_ERR_CANT_OPEN_INFILE:
437       cfile_open_failure_message("mergecap", argv[optind + err_fileno],
438                                  err, err_info);
439       break;
440
441     case MERGE_ERR_CANT_OPEN_OUTFILE:
442       cfile_dump_open_failure_message("mergecap", out_filename, err, file_type);
443       break;
444
445     case MERGE_ERR_CANT_READ_INFILE:
446       cfile_read_failure_message("mergecap", argv[optind + err_fileno],
447                                  err, err_info);
448       break;
449
450     case MERGE_ERR_BAD_PHDR_INTERFACE_ID:
451       cmdarg_err("Record %u of \"%s\" has an interface ID that does not match any IDB in its file.",
452                  err_framenum, argv[optind + err_fileno]);
453       break;
454
455     case MERGE_ERR_CANT_WRITE_OUTFILE:
456        cfile_write_failure_message("mergecap", argv[optind + err_fileno],
457                                    out_filename, err, err_info, err_framenum,
458                                    file_type);
459        break;
460
461     case MERGE_ERR_CANT_CLOSE_OUTFILE:
462         cfile_close_failure_message(out_filename, err);
463         break;
464
465     default:
466       cmdarg_err("Unknown merge_files error %d", status);
467       break;
468   }
469
470 clean_exit:
471   wtap_cleanup();
472   free_progdirs();
473   return (status == MERGE_OK) ? 0 : 2;
474 }
475
476 /*
477  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
478  *
479  * Local variables:
480  * c-basic-offset: 2
481  * tab-width: 8
482  * indent-tabs-mode: nil
483  * End:
484  *
485  * vi: set shiftwidth=2 tabstop=8 expandtab:
486  * :indentSize=2:tabSize=8:noTabs=true:
487  */
488