mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 17:48:21 +00:00
166 lines
5.7 KiB
Text
166 lines
5.7 KiB
Text
##! 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 "NetVar.h"
|
|
%%}
|
|
|
|
## 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.
|
|
##
|
|
## .. bro:see:: Option::set_change_handler
|
|
function Option::set%(ID: string, val: any, location: string &default=""%): bool
|
|
%{
|
|
auto i = global_scope()->Lookup(ID->CheckString());
|
|
if ( ! i )
|
|
{
|
|
builtin_error(fmt("Could not find ID named '%s'", ID->CheckString()));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( ! i->HasVal() )
|
|
{
|
|
// should be impossible because initialization is enforced
|
|
builtin_error(fmt("ID '%s' has no value", ID->CheckString()));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( ! i->IsOption() )
|
|
{
|
|
builtin_error(fmt("ID '%s' is not an option", ID->CheckString()));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( ! same_type(i->Type(), val->Type()) )
|
|
{
|
|
builtin_error(fmt("Incompatible type for set of ID '%s': got '%s', need '%s'",
|
|
ID->CheckString(), type_name(val->Type()->Tag()), type_name(i->Type()->Tag())));
|
|
}
|
|
|
|
val->Ref();
|
|
if ( i->HasOptionHandlers() )
|
|
{
|
|
for ( auto handler_function : i->GetOptionHandlers() )
|
|
{
|
|
val_list vl(2);
|
|
vl.append(ID->Ref());
|
|
vl.append(val);
|
|
if ( handler_function->FType()->AsFuncType()->ArgTypes()->Types()->length() == 3 )
|
|
vl.append(location->Ref());
|
|
|
|
val = handler_function->Call(&vl); // consumed by next call.
|
|
if ( ! val )
|
|
{
|
|
// Someone messed up, don't change value and just return
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// clone to prevent changes
|
|
i->SetVal(val->Clone());
|
|
Unref(val); // Either ref'd once or function call result.
|
|
return new Val(1, TYPE_BOOL);
|
|
%}
|
|
|
|
## Set a change handler for an option. The change handler will be
|
|
## called anytime :bro: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.
|
|
##
|
|
## .. bro:see:: Option::set
|
|
function Option::set_change_handler%(ID: string, on_change: any, priority: int &default=0%): bool
|
|
%{
|
|
auto i = global_scope()->Lookup(ID->CheckString());
|
|
if ( ! i )
|
|
{
|
|
builtin_error(fmt("Could not find ID named '%s'", ID->CheckString()));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( ! i->IsOption() )
|
|
{
|
|
builtin_error(fmt("ID '%s' is not an option", ID->CheckString()));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( on_change->Type()->Tag() != TYPE_FUNC )
|
|
{
|
|
builtin_error(fmt("Option::on_change needs function argument; got '%s' for ID '%s'",
|
|
type_name(on_change->Type()->Tag()), ID->CheckString()));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( on_change->Type()->AsFuncType()->Flavor() != FUNC_FLAVOR_FUNCTION )
|
|
{
|
|
builtin_error("Option::on_change needs function argument; not hook or event");
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
const type_list* args = on_change->Type()->AsFuncType()->ArgTypes()->Types();
|
|
if ( args->length() < 2 || args->length() > 3 )
|
|
{
|
|
builtin_error(fmt("Wrong number of arguments for passed function in Option::on_change for ID '%s'; expected 2 or 3, got %d",
|
|
ID->CheckString(), args->length()));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( (*args)[0]->Tag() != TYPE_STRING )
|
|
{
|
|
builtin_error(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 new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( ! same_type((*args)[1], i->Type()) )
|
|
{
|
|
builtin_error(fmt("Second argument of passed function has to be %s in Option::on_change for ID '%s'; got '%s'",
|
|
type_name(i->Type()->Tag()), ID->CheckString(), type_name((*args)[1]->Tag())));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( args->length() == 3 && (*args)[2]->Tag() != TYPE_STRING )
|
|
{
|
|
builtin_error(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 new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
if ( ! same_type(on_change->Type()->AsFuncType()->YieldType(), i->Type()) )
|
|
{
|
|
builtin_error(fmt("Passed function needs to return type '%s' for ID '%s'; got '%s'",
|
|
type_name(i->Type()->Tag()), ID->CheckString(), type_name(on_change->Type()->AsFuncType()->YieldType()->Tag())));
|
|
return new Val(0, TYPE_BOOL);
|
|
}
|
|
|
|
i->AddOptionHandler(on_change->Ref()->AsFunc(), -priority);
|
|
return new Val(1, TYPE_BOOL);
|
|
%}
|