Ringbuffer rework.
[obnox/wireshark/wip.git] / ringbuffer.c
1 /* ringbuffer.c
2  * Routines for packet capture windows
3  *
4  * $Id: ringbuffer.c,v 1.7 2003/06/22 16:06:03 deniel 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 /*
26  * <laurent.deniel@free.fr>
27  *
28  * Almost completely rewritten in order to:
29  * 
30  * - be able to use a unlimited number of ringbuffer files
31  * - close the current file and open (truncating) the next file at switch
32  * - set the final file name once open (or reopen)
33  * - avoid the deletion of files that could not be truncated (can't arise now)
34  *   and do not erase empty files
35  *
36  * The idea behind that is to remove the limitation of the maximum # of 
37  * ringbuffer files being less than the maximum # of open fd per process
38  * and to be able to reduce the amount of virtual memory usage (having only
39  * one file open at most) or the amount of file system usage (by truncating
40  * the files at switch and not the capture stop, and by closing them which 
41  * makes possible their move or deletion after a switch).
42  *
43  */
44
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48
49 #ifdef HAVE_LIBPCAP
50
51 #ifdef HAVE_IO_H
52 #include <io.h>
53 #endif
54
55 #ifdef HAVE_FCNTL_H
56 #include <fcntl.h>
57 #endif
58
59 #ifdef HAVE_SYS_STAT_H
60 #include <sys/stat.h>
61 #endif
62
63 #ifdef HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66
67 #include <stdio.h>
68 #include <string.h>
69 #include <time.h>
70 #include <errno.h>
71
72 #ifdef NEED_SNPRINTF_H
73 #include "snprintf.h"
74 #endif
75
76 #include "wiretap/wtap.h"
77 #include "ringbuffer.h"
78
79 /* Win32 needs the O_BINARY flag for open() */
80 #ifndef O_BINARY
81 #define O_BINARY        0
82 #endif
83
84 /* Ringbuffer file structure */
85 typedef struct _rb_file {
86   gchar         *name;
87 } rb_file;
88
89 /* Ringbuffer data structure */
90 typedef struct _ringbuf_data {
91   rb_file      *files;
92   guint         num_files;           /* Number of ringbuffer files */
93   guint         curr_file_num;       /* Number of the current file */
94   gchar        *fprefix;             /* Filename prefix */
95   gchar        *fsuffix;             /* Filename suffix */
96   gboolean      unlimited;           /* TRUE if unlimited number of files */
97   int           filetype;
98   int           linktype;
99   int           snaplen;
100   guint16       number;
101   int           fd;                  /* Current ringbuffer file descriptor */
102   wtap_dumper  *pdh;  
103 } ringbuf_data;
104
105 static ringbuf_data rb_data;
106
107 static int ringbuf_open_file(rb_file *rfile, int *err)
108 {
109   char    filenum[5+1];
110   char    timestr[14+1];
111   time_t  current_time;
112
113   if (rfile->name != NULL) {
114     if (rb_data.unlimited == FALSE) {
115       /* remove old file (if any, so ignore error) */
116       unlink(rfile->name);
117     }
118     g_free(rfile->name);
119   }
120
121 #ifdef _WIN32
122   _tzset();
123 #endif
124   current_time = time(NULL);
125
126   rb_data.number++;
127
128   snprintf(filenum, sizeof(filenum), "%05d", rb_data.number);
129   strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", localtime(&current_time));
130   rfile->name = g_strconcat(rb_data.fprefix, "_", filenum, "_", timestr,
131                             rb_data.fsuffix, NULL);
132
133   if (rfile->name == NULL) {
134     *err = ENOMEM;
135     return -1;
136   }
137
138   rb_data.fd = open(rfile->name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT, 0600);
139
140   if (rb_data.fd == -1 && err != NULL) {
141     *err = errno;
142   }
143
144   return rb_data.fd;
145 }
146
147 /*
148  * Initialize the ringbuffer data structures
149  */
150 int
151 ringbuf_init(const char *capfile_name, guint num_files)
152 {
153   unsigned int i;
154   char        *pfx;
155   gchar       *save_file;
156
157   rb_data.files = NULL;
158   rb_data.curr_file_num = 0;
159   rb_data.fprefix = NULL;
160   rb_data.fsuffix = NULL;
161   rb_data.unlimited = FALSE;
162   rb_data.fd = -1;
163   rb_data.pdh = NULL;
164   rb_data.number = 0;
165
166   /* just to be sure ... */
167   if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
168     rb_data.num_files = num_files;
169   } else {
170     rb_data.num_files = RINGBUFFER_MAX_NUM_FILES;
171   }
172
173   /* Check file name */
174   if (capfile_name == NULL) {
175     /* ringbuffer does not work with temporary files! */
176     return -1;
177   }
178
179   /* set file name prefix/suffix */
180
181   save_file = g_strdup(capfile_name);
182   pfx = strrchr(save_file,'.');
183   if (pfx != NULL) {
184     pfx[0] = '\0';
185     rb_data.fprefix = g_strdup(save_file);
186     pfx[0] = '.'; /* restore capfile_name */
187     rb_data.fsuffix = g_strdup(pfx);
188   } else {
189     rb_data.fprefix = g_strdup(save_file);
190     rb_data.fsuffix = NULL;
191   }
192   g_free(save_file);
193   save_file = NULL;
194
195   /* allocate rb_file structures (only one if unlimited since there is no
196      need to save all file names in that case) */
197
198   if (num_files == RINGBUFFER_UNLIMITED_FILES) {
199     rb_data.unlimited = TRUE;
200     rb_data.num_files = 1;
201   }
202
203   rb_data.files = g_malloc(rb_data.num_files * sizeof(rb_file));
204   if (rb_data.files == NULL) {
205     return -1;
206   }
207
208   for (i=0; i < rb_data.num_files; i++) {
209     rb_data.files[i].name = NULL;
210   }
211
212   /* create the first file */
213   if (ringbuf_open_file(&rb_data.files[0], NULL) == -1) {
214     ringbuf_error_cleanup();
215     return -1;
216   }
217
218   return rb_data.fd;
219 }
220
221 /*
222  * Calls wtap_dump_fdopen() for the current ringbuffer file
223  */
224 wtap_dumper*
225 ringbuf_init_wtap_dump_fdopen(int filetype, int linktype, int snaplen, int *err)
226 {
227
228   rb_data.filetype = filetype;
229   rb_data.linktype = linktype;
230   rb_data.snaplen  = snaplen;
231
232   rb_data.pdh = wtap_dump_fdopen(rb_data.fd, filetype, linktype, snaplen, err);
233
234   return rb_data.pdh;
235 }
236
237 /*
238  * Switches to the next ringbuffer file
239  */
240 gboolean
241 ringbuf_switch_file(capture_file *cf, wtap_dumper **pdh, int *err)
242 {
243   int     next_file_num;
244   rb_file *next_rfile = NULL;
245
246   /* close current file */
247
248   if (!wtap_dump_close(rb_data.pdh, err)) {
249     close(rb_data.fd);
250     rb_data.fd = -1;
251     return FALSE;
252   }
253
254   rb_data.pdh = NULL;
255   rb_data.fd  = -1;
256
257   /* get the next file number and open it */
258
259   next_file_num = (rb_data.curr_file_num + 1) % rb_data.num_files;  
260   next_rfile = &rb_data.files[next_file_num];
261
262   if (ringbuf_open_file(next_rfile, err) == -1) {
263     return FALSE;
264   }
265
266   if (ringbuf_init_wtap_dump_fdopen(rb_data.filetype, rb_data.linktype,
267                                     rb_data.snaplen, err) == NULL) {
268     return FALSE;
269   }
270
271   /* switch to the new file */
272   rb_data.curr_file_num = next_file_num;
273   cf->save_file = next_rfile->name;
274   cf->save_file_fd = rb_data.fd;
275   (*pdh) = rb_data.pdh;
276
277   return TRUE;
278 }
279
280 /*
281  * Calls wtap_dump_close() for the current ringbuffer file
282  */
283 gboolean
284 ringbuf_wtap_dump_close(capture_file *cf, int *err)
285 {
286   gboolean  ret_val = TRUE;
287
288   /* close current file */
289
290   if (!wtap_dump_close(rb_data.pdh, err)) {
291     close(rb_data.fd);
292     ret_val = FALSE;
293   }
294
295   rb_data.pdh = NULL;
296   rb_data.fd  = -1;
297
298   cf->save_file = rb_data.files[rb_data.curr_file_num].name;
299   return ret_val;
300 }
301
302 /*
303  * Frees all memory allocated by the ringbuffer
304  */
305 void
306 ringbuf_free()
307 {
308   unsigned int i;
309
310   if (rb_data.files != NULL) {
311     for (i=0; i < rb_data.num_files; i++) {
312       if (rb_data.files[i].name != NULL) {
313         g_free(rb_data.files[i].name);
314         rb_data.files[i].name = NULL;
315       }
316     }
317     g_free(rb_data.files);
318     rb_data.files = NULL;
319   }
320   if (rb_data.fprefix != NULL) {
321     g_free(rb_data.fprefix);
322     rb_data.fprefix = NULL;
323   }
324   if (rb_data.fsuffix != NULL) {
325     g_free(rb_data.fsuffix);
326     rb_data.fsuffix = NULL;
327   }
328 }
329
330 /*
331  * Frees all memory allocated by the ringbuffer
332  */
333 void
334 ringbuf_error_cleanup(void)
335 {
336   unsigned int i;
337
338   /* try to close via wtap */
339   if (rb_data.pdh != NULL) {
340     if (wtap_dump_close(rb_data.pdh, NULL)) {
341       rb_data.fd = -1;
342     }
343     rb_data.pdh = NULL;
344   }
345
346   /* close directly if still open */
347   if (rb_data.fd != -1) {
348     close(rb_data.fd);
349     rb_data.fd = -1;
350   }
351
352   for (i=0; i < rb_data.num_files; i++) {
353     if (rb_data.files[i].name != NULL) {
354       unlink(rb_data.files[i].name);
355     }
356   }
357   /* free the memory */
358   ringbuf_free();
359 }
360
361 #endif /* HAVE_LIBPCAP */