diff --git a/NEWS b/NEWS index 3c77ead2d2..53e5f31eb0 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,15 @@ New Functionality delete v; assert |v| == 0; +- A new helper function ``can_load()`` backed by a new bif ``find_in_zeekpath()`` + was added to determine if a non-relative ``@load`` directive might work. This + can be used to guard ``@load`` directives when script packages may or may not + be installed. + + @if ( can_load("my-package") ) + @load my-package + @endif + Changed Functionality --------------------- diff --git a/scripts/base/init-default.zeek b/scripts/base/init-default.zeek index 3a3efb6853..3c65158182 100644 --- a/scripts/base/init-default.zeek +++ b/scripts/base/init-default.zeek @@ -17,6 +17,7 @@ @load base/utils/geoip-distance @load base/utils/hash_hrw @load base/utils/numbers +@load base/utils/packages @load base/utils/paths @load base/utils/patterns @load base/utils/queue diff --git a/scripts/base/utils/packages.zeek b/scripts/base/utils/packages.zeek new file mode 100644 index 0000000000..24d6197bbd --- /dev/null +++ b/scripts/base/utils/packages.zeek @@ -0,0 +1,16 @@ +##! Rudimentary functions for helping with Zeek packages. + +## Checks whether @load of a given package name could +## be successful. +## +## This tests for the existence of corresponding script files +## in ZEEKPATH. It does not attempt to parse and validate +## any actual Zeek script code. +## +## path: The filename, package or path to test. +## +## Returns: T if the given filename, package or path may load. +function can_load(p: string): bool + { + return find_in_zeekpath(p) != ""; + } diff --git a/src/zeek.bif b/src/zeek.bif index e3b1f5caa6..18ffb101bd 100644 --- a/src/zeek.bif +++ b/src/zeek.bif @@ -5328,3 +5328,35 @@ function table_pattern_matcher_stats%(tbl: any%) : MatcherStats return std::move(result); %} + +## Determine the path used by a non-relative @load directive. +## +## This function is package aware: Passing *package* will yield the +## path to *package.zeek*, *package/__load__.zeek* or an empty string +## if neither can be found. Note that passing a relative path or absolute +## path is an error. +## +## path: The filename, package or path to search for in ZEEKPATH. +## +## Returns: Path of script file that would be loaded by an @load directive. +function find_in_zeekpath%(p: string%): string + %{ + auto path = p->ToStdString(); + if ( ! path.empty() && (path[0] == '.' || path[0] == '/') ) + { + zeek::reporter->Error("find_in_zeek_path: path must be relative or absolute"); + return zeek::val_mgr->EmptyString(); + } + + auto resolved = zeek::util::find_script_file(path, zeek::util::zeek_path()); + if ( ! resolved.empty() && zeek::util::is_dir(resolved.c_str()) ) + { + // If it's a directory, try opening the package using + // the absolute path. This is zeek::util::open_package() + // without the noisy error log. + resolved.append("/__load__.zeek"); + resolved = zeek::util::find_file(resolved, ""); + } + + return zeek::make_intrusive(resolved.c_str()); + %} diff --git a/testing/btest/Baseline/bifs.find_in_zeekpath/.stderr b/testing/btest/Baseline/bifs.find_in_zeekpath/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/bifs.find_in_zeekpath/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/bifs.find_in_zeekpath/errors.stderr b/testing/btest/Baseline/bifs.find_in_zeekpath/errors.stderr new file mode 100644 index 0000000000..6ac968308b --- /dev/null +++ b/testing/btest/Baseline/bifs.find_in_zeekpath/errors.stderr @@ -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: find_in_zeek_path: path must be relative or absolute +error: find_in_zeek_path: path must be relative or absolute diff --git a/testing/btest/Baseline/bifs.find_in_zeekpath/errors.stdout b/testing/btest/Baseline/bifs.find_in_zeekpath/errors.stdout new file mode 100644 index 0000000000..0c8d6fd0fd --- /dev/null +++ b/testing/btest/Baseline/bifs.find_in_zeekpath/errors.stdout @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +relative, +absolute, diff --git a/testing/btest/Baseline/bifs.find_in_zeekpath/out b/testing/btest/Baseline/bifs.find_in_zeekpath/out new file mode 100644 index 0000000000..878b9c6d19 --- /dev/null +++ b/testing/btest/Baseline/bifs.find_in_zeekpath/out @@ -0,0 +1,10 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +find_in_zeekpath base/protocols/conn, base/protocols/conn/__load__.zeek +find_in_zeekpath protocols/conn (empty expected, no __load__.zeek), +find_in_zeekpath protocols/conn/vlan-logging, policy/protocols/conn/vlan-logging.zeek +find_in_zeekpath pkg1, ./pkg1.zeek +find_in_zeekpath pkg1.zeek, ./pkg1.zeek +find_in_zeekpath pkg2, ./pkg2/__load__.zeek +find_in_zeekpath pkg3, +pkg1! +pkg2! diff --git a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log index 77e4e88706..a34d335a26 100644 --- a/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log +++ b/testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log @@ -290,6 +290,7 @@ scripts/base/init-default.zeek scripts/base/utils/files.zeek scripts/base/utils/geoip-distance.zeek scripts/base/utils/numbers.zeek + scripts/base/utils/packages.zeek scripts/base/utils/queue.zeek scripts/base/utils/strings.zeek scripts/base/utils/thresholds.zeek diff --git a/testing/btest/Baseline/scripts.base.utils.packages/output b/testing/btest/Baseline/scripts.base.utils.packages/output new file mode 100644 index 0000000000..459feb4f3f --- /dev/null +++ b/testing/btest/Baseline/scripts.base.utils.packages/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. +no pkg3 +pkg1! +pkg2! diff --git a/testing/btest/bifs/find_in_zeekpath.zeek b/testing/btest/bifs/find_in_zeekpath.zeek new file mode 100644 index 0000000000..bc212cb15c --- /dev/null +++ b/testing/btest/bifs/find_in_zeekpath.zeek @@ -0,0 +1,59 @@ +# @TEST-DOC: Test find_in_zeekpath() and demo conditional @load'ing. +# +# @TEST-EXEC: zeek -b %INPUT >out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff .stderr +# +# @TEST-EXEC: zeek -b errors.zeek >errors.stdout 2>errors.stderr +# @TEST-EXEC: btest-diff errors.stdout +# @TEST-EXEC: btest-diff errors.stderr + + +@if ( find_in_zeekpath("pkg1") != "" ) +@load pkg1 +@endif + +@if ( find_in_zeekpath("pkg2") != "" ) +@load pkg2 +@endif + +@if ( find_in_zeekpath("pkg3") != "" ) +@load pkg3 +@endif + +function path_tail(r: string): string + { + if ( |r| == 0 ) + return r; + local parts = split_string(r, /\//); + return join_string_vec(parts[-4:], "/"); + } + +print "find_in_zeekpath base/protocols/conn", path_tail(find_in_zeekpath("base/protocols/conn")); +print "find_in_zeekpath protocols/conn (empty expected, no __load__.zeek)", find_in_zeekpath("protocols/conn"); +print "find_in_zeekpath protocols/conn/vlan-logging", path_tail(find_in_zeekpath("protocols/conn/vlan-logging")); + +print "find_in_zeekpath pkg1", find_in_zeekpath("pkg1"); +print "find_in_zeekpath pkg1.zeek", find_in_zeekpath("pkg1.zeek"); +print "find_in_zeekpath pkg2", find_in_zeekpath("pkg2"); +print "find_in_zeekpath pkg3", find_in_zeekpath("pkg3"); + +@TEST-START-FILE pkg1.zeek +event zeek_init() + { + print "pkg1!"; + } +@TEST-END-FILE + +@TEST-START-FILE pkg2/__load__.zeek +event zeek_init() + { + print "pkg2!"; + } +@TEST-END-FILE + +@TEST-START-FILE errors.zeek +# Using relative and absolute paths is an error (empty string) +print "relative", find_in_zeekpath("./pkg1.zeek"); +print "absolute", find_in_zeekpath("/pkg1"); +@TEST-END-FILE diff --git a/testing/btest/scripts/base/utils/packages.zeek b/testing/btest/scripts/base/utils/packages.zeek new file mode 100644 index 0000000000..f9636def0a --- /dev/null +++ b/testing/btest/scripts/base/utils/packages.zeek @@ -0,0 +1,32 @@ +# @TEST-EXEC: zeek -b %INPUT >output +# @TEST-EXEC: btest-diff output + +@load base/utils/packages + +@if ( can_load("pkg1") ) +@load pkg1 +@endif + +@if ( can_load("pkg2") ) +@load pkg2 +@endif + +@if ( can_load("pkg3") ) +@load pkg3 +@else +print "no pkg3"; +@endif + +@TEST-START-FILE pkg1.zeek +event zeek_init() + { + print "pkg1!"; + } +@TEST-END-FILE + +@TEST-START-FILE pkg2/__load__.zeek +event zeek_init() + { + print "pkg2!"; + } +@TEST-END-FILE