Update manuf to current IEEE entries.
[obnox/wireshark/wip.git] / ringbuffer.c
1 /* ringbuffer.c
2  * Routines for packet capture windows
3  *
4  * $Id: ringbuffer.c,v 1.6 2002/09/22 16:17:41 gerald 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   gboolean     data_captured = TRUE;
296   unsigned int i;
297   long         curr_pos;
298   long         curr_file_curr_pos = 0;  /* Initialise to avoid GCC warning */
299   gchar       *new_name;
300   char         filenum[5+1];
301   char         timestr[14+1];
302   FILE        *fh;
303
304   /* assume success */
305   ret_val = TRUE;
306   /* close all files */
307   for (i=0; i<rb_data.num_files; i++) {
308     fh = wtap_dump_file(rb_data.files[i].pdh);
309     clearerr(fh);
310
311     /* Get the current file position */
312     if ((curr_pos = ftell(fh)) < 0) {
313       if (err != NULL) {
314         *err = errno;
315       }
316       ret_val = FALSE;
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
320          remove it too. */
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;
325       continue;
326     }
327
328     if (i == rb_data.curr_file_num)
329       curr_file_curr_pos = curr_pos;
330
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;
336
337     /* Flush the file */
338     errno = WTAP_ERR_CANT_CLOSE;
339     if (fflush(fh) == EOF) {
340       if (err != NULL) {
341         *err = errno;
342       }
343       ret_val = FALSE;
344       close(rb_data.files[i].fd);
345       unlink(rb_data.files[i].name);
346       cf->save_file = rb_data.files[i].name;
347       continue;
348     }
349
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
352        previous usage */
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 */
356         if (err != NULL) {
357           *err = errno;
358         }
359         ret_val = FALSE;
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;
364         continue;
365       }
366     }
367     /* close the file */
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) {
371         ret_val = FALSE;
372         /* Don't unlink it; maybe the user can salvage it. */
373         cf->save_file = rb_data.files[i].name;
374         continue;
375       }
376     }
377
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 */
391         if (err != NULL) {
392           *err = errno;
393         }
394         ret_val = FALSE;
395         cf->save_file = rb_data.files[i].name;
396         g_free(new_name);
397       } else {
398         g_free(rb_data.files[i].name);
399         rb_data.files[i].name = new_name;
400       }
401     } else {
402       /* this file is empty - remove it */
403       unlink(rb_data.files[i].name);
404     }
405   }
406
407   if (ret_val) {
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 &&
413         data_captured) {
414       if (rb_data.curr_file_num > 0)
415         rb_data.curr_file_num -= 1;
416       else
417         rb_data.curr_file_num = rb_data.num_files - 1;
418     }
419     cf->save_file = rb_data.files[rb_data.curr_file_num].name;
420   }
421   return ret_val;
422 }
423
424 /*
425  * Frees all memory allocated by the ringbuffer
426  */
427 void
428 ringbuf_free()
429 {
430   unsigned int i;
431
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;
436     }
437     free(rb_data.files);
438     rb_data.files = NULL;
439   }
440   g_free(rb_data.fprefix);
441   g_free(rb_data.fsuffix);
442 }
443
444 /*
445  * Frees all memory allocated by the ringbuffer
446  */
447 void
448 ringbuf_error_cleanup(void)
449 {
450   unsigned int i;
451
452   if (rb_data.files == NULL) {
453     ringbuf_free();
454     return;
455   }
456
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)) {
461         /* done */
462         rb_data.files[i].fd = -1;
463       }
464     }
465     /* close directly if still open */
466     if (rb_data.files[i].fd != -1) {
467       close(rb_data.files[i].fd);
468     }
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);
473     }
474   }
475   /* free the memory */
476   ringbuf_free();
477 }
478
479 #endif /* HAVE_LIBPCAP */