922d9b12ae86ea21a9ea6f5418f4f18ac0284c43
[obnox/wireshark/wip.git] / ringbuffer.c
1 /* ringbuffer.c
2  * Routines for packet capture windows
3  *
4  * $Id: ringbuffer.c,v 1.5 2002/08/28 21:00:41 jmayer 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_STAT_H
40 #include <sys/stat.h>
41 #endif
42
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #include <stdio.h>
48 #include <string.h>
49 #include <time.h>
50 #include <errno.h>
51
52 #ifdef NEED_SNPRINTF_H
53 #include "snprintf.h"
54 #endif
55
56 #include "wiretap/wtap.h"
57 #include "ringbuffer.h"
58
59 /* Win32 needs the O_BINARY flag for open() */
60 #ifndef O_BINARY
61 #define O_BINARY        0
62 #endif
63
64 /* Ringbuffer file structure */
65 typedef struct _rb_file {
66   gchar*        name;
67   int           fd;
68   time_t        creation_time;
69   gboolean      is_new;
70   guint16       number;
71   wtap_dumper*  pdh;
72   long          start_pos;
73 } rb_file;
74
75 /* Ringbuffer data structure */
76 typedef struct _ringbuf_data {
77   rb_file*      files;
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 */
82 } ringbuf_data;
83
84 /* Create the ringbuffer data structure */
85 static ringbuf_data rb_data;
86
87 /*
88  * Initialize the ringbuffer data structure
89  */
90 int
91 ringbuf_init(const char *capfile_name, guint num_files)
92 {
93   int          save_file_fd;
94   unsigned int i;
95   char        *pfx;
96   gchar       *save_file;
97   char         save_file_num[3+1];
98
99   /* just to be sure ... */
100   if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
101     rb_data.num_files = num_files;
102   } else {
103     rb_data.num_files = RINGBUFFER_MAX_NUM_FILES;
104   }
105
106   /* Check file name */
107   if (capfile_name == NULL) {
108     /* ringbuffer does not work with temporary files! */
109     return -1;
110   }
111
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) {
115     /* open failed */
116     return -1;
117   }
118
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 */
123     return -1;
124   }
125
126   /* initialize */
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;
132   }
133
134   /* get file name prefix/suffix */
135   save_file = g_strdup(capfile_name);
136   pfx = strrchr(save_file,'.');
137   if (pfx != NULL) {
138     pfx[0] = '\0';
139     rb_data.fprefix = g_strdup(save_file);
140     pfx[0] = '.'; /* restore capfile_name */
141     rb_data.fsuffix = g_strdup(pfx);
142   } else {
143     rb_data.fprefix = g_strdup(save_file);
144     rb_data.fsuffix = NULL;
145   }
146   g_free(save_file);
147   save_file = NULL;
148
149 #ifdef _WIN32
150   _tzset();
151 #endif
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;
158
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);
164     /* open the file */
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;
172     } else {
173       /* could not open a file  */
174       ringbuf_error_cleanup();
175       return -1;
176     }
177   }
178
179   /* done */
180   rb_data.curr_file_num = 0;
181   return rb_data.files[0].fd;
182 }
183
184 /*
185  * Calls wtap_dump_fdopen() for all ringbuffer files
186  */
187 wtap_dumper*
188 ringbuf_init_wtap_dump_fdopen(int filetype, int linktype,
189   int snaplen, int *err)
190 {
191   unsigned int  i;
192   FILE         *fh;
193
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 */
199       return NULL;
200     } else {
201       /*
202        * Flush out the capture file header, as written by "wtap_dump_fdopen()".
203        *
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.
210        */
211       fh = wtap_dump_file(rb_data.files[i].pdh);
212       if (fflush(fh) == EOF) {
213         if (err != NULL) {
214           *err = errno;
215         }
216         return NULL;
217       }
218       if ((rb_data.files[i].start_pos = ftell(fh)) < 0) {
219         if (err != NULL) {
220           *err = errno;
221         }
222         return NULL;
223       }
224       clearerr(fh);
225     }
226   }
227   /* done */
228   rb_data.files[0].is_new = FALSE;
229   return rb_data.files[0].pdh;
230 }
231
232 /*
233  * Switches to the next ringbuffer file
234  */
235 gboolean
236 ringbuf_switch_file(capture_file *cf, wtap_dumper **pdh, int *err)
237 {
238   int   next_file_num;
239   FILE *fh;
240   gboolean err_on_next = FALSE;
241
242   /* flush the current file */
243   fh = wtap_dump_file(rb_data.files[rb_data.curr_file_num].pdh);
244   clearerr(fh);
245   errno = WTAP_ERR_CANT_CLOSE;
246   if (fflush(fh) == EOF) {
247     if (err != NULL) {
248       *err = errno;
249     }
250     return FALSE;
251   }
252
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) {
260       *err = errno;
261       /* Don't return straight away: have caller report correct save_file */
262       err_on_next = TRUE;
263     }
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;
268   }
269 #ifdef _WIN32
270   _tzset();
271 #endif
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;
281
282   if (err_on_next)
283     return FALSE;
284
285   return TRUE;
286 }
287
288 /*
289  * Calls wtap_dump_close() for all ringbuffer files
290  */
291 gboolean
292 ringbuf_wtap_dump_close(capture_file *cf, int *err)
293 {
294   gboolean     ret_val;
295   unsigned int i;
296   gchar       *new_name;
297   char         filenum[5+1];
298   char         timestr[14+1];
299   FILE        *fh;
300
301   /* assume success */
302   ret_val = TRUE;
303   /* close all files */
304   for (i=0; i<rb_data.num_files; i++) {
305     fh = wtap_dump_file(rb_data.files[i].pdh);
306     clearerr(fh);
307     /* Flush the file */
308     errno = WTAP_ERR_CANT_CLOSE;
309     if (fflush(fh) == EOF) {
310       if (err != NULL) {
311         *err = errno;
312       }
313       ret_val = FALSE;
314       /* If the file's not a new one, remove it as it hasn't been truncated
315          and thus contains garbage at the end.
316
317          We don't remove it if it's new - it's incomplete, but at least
318          the stuff before the incomplete record is usable. */
319       close(rb_data.files[i].fd);
320       if (!rb_data.files[i].is_new) {
321         unlink(rb_data.files[i].name);
322       }
323       continue;
324     }
325
326     /* Truncate the file to the current size. This must be done in order
327        to get rid of the 'garbage' packets at the end of the file from
328        previous usage */
329     if (!rb_data.files[i].is_new) {
330       if (ftruncate(rb_data.files[i].fd,ftell(fh)) != 0) {
331         /* could not truncate the file */
332         if (err != NULL) {
333           *err = errno;
334         }
335         ret_val = FALSE;
336         /* remove the file since it contains garbage at the end */
337         close(rb_data.files[i].fd);
338         unlink(rb_data.files[i].name);
339         continue;
340       }
341     }
342     /* close the file */
343     if (!wtap_dump_close(rb_data.files[i].pdh, err)) {
344       /* error only if it is a used file */
345       if (!rb_data.files[i].is_new) {
346         ret_val = FALSE;
347       }
348     }
349     if (!rb_data.files[i].is_new) {
350       /* rename the file */
351       snprintf(filenum,5+1,"%05d",rb_data.files[i].number);
352       strftime(timestr,14+1,"%Y%m%d%H%M%S",
353         localtime(&(rb_data.files[i].creation_time)));
354       new_name = g_strconcat(rb_data.fprefix,"_", filenum, "_", timestr,
355         rb_data.fsuffix, NULL);
356       if (rename(rb_data.files[i].name, new_name) != 0) {
357         /* save the latest error */
358         if (err != NULL) {
359           *err = errno;
360         }
361         ret_val = FALSE;
362         g_free(new_name);
363       } else {
364         g_free(rb_data.files[i].name);
365         rb_data.files[i].name = new_name;
366       }
367     } else {
368       /* this file has never been used - remove it */
369       unlink(rb_data.files[i].name);
370     }
371   }
372   /* make the current file the save file */
373   cf->save_file = rb_data.files[rb_data.curr_file_num].name;
374   return ret_val;
375 }
376
377 /*
378  * Frees all memory allocated by the ringbuffer
379  */
380 void
381 ringbuf_free()
382 {
383   unsigned int i;
384
385   if (rb_data.files != NULL) {
386     for (i=0; i < rb_data.num_files; i++) {
387       g_free(rb_data.files[i].name);
388       rb_data.files[i].name = NULL;
389     }
390     free(rb_data.files);
391     rb_data.files = NULL;
392   }
393   g_free(rb_data.fprefix);
394   g_free(rb_data.fsuffix);
395 }
396
397 /*
398  * Frees all memory allocated by the ringbuffer
399  */
400 void
401 ringbuf_error_cleanup(void)
402 {
403   unsigned int i;
404
405   if (rb_data.files == NULL) {
406     ringbuf_free();
407     return;
408   }
409
410   for (i=0; i<rb_data.num_files; i++) {
411     /* try to close via wtap */
412     if (rb_data.files[i].pdh != NULL) {
413       if (wtap_dump_close(rb_data.files[i].pdh, NULL)) {
414         /* done */
415         rb_data.files[i].fd = -1;
416       }
417     }
418     /* close directly if still open */
419     if (rb_data.files[i].fd != -1) {
420       close(rb_data.files[i].fd);
421     }
422     /* remove the other files, the initial file will be handled
423        by the calling funtion */
424     if (rb_data.files[i].name != NULL) {
425       unlink(rb_data.files[i].name);
426     }
427   }
428   /* free the memory */
429   ringbuf_free();
430 }
431
432 #endif /* HAVE_LIBPCAP */