From Graeme Hewson: flush the output after every frame if Tethereal is
[obnox/wireshark/wip.git] / ringbuffer.c
1 /* ringbuffer.c
2  * Routines for packet capture windows
3  *
4  * $Id: ringbuffer.c,v 1.3 2002/06/23 20:30:01 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  * 
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.
14  * 
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.
19  * 
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.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef HAVE_LIBPCAP
30
31 #ifdef HAVE_IO_H
32 #include <io.h>
33 #endif
34
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38
39 #ifdef HAVE_SYS_TYPES_H
40 #include <sys/types.h>
41 #endif
42
43 #ifdef HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50
51 #include <stdio.h>
52 #include <string.h>
53 #include <time.h>
54 #include <errno.h>
55
56 #ifdef NEED_SNPRINTF_H
57 #include "snprintf.h"
58 #endif
59
60 #include "wiretap/wtap.h"
61 #include "ringbuffer.h"
62
63 /* Win32 needs the O_BINARY flag for open() */
64 #ifndef O_BINARY
65 #define O_BINARY        0
66 #endif
67
68 /* Ringbuffer file structure */
69 typedef struct _rb_file {
70   gchar*        name;
71   int           fd;
72   time_t        creation_time;
73   gboolean      is_new;
74   guint16       number;
75   wtap_dumper*  pdh;
76   long          start_pos;
77 } rb_file;
78
79 /* Ringbuffer data structure */
80 typedef struct _ringbuf_data {
81   rb_file*      files;
82   guint         num_files;      /* Number of ringbuffer files */
83   guint         curr_file_num;  /* Number of the current file */
84   gchar*        fprefix;        /* Filename prefix */
85   gchar*        fsuffix;        /* Filename suffix */
86 } ringbuf_data; 
87
88 /* Create the ringbuffer data structure */
89 static ringbuf_data rb_data;
90
91 /* 
92  * Initialize the ringbuffer data structure
93  */
94 int 
95 ringbuf_init(const char *capfile_name, guint num_files)
96 {
97   int          save_file_fd;
98   unsigned int i;
99   char        *pfx;
100   gchar       *save_file;
101   char         save_file_num[3+1];
102
103   /* just to be sure ... */
104   if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
105     rb_data.num_files = num_files;
106   } else {
107     rb_data.num_files = RINGBUFFER_MAX_NUM_FILES;
108   }
109
110   /* Check file name */
111   if (capfile_name == NULL) {
112     /* ringbuffer does not work with temporary files! */
113     return -1;
114   }
115
116   /* Open the initial file */
117   save_file_fd = open(capfile_name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600);
118   if (save_file_fd == -1) {
119     /* open failed */
120     return -1;
121   }
122  
123   /* allocate memory */
124   rb_data.files = (rb_file *)calloc(num_files, sizeof(rb_file));
125   if (rb_data.files == NULL) {
126     /* could not allocate memory */
127     return -1;
128   }
129
130   /* initialize */
131   rb_data.fprefix = NULL;
132   rb_data.fsuffix = NULL;
133   for (i=0; i<rb_data.num_files; i++) {
134     rb_data.files[i].name = NULL;
135     rb_data.files[i].fd = -1;
136   }
137
138   /* get file name prefix/suffix */
139   save_file = g_strdup(capfile_name);
140   pfx = strrchr(save_file,'.');
141   if (pfx != NULL) {
142     pfx[0] = '\0';
143     rb_data.fprefix = g_strdup(save_file);
144     pfx[0] = '.'; /* restore capfile_name */
145     rb_data.fsuffix = g_strdup(pfx);
146   } else {
147     rb_data.fprefix = g_strdup(save_file);
148     rb_data.fsuffix = NULL;
149   }
150   g_free(save_file);
151   save_file = NULL;
152
153 #ifdef _WIN32
154   _tzset();
155 #endif
156   /* save the initial file parameters */
157   rb_data.files[0].name = g_strdup(capfile_name);
158   rb_data.files[0].fd = save_file_fd;
159   rb_data.files[0].creation_time = time(NULL);
160   rb_data.files[0].number = 0;
161   rb_data.files[0].is_new = TRUE;
162
163   /* create the other files */
164   for (i=1; i<rb_data.num_files; i++) {
165     /* create a file name */
166     snprintf(save_file_num,3+1,"%03d",i);
167     save_file = g_strconcat(capfile_name, ".", save_file_num, NULL);
168     /* open the file */
169     save_file_fd = open(save_file, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600);
170     if (save_file_fd != -1) {
171       rb_data.files[i].name = save_file;
172       rb_data.files[i].fd = save_file_fd;
173       rb_data.files[i].creation_time = time(NULL);
174       rb_data.files[i].number = i;
175       rb_data.files[i].is_new = TRUE;
176     } else {
177       /* could not open a file  */
178       ringbuf_error_cleanup();
179       return -1;
180     }
181   }
182   
183   /* done */
184   rb_data.curr_file_num = 0;
185   return rb_data.files[0].fd;
186 }
187
188 /* 
189  * Calls wtap_dump_fdopen() for all ringbuffer files
190  */
191 wtap_dumper* 
192 ringbuf_init_wtap_dump_fdopen(int filetype, int linktype, 
193   int snaplen, int *err)
194 {
195   unsigned int  i;
196   FILE         *fh;
197
198   for (i=0; i<rb_data.num_files; i++) {
199     rb_data.files[i].pdh = wtap_dump_fdopen(rb_data.files[i].fd, filetype,
200       linktype, snaplen, err);
201     if (rb_data.files[i].pdh == NULL) {
202       /* could not open file */
203       return NULL;
204     } else {
205       /*
206        * Flush out the capture file header, as written by "wtap_dump_fdopen()".
207        *
208        * XXX - this relies on Wiretap writing out data sequentially,
209        * and writing the entire capture file header when the file
210        * is created.  That happens to be true for libpcap files,
211        * which are Ethereal's native capture files, and which are
212        * therefore the capture file types we're writing, but is not
213        * true for all the capture file types Wiretap can write.
214        */
215       fh = wtap_dump_file(rb_data.files[i].pdh);
216       if (fflush(fh) == EOF) {
217         if (err != NULL) {
218           *err = errno;
219         }
220         return NULL;
221       }
222       if ((rb_data.files[i].start_pos = ftell(fh)) < 0) {
223         if (err != NULL) {
224           *err = errno;
225         }
226         return NULL;
227       }
228       clearerr(fh);
229     }
230   }
231   /* done */
232   rb_data.files[0].is_new = FALSE;
233   return rb_data.files[0].pdh;
234 }
235
236 /* 
237  * Switches to the next ringbuffer file
238  */
239 gboolean
240 ringbuf_switch_file(capture_file *cf, wtap_dumper **pdh, int *err)
241 {
242   int   next_file_num;
243   FILE *fh;
244   gboolean err_on_next = FALSE;
245
246   /* flush the current file */
247   fh = wtap_dump_file(rb_data.files[rb_data.curr_file_num].pdh);
248   clearerr(fh);
249   errno = WTAP_ERR_CANT_CLOSE;
250   if (fflush(fh) == EOF) {
251     if (err != NULL) {
252       *err = errno;
253     }
254     return FALSE;
255   }
256     
257   /* get the next file number */
258   next_file_num = (rb_data.curr_file_num + 1) % rb_data.num_files;
259   /* prepare the file if it was already used */
260   if (!rb_data.files[next_file_num].is_new) {
261     /* rewind to the position after the file header */
262     fh = wtap_dump_file(rb_data.files[next_file_num].pdh);
263     if (fseek(fh, rb_data.files[next_file_num].start_pos, SEEK_SET) < 0) {
264       *err = errno;
265       /* Don't return straight away: have caller report correct save_file */
266       err_on_next = TRUE;
267     }
268     wtap_set_bytes_dumped(rb_data.files[next_file_num].pdh,
269       rb_data.files[next_file_num].start_pos);
270     /* set the absolute file number */
271     rb_data.files[next_file_num].number += rb_data.num_files;
272   }
273 #ifdef _WIN32
274   _tzset();
275 #endif
276   rb_data.files[next_file_num].creation_time = time(NULL);
277   /* switch to the new file */
278   cf->save_file = rb_data.files[next_file_num].name;
279   cf->save_file_fd = rb_data.files[next_file_num].fd;
280   (*pdh) = rb_data.files[next_file_num].pdh;
281   /* mark the file as used */
282   rb_data.files[next_file_num].is_new = FALSE;
283   /* finally set the current file number */
284   rb_data.curr_file_num = next_file_num;
285
286   if (err_on_next)
287     return FALSE;
288
289   return TRUE;
290 }
291
292 /* 
293  * Calls wtap_dump_close() for all ringbuffer files
294  */
295 gboolean
296 ringbuf_wtap_dump_close(capture_file *cf, int *err)
297 {
298   gboolean     ret_val;
299   unsigned int i;
300   gchar       *new_name;
301   char         filenum[5+1];
302   char         timestr[14+1];
303   FILE        *fh;
304
305   /* assume success */
306   ret_val = TRUE;
307   /* close all files */
308   for (i=0; i<rb_data.num_files; i++) {
309     fh = wtap_dump_file(rb_data.files[i].pdh);
310     clearerr(fh);
311     /* Flush the file */
312     errno = WTAP_ERR_CANT_CLOSE;
313     if (fflush(fh) == EOF) {
314       if (err != NULL) {
315         *err = errno;
316       }
317       ret_val = FALSE;
318       /* If the file's not a new one, remove it as it hasn't been truncated
319          and thus contains garbage at the end.
320
321          We don't remove it if it's new - it's incomplete, but at least
322          the stuff before the incomplete record is usable. */
323       close(rb_data.files[i].fd);
324       if (!rb_data.files[i].is_new) {
325         unlink(rb_data.files[i].name);
326       }
327       continue;
328     }
329
330     /* Truncate the file to the current size. This must be done in order
331        to get rid of the 'garbage' packets at the end of the file from
332        previous usage */
333     if (!rb_data.files[i].is_new) {
334       if (ftruncate(rb_data.files[i].fd,ftell(fh)) != 0) {
335         /* could not truncate the file */
336         if (err != NULL) {
337           *err = errno;
338         }
339         ret_val = FALSE;
340         /* remove the file since it contains garbage at the end */
341         close(rb_data.files[i].fd);
342         unlink(rb_data.files[i].name);
343         continue;
344       }
345     }
346     /* close the file */
347     if (!wtap_dump_close(rb_data.files[i].pdh, err)) {
348       /* error only if it is a used file */
349       if (!rb_data.files[i].is_new) {
350         ret_val = FALSE;
351       }
352     }
353     if (!rb_data.files[i].is_new) {
354       /* rename the file */
355       snprintf(filenum,5+1,"%05d",rb_data.files[i].number);
356       strftime(timestr,14+1,"%Y%m%d%H%M%S", 
357         localtime(&(rb_data.files[i].creation_time)));
358       new_name = g_strconcat(rb_data.fprefix,"_", filenum, "_", timestr, 
359         rb_data.fsuffix, NULL);
360       if (rename(rb_data.files[i].name, new_name) != 0) {
361         /* save the latest error */
362         if (err != NULL) {
363           *err = errno;
364         }
365         ret_val = FALSE;
366         g_free(new_name);
367       } else {
368         g_free(rb_data.files[i].name);
369         rb_data.files[i].name = new_name;
370       }
371     } else {
372       /* this file has never been used - remove it */
373       unlink(rb_data.files[i].name);
374     }
375   }
376   /* make the current file the save file */
377   cf->save_file = rb_data.files[rb_data.curr_file_num].name;
378   return ret_val;
379 }
380
381 /* 
382  * Frees all memory allocated by the ringbuffer
383  */
384 void
385 ringbuf_free()
386
387   unsigned int i;
388   
389   if (rb_data.files != NULL) {
390     for (i=0; i < rb_data.num_files; i++) {
391       g_free(rb_data.files[i].name);
392       rb_data.files[i].name = NULL;
393     }
394     free(rb_data.files);
395     rb_data.files = NULL;
396   }
397   g_free(rb_data.fprefix);
398   g_free(rb_data.fsuffix);
399 }
400
401 /* 
402  * Frees all memory allocated by the ringbuffer
403  */
404 void 
405 ringbuf_error_cleanup(void)
406 {
407   unsigned int i;
408   
409   if (rb_data.files == NULL) {
410     ringbuf_free();
411     return;
412   }
413
414   for (i=0; i<rb_data.num_files; i++) {
415     /* try to close via wtap */
416     if (rb_data.files[i].pdh != NULL) {
417       if (wtap_dump_close(rb_data.files[i].pdh, NULL)) {
418         /* done */
419         rb_data.files[i].fd = -1;
420       }
421     }
422     /* close directly if still open */
423     if (rb_data.files[i].fd != -1) {
424       close(rb_data.files[i].fd);
425     }
426     /* remove the other files, the initial file will be handled
427        by the calling funtion */
428     if (rb_data.files[i].name != NULL) {
429       unlink(rb_data.files[i].name);
430     }
431   }
432   /* free the memory */  
433   ringbuf_free();
434 }
435
436 #endif /* HAVE_LIBPCAP */