Merge pull request #5 from draios/replumb-events

Support output formats
This commit is contained in:
Henri DF 2016-02-24 08:43:28 -08:00
commit ef3b2728f5
10 changed files with 200 additions and 66 deletions

View File

@ -10,7 +10,7 @@ include_directories(${PROJECT_SOURCE_DIR}/../sysdig/userspace/libscap)
include_directories(${PROJECT_SOURCE_DIR}/../sysdig/userspace/libsinsp)
include_directories("${PROJECT_BINARY_DIR}/userspace/digwatch")
add_executable(digwatch rules.cpp digwatch.cpp)
add_executable(digwatch formats.cpp rules.cpp digwatch.cpp)
target_link_libraries(digwatch sinsp)

View File

@ -18,6 +18,7 @@ extern "C" {
#include <sinsp.h>
#include <config_digwatch.h>
#include "rules.h"
#include "formats.h"
#include "digwatch.h"
#include "utils.h"
@ -28,7 +29,7 @@ extern "C" {
static void usage()
{
printf(
"Usage: digwatch [options] [-p <output_format>] rules_filename\n\n"
"Usage: digwatch [options] rules_filename\n\n"
"Options:\n"
" -h, --help Print this page\n"
" -m <filename>, --main-lua <filename>\n"
@ -55,13 +56,15 @@ static void usage()
captureinfo do_inspect(sinsp* inspector,
uint64_t cnt,
int duration_to_tot,
sinsp_evt_formatter* formatter)
digwatch_rules* rules,
digwatch_formats* formats)
{
captureinfo retval;
int32_t res;
sinsp_evt* ev;
string line;
int duration_start = 0;
sinsp_evt_formatter* formatter;
//
// Loop through the events
@ -113,12 +116,21 @@ captureinfo do_inspect(sinsp* inspector,
{
continue;
}
if(formatter->tostring(ev, &line))
formatter = formats->lookup_formatter(ev->get_check_id());
if (!formatter)
{
cout << line;
cout << endl;
throw sinsp_exception("Error: No formatter for event with id %d " + to_string(ev->get_check_id()));
}
bool has_all = formatter->tostring(ev, &line);
if (!has_all) {
cout << "(missing fields) ";
}
cout << line;
cout << endl;
}
return retval;
@ -132,15 +144,16 @@ int digwatch_init(int argc, char **argv)
int result;
sinsp* inspector = NULL;
digwatch_rules* rules = NULL;
digwatch_formats* formats = NULL;
int op;
uint64_t cnt = -1;
sinsp_evt::param_fmt event_buffer_format = sinsp_evt::PF_NORMAL;
int duration_to_tot = 0;
captureinfo cinfo;
string output_format;
int long_index = 0;
string lua_main_filename;
string lua_dir = DIGWATCH_INSTALLATION_DIR;
lua_State* ls;
static struct option long_options[] =
{
@ -152,8 +165,6 @@ int digwatch_init(int argc, char **argv)
{0, 0, 0, 0}
};
output_format = "*%evt.num %evt.outputtime %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.info";
try
{
inspector = new sinsp();
@ -240,10 +251,6 @@ int digwatch_init(int argc, char **argv)
}
//
// Create the event formatter
//
sinsp_evt_formatter formatter(inspector, output_format);
char* env_lua_dir = getenv("DIGWATCH_LUA_DIR");
if (env_lua_dir)
{
@ -256,7 +263,12 @@ int digwatch_init(int argc, char **argv)
lua_main_filename = lua_dir + DIGWATCH_LUA_MAIN;
}
rules = new digwatch_rules(inspector, lua_main_filename, lua_dir);
// Initialize Lua interpreter
ls = lua_open();
luaL_openlibs(ls);
rules = new digwatch_rules(inspector, ls, lua_main_filename, lua_dir);
formats = new digwatch_formats(inspector, ls);
rules->load_rules(rules_file);
inspector->set_filter(rules->get_filter());
@ -265,7 +277,8 @@ int digwatch_init(int argc, char **argv)
cinfo = do_inspect(inspector,
cnt,
duration_to_tot,
&formatter);
rules,
formats);
inspector->close();
}
@ -287,6 +300,7 @@ exit:
delete inspector;
}
lua_close(ls);
return result;
}

View File

