Add explicit event queue to avoid possible race collisions when many signals arrive at the same time (or very very close to each other)
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
parent
77cf2fea95
commit
46ac93353e
|
@ -117,6 +117,8 @@ set(sources
|
|||
edns0.c
|
||||
edns0.h
|
||||
enums.h
|
||||
events.c
|
||||
events.h
|
||||
files.c
|
||||
files.h
|
||||
FTL.h
|
||||
|
|
|
@ -8,16 +8,19 @@
|
|||
* 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 "../FTL.h"
|
||||
#include "api.h"
|
||||
#include "shmem.h"
|
||||
#include "timers.h"
|
||||
#include "../shmem.h"
|
||||
#include "../timers.h"
|
||||
#include "request.h"
|
||||
#include "socket.h"
|
||||
#include "resolve.h"
|
||||
#include "regex_r.h"
|
||||
#include "database/network-table.h"
|
||||
#include "log.h"
|
||||
#include "../resolve.h"
|
||||
#include "../regex_r.h"
|
||||
#include "../database/network-table.h"
|
||||
#include "../log.h"
|
||||
#include "../signals.h"
|
||||
// Eventqueue routines
|
||||
#include "../events.h"
|
||||
|
||||
bool __attribute__((pure)) command(const char *client_message, const char* cmd) {
|
||||
return strstr(client_message, cmd) != NULL;
|
||||
|
@ -155,8 +158,7 @@ void process_request(const char *client_message, int *sock)
|
|||
// Important: Don't obtain a lock for this request
|
||||
// Locking will be done internally when needed
|
||||
// onlynew=false -> reresolve all host names
|
||||
resolveClients(false);
|
||||
resolveForwardDestinations(false);
|
||||
set_event(RELOAD_PRIVACY_LEVEL);
|
||||
logg("Done re-resolving host names");
|
||||
}
|
||||
else if(command(client_message, ">recompile-regex"))
|
||||
|
|
|
@ -712,6 +712,10 @@ void read_debuging_settings(FILE *fp)
|
|||
// defaults to: false
|
||||
setDebugOption(fp, "DEBUG_CLIENTS", DEBUG_CLIENTS);
|
||||
|
||||
// DEBUG_EVENTS
|
||||
// defaults to: false
|
||||
setDebugOption(fp, "DEBUG_EVENTS", DEBUG_EVENTS);
|
||||
|
||||
if(config.debug)
|
||||
{
|
||||
logg("*****************************");
|
||||
|
@ -734,6 +738,7 @@ void read_debuging_settings(FILE *fp)
|
|||
logg("* DEBUG_RESOLVER %s *", (config.debug & DEBUG_RESOLVER)? "YES":"NO ");
|
||||
logg("* DEBUG_EDNS0 %s *", (config.debug & DEBUG_EDNS0)? "YES":"NO ");
|
||||
logg("* DEBUG_CLIENTS %s *", (config.debug & DEBUG_CLIENTS)? "YES":"NO ");
|
||||
logg("* DEBUG_EVENTS %s *", (config.debug & DEBUG_EVENTS)? "YES":"NO ");
|
||||
logg("*****************************");
|
||||
}
|
||||
|
||||
|
|
|
@ -8,20 +8,22 @@
|
|||
* 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 "../FTL.h"
|
||||
#include "database-thread.h"
|
||||
#include "common.h"
|
||||
// [un]lock_shm();
|
||||
#include "shmem.h"
|
||||
#include "../shmem.h"
|
||||
// parse_neighbor_cache()
|
||||
#include "network-table.h"
|
||||
// DB_save_queries()
|
||||
#include "query-table.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "timers.h"
|
||||
#include "../config.h"
|
||||
#include "../log.h"
|
||||
#include "../timers.h"
|
||||
// global variable killed
|
||||
#include "signals.h"
|
||||
#include "../signals.h"
|
||||
// Eventqueue routines
|
||||
#include "../events.h"
|
||||
|
||||
void *DB_thread(void *val)
|
||||
{
|
||||
|
@ -38,42 +40,53 @@ void *DB_thread(void *val)
|
|||
// to the database
|
||||
time_t lastDBsave = time(NULL) - time(NULL)%config.DBinterval;
|
||||
|
||||
while(!killed && database)
|
||||
while(!killed)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
if(now - lastDBsave >= config.DBinterval)
|
||||
if(database)
|
||||
{
|
||||
// Update lastDBsave timer
|
||||
lastDBsave = time(NULL) - time(NULL)%config.DBinterval;
|
||||
|
||||
// Lock FTL's data structures, since it is
|
||||
// likely that they will be changed here
|
||||
lock_shm();
|
||||
|
||||
// Save data to database
|
||||
DB_save_queries();
|
||||
|
||||
// Release data lock
|
||||
unlock_shm();
|
||||
|
||||
// Check if GC should be done on the database
|
||||
if(DBdeleteoldqueries)
|
||||
time_t now = time(NULL);
|
||||
if(now - lastDBsave >= config.DBinterval)
|
||||
{
|
||||
// No thread locks needed
|
||||
delete_old_queries_in_DB();
|
||||
DBdeleteoldqueries = false;
|
||||
// Update lastDBsave timer
|
||||
lastDBsave = time(NULL) - time(NULL)%config.DBinterval;
|
||||
|
||||
// Lock FTL's data structures, since it is
|
||||
// likely that they will be changed here
|
||||
lock_shm();
|
||||
|
||||
// Save data to database
|
||||
DB_save_queries();
|
||||
|
||||
// Release data lock
|
||||
unlock_shm();
|
||||
|
||||
// Check if GC should be done on the database
|
||||
if(DBdeleteoldqueries)
|
||||
{
|
||||
// No thread locks needed
|
||||
delete_old_queries_in_DB();
|
||||
DBdeleteoldqueries = false;
|
||||
}
|
||||
|
||||
// Parse neighbor cache (fill network table) if enabled
|
||||
if (config.parse_arp_cache)
|
||||
parse_neighbor_cache();
|
||||
}
|
||||
|
||||
// Parse neighbor cache (fill network table) if enabled
|
||||
if (config.parse_arp_cache)
|
||||
parse_neighbor_cache();
|
||||
// Update MAC vendor strings once a month (the MAC vendor
|
||||
// database is not updated very often)
|
||||
if(now % 2592000L == 0)
|
||||
updateMACVendorRecords();
|
||||
}
|
||||
|
||||
// Update MAC vendor strings once a month (the MAC vendor
|
||||
// database is not updated very often)
|
||||
if(now % 2592000L == 0)
|
||||
updateMACVendorRecords();
|
||||
// Process database related event queue elements
|
||||
if(get_and_clear_event(RELOAD_GRAVITY))
|
||||
FTL_reload_all_domainlists();
|
||||
|
||||
if(get_and_clear_event(RELOAD_PRIVACY_LEVEL))
|
||||
get_privacy_level(NULL);
|
||||
|
||||
// Sleep 0.1 seconds
|
||||
sleepms(100);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include "signals.h"
|
||||
// atomic_flag_test_and_set()
|
||||
#include <stdatomic.h>
|
||||
// Eventqueue routines
|
||||
#include "events.h"
|
||||
|
||||
static void print_flags(const unsigned int flags);
|
||||
static void save_reply_type(const unsigned int flags, const union all_addr *addr,
|
||||
|
@ -869,8 +871,8 @@ void FTL_dnsmasq_reload(void)
|
|||
|
||||
logg("Reloading DNS cache");
|
||||
|
||||
// Reload the privacy level in case the user changed it
|
||||
get_privacy_level(NULL);
|
||||
// Request reload the privacy level
|
||||
set_event(RELOAD_PRIVACY_LEVEL);
|
||||
|
||||
// Inspect 01-pihole.conf to see if Pi-hole blocking is enabled,
|
||||
// i.e. if /etc/pihole/gravity.list is sourced as addn-hosts file
|
||||
|
@ -894,7 +896,7 @@ void FTL_dnsmasq_reload(void)
|
|||
// - Get number of blocked domains
|
||||
// - Read and compile regex filters (incl. per-client)
|
||||
// - Flush FTL's DNS cache
|
||||
FTL_reload_all_domainlists();
|
||||
set_event(RELOAD_GRAVITY);
|
||||
|
||||
// Print current set of capabilities if requested via debug flag
|
||||
if(config.debug & DEBUG_CAPS)
|
||||
|
@ -904,7 +906,7 @@ void FTL_dnsmasq_reload(void)
|
|||
resolver_ready = true;
|
||||
}
|
||||
|
||||
void _FTL_reply(const unsigned short flags, const char *name, const union all_addr *addr, const int id,
|
||||
void _FTL_reply(const unsigned int flags, const char *name, const union all_addr *addr, const int id,
|
||||
const char* file, const int line)
|
||||
{
|
||||
// Lock shared memory
|
||||
|
|
|
@ -28,7 +28,7 @@ bool _FTL_new_query(const unsigned int flags, const char *name, const char** blo
|
|||
void _FTL_forwarded(const unsigned int flags, const char *name, const union all_addr *addr, const int id, const char* file, const int line);
|
||||
|
||||
#define FTL_reply(flags, name, addr, id) _FTL_reply(flags, name, addr, id, __FILE__, __LINE__)
|
||||
void _FTL_reply(const unsigned short flags, const char *name, const union all_addr *addr, const int id, const char* file, const int line);
|
||||
void _FTL_reply(const unsigned int flags, const char *name, const union all_addr *addr, const int id, const char* file, const int line);
|
||||
|
||||
#define FTL_cache(flags, name, addr, arg, id) _FTL_cache(flags, name, addr, arg, id, __FILE__, __LINE__)
|
||||
void _FTL_cache(const unsigned int flags, const char *name, const union all_addr *addr, const char * arg, const int id, const char* file, const int line);
|
||||
|
|
10
src/enums.h
10
src/enums.h
|
@ -133,7 +133,15 @@ enum debug_flags {
|
|||
DEBUG_VECTORS = (1 << 14), /* 00000000 00000000 01000000 00000000 */
|
||||
DEBUG_RESOLVER = (1 << 15), /* 00000000 00000000 10000000 00000000 */
|
||||
DEBUG_EDNS0 = (1 << 16), /* 00000000 00000001 00000000 00000000 */
|
||||
DEBUG_CLIENTS = (1 << 17) /* 00000000 00000010 00000000 00000000 */
|
||||
DEBUG_CLIENTS = (1 << 17), /* 00000000 00000010 00000000 00000000 */
|
||||
DEBUG_EVENTS = (1 << 18), /* 00000000 00000100 00000000 00000000 */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum events {
|
||||
RELOAD_GRAVITY,
|
||||
RELOAD_PRIVACY_LEVEL,
|
||||
RERESOLVE_HOSTNAMES,
|
||||
EVENTS_MAX
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif // ENUMS_H
|
|
@ -0,0 +1,99 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* Event queue processing routines
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
|
||||
#include "FTL.h"
|
||||
// public prototypes
|
||||
#include "events.h"
|
||||
// struct config
|
||||
#include "config.h"
|
||||
// logg()
|
||||
#include "log.h"
|
||||
// atomic_flag_test_and_set()
|
||||
#include <stdatomic.h>
|
||||
|
||||
// Private prototypes
|
||||
static const char *eventtext(const enum events event);
|
||||
|
||||
// Queue containing all possible events
|
||||
static volatile atomic_flag eventqueue[EVENTS_MAX] = { ATOMIC_FLAG_INIT };
|
||||
|
||||
// Set/Request event
|
||||
// We set the events atomically to ensure no race collisons can happen. If an
|
||||
// event has already been requested, this has no consequences as event cannot be
|
||||
// added multiple times
|
||||
void _set_event(const enum events event, int line, const char *function, const char *file)
|
||||
{
|
||||
bool is_set = false;
|
||||
// Set eventqueue bit
|
||||
if(atomic_flag_test_and_set(&eventqueue[event]))
|
||||
is_set = true;
|
||||
|
||||
// Possible debug logging
|
||||
if(config.debug & DEBUG_EVENTS)
|
||||
{
|
||||
logg("Event %s -> %s called from %s() (%s:%i)",
|
||||
eventtext(event),
|
||||
is_set ? "was ALREADY SET" : "now SET",
|
||||
function, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
// Get and clear event
|
||||
// Unfortunately, we cannot read the value of an atomic_flag without setting it
|
||||
// either to true or false. This is by design. Hence, we implement testing by
|
||||
// first trying to set the the flag to true. If this "fails", we know the flag
|
||||
// has already been set.
|
||||
// On x86_64 and i686 CPUs, these atomic instrictions are implemented using the
|
||||
// XCHG asm instruction, which simply exchanges the content of two registers or,
|
||||
// in this case, a register and a memory location (the respective eventqueue
|
||||
// pointer). This is guaranteed to happen atomically by automatically
|
||||
// implementing the processor's locking protocol during the operation.
|
||||
// On other architecture, similar instructions are used to reassemble the same
|
||||
// effect (but typically with a few more instructions). ARM64, for instance,
|
||||
// uses LDAXRB (Load-aquire exclusive register byte) and STAXRB (Store-release
|
||||
// exclusive register byte) to implement the same thing with a few more
|
||||
// instructions.
|
||||
bool _get_and_clear_event(const enum events event, int line, const char *function, const char *file)
|
||||
{
|
||||
bool is_set = false;
|
||||
if(atomic_flag_test_and_set(&eventqueue[event]))
|
||||
is_set = true;
|
||||
|
||||
// Possible debug logging only for SET status, to avoid log file flooding with NOT SET messages
|
||||
if(is_set && config.debug & DEBUG_EVENTS)
|
||||
{
|
||||
logg("Event %s -> was SET, now CLEARED called from %s() (%s:%i)",
|
||||
eventtext(event), function, file, line);
|
||||
}
|
||||
|
||||
// Clear eventqueue bit (we set it above) ...
|
||||
atomic_flag_clear(&eventqueue[event]);
|
||||
|
||||
// ... and return status
|
||||
return is_set;
|
||||
}
|
||||
|
||||
// Output human-readable version event text representation
|
||||
static const char *eventtext(const enum events event)
|
||||
{
|
||||
switch(event)
|
||||
{
|
||||
case RELOAD_GRAVITY:
|
||||
return "RELOAD_GRAVITY";
|
||||
case RELOAD_PRIVACY_LEVEL:
|
||||
return "RELOAD_PRIVACY_LEVEL";
|
||||
case RERESOLVE_HOSTNAMES:
|
||||
return "RERESOLVE_HOSTNAMES";
|
||||
case EVENTS_MAX: // fall through
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* FTL Engine
|
||||
* Event queue processing prototypes
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#ifndef EVENTS_H
|
||||
#define EVENTS_H
|
||||
|
||||
// enum events
|
||||
#include "enums.h"
|
||||
|
||||
#define set_event(event) _set_event(event, __LINE__, __FUNCTION__, __FILE__)
|
||||
void _set_event(const enum events event, int line, const char *function, const char *file);
|
||||
#define get_and_clear_event(event) _get_and_clear_event(event, __LINE__, __FUNCTION__, __FILE__)
|
||||
bool _get_and_clear_event(const enum events event, int line, const char *function, const char *file);
|
||||
|
||||
#endif // EVENTS_H
|
|
@ -28,6 +28,8 @@
|
|||
#include "daemon.h"
|
||||
// logg_hostname_warning()
|
||||
#include "database/message-table.h"
|
||||
// Eventqueue routines
|
||||
#include "events.h"
|
||||
|
||||
static bool res_initialized = false;
|
||||
|
||||
|
@ -467,7 +469,7 @@ void *DNSclient_thread(void *val)
|
|||
while(!killed)
|
||||
{
|
||||
// Run every minute to resolve only new clients and upstream servers
|
||||
if(resolver_ready && time(NULL) % RESOLVE_INTERVAL == 0)
|
||||
if(resolver_ready && (time(NULL) % RESOLVE_INTERVAL == 0))
|
||||
{
|
||||
// Try to resolve new client host names (onlynew=true)
|
||||
resolveClients(true);
|
||||
|
@ -478,7 +480,11 @@ void *DNSclient_thread(void *val)
|
|||
}
|
||||
|
||||
// Run every hour to update possibly changed client host names
|
||||
if(resolver_ready && time(NULL) % RERESOLVE_INTERVAL == 0)
|
||||
if(resolver_ready && (time(NULL) % RERESOLVE_INTERVAL == 0))
|
||||
set_event(RERESOLVE_HOSTNAMES);
|
||||
|
||||
// Process resolver related event queue elements
|
||||
if(get_and_clear_event(RERESOLVE_HOSTNAMES))
|
||||
{
|
||||
// Try to resolve all client host names (onlynew=false)
|
||||
resolveClients(false);
|
||||
|
@ -491,8 +497,8 @@ void *DNSclient_thread(void *val)
|
|||
sleepms(500);
|
||||
}
|
||||
|
||||
// Idle for 0.5 sec before checking again the time criteria
|
||||
sleepms(500);
|
||||
// Idle for 0.1 sec before checking again the time criteria
|
||||
sleepms(100);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
#include "memory.h"
|
||||
// ls_dir()
|
||||
#include "files.h"
|
||||
// FTL_reload_all_domainlists()
|
||||
#include "datastructure.h"
|
||||
#include "config.h"
|
||||
// gettid()
|
||||
#include "daemon.h"
|
||||
// Eventqueue routines
|
||||
#include "events.h"
|
||||
|
||||
#define BINARY_NAME "pihole-FTL"
|
||||
|
||||
|
@ -171,6 +171,7 @@ static void __attribute__((noreturn)) SIGSEGV_handler(int sig, siginfo_t *si, vo
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#define RTSIG_MAX 2
|
||||
static void SIGRT_handler(int signum, siginfo_t *si, void *unused)
|
||||
{
|
||||
// Ignore real-time signals outside of the main process (TCP forks)
|
||||
|
@ -189,10 +190,10 @@ static void SIGRT_handler(int signum, siginfo_t *si, void *unused)
|
|||
// - exact blacklist
|
||||
// - exact blacklist
|
||||
// WITHOUT wiping the DNS cache itself
|
||||
FTL_reload_all_domainlists();
|
||||
set_event(RELOAD_GRAVITY);
|
||||
|
||||
// Reload the privacy level in case the user changed it
|
||||
get_privacy_level(NULL);
|
||||
set_event(RELOAD_PRIVACY_LEVEL);
|
||||
}
|
||||
else if(rtsig == 2)
|
||||
{
|
||||
|
@ -232,7 +233,7 @@ void handle_realtime_signals(void)
|
|||
mpid = getpid();
|
||||
|
||||
// Catch first five real-time signals
|
||||
for(unsigned int i = 0; i < 5; i++)
|
||||
for(unsigned char i = 0; i < (RTSIG_MAX+1); i++)
|
||||
{
|
||||
struct sigaction SIGACTION;
|
||||
memset(&SIGACTION, 0, sizeof(struct sigaction));
|
||||
|
|
Loading…
Reference in New Issue