You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Nginx/goaccess++/src/commons.c

505 lines
12 KiB

/**
* commons.c -- holds different data types
* ______ ___
* / ____/___ / | _____________ __________
* / / __/ __ \/ /| |/ ___/ ___/ _ \/ ___/ ___/
* / /_/ / /_/ / ___ / /__/ /__/ __(__ |__ )
* \____/\____/_/ |_\___/\___/\___/____/____/
*
* The MIT License (MIT)
* Copyright (c) 2009-2016 Gerardo Orellana <hello @ goaccess.io>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "commons.h"
#include "error.h"
#include "labels.h"
#include "settings.h"
#include "util.h"
#include "xmalloc.h"
/* processing time */
time_t end_proc;
time_t timestamp;
time_t start_proc;
/* list of available modules/panels */
int module_list[TOTAL_MODULES] = {[0 ... TOTAL_MODULES - 1] = -1 };
/* Get number of items per panel to parse.
*
* The number of items per panel is returned. */
int
get_max_choices (void)
{
char *csv = NULL, *json = NULL, *html = NULL;
int max = MAX_CHOICES;
/* no max choices, return defaults */
if (conf.max_items <= 0)
return conf.real_time_html ? MAX_CHOICES_RT : MAX_CHOICES;
/* TERM */
if (!conf.output_stdout)
return conf.max_items > MAX_CHOICES ? MAX_CHOICES : conf.max_items;
/* REAL-TIME STDOUT */
/* real time HTML, display max rt choices */
if (conf.real_time_html)
return conf.max_items > MAX_CHOICES_RT ? MAX_CHOICES_RT : conf.max_items;
/* STDOUT */
/* CSV - allow n amount of choices */
if (find_output_type (&csv, "csv", 1) == 0)
max = conf.max_items;
/* JSON - allow n amount of choices */
if (find_output_type (&json, "json", 1) == 0 && conf.max_items > 0)
max = conf.max_items;
/* HTML - takes priority on cases where multiple outputs were given. Note that
* we check either for an .html extension or we assume not extension was passed
* via -o and therefore we are redirecting the output to a file. */
if (find_output_type (&html, "html", 1) == 0 || conf.output_format_idx == 0)
max = conf.max_items > MAX_CHOICES ? MAX_CHOICES : conf.max_items;
free (csv);
free (html);
free (json);
return max;
}
/* Calculate a percentage.
*
* The percentage is returned. */
float
get_percentage (unsigned long long total, unsigned long long hit)
{
return (total == 0 ? 0 : (((float) hit) / total) * 100);
}
/* Display the storage being used. */
void
display_storage (void)
{
#ifdef TCB_BTREE
fprintf (stdout, "%s\n", BUILT_WITH_TCBTREE);
#elif TCB_MEMHASH
fprintf (stdout, "%s\n", BUILT_WITH_TCMEMHASH);
#else
fprintf (stdout, "%s\n", BUILT_WITH_DEFHASH);
#endif
}
/* Display the path of the default configuration file when `-p` is not used */
void
display_default_config_file (void)
{
char *path = get_config_file_path ();
if (!path) {
fprintf (stdout, "%s\n", ERR_NODEF_CONF_FILE);
fprintf (stdout, "%s `-p /path/goaccess.conf`\n", ERR_NODEF_CONF_FILE_DESC);
} else {
fprintf (stdout, "%s\n", path);
free (path);
}
}
/* Display the current version. */
void
display_version (void)
{
fprintf (stdout, "GoAccess - %s.\n", GO_VERSION);
fprintf (stdout, "%s: http://goaccess.io\n", INFO_MORE_INFO);
fprintf (stdout, "Copyright (C) 2009-2016 by Gerardo Orellana\n");
fprintf (stdout, "\nBuild configure arguments:\n");
#ifdef DEBUG
fprintf (stdout, " --enable-debug\n");
#endif
#ifdef HAVE_NCURSESW_NCURSES_H
fprintf (stdout, " --enable-utf8\n");
#endif
#ifdef HAVE_LIBGEOIP
fprintf (stdout, " --enable-geoip=legacy\n");
#endif
#ifdef HAVE_LIBMAXMINDDB
fprintf (stdout, " --enable-geoip=mmdb\n");
#endif
#ifdef TCB_MEMHASH
fprintf (stdout, " --enable-tcb=memhash\n");
#endif
#ifdef TCB_BTREE
fprintf (stdout, " --enable-tcb=btree\n");
#endif
#if defined(TCB_MEMHASH) || defined(TCB_BTREE)
#ifndef HAVE_ZLIB
fprintf (stdout, " --disable-zlib\n");
#endif
#ifndef HAVE_BZ2
fprintf (stdout, " --disable-bzip\n");
#endif
#endif
#ifdef WITH_GETLINE
fprintf (stdout, " --with-getline\n");
#endif
#ifdef HAVE_LIBSSL
fprintf (stdout, " --with-openssl\n");
#endif
}
/* Get the enumerated value given a string.
*
* On error, -1 is returned.
* On success, the enumerated module value is returned. */
int
str2enum (const GEnum map[], int len, const char *str)
{
int i;
for (i = 0; i < len; ++i) {
if (!strcmp (str, map[i].str))
return map[i].idx;
}
return -1;
}
/* Get the enumerated module value given a module string.
*
* On error, -1 is returned.
* On success, the enumerated module value is returned. */
int
get_module_enum (const char *str)
{
/* *INDENT-OFF* */
/* String modules to enumerated modules */
GEnum enum_modules[] = {
{"VISITORS" , VISITORS} ,
{"REQUESTS" , REQUESTS} ,
{"REQUESTS_STATIC" , REQUESTS_STATIC} ,
{"NOT_FOUND" , NOT_FOUND} ,
{"HOSTS" , HOSTS} ,
{"OS" , OS} ,
{"BROWSERS" , BROWSERS} ,
{"VISIT_TIMES" , VISIT_TIMES} ,
{"VIRTUAL_HOSTS" , VIRTUAL_HOSTS} ,
{"REFERRERS" , REFERRERS} ,
{"REFERRING_SITES" , REFERRING_SITES} ,
{"KEYPHRASES" , KEYPHRASES} ,
{"STATUS_CODES" , STATUS_CODES} ,
{"REMOTE_USER" , REMOTE_USER} ,
#ifdef HAVE_GEOLOCATION
{"GEO_LOCATION" , GEO_LOCATION} ,
#endif
};
/* *INDENT-ON* */
return str2enum (enum_modules, ARRAY_SIZE (enum_modules), str);
}
/* Instantiate a new GAgents structure.
*
* On success, the newly malloc'd structure is returned. */
GAgents *
new_gagents (void)
{
GAgents *agents = xmalloc (sizeof (GAgents));
memset (agents, 0, sizeof *agents);
return agents;
}
/* Instantiate a new GAgentItem structure.
*
* On success, the newly malloc'd structure is returned. */
GAgentItem *
new_gagent_item (uint32_t size)
{
GAgentItem *item = xcalloc (size, sizeof (GAgentItem));
return item;
}
/* Clean the array of agents. */
void
free_agents_array (GAgents * agents)
{
int i;
if (agents == NULL)
return;
/* clean stuff up */
for (i = 0; i < agents->size; ++i)
free (agents->items[i].agent);
if (agents->items)
free (agents->items);
free (agents);
}
/* Determine if the given date format is a timestamp.
*
* On error, 0 is returned.
* On success, 1 is returned. */
int
has_timestamp (const char *fmt)
{
if (strcmp ("%s", fmt) == 0 || strcmp ("%f", fmt) == 0)
return 1;
return 0;
}
/* Determine if the given module is set to be enabled.
*
* If enabled, 1 is returned, else 0 is returned. */
int
enable_panel (GModule mod)
{
int i, module;
for (i = 0; i < conf.enable_panel_idx; ++i) {
if ((module = get_module_enum (conf.enable_panels[i])) == -1)
continue;
if (mod == (unsigned int) module) {
return 1;
}
}
return 0;
}
/* Determine if the given module is set to be ignored.
*
* If ignored, 1 is returned, else 0 is returned. */
int
ignore_panel (GModule mod)
{
int i, module;
for (i = 0; i < conf.ignore_panel_idx; ++i) {
if ((module = get_module_enum (conf.ignore_panels[i])) == -1)
continue;
if (mod == (unsigned int) module) {
return 1;
}
}
return 0;
}
/* Get the number of available modules/panels.
*
* The number of modules available is returned. */
uint32_t
get_num_modules (void)
{
size_t idx = 0;
uint32_t num = 0;
FOREACH_MODULE (idx, module_list) {
num++;
}
return num;
}
/* Get the index from the module_list given a module.
*
* If the module is not within the array, -1 is returned.
* If the module is within the array, the index is returned. */
int
get_module_index (int module)
{
size_t idx = 0;
FOREACH_MODULE (idx, module_list) {
if (module_list[idx] == module)
return idx;
}
return -1;
}
/* Remove the given module from the module_list array.
*
* If the module is not within the array, 1 is returned.
* If the module is within the array, it is removed from the array and
* 0 is returned. */
int
remove_module (GModule module)
{
int idx = get_module_index (module);
if (idx == -1)
return 1;
if (idx < TOTAL_MODULES - 1)
memmove (&module_list[idx], &module_list[idx + 1],
((TOTAL_MODULES - 1) - idx) * sizeof (module_list[0]));
module_list[TOTAL_MODULES - 1] = -1;
return 0;
}
/* Find the next module given the current module.
*
* The next available module in the array is returned. */
int
get_next_module (GModule module)
{
int next = get_module_index (module) + 1;
if (next == TOTAL_MODULES || module_list[next] == -1)
return module_list[0];
return module_list[next];
}
/* Find the previous module given the current module.
*
* The previous available module in the array is returned. */
int
get_prev_module (GModule module)
{
int i;
int next = get_module_index (module) - 1;
if (next >= 0 && module_list[next] != -1)
return module_list[next];
for (i = TOTAL_MODULES - 1; i >= 0; i--) {
if (module_list[i] != -1) {
return module_list[i];
}
}
return 0;
}
/* Perform some additional tasks to panels before they are being
* parsed.
*
* Note: This overwrites --enable-panel since it assumes there's
* truly nothing to do with the panel */
void
verify_panels (void)
{
int ignore_panel_idx = conf.ignore_panel_idx;
/* Remove virtual host panel if no '%v' within log format */
if (!conf.log_format)
return;
if (!strstr (conf.log_format, "%v") && ignore_panel_idx < TOTAL_MODULES) {
if (str_inarray ("VIRTUAL_HOSTS", conf.ignore_panels, ignore_panel_idx) < 0)
remove_module (VIRTUAL_HOSTS);
}
if (!strstr (conf.log_format, "%e") && ignore_panel_idx < TOTAL_MODULES) {
if (str_inarray ("REMOTE_USER", conf.ignore_panels, ignore_panel_idx) < 0)
remove_module (REMOTE_USER);
}
}
/* Build an array of available modules (ignores listed panels).
*
* If there are no modules enabled, 0 is returned.
* On success, the first enabled module is returned. */
int
init_modules (void)
{
GModule module;
int i;
/* init - terminating with -1 */
for (module = 0; module < TOTAL_MODULES; ++module)
module_list[module] = -1;
for (i = 0, module = 0; module < TOTAL_MODULES; ++module) {
if (!ignore_panel (module) || enable_panel (module)) {
module_list[i++] = module;
}
}
return module_list[0] > -1 ? module_list[0] : 0;
}
/* Get the logs size.
*
* If log was piped (from stdin), 0 is returned.
* On success, it adds up all log sizes and its value is returned.
* if --log-size was specified, it will be returned explicitly */
intmax_t
get_log_sizes (void)
{
int i;
off_t size = 0;
/* --log-size */
if (conf.log_size > 0)
return (intmax_t) conf.log_size;
for (i = 0; i < conf.filenames_idx; ++i) {
if (conf.filenames[i][0] == '-' && conf.filenames[i][1] == '\0')
size += 0;
else
size += file_size (conf.filenames[i]);
}
return (intmax_t) size;
}
/* Get the log sources used.
*
* On success, a newly malloc'd string containing the log source either
* from filename(s) and/or STDIN is returned. */
char *
get_log_source_str (int max_len)
{
char *str = xstrdup ("");
int i, len = 0;
for (i = 0; i < conf.filenames_idx; ++i) {
if (conf.filenames[i][0] == '-' && conf.filenames[i][1] == '\0')
append_str (&str, "STDIN");
else
append_str (&str, conf.filenames[i]);
if (i != conf.filenames_idx - 1)
append_str (&str, "; ");
}
len = strlen (str);
if (max_len > 0 && len > 0 && len > max_len) {
str[max_len - 3] = 0;
append_str (&str, "...");
}
return str;
}