Implement proper testing of dnsmasq options before applying new configuration
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
parent
2d6c25d573
commit
7b479782fd
|
@ -113,6 +113,7 @@ int api_handler(struct mg_connection *conn, void *ignored)
|
|||
int ret = 0;
|
||||
|
||||
// Loop over all API endpoints and check if the requested URI matches
|
||||
bool unauthorized = false;
|
||||
for(unsigned int i = 0; i < sizeof(api_request)/sizeof(api_request[0]); i++)
|
||||
{
|
||||
// Check if the requested method is allowed
|
||||
|
@ -127,7 +128,10 @@ int api_handler(struct mg_connection *conn, void *ignored)
|
|||
|
||||
// Verify requesting client is allowed to see this ressource
|
||||
if(api_request[i].require_auth && check_client_auth(&api) == API_AUTH_UNAUTHORIZED)
|
||||
return send_json_unauthorized(&api);
|
||||
{
|
||||
unauthorized = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Call the API function and get the return code
|
||||
log_debug(DEBUG_API, "Sending to %s", api_request[i].uri);
|
||||
|
@ -163,11 +167,16 @@ int api_handler(struct mg_connection *conn, void *ignored)
|
|||
"Not found",
|
||||
api.request->local_uri_raw);
|
||||
}
|
||||
else if(unauthorized)
|
||||
{
|
||||
// Return with unauthorized payload
|
||||
// Do this only after having cleaned up above
|
||||
return send_json_unauthorized(&api);
|
||||
}
|
||||
|
||||
// Restart FTL if requested
|
||||
if(api.ftl.restart)
|
||||
{
|
||||
// Trigger an automatic restart by systemd
|
||||
exit_code = RESTART_FTL_CODE;
|
||||
FTL_terminate = 1;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "config/toml_writer.h"
|
||||
// write_dnsmasq_config()
|
||||
#include "config/dnsmasq_config.h"
|
||||
// shm_lock()
|
||||
#include "shmem.h"
|
||||
|
||||
// The following functions are used to create the JSON output
|
||||
// of the /api/config endpoint.
|
||||
|
@ -352,7 +354,7 @@ static int api_config_get(struct ftl_conn *api)
|
|||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *conf_item = get_conf_item(i);
|
||||
struct conf_item *conf_item = get_conf_item(&config, i);
|
||||
|
||||
// Get path depth
|
||||
unsigned int level = config_path_depth(conf_item->p);
|
||||
|
@ -455,10 +457,12 @@ static int api_config_patch(struct ftl_conn *api)
|
|||
|
||||
// Read all known config items
|
||||
bool dnsmasq_changed = false;
|
||||
struct config conf_copy;
|
||||
duplicate_config(&conf_copy);
|
||||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *conf_item = get_conf_item(i);
|
||||
struct conf_item *conf_item = get_conf_item(&conf_copy, i);
|
||||
|
||||
// Get path depth
|
||||
unsigned int level = config_path_depth(conf_item->p);
|
||||
|
@ -489,16 +493,37 @@ static int api_config_patch(struct ftl_conn *api)
|
|||
dnsmasq_changed = true;
|
||||
}
|
||||
|
||||
// Reload debug levels
|
||||
set_debug_flags();
|
||||
// Request restart of FTL
|
||||
if(dnsmasq_changed)
|
||||
{
|
||||
char errbuf[ERRBUF_SIZE] = { 0 };
|
||||
if(write_dnsmasq_config(&conf_copy, true, errbuf))
|
||||
api->ftl.restart = true;
|
||||
else
|
||||
{
|
||||
return send_json_error(api, 400,
|
||||
"bad_request",
|
||||
"Invalid configuration",
|
||||
errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Install new configuration
|
||||
lock_shm();
|
||||
// Backup old config struct (so we can free it)
|
||||
struct config old_conf;
|
||||
memcpy(&old_conf, &config, sizeof(struct config));
|
||||
// Replace old config struct by changed one
|
||||
memcpy(&config, &conf_copy, sizeof(struct config));
|
||||
// Free old backup struct
|
||||
free_config(&old_conf);
|
||||
unlock_shm();
|
||||
|
||||
// Store changed configuration to disk
|
||||
writeFTLtoml(true);
|
||||
|
||||
// Request restart of FTL
|
||||
if(dnsmasq_changed)
|
||||
if(write_dnsmasq_config(true))
|
||||
api->ftl.restart = true;
|
||||
// Reload debug levels
|
||||
set_debug_flags();
|
||||
|
||||
// Return full config after possible changes above
|
||||
return api_config_get(api);
|
||||
|
@ -546,10 +571,12 @@ static int api_config_put_delete(struct ftl_conn *api)
|
|||
// Read all known config items
|
||||
bool dnsmasq_changed = false;
|
||||
bool found = false;
|
||||
struct config conf_copy;
|
||||
duplicate_config(&conf_copy);
|
||||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *conf_item = get_conf_item(i);
|
||||
struct conf_item *conf_item = get_conf_item(&conf_copy, i);
|
||||
|
||||
// We support PUT only for adding to string arrays
|
||||
if(conf_item->t != CONF_JSON_STRING_ARRAY)
|
||||
|
@ -637,13 +664,40 @@ static int api_config_put_delete(struct ftl_conn *api)
|
|||
hint);
|
||||
}
|
||||
|
||||
// We need to build a new config (and carefully test it!) whenever dnsmasq
|
||||
// options have changed that need a restart of the resolver
|
||||
if(dnsmasq_changed)
|
||||
{
|
||||
char errbuf[ERRBUF_SIZE] = { 0 };
|
||||
// Request restart of FTL
|
||||
if(write_dnsmasq_config(&conf_copy, true, errbuf))
|
||||
api->ftl.restart = true;
|
||||
else
|
||||
{
|
||||
// The new config did not work
|
||||
return send_json_error(api, 400,
|
||||
"bad_request",
|
||||
"Invalid configuration",
|
||||
errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Install new configuration
|
||||
lock_shm();
|
||||
// Backup old config struct (so we can free it)
|
||||
struct config old_conf;
|
||||
memcpy(&old_conf, &config, sizeof(struct config));
|
||||
// Replace old config struct by changed one
|
||||
memcpy(&config, &conf_copy, sizeof(struct config));
|
||||
// Free old backup struct
|
||||
free_config(&old_conf);
|
||||
unlock_shm();
|
||||
|
||||
// Store changed configuration to disk
|
||||
writeFTLtoml(true);
|
||||
|
||||
// Request restart of FTL
|
||||
if(dnsmasq_changed)
|
||||
if(write_dnsmasq_config(true))
|
||||
api->ftl.restart = false;
|
||||
// Reload debug levels
|
||||
set_debug_flags();
|
||||
|
||||
return api->method == HTTP_PUT ? 201 : 204; // 201 - Created or 204 - No content
|
||||
}
|
||||
|
|
12
src/args.c
12
src/args.c
|
@ -644,3 +644,15 @@ void parse_args(int argc, char* argv[])
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defined in src/dnsmasq/option.c
|
||||
extern void reset_usage_indicator(void);
|
||||
void test_dnsmasq_options(int argc, const char *argv[])
|
||||
{
|
||||
// Reset getopt before calling read_opts
|
||||
optind = 0;
|
||||
// Call dnsmasq's option parser
|
||||
reset_usage_indicator();
|
||||
// Call read_opts
|
||||
read_opts(argc, (char**)argv, NULL);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ const char *cli_done(void) __attribute__ ((pure));
|
|||
const char *cli_bold(void) __attribute__ ((pure));
|
||||
const char *cli_normal(void) __attribute__ ((pure));
|
||||
|
||||
void test_dnsmasq_options(int argc, const char *argv[]);
|
||||
|
||||
// defined in dnsmasq_interface.c
|
||||
int check_struct_sizes(void);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "config/config.h"
|
||||
#include "config/toml_helper.h"
|
||||
#include "config/toml_writer.h"
|
||||
#include "config/dnsmasq_config.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "datastructure.h"
|
||||
|
@ -255,11 +256,13 @@ static bool readStringvalue(struct conf_item *conf_item, const char *value)
|
|||
bool set_config_from_CLI(const char *key, const char *value)
|
||||
{
|
||||
// Identify config option
|
||||
struct config conf_copy;
|
||||
duplicate_config(&conf_copy);
|
||||
struct conf_item *conf_item = NULL;
|
||||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *item = get_conf_item(i);
|
||||
struct conf_item *item = get_conf_item(&conf_copy, i);
|
||||
|
||||
if(strcmp(item->k, key) != 0)
|
||||
continue;
|
||||
|
@ -277,16 +280,37 @@ bool set_config_from_CLI(const char *key, const char *value)
|
|||
}
|
||||
|
||||
// Parse value
|
||||
if(readStringvalue(conf_item, value))
|
||||
if(!readStringvalue(conf_item, value))
|
||||
return false;
|
||||
|
||||
// Is this a dnsmasq option we need to check?
|
||||
if(conf_item->restart_dnsmasq)
|
||||
{
|
||||
// Print value
|
||||
writeTOMLvalue(stdout, -1, conf_item->t, &conf_item->v);
|
||||
putchar('\n');
|
||||
writeFTLtoml(false);
|
||||
return true;
|
||||
char errbuf[ERRBUF_SIZE] = { 0 };
|
||||
if(!write_dnsmasq_config(&conf_copy, true, errbuf))
|
||||
{
|
||||
int lineno = get_lineno_from_string(errbuf);
|
||||
char *line = get_dnsmasq_line(lineno);
|
||||
log_err("Corresponding line: \"%s\"", line);
|
||||
free(line);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
// Install new configuration
|
||||
// Backup old config struct (so we can free it)
|
||||
struct config old_conf;
|
||||
memcpy(&old_conf, &config, sizeof(struct config));
|
||||
// Replace old config struct by changed one
|
||||
memcpy(&config, &conf_copy, sizeof(struct config));
|
||||
// Free old backup struct
|
||||
free_config(&old_conf);
|
||||
|
||||
// Print value
|
||||
writeTOMLvalue(stdout, -1, conf_item->t, &conf_item->v);
|
||||
putchar('\n');
|
||||
writeFTLtoml(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_config_from_CLI(const char *key)
|
||||
|
@ -296,7 +320,7 @@ bool get_config_from_CLI(const char *key)
|
|||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *item = get_conf_item(i);
|
||||
struct conf_item *item = get_conf_item(&config, i);
|
||||
|
||||
if(strcmp(item->k, key) != 0)
|
||||
continue;
|
||||
|
|
|
@ -29,7 +29,7 @@ void set_all_debug(const bool status)
|
|||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *conf_item = get_conf_item(i);
|
||||
struct conf_item *conf_item = get_conf_item(&config, i);
|
||||
|
||||
// Skip config entries whose path's are not starting in "debug."
|
||||
if(strcmp("debug", conf_item->p[0]) != 0)
|
||||
|
@ -131,8 +131,7 @@ bool __attribute__ ((pure)) check_paths_equal(char **paths1, char **paths2, unsi
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct conf_item *get_conf_item(const unsigned int n)
|
||||
struct conf_item *get_conf_item(struct config *conf, const unsigned int n)
|
||||
{
|
||||
// Sanity check
|
||||
if(n > CONFIG_ELEMENTS-1)
|
||||
|
@ -142,7 +141,7 @@ struct conf_item *get_conf_item(const unsigned int n)
|
|||
}
|
||||
|
||||
// Return n-th config element
|
||||
return (void*)&config + n*sizeof(struct conf_item);
|
||||
return (void*)conf + n*sizeof(struct conf_item);
|
||||
}
|
||||
|
||||
struct conf_item *get_debug_item(const enum debug_flag debug)
|
||||
|
@ -171,6 +170,92 @@ unsigned int __attribute__ ((pure)) config_path_depth(char **paths)
|
|||
|
||||
}
|
||||
|
||||
void duplicate_config(struct config *conf)
|
||||
{
|
||||
// Post-processing:
|
||||
// Initialize and verify config data
|
||||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item (original)
|
||||
struct conf_item *conf_item = get_conf_item(&config, i);
|
||||
|
||||
// Get pointer to memory location of this conf_item (copy)
|
||||
struct conf_item *copy_item = get_conf_item(conf, i);
|
||||
|
||||
// Copy constant/static fields
|
||||
memcpy(copy_item, conf_item, sizeof(*conf_item));
|
||||
|
||||
// Make a type-dependent copy of the value
|
||||
switch(conf_item->t)
|
||||
{
|
||||
case CONF_BOOL:
|
||||
case CONF_INT:
|
||||
case CONF_UINT:
|
||||
case CONF_UINT16:
|
||||
case CONF_LONG:
|
||||
case CONF_ULONG:
|
||||
case CONF_DOUBLE:
|
||||
case CONF_STRING:
|
||||
case CONF_ENUM_PTR_TYPE:
|
||||
case CONF_ENUM_BUSY_TYPE:
|
||||
case CONF_ENUM_BLOCKING_MODE:
|
||||
case CONF_ENUM_REFRESH_HOSTNAMES:
|
||||
case CONF_ENUM_PRIVACY_LEVEL:
|
||||
case CONF_ENUM_LISTENING_MODE:
|
||||
case CONF_STRUCT_IN_ADDR:
|
||||
case CONF_STRUCT_IN6_ADDR:
|
||||
// Nothing to do, the memcpy above has already covered this
|
||||
break;
|
||||
case CONF_STRING_ALLOCATED:
|
||||
copy_item->v.s = strdup(conf_item->v.s);
|
||||
break;
|
||||
case CONF_JSON_STRING_ARRAY:
|
||||
copy_item->v.json = cJSON_Duplicate(conf_item->v.json, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void free_config(struct config *conf)
|
||||
{
|
||||
// Post-processing:
|
||||
// Initialize and verify config data
|
||||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item (copy)
|
||||
struct conf_item *copy_item = get_conf_item(conf, i);
|
||||
|
||||
// Make a type-dependent copy of the value
|
||||
switch(copy_item->t)
|
||||
{
|
||||
case CONF_BOOL:
|
||||
case CONF_INT:
|
||||
case CONF_UINT:
|
||||
case CONF_UINT16:
|
||||
case CONF_LONG:
|
||||
case CONF_ULONG:
|
||||
case CONF_DOUBLE:
|
||||
case CONF_STRING:
|
||||
case CONF_ENUM_PTR_TYPE:
|
||||
case CONF_ENUM_BUSY_TYPE:
|
||||
case CONF_ENUM_BLOCKING_MODE:
|
||||
case CONF_ENUM_REFRESH_HOSTNAMES:
|
||||
case CONF_ENUM_PRIVACY_LEVEL:
|
||||
case CONF_ENUM_LISTENING_MODE:
|
||||
case CONF_STRUCT_IN_ADDR:
|
||||
case CONF_STRUCT_IN6_ADDR:
|
||||
// Nothing to do
|
||||
break;
|
||||
case CONF_STRING_ALLOCATED:
|
||||
free(copy_item->v.s);
|
||||
break;
|
||||
case CONF_JSON_STRING_ARRAY:
|
||||
cJSON_Delete(copy_item->v.json);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initConfig(void)
|
||||
{
|
||||
// struct dns
|
||||
|
@ -862,7 +947,7 @@ void initConfig(void)
|
|||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *conf_item = get_conf_item(i);
|
||||
struct conf_item *conf_item = get_conf_item(&config, i);
|
||||
|
||||
// Initialize config value with default one for all *except* the log file path
|
||||
if(conf_item != &config.files.log.ftl)
|
||||
|
@ -908,7 +993,7 @@ void readFTLconf(const bool rewrite)
|
|||
if(rewrite)
|
||||
{
|
||||
writeFTLtoml(true);
|
||||
write_dnsmasq_config(false);
|
||||
write_dnsmasq_config(&config, false, NULL);
|
||||
}
|
||||
read_legacy_dhcp_static_config();
|
||||
read_legacy_cnames_config();
|
||||
|
@ -939,7 +1024,7 @@ void readFTLconf(const bool rewrite)
|
|||
|
||||
// Initialize the TOML config file
|
||||
writeFTLtoml(true);
|
||||
write_dnsmasq_config(false);
|
||||
write_dnsmasq_config(&config, false, NULL);
|
||||
}
|
||||
|
||||
bool getLogFilePath(void)
|
||||
|
|
|
@ -28,26 +28,6 @@
|
|||
|
||||
#define GLOBALTOMLPATH "/etc/pihole/pihole-FTL.toml"
|
||||
|
||||
// Defined in config.c
|
||||
void set_all_debug(const bool status);
|
||||
void initConfig(void);
|
||||
void readFTLconf(const bool rewrite);
|
||||
bool getLogFilePath(void);
|
||||
struct conf_item *get_conf_item(unsigned int n);
|
||||
struct conf_item *get_debug_item(const enum debug_flag debug);
|
||||
unsigned int config_path_depth(char **paths) __attribute__ ((pure));
|
||||
char **gen_config_path(const char *pathin, const char delim);
|
||||
void free_config_path(char **paths);
|
||||
bool check_paths_equal(char **paths1, char **paths2, unsigned int max_level) __attribute__ ((pure));
|
||||
|
||||
// Defined in toml_reader.c
|
||||
bool getPrivacyLevel(void);
|
||||
bool getBlockingMode(void);
|
||||
bool readDebugSettings(void);
|
||||
void init_config_mutex(void);
|
||||
bool get_blockingstatus(void) __attribute__((pure));
|
||||
void set_blockingstatus(bool enabled);
|
||||
|
||||
union conf_value {
|
||||
bool b; // boolean value
|
||||
int i; // integer value
|
||||
|
@ -289,4 +269,26 @@ extern struct config config;
|
|||
#define CONFIG_ELEMENTS (sizeof(config)/sizeof(struct conf_item))
|
||||
#define DEBUG_ELEMENTS (sizeof(config.debug)/sizeof(struct conf_item))
|
||||
|
||||
// Defined in config.c
|
||||
void set_all_debug(const bool status);
|
||||
void initConfig(void);
|
||||
void readFTLconf(const bool rewrite);
|
||||
bool getLogFilePath(void);
|
||||
struct conf_item *get_conf_item(struct config *conf, const unsigned int n);
|
||||
struct conf_item *get_debug_item(const enum debug_flag debug);
|
||||
unsigned int config_path_depth(char **paths) __attribute__ ((pure));
|
||||
void duplicate_config(struct config *conf);
|
||||
void free_config(struct config *conf);
|
||||
char **gen_config_path(const char *pathin, const char delim);
|
||||
void free_config_path(char **paths);
|
||||
bool check_paths_equal(char **paths1, char **paths2, unsigned int max_level) __attribute__ ((pure));
|
||||
|
||||
// Defined in toml_reader.c
|
||||
bool getPrivacyLevel(void);
|
||||
bool getBlockingMode(void);
|
||||
bool readDebugSettings(void);
|
||||
void init_config_mutex(void);
|
||||
bool get_blockingstatus(void) __attribute__((pure));
|
||||
void set_blockingstatus(bool enabled);
|
||||
|
||||
#endif //CONFIG_H
|
||||
|
|
|
@ -24,16 +24,145 @@
|
|||
#include "files.h"
|
||||
// trim_whitespace()
|
||||
#include "setupVars.h"
|
||||
// run_dnsmasq_main()
|
||||
#include "args.h"
|
||||
// optind
|
||||
#include <unistd.h>
|
||||
// wait
|
||||
#include <sys/wait.h>
|
||||
|
||||
static bool test_dnsmasq_config(void)
|
||||
static bool test_dnsmasq_config(char errbuf[ERRBUF_SIZE])
|
||||
{
|
||||
FILE *pipe = popen("pihole-FTL dnsmasq-test-file "DNSMASQ_TEMP_CONF, "r");
|
||||
if(!pipe)
|
||||
// Create a pipe for communication with our child
|
||||
int pipefd[2];
|
||||
if(pipe(pipefd) !=0)
|
||||
{
|
||||
log_err("Cannot open pipe to pihole-FTL dnsmasq-test-file: %s", strerror(errno));
|
||||
log_err("Cannot create pipe while testing new dnsmasq config: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return WEXITSTATUS(pclose(pipe)) == EXIT_SUCCESS;
|
||||
|
||||
// Fork!
|
||||
pid_t cpid = fork();
|
||||
int code = -1;
|
||||
bool crashed = false;
|
||||
if (cpid == 0)
|
||||
{
|
||||
/*** CHILD ***/
|
||||
// Close the reading end of the pipe
|
||||
close(pipefd[0]);
|
||||
|
||||
const char *argv[3];
|
||||
argv[0] = "X";
|
||||
argv[1] = "--conf-file="DNSMASQ_TEMP_CONF;
|
||||
argv[2] = "--test";
|
||||
|
||||
// Disable logging
|
||||
log_ctrl(false, false);
|
||||
|
||||
// Flush STDERR
|
||||
fflush(stderr);
|
||||
|
||||
// Redirect STDERR into our pipe
|
||||
dup2(pipefd[1], STDERR_FILENO);
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
|
||||
// Call dnsmasq's option parser
|
||||
test_dnsmasq_options(3, argv);
|
||||
|
||||
// We'll never actually reach this point as test_dnsmasq_options() will
|
||||
// exit. We still close the fork nicely in case other stumble upon this
|
||||
// code and want to use it in their projects
|
||||
|
||||
// Close the writing end of the pipe, thus sending EOF to the reader
|
||||
close(pipefd[1]);
|
||||
|
||||
// Exit the fork
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*** PARENT ***/
|
||||
// Close the writing end of the pipe
|
||||
close(pipefd[1]);
|
||||
|
||||
// Read readirected STDERR until EOF
|
||||
if(errbuf != NULL)
|
||||
{
|
||||
// We are only interested in the last pipe line
|
||||
while(read(pipefd[0], errbuf, ERRBUF_SIZE) > 0)
|
||||
{
|
||||
// Remove initial newline character (if present)
|
||||
if(errbuf[0] == '\n')
|
||||
memmove(errbuf, &errbuf[1], ERRBUF_SIZE-1);
|
||||
// Strip newline character (if present)
|
||||
if(errbuf[strlen(errbuf)-1] == '\n')
|
||||
errbuf[strlen(errbuf)-1] = '\0';
|
||||
log_debug(DEBUG_CONFIG, "dnsmasq pipe: %s", errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until child has exited to get its return code
|
||||
int status;
|
||||
waitpid(cpid, &status, 0);
|
||||
code = WEXITSTATUS(status);
|
||||
|
||||
if(WIFSIGNALED(status))
|
||||
{
|
||||
crashed = true;
|
||||
log_err("dnsmasq test failed with signal %d %s",
|
||||
WTERMSIG(status),
|
||||
WCOREDUMP(status) ? "(core dumped)" : "");
|
||||
}
|
||||
|
||||
log_debug(DEBUG_CONFIG, "Code: %d", code);
|
||||
|
||||
// Close the reading end of the pipe
|
||||
close(pipefd[0]);
|
||||
}
|
||||
|
||||
return code == EXIT_SUCCESS && !crashed;
|
||||
}
|
||||
|
||||
int get_lineno_from_string(const char *string)
|
||||
{
|
||||
int lineno = -1;
|
||||
char *ptr = strstr(string, " at line ");
|
||||
if(ptr == NULL)
|
||||
return -1;
|
||||
if(sscanf(ptr, " at line %d of ", &lineno) == 1)
|
||||
return lineno;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *get_dnsmasq_line(const unsigned int lineno)
|
||||
{
|
||||
// Open temporary file
|
||||
FILE *fp = fopen(DNSMASQ_TEMP_CONF, "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
log_warn("Cannot read "DNSMASQ_TEMP_CONF);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read file line-by-line until we reach the requested line
|
||||
char *linebuffer = NULL;
|
||||
size_t size = 0u;
|
||||
unsigned int count = 1;
|
||||
while(getline(&linebuffer, &size, fp) != -1)
|
||||
{
|
||||
if (count == lineno)
|
||||
{
|
||||
fclose(fp);
|
||||
if(linebuffer[strlen(linebuffer)] == '\n')
|
||||
linebuffer[strlen(linebuffer)] = '\0';
|
||||
return linebuffer;
|
||||
}
|
||||
else
|
||||
count++;
|
||||
}
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void write_config_header(FILE *fp)
|
||||
|
@ -66,8 +195,9 @@ static void write_config_header(FILE *fp)
|
|||
fputs("###############################################################################\n\n", fp);
|
||||
}
|
||||
|
||||
bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
||||
bool __attribute__((const)) write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE])
|
||||
{
|
||||
log_debug(DEBUG_CONFIG, "Opening "DNSMASQ_TEMP_CONF" for writing");
|
||||
FILE *pihole_conf = fopen(DNSMASQ_TEMP_CONF, "w");
|
||||
// Return early if opening failed
|
||||
if(!pihole_conf)
|
||||
|
@ -92,14 +222,14 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
fputs("no-resolv\n", pihole_conf);
|
||||
fputs("\n", pihole_conf);
|
||||
fputs("# DNS port to be used\n", pihole_conf);
|
||||
fprintf(pihole_conf, "port=%u\n", config.dnsmasq.port.v.u16);
|
||||
if(cJSON_GetArraySize(config.dnsmasq.upstreams.v.json) > 0)
|
||||
fprintf(pihole_conf, "port=%u\n", conf->dnsmasq.port.v.u16);
|
||||
if(cJSON_GetArraySize(conf->dnsmasq.upstreams.v.json) > 0)
|
||||
{
|
||||
fputs("# List of upstream DNS server\n", pihole_conf);
|
||||
const int n = cJSON_GetArraySize(config.dnsmasq.upstreams.v.json);
|
||||
const int n = cJSON_GetArraySize(conf->dnsmasq.upstreams.v.json);
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
cJSON *server = cJSON_GetArrayItem(config.dnsmasq.upstreams.v.json, i);
|
||||
cJSON *server = cJSON_GetArrayItem(conf->dnsmasq.upstreams.v.json, i);
|
||||
if(server != NULL && cJSON_IsString(server))
|
||||
fprintf(pihole_conf, "server=%s\n", server->valuestring);
|
||||
}
|
||||
|
@ -107,7 +237,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
}
|
||||
fputs("# Set the size of dnsmasq's cache. The default is 150 names. Setting the cache\n", pihole_conf);
|
||||
fputs("# size to zero disables caching. Note: huge cache size impacts performance\n", pihole_conf);
|
||||
fprintf(pihole_conf, "cache-size=%u\n", config.dnsmasq.cache_size.v.ui);
|
||||
fprintf(pihole_conf, "cache-size=%u\n", conf->dnsmasq.cache_size.v.ui);
|
||||
fputs("\n", pihole_conf);
|
||||
|
||||
fputs("# Return answers to DNS queries from /etc/hosts and interface-name and\n", pihole_conf);
|
||||
|
@ -119,7 +249,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
fputs("localise-queries\n", pihole_conf);
|
||||
fputs("\n", pihole_conf);
|
||||
|
||||
if(config.dnsmasq.logging.v.b)
|
||||
if(conf->dnsmasq.logging.v.b)
|
||||
{
|
||||
fputs("# Enable query logging\n", pihole_conf);
|
||||
fputs("log-queries\n", pihole_conf);
|
||||
|
@ -132,16 +262,16 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
fputs("#log-async\n", pihole_conf);
|
||||
}
|
||||
|
||||
if(strlen(config.files.log.dnsmasq.v.s) > 0)
|
||||
if(strlen(conf->files.log.dnsmasq.v.s) > 0)
|
||||
{
|
||||
fputs("# Specify the log file to use\n", pihole_conf);
|
||||
fputs("# We set this even if logging is disabled to store warnings\n", pihole_conf);
|
||||
fputs("# and errors in this file. This is useful for debugging.\n", pihole_conf);
|
||||
fprintf(pihole_conf, "log-facility=%s\n", config.files.log.dnsmasq.v.s);
|
||||
fprintf(pihole_conf, "log-facility=%s\n", conf->files.log.dnsmasq.v.s);
|
||||
fputs("\n", pihole_conf);
|
||||
}
|
||||
|
||||
if(config.dnsmasq.bogus_priv.v.b)
|
||||
if(conf->dnsmasq.bogus_priv.v.b)
|
||||
{
|
||||
fputs("# Bogus private reverse lookups. All reverse lookups for private IP\n", pihole_conf);
|
||||
fputs("# ranges (ie 192.168.x.x, etc) which are not found in /etc/hosts or the\n", pihole_conf);
|
||||
|
@ -150,7 +280,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
fputs("\n", pihole_conf);
|
||||
}
|
||||
|
||||
if(config.dnsmasq.domain_needed.v.b)
|
||||
if(conf->dnsmasq.domain_needed.v.b)
|
||||
{
|
||||
fputs("# Add the domain to simple names (without a period) in /etc/hosts in\n", pihole_conf);
|
||||
fputs("# the same way as for DHCP-derived names\n", pihole_conf);
|
||||
|
@ -158,7 +288,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
fputs("\n", pihole_conf);
|
||||
}
|
||||
|
||||
if(config.dnsmasq.expand_hosts.v.b)
|
||||
if(conf->dnsmasq.expand_hosts.v.b)
|
||||
{
|
||||
fputs("# Never forward A or AAAA queries for plain names, without dots or\n", pihole_conf);
|
||||
fputs("# domain parts, to upstream nameservers\n", pihole_conf);
|
||||
|
@ -166,7 +296,7 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
fputs("\n", pihole_conf);
|
||||
}
|
||||
|
||||
if(config.dnsmasq.dnssec.v.b)
|
||||
if(conf->dnsmasq.dnssec.v.b)
|
||||
{
|
||||
fputs("# Use DNNSEC\n", pihole_conf);
|
||||
fputs("dnssec\n", pihole_conf);
|
||||
|
@ -176,38 +306,38 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
fputs("\n", pihole_conf);
|
||||
}
|
||||
|
||||
if(strlen(config.dnsmasq.domain.v.s) > 0 && strcasecmp("none", config.dnsmasq.domain.v.s) != 0)
|
||||
if(strlen(conf->dnsmasq.domain.v.s) > 0 && strcasecmp("none", conf->dnsmasq.domain.v.s) != 0)
|
||||
{
|
||||
fputs("# DNS domain for the DNS server\n", pihole_conf);
|
||||
fprintf(pihole_conf, "domain=%s\n", config.dnsmasq.domain.v.s);
|
||||
fprintf(pihole_conf, "domain=%s\n", conf->dnsmasq.domain.v.s);
|
||||
fputs("\n", pihole_conf);
|
||||
// When there is a Pi-hole domain set and "Never forward non-FQDNs" is
|
||||
// ticked, we add `local=/domain/` to signal that this domain is purely
|
||||
// local and FTL may answer queries from /etc/hosts or DHCP but should
|
||||
// never forward queries on that domain to any upstream servers
|
||||
if(config.dnsmasq.domain_needed.v.b)
|
||||
if(conf->dnsmasq.domain_needed.v.b)
|
||||
{
|
||||
fputs("# Never forward A or AAAA queries for plain names, without\n",pihole_conf);
|
||||
fputs("# dots or domain parts, to upstream nameservers. If the name\n", pihole_conf);
|
||||
fputs("# is not known from /etc/hosts or DHCP a NXDOMAIN is returned\n", pihole_conf);
|
||||
fprintf(pihole_conf, "local=/%s/\n",
|
||||
config.dnsmasq.domain.v.s);
|
||||
conf->dnsmasq.domain.v.s);
|
||||
fputs("\n", pihole_conf);
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen(config.dnsmasq.host_record.v.s) > 0)
|
||||
if(strlen(conf->dnsmasq.host_record.v.s) > 0)
|
||||
{
|
||||
fputs("# Add A, AAAA and PTR records to the DNS\n", pihole_conf);
|
||||
fprintf(pihole_conf, "host-record=%s\n", config.dnsmasq.host_record.v.s);
|
||||
fprintf(pihole_conf, "host-record=%s\n", conf->dnsmasq.host_record.v.s);
|
||||
}
|
||||
|
||||
const char *interface = config.dnsmasq.interface.v.s;
|
||||
const char *interface = conf->dnsmasq.interface.v.s;
|
||||
// Use eth0 as fallback interface if the interface is missing
|
||||
if(strlen(interface) == 0)
|
||||
interface = "eth0";
|
||||
|
||||
switch(config.dnsmasq.listening_mode.v.listening_mode)
|
||||
switch(conf->dnsmasq.listening_mode.v.listening_mode)
|
||||
{
|
||||
case LISTEN_LOCAL:
|
||||
fputs("# Only respond to queries from devices that are at most one hop away (local devices)\n",
|
||||
|
@ -230,54 +360,54 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
}
|
||||
fputs("\n", pihole_conf);
|
||||
|
||||
if(config.dnsmasq.rev_server.active.v.b)
|
||||
if(conf->dnsmasq.rev_server.active.v.b)
|
||||
{
|
||||
fputs("# Reverse server setting\n", pihole_conf);
|
||||
fprintf(pihole_conf, "rev-server=%s,%s\n",
|
||||
config.dnsmasq.rev_server.cidr.v.s, config.dnsmasq.rev_server.target.v.s);
|
||||
conf->dnsmasq.rev_server.cidr.v.s, conf->dnsmasq.rev_server.target.v.s);
|
||||
|
||||
// If we have a reverse domain, we forward all queries to this domain to
|
||||
// the same destination
|
||||
if(strlen(config.dnsmasq.rev_server.domain.v.s) > 0)
|
||||
if(strlen(conf->dnsmasq.rev_server.domain.v.s) > 0)
|
||||
fprintf(pihole_conf, "server=/%s/%s\n",
|
||||
config.dnsmasq.rev_server.domain.v.s, config.dnsmasq.rev_server.target.v.s);
|
||||
conf->dnsmasq.rev_server.domain.v.s, conf->dnsmasq.rev_server.target.v.s);
|
||||
|
||||
// Forward unqualified names to the target only when the "never forward
|
||||
// non-FQDN" option is NOT ticked
|
||||
if(!config.dnsmasq.domain_needed.v.b)
|
||||
if(!conf->dnsmasq.domain_needed.v.b)
|
||||
fprintf(pihole_conf, "server=//%s\n",
|
||||
config.dnsmasq.rev_server.target.v.s);
|
||||
conf->dnsmasq.rev_server.target.v.s);
|
||||
fputs("\n", pihole_conf);
|
||||
}
|
||||
|
||||
if(config.dnsmasq.dhcp.active.v.b)
|
||||
if(conf->dnsmasq.dhcp.active.v.b)
|
||||
{
|
||||
fputs("# DHCP server setting\n", pihole_conf);
|
||||
fputs("dhcp-authoritative\n", pihole_conf);
|
||||
fputs("dhcp-leasefile=/etc/pihole/dhcp.leases\n", pihole_conf);
|
||||
fprintf(pihole_conf, "dhcp-range=%s,%s,%s\n",
|
||||
config.dnsmasq.dhcp.start.v.s,
|
||||
config.dnsmasq.dhcp.end.v.s,
|
||||
config.dnsmasq.dhcp.leasetime.v.s);
|
||||
conf->dnsmasq.dhcp.start.v.s,
|
||||
conf->dnsmasq.dhcp.end.v.s,
|
||||
conf->dnsmasq.dhcp.leasetime.v.s);
|
||||
fprintf(pihole_conf, "dhcp-option=option:router,%s\n",
|
||||
config.dnsmasq.dhcp.router.v.s);
|
||||
conf->dnsmasq.dhcp.router.v.s);
|
||||
|
||||
if(config.dnsmasq.dhcp.rapid_commit.v.b)
|
||||
if(conf->dnsmasq.dhcp.rapid_commit.v.b)
|
||||
fputs("dhcp-rapid-commit\n", pihole_conf);
|
||||
|
||||
if(config.dnsmasq.dhcp.ipv6.v.b)
|
||||
if(conf->dnsmasq.dhcp.ipv6.v.b)
|
||||
{
|
||||
fputs("dhcp-option=option6:dns-server,[::]\n", pihole_conf);
|
||||
fprintf(pihole_conf, "dhcp-range=::,constructor:%s,ra-names,ra-stateless,64\n", interface);
|
||||
}
|
||||
fputs("\n", pihole_conf);
|
||||
if(cJSON_GetArraySize(config.dnsmasq.dhcp.hosts.v.json) > 0)
|
||||
if(cJSON_GetArraySize(conf->dnsmasq.dhcp.hosts.v.json) > 0)
|
||||
{
|
||||
fputs("# Per host parameters for the DHCP server\n", pihole_conf);
|
||||
const int n = cJSON_GetArraySize(config.dnsmasq.dhcp.hosts.v.json);
|
||||
const int n = cJSON_GetArraySize(conf->dnsmasq.dhcp.hosts.v.json);
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
cJSON *server = cJSON_GetArrayItem(config.dnsmasq.dhcp.hosts.v.json, i);
|
||||
cJSON *server = cJSON_GetArrayItem(conf->dnsmasq.dhcp.hosts.v.json, i);
|
||||
if(server != NULL && cJSON_IsString(server))
|
||||
fprintf(pihole_conf, "dhcp-host=%s\n", server->valuestring);
|
||||
}
|
||||
|
@ -285,13 +415,13 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
}
|
||||
}
|
||||
|
||||
if(cJSON_GetArraySize(config.dnsmasq.cnames.v.json) > 0)
|
||||
if(cJSON_GetArraySize(conf->dnsmasq.cnames.v.json) > 0)
|
||||
{
|
||||
fputs("# User-defined custom CNAMEs\n", pihole_conf);
|
||||
const int n = cJSON_GetArraySize(config.dnsmasq.cnames.v.json);
|
||||
const int n = cJSON_GetArraySize(conf->dnsmasq.cnames.v.json);
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
cJSON *server = cJSON_GetArrayItem(config.dnsmasq.cnames.v.json, i);
|
||||
cJSON *server = cJSON_GetArrayItem(conf->dnsmasq.cnames.v.json, i);
|
||||
if(server != NULL && cJSON_IsString(server))
|
||||
fprintf(pihole_conf, "cname=%s\n", server->valuestring);
|
||||
}
|
||||
|
@ -344,12 +474,14 @@ bool __attribute__((const)) write_dnsmasq_config(bool test_config)
|
|||
return false;
|
||||
}
|
||||
|
||||
if(test_config && !test_dnsmasq_config())
|
||||
log_debug(DEBUG_CONFIG, "Testing "DNSMASQ_TEMP_CONF);
|
||||
if(test_config && !test_dnsmasq_config(errbuf))
|
||||
{
|
||||
log_warn("New dnsmasq configuration is not valid, using previous one");
|
||||
log_warn("New dnsmasq configuration is not valid (%s), config remains unchanged", errbuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_debug(DEBUG_CONFIG, "Installing "DNSMASQ_TEMP_CONF" to "DNSMASQ_PH_CONFIG);
|
||||
if(rename(DNSMASQ_TEMP_CONF, DNSMASQ_PH_CONFIG) != 0)
|
||||
{
|
||||
log_err("Cannot install dnsmasq config file: %s", strerror(errno));
|
||||
|
|
|
@ -10,7 +10,13 @@
|
|||
#ifndef DNSMASQ_CONFIG_H
|
||||
#define DNSMASQ_CONFIG_H
|
||||
|
||||
bool write_dnsmasq_config(bool test_config);
|
||||
#include "config/config.h"
|
||||
|
||||
#define ERRBUF_SIZE 1024
|
||||
|
||||
bool write_dnsmasq_config(struct config *conf, bool test_config, char errbuf[ERRBUF_SIZE]);
|
||||
int get_lineno_from_string(const char *string);
|
||||
char *get_dnsmasq_line(const unsigned int lineno);
|
||||
bool read_legacy_dhcp_static_config(void);
|
||||
bool read_legacy_cnames_config(void);
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ bool readFTLtoml(void)
|
|||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *conf_item = get_conf_item(i);
|
||||
struct conf_item *conf_item = get_conf_item(&config, i);
|
||||
|
||||
// Get config path depth
|
||||
unsigned int level = config_path_depth(conf_item->p);
|
||||
|
|
|
@ -45,7 +45,7 @@ bool writeFTLtoml(const bool verbose)
|
|||
for(unsigned int i = 0; i < CONFIG_ELEMENTS; i++)
|
||||
{
|
||||
// Get pointer to memory location of this conf_item
|
||||
struct conf_item *conf_item = get_conf_item(i);
|
||||
struct conf_item *conf_item = get_conf_item(&config, i);
|
||||
|
||||
// Get path depth
|
||||
unsigned int level = config_path_depth(conf_item->p);
|
||||
|
|
|
@ -6067,3 +6067,13 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
|||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/******************** Pi-hole extension ********************/
|
||||
void reset_usage_indicator(void)
|
||||
{
|
||||
for (unsigned int i = 0; usage[i].opt != 0; i++)
|
||||
if(usage[i].rept == ARG_USED_CL ||
|
||||
usage[i].rept == ARG_USED_FILE)
|
||||
usage[i].rept = ARG_ONE;
|
||||
}
|
||||
/**********************************************************/
|
||||
|
|
|
@ -134,7 +134,7 @@ int main (int argc, char *argv[])
|
|||
startup = false;
|
||||
main_dnsmasq(argc_dnsmasq, argv_dnsmasq);
|
||||
|
||||
log_info("Shutting down...");
|
||||
log_info("Shutting down... // exit code %d", exit_code);
|
||||
// Extra grace time is needed as dnsmasq script-helpers and the API may not
|
||||
// be terminating immediately
|
||||
sleepms(250);
|
||||
|
|
|
@ -402,6 +402,9 @@ 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);
|
||||
|
||||
if(shmLock == NULL)
|
||||
return;
|
||||
|
||||
int result = pthread_mutex_lock(&shmLock->lock.outer);
|
||||
|
||||
if(result != 0)
|
||||
|
@ -460,6 +463,9 @@ void _unlock_shm(const char* func, const int line, const char * file)
|
|||
(long int)shmLock->owner.pid, (long int)shmLock->owner.tid);
|
||||
}
|
||||
|
||||
if(shmLock == NULL)
|
||||
return;
|
||||
|
||||
// Unlock mutex
|
||||
int result = pthread_mutex_unlock(&shmLock->lock.inner);
|
||||
shmLock->owner.pid = 0;
|
||||
|
@ -861,6 +867,9 @@ static void delete_shm(SharedMemory *sharedMemory)
|
|||
sharedMemory->ptr, sharedMemory->size, strerror(errno));
|
||||
}
|
||||
|
||||
// Set unmapped pointer to NULL
|
||||
sharedMemory->ptr = NULL;
|
||||
|
||||
// Now you can no longer `shm_open` the memory, and once all others
|
||||
// unlink, it will be destroyed.
|
||||
if(shm_unlink(sharedMemory->name) != 0)
|
||||
|
|
Loading…
Reference in New Issue