@ -0,0 +1,57 @@
#include "formats.h"
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
std::map<uint32_t, sinsp_evt_formatter*> g_format_map;
sinsp* g_inspector;
const static struct luaL_reg ll_digwatch [] =
{
{"set_formatter", &digwatch_formats::set_formatter},
{NULL,NULL}
};
digwatch_formats::digwatch_formats(sinsp* inspector, lua_State *ls)
{
g_inspector = inspector;
m_ls = ls;
luaL_openlib(m_ls, "digwatch", ll_digwatch, 0);
}
int digwatch_formats::set_formatter (lua_State *ls) {
uint32_t index = luaL_checkinteger(ls, 1);
string format = luaL_checkstring(ls, 2);
try
{
if(format == "" || format == "default")
{
g_format_map[index] = new sinsp_evt_formatter(g_inspector, DEFAULT_OUTPUT_STR);
}
else
{
g_format_map[index] = new sinsp_evt_formatter(g_inspector, format);
}
}
catch(sinsp_exception& e)
{
string err = "invalid output format " + format;
fprintf(stderr, "%s\n", err.c_str());
throw sinsp_exception("set_formatter error");
}
return 0;
}
sinsp_evt_formatter* digwatch_formats::lookup_formatter(uint32_t index)
{
return g_format_map[index];
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "sinsp.h"
#include "lua_parser.h"
class sinsp_evt_formatter;
class digwatch_formats
{
public:
digwatch_formats(sinsp* inspector, lua_State *ls);
// set_formatter(index, format_string)
static int set_formatter(lua_State *ls);
sinsp_evt_formatter* lookup_formatter(uint32_t index);
private:
lua_State* m_ls;
};

View File

@ -168,6 +168,9 @@ local function outputformat (format)
end
local function rule(filter, output)
if not output then
output = outputformat("")
end
return {type = "Rule", filter = filter, output = output}
end
@ -336,14 +339,24 @@ end
--]]
function expand_macros(node, defs, changed)
if node.type == "Filter" then
function copy(obj)
if type(obj) ~= 'table' then return obj end
local res = {}
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
return res
end
if (node.type == "Rule") then
macros = expand_macros(node.filter, defs, changed)
elseif node.type == "Filter" then
if (node.value.type == "Macro") then
if (defs[node.value.value] == nil) then
tostring = require 'ml'.tstring
error("Undefined macro '".. node.value.value .. "' used in filter.")
end
node.value = defs[node.value.value]
node.value = copy(defs[node.value.value])
changed = true
return changed
end
return expand_macros(node.value, defs, changed)
@ -353,7 +366,7 @@ function expand_macros(node, defs, changed)
if (defs[node.left.value] == nil) then
error("Undefined macro '".. node.left.value .. "' used in filter.")
end
node.left = defs[node.left.value]
node.left = copy(defs[node.left.value])
changed = true
end
@ -361,7 +374,7 @@ function expand_macros(node, defs, changed)
if (defs[node.right.value] == nil) then
error("Undefined macro ".. node.right.value .. "used in filter.")
end
node.right = defs[node.right.value]
node.right = copy(defs[node.right.value])
changed = true
end
@ -374,7 +387,7 @@ function expand_macros(node, defs, changed)
if (defs[node.argument.value] == nil) then
error("Undefined macro ".. node.argument.value .. "used in filter.")
end
node.argument = defs[node.argument.value]
node.argument = copy(defs[node.argument.value])
changed = true
end
return expand_macros(node.argument, defs, changed)
@ -442,7 +455,7 @@ function print_ast(node, level)
elseif t == "MacroDef" then
-- don't print for now
else
error ("Unexpected type: "..t)
error ("Unexpected type in print_ast: "..t)
end
end
compiler.parser.print_ast = print_ast
@ -460,22 +473,9 @@ end
--[[
Sets up compiler state and returns it.
This is an opaque blob that is passed into subsequent compiler calls and
should not be modified by the client.
It holds state such as macro definitions that must be kept across calls
to the line-oriented compiler.
Compiles a single line from a digwatch ruleset and updates the passed-in macros table. Returns the AST of the line.
--]]
function compiler.init()
return {macros={}, ast=nil}
end
--[[
Compiles a single line from a digwatch ruleset and updates the passed-in state object. Returns the AST of the line.
--]]
function compiler.compile_line(line, state)
function compiler.compile_line(line, macro_defs)
local ast, error_msg = compiler.parser.parse_line(line)
if (error_msg) then
@ -501,7 +501,7 @@ function compiler.compile_line(line, state)
end
for m, _ in pairs(macros) do
if state.macros[m] == nil then
if macros[m] == nil then
error ("Undefined macro '"..m.."' used in '"..line.."'")
end
end
@ -509,7 +509,7 @@ function compiler.compile_line(line, state)
if (ast.type == "MacroDef") then
-- Parsed line is a macro definition, so update our dictionary of macros and
-- return
state.macros[ast.name] = ast.value
macro_defs[ast.name] = ast.value
return ast
elseif (ast.type == "Rule") then
@ -519,14 +519,9 @@ function compiler.compile_line(line, state)
expand_in(ast.filter)
repeat
expanded = expand_macros(ast, state.macros, false)
expanded = expand_macros(ast, macro_defs, false)
until expanded == false
if (state.ast == nil) then
state.ast = ast
else
state.ast = { type = "BinaryBoolOp", operator = "or", left = state.ast, right = ast }
end
else
error("Unexpected top-level AST type: "..ast.type)
end

View File

@ -38,6 +38,7 @@ good "not (not (a))"
good "not (a.b=1)"
good "not (a.a exists)"
good "not a"
good "a.b = 1 and not a"
good "not not a"
good "(not not a)"
good "not a.b=1"

View File

