1 /**
2 * settings.c -- goaccess configuration
3 * ______ ___
4 * / ____/___ / | _____________ __________
5 * / / __/ __ \/ /| |/ ___/ ___/ _ \/ ___/ ___/
6 * / /_/ / /_/ / ___ / /__/ /__/ __(__ |__ )
7 * \____/\____/_/ |_\___/\___/\___/____/____/
8 *
9 * The MIT License (MIT)
10 * Copyright (c) 2009-2020 Gerardo Orellana <hello @ goaccess.io>
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a copy
13 * of this software and associated documentation files (the "Software"), to deal
14 * in the Software without restriction, including without limitation the rights
15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 * copies of the Software, and to permit persons to whom the Software is
17 * furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included in all
20 * copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 * SOFTWARE.
29 */
30
31 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
32
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <errno.h>
39
40 #include "settings.h"
41
42 #include "error.h"
43 #include "gkhash.h"
44 #include "labels.h"
45 #include "pdjson.h"
46 #include "util.h"
47 #include "xmalloc.h"
48
49 static char **nargv;
50 static int nargc = 0;
51
52 /* *INDENT-OFF* */
53 static GEnum LOGTYPE[] = {
54 {"COMBINED" , COMBINED} ,
55 {"VCOMBINED" , VCOMBINED} ,
56 {"COMMON" , COMMON} ,
57 {"VCOMMON" , VCOMMON} ,
58 {"W3C" , W3C} ,
59 {"SQUID" , SQUID} ,
60 {"CLOUDFRONT" , CLOUDFRONT} ,
61 {"CLOUDSTORAGE" , CLOUDSTORAGE} ,
62 {"AWSELB" , AWSELB} ,
63 {"AWSS3" , AWSS3} ,
64 {"CADDY" , CADDY} ,
65 };
66
67 static const GPreConfLog logs = {
68 "%h %^[%d:%t %^] \"%r\" %s %b \"%R\" \"%u\"", /* NCSA */
69 "%v:%^ %h %^[%d:%t %^] \"%r\" %s %b \"%R\" \"%u\"", /* NCSA + VHost */
70 "%h %^[%d:%t %^] \"%r\" %s %b", /* CLF */
71 "%v:%^ %h %^[%d:%t %^] \"%r\" %s %b", /* CLF+VHost */
72 "%d %t %^ %m %U %q %^ %^ %h %u %R %s %^ %^ %L", /* W3C */
73 "%d\\t%t\\t%^\\t%b\\t%h\\t%m\\t%v\\t%U\\t%s\\t%R\\t%u\\t%q\\t%^\\t%C\\t%^\\t%^\\t%^\\t%^\\t%T\\t%^\\t%K\\t%k\\t%^\\t%H\\t%^", /* CloudFront */
74 "\"%x\",\"%h\",%^,%^,\"%m\",\"%U\",\"%s\",%^,\"%b\",\"%D\",%^,\"%R\",\"%u\"", /* Cloud Storage */
75 "%^ %dT%t.%^ %v %h:%^ %^ %T %^ %^ %s %^ %b %^ \"%r\" \"%u\" %^", /* AWS Elastic Load Balancing */
76 "%^ %^ %^ %v %^: %x.%^ %~%L %h %^/%s %b %m %U", /* Squid Native */
77 "%^ %v [%d:%t %^] %h %^\"%r\" %s %^ %b %^ %L %^ \"%R\" \"%u\"", /* Amazon S3 */
78
79 /* Caddy JSON */
80 "{ \"ts\": \"%x.%^\", \"request\": { \"remote_addr\": \"%h:%^\", \"proto\":"
81 "\"%H\", \"method\": \"%m\", \"host\": \"%v\", \"uri\": \"%U\", \"headers\": {"
82 "\"User-Agent\": [\"%u\"], \"Referer\": [\"%R\"] }, \"tls\": { \"cipher_suite\":"
83 "\"%k\", \"proto\": \"%K\" } }, \"duration\": \"%T\", \"size\": \"%b\","
84 "\"status\": \"%s\", \"resp_headers\": { \"Content-Type\": [\"%M\"] } }"
85 };
86
87 static const GPreConfTime times = {
88 "%H:%M:%S",
89 "%f", /* Cloud Storage (usec) */
90 "%s", /* Squid (sec) */
91 };
92
93 static const GPreConfDate dates = {
94 "%d/%b/%Y", /* Apache */
95 "%Y-%m-%d", /* W3C */
96 "%f", /* Cloud Storage (usec) */
97 "%s", /* Squid (sec) */
98 };
99 /* *INDENT-ON* */
100
101 /* Ignore the following options */
102 static const char *ignore_cmd_opts[] = {
103 "help",
104 "storage",
105 "version",
106 };
107
108 /* Determine if the given command line option needs to be ignored.
109 *
110 * If needs to be ignored, 1 is returned.
111 * If not within the list of ignored command line options, 0 is returned. */
112 static int
in_ignore_cmd_opts(const char * val)113 in_ignore_cmd_opts (const char *val) {
114 size_t i;
115 for (i = 0; i < ARRAY_SIZE (ignore_cmd_opts); i++) {
116 if (strstr (val, ignore_cmd_opts[i]) != NULL)
117 return 1;
118 }
119 return 0;
120 }
121
122 /* Get the location of the configuration file.
123 *
124 * By default, it attempts to read it from the user supplied path, else it will
125 * try to open the global config file path (sysconfdir) or from the HOME
126 * environment variable (~/.goaccessrc).
127 *
128 * On success, the path to the configuration file is returned. */
129 char *
get_config_file_path(void)130 get_config_file_path (void) {
131 char *upath = NULL, *gpath = NULL, *rpath = NULL;
132
133 /* determine which config file to open, default or custom */
134 if (conf.iconfigfile != NULL) {
135 rpath = realpath (conf.iconfigfile, NULL);
136 if (rpath == NULL)
137 FATAL ("Unable to open the specified config file. %s", strerror (errno));
138 return rpath;
139 }
140
141 /* first attempt to use the user's config file, e.g., ~/.goaccessrc */
142 upath = get_user_config ();
143 /* failure, e.g. if the file does not exist */
144 if ((rpath = realpath (upath, NULL)) != NULL) {
145 free (upath);
146 return rpath;
147 }
148 LOG_DEBUG (("Unable to find user's config file %s %s", upath, strerror (errno)));
149 free (upath);
150
151 /* otherwise, fallback to global config file, e.g.,%sysconfdir%/goaccess.conf */
152 gpath = get_global_config ();
153 if ((rpath = realpath (gpath, NULL)) != NULL && conf.load_global_config) {
154 free (gpath);
155 return rpath;
156 }
157 LOG_DEBUG (("Unable to find global config file %s %s", gpath, strerror (errno)));
158 free (gpath);
159
160 return NULL;
161 }
162
163 /* Use predefined static files when no config file is used. Note that
164 * the order in which are listed is from the most to the least common
165 * (most cases). */
166 void
set_default_static_files(void)167 set_default_static_files (void) {
168 size_t i;
169 const char *exts[] = {
170 ".css",
171 ".js ",
172 ".jpg",
173 ".png",
174 ".gif",
175 ".ico",
176 ".jpeg",
177 ".pdf",
178 ".txt",
179 ".csv",
180 ".mpeg",
181 ".mpg",
182 ".swf",
183 ".woff",
184 ".woff2",
185 ".xls",
186 ".xlsx",
187 ".doc ",
188 ".docx",
189 ".ppt ",
190 ".pptx",
191 ".zip",
192 ".mp3",
193 ".mp4",
194 ".exe",
195 ".iso ",
196 ".gz ",
197 ".rar ",
198 ".svg ",
199 ".bmp ",
200 ".tar ",
201 ".tgz ",
202 ".tiff",
203 ".tif ",
204 ".ttf ",
205 ".flv ",
206 };
207
208 if (conf.static_file_idx > 0)
209 return;
210
211 for (i = 0; i < ARRAY_SIZE (exts); i++) {
212 if (conf.static_file_max_len < strlen (exts[i]))
213 conf.static_file_max_len = strlen (exts[i]);
214 conf.static_files[conf.static_file_idx++] = exts[i];
215 }
216 }
217
218 /* Clean malloc'd log/date/time escaped formats. */
219 void
free_formats(void)220 free_formats (void) {
221 free (conf.log_format);
222 free (conf.date_format);
223 free (conf.date_num_format);
224 free (conf.spec_date_time_format);
225 free (conf.spec_date_time_num_format);
226 free (conf.time_format);
227 }
228
229 /* Clean malloc'd command line arguments. */
230 void
free_cmd_args(void)231 free_cmd_args (void) {
232 int i;
233 if (nargc == 0)
234 return;
235 for (i = 0; i < nargc; i++)
236 free (nargv[i]);
237 free (nargv);
238 free (conf.iconfigfile);
239 }
240
241 /* Append extra value to argv */
242 static void
append_to_argv(int * argc,char *** argv,char * val)243 append_to_argv (int *argc, char ***argv, char *val) {
244 char **_argv = xrealloc (*argv, (*argc + 2) * sizeof (*_argv));
245 _argv[*argc] = val;
246 _argv[*argc + 1] = (char *) '\0';
247 (*argc)++;
248 *argv = _argv;
249 }
250
251 /* Parses the configuration file to feed getopt_long.
252 *
253 * On error, ENOENT error code is returned.
254 * On success, 0 is returned and config file enabled options are appended to
255 * argv. */
256 int
parse_conf_file(int * argc,char *** argv)257 parse_conf_file (int *argc, char ***argv) {
258 char line[MAX_LINE_CONF + 1];
259 char *path = NULL, *val, *opt, *p;
260 FILE *file;
261 int i, n = 0;
262 size_t idx;
263
264 /* assumes program name is on argv[0], though, it is not guaranteed */
265 append_to_argv (&nargc, &nargv, xstrdup ((char *) *argv[0]));
266
267 /* determine which config file to open, default or custom */
268 path = get_config_file_path ();
269 if (path == NULL)
270 return ENOENT;
271
272 /* could not open conf file, if so prompt conf dialog */
273 if ((file = fopen (path, "r")) == NULL) {
274 free (path);
275 return ENOENT;
276 }
277
278 while (fgets (line, sizeof line, file) != NULL) {
279 while (line[0] == ' ' || line[0] == '\t')
280 memmove (line, line + 1, strlen (line));
281 n++;
282 if (line[0] == '\n' || line[0] == '\r' || line[0] == '#')
283 continue;
284
285 /* key */
286 idx = strcspn (line, " \t");
287 if (strlen (line) == idx)
288 FATAL ("Malformed config key at line: %d", n);
289
290 line[idx] = '\0';
291
292 /* make old config options backwards compatible by
293 * substituting underscores with dashes */
294 while ((p = strpbrk (line, "_")) != NULL)
295 *p = '-';
296
297 /* Ignore the following options when reading the config file */
298 if (in_ignore_cmd_opts (line))
299 continue;
300
301 /* value */
302 val = line + (idx + 1);
303 idx = strspn (val, " \t");
304 if (strlen (line) == idx)
305 FATAL ("Malformed config value at line: %d", n);
306 val = val + idx;
307 val = trim_str (val);
308
309 if (strcmp ("false", val) == 0)
310 continue;
311
312 /* set it as command line options */
313 opt = xmalloc (snprintf (NULL, 0, "--%s", line) + 1);
314 sprintf (opt, "--%s", line);
315
316 append_to_argv (&nargc, &nargv, opt);
317 if (strcmp ("true", val) != 0)
318 append_to_argv (&nargc, &nargv, xstrdup (val));
319 }
320
321 /* give priority to command line arguments */
322 for (i = 1; i < *argc; i++)
323 append_to_argv (&nargc, &nargv, xstrdup ((char *) (*argv)[i]));
324
325 *argc = nargc;
326 *argv = (char **) nargv;
327
328 fclose (file);
329
330 if (conf.iconfigfile == NULL)
331 conf.iconfigfile = xstrdup (path);
332
333 free (path);
334 return 0;
335 }
336
337 /* Get the enumerated log format given its equivalent format string.
338 * The case in the format string does not matter.
339 *
340 * On error, -1 is returned.
341 * On success, the enumerated format is returned. */
342 static int
get_log_format_item_enum(const char * str)343 get_log_format_item_enum (const char *str) {
344 int ret;
345 char *upstr;
346
347 ret = str2enum (LOGTYPE, ARRAY_SIZE (LOGTYPE), str);
348 if (ret >= 0)
349 return ret;
350
351 /* uppercase the input string and try again */
352 upstr = strtoupper (xstrdup (str));
353 ret = str2enum (LOGTYPE, ARRAY_SIZE (LOGTYPE), upstr);
354 free (upstr);
355
356 return ret;
357 }
358
359 /* Determine the selected log format from the config file or command line
360 * option.
361 *
362 * On error, -1 is returned.
363 * On success, the index of the matched item is returned. */
364 size_t
get_selected_format_idx(void)365 get_selected_format_idx (void) {
366 if (conf.log_format == NULL)
367 return (size_t) -1;
368 if (strcmp (conf.log_format, logs.common) == 0)
369 return COMMON;
370 else if (strcmp (conf.log_format, logs.vcommon) == 0)
371 return VCOMMON;
372 else if (strcmp (conf.log_format, logs.combined) == 0)
373 return COMBINED;
374 else if (strcmp (conf.log_format, logs.vcombined) == 0)
375 return VCOMBINED;
376 else if (strcmp (conf.log_format, logs.w3c) == 0)
377 return W3C;
378 else if (strcmp (conf.log_format, logs.cloudfront) == 0)
379 return CLOUDFRONT;
380 else if (strcmp (conf.log_format, logs.cloudstorage) == 0)
381 return CLOUDSTORAGE;
382 else if (strcmp (conf.log_format, logs.awselb) == 0)
383 return AWSELB;
384 else if (strcmp (conf.log_format, logs.squid) == 0)
385 return SQUID;
386 else if (strcmp (conf.log_format, logs.awss3) == 0)
387 return AWSS3;
388 else if (strcmp (conf.log_format, logs.caddy) == 0)
389 return CADDY;
390 else
391 return (size_t) -1;
392 }
393
394 /* Determine the selected log format from the config file or command line
395 * option.
396 *
397 * On error, NULL is returned.
398 * On success, an allocated string containing the log format is returned. */
399 char *
get_selected_format_str(size_t idx)400 get_selected_format_str (size_t idx) {
401 char *fmt = NULL;
402 switch (idx) {
403 case COMMON:
404 fmt = alloc_string (logs.common);
405 break;
406 case VCOMMON:
407 fmt = alloc_string (logs.vcommon);
408 break;
409 case COMBINED:
410 fmt = alloc_string (logs.combined);
411 break;
412 case VCOMBINED:
413 fmt = alloc_string (logs.vcombined);
414 break;
415 case W3C:
416 fmt = alloc_string (logs.w3c);
417 break;
418 case CLOUDFRONT:
419 fmt = alloc_string (logs.cloudfront);
420 break;
421 case CLOUDSTORAGE:
422 fmt = alloc_string (logs.cloudstorage);
423 break;
424 case AWSELB:
425 fmt = alloc_string (logs.awselb);
426 break;
427 case SQUID:
428 fmt = alloc_string (logs.squid);
429 break;
430 case AWSS3:
431 fmt = alloc_string (logs.awss3);
432 break;
433 case CADDY:
434 fmt = alloc_string (logs.caddy);
435 break;
436 }
437
438 return fmt;
439 }
440
441 /* Determine the selected date format from the config file or command line
442 * option.
443 *
444 * On error, NULL is returned.
445 * On success, an allocated string containing the date format is returned. */
446 char *
get_selected_date_str(size_t idx)447 get_selected_date_str (size_t idx) {
448 char *fmt = NULL;
449 switch (idx) {
450 case COMMON:
451 case VCOMMON:
452 case COMBINED:
453 case VCOMBINED:
454 case AWSS3:
455 fmt = alloc_string (dates.apache);
456 break;
457 case AWSELB:
458 case CLOUDFRONT:
459 case W3C:
460 fmt = alloc_string (dates.w3c);
461 break;
462 case CLOUDSTORAGE:
463 fmt = alloc_string (dates.usec);
464 break;
465 case SQUID:
466 case CADDY:
467 fmt = alloc_string (dates.sec);
468 break;
469 }
470
471 return fmt;
472 }
473
474 /* Determine the selected time format from the config file or command line
475 * option.
476 *
477 * On error, NULL is returned.
478 * On success, an allocated string containing the time format is returned. */
479 char *
get_selected_time_str(size_t idx)480 get_selected_time_str (size_t idx) {
481 char *fmt = NULL;
482 switch (idx) {
483 case AWSELB:
484 case CLOUDFRONT:
485 case COMBINED:
486 case COMMON:
487 case VCOMBINED:
488 case VCOMMON:
489 case W3C:
490 case AWSS3:
491 fmt = alloc_string (times.fmt24);
492 break;
493 case CLOUDSTORAGE:
494 fmt = alloc_string (times.usec);
495 break;
496 case SQUID:
497 case CADDY:
498 fmt = alloc_string (times.sec);
499 break;
500 }
501
502 return fmt;
503 }
504
505 /* Determine if the log/date/time were set, otherwise exit the program
506 * execution. */
507 const char *
verify_formats(void)508 verify_formats (void) {
509 if (conf.time_format == NULL || *conf.time_format == '\0')
510 return ERR_FORMAT_NO_TIME_FMT;
511
512 if (conf.date_format == NULL || *conf.date_format == '\0')
513 return ERR_FORMAT_NO_DATE_FMT;
514
515 if (conf.log_format == NULL || *conf.log_format == '\0')
516 return ERR_FORMAT_NO_LOG_FMT;
517
518 return NULL;
519 }
520
521 /* A wrapper function to concat the given specificity to the date
522 * format. */
523 static char *
append_spec_date_format(const char * date_format,const char * spec_format)524 append_spec_date_format (const char *date_format, const char *spec_format) {
525 char *s = xmalloc (snprintf (NULL, 0, "%s%s", date_format, spec_format) + 1);
526 sprintf (s, "%s%s", date_format, spec_format);
527
528 return s;
529 }
530
531 /* Iterate over the given format and clean unwanted chars and keep all
532 * date/time specifiers such as %b%Y%d%M%S.
533 *
534 * On error NULL is returned.
535 * On success, a clean format containing only date/time specifiers is
536 * returned. */
537 static char *
clean_date_time_format(const char * format)538 clean_date_time_format (const char *format) {
539 char *fmt = NULL, *pr = NULL, *pw = NULL;
540 int special = 0;
541
542 if (format == NULL || *format == '\0')
543 return NULL;
544
545 fmt = xstrdup (format);
546 pr = fmt;
547 pw = fmt;
548 while (*pr) {
549 *pw = *pr++;
550 if (*pw == '%' || special) {
551 special = !special;
552 pw++;
553 }
554 }
555 *pw = '\0';
556
557 return fmt;
558 }
559
560 /* Determine if the given specifier character is an abbreviated type
561 * of date.
562 *
563 * If it is, 1 is returned, otherwise, 0 is returned. */
564 static int
is_date_abbreviated(const char * fdate)565 is_date_abbreviated (const char *fdate) {
566 if (strpbrk (fdate, "cDF"))
567 return 1;
568
569 return 0;
570 }
571
572 /* A wrapper to extract time specifiers from a time format.
573 *
574 * On error NULL is returned.
575 * On success, a clean format containing only time specifiers is
576 * returned. */
577 static char *
set_format_time(void)578 set_format_time (void) {
579 char *ftime = NULL;
580
581 if (has_timestamp (conf.date_format) || !strcmp ("%T", conf.time_format))
582 ftime = xstrdup ("%H%M%S");
583 else
584 ftime = clean_date_time_format (conf.time_format);
585
586 return ftime;
587 }
588
589 /* A wrapper to extract date specifiers from a date format.
590 *
591 * On error NULL is returned.
592 * On success, a clean format containing only date specifiers is
593 * returned. */
594 static char *
set_format_date(void)595 set_format_date (void) {
596 char *fdate = NULL;
597
598 if (has_timestamp (conf.date_format))
599 fdate = xstrdup ("%Y%m%d");
600 else
601 fdate = clean_date_time_format (conf.date_format);
602
603 return fdate;
604 }
605
606 /* Once we have a numeric date format, we attempt to read the time
607 * format and construct a date_time numeric specificity format (if any
608 * specificity is given). The result may look like Ymd[HM].
609 *
610 * On success, the numeric date time specificity format is set. */
611 static void
set_spec_date_time_num_format(void)612 set_spec_date_time_num_format (void) {
613 char *buf = NULL, *tf = set_format_time ();
614 const char *df = conf.date_num_format;
615
616 if (!df || !tf) {
617 free (tf);
618 return;
619 }
620
621 if (conf.date_spec_hr && strchr (tf, 'H'))
622 buf = append_spec_date_format (df, "%H");
623 else
624 buf = xstrdup (df);
625
626 conf.spec_date_time_num_format = buf;
627 free (tf);
628 }
629
630 /* Set a human readable specificity date and time format.
631 *
632 * On success, the human readable date time specificity format is set. */
633 static void
set_spec_date_time_format(void)634 set_spec_date_time_format (void) {
635 char *buf = NULL;
636 const char *fmt = conf.spec_date_time_num_format;
637 int buflen = 0, flen = 0;
638
639 if (!fmt)
640 return;
641
642 flen = (strlen (fmt) * 2) + 1;
643 buf = xcalloc (flen, sizeof (char));
644
645 if (strchr (fmt, 'd'))
646 buflen += snprintf (buf + buflen, flen - buflen, "%%d/");
647 if (strchr (fmt, 'm'))
648 buflen += snprintf (buf + buflen, flen - buflen, "%%b/");
649 if (strchr (fmt, 'Y'))
650 buflen += snprintf (buf + buflen, flen - buflen, "%%Y");
651 if (strchr (fmt, 'H'))
652 buflen += snprintf (buf + buflen, flen - buflen, ":%%H");
653
654 conf.spec_date_time_format = buf;
655 }
656
657 /* Normalize the date format from the date format given by the user to
658 * Ymd so it can be sorted out properly afterwards.
659 *
660 * On error or unable to determine the format, 1 is returned.
661 * On success, the numeric date format as Ymd is set and 0 is
662 * returned. */
663 static int
set_date_num_format(void)664 set_date_num_format (void) {
665 char *fdate = NULL, *buf = NULL;
666 int buflen = 0, flen = 0;
667
668 fdate = set_format_date ();
669 if (!fdate)
670 return 1;
671
672 if (is_date_abbreviated (fdate)) {
673 free (fdate);
674 conf.date_num_format = xstrdup ("%Y%m%d");
675 return 0;
676 }
677
678 flen = strlen (fdate) + 1;
679 flen = MAX (MIN_DATENUM_FMT_LEN, flen); /* at least %Y%m%d + 1 */
680 buf = xcalloc (flen, sizeof (char));
681
682 /* always add a %Y */
683 buflen += snprintf (buf + buflen, flen - buflen, "%%Y");
684 if (strpbrk (fdate, "hbmBf*"))
685 buflen += snprintf (buf + buflen, flen - buflen, "%%m");
686 if (strpbrk (fdate, "def*"))
687 buflen += snprintf (buf + buflen, flen - buflen, "%%d");
688
689 conf.date_num_format = buf;
690 free (fdate);
691
692 return buflen == 0 ? 1 : 0;
693 }
694
695 /* Determine if we have a valid JSON format */
696 int
is_json_log_format(const char * fmt)697 is_json_log_format (const char *fmt) {
698 enum json_type t = JSON_ERROR;
699 json_stream json;
700
701 json_open_string (&json, fmt);
702 /* ensure we use strict JSON when determining if we're using a JSON format */
703 json_set_streaming (&json, false);
704 do {
705 t = json_next (&json);
706 switch (t) {
707 case JSON_ERROR:
708 json_close (&json);
709 return 0;
710 default:
711 break;
712 }
713 } while (t != JSON_DONE && t != JSON_ERROR);
714 json_close (&json);
715
716 return 1;
717 }
718
719 /* Append the source string to destination and reallocates and
720 * updating the destination buffer appropriately. */
721 static void
ws_append_str(char ** dest,const char * src)722 ws_append_str (char **dest, const char *src) {
723 size_t curlen = strlen (*dest);
724 size_t srclen = strlen (src);
725 size_t newlen = curlen + srclen;
726
727 char *str = xrealloc (*dest, newlen + 1);
728 memcpy (str + curlen, src, srclen + 1);
729 *dest = str;
730 }
731
732 /* Delete the given key from a nested object key or empty the key. */
733 static void
dec_json_key(char * key)734 dec_json_key (char *key) {
735 char *ptr = NULL;
736 if (key && (ptr = strrchr (key, '.')))
737 *ptr = '\0';
738 else if (key && !(ptr = strrchr (key, '.')))
739 key[0] = '\0';
740 }
741
742 /* Given a JSON string, parse it and call the given function pointer after each
743 * value.
744 *
745 * On error, a non-zero value is returned.
746 * On success, 0 is returned. */
747 int
parse_json_string(void * ptr_data,const char * str,int (* cb)(void *,char *,char *))748 parse_json_string (void *ptr_data, const char *str, int (*cb) (void *, char *, char *)) {
749 char *key = NULL, *val = NULL;
750 enum json_type ctx = JSON_ERROR, t = JSON_ERROR;
751 int ret = 0;
752 size_t len = 0, level = 0;
753 json_stream json;
754
755 json_open_string (&json, str);
756 do {
757 t = json_next (&json);
758
759 switch (t) {
760 case JSON_OBJECT:
761 if (key == NULL)
762 key = xstrdup ("");
763 break;
764 case JSON_ARRAY_END:
765 case JSON_OBJECT_END:
766 dec_json_key (key);
767 break;
768 case JSON_TRUE:
769 val = xstrdup ("true");
770 if ((ret = (*cb) (ptr_data, key, val)))
771 goto clean;
772 ctx = json_get_context (&json, &level);
773 if (ctx != JSON_ARRAY)
774 dec_json_key (key);
775 free (val);
776 val = NULL;
777 break;
778 case JSON_FALSE:
779 val = xstrdup ("false");
780 if ((ret = (*cb) (ptr_data, key, val)))
781 goto clean;
782 ctx = json_get_context (&json, &level);
783 if (ctx != JSON_ARRAY)
784 dec_json_key (key);
785 free (val);
786 val = NULL;
787 break;
788 case JSON_NULL:
789 val = xstrdup ("-");
790 if ((ret = (*cb) (ptr_data, key, val)))
791 goto clean;
792 ctx = json_get_context (&json, &level);
793 if (ctx != JSON_ARRAY)
794 dec_json_key (key);
795 free (val);
796 val = NULL;
797 break;
798 case JSON_STRING:
799 case JSON_NUMBER:
800 ctx = json_get_context (&json, &level);
801 /* key */
802 if ((level % 2) != 0 && ctx != JSON_ARRAY) {
803 if (strlen (key) != 0)
804 ws_append_str (&key, ".");
805 ws_append_str (&key, json_get_string (&json, &len));
806 }
807 /* val */
808 else if (key && (ctx == JSON_ARRAY || ((level % 2) == 0 && ctx != JSON_ARRAY))) {
809 val = xstrdup (json_get_string (&json, &len));
810 if ((ret = (*cb) (ptr_data, key, val)))
811 goto clean;
812 if (ctx != JSON_ARRAY)
813 dec_json_key (key);
814
815 free (val);
816 val = NULL;
817 }
818 break;
819 case JSON_ERROR:
820 ret = -1;
821 goto clean;
822 break;
823 default:
824 break;
825 }
826 } while (t != JSON_DONE && t != JSON_ERROR);
827
828 clean:
829 free (val);
830 free (key);
831 json_close (&json);
832
833 return ret;
834 }
835
836 /* If specificity is supplied, then determine which value we need to
837 * append to the date format. */
838 void
set_spec_date_format(void)839 set_spec_date_format (void) {
840 if (verify_formats ())
841 return;
842
843 if (conf.is_json_log_format) {
844 if (parse_json_string (NULL, conf.log_format, ht_insert_json_logfmt) == -1)
845 FATAL ("Invalid JSON log format. Verify the syntax.");
846 }
847
848 if (conf.date_num_format)
849 free (conf.date_num_format);
850 if (conf.spec_date_time_format)
851 free (conf.spec_date_time_format);
852 if (conf.spec_date_time_num_format)
853 free (conf.spec_date_time_num_format);
854
855 if (set_date_num_format () == 0) {
856 set_spec_date_time_num_format ();
857 set_spec_date_time_format ();
858 }
859 }
860
861 /* Attempt to set the date format given a command line option
862 * argument. The supplied optarg can be either an actual format string
863 * or the enumerated value such as VCOMBINED */
864 void
set_date_format_str(const char * oarg)865 set_date_format_str (const char *oarg) {
866 char *fmt = NULL;
867 int type = get_log_format_item_enum (oarg);
868
869 /* free date format if it was previously set by set_log_format_str() */
870 if (conf.date_format)
871 free (conf.date_format);
872
873 /* type not found, use whatever was given by the user then */
874 if (type == -1) {
875 conf.date_format = unescape_str (oarg);
876 return;
877 }
878
879 /* attempt to get the format string by the enum value */
880 if ((fmt = get_selected_date_str (type)) == NULL) {
881 LOG_DEBUG (("Unable to set date format from enum: %s\n", oarg));
882 return;
883 }
884
885 conf.date_format = fmt;
886 }
887
888 /* Attempt to set the time format given a command line option
889 * argument. The supplied optarg can be either an actual format string
890 * or the enumerated value such as VCOMBINED */
891 void
set_time_format_str(const char * oarg)892 set_time_format_str (const char *oarg) {
893 char *fmt = NULL;
894 int type = get_log_format_item_enum (oarg);
895
896 /* free time format if it was previously set by set_log_format_str() */
897 if (conf.time_format)
898 free (conf.time_format);
899
900 /* type not found, use whatever was given by the user then */
901 if (type == -1) {
902 conf.time_format = unescape_str (oarg);
903 return;
904 }
905
906 /* attempt to get the format string by the enum value */
907 if ((fmt = get_selected_time_str (type)) == NULL) {
908 LOG_DEBUG (("Unable to set time format from enum: %s\n", oarg));
909 return;
910 }
911
912 conf.time_format = fmt;
913 }
914
915 /* Determine if some global flags were set through log-format. */
916 static void
contains_specifier(void)917 contains_specifier (void) {
918 conf.serve_usecs = conf.bandwidth = 0; /* flag */
919 if (!conf.log_format)
920 return;
921
922 if (strstr (conf.log_format, "%b"))
923 conf.bandwidth = 1; /* flag */
924 if (strstr (conf.log_format, "%D"))
925 conf.serve_usecs = 1; /* flag */
926 if (strstr (conf.log_format, "%T"))
927 conf.serve_usecs = 1; /* flag */
928 if (strstr (conf.log_format, "%L"))
929 conf.serve_usecs = 1; /* flag */
930 }
931
932 /* Attempt to set the log format given a command line option argument.
933 * The supplied optarg can be either an actual format string or the
934 * enumerated value such as VCOMBINED */
935 void
set_log_format_str(const char * oarg)936 set_log_format_str (const char *oarg) {
937 char *fmt = NULL;
938 int type = get_log_format_item_enum (oarg);
939
940 /* free log format if it was previously set */
941 if (conf.log_format)
942 free (conf.log_format);
943
944 if (type == -1 && is_json_log_format (oarg)) {
945 conf.is_json_log_format = 1;
946 conf.log_format = unescape_str (oarg);
947 contains_specifier (); /* set flag */
948 return;
949 }
950
951 /* type not found, use whatever was given by the user then */
952 if (type == -1) {
953 conf.log_format = unescape_str (oarg);
954 contains_specifier (); /* set flag */
955 return;
956 }
957
958 /* attempt to get the format string by the enum value */
959 if ((fmt = get_selected_format_str (type)) == NULL) {
960 LOG_DEBUG (("Unable to set log format from enum: %s\n", oarg));
961 return;
962 }
963
964 if (is_json_log_format (fmt))
965 conf.is_json_log_format = 1;
966
967 conf.log_format = unescape_str (fmt);
968 contains_specifier (); /* set flag */
969
970 /* assume we are using the default date/time formats */
971 set_time_format_str (oarg);
972 set_date_format_str (oarg);
973 free (fmt);
974 }
975