mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00

* origin/topic/vern/zval: (42 commits) whitespace tweaks resolved some TODO comments remove unnecessary casts, and change necessary ones to use static_cast<> explain cmp_func default change functions for ZVal type management to static members fix some unsigned/signed integer warnings address lint concern about uninitialized variable Remove use of obsolete forward-declaration macros fix #include's that lack zeek/ prefixes explicitly populate holes created in vectors fixes for now-incorrect assumption that GetField always returns an existing ValPtr memory management for assignment to vector elements memory management for assignment to record fields destructor cleanup from ZAM_vector/ZAM_record fix #include's that lack zeek/ prefixes overlooked another way in which vector holes can be created initialize vector holes to the correct corresponding type explicitly populate holes created in vectors fix other instances of GetField().get() assuming long-lived ValPtr's fix for now-incorrect assumption that GetField always returns an existing ValPtr ...
243 lines
8.8 KiB
C++
243 lines
8.8 KiB
C++
##! Definitions of built-in functions that allow the scripting layer to
|
|
##! change the value of options and to be notified when option values change.
|
|
|
|
module Option;
|
|
|
|
%%{
|
|
#include "zeek/NetVar.h"
|
|
#include "zeek/broker/Data.h"
|
|
|
|
static bool call_option_handlers_and_set_value(zeek::StringVal* name, const zeek::detail::IDPtr& i,
|
|
zeek::ValPtr val, zeek::StringVal* location)
|
|
{
|
|
if ( i->HasOptionHandlers() )
|
|
{
|
|
for ( auto handler_function : i->GetOptionHandlers() )
|
|
{
|
|
bool add_loc = handler_function->GetType()->ParamList()->GetTypes().size() == 3;
|
|
zeek::Args vl;
|
|
vl.reserve(2 + add_loc);
|
|
vl.emplace_back(zeek::NewRef{}, name);
|
|
vl.emplace_back(val);
|
|
|
|
if ( add_loc )
|
|
vl.emplace_back(zeek::NewRef{}, location);
|
|
|
|
val = handler_function->Invoke(&vl); // consumed by next call.
|
|
|
|
if ( ! val )
|
|
{
|
|
// Someone messed up, don't change value and just return
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// clone to prevent changes
|
|
i->SetVal(val->Clone());
|
|
return true;
|
|
}
|
|
%%}
|
|
|
|
## Set an option to a new value. This change will also cause the option change
|
|
## handlers to be called.
|
|
##
|
|
## 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 occurred.
|
|
##
|
|
## .. zeek:see:: Option::set_change_handler Config::set_value
|
|
##
|
|
## .. note:: :zeek:id:`Option::set` only works on one node and does not distribute
|
|
## new values across a cluster. The higher-level :zeek:id:`Config::set_value`
|
|
## supports clusterization and should typically be used instead of this
|
|
## lower-level function.
|
|
function Option::set%(ID: string, val: any, location: string &default=""%): bool
|
|
%{
|
|
const auto& i = zeek::detail::global_scope()->Find(ID->CheckString());
|
|
if ( ! i )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Could not find ID named '%s'", ID->CheckString()));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( ! i->HasVal() )
|
|
{
|
|
// should be impossible because initialization is enforced
|
|
zeek::emit_builtin_error(zeek::util::fmt("ID '%s' has no value", ID->CheckString()));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( ! i->IsOption() )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("ID '%s' is not an option", ID->CheckString()));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( same_type(val->GetType(), zeek::Broker::detail::DataVal::ScriptDataType()) )
|
|
{
|
|
auto valptr = val->AsRecordVal()->GetField(0);
|
|
auto dv = static_cast<zeek::Broker::detail::DataVal*>(valptr.get());
|
|
auto val_from_data = dv->castTo(i->GetType().get());
|
|
|
|
if ( ! val_from_data )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Incompatible type for set of ID '%s': got broker data '%s', need '%s'",
|
|
ID->CheckString(), dv->data.get_type_name(),
|
|
type_name(i->GetType()->Tag())));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
auto rval = call_option_handlers_and_set_value(ID, i, std::move(val_from_data), location);
|
|
return zeek::val_mgr->Bool(rval);
|
|
}
|
|
|
|
if ( ! same_type(i->GetType(), val->GetType()) )
|
|
{
|
|
if ( i->GetType()->Tag() == TYPE_TABLE &&
|
|
val->GetType()->Tag() == TYPE_TABLE &&
|
|
val->GetType()->AsTableType()->IsUnspecifiedTable() )
|
|
{
|
|
// Just coerce an empty/unspecified table to the right type.
|
|
auto tv = zeek::make_intrusive<zeek::TableVal>(
|
|
zeek::cast_intrusive<TableType>(i->GetType()),
|
|
i->GetVal()->AsTableVal()->GetAttrs());
|
|
auto rval = call_option_handlers_and_set_value(ID, i, std::move(tv), location);
|
|
return zeek::val_mgr->Bool(rval);
|
|
}
|
|
|
|
zeek::emit_builtin_error(zeek::util::fmt("Incompatible type for set of ID '%s': got '%s', need '%s'",
|
|
ID->CheckString(), type_name(val->GetType()->Tag()),
|
|
type_name(i->GetType()->Tag())));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
auto rval = call_option_handlers_and_set_value(ID, i, {zeek::NewRef{}, val}, location);
|
|
return zeek::val_mgr->Bool(rval);
|
|
%}
|
|
|
|
## Set a change handler for an option. The change handler will be
|
|
## called anytime :zeek:id:`Option::set` is called for the option.
|
|
##
|
|
## ID: The ID of the option for which change notifications are desired.
|
|
##
|
|
## on_change: The function that will be called when a change occurs. The
|
|
## function can choose to receive two or three parameters: the first
|
|
## parameter is a string containing *ID*, the second parameter is
|
|
## the new option value. The third, optional, parameter is the
|
|
## location string as passed to Option::set. Note that the global
|
|
## value is not yet changed when the function is called. The passed
|
|
## function has to return the new value that it wants the option to
|
|
## be set to. This enables it to reject changes, or change values
|
|
## that are being set. When several change handlers are set for an
|
|
## option they are chained; the second change handler will see the
|
|
## return value of the first change handler as the "new value".
|
|
##
|
|
## priority: The priority of the function that was added; functions with higher
|
|
## priority are called first, functions with the same priority are
|
|
## called in the order in which they were added.
|
|
##
|
|
## Returns: true when the change handler was set, false when an error occurred.
|
|
##
|
|
## .. zeek:see:: Option::set
|
|
function Option::set_change_handler%(ID: string, on_change: any, priority: int &default=0%): bool
|
|
%{
|
|
const auto& i = zeek::detail::global_scope()->Find(ID->CheckString());
|
|
if ( ! i )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Could not find ID named '%s'", ID->CheckString()));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( ! i->IsOption() )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("ID '%s' is not an option", ID->CheckString()));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( on_change->GetType()->Tag() != TYPE_FUNC )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Option::on_change needs function argument; got '%s' for ID '%s'",
|
|
type_name(on_change->GetType()->Tag()), ID->CheckString()));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( on_change->GetType()->AsFuncType()->Flavor() != zeek::FUNC_FLAVOR_FUNCTION )
|
|
{
|
|
zeek::emit_builtin_error("Option::on_change needs function argument; not hook or event");
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
const auto& args = on_change->GetType()->AsFuncType()->ParamList()->GetTypes();
|
|
if ( args.size() < 2 || args.size() > 3 )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Wrong number of arguments for passed function in Option::on_change for ID '%s'; expected 2 or 3, got %zu",
|
|
ID->CheckString(), args.size()));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( args[0]->Tag() != TYPE_STRING )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("First argument of passed function has to be string in Option::on_change for ID '%s'; got '%s'",
|
|
ID->CheckString(), type_name(args[0]->Tag())));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( ! same_type(args[1], i->GetType()) )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Second argument of passed function has to be %s in Option::on_change for ID '%s'; got '%s'",
|
|
type_name(i->GetType()->Tag()), ID->CheckString(),
|
|
type_name(args[1]->Tag())));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( args.size() == 3 && args[2]->Tag() != TYPE_STRING )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Third argument of passed function has to be string in Option::on_change for ID '%s'; got '%s'",
|
|
ID->CheckString(), type_name(args[2]->Tag())));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
if ( ! same_type(on_change->GetType()->AsFuncType()->Yield(), i->GetType()) )
|
|
{
|
|
zeek::emit_builtin_error(zeek::util::fmt("Passed function needs to return type '%s' for ID '%s'; got '%s'",
|
|
type_name(i->GetType()->Tag()), ID->CheckString(),
|
|
type_name(on_change->GetType()->AsFuncType()->Yield()->Tag())));
|
|
return zeek::val_mgr->False();
|
|
}
|
|
|
|
auto func = on_change->As<FuncVal*>()->AsFuncPtr();
|
|
i->AddOptionHandler(func, -priority);
|
|
return zeek::val_mgr->True();
|
|
%}
|
|
|
|
## Helper function that converts a set (of arbitrary index type) to
|
|
## a "vector of any".
|
|
##
|
|
## v: an "any" type corresponding to a set.
|
|
##
|
|
## Returns: a vector-of-any with one element for each member of v.
|
|
function Option::any_set_to_any_vec%(v: any%): any_vec
|
|
%{
|
|
auto ret_type = make_intrusive<VectorType>(base_type(TYPE_ANY));
|
|
auto ret = make_intrusive<VectorVal>(ret_type);
|
|
const auto& t = v->GetType();
|
|
|
|
if ( ! t->IsSet() )
|
|
{
|
|
zeek::emit_builtin_error("argument is not a set");
|
|
return ret;
|
|
}
|
|
|
|
auto lv = v->AsTableVal()->ToListVal();
|
|
auto n = lv->Length();
|
|
|
|
for ( int i = 0; i < n; ++i )
|
|
ret->Assign(i, lv->Idx(i));
|
|
|
|
return ret;
|
|
%}
|