Reintroduce event groups

This started with reverting commit 52cd02173d
and then rewriting it to be per handler rather than handler identifier
and adding support for hooks as well as adding implicit module groups.
This commit is contained in:
Arne Welzel 2022-10-25 16:53:17 +02:00
parent 5aa7d80e88
commit 2ad609cbbb
30 changed files with 730 additions and 49 deletions

21
NEWS
View file

@ -70,6 +70,27 @@ New Functionality
As noted under breaking changes, the blank identifier ``_`` cannot be As noted under breaking changes, the blank identifier ``_`` cannot be
referenced in expression anymore. 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 Changed Functionality
--------------------- ---------------------

View file

@ -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<EventGroup>(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 } // namespace zeek

View file

@ -11,13 +11,31 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#include "zeek/IntrusivePtr.h"
namespace zeek namespace zeek
{ {
// The different kinds of event groups that exist.
enum class EventGroupKind
{
Attribute,
Module,
};
class EventGroup;
class EventHandler; class EventHandler;
class EventHandlerPtr; class EventHandlerPtr;
class RE_Matcher; class RE_Matcher;
using EventGroupPtr = std::shared_ptr<EventGroup>;
namespace detail
{
class ScriptFunc;
using ScriptFuncPtr = zeek::IntrusivePtr<ScriptFunc>;
}
// The registry keeps track of all events that we provide or handle. // The registry keeps track of all events that we provide or handle.
class EventRegistry class EventRegistry
{ {
@ -71,11 +89,89 @@ public:
*/ */
void ActivateAllHandlers(); 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: private:
std::map<std::string, std::unique_ptr<EventHandler>, std::less<>> handlers; std::map<std::string, std::unique_ptr<EventHandler>, std::less<>> handlers;
// Tracks whether a given event handler was registered in a // Tracks whether a given event handler was registered in a
// non-script context. // non-script context.
std::unordered_set<std::string> not_only_from_script; std::unordered_set<std::string> not_only_from_script;
// Map event groups identified by kind and name to their instances.
std::map<std::pair<EventGroupKind, std::string>, std::shared_ptr<EventGroup>, 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<detail::ScriptFuncPtr> funcs;
}; };
extern EventRegistry* event_registry; extern EventRegistry* event_registry;

View file

@ -4762,8 +4762,8 @@ LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing, IDPList ar
// Install a dummy version of the function globally for use only // Install a dummy version of the function globally for use only
// when broker provides a closure. // when broker provides a closure.
auto dummy_func = make_intrusive<ScriptFunc>(ingredients->id, ingredients->body, auto dummy_func = make_intrusive<ScriptFunc>(ingredients->id);
ingredients->inits, ingredients->frame_size, dummy_func->AddBody(ingredients->body, ingredients->inits, ingredients->frame_size,
ingredients->priority); ingredients->priority);
dummy_func->SetOuterIDs(outer_ids); dummy_func->SetOuterIDs(outer_ids);
@ -4892,8 +4892,9 @@ ScopePtr LambdaExpr::GetScope() const
ValPtr LambdaExpr::Eval(Frame* f) const ValPtr LambdaExpr::Eval(Frame* f) const
{ {
auto lamb = make_intrusive<ScriptFunc>(ingredients->id, ingredients->body, ingredients->inits, auto lamb = make_intrusive<ScriptFunc>(ingredients->id);
ingredients->frame_size, ingredients->priority); lamb->AddBody(ingredients->body, ingredients->inits, ingredients->frame_size,
ingredients->priority);
lamb->CreateCaptures(f); lamb->CreateCaptures(f);

View file

@ -121,9 +121,16 @@ std::string render_call_stack()
return rval; return rval;
} }
void Func::AddBody(detail::StmtPtr new_body, const std::vector<detail::IDPtr>& new_inits,
size_t new_frame_size, int priority)
{
std::set<EventGroupPtr> groups;
AddBody(new_body, new_inits, new_frame_size, priority, groups);
}
void Func::AddBody(detail::StmtPtr /* new_body */, void Func::AddBody(detail::StmtPtr /* new_body */,
const std::vector<detail::IDPtr>& /* new_inits */, size_t /* new_frame_size */, const std::vector<detail::IDPtr>& /* new_inits */, size_t /* new_frame_size */,
int /* priority */) int /* priority */, const std::set<EventGroupPtr>& /* groups */)
{ {
Internal("Func::AddBody called"); Internal("Func::AddBody called");
} }
@ -271,22 +278,11 @@ void Func::CheckPluginResult(bool handled, const ValPtr& hook_result, FunctionFl
namespace detail namespace detail
{ {
ScriptFunc::ScriptFunc(const IDPtr& arg_id, StmtPtr arg_body, const std::vector<IDPtr>& aggr_inits, ScriptFunc::ScriptFunc(const IDPtr& arg_id) : Func(SCRIPT_FUNC)
size_t arg_frame_size, int priority)
: Func(SCRIPT_FUNC)
{ {
name = arg_id->Name(); name = arg_id->Name();
type = arg_id->GetType<zeek::FuncType>(); type = arg_id->GetType<zeek::FuncType>();
frame_size = arg_frame_size; frame_size = 0;
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);
}
} }
ScriptFunc::ScriptFunc(std::string _name, FuncTypePtr ft, std::vector<StmtPtr> bs, ScriptFunc::ScriptFunc(std::string _name, FuncTypePtr ft, std::vector<StmtPtr> bs,
@ -383,6 +379,9 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const
for ( const auto& body : bodies ) for ( const auto& body : bodies )
{ {
if ( body.disabled )
continue;
if ( sample_logger ) if ( sample_logger )
sample_logger->LocationSeen(body.stmts->GetLocationInfo()); sample_logger->LocationSeen(body.stmts->GetLocationInfo());
@ -530,14 +529,14 @@ void ScriptFunc::SetCaptures(Frame* f)
} }
void ScriptFunc::AddBody(StmtPtr new_body, const std::vector<IDPtr>& new_inits, void ScriptFunc::AddBody(StmtPtr new_body, const std::vector<IDPtr>& new_inits,
size_t new_frame_size, int priority) size_t new_frame_size, int priority, const std::set<EventGroupPtr>& groups)
{ {
if ( new_frame_size > frame_size ) if ( new_frame_size > frame_size )
frame_size = new_frame_size; frame_size = new_frame_size;
auto num_args = GetType()->Params()->NumFields(); auto num_args = static_cast<size_t>(GetType()->Params()->NumFields());
if ( num_args > static_cast<int>(frame_size) ) if ( num_args > frame_size )
frame_size = num_args; frame_size = num_args;
new_body = AddInits(std::move(new_body), new_inits); new_body = AddInits(std::move(new_body), new_inits);
@ -551,6 +550,7 @@ void ScriptFunc::AddBody(StmtPtr new_body, const std::vector<IDPtr>& new_inits,
Body b; Body b;
b.stmts = new_body; b.stmts = new_body;
b.groups = groups;
current_body = new_body; current_body = new_body;
current_priority = b.priority = priority; current_priority = b.priority = priority;
@ -788,7 +788,7 @@ static int get_func_priority(const std::vector<AttrPtr>& attrs)
for ( const auto& a : 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; continue;
if ( a->Tag() != ATTR_PRIORITY ) if ( a->Tag() != ATTR_PRIORITY )
@ -817,7 +817,42 @@ static int get_func_priority(const std::vector<AttrPtr>& attrs)
return priority; 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<EventGroupPtr> get_func_groups(const std::vector<AttrPtr>& attrs)
{
std::set<EventGroupPtr> 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(); frame_size = scope->Length();
inits = scope->GetInits(); inits = scope->GetInits();
@ -831,6 +866,8 @@ function_ingredients::function_ingredients(ScopePtr scope, StmtPtr body)
{ {
priority = get_func_priority(*attrs); priority = get_func_priority(*attrs);
groups = get_func_groups(*attrs);
for ( const auto& a : *attrs ) for ( const auto& a : *attrs )
if ( a->Tag() == ATTR_IS_USED ) if ( a->Tag() == ATTR_IS_USED )
{ {
@ -845,6 +882,16 @@ function_ingredients::function_ingredients(ScopePtr scope, StmtPtr body)
priority = 0; priority = 0;
this->body = std::move(body); this->body = std::move(body);
this->module_name = module_name;
// Implicit module event groups for events and hooks.
auto flavor = id->GetType<zeek::FuncType>()->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) static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind)

View file

@ -47,6 +47,9 @@ class ScriptFunc;
} // namespace detail } // namespace detail
class EventGroup;
using EventGroupPtr = std::shared_ptr<EventGroup>;
class Func; class Func;
using FuncPtr = IntrusivePtr<Func>; using FuncPtr = IntrusivePtr<Func>;
@ -70,6 +73,11 @@ public:
{ {
detail::StmtPtr stmts; detail::StmtPtr stmts;
int priority; int priority;
std::set<EventGroupPtr> 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 bool operator<(const Body& other) const
{ {
return priority > other.priority; return priority > other.priority;
@ -99,6 +107,11 @@ public:
return Invoke(&zargs); return Invoke(&zargs);
} }
// Add a new event handler to an existing function (event).
virtual void AddBody(detail::StmtPtr new_body, const std::vector<detail::IDPtr>& new_inits,
size_t new_frame_size, int priority,
const std::set<EventGroupPtr>& groups);
// Add a new event handler to an existing function (event). // Add a new event handler to an existing function (event).
virtual void AddBody(detail::StmtPtr new_body, const std::vector<detail::IDPtr>& new_inits, virtual void AddBody(detail::StmtPtr new_body, const std::vector<detail::IDPtr>& new_inits,
size_t new_frame_size, int priority = 0); size_t new_frame_size, int priority = 0);
@ -134,6 +147,10 @@ protected:
Kind kind = SCRIPT_FUNC; Kind kind = SCRIPT_FUNC;
FuncTypePtr type; FuncTypePtr type;
std::string name; std::string name;
private:
// EventGroup updates Func::Body.disabled
friend class EventGroup;
}; };
namespace detail namespace detail
@ -142,8 +159,7 @@ namespace detail
class ScriptFunc : public Func class ScriptFunc : public Func
{ {
public: public:
ScriptFunc(const IDPtr& id, StmtPtr body, const std::vector<IDPtr>& inits, size_t frame_size, ScriptFunc(const IDPtr& id);
int priority);
// For compiled scripts. // For compiled scripts.
ScriptFunc(std::string name, FuncTypePtr ft, std::vector<StmtPtr> bodies, ScriptFunc(std::string name, FuncTypePtr ft, std::vector<StmtPtr> bodies,
@ -196,8 +212,11 @@ public:
*/ */
bool DeserializeCaptures(const broker::vector& data); bool DeserializeCaptures(const broker::vector& data);
void AddBody(StmtPtr new_body, const std::vector<IDPtr>& new_inits, size_t new_frame_size, using Func::AddBody;
int priority) override;
void AddBody(detail::StmtPtr new_body, const std::vector<detail::IDPtr>& new_inits,
size_t new_frame_size, int priority,
const std::set<EventGroupPtr>& groups) override;
/** /**
* Replaces the given current instance of a function body with * 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 // Gathers all of the information from a scope and a function body needed
// to build a function. // to build a function.
function_ingredients(ScopePtr scope, StmtPtr body); function_ingredients(ScopePtr scope, StmtPtr body, const std::string& module_name);
IDPtr id; IDPtr id;
StmtPtr body; StmtPtr body;
std::string module_name; // current module name where function body is defined
std::vector<IDPtr> inits; std::vector<IDPtr> inits;
int frame_size = 0; size_t frame_size = 0;
int priority = 0; int priority = 0;
ScopePtr scope; ScopePtr scope;
std::set<EventGroupPtr> groups;
}; };
extern std::vector<CallInfo> call_stack; extern std::vector<CallInfo> call_stack;

View file

@ -1997,7 +1997,8 @@ void WhenInfo::Build(StmtPtr ws)
auto shebang = make_intrusive<StmtList>(do_test, do_bodies, dummy_return); auto shebang = make_intrusive<StmtList>(do_test, do_bodies, dummy_return);
auto ingredients = std::make_unique<function_ingredients>(current_scope(), shebang); auto ingredients = std::make_unique<function_ingredients>(current_scope(), shebang,
current_module);
auto outer_ids = gather_outer_ids(pop_scope(), ingredients->body); auto outer_ids = gather_outer_ids(pop_scope(), ingredients->body);
lambda = make_intrusive<LambdaExpr>(std::move(ingredients), std::move(outer_ids), ws); lambda = make_intrusive<LambdaExpr>(std::move(ingredients), std::move(outer_ids), ws);

View file

@ -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 // For events, add a function value (without any body) here so that
// we can later access the ID even if no implementations have been // we can later access the ID even if no implementations have been
// defined. // defined.
std::vector<IDPtr> inits; auto f = make_intrusive<ScriptFunc>(id);
auto f = make_intrusive<ScriptFunc>(id, nullptr, inits, 0, 0);
id->SetVal(make_intrusive<FuncVal>(std::move(f))); id->SetVal(make_intrusive<FuncVal>(std::move(f)));
} }
} }
@ -825,7 +824,7 @@ TraversalCode OuterIDBindingFinder::PostExpr(const Expr* expr)
// with this variable set can find flaws in the duplication machinery. // with this variable set can find flaws in the duplication machinery.
static bool duplicate_ASTs = getenv("ZEEK_DUPLICATE_ASTS"); 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 ) if ( duplicate_ASTs && reporter->Errors() == 0 )
// Only try duplication in the absence of errors. If errors // 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_stmts = Stmt::GetNumStmts();
oi->num_exprs = Expr::GetNumExprs(); oi->num_exprs = Expr::GetNumExprs();
auto ingredients = std::make_unique<function_ingredients>(pop_scope(), std::move(body)); auto ingredients = std::make_unique<function_ingredients>(pop_scope(), std::move(body),
module_name);
if ( ingredients->id->HasVal() ) if ( ! ingredients->id->HasVal() )
ingredients->id->GetVal()->AsFunc()->AddBody(
ingredients->body, ingredients->inits, ingredients->frame_size, ingredients->priority);
else
{ {
auto f = make_intrusive<ScriptFunc>(ingredients->id, ingredients->body, ingredients->inits, auto f = make_intrusive<ScriptFunc>(ingredients->id);
ingredients->frame_size, ingredients->priority);
ingredients->id->SetVal(make_intrusive<FuncVal>(std::move(f))); ingredients->id->SetVal(make_intrusive<FuncVal>(std::move(f)));
ingredients->id->SetConst(); ingredients->id->SetConst();
} }
ingredients->id->GetVal()->AsFunc()->AddBody(ingredients->body, ingredients->inits,
ingredients->frame_size, ingredients->priority,
ingredients->groups);
auto func_ptr = cast_intrusive<FuncVal>(ingredients->id->GetVal())->AsFuncPtr(); auto func_ptr = cast_intrusive<FuncVal>(ingredients->id->GetVal())->AsFuncPtr();
auto func = cast_intrusive<ScriptFunc>(func_ptr); auto func = cast_intrusive<ScriptFunc>(func_ptr);
func->SetScope(ingredients->scope); func->SetScope(ingredients->scope);
for ( const auto& group : ingredients->groups )
group->AddFunc(func);
analyze_func(std::move(func)); analyze_func(std::move(func));
// Note: ideally, something would take ownership of this memory until the // Note: ideally, something would take ownership of this memory until the

View file

@ -45,7 +45,7 @@ extern void add_type(ID* id, TypePtr t, std::unique_ptr<std::vector<AttrPtr>> at
extern void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool is_redef, extern void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool is_redef,
FuncTypePtr t, std::unique_ptr<std::vector<AttrPtr>> attrs = nullptr); FuncTypePtr t, std::unique_ptr<std::vector<AttrPtr>> 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. // Gather all IDs referenced inside a body that aren't part of a given scope.
extern IDPList gather_outer_ids(ScopePtr scope, StmtPtr body); extern IDPList gather_outer_ids(ScopePtr scope, StmtPtr body);

View file

@ -5,7 +5,7 @@
// Switching parser table type fixes ambiguity problems. // Switching parser table type fixes ambiguity problems.
%define lr.type ielr %define lr.type ielr
%expect 205 %expect 211
%token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY
%token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF %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_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_RAW_OUTPUT TOK_ATTR_ON_CHANGE TOK_ATTR_BROKER_STORE
%token TOK_ATTR_BROKER_STORE_ALLOW_COMPLEX TOK_ATTR_BACKEND %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_TYPE_COLUMN TOK_ATTR_DEPRECATED
%token TOK_ATTR_IS_ASSIGNED TOK_ATTR_IS_USED TOK_ATTR_ORDERED %token TOK_ATTR_IS_ASSIGNED TOK_ATTR_IS_USED TOK_ATTR_ORDERED
@ -1496,7 +1496,7 @@ func_body:
conditional_epoch > func_hdr_cond_epoch ) conditional_epoch > func_hdr_cond_epoch )
free_of_conditionals = false; 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 // Gather the ingredients for a Func from the
// current scope. // current scope.
auto ingredients = std::make_unique<function_ingredients>( auto ingredients = std::make_unique<function_ingredients>(
current_scope(), IntrusivePtr{AdoptRef{}, $3}); current_scope(), IntrusivePtr{AdoptRef{}, $3}, current_module.c_str());
auto outer_ids = gather_outer_ids(pop_scope(), ingredients->body); auto outer_ids = gather_outer_ids(pop_scope(), ingredients->body);
$$ = new LambdaExpr(std::move(ingredients), std::move(outer_ids)); $$ = new LambdaExpr(std::move(ingredients), std::move(outer_ids));
@ -1714,6 +1714,8 @@ attr:
{ $$ = new Attr(ATTR_RAW_OUTPUT); } { $$ = new Attr(ATTR_RAW_OUTPUT); }
| TOK_ATTR_PRIORITY '=' expr | TOK_ATTR_PRIORITY '=' expr
{ $$ = new Attr(ATTR_PRIORITY, {AdoptRef{}, $3}); } { $$ = new Attr(ATTR_PRIORITY, {AdoptRef{}, $3}); }
| TOK_ATTR_GROUP '=' expr
{ $$ = new Attr(ATTR_GROUP, {AdoptRef{}, $3}); }
| TOK_ATTR_TYPE_COLUMN '=' expr | TOK_ATTR_TYPE_COLUMN '=' expr
{ $$ = new Attr(ATTR_TYPE_COLUMN, {AdoptRef{}, $3}); } { $$ = new Attr(ATTR_TYPE_COLUMN, {AdoptRef{}, $3}); }
| TOK_ATTR_LOG | TOK_ATTR_LOG

View file

@ -317,6 +317,7 @@ when return TOK_WHEN;
&raw_output return TOK_ATTR_RAW_OUTPUT; &raw_output return TOK_ATTR_RAW_OUTPUT;
&error_handler return TOK_ATTR_ERROR_HANDLER; &error_handler return TOK_ATTR_ERROR_HANDLER;
&expire_func return TOK_ATTR_EXPIRE_FUNC; &expire_func return TOK_ATTR_EXPIRE_FUNC;
&group return TOK_ATTR_GROUP;
&log return TOK_ATTR_LOG; &log return TOK_ATTR_LOG;
&optional return TOK_ATTR_OPTIONAL; &optional return TOK_ATTR_OPTIONAL;
&is_assigned return TOK_ATTR_IS_ASSIGNED; &is_assigned return TOK_ATTR_IS_ASSIGNED;

View file

@ -59,7 +59,8 @@ const FuncInfo* analyze_global_stmts(Stmt* stmts)
auto sc = current_scope(); auto sc = current_scope();
std::vector<IDPtr> empty_inits; std::vector<IDPtr> empty_inits;
StmtPtr stmts_p{NewRef{}, stmts}; StmtPtr stmts_p{NewRef{}, stmts};
global_stmts = make_intrusive<ScriptFunc>(id, stmts_p, empty_inits, sc->Length(), 0); global_stmts = make_intrusive<ScriptFunc>(id);
global_stmts->AddBody(stmts_p, empty_inits, sc->Length());
funcs.emplace_back(global_stmts, sc, stmts_p, 0); funcs.emplace_back(global_stmts, sc, stmts_p, 0);

View file

@ -5581,3 +5581,88 @@ function is_packet_analyzer%(atype: AllAnalyzers::Tag%) : bool
auto val = atype->AsEnumVal(); auto val = atype->AsEnumVal();
return val_mgr->Bool(zeek::packet_mgr->Lookup(val) != nullptr); 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()));
%}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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";
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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";
}

View file

@ -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";
}

View file

@ -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";
}

View file

@ -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);
}