Skip to content

Commit 5006a02

Browse files
alessandrogariomkareta
authored andcommitted
Bundle C++ extensions into a single executable (osquery#4335)
1 parent 566f07e commit 5006a02

File tree

8 files changed

+295
-0
lines changed

8 files changed

+295
-0
lines changed

CMake/CMakeLibs.cmake

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,180 @@ macro(ADD_OSQUERY_EXTENSION TARGET)
283283
set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME "${TARGET}.ext")
284284
endmacro(ADD_OSQUERY_EXTENSION)
285285

286+
function(add_osquery_extension_ex class_name extension_type extension_name ${ARGN})
287+
# Make sure the extension type is valid
288+
if(NOT "${extension_type}" STREQUAL "config" AND NOT "${extension_type}" STREQUAL "table")
289+
message(FATAL_ERROR "Invalid extension type specified")
290+
endif()
291+
292+
# Update the initializer list; this will be added to the main.cpp file of the extension
293+
# group
294+
set_property(GLOBAL APPEND_STRING
295+
PROPERTY OSQUERY_EXTENSION_GROUP_INITIALIZERS
296+
"REGISTER_EXTERNAL(${class_name}, \"${extension_type}\", \"${extension_name}\");\n"
297+
)
298+
299+
# Loop through each argument
300+
foreach(argument ${ARGN})
301+
if("${argument}" STREQUAL "SOURCES" OR "${argument}" STREQUAL "LIBRARIES" OR
302+
"${argument}" STREQUAL "INCLUDEDIRS" OR "${argument}" STREQUAL "MAININCLUDES")
303+
304+
set(current_scope "${argument}")
305+
continue()
306+
endif()
307+
308+
if("${current_scope}" STREQUAL "SOURCES")
309+
if(NOT IS_ABSOLUTE "${argument}")
310+
set(argument "${CMAKE_CURRENT_SOURCE_DIR}/${argument}")
311+
endif()
312+
313+
list(APPEND source_file_list "${argument}")
314+
315+
elseif("${current_scope}" STREQUAL "INCLUDEDIRS")
316+
if(NOT IS_ABSOLUTE "${argument}")
317+
set(argument "${CMAKE_CURRENT_SOURCE_DIR}/${argument}")
318+
endif()
319+
320+
list(APPEND include_folder_list "${argument}")
321+
322+
elseif("${current_scope}" STREQUAL "LIBRARIES")
323+
list(APPEND library_list "${argument}")
324+
elseif("${current_scope}" STREQUAL "MAININCLUDES")
325+
list(APPEND main_include_list "${argument}")
326+
else()
327+
message(FATAL_ERROR "Invalid scope")
328+
endif()
329+
endforeach()
330+
331+
# Validate the arguments
332+
if("${source_file_list}" STREQUAL "")
333+
message(FATAL_ERROR "Source files are missing")
334+
endif()
335+
336+
if("${main_include_list}" STREQUAL "")
337+
message(FATAL_ERROR "The main include list is missing")
338+
endif()
339+
340+
# Update the global properties
341+
set_property(GLOBAL APPEND
342+
PROPERTY OSQUERY_EXTENSION_GROUP_SOURCES
343+
${source_file_list}
344+
)
345+
346+
set_property(GLOBAL APPEND
347+
PROPERTY OSQUERY_EXTENSION_GROUP_MAIN_INCLUDES
348+
${main_include_list}
349+
)
350+
351+
if(NOT "${library_list}" STREQUAL "")
352+
set_property(GLOBAL APPEND
353+
PROPERTY OSQUERY_EXTENSION_GROUP_LIBRARIES
354+
${library_list}
355+
)
356+
endif()
357+
358+
if(NOT "${include_folder_list}" STREQUAL "")
359+
set_property(GLOBAL APPEND
360+
PROPERTY OSQUERY_EXTENSION_GROUP_INCLUDE_FOLDERS
361+
${include_folder_list}
362+
)
363+
endif()
364+
endfunction()
365+
366+
# This function takes the global properties saved by add_osquery_extension_ex and generates
367+
# a single extenion executable containing all the user code
368+
function(generate_osquery_extension_group)
369+
get_property(extension_source_files GLOBAL PROPERTY OSQUERY_EXTENSION_GROUP_SOURCES)
370+
if("${extension_source_files}" STREQUAL "")
371+
return()
372+
endif()
373+
374+
# Allow the user to customize the extension name and version using
375+
# environment variables
376+
if(DEFINED ENV{OSQUERY_EXTENSION_GROUP_NAME})
377+
set(OSQUERY_EXTENSION_GROUP_NAME $ENV{OSQUERY_EXTENSION_GROUP_NAME})
378+
else()
379+
set(OSQUERY_EXTENSION_GROUP_NAME "osquery_extension_group")
380+
endif()
381+
382+
if(DEFINED ENV{OSQUERY_EXTENSION_GROUP_VERSION})
383+
set(OSQUERY_EXTENSION_GROUP_VERSION $ENV{OSQUERY_EXTENSION_GROUP_VERSION})
384+
else()
385+
set(OSQUERY_EXTENSION_GROUP_VERSION "1.0")
386+
endif()
387+
388+
# Build the include list; this contains the files required to declare
389+
# the classes used in the REGISTER_EXTERNAL directives
390+
#
391+
# Note: The variables in uppercase are used by the template
392+
get_property(main_include_list GLOBAL PROPERTY OSQUERY_EXTENSION_GROUP_MAIN_INCLUDES)
393+
foreach(include_file ${main_include_list})
394+
set(OSQUERY_EXTENSION_GROUP_INCLUDES "${OSQUERY_EXTENSION_GROUP_INCLUDES}\n#include <${include_file}>")
395+
endforeach()
396+
397+
# We need to generate the main.cpp file, containing all the required
398+
# REGISTER_EXTERNAL directives
399+
get_property(OSQUERY_EXTENSION_GROUP_INITIALIZERS GLOBAL PROPERTY OSQUERY_EXTENSION_GROUP_INITIALIZERS)
400+
configure_file(
401+
"${CMAKE_SOURCE_DIR}/tools/codegen/templates/osquery_extension_group_main.cpp.in"
402+
"${CMAKE_CURRENT_BINARY_DIR}/osquery_extension_group_main.cpp"
403+
)
404+
405+
# Extensions can no longer control which compilation flags to use here (as they are shared) so
406+
# we are going to enforce sane defaults
407+
if(UNIX)
408+
set(extension_cxx_flags
409+
-pedantic -Wall -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization
410+
-Wformat=2 -Winit-self -Wlong-long -Wmissing-declarations -Wmissing-include-dirs -Wcomment
411+
-Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion
412+
-Wsign-promo -Wstrict-overflow=5 -Wswitch-default -Wundef -Werror -Wunused -Wuninitialized
413+
-Wconversion
414+
)
415+
416+
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
417+
list(APPEND extension_cxx_flags -g3 --gdwarf-2)
418+
endif()
419+
else()
420+
set(extension_cxx_flags /W4)
421+
endif()
422+
423+
# Generate the extension target
424+
add_executable("${OSQUERY_EXTENSION_GROUP_NAME}"
425+
"${CMAKE_CURRENT_BINARY_DIR}/osquery_extension_group_main.cpp"
426+
${extension_source_files}
427+
)
428+
429+
set_property(TARGET "${OSQUERY_EXTENSION_GROUP_NAME}" PROPERTY INCLUDE_DIRECTORIES "")
430+
target_compile_features("${OSQUERY_EXTENSION_GROUP_NAME}" PUBLIC cxx_std_14)
431+
target_compile_options("${OSQUERY_EXTENSION_GROUP_NAME}" PRIVATE ${extension_cxx_flags})
432+
433+
set_target_properties("${OSQUERY_EXTENSION_GROUP_NAME}" PROPERTIES
434+
OUTPUT_NAME "${OSQUERY_EXTENSION_GROUP_NAME}.ext"
435+
)
436+
437+
# Import the core libraries; note that we are going to inherit include directories
438+
# with the wrong scope, so we'll have to fix it
439+
set_property(TARGET "${OSQUERY_EXTENSION_GROUP_NAME}" PROPERTY INCLUDE_DIRECTORIES "")
440+
441+
get_property(include_folder_list TARGET libosquery PROPERTY INCLUDE_DIRECTORIES)
442+
target_include_directories("${OSQUERY_EXTENSION_GROUP_NAME}" SYSTEM PRIVATE ${include_folder_list})
443+
444+
TARGET_OSQUERY_LINK_WHOLE("${OSQUERY_EXTENSION_GROUP_NAME}" libosquery)
445+
446+
# Apply the user (extension) settings
447+
get_property(library_list GLOBAL PROPERTY OSQUERY_EXTENSION_GROUP_LIBRARIES)
448+
if(NOT "${library_list}" STREQUAL "")
449+
target_link_libraries("${OSQUERY_EXTENSION_GROUP_NAME}" ${library_list})
450+
endif()
451+
452+
get_property(include_folder_list GLOBAL PROPERTY OSQUERY_EXTENSION_GROUP_INCLUDE_FOLDERS)
453+
if(NOT "${include_folder_list}" STREQUAL "")
454+
target_include_directories("${OSQUERY_EXTENSION_GROUP_NAME}" PRIVATE
455+
${include_folder_list}
456+
)
457+
endif()
458+
endfunction()
459+
286460
# Helper to abstract OS/Compiler whole linking.
287461
macro(TARGET_OSQUERY_LINK_WHOLE TARGET OSQUERY_LIB)
288462
if(WINDOWS)

