Compare commits

..

22 Commits

Author SHA1 Message Date
Lorenzo Fontana
d0a1d4d75d update: reference for the logo
Signed-off-by: Lorenzo Fontana <lo@linux.com>
2020-12-09 18:36:22 +01:00
kaizhe
0a901e4f52 add exception macro
Signed-off-by: kaizhe <derek0405@gmail.com>
2020-12-04 06:21:34 -05:00
kaizhe
22732e9edb rule(Container Run as Root User): new rule created
Signed-off-by: kaizhe <derek0405@gmail.com>
2020-12-04 06:21:34 -05:00
Leonardo Grasso
6a352338e3 update(userspace/falco): output worker should not throw exceptions
Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
f8b66d051b fix(userspace/falco) class naming convention
Co-authored-by: Lorenzo Fontana <fontanalorenz@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
c237ddc738 chore(userspace/falco): apply suggestions from review
Co-authored-by: deepskyblue86 <angelopuglisi86@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
9d31164a71 update(userspace/falco): clear output queue if still blocked during the shutdown
Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
f433b449d9 chore(userspace/falco): add_output init check
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
44955004e3 chore(userspace/falco): handle freeing of output objects
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
a9dac551b8 docs(falco.yaml): better explanation on "output_timeout"
Co-authored-by: Leonardo Di Donato <leodidonato@gmail.com>
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
df8e4e0545 new: Falco config for output timeout
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
321da3e5bf chore(userspace/falco): configurable outputs timeout
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
4b34b83739 new(userspace/falco): add "output_timeout" config node
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
5b558cd600 update(userspace/falco): watchdog for outputs
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
3b7401c2e5 new(userspace/falco): Watchdog timer utility
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
aea12f4f3b update(userspace/falco): outputs error handling
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
f2637c8600 update(userspace/falco): add accessor method for output's name
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
0a14d34e16 chore(userspace/falco): correct exception message
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
a1bdf3ed61 update(userspace/falco): add "internal" source to outputs and proto
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
d3c41c2d97 chore(userspace/falco): avoid multiple outputs init
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
90d71a8e92 feat(userspace/falco): non-blocking outputs
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
Leonardo Grasso
8eb7d83ee8 update(userspace/falco): introduce message struct for outputs
Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
2020-12-01 04:18:04 -05:00
31 changed files with 665 additions and 689 deletions

View File

@@ -179,9 +179,6 @@ endif()
#string-view-lite #string-view-lite
include(DownloadStringViewLite) include(DownloadStringViewLite)
# cxxopts
include(DownloadCxxOpts)
if(NOT MINIMAL_BUILD) if(NOT MINIMAL_BUILD)
# gRPC # gRPC
include(gRPC) include(gRPC)

View File

@@ -11,9 +11,8 @@ Content in this document can be used to publically share about Falco.
### Logo ### Logo
There are 3 logos available for use in this directory. Use the primary logo unless required otherwise due to background issues, or printing. You can find the Falco logo in PNG and SVG in different colors in
the [CNCF's artwork repository](https://github.com/cncf/artwork/blob/master/examples/incubating.md#falco-logos).
The Falco logo is Apache 2 licensed and free to use in media and publication for the CNCF Falco project.
### Colors ### Colors

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 708.41 374.92"><defs><style>.cls-1{fill:#00b4c8;}</style></defs><title>Falco horizontal logo_teal2</title><g id="fqqZXT"><path class="cls-1" d="M204.69,154.4Q151.5,208,98,261.25a48.42,48.42,0,0,1-5.27,4.87c-2.55,1.89-5.34,2-7.65-.45s-1.51-5,.41-7.06c4.6-4.94,9.35-9.74,14.13-14.5q52.56-52.31,105.14-104.59c3.35-3.34,18.05,7.52,21.58,11.1"/><path class="cls-1" d="M215.06,171.36c-.15,2.14-1.54,3.55-2.93,4.94l-87.82,87.79c-2.75,2.74-6,5.42-9.46,1.68-3.15-3.39-.5-6.44,2.06-9q43.44-43.44,86.89-86.87c2.21-2.22,4.58-4.23,8-3A4.61,4.61,0,0,1,215.06,171.36Z"/><path class="cls-1" d="M70.93,71c2.42-.09,4.09,1.31,5.64,2.87q41.82,41.79,83.61,83.59c2.6,2.61,5,5.74,1.69,9s-6.41,1-9-1.66Q111,123,69.25,81.2c-2.09-2.1-3.72-4.39-2.45-7.53A4.34,4.34,0,0,1,70.93,71Z"/><path class="cls-1" d="M203.42,268c-5,1-8.9-1.34-12.45-5-6.35-6.61-12.87-13-19.41-19.46-3.85-3.8-4-7.41-.14-11.28,11.14-11.07,22.21-22.21,33.35-33.29,2.45-2.44,5.43-4.49,8.55-1.55,3.48,3.29,1.19,6.41-1.39,9-8.74,8.84-17.44,17.73-26.4,26.35-3.4,3.27-3.93,5.72-.19,9.06,4.22,3.78,8.13,7.91,12,12,2.54,2.68,5.35,4.25,9.18,4.11s8.28-.12,8.16,5.09c-.12,5-4.74,4.8-8.4,5.14A21,21,0,0,1,203.42,268Z"/><path class="cls-1" d="M148.7,178.36c-.75,3.49-2.68,5.6-6.43,4.36a13,13,0,0,1-4.74-3.31q-30.11-30-60.1-60a23.14,23.14,0,0,1-2.56-3c-1.72-2.42-1.88-5,.3-7.11s4.84-1.76,7,.26c3.65,3.42,7.17,7,10.71,10.53q25.65,25.64,51.28,51.3C146.12,173.37,148.49,175.13,148.7,178.36Z"/><path class="cls-1" d="M133.74,192.93a4.9,4.9,0,0,1-2.53,4.29,5.37,5.37,0,0,1-6.63-.95c-3.35-3.1-6.57-6.34-9.8-9.57q-14.34-14.3-28.61-28.63a34.27,34.27,0,0,1-4.17-5,4.57,4.57,0,0,1,.36-6,5,5,0,0,1,6-1.12,11.65,11.65,0,0,1,3.7,2.58q19.44,19.33,38.79,38.76C132.4,188.85,133.77,190.54,133.74,192.93Z"/></g><path class="cls-1" d="M413.15,190.86a25.57,25.57,0,0,0-10.35-6.63,46.78,46.78,0,0,0-16-2.37A83.35,83.35,0,0,0,372,183.12a75.16,75.16,0,0,0-10.58,2.53l2.37,15.48a53.47,53.47,0,0,1,9-2.21A72.44,72.44,0,0,1,385,198a22.61,22.61,0,0,1,8.13,1.26,13,13,0,0,1,5.22,3.56,13.23,13.23,0,0,1,2.76,5.29,24.6,24.6,0,0,1,.79,6.32v3.16a61.65,61.65,0,0,0-7.42-1.34,57.43,57.43,0,0,0-6.64-.4,61.45,61.45,0,0,0-13,1.35,32.26,32.26,0,0,0-11,4.42,22.7,22.7,0,0,0-7.51,8,24.09,24.09,0,0,0-2.76,12A28.39,28.39,0,0,0,356,254.05a21.6,21.6,0,0,0,6.79,8.22,28.56,28.56,0,0,0,10.51,4.58,60.24,60.24,0,0,0,13.58,1.42A137.25,137.25,0,0,0,407,266.93c5.94-.9,10.4-1.66,13.35-2.29V214.56a50.84,50.84,0,0,0-1.66-13.35A24.93,24.93,0,0,0,413.15,190.86Zm-11.3,61.3a71.4,71.4,0,0,1-13.43.94q-7.26,0-11.53-2.6t-4.26-9.4a10,10,0,0,1,1.57-5.77,10.67,10.67,0,0,1,4.19-3.55,20.18,20.18,0,0,1,5.85-1.74,43.43,43.43,0,0,1,6.39-.47,42.23,42.23,0,0,1,6.64.47,37,37,0,0,1,4.58,1Z"/><path class="cls-1" d="M461.38,248.44a9.27,9.27,0,0,1-2-4,26.17,26.17,0,0,1-.55-5.85V143.94l-19.12,3.16v95.1a40.74,40.74,0,0,0,1.35,11,17.57,17.57,0,0,0,4.66,8.06,21.71,21.71,0,0,0,8.92,5,52,52,0,0,0,14.14,1.89l2.69-15.8a29.78,29.78,0,0,1-6.24-1.34A8.76,8.76,0,0,1,461.38,248.44Z"/><path class="cls-1" d="M532.2,251.05a49.24,49.24,0,0,1-9.64.95q-13.11,0-18.64-7.19t-5.53-19.51q0-12.8,5.85-19.83t17.06-7a40.4,40.4,0,0,1,8.92.95,43.38,43.38,0,0,1,7.51,2.37l4.1-15.64a57.88,57.88,0,0,0-22.11-4.26,42.15,42.15,0,0,0-17.06,3.31,37.35,37.35,0,0,0-12.88,9.17,40.64,40.64,0,0,0-8.14,13.82,50.82,50.82,0,0,0-2.84,17.14,56.83,56.83,0,0,0,2.53,17.3A37.22,37.22,0,0,0,489,256.34a34.82,34.82,0,0,0,13,9,47.83,47.83,0,0,0,18.4,3.24,68.05,68.05,0,0,0,13.19-1.27,39.84,39.84,0,0,0,9.56-2.84l-2.69-15.8A45,45,0,0,1,532.2,251.05Z"/><path class="cls-1" d="M625.77,207.37a40.7,40.7,0,0,0-8.14-13.67,35.23,35.23,0,0,0-12.56-8.76,40.93,40.93,0,0,0-16-3.08,40.34,40.34,0,0,0-16,3.08,36.32,36.32,0,0,0-12.56,8.76,39.88,39.88,0,0,0-8.21,13.67,51.31,51.31,0,0,0-2.93,17.77A52,52,0,0,0,552.31,243a40.47,40.47,0,0,0,8.13,13.75,36.57,36.57,0,0,0,12.48,8.85A40.14,40.14,0,0,0,589,268.74a40.69,40.69,0,0,0,16.19-3.15,36.32,36.32,0,0,0,12.56-8.85A39.7,39.7,0,0,0,625.85,243a53.47,53.47,0,0,0,2.84-17.85A51.55,51.55,0,0,0,625.77,207.37Zm-22,37.52q-5.29,7.28-14.77,7.27t-14.77-7.27q-5.29-7.26-5.3-19.75,0-12.31,5.3-19.51T589,198.44q9.48,0,14.77,7.19t5.29,19.51Q609.1,237.62,603.81,244.89Z"/><path class="cls-1" d="M347.24,218h-47.8v50.57H279.65V150h75.89v15.88h-56.1v36.23h47.8Z"/></svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,28 +0,0 @@
#
# Copyright (C) 2020 The Falco Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
include(ExternalProject)
set(CXXOPTS_PREFIX ${CMAKE_BINARY_DIR}/cxxopts-prefix)
set(CXXOPTS_INCLUDE ${CXXOPTS_PREFIX}/include)
message(STATUS "Using bundled cxxopts in ${CXXOPTS_INCLUDE}")
ExternalProject_Add(
cxxopts
PREFIX ${CXXOPTS_PREFIX}
GIT_REPOSITORY "https://github.com/jarro2783/cxxopts.git"
GIT_TAG "master"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CXXOPTS_PREFIX}/src/cxxopts/include/cxxopts.hpp
${CXXOPTS_INCLUDE}/cxxopts.hpp)

