mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00

The input framework's Manager::IsCompatibleType() already rejected sets with multiple index types that aren't all the same (i.e. that are not pure). Pure ones (e.g. "set[addr,addr]") slipped through and could cause Zeek to segfault elsewhere in the config framework due to type comparison subtleties. Note that the ASCII reader can't read such sets anyway, so this method now rejects sets with any kind of index-type tuple. In the config framework, the script-level change handler has a risky conversion from any to set[bool], which can trigger segfaults when the underlying set's index is a type tuple. We now prevent this code path by ensuring it only applies to sets with a single index type.
178 lines
5.5 KiB
Text
178 lines
5.5 KiB
Text
##! The configuration framework provides a way to change Zeek options
|
|
##! (as specified by the "option" keyword) at runtime. It also logs runtime
|
|
##! changes to options to config.log.
|
|
|
|
@load base/frameworks/cluster
|
|
|
|
module Config;
|
|
|
|
export {
|
|
## The config logging stream identifier.
|
|
redef enum Log::ID += { LOG };
|
|
|
|
## A default logging policy hook for the stream.
|
|
global log_policy: Log::PolicyHook;
|
|
|
|
## Represents the data in config.log.
|
|
type Info: record {
|
|
## Timestamp at which the configuration change occured.
|
|
ts: time &log;
|
|
## ID of the value that was changed.
|
|
id: string &log;
|
|
## Value before the change.
|
|
old_value: string &log;
|
|
## Value after the change.
|
|
new_value: string &log;
|
|
## Optional location that triggered the change.
|
|
location: string &optional &log;
|
|
};
|
|
|
|
## Event that can be handled to access the :zeek:type:`Config::Info`
|
|
## record as it is sent on to the logging framework.
|
|
global log_config: event(rec: Info);
|
|
|
|
## This function is the config framework layer around the lower-level
|
|
## :zeek:see:`Option::set` call. Config::set_value will set the configuration
|
|
## value for all nodes in the cluster, no matter where it was called. Note
|
|
## that :zeek:see:`Option::set` does not distribute configuration changes
|
|
## to other nodes.
|
|
##
|
|
## ID: The ID of the option to update.
|
|
##
|
|
## val: The new value of the option.
|
|
##
|
|
## location: Optional parameter detailing where this change originated from.
|
|
##
|
|
## Returns: true on success, false when an error occurs.
|
|
global set_value: function(ID: string, val: any, location: string &default = ""): bool;
|
|
}
|
|
|
|
@if ( Cluster::is_enabled() )
|
|
type OptionCacheValue: record {
|
|
val: any;
|
|
location: string;
|
|
};
|
|
|
|
global option_cache: table[string] of OptionCacheValue;
|
|
|
|
global Config::cluster_set_option: event(ID: string, val: any, location: string);
|
|
|
|
function broadcast_option(ID: string, val: any, location: string)
|
|
{
|
|
# There's not currently a common topic to broadcast to as then enabling
|
|
# implicit Broker forwarding would cause a routing loop.
|
|
Broker::publish(Cluster::worker_topic, Config::cluster_set_option,
|
|
ID, val, location);
|
|
Broker::publish(Cluster::proxy_topic, Config::cluster_set_option,
|
|
ID, val, location);
|
|
Broker::publish(Cluster::logger_topic, Config::cluster_set_option,
|
|
ID, val, location);
|
|
}
|
|
|
|
event Config::cluster_set_option(ID: string, val: any, location: string)
|
|
{
|
|
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
|
option_cache[ID] = OptionCacheValue($val=val, $location=location);
|
|
broadcast_option(ID, val, location);
|
|
@endif
|
|
|
|
Option::set(ID, val, location);
|
|
}
|
|
|
|
function set_value(ID: string, val: any, location: string &default = ""): bool
|
|
{
|
|
# Always copy the value to break references -- if caller mutates their
|
|
# value afterwards, we still guarantee the option has not changed. If
|
|
# one wants it to change, they need to explicitly call Option::set_value
|
|
# or Option::set with the intended value at the time of the call.
|
|
val = copy(val);
|
|
|
|
# First try setting it locally - abort if not possible.
|
|
if ( ! Option::set(ID, val, location) )
|
|
return F;
|
|
|
|
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
|
option_cache[ID] = OptionCacheValue($val=val, $location=location);
|
|
broadcast_option(ID, val, location);
|
|
@else
|
|
Broker::publish(Cluster::manager_topic, Config::cluster_set_option,
|
|
ID, val, location);
|
|
@endif
|
|
|
|
return T;
|
|
}
|
|
@else # Standalone implementation
|
|
function set_value(ID: string, val: any, location: string &default = ""): bool
|
|
{
|
|
return Option::set(ID, val, location);
|
|
}
|
|
@endif # Cluster::is_enabled
|
|
|
|
@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER )
|
|
# Handling of new worker nodes.
|
|
event Cluster::node_up(name: string, id: string) &priority=-10
|
|
{
|
|
# When a node connects, send it all current Option values.
|
|
if ( name in Cluster::nodes )
|
|
for ( ID in option_cache )
|
|
Broker::publish(Cluster::node_topic(name), Config::cluster_set_option, ID, option_cache[ID]$val, option_cache[ID]$location);
|
|
}
|
|
@endif
|
|
|
|
|
|
function format_value(value: any) : string
|
|
{
|
|
local tn = type_name(value);
|
|
local part: string_vec = vector();
|
|
|
|
if ( /^set/ in tn && strstr(tn, ",") == 0 )
|
|
{
|
|
# The conversion to set here is tricky and assumes
|
|
# that the set isn't indexed via a tuple of types.
|
|
# The above check for commas in the type name
|
|
# ensures this.
|
|
local it: set[bool] = value;
|
|
for ( sv in it )
|
|
part += cat(sv);
|
|
return join_string_vec(part, ",");
|
|
}
|
|
else if ( /^vector/ in tn )
|
|
{
|
|
local vit: vector of any = value;
|
|
for ( i in vit )
|
|
part += cat(vit[i]);
|
|
return join_string_vec(part, ",");
|
|
}
|
|
else if ( tn == "string" )
|
|
return value;
|
|
|
|
return cat(value);
|
|
}
|
|
|
|
function config_option_changed(ID: string, new_value: any, location: string): any
|
|
{
|
|
local log = Info($ts=network_time(), $id=ID, $old_value=format_value(lookup_ID(ID)), $new_value=format_value(new_value));
|
|
if ( location != "" )
|
|
log$location = location;
|
|
Log::write(LOG, log);
|
|
return new_value;
|
|
}
|
|
|
|
event zeek_init() &priority=10
|
|
{
|
|
Log::create_stream(LOG, [$columns=Info, $ev=log_config, $path="config", $policy=log_policy]);
|
|
|
|
# Limit logging to the manager - everyone else just feeds off it.
|
|
@if ( !Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER )
|
|
# Iterate over all existing options and add ourselves as change handlers
|
|
# with a low priority so that we can log the changes.
|
|
local gids = global_ids();
|
|
for ( i, gid in gids )
|
|
{
|
|
if ( ! gid$option_value )
|
|
next;
|
|
|
|
Option::set_change_handler(i, config_option_changed, -100);
|
|
}
|
|
@endif
|
|
}
|