diff --git a/.travis.yml b/.travis.yml index 7655a564..2b3709f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,9 @@ language: c env: - BUILD_TYPE=Debug - BUILD_TYPE=Release +sudo: required +services: + - docker before_install: - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update @@ -9,12 +12,12 @@ install: - sudo apt-get --force-yes install g++-4.8 - sudo apt-get install rpm linux-headers-$(uname -r) - git clone https://github.com/draios/sysdig.git ../sysdig - - sudo apt-get install -y python-pip libvirt-dev jq + - sudo apt-get install -y python-pip libvirt-dev jq dkms - cd .. - curl -Lo avocado-36.0-tar.gz https://github.com/avocado-framework/avocado/archive/36.0lts.tar.gz - tar -zxvf avocado-36.0-tar.gz - cd avocado-36.0lts - - sudo pip install -r requirements-travis.txt + - sudo -H pip install -r requirements.txt - sudo python setup.py install - cd ../falco before_script: @@ -35,7 +38,10 @@ script: - cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DDRAIOS_DEBUG_FLAGS="-D_DEBUG -DNDEBUG" - make VERBOSE=1 - make package - - cd .. + - cp falco*.deb ../docker/local + - cd ../docker/local + - docker build -t sysdig/falco:test . + - cd ../.. - sudo test/run_regression_tests.sh $TRAVIS_BRANCH notifications: webhooks: diff --git a/CMakeLists.txt b/CMakeLists.txt index 343bfb3b..9c815b1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,8 @@ endif() set(PACKAGE_NAME "falco") set(PROBE_VERSION "${FALCO_VERSION}") -set(PROBE_NAME "sysdig-probe") -set(PROBE_DEVICE_NAME "sysdig") +set(PROBE_NAME "falco-probe") +set(PROBE_DEVICE_NAME "falco") set(CMAKE_INSTALL_PREFIX /usr) set(CMD_MAKE make) @@ -415,12 +415,12 @@ set(CPACK_GENERATOR DEB RPM TGZ) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Sysdig ") set(CPACK_DEBIAN_PACKAGE_SECTION "utils") set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://www.sysdig.org") -set(CPACK_DEBIAN_PACKAGE_DEPENDS "sysdig") -set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${PROJECT_SOURCE_DIR}/scripts/debian/postinst;${PROJECT_SOURCE_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "dkms (>= 2.1.0.0)") +set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_BINARY_DIR}/scripts/debian/postinst;${CMAKE_BINARY_DIR}/scripts/debian/prerm;${PROJECT_SOURCE_DIR}/scripts/debian/postrm") set(CPACK_RPM_PACKAGE_LICENSE "GPLv2") set(CPACK_RPM_PACKAGE_URL "http://www.sysdig.org") -set(CPACK_RPM_PACKAGE_REQUIRES "sysdig") +set(CPACK_RPM_PACKAGE_REQUIRES "dkms, gcc, make, kernel-devel, perl") set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postinstall") set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/preuninstall") set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/rpm/postuninstall") diff --git a/docker/dev/docker-entrypoint.sh b/docker/dev/docker-entrypoint.sh index f0d6e32c..ffa7e671 100755 --- a/docker/dev/docker-entrypoint.sh +++ b/docker/dev/docker-entrypoint.sh @@ -11,7 +11,7 @@ if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i done - /usr/bin/sysdig-probe-loader + /usr/bin/falco-probe-loader fi exec "$@" diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile index acf3ac1e..630196d6 100644 --- a/docker/local/Dockerfile +++ b/docker/local/Dockerfile @@ -25,7 +25,7 @@ RUN echo "deb http://httpredir.debian.org/debian jessie main" > /etc/apt/sources gcc \ gcc-5 \ gcc-4.9 \ - sysdig && rm -rf /var/lib/apt/lists/* + dkms && rm -rf /var/lib/apt/lists/* # Since our base Debian image ships with GCC 5.0 which breaks older kernels, revert the # default to gcc-4.9. Also, since some customers use some very old distributions whose kernel diff --git a/docker/local/docker-entrypoint.sh b/docker/local/docker-entrypoint.sh index f0d6e32c..ffa7e671 100755 --- a/docker/local/docker-entrypoint.sh +++ b/docker/local/docker-entrypoint.sh @@ -11,7 +11,7 @@ if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i done - /usr/bin/sysdig-probe-loader + /usr/bin/falco-probe-loader fi exec "$@" diff --git a/docker/stable/docker-entrypoint.sh b/docker/stable/docker-entrypoint.sh index f0d6e32c..ffa7e671 100755 --- a/docker/stable/docker-entrypoint.sh +++ b/docker/stable/docker-entrypoint.sh @@ -11,7 +11,7 @@ if [[ -z "${SYSDIG_SKIP_LOAD}" ]]; then ln -s $SYSDIG_HOST_ROOT/usr/src/$i /usr/src/$i done - /usr/bin/sysdig-probe-loader + /usr/bin/falco-probe-loader fi exec "$@" diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index b8807b81..823b8d17 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -1,5 +1,12 @@ +configure_file(debian/postinst.in debian/postinst) +configure_file(debian/prerm.in debian/prerm) + file(COPY "${PROJECT_SOURCE_DIR}/scripts/debian/falco" DESTINATION "${PROJECT_BINARY_DIR}/scripts/debian") file(COPY "${PROJECT_SOURCE_DIR}/scripts/rpm/falco" DESTINATION "${PROJECT_BINARY_DIR}/scripts/rpm") + +install(PROGRAMS ${SYSDIG_DIR}/scripts/sysdig-probe-loader + DESTINATION bin + RENAME falco-probe-loader) diff --git a/scripts/debian/postinst b/scripts/debian/postinst deleted file mode 100755 index 0084f213..00000000 --- a/scripts/debian/postinst +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e - -NAME=falco - -if [ -x "/etc/init.d/$NAME" ]; then - update-rc.d $NAME defaults >/dev/null -fi - diff --git a/scripts/debian/postinst.in b/scripts/debian/postinst.in new file mode 100755 index 00000000..04d0d85b --- /dev/null +++ b/scripts/debian/postinst.in @@ -0,0 +1,32 @@ +#!/bin/sh +set -e + +DKMS_PACKAGE_NAME="@PACKAGE_NAME@" +DKMS_VERSION="@PROBE_VERSION@" +NAME="@PACKAGE_NAME@" + +postinst_found=0 + +case "$1" in + configure) + for DKMS_POSTINST in /usr/lib/dkms/common.postinst /usr/share/$DKMS_PACKAGE_NAME/postinst; do + if [ -f $DKMS_POSTINST ]; then + $DKMS_POSTINST $DKMS_PACKAGE_NAME $DKMS_VERSION /usr/share/$DKMS_PACKAGE_NAME "" $2 + postinst_found=1 + break + fi + done + if [ "$postinst_found" -eq 0 ]; then + echo "ERROR: DKMS version is too old and $DKMS_PACKAGE_NAME was not" + echo "built with legacy DKMS support." + echo "You must either rebuild $DKMS_PACKAGE_NAME with legacy postinst" + echo "support or upgrade DKMS to a more current version." + exit 1 + fi + ;; +esac + +if [ -x "/etc/init.d/$NAME" ]; then + update-rc.d $NAME defaults >/dev/null +fi + diff --git a/scripts/debian/prerm b/scripts/debian/prerm deleted file mode 100755 index 5270e189..00000000 --- a/scripts/debian/prerm +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -e - -NAME=falco - -if [ -x "/etc/init.d/$NAME" ]; then - if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then - invoke-rc.d $NAME stop || exit $? - else - /etc/init.d/$NAME stop || exit $? - fi -fi - diff --git a/scripts/debian/prerm.in b/scripts/debian/prerm.in new file mode 100755 index 00000000..fd353e7d --- /dev/null +++ b/scripts/debian/prerm.in @@ -0,0 +1,23 @@ +#!/bin/sh +set -e + +NAME="@PACKAGE_NAME@" + +if [ -x "/etc/init.d/$NAME" ]; then + if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then + invoke-rc.d $NAME stop || exit $? + else + /etc/init.d/$NAME stop || exit $? + fi +fi + +DKMS_PACKAGE_NAME="@PACKAGE_NAME@" +DKMS_VERSION="@PROBE_VERSION@" + +case "$1" in + remove|upgrade|deconfigure) + if [ "$(dkms status -m $DKMS_PACKAGE_NAME -v $DKMS_VERSION)" ]; then + dkms remove -m $DKMS_PACKAGE_NAME -v $DKMS_VERSION --all + fi + ;; +esac diff --git a/scripts/rpm/postinstall b/scripts/rpm/postinstall index 56e0be45..e24af669 100755 --- a/scripts/rpm/postinstall +++ b/scripts/rpm/postinstall @@ -1 +1,15 @@ +dkms add -m falco -v %{version} --rpm_safe_upgrade +if [ `uname -r | grep -c "BOOT"` -eq 0 ] && [ -e /lib/modules/`uname -r`/build/include ]; then + dkms build -m falco -v %{version} + dkms install --force -m falco -v %{version} +elif [ `uname -r | grep -c "BOOT"` -gt 0 ]; then + echo -e "" + echo -e "Module build for the currently running kernel was skipped since you" + echo -e "are running a BOOT variant of the kernel." +else + echo -e "" + echo -e "Module build for the currently running kernel was skipped since the" + echo -e "kernel source for this kernel does not seem to be installed." +fi + /sbin/chkconfig --add falco diff --git a/scripts/rpm/preuninstall b/scripts/rpm/preuninstall index 10d3b98f..ce357b38 100755 --- a/scripts/rpm/preuninstall +++ b/scripts/rpm/preuninstall @@ -2,3 +2,5 @@ if [ $1 = 0 ]; then /sbin/service falco stop > /dev/null 2>&1 /sbin/chkconfig --del falco fi + +dkms remove -m falco -v %{version} --all --rpm_safe_upgrade diff --git a/test/falco_test.py b/test/falco_test.py index a0a51bdc..7aaf3595 100644 --- a/test/falco_test.py +++ b/test/falco_test.py @@ -4,6 +4,9 @@ import os import re import json import sets +import glob +import shutil +import subprocess from avocado import Test from avocado.utils import process @@ -21,9 +24,9 @@ class FalcoTest(Test): self.stderr_contains = self.params.get('stderr_contains', '*', default='') self.exit_status = self.params.get('exit_status', '*', default=0) self.should_detect = self.params.get('detect', '*', default=False) - self.trace_file = self.params.get('trace_file', '*') + self.trace_file = self.params.get('trace_file', '*', default='') - if not os.path.isabs(self.trace_file): + if self.trace_file and not os.path.isabs(self.trace_file): self.trace_file = os.path.join(self.basedir, self.trace_file) self.json_output = self.params.get('json_output', '*', default=False) @@ -43,6 +46,8 @@ class FalcoTest(Test): if not os.path.isabs(self.conf_file): self.conf_file = os.path.join(self.basedir, self.conf_file) + self.run_duration = self.params.get('run_duration', '*', default='') + self.disabled_rules = self.params.get('disabled_rules', '*', default='') if self.disabled_rules == '': @@ -89,15 +94,23 @@ class FalcoTest(Test): if not isinstance(self.detect_level, list): self.detect_level = [self.detect_level] - # Doing this in 2 steps instead of simply using - # module_is_loaded to avoid logging lsmod output to the log. - lsmod_output = process.system_output("lsmod", verbose=False) + self.package = self.params.get('package', '*', default='None') - if linux_modules.parse_lsmod_for_module(lsmod_output, 'sysdig_probe') == {}: - self.log.debug("Loading sysdig kernel module") - process.run('sudo insmod {}/driver/sysdig-probe.ko'.format(self.falcodir)) + if self.package == 'None': + # Doing this in 2 steps instead of simply using + # module_is_loaded to avoid logging lsmod output to the log. + lsmod_output = process.system_output("lsmod", verbose=False) - self.str_variant = self.trace_file + if linux_modules.parse_lsmod_for_module(lsmod_output, 'falco_probe') == {}: + self.log.debug("Loading falco kernel module") + process.run('insmod {}/driver/falco-probe.ko'.format(self.falcodir), sudo=True) + + self.addl_docker_run_args = self.params.get('addl_docker_run_args', '*', default='') + + self.copy_local_driver = self.params.get('copy_local_driver', '*', default=False) + + # Used by possibly_copy_local_driver as well as docker run + self.module_dir = os.path.expanduser("~/.sysdig") self.outputs = self.params.get('outputs', '*', default='') @@ -111,6 +124,10 @@ class FalcoTest(Test): output['file'] = item2[0] output['line'] = item2[1] outputs.append(output) + filedir = os.path.dirname(output['file']) + # Create the parent directory for the trace file if it doesn't exist. + if not os.path.isdir(filedir): + os.makedirs(filedir) self.outputs = outputs self.disable_tags = self.params.get('disable_tags', '*', default='') @@ -123,6 +140,10 @@ class FalcoTest(Test): if self.run_tags == '': self.run_tags=[] + def tearDown(self): + if self.package != 'None': + self.uninstall_package() + def check_rules_warnings(self, res): found_warning = sets.Set() @@ -231,12 +252,103 @@ class FalcoTest(Test): if not attr in obj: self.fail("Falco JSON object {} does not contain property \"{}\"".format(line, attr)) + def install_package(self): + + if self.package.startswith("docker:"): + + image = self.package.split(":", 1)[1] + # Remove an existing falco-test container first. Note we don't check the output--docker rm + # doesn't have an -i equivalent. + res = process.run("docker rm falco-test", ignore_status=True) + rules_dir = os.path.abspath(os.path.join(self.basedir, "./rules")) + conf_dir = os.path.abspath(os.path.join(self.basedir, "../")) + traces_dir = os.path.abspath(os.path.join(self.basedir, "./trace_files")) + self.falco_binary_path = "docker run -i -t --name falco-test --privileged " \ + "-v {}:/host/rules -v {}:/host/conf -v {}:/host/traces " \ + "-v /var/run/docker.sock:/host/var/run/docker.sock " \ + "-v /dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro " \ + "-v /lib/modules:/host/lib/modules:ro -v {}:/root/.sysdig:ro -v " \ + "/usr:/host/usr:ro {} {} falco".format( + rules_dir, conf_dir, traces_dir, + self.module_dir, self.addl_docker_run_args, image) + + elif self.package.endswith(".deb"): + self.falco_binary_path = '/usr/bin/falco'; + + package_glob = "{}/{}".format(self.falcodir, self.package) + + matches = glob.glob(package_glob) + + if len(matches) != 1: + self.fail("Package path {} did not match exactly 1 file. Instead it matched: {}", package_glob, ",".join(matches)) + + package_path = matches[0] + + cmdline = "dpkg -i {}".format(package_path) + self.log.debug("Installing debian package via \"{}\"".format(cmdline)) + res = process.run(cmdline, timeout=120, sudo=True) + + def uninstall_package(self): + + if self.package.startswith("docker:"): + # Remove the falco-test image. Here we *do* check the return value + res = process.run("docker rm falco-test") + + elif self.package.endswith(".deb"): + cmdline = "dpkg -r falco" + self.log.debug("Uninstalling debian package via \"{}\"".format(cmdline)) + res = process.run(cmdline, timeout=120, sudo=True) + + def possibly_copy_driver(self): + # Remove the contents of ~/.sysdig regardless of + # copy_local_driver. + self.log.debug("Checking for module dir {}".format(self.module_dir)) + if os.path.isdir(self.module_dir): + self.log.info("Removing files below directory {}".format(self.module_dir)) + for rmfile in glob.glob(self.module_dir + "/*"): + self.log.debug("Removing file {}".format(rmfile)) + os.remove(rmfile) + + if self.copy_local_driver: + verstr = subprocess.check_output([self.falco_binary_path, "--version"]).rstrip() + self.log.info("verstr {}".format(verstr)) + falco_version = verstr.split(" ")[2] + self.log.info("falco_version {}".format(falco_version)) + arch = subprocess.check_output(["uname", "-m"]).rstrip() + self.log.info("arch {}".format(arch)) + kernel_release = subprocess.check_output(["uname", "-r"]).rstrip() + self.log.info("kernel release {}".format(kernel_release)) + + # sysdig-probe-loader has a more comprehensive set of ways to + # find the config hash. We only look at /boot/config- + md5_output = subprocess.check_output(["md5sum", "/boot/config-{}".format(kernel_release)]).rstrip() + config_hash = md5_output.split(" ")[0] + + probe_filename = "falco-probe-{}-{}-{}-{}.ko".format(falco_version, arch, kernel_release, config_hash) + driver_path = os.path.join(self.falcodir, "driver", "falco-probe.ko") + module_path = os.path.join(self.module_dir, probe_filename) + self.log.debug("Copying {} to {}".format(driver_path, module_path)) + shutil.copyfile(driver_path, module_path) + def test(self): self.log.info("Trace file %s", self.trace_file) - # Run the provided trace file though falco - cmd = '{}/userspace/falco/falco {} {} -c {} -e {} -o json_output={} -v'.format( - self.falcodir, self.rules_args, self.disabled_args, self.conf_file, self.trace_file, self.json_output) + self.falco_binary_path = '{}/userspace/falco/falco'.format(self.falcodir) + + self.possibly_copy_driver() + + if self.package != 'None': + # This sets falco_binary_path as a side-effect. + self.install_package() + + trace_arg = self.trace_file + + if self.trace_file: + trace_arg = "-e {}".format(self.trace_file) + + # Run falco + cmd = '{} {} {} -c {} {} -o json_output={} -v'.format( + self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output) for tag in self.disable_tags: cmd += ' -T {}'.format(tag) @@ -244,6 +356,9 @@ class FalcoTest(Test): for tag in self.run_tags: cmd += ' -t {}'.format(tag) + if self.run_duration: + cmd += ' -M {}'.format(self.run_duration) + self.falco_proc = process.SubProcess(cmd) res = self.falco_proc.run(timeout=180, sig=9) diff --git a/test/falco_tests.yaml.in b/test/falco_tests.yaml.in index 1453dccb..35e89628 100644 --- a/test/falco_tests.yaml.in +++ b/test/falco_tests.yaml.in @@ -1,4 +1,38 @@ trace_files: !mux + + docker_package: + package: docker:sysdig/falco:test + detect: True + detect_level: WARNING + rules_file: /host/rules/rule_names_with_spaces.yaml + trace_file: /host/traces/cat_write.scap + conf_file: /host/conf/falco.yaml + + # This uses a volume mount to overwrite and prevent /usr/sbin/dkms + # from being run. As a result, it will force falco-probe-loader to + # fall back to loading the driver from ~/.sysdig. Setting + # copy_local_driver to True copied the driver to ~/.sysdig, so it + # will be available. In this case, we're running live for 5 seconds + # just to see if falco can load the driver. + + docker_package_local_driver: + package: docker:sysdig/falco:test + addl_docker_run_args: -v /dev/null:/usr/sbin/dkms + copy_local_driver: True + detect: False + detect_level: WARNING + rules_file: /host/rules/tagged_rules.yaml + conf_file: /host/conf/falco.yaml + run_duration: 5 + + debian_package: + package: falco*.deb + detect: True + detect_level: WARNING + rules_file: + - rules/rule_names_with_spaces.yaml + trace_file: trace_files/cat_write.scap + builtin_rules_no_warnings: detect: False trace_file: trace_files/empty.scap diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index 455ab3a1..690271ab 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -53,6 +53,7 @@ static void signal_callback(int signal) static void usage() { printf( + "falco version " FALCO_VERSION "\n" "Usage: falco [options]\n\n" "Options:\n" " -h, --help Print this page\n" @@ -86,6 +87,7 @@ static void usage() " Marathon url is optional and defaults to Mesos address, port 8080.\n" " The API servers can also be specified via the environment variable\n" " FALCO_MESOS_API.\n" + " -M Stop collecting after reached.\n" " -o, --option = Set the value of option to . Overrides values in configuration file.\n" " can be a two-part .\n" " -p , --print=\n" @@ -106,6 +108,7 @@ static void usage() " -t Only run those rules with a tag=. Can be specified multiple times.\n" " Can not be specified with -T/-D.\n" " -v Verbose output.\n" + " --version Print version number.\n" "\n" ); } @@ -133,12 +136,14 @@ std::list cmdline_options; uint64_t do_inspect(falco_engine *engine, falco_outputs *outputs, sinsp* inspector, + uint64_t duration_to_tot_ns, string &stats_filename) { uint64_t num_evts = 0; int32_t res; sinsp_evt* ev; StatsFileWriter writer; + uint64_t duration_start = 0; if (stats_filename != "") { @@ -182,6 +187,17 @@ uint64_t do_inspect(falco_engine *engine, throw sinsp_exception(inspector->getlasterr().c_str()); } + if (duration_start == 0) + { + duration_start = ev->get_ts(); + } else if(duration_to_tot_ns > 0) + { + if(ev->get_ts() - duration_start >= duration_to_tot_ns) + { + break; + } + } + if(!inspector->is_debug_enabled() && ev->get_category() & EC_INTERNAL) { @@ -232,6 +248,7 @@ int falco_init(int argc, char **argv) string* mesos_api = 0; string output_format = ""; bool replace_container_info = false; + int duration_to_tot = 0; // Used for writing trace files int duration_seconds = 0; @@ -255,6 +272,7 @@ int falco_init(int argc, char **argv) {"option", required_argument, 0, 'o'}, {"print", required_argument, 0, 'p' }, {"pidfile", required_argument, 0, 'P' }, + {"version", no_argument, 0, 0 }, {"writefile", required_argument, 0, 'w' }, {0, 0, 0, 0} @@ -272,7 +290,7 @@ int falco_init(int argc, char **argv) // Parse the args // while((op = getopt_long(argc, argv, - "hc:AdD:e:k:K:Ll:m:o:P:p:r:s:T:t:vw:", + "hc:AdD:e:k:K:Ll:m:M:o:P:p:r:s:T:t:vw:", long_options, &long_index)) != -1) { switch(op) @@ -313,6 +331,13 @@ int falco_init(int argc, char **argv) case 'm': mesos_api = new string(optarg); break; + case 'M': + duration_to_tot = atoi(optarg); + if(duration_to_tot <= 0) + { + throw sinsp_exception(string("invalid duration") + optarg); + } + break; case 'o': cmdline_options.push_back(optarg); break; @@ -368,6 +393,13 @@ int falco_init(int argc, char **argv) } + if(string(long_options[long_index].name) == "version") + { + printf("falco version %s\n", FALCO_VERSION); + return EXIT_SUCCESS; + } + + inspector = new sinsp(); engine = new falco_engine(); engine->set_inspector(inspector); @@ -652,6 +684,7 @@ int falco_init(int argc, char **argv) num_evts = do_inspect(engine, outputs, inspector, + uint64_t(duration_to_tot*ONE_SECOND_IN_NS), stats_filename); duration = ((double)clock()) / CLOCKS_PER_SEC - duration;