From e4379f0ea1ae75045cd7969b18bd40c9f3fefa6c Mon Sep 17 00:00:00 2001 From: David Perry Date: Wed, 29 Jul 2020 09:36:19 -0400 Subject: [PATCH] Dumpcap: print closed ring-buffer file names This proposal adds a new option '-b printname:' to dumpcap. If used, dumpcap will print the name of each ring buffer file it creates after it is closed. Allows the use of '-'/'stdout' and 'stderr'. Use case: Since the file name is printed after the file is closed for writing, an automated capture process can do something like the following with the guarantee that the file in question will not be changed. dumpcap -i eth0 -b files:2 -b printname:stdout [-b ...] | \ while read cap_file_name ; do # Do something with $cap_file_name done This sort of scripting is difficult in dumpcap's current form. Dumpcap prints the names of new files to stderr as it *opens* them, so a script attempting to use this must sleep for "-b duration:value" seconds plus some fudge time to be sure it's getting a closed, unchanging file. Change-Id: Idb288cc7c8c30443256d35c8cd4460a2e3f0861c Reviewed-on: https://code.wireshark.org/review/37994 Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- capture_opts.c | 6 ++++++ capture_opts.h | 3 +++ doc/dumpcap.pod | 4 ++++ dumpcap.c | 12 ++++++++++++ ringbuffer.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ ringbuffer.h | 1 + 6 files changed, 76 insertions(+) diff --git a/capture_opts.c b/capture_opts.c index 5d4ed86355..05a8e67897 100644 --- a/capture_opts.c +++ b/capture_opts.c @@ -116,6 +116,8 @@ capture_opts_init(capture_options *capture_opts) capture_opts->output_to_pipe = FALSE; capture_opts->capture_child = FALSE; + capture_opts->print_file_names = FALSE; + capture_opts->print_name_to = NULL; } void @@ -248,6 +250,7 @@ capture_opts_log(const char *log_domain, GLogLevelFlags log_level, capture_optio g_log(log_domain, log_level, "FileInterval (%u) : %u", capture_opts->has_file_interval, capture_opts->file_interval); g_log(log_domain, log_level, "FilePackets (%u) : %u", capture_opts->has_file_packets, capture_opts->file_packets); g_log(log_domain, log_level, "RingNumFiles (%u) : %u", capture_opts->has_ring_num_files, capture_opts->ring_num_files); + g_log(log_domain, log_level, "RingPrintFiles (%u) : %s", capture_opts->print_file_names, (capture_opts->print_file_names ? capture_opts->print_name_to : "")); g_log(log_domain, log_level, "AutostopFiles (%u) : %u", capture_opts->has_autostop_files, capture_opts->autostop_files); g_log(log_domain, log_level, "AutostopPackets (%u) : %u", capture_opts->has_autostop_packets, capture_opts->autostop_packets); @@ -411,6 +414,9 @@ get_ring_arguments(capture_options *capture_opts, const char *arg) } else if (strcmp(arg,"packets") == 0) { capture_opts->has_file_packets = TRUE; capture_opts->file_packets = get_positive_int(p, "ring buffer packet count"); + } else if (strcmp(arg,"printname") == 0) { + capture_opts->print_file_names = TRUE; + capture_opts->print_name_to = g_strdup(p); } *colonp = ':'; /* put the colon back */ diff --git a/capture_opts.h b/capture_opts.h index e81aa72d40..82029b8eea 100644 --- a/capture_opts.h +++ b/capture_opts.h @@ -310,6 +310,9 @@ typedef struct capture_options_tag { gchar *capture_comment; /** capture comment to write to the output file */ + gboolean print_file_names; /**< TRUE if printing names of completed + files as we close them */ + gchar *print_name_to; /**< output file name */ /* internally used (don't touch from outside) */ gboolean output_to_pipe; /**< save_file is a pipe (named or stdout) */ diff --git a/doc/dumpcap.pod b/doc/dumpcap.pod index ff8ea76c67..4dbf25f786 100644 --- a/doc/dumpcap.pod +++ b/doc/dumpcap.pod @@ -129,6 +129,10 @@ every hour on the hour. B:I switch to the next file after it contains I packets. +B:I print the name of the most recently written file +to I after the file is closed. I can be C or C<-> +for standard output, or C for standard error. + Example: B<-b filesize:1000 -b files:5> results in a ring buffer of five files of size one megabyte each. diff --git a/dumpcap.c b/dumpcap.c index 2d4e0195d3..7d3a88013f 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -420,6 +420,8 @@ print_usage(FILE *output) fprintf(output, " packets:NUM - ringbuffer: replace after NUM packets\n"); fprintf(output, " interval:NUM - switch to next file when the time is\n"); fprintf(output, " an exact multiple of NUM secs\n"); + fprintf(output, " printname:FILE - print filename to FILE when written\n"); + fprintf(output, " (can use 'stdout' or 'stderr')\n"); fprintf(output, " -n use pcapng format instead of pcap (default)\n"); fprintf(output, " -P use libpcap format instead of pcapng\n"); fprintf(output, " --capture-comment \n"); @@ -3400,6 +3402,16 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd, g_free(capfile_name); capfile_name = NULL; } + if (capture_opts->print_file_names) { + if (!ringbuf_set_print_name(capture_opts->print_name_to, NULL)) { + g_snprintf(errmsg, errmsg_len, "Could not write filenames to %s: %s.\n", + capture_opts->print_name_to, + g_strerror(errno)); + g_free(capfile_name); + ringbuf_error_cleanup(); + return FALSE; + } + } } else { /* Try to open/create the specified file for use as a capture buffer. */ *save_file_fd = ws_open(capfile_name, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, diff --git a/ringbuffer.c b/ringbuffer.c index 2069f4de98..f674643635 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -63,6 +63,7 @@ typedef struct _ringbuf_data { FILE *pdh; 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 */ } ringbuf_data; static ringbuf_data rb_data; @@ -135,6 +136,7 @@ ringbuf_init(const char *capfile_name, guint num_files, gboolean group_read_acce rb_data.pdh = NULL; rb_data.io_buffer = NULL; rb_data.group_read_access = group_read_access; + rb_data.name_h = NULL; /* just to be sure ... */ if (num_files <= RINGBUFFER_MAX_NUM_FILES) { @@ -204,6 +206,34 @@ ringbuf_init(const char *capfile_name, guint num_files, gboolean group_read_acce return rb_data.fd; } +/* + * Set name of file to which to print ringbuffer file names. + */ +gboolean +ringbuf_set_print_name(gchar *name, int *err) +{ + if (rb_data.name_h != NULL) { + if (EOF == fclose(rb_data.name_h)) { + if (err != NULL) { + *err = errno; + } + return FALSE; + } + } + if (!strcmp(name, "-") || !strcmp(name, "stdout")) { + rb_data.name_h = stdout; + } else if (!strcmp(name, "stderr")) { + rb_data.name_h = stderr; + } else { + if (NULL == (rb_data.name_h = ws_fopen(name, "wt"))) { + if (err != NULL) { + *err = errno; + } + return FALSE; + } + } + return TRUE; +} /* * Whether the ringbuf filenames are ready. @@ -276,6 +306,11 @@ ringbuf_switch_file(FILE **pdh, gchar **save_file, int *save_file_fd, int *err) rb_data.pdh = NULL; rb_data.fd = -1; + if (rb_data.name_h != NULL) { + fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename()); + fflush(rb_data.name_h); + } + /* get the next file number and open it */ rb_data.curr_file_num++ /* = next_file_num*/; @@ -322,6 +357,15 @@ ringbuf_libpcap_dump_close(gchar **save_file, int *err) } + if (rb_data.name_h != NULL) { + fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename()); + fflush(rb_data.name_h); + + if (EOF == fclose(rb_data.name_h)) { + /* Can't really do much about this, can we? */ + } + } + /* set the save file name to the current file */ *save_file = rb_data.files[rb_data.curr_file_num % rb_data.num_files].name; return ret_val; @@ -387,6 +431,12 @@ ringbuf_error_cleanup(void) g_free(rb_data.io_buffer); rb_data.io_buffer = NULL; + if (rb_data.name_h != NULL) { + if (EOF == fclose(rb_data.name_h)) { + /* Can't really do much about this, can we? */ + } + } + /* free the memory */ ringbuf_free(); } diff --git a/ringbuffer.h b/ringbuffer.h index 4c1a3baaa9..d95dff2636 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -32,6 +32,7 @@ gboolean ringbuf_switch_file(FILE **pdh, gchar **save_file, int *save_file_fd, gboolean ringbuf_libpcap_dump_close(gchar **save_file, int *err); void ringbuf_free(void); void ringbuf_error_cleanup(void); +gboolean ringbuf_set_print_name(gchar *name, int *err); #endif /* ringbuffer.h */