Add /api/stats/database/upstreams
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
parent
a7500f1d58
commit
ef5cfe0a17
|
@ -141,7 +141,8 @@ int api_auth(struct mg_connection *conn)
|
|||
for(unsigned int i = 0; i < API_MAX_CLIENTS; i++)
|
||||
{
|
||||
// Expired slow, mark as unused
|
||||
if(auth_data[i].valid_until < now)
|
||||
if(auth_data[i].used &&
|
||||
auth_data[i].valid_until < now)
|
||||
{
|
||||
if(config.debug & DEBUG_API)
|
||||
{
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#include "../cJSON/cJSON.h"
|
||||
// logg()
|
||||
#include "../log.h"
|
||||
|
||||
#define JSON_NEW_OBJ() cJSON_CreateObject();
|
||||
#define JSON_NEW_ARRAY() cJSON_CreateArray();
|
||||
|
@ -21,6 +23,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_OBJ_COPY_STR FAILED (key: \"%s\", string: \"%s\")!", key, string); \
|
||||
return 500; \
|
||||
} \
|
||||
cJSON_AddItemToObject(object, key, string_item); \
|
||||
|
@ -32,6 +35,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_OBJ_REF_STR FAILED (key: \"%s\", string: \"%s\")!", key, string); \
|
||||
return 500; \
|
||||
} \
|
||||
cJSON_AddItemToObject(object, key, string_item); \
|
||||
|
@ -42,6 +46,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_OBJ_ADD_NUMBER FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
}
|
||||
|
@ -52,6 +57,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_OBJ_ADD_NULL FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
cJSON_AddItemToObject(object, key, null_item); \
|
||||
|
@ -63,6 +69,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_OBJ_ADD_BOOL FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
cJSON_AddItemToObject(object, key, bool_item); \
|
||||
|
@ -84,6 +91,7 @@
|
|||
{ \
|
||||
cJSON_Delete(array); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_ARRAY_REF_STR FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
cJSON_AddItemToArray(array, string_item); \
|
||||
|
@ -95,6 +103,7 @@
|
|||
{ \
|
||||
cJSON_Delete(array); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_ARRAY_COPY_STR FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
cJSON_AddItemToArray(array, string_item); \
|
||||
|
@ -113,6 +122,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_SEND_OBJECT FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
send_http(conn, "application/json; charset=utf-8", msg); \
|
||||
|
@ -126,6 +136,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_SEND_OBJECT_CODE FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
send_http_code(conn, "application/json; charset=utf-8", code, msg); \
|
||||
|
@ -139,6 +150,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_SEND_OBJECT_AND_HEADERS FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
send_http(conn, "application/json; charset=utf-8", additional_headers, msg); \
|
||||
|
@ -153,6 +165,7 @@
|
|||
{ \
|
||||
cJSON_Delete(object); \
|
||||
send_http_internal_error(conn); \
|
||||
logg("JSON_SEND_OBJECT_AND_HEADERS_CODE FAILED!"); \
|
||||
return 500; \
|
||||
} \
|
||||
send_http_code(conn, "application/json; charset=utf-8", additional_headers, code, msg); \
|
||||
|
|
|
@ -135,6 +135,10 @@ int api_handler(struct mg_connection *conn, void *ignored)
|
|||
{
|
||||
ret = api_stats_database_query_types(conn);
|
||||
}
|
||||
else if(startsWith("/api/stats/database/upstreams", request->local_uri))
|
||||
{
|
||||
ret = api_stats_database_upstreams(conn);
|
||||
}
|
||||
/******************************** api/version ****************************/
|
||||
else if(startsWith("/api/version", request->local_uri))
|
||||
{
|
||||
|
|
|
@ -33,6 +33,7 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
|
|||
int api_stats_database_summary(struct mg_connection *conn);
|
||||
int api_stats_database_overTime_clients(struct mg_connection *conn);
|
||||
int api_stats_database_query_types(struct mg_connection *conn);
|
||||
int api_stats_database_upstreams(struct mg_connection *conn);
|
||||
|
||||
// FTL methods
|
||||
int api_ftl_clientIP(struct mg_connection *conn);
|
||||
|
|
|
@ -273,7 +273,20 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
|
|||
if( rc != SQLITE_OK ){
|
||||
logg("api_stats_database_overTime_history() - SQL error prepare (%i): %s",
|
||||
rc, sqlite3_errmsg(FTL_db));
|
||||
return false;
|
||||
|
||||
dbclose();
|
||||
|
||||
// Relock shared memory
|
||||
lock_shm();
|
||||
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
JSON_OBJ_ADD_NUMBER(json, "from", from);
|
||||
JSON_OBJ_ADD_NUMBER(json, "until", until);
|
||||
JSON_OBJ_REF_STR(json, "querystr", querystr);
|
||||
return send_json_error(conn, 500,
|
||||
"internal_error",
|
||||
"Failed to prepare query string",
|
||||
json);
|
||||
}
|
||||
|
||||
// Bind from to prepared statement
|
||||
|
@ -350,7 +363,9 @@ int api_stats_database_top_items(bool blocked, bool domains, struct mg_connectio
|
|||
JSON_OBJ_COPY_STR(item, (domains ? "domain" : "ip"), string);
|
||||
// Add empty name field for top_client requests
|
||||
if(!domains)
|
||||
{
|
||||
JSON_OBJ_REF_STR(item, "name", "");
|
||||
}
|
||||
JSON_OBJ_ADD_NUMBER(item, "count", count);
|
||||
JSON_ARRAY_ADD_ITEM(top_items, item);
|
||||
total += count;
|
||||
|
@ -419,6 +434,10 @@ int api_stats_database_summary(struct mg_connection *conn)
|
|||
|
||||
if(sum_queries < 0 || blocked_queries < 0 || total_clients < 0)
|
||||
{
|
||||
|
||||
// Close (= unlock) database connection
|
||||
dbclose();
|
||||
|
||||
// Relock shared memory
|
||||
lock_shm();
|
||||
|
||||
|
@ -772,3 +791,166 @@ int api_stats_database_query_types(struct mg_connection *conn)
|
|||
// Send JSON object
|
||||
JSON_SEND_OBJECT(json);
|
||||
}
|
||||
|
||||
|
||||
int api_stats_database_upstreams(struct mg_connection *conn)
|
||||
{
|
||||
int from = 0, until = 0;
|
||||
const struct mg_request_info *request = mg_get_request_info(conn);
|
||||
if(request->query_string != NULL)
|
||||
{
|
||||
int num;
|
||||
if((num = get_int_var(request->query_string, "from")) > 0)
|
||||
from = num;
|
||||
if((num = get_int_var(request->query_string, "until")) > 0)
|
||||
until = num;
|
||||
}
|
||||
|
||||
// Check if we received the required information
|
||||
if(from == 0 || until == 0)
|
||||
{
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
JSON_OBJ_ADD_NUMBER(json, "from", from);
|
||||
JSON_OBJ_ADD_NUMBER(json, "until", until);
|
||||
return send_json_error(conn, 400,
|
||||
"bad_request",
|
||||
"You need to specify both \"from\" and \"until\" in the request.",
|
||||
json);
|
||||
}
|
||||
|
||||
// Unlock shared memory (DNS resolver can continue to work while we're preforming database queries)
|
||||
unlock_shm();
|
||||
|
||||
// Open the database (this also locks the database)
|
||||
dbopen();
|
||||
|
||||
// Perform simple SQL queries
|
||||
unsigned int sum_queries = 0;
|
||||
const char *querystr;
|
||||
querystr = "SELECT COUNT(*) FROM queries "
|
||||
"WHERE timestamp >= :from AND timestamp <= :until "
|
||||
"AND status = 2";
|
||||
int forwarded_queries = db_query_int_from_until(querystr, from, until);
|
||||
sum_queries = forwarded_queries;
|
||||
|
||||
querystr = "SELECT COUNT(*) FROM queries "
|
||||
"WHERE timestamp >= :from AND timestamp <= :until "
|
||||
"AND status = 3";
|
||||
int cached_queries = db_query_int_from_until(querystr, from, until);
|
||||
sum_queries += cached_queries;
|
||||
|
||||
querystr = "SELECT COUNT(*) FROM queries "
|
||||
"WHERE timestamp >= :from AND timestamp <= :until "
|
||||
"AND status != 0 AND status != 2 AND status != 3";
|
||||
int blocked_queries = db_query_int_from_until(querystr, from, until);
|
||||
sum_queries += blocked_queries;
|
||||
|
||||
querystr = "SELECT forward,COUNT(*) FROM queries "
|
||||
"WHERE timestamp >= :from AND timestamp <= :until "
|
||||
"AND forward IS NOT NULL "
|
||||
"GROUP BY forward ORDER BY forward";
|
||||
|
||||
// Prepare SQLite statement
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(FTL_db, querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK ){
|
||||
logg("api_stats_database_overTime_clients() - SQL error prepare (%i): %s",
|
||||
rc, sqlite3_errmsg(FTL_db));
|
||||
|
||||
// Relock shared memory
|
||||
lock_shm();
|
||||
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
JSON_OBJ_ADD_NUMBER(json, "from", from);
|
||||
JSON_OBJ_ADD_NUMBER(json, "until", until);
|
||||
return send_json_error(conn, 500,
|
||||
"internal_error",
|
||||
"Failed to prepare statement",
|
||||
json);
|
||||
}
|
||||
|
||||
// Bind from to prepared statement
|
||||
if((rc = sqlite3_bind_int(stmt, 1, from)) != SQLITE_OK)
|
||||
{
|
||||
logg("api_stats_database_overTime_clients(): Failed to bind from (error %d) - %s",
|
||||
rc, sqlite3_errmsg(FTL_db));
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
dbclose();
|
||||
|
||||
// Relock shared memory
|
||||
lock_shm();
|
||||
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
JSON_OBJ_ADD_NUMBER(json, "from", from);
|
||||
JSON_OBJ_ADD_NUMBER(json, "until", until);
|
||||
return send_json_error(conn, 500,
|
||||
"internal_error",
|
||||
"Failed to bind from",
|
||||
json);
|
||||
}
|
||||
|
||||
// Bind until to prepared statement
|
||||
if((rc = sqlite3_bind_int(stmt, 2, until)) != SQLITE_OK)
|
||||
{
|
||||
logg("api_stats_database_overTime_clients(): Failed to bind until (error %d) - %s",
|
||||
rc, sqlite3_errmsg(FTL_db));
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
dbclose();
|
||||
|
||||
// Relock shared memory
|
||||
lock_shm();
|
||||
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
JSON_OBJ_ADD_NUMBER(json, "from", from);
|
||||
JSON_OBJ_ADD_NUMBER(json, "until", until);
|
||||
return send_json_error(conn, 500,
|
||||
"internal_error",
|
||||
"Failed to bind until",
|
||||
json);
|
||||
}
|
||||
|
||||
// Loop over clients and accumulate results
|
||||
cJSON *upstreams = JSON_NEW_ARRAY();
|
||||
while((rc = sqlite3_step(stmt)) == SQLITE_ROW)
|
||||
{
|
||||
const char* upstream = (char*)sqlite3_column_text(stmt, 0);
|
||||
const int count = sqlite3_column_int(stmt, 1);
|
||||
cJSON *item = JSON_NEW_OBJ();
|
||||
JSON_OBJ_COPY_STR(item, "ip", upstream);
|
||||
JSON_OBJ_REF_STR(item, "name", "");
|
||||
JSON_OBJ_ADD_NUMBER(item, "count", count);
|
||||
JSON_ARRAY_ADD_ITEM(upstreams, item);
|
||||
sum_queries += count;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Add cache and blocklist as upstreams
|
||||
cJSON *cached = JSON_NEW_OBJ();
|
||||
JSON_OBJ_REF_STR(cached, "ip", "");
|
||||
JSON_OBJ_REF_STR(cached, "name", "cache");
|
||||
JSON_OBJ_ADD_NUMBER(cached, "count", cached_queries);
|
||||
JSON_ARRAY_ADD_ITEM(upstreams, cached);
|
||||
|
||||
logg("%d / %d / %d", forwarded_queries, cached_queries, blocked_queries);
|
||||
|
||||
cJSON *blocked = JSON_NEW_OBJ();
|
||||
JSON_OBJ_REF_STR(blocked, "ip", "");
|
||||
JSON_OBJ_REF_STR(blocked, "name", "blocklist");
|
||||
JSON_OBJ_ADD_NUMBER(blocked, "count", blocked_queries);
|
||||
JSON_ARRAY_ADD_ITEM(upstreams, blocked);
|
||||
|
||||
// Close (= unlock) database connection
|
||||
dbclose();
|
||||
|
||||
// Re-lock shared memory before returning back to router subroutine
|
||||
lock_shm();
|
||||
|
||||
// Send JSON object
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
JSON_OBJ_ADD_ITEM(json, "upstreams", upstreams);
|
||||
JSON_OBJ_ADD_NUMBER(json, "forwarded_queries", forwarded_queries);
|
||||
JSON_OBJ_ADD_NUMBER(json, "total_queries", sum_queries);
|
||||
JSON_SEND_OBJECT(json);
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ void _lock_shm(const char* func, const int line, const char * file) {
|
|||
}
|
||||
|
||||
if(result != 0)
|
||||
logg("Failed to obtain SHM lock: %s", strerror(result));
|
||||
logg("Failed to obtain SHM lock: %s in %s() (%s:%i)", strerror(result), func, file, line);
|
||||
}
|
||||
|
||||
void _unlock_shm(const char* func, const int line, const char * file) {
|
||||
|
@ -262,7 +262,7 @@ void _unlock_shm(const char* func, const int line, const char * file) {
|
|||
logg("Removed lock in %s() (%s:%i)", func, file, line);
|
||||
|
||||
if(result != 0)
|
||||
logg("Failed to unlock SHM lock: %s", strerror(result));
|
||||
logg("Failed to unlock SHM lock: %s in %s() (%s:%i)", strerror(result), func, file, line);
|
||||
}
|
||||
|
||||
bool init_shmem(void)
|
||||
|
|
Loading…
Reference in New Issue