/* ringbuffer.c * Routines for packet capture windows * * $Id: ringbuffer.c,v 1.6 2002/09/22 16:17:41 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_LIBPCAP #ifdef HAVE_IO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #ifdef NEED_SNPRINTF_H #include "snprintf.h" #endif #include "wiretap/wtap.h" #include "ringbuffer.h" /* Win32 needs the O_BINARY flag for open() */ #ifndef O_BINARY #define O_BINARY 0 #endif /* Ringbuffer file structure */ typedef struct _rb_file { gchar* name; int fd; time_t creation_time; gboolean is_new; guint16 number; wtap_dumper* pdh; long start_pos; } rb_file; /* Ringbuffer data structure */ typedef struct _ringbuf_data { rb_file* files; guint num_files; /* Number of ringbuffer files */ guint curr_file_num; /* Number of the current file */ gchar* fprefix; /* Filename prefix */ gchar* fsuffix; /* Filename suffix */ } ringbuf_data; /* Create the ringbuffer data structure */ static ringbuf_data rb_data; /* * Initialize the ringbuffer data structure */ int ringbuf_init(const char *capfile_name, guint num_files) { int save_file_fd; unsigned int i; char *pfx; gchar *save_file; char save_file_num[3+1]; /* just to be sure ... */ if (num_files <= RINGBUFFER_MAX_NUM_FILES) { rb_data.num_files = num_files; } else { rb_data.num_files = RINGBUFFER_MAX_NUM_FILES; } /* Check file name */ if (capfile_name == NULL) { /* ringbuffer does not work with temporary files! */ return -1; } /* Open the initial file */ save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600); if (save_file_fd == -1) { /* open failed */ return -1; } /* allocate memory */ rb_data.files = (rb_file *)calloc(num_files, sizeof(rb_file)); if (rb_data.files == NULL) { /* could not allocate memory */ return -1; } /* initialize */ rb_data.fprefix = NULL; rb_data.fsuffix = NULL; for (i=0; isave_file = rb_data.files[next_file_num].name; cf->save_file_fd = rb_data.files[next_file_num].fd; (*pdh) = rb_data.files[next_file_num].pdh; /* mark the file as used */ rb_data.files[next_file_num].is_new = FALSE; /* finally set the current file number */ rb_data.curr_file_num = next_file_num; if (err_on_next) return FALSE; return TRUE; } /* * Calls wtap_dump_close() for all ringbuffer files */ gboolean ringbuf_wtap_dump_close(capture_file *cf, int *err) { gboolean ret_val; gboolean data_captured = TRUE; unsigned int i; long curr_pos; long curr_file_curr_pos = 0; /* Initialise to avoid GCC warning */ gchar *new_name; char filenum[5+1]; char timestr[14+1]; FILE *fh; /* assume success */ ret_val = TRUE; /* close all files */ for (i=0; isave_file = rb_data.files[i].name; continue; } if (i == rb_data.curr_file_num) curr_file_curr_pos = curr_pos; /* If buffer 0 is empty and the ring hasn't wrapped, no data has been captured. */ if (i == 0 && curr_pos == rb_data.files[0].start_pos && rb_data.files[0].number == 0) data_captured = FALSE; /* Flush the file */ errno = WTAP_ERR_CANT_CLOSE; if (fflush(fh) == EOF) { if (err != NULL) { *err = errno; } ret_val = FALSE; close(rb_data.files[i].fd); unlink(rb_data.files[i].name); cf->save_file = rb_data.files[i].name; continue; } /* Truncate the file to the current size. This must be done in order to get rid of the 'garbage' packets at the end of the file from previous usage */ if (!rb_data.files[i].is_new) { if (ftruncate(rb_data.files[i].fd, curr_pos) != 0) { /* could not truncate the file */ if (err != NULL) { *err = errno; } ret_val = FALSE; /* remove the file since it contains garbage at the end */ close(rb_data.files[i].fd); unlink(rb_data.files[i].name); cf->save_file = rb_data.files[i].name; continue; } } /* close the file */ if (!wtap_dump_close(rb_data.files[i].pdh, err)) { /* error only if it is a used file */ if (curr_pos > rb_data.files[i].start_pos) { ret_val = FALSE; /* Don't unlink it; maybe the user can salvage it. */ cf->save_file = rb_data.files[i].name; continue; } } /* Rename buffers which have data and delete empty buffers -- except if no data at all has been captured we need to keep the empty first buffer. */ if (curr_pos > rb_data.files[i].start_pos || (i == 0 && !data_captured)) { /* rename the file */ snprintf(filenum,5+1,"%05d",rb_data.files[i].number); strftime(timestr,14+1,"%Y%m%d%H%M%S", localtime(&(rb_data.files[i].creation_time))); new_name = g_strconcat(rb_data.fprefix,"_", filenum, "_", timestr, rb_data.fsuffix, NULL); if (rename(rb_data.files[i].name, new_name) != 0) { /* save the latest error */ if (err != NULL) { *err = errno; } ret_val = FALSE; cf->save_file = rb_data.files[i].name; g_free(new_name); } else { g_free(rb_data.files[i].name); rb_data.files[i].name = new_name; } } else { /* this file is empty - remove it */ unlink(rb_data.files[i].name); } } if (ret_val) { /* Make the current file the save file, or if it's empty apart from the header, make the previous file the save file (assuming data has been captured). */ if (curr_file_curr_pos == rb_data.files[rb_data.curr_file_num].start_pos && data_captured) { if (rb_data.curr_file_num > 0) rb_data.curr_file_num -= 1; else rb_data.curr_file_num = rb_data.num_files - 1; } cf->save_file = rb_data.files[rb_data.curr_file_num].name; } return ret_val; } /* * Frees all memory allocated by the ringbuffer */ void ringbuf_free() { unsigned int i; if (rb_data.files != NULL) { for (i=0; i < rb_data.num_files; i++) { g_free(rb_data.files[i].name); rb_data.files[i].name = NULL; } free(rb_data.files); rb_data.files = NULL; } g_free(rb_data.fprefix); g_free(rb_data.fsuffix); } /* * Frees all memory allocated by the ringbuffer */ void ringbuf_error_cleanup(void) { unsigned int i; if (rb_data.files == NULL) { ringbuf_free(); return; } for (i=0; i