add support for compression of capture file

This commit is contained in:
Masaru Tsuchiyama 2020-08-09 12:12:24 +09:00 committed by Ronnie Sahlberg
parent 9d6ebdc8a5
commit c14ea41233
9 changed files with 232 additions and 22 deletions

View File

@ -440,6 +440,10 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, inf
for (i = 0; i < argc; i++) {
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[%d]: %s", i, argv[i]);
}
if (capture_opts->compress_type) {
argv = sync_pipe_add_arg(argv, &argc, "--compress-type");
argv = sync_pipe_add_arg(argv, &argc, capture_opts->compress_type);
}
#ifdef _WIN32
/* init SECURITY_ATTRIBUTES */

View File

@ -118,6 +118,7 @@ capture_opts_init(capture_options *capture_opts)
capture_opts->capture_child = FALSE;
capture_opts->print_file_names = FALSE;
capture_opts->print_name_to = NULL;
capture_opts->compress_type = NULL;
}
void
@ -978,6 +979,21 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg_
}
}
break;
case LONGOPT_COMPRESS_TYPE: /* compress type */
if (capture_opts->compress_type) {
cmdarg_err("--compress-type can be set only once");
return 1;
}
if (strcmp(optarg_str_p, "none") == 0) {
;
} else if (strcmp(optarg_str_p, "gzip") == 0) {
;
} else {
cmdarg_err("parameter of --compress-type can be 'none' or 'gzip'");
return 1;
}
capture_opts->compress_type = g_strdup(optarg_str_p);
break;
default:
/* the caller is responsible to send us only the right opt's */
g_assert_not_reached();

View File

@ -21,6 +21,7 @@
#include <sys/types.h> /* for gid_t */
#include <caputils/capture_ifinfo.h>
#include "ringbuffer.h"
#ifdef _WIN32
#include <windows.h>
@ -45,6 +46,7 @@ extern "C" {
#define LONGOPT_NUM_CAP_COMMENT LONGOPT_BASE_CAPTURE+1
#define LONGOPT_LIST_TSTAMP_TYPES LONGOPT_BASE_CAPTURE+2
#define LONGOPT_SET_TSTAMP_TYPE LONGOPT_BASE_CAPTURE+3
#define LONGOPT_COMPRESS_TYPE LONGOPT_BASE_CAPTURE+4
/*
* Options for capturing common to all capturing programs.
@ -85,7 +87,8 @@ extern "C" {
{"snapshot-length", required_argument, NULL, 's'}, \
{"linktype", required_argument, NULL, 'y'}, \
{"list-time-stamp-types", no_argument, NULL, LONGOPT_LIST_TSTAMP_TYPES}, \
{"time-stamp-type", required_argument, NULL, LONGOPT_SET_TSTAMP_TYPE},
{"time-stamp-type", required_argument, NULL, LONGOPT_SET_TSTAMP_TYPE}, \
{"compress-type", required_argument, NULL, LONGOPT_COMPRESS_TYPE},
#define OPTSTRING_CAPTURE_COMMON \
@ -317,6 +320,7 @@ typedef struct capture_options_tag {
/* internally used (don't touch from outside) */
gboolean output_to_pipe; /**< save_file is a pipe (named or stdout) */
gboolean capture_child; /**< hidden option: Wireshark child mode */
gchar *compress_type; /**< compress type */
} capture_options;
/* initialize the capture_options with some reasonable values */

View File

