1 /* Combine two dump files, either by appending or by merging by timestamp
5 * Written by Scott Renfro <scott@renfro.org> based on
6 * editcap by Richard Sharpe and Guy Harris
22 #ifdef HAVE_SYS_TIME_H
33 int merge_verbose = VERBOSE_NONE;
37 * Routine to write frame to output file
40 write_frame(wtap *wth, merge_out_file_t *out_file, int *err)
42 const struct wtap_pkthdr *phdr = wtap_phdr(wth);
43 struct wtap_pkthdr snap_phdr;
45 if (merge_verbose == VERBOSE_ALL)
46 fprintf(stderr, "Record: %u\n", out_file->count++);
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) {
52 snap_phdr.caplen = out_file->snaplen;
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",
68 append_loop(wtap *wth, int count, merge_out_file_t *out_file, int *err,
74 /* Start by clearing error flag */
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)
85 return TRUE; /* success */
87 return FALSE; /* failure */
94 * routine to concatenate files
97 merge_append_files(int count, merge_in_file_t in_files[], merge_out_file_t *out_file, int *err)
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));
109 case WTAP_ERR_UNSUPPORTED:
110 case WTAP_ERR_UNSUPPORTED_ENCAP:
111 case WTAP_ERR_BAD_RECORD:
112 fprintf(stderr, "(%s)\n", err_info);
125 * returns TRUE if first argument is earlier than second
128 is_earlier(struct timeval *l, struct timeval *r) {
129 if (l->tv_sec > r->tv_sec) { /* left is later */
131 } else if (l->tv_sec < r->tv_sec) { /* left is earlier */
133 } else if (l->tv_usec > r->tv_usec) { /* tv_sec equal, l.usec later */
136 /* either one < two or one == two
137 * either way, return one
144 * returns index of earliest timestamp in set of input files
145 * or -1 if no valid files remain
148 earliest(int count, merge_in_file_t in_files[]) {
151 struct timeval tv = {LONG_MAX, LONG_MAX};
153 for (i = 0; i < count; i++) {
154 struct wtap_pkthdr *phdr = wtap_phdr(in_files[i].wth);
156 if (in_files[i].ok && is_earlier(&(phdr->ts), &tv)) {
165 * actually merge the files
168 merge_files(int count, merge_in_file_t in_files[], merge_out_file_t *out_file, int *err)
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));
179 /* now keep writing the earliest frame until we're out of frames */
180 while ( -1 != (i = earliest(count, in_files))) {
182 /* write out earliest frame, and fetch another from its
185 if(!write_frame(in_files[i].wth, out_file, err))
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));
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
205 merge_select_frame_type(int count, merge_in_file_t files[])
208 int selected_frame_type;
210 selected_frame_type = wtap_file_encap(files[0].wth);
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",
221 wtap_encap_string(selected_frame_type),
222 wtap_encap_short_string(selected_frame_type));
223 fprintf(stderr, " %s had type %s (%s)\n",
225 wtap_encap_string(this_frame_type),
226 wtap_encap_short_string(this_frame_type));
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));
238 return selected_frame_type;
243 * Close the output file
246 merge_close_outfile(merge_out_file_t *out_file)
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",
258 * Open the output file
260 * Return FALSE if file cannot be opened (so caller can clean up)
263 merge_open_outfile(merge_out_file_t *out_file, int snapshot_len, int *err)
267 if (merge_verbose == VERBOSE_ERRORS)
268 fprintf(stderr, "mergecap: internal error (null out_file)\n");
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));
287 * Scan through input files and find maximum snapshot length
290 merge_max_snapshot_length(int count, merge_in_file_t in_files[])
293 int max_snapshot = 0;
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;
302 if (snapshot_length > max_snapshot)
303 max_snapshot = snapshot_length;
310 * Scan through and close each input file
313 merge_close_in_files(int count, merge_in_file_t in_files[])
316 for (i = 0; i < count; i++) {
317 wtap_close(in_files[i].wth);
323 * Scan through the arguments and open the input files
326 merge_open_in_files(int in_file_count, char *in_file_names[], merge_in_file_t *in_files[], int *err)
331 int files_size = in_file_count * sizeof(merge_in_file_t);
332 merge_in_file_t *files;
335 files = g_malloc(files_size);
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));
350 case WTAP_ERR_UNSUPPORTED:
351 case WTAP_ERR_UNSUPPORTED_ENCAP:
352 case WTAP_ERR_BAD_RECORD:
353 fprintf(stderr, "(%s)\n", err_info);
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)));
366 if (merge_verbose == VERBOSE_ALL)
367 fprintf(stderr, "mergecap: opened %d of %d input files\n", count,
375 * Convenience function: merge two files into one.
378 merge_n_files(int out_fd, int in_file_count, char **in_filenames, gboolean do_append, int *err)
382 merge_in_file_t *in_files = NULL;
383 merge_out_file_t out_file;
386 /* initialize out_file */
387 out_file.fd = out_fd;
388 out_file.pdh = NULL; /* wiretap dumpfile */
389 out_file.file_type = WTAP_FILE_PCAP; /* default to "libpcap" */
390 out_file.frame_type = -2; /* leave type alone */
391 out_file.snaplen = 0; /* no limit */
392 out_file.count = 1; /* frames output */
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");
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);
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);
412 /* do the merge (or append) */
414 ret = merge_append_files(in_file_count, in_files, &out_file, err);
416 ret = merge_files(in_file_count, in_files, &out_file, err);
418 merge_close_in_files(in_file_count, in_files);
419 merge_close_outfile(&out_file);