1 /* ringbuffer.c
2 * Routines for packet capture windows
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 /*
12 * <laurent.deniel@free.fr>
13 *
14 * Almost completely rewritten in order to:
15 *
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
21 *
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).
28 *
29 */
30
31 #include <config.h>
32
33 #ifdef HAVE_LIBPCAP
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <time.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <glib.h>
41
42 #include "wspcap.h"
43
44 #include <glib.h>
45
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49
50 #ifdef _WIN32
51 #include <wsutil/win32-utils.h>
52 #endif
53
54 #include "ringbuffer.h"
55 #include <wsutil/file_util.h>
56
57 #ifdef HAVE_ZLIB
58 #include <zlib.h>
59 #endif
60
61 /* Ringbuffer file structure */
62 typedef struct _rb_file {
63 gchar *name;
64 } rb_file;
65
66 #define MAX_FILENAME_QUEUE 100
67
68 /** Ringbuffer data structure */
69 typedef struct _ringbuf_data {
70 rb_file *files;
71 guint num_files; /**< Number of ringbuffer files (1 to ...) */
72 guint curr_file_num; /**< Number of the current file (ever increasing) */
73 gchar *fprefix; /**< Filename prefix */
74 gchar *fsuffix; /**< Filename suffix */
75 gboolean nametimenum; /**< ...num_time... or ...time_num... */
76 gboolean unlimited; /**< TRUE if unlimited number of files */
77
78 int fd; /**< Current ringbuffer file descriptor */
79 FILE *pdh;
80 char *io_buffer; /**< The IO buffer used to write to the file */
81 gboolean group_read_access; /**< TRUE if files need to be opened with group read access */
82 FILE *name_h; /**< write names of completed files to this handle */
83 gchar *compress_type; /**< compress type */
84
85 GMutex mutex; /**< mutex for oldnames */
86 gchar *oldnames[MAX_FILENAME_QUEUE]; /**< filename list of pending to be deleted */
87 } ringbuf_data;
88
89 static ringbuf_data rb_data;
90
91 /*
92 * delete pending uncompressed pcap files.
93 */
CleanupOldCap(gchar * name)94 static void CleanupOldCap(gchar* name)
95 {
96 ws_statb64 statb;
97 size_t i;
98
99 g_mutex_lock(&rb_data.mutex);
100
101 /* Delete pending delete file */
102 for (i = 0; i < sizeof(rb_data.oldnames) / sizeof(rb_data.oldnames[0]); i++) {
103 if (rb_data.oldnames[i] != NULL) {
104 ws_unlink(rb_data.oldnames[i]);
105 if (ws_stat64(rb_data.oldnames[i], &statb) != 0) {
106 g_free(rb_data.oldnames[i]);
107 rb_data.oldnames[i] = NULL;
108 }
109 }
110 }
111
112 if (name) {
113 /* push the current file to pending list if it failed to delete */
114 if (ws_stat64(name, &statb) == 0) {
115 for (i = 0; i < sizeof(rb_data.oldnames) / sizeof(rb_data.oldnames[0]); i++) {
116 if (rb_data.oldnames[i] == NULL) {
117 rb_data.oldnames[i] = g_strdup(name);
118 break;
119 }
120 }
121 }
122 }
123
124 g_mutex_unlock(&rb_data.mutex);
125 }
126
127 /*
128 * compress capture file
129 */
ringbuf_exec_compress(gchar * name)130 static int ringbuf_exec_compress(gchar* name)
131 {
132 guint8 *buffer = NULL;
133 gchar* outgz = NULL;
134 int fd = -1;
135 ssize_t nread;
136 gboolean delete_org_file = TRUE;
137 gzFile fi = NULL;
138
139 fd = ws_open(name, O_RDONLY | O_BINARY, 0000);
140 if (fd < 0) {
141 return -1;
142 }
143
144 outgz = g_strdup_printf("%s.gz", name);
145 fi = gzopen(outgz, "wb");
146 g_free(outgz);
147 if (fi == NULL) {
148 ws_close(fd);
149 return -1;
150 }
151
152 #define FS_READ_SIZE 65536
153 buffer = (guint8*)g_malloc(FS_READ_SIZE);
154 if (buffer == NULL) {
155 ws_close(fd);
156 gzclose(fi);
157 return -1;
158 }
159
160 while ((nread = ws_read(fd, buffer, FS_READ_SIZE)) > 0) {
161 int n = gzwrite(fi, buffer, (unsigned int)nread);
162 if (n <= 0) {
163 /* mark compression as failed */
164 delete_org_file = FALSE;
165 break;
166 }
167 }
168 if (nread < 0) {
169 /* mark compression as failed */
170 delete_org_file = FALSE;
171 }
172 ws_close(fd);
173 gzclose(fi);
174 g_free(buffer);
175
176 /* delete the original file only if compression succeeds */
177 if (delete_org_file) {
178 ws_unlink(name);
179 CleanupOldCap(name);
180 }
181 g_free(name);
182 return 0;
183 }
184
185 /*
186 * thread to compress capture file
187 */
exec_compress_thread(void * arg)188 static void* exec_compress_thread(void* arg)
189 {
190 ringbuf_exec_compress((gchar*)arg);
191 return NULL;
192 }
193
194 /*
195 * start a thread to compress capture file
196 */
ringbuf_start_compress_file(rb_file * rfile)197 static int ringbuf_start_compress_file(rb_file* rfile)
198 {
199 gchar* name = g_strdup(rfile->name);
200 g_thread_new("exec_compress", &exec_compress_thread, name);
201 return 0;
202 }
203
204 /*
205 * create the next filename and open a new binary file with that name
206 */
ringbuf_open_file(rb_file * rfile,int * err)207 static int ringbuf_open_file(rb_file *rfile, int *err)
208 {
209 char filenum[5+1];
210 char timestr[14+1];
211 time_t current_time;
212 struct tm *tm;
213
214 if (rfile->name != NULL) {
215 if (rb_data.unlimited == FALSE) {
216 /* remove old file (if any, so ignore error) */
217 ws_unlink(rfile->name);
218 }
219 else if (rb_data.compress_type != NULL && strcmp(rb_data.compress_type, "gzip") == 0) {
220 ringbuf_start_compress_file(rfile);
221 }
222 g_free(rfile->name);
223 }
224
225 #ifdef _WIN32
226 _tzset();
227 #endif
228 current_time = time(NULL);
229
230 g_snprintf(filenum, sizeof(filenum), "%05u", (rb_data.curr_file_num + 1) % RINGBUFFER_MAX_NUM_FILES);
231 tm = localtime(¤t_time);
232 if (tm != NULL)
233 strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", tm);
234 else
235 (void) g_strlcpy(timestr, "196912312359", sizeof(timestr)); /* second before the Epoch */
236 if (rb_data.nametimenum) {
237 rfile->name = g_strconcat(rb_data.fprefix, "_", timestr, "_", filenum, rb_data.fsuffix, NULL);
238 } else {
239 rfile->name = g_strconcat(rb_data.fprefix, "_", filenum, "_", timestr, rb_data.fsuffix, NULL);
240 }
241
242 if (rfile->name == NULL) {
243 if (err != NULL)
244 *err = ENOMEM;
245 return -1;
246 }
247
248 rb_data.fd = ws_open(rfile->name, O_RDWR|O_BINARY|O_TRUNC|O_CREAT,
249 rb_data.group_read_access ? 0640 : 0600);
250
251 if (rb_data.fd == -1 && err != NULL) {
252 *err = errno;
253 }
254
255 return rb_data.fd;
256 }
257
258 /*
259 * Initialize the ringbuffer data structures
260 */
261 int
ringbuf_init(const char * capfile_name,guint num_files,gboolean group_read_access,gchar * compress_type,gboolean has_nametimenum)262 ringbuf_init(const char *capfile_name, guint num_files, gboolean group_read_access,
263 gchar *compress_type, gboolean has_nametimenum)
264 {
265 unsigned int i;
266 char *pfx, *last_pathsep;
267 gchar *save_file;
268
269 rb_data.files = NULL;
270 rb_data.curr_file_num = 0;
271 rb_data.fprefix = NULL;
272 rb_data.fsuffix = NULL;
273 rb_data.nametimenum = has_nametimenum;
274 rb_data.unlimited = FALSE;
275 rb_data.fd = -1;
276 rb_data.pdh = NULL;
277 rb_data.io_buffer = NULL;
278 rb_data.group_read_access = group_read_access;
279 rb_data.name_h = NULL;
280 rb_data.compress_type = compress_type;
281 g_mutex_init(&rb_data.mutex);
282
283 /* just to be sure ... */
284 if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
285 rb_data.num_files = num_files;
286 } else {
287 rb_data.num_files = RINGBUFFER_MAX_NUM_FILES;
288 }
289
290 /* Check file name */
291 if (capfile_name == NULL) {
292 /* ringbuffer does not work with temporary files! */
293 return -1;
294 }
295
296 /* set file name prefix/suffix */
297
298 save_file = g_strdup(capfile_name);
299 last_pathsep = strrchr(save_file, G_DIR_SEPARATOR);
300 pfx = strrchr(save_file,'.');
301 if (pfx != NULL && (last_pathsep == NULL || pfx > last_pathsep)) {
302 /* The pathname has a "." in it, and it's in the last component
303 of the pathname (because there is either only one component,
304 i.e. last_pathsep is null as there are no path separators,
305 or the "." is after the path separator before the last
306 component.
307
308 Treat it as a separator between the rest of the file name and
309 the file name suffix, and arrange that the names given to the
310 ring buffer files have the specified suffix, i.e. put the
311 changing part of the name *before* the suffix. */
312 pfx[0] = '\0';
313 rb_data.fprefix = g_strdup(save_file);
314 pfx[0] = '.'; /* restore capfile_name */
315 rb_data.fsuffix = g_strdup(pfx);
316 } else {
317 /* Either there's no "." in the pathname, or it's in a directory
318 component, so the last component has no suffix. */
319 rb_data.fprefix = g_strdup(save_file);
320 rb_data.fsuffix = NULL;
321 }
322 g_free(save_file);
323 save_file = NULL;
324
325 /* allocate rb_file structures (only one if unlimited since there is no
326 need to save all file names in that case) */
327
328 if (num_files == RINGBUFFER_UNLIMITED_FILES) {
329 rb_data.unlimited = TRUE;
330 rb_data.num_files = 1;
331 }
332
333 rb_data.files = g_new(rb_file, rb_data.num_files);
334 if (rb_data.files == NULL) {
335 return -1;
336 }
337
338 for (i=0; i < rb_data.num_files; i++) {
339 rb_data.files[i].name = NULL;
340 }
341
342 /* create the first file */
343 if (ringbuf_open_file(&rb_data.files[0], NULL) == -1) {
344 ringbuf_error_cleanup();
345 return -1;
346 }
347
348 return rb_data.fd;
349 }
350
351 /*
352 * Set name of file to which to print ringbuffer file names.
353 */
354 gboolean
ringbuf_set_print_name(gchar * name,int * err)355 ringbuf_set_print_name(gchar *name, int *err)
356 {
357 if (rb_data.name_h != NULL) {
358 if (EOF == fclose(rb_data.name_h)) {
359 if (err != NULL) {
360 *err = errno;
361 }
362 return FALSE;
363 }
364 }
365 if (!strcmp(name, "-") || !strcmp(name, "stdout")) {
366 rb_data.name_h = stdout;
367 } else if (!strcmp(name, "stderr")) {
368 rb_data.name_h = stderr;
369 } else {
370 if (NULL == (rb_data.name_h = ws_fopen(name, "wt"))) {
371 if (err != NULL) {
372 *err = errno;
373 }
374 return FALSE;
375 }
376 }
377 return TRUE;
378 }
379
380 /*
381 * Whether the ringbuf filenames are ready.
382 * (Whether ringbuf_init is called and ringbuf_free is not called.)
383 */
ringbuf_is_initialized(void)384 gboolean ringbuf_is_initialized(void)
385 {
386 return rb_data.files != NULL;
387 }
388
ringbuf_current_filename(void)389 const gchar *ringbuf_current_filename(void)
390 {
391 return rb_data.files[rb_data.curr_file_num % rb_data.num_files].name;
392 }
393
394 /*
395 * Calls ws_fdopen() for the current ringbuffer file
396 */
397 FILE *
ringbuf_init_libpcap_fdopen(int * err)398 ringbuf_init_libpcap_fdopen(int *err)
399 {
400 rb_data.pdh = ws_fdopen(rb_data.fd, "wb");
401 if (rb_data.pdh == NULL) {
402 if (err != NULL) {
403 *err = errno;
404 }
405 } else {
406 size_t buffsize = IO_BUF_SIZE;
407 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
408 ws_statb64 statb;
409
410 if (ws_fstat64(rb_data.fd, &statb) == 0) {
411 if (statb.st_blksize > IO_BUF_SIZE) {
412 buffsize = statb.st_blksize;
413 }
414 }
415 #endif
416 /* Increase the size of the IO buffer */
417 rb_data.io_buffer = (char *)g_realloc(rb_data.io_buffer, buffsize);
418 setvbuf(rb_data.pdh, rb_data.io_buffer, _IOFBF, buffsize);
419 }
420
421 return rb_data.pdh;
422 }
423
424 /*
425 * Switches to the next ringbuffer file
426 */
427 gboolean
ringbuf_switch_file(FILE ** pdh,gchar ** save_file,int * save_file_fd,int * err)428 ringbuf_switch_file(FILE **pdh, gchar **save_file, int *save_file_fd, int *err)
429 {
430 int next_file_index;
431 rb_file *next_rfile = NULL;
432
433 /* close current file */
434
435 if (fclose(rb_data.pdh) == EOF) {
436 if (err != NULL) {
437 *err = errno;
438 }
439 ws_close(rb_data.fd); /* XXX - the above should have closed this already */
440 rb_data.pdh = NULL; /* it's still closed, we just got an error while closing */
441 rb_data.fd = -1;
442 g_free(rb_data.io_buffer);
443 rb_data.io_buffer = NULL;
444 return FALSE;
445 }
446
447 rb_data.pdh = NULL;
448 rb_data.fd = -1;
449
450 if (rb_data.name_h != NULL) {
451 fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename());
452 fflush(rb_data.name_h);
453 }
454
455 /* get the next file number and open it */
456
457 rb_data.curr_file_num++ /* = next_file_num*/;
458 next_file_index = (rb_data.curr_file_num) % rb_data.num_files;
459 next_rfile = &rb_data.files[next_file_index];
460
461 if (ringbuf_open_file(next_rfile, err) == -1) {
462 return FALSE;
463 }
464
465 if (ringbuf_init_libpcap_fdopen(err) == NULL) {
466 return FALSE;
467 }
468
469 /* switch to the new file */
470 *save_file = next_rfile->name;
471 *save_file_fd = rb_data.fd;
472 (*pdh) = rb_data.pdh;
473
474 return TRUE;
475 }
476
477 /*
478 * Calls fclose() for the current ringbuffer file
479 */
480 gboolean
ringbuf_libpcap_dump_close(gchar ** save_file,int * err)481 ringbuf_libpcap_dump_close(gchar **save_file, int *err)
482 {
483 gboolean ret_val = TRUE;
484
485 /* close current file, if it's open */
486 if (rb_data.pdh != NULL) {
487 if (fclose(rb_data.pdh) == EOF) {
488 if (err != NULL) {
489 *err = errno;
490 }
491 ws_close(rb_data.fd);
492 ret_val = FALSE;
493 }
494 rb_data.pdh = NULL;
495 rb_data.fd = -1;
496 g_free(rb_data.io_buffer);
497 rb_data.io_buffer = NULL;
498
499 }
500
501 if (rb_data.name_h != NULL) {
502 fprintf(rb_data.name_h, "%s\n", ringbuf_current_filename());
503 fflush(rb_data.name_h);
504
505 if (EOF == fclose(rb_data.name_h)) {
506 /* Can't really do much about this, can we? */
507 }
508 }
509
510 /* set the save file name to the current file */
511 *save_file = rb_data.files[rb_data.curr_file_num % rb_data.num_files].name;
512 return ret_val;
513 }
514
515 /*
516 * Frees all memory allocated by the ringbuffer
517 */
518 void
ringbuf_free(void)519 ringbuf_free(void)
520 {
521 unsigned int i;
522
523 if (rb_data.files != NULL) {
524 for (i=0; i < rb_data.num_files; i++) {
525 if (rb_data.files[i].name != NULL) {
526 g_free(rb_data.files[i].name);
527 rb_data.files[i].name = NULL;
528 }
529 }
530 g_free(rb_data.files);
531 rb_data.files = NULL;
532 }
533 if (rb_data.fprefix != NULL) {
534 g_free(rb_data.fprefix);
535 rb_data.fprefix = NULL;
536 }
537 if (rb_data.fsuffix != NULL) {
538 g_free(rb_data.fsuffix);
539 rb_data.fsuffix = NULL;
540 }
541
542 CleanupOldCap(NULL);
543 }
544
545 /*
546 * Frees all memory allocated by the ringbuffer
547 */
548 void
ringbuf_error_cleanup(void)549 ringbuf_error_cleanup(void)
550 {
551 unsigned int i;
552
553 /* try to close via wtap */
554 if (rb_data.pdh != NULL) {
555 if (fclose(rb_data.pdh) == 0) {
556 rb_data.fd = -1;
557 }
558 rb_data.pdh = NULL;
559 }
560
561 /* close directly if still open */
562 if (rb_data.fd != -1) {
563 ws_close(rb_data.fd);
564 rb_data.fd = -1;
565 }
566
567 if (rb_data.files != NULL) {
568 for (i=0; i < rb_data.num_files; i++) {
569 if (rb_data.files[i].name != NULL) {
570 ws_unlink(rb_data.files[i].name);
571 }
572 }
573 }
574 g_free(rb_data.io_buffer);
575 rb_data.io_buffer = NULL;
576
577 if (rb_data.name_h != NULL) {
578 if (EOF == fclose(rb_data.name_h)) {
579 /* Can't really do much about this, can we? */
580 }
581 }
582
583 /* free the memory */
584 ringbuf_free();
585 }
586
587 #endif /* HAVE_LIBPCAP */
588
589 /*
590 * Editor modelines - https://www.wireshark.org/tools/modelines.html
591 *
592 * Local Variables:
593 * c-basic-offset: 2
594 * tab-width: 8
595 * indent-tabs-mode: nil
596 * End:
597 *
598 * ex: set shiftwidth=2 tabstop=8 expandtab:
599 * :indentSize=2:tabSize=8:noTabs=true:
600 */
601