diff --git a/examples/logrotate/falco b/examples/logrotate/falco new file mode 100644 index 00000000..13acd0d5 --- /dev/null +++ b/examples/logrotate/falco @@ -0,0 +1,7 @@ +/var/log/falco-events.log { + rotate 5 + size 1M + postrotate + /usr/bin/killall -USR1 falco + endscript +} \ No newline at end of file diff --git a/falco.yaml b/falco.yaml index 78c1355f..b19e5b7f 100644 --- a/falco.yaml +++ b/falco.yaml @@ -66,6 +66,10 @@ syslog_output: # continuously written to, with each output message on its own # line. If keep_alive is set to false, the file will be re-opened # for each output message. +# +# Also, the file will be closed and reopened if falco is signaled with +# SIGUSR1. + file_output: enabled: false keep_alive: false @@ -86,7 +90,9 @@ stdout_output: # continuously written to, with each output message on its own # line. If keep_alive is set to false, the program will be re-spawned # for each output message. - +# +# Also, the program will be closed and reopened if falco is signaled with +# SIGUSR1. program_output: enabled: false keep_alive: false diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp index aa2253b6..ae75bf0b 100644 --- a/userspace/falco/falco.cpp +++ b/userspace/falco/falco.cpp @@ -39,6 +39,8 @@ along with falco. If not, see . #include "statsfilewriter.h" bool g_terminate = false; +bool g_reopen_outputs = false; + // // Helper functions // @@ -47,6 +49,11 @@ static void signal_callback(int signal) g_terminate = true; } +static void reopen_outputs(int signal) +{ + g_reopen_outputs = true; +} + // // Program help // @@ -171,6 +178,12 @@ uint64_t do_inspect(falco_engine *engine, writer.handle(); + if(g_reopen_outputs) + { + outputs->reopen_outputs(); + g_reopen_outputs = false; + } + if (g_terminate) { break; @@ -589,6 +602,13 @@ int falco_init(int argc, char **argv) goto exit; } + if(signal(SIGUSR1, reopen_outputs) == SIG_ERR) + { + fprintf(stderr, "An error occurred while setting SIGUSR1 signal handler.\n"); + result = EXIT_FAILURE; + goto exit; + } + if (scap_filename.size()) { inspector->open(scap_filename); diff --git a/userspace/falco/falco_outputs.cpp b/userspace/falco/falco_outputs.cpp index ed07bff2..c5075fc0 100644 --- a/userspace/falco/falco_outputs.cpp +++ b/userspace/falco/falco_outputs.cpp @@ -142,3 +142,19 @@ void falco_outputs::handle_event(sinsp_evt *ev, string &rule, falco_common::prio } } + +void falco_outputs::reopen_outputs() +{ + lua_getglobal(m_ls, m_lua_output_reopen.c_str()); + + if(!lua_isfunction(m_ls, -1)) + { + throw falco_exception("No function " + m_lua_output_reopen + " found. "); + } + + if(lua_pcall(m_ls, 0, 0, 0) != 0) + { + const char* lerr = lua_tostring(m_ls, -1); + throw falco_exception(string(lerr)); + } +} diff --git a/userspace/falco/falco_outputs.h b/userspace/falco/falco_outputs.h index 9a45d599..ec51bc88 100644 --- a/userspace/falco/falco_outputs.h +++ b/userspace/falco/falco_outputs.h @@ -53,6 +53,8 @@ public: // void handle_event(sinsp_evt *ev, std::string &rule, falco_common::priority_type priority, std::string &format); + void reopen_outputs(); + private: bool m_initialized; @@ -64,5 +66,6 @@ private: std::string m_lua_add_output = "add_output"; std::string m_lua_output_event = "output_event"; std::string m_lua_output_cleanup = "output_cleanup"; + std::string m_lua_output_reopen = "output_reopen"; std::string m_lua_main_filename = "output.lua"; }; diff --git a/userspace/falco/lua/output.lua b/userspace/falco/lua/output.lua index 7d3e0b65..caa6454b 100644 --- a/userspace/falco/lua/output.lua +++ b/userspace/falco/lua/output.lua @@ -20,8 +20,8 @@ local mod = {} local outputs = {} -function mod.stdout(priority, priority_num, buffered, msg) - if buffered == 0 then +function mod.stdout(priority, priority_num, msg, options) + if options.buffered == 0 then io.stdout:setvbuf 'no' end print (msg) @@ -31,6 +31,10 @@ function mod.stdout_cleanup() io.stdout:flush() end +-- Note: not actually closing/reopening stdout +function mod.stdout_reopen(options) +end + function mod.file_validate(options) if (not type(options.filename) == 'string') then error("File output needs to be configured with a valid filename") @@ -44,14 +48,18 @@ function mod.file_validate(options) end -function mod.file(priority, priority_num, buffered, msg, options) - if options.keep_alive == "true" then - if ffile == nil then - ffile = io.open(options.filename, "a+") - if buffered == 0 then - ffile:setvbuf 'no' - end +function mod.file_open(options) + if ffile == nil then + ffile = io.open(options.filename, "a+") + if options.buffered == 0 then + ffile:setvbuf 'no' end + end +end + +function mod.file(priority, priority_num, msg, options) + if options.keep_alive == "true" then + mod.file_open(options) else ffile = io.open(options.filename, "a+") end @@ -73,26 +81,40 @@ function mod.file_cleanup() end end -function mod.syslog(priority, priority_num, buffered, msg, options) +function mod.file_reopen(options) + if options.keep_alive == "true" then + mod.file_cleanup() + mod.file_open(options) + end +end + +function mod.syslog(priority, priority_num, msg, options) falco.syslog(priority_num, msg) end function mod.syslog_cleanup() end -function mod.program(priority, priority_num, buffered, msg, options) +function mod.syslog_reopen() +end + +function mod.program_open(options) + if pfile == nil then + pfile = io.popen(options.program, "w") + if options.buffered == 0 then + pfile:setvbuf 'no' + end + end +end + +function mod.program(priority, priority_num, msg, options) -- XXX Ideally we'd check that the program ran -- successfully. However, the luajit we're using returns true even -- when the shell can't run the program. -- Note: options are all strings if options.keep_alive == "true" then - if pfile == nil then - pfile = io.popen(options.program, "w") - if buffered == 0 then - pfile:setvbuf 'no' - end - end + mod.program_open(options) else pfile = io.popen(options.program, "w") end @@ -114,6 +136,13 @@ function mod.program_cleanup() end end +function mod.program_reopen(options) + if options.keep_alive == "true" then + mod.program_cleanup() + mod.program_open(options) + end +end + function output_event(event, rule, priority, priority_num, format) -- If format starts with a *, remove it, as we're adding our own -- prefix here. @@ -126,7 +155,7 @@ function output_event(event, rule, priority, priority_num, format) msg = formats.format_event(event, rule, priority, format) for index,o in ipairs(outputs) do - o.output(priority, priority_num, o.buffered, msg, o.config) + o.output(priority, priority_num, msg, o.options) end end @@ -137,20 +166,33 @@ function output_cleanup() end end -function add_output(output_name, buffered, config) +function output_reopen() + for index,o in ipairs(outputs) do + o.reopen(o.options) + end +end + +function add_output(output_name, buffered, options) if not (type(mod[output_name]) == 'function') then error("rule_loader.add_output(): invalid output_name: "..output_name) end -- outputs can optionally define a validation function so that we don't - -- find out at runtime (when an event finally matches a rule!) that the config is invalid + -- find out at runtime (when an event finally matches a rule!) that the options are invalid if (type(mod[output_name.."_validate"]) == 'function') then - mod[output_name.."_validate"](config) + mod[output_name.."_validate"](options) end + if options == nil then + options = {} + end + + options.buffered = buffered + table.insert(outputs, {output = mod[output_name], cleanup = mod[output_name.."_cleanup"], - buffered=buffered, config=config}) + reopen = mod[output_name.."_reopen"], + options=options}) end return mod