diff --git a/chrony.texi b/chrony.texi index 396d419..3629d35 100644 --- a/chrony.texi +++ b/chrony.texi @@ -1182,6 +1182,7 @@ directives can occur in any order in the file. * manual directive:: Allow manual entry using chronyc's settime cmd. * maxupdateskew directive:: Stop bad estimates upsetting machine clock * noclientlog directive:: Prevent chronyd from gathering data about clients +* clientloglimit directive:: Set client log memory limit * peer directive:: Specify an NTP peer * pidfile directive:: Specify the file where chronyd's pid is written * port directive:: Set port to use for NTP packets @@ -2087,6 +2088,21 @@ This directive, which takes no arguments, specifies that client accesses are not to be logged. Normally they are logged, allowing statistics to be reported using the @code{clients} command in @code{chronyc}. @c }}} +@c {{{ clientloglimit +@node clientloglimit directive +@subsection clientloglimit +This directive specifies the maximum size of the memory allocated to +log client accesses. When the limit is reached, only information for +clients that have already been logged will be updated. If 0 is +specified, the memory size will be unlimited. The default is 524288 +bytes. + +An example of the use of this directive is + +@example +clientloglimit 1048576 +@end example +@c }}} @c {{{ peer @node peer directive @subsection peer diff --git a/clientlog.c b/clientlog.c index cd7fdac..d4f304d 100644 --- a/clientlog.c +++ b/clientlog.c @@ -88,6 +88,13 @@ static int max_nodes = 0; /* Flag indicating whether facility is turned on or not */ static int active = 0; +/* Flag indicating whether memory allocation limit has been reached + and no new nodes or subnets should be allocated */ +static int alloc_limit_reached; + +static unsigned long alloc_limit; +static unsigned long alloced; + /* ================================================== */ static void @@ -159,6 +166,9 @@ CLG_Initialise(void) max_nodes = 0; n_nodes = 0; + alloced = 0; + alloc_limit = CNF_GetClientLogLimit(); + alloc_limit_reached = 0; } /* ================================================== */ @@ -171,11 +181,25 @@ CLG_Finalise(void) /* ================================================== */ +static void check_alloc_limit() { + if (alloc_limit_reached) + return; + + if (alloced >= alloc_limit) { + LOG(LOGS_WARN, LOGF_ClientLog, "Client log memory limit reached"); + alloc_limit_reached = 1; + } +} + +/* ================================================== */ + static void create_subnet(Subnet *parent_subnet, int the_entry) { parent_subnet->entry[the_entry] = (void *) MallocNew(Subnet); clear_subnet((Subnet *) parent_subnet->entry[the_entry]); + alloced += sizeof (Subnet); + check_alloc_limit(); } /* ================================================== */ @@ -188,6 +212,8 @@ create_node(Subnet *parent_subnet, int the_entry) parent_subnet->entry[the_entry] = (void *) new_node; clear_node(new_node); + alloced += sizeof (Node); + if (n_nodes == max_nodes) { if (nodes) { assert(max_nodes > 0); @@ -198,8 +224,10 @@ create_node(Subnet *parent_subnet, int the_entry) max_nodes = 16; nodes = MallocArray(Node *, max_nodes); } + alloced += sizeof (Node *) * (max_nodes - n_nodes); } nodes[n_nodes++] = (Node *) new_node; + check_alloc_limit(); } /* ================================================== */ @@ -216,11 +244,15 @@ find_subnet(Subnet *subnet, uint32_t *addr, int addr_len, int bits_consumed) if (bits_consumed < 32 * addr_len) { if (!subnet->entry[this_subnet]) { + if (alloc_limit_reached) + return NULL; create_subnet(subnet, this_subnet); } return find_subnet((Subnet *) subnet->entry[this_subnet], addr, addr_len, bits_consumed); } else { if (!subnet->entry[this_subnet]) { + if (alloc_limit_reached) + return NULL; create_node(subnet, this_subnet); } return subnet->entry[this_subnet]; @@ -273,6 +305,9 @@ CLG_LogNTPClientAccess (IPAddr *client, time_t now) assert(0); } + if (node == NULL) + return; + node->ip_addr = *client; ++node->client_hits; node->last_ntp_hit = now; @@ -300,6 +335,9 @@ CLG_LogNTPPeerAccess(IPAddr *client, time_t now) assert(0); } + if (node == NULL) + return; + node->ip_addr = *client; ++node->peer_hits; node->last_ntp_hit = now; @@ -327,6 +365,9 @@ CLG_LogCommandAccess(IPAddr *client, CLG_Command_Type type, time_t now) assert(0); } + if (node == NULL) + return; + node->ip_addr = *client; node->last_cmd_hit = now; switch (type) { diff --git a/conf.c b/conf.c index d9a1f47..d7e42db 100644 --- a/conf.c +++ b/conf.c @@ -87,6 +87,7 @@ static void parse_cmddeny(const char *); static void parse_cmdport(const char *); static void parse_rtconutc(const char *); static void parse_noclientlog(const char *); +static void parse_clientloglimit(const char *); static void parse_logchange(const char *); static void parse_mailonchange(const char *); static void parse_bindaddress(const char *); @@ -158,6 +159,9 @@ static double mail_change_threshold = 0.0; memory */ static int no_client_log = 0; +/* Limit memory allocated for the clients log */ +static unsigned long client_log_limit = 524288; + /* IP addresses for binding the NTP socket to. UNSPEC family means INADDR_ANY will be used */ static IPAddr bind_address4, bind_address6; @@ -213,6 +217,7 @@ static const Command commands[] = { {"cmdport", 7, parse_cmdport}, {"rtconutc", 8, parse_rtconutc}, {"noclientlog", 11, parse_noclientlog}, + {"clientloglimit", 14, parse_clientloglimit}, {"logchange", 9, parse_logchange}, {"mailonchange", 12, parse_mailonchange}, {"bindaddress", 11, parse_bindaddress}, @@ -771,6 +776,21 @@ parse_noclientlog(const char *line) /* ================================================== */ +static void +parse_clientloglimit(const char *line) +{ + if (sscanf(line, "%lu", &client_log_limit) != 1) { + LOG(LOGS_WARN, LOGF_Configure, "Could not read clientlog memory limit at line %d", line_number); + } + + if (client_log_limit == 0) { + /* unlimited */ + client_log_limit = (unsigned long)-1; + } +} + +/* ================================================== */ + static void parse_logchange(const char *line) { @@ -1353,6 +1373,14 @@ CNF_GetNoClientLog(void) /* ================================================== */ +unsigned long +CNF_GetClientLogLimit(void) +{ + return client_log_limit; +} + +/* ================================================== */ + void CNF_GetBindAddress(int family, IPAddr *addr) { diff --git a/conf.h b/conf.h index dc20397..9c90bb8 100644 --- a/conf.h +++ b/conf.h @@ -62,6 +62,7 @@ extern int CNF_GetRTCOnUTC(void); extern void CNF_GetLogChange(int *enabled, double *threshold); extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user); extern int CNF_GetNoClientLog(void); +extern unsigned long CNF_GetClientLogLimit(void); extern void CNF_GetBindAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); extern char *CNF_GetPidFile(void); diff --git a/logging.h b/logging.h index f95a808..81505e1 100644 --- a/logging.h +++ b/logging.h @@ -53,6 +53,7 @@ typedef enum { LOGF_Local, LOGF_Util, LOGF_Main, + LOGF_ClientLog, LOGF_Configure, LOGF_CmdMon, LOGF_Acquire,