• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zeek / zeek / 5591267804971008

07 Apr 2026 06:02PM UTC coverage: 66.986% (+0.06%) from 66.927%
5591267804971008

push

cirrus

awelzel
Merge remote-tracking branch 'origin/topic/awelzel/5167-load-so'

* origin/topic/awelzel/5167-load-so:
  testing/btest/language: Add test showing directory ending in .so
  testing/btest/plugins: Add load-so-bif test
  NEWS: Add entry shared object support for @load
  testing/btest: Test loading of .so files
  scan.l: Support loading of .so files
  scan.l: Shuffle around ScannedFile usage
  plugin/Manager: Extract LoadDynamicPlugin() helper

33 of 41 new or added lines in 1 file covered. (80.49%)

532 existing lines in 10 files now uncovered.

136018 of 203054 relevant lines covered (66.99%)

2339700.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.23
/src/plugin/Manager.cc
1
// See the file "COPYING" in the main distribution directory for copyright.
2

3
#include "zeek/plugin/Manager.h"
4

5
#include <dirent.h>
6
#ifndef _MSC_VER
7
#include <dlfcn.h>
8
#include <glob.h>
9
#endif
10
#include <sys/stat.h>
11
#include <cerrno>
12
#include <cstdlib>
13
#include <filesystem>
14
#include <fstream>
15
#include <optional>
16
#include <ranges>
17
#include <regex>
18
#include <sstream>
19

20
#include "zeek/Event.h"
21
#include "zeek/Func.h"
22
#include "zeek/Reporter.h"
23
#include "zeek/Val.h"
24
#include "zeek/input.h"
25
#include "zeek/util.h"
26

27
using namespace std;
28

