From Francisco Alcoba: declare the tap data as static, so it's still
[obnox/wireshark/wip.git] / ringbuffer.c
1 /* ringbuffer.c
2  * Routines for packet capture windows
3  *
4  * $Id$
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, *last_pathsep;
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   last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
183   pfx = strrchr(save_file,'.');
184   if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
185     /* The pathname has a "." in it, and it's in the last component
186        of the pathname (because there is either only one component,
187        i.e. last_pathsep is null as there are no path separators,
188        or the "." is after the path separator before the last
189        component.
190
191        Treat it as a separator between the rest of the file name and
192        the file name suffix, and arrange that the names given to the
193        ring buffer files have the specified suffix, i.e. put the
194        changing part of the name *before* the suffix. */
195     pfx[0] = '\0';
196     rb_data.fprefix = g_strdup(save_file);
197     pfx[0] = '.'; /* restore capfile_name */
198     rb_data.fsuffix = g_strdup(pfx);
199   } else {
200     /* Either there's no "." in the pathname, or it's in a directory
201        component, so the last component has no suffix. */
202     rb_data.fprefix = g_strdup(save_file);
203     rb_data.fsuffix = NULL;
204   }
205   g_free(save_file);
206   save_file = NULL;
207
208   /* allocate rb_file structures (only one if unlimited since there is no
209      need to save all file names in that case) */
210
211   if (num_files == RINGBUFFER_UNLIMITED_FILES) {
212     rb_data.unlimited = TRUE;
213     rb_data.num_files = 1;
214   }
215
216   rb_data.files = g_malloc(rb_data.num_files * sizeof(rb_file));
217   if (rb_data.files == NULL) {
218     return -1;
219   }
220
221   for (i=0; i < rb_data.num_files; i++) {
222     rb_data.files[i].name = NULL;
223   }
224
225   /* create the first file */
226   if (ringbuf_open_file(&rb_data.files[0], NULL) == -1) {
227     ringbuf_error_cleanup();
228     return -1;
229   }
230
231   return rb_data.fd;
232 }
233
234 /*
235  * Calls wtap_dump_fdopen() for the current ringbuffer file
236  */
237 wtap_dumper*
238 ringbuf_init_wtap_dump_fdopen(int filetype, int linktype, int snaplen, int *err)
239 {
240
241   rb_data.filetype = filetype;
242   rb_data.linktype = linktype;
243   rb_data.snaplen  = snaplen;
244
245   rb_data.pdh = wtap_dump_fdopen(rb_data.fd, filetype, linktype, snaplen, err);
246
247   return rb_data.pdh;
248 }
249
250 /*
251  * Switches to the next ringbuffer file
252  */
253 gboolean
254 ringbuf_switch_file(capture_file *cf, wtap_dumper **pdh, int *err)
255 {
256   int     next_file_num;
257   rb_file *next_rfile = NULL;
258
259   /* close current file */
260
261   if (!wtap_dump_close(rb_data.pdh, err)) {
262     close(rb_data.fd);  /* XXX - the above should have closed this already */
263     rb_data.pdh = NULL; /* it's still closed, we just got an error while closing */
264     rb_data.fd = -1;
265     return FALSE;
266   }
267
268   rb_data.pdh = NULL;
269   rb_data.fd  = -1;
270
271   /* get the next file number and open it */
272
273   next_file_num = (rb_data.curr_file_num + 1) % rb_data.num_files;  
274   next_rfile = &rb_data.files[next_file_num];
275
276   if (ringbuf_open_file(next_rfile, err) == -1) {
277     return FALSE;
278   }
279
280   if (ringbuf_init_wtap_dump_fdopen(rb_data.filetype, rb_data.linktype,
281                                     rb_data.snaplen, err) == NULL) {
282     return FALSE;
283   }
284
285   /* switch to the new file */
286   rb_data.curr_file_num = next_file_num;
287   cf->save_file = next_rfile->name;
288   cf->save_file_fd = rb_data.fd;
289   (*pdh) = rb_data.pdh;
290
291   return TRUE;
292 }
293
294 /*
295  * Calls wtap_dump_close() for the current ringbuffer file
296  */
297 gboolean
298 ringbuf_wtap_dump_close(capture_file *cf, int *err)
299 {
300   gboolean  ret_val = TRUE;
301
302   /* close current file, if it's open */
303   if (rb_data.pdh != NULL) {
304     if (!wtap_dump_close(rb_data.pdh, err)) {
305       close(rb_data.fd);
306       ret_val = FALSE;
307     }
308
309     rb_data.pdh = NULL;
310     rb_data.fd  = -1;
311   }
312
313   /* set the save file name to the current file */
314   cf->save_file = rb_data.files[rb_data.curr_file_num].name;
315   return ret_val;
316 }
317
318 /*
319  * Frees all memory allocated by the ringbuffer
320  */
321 void
322 ringbuf_free()
323 {
324   unsigned int i;
325
326   if (rb_data.files != NULL) {
327     for (i=0; i < rb_data.num_files; i++) {
328       if (rb_data.files[i].name != NULL) {
329         g_free(rb_data.files[i].name);
330         rb_data.files[i].name = NULL;
331       }
332     }
333     g_free(rb_data.files);
334     rb_data.files = NULL;
335   }
336   if (rb_data.fprefix != NULL) {
337     g_free(rb_data.fprefix);
338     rb_data.fprefix = NULL;
339   }
340   if (rb_data.fsuffix != NULL) {
341     g_free(rb_data.fsuffix);
342     rb_data.fsuffix = NULL;
343   }
344 }
345
346 /*
347  * Frees all memory allocated by the ringbuffer
348  */
349 void
350 ringbuf_error_cleanup(void)
351 {
352   unsigned int i;
353
354   /* try to close via wtap */
355   if (rb_data.pdh != NULL) {
356     if (wtap_dump_close(rb_data.pdh, NULL)) {
357       rb_data.fd = -1;
358     }
359     rb_data.pdh = NULL;
360   }
361
362   /* close directly if still open */
363   /* XXX - it shouldn't still be open; "wtap_dump_close()" should leave the
364      file closed even if it fails */
365   if (rb_data.fd != -1) {
366     close(rb_data.fd);
367     rb_data.fd = -1;
368   }
369
370   if (rb_data.files != NULL) {
371     for (i=0; i < rb_data.num_files; i++) {
372       if (rb_data.files[i].name != NULL) {
373         unlink(rb_data.files[i].name);
374       }
375     }
376   }
377   /* free the memory */
378   ringbuf_free();
379 }
380
381 #endif /* HAVE_LIBPCAP */