diff --git a/src/option.bif b/src/option.bif index 011db2f069..67108053f3 100644 --- a/src/option.bif +++ b/src/option.bif @@ -5,6 +5,36 @@ module Option; %%{ #include "NetVar.h" +#include "broker/Data.h" + +static bool call_option_handlers_and_set_value(StringVal* name, ID* i, Val* val, + StringVal* location) + { + val->Ref(); + if ( i->HasOptionHandlers() ) + { + for ( auto handler_function : i->GetOptionHandlers() ) + { + val_list vl(2); + vl.append(name->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 false; + } + } + } + + // clone to prevent changes + i->SetVal(val->Clone()); + Unref(val); // Either ref'd once or function call result. + return true; + } %%} ## Set an option to a new value. This change will also cause the option change @@ -41,36 +71,32 @@ function Option::set%(ID: string, val: any, location: string &default=""%): bool return new Val(0, TYPE_BOOL); } + if ( same_type(val->Type(), bro_broker::DataVal::ScriptDataType()) ) + { + auto dv = static_cast(val->AsRecordVal()->Lookup(0)); + auto val_from_data = dv->castTo(i->Type()); + + if ( ! val_from_data ) + { + builtin_error(fmt("Incompatible type for set of ID '%s': got broker data '%s', need '%s'", + ID->CheckString(), dv->data.get_type_name(), type_name(i->Type()->Tag()))); + return new Val(0, TYPE_BOOL); + } + + auto rval = call_option_handlers_and_set_value(ID, i, val_from_data, location); + Unref(val_from_data); + return new Val(rval, 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()))); + return new Val(0, TYPE_BOOL); } - 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); + auto rval = call_option_handlers_and_set_value(ID, i, val, location); + return new Val(rval, TYPE_BOOL); %} ## Set a change handler for an option. The change handler will be diff --git a/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/manager-1..stdout b/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/manager-1..stdout new file mode 100644 index 0000000000..ad74a79d79 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/manager-1..stdout @@ -0,0 +1,2 @@ +option changed, testport, 44/tcp, +option changed, teststring, b, comment diff --git a/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/worker-1..stdout b/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/worker-1..stdout new file mode 100644 index 0000000000..3558bbe476 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/worker-1..stdout @@ -0,0 +1,4 @@ +option changed, testport, 44/tcp, +option changed, teststring, b, comment +option changed, testport, 44/tcp, +option changed, teststring, b, comment diff --git a/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/worker-2..stdout b/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/worker-2..stdout new file mode 100644 index 0000000000..ad74a79d79 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.config.basic_cluster/worker-2..stdout @@ -0,0 +1,2 @@ +option changed, testport, 44/tcp, +option changed, teststring, b, comment diff --git a/testing/btest/scripts/base/frameworks/config/basic_cluster.bro b/testing/btest/scripts/base/frameworks/config/basic_cluster.bro index 8882c6d66a..1703bee8e5 100644 --- a/testing/btest/scripts/base/frameworks/config/basic_cluster.bro +++ b/testing/btest/scripts/base/frameworks/config/basic_cluster.bro @@ -2,7 +2,10 @@ # @TEST-EXEC: sleep 1 # @TEST-EXEC: btest-bg-run worker-1 BROPATH=$BROPATH:.. CLUSTER_NODE=worker-1 bro %INPUT # @TEST-EXEC: btest-bg-run worker-2 BROPATH=$BROPATH:.. CLUSTER_NODE=worker-2 bro %INPUT -# @TEST-EXEC: btest-bg-wait 10 +# @TEST-EXEC: btest-bg-wait 15 +# @TEST-EXEC: btest-diff manager-1/.stdout +# @TEST-EXEC: btest-diff worker-1/.stdout +# @TEST-EXEC: btest-diff worker-2/.stdout @load base/frameworks/config @@ -24,10 +27,6 @@ export { global n = 0; -event bro_init() &priority=5 - { - } - event Broker::peer_lost(endpoint: Broker::EndpointInfo, msg: string) { terminate(); @@ -46,8 +45,27 @@ event ready_for_data() Config::set_value("testport", 44/tcp); Config::set_value("teststring", "b", "comment"); } + @endif +event die() + { + terminate(); + } + +function option_changed(ID: string, new_value: any, location: string): any + { + print "option changed", ID, new_value, location; + schedule 5sec { die() }; + return new_value; + } + +event bro_init() &priority=5 + { + Option::set_change_handler("testport", option_changed, -100); + Option::set_change_handler("teststring", option_changed, -100); + } + @if ( Cluster::local_node_type() == Cluster::MANAGER ) global peer_count = 0;