29
namespace zeek::plugin {
30

31
Plugin* Manager::current_plugin = nullptr;
32
const char* Manager::current_dir = nullptr;
33
const char* Manager::current_sopath = nullptr;
34

35
Manager::Manager() {
3,836✔
36
    init = false;
3,836✔
37
    hooks = new hook_list*[NUM_HOOKS];
3,836✔
38

39
    for ( int i = 0; i < NUM_HOOKS; i++ )
61,376✔
40
        hooks[i] = nullptr;
57,540✔
41
}
3,836✔
42

43
Manager::~Manager() {
5,762✔
44
    assert(! init);
2,881✔
45

46
    for ( int i = 0; i < NUM_HOOKS; i++ )
46,096✔
47
        delete hooks[i];
46,153✔
48

49
    delete[] hooks;
2,881✔
50
}
5,762✔
51

52
void Manager::SearchDynamicPlugins(const std::string& dir) {
27,578✔
53
    assert(! init);
27,578✔
54

55
    if ( dir.empty() )
27,578✔
56
        return;
3,878✔
57

58
    if ( dir.find(path_list_separator) != string::npos ) {
23,858✔
59
        // Split at ":".
60
        std::stringstream s(dir);
35✔
61
        std::string d;
35✔
62

63
        while ( std::getline(s, d, path_list_separator[0]) )
71✔
64
            SearchDynamicPlugins(d);
36✔
65

66
        return;
35✔
67
    }
35✔
68

69
    if ( ! util::is_dir(dir) ) {
23,823✔
70
        DBG_LOG(DBG_PLUGINS, "Not a valid plugin directory: %s", dir.c_str());
6✔
71
        return;
6✔
72
    }
73

74
    std::error_code ec;
23,817✔
75
    auto canon = filesystem::canonical(dir, ec);
47,634✔
76
    if ( ec ) {
23,817✔
77
        DBG_LOG(DBG_PLUGINS, "skip dynamic plugin search in %s, making path canonical failed: %s", dir.data(),
×
78
                ec.message().c_str());
79
        return;
×
80
    }
81

82
    std::string canon_path = canon.string();
23,817✔
83

84
    if ( searched_dirs.contains(canon_path) )
23,817✔
85
        return;
86

87
    searched_dirs.emplace(canon_path);
23,783✔
88

89
    // Check if it's a plugin directory.
90

91
    const std::string magic = dir + "/__zeek_plugin__";
23,783✔
92

93
    if ( util::is_file(magic) ) {
23,783✔
94
        // It's a plugin, get it's name.
95
        std::ifstream in(magic.c_str());
83✔
96

97
        if ( in.fail() )
83✔
98
            reporter->FatalError("cannot open plugin magic file %s", magic.c_str());
×
99

100
        std::string name;
83✔
101
        std::getline(in, name);
83✔
102
        util::strstrip(name);
166✔
103
        string lower_name = util::strtolower(name);
83✔
104

105
        if ( name.empty() )
83✔
106
            reporter->FatalError("empty plugin magic file %s", magic.c_str());
×
107

108
        if ( const auto& other = dynamic_plugins.find(lower_name); other != dynamic_plugins.end() ) {
166✔
109
            reporter->Warning("ignoring dynamic plugin %s from %s, already found in %s", name.c_str(), dir.c_str(),
1✔
110
                              other->second.c_str());
1✔
111
            return;
1✔
112
        }
113

114
        // Record it, so that we can later activate it.
115
        dynamic_plugins.insert(std::make_pair(lower_name, dir));
164✔
116

117
        DBG_LOG(DBG_PLUGINS, "Found plugin %s in %s", name.c_str(), dir.c_str());
82✔
118
        return;
82✔
119
    }
83✔
120

121
    // No plugin here, traverse subdirectories.
122

123
    DIR* d = opendir(dir.c_str());
23,700✔
124

125
    if ( ! d ) {
23,700✔
126
        DBG_LOG(DBG_PLUGINS, "Cannot open directory %s", dir.c_str());
×
127
        return;
×
128
    }
129

130
    bool found = false;
131

132
    struct dirent* dp;
133

134
    while ( (dp = readdir(d)) ) {
362,287✔
135
        struct stat st;
338,587✔
136

137
        if ( strcmp(dp->d_name, "..") == 0 || strcmp(dp->d_name, ".") == 0 )
338,587✔
138
            continue;
47,475✔
139

140
        // We do not search plugins in discovered dot directories.
141
        if ( (dp->d_name[0] == '.') && dp->d_type == DT_DIR )
291,187✔
142
            continue;
75✔
143

144
        string path = dir + "/" + dp->d_name;
582,224✔
145

146
        if ( stat(path.c_str(), &st) < 0 ) {
291,112✔
147
            DBG_LOG(DBG_PLUGINS, "Cannot stat %s: %s", path.c_str(), strerror(errno));
×
148
            continue;
×
149
        }
150

151
        if ( st.st_mode & S_IFDIR )
291,112✔
152
            SearchDynamicPlugins(path);
23,706✔
153
    }
291,112✔
154

155
    closedir(d);
23,700✔
156
}
47,717✔
157

158
zeek::expected<Plugin*, std::string> Manager::LoadDynamicPlugin(const std::string& path) {
84✔
159
    DBG_LOG(DBG_PLUGINS, "Loading plugin %s", path.c_str());
84✔
160

161
    current_plugin = nullptr;
84✔
162
    current_sopath = path.c_str();
84✔
163
    void* hdl = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL);
84✔
164
    current_sopath = nullptr;
84✔
165

166
    if ( ! hdl ) {
84✔
167
        const char* err = dlerror();
2✔
168
        std::string error = util::fmt("cannot load plugin library %s: %s", path.c_str(), err ? err : "<unknown error>");
2✔
169
        return zeek::unexpected<std::string>(std::move(error));
2✔
170
    }
2✔
171

172
    if ( ! current_plugin ) {
82✔
NEW
173
        std::string error = util::fmt("load plugin library %s did not instantiate a plugin", path.c_str());
×
NEW
174
        dlclose(hdl);
×
NEW
175
        return zeek::unexpected<std::string>(std::move(error));
×
NEW
176
    }
×
177

178

179
    auto* plugin = current_plugin;
82✔
180
    current_plugin = nullptr;
82✔
181

182
    // This is a bit quirky: If we go through ActivateDynamicPluginInternal(),
183
    // its logic sets current_dir to a classic plugin's top-level directory,
184
    // also called base_dir. Concretely, Plugin::Plugin() -> Manager::RegisterPlugin()
185
    // -> Plugin::SetPluginPath() populates the plugin's base_dir and sopath members.
186
    //
187
    // If a plugin is loaded via @load ./plugin.so, there's no classic base_dir.
188
    // Manager::RegisterPlugin() will skip setting the paths on the plugin. We
189
    // recognize this here and set only the sopath. A plugin loaded via
190
    // @load ./plugin.so can be identified by an empty PluginDirectory(), but
191
    // having a populated PluginPath(), though hopefully this never matters.
192
    if ( plugin->PluginPath().empty() )
82✔
193
        plugin->SetPluginLocation("", path);
6✔
194

195
    plugin->SetDynamic(true);
82✔
196
    plugin->DoConfigure();
82✔
197

198
    // After Configure(), we'll have a name. Do not allow plugins with duplicate names:
199
    // Just consider that a conflict and hard-exit: All bets are off. Note that
200
    // the just loaded plugin is already part of ActivePluginsInternal().
201
    std::string plugin_name = util::strtolower(plugin->Name());
82✔
202
    for ( const auto* p : *Manager::ActivePluginsInternal() ) {
9,025✔
203
        if ( util::strtolower(p->Name()) == plugin_name && p != plugin )
17,558✔
NEW
204
            zeek::reporter->FatalError("plugin with name %s from %s conflicts with %s plugin %s",
×
NEW
205
                                       plugin->Name().c_str(), path.c_str(),
×
NEW
206
                                       p->DynamicPlugin() ? "dynamic" : "built-in", p->Name().c_str());
×
207
    }
208

209

210
    DBG_LOG(DBG_PLUGINS, "  InitializingComponents");
82✔
211
    plugin->InitializeComponents();
82✔
212

213
    // We execute the pre-script initialization here; this in
214
    // fact could be *during* script initialization if we got
215
    // triggered via @load-plugin or @load.
216
    plugin->InitPreScript();
82✔
217

218
    DBG_LOG(DBG_PLUGINS, "  Loaded %s", path.c_str());
82✔
219

220
    return plugin;
82✔
221
}
82✔
222

