1 /**
2 * commons.c -- holds different data types
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 #if HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
40
41 #include "commons.h"
42
43 #include "error.h"
44 #include "labels.h"
45 #include "settings.h"
46 #include "util.h"
47 #include "xmalloc.h"
48
49 /* processing time */
50 time_t end_proc;
51 time_t timestamp;
52 time_t start_proc;
53
54 /* list of available modules/panels */
55 int module_list[TOTAL_MODULES] = {[0 ... TOTAL_MODULES - 1] = -1 };
56
57 /* *INDENT-OFF* */
58 /* String modules to enumerated modules */
59 static GEnum enum_modules[] = {
60 {"VISITORS" , VISITORS} ,
61 {"REQUESTS" , REQUESTS} ,
62 {"REQUESTS_STATIC" , REQUESTS_STATIC} ,
63 {"NOT_FOUND" , NOT_FOUND} ,
64 {"HOSTS" , HOSTS} ,
65 {"OS" , OS} ,
66 {"BROWSERS" , BROWSERS} ,
67 {"VISIT_TIMES" , VISIT_TIMES} ,
68 {"VIRTUAL_HOSTS" , VIRTUAL_HOSTS} ,
69 {"REFERRERS" , REFERRERS} ,
70 {"REFERRING_SITES" , REFERRING_SITES} ,
71 {"KEYPHRASES" , KEYPHRASES} ,
72 {"STATUS_CODES" , STATUS_CODES} ,
73 {"REMOTE_USER" , REMOTE_USER} ,
74 {"CACHE_STATUS" , CACHE_STATUS} ,
75 #ifdef HAVE_GEOLOCATION
76 {"GEO_LOCATION" , GEO_LOCATION} ,
77 #endif
78 {"MIME_TYPE" , MIME_TYPE} ,
79 {"TLS_TYPE" , TLS_TYPE} ,
80 };
81 /* *INDENT-ON* */
82
83 /* Get number of items per panel to parse.
84 *
85 * The number of items per panel is returned. */
86 int
get_max_choices(void)87 get_max_choices (void) {
88 char *csv = NULL, *json = NULL, *html = NULL;
89 int max = MAX_CHOICES;
90
91 /* no max choices, return defaults */
92 if (conf.max_items <= 0)
93 return conf.real_time_html ? MAX_CHOICES_RT : MAX_CHOICES;
94
95 /* TERM */
96 if (!conf.output_stdout)
97 return conf.max_items > MAX_CHOICES ? MAX_CHOICES : conf.max_items;
98
99 /* REAL-TIME STDOUT */
100 /* real time HTML, display max rt choices */
101 if (conf.real_time_html)
102 return conf.max_items > MAX_CHOICES_RT ? MAX_CHOICES_RT : conf.max_items;
103
104 /* STDOUT */
105 /* CSV - allow n amount of choices */
106 if (find_output_type (&csv, "csv", 1) == 0)
107 max = conf.max_items;
108 /* JSON - allow n amount of choices */
109 if (find_output_type (&json, "json", 1) == 0 && conf.max_items > 0)
110 max = conf.max_items;
111 /* HTML - takes priority on cases where multiple outputs were given. Note that
112 * we check either for an .html extension or we assume not extension was passed
113 * via -o and therefore we are redirecting the output to a file. */
114 if (find_output_type (&html, "html", 1) == 0 || conf.output_format_idx == 0)
115 max = conf.max_items;
116
117 free (csv);
118 free (html);
119 free (json);
120
121 return max;
122 }
123
124 /* Calculate a percentage.
125 *
126 * The percentage is returned. */
127 float
get_percentage(unsigned long long total,unsigned long long hit)128 get_percentage (unsigned long long total, unsigned long long hit) {
129 return (total == 0 ? 0 : (((float) hit) / total) * 100);
130 }
131
132 /* Display the storage being used. */
133 void
display_storage(void)134 display_storage (void) {
135 fprintf (stdout, "%s\n", BUILT_WITH_DEFHASH);
136 }
137
138 /* Display the path of the default configuration file when `-p` is not used */
139 void
display_default_config_file(void)140 display_default_config_file (void) {
141 char *path = get_config_file_path ();
142
143 if (!path) {
144 fprintf (stdout, "%s\n", ERR_NODEF_CONF_FILE);
145 fprintf (stdout, "%s `-p /path/goaccess.conf`\n", ERR_NODEF_CONF_FILE_DESC);
146 } else {
147 fprintf (stdout, "%s\n", path);
148 free (path);
149 }
150 }
151
152 /* Display the current version. */
153 void
display_version(void)154 display_version (void) {
155 fprintf (stdout, "GoAccess - %s.\n", GO_VERSION);
156 fprintf (stdout, "%s: %s\n", INFO_MORE_INFO, GO_WEBSITE);
157 fprintf (stdout, "Copyright (C) 2009-2020 by Gerardo Orellana\n");
158 fprintf (stdout, "\nBuild configure arguments:\n");
159 #ifdef DEBUG
160 fprintf (stdout, " --enable-debug\n");
161 #endif
162 #ifdef HAVE_NCURSESW_NCURSES_H
163 fprintf (stdout, " --enable-utf8\n");
164 #endif
165 #ifdef HAVE_LIBGEOIP
166 fprintf (stdout, " --enable-geoip=legacy\n");
167 #endif
168 #ifdef HAVE_LIBMAXMINDDB
169 fprintf (stdout, " --enable-geoip=mmdb\n");
170 #endif
171 #ifdef WITH_GETLINE
172 fprintf (stdout, " --with-getline\n");
173 #endif
174 #ifdef HAVE_LIBSSL
175 fprintf (stdout, " --with-openssl\n");
176 #endif
177 }
178
179 /* Get the enumerated value given a string.
180 *
181 * On error, -1 is returned.
182 * On success, the enumerated module value is returned. */
183 int
str2enum(const GEnum map[],int len,const char * str)184 str2enum (const GEnum map[], int len, const char *str) {
185 int i;
186
187 for (i = 0; i < len; ++i) {
188 if (!strcmp (str, map[i].str))
189 return map[i].idx;
190 }
191
192 return -1;
193 }
194
195 /* Get the string value given an enum.
196 *
197 * On error, -1 is returned.
198 * On success, the enumerated module value is returned. */
199 char *
enum2str(const GEnum map[],int len,int idx)200 enum2str (const GEnum map[], int len, int idx) {
201 int i;
202
203 for (i = 0; i < len; ++i) {
204 if (idx == map[i].idx)
205 return xstrdup (map[i].str);
206 }
207
208 return NULL;
209 }
210
211 /* Get the enumerated module value given a module string.
212 *
213 * On error, -1 is returned.
214 * On success, the enumerated module value is returned. */
215 int
get_module_enum(const char * str)216 get_module_enum (const char *str) {
217 return str2enum (enum_modules, ARRAY_SIZE (enum_modules), str);
218 }
219
220 /* Get the module string value given a module enum value.
221 *
222 * On error, NULL is returned.
223 * On success, the string module value is returned. */
224 char *
get_module_str(GModule module)225 get_module_str (GModule module) {
226 return enum2str (enum_modules, ARRAY_SIZE (enum_modules), module);
227 }
228
229 /* Instantiate a new GAgents structure.
230 *
231 * On success, the newly malloc'd structure is returned. */
232 GAgents *
new_gagents(uint32_t size)233 new_gagents (uint32_t size) {
234 GAgents *agents = xmalloc (sizeof (GAgents));
235 memset (agents, 0, sizeof *agents);
236
237 agents->items = xcalloc (size, sizeof (GAgentItem));
238 agents->size = size;
239 agents->idx = 0;
240
241 return agents;
242 }
243
244 /* Clean the array of agents. */
245 void
free_agents_array(GAgents * agents)246 free_agents_array (GAgents * agents) {
247 int i;
248
249 if (agents == NULL)
250 return;
251
252 /* clean stuff up */
253 for (i = 0; i < agents->idx; ++i)
254 free (agents->items[i].agent);
255 if (agents->items)
256 free (agents->items);
257 free (agents);
258 }
259
260 /* Determine if the given date format is a timestamp.
261 *
262 * If not a timestamp, 0 is returned.
263 * If it is a timestamp, 1 is returned. */
264 int
has_timestamp(const char * fmt)265 has_timestamp (const char *fmt) {
266 if (strcmp ("%s", fmt) == 0 || strcmp ("%f", fmt) == 0)
267 return 1;
268 return 0;
269 }
270
271 /* Determine if the given module is set to be enabled.
272 *
273 * If enabled, 1 is returned, else 0 is returned. */
274 int
enable_panel(GModule mod)275 enable_panel (GModule mod) {
276 int i, module;
277
278 for (i = 0; i < conf.enable_panel_idx; ++i) {
279 if ((module = get_module_enum (conf.enable_panels[i])) == -1)
280 continue;
281 if (mod == (unsigned int) module) {
282 return 1;
283 }
284 }
285
286 return 0;
287 }
288
289 /* Determine if the given module is set to be ignored.
290 *
291 * If ignored, 1 is returned, else 0 is returned. */
292 int
ignore_panel(GModule mod)293 ignore_panel (GModule mod) {
294 int i, module;
295
296 for (i = 0; i < conf.ignore_panel_idx; ++i) {
297 if ((module = get_module_enum (conf.ignore_panels[i])) == -1)
298 continue;
299 if (mod == (unsigned int) module) {
300 return 1;
301 }
302 }
303
304 return 0;
305 }
306
307 /* Get the number of available modules/panels.
308 *
309 * The number of modules available is returned. */
310 uint32_t
get_num_modules(void)311 get_num_modules (void) {
312 size_t idx = 0;
313 uint32_t num = 0;
314
315 FOREACH_MODULE (idx, module_list) {
316 num++;
317 }
318
319 return num;
320 }
321
322 /* Get the index from the module_list given a module.
323 *
324 * If the module is not within the array, -1 is returned.
325 * If the module is within the array, the index is returned. */
326 int
get_module_index(int module)327 get_module_index (int module) {
328 size_t idx = 0;
329
330 FOREACH_MODULE (idx, module_list) {
331 if (module_list[idx] == module)
332 return idx;
333 }
334
335 return -1;
336 }
337
338 /* Remove the given module from the module_list array.
339 *
340 * If the module is not within the array, 1 is returned.
341 * If the module is within the array, it is removed from the array and
342 * 0 is returned. */
343 int
remove_module(GModule module)344 remove_module (GModule module) {
345 int idx = get_module_index (module);
346 if (idx == -1)
347 return 1;
348
349 if (idx < TOTAL_MODULES - 1)
350 memmove (&module_list[idx], &module_list[idx + 1],
351 ((TOTAL_MODULES - 1) - idx) * sizeof (module_list[0]));
352 module_list[TOTAL_MODULES - 1] = -1;
353
354 return 0;
355 }
356
357 /* Find the next module given the current module.
358 *
359 * The next available module in the array is returned. */
360 int
get_next_module(GModule module)361 get_next_module (GModule module) {
362 int next = get_module_index (module) + 1;
363
364 if (next == TOTAL_MODULES || module_list[next] == -1)
365 return module_list[0];
366
367 return module_list[next];
368 }
369
370 /* Find the previous module given the current module.
371 *
372 * The previous available module in the array is returned. */
373 int
get_prev_module(GModule module)374 get_prev_module (GModule module) {
375 int i;
376 int next = get_module_index (module) - 1;
377
378 if (next >= 0 && module_list[next] != -1)
379 return module_list[next];
380
381 for (i = TOTAL_MODULES - 1; i >= 0; i--) {
382 if (module_list[i] != -1) {
383 return module_list[i];
384 }
385 }
386
387 return 0;
388 }
389
390 /* Perform some additional tasks to panels before they are being
391 * parsed.
392 *
393 * Note: This overwrites --enable-panel since it assumes there's
394 * truly nothing to do with the panel */
395 void
verify_panels(void)396 verify_panels (void) {
397 int ignore_panel_idx = conf.ignore_panel_idx;
398
399 /* Remove virtual host panel if no '%v' within log format */
400 if (!conf.log_format)
401 return;
402
403 if (!strstr (conf.log_format, "%v") && ignore_panel_idx < TOTAL_MODULES) {
404 if (str_inarray ("VIRTUAL_HOSTS", conf.ignore_panels, ignore_panel_idx) < 0)
405 remove_module (VIRTUAL_HOSTS);
406 }
407 if (!strstr (conf.log_format, "%e") && ignore_panel_idx < TOTAL_MODULES) {
408 if (str_inarray ("REMOTE_USER", conf.ignore_panels, ignore_panel_idx) < 0)
409 remove_module (REMOTE_USER);
410 }
411 if (!strstr (conf.log_format, "%C") && ignore_panel_idx < TOTAL_MODULES) {
412 if (str_inarray ("CACHE_STATUS", conf.ignore_panels, ignore_panel_idx) < 0)
413 remove_module (CACHE_STATUS);
414 }
415 if (!strstr (conf.log_format, "%M") && ignore_panel_idx < TOTAL_MODULES) {
416 if (str_inarray ("MIME_TYPE", conf.ignore_panels, ignore_panel_idx) < 0)
417 remove_module (MIME_TYPE);
418 }
419 if (!strstr (conf.log_format, "%K") && ignore_panel_idx < TOTAL_MODULES) {
420 if (str_inarray ("TLS_TYPE", conf.ignore_panels, ignore_panel_idx) < 0)
421 remove_module (TLS_TYPE);
422 }
423 #ifdef HAVE_GEOLOCATION
424 #ifdef HAVE_LIBMAXMINDDB
425 if (!conf.geoip_database && ignore_panel_idx < TOTAL_MODULES) {
426 if (str_inarray ("GEO_LOCATION", conf.ignore_panels, ignore_panel_idx) < 0)
427 remove_module (GEO_LOCATION);
428 }
429 #endif
430 #endif
431 }
432
433 /* Build an array of available modules (ignores listed panels).
434 *
435 * If there are no modules enabled, 0 is returned.
436 * On success, the first enabled module is returned. */
437 int
init_modules(void)438 init_modules (void) {
439 GModule module;
440 int i;
441
442 /* init - terminating with -1 */
443 for (module = 0; module < TOTAL_MODULES; ++module)
444 module_list[module] = -1;
445
446 for (i = 0, module = 0; module < TOTAL_MODULES; ++module) {
447 if (!ignore_panel (module) || enable_panel (module)) {
448 module_list[i++] = module;
449 }
450 }
451
452 return module_list[0] > -1 ? module_list[0] : 0;
453 }
454
455 /* Get the logs size.
456 *
457 * If log was piped (from stdin), 0 is returned.
458 * On success, it adds up all log sizes and its value is returned.
459 * if --log-size was specified, it will be returned explicitly */
460 intmax_t
get_log_sizes(void)461 get_log_sizes (void) {
462 int i;
463 off_t size = 0;
464
465 /* --log-size */
466 if (conf.log_size > 0)
467 return (intmax_t) conf.log_size;
468
469 for (i = 0; i < conf.filenames_idx; ++i) {
470 if (conf.filenames[i][0] == '-' && conf.filenames[i][1] == '\0')
471 size += 0;
472 else
473 size += file_size (conf.filenames[i]);
474 }
475
476 return (intmax_t) size;
477 }
478
479 /* Get the log sources used.
480 *
481 * On success, a newly malloc'd string containing the log source either
482 * from filename(s) and/or STDIN is returned. */
483 char *
get_log_source_str(int max_len)484 get_log_source_str (int max_len) {
485 char *str = xstrdup ("");
486 int i, len = 0;
487
488 for (i = 0; i < conf.filenames_idx; ++i) {
489 if (conf.filenames[i][0] == '-' && conf.filenames[i][1] == '\0')
490 append_str (&str, "STDIN");
491 else
492 append_str (&str, conf.filenames[i]);
493 if (i != conf.filenames_idx - 1)
494 append_str (&str, "; ");
495 }
496
497 len = strlen (str);
498 if (max_len > 0 && len > 0 && len > max_len) {
499 str[max_len - 3] = 0;
500 append_str (&str, "...");
501 }
502
503 return str;
504 }
505