http://bugs.ethereal.com/bugzilla/show_bug.cgi?id=377
[obnox/wireshark/wip.git] / mergecap.c
1 /* Combine two dump files, either by appending or by merging by timestamp
2  *
3  * $Id$
4  *
5  * Written by Scott Renfro <scott@renfro.org> based on
6  * editcap by Richard Sharpe and Guy Harris
7  *
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <errno.h>
17 #include <glib.h>
18
19 #ifdef HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22
23 #ifdef HAVE_SYS_TIME_H
24 #include <sys/time.h>
25 #endif
26
27 #include <string.h>
28 #include "wtap.h"
29
30 #ifdef NEED_GETOPT_H
31 #include "getopt.h"
32 #endif
33
34 #include "svnversion.h"
35 #include "merge.h"
36
37 #ifdef HAVE_IO_H
38 # include <io.h>
39 #endif
40
41 #ifdef HAVE_FCNTL_H
42 #include <fcntl.h>
43 #endif
44
45 /* Win32 needs the O_BINARY flag for open() */
46 #ifndef O_BINARY
47 #define O_BINARY        0
48 #endif
49
50 static int
51 get_natural_int(const char *string, const char *name)
52 {
53   long number;
54   char *p;
55
56   number = strtol(string, &p, 10);
57   if (p == string || *p != '\0') {
58     fprintf(stderr, "mergecap: The specified %s \"%s\" isn't a decimal number\n",
59             name, string);
60     exit(1);
61   }
62   if (number < 0) {
63     fprintf(stderr, "mergecap: The specified %s is a negative number\n",
64             name);
65     exit(1);
66   }
67   if (number > INT_MAX) {
68     fprintf(stderr, "mergecap: The specified %s is too large (greater than %d)\n",
69             name, INT_MAX);
70     exit(1);
71   }
72   return number;
73 }
74
75 static int
76 get_positive_int(const char *string, const char *name)
77 {
78   long number;
79
80   number = get_natural_int(string, name);
81
82   if (number == 0) {
83     fprintf(stderr, "mergecap: The specified %s is zero\n",
84             name);
85     exit(1);
86   }
87
88   return number;
89 }
90
91 /*
92  * Show the usage
93  */
94 static void
95 usage(void)
96 {
97   int i;
98   const char *string;
99
100   printf("Usage: mergecap [-hva] [-s <snaplen>] [-T <encap type>]\n");
101   printf("          [-F <capture type>] -w <outfile> <infile> [...]\n\n");
102   printf("  where\t-h produces this help listing.\n");
103   printf("       \t-v verbose operation, default is silent\n");
104   printf("       \t-a files should be concatenated, not merged\n");
105   printf("       \t     Default merges based on frame timestamps\n");
106   printf("       \t-s <snaplen>: truncate packets to <snaplen> bytes of data\n");
107   printf("       \t-w <outfile>: sets output filename to <outfile>\n");
108   printf("       \t-T <encap type> encapsulation type to use:\n");
109   for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
110       string = wtap_encap_short_string(i);
111       if (string != NULL)
112         printf("       \t     %s - %s\n",
113           string, wtap_encap_string(i));
114   }
115   printf("       \t     default is the same as the first input file\n");
116   printf("       \t-F <capture type> capture file type to write:\n");
117   for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
118     if (wtap_dump_can_open(i))
119       printf("       \t     %s - %s\n",
120         wtap_file_type_short_string(i), wtap_file_type_string(i));
121   }
122   printf("       \t     default is libpcap\n");
123 }
124
125 int
126 main(int argc, char *argv[])
127 {
128   extern char *optarg;
129   extern int   optind;
130   int          opt;
131   gboolean     do_append     = FALSE;
132   gboolean     verbose       = FALSE;
133   int          in_file_count = 0;
134   guint        snaplen = 0;
135   int          file_type = WTAP_FILE_PCAP;      /* default to libpcap format */
136   int          frame_type = -2;
137   int          out_fd;
138   merge_in_file_t   *in_files      = NULL;
139   int          i;
140   wtap        *wth;
141   struct wtap_pkthdr *phdr, snap_phdr;
142   wtap_dumper *pdh;
143   int          open_err, read_err, write_err, close_err;
144   gchar       *err_info;
145   int          err_fileno;
146   char        *out_filename = NULL;
147   gboolean     got_read_error = FALSE, got_write_error = FALSE;
148   int          count;
149
150   /* Process the options first */
151   while ((opt = getopt(argc, argv, "hvas:T:F:w:")) != -1) {
152
153     switch (opt) {
154     case 'w':
155       out_filename = optarg;
156       break;
157
158     case 'a':
159       do_append = !do_append;
160       break;
161
162     case 'T':
163       frame_type = wtap_short_string_to_encap(optarg);
164       if (frame_type < 0) {
165         fprintf(stderr, "mergecap: \"%s\" isn't a valid encapsulation type\n",
166             optarg);
167         exit(1);
168       }
169       break;
170
171     case 'F':
172       file_type = wtap_short_string_to_file_type(optarg);
173       if (file_type < 0) {
174         fprintf(stderr, "mergecap: \"%s\" isn't a valid capture file type\n",
175             optarg);
176         exit(1);
177       }
178       break;
179
180     case 'v':
181       verbose = TRUE;
182       break;
183
184     case 's':
185       snaplen = get_positive_int(optarg, "snapshot length");
186       break;
187
188     case 'h':
189       printf("mergecap version %s"
190 #ifdef SVNVERSION
191           " (" SVNVERSION ")"
192 #endif
193           "\n", VERSION);
194       usage();
195       exit(0);
196       break;
197
198     case '?':              /* Bad options if GNU getopt */
199       usage();
200       return 1;
201       break;
202
203     }
204
205   }
206
207   /* check for proper args; at a minimum, must have an output
208    * filename and one input file
209    */
210   in_file_count = argc - optind;
211   if (!out_filename) {
212     fprintf(stderr, "mergecap: an output filename must be set with -w\n");
213     fprintf(stderr, "          run with -h for help\n");
214     return 1;
215   }
216   if (in_file_count < 1) {
217     fprintf(stderr, "mergecap: No input files were specified\n");
218     return 1;
219   }
220
221   /* open the input files */
222   if (!merge_open_in_files(in_file_count, &argv[optind], &in_files,
223                            &open_err, &err_info, &err_fileno)) {
224     fprintf(stderr, "mergecap: Can't open %s: %s\n", argv[optind + err_fileno],
225         wtap_strerror(open_err));
226     switch (open_err) {
227
228     case WTAP_ERR_UNSUPPORTED:
229     case WTAP_ERR_UNSUPPORTED_ENCAP:
230     case WTAP_ERR_BAD_RECORD:
231       fprintf(stderr, "(%s)\n", err_info);
232       g_free(err_info);
233       break;
234     }
235     return 2;
236   }
237
238   if (verbose) {
239     for (i = 0; i < in_file_count; i++)
240       fprintf(stderr, "mergecap: %s is type %s.\n", argv[optind + i],
241               wtap_file_type_string(wtap_file_type(in_files[i].wth)));
242   }
243
244   if (snaplen == 0) {
245     /*
246      * Snapshot length not specified - default to the maximum of the
247      * snapshot lengths of the input files.
248      */
249     snaplen = merge_max_snapshot_length(in_file_count, in_files);
250   }
251
252   /* set the outfile frame type */
253   if (frame_type == -2) {
254     /*
255      * Default to the appropriate frame type for the input files.
256      */
257     frame_type = merge_select_frame_type(in_file_count, in_files);
258     if (verbose) {
259       if (frame_type == WTAP_ENCAP_PER_PACKET) {
260         /*
261          * Find out why we had to choose WTAP_ENCAP_PER_PACKET.
262          */
263         int first_frame_type, this_frame_type;
264
265         first_frame_type = wtap_file_encap(in_files[0].wth);
266         for (i = 1; i < in_file_count; i++) {
267           this_frame_type = wtap_file_encap(in_files[i].wth);
268           if (first_frame_type != this_frame_type) {
269             fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
270             fprintf(stderr, "          defaulting to WTAP_ENCAP_PER_PACKET\n");
271             fprintf(stderr, "          %s had type %s (%s)\n",
272                     in_files[0].filename,
273                     wtap_encap_string(first_frame_type),
274                     wtap_encap_short_string(first_frame_type));
275             fprintf(stderr, "          %s had type %s (%s)\n",
276                     in_files[i].filename,
277                     wtap_encap_string(this_frame_type),
278                     wtap_encap_short_string(this_frame_type));
279             break;
280           }
281         }
282       }
283       fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
284               wtap_encap_string(frame_type),
285               wtap_encap_short_string(frame_type));
286     }
287   }
288
289   /* open the outfile */
290   if (strncmp(out_filename, "-", 2) == 0) {  
291     /* use stdout as the outfile */
292     out_fd = 1 /*stdout*/;
293   } else {
294     /* open the outfile */
295     out_fd = open(out_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
296     if (out_fd == -1) {
297       fprintf(stderr, "mergecap: Couldn't open output file %s: %s\n",
298               out_filename, strerror(errno));
299       exit(1);
300     }
301   }  
302     
303   /* prepare the outfile */
304   pdh = wtap_dump_fdopen(out_fd, file_type, frame_type, snaplen, &open_err);
305   if (pdh == NULL) {
306     merge_close_in_files(in_file_count, in_files);
307     free(in_files);
308     fprintf(stderr, "mergecap: Can't open or create %s: %s\n", out_filename,
309             wtap_strerror(open_err));
310     exit(1);
311   }
312
313   /* do the merge (or append) */
314   count = 1;
315   for (;;) {
316     if (do_append)
317       wth = merge_append_read_packet(in_file_count, in_files, &read_err,
318                                      &err_info);
319     else
320       wth = merge_read_packet(in_file_count, in_files, &read_err,
321                               &err_info);
322     if (wth == NULL) {
323       if (read_err != 0)
324         got_read_error = TRUE;
325       break;
326     }
327
328     if (verbose)
329       fprintf(stderr, "Record: %u\n", count++);
330
331     /* We simply write it, perhaps after truncating it; we could do other
332      * things, like modify it. */
333     phdr = wtap_phdr(wth);
334     if (snaplen != 0 && phdr->caplen > snaplen) {
335       snap_phdr = *phdr;
336       snap_phdr.caplen = snaplen;
337       phdr = &snap_phdr;
338     }
339
340     if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth),
341          wtap_buf_ptr(wth), &write_err)) {
342       got_write_error = TRUE;
343       break;
344     }
345   }
346
347   merge_close_in_files(in_file_count, in_files);
348   if (!got_read_error && !got_write_error) {
349     if (!wtap_dump_close(pdh, &write_err))
350       got_write_error = TRUE;
351   } else
352     wtap_dump_close(pdh, &close_err);
353
354   if (got_read_error) {
355     /*
356      * Find the file on which we got the error, and report the error.
357      */
358     for (i = 0; i < in_file_count; i++) {
359       if (in_files[i].state == GOT_ERROR) {
360         fprintf(stderr, "mergecap: Error reading %s: %s\n",
361                 in_files[i].filename, wtap_strerror(read_err));
362         switch (read_err) {
363
364         case WTAP_ERR_UNSUPPORTED:
365         case WTAP_ERR_UNSUPPORTED_ENCAP:
366         case WTAP_ERR_BAD_RECORD:
367           fprintf(stderr, "(%s)\n", err_info);
368           g_free(err_info);
369           break;
370         }
371       }
372     }
373   }
374
375   if (got_write_error) {
376     fprintf(stderr, "mergecap: Error writing to outfile: %s\n",
377             wtap_strerror(write_err));
378   }
379
380   free(in_files);
381
382   return (!got_read_error && !got_write_error) ? 0 : 2;
383 }