223
bool Manager::ActivateDynamicPluginInternal(const std::string& name, bool ok_if_not_found,
210✔
224
                                            std::vector<std::string>* errors) {
225
// Loading dynamic plugins is not currently supported on Windows platform.
226
// Still check for static built-in plugins, though.
227
#ifdef _MSC_VER
228
    if ( ok_if_not_found )
229
        return true;
230

231
    plugin_list* all_plugins_win = Manager::ActivePluginsInternal();
232

233
    for ( const auto& p : *all_plugins_win ) {
234
        if ( p->Name() == name )
235
            return true;
236
    }
237

238
    errors->emplace_back(util::fmt("plugin %s is not available", name.c_str()));
239
    return false;
240
#else
241
    errors->clear(); // caller should pass it in empty, but just to be sure
210✔
242

243
    dynamic_plugin_map::iterator m = dynamic_plugins.find(util::strtolower(name));
420✔
244

245
    plugin_list* all_plugins = Manager::ActivePluginsInternal();
210✔
246

247
    if ( m == dynamic_plugins.end() ) {
420✔
248
        if ( ok_if_not_found )
109✔
249
            return true;
250

251
        // Check if it's a static built-in plugin; they are always
252
        // active, so just ignore. Not the most efficient way, but
253
        // this should be rare to begin with.
254
        for ( const auto& p : *all_plugins ) {
4,733✔
255
            if ( p->Name() == name )
4,515✔
256
                return true;
109✔
257
        }
258

259
        errors->emplace_back(util::fmt("plugin %s is not available", name.c_str()));
×
260
        return false;
×
261
    }
262

263
    if ( m->second.empty() ) {
101✔
264
        // That's our marker that we have already activated this
265
        // plugin. Silently ignore the new request.
266
        return true;
267
    }
268

269
    std::string dir = m->second + "/";
81✔
270

271
    DBG_LOG(DBG_PLUGINS, "Activating plugin %s", name.c_str());
81✔
272

273
    // If there's a plugin with the same name already, report an error and let
274
    // the user do the conflict resolution.
275
    auto lower_name = util::strtolower(name);
81✔
276
    for ( const auto& p : *all_plugins ) {
8,834✔
277
        if ( util::strtolower(p->Name()) == lower_name ) {
8,591✔
278
            auto v = p->Version();
×
279
            auto error = util::fmt("dynamic plugin %s from directory %s conflicts with %s plugin %s (%d.%d.%d)",
×
280
                                   name.c_str(), dir.c_str(), p->DynamicPlugin() ? "dynamic" : "built-in",
×
281
                                   p->Name().c_str(), v.major, v.minor, v.patch);
×
282
            errors->emplace_back(error);
×
283
            return false;
×
284
        }
285
    }
286

287
    // Load shared libraries.
288

289
    string dypattern = dir + "/lib/*." + HOST_ARCHITECTURE + DYNAMIC_PLUGIN_SUFFIX;
243✔
290

291
    DBG_LOG(DBG_PLUGINS, "  Searching for shared libraries %s", dypattern.c_str());
81✔
292

293
    glob_t gl;
81✔
294

295
    if ( glob(dypattern.c_str(), 0, nullptr, &gl) == 0 ) {
81✔
296
        for ( size_t i = 0; i < gl.gl_pathc; i++ ) {
162✔
297
            const char* path = gl.gl_pathv[i];
81✔
298

299
            current_dir = dir.c_str();
81✔
300
            auto result = LoadDynamicPlugin(path);
162✔
301

302
            if ( ! result ) {
81✔
303
                errors->emplace_back(result.error());
2✔
304
                continue;
2✔
305
            }
306

307
            auto* loaded_plugin = *result;
79✔
308

309
            plugins_by_path.insert(std::make_pair(util::detail::normalize_path(dir), loaded_plugin));
316✔
310

311
            // Make sure the name the plugin reports is consistent with
312
            // what we expect from its magic file.
313
            if ( util::strtolower(loaded_plugin->Name()) != util::strtolower(name) ) {
79✔
314
                errors->emplace_back(
×
NEW
315
                    util::fmt("inconsistent plugin name: %s vs %s", loaded_plugin->Name().c_str(), name.c_str()));
×
316
                continue;
×
317
            }
318
        }
81✔
319

320
        globfree(&gl);
81✔
321

322
        if ( ! errors->empty() )
162✔
323
            return false;
324
    }
325

326
    else {
327
        DBG_LOG(DBG_PLUGINS, "  No shared library found");
×
328
    }
329

330
    // Add the "scripts" and "bif" directories to ZEEKPATH.
331
    std::string scripts = dir + "scripts";
79✔
332

333
    if ( util::is_dir(scripts) ) {
79✔
334
        DBG_LOG(DBG_PLUGINS, "  Adding %s to ZEEKPATH", scripts.c_str());
77✔
335
        util::detail::add_to_zeek_path(scripts);
77✔
336
    }
337

338
    string init;
79✔
339

340
    // First load {scripts}/__preload__.zeek automatically.
341
    init = dir + "scripts/__preload__.zeek";
79✔
342

343
    if ( util::is_file(init) ) {
79✔
344
        DBG_LOG(DBG_PLUGINS, "  Loading %s", init.c_str());
77✔
345
        scripts_to_load.push_back(std::move(init));
77✔
346
    }
347

348
    // Load {bif,scripts}/__load__.zeek automatically.
349
    init = dir + "lib/bif/__load__.zeek";
79✔
350

351
    if ( util::is_file(init) ) {
79✔
352
        DBG_LOG(DBG_PLUGINS, "  Loading %s", init.c_str());
67✔
353
        scripts_to_load.push_back(std::move(init));
67✔
354
    }
355

356
    init = dir + "scripts/__load__.zeek";
79✔
357

358
    if ( util::is_file(init) ) {
79✔
359
        DBG_LOG(DBG_PLUGINS, "  Loading %s", init.c_str());
77✔
360
        scripts_to_load.push_back(std::move(init));
77✔
361
    }
362

363
    // Mark this plugin as activated by clearing the path.
364
    m->second.clear();
79✔
365

366
    return true;
79✔
367
#endif
368
}
241✔
369

370
void Manager::ActivateDynamicPlugin(const std::string& name) {
149✔
371
    std::vector<std::string> errors;
149✔
372
    if ( ActivateDynamicPluginInternal(name, false, &errors) )
149✔
373
        UpdateInputFiles();
148✔
374
    else
375
        // Reschedule for another attempt later.
376
        requested_plugins.insert(name);
1✔
377
}
298✔
378