@ -3396,7 +3396,8 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd,
/* ringbuffer is enabled */
*save_file_fd = ringbuf_init(capfile_name,
(capture_opts->has_ring_num_files) ? capture_opts->ring_num_files : 0,
capture_opts->group_read_access);
capture_opts->group_read_access,
capture_opts->compress_type);
/* capfile_name is unused as the ringbuffer provides its own filename. */
if (*save_file_fd != -1) {
@ -4903,6 +4904,7 @@ main(int argc, char *argv[])
#ifdef HAVE_PCAP_CREATE
case 'I': /* Monitor mode */
#endif
case LONGOPT_COMPRESS_TYPE: /* compress type */
status = capture_opts_add_opt(&global_capture_opts, opt, optarg, &start_capture);
if (status != 0) {
exit_main(status);

View File

@ -36,20 +36,35 @@
#include <string.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include <glib.h>
#include "wspcap.h"
#include <glib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef _WIN32
#include <wsutil/win32-utils.h>
#endif
#include "ringbuffer.h"
#include <wsutil/file_util.h>
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
/* Ringbuffer file structure */
typedef struct _rb_file {
gchar *name;
} rb_file;
#define MAX_FILENAME_QUEUE 100
/** Ringbuffer data structure */
typedef struct _ringbuf_data {
rb_file *files;
@ -64,10 +79,125 @@ typedef struct _ringbuf_data {
char *io_buffer; /**< The IO buffer used to write to the file */
gboolean group_read_access; /**< TRUE if files need to be opened with group read access */
FILE *name_h; /**< write names of completed files to this handle */
gchar *compress_type; /**< compress type */
GMutex mutex; /**< mutex for oldnames */
gchar *oldnames[MAX_FILENAME_QUEUE]; /**< filename list of pending to be deleted */
} ringbuf_data;
static ringbuf_data rb_data;
/*
* delete pending uncompressed pcap files.
*/
static void CleanupOldCap(gchar* name)
{
ws_statb64 statb;
size_t i;
g_mutex_lock(&rb_data.mutex);
/* Delete pending delete file */
for (i = 0; i < sizeof(rb_data.oldnames) / sizeof(rb_data.oldnames[0]); i++) {
if (rb_data.oldnames[i] != NULL) {
ws_unlink(rb_data.oldnames[i]);
if (ws_stat64(rb_data.oldnames[i], &statb) != 0) {
g_free(rb_data.oldnames[i]);
rb_data.oldnames[i] = NULL;
}
}
}
if (name) {
/* push the current file to pending list if it failed to delete */
if (ws_stat64(name, &statb) == 0) {
for (i = 0; i < sizeof(rb_data.oldnames) / sizeof(rb_data.oldnames[0]); i++) {
if (rb_data.oldnames[i] == NULL) {
rb_data.oldnames[i] = g_strdup(name);
break;
}
}
}
}
g_mutex_unlock(&rb_data.mutex);
}
/*
* compress capture file
*/
static int ringbuf_exec_compress(gchar* name)
{
guint8 *buffer = NULL;
gchar* outgz = NULL;
int fd = -1;
ssize_t nread;
gboolean delete_org_file = TRUE;
gzFile fi = NULL;
fd = ws_open(name, O_RDONLY | O_BINARY, 0000);
if (fd < 0) {
return -1;
}
outgz = g_strdup_printf("%s.gz", name);
fi = gzopen(outgz, "wb");
g_free(outgz);
if (fi == NULL) {
ws_close(fd);
return -1;
}
#define FS_READ_SIZE 65536
buffer = (guint8*)g_malloc(FS_READ_SIZE);
if (buffer == NULL) {
ws_close(fd);
return -1;
}
while ((nread = ws_read(fd, buffer, FS_READ_SIZE)) > 0) {
int n = gzwrite(fi, buffer, (unsigned int)nread);
if (n <= 0) {
/* mark compression as failed */
delete_org_file = FALSE;
break;
}
}
if (nread < 0) {
/* mark compression as failed */
delete_org_file = FALSE;
}
ws_close(fd);
gzclose(fi);
g_free(buffer);
/* delete the original file only if compression succeeds */
if (delete_org_file) {
ws_unlink(name);
CleanupOldCap(name);
}
g_free(name);
return 0;
}
/*
* thread to compress capture file
*/
static void* exec_compress_thread(void* arg)
{
ringbuf_exec_compress((gchar*)arg);
return NULL;
}
/*
* start a thread to compress capture file
*/
static int ringbuf_start_compress_file(rb_file* rfile)
{
gchar* name = g_strdup(rfile->name);
g_thread_new("exec_compress", &exec_compress_thread, name);
return 0;
}
/*
* create the next filename and open a new binary file with that name
@ -84,6 +214,9 @@ static int ringbuf_open_file(rb_file *rfile, int *err)
/* remove old file (if any, so ignore error) */
ws_unlink(rfile->name);
}
else if (rb_data.compress_type != NULL && strcmp(rb_data.compress_type, "gzip") == 0) {
ringbuf_start_compress_file(rfile);
}
g_free(rfile->name);
}
@ -121,7 +254,7 @@ static int ringbuf_open_file(rb_file *rfile, int *err)
* Initialize the ringbuffer data structures
*/
int
ringbuf_init(const char *capfile_name, guint num_files, gboolean group_read_access)
ringbuf_init(const char *capfile_name, guint num_files, gboolean group_read_access, gchar *compress_type)
{
unsigned int i;
char *pfx, *last_pathsep;
@ -137,6 +270,8 @@ ringbuf_init(const char *capfile_name, guint num_files, gboolean group_read_acce
rb_data.io_buffer = NULL;
rb_data.group_read_access = group_read_access;
rb_data.name_h = NULL;
rb_data.compress_type = compress_type;
g_mutex_init(&rb_data.mutex);
/* just to be sure ... */
if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
@ -397,6 +532,8 @@ ringbuf_free(void)
g_free(rb_data.fsuffix);
rb_data.fsuffix = NULL;
}
CleanupOldCap(NULL);
}
/*

View File

@ -23,7 +23,7 @@
/* Maximum number for FAT filesystems */
#define RINGBUFFER_WARN_NUM_FILES 65535
int ringbuf_init(const char *capture_name, guint num_files, gboolean group_read_access);
int ringbuf_init(const char *capture_name, guint num_files, gboolean group_read_access, gchar* compress_type);
gboolean ringbuf_is_initialized(void);
const gchar *ringbuf_current_filename(void);
FILE *ringbuf_init_libpcap_fdopen(int *err);

View File

@ -1103,6 +1103,7 @@ main(int argc, char *argv[])
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
case 'B': /* Buffer size */
#endif
case LONGOPT_COMPRESS_TYPE: /* compress type */
/* These are options only for packet capture. */
#ifdef HAVE_LIBPCAP
exit_status = capture_opts_add_opt(&global_capture_opts, opt, optarg, &start_capture);

View File

@ -206,6 +206,8 @@ CaptureOptionsDialog::CaptureOptionsDialog(QWidget *parent) :
ui->filenameLineEdit->setPlaceholderText(tr("Leave blank to use a temporary file"));
ui->rbCompressionNone->setChecked(true);
// Changes in interface selections or capture filters should be propagated
// to the main welcome screen where they will be applied to the global
// capture options.
@ -1089,6 +1091,17 @@ bool CaptureOptionsDialog::saveOptionsToPreferences()
#endif
}
}
g_free(global_capture_opts.compress_type);
if (ui->rbCompressionNone->isChecked() ) {
global_capture_opts.compress_type = NULL;
} else if (ui->rbCompressionGzip->isChecked() ) {
global_capture_opts.compress_type = qstring_strdup("gzip");
} else {
global_capture_opts.compress_type = NULL;
}
prefs_main_write();
return true;
}