@ -7,20 +7,37 @@
local compiler = require "compiler"
local function mark_check_nodes(ast, index)
local t = ast.type
if t == "BinaryBoolOp" then
mark_check_nodes(ast.left, index)
mark_check_nodes(ast.right, index)
elseif t == "UnaryBoolOp" then
mark_check_nodes(ast.argument, index)
elseif t == "BinaryRelOp" then
ast.index = index
elseif t == "UnaryRelOp" then
ast.index = index
else
error ("Unexpected type in install_filter: "..t)
end
end
local function install_filter(node)
local t = node.type
if t == "Filter" then
install_filter(node.value)
elseif t == "BinaryBoolOp" then
if t == "BinaryBoolOp" then
filter.nest() --io.write("(")
install_filter(node.left)
filter.bool_op(node.operator) --io.write(" "..node.operator.." ")
install_filter(node.right)
filter.unnest() --io.write(")")
elseif t == "UnaryBoolOp" then
filter.nest() --io.write("(")
filter.bool_op(node.operator) -- io.write(" "..node.operator.." ")
@ -28,15 +45,15 @@ local function install_filter(node)
filter.unnest() -- io.write(")")
elseif t == "BinaryRelOp" then
filter.rel_expr(node.left.value, node.operator, node.right.value)
filter.rel_expr(node.left.value, node.operator, node.right.value, node.index)
-- io.write(node.left.value.." "..node.operator.." "..node.right.value)
elseif t == "UnaryRelOp" then
filter.rel_expr(node.argument.value, node.operator)
filter.rel_expr(node.argument.value, node.operator, node.index)
--io.write(node.argument.value.." "..node.operator)
else
error ("Unexpected type: "..t)
error ("Unexpected type in install_filter: "..t)
end
end
@ -53,14 +70,43 @@ end
local state
--[[
Sets up compiler state and returns it.
It holds state such as macro definitions that must be kept across calls
to the line-oriented compiler.
--]]
local function init()
return {macros={}, filter_ast=nil, n_rules=0}
end
function load_rule(r)
if (state == nil) then
state = compiler.init()
state = init()
end
local line_ast = compiler.compile_line(r, state.macros)
if (line_ast.type == nil) then -- blank line
return
elseif (line_ast.type == "MacroDef") then
return
elseif (not (line_ast.type == "Rule")) then
error ("Unexpected type in load_rule: "..line_ast.type)
end
digwatch.set_formatter(state.n_rules, line_ast.output.value)
mark_check_nodes(line_ast.filter.value, state.n_rules)
state.n_rules = state.n_rules + 1
if (state.filter_ast == nil) then
state.filter_ast = line_ast.filter.value
else
state.filter_ast = { type = "BinaryBoolOp", operator = "or", left = state.filter_ast, right = line_ast.filter.value }
end
compiler.compile_line(r, state)
end
function on_done()
install_filter(state.ast)
install_filter(state.filter_ast)
end

View File

@ -5,10 +5,11 @@ if #arg ~= 1 then
os.exit(1)
end
local state = compiler.init()
local macros = {}
local ast
local function doit(line)
local ast = compiler.compile_line(line, state)
ast = compiler.compile_line(line, macros)
if not ast then
print("error", error_msg)
@ -20,8 +21,8 @@ for str in string.gmatch(arg[1], "([^;]+)") do
doit(str)
end
if not (state.ast == nil) then -- can be nil if only macros
compiler.parser.print_ast(state.ast)
if not (ast) then
compiler.parser.print_ast(ast)
end
os.exit(0)

View File

@ -6,11 +6,10 @@ extern "C" {
#include "lauxlib.h"
}
digwatch_rules::digwatch_rules(sinsp* inspector, string lua_main_filename, string lua_dir)
digwatch_rules::digwatch_rules(sinsp* inspector, lua_State *ls, string lua_main_filename, string lua_dir)
{
// Initialize Lua interpreter
m_ls = lua_open();
luaL_openlibs(m_ls);
m_ls = ls;
m_lua_parser = new lua_parser(inspector, m_ls);
@ -84,7 +83,7 @@ void digwatch_rules::load_rules(string rules_filename)
if(lua_pcall(m_ls, 1, 0, 0) != 0)
{
const char* lerr = lua_tostring(m_ls, -1);
string err = "Error loading rule: " + string(lerr);
string err = "Error loading rule '" + line + "':" + string(lerr);
throw sinsp_exception(err);
}
}

View File

@ -6,7 +6,7 @@
class digwatch_rules
{
public:
digwatch_rules(sinsp* inspector, string lua_main_filename, string lua_dir);
digwatch_rules(sinsp* inspector, lua_State *ls, string lua_main_filename, string lua_dir);
~digwatch_rules();
void load_rules(string rules_filename);
sinsp_filter* get_filter();
@ -17,6 +17,8 @@ class digwatch_rules
lua_parser* m_lua_parser;
lua_State* m_ls;
string m_lua_load_rule = "load_rule";
string m_lua_on_done = "on_done";
string m_lua_on_event = "on_event";
};