379
void Manager::ActivateDynamicPlugins(bool all) {
3,836✔
380
    // Tracks plugins we need to activate as pairs of their names and booleans
381
    // indicating whether an activation failure is to be deemed a fatal error.
382
    std::set<std::pair<std::string, bool>> plugins_to_activate;
3,836✔
383

384
    // Activate plugins that were specifically requested.
385
    for ( const auto& x : requested_plugins ) {
11,509✔
386
        if ( ! x.empty() )
1✔
387
            plugins_to_activate.emplace(x, false);
1✔
388
    }
389

390
    // Activate plugins that our environment tells us to.
391
    vector<string> p;
3,836✔
392
    std::string plugin_activate = util::zeek_plugin_activate();
3,836✔
393
    if ( ! plugin_activate.empty() ) {
3,836✔
394
        util::tokenize_string(util::zeek_plugin_activate(), ",", &p);
22✔
395

396
        for ( const auto& x : p )
88✔
397
            plugins_to_activate.emplace(x, true);
22✔
398
    }
399

400
    if ( all ) {
3,836✔
401
        // Activate all other ones we discovered.
402
        for ( const auto& x : dynamic_plugins )
1,774✔
403
            plugins_to_activate.emplace(x.first, false);
37✔
404
    }
405

406
    // Now we keep iterating over all the plugins, trying to load them, for as
407
    // long as we're successful for at least one further of them each round.
408
    // Doing so ensures that we can resolve (non-cyclic) load dependencies
409
    // independent of any particular order.
410
    while ( ! plugins_to_activate.empty() ) {
3,892✔
411
        std::vector<std::string> errors;
56✔
412
        auto plugins_left = plugins_to_activate;
56✔
413

414
        for ( const auto& x : plugins_to_activate ) {
229✔
415
            if ( ActivateDynamicPluginInternal(x.first, x.second, &errors) )
61✔
416
                plugins_left.erase(x);
60✔
417
        }
418

419
        if ( plugins_left.size() == plugins_to_activate.size() ) {
56✔
420
            // Could not load a single further plugin this round, that's fatal.
421
            for ( const auto& msg : errors )
×
422
                reporter->Error("%s", msg.c_str());
×
423

424
            reporter->FatalError("aborting after plugin errors");
×
425
        }
426

427
        plugins_to_activate = std::move(plugins_left);
56✔
428
    }
56✔
429

430
    UpdateInputFiles();
3,836✔
431
}
7,672✔
432

433
void Manager::UpdateInputFiles() {
3,984✔
434
    for ( const auto& script : std::ranges::reverse_view(scripts_to_load) )
16,378✔
435
        add_input_file_at_front(script.c_str());
221✔
436

437
    scripts_to_load.clear();
3,984✔
438
}
3,984✔
439

440
static bool plugin_cmp(const Plugin* a, const Plugin* b) {
2,021,572✔
441
    return util::strtolower(a->Name()) < util::strtolower(b->Name());
2,021,572✔
442
}
443

444
void Manager::RegisterPlugin(Plugin* plugin) {
410,268✔
445
    Manager::ActivePluginsInternal()->push_back(plugin);
410,268✔
446

447
    if ( current_dir && current_sopath )
410,268✔
448
        // A dynamic plugin, record its location.
449
        plugin->SetPluginLocation(util::detail::normalize_path(current_dir), current_sopath);
158✔
450

451
    current_plugin = plugin;
410,268✔
452
}
410,268✔
453

454
void Manager::RegisterBifFile(const char* plugin, bif_init_func c) {
514,780✔
455
    bif_init_func_map* bifs = BifFilesInternal();
514,780✔
456

457
    std::string lower_plugin = util::strtolower(plugin);
1,029,560✔
458
    bif_init_func_map::iterator i = bifs->find(lower_plugin);
514,780✔
459

460
    if ( i == bifs->end() )
1,029,560✔
461
        i = bifs->insert(std::make_pair(lower_plugin, new bif_init_func_list())).first;
255,490✔
462

463
    i->second->push_back(c);
1,029,560✔
464
}
1,029,560✔
465

466
void Manager::ExtendZeekPathForPlugins() {
3,836✔
467
    // Extend the path outside of the loop to avoid looking through a longer path for each plugin
468
    vector<string> path_additions;
3,836✔
469

470
    for ( const auto& p : Manager::ActivePlugins() ) {
418,124✔
471
        if ( p->DynamicPlugin() || p->Name().empty() )
406,616✔
472
            continue;
×
473

474
        try {
406,616✔
475
            string canon = std::regex_replace(p->Name(), std::regex("::"), "_");
406,616✔
476
            string dir = "builtin-plugins/" + canon;
406,616✔
477

478
            // Use find_file to find the directory in the path.
479
            string script_dir = util::find_file(dir, util::zeek_path());
813,232✔
480
            if ( script_dir.empty() || ! util::is_dir(script_dir) )
406,616✔
481
                continue;
402,780✔
482

483
            DBG_LOG(DBG_PLUGINS, "  Adding %s to ZEEKPATH", script_dir.c_str());
3,836✔
484
            path_additions.push_back(std::move(script_dir));
3,836✔
485
        } catch ( const std::regex_error& e ) {
406,616✔
486
            // This really shouldn't ever happen, but we do need to catch the exception.
487
            // Report a fatal error because something is wrong if this occurs.
488
            reporter->FatalError("Failed to replace colons in plugin name %s: %s", p->Name().c_str(), e.what());
×
489
        }
×
490
    }
3,836✔
491

492
    for ( const auto& plugin_path : path_additions )
15,344✔
493
        util::detail::add_to_zeek_path(plugin_path);
3,836✔
494
}
7,672✔
495

