mirror of
https://github.com/pi-hole/FTL.git
synced 2024-10-26 16:52:18 +02:00
1075 lines
34 KiB
C
1075 lines
34 KiB
C
/* 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
|
|
* API Implementation /api/ftl
|
|
*
|
|
* This file is copyright under the latest version of the EUPL.
|
|
* Please see LICENSE file for your rights under this license. */
|
|
|
|
#include "FTL.h"
|
|
#include "webserver/http-common.h"
|
|
#include "webserver/json_macros.h"
|
|
#include "api/api.h"
|
|
// sysinfo()
|
|
#include <sys/sysinfo.h>
|
|
// get_blockingstatus()
|
|
#include "config/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 <grp.h>
|
|
// config struct
|
|
#include "config/config.h"
|
|
// struct clientsData
|
|
#include "datastructure.h"
|
|
// uname()
|
|
#include <sys/utsname.h>
|
|
// get_cpu_percentage()
|
|
#include "daemon.h"
|
|
// getProcessMemory()
|
|
#include "procps.h"
|
|
|
|
// get_FTL_version()
|
|
#include "log.h"
|
|
#include "version.h"
|
|
// prase_line()
|
|
#include "files.h"
|
|
|
|
// get_messages()
|
|
#include "database/message-table.h"
|
|
// LONG_MIN, LONG_MAX
|
|
#include <limits.h>
|
|
#include "metrics.h"
|
|
|
|
// get_https_port()
|
|
#include "webserver/webserver.h"
|
|
|
|
// DIR
|
|
#include <dirent.h>
|
|
|
|
// timer_elapsed_msec()
|
|
#include "timers.h"
|
|
|
|
#define VERSIONS_FILE "/etc/pihole/versions"
|
|
|
|
int api_info_client(struct ftl_conn *api)
|
|
{
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
// Add client's IP address
|
|
JSON_REF_STR_IN_OBJECT(json, "remote_addr", api->request->remote_addr);
|
|
|
|
// Add HTTP version
|
|
JSON_REF_STR_IN_OBJECT(json, "http_version", api->request->http_version);
|
|
|
|
// Add request method
|
|
JSON_REF_STR_IN_OBJECT(json, "method", api->request->request_method);
|
|
|
|
// Add HTTP headers
|
|
cJSON *headers = JSON_NEW_ARRAY();
|
|
for(int i = 0; i < api->request->num_headers; i++)
|
|
{
|
|
// Add headers
|
|
cJSON *header = JSON_NEW_OBJECT();
|
|
JSON_REF_STR_IN_OBJECT(header, "name", api->request->http_headers[i].name);
|
|
JSON_REF_STR_IN_OBJECT(header, "value", api->request->http_headers[i].value);
|
|
JSON_ADD_ITEM_TO_ARRAY(headers, header);
|
|
}
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "headers", headers);
|
|
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
int api_info_database(struct ftl_conn *api)
|
|
{
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
|
|
// Add database stat details
|
|
struct stat st;
|
|
get_database_stat(&st);
|
|
JSON_ADD_NUMBER_TO_OBJECT(json, "size", st.st_size); // Total size, in bytes
|
|
|
|
// File type
|
|
const char *filetype;
|
|
if((st.st_mode & S_IFMT) == S_IFREG)
|
|
filetype = "Regular file";
|
|
else if((st.st_mode & S_IFMT) == S_IFLNK)
|
|
filetype = "Symbolic link";
|
|
else
|
|
filetype = "Unknown";
|
|
JSON_REF_STR_IN_OBJECT(json, "type", filetype);
|
|
|
|
// File mode
|
|
char permissions[10] = { 0 };
|
|
get_permission_string(permissions, &st);
|
|
JSON_REF_STR_IN_OBJECT(json, "mode", permissions);
|
|
|
|
JSON_ADD_NUMBER_TO_OBJECT(json, "atime", st.st_atime); // Time of last access
|
|
JSON_ADD_NUMBER_TO_OBJECT(json, "mtime", st.st_mtime); // Time of last modification
|
|
JSON_ADD_NUMBER_TO_OBJECT(json, "ctime", st.st_ctime); // Time of last status change (owner or mode change, etc.)
|
|
|
|
// Get owner details
|
|
cJSON *user = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(user, "uid", st.st_uid); // UID
|
|
const struct passwd *pw = getpwuid(st.st_uid);
|
|
if(pw != NULL)
|
|
{
|
|
JSON_COPY_STR_TO_OBJECT(user, "name", pw->pw_name); // User name
|
|
JSON_COPY_STR_TO_OBJECT(user, "info", pw->pw_gecos); // User information
|
|
}
|
|
else
|
|
{
|
|
JSON_ADD_NULL_TO_OBJECT(user, "name");
|
|
JSON_ADD_NULL_TO_OBJECT(user, "info");
|
|
}
|
|
|
|
cJSON *group = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(group, "gid", st.st_gid); // GID
|
|
const struct group *gr = getgrgid(st.st_uid);
|
|
if(gr != NULL)
|
|
{
|
|
JSON_COPY_STR_TO_OBJECT(group, "name", gr->gr_name); // Group name
|
|
}
|
|
else
|
|
{
|
|
JSON_ADD_NULL_TO_OBJECT(group, "name");
|
|
}
|
|
cJSON *owner = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(owner, "user", user);
|
|
JSON_ADD_ITEM_TO_OBJECT(owner, "group", group);
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "owner", owner);
|
|
|
|
// Add number of queries in on-disk database
|
|
const int queries_in_database = get_number_of_queries_in_DB(NULL, "query_storage");
|
|
JSON_ADD_NUMBER_TO_OBJECT(json, "queries", queries_in_database);
|
|
|
|
// Add SQLite library version
|
|
JSON_REF_STR_IN_OBJECT(json, "sqlite_version", get_sqlite3_version());
|
|
|
|
// Send reply to user
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
static int get_system_obj(struct ftl_conn *api, cJSON *system)
|
|
{
|
|
const int nprocs = get_nprocs();
|
|
struct sysinfo info;
|
|
if(sysinfo(&info) != 0)
|
|
return send_json_error(api, 500, "error", strerror(errno), NULL);
|
|
|
|
// Seconds since boot
|
|
JSON_ADD_NUMBER_TO_OBJECT(system, "uptime", info.uptime);
|
|
|
|
cJSON *memory = JSON_NEW_OBJECT();
|
|
cJSON *ram = JSON_NEW_OBJECT();
|
|
// We cannot use the memory information available through sysinfo() as
|
|
// this is not what we want. It is worth noting that freeram in sysinfo
|
|
// is not what most people would call "free RAM". freeram excludes
|
|
// memory used by cached filesystem metadata ("buffers") and contents
|
|
// ("cache"). Both of these can be a significant portion of RAM but are
|
|
// freed by the OS when programs need that memory. sysinfo does contain
|
|
// size used by buffers (sysinfo.bufferram), but not cache. The best
|
|
// option is to use the MemAvailable (as opposed to MemFree) entry in
|
|
// /proc/meminfo instead.
|
|
struct proc_meminfo mem = { 0 };
|
|
parse_proc_meminfo(&mem);
|
|
// Total usable main memory size
|
|
JSON_ADD_NUMBER_TO_OBJECT(ram, "total", mem.total);
|
|
// Free memory size
|
|
JSON_ADD_NUMBER_TO_OBJECT(ram, "free", mem.mfree);
|
|
// Used memory size
|
|
JSON_ADD_NUMBER_TO_OBJECT(ram, "used", mem.used);
|
|
// Available memory size
|
|
// See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
|
|
// This Linux kernel commit message explains there are more nuances. It
|
|
// says: "Many programs check /proc/meminfo to estimate how much free
|
|
// memory is available. They generally do this by adding up "free" and
|
|
// "cached", which was fine ten years ago, but is pretty much guaranteed
|
|
// to be wrong today."
|
|
JSON_ADD_NUMBER_TO_OBJECT(ram, "available", mem.avail);
|
|
JSON_ADD_NUMBER_TO_OBJECT(ram, "%used", mem.total > 0 ? 100.0*mem.used/mem.total : 0);
|
|
JSON_ADD_ITEM_TO_OBJECT(memory, "ram", ram);
|
|
|
|
cJSON *swap = JSON_NEW_OBJECT();
|
|
// Total swap space size
|
|
const float total_swap = info.totalswap * info.mem_unit / 1024;
|
|
JSON_ADD_NUMBER_TO_OBJECT(swap, "total", total_swap);
|
|
// Swap space still available
|
|
JSON_ADD_NUMBER_TO_OBJECT(swap, "free", info.freeswap * info.mem_unit / 1024);
|
|
// Used swap space
|
|
const float used_swap = (info.totalswap - info.freeswap) * info.mem_unit / 1024;
|
|
JSON_ADD_NUMBER_TO_OBJECT(swap, "used", used_swap);
|
|
JSON_ADD_NUMBER_TO_OBJECT(swap, "%used", total_swap > 0 ? 100.0*used_swap/total_swap : 0);
|
|
JSON_ADD_ITEM_TO_OBJECT(memory, "swap", swap);
|
|
JSON_ADD_ITEM_TO_OBJECT(system, "memory", memory);
|
|
|
|
// Number of current processes
|
|
JSON_ADD_NUMBER_TO_OBJECT(system, "procs", info.procs);
|
|
|
|
cJSON *cpu = JSON_NEW_OBJECT();
|
|
// Number of available processors
|
|
JSON_ADD_NUMBER_TO_OBJECT(cpu, "nprocs", nprocs);
|
|
|
|
// 1, 5, and 15 minute load averages (we need to convert them)
|
|
cJSON *raw = JSON_NEW_ARRAY();
|
|
cJSON *percent = JSON_NEW_ARRAY();
|
|
float load_f[3] = { 0.f };
|
|
const float f_load = 1.f / (1 << SI_LOAD_SHIFT);
|
|
for(unsigned int i = 0; i < 3; i++)
|
|
{
|
|
load_f[i] = f_load * info.loads[i];
|
|
JSON_ADD_NUMBER_TO_ARRAY(raw, load_f[i]);
|
|
JSON_ADD_NUMBER_TO_ARRAY(percent, (100.f*load_f[i]/nprocs));
|
|
}
|
|
|
|
// Averaged CPU usage in percent
|
|
cJSON *load = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(load, "raw", raw);
|
|
JSON_ADD_ITEM_TO_OBJECT(load, "percent", percent);
|
|
JSON_ADD_ITEM_TO_OBJECT(cpu, "load", load);
|
|
JSON_ADD_ITEM_TO_OBJECT(system, "cpu", cpu);
|
|
|
|
// All okay
|
|
return 0;
|
|
}
|
|
|
|
static int read_hwmon_sensors(struct ftl_conn *api,
|
|
cJSON *array,
|
|
const char *path,
|
|
const char *value_path,
|
|
const unsigned int sensor_id)
|
|
{
|
|
// Read label and value file
|
|
char label_path[1024];
|
|
char crit_path[1024];
|
|
char max_path[1024];
|
|
char name[1024];
|
|
snprintf(label_path, sizeof(label_path), "%s/temp%u_label", path, sensor_id);
|
|
snprintf(crit_path, sizeof(crit_path), "%s/temp%u_crit", path, sensor_id);
|
|
snprintf(max_path, sizeof(max_path), "%s/temp%u_max", path, sensor_id);
|
|
snprintf(name, sizeof(name), "temp%u", sensor_id);
|
|
|
|
// Open files
|
|
FILE *f_value = fopen(value_path, "r");
|
|
if(f_value == NULL)
|
|
{
|
|
log_warn("Cannot open %s: %s", value_path, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
int raw_temp = 0;
|
|
if(fscanf(f_value, "%i", &raw_temp) == 1 && raw_temp != 0u)
|
|
{
|
|
// Try to read label
|
|
FILE *f_label = NULL;
|
|
if(file_exists(label_path))
|
|
f_label = fopen(label_path, "r");
|
|
|
|
cJSON *item = JSON_NEW_OBJECT();
|
|
char label[1024];
|
|
if(f_label && fgets(label, sizeof(label)-1, f_label))
|
|
{
|
|
// Remove newline if present
|
|
char *p = strchr(label, '\n');
|
|
if (p != NULL) *p = '\0';
|
|
JSON_COPY_STR_TO_OBJECT(item, "name", label);
|
|
}
|
|
else
|
|
{
|
|
JSON_ADD_NULL_TO_OBJECT(item, "name");
|
|
}
|
|
if(f_label != NULL)
|
|
fclose(f_label);
|
|
|
|
// Try to read maximum temperature
|
|
FILE *f_max = NULL;
|
|
if(file_exists(max_path))
|
|
f_max = fopen(max_path, "r");
|
|
|
|
int raw_max = 0;
|
|
bool has_max = false;
|
|
if(f_max)
|
|
has_max = fscanf(f_max, "%i", &raw_max) == 1;
|
|
if(f_max != NULL)
|
|
fclose(f_max);
|
|
|
|
// Try to read maximum temperature
|
|
FILE *f_crit = NULL;
|
|
if(file_exists(crit_path))
|
|
f_crit = fopen(crit_path, "r");
|
|
|
|
int raw_crit = 0;
|
|
bool has_crit = false;
|
|
if(f_crit)
|
|
has_crit = fscanf(f_crit, "%i", &raw_crit) == 1;
|
|
if(f_crit != NULL)
|
|
fclose(f_crit);
|
|
|
|
// Compute actual temperature, the raw unit is millidegree Celsius
|
|
double temp = 1e-3*raw_temp;
|
|
double max = 1e-3*raw_max;
|
|
double crit = 1e-3*raw_crit;
|
|
if(config.webserver.api.temp.unit.v.temp_unit == TEMP_UNIT_F)
|
|
{
|
|
// Convert °Celsius to °Fahrenheit
|
|
temp = 1.8*temp + 32;
|
|
max = 1.8*max + 32;
|
|
crit = 1.8*crit + 32;
|
|
}
|
|
else if(config.webserver.api.temp.unit.v.temp_unit == TEMP_UNIT_K)
|
|
{
|
|
// Convert °Celsius to Kelvin
|
|
temp += 273.15;
|
|
max += 273.15;
|
|
crit += 273.15;
|
|
}
|
|
JSON_ADD_NUMBER_TO_OBJECT(item, "value", temp);
|
|
if(has_max)
|
|
JSON_ADD_NUMBER_TO_OBJECT(item, "max", max);
|
|
else
|
|
JSON_ADD_NULL_TO_OBJECT(item, "max");
|
|
if(has_crit)
|
|
JSON_ADD_NUMBER_TO_OBJECT(item, "crit", crit);
|
|
else
|
|
JSON_ADD_NULL_TO_OBJECT(item, "crit");
|
|
JSON_COPY_STR_TO_OBJECT(item, "sensor", name);
|
|
|
|
JSON_ADD_ITEM_TO_ARRAY(array, item);
|
|
}
|
|
|
|
if(f_value != NULL)
|
|
fclose(f_value);
|
|
|
|
// All okay
|
|
return 0;
|
|
}
|
|
|
|
static int get_hwmon_sensors(struct ftl_conn *api, cJSON *sensors)
|
|
{
|
|
int ret;
|
|
// Source available temperatures, we try to read temperature sensors from
|
|
// different locations. We try to read the sensor label from the label file
|
|
// and the sensor value from the value file. If the label file does not
|
|
// exist, we use the value file as label.
|
|
|
|
// Hwmon sensors
|
|
// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-hwmon
|
|
|
|
// Iterate over content of /sys/class/hwmon
|
|
const char *dirname = "/sys/class/hwmon";
|
|
DIR *hwmon_dir = opendir(dirname);
|
|
if(hwmon_dir == NULL)
|
|
{
|
|
// Nothing to read here, leave array empty
|
|
log_warn("Cannot open %s: %s", dirname, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
// Iterate over all hwmonX directories
|
|
struct dirent *dircontent = NULL;
|
|
while((dircontent = readdir(hwmon_dir)) != NULL)
|
|
{
|
|
// Skip all files that do not start with hwmon
|
|
if(strncmp(dircontent->d_name, "hwmon", 5) != 0)
|
|
continue;
|
|
|
|
// Construct path to /sys/class/hwmon/hwmonX
|
|
char dirpath[1024];
|
|
strncpy(dirpath, "/sys/class/hwmon/", sizeof(dirpath));
|
|
strncat(dirpath, dircontent->d_name, sizeof(dirpath)-strlen(dirpath)-1);
|
|
|
|
// Construct path to /sys/class/hwmon/hwmonX/name
|
|
char namepath[1024];
|
|
strncpy(namepath, dirpath, sizeof(namepath));
|
|
strncat(namepath, "/name", sizeof(namepath)-strlen(namepath)-1);
|
|
|
|
// Read name file
|
|
FILE *f_name = fopen(namepath, "r");
|
|
char name[1024] = { 0 };
|
|
|
|
// Use directory name as fallback if name file does not exist
|
|
strncpy(name, dircontent->d_name, sizeof(name));
|
|
|
|
if(f_name != NULL)
|
|
{
|
|
if(fgets(name, sizeof(name)-1, f_name) != NULL)
|
|
{
|
|
// Remove newline if present
|
|
char *p = strchr(name, '\n');
|
|
if (p != NULL) *p = '\0';
|
|
fclose(f_name);
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
|
|
// Create sensor array item
|
|
cJSON *hwmon = JSON_NEW_OBJECT();
|
|
JSON_COPY_STR_TO_OBJECT(hwmon, "name", name);
|
|
JSON_COPY_STR_TO_OBJECT(hwmon, "path", dircontent->d_name);
|
|
|
|
// Get symlink target
|
|
char *target = get_hwmon_target(dirpath);
|
|
JSON_COPY_STR_TO_OBJECT(hwmon, "source", target);
|
|
free(target);
|
|
|
|
cJSON *temps = JSON_NEW_ARRAY();
|
|
JSON_ADD_ITEM_TO_OBJECT(hwmon, "temps", temps);
|
|
JSON_ADD_ITEM_TO_ARRAY(sensors, hwmon);
|
|
|
|
// Iterate over /sys/class/hwmon/hwmonX/tempY_...
|
|
DIR *sensor_dir = opendir(dirpath);
|
|
if(sensor_dir == NULL)
|
|
{
|
|
log_warn("Cannot open %s: %s", dirpath, strerror(errno));
|
|
continue;
|
|
}
|
|
struct dirent *dircontent_sensor = NULL;
|
|
while((dircontent_sensor = readdir(sensor_dir)) != NULL)
|
|
{
|
|
// Skip all files that do not start with "temp" or end with "_input"
|
|
if(strncmp(dircontent_sensor->d_name, "temp", 4) != 0)
|
|
continue;
|
|
if(strncmp(dircontent_sensor->d_name + strlen(dircontent_sensor->d_name) - 6, "_input", 6) != 0)
|
|
continue;
|
|
|
|
// Extract sensor ID from filename "tempXXX_input"
|
|
const char *sensor_id_ptr = dircontent_sensor->d_name + 4;
|
|
const unsigned int sensor_id = atoi(sensor_id_ptr);
|
|
|
|
// Construct path to /sys/class/hwmon/hwmonX/tempY_...
|
|
char value_path[1024];
|
|
strncpy(value_path, dirpath, sizeof(value_path));
|
|
strncat(value_path, "/", sizeof(value_path)-strlen(value_path)-1);
|
|
strncat(value_path, dircontent_sensor->d_name, sizeof(value_path)-strlen(value_path)-1);
|
|
|
|
// Read sensor
|
|
ret = read_hwmon_sensors(api, temps, dirpath, value_path, sensor_id);
|
|
if(ret != 0)
|
|
break;
|
|
}
|
|
closedir(sensor_dir);
|
|
}
|
|
|
|
// Cloase dir pointer
|
|
closedir(hwmon_dir);
|
|
|
|
// All okay
|
|
return 0;
|
|
}
|
|
|
|
static cJSON *read_sys_property(const char *path)
|
|
{
|
|
if(!file_exists(path))
|
|
return cJSON_CreateNull();
|
|
|
|
FILE *fp = fopen(path, "r");
|
|
if(fp == NULL)
|
|
return cJSON_CreateNull();
|
|
|
|
char buf[256];
|
|
if(fgets(buf, sizeof(buf), fp) == NULL)
|
|
{
|
|
fclose(fp);
|
|
return NULL;
|
|
}
|
|
fclose(fp);
|
|
|
|
// Remove newline if present
|
|
char *p = strchr(buf, '\n');
|
|
if (p != NULL) *p = '\0';
|
|
return cJSON_CreateString(buf);
|
|
}
|
|
|
|
static int get_host_obj(struct ftl_conn *api, cJSON *host)
|
|
{
|
|
cJSON *uname_ = JSON_NEW_OBJECT();
|
|
struct utsname un = { 0 };
|
|
uname(&un);
|
|
JSON_COPY_STR_TO_OBJECT(uname_, "domainname", un.domainname);
|
|
JSON_COPY_STR_TO_OBJECT(uname_, "machine", un.machine);
|
|
JSON_COPY_STR_TO_OBJECT(uname_, "nodename", un.nodename);
|
|
JSON_COPY_STR_TO_OBJECT(uname_, "release", un.release);
|
|
JSON_COPY_STR_TO_OBJECT(uname_, "sysname", un.sysname);
|
|
JSON_COPY_STR_TO_OBJECT(uname_, "version", un.version);
|
|
JSON_ADD_ITEM_TO_OBJECT(host, "uname", uname_);
|
|
|
|
JSON_ADD_ITEM_TO_OBJECT(host, "model", read_sys_property("/sys/firmware/devicetree/base/model"));
|
|
|
|
cJSON *dmi = JSON_NEW_OBJECT();
|
|
cJSON *bios = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(bios, "vendor", read_sys_property("/sys/devices/virtual/dmi/id/bios_vendor"));
|
|
|
|
cJSON *board = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(board, "name", read_sys_property("/sys/devices/virtual/dmi/id/board_name"));
|
|
JSON_ADD_ITEM_TO_OBJECT(board, "vendor", read_sys_property("/sys/devices/virtual/dmi/id/board_vendor"));
|
|
JSON_ADD_ITEM_TO_OBJECT(board, "version", read_sys_property("/sys/devices/virtual/dmi/id/board_version"));
|
|
|
|
cJSON *product = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(product, "name", read_sys_property("/sys/devices/virtual/dmi/id/product_name"));
|
|
JSON_ADD_ITEM_TO_OBJECT(product, "family", read_sys_property("/sys/devices/virtual/dmi/id/product_family"));
|
|
JSON_ADD_ITEM_TO_OBJECT(product, "version", read_sys_property("/sys/devices/virtual/dmi/id/product_version"));
|
|
|
|
cJSON *sys = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(sys, "vendor", read_sys_property("/sys/devices/virtual/dmi/id/sys_vendor"));
|
|
|
|
JSON_ADD_ITEM_TO_OBJECT(dmi, "bios", bios);
|
|
JSON_ADD_ITEM_TO_OBJECT(dmi, "board", board);
|
|
JSON_ADD_ITEM_TO_OBJECT(dmi, "product", product);
|
|
JSON_ADD_ITEM_TO_OBJECT(dmi, "sys", sys);
|
|
JSON_ADD_ITEM_TO_OBJECT(host, "dmi", dmi);
|
|
|
|
// All okay
|
|
return 0;
|
|
}
|
|
|
|
static int get_ftl_obj(struct ftl_conn *api, cJSON *ftl)
|
|
{
|
|
cJSON *database = JSON_NEW_OBJECT();
|
|
|
|
// Source from shared objects within lock
|
|
lock_shm();
|
|
const int db_gravity = counters->database.gravity;
|
|
const int db_groups = counters->database.groups;
|
|
const int db_lists = counters->database.lists;
|
|
const int db_clients = counters->database.clients;
|
|
const int db_allowed = counters->database.domains.allowed;
|
|
const int db_denied = counters->database.domains.denied;
|
|
const int clients_total = counters->clients;
|
|
const int privacylevel = config.misc.privacylevel.v.privacy_level;
|
|
|
|
// unique_clients: count only clients that have been active within the most recent 24 hours
|
|
int activeclients = 0;
|
|
for(int clientID=0; clientID < counters->clients; clientID++)
|
|
{
|
|
// Get client pointer
|
|
const clientsData* client = getClient(clientID, true);
|
|
if(client == NULL)
|
|
continue;
|
|
|
|
if(client->count > 0)
|
|
activeclients++;
|
|
}
|
|
unlock_shm();
|
|
|
|
JSON_ADD_NUMBER_TO_OBJECT(database, "gravity", db_gravity);
|
|
JSON_ADD_NUMBER_TO_OBJECT(database, "groups", db_groups);
|
|
JSON_ADD_NUMBER_TO_OBJECT(database, "lists", db_lists);
|
|
JSON_ADD_NUMBER_TO_OBJECT(database, "clients", db_clients);
|
|
|
|
cJSON *domains = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(domains, "allowed", db_allowed);
|
|
JSON_ADD_NUMBER_TO_OBJECT(domains, "denied", db_denied);
|
|
JSON_ADD_ITEM_TO_OBJECT(database, "domains", domains);
|
|
JSON_ADD_ITEM_TO_OBJECT(ftl, "database", database);
|
|
|
|
JSON_ADD_NUMBER_TO_OBJECT(ftl, "privacy_level", privacylevel);
|
|
|
|
cJSON *clients = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(clients, "total",clients_total);
|
|
JSON_ADD_NUMBER_TO_OBJECT(clients, "active", activeclients);
|
|
JSON_ADD_ITEM_TO_OBJECT(ftl, "clients", clients);
|
|
|
|
JSON_ADD_NUMBER_TO_OBJECT(ftl, "pid", getpid());
|
|
|
|
JSON_ADD_NUMBER_TO_OBJECT(ftl, "uptime", timer_elapsed_msec(EXIT_TIMER));
|
|
|
|
struct proc_mem pmem = { 0 };
|
|
struct proc_meminfo mem = { 0 };
|
|
parse_proc_meminfo(&mem);
|
|
getProcessMemory(&pmem, mem.total);
|
|
JSON_ADD_NUMBER_TO_OBJECT(ftl, "%mem", pmem.VmRSS_percent);
|
|
JSON_ADD_NUMBER_TO_OBJECT(ftl, "%cpu", get_cpu_percentage());
|
|
|
|
JSON_ADD_BOOL_TO_OBJECT(ftl, "allow_destructive", config.webserver.api.allow_destructive.v.b);
|
|
|
|
// dnsmasq struct
|
|
cJSON *dnsmasq = JSON_NEW_OBJECT();
|
|
get_dnsmasq_metrics_obj(dnsmasq);
|
|
JSON_ADD_ITEM_TO_OBJECT(ftl, "dnsmasq", dnsmasq);
|
|
|
|
// All okay
|
|
return 0;
|
|
}
|
|
|
|
int api_info_system(struct ftl_conn *api)
|
|
{
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
|
|
// Get system object
|
|
cJSON *system = JSON_NEW_OBJECT();
|
|
int ret = get_system_obj(api, system);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "system", system);
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
int api_info_ftl(struct ftl_conn *api)
|
|
{
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
|
|
// Get ftl object
|
|
cJSON *ftl = JSON_NEW_OBJECT();
|
|
int ret = get_ftl_obj(api, ftl);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "ftl", ftl);
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
int api_info_host(struct ftl_conn *api)
|
|
{
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
|
|
// Get host object
|
|
cJSON *host = JSON_NEW_OBJECT();
|
|
int ret = get_host_obj(api, host);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "host", host);
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
int api_info_sensors(struct ftl_conn *api)
|
|
{
|
|
cJSON *sensors = JSON_NEW_OBJECT();
|
|
|
|
// Get sensors array
|
|
cJSON *list = JSON_NEW_ARRAY();
|
|
int ret = get_hwmon_sensors(api, list);
|
|
if (ret != 0)
|
|
return ret;
|
|
JSON_ADD_ITEM_TO_OBJECT(sensors, "list", list);
|
|
|
|
// Loop over available sensors and try to identify the most suitable CPU temperature sensor
|
|
int cpu_temp_sensor = -1;
|
|
|
|
// Loop over all sensors
|
|
for(int i = 0; i < cJSON_GetArraySize(list); i++)
|
|
{
|
|
// Get sensor object
|
|
cJSON *sensor = cJSON_GetArrayItem(list, i);
|
|
|
|
// Get sensor name
|
|
cJSON *name = cJSON_GetObjectItemCaseSensitive(sensor, "name");
|
|
if(!cJSON_IsString(name) || name->valuestring == NULL)
|
|
continue;
|
|
|
|
// 1. AMD CPU temperature sensor
|
|
// 2. Intel CPU temperature sensor
|
|
// 3. General CPU temperature sensor
|
|
// 4. General SoC temperature sensor (https://discourse.pi-hole.net/t/temperature-value-not-shown/66883)
|
|
if(strcmp(name->valuestring, "k10temp") == 0 ||
|
|
strcmp(name->valuestring, "coretemp") == 0 ||
|
|
strcmp(name->valuestring, "cpu_thermal") == 0 ||
|
|
strcmp(name->valuestring, "soc_thermal") == 0)
|
|
{
|
|
cpu_temp_sensor = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add CPU temperature sensor
|
|
if(cpu_temp_sensor >= 0)
|
|
{
|
|
cJSON *sensor_group = cJSON_GetArrayItem(list, cpu_temp_sensor);
|
|
cJSON *sensors_array = cJSON_GetObjectItemCaseSensitive(sensor_group, "temps");
|
|
cJSON *first_sensor = cJSON_GetArrayItem(sensors_array, 0);
|
|
cJSON *first_sensor_value = cJSON_GetObjectItemCaseSensitive(first_sensor, "value");
|
|
if(cJSON_IsNumber(first_sensor_value))
|
|
JSON_ADD_NUMBER_TO_OBJECT(sensors, "cpu_temp", first_sensor_value->valuedouble);
|
|
else
|
|
JSON_ADD_NULL_TO_OBJECT(sensors, "cpu_temp");
|
|
}
|
|
else
|
|
{
|
|
JSON_ADD_NULL_TO_OBJECT(sensors, "cpu_temp");
|
|
}
|
|
|
|
// Add hot limit
|
|
JSON_ADD_NUMBER_TO_OBJECT(sensors, "hot_limit", config.webserver.api.temp.limit.v.d);
|
|
|
|
// Add unit
|
|
const char *unit = "C";
|
|
if(config.webserver.api.temp.unit.v.temp_unit == TEMP_UNIT_F)
|
|
unit = "F";
|
|
else if(config.webserver.api.temp.unit.v.temp_unit == TEMP_UNIT_K)
|
|
unit = "K";
|
|
JSON_REF_STR_IN_OBJECT(sensors, "unit", unit);
|
|
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "sensors", sensors);
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
int api_info_version(struct ftl_conn *api)
|
|
{
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
ssize_t read;
|
|
char *key, *value;
|
|
cJSON *core_local = JSON_NEW_OBJECT();
|
|
cJSON *web_local = JSON_NEW_OBJECT();
|
|
cJSON *ftl_local = JSON_NEW_OBJECT();
|
|
cJSON *core_remote = JSON_NEW_OBJECT();
|
|
cJSON *web_remote = JSON_NEW_OBJECT();
|
|
cJSON *ftl_remote = JSON_NEW_OBJECT();
|
|
cJSON *docker = JSON_NEW_OBJECT();
|
|
|
|
FILE *fp = fopen(VERSIONS_FILE, "r");
|
|
if(!fp)
|
|
return send_json_error(api, 500,
|
|
"internal_error",
|
|
"Failed to read " VERSIONS_FILE,
|
|
NULL);
|
|
|
|
// Loop over KEY=VALUE parts in the versions file
|
|
while((read = getline(&line, &len, fp)) != -1)
|
|
{
|
|
if (parse_line(line, &key, &value))
|
|
continue;
|
|
|
|
if(strcmp(key, "CORE_BRANCH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(core_local, "branch", value);
|
|
else if(strcmp(key, "WEB_BRANCH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(web_local, "branch", value);
|
|
// Added below from the running FTL binary itself
|
|
//else if(strcmp(key, "FTL_BRANCH") == 0)
|
|
// JSON_COPY_STR_TO_OBJECT(ftl_local, "branch", value);
|
|
else if(strcmp(key, "CORE_VERSION") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(core_local, "version", value);
|
|
else if(strcmp(key, "WEB_VERSION") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(web_local, "version", value);
|
|
// Added below from the running FTL binary itself
|
|
//else if(strcmp(key, "FTL_VERSION") == 0)
|
|
// JSON_COPY_STR_TO_OBJECT(ftl_local, "version", value);
|
|
else if(strcmp(key, "GITHUB_CORE_VERSION") == 0)
|
|
{
|
|
if(strcmp(value, "null") == 0)
|
|
JSON_ADD_NULL_TO_OBJECT(core_remote, "version");
|
|
else
|
|
JSON_COPY_STR_TO_OBJECT(core_remote, "version", value);
|
|
}
|
|
else if(strcmp(key, "GITHUB_WEB_VERSION") == 0)
|
|
{
|
|
if(strcmp(value, "null") == 0)
|
|
JSON_ADD_NULL_TO_OBJECT(web_remote, "version");
|
|
else
|
|
JSON_COPY_STR_TO_OBJECT(web_remote, "version", value);
|
|
}
|
|
else if(strcmp(key, "GITHUB_FTL_VERSION") == 0)
|
|
{
|
|
if(strcmp(value, "null") == 0)
|
|
JSON_ADD_NULL_TO_OBJECT(ftl_remote, "version");
|
|
else
|
|
JSON_COPY_STR_TO_OBJECT(ftl_remote, "version", value);
|
|
}
|
|
else if(strcmp(key, "CORE_HASH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(core_local, "hash", value);
|
|
else if(strcmp(key, "WEB_HASH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(web_local, "hash", value);
|
|
else if(strcmp(key, "FTL_HASH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(ftl_local, "hash", value);
|
|
else if(strcmp(key, "GITHUB_CORE_HASH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(core_remote, "hash", value);
|
|
else if(strcmp(key, "GITHUB_WEB_HASH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(web_remote, "hash", value);
|
|
else if(strcmp(key, "GITHUB_FTL_HASH") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(ftl_remote, "hash", value);
|
|
else if(strcmp(key, "DOCKER_VERSION") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(docker, "local", value);
|
|
else if(strcmp(key, "GITHUB_DOCKER_VERSION") == 0)
|
|
JSON_COPY_STR_TO_OBJECT(docker, "remote", value);
|
|
}
|
|
|
|
// Free allocated memory and release file pointer
|
|
free(line);
|
|
fclose(fp);
|
|
|
|
// Add remaining properties to ftl object
|
|
JSON_REF_STR_IN_OBJECT(ftl_local, "branch", GIT_BRANCH);
|
|
JSON_REF_STR_IN_OBJECT(ftl_local, "version", get_FTL_version());
|
|
JSON_REF_STR_IN_OBJECT(ftl_local, "date", GIT_DATE);
|
|
|
|
cJSON *version = JSON_NEW_OBJECT();
|
|
|
|
cJSON *core = JSON_NEW_OBJECT();
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(core_local, "branch");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(core_local, "version");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(core_local, "hash");
|
|
JSON_ADD_ITEM_TO_OBJECT(core, "local", core_local);
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(core_remote, "version");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(core_remote, "hash");
|
|
JSON_ADD_ITEM_TO_OBJECT(core, "remote", core_remote);
|
|
JSON_ADD_ITEM_TO_OBJECT(version, "core", core);
|
|
|
|
cJSON *web = JSON_NEW_OBJECT();
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(web_local, "branch");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(web_local, "version");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(web_local, "hash");
|
|
JSON_ADD_ITEM_TO_OBJECT(web, "local", web_local);
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(web_remote, "version");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(web_remote, "hash");
|
|
JSON_ADD_ITEM_TO_OBJECT(web, "remote", web_remote);
|
|
JSON_ADD_ITEM_TO_OBJECT(version, "web", web);
|
|
|
|
cJSON *ftl = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(ftl, "local", ftl_local);
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(ftl_local, "branch");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(ftl_local, "version");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(ftl_local, "hash");
|
|
JSON_ADD_ITEM_TO_OBJECT(ftl, "remote", ftl_remote);
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(ftl_remote, "version");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(ftl_remote, "hash");
|
|
JSON_ADD_ITEM_TO_OBJECT(version, "ftl", ftl);
|
|
|
|
// Add nulls to docker if we didn't find any version
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(docker, "local");
|
|
JSON_ADD_NULL_IF_NOT_EXISTS(docker, "remote");
|
|
JSON_ADD_ITEM_TO_OBJECT(version, "docker", docker);
|
|
|
|
// Send reply
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "version", version);
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
int api_info_messages_count(struct ftl_conn *api)
|
|
{
|
|
// Filtering based on GET parameters?
|
|
bool filter_dnsmasq_warnings = false;
|
|
if(api->request->query_string != NULL)
|
|
{
|
|
get_bool_var(api->request->query_string, "filter_dnsmasq_warnings", &filter_dnsmasq_warnings);
|
|
}
|
|
|
|
// Send reply
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
cJSON_AddNumberToObject(json, "count", count_messages(filter_dnsmasq_warnings));
|
|
JSON_SEND_OBJECT(json);
|
|
return 0;
|
|
}
|
|
|
|
static int api_info_messages_GET(struct ftl_conn *api)
|
|
{
|
|
// Filtering based on GET parameters?
|
|
bool filter_dnsmasq_warnings = false;
|
|
if(api->request->query_string != NULL)
|
|
{
|
|
get_bool_var(api->request->query_string, "filter_dnsmasq_warnings", &filter_dnsmasq_warnings);
|
|
}
|
|
|
|
// Create messages array
|
|
cJSON *messages = cJSON_CreateArray();
|
|
if(!format_messages(messages))
|
|
{
|
|
// Send error reply
|
|
cJSON_Delete(messages);
|
|
return send_json_error(api, 500, // 500 Internal Server Error
|
|
"internal_error",
|
|
"An internal error occurred while formatting the messages array.",
|
|
NULL);
|
|
}
|
|
|
|
// Filter messages if requested
|
|
if(filter_dnsmasq_warnings)
|
|
{
|
|
// Create new array
|
|
cJSON *filtered = cJSON_CreateArray();
|
|
|
|
// Loop over all messages
|
|
for(int i = 0; i < cJSON_GetArraySize(messages); i++)
|
|
{
|
|
// Get message
|
|
cJSON *message = cJSON_GetArrayItem(messages, i);
|
|
if(message == NULL)
|
|
continue;
|
|
|
|
// Get type
|
|
cJSON *type = cJSON_GetObjectItem(message, "type");
|
|
if(type == NULL)
|
|
continue;
|
|
|
|
// Skip if it is a DNSMASQ_WARN message
|
|
if(strcmp(type->valuestring, "DNSMASQ_WARN") == 0)
|
|
continue;
|
|
|
|
// else: Add a copy to the filtered array
|
|
cJSON_AddItemToArray(filtered, cJSON_Duplicate(message, true));
|
|
}
|
|
|
|
// Free old array and replace with filtered one
|
|
cJSON_Delete(messages);
|
|
messages = filtered;
|
|
}
|
|
|
|
// Create object, add the array and send them
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "messages", messages);
|
|
JSON_SEND_OBJECT(json);
|
|
}
|
|
|
|
static int api_info_messages_DELETE(struct ftl_conn *api)
|
|
{
|
|
// Check if we have an ID
|
|
errno = 0;
|
|
if(api->item == NULL)
|
|
{
|
|
// Send error reply
|
|
return send_json_error(api, 400, // 400 Bad Request
|
|
"uri_error",
|
|
"Specify ID of message to delete in path",
|
|
api->action_path);
|
|
}
|
|
|
|
// Split ID at commas and validate every ID as a number
|
|
cJSON *ids = cJSON_CreateArray();
|
|
char *id = strdup(api->item);
|
|
char *saveptr = NULL;
|
|
char *token = strtok_r(id, ",", &saveptr);
|
|
while(token != NULL)
|
|
{
|
|
// Validate ID
|
|
char *endptr = NULL;
|
|
long int idval = strtol(token, &endptr, 10);
|
|
if(errno != 0 || endptr == token || *endptr != '\0' || idval < 0)
|
|
{
|
|
// Send error reply
|
|
free(id);
|
|
return send_json_error(api, 400, // 400 Bad Request
|
|
"uri_error",
|
|
"Invalid ID in path",
|
|
api->action_path);
|
|
}
|
|
|
|
cJSON_AddNumberToObject(ids, "id", idval);
|
|
|
|
// Get next token
|
|
token = strtok_r(NULL, ",", &saveptr);
|
|
}
|
|
|
|
// Delete message with this ID from the database
|
|
int deleted = 0;
|
|
delete_message(ids, &deleted);
|
|
|
|
// Free memory
|
|
free(id);
|
|
cJSON_free(ids);
|
|
|
|
// Send empty reply with codes:
|
|
// - 204 No Content (if any items were deleted)
|
|
// - 404 Not Found (if no items were deleted)
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
JSON_SEND_OBJECT_CODE(json, deleted > 0 ? 204 : 404);
|
|
}
|
|
|
|
int api_info_messages(struct ftl_conn *api)
|
|
{
|
|
if(api->method == HTTP_GET)
|
|
return api_info_messages_GET(api);
|
|
else if(api->method == HTTP_DELETE)
|
|
return api_info_messages_DELETE(api);
|
|
else
|
|
return send_json_error(api, 405, "method_not_allowed", "Method not allowed", NULL);
|
|
}
|
|
|
|
int api_info_metrics(struct ftl_conn *api)
|
|
{
|
|
struct metrics metrics = { 0 };
|
|
get_dnsmasq_metrics(&metrics);
|
|
cJSON *cache = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(cache, "size", metrics.dns.cache.size);
|
|
JSON_ADD_NUMBER_TO_OBJECT(cache, "inserted", metrics.dns.cache.inserted);
|
|
JSON_ADD_NUMBER_TO_OBJECT(cache, "evicted", metrics.dns.cache.live_freed);
|
|
JSON_ADD_NUMBER_TO_OBJECT(cache, "expired", metrics.dns.cache.expired);
|
|
JSON_ADD_NUMBER_TO_OBJECT(cache, "immortal", metrics.dns.cache.immortal);
|
|
|
|
cJSON *content = JSON_NEW_ARRAY();
|
|
for(unsigned int i = 0; i < RRTYPES; i++)
|
|
{
|
|
// Skip empty entries
|
|
if(metrics.dns.cache.content[i].count[CACHE_VALID] == 0 &&
|
|
metrics.dns.cache.content[i].count[CACHE_STALE] == 0)
|
|
continue;
|
|
|
|
// Add this entry to the array
|
|
cJSON *obj = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(obj, "type", metrics.dns.cache.content[i].type);
|
|
JSON_REF_STR_IN_OBJECT(obj, "name", rrtype_name(metrics.dns.cache.content[i].type));
|
|
cJSON *count = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(count, "valid", metrics.dns.cache.content[i].count[CACHE_VALID]);
|
|
JSON_ADD_NUMBER_TO_OBJECT(count, "stale", metrics.dns.cache.content[i].count[CACHE_STALE]);
|
|
JSON_ADD_ITEM_TO_OBJECT(obj, "count", count);
|
|
JSON_ADD_ITEM_TO_ARRAY(content, obj);
|
|
}
|
|
JSON_ADD_ITEM_TO_OBJECT(cache, "content", content);
|
|
|
|
cJSON *replies = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(replies, "local", metrics.dns.local_answered);
|
|
JSON_ADD_NUMBER_TO_OBJECT(replies, "forwarded", metrics.dns.forwarded_queries);
|
|
JSON_ADD_NUMBER_TO_OBJECT(replies, "optimized", metrics.dns.stale_answered);
|
|
JSON_ADD_NUMBER_TO_OBJECT(replies, "unanswered", metrics.dns.unanswered_queries);
|
|
JSON_ADD_NUMBER_TO_OBJECT(replies, "auth", metrics.dns.auth_answered);
|
|
const int sum = metrics.dns.local_answered
|
|
+ metrics.dns.forwarded_queries
|
|
+ metrics.dns.stale_answered
|
|
+ metrics.dns.unanswered_queries
|
|
+ metrics.dns.auth_answered;
|
|
JSON_ADD_NUMBER_TO_OBJECT(replies, "sum", sum);
|
|
|
|
cJSON *dhcp = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "ack", metrics.dhcp.ack);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "decline", metrics.dhcp.decline);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "discover", metrics.dhcp.discover);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "inform", metrics.dhcp.inform);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "nak", metrics.dhcp.nak);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "offer", metrics.dhcp.offer);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "release", metrics.dhcp.release);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "request", metrics.dhcp.request);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "noanswer", metrics.dhcp.noanswer);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "bootp", metrics.dhcp.bootp);
|
|
JSON_ADD_NUMBER_TO_OBJECT(dhcp, "pxe", metrics.dhcp.pxe);
|
|
|
|
cJSON *leases = JSON_NEW_OBJECT();
|
|
JSON_ADD_NUMBER_TO_OBJECT(leases, "allocated_4", metrics.dhcp.leases.allocated_4);
|
|
JSON_ADD_NUMBER_TO_OBJECT(leases, "pruned_4", metrics.dhcp.leases.pruned_4);
|
|
JSON_ADD_NUMBER_TO_OBJECT(leases, "allocated_6", metrics.dhcp.leases.allocated_6);
|
|
JSON_ADD_NUMBER_TO_OBJECT(leases, "pruned_6", metrics.dhcp.leases.pruned_6);
|
|
JSON_ADD_ITEM_TO_OBJECT(dhcp, "leases", leases);
|
|
|
|
cJSON *dns = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(dns, "cache", cache);
|
|
JSON_ADD_ITEM_TO_OBJECT(dns, "replies", replies);
|
|
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "dns", dns);
|
|
JSON_ADD_ITEM_TO_OBJECT(json, "dhcp", dhcp);
|
|
|
|
cJSON *json2 = JSON_NEW_OBJECT();
|
|
JSON_ADD_ITEM_TO_OBJECT(json2, "metrics", json);
|
|
JSON_SEND_OBJECT(json2);
|
|
}
|
|
|
|
int api_info_login(struct ftl_conn *api)
|
|
{
|
|
cJSON *json = JSON_NEW_OBJECT();
|
|
|
|
const bool dns = get_blockingstatus() != DNS_FAILED;
|
|
JSON_ADD_BOOL_TO_OBJECT(json, "dns", dns);
|
|
|
|
JSON_ADD_NUMBER_TO_OBJECT(json, "https_port", get_https_port());
|
|
|
|
JSON_SEND_OBJECT(json);
|
|
}
|