From 1ae49c7bafcbfc05b79e4e31d1056d86389b6a87 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 18 Jan 2023 04:22:50 +0100 Subject: [PATCH] Add /api/logs/{ftl,http,ph7} Signed-off-by: DL6ER --- src/CMakeLists.txt | 2 - src/api/api.c | 85 +++++++++++++------------ src/api/api.h | 2 +- src/api/config.c | 20 ------ src/api/docs/content/specs/logs.yaml | 93 ++++++++++++++++++++++++++++ src/api/docs/content/specs/main.yaml | 9 +++ src/api/logs.c | 20 +++--- src/api/stats.c | 11 +--- src/api/stats_database.c | 4 +- src/args.c | 5 +- src/args.h | 1 - src/dnsmasq_interface.c | 15 +++-- src/enums.h | 13 ++-- src/fifo.c | 51 --------------- src/fifo.h | 33 ---------- src/log.c | 84 ++++++++++++++++++++----- src/log.h | 22 ++++++- src/ph7/ph7.c | 6 +- src/shmem.c | 5 +- src/webserver/http-common.h | 14 ++++- src/webserver/ph7.c | 16 ++--- src/webserver/webserver.c | 8 +-- 22 files changed, 299 insertions(+), 220 deletions(-) delete mode 100644 src/fifo.c delete mode 100644 src/fifo.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da01f579..30812ece 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -121,8 +121,6 @@ set(sources dhcp-discover.h dnsmasq_interface.c dnsmasq_interface.h - fifo.c - fifo.h edns0.c edns0.h enums.h diff --git a/src/api/api.c b/src/api/api.c index 95e0ceb8..c8ec6152 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -27,51 +27,54 @@ static struct { const char *uri; const char *parameters; int (*func)(struct ftl_conn *api); - const bool opts[2]; + struct api_options opts; bool require_auth; enum http_method methods; } api_request[] = { // URI ARGUMENTS FUNCTION OPTIONS AUTH ALLOWED METHODS // Note: The order of appearance matters here, more specific URIs have to // appear *before* less specific URIs: 1. "/a/b/c", 2. "/a/b", 3. "/a" - { "/api/dns/blocking", "", api_dns_blocking, { false, false }, true, HTTP_GET | HTTP_POST }, - { "/api/dns/cache", "", api_dns_cache, { false, false }, true, HTTP_GET }, - { "/api/dns/port", "", api_dns_port, { false, false }, true, HTTP_GET }, - { "/api/dns/entries", "", api_dns_entries, { false, false }, true, HTTP_GET }, - { "/api/dns/entries", "/{ip}/{host}", api_dns_entries, { false, false }, true, HTTP_PUT | HTTP_DELETE }, - { "/api/clients", "/{client}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, - { "/api/domains", "/{type}/{kind}/{domain}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, - { "/api/groups", "/{name}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, - { "/api/lists", "/{list}", api_list, { false, false }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, - { "/api/ftl/client", "", api_ftl_client, { false, false }, false, HTTP_GET }, - { "/api/ftl/sysinfo", "", api_ftl_sysinfo, { false, false }, true, HTTP_GET }, - { "/api/ftl/dbinfo", "", api_ftl_dbinfo, { false, false }, true, HTTP_GET }, - { "/api/logs/dnsmasq", "", api_logs_dnsmasq, { false, false }, true, HTTP_GET }, - { "/api/history/clients", "", api_history_clients, { false, false }, true, HTTP_GET }, - { "/api/history/database/clients", "", api_history_database_clients, { false, false }, true, HTTP_GET }, - { "/api/history/database", "", api_history_database, { false, false }, true, HTTP_GET }, - { "/api/history", "", api_history, { false, false }, true, HTTP_GET }, - { "/api/queries/suggestions", "", api_queries_suggestions, { false, false }, true, HTTP_GET }, - { "/api/queries", "", api_queries, { false, false }, true, HTTP_GET }, - { "/api/stats/summary", "", api_stats_summary, { false, false }, true, HTTP_GET }, - { "/api/stats/query_types", "", api_stats_query_types, { false, false }, true, HTTP_GET }, - { "/api/stats/upstreams", "", api_stats_upstreams, { false, false }, true, HTTP_GET }, - { "/api/stats/top_domains", "", api_stats_top_domains, { false, false }, true, HTTP_GET }, - { "/api/stats/top_clients", "", api_stats_top_clients, { false, false }, true, HTTP_GET }, - { "/api/stats/recent_blocked", "", api_stats_recentblocked, { false, false }, true, HTTP_GET }, - { "/api/stats/database/top_domains", "", api_stats_database_top_items, { false, true }, true, HTTP_GET }, - { "/api/stats/database/top_clients", "", api_stats_database_top_items, { false, false }, true, HTTP_GET }, - { "/api/stats/database/summary", "", api_stats_database_summary, { false, false }, true, HTTP_GET }, - { "/api/stats/database/query_types", "", api_stats_database_query_types, { false, false }, true, HTTP_GET }, - { "/api/stats/database/upstreams", "", api_stats_database_upstreams, { false, false }, true, HTTP_GET }, - { "/api/version", "", api_version, { false, false }, true, HTTP_GET }, - { "/api/auth", "", api_auth, { false, false }, false, HTTP_GET | HTTP_POST | HTTP_DELETE }, - { "/api/config", "", api_config, { false, false }, true, HTTP_GET | HTTP_PATCH }, - { "/api/network/gateway", "", api_network_gateway, { false, false }, true, HTTP_GET }, - { "/api/network/interfaces", "", api_network_interfaces, { false, false }, true, HTTP_GET }, - { "/api/network/devices", "", api_network_devices, { false, false }, true, HTTP_GET }, - { "/api/endpoints", "", api_endpoints, { false, false }, true, HTTP_GET }, - { "/api/docs", "", api_docs, { false, false }, false, HTTP_GET }, + { "/api/dns/blocking", "", api_dns_blocking, { false, 0 }, true, HTTP_GET | HTTP_POST }, + { "/api/dns/cache", "", api_dns_cache, { false, 0 }, true, HTTP_GET }, + { "/api/dns/port", "", api_dns_port, { false, 0 }, true, HTTP_GET }, + { "/api/dns/entries", "", api_dns_entries, { false, 0 }, true, HTTP_GET }, + { "/api/dns/entries", "/{ip}/{host}", api_dns_entries, { false, 0 }, true, HTTP_PUT | HTTP_DELETE }, + { "/api/clients", "/{client}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, + { "/api/domains", "/{type}/{kind}/{domain}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, + { "/api/groups", "/{name}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, + { "/api/lists", "/{list}", api_list, { false, 0 }, true, HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_DELETE }, + { "/api/ftl/client", "", api_ftl_client, { false, 0 }, false, HTTP_GET }, + { "/api/ftl/sysinfo", "", api_ftl_sysinfo, { false, 0 }, true, HTTP_GET }, + { "/api/ftl/dbinfo", "", api_ftl_dbinfo, { false, 0 }, true, HTTP_GET }, + { "/api/logs/dnsmasq", "", api_logs, { false, FIFO_DNSMASQ }, true, HTTP_GET }, + { "/api/logs/ftl", "", api_logs, { false, FIFO_FTL }, true, HTTP_GET }, + { "/api/logs/http", "", api_logs, { false, FIFO_CIVETWEB }, true, HTTP_GET }, + { "/api/logs/ph7", "", api_logs, { false, FIFO_PH7 }, true, HTTP_GET }, + { "/api/history/clients", "", api_history_clients, { false, 0 }, true, HTTP_GET }, + { "/api/history/database/clients", "", api_history_database_clients, { false, 0 }, true, HTTP_GET }, + { "/api/history/database", "", api_history_database, { false, 0 }, true, HTTP_GET }, + { "/api/history", "", api_history, { false, 0 }, true, HTTP_GET }, + { "/api/queries/suggestions", "", api_queries_suggestions, { false, 0 }, true, HTTP_GET }, + { "/api/queries", "", api_queries, { false, 0 }, true, HTTP_GET }, + { "/api/stats/summary", "", api_stats_summary, { false, 0 }, true, HTTP_GET }, + { "/api/stats/query_types", "", api_stats_query_types, { false, 0 }, true, HTTP_GET }, + { "/api/stats/upstreams", "", api_stats_upstreams, { false, 0 }, true, HTTP_GET }, + { "/api/stats/top_domains", "", api_stats_top_domains, { false, 0 }, true, HTTP_GET }, + { "/api/stats/top_clients", "", api_stats_top_clients, { false, 0 }, true, HTTP_GET }, + { "/api/stats/recent_blocked", "", api_stats_recentblocked, { false, 0 }, true, HTTP_GET }, + { "/api/stats/database/top_domains", "", api_stats_database_top_items, { true, 0 }, true, HTTP_GET }, + { "/api/stats/database/top_clients", "", api_stats_database_top_items, { false, 0 }, true, HTTP_GET }, + { "/api/stats/database/summary", "", api_stats_database_summary, { false, 0 }, true, HTTP_GET }, + { "/api/stats/database/query_types", "", api_stats_database_query_types, { false, 0 }, true, HTTP_GET }, + { "/api/stats/database/upstreams", "", api_stats_database_upstreams, { false, 0 }, true, HTTP_GET }, + { "/api/version", "", api_version, { false, 0 }, true, HTTP_GET }, + { "/api/auth", "", api_auth, { false, 0 }, false, HTTP_GET | HTTP_POST | HTTP_DELETE }, + { "/api/config", "", api_config, { false, 0 }, true, HTTP_GET | HTTP_PATCH }, + { "/api/network/gateway", "", api_network_gateway, { false, 0 }, true, HTTP_GET }, + { "/api/network/interfaces", "", api_network_interfaces, { false, 0 }, true, HTTP_GET }, + { "/api/network/devices", "", api_network_devices, { false, 0 }, true, HTTP_GET }, + { "/api/endpoints", "", api_endpoints, { false, 0 }, true, HTTP_GET }, + { "/api/docs", "", api_docs, { false, 0 }, false, HTTP_GET }, }; int api_handler(struct mg_connection *conn, void *ignored) @@ -85,7 +88,7 @@ int api_handler(struct mg_connection *conn, void *ignored) NULL, { false, NULL, NULL, 0u }, { false }, - { false, false } + { false, 0 } }; // Allocate memory for the payload @@ -119,7 +122,7 @@ int api_handler(struct mg_connection *conn, void *ignored) if((api.item = startsWith(api_request[i].uri, &api)) != NULL) { // Copy options to API struct - memcpy(api.opts, api_request[i].opts, sizeof(api.opts)); + memcpy(&api.opts, &api_request[i].opts, sizeof(api.opts)); // Verify requesting client is allowed to see this ressource if(api_request[i].require_auth && check_client_auth(&api) == API_AUTH_UNAUTHORIZED) diff --git a/src/api/api.h b/src/api/api.h index 4560636c..8cfebe12 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -54,7 +54,7 @@ int get_system_obj(struct ftl_conn *api, cJSON *system); int api_config(struct ftl_conn *api); // Log methods -int api_logs_dnsmasq(struct ftl_conn *api); +int api_logs(struct ftl_conn *api); // Network methods int api_network_gateway(struct ftl_conn *api); diff --git a/src/api/config.c b/src/api/config.c index d0cc1caa..a7a345dd 100644 --- a/src/api/config.c +++ b/src/api/config.c @@ -12,30 +12,10 @@ #include "webserver/http-common.h" #include "webserver/json_macros.h" #include "api/api.h" -// struct fifologData -#include "fifo.h" -// sysinfo() -#include -// get_blockingstatus() -#include "setupVars.h" -// counters -#include "shmem.h" -// get_FTL_db_filesize() -#include "files.h" -// get_sqlite3_version() -#include "database/common.h" -// get_number_of_queries_in_DB() -#include "database/query-table.h" -// getgrgid() -#include // config struct #include "config/config.h" // struct clientsData #include "datastructure.h" -// Routing information and flags -#include -// Interate through directories -#include // INT_MIN, INT_MAX, ... #include // writeFTLtoml() diff --git a/src/api/docs/content/specs/logs.yaml b/src/api/docs/content/specs/logs.yaml index 2c2d93bc..db8491a0 100644 --- a/src/api/docs/content/specs/logs.yaml +++ b/src/api/docs/content/specs/logs.yaml @@ -11,6 +11,99 @@ components: description: | This API hook returns content from the log of the embedded DNS resolver `dnsmasq`. + Every successful request will return a `nextID`. + This ID can be used on the next request to only get lines which were added *after* the last request. + This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary. + The expected behavior for an immediate re-request of a log line with the same ID is an empty response. + As soon as the next message arrived, this will be included in your request and `nextID` is incremented by one. + parameters: + - $ref: 'logs.yaml#/components/parameters/logs/dnsmasq/nextID' + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: + - $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/log' + - $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/nextID' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: 'common.yaml#/components/errors/unauthorized' + ftl: + get: + summary: Get DNS log content + tags: + - "FTL information" + operationId: "get_dns_log" + description: | + This API hook returns content from the log of FTL. + + Every successful request will return a `nextID`. + This ID can be used on the next request to only get lines which were added *after* the last request. + This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary. + The expected behavior for an immediate re-request of a log line with the same ID is an empty response. + As soon as the next message arrived, this will be included in your request and `nextID` is incremented by one. + parameters: + - $ref: 'logs.yaml#/components/parameters/logs/dnsmasq/nextID' + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: + - $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/log' + - $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/nextID' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: 'common.yaml#/components/errors/unauthorized' + http: + get: + summary: Get DNS log content + tags: + - "FTL information" + operationId: "get_dns_log" + description: | + This API hook returns content from the log of the embedded CivetWeb HTTP server. + + Every successful request will return a `nextID`. + This ID can be used on the next request to only get lines which were added *after* the last request. + This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary. + The expected behavior for an immediate re-request of a log line with the same ID is an empty response. + As soon as the next message arrived, this will be included in your request and `nextID` is incremented by one. + parameters: + - $ref: 'logs.yaml#/components/parameters/logs/dnsmasq/nextID' + responses: + '200': + description: OK + content: + application/json: + schema: + allOf: + - $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/log' + - $ref: 'logs.yaml#/components/schemas/logs/dnsmasq/nextID' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: 'common.yaml#/components/errors/unauthorized' + ph7: + get: + summary: Get DNS log content + tags: + - "FTL information" + operationId: "get_dns_log" + description: | + This API hook returns content from the log of the embedded PH7 engine. + Every successful request will return a `nextID`. This ID can be used on the next request to only get lines which were added *after* the last request. This makes periodic polling for new log lines easy as no check for duplicated log lines is necessary. diff --git a/src/api/docs/content/specs/main.yaml b/src/api/docs/content/specs/main.yaml index 631a1c0d..05f7b021 100644 --- a/src/api/docs/content/specs/main.yaml +++ b/src/api/docs/content/specs/main.yaml @@ -153,6 +153,15 @@ paths: /logs/dnsmasq: $ref: 'logs.yaml#/components/paths/logs/dnsmasq' + /logs/ftl: + $ref: 'logs.yaml#/components/paths/logs/ftl' + + /logs/http: + $ref: 'logs.yaml#/components/paths/logs/http' + + /logs/ph7: + $ref: 'logs.yaml#/components/paths/logs/ph7' + /endpoints: $ref: 'endpoints.yaml#/components/paths/endpoints' diff --git a/src/api/logs.c b/src/api/logs.c index 8b72a7dc..54400148 100644 --- a/src/api/logs.c +++ b/src/api/logs.c @@ -13,11 +13,11 @@ #include "webserver/json_macros.h" #include "api/api.h" // struct fifologData -#include "fifo.h" +#include "log.h" // fifologData is allocated in shared memory for cross-fork compatibility fifologData *fifo_log = NULL; -int api_logs_dnsmasq(struct ftl_conn *api) +int api_logs(struct ftl_conn *api) { unsigned int start = 0u; if(api->request->query_string != NULL) @@ -26,22 +26,22 @@ int api_logs_dnsmasq(struct ftl_conn *api) unsigned int nextID; if(get_uint_var(api->request->query_string, "nextID", &nextID)) { - if(nextID >= fifo_log->next_id) + if(nextID >= fifo_log->logs[api->opts.which].next_id) { // Do not return any data start = LOG_SIZE; } - else if((fifo_log->next_id > LOG_SIZE) && nextID < (fifo_log->next_id) - LOG_SIZE) + else if((fifo_log->logs[api->opts.which].next_id > LOG_SIZE) && nextID < (fifo_log->logs[api->opts.which].next_id) - LOG_SIZE) { // Requested an ID smaller than the lowest one we have // We return the entire buffer start = 0u; } - else if(fifo_log->next_id >= LOG_SIZE) + else if(fifo_log->logs[api->opts.which].next_id >= LOG_SIZE) { // Reply with partial buffer, measure from the end // (the log is full) - start = LOG_SIZE - (fifo_log->next_id - nextID); + start = LOG_SIZE - (fifo_log->logs[api->opts.which].next_id - nextID); } else { @@ -57,19 +57,19 @@ int api_logs_dnsmasq(struct ftl_conn *api) cJSON *log = JSON_NEW_ARRAY(); for(unsigned int i = start; i < LOG_SIZE; i++) { - if(fifo_log->timestamp[i] < 1.0) + if(fifo_log->logs[api->opts.which].timestamp[i] < 1.0) { // Uninitialized buffer entry break; } cJSON *entry = JSON_NEW_OBJECT(); - JSON_ADD_NUMBER_TO_OBJECT(entry, "timestamp", fifo_log->timestamp[i]); - JSON_REF_STR_IN_OBJECT(entry, "message", fifo_log->message[i]); + JSON_ADD_NUMBER_TO_OBJECT(entry, "timestamp", fifo_log->logs[api->opts.which].timestamp[i]); + JSON_REF_STR_IN_OBJECT(entry, "message", fifo_log->logs[api->opts.which].message[i]); JSON_ADD_ITEM_TO_ARRAY(log, entry); } JSON_ADD_ITEM_TO_OBJECT(json, "log", log); - JSON_ADD_NUMBER_TO_OBJECT(json, "nextID", fifo_log->next_id); + JSON_ADD_NUMBER_TO_OBJECT(json, "nextID", fifo_log->logs[api->opts.which].next_id); // Send data JSON_SEND_OBJECT(json); diff --git a/src/api/stats.c b/src/api/stats.c index 9acd5142..174cf17c 100644 --- a/src/api/stats.c +++ b/src/api/stats.c @@ -116,9 +116,6 @@ int api_stats_top_domains(struct ftl_conn *api) int temparray[counters->domains][2], count = 10; bool audit = false; - // Get options from API struct - bool blocked = api->opts[0]; // Can be overwritten by query string - // Exit before processing any data if requested via config setting if(config.misc.privacylevel.v.privacy_level >= PRIVACY_HIDE_DOMAINS) { @@ -133,7 +130,8 @@ int api_stats_top_domains(struct ftl_conn *api) JSON_SEND_OBJECT(json); } - // /api/stats/top_domains?blocked=true is allowed as well + bool blocked = false; // Can be overwritten by query string + // /api/stats/top_domains?blocked=true if(api->request->query_string != NULL) { // Should blocked clients be shown? @@ -267,9 +265,6 @@ int api_stats_top_clients(struct ftl_conn *api) int temparray[counters->clients][2], count = 10; bool includezeroclients = false; - // Get options from API struct - bool blocked = api->opts[0]; // Can be overwritten by query string - // Exit before processing any data if requested via config setting if(config.misc.privacylevel.v.privacy_level >= PRIVACY_HIDE_DOMAINS_CLIENTS) { @@ -284,7 +279,7 @@ int api_stats_top_clients(struct ftl_conn *api) JSON_SEND_OBJECT(json); } - // /api/stats/top_clients9?blocked=true is allowed as well + bool blocked = false; // /api/stats/top_clients?blocked=true if(api->request->query_string != NULL) { // Should blocked clients be shown? diff --git a/src/api/stats_database.c b/src/api/stats_database.c index a4b1907f..85a3bb5b 100644 --- a/src/api/stats_database.c +++ b/src/api/stats_database.c @@ -180,8 +180,8 @@ int api_stats_database_top_items(struct ftl_conn *api) double from = 0.0, until = 0.0; // Get options from API struct - bool blocked = api->opts[0]; // Can be overwritten by query string - const bool domains = api->opts[1]; + bool blocked = false; // Can be overwritten by query string + const bool domains = api->opts.domains; // Get parameters from query string if(api->request->query_string != NULL) diff --git a/src/args.c b/src/args.c index aaa3ef1d..980faddc 100644 --- a/src/args.c +++ b/src/args.c @@ -50,7 +50,6 @@ extern void print_dnsmasq_version(const char *yellow, const char *green, const c extern int sqlite3_shell_main(int argc, char **argv); bool dnsmasq_debug = false; -bool no_ftl_logging = false; bool daemonmode = true, cli_mode = false; int argc_dnsmasq = 0; const char** argv_dnsmasq = NULL; @@ -244,7 +243,7 @@ void parse_args(int argc, char* argv[]) const char *arg[2]; arg[0] = ""; arg[1] = "--test"; - no_ftl_logging = true; + log_ctrl(false, true); exit(main_dnsmasq(2, arg)); } @@ -259,7 +258,7 @@ void parse_args(int argc, char* argv[]) sprintf(filename, "--conf-file=%s", argv[2]); arg[1] = filename; arg[2] = "--test"; - no_ftl_logging = true; + log_ctrl(false, true); exit(main_dnsmasq(3, arg)); } diff --git a/src/args.h b/src/args.h index 879f50d5..57371e4e 100644 --- a/src/args.h +++ b/src/args.h @@ -15,7 +15,6 @@ void parse_args(int argc, char* argv[]); extern bool daemonmode, cli_mode, dnsmasq_debug; extern int argc_dnsmasq; extern const char ** argv_dnsmasq; -extern bool no_ftl_logging; const char *cli_tick(void) __attribute__ ((pure)); const char *cli_cross(void) __attribute__ ((pure)); diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index ca96cae0..8424e397 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -29,6 +29,7 @@ #include "capabilities.h" #include "resolve.h" #include "files.h" +// add_to_fifo_buffer() u.a. #include "log.h" // Prototype of getCacheInformation() #include "api/api.h" @@ -47,8 +48,6 @@ #include "api/api_helper.h" // logg_rate_limit_message() #include "database/message-table.h" -// add_to_dnsmasq_log_fifo_buffer() -#include "fifo.h" // http_init() #include "webserver/webserver.h" // type struct sqlite3_stmt_vec @@ -3293,10 +3292,14 @@ static void _query_set_dnssec(queriesData *query, const enum dnssec_status dnsse // Add dnsmasq log line to internal FIFO buffer (can be queried via the API) void FTL_dnsmasq_log(const char *payload, const int length) { - // e.g. on config testing - if(no_ftl_logging) - return; - add_to_dnsmasq_log_fifo_buffer(payload, length); + // Lock SHM + lock_shm(); + + // Add to FIFO buffer + add_to_fifo_buffer(FIFO_DNSMASQ, payload, length); + + // Unlock SHM + unlock_shm(); } // Check sizes of all important in-memory objects. This routine returns the number of diff --git a/src/enums.h b/src/enums.h index b9b9a9ef..1e94d0c5 100644 --- a/src/enums.h +++ b/src/enums.h @@ -209,11 +209,6 @@ enum timers { LAST_TIMER } __attribute__ ((packed)); -enum web_code { - HTTP_INFO, - PH7_ERROR -} __attribute__ ((packed)); - enum refresh_hostnames { REFRESH_ALL, REFRESH_IPV4_ONLY, @@ -287,4 +282,12 @@ enum listening_mode { LISTEN_BIND } __attribute__ ((packed)); +enum fifo_logs { + FIFO_FTL = 1, + FIFO_DNSMASQ, + FIFO_CIVETWEB, + FIFO_PH7, + FIFO_MAX +} __attribute__ ((packed)); + #endif // ENUMS_H diff --git a/src/fifo.c b/src/fifo.c deleted file mode 100644 index 85262d62..00000000 --- a/src/fifo.c +++ /dev/null @@ -1,51 +0,0 @@ -/* Pi-hole: A black hole for Internet advertisements -* (c) 2019 Pi-hole, LLC (https://pi-hole.net) -* Network-wide ad blocking via your own hardware. -* -* FTL Engine -* dnsmasq FIFO log for Pi-hole's API -* -* This file is copyright under the latest version of the EUPL. -* Please see LICENSE file for your rights under this license. */ - -#include "fifo.h" -// {un,}lock_shm() -#include "shmem.h" -// double_time() -#include "log.h" - -void add_to_dnsmasq_log_fifo_buffer(const char *payload, const size_t length) -{ - // Lock SHM - lock_shm(); - - unsigned int idx = fifo_log->next_id++; - if(idx >= LOG_SIZE) - { - // Log is full, move everything one slot forward to make space for a new record at the end - // This pruges the oldest message from the list (it is overwritten by the second message) - memmove(&fifo_log->message[0][0], &fifo_log->message[1][0], (LOG_SIZE - 1u) * MAX_MSG_FIFO); - memmove(&fifo_log->timestamp[0], &fifo_log->timestamp[1], (LOG_SIZE - 1u) * sizeof(fifo_log->timestamp[0])); - idx = LOG_SIZE - 1u; - } - - // Copy relevant string into temporary buffer - size_t copybytes = length < MAX_MSG_FIFO ? length : MAX_MSG_FIFO; - memcpy(fifo_log->message[idx], payload, copybytes); - - // Zero-terminate buffer, truncate newline if found - if(fifo_log->message[idx][copybytes - 1u] == '\n') - { - fifo_log->message[idx][copybytes - 1u] = '\0'; - } - else - { - fifo_log->message[idx][copybytes] = '\0'; - } - - // Set timestamp - fifo_log->timestamp[idx] = double_time(); - - // Unlock SHM - unlock_shm(); -} \ No newline at end of file diff --git a/src/fifo.h b/src/fifo.h deleted file mode 100644 index 7066b447..00000000 --- a/src/fifo.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Pi-hole: A black hole for Internet advertisements -* (c) 2019 Pi-hole, LLC (https://pi-hole.net) -* Network-wide ad blocking via your own hardware. -* -* FTL Engine -* dnsmasq FIFO log prototypes -* -* This file is copyright under the latest version of the EUPL. -* Please see LICENSE file for your rights under this license. */ -#ifndef API_FTL_H -#define API_FTL_H - -#include "FTL.h" - -/* From RFC 3164 */ -#define MAX_MSG_FIFO 1024u - -// How many messages do we keep in memory (FIFO message buffer)? -// This number multiplied by MAX_MSG_FIFO (see above) gives the total buffer size -// Defaults to 128 [use 128 KB of memory for the log] -#define LOG_SIZE 128u - -void add_to_dnsmasq_log_fifo_buffer(const char *payload, const size_t length); - -typedef struct { - unsigned int next_id; - double timestamp[LOG_SIZE]; - char message[LOG_SIZE][MAX_MSG_FIFO]; -} fifologData; - -extern fifologData *fifo_log; - -#endif // API_FTL_H \ No newline at end of file diff --git a/src/log.c b/src/log.c index bd44c886..f40e622c 100644 --- a/src/log.c +++ b/src/log.c @@ -294,6 +294,13 @@ void __attribute__ ((format (gnu_printf, 3, 4))) _FTL_log(const int priority, co // Print to log file or syslog if(print_log) { + // Add line to FIFO buffer + char buffer[MAX_MSG_FIFO + 1u]; + va_start(args, format); + const size_t len = vsnprintf(buffer, MAX_MSG_FIFO, format, args) + 1u; /* include zero-terminator */ + va_end(args); + add_to_fifo_buffer(FIFO_FTL, buffer, len > MAX_MSG_FIFO ? MAX_MSG_FIFO : len); + if(config.files.log.ftl.v.s != NULL) { // Open log file @@ -332,40 +339,51 @@ void __attribute__ ((format (gnu_printf, 3, 4))) _FTL_log(const int priority, co } } -static FILE * __attribute__((malloc, warn_unused_result)) open_web_log(const enum web_code code) +static FILE * __attribute__((malloc, warn_unused_result)) open_web_log(const enum fifo_logs which) { // Open the log file in append/create mode char *file = NULL; - switch (code) + switch (which) { - case HTTP_INFO: - file = config.files.http_info.v.s; - break; - case PH7_ERROR: - file = config.files.ph7_error.v.s; - break; - default: - file = config.files.ph7_error.v.s; - break; + case FIFO_CIVETWEB: + file = config.files.http_info.v.s; + break; + case FIFO_PH7: + file = config.files.ph7_error.v.s; + break; + case FIFO_FTL: + case FIFO_DNSMASQ: + case FIFO_MAX: + default: + log_err("Invalid logging requested"); + return NULL; } return fopen(file, "a+"); } -void __attribute__ ((format (gnu_printf, 2, 3))) logg_web(enum web_code code, const char *format, ...) +void __attribute__ ((format (gnu_printf, 2, 3))) logg_web(enum fifo_logs which, const char *format, ...) { char timestring[84] = ""; + const time_t now = time(NULL); va_list args; + // Add line to FIFO buffer + char buffer[MAX_MSG_FIFO + 1u]; + va_start(args, format); + const size_t len = vsnprintf(buffer, MAX_MSG_FIFO, format, args) + 1u; /* include zero-terminator */ + va_end(args); + add_to_fifo_buffer(which, buffer, len > MAX_MSG_FIFO ? MAX_MSG_FIFO : len); + // Get human-readable time - get_timestr(timestring, time(NULL), true); + get_timestr(timestring, now, true); // Get and log PID of current process to avoid ambiguities when more than one // pihole-FTL instance is logging into the same file const long pid = (long)getpid(); // Open web log file - FILE *weblog = open_web_log(code); + FILE *weblog = open_web_log(which); // Write to web log file if(weblog != NULL) @@ -481,7 +499,7 @@ void format_time(char buffer[42], unsigned long seconds, double milliseconds) void FTL_log_dnsmasq_fatal(const char *format, ...) { - if(no_ftl_logging) + if(!print_log) return; // Build a complete string from possible multi-part string passed from dnsmasq char message[256] = { 0 }; @@ -703,3 +721,39 @@ void dnsmasq_diagnosis_warning(char *message) // Crop away any existing initial "warning: " logg_warn_dnsmasq_message(skipStr("warning: ", message)); } + +void add_to_fifo_buffer(const enum fifo_logs which, const char *payload, const size_t length) +{ + const double now = double_time(); + + // Do not try to log when shared memory isn't initialized yet + if(!fifo_log) + return; + + unsigned int idx = fifo_log->logs[which].next_id++; + if(idx >= LOG_SIZE) + { + // Log is full, move everything one slot forward to make space for a new record at the end + // This pruges the oldest message from the list (it is overwritten by the second message) + memmove(&fifo_log->logs[which].message[0][0], &fifo_log->logs[which].message[1][0], (LOG_SIZE - 1u) * MAX_MSG_FIFO); + memmove(&fifo_log->logs[which].timestamp[0], &fifo_log->logs[which].timestamp[1], (LOG_SIZE - 1u) * sizeof(fifo_log->logs[which].timestamp[0])); + idx = LOG_SIZE - 1u; + } + + // Copy relevant string into temporary buffer + size_t copybytes = length < MAX_MSG_FIFO ? length : MAX_MSG_FIFO; + memcpy(fifo_log->logs[which].message[idx], payload, copybytes); + + // Zero-terminate buffer, truncate newline if found + if(fifo_log->logs[which].message[idx][copybytes - 1u] == '\n') + { + fifo_log->logs[which].message[idx][copybytes - 1u] = '\0'; + } + else + { + fifo_log->logs[which].message[idx][copybytes] = '\0'; + } + + // Set timestamp + fifo_log->logs[which].timestamp[idx] = now; +} diff --git a/src/log.h b/src/log.h index c828dc64..d92425b2 100644 --- a/src/log.h +++ b/src/log.h @@ -32,7 +32,7 @@ void log_FTL_version(bool crashreport); double double_time(void); void get_timestr(char * const timestring, const time_t timein, const bool millis); void debugstr(const enum debug_flag flag, const char **name); -void logg_web(enum web_code code, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3))); +void logg_web(enum fifo_logs which, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3))); const char *get_ordinal_suffix(unsigned int number) __attribute__ ((const)); void print_FTL_version(void); void dnsmasq_diagnosis_warning(char *message); @@ -61,4 +61,24 @@ int blocked_queries(void) __attribute__ ((pure)); const char *short_path(const char *full_path) __attribute__ ((pure)); +/* From RFC 3164 */ +#define MAX_MSG_FIFO 1024u + +// How many messages do we keep in memory (FIFO message buffer)? +// This number multiplied by MAX_MSG_FIFO (see above) gives the total buffer size +// Defaults to 128 [use 128 KB of memory for the log] +#define LOG_SIZE 128u + +void add_to_fifo_buffer(const enum fifo_logs which, const char *payload, const size_t length); + +typedef struct { + struct { + unsigned int next_id; + double timestamp[LOG_SIZE]; + char message[LOG_SIZE][MAX_MSG_FIFO]; + } logs[FIFO_MAX]; +} fifologData; + +extern fifologData *fifo_log; + #endif //LOG_H diff --git a/src/ph7/ph7.c b/src/ph7/ph7.c index 192b0409..6cede664 100644 --- a/src/ph7/ph7.c +++ b/src/ph7/ph7.c @@ -126,8 +126,8 @@ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /******************************* Pi-hole modification ******************************/ -enum web_code { HTTP_INFO, PH7_ERROR }; -extern void logg_web(enum web_code code, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3))); +#include "enums.h" +extern void logg_web(enum fifo_logs which, const char *format, ...) __attribute__ ((format (gnu_printf, 2, 3))); /***********************************************************************************/ /* $SymiscID: ph7.h v2.1 UNIX|WIN32/64 2012-09-15 09:43 stable $ */ #include /* needed for the definition of va_list */ @@ -5010,7 +5010,7 @@ static sxi32 VmThrowException(ph7_vm *pVm,ph7_class_instance *pThis); static sxi32 VmCallErrorHandler(ph7_vm *pVm,SyBlob *pMsg) { /* Invoke the output consumer callback */ - logg_web(PH7_ERROR, "%.*s", SyBlobLength(pMsg), (char *)SyBlobData(pMsg)); + logg_web(FIFO_PH7, "%.*s", SyBlobLength(pMsg), (char *)SyBlobData(pMsg)); return SXRET_OK; } /* diff --git a/src/shmem.c b/src/shmem.c index 1eb367e2..2d9a9cec 100644 --- a/src/shmem.c +++ b/src/shmem.c @@ -16,8 +16,6 @@ #include "config/config.h" // data getter functions #include "datastructure.h" -// fifologData -#include "fifo.h" // get_num_regex() #include "regex_r.h" // sleepms() @@ -90,7 +88,7 @@ static SharedMemory *sharedMemories[] = { &shm_lock, &shm_settings, &shm_dns_cache, &shm_per_client_regex, - &shm_fifo_log }; + &shm_fifo_log }; #define NUM_SHMEM (sizeof(sharedMemories)/sizeof(SharedMemory*)) // Variable size array structs @@ -402,6 +400,7 @@ static void remap_shm(void) void _lock_shm(const char *func, const int line, const char *file) { log_debug(DEBUG_LOCKS, "Waiting for SHM lock in %s() (%s:%i)", func, file, line); + log_debug(DEBUG_LOCKS, "SHM lock: %p", shmLock); int result = pthread_mutex_lock(&shmLock->lock.outer); diff --git a/src/webserver/http-common.h b/src/webserver/http-common.h index 399cb808..bff9c8b5 100644 --- a/src/webserver/http-common.h +++ b/src/webserver/http-common.h @@ -11,8 +11,10 @@ #define HTTP_H // External components -#include "../civetweb/civetweb.h" -#include "../cJSON/cJSON.h" +#include "civetweb/civetweb.h" +#include "cJSON/cJSON.h" +// enum fifo_logs +#include "enums.h" // strlen() #include @@ -29,6 +31,12 @@ enum http_method { HTTP_PATCH = 1 << 3, HTTP_DELETE = 1 << 4, }; + +struct api_options { + bool domains :1; + enum fifo_logs which; +}; + struct ftl_conn { struct mg_connection *conn; const struct mg_request_info *request; @@ -45,7 +53,7 @@ struct ftl_conn { bool restart; } ftl; - bool opts[2]; + struct api_options opts; }; diff --git a/src/webserver/ph7.c b/src/webserver/ph7.c index c162dcb8..e58af5b9 100644 --- a/src/webserver/ph7.c +++ b/src/webserver/ph7.c @@ -84,20 +84,20 @@ int ph7_handler(struct mg_connection *conn, void *cbdata) { if( rc == PH7_IO_ERR ) { - logg_web(PH7_ERROR, "%s: IO error while opening the target file", full_path); + logg_web(FIFO_PH7, "%s: IO error while opening the target file", full_path); // Fall back to HTTP server to handle the 404 event return 0; } else if( rc == PH7_VM_ERR ) { - logg_web(PH7_ERROR, "%s: VM initialization error", full_path); + logg_web(FIFO_PH7, "%s: VM initialization error", full_path); // Mark file as processed - this prevents the HTTP server // from printing the raw PHP source code to the user return 1; } else { - logg_web(PH7_ERROR, "%s: Compile error (%d)", full_path, rc); + logg_web(FIFO_PH7, "%s: Compile error (%d)", full_path, rc); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" "PHP compilation error, check %s for further details.", @@ -109,7 +109,7 @@ int ph7_handler(struct mg_connection *conn, void *cbdata) ph7_config(pEngine, PH7_CONFIG_ERR_LOG, &zErrLog, &niLen); if( niLen > 0 ){ /* zErrLog is null terminated */ - logg_web(PH7_ERROR, " ---> %s", zErrLog); + logg_web(FIFO_PH7, " ---> %s", zErrLog); } // Mark file as processed - this prevents the HTTP server // from printing the raw PHP source code to the user @@ -134,7 +134,7 @@ int ph7_handler(struct mg_connection *conn, void *cbdata) { rc = ph7_create_function(pVm, aFunc[i].zName, aFunc[i].xProc, NULL /* NULL: No private data */); if( rc != PH7_OK ){ - logg_web(PH7_ERROR, "%s: Error while registering foreign function %s()", + logg_web(FIFO_PH7, "%s: Error while registering foreign function %s()", full_path, aFunc[i].zName); } } @@ -143,7 +143,7 @@ int ph7_handler(struct mg_connection *conn, void *cbdata) rc = ph7_vm_exec(pVm,0); if( rc != PH7_OK ) { - logg_web(PH7_ERROR, "%s: VM execution error", full_path); + logg_web(FIFO_PH7, "%s: VM execution error", full_path); // Mark file as processed - this prevents the HTTP server // from printing the raw PHP source code to the user return 1; @@ -173,7 +173,7 @@ static int PH7_error_report(const void *pOutput, unsigned int nOutputLen, // Log error message, strip trailing newline character if any if(((const char*)pOutput)[nOutputLen-1] == '\n') nOutputLen--; - logg_web(PH7_ERROR, "%.*s", nOutputLen, (const char*)pOutput); + logg_web(FIFO_PH7, "%.*s", nOutputLen, (const char*)pOutput); return PH7_OK; } @@ -181,7 +181,7 @@ void init_ph7(void) { if(ph7_init(&pEngine) != PH7_OK ) { - logg_web(PH7_ERROR, "Error while initializing a new PH7 engine instance"); + logg_web(FIFO_PH7, "Error while initializing a new PH7 engine instance"); return; } diff --git a/src/webserver/webserver.c b/src/webserver/webserver.c index 7baaa0af..597a83f1 100644 --- a/src/webserver/webserver.c +++ b/src/webserver/webserver.c @@ -85,7 +85,7 @@ static int redirect_root_handler(struct mg_connection *conn, void *input) static int log_http_message(const struct mg_connection *conn, const char *message) { - logg_web(HTTP_INFO, "HTTP info: %s", message); + logg_web(FIFO_CIVETWEB, "HTTP info: %s", message); return 1; } @@ -93,14 +93,14 @@ static int log_http_access(const struct mg_connection *conn, const char *message { // Only log when in API debugging mode if(config.debug.api.v.b) - logg_web(HTTP_INFO, "ACCESS: %s", message); + logg_web(FIFO_CIVETWEB, "ACCESS: %s", message); return 1; } void http_init(void) { - logg_web(HTTP_INFO, "Initializing HTTP server on port %s", config.http.port.v.s); + logg_web(FIFO_CIVETWEB, "Initializing HTTP server on port %s", config.http.port.v.s); /* Initialize the library */ unsigned int features = MG_FEATURES_FILES | @@ -111,7 +111,7 @@ void http_init(void) if(mg_init_library(features) == 0) { - logg_web(HTTP_INFO, "Initializing HTTP library failed!"); + logg_web(FIFO_CIVETWEB, "Initializing HTTP library failed!"); return; }