496
void Manager::InitPreScript() {
3,836✔
497
    assert(! init);
3,836✔
498

499
    for ( Plugin* plugin : *Manager::ActivePluginsInternal() ) {
418,124✔
500
        plugin->DoConfigure();
406,616✔
501
    }
502

503
    // Sort plugins by name to make sure we have a deterministic order.
504
    // We cannot do this before, because the plugin name (used for plugin_cmp) is only
505
    // set in DoConfigure.
506
    // We need things sorted to generate the tags (in InitializeComponents) in a deterministic
507
    // order.
508
    ActivePluginsInternal()->sort(plugin_cmp);
3,836✔
509

510
    for ( Plugin* plugin : *Manager::ActivePluginsInternal() ) {
418,124✔
511
        plugin->InitializeComponents();
406,616✔
512
        plugin->InitPreScript();
406,616✔
513
    }
514

515
    init = true;
3,836✔
516
}
3,836✔
517

518
void Manager::InitBifs() {
3,648✔
519
    bif_init_func_map* bifs = BifFilesInternal();
3,648✔
520

521
    for ( Plugin* plugin : *Manager::ActivePluginsInternal() ) {
397,712✔
522
        bif_init_func_map::const_iterator b = bifs->find(util::strtolower(plugin->Name()));
773,536✔
523

524
        if ( b != bifs->end() ) {
773,536✔
525
            for ( const auto& func : *(b->second) )
1,207,748✔
526
                func(plugin);
485,249✔
527
        }
528
    }
529
}
3,648✔
530

531
void Manager::InitPostScript() {
3,648✔
532
    assert(init);
3,648✔
533

534
    for ( Plugin* plugin : *Manager::ActivePluginsInternal() )
397,712✔
535
        plugin->InitPostScript();
386,768✔
536
}
3,648✔
537

538
void Manager::InitPreExecution() {
2,884✔
539
    assert(init);
2,884✔
540

541
    for ( Plugin* plugin : *Manager::ActivePluginsInternal() )
314,411✔
542
        plugin->InitPreExecution();
305,759✔
543
}
2,884✔
544

545
void Manager::FinishPlugins() {
2,881✔
546
    assert(init);
2,881✔
547

548
    for ( Plugin* plugin : *Manager::ActivePluginsInternal() )
314,084✔
549
        plugin->Done();
305,441✔
550

551
    Manager::ActivePluginsInternal()->clear();
2,881✔
552

553
    init = false;
2,881✔
554
}
2,881✔
555

556
Manager::plugin_list Manager::ActivePlugins() const { return *Manager::ActivePluginsInternal(); }
7,496✔
557

558
Manager::inactive_plugin_list Manager::InactivePlugins() const {
113✔
559
    plugin_list* all = ActivePluginsInternal();
113✔
560

561
    inactive_plugin_list inactives;
113✔
562

563
    for ( const auto& [index, plugin] : dynamic_plugins ) {
366✔
564
        bool found = false;
27✔
565

566
        for ( Plugin* plugin : *all ) {
2,952✔
567
            if ( index == util::strtolower(plugin->Name()) ) {
2,896✔
568
                found = true;
569
                break;
570
            }
571
        }
572

573
        if ( ! found )
27✔
574
            inactives.emplace_back(index, plugin);
2✔
575
    }
576

577
    return inactives;
113✔
578
}
×
579

580
Manager::plugin_list* Manager::ActivePluginsInternal() {
595,669✔
581
    static plugin_list* plugins = nullptr;
595,669✔
582

583
    if ( ! plugins )
595,669✔
584
        plugins = new plugin_list;
3,870✔
585

586
    return plugins;
595,669✔
587
}
588

589
Manager::bif_init_func_map* Manager::BifFilesInternal() {
518,428✔
590
    static bif_init_func_map* bifs = nullptr;
518,428✔
591

592
    if ( ! bifs )
518,428✔
593
        bifs = new bif_init_func_map;
3,870✔
594

595
    return bifs;
518,428✔
596
}
597

598
Plugin* Manager::LookupPluginByPath(std::string_view _path) {
696,805✔
599
    auto path = util::detail::normalize_path(_path);
696,805✔
600

601
    if ( util::is_file(path) )
696,805✔
602
        path = util::SafeDirname(path).result;
684,452✔
603

604
    while ( ! path.empty() ) {
3,976,019✔
605
        auto i = plugins_by_path.find(path);
3,279,243✔
606

607
        if ( i != plugins_by_path.end() )
6,558,486✔
608
            return i->second;
×
609

610
        auto j = path.rfind('/');
3,279,243✔
611

612
        if ( j == std::string::npos )
3,279,243✔
613
            break;
614

615
        path.erase(j);
3,279,214✔
616
    }
617

618
    return nullptr;
619
}
696,805✔
620

621
static bool hook_cmp(std::pair<int, Plugin*> a, std::pair<int, Plugin*> b) {
3,840✔
622
    if ( a.first == b.first )
3,840✔
623
        return util::strtolower(a.second->Name()) < util::strtolower(b.second->Name());
3,840✔
624

625
    // Reverse sort.
626
    return a.first > b.first;
×
627
}
628

629
std::list<std::pair<HookType, int>> Manager::HooksEnabledForPlugin(const Plugin* plugin) const {
443✔
630
    std::list<std::pair<HookType, int>> enabled;
443✔
631

632
    for ( int i = 0; i < NUM_HOOKS; i++ ) {
7,088✔
633
        if ( hook_list* l = hooks[i] )
6,645✔
634
            for ( const auto& [hook, hook_plugin] : *l )
2,219✔
635
                if ( hook_plugin == plugin )
887✔
636
                    enabled.emplace_back(static_cast<HookType>(i), hook);
11✔
637
    }
638

639
    return enabled;
443✔
640
}
×
641

