update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / ringbuffer.c
blob5920e8f1395e42930bcaf10acd7eff06fb31b567
1 /* ringbuffer.c
2 * Routines for packet capture windows
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
12 * <laurent.deniel@free.fr>
14 * Almost completely rewritten in order to:
16 * - be able to use a unlimited number of ringbuffer files
17 * - close the current file and open (truncating) the next file at switch
18 * - set the final file name once open (or reopen)
19 * - avoid the deletion of files that could not be truncated (can't arise now)
20 * and do not erase empty files
22 * The idea behind that is to remove the limitation of the maximum # of
23 * ringbuffer files being less than the maximum # of open fd per process
24 * and to be able to reduce the amount of virtual memory usage (having only
25 * one file open at most) or the amount of file system usage (by truncating
26 * the files at switch and not the capture stop, and by closing them which
27 * makes possible their move or deletion after a switch).
31 #include <config.h>
33 #ifdef HAVE_LIBPCAP
35 #include <stdio.h>
36 #include <string.h>
37 #include <time.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <glib.h>
42 #include <pcap.h>
44 #include <glib.h>
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
50 #ifdef _WIN32
51 #include <wsutil/win32-utils.h>
52 #endif
54 #include "ringbuffer.h"
55 #include <wsutil/array.h>
56 #include <wsutil/file_util.h>
58 #ifdef HAVE_ZLIBNG
59 #define ZLIB_PREFIX(x) zng_ ## x
60 #include <zlib-ng.h>
61 typedef zng_stream zlib_stream;
62 #else
63 #ifdef HAVE_ZLIB
64 #define ZLIB_PREFIX(x) x
65 #include <zlib.h>
66 typedef z_stream zlib_stream;
67 #endif /* HAVE_ZLIB */
68 #endif
70 /* Ringbuffer file structure */
71 typedef struct _rb_file {
72 char *name;
73 } rb_file;
75 #define MAX_FILENAME_QUEUE 100
77 /** Ringbuffer data structure */
78 typedef struct _ringbuf_data {
79 rb_file *files;
80 unsigned num_files; /**< Number of ringbuffer files (1 to ...) */
81 unsigned curr_file_num; /**< Number of the current file (ever increasing) */
82 char *fprefix; /**< Filename prefix */
83 char *fsuffix; /**< Filename suffix */
84 bool nametimenum; /**< ...num_time... or ...time_num... */
85 bool unlimited; /**< true if unlimited number of files */
87 int fd; /**< Current ringbuffer file descriptor */
88 FILE *pdh;
89 char *io_buffer; /**< The IO buffer used to write to the file */
90 bool group_read_access; /**< true if files need to be opened with group read access */
91 FILE *name_h; /**< write names of completed files to this handle */
92 char *compress_type; /**< compress type */
94 GMutex mutex; /**< mutex for oldnames */
95 char *oldnames[MAX_FILENAME_QUEUE]; /**< filename list of pending to be deleted */
96 } ringbuf_data;
98 static ringbuf_data rb_data;
101 * delete pending uncompressed pcap files.
103 static void
104 CleanupOldCap(char* name)
106 ws_statb64 statb;
107 size_t i;
109 g_mutex_lock(&rb_data.mutex);
111 /* Delete pending delete file */
112 for (i = 0; i < array_length(rb_data.oldnames); i++) {
113 if (rb_data.oldnames[i] != NULL) {
114 ws_unlink(rb_data.oldnames[i]);
115 if (ws_stat64(rb_data.oldnames[i], &statb) != 0) {
116 g_free(rb_data.oldnames[i]);
117 rb_data.oldnames[i] = NULL;
122 if (name) {
123 /* push the current file to pending list if it failed to delete */
124 if (ws_stat64(name, &statb) == 0) {
125 for (i = 0; i < array_length(rb_data.oldnames); i++) {
126 if (rb_data.oldnames[i] == NULL) {
127 rb_data.oldnames[i] = g_strdup(name);
128 break;
134 g_mutex_unlock(&rb_data.mutex);
137 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
139 * compress capture file
141 static int
142 ringbuf_exec_compress(char* name)
144 uint8_t *buffer = NULL;
145 char* outgz = NULL;
146 int fd = -1;
147 ssize_t nread;
148 bool delete_org_file = true;
149 gzFile fi = NULL;
151 fd = ws_open(name, O_RDONLY | O_BINARY, 0000);
152 if (fd < 0) {
153 return -1;
156 outgz = ws_strdup_printf("%s.gz", name);
157 fi = ZLIB_PREFIX(gzopen)(outgz, "wb");
158 g_free(outgz);
159 if (fi == NULL) {
160 ws_close(fd);
161 return -1;
164 #define FS_READ_SIZE 65536
165 buffer = (uint8_t*)g_malloc(FS_READ_SIZE);
166 if (buffer == NULL) {
167 ws_close(fd);
168 ZLIB_PREFIX(gzclose)(fi);
169 return -1;
172 while ((nread = ws_read(fd, buffer, FS_READ_SIZE)) > 0) {
173 int n = ZLIB_PREFIX(gzwrite)(fi, buffer, (unsigned int)nread);
174 if (n <= 0) {
175 /* mark compression as failed */
176 delete_org_file = false;
177 break;
180 if (nread < 0) {
181 /* mark compression as failed */
182 delete_org_file = false;
184 ws_close(fd);
185 ZLIB_PREFIX(gzclose)(fi);
186 g_free(buffer);
188 /* delete the original file only if compression succeeds */
189 if (delete_org_file) {
190 ws_unlink(name);
191 CleanupOldCap(name);
193 g_free(name);
194 return 0;
198 * thread to compress capture file
200 static void*
201 exec_compress_thread(void* arg)
203 ringbuf_exec_compress((char*)arg);
204 return NULL;
208 * start a thread to compress capture file
210 static int
211 ringbuf_start_compress_file(rb_file* rfile)
213 char* name = g_strdup(rfile->name);
214 g_thread_new("exec_compress", &exec_compress_thread, name);
215 return 0;
217 #endif
220 * create the next filename and open a new binary file with that name
222 static int
223 ringbuf_open_file(rb_file *rfile, int *err)
225 char filenum[5+1];
226 char timestr[14+1];
227 time_t current_time;
228 struct tm *tm;
230 if (rfile->name != NULL) {
231 if (rb_data.unlimited == false) {
232 /* remove old file (if any, so ignore error) */
233 ws_unlink(rfile->name);
235 #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
236 else if (rb_data.compress_type != NULL && strcmp(rb_data.compress_type, "gzip") == 0) {
237 ringbuf_start_compress_file(rfile);
239 #endif
240 g_free(rfile->name);
243 #ifdef _WIN32
244 _tzset();
245 #endif
246 current_time = time(NULL);
248 snprintf(filenum, sizeof(filenum), "%05u", (rb_data.curr_file_num + 1) % RINGBUFFER_MAX_NUM_FILES);
249 tm = localtime(&current_time);
250 if (tm != NULL)
251 strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", tm);
252 else
253 (void) g_strlcpy(timestr, "196912312359", sizeof(timestr)); /* second before the Epoch */
254 if (rb_data.nametimenum) {
255 rfile->name = g_strconcat(rb_data.fprefix, "_", timestr, "_", filenum, rb_data.fsuffix, NULL);
256 } else {
257 rfile->name = g_strconcat(rb_data.fprefix, "_", filenum, "_", timestr, rb_data.fsuffix, NULL);
260 if (rfile->name == NULL) {
261 if (err != NULL)
262 *err = ENOMEM;
263 return -1;
266 rb_data.fd = ws_open(rfile->name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
267 rb_data.group_read_access ? 0640 : 0600);
269 if (rb_data.fd == -1 && err != NULL) {
270 *err = errno;
273 return rb_data.fd;
277 * Initialize the ringbuffer data structures
280 ringbuf_init(const char *capfile_name, unsigned num_files, bool group_read_access,
281 char *compress_type, bool has_nametimenum)
283 unsigned int i;
284 char *pfx;
285 char *dir_name, *base_name;
287 rb_data.files = NULL;
288 rb_data.curr_file_num = 0;
289 rb_data.fprefix = NULL;
290 rb_data.fsuffix = NULL;
291 rb_data.nametimenum = has_nametimenum;
292 rb_data.unlimited = false;
293 rb_data.fd = -1;
294 rb_data.pdh = NULL;
295 rb_data.io_buffer = NULL;
296 rb_data.group_read_access = group_read_access;
297 rb_data.name_h = NULL;
298 rb_data.compress_type = compress_type;
299 g_mutex_init(&rb_data.mutex);
301 /* just to be sure ... */
302 if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
303 rb_data.num_files = num_files;
304 } else {
305 rb_data.num_files = RINGBUFFER_MAX_NUM_FILES;
308 /* Check file name */
309 if (capfile_name == NULL) {
310 /* ringbuffer does not work with temporary files! */
311 return -1;
314 /* set file name prefix/suffix */
316 base_name = g_path_get_basename(capfile_name);
317 dir_name = g_path_get_dirname(capfile_name);
318 pfx = strrchr(base_name, '.');
319 if (pfx != NULL) {
320 /* The basename has a "." in it.
322 Treat it as a separator between the rest of the file name and
323 the file name suffix, and arrange that the names given to the
324 ring buffer files have the specified suffix, i.e. put the
325 changing part of the name *before* the suffix.
327 XXX - If we ever handle writing compressed files directly
328 (#19159) make sure we deal with any compression suffix
329 appropriately. */
330 pfx[0] = '\0';
331 rb_data.fprefix = g_build_filename(dir_name, base_name, NULL);
332 pfx[0] = '.'; /* restore capfile_name */
333 rb_data.fsuffix = g_strdup(pfx);
334 } else {
335 /* The last component has no suffix. */
336 rb_data.fprefix = g_strdup(capfile_name);
337 rb_data.fsuffix = NULL;
339 g_free(dir_name);
340 g_free(base_name);
342 /* allocate rb_file structures (only one if unlimited since there is no
343 need to save all file names in that case) */
345 if (num_files == RINGBUFFER_UNLIMITED_FILES) {
346 rb_data.unlimited = true;
347 rb_data.num_files = 1;
350 rb_data.files = g_new(rb_file, rb_data.num_files);
351 if (rb_data.files == NULL) {
352 return -1;
355 for (i=0; i < rb_data.num_files; i++) {
356 rb_data.files[i].name = NULL;
359 /* create the first file */
360 if (ringbuf_open_file(&rb_data.files[0], NULL) == -1) {
361 ringbuf_error_cleanup();
362 return -1;
365 return rb_data.fd;
369 * Set name of file to which to print ringbuffer file names.
371 bool
372 ringbuf_set_print_name(char *name, int *err)
374 if (rb_data.name_h != NULL) {
375 if (EOF == fclose(rb_data.name_h)) {
376 if (err != NULL) {
377 *err = errno;
379 return false;
382 if (!strcmp(name, "-") || !strcmp(name, "stdout")) {
383 rb_data.name_h = stdout;
384 } else if (!strcmp(name, "stderr")) {
385 rb_data.name_h = stderr;
386 } else {
387 if (NULL == (rb_data.name_h = ws_fopen(name, "wt"))) {
388 if (err != NULL) {
389 *err = errno;
391 return false;
394 return true;
398 * Whether the ringbuf filenames are ready.
399 * (Whether ringbuf_init is called and ringbuf_free is not called.)
401 bool
402 ringbuf_is_initialized(void)
404 return rb_data.files != NULL;
407 const char *
408 ringbuf_current_filename(void)
410 return rb_data.files[rb_data.curr_file_num % rb_data.num_files].name;
414 * Calls ws_fdopen() for the current ringbuffer file
416 FILE *
417 ringbuf_init_libpcap_fdopen(int *err)
419 rb_data.pdh = ws_fdopen(rb_data.fd, "wb");
420 if (rb_data.pdh == NULL) {
421 if (err != NULL) {
422 *err = errno;
424 } else {
425 size_t buffsize = IO_BUF_SIZE;
426 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
427 ws_statb64 statb;
429 if (ws_fstat64(rb_data.fd, &statb) == 0) {
430 if (statb.st_blksize > IO_BUF_SIZE) {
431 buffsize = statb.st_blksize;
434 #endif
435 /* Increase the size of the IO buffer */
436 rb_data.io_buffer = (char *)g_realloc(rb_data.io_buffer, buffsize);
437 setvbuf(rb_data.pdh, rb_data.io_buffer, _IOFBF, buffsize);
440 return rb_data.pdh;
444 * Switches to the next ringbuffer file
446 bool
447 ringbuf_switch_file(FILE **pdh, char **save_file, int *save_file_fd, int *err)
449 int next_file_index;
450 rb_file *next_rfile = NULL;
452 /* close current file */
454 if (fclose(rb_data.pdh) == EOF) {
455 if (err != NULL) {
456 *err = errno;
458 ws_close(rb_data.fd); /* XXX - the above should have closed this already */
459 rb_data.pdh = NULL; /* it's still closed, we just got an error while closing */
460 rb_data.fd = -1;
461 g_free(rb_data.io_buffer);
462 rb_data.io_buffer = NULL;
463 return false;
466 rb_data.pdh = NULL;
467 rb_data.fd = -1;
469 if (rb_data.name_h != NULL) {
470 fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename());
471 fflush(rb_data.name_h);
474 /* get the next file number and open it */
476 rb_data.curr_file_num++ /* = next_file_num*/;
477 next_file_index = (rb_data.curr_file_num) % rb_data.num_files;
478 next_rfile = &rb_data.files[next_file_index];
480 if (ringbuf_open_file(next_rfile, err) == -1) {
481 return false;
484 if (ringbuf_init_libpcap_fdopen(err) == NULL) {
485 return false;
488 /* switch to the new file */
489 *save_file = next_rfile->name;
490 *save_file_fd = rb_data.fd;
491 (*pdh) = rb_data.pdh;
493 return true;
497 * Calls fclose() for the current ringbuffer file
499 bool
500 ringbuf_libpcap_dump_close(char **save_file, int *err)
502 bool ret_val = true;
504 /* close current file, if it's open */
505 if (rb_data.pdh != NULL) {
506 if (fclose(rb_data.pdh) == EOF) {
507 if (err != NULL) {
508 *err = errno;
510 ws_close(rb_data.fd);
511 ret_val = false;
513 rb_data.pdh = NULL;
514 rb_data.fd = -1;
515 g_free(rb_data.io_buffer);
516 rb_data.io_buffer = NULL;
520 if (rb_data.name_h != NULL) {
521 fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename());
522 fflush(rb_data.name_h);
524 if (EOF == fclose(rb_data.name_h)) {
525 /* Can't really do much about this, can we? */
529 /* set the save file name to the current file */
530 *save_file = rb_data.files[rb_data.curr_file_num % rb_data.num_files].name;
531 return ret_val;
535 * Frees all memory allocated by the ringbuffer
537 void
538 ringbuf_free(void)
540 unsigned int i;
542 if (rb_data.files != NULL) {
543 for (i=0; i < rb_data.num_files; i++) {
544 if (rb_data.files[i].name != NULL) {
545 g_free(rb_data.files[i].name);
546 rb_data.files[i].name = NULL;
549 g_free(rb_data.files);
550 rb_data.files = NULL;
552 if (rb_data.fprefix != NULL) {
553 g_free(rb_data.fprefix);
554 rb_data.fprefix = NULL;
556 if (rb_data.fsuffix != NULL) {
557 g_free(rb_data.fsuffix);
558 rb_data.fsuffix = NULL;
561 CleanupOldCap(NULL);
565 * Frees all memory allocated by the ringbuffer
567 void
568 ringbuf_error_cleanup(void)
570 unsigned int i;
572 /* try to close via wtap */
573 if (rb_data.pdh != NULL) {
574 if (fclose(rb_data.pdh) == 0) {
575 rb_data.fd = -1;
577 rb_data.pdh = NULL;
580 /* close directly if still open */
581 if (rb_data.fd != -1) {
582 ws_close(rb_data.fd);
583 rb_data.fd = -1;
586 if (rb_data.files != NULL) {
587 for (i=0; i < rb_data.num_files; i++) {
588 if (rb_data.files[i].name != NULL) {
589 ws_unlink(rb_data.files[i].name);
593 g_free(rb_data.io_buffer);
594 rb_data.io_buffer = NULL;
596 if (rb_data.name_h != NULL) {
597 if (EOF == fclose(rb_data.name_h)) {
598 /* Can't really do much about this, can we? */
602 /* free the memory */
603 ringbuf_free();
606 #endif /* HAVE_LIBPCAP */