2 * Routines for packet capture windows
4 * $Id: ringbuffer.c,v 1.6 2002/09/22 16:17:41 gerald Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
39 #ifdef HAVE_SYS_STAT_H
52 #ifdef NEED_SNPRINTF_H
56 #include "wiretap/wtap.h"
57 #include "ringbuffer.h"
59 /* Win32 needs the O_BINARY flag for open() */
64 /* Ringbuffer file structure */
65 typedef struct _rb_file {
75 /* Ringbuffer data structure */
76 typedef struct _ringbuf_data {
78 guint num_files; /* Number of ringbuffer files */
79 guint curr_file_num; /* Number of the current file */
80 gchar* fprefix; /* Filename prefix */
81 gchar* fsuffix; /* Filename suffix */
84 /* Create the ringbuffer data structure */
85 static ringbuf_data rb_data;
88 * Initialize the ringbuffer data structure
91 ringbuf_init(const char *capfile_name, guint num_files)
97 char save_file_num[3+1];
99 /* just to be sure ... */
100 if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
101 rb_data.num_files = num_files;
103 rb_data.num_files = RINGBUFFER_MAX_NUM_FILES;
106 /* Check file name */
107 if (capfile_name == NULL) {
108 /* ringbuffer does not work with temporary files! */
112 /* Open the initial file */
113 save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600);
114 if (save_file_fd == -1) {
119 /* allocate memory */
120 rb_data.files = (rb_file *)calloc(num_files, sizeof(rb_file));
121 if (rb_data.files == NULL) {
122 /* could not allocate memory */
127 rb_data.fprefix = NULL;
128 rb_data.fsuffix = NULL;
129 for (i=0; i<rb_data.num_files; i++) {
130 rb_data.files[i].name = NULL;
131 rb_data.files[i].fd = -1;
134 /* get file name prefix/suffix */
135 save_file = g_strdup(capfile_name);
136 pfx = strrchr(save_file,'.');
139 rb_data.fprefix = g_strdup(save_file);
140 pfx[0] = '.'; /* restore capfile_name */
141 rb_data.fsuffix = g_strdup(pfx);
143 rb_data.fprefix = g_strdup(save_file);
144 rb_data.fsuffix = NULL;
152 /* save the initial file parameters */
153 rb_data.files[0].name = g_strdup(capfile_name);
154 rb_data.files[0].fd = save_file_fd;
155 rb_data.files[0].creation_time = time(NULL);
156 rb_data.files[0].number = 0;
157 rb_data.files[0].is_new = TRUE;
159 /* create the other files */
160 for (i=1; i<rb_data.num_files; i++) {
161 /* create a file name */
162 snprintf(save_file_num,3+1,"%03d",i);
163 save_file = g_strconcat(capfile_name, ".", save_file_num, NULL);
165 save_file_fd = open(save_file, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600);
166 if (save_file_fd != -1) {
167 rb_data.files[i].name = save_file;
168 rb_data.files[i].fd = save_file_fd;
169 rb_data.files[i].creation_time = time(NULL);
170 rb_data.files[i].number = i;
171 rb_data.files[i].is_new = TRUE;
173 /* could not open a file */
174 ringbuf_error_cleanup();
180 rb_data.curr_file_num = 0;
181 return rb_data.files[0].fd;
185 * Calls wtap_dump_fdopen() for all ringbuffer files
188 ringbuf_init_wtap_dump_fdopen(int filetype, int linktype,
189 int snaplen, int *err)
194 for (i=0; i<rb_data.num_files; i++) {
195 rb_data.files[i].pdh = wtap_dump_fdopen(rb_data.files[i].fd, filetype,
196 linktype, snaplen, err);
197 if (rb_data.files[i].pdh == NULL) {
198 /* could not open file */
202 * Flush out the capture file header, as written by "wtap_dump_fdopen()".
204 * XXX - this relies on Wiretap writing out data sequentially,
205 * and writing the entire capture file header when the file
206 * is created. That happens to be true for libpcap files,
207 * which are Ethereal's native capture files, and which are
208 * therefore the capture file types we're writing, but is not
209 * true for all the capture file types Wiretap can write.
211 fh = wtap_dump_file(rb_data.files[i].pdh);
212 if (fflush(fh) == EOF) {
218 if ((rb_data.files[i].start_pos = ftell(fh)) < 0) {
228 rb_data.files[0].is_new = FALSE;
229 return rb_data.files[0].pdh;
233 * Switches to the next ringbuffer file
236 ringbuf_switch_file(capture_file *cf, wtap_dumper **pdh, int *err)
240 gboolean err_on_next = FALSE;
242 /* flush the current file */
243 fh = wtap_dump_file(rb_data.files[rb_data.curr_file_num].pdh);
245 errno = WTAP_ERR_CANT_CLOSE;
246 if (fflush(fh) == EOF) {
253 /* get the next file number */
254 next_file_num = (rb_data.curr_file_num + 1) % rb_data.num_files;
255 /* prepare the file if it was already used */
256 if (!rb_data.files[next_file_num].is_new) {
257 /* rewind to the position after the file header */
258 fh = wtap_dump_file(rb_data.files[next_file_num].pdh);
259 if (fseek(fh, rb_data.files[next_file_num].start_pos, SEEK_SET) < 0) {
261 /* Don't return straight away: have caller report correct save_file */
264 wtap_set_bytes_dumped(rb_data.files[next_file_num].pdh,
265 rb_data.files[next_file_num].start_pos);
266 /* set the absolute file number */
267 rb_data.files[next_file_num].number += rb_data.num_files;
272 rb_data.files[next_file_num].creation_time = time(NULL);
273 /* switch to the new file */
274 cf->save_file = rb_data.files[next_file_num].name;
275 cf->save_file_fd = rb_data.files[next_file_num].fd;
276 (*pdh) = rb_data.files[next_file_num].pdh;
277 /* mark the file as used */
278 rb_data.files[next_file_num].is_new = FALSE;
279 /* finally set the current file number */
280 rb_data.curr_file_num = next_file_num;
289 * Calls wtap_dump_close() for all ringbuffer files
292 ringbuf_wtap_dump_close(capture_file *cf, int *err)
295 gboolean data_captured = TRUE;
298 long curr_file_curr_pos = 0; /* Initialise to avoid GCC warning */
306 /* close all files */
307 for (i=0; i<rb_data.num_files; i++) {
308 fh = wtap_dump_file(rb_data.files[i].pdh);
311 /* Get the current file position */
312 if ((curr_pos = ftell(fh)) < 0) {
317 /* If the file's not a new one, remove it as it hasn't been truncated
318 and thus contains garbage at the end.
319 If the file is a new one, it contains only the dump header, so
321 close(rb_data.files[i].fd);
322 unlink(rb_data.files[i].name);
323 /* Set name for caller's error message */
324 cf->save_file = rb_data.files[i].name;
328 if (i == rb_data.curr_file_num)
329 curr_file_curr_pos = curr_pos;
331 /* If buffer 0 is empty and the ring hasn't wrapped,
332 no data has been captured. */
333 if (i == 0 && curr_pos == rb_data.files[0].start_pos &&
334 rb_data.files[0].number == 0)
335 data_captured = FALSE;
338 errno = WTAP_ERR_CANT_CLOSE;
339 if (fflush(fh) == EOF) {
344 close(rb_data.files[i].fd);
345 unlink(rb_data.files[i].name);
346 cf->save_file = rb_data.files[i].name;
350 /* Truncate the file to the current size. This must be done in order
351 to get rid of the 'garbage' packets at the end of the file from
353 if (!rb_data.files[i].is_new) {
354 if (ftruncate(rb_data.files[i].fd, curr_pos) != 0) {
355 /* could not truncate the file */
360 /* remove the file since it contains garbage at the end */
361 close(rb_data.files[i].fd);
362 unlink(rb_data.files[i].name);
363 cf->save_file = rb_data.files[i].name;
368 if (!wtap_dump_close(rb_data.files[i].pdh, err)) {
369 /* error only if it is a used file */
370 if (curr_pos > rb_data.files[i].start_pos) {
372 /* Don't unlink it; maybe the user can salvage it. */
373 cf->save_file = rb_data.files[i].name;
378 /* Rename buffers which have data and delete empty buffers --
379 except if no data at all has been captured we need to keep
380 the empty first buffer. */
381 if (curr_pos > rb_data.files[i].start_pos ||
382 (i == 0 && !data_captured)) {
383 /* rename the file */
384 snprintf(filenum,5+1,"%05d",rb_data.files[i].number);
385 strftime(timestr,14+1,"%Y%m%d%H%M%S",
386 localtime(&(rb_data.files[i].creation_time)));
387 new_name = g_strconcat(rb_data.fprefix,"_", filenum, "_", timestr,
388 rb_data.fsuffix, NULL);
389 if (rename(rb_data.files[i].name, new_name) != 0) {
390 /* save the latest error */
395 cf->save_file = rb_data.files[i].name;
398 g_free(rb_data.files[i].name);
399 rb_data.files[i].name = new_name;
402 /* this file is empty - remove it */
403 unlink(rb_data.files[i].name);
408 /* Make the current file the save file, or if it's empty apart from
409 the header, make the previous file the save file (assuming data
410 has been captured). */
411 if (curr_file_curr_pos ==
412 rb_data.files[rb_data.curr_file_num].start_pos &&
414 if (rb_data.curr_file_num > 0)
415 rb_data.curr_file_num -= 1;
417 rb_data.curr_file_num = rb_data.num_files - 1;
419 cf->save_file = rb_data.files[rb_data.curr_file_num].name;
425 * Frees all memory allocated by the ringbuffer
432 if (rb_data.files != NULL) {
433 for (i=0; i < rb_data.num_files; i++) {
434 g_free(rb_data.files[i].name);
435 rb_data.files[i].name = NULL;
438 rb_data.files = NULL;
440 g_free(rb_data.fprefix);
441 g_free(rb_data.fsuffix);
445 * Frees all memory allocated by the ringbuffer
448 ringbuf_error_cleanup(void)
452 if (rb_data.files == NULL) {
457 for (i=0; i<rb_data.num_files; i++) {
458 /* try to close via wtap */
459 if (rb_data.files[i].pdh != NULL) {
460 if (wtap_dump_close(rb_data.files[i].pdh, NULL)) {
462 rb_data.files[i].fd = -1;
465 /* close directly if still open */
466 if (rb_data.files[i].fd != -1) {
467 close(rb_data.files[i].fd);
469 /* remove the other files, the initial file will be handled
470 by the calling funtion */
471 if (rb_data.files[i].name != NULL) {
472 unlink(rb_data.files[i].name);
475 /* free the memory */
479 #endif /* HAVE_LIBPCAP */