642
void Manager::EnableHook(HookType hook, Plugin* plugin, int prio) {
11,558✔
643
    if ( ! hooks[hook] )
11,558✔
644
        hooks[hook] = new hook_list;
7,719✔
645

646
    hook_list* l = hooks[hook];
11,558✔
647

648
    for ( const auto& [_, hook_plugin] : *l ) {
38,514✔
649
        // Already enabled for this plugin.
650
        if ( hook_plugin == plugin )
3,840✔
651
            return;
×
652
    }
653

654
    l->emplace_back(prio, plugin);
11,558✔
655
    l->sort(hook_cmp);
11,558✔
656
}
657

658
void Manager::DisableHook(HookType hook, Plugin* plugin) {
3,639✔
659
    hook_list* l = hooks[hook];
3,639✔
660

661
    if ( ! l )
3,639✔
662
        return;
663

664
    for ( hook_list::iterator i = l->begin(); i != l->end(); i++ ) {
10,923✔
665
        if ( (*i).second == plugin ) {
3,641✔
666
            l->erase(i);
3,639✔
667
            break;
3,639✔
668
        }
669
    }
670

671
    if ( l->empty() ) {
3,639✔
672
        delete l;
3,637✔
673
        hooks[hook] = nullptr;
3,637✔
674
    }
675
}
676

677
void Manager::RequestEvent(EventHandlerPtr handler, Plugin* plugin) {
×
678
    DBG_LOG(DBG_PLUGINS, "Plugin %s requested event %s", plugin->Name().c_str(), handler->Name());
×
679
    handler->SetGenerateAlways();
×
680
}
×
681

682
void Manager::RequestObjDtor(Obj* obj, Plugin* plugin) { obj->NotifyPluginsOnDtor(); }
1✔
683

684
int Manager::HookLoadFile(const Plugin::LoadType type, const string& file, const string& resolved) {
1,723,084✔
685
    HookArgumentList args;
1,723,084✔
686

687
    if ( HavePluginForHook(META_HOOK_PRE) ) {
1,723,084✔
688
        args.emplace_back(type);
1,197✔
689
        args.emplace_back(file);
1,197✔
690
        args.emplace_back(resolved);
1,197✔
691
        MetaHookPre(HOOK_LOAD_FILE, args);
1,197✔
692
    }
693

694
    hook_list* l = hooks[HOOK_LOAD_FILE];
1,723,084✔
695

696
    int rc = -1;
1,723,084✔
697

698
    if ( l )
1,723,084✔
699
        for ( const auto& [_, p] : *l ) {
8,615,722✔
700
            rc = p->HookLoadFile(type, file, resolved);
3,446,571✔
701

702
            if ( rc >= 0 )
3,446,571✔
703
                break;
704
        }
705

706
    if ( HavePluginForHook(META_HOOK_POST) )
1,723,084✔
707
        MetaHookPost(HOOK_LOAD_FILE, args, HookArgument(rc));
3,591✔
708

709
    return rc;
3,446,168✔
710
}
1,723,084✔
711

712
std::pair<int, std::optional<std::string>> Manager::HookLoadFileExtended(const Plugin::LoadType type,
788✔
713
                                                                         const string& file, const string& resolved) {
714
    HookArgumentList args;
788✔
715

716
    if ( HavePluginForHook(META_HOOK_PRE) ) {
788✔
717
        args.emplace_back(type);
412✔
718
        args.emplace_back(file);
412✔
719
        args.emplace_back(resolved);
412✔
720
        MetaHookPre(HOOK_LOAD_FILE_EXT, args);
412✔
721
    }
722

723
    hook_list* l = hooks[HOOK_LOAD_FILE_EXT];
788✔
724

725
    std::pair<int, std::optional<std::string>> rc = {-1, std::nullopt};
788✔
726

727
    if ( l )
788✔
728
        for ( const auto& [_, p] : *l ) {
3,149✔
729
            rc = p->HookLoadFileExtended(type, file, resolved);
1,576✔
730

731
            if ( rc.first >= 0 )
788✔
732
                break;
733
        }
734

735
    if ( HavePluginForHook(META_HOOK_POST) )
788✔
736
        MetaHookPost(HOOK_LOAD_FILE_EXT, args, HookArgument(rc));
1,236✔
737

738
    return rc;
1,576✔
739
}
788✔
740

741
std::pair<bool, ValPtr> Manager::HookCallFunction(const Func* func, zeek::detail::Frame* parent, Args* vecargs) const {
714✔
742
    HookArgumentList args;
714✔
743
    ValPList vargs;
714✔
744

745
    if ( HavePluginForHook(META_HOOK_PRE) ) {
714✔
746
        vargs.resize(vecargs->size());
714✔
747

748
        for ( const auto& v : *vecargs )
3,871✔
749
            vargs.push_back(v.get());
1,729✔
750

751
        args.emplace_back(func);
714✔
752
        args.emplace_back(parent);
714✔
753
        args.emplace_back(&vargs);
714✔
754
        MetaHookPre(HOOK_CALL_FUNCTION, args);
714✔
755
    }
756

757
    hook_list* l = hooks[HOOK_CALL_FUNCTION];
714✔
758

759
    std::pair<bool, ValPtr> rval{false, nullptr};
714✔
760

761
    if ( l ) {
714✔
762
        for ( const auto& [_, p] : *l ) {
2,856✔
763
            rval = p->HookFunctionCall(func, parent, vecargs);
714✔
764

765
            if ( rval.first )
714✔
766
                break;
767
        }
768
    }
769

770
    if ( HavePluginForHook(META_HOOK_POST) )
714✔
771
        MetaHookPost(HOOK_CALL_FUNCTION, args, HookArgument(std::make_pair(rval.first, rval.second.get())));
2,856✔
772

773
    return rval;
1,428✔
774
}
714✔
775

