Check whether "fflush()" succeeds, and clean up and return an error if
[obnox/wireshark/wip.git] / ringbuffer.c
1 /* ringbuffer.c
2  * Routines for packet capture windows
3  *
4  * $Id: ringbuffer.c,v 1.2 2002/05/04 10:10:42 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       rb_data.files[i].start_pos = ftell(fh);
223       clearerr(fh);
224     }
225   }
226   /* done */
227   rb_data.files[0].is_new = FALSE;
228   return rb_data.files[0].pdh;
229 }
230
231 /* 
232  * Switches to the next ringbuffer file
233  */
234 gboolean
235 ringbuf_switch_file(capture_file *cf, wtap_dumper **pdh, int *err)
236 {
237   int   next_file_num;
238   FILE *fh;
239
240   /* flush the current file */
241   fh = wtap_dump_file(rb_data.files[rb_data.curr_file_num].pdh);
242   clearerr(fh);
243   errno = WTAP_ERR_CANT_CLOSE;
244   if (fflush(fh) == EOF) {
245     if (err != NULL) {
246       *err = errno;
247     }
248     return FALSE;
249   }
250     
251   /* get the next file number */
252   next_file_num = (rb_data.curr_file_num + 1) % rb_data.num_files;
253   /* prepare the file if it was already used */
254   if (!rb_data.files[next_file_num].is_new) {
255     /* rewind to the position after the file header */
256     fh = wtap_dump_file(rb_data.files[next_file_num].pdh);
257     fseek(fh, rb_data.files[next_file_num].start_pos, SEEK_SET);
258     wtap_set_bytes_dumped(rb_data.files[next_file_num].pdh,
259       rb_data.files[next_file_num].start_pos);
260     /* set the absolute file number */
261     rb_data.files[next_file_num].number += rb_data.num_files;
262   }
263 #ifdef _WIN32
264   _tzset();
265 #endif
266   rb_data.files[next_file_num].creation_time = time(NULL);
267   /* switch to the new file */
268   cf->save_file = rb_data.files[next_file_num].name;
269   cf->save_file_fd = rb_data.files[next_file_num].fd;
270   (*pdh) = rb_data.files[next_file_num].pdh;
271   /* mark the file as used */
272   rb_data.files[next_file_num].is_new = FALSE;
273   /* finally set the current file number */
274   rb_data.curr_file_num = next_file_num;
275
276   return TRUE;
277 }
278
279 /* 
280  * Calls wtap_dump_close() for all ringbuffer files
281  */
282 gboolean
283 ringbuf_wtap_dump_close(capture_file *cf, int *err)
284 {
285   gboolean     ret_val;
286   unsigned int i;
287   gchar       *new_name;
288   char         filenum[5+1];
289   char         timestr[14+1];
290   FILE        *fh;
291
292   /* assume success */
293   ret_val = TRUE;
294   /* close all files */
295   for (i=0; i<rb_data.num_files; i++) {
296     fh = wtap_dump_file(rb_data.files[i].pdh);
297     clearerr(fh);
298     /* Flush the file */
299     errno = WTAP_ERR_CANT_CLOSE;
300     if (fflush(fh) == EOF) {
301       if (err != NULL) {
302         *err = errno;
303       }
304       ret_val = FALSE;
305       /* If the file's not a new one, remove it as it hasn't been truncated
306          and thus contains garbage at the end.
307
308          We don't remove it if it's new - it's incomplete, but at least
309          the stuff before the incomplete record is usable. */
310       close(rb_data.files[i].fd);
311       if (!rb_data.files[i].is_new) {
312         unlink(rb_data.files[i].name);
313       }
314       continue;
315     }
316
317     /* Truncate the file to the current size. This must be done in order
318        to get rid of the 'garbage' packets at the end of the file from
319        previous usage */
320     if (!rb_data.files[i].is_new) {
321       if (ftruncate(rb_data.files[i].fd,ftell(fh)) != 0) {
322         /* could not truncate the file */
323         if (err != NULL) {
324           *err = errno;
325         }
326         ret_val = FALSE;
327         /* remove the file since it contains garbage at the end */
328         close(rb_data.files[i].fd);
329         unlink(rb_data.files[i].name);
330         continue;
331       }
332     }
333     /* close the file */
334     if (!wtap_dump_close(rb_data.files[i].pdh, err)) {
335       /* error only if it is a used file */
336       if (!rb_data.files[i].is_new) {
337         ret_val = FALSE;
338       }
339     }
340     if (!rb_data.files[i].is_new) {
341       /* rename the file */
342       snprintf(filenum,5+1,"%05d",rb_data.files[i].number);
343       strftime(timestr,14+1,"%Y%m%d%H%M%S", 
344         localtime(&(rb_data.files[i].creation_time)));
345       new_name = g_strconcat(rb_data.fprefix,"_", filenum, "_", timestr, 
346         rb_data.fsuffix, NULL);
347       if (rename(rb_data.files[i].name, new_name) != 0) {
348         /* save the latest error */
349         if (err != NULL) {
350           *err = errno;
351         }
352         ret_val = FALSE;
353         g_free(new_name);
354       } else {
355         g_free(rb_data.files[i].name);
356         rb_data.files[i].name = new_name;
357       }
358     } else {
359       /* this file has never been used - remove it */
360       unlink(rb_data.files[i].name);
361     }
362   }
363   /* make the current file the save file */
364   cf->save_file = rb_data.files[rb_data.curr_file_num].name;
365   return ret_val;
366 }
367
368 /* 
369  * Frees all memory allocated by the ringbuffer
370  */
371 void
372 ringbuf_free()
373
374   unsigned int i;
375   
376   if (rb_data.files != NULL) {
377     for (i=0; i < rb_data.num_files; i++) {
378       g_free(rb_data.files[i].name);
379       rb_data.files[i].name = NULL;
380     }
381     free(rb_data.files);
382     rb_data.files = NULL;
383   }
384   g_free(rb_data.fprefix);
385   g_free(rb_data.fsuffix);
386 }
387
388 /* 
389  * Frees all memory allocated by the ringbuffer
390  */
391 void 
392 ringbuf_error_cleanup(void)
393 {
394   unsigned int i;
395   
396   if (rb_data.files == NULL) {
397     ringbuf_free();
398     return;
399   }
400
401   for (i=0; i<rb_data.num_files; i++) {
402     /* try to close via wtap */
403     if (rb_data.files[i].pdh != NULL) {
404       if (wtap_dump_close(rb_data.files[i].pdh, NULL)) {
405         /* done */
406         rb_data.files[i].fd = -1;
407       }
408     }
409     /* close directly if still open */
410     if (rb_data.files[i].fd != -1) {
411       close(rb_data.files[i].fd);
412     }
413     /* remove the other files, the initial file will be handled
414        by the calling funtion */
415     if (rb_data.files[i].name != NULL) {
416       unlink(rb_data.files[i].name);
417     }
418   }
419   /* free the memory */  
420   ringbuf_free();
421 }
422
423 #endif /* HAVE_LIBPCAP */