docs/wiki/development/osquery-sdk.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,20 @@ Scanning dependencies of target external_extension_awesome
148148
[100%] Built target externals
149149
```
150150

151+
## Bundling extensions into a single executable
152+
All the extensions declared with the **add_osquery_extension_ex()** CMake function will be automatically bundled into a single executable.
153+
154+
The executable name and version can be changed using the following two environment variables:
155+
156+
1. OSQUERY_EXTENSION_GROUP_NAME (default: osquery_extension_group)
157+
2. OSQUERY_EXTENSION_GROUP_VERSION (default: 1.0)
158+
159+
It is important to provide a header file that can be included by the generated main.cpp file; its purpose is to define the types used by the **REGISTER_EXTERNAL** directive.
160+
161+
An example is included in the `osquery/examples/extension_group_example`.
162+
163+
Please note that when using bundling the source directory of each extension is added to the include folder list; developers should always use uniquely named include files.
164+
151165
## Thrift API
152166

153167
[Thrift](https://thrift.apache.org/) is a code-generation/cross-language service development framework. osquery uses Thrift to allow plugin extensions for config retrieval, log export, table implementations, event subscribers, and event publishers. We also use Thrift to wrap our SQL implementation using SQLite.

external/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ foreach(external_project ${EXTERNAL_PROJECTS})
5353
endif()
5454
endif()
5555
endforeach()
56+
57+
# If the user has generated extensions using the new generate_osquery_extension_group
58+
# function, then this call will generate the bundle
59+
generate_osquery_extension_group()

include/osquery/extensions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ class ExternalSQLPlugin : public SQLPlugin {
5656
Status query(const std::string& query,
5757
QueryData& results,
5858
bool use_cache = false) const override {
59+
static_cast<void>(use_cache);
5960
return queryExternal(query, results);
6061
}
6162

6263
Status getQueryTables(const std::string& query,
6364
std::vector<std::string>& tables) const override {
65+
static_cast<void>(query);
66+
static_cast<void>(tables);
6467
return Status(0, "Not used");
6568
}
6669

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
project(ext_example)
2+
3+
function(main)
4+
set(PROJECT_SOURCEFILES
5+
"${CMAKE_CURRENT_SOURCE_DIR}/src/example.h"
6+
"${CMAKE_CURRENT_SOURCE_DIR}/src/example.cpp"
7+
)
8+
9+
add_osquery_extension_ex("ExampleTable" "table" "example"
10+
SOURCES ${PROJECT_SOURCEFILES}
11+
INCLUDEDIRS "${CMAKE_CURRENT_SOURCE_DIR}/src"
12+
MAININCLUDES "example.h"
13+
)
14+
endfunction()
15+
16+
main()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under both the Apache 2.0 license (found in the
6+
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
7+
* in the COPYING file in the root directory of this source tree).
8+
* You may select, at your option, one of the above-listed licenses.
9+
*/
10+
11+
#include "example.h"
12+
13+
namespace osquery {
14+
TableColumns ExampleTable::columns() const {
15+
return {
16+
std::make_tuple("example_text", TEXT_TYPE, ColumnOptions::DEFAULT),
17+
std::make_tuple("example_integer", INTEGER_TYPE, ColumnOptions::DEFAULT),
18+
};
19+
}
20+
21+
QueryData ExampleTable::generate(QueryContext& request) {
22+
static_cast<void>(request);
23+
24+
Row r;
25+
r["example_text"] = "example";
26+
r["example_integer"] = INTEGER(1);
27+
28+
return {r};
29+
}
30+
} // namespace osquery
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under both the Apache 2.0 license (found in the
6+
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
7+
* in the COPYING file in the root directory of this source tree).
8+
* You may select, at your option, one of the above-listed licenses.
9+
*/
10+
11+
#pragma once
12+
13+
#include <osquery/sdk.h>
14+
#include <osquery/system.h>
15+
16+
namespace osquery {
17+
class ExampleTable : public TablePlugin {
18+
private:
19+
TableColumns columns() const;
20+
QueryData generate(QueryContext& request);
21+
};
22+
} // namespace osquery
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under both the Apache 2.0 license (found in the
6+
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
7+
* in the COPYING file in the root directory of this source tree).
8+
* You may select, at your option, one of the above-listed licenses.
9+
*/
10+
11+
#include <osquery/sdk.h>
12+
#include <osquery/system.h>
13+
14+
using namespace osquery;
15+
16+
@OSQUERY_EXTENSION_GROUP_INCLUDES@
17+
18+
@OSQUERY_EXTENSION_GROUP_INITIALIZERS@
19+
20+
int main(int argc, char* argv[]) {
21+
osquery::Initializer runner(argc, argv, ToolType::EXTENSION);
22+
23+
auto status = startExtension("@OSQUERY_EXTENSION_GROUP_NAME@", "@OSQUERY_EXTENSION_GROUP_VERSION@");
24+
if (!status.ok()) {
25+
LOG(ERROR) << status.getMessage();
26+
runner.requestShutdown(status.getCode());
27+
}
28+
29+
// Finally wait for a signal / interrupt to shutdown.
30+
runner.waitForShutdown();
31+
return 0;
32+
}

0 commit comments

Comments
 (0)