776
bool Manager::HookQueueEvent(Event* event) const {
49✔
777
    HookArgumentList args;
49✔
778

779
    if ( HavePluginForHook(META_HOOK_PRE) ) {
49✔
780
        args.emplace_back(event);
43✔
781
        MetaHookPre(HOOK_QUEUE_EVENT, args);
43✔
782
    }
783

784
    hook_list* l = hooks[HOOK_QUEUE_EVENT];
49✔
785

786
    bool result = false;
49✔
787

788
    if ( l )
49✔
789
        for ( const auto& [_, p] : *l ) {
196✔
790
            if ( p->HookQueueEvent(event) ) {
49✔
791
                result = true;
792
                break;
793
            }
794
        }
795

796
    if ( HavePluginForHook(META_HOOK_POST) )
49✔
797
        MetaHookPost(HOOK_QUEUE_EVENT, args, HookArgument(result));
129✔
798

799
    return result;
98✔
800
}
49✔
801

802
void Manager::HookDrainEvents() const {
654✔
803
    HookArgumentList args;
654✔
804

805
    if ( HavePluginForHook(META_HOOK_PRE) )
654✔
806
        MetaHookPre(HOOK_DRAIN_EVENTS, args);
46✔
807

808
    hook_list* l = hooks[HOOK_DRAIN_EVENTS];
654✔
809

810
    if ( l )
654✔
811
        for ( const auto& [_, p] : *l ) {
2,616✔
812
            p->HookDrainEvents();
654✔
813
        }
814

815
    if ( HavePluginForHook(META_HOOK_POST) )
654✔
816
        MetaHookPost(HOOK_DRAIN_EVENTS, args, HookArgument());
138✔
817
}
654✔
818

819
void Manager::HookSetupAnalyzerTree(Connection* conn) const {
81✔
820
    HookArgumentList args;
81✔
821

822
    if ( HavePluginForHook(META_HOOK_PRE) ) {
81✔
823
        args.emplace_back(conn);
1✔
824
        MetaHookPre(HOOK_SETUP_ANALYZER_TREE, args);
1✔
825
    }
826

827
    hook_list* l = hooks[HOOK_SETUP_ANALYZER_TREE];
81✔
828

829
    if ( l ) {
81✔
830
        for ( const auto& [_, p] : *l ) {
324✔
831
            p->HookSetupAnalyzerTree(conn);
81✔
832
        }
833
    }
834

835
    if ( HavePluginForHook(META_HOOK_POST) ) {
81✔
836
        MetaHookPost(HOOK_SETUP_ANALYZER_TREE, args, HookArgument());
3✔
837
    }
838
}
81✔
839

840
void Manager::HookUpdateNetworkTime(double network_time) const {
14✔
841
    HookArgumentList args;
14✔
842

843
    if ( HavePluginForHook(META_HOOK_PRE) ) {
14✔
844
        args.emplace_back(network_time);
14✔
845
        MetaHookPre(HOOK_UPDATE_NETWORK_TIME, args);
14✔
846
    }
847

848
    hook_list* l = hooks[HOOK_UPDATE_NETWORK_TIME];
14✔
849

850
    if ( l )
14✔
851
        for ( const auto& [_, p] : *l ) {
56✔
852
            p->HookUpdateNetworkTime(network_time);
14✔
853
        }
854

855
    if ( HavePluginForHook(META_HOOK_POST) )
14✔
856
        MetaHookPost(HOOK_UPDATE_NETWORK_TIME, args, HookArgument());
42✔
857
}
14✔
858

859
void Manager::HookObjDtor(void* obj) const {
1✔
860
    HookArgumentList args;
1✔
861

862
    if ( HavePluginForHook(META_HOOK_PRE) ) {
1✔
863
        args.emplace_back(obj);
1✔
864
        MetaHookPre(HOOK_OBJ_DTOR, args);
1✔
865
    }
866

867
    hook_list* l = hooks[HOOK_OBJ_DTOR];
1✔
868

869
    if ( l )
1✔
870
        for ( const auto& [_, p] : *l ) {
4✔
871
            p->HookObjDtor(obj);
1✔
872
        }
873

874
    if ( HavePluginForHook(META_HOOK_POST) )
1✔
875
        MetaHookPost(HOOK_OBJ_DTOR, args, HookArgument());
3✔
876
}
1✔
877

878
void Manager::HookLogInit(const std::string& writer, const std::string& instantiating_filter, bool local, bool remote,
4✔
879
                          const logging::WriterBackend::WriterInfo& info, int num_fields,
880
                          const threading::Field* const* fields) const {
881
    HookArgumentList args;
4✔
882

883
    if ( HavePluginForHook(META_HOOK_PRE) ) {
4✔
884
        args.emplace_back(writer);
3✔
885
        args.emplace_back(instantiating_filter);
3✔
886
        args.emplace_back(local);
3✔
887
        args.emplace_back(remote);
3✔
888
        args.emplace_back(&info);
3✔
889
        args.emplace_back(num_fields);
3✔
890
        args.emplace_back(std::make_pair(num_fields, fields));
6✔
891
        MetaHookPre(HOOK_LOG_INIT, args);
3✔
892
    }
893

894
    hook_list* l = hooks[HOOK_LOG_INIT];
4✔
895

896
    if ( l )
4✔
897
        for ( const auto& [_, p] : *l ) {
16✔
898
            p->HookLogInit(writer, instantiating_filter, local, remote, info, num_fields, fields);
4✔
899
        }
900

901
    if ( HavePluginForHook(META_HOOK_POST) )
4✔
902
        MetaHookPost(HOOK_LOG_INIT, args, HookArgument());
9✔
903
}
4✔
904

