The common merge code merely needs to offer the abstraction of routines

that return the next packet from a set of {chronologically sorted,
sequential-by-file} packets; it doesn't need to have a loop over all
those packets, or any code to write packets.

Supply those abstractions, change the code that merges packets to do its
own writing, and have the Ethereal version manage a progress bar and
have the mergecap version print packet numbers in verbose mode, as the
common merge code used to do.

svn path=/trunk/; revision=12427
This commit is contained in:
Guy Harris 2004-10-29 00:36:52 +00:00
parent 24f30a8850
commit e09e12621a
4 changed files with 287 additions and 267 deletions

143
file.c
View File

@ -975,60 +975,136 @@ cf_merge_files(const char *out_filename, int out_fd, int in_file_count,
char *const *in_filenames, int file_type, gboolean do_append)
{
merge_in_file_t *in_files;
merge_out_file_t out_file;
int err, close_err;
wtap *wth;
wtap_dumper *pdh;
int open_err, read_err, write_err, close_err;
gchar *err_info;
int err_fileno;
merge_status_e status;
int i;
char errmsg_errno[1024+1];
gchar err_str[2048+1];
char *errmsg;
gboolean got_read_error = FALSE, got_write_error = FALSE;
long data_offset;
progdlg_t *progbar = NULL;
gboolean stop_flag;
/*
* XXX - should be "off_t", but Wiretap would need more work to handle
* the full size of "off_t" on platforms where it's more than a "long"
* as well.
*/
long f_len, file_pos;
float prog_val;
GTimeVal start_time;
gchar status_str[100];
int progbar_nextstep;
int progbar_quantum;
/* open the input files */
if (!merge_open_in_files(in_file_count, in_filenames, &in_files,
&err, &err_info, &err_fileno)) {
&open_err, &err_info, &err_fileno)) {
free(in_files);
cf_open_failure_alert_box(in_filenames[err_fileno], err, err_info, FALSE, 0);
cf_open_failure_alert_box(in_filenames[err_fileno], open_err, err_info,
FALSE, 0);
return FALSE;
}
if (!merge_open_outfile(&out_file, out_fd, file_type,
pdh = wtap_dump_fdopen(out_fd, file_type,
merge_select_frame_type(in_file_count, in_files),
merge_max_snapshot_length(in_file_count, in_files), &err)) {
merge_max_snapshot_length(in_file_count, in_files), &open_err);
if (pdh == NULL) {
merge_close_in_files(in_file_count, in_files);
free(in_files);
cf_open_failure_alert_box(out_filename, err, err_info, TRUE, file_type);
cf_open_failure_alert_box(out_filename, open_err, err_info, TRUE,
file_type);
return FALSE;
}
/* Get the sum of the sizes of all the files. */
f_len = 0;
for (i = 0; i < in_file_count; i++)
f_len += in_files[i].size;
/* Update the progress bar when it gets to this value. */
progbar_nextstep = 0;
/* When we reach the value that triggers a progress bar update,
bump that value by this amount. */
progbar_quantum = f_len/N_PROGBAR_UPDATES;
stop_flag = FALSE;
g_get_current_time(&start_time);
/* do the merge (or append) */
if (do_append)
status = merge_append_files(in_file_count, in_files, &out_file, &err);
else
status = merge_files(in_file_count, in_files, &out_file, &err);
for (;;) {
if (do_append)
wth = merge_append_read_packet(in_file_count, in_files, &read_err,
&err_info);
else
wth = merge_read_packet(in_file_count, in_files, &read_err,
&err_info);
if (wth == NULL) {
if (read_err != 0)
got_read_error = TRUE;
break;
}
/* Get the sum of the data offsets in all of the files. */
data_offset = 0;
for (i = 0; i < in_file_count; i++)
data_offset += in_files[i].data_offset;
if (data_offset >= progbar_nextstep) {
/* Get the sum of the seek positions in all of the files. */
file_pos = 0;
for (i = 0; i < in_file_count; i++)
file_pos += lseek(wtap_fd(in_files[i].wth), 0, SEEK_CUR);
prog_val = (gfloat) file_pos / (gfloat) f_len;
if (prog_val > 1.0) {
/* Some file probably grew while we were reading it.
That "shouldn't happen", so we'll just clip the progress
value at 1.0. */
prog_val = 1.0;
}
if (progbar == NULL) {
/* Create the progress bar if necessary */
progbar = delayed_create_progress_dlg("Merging", "files",
&stop_flag, &start_time, prog_val);
}
if (progbar != NULL) {
g_snprintf(status_str, sizeof(status_str),
"%luKB of %luKB", file_pos / 1024, f_len / 1024);
update_progress_dlg(progbar, prog_val, status_str);
}
progbar_nextstep += progbar_quantum;
}
if (!wtap_dump(pdh, wtap_phdr(wth), wtap_pseudoheader(wth),
wtap_buf_ptr(wth), &write_err)) {
got_write_error = TRUE;
break;
}
}
/* We're done merging the files; destroy the progress bar if it was created. */
if (progbar != NULL)
destroy_progress_dlg(progbar);
merge_close_in_files(in_file_count, in_files);
if (status == MERGE_SUCCESS) {
if (!merge_close_outfile(&out_file, &err))
status = MERGE_WRITE_ERROR;
if (!got_read_error && !got_write_error) {
if (!wtap_dump_close(pdh, &write_err))
got_write_error = TRUE;
} else
merge_close_outfile(&out_file, &close_err);
wtap_dump_close(pdh, &close_err);
switch (status) {
case MERGE_SUCCESS:
break;
case MERGE_READ_ERROR:
if (got_read_error) {
/*
* Find the file on which we got the error, and report the error.
*/
for (i = 0; i < in_file_count; i++) {
if (!in_files[i].ok) {
/* Put up a message box noting that the read failed somewhere along
if (in_files[i].state == GOT_ERROR) {
/* Put up a message box noting that a read failed somewhere along
the line. */
switch (err) {
switch (read_err) {
case WTAP_ERR_UNSUPPORTED_ENCAP:
snprintf(errmsg_errno, sizeof(errmsg_errno),
@ -1050,7 +1126,7 @@ cf_merge_files(const char *out_filename, int out_fd, int in_file_count,
case WTAP_ERR_BAD_RECORD:
snprintf(errmsg_errno, sizeof(errmsg_errno),
"The capture file %%sappears to be damaged or corrupt.\n(%s)",
"The capture file %%s appears to be damaged or corrupt.\n(%s)",
err_info);
g_free(err_info);
errmsg = errmsg_errno;
@ -1059,7 +1135,7 @@ cf_merge_files(const char *out_filename, int out_fd, int in_file_count,
default:
snprintf(errmsg_errno, sizeof(errmsg_errno),
"An error occurred while reading the"
" capture file %%s: %s.", wtap_strerror(err));
" capture file %%s: %s.", wtap_strerror(read_err));
errmsg = errmsg_errno;
break;
}
@ -1067,13 +1143,14 @@ cf_merge_files(const char *out_filename, int out_fd, int in_file_count,
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, err_str);
}
}
break;
case MERGE_WRITE_ERROR:
cf_write_failure_alert_box(out_filename, err);
break;
}
return (status == MERGE_SUCCESS);
if (got_write_error) {
/* Put up an alert box for the write error. */
cf_write_failure_alert_box(out_filename, write_err);
}
return (!got_read_error && !got_write_error);
}
gboolean

230
merge.c
View File

@ -1,4 +1,4 @@
/* Combine two dump files, either by appending or by merging by timestamp
/* Combine multiple dump files, either by appending or by merging by timestamp
*
* $Id$
*
@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@ -23,6 +24,10 @@
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <string.h>
#include "wtap.h"
#include "merge.h"
@ -38,6 +43,7 @@ merge_open_in_files(int in_file_count, char *const *in_file_names,
int i, j;
int files_size = in_file_count * sizeof(merge_in_file_t);
merge_in_file_t *files;
struct stat statb;
files = g_malloc(files_size);
*in_files = files;
@ -45,9 +51,8 @@ merge_open_in_files(int in_file_count, char *const *in_file_names,
for (i = 0; i < in_file_count; i++) {
files[i].filename = in_file_names[i];
files[i].wth = wtap_open_offline(in_file_names[i], err, err_info, FALSE);
files[i].err = 0;
files[i].data_offset = 0;
files[i].ok = TRUE;
files[i].state = PACKET_NOT_PRESENT;
if (!files[i].wth) {
/* Close the files we've already opened. */
for (j = 0; j < i; j++)
@ -55,6 +60,14 @@ merge_open_in_files(int in_file_count, char *const *in_file_names,
*err_fileno = i;
return FALSE;
}
if (fstat(wtap_fd(files[i].wth), &statb) < 0) {
*err = errno;
for (j = 0; j <= i; j++)
wtap_close(files[j].wth);
*err_fileno = i;
return FALSE;
}
files[i].size = statb.st_size;
}
return TRUE;
}
@ -71,37 +84,6 @@ merge_close_in_files(int count, merge_in_file_t in_files[])
}
}
/*
* Open the output file
*
* Return FALSE if file cannot be opened (so caller can report an error
* and clean up)
*/
gboolean
merge_open_outfile(merge_out_file_t *out_file, int fd, int file_type,
int frame_type, int snapshot_len, int *err)
{
out_file->pdh = wtap_dump_fdopen(fd, file_type, frame_type, snapshot_len,
err);
if (!out_file->pdh)
return FALSE;
out_file->snaplen = snapshot_len;
out_file->count = 1;
return TRUE;
}
/*
* Close the output file
*/
gboolean
merge_close_outfile(merge_out_file_t *out_file, int *err)
{
if (!wtap_dump_close(out_file->pdh, err))
return FALSE;
return TRUE;
}
/*
* Select an output frame type based on the input files
* From Guy: If all files have the same frame type, then use that.
@ -151,29 +133,6 @@ merge_max_snapshot_length(int count, merge_in_file_t in_files[])
return max_snapshot;
}
/*
* Routine to write frame to output file
*/
static gboolean
write_frame(wtap *wth, merge_out_file_t *out_file, int *err)
{
const struct wtap_pkthdr *phdr = wtap_phdr(wth);
struct wtap_pkthdr snap_phdr;
/* We simply write it, perhaps after truncating it; we could do other
* things, like modify it. */
if (out_file->snaplen != 0 && phdr->caplen > out_file->snaplen) {
snap_phdr = *phdr;
snap_phdr.caplen = out_file->snaplen;
phdr = &snap_phdr;
}
if (!wtap_dump(out_file->pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth), err))
return FALSE;
return TRUE;
}
/*
* returns TRUE if first argument is earlier than second
*/
@ -192,105 +151,94 @@ is_earlier(struct timeval *l, struct timeval *r) {
return TRUE;
}
/*
* returns index of earliest timestamp in set of input files
* or -1 if no valid files remain
* Read the next packet, in chronological order, from the set of files
* to be merged.
*/
static int
earliest(int count, merge_in_file_t in_files[]) {
wtap *
merge_read_packet(int in_file_count, merge_in_file_t in_files[], int *err,
gchar **err_info)
{
int i;
int ei = -1;
struct timeval tv = {LONG_MAX, LONG_MAX};
struct wtap_pkthdr *phdr;
for (i = 0; i < count; i++) {
struct wtap_pkthdr *phdr = wtap_phdr(in_files[i].wth);
if (in_files[i].ok && is_earlier(&(phdr->ts), &tv)) {
tv = phdr->ts;
ei = i;
/*
* Make sure we have a packet available from each file, if there are any
* packets left in the file in question, and search for the packet
* with the earliest time stamp.
*/
for (i = 0; i < in_file_count; i++) {
if (in_files[i].state == PACKET_NOT_PRESENT) {
/*
* No packet available, and we haven't seen an error or EOF yet,
* so try to read the next packet.
*/
if (!wtap_read(in_files[i].wth, err, err_info, &in_files[i].data_offset)) {
if (*err != 0) {
in_files[i].state = GOT_ERROR;
return NULL;
}
in_files[i].state = AT_EOF;
} else
in_files[i].state = PACKET_PRESENT;
}
if (in_files[i].state == PACKET_PRESENT) {
phdr = wtap_phdr(in_files[i].wth);
if (is_earlier(&phdr->ts, &tv)) {
tv = phdr->ts;
ei = i;
}
}
}
return ei;
if (ei == -1) {
/* All the streams are at EOF. Return an EOF indication. */
*err = 0;
return NULL;
}
/* We'll need to read another packet from this file. */
in_files[ei].state = PACKET_NOT_PRESENT;
/* Return a pointer to the wtap structure for the file with that frame. */
return in_files[ei].wth;
}
/*
* actually merge the files
* Read the next packet, in file sequence order, from the set of files
* to be merged.
*/
merge_status_e
merge_files(int count, merge_in_file_t in_files[], merge_out_file_t *out_file, int *err)
wtap *
merge_append_read_packet(int in_file_count, merge_in_file_t in_files[],
int *err, gchar **err_info)
{
int i;
/* prime the pump (read in first frame from each file) */
for (i = 0; i < count; i++) {
in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
&(in_files[i].err_info),
&(in_files[i].data_offset));
if (!in_files[i].ok)
return MERGE_READ_ERROR;
/*
* Find the first file not at EOF, and read the next packet from it.
*/
for (i = 0; i < in_file_count; i++) {
if (in_files[i].state == AT_EOF)
continue; /* This file is already at EOF */
if (wtap_read(in_files[i].wth, err, err_info, &in_files[i].data_offset))
break; /* We have a packet */
if (*err != 0) {
/* Read error - quit immediately. */
in_files[i].state = GOT_ERROR;
return NULL;
}
/* EOF - flag this file as being at EOF, and try the next one. */
in_files[i].state = AT_EOF;
}
if (i == in_file_count) {
/* All the streams are at EOF. Return an EOF indication. */
*err = 0;
return NULL;
}
/* now keep writing the earliest frame until we're out of frames */
while ( -1 != (i = earliest(count, in_files))) {
/* write out earliest frame, and fetch another from its
* input file
*/
if(!write_frame(in_files[i].wth, out_file, err))
return MERGE_WRITE_ERROR;
in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
&(in_files[i].err_info),
&(in_files[i].data_offset));
if (!in_files[i].ok)
return MERGE_READ_ERROR;
}
return MERGE_SUCCESS;
}
static merge_status_e
append_loop(merge_in_file_t in_files[], int i, int count,
merge_out_file_t *out_file, int *err)
{
gchar *err_info;
long data_offset;
int loop = 0;
/* Start by clearing error flag */
*err = 0;
while ( (wtap_read(in_files[i].wth, err, &err_info, &data_offset)) ) {
if(!write_frame(in_files[i].wth, out_file, err))
return MERGE_WRITE_ERROR;
if (count > 0 && ++loop >= count)
break;
}
if (*err != 0) {
in_files[i].ok = FALSE;
in_files[i].err = *err;
in_files[i].err_info = err_info;
return MERGE_READ_ERROR;
}
return MERGE_SUCCESS;
}
/*
* routine to concatenate files
*/
merge_status_e
merge_append_files(int count, merge_in_file_t in_files[],
merge_out_file_t *out_file, int *err)
{
int i;
merge_status_e status;
for (i = 0; i < count; i++) {
status = append_loop(in_files, i, 0, out_file, err);
if (status != MERGE_SUCCESS)
return status;
}
return MERGE_SUCCESS;
/* Return a pointer to the wtap structure for the file with that frame. */
return in_files[i].wth;
}

89
merge.h
View File

@ -29,27 +29,24 @@
extern "C" {
#endif /* __cplusplus */
typedef enum {
PACKET_PRESENT,
PACKET_NOT_PRESENT,
AT_EOF,
GOT_ERROR
} in_file_state_e;
/**
* Structures to manage our input files.
*/
typedef struct merge_in_file_s {
const char *filename;
wtap *wth;
int err;
gchar *err_info;
long data_offset;
gboolean ok;
const char *filename;
wtap *wth;
long data_offset;
in_file_state_e state;
long size; /* file size */
} merge_in_file_t;
/**
* Structures to manage our output file.
*/
typedef struct merge_out_file_s {
wtap_dumper *pdh;
unsigned int snaplen;
int count;
} merge_out_file_t;
/** Open a number of input files to merge.
*
* @param in_file_count number of entries in in_file_names and in_files
@ -73,29 +70,6 @@ merge_open_in_files(int in_file_count, char *const *in_file_names,
extern void
merge_close_in_files(int in_file_count, merge_in_file_t in_files[]);
/** Open the output file.
*
* @param out_file the output file array, which we fill in
* @param fd the file descriptor to use for the output file
* @param file_type the file type to write
* @param frame_type the frame type to write
* @param snapshot_len the snapshot length of the output file
* @param err wiretap error, if failed
* @return TRUE, if the output file could be opened, and FALSE otherwise
*/
extern gboolean
merge_open_outfile(merge_out_file_t *out_file, int fd, int file_type,
int frame_type, int snapshot_len, int *err);
/** Close the output file again.
*
* @param out_file the output file array
* @param err wiretap error, if failed
* @return TRUE if the close succeeded, FALSE otherwise
*/
extern gboolean
merge_close_outfile(merge_out_file_t *out_file, int *err);
/** Try to get the frame type from the input files.
*
* @param in_file_count number of entries in in_files
@ -114,39 +88,34 @@ merge_select_frame_type(int in_file_count, merge_in_file_t in_files[]);
extern int
merge_max_snapshot_length(int in_file_count, merge_in_file_t in_files[]);
/*
* Status from the merge-files routines.
*/
typedef enum {
MERGE_SUCCESS,
MERGE_READ_ERROR,
MERGE_WRITE_ERROR
} merge_status_e;
/** Merge the packets from the input files into the output file sorted chronologically.
/** Read the next packet, in chronological order, from the set of files to
* be merged.
*
* @param in_file_count number of entries in in_files
* @param in_files input file array
* @param out_file the output file array
* @param err wiretap error, if failed
* @return MERGE_SUCCESS on success, MERGE_READ_ERROR on read error,
* MERGE_WRITE_ERROR on write error
* @param err_info wiretap error string, if failed
* @return pointer to wtap for file from which that packet came, or NULL on
* error or EOF
*/
extern merge_status_e
merge_files(int in_file_count, merge_in_file_t in_files[], merge_out_file_t *out_file, int *err);
extern wtap *
merge_read_packet(int in_file_count, merge_in_file_t in_files[], int *err,
gchar **err_info);
/** Append the packets from the input files into the output file.
/** Read the next packet, in file sequence order, from the set of files
* to be merged.
*
* @param in_file_count number of entries in in_files
* @param in_files input file array
* @param out_file the output file array
* @param err wiretap error, if failed
* @return MERGE_SUCCESS on success, MERGE_READ_ERROR on read error,
* MERGE_WRITE_ERROR on write error
* @param err_info wiretap error string, if failed
* @return pointer to wtap for file from which that packet came, or NULL on
* error or EOF
*/
extern merge_status_e
merge_append_files(int in_file_count, merge_in_file_t in_files[],
merge_out_file_t *out_file, int *err);
extern wtap *
merge_append_read_packet(int in_file_count, merge_in_file_t in_files[],
int *err, gchar **err_info);
#ifdef __cplusplus
}

View File

@ -131,18 +131,21 @@ main(int argc, char *argv[])
gboolean do_append = FALSE;
gboolean verbose = FALSE;
int in_file_count = 0;
int snaplen = 0;
guint snaplen = 0;
int file_type = WTAP_FILE_PCAP; /* default to libpcap format */
int frame_type = -2;
int out_fd;
merge_in_file_t *in_files = NULL;
int i;
merge_out_file_t out_file;
int err, close_err;
wtap *wth;
struct wtap_pkthdr *phdr, snap_phdr;
wtap_dumper *pdh;
int open_err, read_err, write_err, close_err;
gchar *err_info;
int err_fileno;
char *out_filename = NULL;
merge_status_e status;
gboolean got_read_error = FALSE, got_write_error = FALSE;
int count;
/* Process the options first */
while ((opt = getopt(argc, argv, "hvas:T:F:w:")) != -1) {
@ -217,10 +220,10 @@ main(int argc, char *argv[])
/* open the input files */
if (!merge_open_in_files(in_file_count, &argv[optind], &in_files,
&err, &err_info, &err_fileno)) {
&open_err, &err_info, &err_fileno)) {
fprintf(stderr, "mergecap: Can't open %s: %s\n", argv[optind + err_fileno],
wtap_strerror(err));
switch (err) {
wtap_strerror(open_err));
switch (open_err) {
case WTAP_ERR_UNSUPPORTED:
case WTAP_ERR_UNSUPPORTED_ENCAP:
@ -298,60 +301,83 @@ main(int argc, char *argv[])
}
/* prepare the outfile */
if (!merge_open_outfile(&out_file, out_fd, file_type, frame_type, snaplen,
&err)) {
pdh = wtap_dump_fdopen(out_fd, file_type, frame_type, snaplen, &open_err);
if (pdh == NULL) {
merge_close_in_files(in_file_count, in_files);
free(in_files);
fprintf(stderr, "mergecap: Can't open or create %s: %s\n", out_filename,
wtap_strerror(err));
wtap_strerror(open_err));
exit(1);
}
/* do the merge (or append) */
if (do_append)
status = merge_append_files(in_file_count, in_files, &out_file, &err);
else
status = merge_files(in_file_count, in_files, &out_file, &err);
count = 1;
for (;;) {
if (do_append)
wth = merge_append_read_packet(in_file_count, in_files, &read_err,
&err_info);
else
wth = merge_read_packet(in_file_count, in_files, &read_err,
&err_info);
if (wth == NULL) {
if (read_err != 0)
got_read_error = TRUE;
break;
}
if (verbose)
fprintf(stderr, "Record: %u\n", count++);
/* We simply write it, perhaps after truncating it; we could do other
* things, like modify it. */
phdr = wtap_phdr(wth);
if (snaplen != 0 && phdr->caplen > snaplen) {
snap_phdr = *phdr;
snap_phdr.caplen = snaplen;
phdr = &snap_phdr;
}
if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth),
wtap_buf_ptr(wth), &write_err)) {
got_write_error = TRUE;
break;
}
}
merge_close_in_files(in_file_count, in_files);
if (status == MERGE_SUCCESS) {
if (!merge_close_outfile(&out_file, &err))
status = MERGE_WRITE_ERROR;
if (!got_read_error && !got_write_error) {
if (!wtap_dump_close(pdh, &write_err))
got_write_error = TRUE;
} else
merge_close_outfile(&out_file, &close_err);
switch (status) {
wtap_dump_close(pdh, &close_err);
case MERGE_SUCCESS:
break;
case MERGE_READ_ERROR:
if (got_read_error) {
/*
* Find the file on which we got the error, and report the error.
*/
for (i = 0; i < in_file_count; i++) {
if (!in_files[i].ok) {
if (in_files[i].state == GOT_ERROR) {
fprintf(stderr, "mergecap: Error reading %s: %s\n",
in_files[i].filename, wtap_strerror(in_files[i].err));
switch (err) {
in_files[i].filename, wtap_strerror(read_err));
switch (read_err) {
case WTAP_ERR_UNSUPPORTED:
case WTAP_ERR_UNSUPPORTED_ENCAP:
case WTAP_ERR_BAD_RECORD:
fprintf(stderr, "(%s)\n", in_files[i].err_info);
g_free(in_files[i].err_info);
fprintf(stderr, "(%s)\n", err_info);
g_free(err_info);
break;
}
}
}
break;
}
case MERGE_WRITE_ERROR:
if (got_write_error) {
fprintf(stderr, "mergecap: Error writing to outfile: %s\n",
wtap_strerror(err));
break;
wtap_strerror(write_err));
}
free(in_files);
return (status == MERGE_SUCCESS) ? 0 : 2;
return (!got_read_error && !got_write_error) ? 0 : 2;
}