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:
DL6ER 2020-09-02 21:41:46 +02:00
parent 77cf2fea95
commit 46ac93353e
No known key found for this signature in database
GPG Key ID: 00135ACBD90B28DD
11 changed files with 218 additions and 58 deletions

View File

@ -117,6 +117,8 @@ set(sources
edns0.c
edns0.h
enums.h
events.c
events.h
files.c
files.h
FTL.h

View File

@ -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"))

View File

@ -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("*****************************");
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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

99
src/events.c Normal file
View File

@ -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";
}
}

22
src/events.h Normal file
View File

@ -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

View File

@ -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;

View File

@ -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));