View File

@ -6,15 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>950</width>
<height>440</height>
<width>1198</width>
<height>988</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="inputTab">
<attribute name="title">
@ -277,12 +277,12 @@
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="PktCheckBox">
<property name="text">
<string>after</string>
</property>
<property name="toolTip">
<string>Switch to the next file after the specified number of packets have been captured.</string>
</property>
<property name="text">
<string>after</string>
</property>
</widget>
</item>
<item row="1" column="1">
@ -310,12 +310,12 @@
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="MBCheckBox">
<property name="text">
<string>after</string>
</property>
<property name="toolTip">
<string>Switch to the next file after the file size exceeds the specified file size.</string>
</property>
<property name="text">
<string>after</string>
</property>
</widget>
</item>
<item row="2" column="1">
@ -364,12 +364,12 @@
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="SecsCheckBox">
<property name="text">
<string>after</string>
</property>
<property name="toolTip">
<string>Switch to the next file when the time capturing to the current file exceeds the specified time.</string>
</property>
<property name="text">
<string>after</string>
</property>
</widget>
</item>
<item row="3" column="1">
@ -418,13 +418,13 @@
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="IntervalSecsCheckBox">
<property name="text">
<string>when time is a multiple of</string>
</property>
<property name="toolTip">
<string>Switch to the next file when the (wall clock) time is an even multiple of the specified interval.
For example, use 1 hour to have a new file created every hour on the hour.</string>
</property>
<property name="text">
<string>when time is a multiple of</string>
</property>
</widget>
</item>
<item row="4" column="1">
@ -456,6 +456,9 @@ For example, use 1 hour to have a new file created every hour on the hour.</stri
<string>Switch to the next file when the (wall clock) time is an even multiple of the specified interval.
For example, use 1 hour to have a new file created every hour on the hour.</string>
</property>
<property name="currentIndex">
<number>2</number>
</property>
<item>
<property name="text">
<string>seconds</string>
@ -471,9 +474,6 @@ For example, use 1 hour to have a new file created every hour on the hour.</stri
<string>hours</string>
</property>
</item>
<property name="currentIndex">
<number>2</number>
</property>
</widget>
</item>
</layout>
@ -529,6 +529,35 @@ For example, use 1 hour to have a new file created every hour on the hour.</stri
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="gbCompression">
<property name="title">
<string>compression</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QRadioButton" name="rbCompressionNone">
<property name="text">
<string>None</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbCompressionGzip">
<property name="text">
<string>gzip</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
@ -545,6 +574,7 @@ For example, use 1 hour to have a new file created every hour on the hour.</stri
</layout>
<zorder>gbNewFileAuto</zorder>
<zorder>gbCaptureToFile</zorder>
<zorder>gbCompression</zorder>
</widget>
<widget class="QWidget" name="optionsTab">
<attribute name="title">
@ -902,4 +932,7 @@ For example, use 1 hour to have a new file created every hour on the hour.</stri
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>