View File

@@ -87,6 +87,23 @@ syscall_event_drops:
rate: .03333 rate: .03333
max_burst: 10 max_burst: 10
# Falco continuously monitors outputs performance. When an output channel does not allow
# to deliver an alert within a given deadline, an error is reported indicating
# which output is blocking notifications.
# The timeout error will be reported to the log according to the above log_* settings.
# Note that the notification will not be discarded from the output queue; thus,
# output channels may indefinitely remain blocked.
# An output timeout error indeed indicate a misconfiguration issue or I/O problems
# that cannot be recovered by Falco and should be fixed by the user.
#
# The "output_timeout" value specifies the duration in milliseconds to wait before
# considering the deadline exceed.
#
# With a 2000ms default, the notification consumer can block the Falco output
# for up to 2 seconds without reaching the timeout.
output_timeout: 2000
# A throttling mechanism implemented as a token bucket limits the # A throttling mechanism implemented as a token bucket limits the
# rate of falco notifications. This throttling is controlled by the following configuration # rate of falco notifications. This throttling is controlled by the following configuration
# options: # options:

View File

@@ -3070,6 +3070,22 @@
priority: WARNING priority: WARNING
tags: [process] tags: [process]
- list: run_as_root_image_list
items: []
- macro: user_known_run_as_root_container
condition: (container.image.repository in (run_as_root_image_list))
# The rule is disabled by default and should be enabled when non-root container policy has been applied.
# Note the rule will not work as expected when usernamespace is applied, e.g. userns-remap is enabled.
- rule: Container Run as Root User
desc: Detected container running as root user
condition: spawned_process and container and proc.vpid=1 and user.uid=0 and not user_known_run_as_root_container
enabled: false
output: Container launched with root user privilege (uid=%user.uid container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
priority: INFO
tags: [container, process]
# Application rules have moved to application_rules.yaml. Please look # Application rules have moved to application_rules.yaml. Please look
# there if you want to enable them by adding to # there if you want to enable them by adding to
# falco_rules.local.yaml. # falco_rules.local.yaml.

View File

@@ -15,7 +15,6 @@ configure_file(config_falco.h.in config_falco.h)
set( set(
FALCO_SOURCES FALCO_SOURCES
cli.cpp
configuration.cpp configuration.cpp
logger.cpp logger.cpp
falco_outputs.cpp falco_outputs.cpp
@@ -35,7 +34,6 @@ set(
"${PROJECT_BINARY_DIR}/userspace/falco" "${PROJECT_BINARY_DIR}/userspace/falco"
"${PROJECT_BINARY_DIR}/driver/src" "${PROJECT_BINARY_DIR}/driver/src"
"${STRING_VIEW_LITE_INCLUDE}" "${STRING_VIEW_LITE_INCLUDE}"
"${CXXOPTS_INCLUDE}"
"${YAMLCPP_INCLUDE_DIR}" "${YAMLCPP_INCLUDE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
"${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include" "${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include"
@@ -43,7 +41,6 @@ set(
set( set(
FALCO_DEPENDENCIES FALCO_DEPENDENCIES
cxxopts
string-view-lite string-view-lite
libyaml libyaml
b64 b64
@@ -126,16 +123,16 @@ target_include_directories(
${FALCO_INCLUDE_DIRECTORIES} ${FALCO_INCLUDE_DIRECTORIES}
) )
# if(NOT MINIMAL_BUILD) if(NOT MINIMAL_BUILD)
# add_custom_command( add_custom_command(
# TARGET falco TARGET falco
# COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR} COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/verify_engine_fields.sh ${CMAKE_SOURCE_DIR}
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
# COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields" COMMENT "Comparing engine fields checksum in falco_engine.h to actual fields"
# ) )
# else() else()
# message(STATUS "Skipping engine fields checksum when building the minimal Falco.") message(STATUS "Skipping engine fields checksum when building the minimal Falco.")
# endif() endif()
if(NOT MINIMAL_BUILD) if(NOT MINIMAL_BUILD)
add_custom_command( add_custom_command(

View File

@@ -1,22 +0,0 @@
/*
Copyright (C) 2020 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "cli.h"
namespace falco
{
} // namespace falco

View File

@@ -1,188 +0,0 @@
/*
Copyright (C) 2020 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <cxxopts.hpp>
namespace falco
{
class option_requires_specific_argument_exception : public cxxopts::OptionParseException
{
public:
explicit option_requires_specific_argument_exception(const std::string& option, const std::string& values):
OptionParseException("Option " + cxxopts::LQUOTE + option + cxxopts::RQUOTE + " requires an argument equal to " + values)
{
}
};
class option_cannot_be_specified_exception : public cxxopts::OptionParseException
{
public:
explicit option_cannot_be_specified_exception(const std::string& option1, const std::string& option2):
OptionParseException("Options " + cxxopts::LQUOTE + option1 + cxxopts::RQUOTE + " and " + cxxopts::LQUOTE + option2 + cxxopts::RQUOTE + " can not be specified together")
{
}
};
class cli
{
public:
cli(int argc, const char** argv):
m_argc(argc), m_argv(argv), m_options("falco", "Cloud-Native Runtime Security")
{
}
virtual ~cli()
{
}
void run()
{
// These options give some info about Falco (Falco exits).
m_options.add_options(
"help",
{
{"h,help", "Print help page."},
{"support", "Print support information (version, rules files, etc.)."},
{"version", "Print version info."},
});
// These are options responsible for listing Falco elements (Falco exits).
m_options.add_options(
"list",
{
{"L", "Show name and description of all rules."},
{"l", "Show name and description of a specific rule.", cxxopts::value<std::string>(), "rule name"},
{"list", "Show all fields.", cxxopts::value<std::string>()->implicit_value("all"), "sycall|k8s_audit"},
{"N", "Show field names only."},
});
// m_options.add_options(
// "output",
// {
// {},
// });
// m_options.add_options(
// "input",
// {
// {},
// });
m_options.add_options(
"filtering",
{
{"D", "Disable any rules with names having the given substring. Can be specified multiple times. Can not be specified with -t.", cxxopts::value<std::vector<std::string>>(), "substring"},
{"T", "Disable any rules with a specific tag. Can be specified several times. Can not be specified with -t.", cxxopts::value<std::vector<std::string>>(), "tag"},
{"t", "Only run those rules with a specific tag. Can be specified several times. Can not be specified with -T or -D.", cxxopts::value<std::vector<std::string>>(), "tag"},
});
m_result = m_options.parse(m_argc, m_argv);
process();
}
private:
void process()
{
if(m_result.count("help") && m_result["help"].as<bool>())
{
std::cout << m_options.help() << std::endl;
// todo: print > exit
}
if(m_result.count("support") && m_result["support"].as<bool>())
{
// todo: argv + config rule filenames > cmdline > print > exit
}
if(m_result.count("version") && m_result["version"].as<bool>())
{
// todo: print > exit
}
if(m_result.count("L") && m_result["L"].as<bool>())
{
// todo: engine > print > exit
// engine->describe_rule(NULL)
}
if(m_result.count("l"))
{
// todo: engine > print > exit
// engine->describe_rule(m_result["l"].as<string>());
}
if(m_result.count("list"))
{
auto source = m_result["list"].as<std::string>();
// todo: retrieve implicit value
if(source.empty() || (source != "syscall" && source != "k8s_audit" && source != "all"))
{
throw falco::option_requires_specific_argument_exception(
"list",
cxxopts::LQUOTE + "syscall" + cxxopts::RQUOTE + " or " + cxxopts::LQUOTE + "k8s_audit" + cxxopts::RQUOTE);
}
bool names_only = false;
if(m_result.count("N"))
{
names_only = m_result["N"].as<bool>();
}
// todo: engine + names_only + source
// se valore == syscall ==> + [-V]
}
bool count_D = m_result.count("D");
bool count_t = m_result.count("t");
bool count_T = m_result.count("T");
if(count_D > 0)
{
if(count_t > 0)
{
throw falco::option_cannot_be_specified_exception("D", "t");
}
// todo
// engine > not exit
}
if(count_T > 0)
{
if(count_t > 0)
{
throw falco::option_cannot_be_specified_exception("T", "t");
}
// todo
// engine > not exit
}
if(count_t > 0)
{
// todo
// engine > not exit
}
}
int m_argc;
const char** m_argv;
cxxopts::Options m_options;
cxxopts::ParseResult m_result;
};
} // namespace falco
// 3 tipi di azioni
// quelle che una volta date devono farlo uscire e non hanno bisogno di nessuna istanza
// quelle che hanno bisogno di inspector e/o engine e poi falco esce
// quelle che hanno bisogno di inspector e/o engine e poi falco esegue

View File

@@ -176,6 +176,8 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
falco_logger::set_level(m_log_level); falco_logger::set_level(m_log_level);
m_output_timeout = m_config->get_scalar<uint32_t>("output_timeout", 2000);
m_notifications_rate = m_config->get_scalar<uint32_t>("outputs", "rate", 1); m_notifications_rate = m_config->get_scalar<uint32_t>("outputs", "rate", 1);
m_notifications_max_burst = m_config->get_scalar<uint32_t>("outputs", "max_burst", 1000); m_notifications_max_burst = m_config->get_scalar<uint32_t>("outputs", "max_burst", 1000);

View File

@@ -204,6 +204,7 @@ public:
bool m_buffered_outputs; bool m_buffered_outputs;
bool m_time_format_iso_8601; bool m_time_format_iso_8601;
uint32_t m_output_timeout;
bool m_grpc_enabled; bool m_grpc_enabled;
uint32_t m_grpc_threadiness; uint32_t m_grpc_threadiness;

View File

@@ -43,7 +43,6 @@ limitations under the License.
#include "falco_engine.h" #include "falco_engine.h"
#include "config_falco.h" #include "config_falco.h"
#include "statsfilewriter.h" #include "statsfilewriter.h"
#include "cli.h"
#ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
#include "webserver.h" #include "webserver.h"
#include "grpc_server.h" #include "grpc_server.h"
@@ -75,102 +74,106 @@ static void restart_falco(int signal)
g_restart = true; g_restart = true;
} }
// // //
// // Program help // Program help
// // //
// static void usage() static void usage()
// { {
// printf( printf(
// "Falco version: " FALCO_VERSION "\n" "Falco version: " FALCO_VERSION "\n"
// "Usage: falco [options]\n\n" "Usage: falco [options]\n\n"
// "Options:\n" "Options:\n"
" -h, --help Print this page\n"
// " -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n" " -c Configuration file (default " FALCO_SOURCE_CONF_FILE ", " FALCO_INSTALL_CONF_FILE ")\n"
// " -A Monitor all events, including those with EF_DROP_SIMPLE_CONS flag.\n" " -A Monitor all events, including those with EF_DROP_SIMPLE_CONS flag.\n"
// " --alternate-lua-dir <path> Specify an alternate path for loading Falco lua files\n" " --alternate-lua-dir <path> Specify an alternate path for loading Falco lua files\n"
// " -b, --print-base64 Print data buffers in base64.\n" " -b, --print-base64 Print data buffers in base64.\n"
// " This is useful for encoding binary data that needs to be used over media designed to.\n" " This is useful for encoding binary data that needs to be used over media designed to.\n"
// " --cri <path> Path to CRI socket for container metadata.\n" " --cri <path> Path to CRI socket for container metadata.\n"
// " Use the specified socket to fetch data from a CRI-compatible runtime.\n" " Use the specified socket to fetch data from a CRI-compatible runtime.\n"
// " -d, --daemon Run as a daemon.\n" " -d, --daemon Run as a daemon.\n"
// " --disable-cri-async Disable asynchronous CRI metadata fetching.\n" " --disable-cri-async Disable asynchronous CRI metadata fetching.\n"
// " This is useful to let the input event wait for the container metadata fetch\n" " This is useful to let the input event wait for the container metadata fetch\n"
// " to finish before moving forward. Async fetching, in some environments leads\n" " to finish before moving forward. Async fetching, in some environments leads\n"
// " to empty fields for container metadata when the fetch is not fast enough to be\n" " to empty fields for container metadata when the fetch is not fast enough to be\n"
// " completed asynchronously. This can have a performance penalty on your environment\n" " completed asynchronously. This can have a performance penalty on your environment\n"
// " depending on the number of containers and the frequency at which they are created/started/stopped\n" " depending on the number of containers and the frequency at which they are created/started/stopped\n"
// " --disable-source <event_source>\n" " --disable-source <event_source>\n"
// " Disable a specific event source.\n" " Disable a specific event source.\n"
// " Available event sources are: syscall, k8s_audit.\n" " Available event sources are: syscall, k8s_audit.\n"
// " It can be passed multiple times.\n" " It can be passed multiple times.\n"
// " Can not disable both the event sources.\n" " Can not disable both the event sources.\n"
" -D <substring> Disable any rules with names having the substring <substring>. Can be specified multiple times.\n"
// " -e <events_file> Read the events from <events_file> (in .scap format for sinsp events, or jsonl for\n" " Can not be specified with -t.\n"
// " k8s audit events) instead of tapping into live.\n" " -e <events_file> Read the events from <events_file> (in .scap format for sinsp events, or jsonl for\n"
// #ifndef MINIMAL_BUILD " k8s audit events) instead of tapping into live.\n"
// " -k <url>, --k8s-api <url>\n" #ifndef MINIMAL_BUILD
// " Enable Kubernetes support by connecting to the API server specified as argument.\n" " -k <url>, --k8s-api <url>\n"
// " E.g. \"http://admin:password@127.0.0.1:8080\".\n" " Enable Kubernetes support by connecting to the API server specified as argument.\n"
// " The API server can also be specified via the environment variable FALCO_K8S_API.\n" " E.g. \"http://admin:password@127.0.0.1:8080\".\n"
// " -K <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>], --k8s-api-cert <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>]\n" " The API server can also be specified via the environment variable FALCO_K8S_API.\n"
// " Use the provided files names to authenticate user and (optionally) verify the K8S API server identity.\n" " -K <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>], --k8s-api-cert <bt_file> | <cert_file>:<key_file[#password]>[:<ca_cert_file>]\n"
// " Each entry must specify full (absolute, or relative to the current directory) path to the respective file.\n" " Use the provided files names to authenticate user and (optionally) verify the K8S API server identity.\n"
// " Private key password is optional (needed only if key is password protected).\n" " Each entry must specify full (absolute, or relative to the current directory) path to the respective file.\n"
// " CA certificate is optional. For all files, only PEM file format is supported. \n" " Private key password is optional (needed only if key is password protected).\n"
// " Specifying CA certificate only is obsoleted - when single entry is provided \n" " CA certificate is optional. For all files, only PEM file format is supported. \n"
// " for this option, it will be interpreted as the name of a file containing bearer token.\n" " Specifying CA certificate only is obsoleted - when single entry is provided \n"
// " Note that the format of this command-line option prohibits use of files whose names contain\n" " for this option, it will be interpreted as the name of a file containing bearer token.\n"
// " ':' or '#' characters in the file name.\n" " Note that the format of this command-line option prohibits use of files whose names contain\n"
// #endif " ':' or '#' characters in the file name.\n"
#endif
" -L Show the name and description of all rules and exit.\n"
" -l <rule> Show the name and description of the rule with name <rule> and exit.\n"
" --list [<source>] List all defined fields. If <source> is provided, only list those fields for\n"
// #ifndef MINIMAL_BUILD " the source <source>. Current values for <source> are \"syscall\", \"k8s_audit\"\n"
// " -m <url[,marathon_url]>, --mesos-api <url[,marathon_url]>\n" #ifndef MINIMAL_BUILD
// " Enable Mesos support by connecting to the API server\n" " -m <url[,marathon_url]>, --mesos-api <url[,marathon_url]>\n"
// " specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n" " Enable Mesos support by connecting to the API server\n"
// " Marathon url is optional and defaults to Mesos address, port 8080.\n" " specified as argument. E.g. \"http://admin:password@127.0.0.1:5050\".\n"
// " The API servers can also be specified via the environment variable FALCO_MESOS_API.\n" " Marathon url is optional and defaults to Mesos address, port 8080.\n"
// #endif " The API servers can also be specified via the environment variable FALCO_MESOS_API.\n"
// " -M <num_seconds> Stop collecting after <num_seconds> reached.\n" #endif
" -M <num_seconds> Stop collecting after <num_seconds> reached.\n"
// " -o, --option <key>=<val> Set the value of option <key> to <val>. Overrides values in configuration file.\n" " -N When used with --list, only print field names.\n"
// " <key> can be a two-part <key>.<subkey>\n" " -o, --option <key>=<val> Set the value of option <key> to <val>. Overrides values in configuration file.\n"
// " -p <output_format>, --print <output_format>\n" " <key> can be a two-part <key>.<subkey>\n"
// " Add additional information to each falco notification's output.\n" " -p <output_format>, --print <output_format>\n"
// " With -pc or -pcontainer will use a container-friendly format.\n" " Add additional information to each falco notification's output.\n"
// " With -pk or -pkubernetes will use a kubernetes-friendly format.\n" " With -pc or -pcontainer will use a container-friendly format.\n"
// " With -pm or -pmesos will use a mesos-friendly format.\n" " With -pk or -pkubernetes will use a kubernetes-friendly format.\n"
// " Additionally, specifying -pc/-pk/-pm will change the interpretation\n" " With -pm or -pmesos will use a mesos-friendly format.\n"
// " of %%container.info in rule output fields.\n" " Additionally, specifying -pc/-pk/-pm will change the interpretation\n"
// " -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n" " of %%container.info in rule output fields.\n"
// " -r <rules_file> Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n" " -P, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
// " Can be specified multiple times to read from multiple files/directories.\n" " -r <rules_file> Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
// " -s <stats_file> If specified, append statistics related to Falco's reading/processing of events\n" " Can be specified multiple times to read from multiple files/directories.\n"
// " to this file (only useful in live mode).\n" " -s <stats_file> If specified, append statistics related to Falco's reading/processing of events\n"
// " --stats-interval <msec> When using -s <stats_file>, write statistics every <msec> ms.\n" " to this file (only useful in live mode).\n"
// " This uses signals, so don't recommend intervals below 200 ms.\n" " --stats-interval <msec> When using -s <stats_file>, write statistics every <msec> ms.\n"
// " Defaults to 5000 (5 seconds).\n" " This uses signals, so don't recommend intervals below 200 ms.\n"
// " -S <len>, --snaplen <len>\n" " Defaults to 5000 (5 seconds).\n"
// " Capture the first <len> bytes of each I/O buffer.\n" " -S <len>, --snaplen <len>\n"
// " By default, the first 80 bytes are captured. Use this\n" " Capture the first <len> bytes of each I/O buffer.\n"
// " option with caution, it can generate huge trace files.\n" " By default, the first 80 bytes are captured. Use this\n"
" option with caution, it can generate huge trace files.\n"
" --support Print support information including version, rules files used, etc. and exit.\n"
// " -U,--unbuffered Turn off output buffering to configured outputs.\n" " -T <tag> Disable any rules with a tag=<tag>. Can be specified multiple times.\n"
// " This causes every single line emitted by falco to be flushed,\n" " Can not be specified with -t.\n"
// " which generates higher CPU usage but is useful when piping those outputs\n" " -t <tag> Only run those rules with a tag=<tag>. Can be specified multiple times.\n"
// " into another process or into a script.\n" " Can not be specified with -T/-D.\n"
// " -u, --userspace Parse events from userspace.\n" " -U,--unbuffered Turn off output buffering to configured outputs.\n"
// " To be used in conjunction with the ptrace(2) based driver (pdig).\n" " This causes every single line emitted by falco to be flushed,\n"
// " -V, --validate <rules_file> Read the contents of the specified rules(s) file and exit.\n" " which generates higher CPU usage but is useful when piping those outputs\n"
// " Can be specified multiple times to validate multiple files.\n" " into another process or into a script.\n"
// " -v Verbose output.\n" " -u, --userspace Parse events from userspace.\n"
" To be used in conjunction with the ptrace(2) based driver (pdig).\n"
// "\n" " -V, --validate <rules_file> Read the contents of the specified rules(s) file and exit.\n"
// ); " Can be specified multiple times to validate multiple files.\n"
// } " -v Verbose output.\n"
" --version Print version number.\n"
"\n"
);
}
static void display_fatal_err(const string &msg) static void display_fatal_err(const string &msg)
{ {
@@ -392,7 +395,8 @@ static void print_all_ignored_events(sinsp *inspector)
static void list_source_fields(falco_engine *engine, bool verbose, bool names_only, std::string &source) static void list_source_fields(falco_engine *engine, bool verbose, bool names_only, std::string &source)
{ {
if(!source.empty() && !(source == "syscall" || source == "k8s_audit")) if(source.size() > 0 &&
!(source == "syscall" || source == "k8s_audit"))
{ {
throw std::invalid_argument("Value for --list must be \"syscall\" or \"k8s_audit\""); throw std::invalid_argument("Value for --list must be \"syscall\" or \"k8s_audit\"");
} }
@@ -409,7 +413,7 @@ static void list_source_fields(falco_engine *engine, bool verbose, bool names_on
// //
// ARGUMENT PARSING AND PROGRAM SETUP // ARGUMENT PARSING AND PROGRAM SETUP
// //
int falco_init() int falco_init(int argc, char **argv)
{ {
int result = EXIT_SUCCESS; int result = EXIT_SUCCESS;
sinsp* inspector = NULL; sinsp* inspector = NULL;
@@ -510,197 +514,198 @@ int falco_init()
set<string> disabled_rule_tags; set<string> disabled_rule_tags;
set<string> enabled_rule_tags; set<string> enabled_rule_tags;
// // //
// // Parse the args // Parse the args
// // //
// while((op = getopt_long(argc, argv, while((op = getopt_long(argc, argv,
// "hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:", "hc:AbdD:e:F:ik:K:Ll:m:M:No:P:p:r:S:s:T:t:UuvV:w:",
// long_options, &long_index)) != -1) long_options, &long_index)) != -1)
// { {
// switch(op) switch(op)
// { {
// case 'h': case 'h':
// usage(); usage();
// goto exit; goto exit;
// case 'c': case 'c':
// conf_filename = optarg; conf_filename = optarg;
// break; break;
// case 'A': case 'A':
// all_events = true; all_events = true;
// break; break;
// case 'b': case 'b':
// event_buffer_format = sinsp_evt::PF_BASE64; event_buffer_format = sinsp_evt::PF_BASE64;
// break; break;
// case 'd': case 'd':
// daemon = true; daemon = true;
// break; break;
// case 'D': case 'D':
// substring = optarg; substring = optarg;
// disabled_rule_substrings.insert(substring); disabled_rule_substrings.insert(substring);
// break; break;
// case 'e': case 'e':
// trace_filename = optarg; trace_filename = optarg;
// #ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
// k8s_api = new string(); k8s_api = new string();
// mesos_api = new string(); mesos_api = new string();
// #endif #endif
// break; break;
// case 'F': case 'F':
// list_flds = optarg; list_flds = optarg;
// break; break;
// case 'i': case 'i':
// print_ignored_events = true; print_ignored_events = true;
// break; break;
// #ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
// case 'k': case 'k':
// k8s_api = new string(optarg); k8s_api = new string(optarg);
// break; break;
// case 'K': case 'K':
// k8s_api_cert = new string(optarg); k8s_api_cert = new string(optarg);
// break; break;
// #endif #endif
// case 'L': case 'L':
// describe_all_rules = true; describe_all_rules = true;
// break; break;
// case 'l': case 'l':
// describe_rule = optarg; describe_rule = optarg;
// break; break;
// #ifndef MINIMAL_BUILD #ifndef MINIMAL_BUILD
// case 'm': case 'm':
// mesos_api = new string(optarg); mesos_api = new string(optarg);
// break; break;
// #endif #endif
// case 'M': case 'M':
// duration_to_tot = atoi(optarg); duration_to_tot = atoi(optarg);
// if(duration_to_tot <= 0) if(duration_to_tot <= 0)
// { {
// throw sinsp_exception(string("invalid duration") + optarg); throw sinsp_exception(string("invalid duration") + optarg);
// } }
// break; break;
// case 'N': case 'N':
// names_only = true; names_only = true;
// break; break;
// case 'o': case 'o':
// cmdline_options.push_back(optarg); cmdline_options.push_back(optarg);
// break; break;
// case 'P': case 'P':
// pidfilename = optarg; pidfilename = optarg;
// break; break;
// case 'p': case 'p':
// if(string(optarg) == "c" || string(optarg) == "container") if(string(optarg) == "c" || string(optarg) == "container")
// { {
// output_format = "container=%container.name (id=%container.id)"; output_format = "container=%container.name (id=%container.id)";
// replace_container_info = true; replace_container_info = true;
// } }
// else if(string(optarg) == "k" || string(optarg) == "kubernetes") else if(string(optarg) == "k" || string(optarg) == "kubernetes")
// { {
// output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id"; output_format = "k8s.ns=%k8s.ns.name k8s.pod=%k8s.pod.name container=%container.id";
// replace_container_info = true; replace_container_info = true;
// } }
// else if(string(optarg) == "m" || string(optarg) == "mesos") else if(string(optarg) == "m" || string(optarg) == "mesos")
// { {
// output_format = "task=%mesos.task.name container=%container.id"; output_format = "task=%mesos.task.name container=%container.id";
// replace_container_info = true; replace_container_info = true;
// } }
// else else
// { {
// output_format = optarg; output_format = optarg;
// replace_container_info = false; replace_container_info = false;
// } }
// break; break;
// case 'r': case 'r':
// falco_configuration::read_rules_file_directory(string(optarg), rules_filenames); falco_configuration::read_rules_file_directory(string(optarg), rules_filenames);
// break; break;
// case 'S': case 'S':
// snaplen = atoi(optarg); snaplen = atoi(optarg);
// break; break;
// case 's': case 's':
// stats_filename = optarg; stats_filename = optarg;
// break; break;
// case 'T': case 'T':
// disabled_rule_tags.insert(optarg); disabled_rule_tags.insert(optarg);
// break; break;
// case 't': case 't':
// enabled_rule_tags.insert(optarg); enabled_rule_tags.insert(optarg);
// break; break;
// case 'U': case 'U':
// buffered_outputs = false; buffered_outputs = false;
// buffered_cmdline = true; buffered_cmdline = true;
// break; break;
// case 'u': case 'u':
// userspace = true; userspace = true;
// break; break;
// case 'v': case 'v':
// verbose = true; verbose = true;
// break; break;
// case 'V': case 'V':
// validate_rules_filenames.push_back(optarg); validate_rules_filenames.push_back(optarg);
// break; break;
// case 'w': case 'w':
// outfile = optarg; outfile = optarg;
// break; break;
// case '?': case '?':
// result = EXIT_FAILURE; result = EXIT_FAILURE;
// goto exit; goto exit;
// case 0: case 0:
// if(string(long_options[long_index].name) == "version") if(string(long_options[long_index].name) == "version")
// { {
// printf("Falco version: %s\n", FALCO_VERSION); printf("Falco version: %s\n", FALCO_VERSION);
// printf("Driver version: %s\n", DRIVER_VERSION); printf("Driver version: %s\n", DRIVER_VERSION);
// return EXIT_SUCCESS; return EXIT_SUCCESS;
// } }
// else if (string(long_options[long_index].name) == "cri") else if (string(long_options[long_index].name) == "cri")
// { {
// if(optarg != NULL) if(optarg != NULL)
// { {
// cri_socket_path = optarg; cri_socket_path = optarg;
// } }
// } }
// else if (string(long_options[long_index].name) == "disable-cri-async") else if (string(long_options[long_index].name) == "disable-cri-async")
// { {
// cri_async = false; cri_async = false;
// } }
// else if (string(long_options[long_index].name) == "list") else if (string(long_options[long_index].name) == "list")
// { {
// list_flds = true; list_flds = true;
// if(optarg != NULL) if(optarg != NULL)
// { {
// list_flds_source = optarg; list_flds_source = optarg;
// } }
// } }
// else if (string(long_options[long_index].name) == "stats-interval") else if (string(long_options[long_index].name) == "stats-interval")
// { {
// stats_interval = atoi(optarg); stats_interval = atoi(optarg);
// } }
// else if (string(long_options[long_index].name) == "support") else if (string(long_options[long_index].name) == "support")
// { {
// print_support = true; print_support = true;
// } }
// else if (string(long_options[long_index].name) == "disable-source") else if (string(long_options[long_index].name) == "disable-source")
// { {
// if(optarg != NULL) if(optarg != NULL)
// { {
// disable_sources.insert(optarg); disable_sources.insert(optarg);
// } }
// } }
// else if (string(long_options[long_index].name)== "alternate-lua-dir") else if (string(long_options[long_index].name)== "alternate-lua-dir")
// { {
// if(optarg != NULL) if(optarg != NULL)
// { {
// alternate_lua_dir = optarg; alternate_lua_dir = optarg;
// if (alternate_lua_dir.back() != '/') { if (alternate_lua_dir.back() != '/') {
// alternate_lua_dir += '/'; alternate_lua_dir += '/';
// } }
// } }
// } }
// break; break;
// default: default:
// break; break;
// } }
}
// }
inspector = new sinsp(); inspector = new sinsp();
inspector->set_buffer_format(event_buffer_format); inspector->set_buffer_format(event_buffer_format);
@@ -916,14 +921,14 @@ int falco_init()
throw std::runtime_error(string("Could not uname() to find system info: %s\n") + strerror(errno)); throw std::runtime_error(string("Could not uname() to find system info: %s\n") + strerror(errno));
} }
// for(char **arg = argv; *arg; arg++) for(char **arg = argv; *arg; arg++)
// { {
// if(cmdline.size() > 0) if(cmdline.size() > 0)
// { {
// cmdline += " "; cmdline += " ";
// } }
// cmdline += *arg; cmdline += *arg;
// } }
support["version"] = FALCO_VERSION; support["version"] = FALCO_VERSION;
support["system_info"]["sysname"] = sysinfo.sysname; support["system_info"]["sysname"] = sysinfo.sysname;
@@ -967,6 +972,7 @@ int falco_init()
outputs->init(config.m_json_output, outputs->init(config.m_json_output,
config.m_json_include_output_property, config.m_json_include_output_property,
config.m_output_timeout,
config.m_notifications_rate, config.m_notifications_max_burst, config.m_notifications_rate, config.m_notifications_max_burst,
config.m_buffered_outputs, config.m_buffered_outputs,
config.m_time_format_iso_8601, config.m_time_format_iso_8601,
@@ -1373,23 +1379,13 @@ exit:
// //
// MAIN // MAIN
// //
int main(int argc, const char **argv) int main(int argc, char **argv)
{ {
try
{
auto cli = new falco::cli(argc, argv);
cli->run();
}
catch(const cxxopts::OptionException &e)
{
display_fatal_err("Error parsing options: " + string(e.what()) + "\n");
return EXIT_FAILURE;
}
int rc; int rc;
// g_restart will cause the falco loop to exit, but we // g_restart will cause the falco loop to exit, but we
// should reload everything and start over. // should reload everything and start over.
while((rc = falco_init()) == EXIT_SUCCESS && g_restart) while((rc = falco_init(argc, argv)) == EXIT_SUCCESS && g_restart)
{ {
g_restart = false; g_restart = false;
optind = 1; optind = 1;

View File

@@ -24,6 +24,7 @@ limitations under the License.
#include "formats.h" #include "formats.h"
#include "logger.h" #include "logger.h"
#include "watchdog.h"
#include "outputs_file.h" #include "outputs_file.h"
#include "outputs_program.h" #include "outputs_program.h"
@@ -51,18 +52,26 @@ falco_outputs::~falco_outputs()
{ {
if(m_initialized) if(m_initialized)
{ {
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it) this->stop_worker();
for(auto o : m_outputs)
{ {
(*it)->cleanup(); delete o;
} }
} }
} }
void falco_outputs::init(bool json_output, void falco_outputs::init(bool json_output,
bool json_include_output_property, bool json_include_output_property,
uint32_t timeout,
uint32_t rate, uint32_t max_burst, bool buffered, uint32_t rate, uint32_t max_burst, bool buffered,
bool time_format_iso_8601, string hostname) bool time_format_iso_8601, std::string hostname)
{ {
// Cannot be initialized more than one time.
if(m_initialized)
{
throw falco_exception("falco_outputs already initialized");
}
m_json_output = json_output; m_json_output = json_output;
// Note that falco_formats is already initialized by the engine, // Note that falco_formats is already initialized by the engine,
@@ -71,17 +80,29 @@ void falco_outputs::init(bool json_output,
falco_formats::s_json_output = json_output; falco_formats::s_json_output = json_output;
falco_formats::s_json_include_output_property = json_include_output_property; falco_formats::s_json_include_output_property = json_include_output_property;
m_timeout = std::chrono::milliseconds(timeout);
m_notifications_tb.init(rate, max_burst); m_notifications_tb.init(rate, max_burst);
m_buffered = buffered; m_buffered = buffered;
m_time_format_iso_8601 = time_format_iso_8601; m_time_format_iso_8601 = time_format_iso_8601;
m_hostname = hostname; m_hostname = hostname;
m_worker_thread = std::thread(&falco_outputs::worker, this);
m_initialized = true; m_initialized = true;
} }
// This function has to be called after init() since some configuration settings
// need to be passed to the output plugins. Then, although the worker has started,
// the worker is still on hold, waiting for a message.
// Thus it is still safe to call add_output() before any message has been enqueued.
void falco_outputs::add_output(falco::outputs::config oc) void falco_outputs::add_output(falco::outputs::config oc)
{ {
if(!m_initialized)
{
throw falco_exception("cannot add output: falco_outputs not initialized yet");
}
falco::outputs::abstract_output *oo; falco::outputs::abstract_output *oo;
@@ -129,6 +150,12 @@ void falco_outputs::handle_event(gen_event *evt, string &rule, string &source,
return; return;
} }
falco_outputs::ctrl_msg cmsg = {};
cmsg.ts = evt->get_ts();
cmsg.priority = priority;
cmsg.source = source;
cmsg.rule = rule;
string sformat; string sformat;
if(source == "syscall") if(source == "syscall")
{ {
@@ -163,35 +190,38 @@ void falco_outputs::handle_event(gen_event *evt, string &rule, string &source,
sformat += " " + format; sformat += " " + format;
} }
string msg; cmsg.msg = falco_formats::format_event(evt, rule, source, falco_common::priority_names[priority], sformat);
msg = falco_formats::format_event(evt, rule, source, falco_common::priority_names[priority], sformat); cmsg.fields = falco_formats::resolve_tokens(evt, source, sformat);
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it) cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT;
{ m_queue.push(cmsg);
(*it)->output_event(evt, rule, source, priority, sformat, msg);
}
} }
void falco_outputs::handle_msg(uint64_t now, void falco_outputs::handle_msg(uint64_t ts,
falco_common::priority_type priority, falco_common::priority_type priority,
std::string &msg, std::string &msg,
std::string &rule, std::string &rule,
std::map<std::string, std::string> &output_fields) std::map<std::string, std::string> &output_fields)
{ {
std::string full_msg; falco_outputs::ctrl_msg cmsg = {};
cmsg.ts = ts;
cmsg.priority = priority;
cmsg.source = "internal";
cmsg.rule = rule;
cmsg.fields = output_fields;
if(m_json_output) if(m_json_output)
{ {
nlohmann::json jmsg; nlohmann::json jmsg;
// Convert the time-as-nanoseconds to a more json-friendly ISO8601. // Convert the time-as-nanoseconds to a more json-friendly ISO8601.
time_t evttime = now / 1000000000; time_t evttime = ts / 1000000000;
char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS" char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS"
char time_ns[12]; // sizeof ".sssssssssZ" char time_ns[12]; // sizeof ".sssssssssZ"
string iso8601evttime; string iso8601evttime;
strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime)); strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime));
snprintf(time_ns, sizeof(time_ns), ".%09luZ", now % 1000000000); snprintf(time_ns, sizeof(time_ns), ".%09luZ", ts % 1000000000);
iso8601evttime = time_sec; iso8601evttime = time_sec;
iso8601evttime += time_ns; iso8601evttime += time_ns;
@@ -201,15 +231,15 @@ void falco_outputs::handle_msg(uint64_t now,
jmsg["time"] = iso8601evttime; jmsg["time"] = iso8601evttime;
jmsg["output_fields"] = output_fields; jmsg["output_fields"] = output_fields;
full_msg = jmsg.dump(); cmsg.msg = jmsg.dump();
} }
else else
{ {
std::string timestr; std::string timestr;
bool first = true; bool first = true;
sinsp_utils::ts_to_string(now, &timestr, false, true); sinsp_utils::ts_to_string(ts, &timestr, false, true);
full_msg = timestr + ": " + falco_common::priority_names[priority] + " " + msg + " ("; cmsg.msg = timestr + ": " + falco_common::priority_names[priority] + " " + msg + " (";
for(auto &pair : output_fields) for(auto &pair : output_fields)
{ {
if(first) if(first)
@@ -218,23 +248,95 @@ void falco_outputs::handle_msg(uint64_t now,
} }
else else
{ {
full_msg += " "; cmsg.msg += " ";
} }
full_msg += pair.first + "=" + pair.second; cmsg.msg += pair.first + "=" + pair.second;
} }
full_msg += ")"; cmsg.msg += ")";
} }
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it) cmsg.type = ctrl_msg_type::CTRL_MSG_OUTPUT;
{ m_queue.push(cmsg);
(*it)->output_msg(priority, full_msg);
} }
void falco_outputs::cleanup_outputs()
{
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_CLEANUP);
} }
void falco_outputs::reopen_outputs() void falco_outputs::reopen_outputs()
{ {
for(auto it = m_outputs.cbegin(); it != m_outputs.cend(); ++it) this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_REOPEN);
}
void falco_outputs::stop_worker()
{ {
(*it)->reopen(); watchdog<void *> wd;
wd.start([&](void *) -> void {
falco_logger::log(LOG_NOTICE, "output channels still blocked, discarding all remaining notifications\n");
m_queue.clear();
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
});
wd.set_timeout(m_timeout, nullptr);
this->push(falco_outputs::ctrl_msg_type::CTRL_MSG_STOP);
if(m_worker_thread.joinable())
{
m_worker_thread.join();
} }
} }
inline void falco_outputs::push(ctrl_msg_type cmt)
{
falco_outputs::ctrl_msg cmsg = {};
cmsg.type = cmt;
m_queue.push(cmsg);
}
// todo(leogr,leodido): this function is not supposed to throw exceptions, and with "noexcept",
// the program is terminated if that occurs. Although that's the wanted behavior,
// we still need to improve the error reporting since some inner functions can throw exceptions.
void falco_outputs::worker() noexcept
{
watchdog<std::string> wd;
wd.start([&](std::string payload) -> void {
falco_logger::log(LOG_CRIT, "\"" + payload + "\" output timeout, all output channels are blocked\n");
});
auto timeout = m_timeout;
falco_outputs::ctrl_msg cmsg;
do
{
// Block until a message becomes available.
m_queue.pop(cmsg);
for(const auto o : m_outputs)
{
wd.set_timeout(timeout, o->get_name());
try
{
switch(cmsg.type)
{
case ctrl_msg_type::CTRL_MSG_OUTPUT:
o->output(&cmsg);
break;
case ctrl_msg_type::CTRL_MSG_CLEANUP:
case ctrl_msg_type::CTRL_MSG_STOP:
o->cleanup();
break;
case ctrl_msg_type::CTRL_MSG_REOPEN:
o->reopen();
break;
default:
falco_logger::log(LOG_DEBUG, "Outputs worker received an unknown message type\n");
}
}
catch(const exception &e)
{
falco_logger::log(LOG_ERR, o->get_name() + ": " + string(e.what()) + "\n");
}
}
wd.cancel_timeout();
} while(cmsg.type != ctrl_msg_type::CTRL_MSG_STOP);
}

View File

@@ -25,6 +25,7 @@ limitations under the License.
#include "token_bucket.h" #include "token_bucket.h"
#include "falco_engine.h" #include "falco_engine.h"
#include "outputs.h" #include "outputs.h"
#include "tbb/concurrent_queue.h"
// //
// This class acts as the primary interface between a program and the // This class acts as the primary interface between a program and the
@@ -39,25 +40,25 @@ public:
void init(bool json_output, void init(bool json_output,
bool json_include_output_property, bool json_include_output_property,
uint32_t timeout,
uint32_t rate, uint32_t max_burst, bool buffered, uint32_t rate, uint32_t max_burst, bool buffered,
bool time_format_iso_8601, std::string hostname); bool time_format_iso_8601, std::string hostname);
void add_output(falco::outputs::config oc); void add_output(falco::outputs::config oc);
// // Format then send the event to all configured outputs (`evt` is an event that has matched some rule).
// evt is an event that has matched some rule. Pass the event
// to all configured outputs.
//
void handle_event(gen_event *evt, std::string &rule, std::string &source, void handle_event(gen_event *evt, std::string &rule, std::string &source,
falco_common::priority_type priority, std::string &format); falco_common::priority_type priority, std::string &format);
// Send a generic message to all outputs. Not necessarily associated with any event. // Format then send a generic message to all outputs. Not necessarily associated with any event.
void handle_msg(uint64_t now, void handle_msg(uint64_t now,
falco_common::priority_type priority, falco_common::priority_type priority,
std::string &msg, std::string &msg,
std::string &rule, std::string &rule,
std::map<std::string, std::string> &output_fields); std::map<std::string, std::string> &output_fields);
void cleanup_outputs();
void reopen_outputs(); void reopen_outputs();
private: private:
@@ -71,5 +72,28 @@ private:
bool m_buffered; bool m_buffered;
bool m_json_output; bool m_json_output;
bool m_time_format_iso_8601; bool m_time_format_iso_8601;
std::chrono::milliseconds m_timeout;
std::string m_hostname; std::string m_hostname;
enum ctrl_msg_type
{
CTRL_MSG_STOP = 0,
CTRL_MSG_OUTPUT = 1,
CTRL_MSG_CLEANUP = 2,
CTRL_MSG_REOPEN = 3,
};
struct ctrl_msg : falco::outputs::message
{
ctrl_msg_type type;
};
typedef tbb::concurrent_bounded_queue<ctrl_msg> falco_outputs_cbq;
falco_outputs_cbq m_queue;
std::thread m_worker_thread;
inline void push(ctrl_msg_type cmt);
void worker() noexcept;
void stop_worker();
}; };

View File

@@ -37,6 +37,21 @@ struct config
std::map<std::string, std::string> options; std::map<std::string, std::string> options;
}; };
//
// The message to be outputted. It can either refer to:
// - an event that has matched some rule,
// - or a generic message (e.g., a drop alert).
//
struct message
{
uint64_t ts;
falco_common::priority_type priority;
std::string msg;
std::string rule;
std::string source;
map<std::string, std::string> fields;
};
// //
// This class acts as the primary interface for implementing // This class acts as the primary interface for implementing
// a Falco output class. // a Falco output class.
@@ -45,6 +60,8 @@ struct config
class abstract_output class abstract_output
{ {
public: public:
virtual ~abstract_output() {}
void init(config oc, bool buffered, std::string hostname) void init(config oc, bool buffered, std::string hostname)
{ {
m_oc = oc; m_oc = oc;
@@ -52,15 +69,19 @@ public:
m_hostname = hostname; m_hostname = hostname;
} }
// Output an event that has matched some rule. // Return the output's name as per its configuration.
virtual void output_event(gen_event *evt, std::string &rule, std::string &source, const std::string get_name() const
falco_common::priority_type priority, std::string &format, std::string &msg) = 0; {
return m_oc.name;
}
// Output a generic message. Not necessarily associated with any event. // Output a message.
virtual void output_msg(falco_common::priority_type priority, std::string &msg) = 0; virtual void output(const message *msg) = 0;
// Possibly close the output and open it again.
virtual void reopen() {} virtual void reopen() {}
// Possibly flush the output.
virtual void cleanup() {} virtual void cleanup() {}
protected: protected:

View File

@@ -31,16 +31,10 @@ void falco::outputs::output_file::open_file()
} }
} }
void falco::outputs::output_file::output_event(gen_event *evt, std::string &rule, std::string &source, void falco::outputs::output_file::output(const message *msg)
falco_common::priority_type priority, std::string &format, std::string &msg)
{
output_msg(priority, msg);
}
void falco::outputs::output_file::output_msg(falco_common::priority_type priority, std::string &msg)
{ {
open_file(); open_file();
m_outfile << msg + "\n"; m_outfile << msg->msg + "\n";
if(m_oc.options["keep_alive"] != "true") if(m_oc.options["keep_alive"] != "true")
{ {

View File

@@ -27,10 +27,7 @@ namespace outputs
class output_file : public abstract_output class output_file : public abstract_output
{ {
void output_event(gen_event *evt, std::string &rule, std::string &source, void output(const message *msg);
falco_common::priority_type priority, std::string &format, std::string &msg);
void output_msg(falco_common::priority_type priority, std::string &msg);
void cleanup(); void cleanup();

View File

@@ -21,44 +21,41 @@ limitations under the License.
#include "formats.h" #include "formats.h"
#include "banned.h" // This raises a compilation error when certain functions are used #include "banned.h" // This raises a compilation error when certain functions are used
void falco::outputs::output_grpc::output_event(gen_event *evt, std::string &rule, std::string &source, void falco::outputs::output_grpc::output(const message *msg)
falco_common::priority_type priority, std::string &format,
std::string &msg)
{ {
falco::outputs::response grpc_res; falco::outputs::response grpc_res;
// time // time
auto timestamp = grpc_res.mutable_time(); auto timestamp = grpc_res.mutable_time();
*timestamp = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(evt->get_ts()); *timestamp = google::protobuf::util::TimeUtil::NanosecondsToTimestamp(msg->ts);
// rule // rule
auto r = grpc_res.mutable_rule(); auto r = grpc_res.mutable_rule();
*r = rule; *r = msg->rule;
// source // source
falco::schema::source s = falco::schema::source::SYSCALL; falco::schema::source s = falco::schema::source::SYSCALL;
if(!falco::schema::source_Parse(source, &s)) if(!falco::schema::source_Parse(msg->source, &s))
{ {
throw falco_exception("Unknown source passed to output_grpc::output_event()"); throw falco_exception("Unknown source passed to output_grpc::output()");
} }
grpc_res.set_source(s); grpc_res.set_source(s);
// priority // priority
falco::schema::priority p = falco::schema::priority::EMERGENCY; falco::schema::priority p = falco::schema::priority::EMERGENCY;
if(!falco::schema::priority_Parse(falco_common::priority_names[priority], &p)) if(!falco::schema::priority_Parse(falco_common::priority_names[msg->priority], &p))
{ {
throw falco_exception("Unknown priority passed to output_grpc::output_event()"); throw falco_exception("Unknown priority passed to output_grpc::output()");
} }
grpc_res.set_priority(p); grpc_res.set_priority(p);
// output // output
auto output = grpc_res.mutable_output(); auto output = grpc_res.mutable_output();
*output = msg; *output = msg->msg;
// output fields // output fields
auto &fields = *grpc_res.mutable_output_fields(); auto &fields = *grpc_res.mutable_output_fields();
auto resolvedTkns = falco_formats::resolve_tokens(evt, source, format); for(const auto &kv : msg->fields)
for(const auto &kv : resolvedTkns)
{ {
fields[kv.first] = kv.second; fields[kv.first] = kv.second;
} }
@@ -69,8 +66,3 @@ void falco::outputs::output_grpc::output_event(gen_event *evt, std::string &rule
falco::grpc::queue::get().push(grpc_res); falco::grpc::queue::get().push(grpc_res);
} }
void falco::outputs::output_grpc::output_msg(falco_common::priority_type priority, std::string &msg)
{
// todo(fntlnz, leodido, leogr) > gRPC does not support subscribing to dropped events yet
}

View File

@@ -25,10 +25,7 @@ namespace outputs
class output_grpc : public abstract_output class output_grpc : public abstract_output
{ {
void output_event(gen_event *evt, std::string &rule, std::string &source, void output(const message *msg);
falco_common::priority_type priority, std::string &format, std::string &msg);
void output_msg(falco_common::priority_type priority, std::string &msg);
}; };
} // namespace outputs } // namespace outputs

View File

@@ -18,13 +18,7 @@ limitations under the License.
#include "logger.h" #include "logger.h"
#include "banned.h" // This raises a compilation error when certain functions are used #include "banned.h" // This raises a compilation error when certain functions are used
void falco::outputs::output_http::output_event(gen_event *evt, std::string &rule, std::string &source, void falco::outputs::output_http::output(const message *msg)
falco_common::priority_type priority, std::string &format, std::string &msg)
{
output_msg(priority, msg);
}
void falco::outputs::output_http::output_msg(falco_common::priority_type priority, std::string &msg)
{ {
CURL *curl = NULL; CURL *curl = NULL;
CURLcode res = CURLE_FAILED_INIT; CURLcode res = CURLE_FAILED_INIT;
@@ -37,7 +31,7 @@ void falco::outputs::output_http::output_msg(falco_common::priority_type priorit
slist1 = curl_slist_append(slist1, "Content-Type: application/json"); slist1 = curl_slist_append(slist1, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(curl, CURLOPT_URL, m_oc.options["url"].c_str()); curl_easy_setopt(curl, CURLOPT_URL, m_oc.options["url"].c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg.c_str()); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg->msg.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
res = curl_easy_perform(curl); res = curl_easy_perform(curl);

View File

@@ -25,10 +25,7 @@ namespace outputs
class output_http : public abstract_output class output_http : public abstract_output
{ {
void output_event(gen_event *evt, std::string &rule, std::string &source, void output(const message *msg);
falco_common::priority_type priority, std::string &format, std::string &msg);
void output_msg(falco_common::priority_type priority, std::string &msg);
}; };
} // namespace outputs } // namespace outputs

View File

@@ -31,17 +31,11 @@ void falco::outputs::output_program::open_pfile()
} }
} }
void falco::outputs::output_program::output_event(gen_event *evt, std::string &rule, std::string &source, void falco::outputs::output_program::output(const message *msg)
falco_common::priority_type priority, std::string &format, std::string &msg)
{
output_msg(priority, msg);
}
void falco::outputs::output_program::output_msg(falco_common::priority_type priority, std::string &msg)
{ {
open_pfile(); open_pfile();
fprintf(m_pfile, "%s\n", msg.c_str()); fprintf(m_pfile, "%s\n", msg->msg.c_str());
if(m_oc.options["keep_alive"] != "true") if(m_oc.options["keep_alive"] != "true")
{ {

View File

@@ -25,10 +25,7 @@ namespace outputs
class output_program : public abstract_output class output_program : public abstract_output
{ {
void output_event(gen_event *evt, std::string &rule, std::string &source, void output(const message *msg);
falco_common::priority_type priority, std::string &format, std::string &msg);
void output_msg(falco_common::priority_type priority, std::string &msg);
void cleanup(); void cleanup();

View File

@@ -18,13 +18,7 @@ limitations under the License.
#include <iostream> #include <iostream>
#include "banned.h" // This raises a compilation error when certain functions are used #include "banned.h" // This raises a compilation error when certain functions are used
void falco::outputs::output_stdout::output_event(gen_event *evt, std::string &rule, std::string &source, void falco::outputs::output_stdout::output(const message *msg)
falco_common::priority_type priority, std::string &format, std::string &msg)
{
output_msg(priority, msg);
}
void falco::outputs::output_stdout::output_msg(falco_common::priority_type priority, std::string &msg)
{ {
// //
// By default, the stdout stream is fully buffered or line buffered // By default, the stdout stream is fully buffered or line buffered
@@ -36,7 +30,7 @@ void falco::outputs::output_stdout::output_msg(falco_common::priority_type prior
{ {
std::cout << std::unitbuf; std::cout << std::unitbuf;
} }
std::cout << msg + "\n"; std::cout << msg->msg + "\n";
} }
void falco::outputs::output_stdout::cleanup() void falco::outputs::output_stdout::cleanup()

View File

@@ -25,10 +25,7 @@ namespace outputs
class output_stdout : public abstract_output class output_stdout : public abstract_output
{ {
void output_event(gen_event *evt, std::string &rule, std::string &source, void output(const message *msg);
falco_common::priority_type priority, std::string &format, std::string &msg);
void output_msg(falco_common::priority_type priority, std::string &msg);
void cleanup(); void cleanup();
}; };

View File

@@ -18,14 +18,8 @@ limitations under the License.
#include <syslog.h> #include <syslog.h>
#include "banned.h" // This raises a compilation error when certain functions are used #include "banned.h" // This raises a compilation error when certain functions are used
void falco::outputs::output_syslog::output_event(gen_event *evt, std::string &rule, std::string &source, void falco::outputs::output_syslog::output(const message *msg)
falco_common::priority_type priority, std::string &format, std::string &msg)
{
output_msg(priority, msg);
}
void falco::outputs::output_syslog::output_msg(falco_common::priority_type priority, std::string &msg)
{ {
// Syslog output should not have any trailing newline // Syslog output should not have any trailing newline
::syslog(priority, "%s", msg.c_str()); ::syslog(msg->priority, "%s", msg->msg.c_str());
} }

View File

@@ -25,10 +25,7 @@ namespace outputs
class output_syslog : public abstract_output class output_syslog : public abstract_output
{ {
void output_event(gen_event *evt, std::string &rule, std::string &source, void output(const message *msg);
falco_common::priority_type priority, std::string &format, std::string &msg);
void output_msg(falco_common::priority_type priority, std::string &msg);
}; };
} // namespace outputs } // namespace outputs

View File

@@ -57,4 +57,7 @@ enum source {
k8s_audit = 1; k8s_audit = 1;
K8s_audit = 1; K8s_audit = 1;
K8S_audit = 1; K8S_audit = 1;
INTERNAL = 2;
internal = 2;
Internal = 2;
} }

View File

@@ -0,0 +1,96 @@
/*
Copyright (C) 2020 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <chrono>
#include <thread>
#include <functional>
#include <atomic>
template<typename _T>
class watchdog
{
public:
watchdog():
m_timeout(nullptr),
m_is_running(false)
{
}
~watchdog()
{
stop();
}
void start(std::function<void(_T)> cb,
std::chrono::milliseconds resolution = std::chrono::milliseconds(100))
{
stop();
m_is_running.store(true, std::memory_order_release);
m_thread = std::thread([this, cb, resolution]() {
const auto no_deadline = time_point{};
timeout_data curr;
while(m_is_running.load(std::memory_order_acquire))
{
auto t = m_timeout.exchange(nullptr, std::memory_order_release);
if(t)
{
curr = *t;
delete t;
}
if(curr.deadline != no_deadline && curr.deadline < std::chrono::steady_clock::now())
{
cb(curr.payload);
curr.deadline = no_deadline;
}
std::this_thread::sleep_for(resolution);
}
});
}
void stop()
{
if(m_is_running.load(std::memory_order_acquire))
{
m_is_running.store(false, std::memory_order_release);
if(m_thread.joinable())
{
m_thread.join();
}
delete m_timeout.exchange(nullptr, std::memory_order_release);
}
}
inline void set_timeout(std::chrono::milliseconds timeout, _T payload) noexcept
{
delete m_timeout.exchange(new timeout_data{std::chrono::steady_clock::now() + timeout, payload}, std::memory_order_release);
}
inline void cancel_timeout() noexcept
{
delete m_timeout.exchange(new timeout_data, std::memory_order_release);
}
private:
typedef std::chrono::time_point<std::chrono::steady_clock> time_point;
struct timeout_data
{
time_point deadline;
_T payload;
};
std::atomic<timeout_data *> m_timeout;
std::atomic<bool> m_is_running;
std::thread m_thread;
};