Fix indentation.
[obnox/wireshark/wip.git] / merge.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 <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 outfile: %s\n",
59             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 gboolean
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 outfile: %s\n",
106                   in_files[i].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 output file: %s\n",
252             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
273   out_file->pdh = wtap_dump_fdopen(out_file->fd, out_file->file_type,
274                                  out_file->frame_type, snapshot_len, err);
275   if (!out_file->pdh) {
276     if (merge_verbose == VERBOSE_ERRORS) {
277         fprintf(stderr, "mergecap: Can't open/create output file:\n");
278         fprintf(stderr, "          %s\n", wtap_strerror(*err));
279     }
280     return FALSE;
281   }
282   return TRUE;
283 }
284
285
286 /*
287  * Scan through input files and find maximum snapshot length
288  */
289 int
290 merge_max_snapshot_length(int count, merge_in_file_t in_files[])
291 {
292   int i;
293   int max_snapshot = 0;
294   int snapshot_length;
295
296   for (i = 0; i < count; i++) {
297     snapshot_length = wtap_snapshot_length(in_files[i].wth);
298     if (snapshot_length == 0) {
299       /* Snapshot length of input file not known. */
300       snapshot_length = WTAP_MAX_PACKET_SIZE;
301     }
302     if (snapshot_length > max_snapshot)
303       max_snapshot = snapshot_length;
304   }
305   return max_snapshot;
306 }
307
308
309 /*
310  * Scan through and close each input file
311  */
312 void
313 merge_close_in_files(int count, merge_in_file_t in_files[])
314 {
315   int i;
316   for (i = 0; i < count; i++) {
317     wtap_close(in_files[i].wth);
318   }
319 }
320
321
322 /*
323  * Scan through the arguments and open the input files
324  */
325 int
326 merge_open_in_files(int in_file_count, char *in_file_names[], merge_in_file_t *in_files[], int *err)
327 {
328   int i;
329   int count = 0;
330   gchar *err_info;
331   int files_size = in_file_count * sizeof(merge_in_file_t);
332   merge_in_file_t *files;
333
334
335   files = g_malloc(files_size);
336   *in_files = files;
337
338   for (i = 0; i < in_file_count; i++) {
339     files[count].filename    = in_file_names[i];
340     files[count].wth         = wtap_open_offline(in_file_names[i], err, &err_info, FALSE);
341     files[count].err         = 0;
342     files[count].data_offset = 0;
343     files[count].ok          = TRUE;
344     if (!files[count].wth) {
345       if (merge_verbose == VERBOSE_ERRORS) {
346         fprintf(stderr, "mergecap: skipping %s: %s\n", in_file_names[i],
347               wtap_strerror(*err));
348         switch (*err) {
349
350         case WTAP_ERR_UNSUPPORTED:
351         case WTAP_ERR_UNSUPPORTED_ENCAP:
352         case WTAP_ERR_BAD_RECORD:
353           fprintf(stderr, "(%s)\n", err_info);
354           g_free(err_info);
355           break;
356         }
357       }
358     } else {
359       if (merge_verbose == VERBOSE_ALL) {
360         fprintf(stderr, "mergecap: %s is type %s.\n", in_file_names[i],
361                 wtap_file_type_string(wtap_file_type(files[count].wth)));
362       }
363       count++;
364     }
365   }
366   if (merge_verbose == VERBOSE_ALL)
367     fprintf(stderr, "mergecap: opened %d of %d input files\n", count,
368     in_file_count);
369
370   return count;
371 }
372
373
374 /*
375  * Convenience function: merge two files into one.
376  */
377 gboolean
378 merge_n_files(int out_fd, int in_file_count, char **in_filenames, int filetype, gboolean do_append, int *err)
379 {
380   extern char *optarg;
381   extern int   optind;
382   merge_in_file_t   *in_files      = NULL;
383   merge_out_file_t   out_file;
384   gboolean     ret;
385
386   /* initialize out_file */
387   out_file.fd         = out_fd;
388   out_file.pdh        = NULL;              /* wiretap dumpfile */
389   out_file.file_type  = filetype;
390   out_file.frame_type = -2;                /* leave type alone */
391   out_file.snaplen    = 0;                 /* no limit */
392   out_file.count      = 1;                 /* frames output */
393
394   /* open the input files */
395   in_file_count = merge_open_in_files(in_file_count, in_filenames, &in_files, err);
396   if (in_file_count < 2) {
397     if (merge_verbose == VERBOSE_ALL)
398         fprintf(stderr, "mergecap: Not all input files valid\n");
399     return FALSE;
400   }
401
402   /* set the outfile frame type */
403   if (out_file.frame_type == -2)
404     out_file.frame_type = merge_select_frame_type(in_file_count, in_files);
405
406   /* open the outfile */
407   if (!merge_open_outfile(&out_file, merge_max_snapshot_length(in_file_count, in_files), err)) {
408     merge_close_in_files(in_file_count, in_files);
409     return FALSE;
410   }
411
412   /* do the merge (or append) */
413   if (do_append)
414     ret = merge_append_files(in_file_count, in_files, &out_file, err);
415   else
416     ret = merge_files(in_file_count, in_files, &out_file, err);
417
418   merge_close_in_files(in_file_count, in_files);
419   merge_close_outfile(&out_file);
420
421   free(in_files);
422
423   return ret;
424 }