905
bool Manager::HookLogWrite(const std::string& writer, const std::string& filter,
6✔
906
                           const logging::WriterBackend::WriterInfo& info, int num_fields,
907
                           const threading::Field* const* fields, threading::Value** vals) const {
908
    HookArgumentList args;
6✔
909

910
    if ( HavePluginForHook(META_HOOK_PRE) ) {
6✔
911
        args.emplace_back(writer);
3✔
912
        args.emplace_back(filter);
3✔
913
        args.emplace_back(&info);
3✔
914
        args.emplace_back(num_fields);
3✔
915
        args.emplace_back(std::make_pair(num_fields, fields));
6✔
916
        args.emplace_back(vals);
3✔
917
        MetaHookPre(HOOK_LOG_WRITE, args);
3✔
918
    }
919

920
    hook_list* l = hooks[HOOK_LOG_WRITE];
6✔
921

922
    bool result = true;
6✔
923

924
    if ( l )
6✔
925
        for ( const auto& [_, p] : *l ) {
23✔
926
            if ( ! p->HookLogWrite(writer, filter, info, num_fields, fields, vals) ) {
6✔
927
                result = false;
928
                break;
929
            }
930
        }
931

932
    if ( HavePluginForHook(META_HOOK_POST) )
6✔
933
        MetaHookPost(HOOK_LOG_WRITE, args, HookArgument(result));
9✔
934

935
    return result;
12✔
936
}
6✔
937

938
bool Manager::HookReporter(const std::string& prefix, const EventHandlerPtr event, const Connection* conn,
5✔
939
                           const ValPList* addl, bool location, const zeek::detail::Location* location1,
940
                           const zeek::detail::Location* location2, bool time, const std::string& message)
941

942
{
943
    HookArgumentList args;
5✔
944

945
    if ( HavePluginForHook(META_HOOK_PRE) ) {
5✔
946
        args.emplace_back(prefix);
×
947
        args.emplace_back(conn);
×
948
        args.emplace_back(addl);
×
949
        args.emplace_back(location1);
×
950
        args.emplace_back(location2);
×
951
        args.emplace_back(location);
×
952
        args.emplace_back(time);
×
953
        args.emplace_back(message);
×
954
        MetaHookPre(HOOK_REPORTER, args);
×
955
    }
956

957
    hook_list* l = hooks[HOOK_REPORTER];
5✔
958

959
    bool result = true;
5✔
960

961
    if ( l ) {
5✔
962
        for ( const auto& [_, p] : *l ) {
19✔
963
            if ( ! p->HookReporter(prefix, event, conn, addl, location, location1, location2, time, message) ) {
5✔
964
                result = false;
965
                break;
966
            }
967
        }
968
    }
969

970
    if ( HavePluginForHook(META_HOOK_POST) )
5✔
971
        MetaHookPost(HOOK_REPORTER, args, HookArgument(result));
×
972

973
    return result;
10✔
974
}
5✔
975

976
void Manager::HookUnprocessedPacket(const Packet* packet) const {
134,749✔
977
    HookArgumentList args;
134,749✔
978

979
    if ( HavePluginForHook(META_HOOK_PRE) ) {
134,749✔
980
        args.emplace_back(packet);
×
981
        MetaHookPre(HOOK_UNPROCESSED_PACKET, args);
×
982
    }
983

984
    hook_list* l = hooks[HOOK_UNPROCESSED_PACKET];
134,749✔
985

986
    if ( l )
134,749✔
987
        for ( const auto& [_, p] : *l ) {
264✔
988
            p->HookUnprocessedPacket(packet);
66✔
989
        }
990

991
    if ( HavePluginForHook(META_HOOK_POST) )
134,749✔
992
        MetaHookPost(HOOK_UNPROCESSED_PACKET, args, HookArgument());
×
993
}
134,749✔
994

995
bool Manager::HookPublishEvent(zeek::cluster::Backend& backend, const std::string& topic,
8✔
996
                               zeek::cluster::Event& event) const {
997
    HookArgumentList args;
8✔
998

999
    if ( HavePluginForHook(META_HOOK_PRE) ) {
8✔
1000
        args.emplace_back(&backend);
2✔
1001
        args.emplace_back(topic);
2✔
1002
        args.emplace_back(&event);
2✔
1003
        MetaHookPre(HOOK_PUBLISH_EVENT, args);
2✔
1004
    }
1005

1006
    hook_list* l = hooks[HOOK_PUBLISH_EVENT];
8✔
1007

1008
    bool result = true;
8✔
1009

1010
    if ( l ) {
8✔
1011
        for ( const auto& [_, p] : *l ) {
31✔
1012
            if ( ! p->HookPublishEvent(backend, topic, event) ) {
8✔
1013
                result = false;
1014
                break;
1015
            }
1016
        }
1017
    }
1018

1019
    if ( HavePluginForHook(META_HOOK_POST) )
8✔
1020
        MetaHookPost(HOOK_PUBLISH_EVENT, args, HookArgument(result));
6✔
1021

1022
    return result;
16✔
1023
}
8✔
1024

1025
void Manager::MetaHookPre(HookType hook, const HookArgumentList& args) const {
2,436✔
1026
    if ( hook_list* l = hooks[META_HOOK_PRE] )
2,436✔
1027
        for ( const auto& [hook_type, plugin] : *l )
9,744✔
1028
            plugin->MetaHookPre(hook, args);
2,436✔
1029
}
2,436✔
1030

1031
void Manager::MetaHookPost(HookType hook, const HookArgumentList& args, const HookArgument& result) const {
2,436✔
1032
    if ( hook_list* l = hooks[META_HOOK_POST] )
2,436✔
1033
        for ( const auto& [hook_type, plugin] : *l )
9,744✔
1034
            plugin->MetaHookPost(hook, args, result);
4,872✔
1035
}
2,436✔
1036

1037
} // namespace zeek::plugin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc