merge: remove code duplication in merge.c and mergecap.c (put it in merge.c),
[obnox/wireshark/wip.git] / merge.c
1 /* Combine two dump files, either by appending or by merging by timestamp
2  *
3  * $Id: merge.c,v 1.3 2004/06/18 10:01:59 ulfl Exp $
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 <glib.h>
17
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21
22 #ifdef HAVE_SYS_TIME_H
23 #include <sys/time.h>
24 #endif
25
26 #include <string.h>
27 #include "wtap.h"
28 #include "merge.h"
29
30 /*
31  * Global variables
32  */
33 int merge_verbose = VERBOSE_NONE;
34
35
36 /*
37  * Routine to write frame to output file
38  */
39 static gboolean
40 write_frame(wtap *wth, merge_out_file_t *out_file, int *err)
41 {
42   const struct wtap_pkthdr *phdr = wtap_phdr(wth);
43   struct wtap_pkthdr snap_phdr;
44
45   if (merge_verbose == VERBOSE_ALL)
46     fprintf(stderr, "Record: %u\n", out_file->count++);
47
48   /* We simply write it, perhaps after truncating it; we could do other
49    * things, like modify it. */
50   if (out_file->snaplen != 0 && phdr->caplen > out_file->snaplen) {
51     snap_phdr = *phdr;
52     snap_phdr.caplen = out_file->snaplen;
53     phdr = &snap_phdr;
54   }
55
56   if (!wtap_dump(out_file->pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth), err)) {
57     if (merge_verbose == VERBOSE_ERRORS)
58       fprintf(stderr, "mergecap: Error writing to %s: %s\n",
59             out_file->filename, wtap_strerror(*err));
60     return FALSE;
61   }
62
63   return TRUE;
64 }
65
66
67 static gboolean
68 append_loop(wtap *wth, int count, merge_out_file_t *out_file, int *err,
69     gchar **err_info)
70 {
71         long            data_offset;
72         int             loop = 0;
73
74         /* Start by clearing error flag */
75         *err = 0;
76
77         while ( (wtap_read(wth, err, err_info, &data_offset)) ) {
78                 if(!write_frame(wth, out_file, err))
79             return FALSE;   /* failure */
80                 if (count > 0 && ++loop >= count)
81                         break;
82         }
83
84     if (*err == 0) {
85                 return TRUE;    /* success */
86     } else {
87                 return FALSE;   /* failure */
88     }
89 }
90
91
92
93 /*
94  * routine to concatenate files
95  */
96 boolean
97 merge_append_files(int count, merge_in_file_t in_files[], merge_out_file_t *out_file, int *err)
98 {
99   int i;
100   gchar *err_info;
101
102   for (i = 0; i < count; i++) {
103     if (!append_loop(in_files[i].wth, 0, out_file, err, &err_info)) {
104         if (merge_verbose == VERBOSE_ERRORS)
105           fprintf(stderr, "mergecap: Error appending %s to %s: %s\n",
106                   in_files[i].filename, out_file->filename, wtap_strerror(*err));
107           switch (*err) {
108
109           case WTAP_ERR_UNSUPPORTED:
110           case WTAP_ERR_UNSUPPORTED_ENCAP:
111           case WTAP_ERR_BAD_RECORD:
112               fprintf(stderr, "(%s)\n", err_info);
113
114             break;
115       }
116       return FALSE;
117     }
118   }
119
120   return TRUE;
121 }
122
123
124 /*
125  * returns TRUE if first argument is earlier than second
126  */
127 static gboolean
128 is_earlier(struct timeval *l, struct timeval *r) {
129   if (l->tv_sec > r->tv_sec) {  /* left is later */
130     return FALSE;
131   } else if (l->tv_sec < r->tv_sec) { /* left is earlier */
132     return TRUE;
133   } else if (l->tv_usec > r->tv_usec) { /* tv_sec equal, l.usec later */
134     return FALSE;
135   }
136   /* either one < two or one == two
137    * either way, return one
138    */
139   return TRUE;
140 }
141
142
143 /*
144  * returns index of earliest timestamp in set of input files
145  * or -1 if no valid files remain
146  */
147 static int
148 earliest(int count, merge_in_file_t in_files[]) {
149   int i;
150   int ei = -1;
151   struct timeval tv = {LONG_MAX, LONG_MAX};
152
153   for (i = 0; i < count; i++) {
154     struct wtap_pkthdr *phdr = wtap_phdr(in_files[i].wth);
155
156     if (in_files[i].ok && is_earlier(&(phdr->ts), &tv)) {
157       tv = phdr->ts;
158       ei = i;
159     }
160   }
161   return ei;
162 }
163
164 /*
165  * actually merge the files
166  */
167 gboolean
168 merge_files(int count, merge_in_file_t in_files[], merge_out_file_t *out_file, int *err)
169 {
170   int i;
171
172   /* prime the pump (read in first frame from each file) */
173   for (i = 0; i < count; i++) {
174     in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
175                                &(in_files[i].err_info),
176                                &(in_files[i].data_offset));
177   }
178
179   /* now keep writing the earliest frame until we're out of frames */
180   while ( -1 != (i = earliest(count, in_files))) {
181
182     /* write out earliest frame, and fetch another from its
183      * input file
184      */
185     if(!write_frame(in_files[i].wth, out_file, err))
186                 return FALSE;
187     in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
188                                &(in_files[i].err_info),
189                                &(in_files[i].data_offset));
190   }
191
192   return TRUE;
193 }
194
195
196 /*
197  * Select an output frame type based on the input files
198  * From Guy: If all files have the same frame type, then use that.
199  *           Otherwise select WTAP_ENCAP_PER_PACKET.  If the selected
200  *           output file type doesn't support per packet frame types,
201  *           then the wtap_dump_open call will fail with a reasonable
202  *           error condition.
203  */
204 int
205 merge_select_frame_type(int count, merge_in_file_t files[])
206 {
207   int i;
208   int selected_frame_type;
209
210   selected_frame_type = wtap_file_encap(files[0].wth);
211
212   for (i = 1; i < count; i++) {
213     int this_frame_type = wtap_file_encap(files[i].wth);
214     if (selected_frame_type != this_frame_type) {
215       selected_frame_type = WTAP_ENCAP_PER_PACKET;
216       if (merge_verbose == VERBOSE_ALL) {
217         fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
218         fprintf(stderr, "          defaulting to WTAP_ENCAP_PER_PACKET\n");
219         fprintf(stderr, "          %s had type %s (%s)\n",
220                 files[0].filename,
221                 wtap_encap_string(selected_frame_type),
222                 wtap_encap_short_string(selected_frame_type));
223         fprintf(stderr, "          %s had type %s (%s)\n",
224                 files[i].filename,
225                 wtap_encap_string(this_frame_type),
226                 wtap_encap_short_string(this_frame_type));
227       }
228       break;
229     }
230   }
231
232   if (merge_verbose == VERBOSE_ALL) {
233       fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
234               wtap_encap_string(selected_frame_type),
235               wtap_encap_short_string(selected_frame_type));
236   }
237
238   return selected_frame_type;
239 }
240
241
242 /*
243  * Close the output file
244  */
245 void
246 merge_close_outfile(merge_out_file_t *out_file)
247 {
248   int err;
249   if (!wtap_dump_close(out_file->pdh, &err)) {
250     if (merge_verbose == VERBOSE_ERRORS)
251         fprintf(stderr, "mergecap: Error closing file %s: %s\n",
252             out_file->filename, wtap_strerror(err));
253   }
254 }
255
256
257 /*
258  * Open the output file
259  *
260  * Return FALSE if file cannot be opened (so caller can clean up)
261  */
262 gboolean
263 merge_open_outfile(merge_out_file_t *out_file, int snapshot_len, int *err)
264 {
265
266   if (!out_file) {
267     if (merge_verbose == VERBOSE_ERRORS)
268         fprintf(stderr, "mergecap: internal error (null out_file)\n");
269     return FALSE;
270   }
271
272   /* Allow output to stdout by using - */
273   if (strncmp(out_file->filename, "-", 2) == 0)
274     out_file->filename = "";
275
276
277   out_file->pdh = wtap_dump_open(out_file->filename, out_file->file_type,
278                                  out_file->frame_type, snapshot_len, err);
279   if (!out_file->pdh) {
280     if (merge_verbose == VERBOSE_ERRORS) {
281         fprintf(stderr, "mergecap: Can't open/create %s:\n", out_file->filename);
282         fprintf(stderr, "          %s\n", wtap_strerror(*err));
283     }
284     return FALSE;
285   }
286   return TRUE;
287 }
288
289
290 /*
291  * Scan through input files and find maximum snapshot length
292  */
293 int
294 merge_max_snapshot_length(int count, merge_in_file_t in_files[])
295 {
296   int i;
297   int max_snapshot = 0;
298   int snapshot_length;
299
300   for (i = 0; i < count; i++) {
301     snapshot_length = wtap_snapshot_length(in_files[i].wth);
302     if (snapshot_length == 0) {
303       /* Snapshot length of input file not known. */
304       snapshot_length = WTAP_MAX_PACKET_SIZE;
305     }
306     if (snapshot_length > max_snapshot)
307       max_snapshot = snapshot_length;
308   }
309   return max_snapshot;
310 }
311
312
313 /*
314  * Scan through and close each input file
315  */
316 void
317 merge_close_in_files(int count, merge_in_file_t in_files[])
318 {
319   int i;
320   for (i = 0; i < count; i++) {
321     wtap_close(in_files[i].wth);
322   }
323 }
324
325
326 /*
327  * Scan through the arguments and open the input files
328  */
329 int
330 merge_open_in_files(int in_file_count, char *in_file_names[], merge_in_file_t *in_files[], int *err)
331 {
332   int i;
333   int count = 0;
334   gchar *err_info;
335   int files_size = in_file_count * sizeof(merge_in_file_t);
336   merge_in_file_t *files;
337
338
339   files = g_malloc(files_size);
340   *in_files = files;
341
342   for (i = 0; i < in_file_count; i++) {
343     files[count].filename    = in_file_names[i];
344     files[count].wth         = wtap_open_offline(in_file_names[i], err, &err_info, FALSE);
345     files[count].err         = 0;
346     files[count].data_offset = 0;
347     files[count].ok          = TRUE;
348     if (!files[count].wth) {
349       if (merge_verbose == VERBOSE_ERRORS) {
350         fprintf(stderr, "mergecap: skipping %s: %s\n", in_file_names[i],
351               wtap_strerror(*err));
352       switch (*err) {
353
354       case WTAP_ERR_UNSUPPORTED:
355       case WTAP_ERR_UNSUPPORTED_ENCAP:
356       case WTAP_ERR_BAD_RECORD:
357         fprintf(stderr, "(%s)\n", err_info);
358         g_free(err_info);
359         break;
360       }
361       }
362     } else {
363       if (merge_verbose == VERBOSE_ALL) {
364         fprintf(stderr, "mergecap: %s is type %s.\n", in_file_names[i],
365                 wtap_file_type_string(wtap_file_type(files[count].wth)));
366       }
367       count++;
368     }
369   }
370   if (merge_verbose == VERBOSE_ALL)
371     fprintf(stderr, "mergecap: opened %d of %d input files\n", count,
372     in_file_count);
373
374   return count;
375 }
376
377
378 /*
379  * Convenience function: merge two files into one.
380  */
381 gboolean
382 merge_two_files(char *out_filename, char *in_file0, char *in_file1, gboolean do_append, int *err)
383 {
384   extern char *optarg;
385   extern int   optind;
386   int          in_file_count = 0;
387   char        *in_filenames[2];
388   merge_in_file_t   *in_files      = NULL;
389   merge_out_file_t   out_file;
390   gboolean     ret;
391
392   /* initialize out_file */
393   out_file.filename   = out_filename;
394   out_file.pdh        = NULL;              /* wiretap dumpfile */
395   out_file.file_type  = WTAP_FILE_PCAP;    /* default to "libpcap" */
396   out_file.frame_type = -2;                /* leave type alone */
397   out_file.snaplen    = 0;                 /* no limit */
398   out_file.count      = 1;                 /* frames output */
399
400   /* check for proper args; at a minimum, must have an output
401    * filename and one input file
402    */
403   in_file_count = 2;
404
405   in_filenames[0] = in_file0;
406   in_filenames[1] = in_file1;
407
408   /* open the input files */
409   in_file_count = merge_open_in_files(in_file_count, in_filenames, &in_files, err);
410   if (in_file_count < 2) {
411     if (merge_verbose == VERBOSE_ALL)
412         fprintf(stderr, "mergecap: Not all input files valid\n");
413     return FALSE;
414   }
415
416   /* set the outfile frame type */
417   if (out_file.frame_type == -2)
418     out_file.frame_type = merge_select_frame_type(in_file_count, in_files);
419
420   /* open the outfile */
421   if (!merge_open_outfile(&out_file, merge_max_snapshot_length(in_file_count, in_files), err)) {
422     merge_close_in_files(in_file_count, in_files);
423     return FALSE;
424   }
425
426   /* do the merge (or append) */
427   if (do_append)
428     ret = merge_append_files(in_file_count, in_files, &out_file, err);
429   else
430     ret = merge_files(in_file_count, in_files, &out_file, err);
431
432   merge_close_in_files(in_file_count, in_files);
433   merge_close_outfile(&out_file);
434
435   free(in_files);
436
437   return ret;
438 }