diff --git a/NEWS b/NEWS index 607a26eadd..3c89a53ceb 100644 --- a/NEWS +++ b/NEWS @@ -70,6 +70,27 @@ New Functionality As noted under breaking changes, the blank identifier ``_`` cannot be referenced in expression anymore. +- Re-introduce event groups. Allow the &group attribute on event and hook + handlers for annotating them with one or more event groups. These groups + can be disabled and enable during runtime. Disabling an event group implies + disabling all event and hook handlers that are part of it. + + The main difference to a previous implementation in (very) old Zeek versions + is its granularity: It is now possible to toggle individual event handlers + (event handler bodies). The original implementation worked at the level of + events, disabling or enabling all event handlers for a given event at once. + Additionally, support for hooks was added as these are structurally similar + to events. + + The BIFs ``disable_event_group()`` and ``enable_event_group()`` are + re-instantiated and allow controlling event groups based on the group + attribute. + + Additionally, event and hook handlers are implicitly placed into event + module groups based on the module they are implemented in. All events + implemented in a given module can be toggled with ``disable_module_events()`` + and ``enable_module_events()``. + Changed Functionality --------------------- diff --git a/src/EventRegistry.cc b/src/EventRegistry.cc index 6140746b54..f4c68c5ad2 100644 --- a/src/EventRegistry.cc +++ b/src/EventRegistry.cc @@ -146,4 +146,69 @@ void EventRegistry::ActivateAllHandlers() } } +EventGroupPtr EventRegistry::RegisterGroup(EventGroupKind kind, std::string_view name) + { + auto key = std::pair{kind, std::string{name}}; + if ( const auto& it = event_groups.find(key); it != event_groups.end() ) + return it->second; + + auto group = std::make_shared(kind, name); + return event_groups.emplace(key, group).first->second; + } + +EventGroupPtr EventRegistry::LookupGroup(EventGroupKind kind, std::string_view name) + { + auto key = std::pair{kind, std::string{name}}; + if ( const auto& it = event_groups.find(key); it != event_groups.end() ) + return it->second; + + return nullptr; + } + +EventGroup::EventGroup(EventGroupKind kind, std::string_view name) : kind(kind), name(name) { } + +EventGroup::~EventGroup() noexcept { } + +// Run through all ScriptFunc instances associated with this group and +// update their bodies after a group's enable/disable state has changed. +// +// EventGroup is private friend with Func, so fiddeling with the bodies +// directly works and keeps the logic away from Func for now. +void EventGroup::UpdateFuncBodies() + { + static auto is_disabled = [](const auto& g) + { + return g->IsDisabled(); + }; + + for ( const auto& func : funcs ) + for ( auto& b : func->bodies ) + b.disabled = std::any_of(b.groups.cbegin(), b.groups.cend(), is_disabled); + } + +void EventGroup::Enable() + { + if ( enabled ) + return; + + enabled = true; + + UpdateFuncBodies(); + } + +void EventGroup::Disable() + { + if ( ! enabled ) + return; + + enabled = false; + + UpdateFuncBodies(); + } + +void EventGroup::AddFunc(detail::ScriptFuncPtr f) + { + funcs.insert(f); + } + } // namespace zeek diff --git a/src/EventRegistry.h b/src/EventRegistry.h index bc19644793..52dbdec005 100644 --- a/src/EventRegistry.h +++ b/src/EventRegistry.h @@ -11,13 +11,31 @@ #include #include +#include "zeek/IntrusivePtr.h" + namespace zeek { +// The different kinds of event groups that exist. +enum class EventGroupKind + { + Attribute, + Module, + }; + +class EventGroup; class EventHandler; class EventHandlerPtr; class RE_Matcher; +using EventGroupPtr = std::shared_ptr; + +namespace detail + { +class ScriptFunc; +using ScriptFuncPtr = zeek::IntrusivePtr; + } + // The registry keeps track of all events that we provide or handle. class EventRegistry { @@ -71,11 +89,89 @@ public: */ void ActivateAllHandlers(); + /** + * Lookup or register a new event group. + * + * @return Pointer to the group. + */ + EventGroupPtr RegisterGroup(EventGroupKind kind, std::string_view name); + + /** + * Lookup an event group. + * + * @return Pointer to the group or nil if the group does not exist. + */ + EventGroupPtr LookupGroup(EventGroupKind kind, std::string_view name); + private: std::map, std::less<>> handlers; // Tracks whether a given event handler was registered in a // non-script context. std::unordered_set not_only_from_script; + + // Map event groups identified by kind and name to their instances. + std::map, std::shared_ptr, std::less<>> + event_groups; + }; + +/** + * Event group. + * + * Event and hook handlers (Func::Body instances) can be part of event groups. + * + * By default, all groups are enabled. An event or hook handler that is part of + * any group that is disabled will be disabled and its execution prevented. + * + * Different kinds of event groups exist. Currently, attribute and module + * event groups are implemented. The first relates to event handler tagged + * with the &group attribute. The second is based on grouping event and hook + * handlers by the module in which and these are implmented. + * + * Different kinds of are separate: Disabling the "HTTP" module event group does + * not disable event handlers tagged with &group="HTTP", or vice versa. + * + * As an implementation detail: Event groups hold pointers to ScriptFunc + * instances. Enabling or disabling individual groups iterates through all + * bodies of the tracked ScriptFuncs and updates them to reflect the current + * group state. + */ +class EventGroup + { +public: + EventGroup(EventGroupKind kind, std::string_view name); + ~EventGroup() noexcept; + EventGroup(const EventGroup& g) = delete; + EventGroup& operator=(const EventGroup&) = delete; + + /** + * Enable this event group and update all event handlers associated with it. + */ + void Enable(); + + /** + * Disable this event group and update all event handlers associated with it. + */ + void Disable(); + + /** + * @return True if this group is disabled else false. + */ + bool IsDisabled() { return ! enabled; } + + /** + * Add a function to this group that may contain matching bodies. + * + * @param f Pointer to the function to track. + */ + void AddFunc(detail::ScriptFuncPtr f); + +private: + void UpdateFuncBodies(); + + EventGroupKind kind; + std::string name; + bool enabled = true; + std::unordered_set funcs; }; extern EventRegistry* event_registry; diff --git a/src/Expr.cc b/src/Expr.cc index bb1717f36e..318d9369cb 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -4762,9 +4762,9 @@ LambdaExpr::LambdaExpr(std::unique_ptr arg_ing, IDPList ar // Install a dummy version of the function globally for use only // when broker provides a closure. - auto dummy_func = make_intrusive(ingredients->id, ingredients->body, - ingredients->inits, ingredients->frame_size, - ingredients->priority); + auto dummy_func = make_intrusive(ingredients->id); + dummy_func->AddBody(ingredients->body, ingredients->inits, ingredients->frame_size, + ingredients->priority); dummy_func->SetOuterIDs(outer_ids); @@ -4892,8 +4892,9 @@ ScopePtr LambdaExpr::GetScope() const ValPtr LambdaExpr::Eval(Frame* f) const { - auto lamb = make_intrusive(ingredients->id, ingredients->body, ingredients->inits, - ingredients->frame_size, ingredients->priority); + auto lamb = make_intrusive(ingredients->id); + lamb->AddBody(ingredients->body, ingredients->inits, ingredients->frame_size, + ingredients->priority); lamb->CreateCaptures(f); diff --git a/src/Func.cc b/src/Func.cc index a4e1806c8b..4b5f4e7e8e 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -121,9 +121,16 @@ std::string render_call_stack() return rval; } +void Func::AddBody(detail::StmtPtr new_body, const std::vector& new_inits, + size_t new_frame_size, int priority) + { + std::set groups; + AddBody(new_body, new_inits, new_frame_size, priority, groups); + } + void Func::AddBody(detail::StmtPtr /* new_body */, const std::vector& /* new_inits */, size_t /* new_frame_size */, - int /* priority */) + int /* priority */, const std::set& /* groups */) { Internal("Func::AddBody called"); } @@ -271,22 +278,11 @@ void Func::CheckPluginResult(bool handled, const ValPtr& hook_result, FunctionFl namespace detail { -ScriptFunc::ScriptFunc(const IDPtr& arg_id, StmtPtr arg_body, const std::vector& aggr_inits, - size_t arg_frame_size, int priority) - : Func(SCRIPT_FUNC) +ScriptFunc::ScriptFunc(const IDPtr& arg_id) : Func(SCRIPT_FUNC) { name = arg_id->Name(); type = arg_id->GetType(); - frame_size = arg_frame_size; - - if ( arg_body ) - { - Body b; - b.stmts = AddInits(std::move(arg_body), aggr_inits); - current_body = b.stmts; - current_priority = b.priority = priority; - bodies.push_back(b); - } + frame_size = 0; } ScriptFunc::ScriptFunc(std::string _name, FuncTypePtr ft, std::vector bs, @@ -383,6 +379,9 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const for ( const auto& body : bodies ) { + if ( body.disabled ) + continue; + if ( sample_logger ) sample_logger->LocationSeen(body.stmts->GetLocationInfo()); @@ -530,14 +529,14 @@ void ScriptFunc::SetCaptures(Frame* f) } void ScriptFunc::AddBody(StmtPtr new_body, const std::vector& new_inits, - size_t new_frame_size, int priority) + size_t new_frame_size, int priority, const std::set& groups) { if ( new_frame_size > frame_size ) frame_size = new_frame_size; - auto num_args = GetType()->Params()->NumFields(); + auto num_args = static_cast(GetType()->Params()->NumFields()); - if ( num_args > static_cast(frame_size) ) + if ( num_args > frame_size ) frame_size = num_args; new_body = AddInits(std::move(new_body), new_inits); @@ -551,6 +550,7 @@ void ScriptFunc::AddBody(StmtPtr new_body, const std::vector& new_inits, Body b; b.stmts = new_body; + b.groups = groups; current_body = new_body; current_priority = b.priority = priority; @@ -788,7 +788,7 @@ static int get_func_priority(const std::vector& attrs) for ( const auto& a : attrs ) { - if ( a->Tag() == ATTR_DEPRECATED || a->Tag() == ATTR_IS_USED ) + if ( a->Tag() == ATTR_DEPRECATED || a->Tag() == ATTR_IS_USED || a->Tag() == ATTR_GROUP ) continue; if ( a->Tag() != ATTR_PRIORITY ) @@ -817,7 +817,42 @@ static int get_func_priority(const std::vector& attrs) return priority; } -function_ingredients::function_ingredients(ScopePtr scope, StmtPtr body) +// Get a function's groups from its Scope's attributes. Errors if it sees any +// problems with the group tag. get_func_priority() checks for illegal +// attributes, so we don't do this here. +static std::set get_func_groups(const std::vector& attrs) + { + std::set groups; + + for ( const auto& a : attrs ) + { + if ( a->Tag() != ATTR_GROUP ) + continue; + + auto v = a->GetExpr()->Eval(nullptr); + + if ( ! v ) + { + a->Error("cannot evaluate attribute expression"); + continue; + } + + if ( ! IsString(v->GetType()->Tag()) ) + { + a->Error("expression is not of string type"); + continue; + } + + auto group = event_registry->RegisterGroup(EventGroupKind::Attribute, + v->AsStringVal()->ToStdStringView()); + groups.insert(group); + } + + return groups; + } + +function_ingredients::function_ingredients(ScopePtr scope, StmtPtr body, + const std::string& module_name) { frame_size = scope->Length(); inits = scope->GetInits(); @@ -831,6 +866,8 @@ function_ingredients::function_ingredients(ScopePtr scope, StmtPtr body) { priority = get_func_priority(*attrs); + groups = get_func_groups(*attrs); + for ( const auto& a : *attrs ) if ( a->Tag() == ATTR_IS_USED ) { @@ -845,6 +882,16 @@ function_ingredients::function_ingredients(ScopePtr scope, StmtPtr body) priority = 0; this->body = std::move(body); + this->module_name = module_name; + + // Implicit module event groups for events and hooks. + auto flavor = id->GetType()->Flavor(); + if ( flavor == FUNC_FLAVOR_EVENT || flavor == FUNC_FLAVOR_HOOK ) + { + auto module_group = event_registry->RegisterGroup(EventGroupKind::Module, + this->module_name); + groups.insert(module_group); + } } static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) diff --git a/src/Func.h b/src/Func.h index 567e60944f..05b2b6dc9a 100644 --- a/src/Func.h +++ b/src/Func.h @@ -47,6 +47,9 @@ class ScriptFunc; } // namespace detail +class EventGroup; +using EventGroupPtr = std::shared_ptr; + class Func; using FuncPtr = IntrusivePtr; @@ -70,6 +73,11 @@ public: { detail::StmtPtr stmts; int priority; + std::set groups; + // If any of the groups are disabled, this body is disabled. + // The disabled field is updated from EventGroup instances. + bool disabled = false; + bool operator<(const Body& other) const { return priority > other.priority; @@ -99,6 +107,11 @@ public: return Invoke(&zargs); } + // Add a new event handler to an existing function (event). + virtual void AddBody(detail::StmtPtr new_body, const std::vector& new_inits, + size_t new_frame_size, int priority, + const std::set& groups); + // Add a new event handler to an existing function (event). virtual void AddBody(detail::StmtPtr new_body, const std::vector& new_inits, size_t new_frame_size, int priority = 0); @@ -134,6 +147,10 @@ protected: Kind kind = SCRIPT_FUNC; FuncTypePtr type; std::string name; + +private: + // EventGroup updates Func::Body.disabled + friend class EventGroup; }; namespace detail @@ -142,8 +159,7 @@ namespace detail class ScriptFunc : public Func { public: - ScriptFunc(const IDPtr& id, StmtPtr body, const std::vector& inits, size_t frame_size, - int priority); + ScriptFunc(const IDPtr& id); // For compiled scripts. ScriptFunc(std::string name, FuncTypePtr ft, std::vector bodies, @@ -196,8 +212,11 @@ public: */ bool DeserializeCaptures(const broker::vector& data); - void AddBody(StmtPtr new_body, const std::vector& new_inits, size_t new_frame_size, - int priority) override; + using Func::AddBody; + + void AddBody(detail::StmtPtr new_body, const std::vector& new_inits, + size_t new_frame_size, int priority, + const std::set& groups) override; /** * Replaces the given current instance of a function body with @@ -310,14 +329,16 @@ struct function_ingredients // Gathers all of the information from a scope and a function body needed // to build a function. - function_ingredients(ScopePtr scope, StmtPtr body); + function_ingredients(ScopePtr scope, StmtPtr body, const std::string& module_name); IDPtr id; StmtPtr body; + std::string module_name; // current module name where function body is defined std::vector inits; - int frame_size = 0; + size_t frame_size = 0; int priority = 0; ScopePtr scope; + std::set groups; }; extern std::vector call_stack; diff --git a/src/Stmt.cc b/src/Stmt.cc index f13c21455c..f017301c63 100644 --- a/src/Stmt.cc +++ b/src/Stmt.cc @@ -1997,7 +1997,8 @@ void WhenInfo::Build(StmtPtr ws) auto shebang = make_intrusive(do_test, do_bodies, dummy_return); - auto ingredients = std::make_unique(current_scope(), shebang); + auto ingredients = std::make_unique(current_scope(), shebang, + current_module); auto outer_ids = gather_outer_ids(pop_scope(), ingredients->body); lambda = make_intrusive(std::move(ingredients), std::move(outer_ids), ws); diff --git a/src/Var.cc b/src/Var.cc index 490761314a..0fadd1420d 100644 --- a/src/Var.cc +++ b/src/Var.cc @@ -391,8 +391,7 @@ static void make_var(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init, // For events, add a function value (without any body) here so that // we can later access the ID even if no implementations have been // defined. - std::vector inits; - auto f = make_intrusive(id, nullptr, inits, 0, 0); + auto f = make_intrusive(id); id->SetVal(make_intrusive(std::move(f))); } } @@ -825,7 +824,7 @@ TraversalCode OuterIDBindingFinder::PostExpr(const Expr* expr) // with this variable set can find flaws in the duplication machinery. static bool duplicate_ASTs = getenv("ZEEK_DUPLICATE_ASTS"); -void end_func(StmtPtr body, bool free_of_conditionals) +void end_func(StmtPtr body, const char* module_name, bool free_of_conditionals) { if ( duplicate_ASTs && reporter->Errors() == 0 ) // Only try duplication in the absence of errors. If errors @@ -842,24 +841,26 @@ void end_func(StmtPtr body, bool free_of_conditionals) oi->num_stmts = Stmt::GetNumStmts(); oi->num_exprs = Expr::GetNumExprs(); - auto ingredients = std::make_unique(pop_scope(), std::move(body)); - - if ( ingredients->id->HasVal() ) - ingredients->id->GetVal()->AsFunc()->AddBody( - ingredients->body, ingredients->inits, ingredients->frame_size, ingredients->priority); - else + auto ingredients = std::make_unique(pop_scope(), std::move(body), + module_name); + if ( ! ingredients->id->HasVal() ) { - auto f = make_intrusive(ingredients->id, ingredients->body, ingredients->inits, - ingredients->frame_size, ingredients->priority); - + auto f = make_intrusive(ingredients->id); ingredients->id->SetVal(make_intrusive(std::move(f))); ingredients->id->SetConst(); } + ingredients->id->GetVal()->AsFunc()->AddBody(ingredients->body, ingredients->inits, + ingredients->frame_size, ingredients->priority, + ingredients->groups); + auto func_ptr = cast_intrusive(ingredients->id->GetVal())->AsFuncPtr(); auto func = cast_intrusive(func_ptr); func->SetScope(ingredients->scope); + for ( const auto& group : ingredients->groups ) + group->AddFunc(func); + analyze_func(std::move(func)); // Note: ideally, something would take ownership of this memory until the diff --git a/src/Var.h b/src/Var.h index 85f62f4d0f..51a6543202 100644 --- a/src/Var.h +++ b/src/Var.h @@ -45,7 +45,7 @@ extern void add_type(ID* id, TypePtr t, std::unique_ptr> at extern void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool is_redef, FuncTypePtr t, std::unique_ptr> attrs = nullptr); -extern void end_func(StmtPtr body, bool free_of_conditionals); +extern void end_func(StmtPtr body, const char* module_name, bool free_of_conditionals); // Gather all IDs referenced inside a body that aren't part of a given scope. extern IDPList gather_outer_ids(ScopePtr scope, StmtPtr body); diff --git a/src/parse.y b/src/parse.y index 337a1e63dc..e8b2afc50a 100644 --- a/src/parse.y +++ b/src/parse.y @@ -5,7 +5,7 @@ // Switching parser table type fixes ambiguity problems. %define lr.type ielr -%expect 205 +%expect 211 %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF @@ -27,7 +27,7 @@ %token TOK_ATTR_EXPIRE_CREATE TOK_ATTR_EXPIRE_READ TOK_ATTR_EXPIRE_WRITE %token TOK_ATTR_RAW_OUTPUT TOK_ATTR_ON_CHANGE TOK_ATTR_BROKER_STORE %token TOK_ATTR_BROKER_STORE_ALLOW_COMPLEX TOK_ATTR_BACKEND -%token TOK_ATTR_PRIORITY TOK_ATTR_LOG TOK_ATTR_ERROR_HANDLER +%token TOK_ATTR_PRIORITY TOK_ATTR_LOG TOK_ATTR_ERROR_HANDLER TOK_ATTR_GROUP %token TOK_ATTR_TYPE_COLUMN TOK_ATTR_DEPRECATED %token TOK_ATTR_IS_ASSIGNED TOK_ATTR_IS_USED TOK_ATTR_ORDERED @@ -1496,7 +1496,7 @@ func_body: conditional_epoch > func_hdr_cond_epoch ) free_of_conditionals = false; - end_func({AdoptRef{}, $3}, free_of_conditionals); + end_func({AdoptRef{}, $3}, current_module.c_str(), free_of_conditionals); } ; @@ -1526,7 +1526,7 @@ lambda_body: // Gather the ingredients for a Func from the // current scope. auto ingredients = std::make_unique( - current_scope(), IntrusivePtr{AdoptRef{}, $3}); + current_scope(), IntrusivePtr{AdoptRef{}, $3}, current_module.c_str()); auto outer_ids = gather_outer_ids(pop_scope(), ingredients->body); $$ = new LambdaExpr(std::move(ingredients), std::move(outer_ids)); @@ -1714,6 +1714,8 @@ attr: { $$ = new Attr(ATTR_RAW_OUTPUT); } | TOK_ATTR_PRIORITY '=' expr { $$ = new Attr(ATTR_PRIORITY, {AdoptRef{}, $3}); } + | TOK_ATTR_GROUP '=' expr + { $$ = new Attr(ATTR_GROUP, {AdoptRef{}, $3}); } | TOK_ATTR_TYPE_COLUMN '=' expr { $$ = new Attr(ATTR_TYPE_COLUMN, {AdoptRef{}, $3}); } | TOK_ATTR_LOG diff --git a/src/scan.l b/src/scan.l index 0d19162615..ab5ad8dd25 100644 --- a/src/scan.l +++ b/src/scan.l @@ -317,6 +317,7 @@ when return TOK_WHEN; &raw_output return TOK_ATTR_RAW_OUTPUT; &error_handler return TOK_ATTR_ERROR_HANDLER; &expire_func return TOK_ATTR_EXPIRE_FUNC; +&group return TOK_ATTR_GROUP; &log return TOK_ATTR_LOG; &optional return TOK_ATTR_OPTIONAL; &is_assigned return TOK_ATTR_IS_ASSIGNED; diff --git a/src/script_opt/ScriptOpt.cc b/src/script_opt/ScriptOpt.cc index 966dd13ff8..a29ca2b79a 100644 --- a/src/script_opt/ScriptOpt.cc +++ b/src/script_opt/ScriptOpt.cc @@ -59,7 +59,8 @@ const FuncInfo* analyze_global_stmts(Stmt* stmts) auto sc = current_scope(); std::vector empty_inits; StmtPtr stmts_p{NewRef{}, stmts}; - global_stmts = make_intrusive(id, stmts_p, empty_inits, sc->Length(), 0); + global_stmts = make_intrusive(id); + global_stmts->AddBody(stmts_p, empty_inits, sc->Length()); funcs.emplace_back(global_stmts, sc, stmts_p, 0); diff --git a/src/zeek.bif b/src/zeek.bif index c250a6ae18..85607f2d86 100644 --- a/src/zeek.bif +++ b/src/zeek.bif @@ -5581,3 +5581,88 @@ function is_packet_analyzer%(atype: AllAnalyzers::Tag%) : bool auto val = atype->AsEnumVal(); return val_mgr->Bool(zeek::packet_mgr->Lookup(val) != nullptr); %} + +%%{ // C segment + +static bool enable_event_group(zeek::EventGroupKind kind, const char *group) + { + auto g = zeek::event_registry->LookupGroup(kind, group); + if ( ! g ) + { + zeek::emit_builtin_error(zeek::util::fmt("no such event group: %s", group)); + return false; + } + + g->Enable(); + return true; + } + +static bool disable_event_group(zeek::EventGroupKind kind, const char *group) + { + auto g = zeek::event_registry->LookupGroup(kind, group); + if ( ! g ) + { + zeek::emit_builtin_error(zeek::util::fmt("no such event group: %s", group)); + return false; + } + + g->Disable(); + return true; + } + +%%} + +## Enabled the given event group. +## +## All event and hook handlers with a matching :zeek:attr:`&group` attribute +## will be enabled if this group was the last disabled group of these handlers. +## +## group: The group to enable. +## +## .. zeek:see:: disable_event_group +function enable_event_group%(group: string%) : bool + %{ + return zeek::val_mgr->Bool(enable_event_group(zeek::EventGroupKind::Attribute, + group->CheckString())); + %} + +## Disabled the given event group. +## +## All event and hook handlers with a matching :zeek:attr:`&group` attribute +## will be disabled if not already disabled through another group. +## +## group: The group to disable. +## +## .. zeek:see:: enable_event_group +function disable_event_group%(group: string%) : bool + %{ + return zeek::val_mgr->Bool(disable_event_group(zeek::EventGroupKind::Attribute, + group->CheckString())); + %} + +## Enable all event handlers and hooks in the given module. +## +## All event handlers and hooks defined in the given module will be enabled +## if not disabled otherwise through an event group. +## +## module_name: The module to enable. +## +## .. zeek:see:: disable_module_events enable_event_group disable_event_group +function enable_module_events%(module_name: string%) : bool + %{ + return zeek::val_mgr->Bool(enable_event_group(zeek::EventGroupKind::Module, + module_name->CheckString())); + %} + +## Disable all event handlers and hooks in the given module. +## +## All event handlers and hooks defined in the given module will be disabled. +## +## module_name: The module to disable. +## +## .. zeek:see:: enable_module_events enable_event_group disable_event_group +function disable_module_events%(module_name: string%) : bool + %{ + return zeek::val_mgr->Bool(disable_event_group(zeek::EventGroupKind::Module, + module_name->CheckString())); + %} diff --git a/testing/btest/Baseline/core.event-groups.basic/output b/testing/btest/Baseline/core.event-groups.basic/output new file mode 100644 index 0000000000..d6ba68e6c7 --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.basic/output @@ -0,0 +1,4 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +zeek_init +zeek_done without group +zeek_done with group my-group2 diff --git a/testing/btest/Baseline/core.event-groups.errors-2/output b/testing/btest/Baseline/core.event-groups.errors-2/output new file mode 100644 index 0000000000..c32811c361 --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.errors-2/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/errors.zeek, line 3: no such event group: my-group (disable_event_group(my-group)) +disable non existing event group, F diff --git a/testing/btest/Baseline/core.event-groups.errors-3/output b/testing/btest/Baseline/core.event-groups.errors-3/output new file mode 100644 index 0000000000..0753016940 --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.errors-3/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/errors.zeek, line 3: no such event group: MyModule (enable_module_events(MyModule)) +enabling non existing module event group, F diff --git a/testing/btest/Baseline/core.event-groups.errors-4/output b/testing/btest/Baseline/core.event-groups.errors-4/output new file mode 100644 index 0000000000..ac95f0b8a0 --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.errors-4/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/errors.zeek, line 3: no such event group: MyModule (disable_module_events(MyModule)) +disable non existing module event group, F diff --git a/testing/btest/Baseline/core.event-groups.errors/output b/testing/btest/Baseline/core.event-groups.errors/output new file mode 100644 index 0000000000..54666dc63f --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.errors/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/errors.zeek, line 6: no such event group: my-group (enable_event_group(my-group)) +enable non existing event group, F diff --git a/testing/btest/Baseline/core.event-groups.hooks-basic/output b/testing/btest/Baseline/core.event-groups.hooks-basic/output new file mode 100644 index 0000000000..b8e534e228 --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.hooks-basic/output @@ -0,0 +1,8 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +the_hook without group, 1 +the_hook with my-group1, 1 +the_hook with my-group2, 1 +the_hook with my-group1 and my-group2, 1 +=== disable_event_group(my-group1) +the_hook without group, 2 +the_hook with my-group2, 2 diff --git a/testing/btest/Baseline/core.event-groups.hooks-module/output b/testing/btest/Baseline/core.event-groups.hooks-module/output new file mode 100644 index 0000000000..43b0299c7b --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.hooks-module/output @@ -0,0 +1,9 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +HookModule::module_hook in MyModule, 1 +HookModule::module_hook in MyOtherModule, 1 +HookModule::module_hook in GLOBAL, 1 +=== disable_module_events(MyModule) +HookModule::module_hook in MyOtherModule, 2 +HookModule::module_hook in GLOBAL, 2 +zeek_done in MyOtherModule +zeek_done in global diff --git a/testing/btest/Baseline/core.event-groups.module/output b/testing/btest/Baseline/core.event-groups.module/output new file mode 100644 index 0000000000..0616909e94 --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.module/output @@ -0,0 +1,4 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +zeek_init +zeek_done within MyOtherModule +zeek_done within MyOtherModule (&group=MyModule) diff --git a/testing/btest/Baseline/core.event-groups.multi-group/output b/testing/btest/Baseline/core.event-groups.multi-group/output new file mode 100644 index 0000000000..3103791875 --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.multi-group/output @@ -0,0 +1,4 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +zeek_init +zeek_done with group my-group1 +zeek_done without groups diff --git a/testing/btest/Baseline/core.event-groups.runtime/output b/testing/btest/Baseline/core.event-groups.runtime/output new file mode 100644 index 0000000000..fc2846a07b --- /dev/null +++ b/testing/btest/Baseline/core.event-groups.runtime/output @@ -0,0 +1,22 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +5, e1-no-group +5, e1-group1 +5, e2-no-group +5, e2-group1 +5, e3-group2 +disable group1 +4, e1-no-group +4, e2-no-group +4, e3-group2 +3, e1-no-group +3, e2-no-group +3, e3-group2 +2, e1-no-group +2, e2-no-group +2, e3-group2 +enable group1 +1, e1-no-group +1, e1-group1 +1, e2-no-group +1, e2-group1 +1, e3-group2 diff --git a/testing/btest/core/event-groups/basic.zeek b/testing/btest/core/event-groups/basic.zeek new file mode 100644 index 0000000000..5df462cff0 --- /dev/null +++ b/testing/btest/core/event-groups/basic.zeek @@ -0,0 +1,27 @@ +# @TEST-DOC: Very basic testing of event groups with zeek_init / zeek_done. +# @TEST-EXEC: zeek %INPUT > output +# @TEST-EXEC: btest-diff output + +event zeek_init() + { + print "zeek_init"; + + # Disable the my-group1 group + disable_event_group("my-group1"); + } + +# Disabled above and shouldn't show. +event zeek_done() &group="my-group1" + { + print "FAIL: zeek_done with group my-group1"; + } + +event zeek_done() + { + print "zeek_done without group"; + } + +event zeek_done() &group="my-group2" + { + print "zeek_done with group my-group2"; + } diff --git a/testing/btest/core/event-groups/errors.zeek b/testing/btest/core/event-groups/errors.zeek new file mode 100644 index 0000000000..b8aa36461a --- /dev/null +++ b/testing/btest/core/event-groups/errors.zeek @@ -0,0 +1,29 @@ +# @TEST-DOC: Very basic testing of errors for non-existing groups. +# @TEST-EXEC: zeek %INPUT > output 2>&1 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff output +event zeek_init() + { + local r = enable_event_group("my-group"); + print "enable non existing event group", r; + } + +@TEST-START-NEXT +event zeek_init() + { + local r = disable_event_group("my-group"); + print "disable non existing event group", r; + } + +@TEST-START-NEXT +event zeek_init() + { + local r = enable_module_events("MyModule"); + print "enabling non existing module event group", r; + } + +@TEST-START-NEXT +event zeek_init() + { + local r = disable_module_events("MyModule"); + print "disable non existing module event group", r; + } diff --git a/testing/btest/core/event-groups/hooks-basic.zeek b/testing/btest/core/event-groups/hooks-basic.zeek new file mode 100644 index 0000000000..4d71b3b80d --- /dev/null +++ b/testing/btest/core/event-groups/hooks-basic.zeek @@ -0,0 +1,33 @@ +# @TEST-DOC: Hooks can be annotated with &group and work. +# @TEST-EXEC: zeek %INPUT > output +# @TEST-EXEC: btest-diff output + +global the_hook: hook(c: count); + +event zeek_init() +{ + hook the_hook(1); + print "=== disable_event_group(my-group1)"; + disable_event_group("my-group1"); + hook the_hook(2); +} + +hook the_hook(c: count) + { + print "the_hook without group", c; + } + +hook the_hook(c: count) &group="my-group1" + { + print "the_hook with my-group1", c; + } + +hook the_hook(c: count) &group="my-group2" + { + print "the_hook with my-group2", c; + } + +hook the_hook(c: count) &group="my-group1" &group="my-group2" + { + print "the_hook with my-group1 and my-group2", c; + } diff --git a/testing/btest/core/event-groups/hooks-module.zeek b/testing/btest/core/event-groups/hooks-module.zeek new file mode 100644 index 0000000000..f7a4ec8c58 --- /dev/null +++ b/testing/btest/core/event-groups/hooks-module.zeek @@ -0,0 +1,53 @@ +# @TEST-DOC: Very basic testing that hooks in modules are disabled through disable_module_events(), too. +# @TEST-EXEC: zeek %INPUT > output +# @TEST-EXEC: btest-diff output + +module HookModule; + +export { + global module_hook: hook(c: count); +} + +module MyModule; + +hook HookModule::module_hook(c: count) + { + print "HookModule::module_hook in MyModule", c; + } + +event zeek_done() + { + print "FAIL: zeek_done in MyModule"; + } + +module MyOtherModule; + +hook HookModule::module_hook(c: count) + { + print "HookModule::module_hook in MyOtherModule", c; + } + +event zeek_done() + { + print "zeek_done in MyOtherModule"; + } + +module GLOBAL; + +hook HookModule::module_hook(c: count) + { + print "HookModule::module_hook in GLOBAL", c; + } + +event zeek_init() + { + hook HookModule::module_hook(1); + print "=== disable_module_events(MyModule)"; + disable_module_events("MyModule"); + hook HookModule::module_hook(2); + } + +event zeek_done() + { + print "zeek_done in global"; + } diff --git a/testing/btest/core/event-groups/module.zeek b/testing/btest/core/event-groups/module.zeek new file mode 100644 index 0000000000..706492e495 --- /dev/null +++ b/testing/btest/core/event-groups/module.zeek @@ -0,0 +1,49 @@ +# @TEST-DOC: Very basic testing of event groups for modules. The MyModule event group is disabled. +# @TEST-EXEC: zeek %INPUT > output +# @TEST-EXEC: btest-diff output + +module MyModule; + +event zeek_done() + { + print "FAIL: zeek_done within MyModule"; + } + +module My::Nested::Module; + +event zeek_done() + { + print "FAIL: zeek_done within My::Nested::Module"; + } + +module MyOtherModule; + +event zeek_done() + { + print "zeek_done within MyOtherModule"; + } + +event zeek_done() &group="MyModule" + { + # continues to run because &group="MyModule" isn't the same + # as the "MyModule" module group. + print "zeek_done within MyOtherModule (&group=MyModule)"; + } + +module GLOBAL; + +event zeek_init() + { + print "zeek_init"; + + disable_module_events("MyModule"); + disable_module_events("My::Nested::Module"); + } + +# Re-open the MyModule module here once more. +module MyModule; + +event zeek_done() + { + print "FAIL: Another zeek_done() within MyModule"; + } diff --git a/testing/btest/core/event-groups/multi-group.zeek b/testing/btest/core/event-groups/multi-group.zeek new file mode 100644 index 0000000000..95e1ac41b7 --- /dev/null +++ b/testing/btest/core/event-groups/multi-group.zeek @@ -0,0 +1,35 @@ +# @TEST-DOC: Test support for multiple &group attributes +# @TEST-EXEC: zeek %INPUT > output +# @TEST-EXEC: btest-diff output + +event zeek_init() + { + print "zeek_init"; + + disable_event_group("my-group2"); + } + +event zeek_done() &group="my-group1" &group="my-group2" + { + print "FAIL: zeek_done with group my-group1, my-group2"; + } + +event zeek_done() &group="my-group2" &group="my-group1" + { + print "FAIL: zeek_done with group my-group2, my-group1"; + } + +event zeek_done() &group="my-group2" + { + print "FAIL: zeek_done with group my-group2"; + } + +event zeek_done() &group="my-group1" + { + print "zeek_done with group my-group1"; + } + +event zeek_done() + { + print "zeek_done without groups"; + } diff --git a/testing/btest/core/event-groups/runtime.zeek b/testing/btest/core/event-groups/runtime.zeek new file mode 100644 index 0000000000..dc3d496903 --- /dev/null +++ b/testing/btest/core/event-groups/runtime.zeek @@ -0,0 +1,50 @@ +# @TEST-DOC: Enabling/disabling groups at runtime driven by ticks. +# @TEST-EXEC: zeek %INPUT > output +# @TEST-EXEC: btest-diff output + +event e1(c: count) { + print c, "e1-no-group"; +} +event e2(c: count) { + print c, "e2-no-group"; +} + +event e1(c: count) &group="group1" { + print c, "e1-group1"; +} +event e2(c: count) &group="group1" { + print c, "e2-group1"; +} + +event e3(c: count) &group="group2" { + print c, "e3-group2"; +} + +event tick(c: count) + { + event e1(c); + event e2(c); + event e3(c); + + if ( c == 4 ) + { + print "disable group1"; + disable_event_group("group1"); + } + + if ( c == 1 ) + { + print "enable group1"; + enable_event_group("group1"); + } + + --c; + if ( c > 0 ) + event tick(c); + + } + +event zeek_init() + { + event tick(5); + }