Reorganize directory structure
authorSangwan Kwon <sangwan.kwon@samsung.com>
Thu, 5 Sep 2019 08:13:13 +0000 (17:13 +0900)
committerSangwan Kwon <sangwan.kwon@samsung.com>
Fri, 6 Sep 2019 06:35:23 +0000 (15:35 +0900)
Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
385 files changed:
CMake/Macro.cmake
CMakeLists.txt
include/osquery/config.h [deleted file]
include/osquery/core.h [deleted file]
include/osquery/database.h [deleted file]
include/osquery/database/results.h [deleted file]
include/osquery/events.h [deleted file]
include/osquery/extensions.h [deleted file]
include/osquery/filesystem.h [deleted file]
include/osquery/flags.h [deleted file]
include/osquery/hash.h [deleted file]
include/osquery/logger.h [deleted file]
include/osquery/notification.h [deleted file]
include/osquery/registry.h [deleted file]
include/osquery/sdk.h [deleted file]
include/osquery/sql.h [deleted file]
include/osquery/status.h [deleted file]
include/osquery/tables.h [deleted file]
osquery/CMakeLists.txt [deleted file]
osquery/config/CMakeLists.txt [deleted file]
osquery/config/config.cpp [deleted file]
osquery/config/parsers/query_packs.cpp [deleted file]
osquery/config/parsers/tests/query_packs_tests.cpp [deleted file]
osquery/config/plugins/filesystem.cpp [deleted file]
osquery/config/tests/config_tests.cpp [deleted file]
osquery/config/update.cpp [deleted file]
osquery/core/CMakeLists.txt [deleted file]
osquery/core/conversions.cpp [deleted file]
osquery/core/conversions.h [deleted file]
osquery/core/flags.cpp [deleted file]
osquery/core/hash.cpp [deleted file]
osquery/core/init.cpp [deleted file]
osquery/core/system.cpp [deleted file]
osquery/core/tables.cpp [deleted file]
osquery/core/test_util.cpp [deleted file]
osquery/core/test_util.h [deleted file]
osquery/core/tests/conversions_tests.cpp [deleted file]
osquery/core/tests/flags_tests.cpp [deleted file]
osquery/core/tests/hash_tests.cpp [deleted file]
osquery/core/tests/status_tests.cpp [deleted file]
osquery/core/tests/tables_tests.cpp [deleted file]
osquery/core/tests/text_tests.cpp [deleted file]
osquery/core/text.cpp [deleted file]
osquery/core/watcher.cpp [deleted file]
osquery/core/watcher.h [deleted file]
osquery/database/CMakeLists.txt [deleted file]
osquery/database/database.cpp [deleted file]
osquery/database/db_handle.cpp [deleted file]
osquery/database/db_handle.h [deleted file]
osquery/database/query.cpp [deleted file]
osquery/database/query.h [deleted file]
osquery/database/tests/db_handle_tests.cpp [deleted file]
osquery/database/tests/query_tests.cpp [deleted file]
osquery/database/tests/results_tests.cpp [deleted file]
osquery/devtools/CMakeLists.txt [deleted file]
osquery/devtools/devtools.h [deleted file]
osquery/devtools/printer.cpp [deleted file]
osquery/devtools/shell.cpp [deleted file]
osquery/devtools/tests/printer_tests.cpp [deleted file]
osquery/dispatcher/CMakeLists.txt [deleted file]
osquery/dispatcher/dispatcher.cpp [deleted file]
osquery/dispatcher/dispatcher.h [deleted file]
osquery/dispatcher/scheduler.cpp [deleted file]
osquery/dispatcher/scheduler.h [deleted file]
osquery/dispatcher/tests/dispatcher_tests.cpp [deleted file]
osquery/distributed/CMakeLists.txt [deleted file]
osquery/distributed/distributed.cpp [deleted file]
osquery/distributed/distributed.h [deleted file]
osquery/distributed/tests/distributed_tests.cpp [deleted file]
osquery/events/CMakeLists.txt [deleted file]
osquery/events/events.cpp [deleted file]
osquery/events/linux/inotify.cpp [deleted file]
osquery/events/linux/inotify.h [deleted file]
osquery/events/linux/tests/inotify_tests.cpp [deleted file]
osquery/events/linux/udev.cpp [deleted file]
osquery/events/linux/udev.h [deleted file]
osquery/events/tests/events_database_tests.cpp [deleted file]
osquery/events/tests/events_tests.cpp [deleted file]
osquery/examples/example_extension.cpp [deleted file]
osquery/examples/example_module.cpp [deleted file]
osquery/extensions/CMakeLists.txt [deleted file]
osquery/extensions/extensions.cpp [deleted file]
osquery/extensions/interface.cpp [deleted file]
osquery/extensions/interface.h [deleted file]
osquery/extensions/tests/extensions_tests.cpp [deleted file]
osquery/filesystem/CMakeLists.txt [deleted file]
osquery/filesystem/filesystem.cpp [deleted file]
osquery/filesystem/linux/mem.cpp [deleted file]
osquery/filesystem/linux/proc.cpp [deleted file]
osquery/filesystem/tests/filesystem_tests.cpp [deleted file]
osquery/logger/CMakeLists.txt [deleted file]
osquery/logger/logger.cpp [deleted file]
osquery/logger/plugins/filesystem.cpp [deleted file]
osquery/logger/plugins/syslog.cpp [deleted file]
osquery/logger/tests/logger_tests.cpp [deleted file]
osquery/main/daemon.cpp [deleted file]
osquery/main/empty.cpp [deleted file]
osquery/main/lib.cpp [deleted file]
osquery/main/run.cpp [deleted file]
osquery/main/shell.cpp [deleted file]
osquery/main/tests.cpp [deleted file]
osquery/registry/CMakeLists.txt [deleted file]
osquery/registry/registry.cpp [deleted file]
osquery/registry/tests/registry_tests.cpp [deleted file]
osquery/sql/CMakeLists.txt [deleted file]
osquery/sql/sql.cpp [deleted file]
osquery/sql/sqlite_util.cpp [deleted file]
osquery/sql/sqlite_util.h [deleted file]
osquery/sql/tests/sql_tests.cpp [deleted file]
osquery/sql/tests/sqlite_util_tests.cpp [deleted file]
osquery/sql/tests/virtual_table_tests.cpp [deleted file]
osquery/sql/virtual_table.cpp [deleted file]
osquery/sql/virtual_table.h [deleted file]
osquery/tables/CMakeLists.txt [deleted file]
osquery/tables/events/linux/file_events.cpp [deleted file]
osquery/tables/events/linux/hardware_events.cpp [deleted file]
osquery/tables/events/linux/passwd_changes.cpp [deleted file]
osquery/tables/networking/etc_hosts.cpp [deleted file]
osquery/tables/networking/etc_protocols.cpp [deleted file]
osquery/tables/networking/etc_services.cpp [deleted file]
osquery/tables/networking/linux/arp_cache.cpp [deleted file]
osquery/tables/networking/linux/inet_diag.h [deleted file]
osquery/tables/networking/linux/ip_tables.h [deleted file]
osquery/tables/networking/linux/iptables.cpp [deleted file]
osquery/tables/networking/linux/libiptc.h [deleted file]
osquery/tables/networking/linux/process_open_sockets.cpp [deleted file]
osquery/tables/networking/linux/routes.cpp [deleted file]
osquery/tables/networking/linux/tests/iptables_tests.cpp [deleted file]
osquery/tables/networking/listening_ports.cpp [deleted file]
osquery/tables/networking/tests/etc_hosts_tests.cpp [deleted file]
osquery/tables/networking/tests/etc_protocols_tests.cpp [deleted file]
osquery/tables/networking/utils.cpp [deleted file]
osquery/tables/networking/utils.h [deleted file]
osquery/tables/system/crontab.cpp [deleted file]
osquery/tables/system/last.cpp [deleted file]
osquery/tables/system/linux/acpi_tables.cpp [deleted file]
osquery/tables/system/linux/groups.cpp [deleted file]
osquery/tables/system/linux/kernel_info.cpp [deleted file]
osquery/tables/system/linux/kernel_modules.cpp [deleted file]
osquery/tables/system/linux/memory_map.cpp [deleted file]
osquery/tables/system/linux/model_specific_register.cpp [deleted file]
osquery/tables/system/linux/mounts.cpp [deleted file]
osquery/tables/system/linux/os_version.cpp [deleted file]
osquery/tables/system/linux/pci_devices.cpp [deleted file]
osquery/tables/system/linux/process_open_files.cpp [deleted file]
osquery/tables/system/linux/processes.cpp [deleted file]
osquery/tables/system/linux/shared_memory.cpp [deleted file]
osquery/tables/system/linux/smbios_tables.cpp [deleted file]
osquery/tables/system/linux/sysctl_utils.cpp [deleted file]
osquery/tables/system/linux/usb_devices.cpp [deleted file]
osquery/tables/system/linux/user_groups.cpp [deleted file]
osquery/tables/system/linux/users.cpp [deleted file]
osquery/tables/system/logged_in_users.cpp [deleted file]
osquery/tables/system/shell_history.cpp [deleted file]
osquery/tables/system/smbios_utils.cpp [deleted file]
osquery/tables/system/smbios_utils.h [deleted file]
osquery/tables/system/suid_bin.cpp [deleted file]
osquery/tables/system/sysctl_utils.h [deleted file]
osquery/tables/system/system_controls.cpp [deleted file]
osquery/tables/system/uptime.cpp [deleted file]
osquery/tables/system/user_groups.h [deleted file]
osquery/tables/utility/file.cpp [deleted file]
osquery/tables/utility/hash.cpp [deleted file]
osquery/tables/utility/osquery.cpp [deleted file]
osquery/tables/utility/time.cpp [deleted file]
osquery/tizen/CMakeLists.txt [deleted file]
osquery/tizen/manager/manager.cpp [deleted file]
osquery/tizen/manager/manager_impl.cpp [deleted file]
osquery/tizen/manager/manager_impl.h [deleted file]
osquery/tizen/manager/tests/manager_tests.cpp [deleted file]
osquery/tizen/notification/notification.cpp [deleted file]
osquery/tizen/notification/tests/notification_tests.cpp [deleted file]
osquery/tizen/property/property.cpp [deleted file]
osquery/tizen/property/tests/property_tests.cpp [deleted file]
osquery/tizen/tables/bluetooth_policy.cpp [deleted file]
osquery/tizen/tables/tests/policy_tests.cpp [deleted file]
osquery/tizen/tables/usb_policy.cpp [deleted file]
osquery/tizen/tables/wifi_policy.cpp [deleted file]
osquery/tizen/tsqb/include/column-pack.hxx [deleted file]
osquery/tizen/tsqb/include/column.hxx [deleted file]
osquery/tizen/tsqb/include/condition.hxx [deleted file]
osquery/tizen/tsqb/include/crud.hxx [deleted file]
osquery/tizen/tsqb/include/database.hxx [deleted file]
osquery/tizen/tsqb/include/expression.hxx [deleted file]
osquery/tizen/tsqb/include/table-pack.hxx [deleted file]
osquery/tizen/tsqb/include/table.hxx [deleted file]
osquery/tizen/tsqb/include/tuple-helper.hxx [deleted file]
osquery/tizen/tsqb/include/type.hxx [deleted file]
osquery/tizen/tsqb/include/util.hxx [deleted file]
osquery/tizen/tsqb/tests/tsqb-tests.cpp [deleted file]
osquery/tizen/tsqb/tsqb.hxx [deleted file]
packaging/osquery.spec
plugins/CMakeLists.txt
plugins/bluetooth/CMakeLists.txt
plugins/wifi/CMakeLists.txt
src/CMakeLists.txt [new file with mode: 0644]
src/apix/CMakeLists.txt [new file with mode: 0644]
src/apix/main/tests.cpp [new file with mode: 0644]
src/apix/manager/CMakeLists.txt [new file with mode: 0644]
src/apix/manager/manager.cpp [new file with mode: 0644]
src/apix/manager/manager_impl.cpp [new file with mode: 0644]
src/apix/manager/manager_impl.h [new file with mode: 0644]
src/apix/manager/tests/manager_tests.cpp [new file with mode: 0644]
src/apix/notification/CMakeLists.txt [new file with mode: 0644]
src/apix/notification/notification.cpp [new file with mode: 0644]
src/apix/notification/tests/notification_tests.cpp [new file with mode: 0644]
src/apix/property/CMakeLists.txt [new file with mode: 0644]
src/apix/property/property.cpp [new file with mode: 0644]
src/apix/property/tests/property_tests.cpp [new file with mode: 0644]
src/apix/tsqb/CMakeLists.txt [new file with mode: 0644]
src/apix/tsqb/include/column-pack.hxx [new file with mode: 0644]
src/apix/tsqb/include/column.hxx [new file with mode: 0644]
src/apix/tsqb/include/condition.hxx [new file with mode: 0644]
src/apix/tsqb/include/crud.hxx [new file with mode: 0644]
src/apix/tsqb/include/database.hxx [new file with mode: 0644]
src/apix/tsqb/include/expression.hxx [new file with mode: 0644]
src/apix/tsqb/include/table-pack.hxx [new file with mode: 0644]
src/apix/tsqb/include/table.hxx [new file with mode: 0644]
src/apix/tsqb/include/tuple-helper.hxx [new file with mode: 0644]
src/apix/tsqb/include/type.hxx [new file with mode: 0644]
src/apix/tsqb/include/util.hxx [new file with mode: 0644]
src/apix/tsqb/tests/tsqb-tests.cpp [new file with mode: 0644]
src/apix/tsqb/tsqb.hxx [new file with mode: 0644]
src/osquery/CMakeLists.txt [new file with mode: 0644]
src/osquery/config/CMakeLists.txt [new file with mode: 0644]
src/osquery/config/config.cpp [new file with mode: 0644]
src/osquery/config/parsers/query_packs.cpp [new file with mode: 0644]
src/osquery/config/parsers/tests/query_packs_tests.cpp [new file with mode: 0644]
src/osquery/config/plugins/filesystem.cpp [new file with mode: 0644]
src/osquery/config/tests/config_tests.cpp [new file with mode: 0644]
src/osquery/config/update.cpp [new file with mode: 0644]
src/osquery/core/CMakeLists.txt [new file with mode: 0644]
src/osquery/core/conversions.cpp [new file with mode: 0644]
src/osquery/core/conversions.h [new file with mode: 0644]
src/osquery/core/flags.cpp [new file with mode: 0644]
src/osquery/core/hash.cpp [new file with mode: 0644]
src/osquery/core/init.cpp [new file with mode: 0644]
src/osquery/core/system.cpp [new file with mode: 0644]
src/osquery/core/tables.cpp [new file with mode: 0644]
src/osquery/core/test_util.cpp [new file with mode: 0644]
src/osquery/core/test_util.h [new file with mode: 0644]
src/osquery/core/tests/conversions_tests.cpp [new file with mode: 0644]
src/osquery/core/tests/flags_tests.cpp [new file with mode: 0644]
src/osquery/core/tests/hash_tests.cpp [new file with mode: 0644]
src/osquery/core/tests/status_tests.cpp [new file with mode: 0644]
src/osquery/core/tests/tables_tests.cpp [new file with mode: 0644]
src/osquery/core/tests/text_tests.cpp [new file with mode: 0644]
src/osquery/core/text.cpp [new file with mode: 0644]
src/osquery/core/watcher.cpp [new file with mode: 0644]
src/osquery/core/watcher.h [new file with mode: 0644]
src/osquery/database/CMakeLists.txt [new file with mode: 0644]
src/osquery/database/database.cpp [new file with mode: 0644]
src/osquery/database/db_handle.cpp [new file with mode: 0644]
src/osquery/database/db_handle.h [new file with mode: 0644]
src/osquery/database/query.cpp [new file with mode: 0644]
src/osquery/database/query.h [new file with mode: 0644]
src/osquery/database/tests/db_handle_tests.cpp [new file with mode: 0644]
src/osquery/database/tests/query_tests.cpp [new file with mode: 0644]
src/osquery/database/tests/results_tests.cpp [new file with mode: 0644]
src/osquery/devtools/CMakeLists.txt [new file with mode: 0644]
src/osquery/devtools/devtools.h [new file with mode: 0644]
src/osquery/devtools/printer.cpp [new file with mode: 0644]
src/osquery/devtools/shell.cpp [new file with mode: 0644]
src/osquery/devtools/tests/printer_tests.cpp [new file with mode: 0644]
src/osquery/dispatcher/CMakeLists.txt [new file with mode: 0644]
src/osquery/dispatcher/dispatcher.cpp [new file with mode: 0644]
src/osquery/dispatcher/dispatcher.h [new file with mode: 0644]
src/osquery/dispatcher/scheduler.cpp [new file with mode: 0644]
src/osquery/dispatcher/scheduler.h [new file with mode: 0644]
src/osquery/dispatcher/tests/dispatcher_tests.cpp [new file with mode: 0644]
src/osquery/distributed/CMakeLists.txt [new file with mode: 0644]
src/osquery/distributed/distributed.cpp [new file with mode: 0644]
src/osquery/distributed/distributed.h [new file with mode: 0644]
src/osquery/distributed/tests/distributed_tests.cpp [new file with mode: 0644]
src/osquery/events/CMakeLists.txt [new file with mode: 0644]
src/osquery/events/events.cpp [new file with mode: 0644]
src/osquery/events/linux/inotify.cpp [new file with mode: 0644]
src/osquery/events/linux/inotify.h [new file with mode: 0644]
src/osquery/events/linux/tests/inotify_tests.cpp [new file with mode: 0644]
src/osquery/events/linux/udev.cpp [new file with mode: 0644]
src/osquery/events/linux/udev.h [new file with mode: 0644]
src/osquery/events/tests/events_database_tests.cpp [new file with mode: 0644]
src/osquery/events/tests/events_tests.cpp [new file with mode: 0644]
src/osquery/examples/example_extension.cpp [new file with mode: 0644]
src/osquery/examples/example_module.cpp [new file with mode: 0644]
src/osquery/extensions/CMakeLists.txt [new file with mode: 0644]
src/osquery/extensions/extensions.cpp [new file with mode: 0644]
src/osquery/extensions/interface.cpp [new file with mode: 0644]
src/osquery/extensions/interface.h [new file with mode: 0644]
src/osquery/extensions/tests/extensions_tests.cpp [new file with mode: 0644]
src/osquery/filesystem/CMakeLists.txt [new file with mode: 0644]
src/osquery/filesystem/filesystem.cpp [new file with mode: 0644]
src/osquery/filesystem/linux/mem.cpp [new file with mode: 0644]
src/osquery/filesystem/linux/proc.cpp [new file with mode: 0644]
src/osquery/filesystem/tests/filesystem_tests.cpp [new file with mode: 0644]
src/osquery/include/osquery/config.h [new file with mode: 0644]
src/osquery/include/osquery/core.h [new file with mode: 0644]
src/osquery/include/osquery/database.h [new file with mode: 0644]
src/osquery/include/osquery/database/results.h [new file with mode: 0644]
src/osquery/include/osquery/events.h [new file with mode: 0644]
src/osquery/include/osquery/extensions.h [new file with mode: 0644]
src/osquery/include/osquery/filesystem.h [new file with mode: 0644]
src/osquery/include/osquery/flags.h [new file with mode: 0644]
src/osquery/include/osquery/hash.h [new file with mode: 0644]
src/osquery/include/osquery/logger.h [new file with mode: 0644]
src/osquery/include/osquery/notification.h [new file with mode: 0644]
src/osquery/include/osquery/registry.h [new file with mode: 0644]
src/osquery/include/osquery/sdk.h [new file with mode: 0644]
src/osquery/include/osquery/sql.h [new file with mode: 0644]
src/osquery/include/osquery/status.h [new file with mode: 0644]
src/osquery/include/osquery/tables.h [new file with mode: 0644]
src/osquery/logger/CMakeLists.txt [new file with mode: 0644]
src/osquery/logger/logger.cpp [new file with mode: 0644]
src/osquery/logger/plugins/filesystem.cpp [new file with mode: 0644]
src/osquery/logger/plugins/syslog.cpp [new file with mode: 0644]
src/osquery/logger/tests/logger_tests.cpp [new file with mode: 0644]
src/osquery/main/empty.cpp [new file with mode: 0644]
src/osquery/main/lib.cpp [new file with mode: 0644]
src/osquery/main/run.cpp [new file with mode: 0644]
src/osquery/main/shell.cpp [new file with mode: 0644]
src/osquery/main/tests.cpp [new file with mode: 0644]
src/osquery/registry/CMakeLists.txt [new file with mode: 0644]
src/osquery/registry/registry.cpp [new file with mode: 0644]
src/osquery/registry/tests/registry_tests.cpp [new file with mode: 0644]
src/osquery/sql/CMakeLists.txt [new file with mode: 0644]
src/osquery/sql/sql.cpp [new file with mode: 0644]
src/osquery/sql/sqlite_util.cpp [new file with mode: 0644]
src/osquery/sql/sqlite_util.h [new file with mode: 0644]
src/osquery/sql/tests/sql_tests.cpp [new file with mode: 0644]
src/osquery/sql/tests/sqlite_util_tests.cpp [new file with mode: 0644]
src/osquery/sql/tests/virtual_table_tests.cpp [new file with mode: 0644]
src/osquery/sql/virtual_table.cpp [new file with mode: 0644]
src/osquery/sql/virtual_table.h [new file with mode: 0644]
src/osquery/tables/CMakeLists.txt [new file with mode: 0644]
src/osquery/tables/events/linux/file_events.cpp [new file with mode: 0644]
src/osquery/tables/events/linux/hardware_events.cpp [new file with mode: 0644]
src/osquery/tables/events/linux/passwd_changes.cpp [new file with mode: 0644]
src/osquery/tables/networking/etc_hosts.cpp [new file with mode: 0644]
src/osquery/tables/networking/etc_protocols.cpp [new file with mode: 0644]
src/osquery/tables/networking/etc_services.cpp [new file with mode: 0644]
src/osquery/tables/networking/linux/arp_cache.cpp [new file with mode: 0644]
src/osquery/tables/networking/linux/inet_diag.h [new file with mode: 0644]
src/osquery/tables/networking/linux/ip_tables.h [new file with mode: 0644]
src/osquery/tables/networking/linux/iptables.cpp [new file with mode: 0644]
src/osquery/tables/networking/linux/libiptc.h [new file with mode: 0644]
src/osquery/tables/networking/linux/process_open_sockets.cpp [new file with mode: 0644]
src/osquery/tables/networking/linux/routes.cpp [new file with mode: 0644]
src/osquery/tables/networking/linux/tests/iptables_tests.cpp [new file with mode: 0644]
src/osquery/tables/networking/listening_ports.cpp [new file with mode: 0644]
src/osquery/tables/networking/tests/etc_hosts_tests.cpp [new file with mode: 0644]
src/osquery/tables/networking/tests/etc_protocols_tests.cpp [new file with mode: 0644]
src/osquery/tables/networking/utils.cpp [new file with mode: 0644]
src/osquery/tables/networking/utils.h [new file with mode: 0644]
src/osquery/tables/system/crontab.cpp [new file with mode: 0644]
src/osquery/tables/system/last.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/acpi_tables.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/groups.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/kernel_info.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/kernel_modules.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/memory_map.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/model_specific_register.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/mounts.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/os_version.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/pci_devices.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/process_open_files.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/processes.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/shared_memory.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/smbios_tables.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/sysctl_utils.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/usb_devices.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/user_groups.cpp [new file with mode: 0644]
src/osquery/tables/system/linux/users.cpp [new file with mode: 0644]
src/osquery/tables/system/logged_in_users.cpp [new file with mode: 0644]
src/osquery/tables/system/shell_history.cpp [new file with mode: 0644]
src/osquery/tables/system/smbios_utils.cpp [new file with mode: 0644]
src/osquery/tables/system/smbios_utils.h [new file with mode: 0644]
src/osquery/tables/system/suid_bin.cpp [new file with mode: 0644]
src/osquery/tables/system/sysctl_utils.h [new file with mode: 0644]
src/osquery/tables/system/system_controls.cpp [new file with mode: 0644]
src/osquery/tables/system/uptime.cpp [new file with mode: 0644]
src/osquery/tables/system/user_groups.h [new file with mode: 0644]
src/osquery/tables/utility/file.cpp [new file with mode: 0644]
src/osquery/tables/utility/hash.cpp [new file with mode: 0644]
src/osquery/tables/utility/osquery.cpp [new file with mode: 0644]
src/osquery/tables/utility/time.cpp [new file with mode: 0644]

index 4cf3eb9b213ca307bbc03ea6a6e87944d14abe88..e67c9f099dc4bfd3b3ac515deefd8bb9d026b12d 100644 (file)
@@ -12,6 +12,7 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License
 
+## osquery #####################
 MACRO(ADD_OSQUERY_LIBRARY TARGET)
        ADD_LIBRARY(${TARGET} OBJECT ${ARGN})
        LIST(APPEND ${TARGET_OSQUERY_LIB}_SRCS $<TARGET_OBJECTS:${TARGET}>)
@@ -28,16 +29,28 @@ MACRO(ADD_OSQUERY_LINK)
        SET(${TARGET_OSQUERY_LIB}_DEPS ${${TARGET_OSQUERY_LIB}_DEPS} PARENT_SCOPE)
 ENDMACRO(ADD_OSQUERY_LINK)
 
-MACRO(TARGET_OSQUERY_LINK_WHOLE TARGET LIBRARY)
+## apix  #####################
+MACRO(ADD_APIX_LIBRARY TARGET)
+       ADD_LIBRARY(${TARGET} OBJECT ${ARGN})
+       LIST(APPEND ${TARGET_APIX_LIB}_SRCS $<TARGET_OBJECTS:${TARGET}>)
+       SET(${TARGET_APIX_LIB}_SRCS ${${TARGET_APIX_LIB}_SRCS} PARENT_SCOPE)
+ENDMACRO(ADD_APIX_LIBRARY)
+
+MACRO(ADD_APIX_TEST)
+       LIST(APPEND ${TARGET_APIX_LIB}_TESTS ${ARGN})
+       SET(${TARGET_APIX_LIB}_TESTS ${${TARGET_APIX_LIB}_TESTS} PARENT_SCOPE)
+ENDMACRO(ADD_APIX_TEST)
+
+MACRO(ADD_APIX_LINK)
+       LIST(APPEND ${TARGET_APIX_LIB}_DEPS ${ARGN})
+       SET(${TARGET_APIX_LIB}_DEPS ${${TARGET_APIX_LIB}_DEPS} PARENT_SCOPE)
+ENDMACRO(ADD_APIX_LINK)
+
+## common #############################
+MACRO(TARGET_LINK_WHOLE TARGET LIBRARY)
        TARGET_LINK_LIBRARIES(${TARGET} "-Wl,-whole-archive")
        TARGET_LINK_LIBRARIES(${TARGET} ${LIBRARY})
        TARGET_LINK_LIBRARIES(${TARGET} "-Wl,-no-whole-archive")
-ENDMACRO(TARGET_OSQUERY_LINK_WHOLE)
-
-MACRO(ADD_OSQUERY_MODULE TARGET)
-       ADD_LIBRARY(${TARGET} SHARED ${ARGN})
-       TARGET_LINK_LIBRARIES(${TARGET} dl)
-       ADD_DEPENDENCIES(${TARGET} ${TARGET_OSQUERY_LIB} glog)
-       SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_FLAGS "-fPIC")
-       SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME ${TARGET})
-ENDMACRO(ADD_OSQUERY_MODULE)
+ENDMACRO(TARGET_LINK_WHOLE)
+
+
index f67f781f8d67a45e9a7d132b00e0b9b1cfff2ad2..c3d1ed50424ebdf418dcbe8c749591f1894555cd 100644 (file)
@@ -44,9 +44,9 @@ ADD_DEFINITIONS(-DOSQUERY_BUILD_VERSION=${OSQUERY_BUILD_VERSION}
 
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}")
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/api")
-INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include")
 INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/tools/sqlite3")
-INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/osquery/tizen/tsqb")
+INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/osquery/include")
+INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/apix/tsqb")
 INCLUDE_DIRECTORIES("/usr/local/include")
 
 ENABLE_TESTING()
@@ -57,9 +57,9 @@ INCLUDE(CMake/Thrift.cmake)
 # Make sure the generated paths exist
 EXECUTE_PROCESS(COMMAND mkdir -p "${CMAKE_BINARY_DIR}/generated")
 
-ADD_SUBDIRECTORY(osquery)
+ADD_SUBDIRECTORY(specs)
+ADD_SUBDIRECTORY(src)
 ADD_SUBDIRECTORY(tools/sqlite3)
-
 IF(DEFINED GBS_BUILD)
        ADD_SUBDIRECTORY(plugins)
 ENDIF()
diff --git a/include/osquery/config.h b/include/osquery/config.h
deleted file mode 100644 (file)
index 38c9002..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <memory>
-#include <vector>
-
-#include <boost/noncopyable.hpp>
-#include <boost/property_tree/ptree.hpp>
-#include <boost/property_tree/json_parser.hpp>
-#include <boost/thread/shared_mutex.hpp>
-
-#include <osquery/database.h>
-#include <osquery/flags.h>
-#include <osquery/registry.h>
-#include <osquery/status.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-/// The builder or invoker may change the default config plugin.
-DECLARE_string(config_plugin);
-
-/**
- * @brief The osquery config is updated names sources containing JSON.
- *
- * A ConfigSourceMap is a named mapping from source (the key) to a JSON blob.
- * This map is generated by a ConfigPlugin an provided to the Config via an
- * update call. ConfigPlugin%s may update the Config asynchronously.
- *
- * The osquery Config instance will perform source merging by amalgamating
- * the JSON literal types (lists and maps) for well known top-level keys.
- * The merging will happen in lexicographical order based on source name.
- */
-typedef std::map<std::string, std::string> ConfigSourceMap;
-
-/**
- * @brief A native representation of osquery configuration data.
- *
- * When you use osquery::Config::getInstance(), you are getting a singleton
- * handle to interact with the data stored in an instance of this struct.
- */
-struct ConfigData {
-  /// A vector of all of the queries that are scheduled to execute.
-  std::map<std::string, ScheduledQuery> schedule;
-  std::map<std::string, std::string> options;
-  std::map<std::string, std::vector<std::string> > files;
-  /// All data catches optional/plugin-parsed configuration keys.
-  pt::ptree all_data;
-};
-
-class ConfigParserPlugin;
-typedef std::shared_ptr<ConfigParserPlugin> ConfigPluginRef;
-
-/**
- * @brief A singleton that exposes accessors to osquery's configuration data.
- *
- * osquery has two types on configurations. Things that don't change during
- * the execution of the process should be configured as command-line
- * arguments. Things that can change during the lifetime of program execution
- * should be defined using the osquery::config::Config class and the pluggable
- * plugin interface that is included with it.
- */
-class Config : private boost::noncopyable {
- public:
-  /**
-   * @brief The primary way to access the Config singleton.
-   *
-   * osquery::config::Config::getInstance() provides access to the Config
-   * singleton
-   *
-   * @code{.cpp}
-   *   auto config = osquery::config::Config::getInstance();
-   * @endcode
-   *
-   * @return a singleton instance of Config.
-   */
-  static Config& getInstance() {
-    static Config cfg;
-    return cfg;
-  }
-
-  /**
-   * @brief Call the genConfig method of the config retriever plugin.
-   *
-   * This may perform a resource load such as TCP request or filesystem read.
-   */
-  static Status load();
-
-  /**
-   * @brief Update the internal config data.
-   *
-   * @param config A map of domain or namespace to config data.
-   * @return If the config changes were applied.
-   */
-  static Status update(const ConfigSourceMap& config);
-
-  /**
-   * @brief Calculate the has of the osquery config
-   *
-   * @return The MD5 of the osquery config
-   */
-  static Status getMD5(std::string& hashString);
-
-  /**
-   * @brief Adds a new query to the scheduled queries.
-   *
-   */
-  static void addScheduledQuery(const std::string& name,
-                                const std::string& query,
-                                int interval);
-
-  /**
-   * @brief Checks if a query exists in the query schedule.
-   *
-   */
-  static bool checkScheduledQuery(const std::string& query);
-
-  /**
-   * @brief Checks if the query name exists in the query schedule.
-   *
-   */
-  static bool checkScheduledQueryName(const std::string& query_name);
-
-  /**
-   * @brief Check to ensure that the config is accessible and properly
-   * formatted
-   *
-   * @return an instance of osquery::Status, indicating the success or failure
-   * of the operation.
-   */
-  static Status checkConfig();
-
- private:
-  /**
-   * @brief Default constructor.
-   *
-   * Since instances of Config should only be created via getInstance(),
-   * Config's constructor is private
-   */
-  Config() : force_merge_success_(false) {}
-  ~Config(){}
-  Config(Config const&);
-  void operator=(Config const&);
-
-  /**
-   * @brief Uses the specified config retriever to populate a string with the
-   * config JSON.
-   *
-   * Internally, genConfig checks to see if there was a config retriever
-   * specified on the command-line. If there was, it checks to see if that
-   * config retriever actually exists. If it does, it gets used to generate
-   * configuration data. If it does not, an error is logged.
-   *
-   * @return status indicating the success or failure of the operation.
-   */
-  static Status genConfig();
-
-  /// Merge a retrieved config source JSON into a working ConfigData.
-  static Status mergeConfig(const std::string& source, ConfigData& conf);
-
- public:
-  /**
-   * @brief Record performance (monitoring) information about a scheduled query.
-   *
-   * The daemon and query scheduler will optionally record process metadata
-   * before and after executing each query. This can be compared and reported
-   * on an interval or within the osquery_schedule table.
-   *
-   * The config consumes and calculates the optional performance differentials.
-   * It would also be possible to store this in the RocksDB backing store or
-   * report directly to a LoggerPlugin sink. The Config is the most appropriate
-   * as the metrics are transient to the process running the schedule and apply
-   * to the updates/changes reflected in the schedule, from the config.
-   *
-   * @param name The unique name of the scheduled item
-   * @param delay Number of seconds (wall time) taken by the query
-   * @param size Number of characters generated by query
-   * @param t0 the process row before the query
-   * @param t1 the process row after the query
-   */
-  static void recordQueryPerformance(const std::string& name,
-                                     size_t delay,
-                                     size_t size,
-                                     const Row& t0,
-                                     const Row& t1);
-
- private:
-  /// The raw osquery config data in a native format
-  ConfigData data_;
-
-  /// The raw JSON source map from the config plugin.
-  std::map<std::string, std::string> raw_;
-
-  /// The reader/writer config data mutex.
-  boost::shared_mutex mutex_;
-
-  /// Enforce merge success.
-  bool force_merge_success_;
-
- private:
-  /**
-   * @brief A ConfigDataInstance requests read-only access to ConfigParser data.
-   *
-   * A ConfigParser plugin will receive several top-level-config keys and
-   * optionally parse and store information. That information is a property tree
-   * called ConfigParser::data_. Use ConfigDataInstance::getParsedData to
-   * retrieve read-only access to this data.
-   *
-   * @param parser The name of the config parser.
-   */
-  static const pt::ptree& getParsedData(const std::string& parser);
-
-  /// See getParsedData but request access to the parser plugin.
-  static const ConfigPluginRef getParser(const std::string& parser);
-
-  /// A default, empty property tree used when a missing parser is requested.
-  pt::ptree empty_data_;
-
- private:
-  /// Config accessors, `ConfigDataInstance`, are the forced use of the config
-  /// data. This forces the caller to use a shared read lock.
-  friend class ConfigDataInstance;
-
- private:
-  FRIEND_TEST(ConfigTests, test_locking);
-};
-
-/**
- * @brief All accesses to the Config's data must request a ConfigDataInstance.
- *
- * This class will request a read-only lock of the config's changeable internal
- * data structures such as query schedule, options, monitored files, etc.
- *
- * Since a variable config plugin may implement `update` calls, internal uses
- * of config data needs simple read and write locking.
- */
-class ConfigDataInstance {
- public:
-  ConfigDataInstance() : lock_(Config::getInstance().mutex_) {}
-  ~ConfigDataInstance() { lock_.unlock(); }
-
-  /// Helper accessor for Config::data_.schedule.
-  const std::map<std::string, ScheduledQuery> schedule() const {
-    return Config::getInstance().data_.schedule;
-  }
-
-  /// Helper accessor for Config::data_.options.
-  const std::map<std::string, std::string>& options() const {
-    return Config::getInstance().data_.options;
-  }
-
-  /// Helper accessor for Config::data_.files.
-  const std::map<std::string, std::vector<std::string> >& files() const {
-    return Config::getInstance().data_.files;
-  }
-
-  const pt::ptree& getParsedData(const std::string& parser) const {
-    return Config::getParsedData(parser);
-  }
-
-  const ConfigPluginRef getParser(const std::string& parser) const {
-    return Config::getParser(parser);
-  }
-
-  /// Helper accessor for Config::data_.all_data.
-  const pt::ptree& data() const { return Config::getInstance().data_.all_data; }
-
- private:
-  /**
-   * @brief ConfigParser plugin's may update the internal config representation.
-   *
-   * If the config parser reads and calculates new information it should store
-   * that derived data itself and rely on ConfigDataInstance::getParsedData.
-   * This means another plugin is aware of the ConfigParser and knowns to make
-   * getParsedData calls. If the parser is augmenting/changing internal state,
-   * such as modifying the osquery schedule or options, then it must write
-   * changed back into the default data.
-   *
-   * Note that this returns the ConfigData instance, not the raw property tree.
-   */
-  ConfigData& mutableConfigData() { return Config::getInstance().data_; }
-
- private:
-  /// A read lock on the reader/writer config data accessor/update mutex.
-  boost::shared_lock<boost::shared_mutex> lock_;
-
- private:
-  friend class ConfigParserPlugin;
-};
-
-/**
- * @brief Superclass for the pluggable config component.
- *
- * In order to make the distribution of configurations to hosts running
- * osquery, we take advantage of a plugin interface which allows you to
- * integrate osquery with your internal configuration distribution mechanisms.
- * You may use ZooKeeper, files on disk, a custom solution, etc. In order to
- * use your specific configuration distribution system, one simply needs to
- * create a custom subclass of ConfigPlugin. That subclass should implement
- * the ConfigPlugin::genConfig method.
- *
- * Consider the following example:
- *
- * @code{.cpp}
- *   class TestConfigPlugin : public ConfigPlugin {
- *    public:
- *     virtual std::pair<osquery::Status, std::string> genConfig() {
- *       std::string config;
- *       auto status = getMyConfig(config);
- *       return std::make_pair(status, config);
- *     }
- *   };
- *
- *   REGISTER(TestConfigPlugin, "config", "test");
- *  @endcode
- */
-class ConfigPlugin : public Plugin {
- public:
-  /**
-   * @brief Virtual method which should implemented custom config retrieval
-   *
-   * ConfigPlugin::genConfig should be implemented by a subclasses of
-   * ConfigPlugin which needs to retrieve config data in a custom way.
-   *
-   * @param config The output ConfigSourceMap, a map of JSON to source names.
-   * @return A failure status will prevent the source map from merging.
-   */
-  virtual Status genConfig(ConfigSourceMap& config) = 0;
-  Status call(const PluginRequest& request, PluginResponse& response);
-};
-
-/// Helper merged and parsed property tree.
-typedef pt::ptree ConfigTree;
-
-/// Helper for a map of requested keys to their merged and parsed property tree.
-typedef std::map<std::string, ConfigTree> ConfigTreeMap;
-
-/**
- * @brief A pluggable configuration parser.
- *
- * An osquery config instance is populated from JSON using a ConfigPlugin.
- * That plugin may update the config data asynchronously and read from
- * several sources, as is the case with "filesystem" and reading multiple files.
- *
- * A ConfigParserPlugin will receive the merged configuration at osquery start
- * and the updated (still merged) config if any ConfigPlugin updates the
- * instance asynchronously. Each parser specifies a set of top-level JSON
- * keys to receive. The config instance will auto-merge the key values
- * from multiple sources if they are dictionaries or lists.
- *
- * If a top-level key is a dictionary, each source with the top-level key
- * will have its own dictionary keys merged and replaced based on the lexical
- * order of sources. For the "filesystem" config plugin this is the lexical
- * sorting of filenames. If the top-level key is a list, each source with the
- * top-level key will have its contents appended.
- *
- * Each config parser plugin will live alongside the config instance for the
- * life of the osquery process. The parser may perform actions at config load
- * and config update "time" as well as keep its own data members and be
- * accessible through the Config class API.
- */
-class ConfigParserPlugin : public Plugin {
- protected:
-  /**
-   * @brief Return a list of top-level config keys to receive in updates.
-   *
-   * The ::update method will receive a map of these keys with a JSON-parsed
-   * property tree of configuration data.
-   *
-   * @return A list of string top-level JSON keys.
-   */
-  virtual std::vector<std::string> keys() = 0;
-
-  /**
-   * @brief Receive a merged property tree for each top-level config key.
-   *
-   * Called when the Config instance is initially loaded with data from the
-   * active config plugin and when it is updated via an async ConfigPlugin
-   * update. Every config parser will receive a map of merged data for each key
-   * they requested in keys().
-   *
-   * @param config A JSON-parsed property tree map.
-   * @return Failure if the parser should no longer receive updates.
-   */
-  virtual Status update(const ConfigTreeMap& config) = 0;
-
- protected:
-  /// Mutable config data accessor for ConfigParser%s.
-  ConfigData& mutableConfigData(ConfigDataInstance& cdi) {
-    return cdi.mutableConfigData();
-  }
-
- protected:
-  /// Allow the config parser to keep some global state.
-  pt::ptree data_;
-
- private:
-  Status setUp();
-
- private:
-  /// Config::update will call all appropriate parser updates.
-  friend class Config;
-  /// A config data instance implements a read/write lock around data_ access.
-  friend class ConfigDataInstance;
-};
-
-/**
- * @brief Calculate a splayed integer based on a variable splay percentage
- *
- * The value of splayPercent must be between 1 and 100. If it's not, the
- * value of original will be returned.
- *
- * @param original The original value to be modified
- * @param splayPercent The percent in which to splay the original value by
- *
- * @return The modified version of original
- */
-int splayValue(int original, int splayPercent);
-
-/**
- * @brief Config plugin registry.
- *
- * This creates an osquery registry for "config" which may implement
- * ConfigPlugin. A ConfigPlugin's call API should make use of a genConfig
- * after reading JSON data in the plugin implementation.
- */
-CREATE_REGISTRY(ConfigPlugin, "config");
-
-/**
- * @brief ConfigParser plugin registry.
- *
- * This creates an osquery registry for "config_parser" which may implement
- * ConfigParserPlugin. A ConfigParserPlugin should not export any call actions
- * but rather have a simple property tree-accessor API through Config.
- */
-CREATE_LAZY_REGISTRY(ConfigParserPlugin, "config_parser");
-}
diff --git a/include/osquery/core.h b/include/osquery/core.h
deleted file mode 100644 (file)
index 30260da..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include <osquery/status.h>
-
-// clang-format off
-#ifndef STR
-#define STR_OF(x) #x
-#define STR(x) STR_OF(x)
-#endif
-#define STR_EX(x) x
-#define CONCAT(x, y) STR(STR_EX(x)STR_EX(y))
-
-#ifndef FRIEND_TEST
-#define FRIEND_TEST(test_case_name, test_name) \
-  friend class test_case_name##_##test_name##_Test
-#endif
-// clang-format on
-
-#ifndef __constructor__
-#define __constructor__ __attribute__((constructor))
-#endif
-
-/// A configuration error is catastrophic and should exit the watcher.
-#define EXIT_CATASTROPHIC 78
-
-namespace osquery {
-
-/**
- * @brief The version of osquery
- */
-extern const std::string kVersion;
-extern const std::string kSDKVersion;
-extern const std::string kSDKPlatform;
-
-/// Use a macro for the sdk/platform literal, symbols available in lib.cpp.
-#define OSQUERY_SDK_VERSION STR(OSQUERY_BUILD_SDK_VERSION)
-#define OSQUERY_PLATFORM STR(OSQUERY_BUILD_PLATFORM)
-
-/**
- * @brief A helpful tool type to report when logging, print help, or debugging.
- */
-enum ToolType {
-  OSQUERY_TOOL_UNKNOWN = 0,
-  OSQUERY_TOOL_SHELL,
-  OSQUERY_TOOL_DAEMON,
-  OSQUERY_TOOL_TEST,
-  OSQUERY_EXTENSION,
-};
-
-/// The osquery tool type for runtime decisions.
-extern ToolType kToolType;
-
-class Initializer {
- public:
-  /**
-   * @brief Sets up various aspects of osquery execution state.
-   *
-   * osquery needs a few things to happen as soon as the process begins
-   * executing. Initializer takes care of setting up the relevant parameters.
-   * Initializer should be called in an executable's `main()` function.
-   *
-   * @param argc the number of elements in argv
-   * @param argv the command-line arguments passed to `main()`
-   * @param tool the type of osquery main (daemon, shell, test, extension).
-   */
-  Initializer(int& argc, char**& argv, ToolType tool = OSQUERY_TOOL_TEST);
-
-  /**
-   * @brief Sets up the process as an osquery daemon.
-   *
-   * A daemon has additional constraints, it can use a process mutex, check
-   * for sane/non-default configurations, etc.
-   */
-  void initDaemon();
-
-  /**
-   * @brief Daemon tools may want to continually spawn worker processes
-   * and monitor their utilization.
-   *
-   * A daemon may call initWorkerWatcher to begin watching child daemon
-   * processes until it-itself is unscheduled. The basic guarantee is that only
-   * workers will return from the function.
-   *
-   * The worker-watcher will implement performance bounds on CPU utilization
-   * and memory, as well as check for zombie/defunct workers and respawn them
-   * if appropriate. The appropriateness is determined from heuristics around
-   * how the worker exited. Various exit states and velocities may cause the
-   * watcher to resign.
-   *
-   * @param name The name of the worker process.
-   */
-  void initWorkerWatcher(const std::string& name);
-
-  /// Assume initialization finished, start work.
-  void start();
-  /// Turns off various aspects of osquery such as event loops.
-  void shutdown();
-
-  /**
-   * @brief Check if a process is an osquery worker.
-   *
-   * By default an osqueryd process will fork/exec then set an environment
-   * variable: `OSQUERY_WORKER` while continually monitoring child I/O.
-   * The environment variable causes subsequent child processes to skip several
-   * initialization steps and jump into extension handling, registry setup,
-   * config/logger discovery and then the event publisher and scheduler.
-   */
-  static bool isWorker();
-
- private:
-  /// Initialize this process as an osquery daemon worker.
-  void initWorker(const std::string& name);
-  /// Initialize the osquery watcher, optionally spawn a worker.
-  void initWatcher();
-  /// Set and wait for an active plugin optionally broadcasted.
-  void initActivePlugin(const std::string& type, const std::string& name);
-
- private:
-  int* argc_;
-  char*** argv_;
-  int tool_;
-  std::string binary_;
-};
-
-/**
- * @brief Split a given string based on an optional delimiter.
- *
- * If no delimiter is supplied, the string will be split based on whitespace.
- *
- * @param s the string that you'd like to split
- * @param delim the delimiter which you'd like to split the string by
- *
- * @return a vector of strings split by delim.
- */
-std::vector<std::string> split(const std::string& s,
-                               const std::string& delim = "\t ");
-
-/**
- * @brief Split a given string based on an delimiter.
- *
- * @param s the string that you'd like to split.
- * @param delim the delimiter which you'd like to split the string by.
- * @param occurrences the number of times to split by delim.
- *
- * @return a vector of strings split by delim for occurrences.
- */
-std::vector<std::string> split(const std::string& s,
-                               const std::string& delim,
-                               size_t occurences);
-
-/**
- * @brief In-line replace all instances of from with to.
- *
- * @param str The input/output mutable string.
- * @param from Search string
- * @param to Replace string
- */
-inline void replaceAll(std::string& str,
-                       const std::string& from,
-                       const std::string& to) {
-  if (from.empty()) {
-    return;
-  }
-
-  size_t start_pos = 0;
-  while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
-    str.replace(start_pos, from.length(), to);
-    start_pos += to.length();
-  }
-}
-
-/**
- * @brief Join a vector of strings using a tokenizer.
- *
- * @param s the string that you'd like to split.
- * @param tok a token glue.
- *
- * @return a joined string.
- */
-std::string join(const std::vector<std::string>& s, const std::string& tok);
-
-/**
- * @brief Getter for a host's current hostname
- *
- * @return a string representing the host's current hostname
- */
-std::string getHostname();
-
-/**
- * @brief generate a uuid to uniquely identify this machine
- *
- * @return uuid string to identify this machine
- */
-std::string generateHostUuid();
-
-/**
- * @brief Getter for the current time, in a human-readable format.
- *
- * @return the current date/time in the format: "Wed Sep 21 10:27:52 2011"
- */
-std::string getAsciiTime();
-
-/**
- * @brief Getter for the current UNIX time.
- *
- * @return an int representing the amount of seconds since the UNIX epoch
- */
-int getUnixTime();
-
-/**
- * @brief In-line helper function for use with utf8StringSize
- */
-template <typename _Iterator1, typename _Iterator2>
-inline size_t incUtf8StringIterator(_Iterator1& it, const _Iterator2& last) {
-  if (it == last) {
-    return 0;
-  }
-
-  unsigned char c;
-  size_t res = 1;
-  for (++it; last != it; ++it, ++res) {
-    c = *it;
-    if (!(c & 0x80) || ((c & 0xC0) == 0xC0)) {
-      break;
-    }
-  }
-
-  return res;
-}
-
-/**
- * @brief Get the length of a UTF-8 string
- *
- * @param str The UTF-8 string
- *
- * @return the length of the string
- */
-inline size_t utf8StringSize(const std::string& str) {
-  size_t res = 0;
-  std::string::const_iterator it = str.begin();
-  for (; it != str.end(); incUtf8StringIterator(it, str.end())) {
-    res++;
-  }
-
-  return res;
-}
-
-/**
- * @brief Create a pid file
- *
- * @return A status object indicating the success or failure of the operation
- */
-Status createPidFile();
-}
diff --git a/include/osquery/database.h b/include/osquery/database.h
deleted file mode 100644 (file)
index eaf35b5..0000000
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/registry.h>
-#include <osquery/status.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-/**
- * @brief A backing storage domain name, used for key/value based storage.
- *
- * There are certain "cached" variables such as a node-unique UUID or negotiated
- * 'node_key' following enrollment. If a value or setting must persist between
- * osqueryi or osqueryd runs it should be stored using the kPersistentSetting%s
- * domain.
- */
-extern const std::string kPersistentSettings;
-
-/// The "domain" where the results of scheduled queries are stored.
-extern const std::string kQueries;
-
-/// The "domain" where event results are stored, queued for querytime retrieval.
-extern const std::string kEvents;
-
-/**
- * @brief The "domain" where buffered log results are stored.
- *
- * Logger plugins may shuttle logs to a remote endpoint or API call
- * asynchronously. The backing store can be used to buffer results and status
- * logs until the logger plugin-specific thread decided to flush.
- */
-extern const std::string kLogs;
-
-/////////////////////////////////////////////////////////////////////////////
-// Row
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief A variant type for the SQLite type affinities.
- */
-typedef std::string RowData;
-
-/**
- * @brief A single row from a database query
- *
- * Row is a simple map where individual column names are keys, which map to
- * the Row's respective value
- */
-typedef std::map<std::string, RowData> Row;
-
-/**
- * @brief Serialize a Row into a property tree
- *
- * @param r the Row to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeRow(const Row& r, pt::ptree& tree);
-
-/**
- * @brief Serialize a Row object into a JSON string
- *
- * @param r the Row to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeRowJSON(const Row& r, std::string& json);
-
-/**
- * @brief Deserialize a Row object from a property tree
- *
- * @param tree the input property tree
- * @param r the output Row structure
- *
- * @return Status indicating the success or failure of the operation
- */
-Status deserializeRow(const pt::ptree& tree, Row& r);
-
-/**
- * @brief Deserialize a Row object from a JSON string
- *
- * @param json the input JSON string
- * @param r the output Row structure
- *
- * @return Status indicating the success or failure of the operation
- */
-Status deserializeRowJSON(const std::string& json, Row& r);
-
-/////////////////////////////////////////////////////////////////////////////
-// QueryData
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief The result set returned from a osquery SQL query
- *
- * QueryData is the canonical way to represent the results of SQL queries in
- * osquery. It's just a vector of Row's.
- */
-typedef std::vector<Row> QueryData;
-
-/**
- * @brief Serialize a QueryData object into a property tree
- *
- * @param q the QueryData to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryData(const QueryData& q, pt::ptree& tree);
-
-/**
- * @brief Serialize a QueryData object into a JSON string
- *
- * @param q the QueryData to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryDataJSON(const QueryData& q, std::string& json);
-
-/// Inverse of serializeQueryData, convert property tree to QueryData.
-Status deserializeQueryData(const pt::ptree& tree, QueryData& qd);
-
-/// Inverse of serializeQueryDataJSON, convert a JSON string to QueryData.
-Status deserializeQueryDataJSON(const std::string& json, QueryData& qd);
-
-/////////////////////////////////////////////////////////////////////////////
-// DiffResults
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief Data structure representing the difference between the results of
- * two queries
- *
- * The representation of two diffed QueryData result sets. Given and old and
- * new QueryData, DiffResults indicates the "added" subset of rows and the
- * "removed" subset of rows.
- */
-struct DiffResults {
-  /// vector of added rows
-  QueryData added;
-
-  /// vector of removed rows
-  QueryData removed;
-
-  /// equals operator
-  bool operator==(const DiffResults& comp) const {
-    return (comp.added == added) && (comp.removed == removed);
-  }
-
-  /// not equals operator
-  bool operator!=(const DiffResults& comp) const { return !(*this == comp); }
-};
-
-/**
- * @brief Serialize a DiffResults object into a property tree
- *
- * @param d the DiffResults to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeDiffResults(const DiffResults& d, pt::ptree& tree);
-
-/**
- * @brief Serialize a DiffResults object into a JSON string
- *
- * @param d the DiffResults to serialize
- * @param json the output JSON string
- *
- * @return an instance of osquery::Status, indicating the success or failure
- * of the operation
- */
-Status serializeDiffResultsJSON(const DiffResults& d, std::string& json);
-
-/**
- * @brief Diff two QueryData objects and create a DiffResults object
- *
- * @param old_ the "old" set of results
- * @param new_ the "new" set of results
- *
- * @return a DiffResults object which indicates the change from old_ to new_
- *
- * @see DiffResults
- */
-DiffResults diff(const QueryData& old_, const QueryData& new_);
-
-/**
- * @brief Add a Row to a QueryData if the Row hasn't appeared in the QueryData
- * already
- *
- * Note that this function will iterate through the QueryData list until a
- * given Row is found (or not found). This shouldn't be that significant of an
- * overhead for most use-cases, but it's worth keeping in mind before you use
- * this in it's current state.
- *
- * @param q the QueryData list to append to
- * @param r the Row to add to q
- *
- * @return true if the Row was added to the QueryData, false if it was not
- */
-bool addUniqueRowToQueryData(QueryData& q, const Row& r);
-
-/**
- * @brief Construct a new QueryData from an existing one, replacing all
- * non-ASCII characters with their \u encoding.
- *
- * This function is intended as a workaround for
- * https://svn.boost.org/trac/boost/ticket/8883,
- * and will allow rows containing data with non-ASCII characters to be stored in
- * the database and parsed back into a property tree.
- *
- * @param oldData the old QueryData to copy
- * @param newData the new escaped QueryData object
- */
-void escapeQueryData(const QueryData& oldData, QueryData& newData);
-
-/**
- * @brief represents the relevant parameters of a scheduled query.
- *
- * Within the context of osqueryd, a scheduled query may have many relevant
- * attributes. Those attributes are represented in this data structure.
- */
-struct ScheduledQuery {
-  /// The SQL query.
-  std::string query;
-
-  /// How often the query should be executed, in second.
-  size_t interval;
-
-  /// A temporary splayed internal.
-  size_t splayed_interval;
-
-  /// Number of executions.
-  size_t executions;
-
-  /// Total wall time taken
-  unsigned long long int wall_time;
-
-  /// Total user time (cycles)
-  unsigned long long int user_time;
-
-  /// Total system time (cycles)
-  unsigned long long int system_time;
-
-  /// Average memory differentials. This should be near 0.
-  unsigned long long int average_memory;
-
-  /// Total characters, bytes, generated by query.
-  unsigned long long int output_size;
-
-  /// Set of query options.
-  std::map<std::string, bool> options;
-
-  ScheduledQuery()
-      : interval(0),
-        splayed_interval(0),
-        executions(0),
-        wall_time(0),
-        user_time(0),
-        system_time(0),
-        average_memory(0),
-        output_size(0) {}
-
-  /// equals operator
-  bool operator==(const ScheduledQuery& comp) const {
-    return (comp.query == query) && (comp.interval == interval);
-  }
-
-  /// not equals operator
-  bool operator!=(const ScheduledQuery& comp) const { return !(*this == comp); }
-};
-
-/////////////////////////////////////////////////////////////////////////////
-// QueryLogItem
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief Query results from a schedule, snapshot, or ad-hoc execution.
- *
- * When a scheduled query yields new results, we need to log that information
- * to our upstream logging receiver. A QueryLogItem contains metadata and
- * results in potentially-differential form for a logger.
- */
-struct QueryLogItem {
-  /// Differential results from the query.
-  DiffResults results;
-
-  /// Optional snapshot results, no differential applied.
-  QueryData snapshot_results;
-
-  /// The name of the scheduled query.
-  std::string name;
-
-  /// The identifier (hostname, or uuid) of the host.
-  std::string identifier;
-
-  /// The time that the query was executed, seconds as UNIX time.
-  int time;
-
-  /// The time that the query was executed, an ASCII string.
-  std::string calendar_time;
-
-  /// equals operator
-  bool operator==(const QueryLogItem& comp) const {
-    return (comp.results == results) && (comp.name == name);
-  }
-
-  /// not equals operator
-  bool operator!=(const QueryLogItem& comp) const { return !(*this == comp); }
-};
-
-/**
- * @brief Serialize a QueryLogItem object into a property tree
- *
- * @param item the QueryLogItem to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItem(const QueryLogItem& item, pt::ptree& tree);
-
-/**
- * @brief Serialize a QueryLogItem object into a JSON string
- *
- * @param item the QueryLogItem to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItemJSON(const QueryLogItem& item, std::string& json);
-
-/// Inverse of serializeQueryLogItem, convert property tree to QueryLogItem.
-Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item);
-
-/// Inverse of serializeQueryLogItem, convert a JSON string to QueryLogItem.
-Status deserializeQueryLogItemJSON(const std::string& json, QueryLogItem& item);
-
-/**
- * @brief Serialize a QueryLogItem object into a property tree
- * of events, a list of actions.
- *
- * @param item the QueryLogItem to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItemAsEvents(const QueryLogItem& item, pt::ptree& tree);
-
-/**
- * @brief Serialize a QueryLogItem object into a JSON string of events,
- * a list of actions.
- *
- * @param i the QueryLogItem to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i,
-                                         std::string& json);
-
-/**
- * @brief An osquery backing storage (database) type that persists executions.
- *
- * The osquery tools need a high-performance storage and indexing mechanism for
- * storing intermediate results from EventPublisher%s, persisting one-time
- * generated values, and performing non-memory backed differentials.
- *
- * Practically, osquery is built around RocksDB's performance guarantees and
- * all of the internal APIs expect RocksDB's indexing and read performance.
- * However, access to this representation of a backing-store is still abstracted
- * to removing RocksDB as a dependency for the osquery SDK.
- */
-class DatabasePlugin : public Plugin {
- protected:
-  /**
-   * @brief Perform a domain and key lookup from the backing store.
-   *
-   * Database value access indexing is abstracted into domains and keys.
-   * Both are string values but exist separately for simple indexing without
-   * API-enforcing tokenization. In some cases we do add a component-specific
-   * tokeninzation to keys.
-   *
-   * @param domain A string value representing abstract storage indexing.
-   * @param key A string value representing the lookup/retrieval key.
-   * @param value The output parameter, left empty if the key does not exist.
-   * @return Failure if the data could not be accessed. It is up to the plugin
-   * to determine if a missing key means a non-success status.
-   */
-  virtual Status get(const std::string& domain,
-                     const std::string& key,
-                     std::string& value) const = 0;
-
-  /**
-   * @brief Store a string-represented value using a domain and key index.
-   *
-   * See DatabasePlugin::get for discussion around domain and key use.
-   *
-   * @param domain A string value representing abstract storage indexing.
-   * @param key A string value representing the lookup/retrieval key.
-   * @param value A string value representing the data.
-   * @return Failure if the data could not be stored. It is up to the plugin
-   * to determine if a conflict/overwrite should return different status text.
-   */
-  virtual Status put(const std::string& domain,
-                     const std::string& key,
-                     const std::string& value) = 0;
-
-  /// Data removal method.
-  virtual Status remove(const std::string& domain, const std::string& k) = 0;
-
-  /// Key/index lookup method.
-  virtual Status scan(const std::string& domain,
-                      std::vector<std::string>& results) const {
-    return Status(0, "Not used");
-  }
-
- public:
-  Status call(const PluginRequest& request, PluginResponse& response);
-};
-
-/**
- * @brief Lookup a value from the active osquery DatabasePlugin storage.
- *
- * See DatabasePlugin::get for discussion around domain and key use.
- * Extensions, components, plugins, and core code should use getDatabaseValue
- * as a wrapper around the current tool's choice of a backing storage plugin.
- *
- * @param domain A string value representing abstract storage indexing.
- * @param key A string value representing the lookup/retrieval key.
- * @param value The output parameter, left empty if the key does not exist.
- * @return Storage operation status.
- */
-Status getDatabaseValue(const std::string& domain,
-                        const std::string& key,
-                        std::string& value);
-
-/**
- * @brief Set or put a value into the active osquery DatabasePlugin storage.
- *
- * See DatabasePlugin::get for discussion around domain and key use.
- * Extensions, components, plugins, and core code should use setDatabaseValue
- * as a wrapper around the current tool's choice of a backing storage plugin.
- *
- * @param domain A string value representing abstract storage indexing.
- * @param key A string value representing the lookup/retrieval key.
- * @param value A string value representing the data.
- * @return Storage operation status.
- */
-Status setDatabaseValue(const std::string& domain,
-                        const std::string& key,
-                        const std::string& value);
-
-/// Remove a domain/key identified value from backing-store.
-Status deleteDatabaseValue(const std::string& domain, const std::string& key);
-
-/// Get a list of keys for a given domain.
-Status scanDatabaseKeys(const std::string& domain,
-                        std::vector<std::string>& keys);
-
-/// Generate a specific-use registry for database access abstraction.
-CREATE_REGISTRY(DatabasePlugin, "database");
-}
diff --git a/include/osquery/database/results.h b/include/osquery/database/results.h
deleted file mode 100644 (file)
index 0d288cd..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/status.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-/////////////////////////////////////////////////////////////////////////////
-// Row
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief A variant type for the SQLite type affinities.
- */
-typedef std::string RowData;
-
-/**
- * @brief A single row from a database query
- *
- * Row is a simple map where individual column names are keys, which map to
- * the Row's respective value
- */
-typedef std::map<std::string, RowData> Row;
-
-/**
- * @brief Serialize a Row into a property tree
- *
- * @param r the Row to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeRow(const Row& r, pt::ptree& tree);
-
-/**
- * @brief Serialize a Row object into a JSON string
- *
- * @param r the Row to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeRowJSON(const Row& r, std::string& json);
-
-/**
- * @brief Deserialize a Row object from a property tree
- *
- * @param tree the input property tree
- * @param r the output Row structure
- *
- * @return Status indicating the success or failure of the operation
- */
-Status deserializeRow(const pt::ptree& tree, Row& r);
-
-/**
- * @brief Deserialize a Row object from a JSON string
- *
- * @param json the input JSON string
- * @param r the output Row structure
- *
- * @return Status indicating the success or failure of the operation
- */
-Status deserializeRowJSON(const std::string& json, Row& r);
-
-/////////////////////////////////////////////////////////////////////////////
-// QueryData
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief The result set returned from a osquery SQL query
- *
- * QueryData is the canonical way to represent the results of SQL queries in
- * osquery. It's just a vector of Row's.
- */
-typedef std::vector<Row> QueryData;
-
-/**
- * @brief Serialize a QueryData object into a property tree
- *
- * @param q the QueryData to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryData(const QueryData& q, pt::ptree& tree);
-
-/**
- * @brief Serialize a QueryData object into a JSON string
- *
- * @param q the QueryData to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryDataJSON(const QueryData& q, std::string& json);
-
-Status deserializeQueryData(const pt::ptree& tree, QueryData& qd);
-Status deserializeQueryDataJSON(const std::string& json, QueryData& qd);
-
-/////////////////////////////////////////////////////////////////////////////
-// DiffResults
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief Data structure representing the difference between the results of
- * two queries
- *
- * The representation of two diffed QueryData result sets. Given and old and
- * new QueryData, DiffResults indicates the "added" subset of rows and the
- * "removed" subset of rows.
- */
-struct DiffResults {
-  /// vector of added rows
-  QueryData added;
-
-  /// vector of removed rows
-  QueryData removed;
-
-  /// equals operator
-  bool operator==(const DiffResults& comp) const {
-    return (comp.added == added) && (comp.removed == removed);
-  }
-
-  /// not equals operator
-  bool operator!=(const DiffResults& comp) const { return !(*this == comp); }
-};
-
-/**
- * @brief Serialize a DiffResults object into a property tree
- *
- * @param d the DiffResults to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeDiffResults(const DiffResults& d, pt::ptree& tree);
-
-/**
- * @brief Serialize a DiffResults object into a JSON string
- *
- * @param d the DiffResults to serialize
- * @param json the output JSON string
- *
- * @return an instance of osquery::Status, indicating the success or failure
- * of the operation
- */
-Status serializeDiffResultsJSON(const DiffResults& d, std::string& json);
-
-/**
- * @brief Diff two QueryData objects and create a DiffResults object
- *
- * @param old_ the "old" set of results
- * @param new_ the "new" set of results
- *
- * @return a DiffResults object which indicates the change from old_ to new_
- *
- * @see DiffResults
- */
-DiffResults diff(const QueryData& old_, const QueryData& new_);
-
-/**
- * @brief Add a Row to a QueryData if the Row hasn't appeared in the QueryData
- * already
- *
- * Note that this function will iterate through the QueryData list until a
- * given Row is found (or not found). This shouldn't be that significant of an
- * overhead for most use-cases, but it's worth keeping in mind before you use
- * this in it's current state.
- *
- * @param q the QueryData list to append to
- * @param r the Row to add to q
- *
- * @return true if the Row was added to the QueryData, false if it was not
- */
-bool addUniqueRowToQueryData(QueryData& q, const Row& r);
-
-/**
- * @brief Construct a new QueryData from an existing one, replacing all
- * non-ASCII characters with their \u encoding.
- *
- * This function is intended as a workaround for
- * https://svn.boost.org/trac/boost/ticket/8883,
- * and will allow rows containing data with non-ASCII characters to be stored in
- * the database and parsed back into a property tree.
- *
- * @param oldData the old QueryData to copy
- * @param newData the new escaped QueryData object
- */
-void escapeQueryData(const QueryData& oldData, QueryData& newData);
-
-/**
- * @brief represents the relevant parameters of a scheduled query.
- *
- * Within the context of osqueryd, a scheduled query may have many relevant
- * attributes. Those attributes are represented in this data structure.
- */
-struct ScheduledQuery {
-  /// The SQL query.
-  std::string query;
-
-  /// How often the query should be executed, in second.
-  size_t interval;
-
-  /// A temporary splayed internal.
-  size_t splayed_interval;
-
-  /// Number of executions.
-  size_t executions;
-
-  /// Total wall time taken
-  size_t wall_time;
-
-  /// Total user time (cycles)
-  size_t user_time;
-
-  /// Total system time (cycles)
-  size_t system_time;
-
-  /// Average memory differentials. This should be near 0.
-  size_t memory;
-
-  /// Total characters, bytes, generated by query.
-  size_t output_size;
-
-  /// Set of query options.
-  std::map<std::string, bool> options;
-
-  ScheduledQuery()
-      : interval(0),
-        splayed_interval(0),
-        executions(0),
-        wall_time(0),
-        user_time(0),
-        system_time(0),
-        memory(0),
-        output_size(0) {}
-
-  /// equals operator
-  bool operator==(const ScheduledQuery& comp) const {
-    return (comp.query == query) && (comp.interval == interval);
-  }
-
-  /// not equals operator
-  bool operator!=(const ScheduledQuery& comp) const { return !(*this == comp); }
-};
-
-/////////////////////////////////////////////////////////////////////////////
-// QueryLogItem
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * @brief Query results from a schedule, snapshot, or ad-hoc execution.
- *
- * When a scheduled query yields new results, we need to log that information
- * to our upstream logging receiver. A QueryLogItem contains metadata and
- * results in potentially-differential form for a logger.
- */
-struct QueryLogItem {
-  /// Differential results from the query.
-  DiffResults results;
-
-  /// Optional snapshot results, no differential applied.
-  QueryData snapshot_results;
-
-  /// The name of the scheduled query.
-  std::string name;
-
-  /// The identifier (hostname, or uuid) of the host.
-  std::string identifier;
-
-  /// The time that the query was executed, seconds as UNIX time.
-  int time;
-
-  /// The time that the query was executed, an ASCII string.
-  std::string calendar_time;
-
-  /// equals operator
-  bool operator==(const QueryLogItem& comp) const {
-    return (comp.results == results) && (comp.name == name);
-  }
-
-  /// not equals operator
-  bool operator!=(const QueryLogItem& comp) const { return !(*this == comp); }
-};
-
-/**
- * @brief Serialize a QueryLogItem object into a property tree
- *
- * @param item the QueryLogItem to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItem(const QueryLogItem& item, pt::ptree& tree);
-
-/**
- * @brief Serialize a QueryLogItem object into a JSON string
- *
- * @param item the QueryLogItem to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItemJSON(const QueryLogItem& item, std::string& json);
-
-Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item);
-Status deserializeQueryLogItemJSON(const std::string& json, QueryLogItem& item);
-
-/**
- * @brief Serialize a QueryLogItem object into a property tree
- * of events, a list of actions.
- *
- * @param item the QueryLogItem to serialize
- * @param tree the output property tree
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItemAsEvents(const QueryLogItem& item, pt::ptree& tree);
-
-/**
- * @brief Serialize a QueryLogItem object into a JSON string of events,
- * a list of actions.
- *
- * @param i the QueryLogItem to serialize
- * @param json the output JSON string
- *
- * @return Status indicating the success or failure of the operation
- */
-Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i,
-                                         std::string& json);
-}
diff --git a/include/osquery/events.h b/include/osquery/events.h
deleted file mode 100644 (file)
index 1baabe6..0000000
+++ /dev/null
@@ -1,879 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <functional>
-#include <memory>
-#include <map>
-#include <vector>
-
-#include <boost/make_shared.hpp>
-#include <boost/thread.hpp>
-#include <boost/thread/locks.hpp>
-#include <boost/thread/mutex.hpp>
-
-#include <osquery/registry.h>
-#include <osquery/status.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-
-struct Subscription;
-template <class SC, class EC> class EventPublisher;
-template <class PUB> class EventSubscriber;
-class EventFactory;
-
-typedef const std::string EventPublisherID;
-typedef const std::string EventSubscriberID;
-typedef const std::string EventID;
-typedef uint32_t EventContextID;
-typedef uint32_t EventTime;
-typedef std::pair<EventID, EventTime> EventRecord;
-
-/**
- * @brief An EventPublisher will define a SubscriptionContext for
- * EventSubscriber%s to use.
- *
- * Most EventPublisher%s will require specific information for interacting with
- * an OS to receive events. The SubscriptionContext contains information the
- * EventPublisher will use to register OS API callbacks, create
- * subscriptioning/listening handles, etc.
- *
- * Linux `inotify` should implement a SubscriptionContext that subscribes
- * filesystem events based on a filesystem path. `libpcap` will subscribe on
- * networking protocols at various stacks. Process creation may subscribe on
- * process name, parent pid, etc.
- */
-struct SubscriptionContext {};
-
-/**
- * @brief An EventSubscriber EventCallback method will receive an EventContext.
- *
- * The EventContext contains the event-related data supplied by an
- * EventPublisher when the event occurs. If a subscribing EventSubscriber
- * would be called for the event, the EventSubscriber%'s EventCallback is
- * passed an EventContext.
- */
-struct EventContext {
-  /// An unique counting ID specific to the EventPublisher%'s fired events.
-  EventContextID id;
-  /// The time the event occurred, as determined by the publisher.
-  EventTime time;
-
-  EventContext() : id(0), time(0) {}
-};
-
-typedef std::shared_ptr<Subscription> SubscriptionRef;
-typedef EventPublisher<SubscriptionContext, EventContext> BaseEventPublisher;
-typedef std::shared_ptr<BaseEventPublisher> EventPublisherRef;
-typedef std::shared_ptr<SubscriptionContext> SubscriptionContextRef;
-typedef std::shared_ptr<EventContext> EventContextRef;
-typedef EventSubscriber<BaseEventPublisher> BaseEventSubscriber;
-typedef std::shared_ptr<EventSubscriber<BaseEventPublisher>> EventSubscriberRef;
-
-/**
- * @brief EventSubscriber%s may exist in various states.
- *
- * The subscriber will move through states when osquery is initializing the
- * registry, starting event publisher loops, and requesting initialization of
- * each subscriber and the optional set of subscriptions it creates. If this
- * initialization fails the publishers or EventFactory may eject, warn, or
- * otherwise not use the subscriber's subscriptions.
- *
- * The supported states are:
- * - None: The default state, uninitialized.
- * - Running: Subscriber is ready for events.
- * - Paused: Subscriber was initialized but is not currently accepting events.
- * - Failed: Subscriber failed to initialize or is otherwise offline.
- */
-enum EventSubscriberState {
-  SUBSCRIBER_NONE,
-  SUBSCRIBER_RUNNING,
-  SUBSCRIBER_PAUSED,
-  SUBSCRIBER_FAILED,
-};
-
-/// Use a single placeholder for the EventContextRef passed to EventCallback.
-using std::placeholders::_1;
-using std::placeholders::_2;
-typedef std::function<Status(const EventContextRef&, const void*)>
-    EventCallback;
-
-/// An EventPublisher must track every subscription added.
-typedef std::vector<SubscriptionRef> SubscriptionVector;
-
-/// The set of search-time binned lookup tables.
-extern const std::vector<size_t> kEventTimeLists;
-
-/**
- * @brief DECLARE_PUBLISHER supplies needed boilerplate code that applies a
- * string-type EventPublisherID to identify the publisher declaration.
- */
-#define DECLARE_PUBLISHER(TYPE) \
- public:                        \
-  EventPublisherID type() const { return TYPE; }
-
-/**
- * @brief A Subscription is used to configure an EventPublisher and bind a
- * callback to a SubscriptionContext.
- *
- * A Subscription is the input to an EventPublisher when the EventPublisher
- * decides on the scope and details of the events it watches/generates.
- * An example includes a filesystem change event. A subscription would include
- * a path with optional recursion and attribute selectors as well as a callback
- * function to fire when an event for that path and selector occurs.
- *
- * A Subscription also functions to greatly scope an EventPublisher%'s work.
- * Using the same filesystem example and the Linux inotify subsystem a
- * Subscription limits the number of inode watches to only those requested by
- * appropriate EventSubscriber%s.
- * Note: EventSubscriber%s and Subscriptions can be configured by the osquery
- * user.
- *
- * Subscriptions are usually created with EventFactory members:
- *
- * @code{.cpp}
- *   EventFactory::addSubscription("MyEventPublisher", my_subscription_context);
- * @endcode
- */
-struct Subscription {
- public:
-  // EventSubscriber name.
-  std::string subscriber_name;
-
-  /// An EventPublisher%-specific SubscriptionContext.
-  SubscriptionContextRef context;
-  /// An EventSubscription member EventCallback method.
-  EventCallback callback;
-  /// A pointer to possible extra data
-  void* user_data;
-
-  explicit Subscription(EventSubscriberID& name)
-      : subscriber_name(name), user_data(nullptr) {}
-
-  static SubscriptionRef create(EventSubscriberID& name) {
-    auto subscription = std::make_shared<Subscription>(name);
-    return subscription;
-  }
-
-  static SubscriptionRef create(EventSubscriberID& name,
-                                const SubscriptionContextRef& mc,
-                                EventCallback ec = 0,
-                                void* user_data = nullptr) {
-    auto subscription = std::make_shared<Subscription>(name);
-    subscription->context = mc;
-    subscription->callback = ec;
-    subscription->user_data = user_data;
-    return subscription;
-  }
-};
-
-class EventPublisherPlugin : public Plugin {
- public:
-  /**
-   * @brief A new Subscription was added, potentially change state based on all
-   * subscriptions for this EventPublisher.
-   *
-   * `configure` allows the EventPublisher to optimize on the state of all
-   * subscriptions. An example is Linux `inotify` where multiple
-   * EventSubscription%s will subscription identical paths, e.g., /etc for
-   * config changes. Since Linux `inotify` has a subscription limit, `configure`
-   * can dedup paths.
-   */
-  virtual void configure() {}
-
-  /**
-   * @brief Perform handle opening, OS API callback registration.
-   *
-   * `setUp` is the event framework's EventPublisher constructor equivalent.
-   * This is called in the main thread before the publisher's run loop has
-   * started, immediately following registration.
-   */
-  virtual Status setUp() { return Status(0, "Not used"); }
-
-  /**
-   * @brief Perform handle closing, resource cleanup.
-   *
-   * osquery is about to end, the EventPublisher should close handle descriptors
-   * unblock resources, and prepare to exit. This will be called from the main
-   * thread after the run loop thread has exited.
-   */
-  virtual void tearDown() {}
-
-  /**
-   * @brief Implement a "step" of an optional run loop.
-   *
-   * @return A SUCCESS status will immediately call `run` again. A FAILED status
-   * will exit the run loop and the thread.
-   */
-  virtual Status run() { return Status(1, "No run loop required"); }
-
-  /**
-   * @brief Allow the EventFactory to interrupt the run loop.
-   *
-   * Assume the main thread may ask the run loop to stop at anytime.
-   * Before end is called the publisher's `isEnding` is set and the EventFactory
-   * run loop manager will exit the stepping loop and fall through to a call
-   * to tearDown followed by a removal of the publisher.
-   */
-  virtual void end() {}
-
-  /**
-   * @brief A new EventSubscriber is subscribing events of this publisher type.
-   *
-   * @param subscription The Subscription context information and optional
-   * EventCallback.
-   *
-   * @return If the Subscription is not appropriate (mismatched type) fail.
-   */
-  virtual Status addSubscription(const SubscriptionRef& subscription) {
-    subscriptions_.push_back(subscription);
-    return Status(0, "OK");
-  }
-
- public:
-  /// Overriding the EventPublisher constructor is not recommended.
-  EventPublisherPlugin() : next_ec_id_(0), ending_(false), started_(false){};
-  virtual ~EventPublisherPlugin() {}
-
-  /// Return a string identifier associated with this EventPublisher.
-  virtual EventPublisherID type() const { return "publisher"; }
-
- public:
-  /// Number of Subscription%s watching this EventPublisher.
-  size_t numSubscriptions() const { return subscriptions_.size(); }
-
-  /**
-   * @brief The number of events fired by this EventPublisher.
-   *
-   * @return The number of events.
-   */
-  size_t numEvents() const { return next_ec_id_; }
-
-  /// Check if the EventFactory is ending all publisher threads.
-  bool isEnding() const { return ending_; }
-
-  /// Set the ending status for this publisher.
-  void isEnding(bool ending) { ending_ = ending; }
-
-  /// Check if the publisher's run loop has started.
-  bool hasStarted() const { return started_; }
-
-  /// Set the run or started status for this publisher.
-  void hasStarted(bool started) { started_ = started; }
-
- protected:
-  /**
-   * @brief The generic check loop to call SubscriptionContext callback methods.
-   *
-   * It is NOT recommended to override `fire`. The simple logic of enumerating
-   * the Subscription%s and using `shouldFire` is more appropriate.
-   *
-   * @param ec The EventContext created and fired by the EventPublisher.
-   * @param time The most accurate time associated with the event.
-   */
-  virtual void fire(const EventContextRef& ec, EventTime time = 0) final;
-
-  /// The internal fire method used by the typed EventPublisher.
-  virtual void fireCallback(const SubscriptionRef& sub,
-                            const EventContextRef& ec) const = 0;
-
-  /// The EventPublisher will keep track of Subscription%s that contain callins.
-  SubscriptionVector subscriptions_;
-
-  /// An Event ID is assigned by the EventPublisher within the EventContext.
-  /// This is not used to store event date in the backing store.
-  EventContextID next_ec_id_;
-
- private:
-  EventPublisherPlugin(EventPublisherPlugin const&);
-  EventPublisherPlugin& operator=(EventPublisherPlugin const&);
-
- private:
-  /// Set ending to True to cause event type run loops to finish.
-  bool ending_;
-
-  /// Set to indicate whether the event run loop ever started.
-  bool started_;
-
-  /// A lock for incrementing the next EventContextID.
-  boost::mutex ec_id_lock_;
-
- private:
-  /// Enable event factory "callins" through static publisher callbacks.
-  friend class EventFactory;
-
- private:
-  FRIEND_TEST(EventsTests, test_event_pub);
-  FRIEND_TEST(EventsTests, test_fire_event);
-};
-
-/**
- * @brief Generate OS events of a type (FS, Network, Syscall, ioctl).
- *
- * A 'class' of OS events is abstracted into an EventPublisher responsible for
- * remaining as agile as possible given a known-set of subscriptions.
- *
- * The life cycle of an EventPublisher may include, `setUp`, `configure`, `run`,
- * `tearDown`, and `fire`. `setUp` and `tearDown` happen when osquery starts and
- * stops either as a daemon or interactive shell. `configure` is a pseudo-start
- * called every time a Subscription is added. EventPublisher%s can adjust their
- * scope/agility specific to each added subscription by overriding
- *`addSubscription`, and/or globally in `configure`.
- *
- * Not all EventPublisher%s leverage pure async OS APIs, and most will require a
- * run loop either polling with a timeout on a descriptor or for a change. When
- * osquery initializes the EventFactory will optionally create a thread for each
- * EventPublisher using `run` as the thread's entrypoint. `run` is called in a
- * within-thread loop where returning a FAILED status ends the run loop and
- * shuts down the thread.
- *
- * To opt-out of polling in a thread, consider the following run implementation:
- *
- * @code{.cpp}
- *   Status run() { return Status(1, "Not Implemented"); }
- * @endcode
- *
- * The final life cycle component, `fire` will iterate over the EventPublisher
- * Subscription%s and call `shouldFire` for each, using the EventContext fired.
- * The `shouldFire` method should check the subscription-specific selectors and
- * only call the Subscription%'s callback function if the EventContext
- * (thus event) matches.
- */
-template <typename SC, typename EC>
-class EventPublisher : public EventPublisherPlugin {
- public:
-  /// A nested helper typename for the templated SubscriptionContextRef.
-  typedef typename std::shared_ptr<SC> SCRef;
-  /// A nested helper typename for the templated EventContextRef.
-  typedef typename std::shared_ptr<EC> ECRef;
-
- public:
-  /// Up-cast a base EventContext reference to the templated ECRef.
-  static ECRef getEventContext(const EventContextRef& ec) {
-    return std::static_pointer_cast<EC>(ec);
-  }
-
-  /// Up-cast a base SubscriptionContext reference to the templated SCRef.
-  static SCRef getSubscriptionContext(const SubscriptionContextRef& sc) {
-    return std::static_pointer_cast<SC>(sc);
-  }
-
-  /// Create a EventContext based on the templated type.
-  static ECRef createEventContext() { return std::make_shared<EC>(); }
-
-  /// Create a SubscriptionContext based on the templated type.
-  static SCRef createSubscriptionContext() { return std::make_shared<SC>(); }
-
- protected:
-  /**
-   * @brief The internal `fire` phase of publishing.
-   *
-   * This is a template-generated method that up-casts the generic fired
-   * event/subscription contexts, and calls the callback if the event should
-   * fire given a subscription.
-   *
-   * @param sub The SubscriptionContext and optional EventCallback.
-   * @param ec The event that was fired.
-   */
-  void fireCallback(const SubscriptionRef& sub,
-                    const EventContextRef& ec) const {
-    auto pub_sc = getSubscriptionContext(sub->context);
-    auto pub_ec = getEventContext(ec);
-    if (shouldFire(pub_sc, pub_ec) && sub->callback != nullptr) {
-      sub->callback(pub_ec, sub->user_data);
-    }
-  }
-
- protected:
-  /**
-   * @brief The generic `fire` will call `shouldFire` for each Subscription.
-   *
-   * @param sc A SubscriptionContext with optional specifications for events
-   * details.
-   * @param ec The event fired with event details.
-   *
-   * @return should the Subscription%'s EventCallback be called for this event.
-   */
-  virtual bool shouldFire(const SCRef& sc, const ECRef& ec) const {
-    return true;
-  }
-
- private:
-  FRIEND_TEST(EventsTests, test_event_sub_subscribe);
-  FRIEND_TEST(EventsTests, test_event_sub_context);
-  FRIEND_TEST(EventsTests, test_fire_event);
-};
-
-class EventSubscriberPlugin : public Plugin {
- protected:
-  /**
-   * @brief Store parsed event data from an EventCallback in a backing store.
-   *
-   * Within a EventCallback the EventSubscriber has an opportunity to create
-   * an osquery Row element, add the relevant table data for the EventSubscriber
-   * and store that element in the osquery backing store. At query-time
-   * the added data will apply selection criteria and return these elements.
-   * The backing store data retrieval is optimized by time-based indexes. It
-   * is important to added EventTime as it relates to "when the event occurred".
-   *
-   * @param r An osquery Row element.
-   * @param time The time the added event occurred.
-   *
-   * @return Was the element added to the backing store.
-   */
-  virtual Status add(Row& r, EventTime event_time) final;
-
-  /**
-   * @brief Return all events added by this EventSubscriber within start, stop.
-   *
-   * This is used internally (for the most part) by EventSubscriber::genTable.
-   *
-   * @param start Inclusive lower bound time limit.
-   * @param stop Inclusive upper bound time limit.
-   * @return Set of event rows matching time limits.
-   */
-  virtual QueryData get(EventTime start, EventTime stop);
-
- private:
-  /*
-   * @brief When `get`ing event results, return EventID%s from time indexes.
-   *
-   * Used by EventSubscriber::get to retrieve EventID, EventTime indexes. This
-   * applies the lookup-efficiency checks for time list appropriate bins.
-   * If the time range in 24 hours and there is a 24-hour list bin it will
-   * be queried using a single backing store `Get` followed by two `Get`s of
-   * the most-specific boundary lists.
-   *
-   * @return List of EventID, EventTime%s
-   */
-  std::vector<EventRecord> getRecords(const std::set<std::string>& indexes);
-
-  /**
-   * @brief Get a unique storage-related EventID.
-   *
-   * An EventID is an index/element-identifier for the backing store.
-   * Each EventPublisher maintains a fired EventContextID to identify the many
-   * events that may or may not be fired based on subscription criteria for this
-   * EventSubscriber. This EventContextID is NOT the same as an EventID.
-   * EventSubscriber development should not require use of EventID%s. If this
-   * indexing is required within-EventCallback consider an
-   * EventSubscriber%-unique indexing, counting mechanic.
-   *
-   * @return A unique ID for backing storage.
-   */
-  EventID getEventID();
-
-  /**
-   * @brief Plan the best set of indexes for event record access.
-   *
-   * @param start an inclusive time to begin searching.
-   * @param stop an inclusive time to end searching.
-   * @param list_key optional key to bind to a specific index binning.
-   *
-   * @return List of 'index.step' index strings.
-   */
-  std::set<std::string> getIndexes(EventTime start,
-                                   EventTime stop,
-                                   int list_key = 0);
-
-  /**
-   * @brief Expire indexes and eventually records.
-   *
-   * @param list_type the string representation of list binning type.
-   * @param indexes complete set of 'index.step' indexes for the list_type.
-   * @param expirations of the indexes, the set to expire.
-   */
-  void expireIndexes(const std::string& list_type,
-                     const std::vector<std::string>& indexes,
-                     const std::vector<std::string>& expirations);
-  /// Expire all datums within a bin.
-  void expireRecords(const std::string& list_type,
-                     const std::string& index,
-                     bool all);
-
-  /**
-   * @brief Add an EventID, EventTime pair to all matching list types.
-   *
-   * The list types are defined by time size. Based on the EventTime this pair
-   * is added to the list bin for each list type. If there are two list types:
-   * 60 seconds and 3600 seconds and `time` is 92, this pair will be added to
-   * list type 1 bin 4 and list type 2 bin 1.
-   *
-   * @param eid A unique EventID.
-   * @param time The time when this EventID%'s event occurred.
-   *
-   * @return Were the indexes recorded.
-   */
-  Status recordEvent(EventID& eid, EventTime time);
-
- public:
-  /**
-   * @brief A single instance requirement for static callback facilities.
-   *
-   * The EventSubscriber constructor is NOT responsible for adding
-   * Subscription%s. Please use `init` for adding Subscription%s as all
-   * EventPublisher instances will have run `setUp` and initialized their run
-   * loops.
-   */
-  EventSubscriberPlugin()
-      : expire_events_(true), expire_time_(0), optimize_time_(0) {}
-  virtual ~EventSubscriberPlugin() {}
-
-  /**
-   * @brief Suggested entrypoint for table generation.
-   *
-   * The EventSubscriber is a convention that removes a lot of boilerplate event
-   * 'subscribing' and acting. The `genTable` static entrypoint is the
-   * suggested method for table specs.
-   *
-   * @return The query-time table data, retrieved from a backing store.
-   */
-  virtual QueryData genTable(QueryContext& context) __attribute__((used));
-
- protected:
-  /**
-   * @brief Backing storage indexing namespace.
-   *
-   * The backing storage will accumulate events for this subscriber. A namespace
-   * is provided to prevent event indexing collisions between subscribers and
-   * publishers. The namespace is a combination of the publisher and subscriber
-   * registry plugin names.
-   */
-  virtual EventPublisherID& dbNamespace() const = 0;
-
-  /// Disable event expiration for this subscriber.
-  void doNotExpire() { expire_events_ = false; }
-
- private:
-  EventSubscriberPlugin(EventSubscriberPlugin const&);
-  EventSubscriberPlugin& operator=(EventSubscriberPlugin const&);
-
- private:
-  Status setUp() { return Status(0, "Setup never used"); }
-
- private:
-  /// Do not respond to periodic/scheduled/triggered event expiration requests.
-  bool expire_events_;
-
-  /// Events before the expire_time_ are invalid and will be purged.
-  EventTime expire_time_;
-
-  /**
-   * @brief Optimize subscriber selects by tracking the last select time.
-   *
-   * Event subscribers may optimize selects when used in a daemon schedule by
-   * requiring an event 'time' constraint and otherwise applying a minimum time
-   * as the last time the scheduled query ran.
-   */
-  EventTime optimize_time_;
-
-  /// Lock used when incrementing the EventID database index.
-  boost::mutex event_id_lock_;
-
-  /// Lock used when recording an EventID and time into search bins.
-  boost::mutex event_record_lock_;
-
- private:
-  FRIEND_TEST(EventsDatabaseTests, test_event_module_id);
-  FRIEND_TEST(EventsDatabaseTests, test_record_indexing);
-  FRIEND_TEST(EventsDatabaseTests, test_record_range);
-  FRIEND_TEST(EventsDatabaseTests, test_record_expiration);
-};
-
-/**
- * @brief A factory for associating event generators to EventPublisherID%s.
- *
- * This factory both registers new event types and the subscriptions that use
- * them. An EventPublisher is also a factory, the single event factory
- * arbitrates Subscription creation and management for each associated
- * EventPublisher.
- *
- * Since event types may be plugins, they are created using the factory.
- * Since subscriptions may be configured/disabled they are also factory-managed.
- */
-class EventFactory : private boost::noncopyable {
- public:
-  /// Access to the EventFactory instance.
-  static EventFactory& getInstance();
-
-  /**
-   * @brief Add an EventPublisher to the factory.
-   *
-   * The registration is mostly abstracted using osquery's registry.
-   *
-   * @param event_pub If for some reason the caller needs access to the
-   * EventPublisher instance they can register-by-instance.
-   *
-   * Access to the EventPublisher instance is not discouraged, but using the
-   * EventFactory `getEventPublisher` accessor is encouraged.
-   */
-  static Status registerEventPublisher(const PluginRef& pub);
-
-  /**
-   * @brief Add an EventSubscriber to the factory.
-   *
-   * The registration is mostly abstracted using osquery's registry.
-   */
-  template <class T>
-  static Status registerEventSubscriber() {
-    auto sub = std::make_shared<T>();
-    return registerEventSubscriber(sub);
-  }
-
-  /**
-   * @brief Add an EventSubscriber to the factory.
-   *
-   * The registration is mostly abstracted using osquery's registry.
-   *
-   * @param sub If the caller must access the EventSubscriber instance
-   * control may be passed to the registry.
-   *
-   * Access to the EventSubscriber instance outside of the within-instance
-   * table generation method and set of EventCallback%s is discouraged.
-   */
-  static Status registerEventSubscriber(const PluginRef& sub);
-
-  /**
-   * @brief Add a SubscriptionContext and EventCallback Subscription to an
-   * EventPublisher.
-   *
-   * Create a Subscription from a given SubscriptionContext and EventCallback
-   * and add that Subscription to the EventPublisher associated identifier.
-   *
-   * @param type_id The string for an EventPublisher receiving the Subscription.
-   * @param sc A SubscriptionContext related to the EventPublisher.
-   * @param cb When the EventPublisher fires an event the SubscriptionContext
-   * will be evaluated, if the event matches optional specifics in the context
-   * this callback function will be called. It should belong to an
-   * EventSubscription.
-   *
-   * @return Was the SubscriptionContext appropriate for the EventPublisher.
-   */
-  static Status addSubscription(EventPublisherID& type_id,
-                                EventSubscriberID& name_id,
-                                const SubscriptionContextRef& sc,
-                                EventCallback cb = 0,
-                                void* user_data = nullptr);
-
-  /// Add a Subscription using a caller Subscription instance.
-  static Status addSubscription(EventPublisherID& type_id,
-                                const SubscriptionRef& subscription);
-
-  /// Get the total number of Subscription%s across ALL EventPublisher%s.
-  static size_t numSubscriptions(EventPublisherID& type_id);
-
-  /// Get the number of EventPublishers.
-  static size_t numEventPublishers() {
-    return EventFactory::getInstance().event_pubs_.size();
-  }
-
-  /**
-   * @brief Halt the EventPublisher run loop.
-   *
-   * Any EventSubscriber%s with Subscription%s for this EventPublisher will
-   * become useless. osquery callers MUST deregister events.
-   * EventPublisher%s assume they can hook/trampoline, which requires cleanup.
-   * This will tear down and remove the publisher if the run loop did not start.
-   * Otherwise it will call end on the publisher and assume the run loop will
-   * tear down and remove.
-   *
-   * @param event_pub The string label for the EventPublisher.
-   *
-   * @return Did the EventPublisher deregister cleanly.
-   */
-  static Status deregisterEventPublisher(const EventPublisherRef& pub);
-
-  /// Deregister an EventPublisher by EventPublisherID.
-  static Status deregisterEventPublisher(EventPublisherID& type_id);
-
-  /// Return an instance to a registered EventPublisher.
-  static EventPublisherRef getEventPublisher(EventPublisherID& pub);
-
-  /// Return an instance to a registered EventSubscriber.
-  static EventSubscriberRef getEventSubscriber(EventSubscriberID& sub);
-
-  /// Check if an event subscriber exists.
-  static bool exists(EventSubscriberID& sub);
-
-  /// Return a list of publisher types, these are their registry names.
-  static std::vector<std::string> publisherTypes();
-
-  /// Return a list of subscriber registry names,
-  static std::vector<std::string> subscriberNames();
-
- public:
-  /// The dispatched event thread's entry-point (if needed).
-  static Status run(EventPublisherID& type_id);
-
-  /// An initializer's entry-point for spawning all event type run loops.
-  static void delay();
-
-  /// If a static EventPublisher callback wants to fire
-  template <typename PUB>
-  static void fire(const EventContextRef& ec) {
-    auto event_pub = getEventPublisher(getType<PUB>());
-    event_pub->fire(ec);
-  }
-
-  /**
-   * @brief Return the publisher registry name given a type.
-   *
-   * Subscriber initialization and runtime static callbacks can lookup the
-   * publisher type name, which is the registry plugin name. This allows static
-   * callbacks to fire into subscribers.
-   */
-  template <class PUB>
-  static EventPublisherID getType() {
-    auto pub = std::make_shared<PUB>();
-    return pub->type();
-  }
-
-  /**
-   * @brief End all EventPublisher run loops and deregister.
-   *
-   * End is NOT the same as deregistration. End will call deregister on all
-   * publishers then either join or detach their run loop threads.
-   * See EventFactory::deregisterEventPublisher for actions taken during
-   * deregistration.
-   *
-   * @param should_end Reset the "is ending" state if False.
-   */
-  static void end(bool join = false);
-
- private:
-  /// An EventFactory will exist for the lifetime of the application.
-  EventFactory() {}
-  EventFactory(EventFactory const&);
-  EventFactory& operator=(EventFactory const&);
-  ~EventFactory() {}
-
- private:
-  /// Set of registered EventPublisher instances.
-  std::map<EventPublisherID, EventPublisherRef> event_pubs_;
-
-  /// Set of instantiated EventSubscriber subscriptions.
-  std::map<EventSubscriberID, EventSubscriberRef> event_subs_;
-
-  /// Set of running EventPublisher run loop threads.
-  std::vector<std::shared_ptr<boost::thread> > threads_;
-};
-
-/**
- * @brief An interface binding Subscriptions, event response, and table
- *generation.
- *
- * Use the EventSubscriber interface when adding event subscriptions and
- * defining callin functions. The EventCallback is usually a member function
- * for an EventSubscriber. The EventSubscriber interface includes a very
- * important `add` method that abstracts the needed event to backing store
- * interaction.
- *
- * Storing event data in the backing store must match a table spec for queries.
- * Small overheads exist that help query-time indexing and lookups.
- */
-template <class PUB>
-class EventSubscriber : public EventSubscriberPlugin {
- protected:
-  typedef typename PUB::SCRef SCRef;
-  typedef typename PUB::ECRef ECRef;
-
- public:
-  /**
-   * @brief Add Subscription%s to the EventPublisher this module will act on.
-   *
-   * When the EventSubscriber%'s `init` method is called you are assured the
-   * EventPublisher has `setUp` and is ready to subscription for events.
-   */
-  virtual Status init() { return Status(0, "OK"); }
-
- protected:
-  /// Helper function to call the publisher's templated subscription generator.
-  SCRef createSubscriptionContext() const {
-    return PUB::createSubscriptionContext();
-  }
-
-  /**
-   * @brief Bind a registered EventSubscriber member function to a Subscription.
-   *
-   * @param entry A templated EventSubscriber member function.
-   * @param sc The subscription context.
-   */
-  template <class T, typename C>
-  void subscribe(Status (T::*entry)(const std::shared_ptr<C>&, const void*),
-                 const SubscriptionContextRef& sc,
-                 void* user_data) {
-    // Up-cast the EventSubscriber to the caller.
-    auto sub = dynamic_cast<T*>(this);
-    // Down-cast the pointer to the member function.
-    auto base_entry =
-        reinterpret_cast<Status (T::*)(const EventContextRef&, void const*)>(
-            entry);
-    // Create a callable through the member function using the instance of the
-    // EventSubscriber and a single parameter placeholder (the EventContext).
-    auto cb = std::bind(base_entry, sub, _1, _2);
-    // Add a subscription using the callable and SubscriptionContext.
-    EventFactory::addSubscription(getType(), sub->getName(), sc, cb, user_data);
-  }
-
-  /**
-   * @brief The registry plugin name for the subscriber's publisher.
-   *
-   * During event factory initialization the subscribers 'peek' at the registry
-   * plugin name assigned to publishers. The corresponding publisher name is
-   * interpreted as the subscriber's event 'type'.
-   */
-  EventPublisherID& getType() const {
-    static EventPublisherID type = EventFactory::getType<PUB>();
-    return type;
-  }
-
-  /// See getType for lookup rational.
-  EventPublisherID& dbNamespace() const {
-    static EventPublisherID _ns = getType() + '.' + getName();
-    return _ns;
-  }
-
- public:
-  /**
-   * @brief Request the subscriber's initialization state.
-   *
-   * When event subscribers are created (initialized) they are expected to emit
-   * a set of subscriptions to their publisher "type". If the subscriber fails
-   * to initialize then the publisher may remove any intermediate subscriptions.
-   */
-  EventSubscriberState state() const { return state_; }
-
-  /// Set the subscriber state.
-  void state(EventSubscriberState state) { state_ = state; }
-
-  EventSubscriber() : EventSubscriberPlugin(), state_(SUBSCRIBER_NONE) {}
-
- private:
-  /// The event subscriber's run state.
-  EventSubscriberState state_;
-
- private:
-  FRIEND_TEST(EventsTests, test_event_sub);
-  FRIEND_TEST(EventsTests, test_event_sub_subscribe);
-  FRIEND_TEST(EventsTests, test_event_sub_context);
-};
-
-/// Iterate the event publisher registry and create run loops for each using
-/// the event factory.
-void attachEvents();
-
-/// Sleep in a boost::thread interruptible state.
-void publisherSleep(size_t milli);
-
-CREATE_REGISTRY(EventPublisherPlugin, "event_publisher");
-CREATE_REGISTRY(EventSubscriberPlugin, "event_subscriber");
-}
diff --git a/include/osquery/extensions.h b/include/osquery/extensions.h
deleted file mode 100644 (file)
index cd2e45c..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <osquery/core.h>
-#include <osquery/flags.h>
-#include <osquery/sql.h>
-
-namespace osquery {
-
-DECLARE_int32(worker_threads);
-DECLARE_string(extensions_socket);
-DECLARE_string(extensions_autoload);
-DECLARE_string(extensions_timeout);
-DECLARE_bool(disable_extensions);
-
-/// A millisecond internal applied to extension initialization.
-extern const size_t kExtensionInitializeLatencyUS;
-
-/**
- * @brief Helper struct for managing extenion metadata.
- *
- * This structure should match the members of Thrift's InternalExtensionInfo.
- */
-struct ExtensionInfo {
-  std::string name;
-  std::string version;
-  std::string min_sdk_version;
-  std::string sdk_version;
-};
-
-typedef std::map<RouteUUID, ExtensionInfo> ExtensionList;
-
-inline std::string getExtensionSocket(
-    RouteUUID uuid, const std::string& path = FLAGS_extensions_socket) {
-  if (uuid == 0) {
-    return path;
-  } else {
-    return path + "." + std::to_string(uuid);
-  }
-}
-
-/// External (extensions) SQL implementation of the osquery query API.
-Status queryExternal(const std::string& query, QueryData& results);
-
-/// External (extensions) SQL implementation of the osquery getQueryColumns API.
-Status getQueryColumnsExternal(const std::string& q, TableColumns& columns);
-
-/// External (extensions) SQL implementation plugin provider for "sql" registry.
-class ExternalSQLPlugin : SQLPlugin {
- public:
-  Status query(const std::string& q, QueryData& results) const {
-    return queryExternal(q, results);
-  }
-
-  Status getQueryColumns(const std::string& q, TableColumns& columns) const {
-    return getQueryColumnsExternal(q, columns);
-  }
-};
-
-/// Status get a list of active extenions.
-Status getExtensions(ExtensionList& extensions);
-
-/// Internal getExtensions using a UNIX domain socket path.
-Status getExtensions(const std::string& manager_path,
-                     ExtensionList& extensions);
-
-/// Ping an extension manager or extension.
-Status pingExtension(const std::string& path);
-
-/**
- * @brief Request the extensions API to autoload any appropriate extensions.
- *
- * Extensions may be 'autoloaded' using the `extensions_autoload` command line
- * argument. loadExtensions should be called before any plugin or registry item
- * is used. This allows appropriate extensions to expose plugin requirements.
- *
- * An 'appropriate' extension is one within the `extensions_autoload` search
- * path with file ownership equivilent or greater (root) than the osquery
- * process requesting autoload.
- */
-void loadExtensions();
-
-/**
- * @brief Load extensions from a delimited search path string.
- *
- * @param paths A colon-delimited path variable, e.g: '/path1:/path2'.
- */
-Status loadExtensions(const std::string& loadfile);
-
-/**
- * @brief Request the extensions API to autoload any appropriate modules.
- *
- * Extension modules are shared libraries that add Plugins to the osquery
- * core's registry at runtime.
- */
-void loadModules();
-
-/**
- * @brief Load extenion modules from a delimited search path string.
- *
- * @param paths A colon-delimited path variable, e.g: '/path1:/path2'.
- */
-Status loadModules(const std::string& loadfile);
-
-/// Load all modules in a direcotry.
-Status loadModuleFile(const std::string& path);
-
-/**
- * @brief Call a Plugin exposed by an Extension Registry route.
- *
- * This is mostly a Registry%-internal method used to call an ExtensionHandler
- * call API if a Plugin is requested and had matched an Extension route.
- *
- * @param uuid Route UUID of the matched Extension
- * @param registry The string name for the registry.
- * @param item A string identifier for this registry item.
- * @param request The plugin request input.
- * @param response The plugin response output.
- * @return Success indicates Extension API call success and Extension's
- * Registry::call success.
- */
-Status callExtension(const RouteUUID uuid,
-                     const std::string& registry,
-                     const std::string& item,
-                     const PluginRequest& request,
-                     PluginResponse& response);
-
-/// Internal callExtension implementation using a UNIX domain socket path.
-Status callExtension(const std::string& extension_path,
-                     const std::string& registry,
-                     const std::string& item,
-                     const PluginRequest& request,
-                     PluginResponse& response);
-
-/// The main runloop entered by an Extension, start an ExtensionRunner thread.
-Status startExtension(const std::string& name, const std::string& version);
-
-/// The main runloop entered by an Extension, start an ExtensionRunner thread.
-Status startExtension(const std::string& name,
-                      const std::string& version,
-                      const std::string& min_sdk_version);
-
-/// Internal startExtension implementation using a UNIX domain socket path.
-Status startExtension(const std::string& manager_path,
-                      const std::string& name,
-                      const std::string& version,
-                      const std::string& min_sdk_version,
-                      const std::string& sdk_version);
-
-/// Start an ExtensionWatcher thread.
-Status startExtensionWatcher(const std::string& manager_path,
-                             size_t interval,
-                             bool fatal);
-
-/// Start an ExtensionManagerRunner thread.
-Status startExtensionManager();
-
-/// Internal startExtensionManager implementation.
-Status startExtensionManager(const std::string& manager_path);
-}
diff --git a/include/osquery/filesystem.h b/include/osquery/filesystem.h
deleted file mode 100644 (file)
index 8c4ef80..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <boost/filesystem/path.hpp>
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/status.h>
-
-namespace osquery {
-
-/// Globbing directory traversal function recursive limit.
-typedef unsigned short GlobLimits;
-
-enum {
-  GLOB_FILES = 0x1,
-  GLOB_FOLDERS = 0x2,
-  GLOB_ALL = GLOB_FILES | GLOB_FOLDERS,
-};
-
-/// Globbing wildcard character.
-const std::string kSQLGlobWildcard = "%";
-/// Globbing wildcard recursive character (double wildcard).
-const std::string kSQLGlobRecursive = kSQLGlobWildcard + kSQLGlobWildcard;
-
-/**
- * @brief Read a file from disk.
- *
- * @param path the path of the file that you would like to read.
- * @param content a reference to a string which will be populated with the
- * contents of the path indicated by the path parameter.
- * @param dry_run do not actually read the file content.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status readFile(const boost::filesystem::path& path,
-                std::string& content,
-                bool dry_run = false);
-
-/**
- * @brief Return the status of an attempted file read.
- *
- * @param path the path of the file that you would like to read.
- *
- * @return success iff the file would have been read. On success the status
- * message is the complete/absolute path.
- */
-Status readFile(const boost::filesystem::path& path);
-
-/**
- * @brief Write text to disk.
- *
- * @param path the path of the file that you would like to write.
- * @param content the text that should be written exactly to disk.
- * @param permissions the filesystem permissions to request when opening.
- * @param force_permissions always `chmod` the path after opening.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status writeTextFile(const boost::filesystem::path& path,
-                     const std::string& content,
-                     int permissions = 0660,
-                     bool force_permissions = false);
-
-/// Check if a path is writable.
-Status isWritable(const boost::filesystem::path& path);
-
-/// Check if a path is readable.
-Status isReadable(const boost::filesystem::path& path);
-
-/**
- * @brief A helper to check if a path exists on disk or not.
- *
- * @param path Target path.
- *
- * @return The code of the Status instance will be -1 if no input was supplied,
- * assuming the caller is not aware of how to check path-getter results.
- * The code will be 0 if the path does not exist on disk and 1 if the path
- * does exist on disk.
- */
-Status pathExists(const boost::filesystem::path& path);
-
-/**
- * @brief List all of the files in a specific directory, non-recursively.
- *
- * @param path the path which you would like to list.
- * @param results a non-const reference to a vector which will be populated
- * with the directory listing of the path param, assuming that all operations
- * completed successfully.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status listFilesInDirectory(const boost::filesystem::path& path,
-                            std::vector<std::string>& results,
-                            bool ignore_error = 1);
-
-/**
- * @brief List all of the directories in a specific directory, non-recursively.
- *
- * @param path the path which you would like to list
- * @param results a non-const reference to a vector which will be populated
- * with the directory listing of the path param, assuming that all operations
- * completed successfully.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status listDirectoriesInDirectory(const boost::filesystem::path& path,
-                                  std::vector<std::string>& results,
-                                  bool ignore_error = 1);
-
-/**
- * @brief Given a filesystem globbing patten, resolve all matching paths.
- *
- * @code{.cpp}
- *   std::vector<std::string> results;
- *   auto s = resolveFilePattern("/Users/marpaia/Downloads/%", results);
- *   if (s.ok()) {
- *     for (const auto& result : results) {
- *       LOG(INFO) << result;
- *     }
- *   }
- * @endcode
- *
- * @param pattern filesystem globbing pattern.
- * @param results output vector of matching paths.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status resolveFilePattern(const boost::filesystem::path& pattern,
-                          std::vector<std::string>& results);
-
-/**
- * @brief Given a filesystem globbing patten, resolve all matching paths.
- *
- * See resolveFilePattern, but supply a limitation to request only directories
- * or files that match the path.
- *
- * @param pattern filesystem globbing pattern.
- * @param results output vector of matching paths.
- * @param setting a bit list of match types, e.g., files, folders.
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status resolveFilePattern(const boost::filesystem::path& pattern,
-                          std::vector<std::string>& results,
-                          GlobLimits setting);
-
-/**
- * @brief Transform a path with SQL wildcards to globbing wildcard.
- *
- * SQL uses '%' as a wildcard matching token, and filesystem globbing uses '*'.
- * In osquery-internal methods the filesystem character is used. This helper
- * method will perform the correct preg/escape and replace.
- *
- * This has a side effect of canonicalizing paths up to the first wildcard.
- * For example: /tmp/% becomes /private/tmp/% on OS X systems. And /tmp/%.
- *
- * @param pattern the input and output filesystem glob pattern.
- */
-void replaceGlobWildcards(std::string& pattern);
-
-/**
- * @brief Get directory portion of a path.
- *
- * @param path input path, either a filename or directory.
- * @param dirpath output path set to the directory-only path.
- *
- * @return If the input path was a directory this will indicate failure. One
- * should use `isDirectory` before.
- */
-Status getDirectory(const boost::filesystem::path& path,
-                    boost::filesystem::path& dirpath);
-
-/// Attempt to remove a directory path.
-Status remove(const boost::filesystem::path& path);
-
-/**
- * @brief Check if an input path is a directory.
- *
- * @param path input path, either a filename or directory.
- *
- * @return If the input path was a directory.
- */
-Status isDirectory(const boost::filesystem::path& path);
-
-/**
- * @brief Return a vector of all home directories on the system.
- *
- * @return a vector of string paths containing all home directories.
- */
-std::set<boost::filesystem::path> getHomeDirectories();
-
-/**
- * @brief Check the permissions of a file and its directory.
- *
- * 'Safe' implies the directory is not a /tmp-like directory in that users
- * cannot control super-user-owner files. The file should be owned by the
- * process's UID or the file should be owned by root.
- *
- * @param dir the directory to check `/tmp` mode.
- * @param path a path to a file to check.
- * @param executable true if the file must also be executable.
- *
- * @return true if the file is 'safe' else false.
- */
-bool safePermissions(const std::string& dir,
-                     const std::string& path,
-                     bool executable = false);
-
-/**
- * @brief osquery may use local storage in a user-protected "home".
- *
- * Return a standard path to an "osquery" home directory. This path may store
- * a protected extensions socket, backing storage database, and debug logs.
- */
-const std::string& osqueryHomeDirectory();
-
-/// Return bit-mask-style permissions.
-std::string lsperms(int mode);
-
-/**
- * @brief Parse a JSON file on disk into a property tree.
- *
- * @param path the path of the JSON file.
- * @param tree output property tree.
- *
- * @return an instance of Status, indicating success or failure if malformed.
- */
-Status parseJSON(const boost::filesystem::path& path,
-                 boost::property_tree::ptree& tree);
-
-/**
- * @brief Parse JSON content into a property tree.
- *
- * @param path JSON string data.
- * @param tree output property tree.
- *
- * @return an instance of Status, indicating success or failure if malformed.
- */
-Status parseJSONContent(const std::string& content,
-                        boost::property_tree::ptree& tree);
-
-#ifdef __APPLE__
-/**
- * @brief Parse a property list on disk into a property tree.
- *
- * @param path the input path to a property list.
- * @param tree the output property tree.
- *
- * @return an instance of Status, indicating success or failure if malformed.
- */
-Status parsePlist(const boost::filesystem::path& path,
-                  boost::property_tree::ptree& tree);
-
-/**
- * @brief Parse property list content into a property tree.
- *
- * @param content the input string-content of a property list.
- * @param tree the output property tree.
- *
- * @return an instance of Status, indicating success or failure if malformed.
- */
-Status parsePlistContent(const std::string& content,
-                         boost::property_tree::ptree& tree);
-#endif
-
-#ifdef __linux__
-/**
- * @brief Iterate over `/proc` process, returns a list of pids.
- *
- * @param processes output list of process pids as strings (int paths in proc).
- *
- * @return an instance of Status, indicating success or failure.
- */
-Status procProcesses(std::set<std::string>& processes);
-
-/**
- * @brief Iterate over a `/proc` process's descriptors, return a list of fds.
- *
- * @param process a string pid from proc.
- * @param descriptors output list of descriptor numbers as strings.
- *
- * @return status of iteration, failure if the process path did not exist.
- */
-Status procDescriptors(const std::string& process,
-                       std::map<std::string, std::string>& descriptors);
-
-/**
- * @brief Read a descriptor's virtual path.
- *
- * @param process a string pid from proc.
- * @param descriptor a string descriptor number for a proc.
- * @param result output variable with value of link.
- *
- * @return status of read, failure on permission error or filesystem error.
- */
-Status procReadDescriptor(const std::string& process,
-                          const std::string& descriptor,
-                          std::string& result);
-
-/**
- * @brief Read bytes from Linux's raw memory.
- *
- * Most Linux kernels include a device node /dev/mem that allows privileged
- * users to map or seek/read pages of physical memory.
- * osquery discourages the use of physical memory reads for security and
- * performance reasons and must first try safer methods for data parsing
- * such as /sys and /proc.
- *
- * A platform user may disable physical memory reads:
- *   --disable_memory=true
- * This flag/option will cause readRawMemory to forcefully fail.
- *
- * @param base The absolute memory address to read from. This does not need
- * to be page aligned, readRawMem will take care of alignment and only
- * return the requested start address and size.
- * @param length The length of the buffer with a max of 0x10000.
- * @param buffer The output buffer, caller is responsible for resources if
- * readRawMem returns success.
- * @return status The status of the read.
- */
-Status readRawMem(size_t base, size_t length, void** buffer);
-
-#endif
-}
diff --git a/include/osquery/flags.h b/include/osquery/flags.h
deleted file mode 100644 (file)
index 8eb17d9..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-
-#include <boost/lexical_cast.hpp>
-
-#define STRIP_FLAG_HELP 1
-#include <gflags/gflags.h>
-
-#include <osquery/core.h>
-
-#define GFLAGS_NAMESPACE google
-
-namespace boost {
-/// We define a lexical_cast template for boolean for Gflags boolean string
-/// values.
-template <>
-bool lexical_cast<bool, std::string>(const std::string& arg);
-
-template <>
-std::string lexical_cast<std::string, bool>(const bool& b);
-}
-
-namespace osquery {
-
-struct FlagDetail {
-  std::string description;
-  bool shell;
-  bool external;
-  bool cli;
-  bool hidden;
-};
-
-struct FlagInfo {
-  std::string type;
-  std::string description;
-  std::string default_value;
-  std::string value;
-  FlagDetail detail;
-};
-
-/**
- * @brief A small tracking wrapper for options, binary flags.
- *
- * The osquery-specific gflags-like options define macro `FLAG` uses a Flag
- * instance to track the options data.
- */
-class Flag {
- public:
-  /*
-   * @brief Create a new flag.
-   *
-   * @param name The 'name' or the options switch data.
-   * @param flag Flag information filled in using the helper macro.
-   *
-   * @return A mostly needless flag instance.
-   */
-  static int create(const std::string& name, const FlagDetail& flag);
-
-  /// Create a Gflags alias to name, using the Flag::getValue accessor.
-  static int createAlias(const std::string& alias, const FlagDetail& flag);
-
-  static Flag& instance() {
-    static Flag f;
-    return f;
-  }
-
- private:
-  /// Keep the ctor private, for accessing through `add` wrapper.
-  Flag() {}
-  virtual ~Flag() {}
-
-  Flag(Flag const&);
-  void operator=(Flag const&);
-
- public:
-  /// The public flags instance, usable when parsing `--help`.
-  static std::map<std::string, FlagInfo> flags();
-
-  /*
-   * @brief Access value for a flag name.
-   *
-   * @param name the flag name.
-   * @param value output parameter filled with the flag value on success.
-   * @return status of the flag did exist.
-   */
-  static Status getDefaultValue(const std::string& name, std::string& value);
-
-  /*
-   * @brief Check if flag value has been overridden.
-   *
-   * @param name the flag name.
-   * @return is the flag set to its default value.
-   */
-  static bool isDefault(const std::string& name);
-
-  /*
-   * @brief Update the flag value by string name,
-   *
-   * @param name the flag name.
-   * @parma value the new value.
-   * @return if the value was updated.
-   */
-  static Status updateValue(const std::string& name, const std::string& value);
-
-  /*
-   * @brief Get the value of an osquery flag.
-   *
-   * @param name the flag name.
-   */
-  static std::string getValue(const std::string& name);
-
-  /*
-   * @brief Get the type as a string of an osquery flag.
-   *
-   * @param name the flag name.
-   */
-  static std::string getType(const std::string& name);
-
-  /*
-   * @brief Get the description as a string of an osquery flag.
-   *
-   * @param name the flag name.
-   */
-  static std::string getDescription(const std::string& name);
-
-  /*
-   * @brief Print help-style output to stdout for a given flag set.
-   *
-   * @param shell Only print shell flags.
-   * @param external Only print external flags (from extensions).
-   */
-  static void printFlags(bool shell = false,
-                         bool external = false,
-                         bool cli = false);
-
- private:
-  std::map<std::string, FlagDetail> flags_;
-  std::map<std::string, FlagDetail> aliases_;
-};
-
-/**
- * @brief Helper accessor/assignment alias class to support deprecated flags.
- *
- * This templated class wraps Flag::updateValue and Flag::getValue to 'alias'
- * a deprecated flag name as the updated name. The helper macro FLAG_ALIAS
- * will create a global variable instances of this wrapper using the same
- * Gflags naming scheme to prevent collisions and support existing callsites.
- */
-template <typename T>
-class FlagAlias {
- public:
-  FlagAlias& operator=(T const& v) {
-    Flag::updateValue(name_, boost::lexical_cast<std::string>(v));
-    return *this;
-  }
-
-  operator T() const { return boost::lexical_cast<T>(Flag::getValue(name_)); }
-
-  FlagAlias(const std::string& alias,
-            const std::string& type,
-            const std::string& name,
-            T* storage)
-      : name_(name) {}
-
- private:
-  std::string name_;
-};
-}
-
-/*
- * @brief Replace gflags' `DEFINE_type` macros to track osquery flags.
- *
- * @param type The `_type` symbol portion of the gflags define.
- * @param name The name symbol passed to gflags' `DEFINE_type`.
- * @param value The default value, use a C++ literal.
- * @param desc A string literal used for help display.
- */
-#define OSQUERY_FLAG(t, n, v, d, s, e, c, h)              \
-  DEFINE_##t(n, v, d);                                    \
-  namespace flags {                                       \
-  const int flag_##n = Flag::create(#n, {d, s, e, c, h}); \
-  }
-
-#define FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 0, 0)
-#define SHELL_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 1, 0, 0, 0)
-#define EXTENSION_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 1, 0, 0)
-#define CLI_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 1, 0)
-#define HIDDEN_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 0, 1)
-
-#define OSQUERY_FLAG_ALIAS(t, a, n, s, e)                             \
-  FlagAlias<t> FLAGS_##a(#a, #t, #n, &FLAGS_##n);                     \
-  namespace flags {                                                   \
-  static GFLAGS_NAMESPACE::FlagRegisterer oflag_##a(                  \
-      #a, #t, #a, &FLAGS_##n, &FLAGS_##n);                            \
-  const int flag_alias_##a = Flag::createAlias(#a, {#n, s, e, 0, 1}); \
-  }
-
-#define FLAG_ALIAS(t, a, n) OSQUERY_FLAG_ALIAS(t, a, n, 0, 0)
-#define SHELL_FLAG_ALIAS(t, a, n) _OSQUERY_FLAG_ALIAS(t, a, n, 1, 0)
-#define EXTENSION_FLAG_ALIAS(a, n) OSQUERY_FLAG_ALIAS(std::string, a, n, 0, 1)
diff --git a/include/osquery/hash.h b/include/osquery/hash.h
deleted file mode 100644 (file)
index b6cc5ae..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <string>
-
-namespace osquery {
-
-/**
- * @brief The supported hashing algorithms in osquery
- *
- * These are usually used as a constructor argument to osquery::Hash
- */
-enum HashType {
-  HASH_TYPE_MD5 = 2,
-  HASH_TYPE_SHA1 = 4,
-  HASH_TYPE_SHA256 = 8,
-};
-
-/**
- * @brief Hash is a general utility class for hashing content
- *
- * @code{.cpp}
- *   Hash my_hash(HASH_TYPE_SHA256);
- *   my_hash.update(my_buffer, my_buffer_size);
- *   std::cout << my_hash.digest();
- * @endcode
- *
- */
-class Hash {
- public:
-  /**
-   * @brief Hash constructor
-   *
-   * The hash class should be initialized with one of osquery::HashType as a
-   * constructor argument.
-   *
-   * @param algorithm The hashing algorithm which will be used to compute the
-   * hash
-   */
-  explicit Hash(HashType algorithm);
-
-  /**
-   * @brief Hash destructor
-   */
-  ~Hash();
-
-  /**
-   * @brief Update the internal context buffer with additional content
-   *
-   * This method allows you to chunk up large content so that it doesn't all
-   * have to be loaded into memory at the same time
-   *
-   * @param buffer The buffer to be hashed
-   * @param size The size of the buffer to be hashed
-   */
-  void update(const void* buffer, size_t size);
-
-  /**
-   * @brief Compute the final hash and return it's result
-   *
-   * @return The final hash value
-   */
-  std::string digest();
-
- private:
-  /**
-   * @brief Private default constructor
-   *
-   * The osquery::Hash class should only ever be instantiated with a HashType
-   */
-  Hash(){};
-
- private:
-  /// The hashing algorithm which is used to compute the hash
-  HashType algorithm_;
-
-  /// The buffer used to maintain the context and state of the hashing
-  /// operations
-  void* ctx_;
-
-  /// The length of the hash to be returned
-  size_t length_;
-};
-
-/**
- * @brief Compute a hash digest from an already allocated buffer.
- *
- * @param hash_type The osquery-supported hash algorithm.
- * @param buffer A caller-controlled buffer.
- * @param size The length of buffer in bytes.
- * @return A string (hex) representation of the hash digest.
- */
-std::string hashFromBuffer(HashType hash_type, const void* buffer, size_t size);
-
-/**
- * @brief Compute a hash digest from the file content at a path.
- *
- *
- * @param hash_type The osquery-supported hash algorithm.
- * @param path Filesystem path, the hash target.
- * @return A string (hex) representation of the hash digest.
- */
-std::string hashFromFile(HashType hash_type, const std::string& path);
-}
diff --git a/include/osquery/logger.h b/include/osquery/logger.h
deleted file mode 100644 (file)
index 0ef8b15..0000000
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include <glog/logging.h>
-
-#include <osquery/database.h>
-#include <osquery/flags.h>
-#include <osquery/registry.h>
-
-namespace osquery {
-
-DECLARE_bool(disable_logging);
-DECLARE_string(logger_plugin);
-
-/**
- * @brief An internal severity set mapping to Glog's LogSeverity levels.
- */
-enum StatusLogSeverity {
-  O_INFO = 0,
-  O_WARNING = 1,
-  O_ERROR = 2,
-  O_FATAL = 3,
-};
-
-/// An intermediate status log line.
-struct StatusLogLine {
- public:
-  /// An integer severity level mimicing Glog's.
-  StatusLogSeverity severity;
-  /// The name of the file emitting the status log.
-  std::string filename;
-  /// The line of the file emitting the status log.
-  int line;
-  /// The string-formatted status message.
-  std::string message;
-};
-
-/**
- * @brief Helper logging macro for table-generated verbose log lines.
- *
- * Since logging in tables does not always mean a critical warning or error
- * but more likely a parsing or expected edge-case, we provide a TLOG.
- *
- * The tool user can set within config or via the CLI what level of logging
- * to tolerate. It's the table developer's job to assume consistency in logging.
- */
-#define TLOG VLOG(1)
-
-/**
- * @brief Prepend a reference number to the log line.
- *
- * A reference number is an external-search helper for somewhat confusing or
- * seeminly-critical log lines.
- */
-#define RLOG(n) "[Ref #" #n "] "
-
-/**
- * @brief Superclass for the pluggable logging facilities.
- *
- * In order to make the logging of osquery results and inline debug, warning,
- * error status easy to integrate into your environment, we take advantage of
- * a plugin interface which allows you to integrate osquery with your internal
- * large-scale logging infrastructure.
- *
- * You may use flume, splunk, syslog, scribe, etc. In order to use your
- * specific upstream logging systems, one simply needs to create a custom
- * subclass of LoggerPlugin. That subclass should at least implement the
- * LoggerPlugin::logString method.
- *
- * Consider the following example:
- *
- * @code{.cpp}
- *   class TestLoggerPlugin : public LoggerPlugin {
- *    public:
- *     osquery::Status logString(const std::string& s) {
- *       int i = 0;
- *       internal::logStringToFlume(s, i);
- *       std::string message;
- *       if (i == 0) {
- *         message = "OK";
- *       } else {
- *         message = "Failed";
- *       }
- *       return osquery::Status(i, message);
- *     }
- *  };
- *
- *  REGISTER(TestLoggerPlugin, "logger", "test");
- * @endcode
- */
-class LoggerPlugin : public Plugin {
- public:
-  /// The LoggerPlugin PluginRequest action router.
-  Status call(const PluginRequest& request, PluginResponse& response);
-
- protected:
-  /** @brief Virtual method which should implement custom logging.
-   *
-   *  LoggerPlugin::logString should be implemented by a subclass of
-   *  LoggerPlugin which needs to log a string in a custom way.
-   *
-   *  @return an instance of osquery::Status which indicates the success or
-   *  failure of the operation.
-   */
-  virtual Status logString(const std::string& s) = 0;
-
-  /**
-   * @brief Initialize the logger with the name of the binary and any status
-   * logs generated between program launch and logger start.
-   *
-   * The logger initialization is called once CLI flags have been parsed, the
-   * registry items are constructed, extension routes broadcasted and extension
-   * plugins discovered (as a logger may be an extension plugin) and the config
-   * has been loaded (which may include additional CLI flag-options).
-   *
-   * All of these actions may have generated VERBOSE, INFO, WARNING, or ERROR
-   * logs. The internal logging facility, Glog, collects these intermediate
-   * status logs and a customized log sink buffers them until the active
-   * osquery logger's `init` method is called.
-   *
-   * The return status of `init` is very important. If a success is returned
-   * then the Glog log sink stays active and now forwards every status log
-   * to the logger's `logStatus` method. If a failure is returned this means
-   * the logger does not support status logging and Glog should continue
-   * as the only status log sink.
-   *
-   * @param binary_name The string name of the process (argv[0]).
-   * @param log The set of status (INFO, WARNING, ERROR) logs generated before
-   * the logger's `init` method was called.
-   * @return Status success if the logger will continue to handle status logs
-   * using `logStatus` or failure if status logging is not supported.
-   */
-  virtual Status init(const std::string& binary_name,
-                      const std::vector<StatusLogLine>& log) {
-    return Status(1, "Status logs are not supported by this logger");
-  }
-
-  /**
-   * @brief If the active logger's `init` method returned success then Glog
-   * log lines will be collected, and forwarded to `logStatus`.
-   *
-   * `logStatus` and `init` are tightly coupled. Glog log lines will ONLY be
-   * forwarded to `logStatus` if the logger's `init` method returned success.
-   *
-   * @param log A vector of parsed Glog log lines.
-   * @return Status non-op indicating success or failure.
-   */
-  virtual Status logStatus(const std::vector<StatusLogLine>& log) {
-    return Status(1, "Not enabled");
-  }
-
-  /**
-   * @brief Optionally handle snapshot query results separately from events.
-   *
-   * If a logger plugin wants to write snapshot query results (potentially
-   * large amounts of data) to a specific sink it should implement logSnapshot.
-   * Otherwise the serialized log item data will be forwarded to logString.
-   *
-   * @param s A special log item will complete results from a query.
-   * @return log status
-   */
-  virtual Status logSnapshot(const std::string& s) { return logString(s); }
-
-  /// An optional health logging facility.
-  virtual Status logHealth(const std::string& s) {
-    return Status(1, "Not used");
-  }
-};
-
-/// Set the verbose mode, changes Glog's sinking logic and will affect plugins.
-void setVerboseLevel();
-
-/// Start status logging to a buffer until the logger plugin is online.
-void initStatusLogger(const std::string& name);
-
-/**
- * @brief Initialize the osquery Logger facility by dumping the buffered status
- * logs and configurating status log forwarding.
- *
- * initLogger will disable the `BufferedLogSink` facility, dump any status logs
- * emitted between process start and this init call, then configure the new
- * logger facility to receive status logs.
- *
- * The `forward_all` control is used when buffering logs in extensions.
- * It is fine if the logger facility in the core app does not want to receive
- * status logs, but this is NOT an option in extensions/modules. All status
- * logs must be forwarded to the core.
- *
- * @param name The process name.
- * @param forward_all Override the LoggerPlugin::init forwarding decision.
- */
-void initLogger(const std::string& name, bool forward_all = false);
-
-/**
- * @brief Log a string using the default logger receiver.
- *
- * Note that this method should only be used to log results. If you'd like to
- * log normal osquery operations, use Google Logging.
- *
- * @param s the string to log
- * @param category a category/metadata key
- *
- * @return Status indicating the success or failure of the operation
- */
-Status logString(const std::string& message, const std::string& category);
-
-/**
- * @brief Log a string using a specific logger receiver.
- *
- * Note that this method should only be used to log results. If you'd like to
- * log normal osquery operations, use Google Logging.
- *
- * @param message the string to log
- * @param category a category/metadata key
- * @param receiver a string representing the log receiver to use
- *
- * @return Status indicating the success or failure of the operation
- */
-Status logString(const std::string& message,
-                 const std::string& category,
-                 const std::string& receiver);
-
-/**
- * @brief Log results of scheduled queries to the default receiver
- *
- * @param item a struct representing the results of a scheduled query
- *
- * @return Status indicating the success or failure of the operation
- */
-Status logQueryLogItem(const QueryLogItem& item);
-
-/**
- * @brief Log results of scheduled queries to a specified receiver
- *
- * @param item a struct representing the results of a scheduled query
- * @param receiver a string representing the log receiver to use
- *
- * @return Status indicating the success or failure of the operation
- */
-Status logQueryLogItem(const QueryLogItem& item, const std::string& receiver);
-
-/**
- * @brief Log raw results from a query (or a snapshot scheduled query).
- *
- * @param results the unmangled results from the query planner.
- *
- * @return Status indicating the success or failure of the operation
- */
-Status logSnapshotQuery(const QueryLogItem& item);
-
-/**
- * @brief Log the worker's health along with health of each query.
- *
- * @param results the query results from the osquery schedule appended with a
- * row of health from the worker.
- *
- * @return Status indicating the success or failure of the operation
- */
-Status logHealthStatus(const QueryLogItem& item);
-
-/**
- * @brief Sink a set of buffered status logs.
- *
- * When the osquery daemon uses a watcher/worker set, the watcher's status logs
- * are accumulated in a buffered log sink. Well-performing workers should have
- * the set of watcher status logs relayed and sent to the configured logger
- * plugin.
- *
- * Status logs from extensions will be forwarded to the extension manager (core)
- * normally, but the watcher does not receive or send registry requests.
- * Extensions, the registry, configuration, and optional config/logger plugins
- * are all protected as a monitored worker.
- */
-void relayStatusLogs();
-
-/**
- * @brief Logger plugin registry.
- *
- * This creates an osquery registry for "logger" which may implement
- * LoggerPlugin. Only strings are logged in practice, and LoggerPlugin provides
- * a helper member for transforming PluginRequest%s to strings.
- */
-CREATE_REGISTRY(LoggerPlugin, "logger");
-}
diff --git a/include/osquery/notification.h b/include/osquery/notification.h
deleted file mode 100644 (file)
index 4f33588..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-
-
-/**
- * @file notification.h
- * @brief Notify to registered stuffs when event-callback called
- */
-
-
-#pragma once
-
-#include <map>
-#include <vector>
-
-#include <osquery_manager.h>
-
-#include <osquery/database.h>
-#include <osquery/status.h>
-#include <osquery/registry.h>
-
-namespace osquery {
-
-using NotifyCallback = Callback;
-
-class Notification final {
-public:
-       static Notification& instance();
-
-       Status add(const std::string& table, const NotifyCallback& callback);
-       Status emit(const std::string& table, const Row& result) const;
-
-public:
-       Notification(const Notification&) = delete;
-       Notification& operator=(const Notification&) = delete;
-
-private:
-       Notification() = default;
-       ~Notification() = default;
-
-       std::multimap<std::string, NotifyCallback> callbacks;
-};
-
-} // namespace osquery
diff --git a/include/osquery/registry.h b/include/osquery/registry.h
deleted file mode 100644 (file)
index 1ea4587..0000000
+++ /dev/null
@@ -1,737 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <mutex>
-#include <vector>
-#include <set>
-
-#include <boost/noncopyable.hpp>
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/core.h>
-
-namespace osquery {
-
-/**
- * @brief A boilerplate code helper to create a registry given a name and
- * plugin base class type.
- *
- * Registries are types of plugins, e.g., config, logger, table. They are
- * defined with a string name and Plugin derived class. There is an expectation
- * that any 'item' registered will inherit from the registry plugin-derived
- * type. But there is NO type enforcement on that intermediate class.
- *
- * This boilerplate macro puts the registry into a 'registry' namespace for
- * organization and create a global const int that may be instantiated
- * in a header or implementation code without symbol duplication.
- * The initialization is also boilerplate, whereas the Registry::create method
- * (a whole-process-lived single instance object) creates and manages the
- * registry instance.
- *
- * @param type A typename that derives from Plugin.
- * @param name A string identifier for the registry.
- */
-#define CREATE_REGISTRY(type, name)              \
-  namespace registry {                           \
-  __constructor__ static void type##Registry() { \
-    Registry::create<type>(name);                \
-  }                                              \
-  }
-
-/**
- * @brief A boilerplate code helper to create a registry given a name and
- * plugin base class type. This 'lazy' registry does not run
- * Plugin::setUp on its items, so the registry will do it.
- *
- * @param type A typename that derives from Plugin.
- * @param name A string identifier for the registry.
- */
-#define CREATE_LAZY_REGISTRY(type, name)         \
-  namespace registry {                           \
-  __constructor__ static void type##Registry() { \
-    Registry::create<type>(name, true);          \
-  }                                              \
-  }
-
-/**
- * @brief A boilerplate code helper to register a plugin.
- *
- * Like CREATE_REGISTRY, REGISTER creates a boilerplate global instance to
- * create an instance of the plugin type within the whole-process-lived registry
- * single instance. Registry items must derive from the `RegistryType` defined
- * by the CREATE_REGISTRY and Registry::create call.
- *
- * @param type A typename that derives from the RegistryType.
- * @param registry The string name for the registry.
- * @param name A string identifier for this registry item.
- */
-#define REGISTER(type, registry, name)               \
-  __constructor__ static void type##RegistryItem() { \
-    Registry::add<type>(registry, name);             \
-  }
-
-/// The same as REGISTER but prevents the plugin item from being broadcasted.
-#define REGISTER_INTERNAL(type, registry, name)      \
-  __constructor__ static void type##RegistryItem() { \
-    Registry::add<type>(registry, name, true);       \
-  }
-
-/**
- * @brief The request part of a plugin (registry item's) call.
- *
- * To use a plugin use Registry::call with a request and response.
- * The request portion is usually simple and normally includes an "action"
- * key where the value is the action you want to perform on the plugin.
- * Refer to the registry's documentation for the actions supported by
- * each of its plugins.
- */
-typedef std::map<std::string, std::string> PluginRequest;
-/**
- * @brief The response part of a plugin (registry item's) call.
- *
- * If a Registry::call succeeds it will fill in a PluginResponse.
- * This response is a vector of key value maps.
- */
-typedef std::vector<PluginRequest> PluginResponse;
-
-/// Registry routes are a map of item name to each optional PluginReponse.
-typedef std::map<std::string, PluginResponse> RegistryRoutes;
-/// An extension or core's broadcast includes routes from every Registry.
-typedef std::map<std::string, RegistryRoutes> RegistryBroadcast;
-
-typedef uint16_t RouteUUID;
-typedef std::function<Status(const std::string&, const PluginResponse&)>
-    AddExternalCallback;
-typedef std::function<void(const std::string&)> RemoveExternalCallback;
-
-/// When a module is being initialized its information is kept in a transient
-/// RegistryFactory lookup location.
-struct ModuleInfo {
-  std::string path;
-  std::string name;
-  std::string version;
-  std::string sdk_version;
-};
-
-/// The call-in prototype for Registry modules.
-typedef void (*ModuleInitalizer)(void);
-
-template <class PluginItem>
-class PluginFactory {};
-
-class Plugin : private boost::noncopyable {
- public:
-  Plugin() : name_("unnamed") {}
-  virtual ~Plugin() {}
-
- public:
-  /// The plugin may perform some initialization, not required.
-  virtual Status setUp() { return Status(0, "Not used"); }
-
-  /// The plugin may perform some tear down, release, not required.
-  virtual void tearDown() {}
-
-  /// The plugin may publish route info (other than registry type and name).
-  virtual PluginResponse routeInfo() const {
-    PluginResponse info;
-    return info;
-  }
-
-  /**
-   * @brief Plugins act by being called, using a request, returning a response.
-   *
-   * The plugin request is a thrift-serializable object. A response is optional
-   * but the API for using a plugin's call is defined by the registry. In most
-   * cases there are multiple supported call 'actions'. A registry type, or
-   * the plugin class, will define the action key and supported actions.
-   *
-   * @param request A plugin request input, including optional action.
-   * @param response A plugin response output.
-   *
-   * @return Status of the call, if the action was handled corrected.
-   */
-  virtual Status call(const PluginRequest& request, PluginResponse& response) {
-    return Status(0, "Not used");
-  }
-
-  // Set the output request key to a serialized property tree.
-  // Used by the plugin to set a serialized PluginResponse.
-  static void setResponse(const std::string& key,
-                          const boost::property_tree::ptree& tree,
-                          PluginResponse& response);
-
-  // Get a PluginResponse key as a property tree.
-  static void getResponse(const std::string& key,
-                          const PluginResponse& response,
-                          boost::property_tree::ptree& tree);
-
-  /// Allow the plugin to introspect into the registered name (for logging).
-  void setName(const std::string& name) { name_ = name; }
-
-  const std::string& getName() const { return name_; }
-
-  /// Allow a specialized plugin type to act when an external plugin is
-  /// registered (e.g., a TablePlugin will attach the table name).
-  static Status addExternal(const std::string& name,
-                            const PluginResponse& info) {
-    return Status(0, "Not used");
-  }
-
-  /// Allow a specialized plugin type to act when an external plugin is removed.
-  static void removeExternal(const std::string& name) {}
-
- protected:
-  std::string name_;
-
- private:
-  Plugin(Plugin const&);
-  Plugin& operator=(Plugin const&);
-};
-
-class RegistryHelperCore : private boost::noncopyable {
- public:
-  explicit RegistryHelperCore(bool auto_setup = false)
-      : auto_setup_(auto_setup) {}
-  virtual ~RegistryHelperCore() {}
-
-  /**
-   * @brief Remove a registry item by its identifier.
-   *
-   * @param item_name An identifier for this registry plugin.
-   */
-  void remove(const std::string& item_name);
-
-  RegistryRoutes getRoutes() const;
-
-  /**
-   * @brief The only method a plugin user should call.
-   *
-   * Registry plugins are used internally and externally. They may belong
-   * to the process making the call or to an external process via a thrift
-   * transport.
-   *
-   * All plugin input and output must be serializable. The plugin types
-   * RegistryType usually exposes protected serialization methods for the
-   * data structures used by plugins (registry items).
-   *
-   * @param item_name The plugin identifier to call.
-   * @param request The plugin request, usually containing an action request.
-   * @param response If successful, the requested information.
-   * @return Success if the plugin was called, and response was filled.
-   */
-  virtual Status call(const std::string& item_name,
-                      const PluginRequest& request,
-                      PluginResponse& response);
-
-  Status add(const std::string& item_name, bool internal = false);
-
-  /**
-   * @brief Allow a plugin to perform some setup functions when osquery starts.
-   *
-   * Doing work in a plugin constructor has unknown behavior. Plugins may
-   * be constructed at anytime during osquery's life, including global variable
-   * instantiation. To have a reliable state (aka, flags have been parsed,
-   * and logs are ready to stream), do construction work in Plugin::setUp.
-   *
-   * The registry `setUp` will iterate over all of its registry items and call
-   * their setup unless the registry is lazy (see CREATE_REGISTRY).
-   */
-  virtual void setUp();
-
-  /// Facility method to check if a registry item exists.
-  bool exists(const std::string& item_name, bool local = false) const;
-
-  /// Create a registry item alias for a given item name.
-  Status addAlias(const std::string& item_name, const std::string& alias);
-
-  /// Get the registry item name for a given alias.
-  const std::string& getAlias(const std::string& alias) const;
-
-  /// Facility method to list the registry item identifiers.
-  std::vector<std::string> names() const;
-
-  /// Facility method to count the number of items in this registry.
-  size_t count() const;
-
-  /// Allow the registry to introspect into the registered name (for logging).
-  void setName(const std::string& name);
-
-  /// Allow others to introspect into the registered name (for reporting).
-  const std::string& getName() const { return name_; }
-
-  /// Check if a given plugin name is considered internal.
-  bool isInternal(const std::string& item_name) const;
-
-  /// Allow others to introspect into the routes from extensions.
-  const std::map<std::string, RouteUUID>& getExternal() const {
-    return external_;
-  }
-
-  /// Set an 'active' plugin to receive registry calls when no item name given.
-  Status setActive(const std::string& item_name);
-
-  /// Get the 'active' plugin, return success with the active plugin name.
-  const std::string& getActive() const;
-
- protected:
-  /// The identifier for this registry, used to register items.
-  std::string name_;
-  /// Does this registry run setUp on each registry item at initialization.
-  bool auto_setup_;
-
- protected:
-  /// A map of registered plugin instances to their registered identifier.
-  std::map<std::string, std::shared_ptr<Plugin> > items_;
-  /// If aliases are used, a map of alias to item name.
-  std::map<std::string, std::string> aliases_;
-  /// Keep a lookup of the external item name to assigned extension UUID.
-  std::map<std::string, RouteUUID> external_;
-  /// Keep a lookup of optional route info. The plugin may handle calls
-  /// to external items differently.
-  std::map<std::string, PluginResponse> routes_;
-  /// Keep a lookup of registry items that are blacklisted from broadcast.
-  std::vector<std::string> internal_;
-  /// Support an 'active' mode where calls without a specific item name will
-  /// be directed to the 'active' plugin.
-  std::string active_;
-  /// If a module was initialized/declared then store lookup information.
-  std::map<std::string, RouteUUID> modules_;
-};
-
-/**
- * @brief The core interface for each registry type.
- *
- * The osquery Registry is partitioned into types. These are literal types
- * but use a canonical string key for lookups and actions.
- * Registries are created using Registry::create with a RegistryType and key.
- */
-template <class RegistryType>
-class RegistryHelper : public RegistryHelperCore {
- protected:
-  typedef std::shared_ptr<RegistryType> RegistryTypeRef;
-
- public:
-  explicit RegistryHelper(bool auto_setup = false)
-      : RegistryHelperCore(auto_setup),
-        add_(&RegistryType::addExternal),
-        remove_(&RegistryType::removeExternal) {}
-  virtual ~RegistryHelper() {}
-
-  /**
-   * @brief Add a set of item names broadcasted by an extension uuid.
-   *
-   * When an extension is registered the RegistryFactory will receive a
-   * RegistryBroadcast containing a all of the extension's registry names and
-   * the set of items with their optional route info. The factory depends on
-   * each registry to manage calls/requests to these external plugins.
-   *
-   * @param uuid The uuid chosen for the extension.
-   * @param routes The plugin name and optional route info list.
-   * @return Success if all routes were added, failure if any failed.
-   */
-  Status addExternal(const RouteUUID& uuid, const RegistryRoutes& routes) {
-    // Add each route name (item name) to the tracking.
-    for (const auto& route : routes) {
-      // Keep the routes info assigned to the registry.
-      routes_[route.first] = route.second;
-      auto status = add_(route.first, route.second);
-      external_[route.first] = uuid;
-      if (!status.ok()) {
-        return status;
-      }
-    }
-    return Status(0, "OK");
-  }
-
-  /// Remove all the routes for a given uuid.
-  void removeExternal(const RouteUUID& uuid) {
-    std::vector<std::string> removed_items;
-    for (const auto& item : external_) {
-      if (item.second == uuid) {
-        remove_(item.first);
-        removed_items.push_back(item.first);
-      }
-    }
-
-    // Remove items belonging to the external uuid.
-    for (const auto& item : removed_items) {
-      external_.erase(item);
-      routes_.erase(item);
-    }
-  }
-
-  /**
-   * @brief Add a plugin to this registry by allocating and indexing
-   * a type Item and a key identifier.
-   *
-   * @code{.cpp}
-   *   /// Instead of calling RegistryFactory::add use:
-   *   REGISTER(Type, "registry_name", "item_name");
-   * @endcode
-   *
-   * @param item_name An identifier for this registry plugin.
-   * @return A success/failure status.
-   */
-  template <class Item>
-  Status add(const std::string& item_name, bool internal = false) {
-    if (items_.count(item_name) > 0) {
-      return Status(1, "Duplicate registry item exists: " + item_name);
-    }
-
-    // Cast the specific registry-type derived item as the API type of the
-    // registry used when created using the registry factory.
-    std::shared_ptr<RegistryType> item((RegistryType*)new Item());
-    item->setName(item_name);
-    items_[item_name] = item;
-    return RegistryHelperCore::add(item_name, internal);
-  }
-
-  /**
-   * @brief A raw accessor for a registry plugin.
-   *
-   * If there is no plugin with an item_name identifier this will throw
-   * and out_of_range exception.
-   *
-   * @param item_name An identifier for this registry plugin.
-   * @return A std::shared_ptr of type RegistryType.
-   */
-  RegistryTypeRef get(const std::string& item_name) const {
-    return std::dynamic_pointer_cast<RegistryType>(items_.at(item_name));
-  }
-
-  const std::map<std::string, RegistryTypeRef> all() const {
-    std::map<std::string, RegistryTypeRef> ditems;
-    for (const auto& item : items_) {
-      ditems[item.first] = std::dynamic_pointer_cast<RegistryType>(item.second);
-    }
-
-    return ditems;
-  }
-
- private:
-  RegistryHelper(RegistryHelper const&);
-  void operator=(RegistryHelper const&);
-  AddExternalCallback add_;
-  RemoveExternalCallback remove_;
-};
-
-/// Helper defintion for a shared pointer to a Plugin.
-typedef std::shared_ptr<Plugin> PluginRef;
-/// Helper definition for a basic-templated Registry type using a base Plugin.
-typedef RegistryHelper<Plugin> PluginRegistryHelper;
-/// Helper definitions for a shared pointer to the basic Registry type.
-typedef std::shared_ptr<PluginRegistryHelper> PluginRegistryHelperRef;
-
-/**
- * @basic A workflow manager for opening a module path and appending to the
- * core registry.
- *
- * osquery Registry modules are part of the extensions API, in that they use
- * the osquery SDK to expose additional features to the osquery core. Modules
- * do not require the Thrift interface and may be compiled as shared objects
- * and loaded late at run time once the core and internal registry has been
- * initialized and setUp.
- *
- * A ModuleLoader interprets search paths, dynamically loads the modules,
- * maintains identification within the RegistryFactory and any registries
- * the module adds items into.
- */
-class RegistryModuleLoader : private boost::noncopyable {
- public:
-  /// Unlock the registry, open, construct, and allow the module to declare.
-  explicit RegistryModuleLoader(const std::string& path);
-  /// Keep the symbol resolution/calling out of construction.
-  void init();
-
-  /// Clear module information, 'lock' the registry.
-  ~RegistryModuleLoader();
-
- private:
-  // Keep the handle for symbol resolution/calling.
-  void* handle_;
-  // Keep the path for debugging/logging.
-  std::string path_;
-
- private:
-  FRIEND_TEST(RegistryTests, test_registry_modules);
-};
-
-class RegistryFactory : private boost::noncopyable {
- public:
-  static RegistryFactory& instance() {
-    static RegistryFactory instance;
-    return instance;
-  }
-
-  /**
-   * @brief Create a registry using a plugin type and identifier.
-   *
-   * A short hard for allocating a new registry type a RegistryHelper and
-   * plugin derived class Type or RegistryType. This shorthand performs
-   * the allocation and initialization of the Type and keeps the instance
-   * identified by registry_name.
-   *
-   * @code{.cpp}
-   *   /// Instead of calling RegistryFactory::create use:
-   *   CREATE_REGISTRY(Type, "registry_name");
-   * @endcode
-   *
-   * @param registry_name The canonical name for this registry.
-   * @param auto_setup Set true if the registry does not setup itself
-   * @return A non-sense int that must be casted const.
-   */
-  template <class Type>
-  static int create(const std::string& registry_name, bool auto_setup = false) {
-    if (locked() || instance().registries_.count(registry_name) > 0) {
-      return 0;
-    }
-
-    PluginRegistryHelperRef registry(
-        (PluginRegistryHelper*)new RegistryHelper<Type>(auto_setup));
-    registry->setName(registry_name);
-    instance().registries_[registry_name] = registry;
-    return 0;
-  }
-
-  /// Direct access to a registry instance.
-  static PluginRegistryHelperRef registry(const std::string& registry_name);
-
-  /**
-   * @brief Add (implies create) a Plugin to a registry.
-   *
-   * REGISTER and REGISTER_INTERNAL are helper macros for `add` usage.
-   *
-   * @code{.cpp}
-   *  /// Instead of calling RegistryFactor::add use:
-   *  REGISTER(Type, "registry_name", "plugin_name");
-   * @endcode
-   *
-   * @param registry_name The canonical name for this registry.
-   * @param item_name The canonical name for this plugin. Specific registries
-   * may apply specialized use of the plugin name, such as table.
-   * @param internal True if this plugin should not be broadcasted externally.
-   */
-  template <class Item>
-  static Status add(const std::string& registry_name,
-                    const std::string& item_name,
-                    bool internal = false) {
-    if (!locked()) {
-      auto registry = instance().registry(registry_name);
-      return registry->template add<Item>(item_name, internal);
-    }
-    return Status(0, "Registry locked");
-  }
-
-  /// Direct access to all registries.
-  static const std::map<std::string, PluginRegistryHelperRef>& all();
-
-  /// Direct access to all plugin instances for a given registry name.
-  static const std::map<std::string, PluginRef> all(
-      const std::string& registry_name);
-
-  /// Direct access to a plugin instance.
-  static PluginRef get(const std::string& registry_name,
-                       const std::string& item_name);
-
-  /// Serialize this core or extension's registry.
-  static RegistryBroadcast getBroadcast();
-
-  /// Add external registry items identified by a Route UUID.
-  static Status addBroadcast(const RouteUUID& uuid,
-                             const RegistryBroadcast& broadcast);
-
-  /// Given an extension UUID remove all external registry items.
-  static Status removeBroadcast(const RouteUUID& uuid);
-
-  /// Adds an alias for an internal registry item. This registry will only
-  /// broadcast the alias name.
-  static Status addAlias(const std::string& registry_name,
-                         const std::string& item_name,
-                         const std::string& alias);
-
-  /// Returns the item_name or the item alias if an alias exists.
-  static const std::string& getAlias(const std::string& registry_name,
-                                     const std::string& alias);
-
-  /**
-   * @brief Call a registry item.
-   *
-   * Registry 'calling' is the primary interaction osquery has with the Plugin
-   * APIs, which register items. Each item is an instance of a specialized
-   * Plugin, whose life/scope is maintained by the specific registry identified
-   * by a unique name.
-   *
-   * The specialized plugin type will expose a `call` method that parses a
-   * PluginRequest then perform some action and return a PluginResponse.
-   * Each registry provides a `call` method that performs the registry item
-   * (Plugin instance) look up, and passes and retrieves the request and
-   * response.
-   *
-   * @param registry_name The unique registry name containing item_name,
-   * @param item_name The name of the plugin used to REGISTER.
-   * @param request The PluginRequest object handled by the Plugin item.
-   * @param response The output.
-   * @return A status from the Plugin.
-   */
-  static Status call(const std::string& registry_name,
-                     const std::string& item_name,
-                     const PluginRequest& request,
-                     PluginResponse& response);
-
-  /// A helper call that does not return a response (only status).
-  static Status call(const std::string& registry_name,
-                     const std::string& item_name,
-                     const PluginRequest& request);
-
-  /// A helper call that uses the active plugin (if the registry has one).
-  static Status call(const std::string& registry_name,
-                     const PluginRequest& request,
-                     PluginResponse& response);
-
-  /// A helper call that uses the active plugin (if the registry has one).
-  static Status call(const std::string& registry_name,
-                     const PluginRequest& request);
-
-  /// Set a registry's active plugin.
-  static Status setActive(const std::string& registry_name,
-                          const std::string& item_name);
-
-  /// Get a registry's active plugin.
-  static const std::string& getActive(const std::string& registry_nane);
-
-  /// Run `setUp` on every registry that is not marked 'lazy'.
-  static void setUp();
-
-  /// Check if a registry item exists, optionally search only local registries.
-  static bool exists(const std::string& registry_name,
-                     const std::string& item_name,
-                     bool local = false);
-
-  /// Get a list of the registry names.
-  static std::vector<std::string> names();
-
-  /// Get a list of the registry item names for a given registry.
-  static std::vector<std::string> names(const std::string& registry_name);
-
-  /// Get a list of the registered extension UUIDs.
-  static std::vector<RouteUUID> routeUUIDs();
-
-  /// Return the number of registries.
-  static size_t count();
-
-  /// Return the number of registry items for a given registry name.
-  static size_t count(const std::string& registry_name);
-
-  /// Enable/disable duplicate registry item support using aliasing.
-  static void allowDuplicates(bool allow) {
-    instance().allow_duplicates_ = allow;
-  }
-
-  /// Check if duplicate registry items using registry aliasing are allowed.
-  static bool allowDuplicates() { return instance().allow_duplicates_; }
-
-  /// Declare a module for initialization and subsequent registration attempts
-  static void declareModule(const std::string& name,
-                            const std::string& version,
-                            const std::string& min_sdk_version,
-                            const std::string& sdk_version);
-
-  /// Access module metadata.
-  static const std::map<RouteUUID, ModuleInfo>& getModules();
-
-  /// Set the registry external (such that internal events are forwarded).
-  /// Once set external, it should not be unset.
-  static void setExternal() { instance().external_ = true; }
-
-  /// Get the registry external status.
-  static bool external() { return instance().external_; }
-
- private:
-  /// Access the current initializing module UUID.
-  static RouteUUID getModule();
-
-  /// Check if the registry is allowing module registrations.
-  static bool usingModule();
-
-  /// Initialize a module for lookup, resolution, and its registrations.
-  static void initModule(const std::string& path);
-
-  static void shutdownModule();
-
-  /// Check if the registries are locked.
-  static bool locked() { return instance().locked_; }
-
-  /// Set the registry locked status.
-  static void locked(bool locked) { instance().locked_ = locked; }
-
- protected:
-  RegistryFactory()
-      : allow_duplicates_(false),
-        locked_(false),
-        module_uuid_(0),
-        external_(false) {}
-  RegistryFactory(RegistryFactory const&);
-  RegistryFactory& operator=(RegistryFactory const&);
-  virtual ~RegistryFactory() {}
-
- private:
-  /// Track duplicate registry item support, used for testing.
-  bool allow_duplicates_;
-  /// Track registry "locking", while locked a registry cannot add/create.
-  bool locked_;
-
-  /// The primary storage for constructed registries.
-  std::map<std::string, PluginRegistryHelperRef> registries_;
-  /**
-   * @brief The registry tracks the set of active extension routes.
-   *
-   * If an extension dies (the process ends or does not respond to a ping),
-   * the registry will be notified via the extension watcher.
-   * When an operation requests to use that extension route the extension
-   * manager will lazily check the registry for changes.
-   */
-  std::set<RouteUUID> extensions_;
-
-  /**
-   * @brief The registry tracks loaded extension module metadata/info.
-   *
-   * Each extension module is assigned a transient RouteUUID for identification
-   * those route IDs are passed to each registry to identify which plugin
-   * items belong to modules, similarly to extensions.
-   */
-  std::map<RouteUUID, ModuleInfo> modules_;
-
-  /// During module initialization store the current-working module ID.
-  RouteUUID module_uuid_;
-  /// Calling startExtension should declare the registry external.
-  /// This will cause extension-internal events to forward to osquery core.
-  bool external_;
-
- private:
-  friend class RegistryHelperCore;
-  friend class RegistryModuleLoader;
-  FRIEND_TEST(RegistryTests, test_registry_modules);
-};
-
-/**
- * @brief The osquery Registry, refer to RegistryFactory for the caller API.
- *
- * The Registry class definition constructs the RegistryFactory behind the
- * scenes using a class definition template API call Plugin.
- * Each registry created by the RegistryFactory using RegistryFactory::create
- * will provide a plugin type called RegistryType that inherits from Plugin.
- * The actual plugins must add themselves to a registry type and should
- * implement the Plugin and RegistryType interfaces.
- */
-class Registry : public RegistryFactory {};
-}
diff --git a/include/osquery/sdk.h b/include/osquery/sdk.h
deleted file mode 100644 (file)
index 0868299..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#ifndef OSQUERY_BUILD_SDK
-#define OSQUERY_BUILD_SDK
-#endif
-
-#include <osquery/config.h>
-#include <osquery/core.h>
-#include <osquery/database.h>
-#include <osquery/events.h>
-#include <osquery/extensions.h>
-#include <osquery/filesystem.h>
-#include <osquery/flags.h>
-#include <osquery/hash.h>
-#include <osquery/logger.h>
-#include <osquery/registry.h>
-#include <osquery/sql.h>
-#include <osquery/status.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-/**
- * @brief Create the external SQLite implementation wrapper.
- *
- * Anything built with only libosquery and not the 'additional' library will
- * not include a native SQL implementation. This applies to extensions and
- * separate applications built with the osquery SDK.
- *
- * The ExternalSQLPlugin is a wrapper around the SQLite API, which forwards
- * calls to an osquery extension manager (core).
- */
-REGISTER_INTERNAL(ExternalSQLPlugin, "sql", "sql");
-
-/**
- * @brief Mimic the REGISTER macro, extensions should use this helper.
- *
- * The SDK does not provide a REGISTER macro for modules or extensions.
- * Tools built with the osquery SDK should use REGISTER_EXTERNAL to add to
- * their own 'external' registry. This registry will broadcast to the osquery
- * extension manager (core) in an extension.
- *
- * osquery 'modules' should not construct their plugin registrations in
- * global scope (global construction time). Instead they should use the
- * module call-in well defined symbol, declare their SDK constraints, then
- * use the REGISTER_MODULE call within `initModule`.
- */
-#define REGISTER_EXTERNAL(type, registry, name)                            \
-  __attribute__((constructor)) static void type##ExtensionRegistryItem() { \
-    Registry::add<type>(registry, name);                                   \
-  }
-
-/// Helper macro to write the `initModule` symbol without rewrites.
-#define DECLARE_MODULE(name)        \
-  extern "C" void initModule(void); \
-  __attribute__((constructor)) static void declareModule()
-
-/**
- * @brief Create an osquery extension 'module'.
- *
- * This helper macro creates a constructor to declare an osquery module is
- * loading. The osquery registry is set up when modules (shared objects) are
- * discovered via search paths and opened. At that phase the registry is locked
- * meaning no additional plugins can be registered. To unlock the registry
- * for modifications a module must call Registry::declareModule. This declares
- * and any plugins added will use the metadata in the declare to determine:
- *  - The name of the module adding the plugin
- *  - The SDK version the module was built with, to determine compatibility
- *  - The minimum SDK the module requires from osquery core
- *
- * The registry is again locked when the module load is complete and a well
- * known module-exported symbol is called.
- */
-#define CREATE_MODULE(name, version, min_sdk_version)         \
-  DECLARE_MODULE(name) {                                      \
-    Registry::declareModule(                                  \
-        name, version, min_sdk_version, OSQUERY_SDK_VERSION); \
-  }
-
-/**
- * @brief Create an osquery extension 'module', if an expression is true.
- *
- * This is a helper testing wrapper around CREATE_MODULE and DECLARE_MODULE.
- * It allows unit and integration tests to generate global construction code
- * that depends on data/variables available during global construction.
- *
- * And example use includes checking if a process environment variable is
- * defined. If defined the module is declared.
- */
-#define CREATE_MODULE_IF(expr, name, version, min_sdk_version)  \
-  DECLARE_MODULE(name) {                                        \
-    if ((expr)) {                                               \
-      Registry::declareModule(                                  \
-          name, version, min_sdk_version, OSQUERY_SDK_VERSION); \
-    }                                                           \
-  }
-
-/// Helper replacement for REGISTER, used within extension modules.
-#define REGISTER_MODULE(type, registry, name) \
-  auto type##ModuleRegistryItem = Registry::add<type>(registry, name)
-
-// Remove registry-helper macros from the SDK.
-#undef REGISTER
-#define REGISTER "Do not REGISTER in the osquery SDK"
-#undef REGISTER_INTERNAL
-#define REGISTER_INTERNAL "Do not REGISTER_INTERNAL in the osquery SDK"
-#undef CREATE_REGISTRY
-#define CREATE_REGISTRY "Do not CREATE_REGISTRY in the osquery SDK"
-#undef CREATE_LAZY_REGISTRY
-#define CREATE_LAZY_REGISTRY "Do not CREATE_LAZY_REGISTRY in the osquery SDK"
-}
diff --git a/include/osquery/sql.h b/include/osquery/sql.h
deleted file mode 100644 (file)
index b8d0f19..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <osquery/database.h>
-#include <osquery/flags.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-
-DECLARE_int32(value_max);
-
-/**
- * @brief The core interface to executing osquery SQL commands
- *
- * @code{.cpp}
- *   auto sql = SQL("SELECT * FROM time");
- *   if (sql.ok()) {
- *     LOG(INFO) << "============================";
- *     for (const auto& row : sql.rows()) {
- *       for (const auto& it : row) {
- *         LOG(INFO) << it.first << " => " << it.second;
- *       }
- *       LOG(INFO) << "============================";
- *     }
- *   } else {
- *     LOG(ERROR) << sql.getMessageString();
- *   }
- * @endcode
- */
-class SQL {
- public:
-  /**
-   * @brief Instantiate an instance of the class with a query
-   *
-   * @param q An osquery SQL query
-   */
-  explicit SQL(const std::string& q);
-
-  /**
-   * @brief Accessor for the rows returned by the query
-   *
-   * @return A QueryData object of the query results
-   */
-  const QueryData& rows();
-
-  /**
-   * @brief Accessor to switch off of when checking the success of a query
-   *
-   * @return A bool indicating the success or failure of the operation
-   */
-  bool ok();
-
-  /**
-   * @brief Get the status returned by the query
-   *
-   * @return The query status
-   */
-  Status getStatus();
-
-  /**
-   * @brief Accessor for the message string indicating the status of the query
-   *
-   * @return The message string indicating the status of the query
-   */
-  std::string getMessageString();
-
-  /**
-   * @brief Add host info columns onto existing QueryData
-   *
-   * Use this to add columns providing host info to the query results.
-   * Distributed queries use this to add host information before returning
-   * results to the aggregator.
-   */
-  void annotateHostInfo();
-
-  /**
-   * @brief Accessor for the list of queryable tables
-   *
-   * @return A vector of table names
-   */
-  static std::vector<std::string> getTableNames();
-
-  /**
-   * @brief Get all, 'SELECT * ...', results given a virtual table name.
-   *
-   * @param table The name of the virtual table.
-   * @return A QueryData object of the 'SELECT *...' query results.
-   */
-  static QueryData selectAllFrom(const std::string& table);
-
-  /**
-   * @brief Get all with constraint, 'SELECT * ... where', results given
-   * a virtual table name and single constraint
-   *
-   * @param table The name of the virtual table.
-   * @param column Table column name to apply constraint.
-   * @param op The SQL comparitive operator.
-   * @param expr The constraint expression.
-   * @return A QueryData object of the 'SELECT *...' query results.
-   */
-  static QueryData selectAllFrom(const std::string& table,
-                                 const std::string& column,
-                                 ConstraintOperator op,
-                                 const std::string& expr);
-
- protected:
-  /**
-   * @brief Private default constructor
-   *
-   * The osquery::SQL class should only ever be instantiated with a query
-   */
-  SQL(){};
-
-  // The key used to store hostname for annotateHostInfo
-  static const std::string kHostColumnName;
-
-  /// the internal member which holds the results of the query
-  QueryData results_;
-
-  /// the internal member which holds the status of the query
-  Status status_;
-};
-
-/**
- * @brief The osquery SQL implementation is managed as a plugin.
- *
- * The osquery RegistryFactory creates a Registry type called "sql", then
- * requires a single plugin registration also called "sql". Calls within
- * the application use boilerplate methods that wrap Registry::call%s to this
- * well-known registry and registry item name.
- *
- * Abstracting the SQL implementation behind the osquery registry allows
- * the SDK (libosquery) to describe how the SQL implementation is used without
- * having dependencies on the thrird-party code.
- *
- * When osqueryd/osqueryi are built libosquery_additional, the library which
- * provides the core plugins and core virtual tables, includes SQLite as
- * the SQL implementation.
- */
-class SQLPlugin : public Plugin {
- public:
-  /// Run a SQL query string against the SQL implementation.
-  virtual Status query(const std::string& q, QueryData& results) const = 0;
-  /// Use the SQL implementation to parse a query string and return details
-  /// (name, type) about the columns.
-  virtual Status getQueryColumns(const std::string& q,
-                                 TableColumns& columns) const = 0;
-
-  /**
-   * @brief Attach a table at runtime.
-   *
-   * The SQL implementation plugin may need to manage how virtual tables are
-   * attached at run time. In the case of SQLite where a single DB object is
-   * managed, tables are enumerated and attached during initialization.
-   */
-  virtual Status attach(const std::string& name) {
-    return Status(0, "Not used");
-  }
-  /// Tables may be detached by name.
-  virtual void detach(const std::string& name) {}
-
- public:
-  Status call(const PluginRequest& request, PluginResponse& response);
-};
-
-/**
- * @brief Execute a query
- *
- * This is a lower-level version of osquery::SQL. Prefer to use osquery::SQL.
- *
- * @code{.cpp}
- *   std::string q = "SELECT * FROM time;";
- *   QueryData results;
- *   auto status = query(q, results);
- *   if (status.ok()) {
- *     for (const auto& each : results) {
- *       for (const auto& it : each) {
- *         LOG(INFO) << it.first << ": " << it.second;
- *       }
- *     }
- *   } else {
- *     LOG(ERROR) << "Error: " << status.what();
- *   }
- * @endcode
- *
- * @param q the query to execute
- * @param results A QueryData structure to emit result rows on success.
- * @return A status indicating query success.
- */
-Status query(const std::string& query, QueryData& results);
-
-/**
- * @brief Analyze a query, providing information about the result columns
- *
- * This function asks SQLite to determine what the names and types are of the
- * result columns of the provided query. Only table columns (not expressions or
- * subqueries) can have their types determined. Types that are not determined
- * are indicated with the string "UNKNOWN".
- *
- * @param q the query to analyze
- * @param columns the vector to fill with column information
- *
- * @return status indicating success or failure of the operation
- */
-Status getQueryColumns(const std::string& q, TableColumns& columns);
-
-/*
- * @brief A mocked subclass of SQL useful for testing
- */
-class MockSQL : public SQL {
- public:
-  explicit MockSQL() : MockSQL(QueryData{}) {}
-  explicit MockSQL(const QueryData& results) : MockSQL(results, Status()) {}
-  explicit MockSQL(const QueryData& results, const Status& status) {
-    results_ = results;
-    status_ = status;
-  }
-};
-
-CREATE_LAZY_REGISTRY(SQLPlugin, "sql");
-}
diff --git a/include/osquery/status.h b/include/osquery/status.h
deleted file mode 100644 (file)
index 185a14a..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <sstream>
-#include <string>
-
-namespace osquery {
-
-/**
- * @brief A utility class which is used to express the state of operations.
- *
- * @code{.cpp}
- *   osquery::Status foobar() {
- *     auto na = doSomeWork();
- *     if (na->itWorked()) {
- *       return osquery::Status(0, "OK");
- *     } else {
- *       return osquery::Status(1, na->getErrorString());
- *     }
- *   }
- * @endcode
- */
-class Status {
- public:
-  /**
-   * @brief Default constructor
-   *
-   * Note that the default constructor initialized an osquery::Status instance
-   * to a state such that a successful operation is indicated.
-   */
-  Status() : code_(0), message_("OK") {}
-
-  /**
-   * @brief A constructor which can be used to concisely express the status of
-   * an operation.
-   *
-   * @param c a status code. The idiom is that a zero status code indicates a
-   * successful operation and a non-zero status code indicates a failed
-   * operation.
-   * @param m a message indicating some extra detail regarding the operation.
-   * If all operations were successful, this message should be "OK".
-   * Otherwise, it doesn't matter what the string is, as long as both the
-   * setter and caller agree.
-   */
-  Status(int c, std::string m) : code_(c), message_(m) {}
-
- public:
-  /**
-   * @brief A getter for the status code property
-   *
-   * @return an integer representing the status code of the operation.
-   */
-  int getCode() const { return code_; }
-
-  /**
-   * @brief A getter for the message property
-   *
-   * @return a string representing arbitrary additional information about the
-   * success or failure of an operation. On successful operations, the idiom
-   * is for the message to be "OK"
-   */
-  std::string getMessage() const { return message_; }
-
-  /**
-   * @brief A convenience method to check if the return code is 0
-   *
-   * @code{.cpp}
-   *   auto s = doSomething();
-   *   if (s.ok()) {
-   *     LOG(INFO) << "doing work";
-   *   } else {
-   *     LOG(ERROR) << s.toString();
-   *   }
-   * @endcode
-   *
-   * @return a boolean which is true if the status code is 0, false otherwise.
-   */
-  bool ok() const { return getCode() == 0; }
-
-  /**
-   * @brief A synonym for osquery::Status::getMessage()
-   *
-   * @see getMessage()
-   */
-  std::string toString() const { return getMessage(); }
-  std::string what() const { return getMessage(); }
-
-  /**
-   * @brief implicit conversion to bool
-   *
-   * Allows easy use of Status in an if statement, as below:
-   *
-   * @code{.cpp}
-   *   if (doSomethingThatReturnsStatus()) {
-   *     LOG(INFO) << "Success!";
-   *   }
-   * @endcode
-   */
-  operator bool() const { return ok(); }
-
-  // Below operator implementations useful for testing with gtest
-
-  // Enables use of gtest (ASSERT|EXPECT)_EQ
-  bool operator==(const Status& rhs) const {
-    return (code_ == rhs.getCode()) && (message_ == rhs.getMessage());
-  }
-
-  // Enables use of gtest (ASSERT|EXPECT)_NE
-  bool operator!=(const Status& rhs) const { return !operator==(rhs); }
-
-  // Enables pretty-printing in gtest (ASSERT|EXPECT)_(EQ|NE)
-  friend ::std::ostream& operator<<(::std::ostream& os, const Status& s);
-
- private:
-  /// the internal storage of the status code
-  int code_;
-
-  /// the internal storage of the status message
-  std::string message_;
-};
-}
diff --git a/include/osquery/tables.h b/include/osquery/tables.h
deleted file mode 100644 (file)
index f2db419..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <memory>
-#include <vector>
-#include <set>
-
-#include <boost/lexical_cast.hpp>
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/registry.h>
-#include <osquery/core.h>
-#include <osquery/database.h>
-#include <osquery/status.h>
-
-/// Allow Tables to use "tracked" deprecated OS APIs.
-#define OSQUERY_USE_DEPRECATED(expr)                                      \
-  do {                                                                    \
-    _Pragma("clang diagnostic push")                                      \
-        _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
-        expr;                                                             \
-    _Pragma("clang diagnostic pop")                                       \
-  } while (0)
-
-namespace osquery {
-
-/**
- * @brief The SQLite type affinities are available as macros
- *
- * Type affinities: TEXT, INTEGER, BIGINT
- *
- * You can represent any data that can be lexically casted to a string.
- * Using the type affinity names helps table developers understand the data
- * types they are storing, and more importantly how they are treated at query
- * time.
- */
-#define TEXT(x) boost::lexical_cast<std::string>(x)
-/// See the affinity type documentation for TEXT.
-#define INTEGER(x) boost::lexical_cast<std::string>(x)
-/// See the affinity type documentation for TEXT.
-#define BIGINT(x) boost::lexical_cast<std::string>(x)
-/// See the affinity type documentation for TEXT.
-#define UNSIGNED_BIGINT(x) boost::lexical_cast<std::string>(x)
-/// See the affinity type documentation for TEXT.
-#define DOUBLE(x) boost::lexical_cast<std::string>(x)
-
-/**
- * @brief The SQLite type affinities as represented as implementation literals.
- *
- * Type affinities: TEXT=std::string, INTEGER=int, BIGINT=long long int
- *
- * Just as the SQLite data is represented as lexically casted strings, as table
- * may make use of the implementation language literals.
- */
-#define TEXT_LITERAL std::string
-/// See the literal type documentation for TEXT_LITERAL.
-#define INTEGER_LITERAL int
-/// See the literal type documentation for TEXT_LITERAL.
-#define BIGINT_LITERAL long long int
-/// See the literal type documentation for TEXT_LITERAL.
-#define UNSIGNED_BIGINT_LITERAL unsigned long long int
-/// See the literal type documentation for TEXT_LITERAL.
-#define DOUBLE_LITERAL double
-/// Cast an SQLite affinity type to the literal type.
-#define AS_LITERAL(literal, value) boost::lexical_cast<literal>(value)
-
-/// Helper alias for TablePlugin names.
-typedef std::string TableName;
-typedef std::vector<std::pair<std::string, std::string> > TableColumns;
-typedef std::map<std::string, std::vector<std::string> > TableData;
-
-/**
- * @brief A ConstraintOperator is applied in an query predicate.
- *
- * If the query contains a join or where clause with a constraint operator and
- * expression the table generator may limit the data appropriately.
- */
-enum ConstraintOperator : unsigned char {
-  EQUALS = 2,
-  GREATER_THAN = 4,
-  LESS_THAN_OR_EQUALS = 8,
-  LESS_THAN = 16,
-  GREATER_THAN_OR_EQUALS = 32
-};
-
-/// Type for flags for what constraint operators are admissible.
-typedef unsigned char ConstraintOperatorFlag;
-/// Flag for any operator type.
-#define ANY_OP 0xFFU
-
-/**
- * @brief A Constraint is an operator and expression.
- *
- * The constraint is applied to columns which have literal and affinity types.
- */
-struct Constraint {
-  unsigned char op;
-  std::string expr;
-
-  /// Construct a Constraint with the most-basic information, the operator.
-  explicit Constraint(unsigned char _op) { op = _op; }
-
-  // A constraint list in a context knows only the operator at creation.
-  explicit Constraint(unsigned char _op, const std::string& _expr)
-      : op(_op), expr(_expr) {}
-};
-
-/**
- * @brief A ConstraintList is a set of constraints for a column. This list
- * should be mapped to a left-hand-side column name.
- *
- * The table generator does not need to check each constraint in its decision
- * logic. The common constraint checking patterns (match) are abstracted using
- * simple logic operators on the literal SQLite affinity types.
- *
- * A constraint list supports all AS_LITERAL types, and all ConstraintOperators.
- */
-struct ConstraintList {
-  /// The SQLite affinity type.
-  std::string affinity;
-
-  /**
-   * @brief Check if an expression matches the query constraints.
-   *
-   * Evaluate ALL constraints in this ConstraintList against the string
-   * expression. The affinity of the constraint will be used as the affinite
-   * and lexical type of the expression and set of constraint expressions.
-   * If there are no predicate constraints in this list, all expression will
-   * match. Constraints are limitations.
-   *
-   * @param expr a SQL type expression of the column literal type to check.
-   * @return If the expression matched all constraints.
-   */
-  bool matches(const std::string& expr) const;
-
-  /**
-   * @brief Check if an expression matches the query constraints.
-   *
-   * `matches` also supports the set of SQL affinite types.
-   * The expression expr will be evaluated as a string and compared using
-   * the affinity of the constraint.
-   *
-   * @param expr a SQL type expression of the column literal type to check.
-   * @return If the expression matched all constraints.
-   */
-  template <typename T>
-  bool matches(const T& expr) const {
-    return matches(TEXT(expr));
-  }
-
-  /**
-   * @brief Check and return if there are constraints on this column.
-   *
-   * A ConstraintList is used in a ConstraintMap with a column name as the
-   * map index. Tables that act on optional constraints should check if any
-   * constraint was provided.  The ops parameter serves to specify which
-   * operators we want to check existence for.
-   *
-   * @param ops (Optional: default ANY_OP) The operators types to look for.
-   * @return true if any constraint exists.
-   */
-  bool exists(const ConstraintOperatorFlag ops = ANY_OP) const {
-    if (ops == ANY_OP) {
-      return (constraints_.size() > 0);
-    } else {
-      for (const struct Constraint &c : constraints_) {
-        if (c.op & ops) {
-          return true;
-        }
-      }
-      return false;
-    }
-  }
-
-  /**
-   * @brief Check if a constraint exist AND matches the type expression.
-   *
-   * See ConstraintList::exists and ConstraintList::matches.
-   *
-   * @param expr The expression to match.
-   * @return true if any constraint exists AND matches the type expression.
-   */
-  template <typename T>
-  bool existsAndMatches(const T& expr) const {
-    return (exists() && matches(expr));
-  }
-
-  /**
-   * @brief Check if a constraint is missing or matches a type expression.
-   *
-   * A ConstraintList is used in a ConstraintMap with a column name as the
-   * map index. Tables that act on required constraints can make decisions
-   * on missing constraints or a constraint match.
-   *
-   * @param expr The expression to match.
-   * @return true if constraint is missing or matches the type expression.
-   */
-  template <typename T>
-  bool notExistsOrMatches(const T& expr) const {
-    return (!exists() || matches(expr));
-  }
-
-  /**
-   * @brief Helper templated function for ConstraintList::matches.
-   */
-  template <typename T>
-  bool literal_matches(const T& base_expr) const;
-
-  /**
-   * @brief Get all expressions for a given ConstraintOperator.
-   *
-   * This is most useful if the table generation requires as column.
-   * The generator may `getAll(EQUALS)` then iterate.
-   *
-   * @param op the ConstraintOperator.
-   * @return A list of TEXT%-represented types matching the operator.
-   */
-  std::set<std::string> getAll(ConstraintOperator op) const;
-
-  /// See ConstraintList::getAll, but as a selected literal type.
-  template<typename T>
-  std::set<T> getAll(ConstraintOperator op) const {
-    std::set<T> literal_matches;
-    auto matches = getAll(op);
-    for (const auto& match : matches) {
-      literal_matches.insert(AS_LITERAL(T, match));
-    }
-    return literal_matches;
-  }
-
-  /// Constraint list accessor, types and operator.
-  const std::vector<struct Constraint> getAll() const { return constraints_; }
-
-  /**
-   * @brief Add a new Constraint to the list of constraints.
-   *
-   * @param constraint a new operator/expression to constrain.
-   */
-  void add(const struct Constraint& constraint) {
-    constraints_.push_back(constraint);
-  }
-
-  /**
-   * @brief Serialize a ConstraintList into a property tree.
-   *
-   * The property tree will use the format:
-   * {
-   *   "affinity": affinity,
-   *   "list": [
-   *     {"op": op, "expr": expr}, ...
-   *   ]
-   * }
-   */
-  void serialize(boost::property_tree::ptree& tree) const;
-
-  /// See ConstraintList::unserialize.
-  void unserialize(const boost::property_tree::ptree& tree);
-
-  ConstraintList() : affinity("TEXT") {}
-
- private:
-  /// List of constraint operator/expressions.
-  std::vector<struct Constraint> constraints_;
-
- private:
-  FRIEND_TEST(TablesTests, test_constraint_list);
-};
-
-/// Pass a constraint map to the query request.
-typedef std::map<std::string, struct ConstraintList> ConstraintMap;
-/// Populate a constraint list from a query's parsed predicate.
-typedef std::vector<std::pair<std::string, struct Constraint> > ConstraintSet;
-
-/**
- * @brief A QueryContext is provided to every table generator for optimization
- * on query components like predicate constraints and limits.
- */
-struct QueryContext {
-  ConstraintMap constraints;
-  /// Support a limit to the number of results.
-  int limit;
-
-  QueryContext() : limit(0) {}
-};
-
-typedef struct QueryContext QueryContext;
-typedef struct Constraint Constraint;
-
-/**
- * @brief The TablePlugin defines the name, types, and column information.
- *
- * To attach a virtual table create a TablePlugin subclass and register the
- * virtual table name as the plugin ID. osquery will enumerate all registered
- * TablePlugins and attempt to attach them to SQLite at instantiation.
- *
- * Note: When updating this class, be sure to update the corresponding template
- * in osquery/tables/templates/default.cpp.in
- */
-class TablePlugin : public Plugin {
- protected:
-  virtual TableColumns columns() const {
-    TableColumns columns;
-    return columns;
-  }
-
-  virtual QueryData generate(QueryContext& request) {
-    QueryData data;
-    return data;
-  }
-
-  virtual Status update(Row& row) {
-    return Status(0, "OK");
-  }
-
- protected:
-  std::string columnDefinition() const;
-  PluginResponse routeInfo() const;
-
- public:
-  /// Public API methods.
-  Status call(const PluginRequest& request, PluginResponse& response);
-
- public:
-  /// Helper data structure transformation methods
-  static void setRequestFromContext(const QueryContext& context,
-                                    PluginRequest& request);
-  static void setResponseFromQueryData(const QueryData& data,
-                                       PluginResponse& response);
-  static void setContextFromRequest(const PluginRequest& request,
-                                    QueryContext& context);
-
- public:
-  /// When external table plugins are registered the core will attach them
-  /// as virtual tables to the SQL internal implementation
-  static Status addExternal(const std::string& name,
-                            const PluginResponse& info);
-  static void removeExternal(const std::string& name);
-
- private:
-  FRIEND_TEST(VirtualTableTests, test_tableplugin_columndefinition);
-  FRIEND_TEST(VirtualTableTests, test_tableplugin_statement);
-};
-
-/// Helper method to generate the virtual table CREATE statement.
-std::string columnDefinition(const TableColumns& columns);
-std::string columnDefinition(const PluginResponse& response);
-
-CREATE_LAZY_REGISTRY(TablePlugin, "table");
-}
diff --git a/osquery/CMakeLists.txt b/osquery/CMakeLists.txt
deleted file mode 100644 (file)
index da22c72..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-SET(TARGET_OSQUERY_LIB osquery)
-SET(TARGET_OSQUERY_LIB_ADDITIONAL osquery_additional)
-SET(TARGET_OSQUERY_TEST osquery-test)
-SET(TARGET_OSQUERY_SHELL osqueryi)
-SET(TARGET_OSQUERY_DAEMON osqueryd)
-
-SET(${TARGET_OSQUERY_LIB}_SRCS "")
-SET(${TARGET_OSQUERY_LIB}_DEPS "")
-SET(${TARGET_OSQUERY_LIB}_TESTS "")
-
-ADD_OSQUERY_LINK(glog
-                                gflags
-                                pthread
-                                libthrift.a
-                               #rocksdb deps
-                                librocksdb.a
-                                snappy
-                                z
-                                bz2
-                                dl
-                                lz4
-                                zstd
-                                boost_regex
-                                boost_system
-                                boost_thread
-                                boost_filesystem
-                                crypto # openssl
-                               #shell deps
-                                readline
-                               #build-in tables deps
-                                systemd)
-
-SET(OSQUERY_CODEGEN_PATH "${CMAKE_SOURCE_DIR}/tools/codegen")
-SET(OSQUERY_TABLES_PATH "${CMAKE_SOURCE_DIR}")
-SET(OSQUERY_GENERATED_PATH "${CMAKE_BINARY_DIR}/generated")
-
-## Table generation #############################################################
-FILE(GLOB TABLE_FILES "${CMAKE_SOURCE_DIR}/specs/*.table")
-FILE(GLOB TABLE_FILES_LINUX "${CMAKE_SOURCE_DIR}/specs/linux/*.table")
-FILE(GLOB TABLE_FILES_UTILITY "${CMAKE_SOURCE_DIR}/specs/utility/*.table")
-LIST(APPEND TABLE_FILES ${TABLE_FILES_LINUX})
-LIST(APPEND TABLE_FILES ${TABLE_FILES_UTILITY})
-
-IF(DEFINED GBS_BUILD)
-       FILE(GLOB TABLE_FILES_TIZEN "${CMAKE_SOURCE_DIR}/specs/tizen/*.table")
-       LIST(APPEND TABLE_FILES ${TABLE_FILES_TIZEN})
-
-       SET(GBS_ONLY_PACKAGES klay
-                                                 dpm-pil
-                                                 capi-base-common
-                                                 capi-system-info
-                                                 capi-network-wifi-manager)
-
-       INCLUDE(FindPkgConfig)
-       PKG_CHECK_MODULES(GBS_DEPS REQUIRED ${GBS_ONLY_PACKAGES})
-       INCLUDE_DIRECTORIES(SYSTEM ${GBS_DEPS_INCLUDE_DIRS})
-
-       ADD_OSQUERY_LINK(${GBS_DEPS_LIBRARIES})
-ENDIF(DEFINED GBS_BUILD)
-
-SET(GENERATED_TABLES "")
-
-FILE(GLOB TABLE_FILES_TEMPLATES "${CMAKE_SOURCE_DIR}/tools/codegen/templates/*.in")
-SET(GENERATION_DEPENDENCIES "${OSQUERY_CODEGEN_PATH}/gentable.py"
-                                                       "${OSQUERY_CODEGEN_PATH}/amalgamate.py"
-                                                       "${OSQUERY_TABLES_PATH}/specs/blacklist")
-
-LIST(APPEND GENERATION_DEPENDENCIES ${TABLE_FILES_TEMPLATES})
-
-FOREACH(TABLE_FILE ${TABLE_FILES})
-       SET(TABLE_FILE_GEN ${TABLE_FILE})
-       STRING(REPLACE "${OSQUERY_TABLES_PATH}/specs"
-                                  "${OSQUERY_GENERATED_PATH}/tables"
-                                  TABLE_FILE_GEN
-                                  ${TABLE_FILE_GEN})
-       STRING(REPLACE "linux/" "" TABLE_FILE_GEN ${TABLE_FILE_GEN})
-       STRING(REPLACE "" "" TABLE_FILE_GEN ${TABLE_FILE_GEN})
-       STRING(REPLACE ".table" ".cpp" TABLE_FILE_GEN ${TABLE_FILE_GEN})
-       ADD_CUSTOM_COMMAND(
-               OUTPUT ${TABLE_FILE_GEN}
-               COMMAND
-                       python "${OSQUERY_CODEGEN_PATH}/gentable.py" "${TABLE_FILE}" "${TABLE_FILE_GEN}" "$ENV{DISABLE_BLACKLIST}"
-               DEPENDS
-                       ${TABLE_FILE} ${GENERATION_DEPENDENCIES}
-
-               WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
-       LIST(APPEND GENERATED_TABLES ${TABLE_FILE_GEN})
-ENDFOREACH()
-
-SET(AMALGAMATION_FILE_GEN "${OSQUERY_GENERATED_PATH}/amalgamation.cpp")
-ADD_CUSTOM_COMMAND(
-       OUTPUT ${AMALGAMATION_FILE_GEN}
-       COMMAND
-               python "${OSQUERY_CODEGEN_PATH}/amalgamate.py" "${OSQUERY_CODEGEN_PATH}" "${OSQUERY_GENERATED_PATH}"
-       DEPENDS
-               ${GENERATED_TABLES}
-       WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
-
-## Library-obj generation ###########################################################
-ADD_SUBDIRECTORY(core)
-ADD_SUBDIRECTORY(config)
-ADD_SUBDIRECTORY(dispatcher)
-ADD_SUBDIRECTORY(distributed)
-ADD_SUBDIRECTORY(devtools)
-ADD_SUBDIRECTORY(database)
-ADD_SUBDIRECTORY(events)
-ADD_SUBDIRECTORY(extensions)
-ADD_SUBDIRECTORY(filesystem)
-ADD_SUBDIRECTORY(logger)
-ADD_SUBDIRECTORY(registry)
-ADD_SUBDIRECTORY(sql)
-ADD_SUBDIRECTORY(tables)
-
-ADD_SUBDIRECTORY(tizen)
-
-## Library generation ###########################################################
-# TODO(sangwan.kwon): Change amalgation files to additional
-# static_lib should include every object file in the archive in the link
-# ref: TARGET_OSQUERY_LINK_WHOLE
-ADD_LIBRARY(osquery_generated_tables OBJECT "${AMALGAMATION_FILE_GEN}")
-ADD_LIBRARY(${TARGET_OSQUERY_LIB}
-                               STATIC main/lib.cpp
-                                          $<TARGET_OBJECTS:osquery_generated_tables>
-                                          $<TARGET_OBJECTS:osquery_sqlite>
-                                          ${${TARGET_OSQUERY_LIB}_SRCS})
-TARGET_LINK_LIBRARIES(${TARGET_OSQUERY_LIB} ${${TARGET_OSQUERY_LIB}_DEPS})
-SET_TARGET_PROPERTIES(${TARGET_OSQUERY_LIB} PROPERTIES OUTPUT_NAME ${TARGET_OSQUERY_LIB})
-
-#INSTALL(TARGETS ${TARGET_OSQUERY_LIB}
-#              DESTINATION ${CMAKE_INSTALL_LIBDIR})
-#INSTALL(DIRECTORY "${CMAKE_SOURCE_DIR}/include/"
-#              DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-
-## osqueryi generation ##########################################################
-ADD_EXECUTABLE(${TARGET_OSQUERY_SHELL} devtools/shell.cpp main/shell.cpp)
-TARGET_OSQUERY_LINK_WHOLE(${TARGET_OSQUERY_SHELL} ${TARGET_OSQUERY_LIB})
-INSTALL(TARGETS ${TARGET_OSQUERY_SHELL}
-               DESTINATION ${CMAKE_INSTALL_BINDIR}
-               PERMISSIONS OWNER_READ
-                                       OWNER_WRITE
-                                       OWNER_EXECUTE
-                                       GROUP_READ
-                                       GROUP_EXECUTE
-                                       WORLD_READ
-                                       WORLD_EXECUTE)
-
-## osqueryd generation ##########################################################
-ADD_EXECUTABLE(${TARGET_OSQUERY_DAEMON} main/daemon.cpp)
-TARGET_OSQUERY_LINK_WHOLE(${TARGET_OSQUERY_DAEMON} ${TARGET_OSQUERY_LIB})
-INSTALL(TARGETS ${TARGET_OSQUERY_DAEMON}
-               DESTINATION ${CMAKE_INSTALL_BINDIR}
-               PERMISSIONS OWNER_READ
-                                       OWNER_WRITE
-                                       OWNER_EXECUTE
-                                       GROUP_READ
-                                       GROUP_EXECUTE
-                                       WORLD_READ
-                                       WORLD_EXECUTE)
-
-## osquery-test generation ##########################################################
-ADD_EXECUTABLE(${TARGET_OSQUERY_TEST} main/tests.cpp
-                                                                         ${${TARGET_OSQUERY_LIB}_TESTS})
-TARGET_OSQUERY_LINK_WHOLE(${TARGET_OSQUERY_TEST} ${TARGET_OSQUERY_LIB})
-TARGET_LINK_LIBRARIES(${TARGET_OSQUERY_TEST} gtest)
-SET_TARGET_PROPERTIES(${TARGET_OSQUERY_TEST}
-                                         PROPERTIES COMPILE_FLAGS "-DGTEST_HAS_TR1_TUPLE=0")
-ADD_TEST(${TARGET_OSQUERY_TEST} ${TARGET_OSQUERY_TEST})
-INSTALL(TARGETS ${TARGET_OSQUERY_TEST}
-               DESTINATION ${CMAKE_INSTALL_BINDIR}
-               PERMISSIONS OWNER_READ
-                                       OWNER_WRITE
-                                       OWNER_EXECUTE
-                                       GROUP_READ
-                                       GROUP_EXECUTE
-                                       WORLD_READ
-                                       WORLD_EXECUTE)
-
-## example extension with the SDK ##############################################
-ADD_EXECUTABLE(example_extension examples/example_extension.cpp)
-TARGET_OSQUERY_LINK_WHOLE(example_extension ${TARGET_OSQUERY_LIB})
-SET_TARGET_PROPERTIES(example_extension PROPERTIES OUTPUT_NAME example_extension.ext)
-
-# Build the example extension module with the SDK
-ADD_OSQUERY_MODULE(modexample examples/example_module.cpp)
diff --git a/osquery/config/CMakeLists.txt b/osquery/config/CMakeLists.txt
deleted file mode 100644 (file)
index 0cb3d7f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_config config.cpp)
-
-ADD_OSQUERY_LIBRARY(osquery_config_plugins update.cpp
-                                                                                  plugins/filesystem.cpp
-                                                                                  parsers/query_packs.cpp)
-
-FILE(GLOB OSQUERY_CONFIG_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_CONFIG_TESTS})
diff --git a/osquery/config/config.cpp b/osquery/config/config.cpp
deleted file mode 100644 (file)
index 52b0c76..0000000
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <chrono>
-#include <mutex>
-#include <random>
-#include <sstream>
-
-#include <osquery/config.h>
-#include <osquery/flags.h>
-#include <osquery/hash.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/registry.h>
-#include <osquery/tables.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-typedef pt::ptree::value_type tree_node;
-typedef std::map<std::string, std::vector<std::string> > EventFileMap_t;
-typedef std::chrono::high_resolution_clock chrono_clock;
-
-/// The config plugin must be known before reading options.
-CLI_FLAG(string, config_plugin, "filesystem", "Config plugin name");
-
-FLAG(int32, schedule_splay_percent, 10, "Percent to splay config times");
-
-Status Config::load() {
-  auto& config_plugin = Registry::getActive("config");
-  if (!Registry::exists("config", config_plugin)) {
-    return Status(1, "Missing config plugin " + config_plugin);
-  }
-
-  return genConfig();
-}
-
-Status Config::update(const std::map<std::string, std::string>& config) {
-  // A config plugin may call update from an extension. This will update
-  // the config instance within the extension process and the update must be
-  // reflected in the core.
-  if (Registry::external()) {
-    for (const auto& source : config) {
-      PluginRequest request = {
-          {"action", "update"},
-          {"source", source.first},
-          {"data", source.second},
-      };
-      // A "update" registry item within core should call the core's update
-      // method. The config plugin call action handling must also know to
-      // update.
-      Registry::call("config", "update", request);
-    }
-  }
-
-  // Request a unique write lock when updating config.
-  {
-    boost::unique_lock<boost::shared_mutex> unique_lock(getInstance().mutex_);
-
-    for (const auto& source : config) {
-      if (Registry::external()) {
-        VLOG(1) << "Updating extension config with source: " << source.first;
-      } else {
-        VLOG(1) << "Updating config with source: " << source.first;
-      }
-      getInstance().raw_[source.first] = source.second;
-    }
-
-    // Now merge all sources together.
-    ConfigData conf;
-    for (const auto& source : getInstance().raw_) {
-      auto status = mergeConfig(source.second, conf);
-      if (getInstance().force_merge_success_ && !status.ok()) {
-        return Status(1, status.what());
-      }
-    }
-
-    // Call each parser with the optionally-empty, requested, top level keys.
-    getInstance().data_ = std::move(conf);
-  }
-
-  for (const auto& plugin : Registry::all("config_parser")) {
-    auto parser = std::static_pointer_cast<ConfigParserPlugin>(plugin.second);
-    if (parser == nullptr || parser.get() == nullptr) {
-      continue;
-    }
-
-    // For each key requested by the parser, add a property tree reference.
-    std::map<std::string, ConfigTree> parser_config;
-    for (const auto& key : parser->keys()) {
-      if (getInstance().data_.all_data.count(key) > 0) {
-        parser_config[key] = getInstance().data_.all_data.get_child(key);
-      } else {
-        parser_config[key] = pt::ptree();
-      }
-    }
-
-    // The config parser plugin will receive a copy of each property tree for
-    // each top-level-config key. The parser may choose to update the config's
-    // internal state by requesting and modifying a ConfigDataInstance.
-    parser->update(parser_config);
-  }
-
-  return Status(0, "OK");
-}
-
-Status Config::genConfig() {
-  PluginResponse response;
-  auto status = Registry::call("config", {{"action", "genConfig"}}, response);
-  if (!status.ok()) {
-    return status;
-  }
-
-  if (response.size() > 0) {
-    return update(response[0]);
-  }
-  return Status(0, "OK");
-}
-
-inline void mergeOption(const tree_node& option, ConfigData& conf) {
-  std::string key = option.first.data();
-  std::string value = option.second.data();
-
-  Flag::updateValue(key, value);
-  // There is a special case for supported Gflags-reserved switches.
-  if (key == "verbose" || key == "verbose_debug" || key == "debug") {
-    setVerboseLevel();
-    if (Flag::getValue("verbose") == "true") {
-      VLOG(1) << "Verbose logging enabled by config option";
-    }
-  }
-
-  conf.options[key] = value;
-  if (conf.all_data.count("options") > 0) {
-    conf.all_data.get_child("options").erase(key);
-  }
-  conf.all_data.add_child("options." + key, option.second);
-}
-
-inline void additionalScheduledQuery(const std::string& name,
-                                     const tree_node& node,
-                                     ConfigData& conf) {
-  // Read tree/JSON into a query structure.
-  ScheduledQuery query;
-  query.query = node.second.get<std::string>("query", "");
-  query.interval = node.second.get<int>("interval", 0);
-  if (query.interval == 0) {
-    VLOG(1) << "Setting invalid interval=0 to 84600 for query: " << name;
-    query.interval = 86400;
-  }
-
-  // This is a candidate for a catch-all iterator with a catch for boolean type.
-  query.options["snapshot"] = node.second.get<bool>("snapshot", false);
-  query.options["removed"] = node.second.get<bool>("removed", true);
-
-  // Check if this query exists, if so, check if it was changed.
-  if (conf.schedule.count(name) > 0) {
-    if (query == conf.schedule.at(name)) {
-      return;
-    }
-  }
-
-  // This is a new or updated scheduled query, update the splay.
-  query.splayed_interval =
-      splayValue(query.interval, FLAGS_schedule_splay_percent);
-  // Update the schedule map and replace the all_data node record.
-  conf.schedule[name] = query;
-}
-
-inline void mergeScheduledQuery(const std::string& name,
-                                const tree_node& node,
-                                ConfigData& conf) {
-  // Add the new query to the configuration.
-  additionalScheduledQuery(name, node, conf);
-  // Replace the all_data node record.
-  if (conf.all_data.count("schedule") > 0) {
-    conf.all_data.get_child("schedule").erase(name);
-  }
-  conf.all_data.add_child("schedule." + name, node.second);
-}
-
-inline void mergeExtraKey(const std::string& name,
-                          const tree_node& node,
-                          ConfigData& conf) {
-  // Automatically merge extra list/dict top level keys.
-  for (const auto& subitem : node.second) {
-    if (node.second.count("") == 0 && conf.all_data.count(name) > 0) {
-      conf.all_data.get_child(name).erase(subitem.first);
-    }
-
-    if (subitem.first.size() == 0) {
-      if (conf.all_data.count(name) == 0) {
-        conf.all_data.add_child(name, subitem.second);
-      }
-      conf.all_data.get_child(name).push_back(subitem);
-    } else {
-      conf.all_data.add_child(name + "." + subitem.first, subitem.second);
-    }
-  }
-}
-
-inline void mergeFilePath(const std::string& name,
-                          const tree_node& node,
-                          ConfigData& conf) {
-  for (const auto& path : node.second) {
-    // Add the exact path after converting wildcards.
-    std::string pattern = path.second.data();
-    replaceGlobWildcards(pattern);
-    conf.files[node.first].push_back(std::move(pattern));
-  }
-  conf.all_data.add_child(name + "." + node.first, node.second);
-}
-
-Status Config::mergeConfig(const std::string& source, ConfigData& conf) {
-  pt::ptree tree;
-  try {
-    std::stringstream json_data;
-    json_data << source;
-    pt::read_json(json_data, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    LOG(WARNING) << "Error parsing config JSON: " << e.what();
-    return Status(1, e.what());
-  }
-
-  if (tree.count("additional_monitoring") > 0) {
-    LOG(INFO) << RLOG(903) << "config 'additional_monitoring' is deprecated";
-    for (const auto& node : tree.get_child("additional_monitoring")) {
-      tree.add_child(node.first, node.second);
-    }
-    tree.erase("additional_monitoring");
-  }
-
-  for (const auto& item : tree) {
-    // Iterate over each top-level configuration key.
-    auto key = std::string(item.first.data());
-    if (key == "scheduledQueries") {
-      LOG(INFO) << RLOG(903) << "config 'scheduledQueries' is deprecated";
-      for (const auto& node : item.second) {
-        auto query_name = node.second.get<std::string>("name", "");
-        mergeScheduledQuery(query_name, node, conf);
-      }
-    } else if (key == "schedule") {
-      for (const auto& node : item.second) {
-        mergeScheduledQuery(node.first.data(), node, conf);
-      }
-    } else if (key == "options") {
-      for (const auto& option : item.second) {
-        mergeOption(option, conf);
-      }
-    } else if (key == "file_paths") {
-      for (const auto& category : item.second) {
-        mergeFilePath(key, category, conf);
-      }
-    } else {
-      mergeExtraKey(key, item, conf);
-    }
-  }
-
-  return Status(0, "OK");
-}
-
-const pt::ptree& Config::getParsedData(const std::string& key) {
-  if (!Registry::exists("config_parser", key)) {
-    return getInstance().empty_data_;
-  }
-
-  const auto& item = Registry::get("config_parser", key);
-  auto parser = std::static_pointer_cast<ConfigParserPlugin>(item);
-  if (parser == nullptr || parser.get() == nullptr) {
-    return getInstance().empty_data_;
-  }
-
-  return parser->data_;
-}
-
-const ConfigPluginRef Config::getParser(const std::string& key) {
-  if (!Registry::exists("config_parser", key)) {
-    return ConfigPluginRef();
-  }
-
-  const auto& item = Registry::get("config_parser", key);
-  const auto parser = std::static_pointer_cast<ConfigParserPlugin>(item);
-  if (parser == nullptr || parser.get() == nullptr) {
-    return ConfigPluginRef();
-  }
-
-  return parser;
-}
-
-Status Config::getMD5(std::string& hash_string) {
-  // Request an accessor to our own config, outside of an update.
-  ConfigDataInstance config;
-
-  std::stringstream out;
-  try {
-    pt::write_json(out, config.data(), false);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return Status(1, e.what());
-  }
-
-  hash_string = osquery::hashFromBuffer(
-      HASH_TYPE_MD5, (void*)out.str().c_str(), out.str().length());
-
-  return Status(0, "OK");
-}
-
-void Config::addScheduledQuery(const std::string& name,
-                               const std::string& query,
-                               const int interval) {
-  // Create structure to add to the schedule.
-  tree_node node;
-  node.second.put("query", query);
-  node.second.put("interval", interval);
-
-  // Call to the inline function.
-  additionalScheduledQuery(name, node, getInstance().data_);
-}
-
-Status Config::checkConfig() {
-  getInstance().force_merge_success_ = true;
-  return load();
-}
-
-bool Config::checkScheduledQuery(const std::string& query) {
-  for (const auto& scheduled_query : getInstance().data_.schedule) {
-    if (scheduled_query.second.query == query) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-bool Config::checkScheduledQueryName(const std::string& query_name) {
-  return (getInstance().data_.schedule.count(query_name) == 0) ? false : true;
-}
-
-void Config::recordQueryPerformance(const std::string& name,
-                                    size_t delay,
-                                    size_t size,
-                                    const Row& r0,
-                                    const Row& r1) {
-  // Grab a lock on the schedule structure and check the name.
-  ConfigDataInstance config;
-  if (config.schedule().count(name) == 0) {
-    // Unknown query schedule name.
-    return;
-  }
-
-  // Grab access to the non-const schedule item.
-  auto& query = getInstance().data_.schedule.at(name);
-  auto diff = AS_LITERAL(BIGINT_LITERAL, r1.at("user_time")) -
-              AS_LITERAL(BIGINT_LITERAL, r0.at("user_time"));
-  if (diff > 0) {
-    query.user_time += diff;
-  }
-
-  diff = AS_LITERAL(BIGINT_LITERAL, r1.at("system_time")) -
-         AS_LITERAL(BIGINT_LITERAL, r0.at("system_time"));
-  if (diff > 0) {
-    query.system_time += diff;
-  }
-
-  diff = AS_LITERAL(BIGINT_LITERAL, r1.at("resident_size")) -
-         AS_LITERAL(BIGINT_LITERAL, r0.at("resident_size"));
-  if (diff > 0) {
-    // Memory is stored as an average of RSS changes between query executions.
-    query.average_memory = (query.average_memory * query.executions) + diff;
-    query.average_memory = (query.average_memory / (query.executions + 1));
-  }
-
-  query.wall_time += delay;
-  query.output_size += size;
-  query.executions += 1;
-}
-
-Status ConfigPlugin::call(const PluginRequest& request,
-                          PluginResponse& response) {
-  if (request.count("action") == 0) {
-    return Status(1, "Config plugins require an action in PluginRequest");
-  }
-
-  if (request.at("action") == "genConfig") {
-    std::map<std::string, std::string> config;
-    auto stat = genConfig(config);
-    response.push_back(config);
-    return stat;
-  } else if (request.at("action") == "update") {
-    if (request.count("source") == 0 || request.count("data") == 0) {
-      return Status(1, "Missing source or data");
-    }
-    return Config::update({{request.at("source"), request.at("data")}});
-  }
-  return Status(1, "Config plugin action unknown: " + request.at("action"));
-}
-
-Status ConfigParserPlugin::setUp() {
-  for (const auto& key : keys()) {
-    data_.put(key, "");
-  }
-  return Status(0, "OK");
-}
-
-int splayValue(int original, int splayPercent) {
-  if (splayPercent <= 0 || splayPercent > 100) {
-    return original;
-  }
-
-  float percent_to_modify_by = (float)splayPercent / 100;
-  int possible_difference = original * percent_to_modify_by;
-  int max_value = original + possible_difference;
-  int min_value = original - possible_difference;
-
-  if (max_value == min_value) {
-    return max_value;
-  }
-
-  std::default_random_engine generator;
-  generator.seed(chrono_clock::now().time_since_epoch().count());
-  std::uniform_int_distribution<int> distribution(min_value, max_value);
-  return distribution(generator);
-}
-}
diff --git a/osquery/config/parsers/query_packs.cpp b/osquery/config/parsers/query_packs.cpp
deleted file mode 100644 (file)
index e5923f3..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- *  Copyright (c) 2015, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <map>
-#include <string>
-
-#include <osquery/config.h>
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-/**
- * @brief A simple ConfigParserPlugin for a "packs" dictionary key.
- *
- */
-class QueryPackConfigParserPlugin : public ConfigParserPlugin {
- public:
-  /// Request "packs" top level key.
-  std::vector<std::string> keys() { return {"packs"}; }
-
- private:
-  /// Store the signatures and file_paths and compile the rules.
-  Status update(const ConfigTreeMap& config);
-};
-
-// Function to check if the pack is valid for this version of osquery.
-// If the osquery version is greater or equal than the pack, it is good to go.
-bool versionChecker(const std::string& pack, const std::string& version) {
-  auto required_version = split(pack, ".");
-  auto build_version = split(version, ".");
-
-  size_t index = 0;
-  for (const auto& chunk : build_version) {
-    if (required_version.size() <= index) {
-      return true;
-    }
-    try {
-      if (std::stoi(chunk) < std::stoi(required_version[index])) {
-        return false;
-      }
-    } catch (const std::invalid_argument& e) {
-      if (chunk.compare(required_version[index]) < 0) {
-        return false;
-      }
-    }
-    index++;
-  }
-  return true;
-}
-
-// Perform a string string search for the actual platform within the required.
-bool platformChecker(const std::string& required, const std::string& platform) {
-  // Match if platform is 'ubuntu12' and required is 'ubuntu'.
-  // Do not match if platform is 'ubuntu12' and required is 'ubuntu14'.
-#ifdef __linux__
-  if (required.find("linux") != std::string::npos) {
-    return true;
-  }
-#endif
-  if (required.find("any") != std::string::npos ||
-      required.find("all") != std::string::npos) {
-    return true;
-  }
-  return (required.find(platform) != std::string::npos);
-}
-
-Status parsePack(const std::string& name, const pt::ptree& data) {
-  if (data.count("queries") == 0) {
-    return Status(0, "Pack contains no queries");
-  }
-
-  // Check the pack-global minimum SDK version and platform.
-  auto version = data.get("version", "");
-  if (version.size() > 0 && !versionChecker(version, kSDKVersion)) {
-    return Status(0, "Minimum SDK version not met");
-  }
-
-  auto platform = data.get("platform", "");
-  if (platform.size() > 0 && !platformChecker(platform, kSDKPlatform)) {
-    return Status(0, "Platform version mismatch");
-  }
-
-  // For each query in the pack's queries, check their version/platform.
-  for (const auto& query : data.get_child("queries")) {
-    auto query_string = query.second.get("query", "");
-    if (Config::checkScheduledQuery(query_string)) {
-      VLOG(1) << "Query pack " << name
-              << " contains a duplicated query: " << query.first;
-      continue;
-    }
-
-    // Check the specific query's required version.
-    version = query.second.get("version", "");
-    if (version.size() > 0 && !versionChecker(version, kSDKVersion)) {
-      continue;
-    }
-
-    // Check the specific query's required platform.
-    platform = query.second.get("platform", "");
-    if (platform.size() > 0 && !platformChecker(platform, kSDKPlatform)) {
-      continue;
-    }
-
-    // Hope there is a supplied/non-0 query interval to apply this query pack
-    // query to the osquery schedule.
-    auto query_interval = query.second.get("interval", 0);
-    if (query_interval > 0) {
-      auto query_name = "pack_" + name + "_" + query.first;
-      Config::addScheduledQuery(query_name, query_string, query_interval);
-    }
-  }
-
-  return Status(0, "OK");
-}
-
-Status QueryPackConfigParserPlugin::update(const ConfigTreeMap& config) {
-  // Iterate through all the packs to get the configuration.
-  for (auto const& pack : config.at("packs")) {
-    auto pack_name = std::string(pack.first.data());
-    auto pack_path = std::string(pack.second.data());
-
-    // Read each pack configuration in JSON
-    pt::ptree pack_data;
-    auto status = osquery::parseJSON(pack_path, pack_data);
-    if (!status.ok()) {
-      LOG(WARNING) << "Error parsing Query Pack " << pack_name << ": "
-                   << status.getMessage();
-      continue;
-    }
-
-    // Parse the pack, meaning compare version/platform requirements and
-    // check the sanity of each query in the pack's queries.
-    status = parsePack(pack_name, pack_data);
-    if (!status.ok()) {
-      return status;
-    }
-
-    // Save the queries list for table-based introspection.
-    data_.put_child(pack_name, pack_data);
-    // Record the pack path.
-    data_.put(pack_name + ".path", pack_path);
-  }
-
-  return Status(0, "OK");
-}
-
-/// Call the simple Query Packs ConfigParserPlugin "packs".
-REGISTER_INTERNAL(QueryPackConfigParserPlugin, "config_parser", "packs");
-}
diff --git a/osquery/config/parsers/tests/query_packs_tests.cpp b/osquery/config/parsers/tests/query_packs_tests.cpp
deleted file mode 100644 (file)
index 22a67fb..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/logger.h>
-
-#include "osquery/core/test_util.h"
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-// Test the pack version checker.
-bool versionChecker(const std::string& pack, const std::string& version);
-// Test the pack platform checker.
-bool platformChecker(const std::string& required, const std::string& platform);
-
-pt::ptree getQueryPacksContent() {
-  pt::ptree pack_tree;
-  auto pack_path = kTestDataPath + "test_pack.conf";
-  auto status = osquery::parseJSON(pack_path, pack_tree);
-  return pack_tree.get_child("queries");
-}
-
-std::map<std::string, pt::ptree> getQueryPacksExpectedResults() {
-  std::map<std::string, pt::ptree> result;
-  pt::ptree aux_data;
-
-  std::string query = "select * from launchd";
-  aux_data.put("query", query);
-  int interval = 414141;
-  aux_data.put("interval", interval);
-  std::string platform = "whatever";
-  aux_data.put("platform", platform);
-  std::string version = "1.0.0";
-  aux_data.put("version", version);
-  std::string description = "Very descriptive description";
-  aux_data.put("description", description);
-  std::string value = "Value overflow";
-  aux_data.put("value", value);
-
-  result.insert(std::pair<std::string, pt::ptree>("launchd", aux_data));
-
-  return result;
-}
-
-class QueryPacksConfigTests : public testing::Test {};
-
-TEST_F(QueryPacksConfigTests, version_comparisons) {
-  EXPECT_TRUE(versionChecker("1.0.0", "1.0.0"));
-  EXPECT_TRUE(versionChecker("1.0.0", "1.2.0"));
-  EXPECT_TRUE(versionChecker("1.0", "1.2.0"));
-  EXPECT_TRUE(versionChecker("1.0", "1.0.2"));
-  EXPECT_TRUE(versionChecker("1.0.0", "1.0.2-r1"));
-  EXPECT_FALSE(versionChecker("1.2", "1.0.2"));
-  EXPECT_TRUE(versionChecker("1.0.0-r1", "1.0.0"));
-}
-
-TEST_F(QueryPacksConfigTests, platform_comparisons) {
-#ifdef __linux__
-  // If the platform is linux and the required platform is linux, match
-  EXPECT_TRUE(platformChecker("linux", "ubuntu"));
-  EXPECT_TRUE(platformChecker("linux", "who_knows_what"));
-#endif
-  EXPECT_TRUE(platformChecker("linux,darwin", "darwin"));
-  EXPECT_TRUE(platformChecker("darwin", "darwin"));
-  EXPECT_FALSE(platformChecker("darwin", "linux"));
-
-  EXPECT_TRUE(platformChecker(" darwin", "darwin"));
-  // There are no logical operators, just matching.
-  EXPECT_TRUE(platformChecker("!darwin", "darwin"));
-
-  EXPECT_TRUE(platformChecker("all", "darwin"));
-  EXPECT_TRUE(platformChecker("any", "darwin"));
-}
-
-TEST_F(QueryPacksConfigTests, test_query_packs_configuration) {
-  auto data = getQueryPacksContent();
-  auto expected = getQueryPacksExpectedResults();
-  auto& real_ld = data.get_child("launchd");
-  auto& expect_ld = expected["launchd"];
-
-  EXPECT_EQ(expect_ld.get("query", ""), real_ld.get("query", ""));
-  EXPECT_EQ(expect_ld.get("interval", 0), real_ld.get("interval", 0));
-  EXPECT_EQ(expect_ld.get("platform", ""), real_ld.get("platform", ""));
-  EXPECT_EQ(expect_ld.get("version", ""), real_ld.get("version", ""));
-  EXPECT_EQ(expect_ld.get("description", ""), real_ld.get("description", ""));
-  EXPECT_EQ(expect_ld.get("value", ""), real_ld.get("value", ""));
-}
-}
diff --git a/osquery/config/plugins/filesystem.cpp b/osquery/config/plugins/filesystem.cpp
deleted file mode 100644 (file)
index e73bb78..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-
-#include <boost/filesystem/operations.hpp>
-
-#include <osquery/config.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/filesystem.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-CLI_FLAG(string,
-         config_path,
-         "/var/osquery/osquery.conf",
-         "Path to JSON config file");
-
-class FilesystemConfigPlugin : public ConfigPlugin {
- public:
-  Status genConfig(std::map<std::string, std::string>& config);
-};
-
-REGISTER(FilesystemConfigPlugin, "config", "filesystem");
-
-Status FilesystemConfigPlugin::genConfig(
-    std::map<std::string, std::string>& config) {
-  if (!fs::is_regular_file(FLAGS_config_path)) {
-    return Status(1, "config file does not exist");
-  }
-
-  std::vector<std::string> conf_files;
-  resolveFilePattern(FLAGS_config_path + ".d/%.conf", conf_files);
-  std::sort(conf_files.begin(), conf_files.end());
-  conf_files.push_back(FLAGS_config_path);
-
-  for (const auto& path : conf_files) {
-    std::string content;
-    if (readFile(path, content).ok()) {
-      config[path] = content;
-    }
-  }
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/config/tests/config_tests.cpp b/osquery/config/tests/config_tests.cpp
deleted file mode 100644 (file)
index 38a786b..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <osquery/config.h>
-#include <osquery/core.h>
-#include <osquery/flags.h>
-#include <osquery/registry.h>
-#include <osquery/sql.h>
-
-#include "osquery/core/test_util.h"
-
-namespace osquery {
-
-// The config_path flag is defined in the filesystem config plugin.
-DECLARE_string(config_path);
-
-class ConfigTests : public testing::Test {
- public:
-  ConfigTests() {
-    Registry::setActive("config", "filesystem");
-    FLAGS_config_path = kTestDataPath + "test.config";
-  }
-
- protected:
-  void SetUp() {
-    createMockFileStructure();
-    Registry::setUp();
-    Config::load();
-  }
-
-  void TearDown() { tearDownMockFileStructure(); }
-};
-
-class TestConfigPlugin : public ConfigPlugin {
- public:
-  TestConfigPlugin() {}
-  Status genConfig(std::map<std::string, std::string>& config) {
-    config["data"] = "foobar";
-    return Status(0, "OK");
-    ;
-  }
-};
-
-TEST_F(ConfigTests, test_plugin) {
-  Registry::add<TestConfigPlugin>("config", "test");
-
-  // Change the active config plugin.
-  EXPECT_TRUE(Registry::setActive("config", "test").ok());
-
-  PluginResponse response;
-  auto status = Registry::call("config", {{"action", "genConfig"}}, response);
-
-  EXPECT_EQ(status.ok(), true);
-  EXPECT_EQ(status.toString(), "OK");
-  EXPECT_EQ(response[0].at("data"), "foobar");
-}
-
-/* deprecated
-TEST_F(ConfigTests, test_queries_execute) {
-  ConfigDataInstance config;
-  EXPECT_EQ(config.schedule().size(), 3);
-}
-
-TEST_F(ConfigTests, test_watched_files) {
-  ConfigDataInstance config;
-  ASSERT_EQ(config.files().size(), 3);
-  // From the deprecated "additional_monitoring" collection.
-  EXPECT_EQ(config.files().at("downloads").size(), 1);
-
-  // From the new, recommended top-level "file_paths" collection.
-  EXPECT_EQ(config.files().at("system_binaries").size(), 2);
-}
-*/
-
-TEST_F(ConfigTests, test_locking) {
-  {
-    // Assume multiple instance accessors will be active.
-    ConfigDataInstance config1;
-    ConfigDataInstance config2;
-
-    // But a unique lock cannot be acquired.
-    boost::unique_lock<boost::shared_mutex> lock(Config::getInstance().mutex_,
-                                                 boost::defer_lock);
-    ASSERT_FALSE(lock.try_lock());
-  }
-
-  {
-    // However, a unique lock can be obtained when without instances accessors.
-    boost::unique_lock<boost::shared_mutex> lock(Config::getInstance().mutex_,
-                                                 boost::defer_lock);
-    ASSERT_TRUE(lock.try_lock());
-  }
-}
-
-TEST_F(ConfigTests, test_config_update) {
-  std::string digest;
-  // Get a snapshot of the digest before making config updates.
-  auto status = Config::getMD5(digest);
-  EXPECT_TRUE(status);
-
-  // Request an update of the 'new_source1'. Set new1 = value.
-  status =
-      Config::update({{"new_source1", "{\"options\": {\"new1\": \"value\"}}"}});
-  EXPECT_TRUE(status);
-
-  // At least, the amalgamated config digest should have changed.
-  std::string new_digest;
-  Config::getMD5(new_digest);
-  EXPECT_NE(digest, new_digest);
-
-  // Access the option that was added in the update to source 'new_source1'.
-  {
-    ConfigDataInstance config;
-    auto option = config.data().get<std::string>("options.new1", "");
-    EXPECT_EQ(option, "value");
-  }
-
-  // Add a lexically larger source that emits the same option 'new1'.
-  Config::update({{"new_source2", "{\"options\": {\"new1\": \"changed\"}}"}});
-
-  {
-    ConfigDataInstance config;
-    auto option = config.data().get<std::string>("options.new1", "");
-    // Expect the amalgamation to have overwritten 'new_source1'.
-    EXPECT_EQ(option, "changed");
-  }
-
-  // Again add a source but emit a different option, both 'new1' and 'new2'
-  // should be in the amalgamated/merged config.
-  Config::update({{"new_source3", "{\"options\": {\"new2\": \"different\"}}"}});
-
-  {
-    ConfigDataInstance config;
-    auto option = config.data().get<std::string>("options.new1", "");
-    EXPECT_EQ(option, "changed");
-    option = config.data().get<std::string>("options.new2", "");
-    EXPECT_EQ(option, "different");
-  }
-}
-
-TEST_F(ConfigTests, test_bad_config_update) {
-  std::string bad_json = "{\"options\": {},}";
-  ASSERT_NO_THROW(Config::update({{"bad_source", bad_json}}));
-}
-
-class TestConfigParserPlugin : public ConfigParserPlugin {
- public:
-  std::vector<std::string> keys() {
-    // This config parser requests the follow top-level-config keys.
-    return {"dictionary", "dictionary2", "list"};
-  }
-
-  Status update(const std::map<std::string, ConfigTree>& config) {
-    // Set a simple boolean indicating the update callin occurred.
-    update_called = true;
-    // Copy all expected keys into the parser's data.
-    for (const auto& key : config) {
-      data_.put_child(key.first, key.second);
-    }
-
-    // Set parser-rendered additional data.
-    // Other plugins may request this "rendered/derived" data using a
-    // ConfigDataInstance and the getParsedData method.
-    data_.put("dictionary3.key2", "value2");
-    return Status(0, "OK");
-  }
-
-  // Flag tracking that the update method was called.
-  static bool update_called;
-
- private:
-  FRIEND_TEST(ConfigTests, test_config_parser);
-};
-
-// An intermediate boolean to check parser updates.
-bool TestConfigParserPlugin::update_called = false;
-
-TEST_F(ConfigTests, test_config_parser) {
-  // Register a config parser plugin, and call setup.
-  Registry::add<TestConfigParserPlugin>("config_parser", "test");
-  Registry::get("config_parser", "test")->setUp();
-
-  {
-    // Access the parser's data without having updated the configuration.
-    ConfigDataInstance config;
-    const auto& test_data = config.getParsedData("test");
-
-    // Expect the setUp method to have run and set blank defaults.
-    // Accessing an invalid property tree key will abort.
-    ASSERT_EQ(test_data.get_child("dictionary").count(""), 0);
-  }
-
-  // Update or load the config, expect the parser to be called.
-  Config::update(
-      {{"source1",
-        "{\"dictionary\": {\"key1\": \"value1\"}, \"list\": [\"first\"]}"}});
-  ASSERT_TRUE(TestConfigParserPlugin::update_called);
-
-  {
-    // Now access the parser's data AFTER updating the config (no longer blank)
-    ConfigDataInstance config;
-    const auto& test_data = config.getParsedData("test");
-
-    // Expect a value that existed in the configuration.
-    EXPECT_EQ(test_data.count("dictionary"), 1);
-    EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1");
-    // Expect a value for every key the parser requested.
-    // Every requested key will be present, event if the key's tree is empty.
-    EXPECT_EQ(test_data.count("dictionary2"), 1);
-    // Expect the parser-created data item.
-    EXPECT_EQ(test_data.count("dictionary3"), 1);
-    EXPECT_EQ(test_data.get("dictionary3.key2", ""), "value2");
-  }
-
-  // Update from a secondary source into a dictionary.
-  // Expect that the keys in the top-level dictionary are merged.
-  Config::update({{"source2", "{\"dictionary\": {\"key3\": \"value3\"}}"}});
-  // Update from a third source into a list.
-  // Expect that the items from each source in the top-level list are merged.
-  Config::update({{"source3", "{\"list\": [\"second\"]}"}});
-
-  {
-    ConfigDataInstance config;
-    const auto& test_data = config.getParsedData("test");
-
-    EXPECT_EQ(test_data.count("dictionary"), 1);
-    EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1");
-    EXPECT_EQ(test_data.get("dictionary.key3", ""), "value3");
-    EXPECT_EQ(test_data.count("list"), 1);
-    EXPECT_EQ(test_data.get_child("list").count(""), 2);
-  }
-}
-
-class TestConfigMutationParserPlugin : public ConfigParserPlugin {
- public:
-  std::vector<std::string> keys() {
-    // This config parser wants access to the well-known schedule key.
-    return {"schedule"};
-  }
-
-  Status update(const std::map<std::string, ConfigTree>& config) {
-    // The merged raw schedule is available as a property tree.
-    auto& schedule_data = config.at("schedule");
-    (void)schedule_data;
-
-    {
-      // But we want access to the parsed schedule structure.
-      ConfigDataInstance _config;
-      auto& data = mutableConfigData(_config);
-
-      ScheduledQuery query;
-      query.query = "new query";
-      query.interval = 1;
-      data.schedule["test_config_mutation"] = query;
-    }
-
-    return Status(0, "OK");
-  }
-
- private:
-  FRIEND_TEST(ConfigTests, test_config_mutaion_parser);
-};
-
-TEST_F(ConfigTests, test_config_mutaion_parser) {
-  Registry::add<TestConfigMutationParserPlugin>("config_parser", "mutable");
-  Registry::get("config_parser", "mutable")->setUp();
-
-  // Update or load the config, expect the parser to be called.
-  Config::update({{"source1", "{\"schedule\": {}}"}});
-
-  {
-    ConfigDataInstance config;
-    // The config schedule should have been mutated.
-    EXPECT_EQ(config.schedule().count("test_config_mutation"), 1);
-  }
-}
-
-TEST_F(ConfigTests, test_splay) {
-  auto val1 = splayValue(100, 10);
-  EXPECT_GE(val1, 90);
-  EXPECT_LE(val1, 110);
-
-  auto val2 = splayValue(100, 10);
-  EXPECT_GE(val2, 90);
-  EXPECT_LE(val2, 110);
-
-  auto val3 = splayValue(10, 0);
-  EXPECT_EQ(val3, 10);
-
-  auto val4 = splayValue(100, 1);
-  EXPECT_GE(val4, 99);
-  EXPECT_LE(val4, 101);
-
-  auto val5 = splayValue(1, 10);
-  EXPECT_EQ(val5, 1);
-}
-}
diff --git a/osquery/config/update.cpp b/osquery/config/update.cpp
deleted file mode 100644 (file)
index 6421c1a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/config.h>
-
-namespace osquery {
-
-/**
- * @brief A special config plugin that updates an osquery core's config.
- *
- * Config plugins may asynchronously change config data for the core osquery
- * process. This is a rare instance where a plugin acts to change core state.
- * Plugins normally act on behalf of a registry or extension call.
- * To acheive plugin-initiated calls, Config plugins chain calls to plugins
- * using the UpdateConfigPlugin named 'update'.
- *
- * Plugins do not need to implement call-chaining explicitly. If an extension
- * plugin implements an asynchronous feature it should call `Config::update`
- * directly. The osquery config will check if the registry is external, meaning
- * the config instance is running as an extension. If external, config will
- * route the update request and the registry will send missing (in this case
- * "config/update" is missing) requests to core.
- */
-class UpdateConfigPlugin : public ConfigPlugin {
- public:
-  Status genConfig(std::map<std::string, std::string>& config) {
-    return Status(0, "Unused");
-  }
-};
-
-REGISTER(UpdateConfigPlugin, "config", "update");
-}
diff --git a/osquery/core/CMakeLists.txt b/osquery/core/CMakeLists.txt
deleted file mode 100644 (file)
index fe39034..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_core init.cpp
-                                                                conversions.cpp
-                                                                system.cpp
-                                                                text.cpp
-                                                                tables.cpp
-                                                                flags.cpp
-                                                                hash.cpp
-                                                                watcher.cpp)
-
-# TODO(Sangwan): Detach from core
-ADD_OSQUERY_LIBRARY(osquery_test_util test_util.cpp)
-
-FILE(GLOB OSQUERY_CORE_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_CORE_TESTS})
diff --git a/osquery/core/conversions.cpp b/osquery/core/conversions.cpp
deleted file mode 100644 (file)
index d26c22a..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sstream>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/archive/iterators/transform_width.hpp>
-#include <boost/archive/iterators/binary_from_base64.hpp>
-#include <boost/archive/iterators/base64_from_binary.hpp>
-
-#include "osquery/core/conversions.h"
-
-namespace bai = boost::archive::iterators;
-
-namespace osquery {
-
-typedef bai::binary_from_base64<const char*> base64_str;
-typedef bai::transform_width<base64_str, 8, 6> base64_dec;
-typedef bai::transform_width<std::string::const_iterator, 6, 8> base64_enc;
-typedef bai::base64_from_binary<base64_enc> it_base64;
-
-std::string base64Decode(const std::string& encoded) {
-  std::string is;
-  std::stringstream os;
-
-  is = encoded;
-  boost::replace_all(is, "\r\n", "");
-  boost::replace_all(is, "\n", "");
-  uint32_t size = is.size();
-
-  // Remove the padding characters
-  if (size && is[size - 1] == '=') {
-    --size;
-    if (size && is[size - 1] == '=') {
-      --size;
-    }
-  }
-
-  if (size == 0) {
-    return "";
-  }
-
-  std::copy(base64_dec(is.data()),
-            base64_dec(is.data() + size),
-            std::ostream_iterator<char>(os));
-
-  return os.str();
-}
-
-std::string base64Encode(const std::string& unencoded) {
-  std::stringstream os;
-
-  if (unencoded.size() == 0) {
-    return std::string();
-  }
-
-  unsigned int writePaddChars = (3-unencoded.length()%3)%3;
-  std::string base64(it_base64(unencoded.begin()), it_base64(unencoded.end()));
-  base64.append(writePaddChars,'=');
-  os << base64;
-  return os.str();
-}
-}
diff --git a/osquery/core/conversions.h b/osquery/core/conversions.h
deleted file mode 100644 (file)
index c8a0a1c..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <memory>
-
-#include <boost/bind.hpp>
-#include <boost/shared_ptr.hpp>
-
-#ifdef DARWIN
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-namespace osquery {
-
-template <typename T>
-void do_release_boost(typename boost::shared_ptr<T> const&, T*) {}
-
-/**
- * @brief Convert a boost::shared_ptr to a std::shared_ptr
- */
-template <typename T>
-typename std::shared_ptr<T> boost_to_std_shared_ptr(
-    typename boost::shared_ptr<T> const& p) {
-  return std::shared_ptr<T>(p.get(), boost::bind(&do_release_boost<T>, p, _1));
-}
-
-template <typename T>
-void do_release_std(typename std::shared_ptr<T> const&, T*) {}
-
-/**
- * @brief Convert a std::shared_ptr to a boost::shared_ptr
- */
-template <typename T>
-typename boost::shared_ptr<T> std_to_boost_shared_ptr(
-    typename std::shared_ptr<T> const& p) {
-  return boost::shared_ptr<T>(p.get(), boost::bind(&do_release_std<T>, p, _1));
-}
-
-/**
- * @brief Decode a base64 encoded string.
- *
- * @param encoded The encode base64 string.
- * @return Decoded string.
- */
-std::string base64Decode(const std::string& encoded);
-
-/**
- * @brief Encode a  string.
- *
- * @param A string to encode.
- * @return Encoded string.
- */
-std::string base64Encode(const std::string& unencoded);
-
-#ifdef DARWIN
-/**
- * @brief Convert a CFStringRef to a std::string.
- */
-std::string stringFromCFString(const CFStringRef& cf_string);
-
-/**
- * @brief Convert a CFNumberRef to a std::string.
- */
-std::string stringFromCFNumber(const CFDataRef& cf_number);
-std::string stringFromCFData(const CFDataRef& cf_data);
-#endif
-
-}
diff --git a/osquery/core/flags.cpp b/osquery/core/flags.cpp
deleted file mode 100644 (file)
index e1474a2..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/flags.h>
-
-namespace boost {
-template <>
-bool lexical_cast<bool, std::string>(const std::string& arg) {
-  std::istringstream ss(arg);
-  bool b;
-  ss >> std::boolalpha >> b;
-  return b;
-}
-
-template <>
-std::string lexical_cast<std::string, bool>(const bool& b) {
-  std::ostringstream ss;
-  ss << std::boolalpha << b;
-  return ss.str();
-}
-}
-
-namespace osquery {
-
-int Flag::create(const std::string& name, const FlagDetail& flag) {
-  instance().flags_.insert(std::make_pair(name, flag));
-  return 0;
-}
-
-int Flag::createAlias(const std::string& alias, const FlagDetail& flag) {
-  instance().aliases_.insert(std::make_pair(alias, flag));
-  return 0;
-}
-
-Status Flag::getDefaultValue(const std::string& name, std::string& value) {
-  GFLAGS_NAMESPACE::CommandLineFlagInfo info;
-  if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) {
-    return Status(1, "Flags name not found.");
-  }
-
-  value = info.default_value;
-  return Status(0, "OK");
-}
-
-bool Flag::isDefault(const std::string& name) {
-  GFLAGS_NAMESPACE::CommandLineFlagInfo info;
-  if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) {
-    return false;
-  }
-
-  return info.is_default;
-}
-
-std::string Flag::getValue(const std::string& name) {
-  std::string current_value;
-  GFLAGS_NAMESPACE::GetCommandLineOption(name.c_str(), &current_value);
-  return current_value;
-}
-
-std::string Flag::getType(const std::string& name) {
-  GFLAGS_NAMESPACE::CommandLineFlagInfo info;
-  if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) {
-    return "";
-  }
-  return info.type;
-}
-
-std::string Flag::getDescription(const std::string& name) {
-  if (instance().flags_.count(name)) {
-    return instance().flags_.at(name).description;
-  }
-
-  if (instance().aliases_.count(name)) {
-    return getDescription(instance().aliases_.at(name).description);
-  }
-  return "";
-}
-
-Status Flag::updateValue(const std::string& name, const std::string& value) {
-  if (instance().flags_.count(name) > 0) {
-    GFLAGS_NAMESPACE::SetCommandLineOption(name.c_str(), value.c_str());
-    return Status(0, "OK");
-  } else if (instance().aliases_.count(name) > 0) {
-    // Updating a flag by an alias name.
-    auto& real_name = instance().aliases_.at(name).description;
-    GFLAGS_NAMESPACE::SetCommandLineOption(real_name.c_str(), value.c_str());
-    return Status(0, "OK");
-  }
-  return Status(1, "Flag not found");
-}
-
-std::map<std::string, FlagInfo> Flag::flags() {
-  std::vector<GFLAGS_NAMESPACE::CommandLineFlagInfo> info;
-  GFLAGS_NAMESPACE::GetAllFlags(&info);
-
-  std::map<std::string, FlagInfo> flags;
-  for (const auto& flag : info) {
-    if (instance().flags_.count(flag.name) == 0) {
-      // This flag info was not defined within osquery.
-      continue;
-    }
-
-    // Set the flag info from the internal info kept by Gflags, except for
-    // the stored description. Gflag keeps an "unknown" value if the flag
-    // was declared without a definition.
-    flags[flag.name] = {flag.type,
-                        instance().flags_.at(flag.name).description,
-                        flag.default_value,
-                        flag.current_value,
-                        instance().flags_.at(flag.name)};
-  }
-  return flags;
-}
-
-void Flag::printFlags(bool shell, bool external, bool cli) {
-  std::vector<GFLAGS_NAMESPACE::CommandLineFlagInfo> info;
-  GFLAGS_NAMESPACE::GetAllFlags(&info);
-  auto& details = instance().flags_;
-
-  // Determine max indent needed for all flag names.
-  size_t max = 0;
-  for (const auto& flag : details) {
-    max = (max > flag.first.size()) ? max : flag.first.size();
-  }
-  // Additional index for flag values.
-  max += 6;
-
-  auto& aliases = instance().aliases_;
-  for (const auto& flag : info) {
-    if (details.count(flag.name) > 0) {
-      const auto& detail = details.at(flag.name);
-      if ((shell && !detail.shell) || (!shell && detail.shell) ||
-          (external && !detail.external) || (!external && detail.external) ||
-          (cli && !detail.cli) || (!cli && detail.cli) || detail.hidden) {
-        continue;
-      }
-    } else if (aliases.count(flag.name) > 0) {
-      const auto& alias = aliases.at(flag.name);
-      // Aliases are only printed if this is an external tool and the alias
-      // is external.
-      if (!alias.external || !external) {
-        continue;
-      }
-    } else {
-      // This flag was not defined as an osquery flag or flag alias.
-      continue;
-    }
-
-    fprintf(stdout, "    --%s", flag.name.c_str());
-
-    int pad = max;
-    if (flag.type != "bool") {
-      fprintf(stdout, " VALUE");
-      pad -= 6;
-    }
-    pad -= flag.name.size();
-
-    if (pad > 0 && pad < 80) {
-      // Never pad more than 80 characters.
-      fprintf(stdout, "%s", std::string(pad, ' ').c_str());
-    }
-    fprintf(stdout, "  %s\n", getDescription(flag.name).c_str());
-  }
-}
-}
diff --git a/osquery/core/hash.cpp b/osquery/core/hash.cpp
deleted file mode 100644 (file)
index fc7d4a9..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <iomanip>
-#include <sstream>
-
-#include <osquery/filesystem.h>
-#include <osquery/hash.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-
-#ifdef __APPLE__
-  #import <CommonCrypto/CommonDigest.h>
-  #define __HASH_API(name) CC_##name
-#else
-  #include <openssl/sha.h>
-  #include <openssl/md5.h>
-  #define __HASH_API(name) name
-
-  #define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
-  #define SHA1_CTX SHA_CTX
-#endif
-
-#define HASH_CHUNK_SIZE 1024
-
-Hash::~Hash() {
-  if (ctx_ != nullptr) {
-    free(ctx_);
-  }
-}
-
-Hash::Hash(HashType algorithm) : algorithm_(algorithm) {
-  if (algorithm_ == HASH_TYPE_MD5) {
-    length_ = __HASH_API(MD5_DIGEST_LENGTH);
-    ctx_ = (__HASH_API(MD5_CTX)*)malloc(sizeof(__HASH_API(MD5_CTX)));
-    __HASH_API(MD5_Init)((__HASH_API(MD5_CTX)*)ctx_);
-  } else if (algorithm_ == HASH_TYPE_SHA1) {
-    length_ = __HASH_API(SHA1_DIGEST_LENGTH);
-    ctx_ = (__HASH_API(SHA1_CTX)*)malloc(sizeof(__HASH_API(SHA1_CTX)));
-    __HASH_API(SHA1_Init)((__HASH_API(SHA1_CTX)*)ctx_);
-  } else if (algorithm_ == HASH_TYPE_SHA256) {
-    length_ = __HASH_API(SHA256_DIGEST_LENGTH);
-    ctx_ = (__HASH_API(SHA256_CTX)*)malloc(sizeof(__HASH_API(SHA256_CTX)));
-    __HASH_API(SHA256_Init)((__HASH_API(SHA256_CTX)*)ctx_);
-  } else {
-    throw std::domain_error("Unknown hash function");
-  }
-}
-
-void Hash::update(const void* buffer, size_t size) {
-  if (algorithm_ == HASH_TYPE_MD5) {
-    __HASH_API(MD5_Update)((__HASH_API(MD5_CTX)*)ctx_, buffer, size);
-  } else if (algorithm_ == HASH_TYPE_SHA1) {
-    __HASH_API(SHA1_Update)((__HASH_API(SHA1_CTX)*)ctx_, buffer, size);
-  } else if (algorithm_ == HASH_TYPE_SHA256) {
-    __HASH_API(SHA256_Update)((__HASH_API(SHA256_CTX)*)ctx_, buffer, size);
-  }
-}
-
-std::string Hash::digest() {
-  unsigned char hash[length_];
-
-  if (algorithm_ == HASH_TYPE_MD5) {
-    __HASH_API(MD5_Final)(hash, (__HASH_API(MD5_CTX)*)ctx_);
-  } else if (algorithm_ == HASH_TYPE_SHA1) {
-    __HASH_API(SHA1_Final)(hash, (__HASH_API(SHA1_CTX)*)ctx_);
-  } else if (algorithm_ == HASH_TYPE_SHA256) {
-    __HASH_API(SHA256_Final)(hash, (__HASH_API(SHA256_CTX)*)ctx_);
-  }
-
-  // The hash value is only relevant as a hex digest.
-  std::stringstream digest;
-  for (int i = 0; i < length_; i++) {
-    digest << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
-  }
-
-  return digest.str();
-}
-
-std::string hashFromBuffer(HashType hash_type, const void* buffer, size_t size) {
-  Hash hash(hash_type);
-  hash.update(buffer, size);
-  return hash.digest();
-}
-
-std::string hashFromFile(HashType hash_type, const std::string& path) {
-  // Perform a dry-run of a file read without filling in any content.
-  auto status = readFile(path);
-  if (!status.ok()) {
-    return "";
-  }
-
-  Hash hash(hash_type);
-  // Use the canonicalized path returned from a successful readFile dry-run.
-  FILE* file = fopen(status.what().c_str(), "rb");
-  if (file == nullptr) {
-    VLOG(1) << "Cannot hash/open file " << path;
-    return "";
-  }
-
-  // Then call updates with read chunks.
-  size_t bytes_read = 0;
-  unsigned char buffer[HASH_CHUNK_SIZE];
-  while ((bytes_read = fread(buffer, 1, HASH_CHUNK_SIZE, file))) {
-    hash.update(buffer, bytes_read);
-  }
-
-  fclose(file);
-  return hash.digest();
-}
-}
diff --git a/osquery/core/init.cpp b/osquery/core/init.cpp
deleted file mode 100644 (file)
index 655ab2d..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <chrono>
-#include <random>
-
-#include <syslog.h>
-#include <stdio.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <iostream>
-
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/filesystem.hpp>
-
-#include <osquery/config.h>
-#include <osquery/core.h>
-#include <osquery/events.h>
-#include <osquery/extensions.h>
-#include <osquery/flags.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/registry.h>
-
-#include "osquery/core/watcher.h"
-#include "osquery/database/db_handle.h"
-
-#ifdef __linux__
-#include <sys/resource.h>
-#include <sys/syscall.h>
-
-/*
- * These are the io priority groups as implemented by CFQ. RT is the realtime
- * class, it always gets premium service. BE is the best-effort scheduling
- * class, the default for any process. IDLE is the idle scheduling class, it
- * is only served when no one else is using the disk.
- */
-enum {
-  IOPRIO_CLASS_NONE,
-  IOPRIO_CLASS_RT,
-  IOPRIO_CLASS_BE,
-  IOPRIO_CLASS_IDLE,
-};
-
-/*
- * 8 best effort priority levels are supported
- */
-#define IOPRIO_BE_NR  (8)
-
-enum {
-  IOPRIO_WHO_PROCESS = 1,
-  IOPRIO_WHO_PGRP,
-  IOPRIO_WHO_USER,
-};
-#endif
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-#define DESCRIPTION \
-  "osquery %s, your OS as a high-performance relational database\n"
-#define EPILOG "\nosquery project page <https://osquery.io>.\n"
-#define OPTIONS \
-  "\nosquery configuration options (set by config or CLI flags):\n\n"
-#define OPTIONS_SHELL "\nosquery shell-only CLI flags:\n\n"
-#define OPTIONS_CLI "osquery%s command line flags:\n\n"
-#define USAGE "Usage: %s [OPTION]... %s\n\n"
-#define CONFIG_ERROR                                                          \
-  "You are using default configurations for osqueryd for one or more of the " \
-  "following\n"                                                               \
-  "flags: pidfile, db_path.\n\n"                                              \
-  "These options create files in /var/osquery but it looks like that path "   \
-  "has not\n"                                                                 \
-  "been created. Please consider explicitly defining those "                  \
-  "options as a different \n"                                                 \
-  "path. Additionally, review the \"using osqueryd\" wiki page:\n"            \
-  " - https://osquery.readthedocs.org/en/latest/introduction/using-osqueryd/" \
-  "\n\n";
-
-typedef std::chrono::high_resolution_clock chrono_clock;
-
-CLI_FLAG(bool,
-         config_check,
-         false,
-         "Check the format of an osquery config and exit");
-
-#ifndef __APPLE__
-CLI_FLAG(bool, daemonize, false, "Run as daemon (osqueryd only)");
-#endif
-
-ToolType kToolType = OSQUERY_TOOL_UNKNOWN;
-
-void printUsage(const std::string& binary, int tool) {
-  // Parse help options before gflags. Only display osquery-related options.
-  fprintf(stdout, DESCRIPTION, kVersion.c_str());
-  if (tool == OSQUERY_TOOL_SHELL) {
-    // The shell allows a caller to run a single SQL statement and exit.
-    fprintf(stdout, USAGE, binary.c_str(), "[SQL STATEMENT]");
-  } else {
-    fprintf(stdout, USAGE, binary.c_str(), "");
-  }
-
-  if (tool == OSQUERY_EXTENSION) {
-    fprintf(stdout, OPTIONS_CLI, " extension");
-    Flag::printFlags(false, true);
-  } else {
-    fprintf(stdout, OPTIONS_CLI, "");
-    Flag::printFlags(false, false, true);
-    fprintf(stdout, OPTIONS);
-    Flag::printFlags();
-  }
-
-  if (tool == OSQUERY_TOOL_SHELL) {
-    // Print shell flags.
-    fprintf(stdout, OPTIONS_SHELL);
-    Flag::printFlags(true);
-  }
-
-  fprintf(stdout, EPILOG);
-}
-
-Initializer::Initializer(int& argc, char**& argv, ToolType tool)
-    : argc_(&argc),
-      argv_(&argv),
-      tool_(tool),
-      binary_(fs::path(std::string(argv[0])).filename().string()) {
-  std::srand(chrono_clock::now().time_since_epoch().count());
-
-  // osquery implements a custom help/usage output.
-  for (int i = 1; i < *argc_; i++) {
-    auto help = std::string((*argv_)[i]);
-    if ((help == "--help" || help == "-help" || help == "--h" ||
-         help == "-h") &&
-        tool != OSQUERY_TOOL_TEST) {
-      printUsage(binary_, tool_);
-      ::exit(0);
-    }
-  }
-
-// To change the default config plugin, compile osquery with
-// -DOSQUERY_DEFAULT_CONFIG_PLUGIN=<new_default_plugin>
-#ifdef OSQUERY_DEFAULT_CONFIG_PLUGIN
-  FLAGS_config_plugin = STR(OSQUERY_DEFAULT_CONFIG_PLUGIN);
-#endif
-
-// To change the default logger plugin, compile osquery with
-// -DOSQUERY_DEFAULT_LOGGER_PLUGIN=<new_default_plugin>
-#ifdef OSQUERY_DEFAULT_LOGGER_PLUGIN
-  FLAGS_logger_plugin = STR(OSQUERY_DEFAULT_LOGGER_PLUGIN);
-#endif
-
-  // Set version string from CMake build
-  GFLAGS_NAMESPACE::SetVersionString(kVersion.c_str());
-
-  // Let gflags parse the non-help options/flags.
-  GFLAGS_NAMESPACE::ParseCommandLineFlags(
-      argc_, argv_, (tool == OSQUERY_TOOL_SHELL));
-
-  // Set the tool type to allow runtime decisions based on daemon, shell, etc.
-  kToolType = tool;
-  if (tool == OSQUERY_TOOL_SHELL) {
-    // The shell is transient, rewrite config-loaded paths.
-    FLAGS_disable_logging = true;
-    // Get the caller's home dir for temporary storage/state management.
-    auto homedir = osqueryHomeDirectory();
-    if (osquery::pathExists(homedir).ok() ||
-        boost::filesystem::create_directory(homedir)) {
-      // Only apply user/shell-specific paths if not overridden by CLI flag.
-      if (Flag::isDefault("database_path")) {
-        osquery::FLAGS_database_path = homedir + "/shell.db";
-      }
-      if (Flag::isDefault("extensions_socket")) {
-        osquery::FLAGS_extensions_socket = homedir + "/shell.em";
-      }
-    }
-  }
-
-  // If the caller is checking configuration, disable the watchdog/worker.
-  if (FLAGS_config_check) {
-    FLAGS_disable_watchdog = true;
-  }
-
-  // Initialize the status and results logger.
-  initStatusLogger(binary_);
-  if (tool != OSQUERY_EXTENSION) {
-    if (isWorker()) {
-      VLOG(1) << "osquery worker initialized [watcher="
-              << getenv("OSQUERY_WORKER") << "]";
-    } else {
-      VLOG(1) << "osquery initialized [version=" << kVersion << "]";
-    }
-  } else {
-    VLOG(1) << "osquery extension initialized [sdk=" << kSDKVersion << "]";
-  }
-}
-
-void Initializer::initDaemon() {
-  if (FLAGS_config_check) {
-    // No need to daemonize, emit log lines, or create process mutexes.
-    return;
-  }
-
-#ifndef __APPLE__
-  // OS X uses launchd to daemonize.
-  if (osquery::FLAGS_daemonize) {
-    if (daemon(0, 0) == -1) {
-      ::exit(EXIT_FAILURE);
-    }
-  }
-#endif
-
-  // Print the version to SYSLOG.
-  syslog(
-      LOG_NOTICE, "%s started [version=%s]", binary_.c_str(), kVersion.c_str());
-
-  // Check if /var/osquery exists
-  if ((Flag::isDefault("pidfile") || Flag::isDefault("database_path")) &&
-      !isDirectory("/var/osquery")) {
-    std::cerr << CONFIG_ERROR
-  }
-
-  // Create a process mutex around the daemon.
-  auto pid_status = createPidFile();
-  if (!pid_status.ok()) {
-    LOG(ERROR) << binary_ << " initialize failed: " << pid_status.toString();
-    ::exit(EXIT_FAILURE);
-  }
-
-  // Nice ourselves if using a watchdog and the level is not too permissive.
-  if (!FLAGS_disable_watchdog &&
-      FLAGS_watchdog_level >= WATCHDOG_LEVEL_DEFAULT &&
-      FLAGS_watchdog_level != WATCHDOG_LEVEL_DEBUG) {
-    // Set CPU scheduling I/O limits.
-    setpriority(PRIO_PGRP, 0, 10);
-#ifdef __linux__
-    // Using: ioprio_set(IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
-    syscall(SYS_ioprio_set, IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
-#elif defined(__APPLE__) || defined(__FreeBSD__)
-    setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
-#endif
-  }
-}
-
-void Initializer::initWatcher() {
-  // The watcher takes a list of paths to autoload extensions from.
-  osquery::loadExtensions();
-
-  // Add a watcher service thread to start/watch an optional worker and set
-  // of optional extensions in the autoload paths.
-  if (Watcher::hasManagedExtensions() || !FLAGS_disable_watchdog) {
-    Dispatcher::addService(std::make_shared<WatcherRunner>(
-        *argc_, *argv_, !FLAGS_disable_watchdog));
-  }
-
-  // If there are no autoloaded extensions, the watcher service will end,
-  // otherwise it will continue as a background thread and respawn them.
-  // If the watcher is also a worker watchdog it will do nothing but monitor
-  // the extensions and worker process.
-  if (!FLAGS_disable_watchdog) {
-    Dispatcher::joinServices();
-    // Execution should never reach this point.
-    ::exit(EXIT_FAILURE);
-  }
-}
-
-void Initializer::initWorker(const std::string& name) {
-  // Clear worker's arguments.
-  size_t name_size = strlen((*argv_)[0]);
-  auto original_name = std::string((*argv_)[0]);
-  for (int i = 0; i < *argc_; i++) {
-    if ((*argv_)[i] != nullptr) {
-      memset((*argv_)[i], ' ', strlen((*argv_)[i]));
-    }
-  }
-
-  // Set the worker's process name.
-  if (name.size() < name_size) {
-    std::copy(name.begin(), name.end(), (*argv_)[0]);
-    (*argv_)[0][name.size()] = '\0';
-  } else {
-    std::copy(original_name.begin(), original_name.end(), (*argv_)[0]);
-    (*argv_)[0][original_name.size()] = '\0';
-  }
-
-  // Start a watcher watcher thread to exit the process if the watcher exits.
-  Dispatcher::addService(std::make_shared<WatcherWatcherRunner>(getppid()));
-}
-
-void Initializer::initWorkerWatcher(const std::string& name) {
-  if (isWorker()) {
-    initWorker(name);
-  } else {
-    // The watcher will forever monitor and spawn additional workers.
-    initWatcher();
-  }
-}
-
-bool Initializer::isWorker() { return (getenv("OSQUERY_WORKER") != nullptr); }
-
-void Initializer::initActivePlugin(const std::string& type,
-                                   const std::string& name) {
-  // Use a delay, meaning the amount of milliseconds waited for extensions.
-  size_t delay = 0;
-  // The timeout is the maximum microseconds in seconds to wait for extensions.
-  size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000;
-  if (timeout < kExtensionInitializeLatencyUS * 10) {
-    timeout = kExtensionInitializeLatencyUS * 10;
-  }
-  while (!Registry::setActive(type, name)) {
-    if (!Watcher::hasManagedExtensions() || delay > timeout) {
-      LOG(ERROR) << "Active " << type << " plugin not found: " << name;
-      ::exit(EXIT_CATASTROPHIC);
-    }
-    delay += kExtensionInitializeLatencyUS;
-    ::usleep(kExtensionInitializeLatencyUS);
-  }
-}
-
-void Initializer::start() {
-  // Load registry/extension modules before extensions.
-  osquery::loadModules();
-
-  // Pre-extension manager initialization options checking.
-  if (FLAGS_config_check && !Watcher::hasManagedExtensions()) {
-    FLAGS_disable_extensions = true;
-  }
-
-  // Check the backing store by allocating and exiting on error.
-  if (!DBHandle::checkDB()) {
-    LOG(ERROR) << binary_ << " initialize failed: Could not open RocksDB";
-    if (isWorker()) {
-      ::exit(EXIT_CATASTROPHIC);
-    } else {
-      ::exit(EXIT_FAILURE);
-    }
-  }
-
-  // Bind to an extensions socket and wait for registry additions.
-  osquery::startExtensionManager();
-
-  // Then set the config plugin, which uses a single/active plugin.
-  initActivePlugin("config", FLAGS_config_plugin);
-
-  // Run the setup for all lazy registries (tables, SQL).
-  Registry::setUp();
-
-  if (FLAGS_config_check) {
-    // The initiator requested an initialization and config check.
-    auto s = Config::checkConfig();
-    if (!s.ok()) {
-      std::cerr << "Error reading config: " << s.toString() << "\n";
-    }
-    // A configuration check exits the application.
-    ::exit(s.getCode());
-  }
-
-  // Load the osquery config using the default/active config plugin.
-  Config::load();
-
-  // Initialize the status and result plugin logger.
-  initActivePlugin("logger", FLAGS_logger_plugin);
-  initLogger(binary_);
-
-  // Start event threads.
-  osquery::attachEvents();
-  EventFactory::delay();
-}
-
-void Initializer::shutdown() {
-  // End any event type run loops.
-  EventFactory::end();
-
-  // Hopefully release memory used by global string constructors in gflags.
-  GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
-}
-}
diff --git a/osquery/core/system.cpp b/osquery/core/system.cpp
deleted file mode 100644 (file)
index 73da087..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <ctime>
-#include <sstream>
-
-#include <sys/types.h>
-#include <signal.h>
-
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/uuid/uuid.hpp>
-#include <boost/uuid/uuid_generators.hpp>
-#include <boost/uuid/uuid_io.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-/// The path to the pidfile for osqueryd
-CLI_FLAG(string,
-         pidfile,
-         "/var/osquery/osqueryd.pidfile",
-         "Path to the daemon pidfile mutex");
-
-/// Should the daemon force unload previously-running osqueryd daemons.
-CLI_FLAG(bool,
-         force,
-         false,
-         "Force osqueryd to kill previously-running daemons");
-
-std::string getHostname() {
-  char hostname[256] = {0}; // Linux max should be 64.
-  gethostname(hostname, sizeof(hostname) - 1);
-  std::string hostname_string = std::string(hostname);
-  boost::algorithm::trim(hostname_string);
-  return hostname_string;
-}
-
-std::string generateNewUuid() {
-  boost::uuids::uuid uuid = boost::uuids::random_generator()();
-  return boost::uuids::to_string(uuid);
-}
-
-std::string generateHostUuid() {
-#ifdef __APPLE__
-  // Use the hardware uuid available on OSX to identify this machine
-  uuid_t id;
-  // wait at most 5 seconds for gethostuuid to return
-  const timespec wait = {5, 0};
-  int result = gethostuuid(id, &wait);
-  if (result == 0) {
-    char out[128];
-    uuid_unparse(id, out);
-    std::string uuid_string = std::string(out);
-    boost::algorithm::trim(uuid_string);
-    return uuid_string;
-  } else {
-    // unable to get the hardware uuid, just return a new uuid
-    return generateNewUuid();
-  }
-#else
-  return generateNewUuid();
-#endif
-}
-
-std::string getAsciiTime() {
-  auto result = std::time(nullptr);
-  auto time_str = std::string(std::asctime(std::gmtime(&result)));
-  boost::algorithm::trim(time_str);
-  return time_str + " UTC";
-}
-
-int getUnixTime() {
-  auto result = std::time(nullptr);
-  return result;
-}
-
-Status checkStalePid(const std::string& content) {
-  int pid;
-  try {
-    pid = boost::lexical_cast<int>(content);
-  } catch (const boost::bad_lexical_cast& e) {
-    if (FLAGS_force) {
-      return Status(0, "Force loading and not parsing pidfile");
-    } else {
-      return Status(1, "Could not parse pidfile");
-    }
-  }
-
-  int status = kill(pid, 0);
-  if (status != ESRCH) {
-    // The pid is running, check if it is an osqueryd process by name.
-    std::stringstream query_text;
-    query_text << "SELECT name FROM processes WHERE pid = " << pid
-               << " AND name = 'osqueryd';";
-    auto q = SQL(query_text.str());
-    if (!q.ok()) {
-      return Status(1, "Error querying processes: " + q.getMessageString());
-    }
-
-    if (q.rows().size() > 0) {
-      // If the process really is osqueryd, return an "error" status.
-      if (FLAGS_force) {
-        // The caller may choose to abort the existing daemon with --force.
-        status = kill(pid, SIGQUIT);
-        ::sleep(1);
-
-        return Status(status, "Tried to force remove the existing osqueryd");
-      }
-
-      return Status(1, "osqueryd (" + content + ") is already running");
-    } else {
-      LOG(INFO) << "Found stale process for osqueryd (" << content
-                << ") removing pidfile";
-    }
-  }
-
-  return Status(0, "OK");
-}
-
-Status createPidFile() {
-  // check if pidfile exists
-  auto exists = pathExists(FLAGS_pidfile);
-  if (exists.ok()) {
-    // if it exists, check if that pid is running.
-    std::string content;
-    auto read_status = readFile(FLAGS_pidfile, content);
-    if (!read_status.ok()) {
-      return Status(1, "Could not read pidfile: " + read_status.toString());
-    }
-
-    auto stale_status = checkStalePid(content);
-    if (!stale_status.ok()) {
-      return stale_status;
-    }
-  }
-
-  // Now the pidfile is either the wrong pid or the pid is not running.
-  try {
-    boost::filesystem::remove(FLAGS_pidfile);
-  } catch (boost::filesystem::filesystem_error& e) {
-    // Unable to remove old pidfile.
-    LOG(WARNING) << "Unable to remove the osqueryd pidfile";
-  }
-
-  // If no pidfile exists or the existing pid was stale, write, log, and run.
-  auto pid = boost::lexical_cast<std::string>(getpid());
-  LOG(INFO) << "Writing osqueryd pid (" << pid << ") to " << FLAGS_pidfile;
-  auto status = writeTextFile(FLAGS_pidfile, pid, 0644);
-  return status;
-}
-}
diff --git a/osquery/core/tables.cpp b/osquery/core/tables.cpp
deleted file mode 100644 (file)
index d71fd22..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/property_tree/json_parser.hpp>
-
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-Status TablePlugin::addExternal(const std::string& name,
-                                const PluginResponse& response) {
-  // Attach the table.
-  if (response.size() == 0) {
-    // Invalid table route info.
-    return Status(1, "Invalid route info");
-  }
-
-  // Use the SQL registry to attach the name/definition.
-  return Registry::call("sql", "sql", {{"action", "attach"}, {"table", name}});
-}
-
-void TablePlugin::removeExternal(const std::string& name) {
-  // Detach the table name.
-  Registry::call("sql", "sql", {{"action", "detach"}, {"table", name}});
-}
-
-void TablePlugin::setRequestFromContext(const QueryContext& context,
-                                        PluginRequest& request) {
-  pt::ptree tree;
-  tree.put("limit", context.limit);
-
-  // The QueryContext contains a constraint map from column to type information
-  // and the list of operand/expression constraints applied to that column from
-  // the query given.
-  pt::ptree constraints;
-  for (const auto& constraint : context.constraints) {
-    pt::ptree child;
-    child.put("name", constraint.first);
-    constraint.second.serialize(child);
-    constraints.push_back(std::make_pair("", child));
-  }
-  tree.add_child("constraints", constraints);
-
-  // Write the property tree as a JSON string into the PluginRequest.
-  std::ostringstream output;
-  try {
-    pt::write_json(output, tree, false);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    // The content could not be represented as JSON.
-  }
-  request["context"] = output.str();
-}
-
-void TablePlugin::setResponseFromQueryData(const QueryData& data,
-                                           PluginResponse& response) {
-  response = std::move(data);
-}
-
-void TablePlugin::setContextFromRequest(const PluginRequest& request,
-                                        QueryContext& context) {
-  if (request.count("context") == 0) {
-    return;
-  }
-
-  // Read serialized context from PluginRequest.
-  pt::ptree tree;
-  try {
-    std::stringstream input;
-    input << request.at("context");
-    pt::read_json(input, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return;
-  }
-
-  // Set the context limit and deserialize each column constraint list.
-  context.limit = tree.get<int>("limit");
-  for (const auto& constraint : tree.get_child("constraints")) {
-    auto column_name = constraint.second.get<std::string>("name");
-    context.constraints[column_name].unserialize(constraint.second);
-  }
-}
-
-Status TablePlugin::call(const PluginRequest& request,
-                         PluginResponse& response) {
-  response.clear();
-  // TablePlugin API calling requires an action.
-  if (request.count("action") == 0) {
-    return Status(1, "Table plugins must include a request action");
-  }
-
-  if (request.at("action") == "generate") {
-    // "generate" runs the table implementation using a PluginRequest with
-    // optional serialized QueryContext and returns the QueryData results as
-    // the PluginRequest data.
-    QueryContext context;
-    if (request.count("context") > 0) {
-      setContextFromRequest(request, context);
-    }
-    setResponseFromQueryData(generate(context), response);
-  } else if (request.at("action") == "columns") {
-    // "columns" returns a PluginRequest filled with column information
-    // such as name and type.
-    const auto& column_list = columns();
-    for (const auto& column : column_list) {
-      response.push_back({{"name", column.first}, {"type", column.second}});
-    }
-  } else if (request.at("action") == "definition") {
-    response.push_back({{"definition", columnDefinition()}});
-  } else if (request.at("action") == "update") {
-    Row row = request;
-    row.erase("action");
-    return update(row);
-  } else {
-    return Status(1, "Unknown table plugin action: " + request.at("action"));
-  }
-
-  return Status(0, "OK");
-}
-
-std::string TablePlugin::columnDefinition() const {
-  return osquery::columnDefinition(columns());
-}
-
-PluginResponse TablePlugin::routeInfo() const {
-  // Route info consists of only the serialized column information.
-  PluginResponse response;
-  for (const auto& column : columns()) {
-    response.push_back({{"name", column.first}, {"type", column.second}});
-  }
-  return response;
-}
-
-std::string columnDefinition(const TableColumns& columns) {
-  std::string statement = "(";
-  for (size_t i = 0; i < columns.size(); ++i) {
-    statement += columns.at(i).first + " " + columns.at(i).second;
-    if (i < columns.size() - 1) {
-      statement += ", ";
-    }
-  }
-  return statement += ")";
-}
-
-std::string columnDefinition(const PluginResponse& response) {
-  TableColumns columns;
-  for (const auto& column : response) {
-    columns.push_back(make_pair(column.at("name"), column.at("type")));
-  }
-  return columnDefinition(columns);
-}
-
-bool ConstraintList::matches(const std::string& expr) const {
-  // Support each SQL affinity type casting.
-  if (affinity == "TEXT") {
-    return literal_matches<TEXT_LITERAL>(expr);
-  } else if (affinity == "INTEGER") {
-    INTEGER_LITERAL lexpr = AS_LITERAL(INTEGER_LITERAL, expr);
-    return literal_matches<INTEGER_LITERAL>(lexpr);
-  } else if (affinity == "BIGINT") {
-    BIGINT_LITERAL lexpr = AS_LITERAL(BIGINT_LITERAL, expr);
-    return literal_matches<BIGINT_LITERAL>(lexpr);
-  } else if (affinity == "UNSIGNED_BIGINT") {
-    UNSIGNED_BIGINT_LITERAL lexpr = AS_LITERAL(UNSIGNED_BIGINT_LITERAL, expr);
-    return literal_matches<UNSIGNED_BIGINT_LITERAL>(lexpr);
-  } else {
-    // Unsupported affinity type.
-    return false;
-  }
-}
-
-template <typename T>
-bool ConstraintList::literal_matches(const T& base_expr) const {
-  bool aggregate = true;
-  for (size_t i = 0; i < constraints_.size(); ++i) {
-    T constraint_expr = AS_LITERAL(T, constraints_[i].expr);
-    if (constraints_[i].op == EQUALS) {
-      aggregate = aggregate && (base_expr == constraint_expr);
-    } else if (constraints_[i].op == GREATER_THAN) {
-      aggregate = aggregate && (base_expr > constraint_expr);
-    } else if (constraints_[i].op == LESS_THAN) {
-      aggregate = aggregate && (base_expr < constraint_expr);
-    } else if (constraints_[i].op == GREATER_THAN_OR_EQUALS) {
-      aggregate = aggregate && (base_expr >= constraint_expr);
-    } else if (constraints_[i].op == LESS_THAN_OR_EQUALS) {
-      aggregate = aggregate && (base_expr <= constraint_expr);
-    } else {
-      // Unsupported constraint.
-      return false;
-    }
-    if (!aggregate) {
-      // Speed up comparison.
-      return false;
-    }
-  }
-  return true;
-}
-
-std::set<std::string> ConstraintList::getAll(ConstraintOperator op) const {
-  std::set<std::string> set;
-  for (size_t i = 0; i < constraints_.size(); ++i) {
-    if (constraints_[i].op == op) {
-      // TODO: this does not apply a distinct.
-      set.insert(constraints_[i].expr);
-    }
-  }
-  return set;
-}
-
-void ConstraintList::serialize(boost::property_tree::ptree& tree) const {
-  boost::property_tree::ptree expressions;
-  for (const auto& constraint : constraints_) {
-    boost::property_tree::ptree child;
-    child.put("op", constraint.op);
-    child.put("expr", constraint.expr);
-    expressions.push_back(std::make_pair("", child));
-  }
-  tree.add_child("list", expressions);
-  tree.put("affinity", affinity);
-}
-
-void ConstraintList::unserialize(const boost::property_tree::ptree& tree) {
-  // Iterate through the list of operand/expressions, then set the constraint
-  // type affinity.
-  for (const auto& list : tree.get_child("list")) {
-    Constraint constraint(list.second.get<unsigned char>("op"));
-    constraint.expr = list.second.get<std::string>("expr");
-    constraints_.push_back(constraint);
-  }
-  affinity = tree.get<std::string>("affinity");
-}
-}
diff --git a/osquery/core/test_util.cpp b/osquery/core/test_util.cpp
deleted file mode 100644 (file)
index 2ed7db2..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <deque>
-#include <sstream>
-
-#include <boost/property_tree/json_parser.hpp>
-#include <boost/filesystem/operations.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-#include "osquery/core/test_util.h"
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-/// Most tests will use binary or disk-backed content for parsing tests.
-std::string kTestDataPath = "../../tools/tests/";
-
-QueryData getTestDBExpectedResults() {
-  QueryData d;
-  Row row1;
-  row1["username"] = "mike";
-  row1["age"] = "23";
-  d.push_back(row1);
-  Row row2;
-  row2["username"] = "matt";
-  row2["age"] = "24";
-  d.push_back(row2);
-  return d;
-}
-
-std::vector<std::pair<std::string, QueryData> > getTestDBResultStream() {
-  std::vector<std::pair<std::string, QueryData> > results;
-
-  std::string q2 =
-      "INSERT INTO test_table (username, age) VALUES (\"joe\", 25)";
-  QueryData d2;
-  Row row2_1;
-  row2_1["username"] = "mike";
-  row2_1["age"] = "23";
-  d2.push_back(row2_1);
-  Row row2_2;
-  row2_2["username"] = "matt";
-  row2_2["age"] = "24";
-  d2.push_back(row2_2);
-  Row row2_3;
-  row2_3["username"] = "joe";
-  row2_3["age"] = "25";
-  d2.push_back(row2_3);
-  results.push_back(std::make_pair(q2, d2));
-
-  std::string q3 = "UPDATE test_table SET age = 27 WHERE username = \"matt\"";
-  QueryData d3;
-  Row row3_1;
-  row3_1["username"] = "mike";
-  row3_1["age"] = "23";
-  d3.push_back(row3_1);
-  Row row3_2;
-  row3_2["username"] = "matt";
-  row3_2["age"] = "27";
-  d3.push_back(row3_2);
-  Row row3_3;
-  row3_3["username"] = "joe";
-  row3_3["age"] = "25";
-  d3.push_back(row3_3);
-  results.push_back(std::make_pair(q3, d3));
-
-  std::string q4 =
-      "DELETE FROM test_table WHERE username = \"matt\" AND age = 27";
-  QueryData d4;
-  Row row4_1;
-  row4_1["username"] = "mike";
-  row4_1["age"] = "23";
-  d4.push_back(row4_1);
-  Row row4_2;
-  row4_2["username"] = "joe";
-  row4_2["age"] = "25";
-  d4.push_back(row4_2);
-  results.push_back(std::make_pair(q4, d4));
-
-  return results;
-}
-
-ScheduledQuery getOsqueryScheduledQuery() {
-  ScheduledQuery sq;
-  sq.query = "SELECT filename FROM fs WHERE path = '/bin' ORDER BY filename";
-  sq.interval = 5;
-  return sq;
-}
-
-std::pair<pt::ptree, Row> getSerializedRow() {
-  Row r;
-  r["foo"] = "bar";
-  r["meaning_of_life"] = "42";
-  pt::ptree arr;
-  arr.put<std::string>("foo", "bar");
-  arr.put<std::string>("meaning_of_life", "42");
-  return std::make_pair(arr, r);
-}
-
-std::pair<pt::ptree, QueryData> getSerializedQueryData() {
-  auto r = getSerializedRow();
-  QueryData q = {r.second, r.second};
-  pt::ptree arr;
-  arr.push_back(std::make_pair("", r.first));
-  arr.push_back(std::make_pair("", r.first));
-  return std::make_pair(arr, q);
-}
-
-std::pair<pt::ptree, DiffResults> getSerializedDiffResults() {
-  auto qd = getSerializedQueryData();
-  DiffResults diff_results;
-  diff_results.added = qd.second;
-  diff_results.removed = qd.second;
-
-  pt::ptree root;
-  root.add_child("added", qd.first);
-  root.add_child("removed", qd.first);
-
-  return std::make_pair(root, diff_results);
-}
-
-std::pair<std::string, DiffResults> getSerializedDiffResultsJSON() {
-  auto results = getSerializedDiffResults();
-  std::ostringstream ss;
-  pt::write_json(ss, results.first, false);
-  return std::make_pair(ss.str(), results.second);
-}
-
-std::pair<std::string, QueryData> getSerializedQueryDataJSON() {
-  auto results = getSerializedQueryData();
-  std::ostringstream ss;
-  pt::write_json(ss, results.first, false);
-  return std::make_pair(ss.str(), results.second);
-}
-
-std::pair<pt::ptree, QueryLogItem> getSerializedQueryLogItem() {
-  QueryLogItem i;
-  pt::ptree root;
-  auto dr = getSerializedDiffResults();
-  i.results = dr.second;
-  i.name = "foobar";
-  i.calendar_time = "Mon Aug 25 12:10:57 2014";
-  i.time = 1408993857;
-  i.identifier = "foobaz";
-  root.add_child("diffResults", dr.first);
-  root.put<std::string>("name", "foobar");
-  root.put<std::string>("hostIdentifier", "foobaz");
-  root.put<std::string>("calendarTime", "Mon Aug 25 12:10:57 2014");
-  root.put<int>("unixTime", 1408993857);
-  return std::make_pair(root, i);
-}
-
-std::pair<std::string, QueryLogItem> getSerializedQueryLogItemJSON() {
-  auto results = getSerializedQueryLogItem();
-
-  std::ostringstream ss;
-  pt::write_json(ss, results.first, false);
-
-  return std::make_pair(ss.str(), results.second);
-}
-
-std::vector<SplitStringTestData> generateSplitStringTestData() {
-  SplitStringTestData s1;
-  s1.test_string = "a b\tc";
-  s1.test_vector = {"a", "b", "c"};
-
-  SplitStringTestData s2;
-  s2.test_string = " a b   c";
-  s2.test_vector = {"a", "b", "c"};
-
-  SplitStringTestData s3;
-  s3.test_string = "  a     b   c";
-  s3.test_vector = {"a", "b", "c"};
-
-  return {s1, s2, s3};
-}
-
-std::string getCACertificateContent() {
-  std::string content;
-  readFile(kTestDataPath + "test_cert.pem", content);
-  return content;
-}
-
-std::string getEtcHostsContent() {
-  std::string content;
-  readFile(kTestDataPath + "test_hosts.txt", content);
-  return content;
-}
-
-std::string getEtcProtocolsContent() {
-  std::string content;
-  readFile(kTestDataPath + "test_protocols.txt", content);
-  return content;
-}
-
-QueryData getEtcHostsExpectedResults() {
-  Row row1;
-  Row row2;
-  Row row3;
-  Row row4;
-  Row row5;
-  Row row6;
-
-  row1["address"] = "127.0.0.1";
-  row1["hostnames"] = "localhost";
-  row2["address"] = "255.255.255.255";
-  row2["hostnames"] = "broadcasthost";
-  row3["address"] = "::1";
-  row3["hostnames"] = "localhost";
-  row4["address"] = "fe80::1%lo0";
-  row4["hostnames"] = "localhost";
-  row5["address"] = "127.0.0.1";
-  row5["hostnames"] = "example.com example";
-  row6["address"] = "127.0.0.1";
-  row6["hostnames"] = "example.net";
-  return {row1, row2, row3, row4, row5, row6};
-}
-
-::std::ostream& operator<<(::std::ostream& os, const Status& s) {
-  return os << "Status(" << s.getCode() << ", \"" << s.getMessage() << "\")";
-}
-
-QueryData getEtcProtocolsExpectedResults() {
-  Row row1;
-  Row row2;
-  Row row3;
-
-  row1["name"] = "ip";
-  row1["number"] = "0";
-  row1["alias"] = "IP";
-  row1["comment"] = "internet protocol, pseudo protocol number";
-  row2["name"] = "icmp";
-  row2["number"] = "1";
-  row2["alias"] = "ICMP";
-  row2["comment"] = "internet control message protocol";
-  row3["name"] = "tcp";
-  row3["number"] = "6";
-  row3["alias"] = "TCP";
-  row3["comment"] = "transmission control protocol";
-
-  return {row1, row2, row3};
-}
-
-void createMockFileStructure() {
-  fs::create_directories(kFakeDirectory + "/deep11/deep2/deep3/");
-  fs::create_directories(kFakeDirectory + "/deep1/deep2/");
-  writeTextFile(kFakeDirectory + "/root.txt", "root");
-  writeTextFile(kFakeDirectory + "/door.txt", "toor");
-  writeTextFile(kFakeDirectory + "/roto.txt", "roto");
-  writeTextFile(kFakeDirectory + "/deep1/level1.txt", "l1");
-  writeTextFile(kFakeDirectory + "/deep11/not_bash", "l1");
-  writeTextFile(kFakeDirectory + "/deep1/deep2/level2.txt", "l2");
-
-  writeTextFile(kFakeDirectory + "/deep11/level1.txt", "l1");
-  writeTextFile(kFakeDirectory + "/deep11/deep2/level2.txt", "l2");
-  writeTextFile(kFakeDirectory + "/deep11/deep2/deep3/level3.txt", "l3");
-
-  boost::system::error_code ec;
-  fs::create_symlink(
-      kFakeDirectory + "/root.txt", kFakeDirectory + "/root2.txt", ec);
-}
-
-void tearDownMockFileStructure() {
-  boost::filesystem::remove_all(kFakeDirectory);
-}
-}
diff --git a/osquery/core/test_util.h b/osquery/core/test_util.h
deleted file mode 100644 (file)
index 40a97a7..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/config.h>
-#include <osquery/core.h>
-#include <osquery/database.h>
-#include <osquery/filesystem.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-/// Any SQL-dependent tests should use kTestQuery for a pre-populated example.
-const std::string kTestQuery = "SELECT * FROM test_table";
-
-extern std::string kTestDataPath;
-
-/// Tests should limit intermediate input/output to a working directory.
-/// Config data, logging results, and intermediate database/caching usage.
-const std::string kTestWorkingDirectory = "/tmp/osquery-tests/";
-
-/// A fake directory tree should be used for filesystem iterator testing.
-const std::string kFakeDirectory = kTestWorkingDirectory + "fstree";
-
-ScheduledQuery getOsqueryScheduledQuery();
-
-// getTestDBExpectedResults returns the results of kTestQuery of the table that
-// initially gets returned from createTestDB()
-QueryData getTestDBExpectedResults();
-
-// Starting with the dataset returned by createTestDB(), getTestDBResultStream
-// returns a vector of std::pair's where pair.first is the query that would
-// need to be performed on the dataset to make the results be pair.second
-std::vector<std::pair<std::string, QueryData> > getTestDBResultStream();
-
-// getSerializedRow() return an std::pair where pair->first is a string which
-// should serialize to pair->second. pair->second should deserialize
-// to pair->first
-std::pair<pt::ptree, Row> getSerializedRow();
-
-// getSerializedQueryData() return an std::pair where pair->first is a string
-// which should serialize to pair->second. pair->second should
-// deserialize to pair->first
-std::pair<pt::ptree, QueryData> getSerializedQueryData();
-std::pair<std::string, QueryData> getSerializedQueryDataJSON();
-
-// getSerializedDiffResults() return an std::pair where pair->first is a string
-// which should serialize to pair->second. pair->second should
-// deserialize to pair->first
-std::pair<pt::ptree, DiffResults> getSerializedDiffResults();
-std::pair<std::string, DiffResults> getSerializedDiffResultsJSON();
-
-// getSerializedQueryLogItem() return an std::pair where pair->first
-// is a string which should serialize to pair->second. pair->second
-// should deserialize to pair->first
-std::pair<pt::ptree, QueryLogItem> getSerializedQueryLogItem();
-std::pair<std::string, QueryLogItem> getSerializedQueryLogItemJSON();
-
-// generate content for a PEM-encoded certificate
-std::string getCACertificateContent();
-
-// generate the content that would be found in an /etc/hosts file
-std::string getEtcHostsContent();
-
-// generate the content that would be found in an /etc/protocols file
-std::string getEtcProtocolsContent();
-
-// generate the expected data that getEtcHostsContent() should parse into
-QueryData getEtcHostsExpectedResults();
-
-// generate the expected data that getEtcProtocolsContent() should parse into
-QueryData getEtcProtocolsExpectedResults();
-
-// the three items that you need to test osquery::splitString
-struct SplitStringTestData {
-  std::string test_string;
-  std::string delim;
-  std::vector<std::string> test_vector;
-};
-
-// generate a set of test data to test osquery::splitString
-std::vector<SplitStringTestData> generateSplitStringTestData();
-
-// generate a small directory structure for testing
-void createMockFileStructure();
-// remove the small directory structure used for testing
-void tearDownMockFileStructure();
-}
diff --git a/osquery/core/tests/conversions_tests.cpp b/osquery/core/tests/conversions_tests.cpp
deleted file mode 100644 (file)
index 34be661..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/make_shared.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <gtest/gtest.h>
-
-#include "osquery/core/conversions.h"
-
-namespace osquery {
-
-class ConversionsTests : public testing::Test {};
-
-class Foobar {};
-
-TEST_F(ConversionsTests, test_conversion) {
-  boost::shared_ptr<Foobar> b1 = boost::make_shared<Foobar>();
-  std::shared_ptr<Foobar> s1 = boost_to_std_shared_ptr(b1);
-  EXPECT_EQ(s1.get(), b1.get());
-
-  std::shared_ptr<Foobar> s2 = std::make_shared<Foobar>();
-  boost::shared_ptr<Foobar> b2 = std_to_boost_shared_ptr(s2);
-  EXPECT_EQ(s2.get(), b2.get());
-}
-
-TEST_F(ConversionsTests, test_base64) {
-  std::string unencoded = "HELLO";
-  auto encoded = base64Encode(unencoded);
-  EXPECT_NE(encoded.size(), 0);
-
-  auto unencoded2 = base64Decode(encoded);
-  EXPECT_EQ(unencoded, unencoded2);
-}
-}
diff --git a/osquery/core/tests/flags_tests.cpp b/osquery/core/tests/flags_tests.cpp
deleted file mode 100644 (file)
index 1f5343f..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-
-DECLARE_string(test_string_flag);
-
-class FlagsTests : public testing::Test {
- public:
-  FlagsTests() {}
-
-  void SetUp() {}
-};
-
-FLAG(string, test_string_flag, "TEST STRING", "TEST DESCRIPTION");
-
-TEST_F(FlagsTests, test_set_get) {
-  // Test the core gflags functionality.
-  EXPECT_EQ(FLAGS_test_string_flag, "TEST STRING");
-
-  // Check that the gflags flag name was recorded in the osquery flag tracker.
-  auto all_flags = Flag::flags();
-  EXPECT_EQ(all_flags.count("test_string_flag"), 1);
-
-  // Update the value of the flag, and access through the osquery wrapper.
-  FLAGS_test_string_flag = "NEW TEST STRING";
-  EXPECT_EQ(Flag::getValue("test_string_flag"), "NEW TEST STRING");
-}
-
-TEST_F(FlagsTests, test_defaults) {
-  // Make sure the flag value was not reset.
-  EXPECT_EQ(FLAGS_test_string_flag, "NEW TEST STRING");
-
-  // Now test that the default value is tracked.
-  EXPECT_FALSE(Flag::isDefault("test_string_flag"));
-
-  // Check the default value accessor.
-  std::string default_value;
-  auto status = Flag::getDefaultValue("test_mistake", default_value);
-  EXPECT_FALSE(status.ok());
-  status = Flag::getDefaultValue("test_string_flag", default_value);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(default_value, "TEST STRING");
-}
-
-TEST_F(FlagsTests, test_details) {
-  // Make sure flag details are tracked correctly.
-  auto all_flags = Flag::flags();
-  auto flag_info = all_flags["test_string_flag"];
-
-  EXPECT_EQ(flag_info.type, "string");
-  EXPECT_EQ(flag_info.description, "TEST DESCRIPTION");
-  EXPECT_EQ(flag_info.default_value, "TEST STRING");
-  EXPECT_EQ(flag_info.value, "NEW TEST STRING");
-  EXPECT_EQ(flag_info.detail.shell, false);
-  EXPECT_EQ(flag_info.detail.external, false);
-}
-
-SHELL_FLAG(bool, shell_only, true, "TEST SHELL DESCRIPTION");
-EXTENSION_FLAG(bool, extension_only, true, "TEST EXTENSION DESCRIPTION");
-
-TEST_F(FlagsTests, test_flag_detail_types) {
-  EXPECT_TRUE(FLAGS_shell_only);
-  EXPECT_TRUE(FLAGS_extension_only);
-
-  auto all_flags = Flag::flags();
-  EXPECT_TRUE(all_flags["shell_only"].detail.shell);
-  EXPECT_TRUE(all_flags["extension_only"].detail.external);
-}
-
-FLAG_ALIAS(bool, shell_only_alias, shell_only);
-
-TEST_F(FlagsTests, test_aliases) {
-  EXPECT_TRUE(FLAGS_shell_only_alias);
-  FLAGS_shell_only = false;
-  EXPECT_FALSE(FLAGS_shell_only);
-  EXPECT_FALSE(FLAGS_shell_only_alias);
-}
-
-FLAG(int32, test_int32, 1, "none");
-FLAG_ALIAS(google::int32, test_int32_alias, test_int32);
-
-FLAG(int64, test_int64, (int64_t)1 << 34, "none");
-FLAG_ALIAS(google::int64, test_int64_alias, test_int64);
-
-FLAG(double, test_double, 4.2, "none");
-FLAG_ALIAS(double, test_double_alias, test_double);
-
-FLAG(string, test_string, "test", "none");
-FLAG_ALIAS(std::string, test_string_alias, test_string);
-
-TEST_F(FlagsTests, test_alias_types) {
-  // Test int32 lexical casting both ways.
-  EXPECT_EQ(FLAGS_test_int32_alias, 1);
-  FLAGS_test_int32_alias = 2;
-  EXPECT_EQ(FLAGS_test_int32, 2);
-  FLAGS_test_int32 = 3;
-  EXPECT_EQ(FLAGS_test_int32_alias, 3);
-  EXPECT_TRUE(FLAGS_test_int32_alias > 0);
-
-  EXPECT_EQ(FLAGS_test_int64_alias, (int64_t)1 << 34);
-  FLAGS_test_int64_alias = (int64_t)1 << 35;
-  EXPECT_EQ(FLAGS_test_int64, (int64_t)1 << 35);
-  FLAGS_test_int64 = (int64_t)1 << 36;
-  EXPECT_EQ(FLAGS_test_int64_alias, (int64_t)1 << 36);
-  EXPECT_TRUE(FLAGS_test_int64_alias > 0);
-
-  EXPECT_EQ(FLAGS_test_double_alias, 4.2);
-  FLAGS_test_double_alias = 2.4;
-  EXPECT_EQ(FLAGS_test_double, 2.4);
-  FLAGS_test_double = 22.44;
-  EXPECT_EQ(FLAGS_test_double_alias, 22.44);
-  EXPECT_TRUE(FLAGS_test_double_alias > 0);
-
-  // Compile-time type checking will not compare typename T to const char*
-  std::string value = FLAGS_test_string_alias;
-  EXPECT_EQ(value, "test");
-  FLAGS_test_string_alias = "test2";
-  EXPECT_EQ(FLAGS_test_string, "test2");
-  FLAGS_test_string = "test3";
-
-  // Test both the copy and assignment constructor aliases.
-  value = FLAGS_test_string_alias;
-  auto value2 = (std::string)FLAGS_test_string_alias;
-  EXPECT_EQ(value, "test3");
-}
-}
diff --git a/osquery/core/tests/hash_tests.cpp b/osquery/core/tests/hash_tests.cpp
deleted file mode 100644 (file)
index b1468b1..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/hash.h>
-
-#include "osquery/core/test_util.h"
-
-namespace osquery {
-
-class HashTests : public testing::Test {};
-
-TEST_F(HashTests, test_algorithms) {
-  const unsigned char buffer[1] = {'0'};
-
-  auto digest = hashFromBuffer(HASH_TYPE_MD5, buffer, 1);
-  EXPECT_EQ(digest, "cfcd208495d565ef66e7dff9f98764da");
-
-  digest = hashFromBuffer(HASH_TYPE_SHA1, buffer, 1);
-  EXPECT_EQ(digest, "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c");
-
-  digest = hashFromBuffer(HASH_TYPE_SHA256, buffer, 1);
-  EXPECT_EQ(digest,
-            "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9");
-}
-
-TEST_F(HashTests, test_update) {
-  const unsigned char buffer[1] = {'0'};
-
-  Hash hash(HASH_TYPE_MD5);
-  hash.update(buffer, 1);
-  hash.update(buffer, 1);
-  auto digest = hash.digest();
-  EXPECT_EQ(digest, "b4b147bc522828731f1a016bfa72c073");
-}
-
-TEST_F(HashTests, test_file_hashing) {
-  auto digest = hashFromFile(HASH_TYPE_MD5, kTestDataPath + "test_hashing.bin");
-  EXPECT_EQ(digest, "88ee11f2aa7903f34b8b8785d92208b1");
-}
-}
diff --git a/osquery/core/tests/status_tests.cpp b/osquery/core/tests/status_tests.cpp
deleted file mode 100644 (file)
index 449a981..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/status.h>
-
-#include <gtest/gtest.h>
-
-namespace osquery {
-
-class StatusTests : public testing::Test {};
-
-TEST_F(StatusTests, test_constructor) {
-  auto s = Status(5, "message");
-  EXPECT_EQ(s.getCode(), 5);
-  EXPECT_EQ(s.getMessage(), "message");
-}
-
-TEST_F(StatusTests, test_constructor_2) {
-  Status s;
-  EXPECT_EQ(s.getCode(), 0);
-  EXPECT_EQ(s.getMessage(), "OK");
-}
-
-TEST_F(StatusTests, test_ok) {
-  auto s1 = Status(5, "message");
-  EXPECT_FALSE(s1.ok());
-  auto s2 = Status(0, "message");
-  EXPECT_TRUE(s2.ok());
-}
-
-TEST_F(StatusTests, test_to_string) {
-  auto s = Status(0, "foobar");
-  EXPECT_EQ(s.toString(), "foobar");
-}
-}
diff --git a/osquery/core/tests/tables_tests.cpp b/osquery/core/tests/tables_tests.cpp
deleted file mode 100644 (file)
index 524dbc2..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/tables.h>
-
-namespace osquery {
-
-class TablesTests : public testing::Test {};
-
-TEST_F(TablesTests, test_constraint) {
-  auto constraint = Constraint(EQUALS);
-  constraint.expr = "none";
-
-  EXPECT_EQ(constraint.op, EQUALS);
-  EXPECT_EQ(constraint.expr, "none");
-}
-
-TEST_F(TablesTests, test_constraint_list) {
-  struct ConstraintList cl;
-
-  auto constraint = Constraint(EQUALS);
-  constraint.expr = "some";
-
-  // The constraint list is a simple struct.
-  cl.add(constraint);
-  EXPECT_EQ(cl.constraints_.size(), 1);
-
-  constraint = Constraint(EQUALS);
-  constraint.expr = "some_other";
-  cl.add(constraint);
-
-  constraint = Constraint(GREATER_THAN);
-  constraint.expr = "more_than";
-  cl.add(constraint);
-  EXPECT_EQ(cl.constraints_.size(), 3);
-
-  auto all_equals = cl.getAll(EQUALS);
-  EXPECT_EQ(all_equals.size(), 2);
-}
-
-TEST_F(TablesTests, test_constraint_matching) {
-  struct ConstraintList cl;
-  // An empty constraint list has expectations.
-  EXPECT_FALSE(cl.exists());
-  EXPECT_FALSE(cl.exists(GREATER_THAN));
-  EXPECT_TRUE(cl.notExistsOrMatches("some"));
-
-  auto constraint = Constraint(EQUALS);
-  constraint.expr = "some";
-  cl.add(constraint);
-
-  // Test existence checks based on flags.
-  EXPECT_TRUE(cl.exists());
-  EXPECT_TRUE(cl.exists(EQUALS));
-  EXPECT_TRUE(cl.exists(EQUALS | LESS_THAN));
-  EXPECT_FALSE(cl.exists(LESS_THAN));
-
-  EXPECT_TRUE(cl.notExistsOrMatches("some"));
-  EXPECT_TRUE(cl.matches("some"));
-  EXPECT_FALSE(cl.notExistsOrMatches("not_some"));
-
-  struct ConstraintList cl2;
-  cl2.affinity = "INTEGER";
-  constraint = Constraint(LESS_THAN);
-  constraint.expr = "1000";
-  cl2.add(constraint);
-  constraint = Constraint(GREATER_THAN);
-  constraint.expr = "1";
-  cl2.add(constraint);
-
-  // Test both SQL-provided string types.
-  EXPECT_TRUE(cl2.matches("10"));
-  // ...and the type literal.
-  EXPECT_TRUE(cl2.matches(10));
-
-  // Test operator lower bounds.
-  EXPECT_FALSE(cl2.matches(0));
-  EXPECT_FALSE(cl2.matches(1));
-
-  // Test operator upper bounds.
-  EXPECT_FALSE(cl2.matches(1000));
-  EXPECT_FALSE(cl2.matches(1001));
-
-  // Now test inclusive bounds.
-  struct ConstraintList cl3;
-  constraint = Constraint(LESS_THAN_OR_EQUALS);
-  constraint.expr = "1000";
-  cl3.add(constraint);
-  constraint = Constraint(GREATER_THAN_OR_EQUALS);
-  constraint.expr = "1";
-  cl3.add(constraint);
-
-  EXPECT_FALSE(cl3.matches(1001));
-  EXPECT_TRUE(cl3.matches(1000));
-
-  EXPECT_FALSE(cl3.matches(0));
-  EXPECT_TRUE(cl3.matches(1));
-}
-
-TEST_F(TablesTests, test_constraint_map) {
-  ConstraintMap cm;
-  ConstraintList cl;
-
-  cl.add(Constraint(EQUALS, "some"));
-  cm["path"] = cl;
-
-  // If a constraint list exists for a map key, normal constraints apply.
-  EXPECT_TRUE(cm["path"].matches("some"));
-  EXPECT_FALSE(cm["path"].matches("not_some"));
-
-  // If a constraint list does not exist, then all checks will match.
-  // If there is no predicate clause then all results will match.
-  EXPECT_TRUE(cm["not_path"].matches("some"));
-  EXPECT_TRUE(cm["not_path"].notExistsOrMatches("some"));
-  EXPECT_FALSE(cm["not_path"].exists());
-  EXPECT_FALSE(cm["not_path"].existsAndMatches("some"));
-
-  // And of the column has constraints:
-  EXPECT_TRUE(cm["path"].notExistsOrMatches("some"));
-  EXPECT_FALSE(cm["path"].notExistsOrMatches("not_some"));
-  EXPECT_TRUE(cm["path"].exists());
-  EXPECT_TRUE(cm["path"].existsAndMatches("some"));
-}
-}
diff --git a/osquery/core/tests/text_tests.cpp b/osquery/core/tests/text_tests.cpp
deleted file mode 100644 (file)
index fbc8a2a..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-
-#include "osquery/core/test_util.h"
-
-namespace osquery {
-
-class TextTests : public testing::Test {};
-
-TEST_F(TextTests, test_split) {
-  for (const auto& i : generateSplitStringTestData()) {
-    EXPECT_EQ(split(i.test_string), i.test_vector);
-  }
-}
-
-TEST_F(TextTests, test_join) {
-  std::vector<std::string> content = {
-      "one", "two", "three",
-  };
-  EXPECT_EQ(join(content, ", "), "one, two, three");
-}
-
-TEST_F(TextTests, test_split_occurences) {
-  std::string content = "T: 'S:S'";
-  std::vector<std::string> expected = {
-      "T", "'S:S'",
-  };
-  EXPECT_EQ(split(content, ":", 1), expected);
-}
-}
diff --git a/osquery/core/text.cpp b/osquery/core/text.cpp
deleted file mode 100644 (file)
index 3ed8962..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-
-#include <osquery/core.h>
-
-#include <boost/algorithm/string/join.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/trim.hpp>
-
-namespace osquery {
-
-std::vector<std::string> split(const std::string& s, const std::string& delim) {
-  std::vector<std::string> elems;
-  boost::split(elems, s, boost::is_any_of(delim));
-  auto start =
-      std::remove_if(elems.begin(), elems.end(), [](const std::string& s) {
-        return s.size() == 0;
-      });
-  elems.erase(start, elems.end());
-  for (auto& each : elems) {
-    boost::algorithm::trim(each);
-  }
-  return elems;
-}
-
-std::vector<std::string> split(const std::string& s,
-                               const std::string& delim,
-                               size_t occurences) {
-  // Split the string normally with the required delimiter.
-  auto content = split(s, delim);
-  // While the result split exceeds the number of requested occurrences, join.
-  std::vector<std::string> accumulator;
-  std::vector<std::string> elems;
-  for (size_t i = 0; i < content.size(); i++) {
-    if (i < occurences) {
-      elems.push_back(content.at(i));
-    } else {
-      accumulator.push_back(content.at(i));
-    }
-  }
-  // Join the optional accumulator.
-  if (accumulator.size() > 0) {
-    elems.push_back(join(accumulator, delim));
-  }
-  return elems;
-}
-
-std::string join(const std::vector<std::string>& s, const std::string& tok) {
-  return boost::algorithm::join(s, tok);
-}
-}
diff --git a/osquery/core/watcher.cpp b/osquery/core/watcher.cpp
deleted file mode 100644 (file)
index fba34f2..0000000
+++ /dev/null
@@ -1,445 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <cstring>
-
-#include <math.h>
-#include <sys/wait.h>
-#include <signal.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/events.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-
-#include "osquery/core/watcher.h"
-#include "osquery/dispatcher/dispatcher.h"
-
-extern char** environ;
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-const std::map<WatchdogLimitType, std::vector<size_t> > kWatchdogLimits = {
-    // Maximum MB worker can privately allocate.
-    {MEMORY_LIMIT, {80, 50, 30, 1000}},
-    // Percent of user or system CPU worker can utilize for LATENCY_LIMIT
-    // seconds.
-    {UTILIZATION_LIMIT, {90, 80, 60, 1000}},
-    // Number of seconds the worker should run, else consider the exit fatal.
-    {RESPAWN_LIMIT, {20, 20, 20, 5}},
-    // If the worker respawns too quickly, backoff on creating additional.
-    {RESPAWN_DELAY, {5, 5, 5, 1}},
-    // Seconds of tolerable UTILIZATION_LIMIT sustained latency.
-    {LATENCY_LIMIT, {12, 6, 3, 1}},
-    // How often to poll for performance limit violations.
-    {INTERVAL, {3, 3, 3, 1}},
-};
-
-const std::string kExtensionExtension = ".ext";
-
-CLI_FLAG(int32,
-         watchdog_level,
-         1,
-         "Performance limit level (0=loose, 1=normal, 2=restrictive, 3=debug)");
-
-CLI_FLAG(bool, disable_watchdog, false, "Disable userland watchdog process");
-
-/// If the worker exits the watcher will inspect the return code.
-void childHandler(int signum) {
-  siginfo_t info;
-  // Make sure WNOWAIT is used to the wait information is not removed.
-  // Watcher::watch implements a thread to poll for this information.
-  waitid(P_ALL, 0, &info, WEXITED | WSTOPPED | WNOHANG | WNOWAIT);
-  if (info.si_code == CLD_EXITED && info.si_status == EXIT_CATASTROPHIC) {
-    // A child process had a catastrophic error, abort the watcher.
-    ::exit(EXIT_FAILURE);
-  }
-}
-
-void Watcher::resetWorkerCounters(size_t respawn_time) {
-  // Reset the monitoring counters for the watcher.
-  auto& state = instance().state_;
-  state.sustained_latency = 0;
-  state.user_time = 0;
-  state.system_time = 0;
-  state.last_respawn_time = respawn_time;
-}
-
-void Watcher::resetExtensionCounters(const std::string& extension,
-                                     size_t respawn_time) {
-  WatcherLocker locker;
-  auto& state = instance().extension_states_[extension];
-  state.sustained_latency = 0;
-  state.user_time = 0;
-  state.system_time = 0;
-  state.last_respawn_time = respawn_time;
-}
-
-std::string Watcher::getExtensionPath(pid_t child) {
-  for (const auto& extension : extensions()) {
-    if (extension.second == child) {
-      return extension.first;
-    }
-  }
-  return "";
-}
-
-void Watcher::removeExtensionPath(const std::string& extension) {
-  WatcherLocker locker;
-  instance().extensions_.erase(extension);
-  instance().extension_states_.erase(extension);
-}
-
-PerformanceState& Watcher::getState(pid_t child) {
-  if (child == instance().worker_) {
-    return instance().state_;
-  } else {
-    return instance().extension_states_[getExtensionPath(child)];
-  }
-}
-
-PerformanceState& Watcher::getState(const std::string& extension) {
-  return instance().extension_states_[extension];
-}
-
-void Watcher::setExtension(const std::string& extension, pid_t child) {
-  WatcherLocker locker;
-  instance().extensions_[extension] = child;
-}
-
-void Watcher::reset(pid_t child) {
-  if (child == instance().worker_) {
-    instance().worker_ = 0;
-    resetWorkerCounters(0);
-    return;
-  }
-
-  // If it was not the worker pid then find the extension name to reset.
-  for (const auto& extension : extensions()) {
-    if (extension.second == child) {
-      setExtension(extension.first, 0);
-      resetExtensionCounters(extension.first, 0);
-    }
-  }
-}
-
-void Watcher::addExtensionPath(const std::string& path) {
-  // Resolve acceptable extension binaries from autoload paths.
-  if (isDirectory(path).ok()) {
-    VLOG(1) << "Cannot autoload extension from directory: " << path;
-    return;
-  }
-
-  // Only autoload extensions which were safe at the time of discovery.
-  // If the extension binary later becomes unsafe (permissions change) then
-  // it will fail to reload if a reload is ever needed.
-  fs::path extension(path);
-  if (safePermissions(extension.parent_path().string(), path, true)) {
-    if (extension.extension().string() == kExtensionExtension) {
-      setExtension(extension.string(), 0);
-      resetExtensionCounters(extension.string(), 0);
-      VLOG(1) << "Found autoloadable extension: " << extension.string();
-    }
-  }
-}
-
-bool Watcher::hasManagedExtensions() {
-  if (instance().extensions_.size() > 0) {
-    return true;
-  }
-
-  // A watchdog process may hint to a worker the number of managed extensions.
-  // Setting this counter to 0 will prevent the worker from waiting for missing
-  // dependent config plugins. Otherwise, its existence, will cause a worker to
-  // wait for missing plugins to broadcast from managed extensions.
-  return (getenv("OSQUERY_EXTENSIONS") != nullptr);
-}
-
-bool WatcherRunner::ok() {
-  interruptableSleep(getWorkerLimit(INTERVAL) * 1000);
-  // Watcher is OK to run if a worker or at least one extension exists.
-  return (Watcher::getWorker() >= 0 || Watcher::hasManagedExtensions());
-}
-
-void WatcherRunner::start() {
-  // Set worker performance counters to an initial state.
-  Watcher::resetWorkerCounters(0);
-  signal(SIGCHLD, childHandler);
-
-  // Enter the watch loop.
-  do {
-    if (use_worker_ && !watch(Watcher::getWorker())) {
-      // The watcher failed, create a worker.
-      createWorker();
-    }
-
-    // Loop over every managed extension and check sanity.
-    std::vector<std::string> failing_extensions;
-    for (const auto& extension : Watcher::extensions()) {
-      if (!watch(extension.second)) {
-        if (!createExtension(extension.first)) {
-          failing_extensions.push_back(extension.first);
-        }
-      }
-    }
-    // If any extension creations failed, stop managing them.
-    for (const auto& failed_extension : failing_extensions) {
-      Watcher::removeExtensionPath(failed_extension);
-    }
-  } while (ok());
-}
-
-bool WatcherRunner::watch(pid_t child) {
-  int status;
-  pid_t result = waitpid(child, &status, WNOHANG);
-  if (child == 0 || result == child) {
-    // Worker does not exist or never existed.
-    return false;
-  } else if (result == 0) {
-    // If the inspect finds problems it will stop/restart the worker.
-    if (!isChildSane(child)) {
-      stopChild(child);
-      return false;
-    }
-  }
-  return true;
-}
-
-void WatcherRunner::stopChild(pid_t child) {
-  kill(child, SIGKILL);
-
-  // Clean up the defunct (zombie) process.
-  waitpid(-1, 0, WNOHANG);
-}
-
-bool WatcherRunner::isChildSane(pid_t child) {
-  auto rows = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(child));
-  if (rows.size() == 0) {
-    // Could not find worker process?
-    return false;
-  }
-
-  // Get the performance state for the worker or extension.
-  size_t sustained_latency = 0;
-  // Compare CPU utilization since last check.
-  BIGINT_LITERAL footprint = 0, user_time = 0, system_time = 0, parent = 0;
-  // IV is the check interval in seconds, and utilization is set per-second.
-  auto iv = std::max(getWorkerLimit(INTERVAL), (size_t)1);
-
-  {
-    WatcherLocker locker;
-    auto& state = Watcher::getState(child);
-    try {
-      parent = AS_LITERAL(BIGINT_LITERAL, rows[0].at("parent"));
-      user_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("user_time")) / iv;
-      system_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("system_time")) / iv;
-      footprint = AS_LITERAL(BIGINT_LITERAL, rows[0].at("resident_size"));
-    } catch (const std::exception& e) {
-      state.sustained_latency = 0;
-    }
-
-    // Check the difference of CPU time used since last check.
-    if (user_time - state.user_time > getWorkerLimit(UTILIZATION_LIMIT) ||
-        system_time - state.system_time > getWorkerLimit(UTILIZATION_LIMIT)) {
-      state.sustained_latency++;
-    } else {
-      state.sustained_latency = 0;
-    }
-    // Update the current CPU time.
-    state.user_time = user_time;
-    state.system_time = system_time;
-
-    // Check if the sustained difference exceeded the acceptable latency limit.
-    sustained_latency = state.sustained_latency;
-
-    // Set the memory footprint as the amount of resident bytes allocated
-    // since the process image was created (estimate).
-    // A more-meaningful check would limit this to writable regions.
-    if (state.initial_footprint == 0) {
-      state.initial_footprint = footprint;
-    }
-
-    // Set the measured/limit-applied footprint to the post-launch allocations.
-    if (footprint < state.initial_footprint) {
-      footprint = 0;
-    } else {
-      footprint = footprint - state.initial_footprint;
-    }
-  }
-
-  // Only make a decision about the child sanity if it is still the watcher's
-  // child. It's possible for the child to die, and its pid reused.
-  if (parent != getpid()) {
-    // The child's parent is not the watcher.
-    Watcher::reset(child);
-    // Do not stop or call the child insane, since it is not our child.
-    return true;
-  }
-
-  if (sustained_latency > 0 &&
-      sustained_latency * iv >= getWorkerLimit(LATENCY_LIMIT)) {
-    LOG(WARNING) << "osqueryd worker (" << child
-                 << ") system performance limits exceeded";
-    return false;
-  }
-  // Check if the private memory exceeds a memory limit.
-  if (footprint > 0 && footprint > getWorkerLimit(MEMORY_LIMIT) * 1024 * 1024) {
-    LOG(WARNING) << "osqueryd worker (" << child
-                 << ") memory limits exceeded: " << footprint;
-    return false;
-  }
-
-  // The worker is sane, no action needed.
-  // Attempt to flush status logs to the well-behaved worker.
-  relayStatusLogs();
-  return true;
-}
-
-void WatcherRunner::createWorker() {
-  {
-    WatcherLocker locker;
-    if (Watcher::getState(Watcher::getWorker()).last_respawn_time >
-        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
-      LOG(WARNING) << "osqueryd worker respawning too quickly: "
-                   << Watcher::workerRestartCount() << " times";
-      Watcher::workerRestarted();
-      interruptableSleep(getWorkerLimit(RESPAWN_DELAY) * 1000);
-      // Exponential back off for quickly-respawning clients.
-      interruptableSleep(pow(2, Watcher::workerRestartCount()) * 1000);
-    }
-  }
-
-  // Get the path of the current process.
-  auto qd = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(getpid()));
-  if (qd.size() != 1 || qd[0].count("path") == 0 || qd[0]["path"].size() == 0) {
-    LOG(ERROR) << "osquery watcher cannot determine process path for worker";
-    ::exit(EXIT_FAILURE);
-  }
-
-  // Set an environment signaling to potential plugin-dependent workers to wait
-  // for extensions to broadcast.
-  if (Watcher::hasManagedExtensions()) {
-    setenv("OSQUERY_EXTENSIONS", "true", 1);
-  }
-
-  // Get the complete path of the osquery process binary.
-  auto exec_path = fs::system_complete(fs::path(qd[0]["path"]));
-  if (!safePermissions(
-          exec_path.parent_path().string(), exec_path.string(), true)) {
-    // osqueryd binary has become unsafe.
-    LOG(ERROR) << "osqueryd has unsafe permissions: " << exec_path.string();
-    ::exit(EXIT_FAILURE);
-  }
-
-  auto worker_pid = fork();
-  if (worker_pid < 0) {
-    // Unrecoverable error, cannot create a worker process.
-    LOG(ERROR) << "osqueryd could not create a worker process";
-    ::exit(EXIT_FAILURE);
-  } else if (worker_pid == 0) {
-    // This is the new worker process, no watching needed.
-    setenv("OSQUERY_WORKER", std::to_string(getpid()).c_str(), 1);
-    execve(exec_path.string().c_str(), argv_, environ);
-    // Code should never reach this point.
-    LOG(ERROR) << "osqueryd could not start worker process";
-    ::exit(EXIT_CATASTROPHIC);
-  }
-
-  Watcher::setWorker(worker_pid);
-  Watcher::resetWorkerCounters(getUnixTime());
-  VLOG(1) << "osqueryd watcher (" << getpid() << ") executing worker ("
-          << worker_pid << ")";
-}
-
-bool WatcherRunner::createExtension(const std::string& extension) {
-  {
-    WatcherLocker locker;
-    if (Watcher::getState(extension).last_respawn_time >
-        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
-      LOG(WARNING) << "Extension respawning too quickly: " << extension;
-      // Unlike a worker, if an extension respawns to quickly we give up.
-      return false;
-    }
-  }
-
-  // Check the path to the previously-discovered extension binary.
-  auto exec_path = fs::system_complete(fs::path(extension));
-  if (!safePermissions(
-          exec_path.parent_path().string(), exec_path.string(), true)) {
-    // Extension binary has become unsafe.
-    LOG(WARNING) << "Extension binary has unsafe permissions: " << extension;
-    return false;
-  }
-
-  auto ext_pid = fork();
-  if (ext_pid < 0) {
-    // Unrecoverable error, cannot create an extension process.
-    LOG(ERROR) << "Cannot create extension process: " << extension;
-    ::exit(EXIT_FAILURE);
-  } else if (ext_pid == 0) {
-    // Pass the current extension socket and a set timeout to the extension.
-    setenv("OSQUERY_EXTENSION", std::to_string(getpid()).c_str(), 1);
-    // Execute extension with very specific arguments.
-    execle(exec_path.string().c_str(),
-           ("osquery extension: " + extension).c_str(),
-           "--socket",
-           Flag::getValue("extensions_socket").c_str(),
-           "--timeout",
-           Flag::getValue("extensions_timeout").c_str(),
-           "--interval",
-           Flag::getValue("extensions_interval").c_str(),
-           (Flag::getValue("verbose") == "true") ? "--verbose" : (char*)nullptr,
-           (char*)nullptr,
-           environ);
-    // Code should never reach this point.
-    VLOG(1) << "Could not start extension process: " << extension;
-    ::exit(EXIT_FAILURE);
-  }
-
-  Watcher::setExtension(extension, ext_pid);
-  Watcher::resetExtensionCounters(extension, getUnixTime());
-  VLOG(1) << "Created and monitoring extension child (" << ext_pid << "): "
-          << extension;
-  return true;
-}
-
-void WatcherWatcherRunner::start() {
-  while (true) {
-    if (getppid() != watcher_) {
-      // Watcher died, the worker must follow.
-      VLOG(1) << "osqueryd worker (" << getpid()
-              << ") detected killed watcher (" << watcher_ << ")";
-      Dispatcher::stopServices();
-      // The watcher watcher is a thread. Do not join services after removing.
-      ::exit(EXIT_SUCCESS);
-    }
-    interruptableSleep(getWorkerLimit(INTERVAL) * 1000);
-  }
-}
-
-size_t getWorkerLimit(WatchdogLimitType name, int level) {
-  if (kWatchdogLimits.count(name) == 0) {
-    return 0;
-  }
-
-  // If no level was provided then use the default (config/switch).
-  if (level == -1) {
-    level = FLAGS_watchdog_level;
-  }
-  if (level > 3) {
-    return kWatchdogLimits.at(name).back();
-  }
-  return kWatchdogLimits.at(name).at(level);
-}
-}
diff --git a/osquery/core/watcher.h b/osquery/core/watcher.h
deleted file mode 100644 (file)
index 8dd06b5..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <string>
-
-#include <unistd.h>
-
-#include <boost/noncopyable.hpp>
-#include <boost/thread/mutex.hpp>
-
-#include <osquery/flags.h>
-
-#include "osquery/dispatcher/dispatcher.h"
-
-/// Define a special debug/testing watchdog level.
-#define WATCHDOG_LEVEL_DEBUG 3
-/// Define the default watchdog level, level below are considered permissive.
-#define WATCHDOG_LEVEL_DEFAULT 1
-
-namespace osquery {
-
-DECLARE_bool(disable_watchdog);
-DECLARE_int32(watchdog_level);
-
-class WatcherRunner;
-
-/**
- * @brief Categories of process performance limitations.
- *
- * Performance limits are applied by a watcher thread on autoloaded extensions
- * and a optional daemon worker process. The performance types are identified
- * here, and organized into levels. Such that a caller may enforce rigor or
- * relax the performance expectations of a osquery daemon.
- */
-enum WatchdogLimitType {
-  MEMORY_LIMIT,
-  UTILIZATION_LIMIT,
-  RESPAWN_LIMIT,
-  RESPAWN_DELAY,
-  LATENCY_LIMIT,
-  INTERVAL,
-};
-
-/**
- * @brief A performance state structure for an autoloaded extension or worker.
- *
- * A watcher thread will continue to check the performance state, and keep a
- * last-checked snapshot for each autoloaded extension and worker process.
- */
-struct PerformanceState {
-  /// A counter of how many intervals the process exceeded performance limits.
-  size_t sustained_latency;
-  /// The last checked user CPU time.
-  size_t user_time;
-  /// The last checked system CPU time.
-  size_t system_time;
-  /// A timestamp when the process/worker was last created.
-  size_t last_respawn_time;
-
-  /// The initial (or as close as possible) process image footprint.
-  size_t initial_footprint;
-
-  PerformanceState() {
-    sustained_latency = 0;
-    user_time = 0;
-    system_time = 0;
-    last_respawn_time = 0;
-    initial_footprint = 0;
-  }
-};
-
-/**
- * @brief Thread-safe watched child process state manager.
- *
- * The Watcher instance is separated from the WatcherRunner thread to allow
- * signals and osquery-introspection to monitor the autoloaded extensions
- * and optional worker stats. A child-process change signal may indicate an
- * autoloaded extension ended. Tables may also report on the historic worker
- * or extension utilizations.
- *
- * Though not critical, it is preferred to remove the extension's broadcasted
- * routes quickly. Locking access to the extensions list between signals and
- * the WatcherRunner thread allows osquery to tearDown registry changes before
- * attempting to respawn an extension process.
- */
-class Watcher : private boost::noncopyable {
- public:
-  /// Instance accessor
-  static Watcher& instance() {
-    static Watcher instance;
-    return instance;
-  }
-
-  /// Reset counters after a worker exits.
-  static void resetWorkerCounters(size_t respawn_time);
-
-  /// Reset counters for an extension path.
-  static void resetExtensionCounters(const std::string& extension,
-                                     size_t respawn_time);
-
-  /// Lock access to extensions.
-  static void lock() { instance().lock_.lock(); }
-
-  /// Unlock access to extensions.
-  static void unlock() { instance().lock_.unlock(); }
-
-  /// Accessor for autoloadable extension paths.
-  static const std::map<std::string, pid_t>& extensions() {
-    return instance().extensions_;
-  }
-
-  /// Lookup extension path from pid.
-  static std::string getExtensionPath(pid_t child);
-
-  /// Remove an autoloadable extension path.
-  static void removeExtensionPath(const std::string& extension);
-
-  /// Add extensions autoloadable paths.
-  static void addExtensionPath(const std::string& path);
-
-  /// Get state information for a worker or extension child.
-  static PerformanceState& getState(pid_t child);
-  static PerformanceState& getState(const std::string& extension);
-
-  /// Accessor for the worker process.
-  static pid_t getWorker() { return instance().worker_; }
-
-  /// Setter for worker process.
-  static void setWorker(pid_t child) { instance().worker_ = child; }
-
-  /// Setter for an extension process.
-  static void setExtension(const std::string& extension, pid_t child);
-
-  /// Reset pid and performance counters for a worker or extension process.
-  static void reset(pid_t child);
-
-  /// Count the number of worker restarts.
-  static size_t workerRestartCount() { return instance().worker_restarts_; }
-
-  /**
-   * @brief Return the state of autoloadable extensions.
-   *
-   * Some initialization decisions are made based on waiting for plugins to
-   * broadcast from potentially-loaded extensions. If no extensions are loaded
-   * and an active (selected at command line) plugin is missing, fail quickly.
-   */
-  static bool hasManagedExtensions();
-
- private:
-  /// Do not request the lock until extensions are used.
-  Watcher()
-      : worker_(-1), worker_restarts_(0), lock_(mutex_, boost::defer_lock) {}
-  Watcher(Watcher const&);
-  void operator=(Watcher const&);
-  virtual ~Watcher() {}
-
- private:
-  /// Inform the watcher that the worker restarted without cause.
-  static void workerRestarted() { instance().worker_restarts_++; }
-
- private:
-  /// Performance state for the worker process.
-  PerformanceState state_;
-  /// Performance states for each autoloadable extension binary.
-  std::map<std::string, PerformanceState> extension_states_;
-
- private:
-  /// Keep the single worker process/thread ID for inspection.
-  pid_t worker_;
-  /// Number of worker restarts NOT induced by a watchdog process.
-  size_t worker_restarts_;
-  /// Keep a list of resolved extension paths and their managed pids.
-  std::map<std::string, pid_t> extensions_;
-  /// Paths to autoload extensions.
-  std::vector<std::string> extensions_paths_;
-
- private:
-  /// Mutex and lock around extensions access.
-  boost::mutex mutex_;
-  /// Mutex and lock around extensions access.
-  boost::unique_lock<boost::mutex> lock_;
-
- private:
-  friend class WatcherRunner;
-};
-
-/**
- * @brief A scoped locker for iterating over watcher extensions.
- *
- * A lock must be used if any part of osquery wants to enumerate the autoloaded
- * extensions or autoloadable extension paths a Watcher may be monitoring.
- * A signal or WatcherRunner thread may stop or start extensions.
- */
-class WatcherLocker {
- public:
-  /// Construct and gain watcher lock.
-  WatcherLocker() { Watcher::lock(); }
-  /// Destruct and release watcher lock.
-  ~WatcherLocker() { Watcher::unlock(); }
-};
-
-/**
- * @brief The watchdog thread responsible for spawning/monitoring children.
- *
- * The WatcherRunner thread will spawn any autoloaded extensions or optional
- * osquery daemon worker processes. It will then poll for their performance
- * state and kill/respawn osquery child processes if they violate limits.
- */
-class WatcherRunner : public InternalRunnable {
- public:
-  /**
-   * @brief Construct a watcher thread.
-   *
-   * @param argc The osquery process argc.
-   * @param argv The osquery process argv.
-   * @param use_worker True if the process should spawn and monitor a worker.
-   */
-  explicit WatcherRunner(int argc, char** argv, bool use_worker)
-      : argc_(argc), argv_(argv), use_worker_(use_worker) {
-    (void)argc_;
-  }
-
- private:
-  /// Dispatcher (this service thread's) entry point.
-  void start();
-  /// Boilerplate function to sleep for some configured latency
-  bool ok();
-  /// Begin the worker-watcher process.
-  bool watch(pid_t child);
-  /// Inspect into the memory, CPU, and other worker/extension process states.
-  bool isChildSane(pid_t child);
-
- private:
-  /// Fork and execute a worker process.
-  void createWorker();
-  /// Fork an extension process.
-  bool createExtension(const std::string& extension);
-  /// If a worker/extension has otherwise gone insane, stop it.
-  void stopChild(pid_t child);
-
- private:
-  /// Keep the invocation daemon's argc to iterate through argv.
-  int argc_;
-  /// When a worker child is spawned the argv will be scrubbed.
-  char** argv_;
-  /// Spawn/monitor a worker process.
-  bool use_worker_;
-};
-
-/// The WatcherWatcher is spawned within the worker and watches the watcher.
-class WatcherWatcherRunner : public InternalRunnable {
- public:
-  explicit WatcherWatcherRunner(pid_t watcher) : watcher_(watcher) {}
-
-  /// Runnable thread's entry point.
-  void start();
-
- private:
-  /// Parent, or watchdog, process ID.
-  pid_t watcher_;
-};
-
-/// Get a performance limit by name and optional level.
-size_t getWorkerLimit(WatchdogLimitType limit, int level = -1);
-}
diff --git a/osquery/database/CMakeLists.txt b/osquery/database/CMakeLists.txt
deleted file mode 100644 (file)
index 4d47844..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_database database.cpp)
-
-ADD_OSQUERY_LIBRARY(osquery_database_internal db_handle.cpp
-                                                                                         query.cpp)
-
-FILE(GLOB OSQUERY_DATABASE_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_DATABASE_TESTS})
diff --git a/osquery/database/database.cpp b/osquery/database/database.cpp
deleted file mode 100644 (file)
index 96e9ec6..0000000
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <algorithm>
-#include <iostream>
-#include <sstream>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <boost/lexical_cast.hpp>
-#include <boost/property_tree/json_parser.hpp>
-
-#include <osquery/database.h>
-#include <osquery/logger.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-typedef unsigned char byte;
-
-/////////////////////////////////////////////////////////////////////////////
-// Row - the representation of a row in a set of database results. Row is a
-// simple map where individual column names are keys, which map to the Row's
-// respective value
-/////////////////////////////////////////////////////////////////////////////
-
-std::string escapeNonPrintableBytes(const std::string& data) {
-  std::string escaped;
-  // clang-format off
-  char const hex_chars[16] = {
-    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-    'A', 'B', 'C', 'D', 'E', 'F',
-  };
-  // clang-format on
-  for (int i = 0; i < data.length(); i++) {
-    if (((byte)data[i]) < 0x20 || ((byte)data[i]) >= 0x80) {
-      escaped += "\\x";
-      escaped += hex_chars[(((byte)data[i])) >> 4];
-      escaped += hex_chars[((byte)data[i] & 0x0F) >> 0];
-    } else {
-      escaped += data[i];
-    }
-  }
-  return escaped;
-}
-
-void escapeQueryData(const QueryData& oldData, QueryData& newData) {
-  for (const auto& r : oldData) {
-    Row newRow;
-    for (auto& i : r) {
-      newRow[i.first] = escapeNonPrintableBytes(i.second);
-    }
-    newData.push_back(newRow);
-  }
-}
-
-Status serializeRow(const Row& r, pt::ptree& tree) {
-  try {
-    for (auto& i : r) {
-      tree.put<std::string>(i.first, i.second);
-    }
-  } catch (const std::exception& e) {
-    return Status(1, e.what());
-  }
-  return Status(0, "OK");
-}
-
-Status serializeRowJSON(const Row& r, std::string& json) {
-  pt::ptree tree;
-  auto status = serializeRow(r, tree);
-  if (!status.ok()) {
-    return status;
-  }
-
-  std::ostringstream output;
-  try {
-    pt::write_json(output, tree, false);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    // The content could not be represented as JSON.
-    return Status(1, e.what());
-  }
-  json = output.str();
-  return Status(0, "OK");
-}
-
-Status deserializeRow(const pt::ptree& tree, Row& r) {
-  for (const auto& i : tree) {
-    if (i.first.length() > 0) {
-      r[i.first] = i.second.data();
-    }
-  }
-  return Status(0, "OK");
-}
-
-Status deserializeRowJSON(const std::string& json, Row& r) {
-  pt::ptree tree;
-  try {
-    std::stringstream input;
-    input << json;
-    pt::read_json(input, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return Status(1, e.what());
-  }
-  return deserializeRow(tree, r);
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// QueryData - the representation of a database query result set. It's a
-// vector of rows
-/////////////////////////////////////////////////////////////////////////////
-
-Status serializeQueryData(const QueryData& q, pt::ptree& tree) {
-  for (const auto& r : q) {
-    pt::ptree serialized;
-    auto s = serializeRow(r, serialized);
-    if (!s.ok()) {
-      return s;
-    }
-    tree.push_back(std::make_pair("", serialized));
-  }
-  return Status(0, "OK");
-}
-
-Status serializeQueryDataJSON(const QueryData& q, std::string& json) {
-  pt::ptree tree;
-  auto status = serializeQueryData(q, tree);
-  if (!status.ok()) {
-    return status;
-  }
-
-  std::ostringstream output;
-  try {
-    pt::write_json(output, tree, false);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    // The content could not be represented as JSON.
-    return Status(1, e.what());
-  }
-  json = output.str();
-  return Status(0, "OK");
-}
-
-Status deserializeQueryData(const pt::ptree& tree, QueryData& qd) {
-  for (const auto& i : tree) {
-    Row r;
-    auto status = deserializeRow(i.second, r);
-    if (!status.ok()) {
-      return status;
-    }
-    qd.push_back(r);
-  }
-  return Status(0, "OK");
-}
-
-Status deserializeQueryDataJSON(const std::string& json, QueryData& qd) {
-  pt::ptree tree;
-  try {
-    std::stringstream input;
-    input << json;
-    pt::read_json(input, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return Status(1, e.what());
-  }
-  return deserializeQueryData(tree, qd);
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// DiffResults - the representation of two diffed QueryData result sets.
-// Given and old and new QueryData, DiffResults indicates the "added" subset
-// of rows and the "removed" subset of Rows
-/////////////////////////////////////////////////////////////////////////////
-
-Status serializeDiffResults(const DiffResults& d, pt::ptree& tree) {
-  pt::ptree added;
-  auto status = serializeQueryData(d.added, added);
-  if (!status.ok()) {
-    return status;
-  }
-  tree.add_child("added", added);
-
-  pt::ptree removed;
-  status = serializeQueryData(d.removed, removed);
-  if (!status.ok()) {
-    return status;
-  }
-  tree.add_child("removed", removed);
-  return Status(0, "OK");
-}
-
-Status deserializeDiffResults(const pt::ptree& tree, DiffResults& dr) {
-  if (tree.count("added") > 0) {
-    auto status = deserializeQueryData(tree.get_child("added"), dr.added);
-    if (!status.ok()) {
-      return status;
-    }
-  }
-
-  if (tree.count("removed") > 0) {
-    auto status = deserializeQueryData(tree.get_child("removed"), dr.removed);
-    if (!status.ok()) {
-      return status;
-    }
-  }
-  return Status(0, "OK");
-}
-
-Status serializeDiffResultsJSON(const DiffResults& d, std::string& json) {
-  pt::ptree tree;
-  auto status = serializeDiffResults(d, tree);
-  if (!status.ok()) {
-    return status;
-  }
-
-  std::ostringstream output;
-  try {
-    pt::write_json(output, tree, false);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    // The content could not be represented as JSON.
-    return Status(1, e.what());
-  }
-  json = output.str();
-  return Status(0, "OK");
-}
-
-DiffResults diff(const QueryData& old, const QueryData& current) {
-  DiffResults r;
-  QueryData overlap;
-
-  for (const auto& i : current) {
-    auto item = std::find(old.begin(), old.end(), i);
-    if (item != old.end()) {
-      overlap.push_back(i);
-    } else {
-      r.added.push_back(i);
-    }
-  }
-
-  std::multiset<Row> overlap_set(overlap.begin(), overlap.end());
-  std::multiset<Row> old_set(old.begin(), old.end());
-  std::set_difference(old_set.begin(),
-                      old_set.end(),
-                      overlap_set.begin(),
-                      overlap_set.end(),
-                      std::back_inserter(r.removed));
-  return r;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// QueryLogItem - the representation of a log result occuring when a
-// scheduled query yields operating system state change.
-/////////////////////////////////////////////////////////////////////////////
-
-Status serializeQueryLogItem(const QueryLogItem& i, pt::ptree& tree) {
-  pt::ptree results_tree;
-  if (i.results.added.size() > 0 || i.results.removed.size() > 0) {
-    auto status = serializeDiffResults(i.results, results_tree);
-    if (!status.ok()) {
-      return status;
-    }
-    tree.add_child("diffResults", results_tree);
-  } else {
-    auto status = serializeQueryData(i.snapshot_results, results_tree);
-    if (!status.ok()) {
-      return status;
-    }
-    tree.add_child("snapshot", results_tree);
-  }
-
-  tree.put<std::string>("name", i.name);
-  tree.put<std::string>("hostIdentifier", i.identifier);
-  tree.put<std::string>("calendarTime", i.calendar_time);
-  tree.put<int>("unixTime", i.time);
-  return Status(0, "OK");
-}
-
-Status serializeQueryLogItemJSON(const QueryLogItem& i, std::string& json) {
-  pt::ptree tree;
-  auto status = serializeQueryLogItem(i, tree);
-  if (!status.ok()) {
-    return status;
-  }
-
-  std::ostringstream output;
-  try {
-    pt::write_json(output, tree, false);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    // The content could not be represented as JSON.
-    return Status(1, e.what());
-  }
-  json = output.str();
-  return Status(0, "OK");
-}
-
-Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item) {
-  if (tree.count("diffResults") > 0) {
-    auto status =
-        deserializeDiffResults(tree.get_child("diffResults"), item.results);
-    if (!status.ok()) {
-      return status;
-    }
-  } else if (tree.count("snapshot") > 0) {
-    auto status =
-        deserializeQueryData(tree.get_child("snapshot"), item.snapshot_results);
-    if (!status.ok()) {
-      return status;
-    }
-  }
-
-  item.name = tree.get<std::string>("name", "");
-  item.identifier = tree.get<std::string>("hostIdentifier", "");
-  item.calendar_time = tree.get<std::string>("calendarTime", "");
-  item.time = tree.get<int>("unixTime", 0);
-  return Status(0, "OK");
-}
-
-Status deserializeQueryLogItemJSON(const std::string& json,
-                                   QueryLogItem& item) {
-  pt::ptree tree;
-  try {
-    std::stringstream input;
-    input << json;
-    pt::read_json(input, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return Status(1, e.what());
-  }
-  return deserializeQueryLogItem(tree, item);
-}
-
-Status serializeEvent(const QueryLogItem& item,
-                      const pt::ptree& event,
-                      pt::ptree& tree) {
-  tree.put<std::string>("name", item.name);
-  tree.put<std::string>("hostIdentifier", item.identifier);
-  tree.put<std::string>("calendarTime", item.calendar_time);
-  tree.put<int>("unixTime", item.time);
-
-  pt::ptree columns;
-  for (auto& i : event) {
-    // Yield results as a "columns." map to avoid namespace collisions.
-    columns.put<std::string>(i.first, i.second.get_value<std::string>());
-  }
-
-  tree.add_child("columns", columns);
-  return Status(0, "OK");
-}
-
-Status serializeQueryLogItemAsEvents(const QueryLogItem& i, pt::ptree& tree) {
-  pt::ptree diff_results;
-  auto status = serializeDiffResults(i.results, diff_results);
-  if (!status.ok()) {
-    return status;
-  }
-
-  for (auto& action : diff_results) {
-    for (auto& row : action.second) {
-      pt::ptree event;
-      serializeEvent(i, row.second, event);
-      event.put<std::string>("action", action.first);
-      tree.push_back(std::make_pair("", event));
-    }
-  }
-  return Status(0, "OK");
-}
-
-Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i,
-                                         std::string& json) {
-  pt::ptree tree;
-  auto status = serializeQueryLogItemAsEvents(i, tree);
-  if (!status.ok()) {
-    return status;
-  }
-
-  std::ostringstream output;
-  for (auto& event : tree) {
-    try {
-      pt::write_json(output, event.second, false);
-    } catch (const pt::json_parser::json_parser_error& e) {
-      return Status(1, e.what());
-    }
-  }
-  json = output.str();
-  return Status(0, "OK");
-}
-
-bool addUniqueRowToQueryData(QueryData& q, const Row& r) {
-  if (std::find(q.begin(), q.end(), r) != q.end()) {
-    return false;
-  }
-  q.push_back(r);
-  return true;
-}
-
-Status DatabasePlugin::call(const PluginRequest& request,
-                            PluginResponse& response) {
-  if (request.count("action") == 0) {
-    return Status(1, "Database plugin must include a request action");
-  }
-
-  // Get a domain/key, which are used for most database plugin actions.
-  auto domain = (request.count("domain") > 0) ? request.at("domain") : "";
-  auto key = (request.count("key") > 0) ? request.at("key") : "";
-
-  // Switch over the possible database plugin actions.
-  if (request.at("action") == "get") {
-    std::string value;
-    auto status = this->get(domain, key, value);
-    response.push_back({{"v", value}});
-    return status;
-  } else if (request.at("action") == "put") {
-    if (request.count("value") == 0) {
-      return Status(1, "Database plugin put action requires a value");
-    }
-    return this->put(domain, key, request.at("value"));
-  } else if (request.at("action") == "remove") {
-    return this->remove(domain, key);
-  } else if (request.at("action") == "scan") {
-    std::vector<std::string> keys;
-    auto status = this->scan(domain, keys);
-    for (const auto& key : keys) {
-      response.push_back({{"k", key}});
-    }
-    return status;
-  }
-
-  return Status(1, "Unknown database plugin action");
-}
-
-Status getDatabaseValue(const std::string& domain,
-                        const std::string& key,
-                        std::string& value) {
-  PluginRequest request = {{"action", "get"}, {"domain", domain}, {"key", key}};
-  PluginResponse response;
-  auto status = Registry::call("database", "rocks", request, response);
-  if (!status.ok()) {
-    VLOG(1) << "Cannot get database " << domain << "/" << key << ": "
-            << status.getMessage();
-    return status;
-  }
-
-  // Set value from the internally-known "v" key.
-  if (response.size() > 0 && response[0].count("v") > 0) {
-    value = response[0].at("v");
-  }
-  return status;
-}
-
-Status setDatabaseValue(const std::string& domain,
-                        const std::string& key,
-                        const std::string& value) {
-  PluginRequest request = {
-      {"action", "put"}, {"domain", domain}, {"key", key}, {"value", value}};
-  return Registry::call("database", "rocks", request);
-}
-
-Status deleteDatabaseValue(const std::string& domain, const std::string& key) {
-  PluginRequest request = {
-      {"action", "remove"}, {"domain", domain}, {"key", key}};
-  return Registry::call("database", "rocks", request);
-}
-
-Status scanDatabaseKeys(const std::string& domain,
-                        std::vector<std::string>& keys) {
-  PluginRequest request = {{"action", "scan"}, {"domain", domain}};
-  PluginResponse response;
-  auto status = Registry::call("database", "rocks", request, response);
-
-  for (const auto& item : response) {
-    if (item.count("k") > 0) {
-      keys.push_back(item.at("k"));
-    }
-  }
-  return status;
-}
-}
diff --git a/osquery/database/db_handle.cpp b/osquery/database/db_handle.cpp
deleted file mode 100644 (file)
index 502af8e..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <algorithm>
-#include <mutex>
-#include <stdexcept>
-
-#include <sys/stat.h>
-
-#include <rocksdb/env.h>
-#include <rocksdb/options.h>
-#include <snappy.h>
-
-#include <osquery/database.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/status.h>
-
-#include "osquery/database/db_handle.h"
-
-namespace osquery {
-
-class RocksDatabasePlugin : public DatabasePlugin {
- public:
-  /// Data retrieval method.
-  Status get(const std::string& domain,
-             const std::string& key,
-             std::string& value) const;
-
-  /// Data storage method.
-  Status put(const std::string& domain,
-             const std::string& key,
-             const std::string& value);
-
-  /// Data removal method.
-  Status remove(const std::string& domain, const std::string& k);
-
-  /// Key/index lookup method.
-  Status scan(const std::string& domain,
-              std::vector<std::string>& results) const;
-};
-
-/// Backing-storage provider for osquery internal/core.
-REGISTER_INTERNAL(RocksDatabasePlugin, "database", "rocks");
-
-/////////////////////////////////////////////////////////////////////////////
-// Constants
-/////////////////////////////////////////////////////////////////////////////
-
-const std::string kPersistentSettings = "configurations";
-const std::string kQueries = "queries";
-const std::string kEvents = "events";
-const std::string kLogs = "logs";
-
-/**
- * @brief A const vector of column families in RocksDB
- *
- * RocksDB has a concept of "column families" which are kind of like tables
- * in other databases. kDomainds is populated with a list of all column
- * families. If a string exists in kDomains, it's a column family in the
- * database.
- */
-const std::vector<std::string> kDomains = {
-    kPersistentSettings, kQueries, kEvents, kLogs
-};
-
-CLI_FLAG(string,
-         database_path,
-         "/var/osquery/osquery.db",
-         "If using a disk-based backing store, specify a path");
-FLAG_ALIAS(std::string, db_path, database_path);
-
-CLI_FLAG(bool,
-         database_in_memory,
-         false,
-         "Keep osquery backing-store in memory");
-FLAG_ALIAS(bool, use_in_memory_database, database_in_memory);
-
-/////////////////////////////////////////////////////////////////////////////
-// constructors and destructors
-/////////////////////////////////////////////////////////////////////////////
-
-DBHandle::DBHandle(const std::string& path, bool in_memory) {
-  options_.create_if_missing = true;
-  options_.create_missing_column_families = true;
-  options_.info_log_level = rocksdb::WARN_LEVEL;
-  options_.log_file_time_to_roll = 0;
-  options_.keep_log_file_num = 10;
-  options_.max_log_file_size = 1024 * 1024 * 1;
-  options_.compaction_style = rocksdb::kCompactionStyleLevel;
-  options_.write_buffer_size = 1 * 1024 * 1024;
-  options_.max_write_buffer_number = 2;
-  options_.max_background_compactions = 1;
-
-  if (in_memory) {
-    // Remove when MemEnv is included in librocksdb
-    // options_.env = rocksdb::NewMemEnv(rocksdb::Env::Default());
-    throw std::runtime_error("Requires MemEnv");
-  }
-
-  if (pathExists(path).ok() && !isWritable(path).ok()) {
-    throw std::runtime_error("Cannot write to RocksDB path: " + path);
-  }
-
-  column_families_.push_back(rocksdb::ColumnFamilyDescriptor(
-      rocksdb::kDefaultColumnFamilyName, rocksdb::ColumnFamilyOptions()));
-
-  for (const auto& cf_name : kDomains) {
-    column_families_.push_back(rocksdb::ColumnFamilyDescriptor(
-        cf_name, rocksdb::ColumnFamilyOptions()));
-  }
-
-  VLOG(1) << "Opening RocksDB handle: " << path;
-  auto s = rocksdb::DB::Open(options_, path, column_families_, &handles_, &db_);
-  if (!s.ok()) {
-    throw std::runtime_error(s.ToString());
-  }
-
-  // RocksDB may not create/append a directory with acceptable permissions.
-  if (chmod(path.c_str(), S_IRWXU) != 0) {
-    throw std::runtime_error("Cannot set permissions on RocksDB path: " + path);
-  }
-}
-
-DBHandle::~DBHandle() {
-  for (auto handle : handles_) {
-    delete handle;
-  }
-  delete db_;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// getInstance methods
-/////////////////////////////////////////////////////////////////////////////
-
-DBHandleRef DBHandle::getInstance() {
-  return getInstance(FLAGS_database_path, FLAGS_database_in_memory);
-}
-
-bool DBHandle::checkDB() {
-  try {
-    auto handle = DBHandle(FLAGS_database_path, FLAGS_database_in_memory);
-  } catch (const std::exception& e) {
-    return false;
-  }
-  return true;
-}
-
-DBHandleRef DBHandle::getInstanceInMemory() {
-  return getInstance("", true);
-}
-
-DBHandleRef DBHandle::getInstanceAtPath(const std::string& path) {
-  return getInstance(path, false);
-}
-
-DBHandleRef DBHandle::getInstance(const std::string& path, bool in_memory) {
-  static DBHandleRef db_handle = DBHandleRef(new DBHandle(path, in_memory));
-  return db_handle;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// getters and setters
-/////////////////////////////////////////////////////////////////////////////
-
-rocksdb::DB* DBHandle::getDB() { return db_; }
-
-rocksdb::ColumnFamilyHandle* DBHandle::getHandleForColumnFamily(
-    const std::string& cf) {
-  try {
-    for (int i = 0; i < kDomains.size(); i++) {
-      if (kDomains[i] == cf) {
-        return handles_[i];
-      }
-    }
-  } catch (const std::exception& e) {
-    // pass through and return nullptr
-  }
-  return nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// Data manipulation methods
-/////////////////////////////////////////////////////////////////////////////
-
-Status DBHandle::Get(const std::string& domain,
-                     const std::string& key,
-                     std::string& value) {
-  auto cfh = getHandleForColumnFamily(domain);
-  if (cfh == nullptr) {
-    return Status(1, "Could not get column family for " + domain);
-  }
-  auto s = getDB()->Get(rocksdb::ReadOptions(), cfh, key, &value);
-  return Status(s.code(), s.ToString());
-}
-
-Status DBHandle::Put(const std::string& domain,
-                     const std::string& key,
-                     const std::string& value) {
-  auto cfh = getHandleForColumnFamily(domain);
-  if (cfh == nullptr) {
-    return Status(1, "Could not get column family for " + domain);
-  }
-  auto s = getDB()->Put(rocksdb::WriteOptions(), cfh, key, value);
-  return Status(s.code(), s.ToString());
-}
-
-Status DBHandle::Delete(const std::string& domain, const std::string& key) {
-  auto cfh = getHandleForColumnFamily(domain);
-  if (cfh == nullptr) {
-    return Status(1, "Could not get column family for " + domain);
-  }
-  auto options = rocksdb::WriteOptions();
-  options.sync = true;
-  auto s = getDB()->Delete(options, cfh, key);
-  return Status(s.code(), s.ToString());
-}
-
-Status DBHandle::Scan(const std::string& domain,
-                      std::vector<std::string>& results) {
-  auto cfh = getHandleForColumnFamily(domain);
-  if (cfh == nullptr) {
-    return Status(1, "Could not get column family for " + domain);
-  }
-  auto it = getDB()->NewIterator(rocksdb::ReadOptions(), cfh);
-  if (it == nullptr) {
-    return Status(1, "Could not get iterator for " + domain);
-  }
-  for (it->SeekToFirst(); it->Valid(); it->Next()) {
-    results.push_back(it->key().ToString());
-  }
-  delete it;
-  return Status(0, "OK");
-}
-
-Status RocksDatabasePlugin::get(const std::string& domain,
-                                const std::string& key,
-                                std::string& value) const {
-  return DBHandle::getInstance()->Get(domain, key, value);
-}
-
-Status RocksDatabasePlugin::put(const std::string& domain,
-                                const std::string& key,
-                                const std::string& value) {
-  return DBHandle::getInstance()->Put(domain, key, value);
-}
-
-Status RocksDatabasePlugin::remove(const std::string& domain,
-                                   const std::string& key) {
-  return DBHandle::getInstance()->Delete(domain, key);
-}
-
-Status RocksDatabasePlugin::scan(const std::string& domain,
-                                 std::vector<std::string>& results) const {
-  return DBHandle::getInstance()->Scan(domain, results);
-}
-}
diff --git a/osquery/database/db_handle.h b/osquery/database/db_handle.h
deleted file mode 100644 (file)
index 26ccf4e..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <rocksdb/db.h>
-
-#include <boost/noncopyable.hpp>
-
-#include <osquery/core.h>
-#include <osquery/flags.h>
-
-namespace osquery {
-
-DECLARE_string(database_path);
-
-class DBHandle;
-typedef std::shared_ptr<DBHandle> DBHandleRef;
-
-/**
- * @brief RAII singleton around RocksDB database access.
- *
- * Accessing RocksDB necessitates creating several pointers which must be
- * carefully memory managed. DBHandle offers you a singleton which takes
- * care of acquiring and releasing the relevant pointers and data structures
- * for you.
- */
-class DBHandle {
- public:
-  /// Removes every column family handle and single DB handle/lock.
-  ~DBHandle();
-
-  /**
-   * @brief The primary way to access the DBHandle singleton.
-   *
-   * DBHandle::getInstance() provides access to the DBHandle singleton.
-   *
-   * @code{.cpp}
-   *   auto db = DBHandle::getInstance();
-   *   std::string value;
-   *   auto status = db->Get("default", "foo", value);
-   *   if (status.ok()) {
-   *     assert(value == "bar");
-   *   }
-   * @endcode
-   *
-   * @return a shared pointer to an instance of DBHandle
-   */
-  static DBHandleRef getInstance();
-
-  /**
-   * @brief Check the sanity of the database configuration options
-   *
-   * Create a handle to the backing store using the database configuration.
-   * Catch any instance creation exceptions and release the handle immediately.
-   *
-   * @return Success if a handle was created without error.
-   */
-  static bool checkDB();
-
- private:
-  /////////////////////////////////////////////////////////////////////////////
-  // Data access methods
-  /////////////////////////////////////////////////////////////////////////////
-
-  /**
-   * @brief Get data from the database
-   *
-   * @param domain the "domain" or "column family" that you'd like to retrieve
-   * the data from
-   * @param key the string key that you'd like to get
-   * @param value a non-const string reference where the result of the
-   * operation will be stored
-   *
-   * @return an instance of osquery::Status indicating the success or failure
-   * of the operation.
-   */
-  Status Get(const std::string& domain,
-             const std::string& key,
-             std::string& value);
-
-  /**
-   * @brief Put data into the database
-   *
-   * @param domain the "domain" or "column family" that you'd like to insert
-   * data into
-   * @param key the string key that you'd like to put
-   * @param value the data that you'd like to put into RocksDB
-   *
-   * @return an instance of osquery::Status indicating the success or failure
-   * of the operation.
-   */
-  Status Put(const std::string& domain,
-             const std::string& key,
-             const std::string& value);
-
-  /**
-   * @brief Delete data from the database
-   *
-   * @param domain the "domain" or "column family" that you'd like to delete
-   * data from
-   * @param key the string key that you'd like to delete
-   *
-   * @return an instance of osquery::Status indicating the success or failure
-   * of the operation.
-   */
-  Status Delete(const std::string& domain, const std::string& key);
-
-  /**
-   * @brief List the data in a "domain"
-   *
-   * @param domain the "domain" or "column family" that you'd like to list
-   * data from
-   * @param results a non-const reference to a vector which will be populated
-   * with all of the keys from the supplied domain.
-   *
-   * @return an instance of osquery::Status indicating the success or failure
-   * of the operation.
-   */
-  Status Scan(const std::string& domain, std::vector<std::string>& results);
-
- private:
-  /**
-   * @brief Default constructor
-   *
-   * DBHandle's constructor takes care of properly connecting to RocksDB and
-   * ensuring that all necessary column families are created. The resulting
-   * database handle can then be accessed via DBHandle::getDB() and the
-   * success of the connection can be determined by inspecting the resulting
-   * status code via DBHandle::getStatus()
-   */
-  DBHandle();
-
-  /**
-   * @brief Internal only constructor used to create instances of DBHandle.
-   *
-   * This constructor allows you to specify a few more details about how you'd
-   * like DBHandle to be used. This is only used internally, so you should
-   * never actually use it.
-   *
-   * @param path the path to create/access the database
-   * @param in_memory a boolean indicating whether or not the database should
-   * be creating in memory or not.
-   */
-  DBHandle(const std::string& path, bool in_memory);
-
-  /**
-   * @brief A method which allows you to override the database path
-   *
-   * This should only be used by unit tests. Never use it in production code.
-   *
-   * @return a shared pointer to an instance of DBHandle
-   */
-  static DBHandleRef getInstanceAtPath(const std::string& path);
-
-  /**
-   * @brief A method which gets you an in-memory RocksDB instance.
-   *
-   * This should only be used by unit tests. Never use it in production code.
-   *
-   * @return a shared pointer to an instance of DBHandle
-   */
-  static DBHandleRef getInstanceInMemory();
-
-  /**
-   * @brief A method which allows you to configure various aspects of RocksDB
-   * database options.
-   *
-   * This should only be used by unit tests. Never use it in production code.
-   *
-   * @param path the path to create/access the database
-   * @param in_memory a boolean indicating whether or not the database should
-   * be creating in memory or not.
-   *
-   * @return a shared pointer to an instance of DBHandle
-   */
-  static DBHandleRef getInstance(const std::string& path, bool in_memory);
-
-  /**
-   * @brief Private helper around accessing the column family handle for a
-   * specific column family, based on it's name
-   */
-  rocksdb::ColumnFamilyHandle* getHandleForColumnFamily(const std::string& cf);
-
-  /**
-   * @brief Helper method which can be used to get a raw pointer to the
-   * underlying RocksDB database handle
-   *
-   * You probably shouldn't use this. DBHandle::getDB() should only be used
-   * when you're positive that it's the right thing to use.
-   *
-   * @return a pointer to the underlying RocksDB database handle
-   */
-  rocksdb::DB* getDB();
-
- private:
-  /////////////////////////////////////////////////////////////////////////////
-  // Private members
-  /////////////////////////////////////////////////////////////////////////////
-
-  /// The database handle
-  rocksdb::DB* db_;
-
-  /// Column family descriptors which are used to connect to RocksDB
-  std::vector<rocksdb::ColumnFamilyDescriptor> column_families_;
-
-  /// A vector of pointers to column family handles
-  std::vector<rocksdb::ColumnFamilyHandle*> handles_;
-
-  /// The RocksDB connection options that are used to connect to RocksDB
-  rocksdb::Options options_;
-
- private:
-  friend class RocksDatabasePlugin;
-  friend class Query;
-  friend class EventSubscriberPlugin;
-
-  /////////////////////////////////////////////////////////////////////////////
-  // Unit tests which can access private members
-  /////////////////////////////////////////////////////////////////////////////
-
-  friend class DBHandleTests;
-  FRIEND_TEST(DBHandleTests, test_get);
-  FRIEND_TEST(DBHandleTests, test_put);
-  FRIEND_TEST(DBHandleTests, test_delete);
-  FRIEND_TEST(DBHandleTests, test_scan);
-  friend class QueryTests;
-  FRIEND_TEST(QueryTests, test_get_query_results);
-  FRIEND_TEST(QueryTests, test_is_query_name_in_database);
-  FRIEND_TEST(QueryTests, test_get_stored_query_names);
-  friend class EventsTests;
-  friend class EventsDatabaseTests;
-};
-}
diff --git a/osquery/database/query.cpp b/osquery/database/query.cpp
deleted file mode 100644 (file)
index 360a329..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <algorithm>
-
-#include "osquery/database/query.h"
-
-namespace osquery {
-
-/////////////////////////////////////////////////////////////////////////////
-// Getters and setters
-/////////////////////////////////////////////////////////////////////////////
-
-std::string Query::getQuery() { return query_.query; }
-
-std::string Query::getQueryName() { return name_; }
-
-int Query::getInterval() { return query_.interval; }
-
-/////////////////////////////////////////////////////////////////////////////
-// Data access methods
-/////////////////////////////////////////////////////////////////////////////
-
-Status Query::getPreviousQueryResults(QueryData& results) {
-  return getPreviousQueryResults(results, DBHandle::getInstance());
-}
-
-Status Query::getPreviousQueryResults(QueryData& results, DBHandleRef db) {
-  if (!isQueryNameInDatabase()) {
-    return Status(0, "Query name not found in database");
-  }
-
-  std::string raw;
-  auto status = db->Get(kQueries, name_, raw);
-  if (!status.ok()) {
-    return status;
-  }
-
-  status = deserializeQueryDataJSON(raw, results);
-  if (!status.ok()) {
-    return status;
-  }
-  return Status(0, "OK");
-}
-
-std::vector<std::string> Query::getStoredQueryNames() {
-  return getStoredQueryNames(DBHandle::getInstance());
-}
-
-std::vector<std::string> Query::getStoredQueryNames(DBHandleRef db) {
-  std::vector<std::string> results;
-  db->Scan(kQueries, results);
-  return results;
-}
-
-bool Query::isQueryNameInDatabase() {
-  return isQueryNameInDatabase(DBHandle::getInstance());
-}
-
-bool Query::isQueryNameInDatabase(DBHandleRef db) {
-  auto names = Query::getStoredQueryNames(db);
-  return std::find(names.begin(), names.end(), name_) != names.end();
-}
-
-Status Query::addNewResults(const osquery::QueryData& qd) {
-  return addNewResults(qd, DBHandle::getInstance());
-}
-
-Status Query::addNewResults(const QueryData& qd, DBHandleRef db) {
-  DiffResults dr;
-  return addNewResults(qd, dr, false, db);
-}
-
-Status Query::addNewResults(const QueryData& qd, DiffResults& dr) {
-  return addNewResults(qd, dr, true, DBHandle::getInstance());
-}
-
-Status Query::addNewResults(const QueryData& current_qd,
-                            DiffResults& dr,
-                            bool calculate_diff,
-                            DBHandleRef db) {
-  // Get the rows from the last run of this query name.
-  QueryData previous_qd;
-  auto status = getPreviousQueryResults(previous_qd);
-  if (!status.ok()) {
-    return status;
-  }
-
-  // Sanitize all non-ASCII characters from the query data values.
-  QueryData escaped_current_qd;
-  escapeQueryData(current_qd, escaped_current_qd);
-  // Calculate the differential between previous and current query results.
-  if (calculate_diff) {
-    dr = diff(previous_qd, escaped_current_qd);
-  }
-
-  // Replace the "previous" query data with the current.
-  std::string json;
-  status = serializeQueryDataJSON(escaped_current_qd, json);
-  if (!status.ok()) {
-    return status;
-  }
-
-  status = db->Put(kQueries, name_, json);
-  if (!status.ok()) {
-    return status;
-  }
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/database/query.h b/osquery/database/query.h
deleted file mode 100644 (file)
index d1852ec..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <osquery/status.h>
-#include <osquery/database.h>
-
-#include "osquery/database/db_handle.h"
-
-namespace osquery {
-
-/// Error message used when a query name isn't found in the database
-extern const std::string kQueryNameNotFoundError;
-
-/**
- * @brief A class that is used to interact with the historical on-disk storage
- * for a given query.
- */
-class Query {
- public:
-  /**
-   * @brief Constructor which sets up necessary parameters of a Query object
-   *
-   * Given a query, this constructor calculates the value of columnFamily_,
-   * which can be accessed via the getColumnFamilyName getter method.
-   *
-   * @param q a SheduledQuery struct
-   */
-  explicit Query(const std::string& name, const ScheduledQuery& q)
-      : query_(q), name_(name) {}
-
-  /////////////////////////////////////////////////////////////////////////////
-  // Getters and setters
-  /////////////////////////////////////////////////////////////////////////////
-
-  /**
-   * @brief Getter for the name of a given scheduled query
-   *
-   * @return the name of the scheduled query which is being operated on
-   */
-  std::string getQueryName();
-
-  /**
-   * @brief Getter for the SQL query of a scheduled query
-   *
-   * @return the SQL of the scheduled query which is being operated on
-   */
-  std::string getQuery();
-
-  /**
-   * @brief Getter for the interval of a scheduled query
-   *
-   * @return the interval of the scheduled query which is being operated on
-   */
-  int getInterval();
-
-  /////////////////////////////////////////////////////////////////////////////
-  // Data access methods
-  /////////////////////////////////////////////////////////////////////////////
-
- public:
-  /**
-   * @brief Serialize the data in RocksDB into a useful data structure
-   *
-   * This method retrieves the data from RocksDB and returns the data in a
-   * HistoricalQueryResults struct.
-   *
-   * @param hQR the output HistoricalQueryResults struct
-   *
-   * @return the success or failure of the operation
-   */
-  // Status getHistoricalQueryResults(HistoricalQueryResults& hQR);
-  Status getPreviousQueryResults(QueryData& results);
-
- private:
-  /**
-   * @brief Serialize the data in RocksDB into a useful data structure using a
-   * custom database handle
-   *
-   * This method is the same as getHistoricalQueryResults, but with the
-   * addition of a parameter which allows you to pass a custom RocksDB
-   * database handle.
-   *
-   * @param hQR the output HistoricalQueryResults struct
-   * @param db a shared pointer to a custom DBHandle
-   *
-   * @return the success or failure of the operation
-   * @see getHistoricalQueryResults
-   */
-  // Status getHistoricalQueryResults(HistoricalQueryResults& hQR,
-  //                                 std::shared_ptr<DBHandle> db);
-  Status getPreviousQueryResults(QueryData& results, DBHandleRef db);
-
- public:
-  /**
-   * @brief Get the names of all historical queries that are stored in RocksDB
-   *
-   * If you'd like to perform some database maintenance, getStoredQueryNames()
-   * allows you to get a vector of the names of all queries which are
-   * currently stored in RocksDB
-   *
-   * @return a vector containing the string names of all scheduled queries
-   * which currently exist in the database
-   */
-  static std::vector<std::string> getStoredQueryNames();
-
- private:
-  /**
-   * @brief Get the names of all historical queries that are stored in RocksDB
-   * using a custom database handle
-   *
-   * This method is the same as getStoredQueryNames(), but with the addition
-   * of a parameter which allows you to pass a custom RocksDB database handle.
-   *
-   * @param db a custom RocksDB database handle
-   *
-   * @return a vector containing the string names of all scheduled queries
-   *
-   * @see getStoredQueryNames()
-   */
-  static std::vector<std::string> getStoredQueryNames(DBHandleRef db);
-
- public:
-  /**
-   * @brief Accessor method for checking if a given scheduled query exists in
-   * the database
-   *
-   * @return does the scheduled query which is already exists in the database
-   */
-  bool isQueryNameInDatabase();
-
- private:
-  /**
-   * @brief Accessor method for checking if a given scheduled query exists in
-   * the database, using a custom database handle
-   *
-   * This method is the same as isQueryNameInDatabase(), but with the addition
-   * of a parameter which allows you to pass a custom RocksDB database handle
-   *
-   * @param db a custom RocksDB database handle
-   *
-   * @return does the scheduled query which is already exists in the database
-   */
-  bool isQueryNameInDatabase(DBHandleRef db);
-
- public:
-  /**
-   * @brief Add a new set of results to the persistant storage
-   *
-   * Given the results of the execution of a scheduled query, add the results
-   * to the database using addNewResults
-   *
-   * @param qd the QueryData object, which has the results of the query which
-   * you would like to store
-   * @param unix_time the time that the query was executed
-   *
-   * @return an instance of osquery::Status indicating the success or failure
-   * of the operation
-   */
-  Status addNewResults(const QueryData& qd);
-
- private:
-  /**
-   * @brief Add a new set of results to the persistant storage using a custom
-   * database handle
-   *
-   * This method is the same as addNewResults(), but with the addition of a
-   * parameter which allows you to pass a custom RocksDB database handle
-   *
-   * @param qd the QueryData object, which has the results of the query which
-   * you would like to store
-   * @param unix_time the time that the query was executed
-   * @param db a custom RocksDB database handle
-   *
-   * @return an instance of osquery::Status indicating the success or failure
-   * of the operation
-   */
-  Status addNewResults(const QueryData& qd, DBHandleRef db);
-
- public:
-  /**
-   * @brief Add a new set of results to the persistent storage and get back
-   * the differential results.
-   *
-   * Given the results of an execution of a scheduled query, add the results
-   * to the database using addNewResults and get back a data structure
-   * indicating what rows in the query's results have changed.
-   *
-   * @param qd the QueryData object containing query results to store
-   * @param dr an output to a DiffResults object populated based on last run
-   *
-   * @return the success or failure of the operation
-   */
-  Status addNewResults(const QueryData& qd, DiffResults& dr);
-
- private:
-  /**
-   * @brief Add a new set of results to the persistent storage and get back
-   * the differential results, using a custom database handle.
-   *
-   * This method is the same as Query::addNewResults, but with the addition of a
-   * parameter which allows you to pass a custom RocksDB database handle
-   *
-   * @param qd the QueryData object containing query results to store
-   * @param dr an output to a DiffResults object populated based on last run
-   *
-   * @return the success or failure of the operation
-   */
-  Status addNewResults(const QueryData& qd,
-                       DiffResults& dr,
-                       bool calculate_diff,
-                       DBHandleRef db);
-
- public:
-  /**
-   * @brief A getter for the most recent result set for a scheduled query
-   *
-   * @param qd the output QueryData object
-   *
-   * @return the success or failure of the operation
-   */
-  Status getCurrentResults(QueryData& qd);
-
- private:
-  /**
-   * @brief A getter for the most recent result set for a scheduled query,
-   * but with the addition of a parameter which allows you to pass a custom
-   * RocksDB database handle.
-   *
-   * This method is the same as Query::getCurrentResults, but with addition of a
-   * parameter which allows you to pass a custom RocksDB database handle.
-   *
-   * @param qd the output QueryData object
-   * @param db a custom RocksDB database handle
-   *
-   * @return the success or failure of the operation
-   */
-  Status getCurrentResults(QueryData& qd, DBHandleRef db);
-
- private:
-  /////////////////////////////////////////////////////////////////////////////
-  // Private members
-  /////////////////////////////////////////////////////////////////////////////
-
-  /// The scheduled query and internal
-  ScheduledQuery query_;
-  /// The scheduled query name.
-  std::string name_;
-
- private:
-  /////////////////////////////////////////////////////////////////////////////
-  // Unit tests which can access private members
-  /////////////////////////////////////////////////////////////////////////////
-
-  FRIEND_TEST(QueryTests, test_private_members);
-  FRIEND_TEST(QueryTests, test_add_and_get_current_results);
-  FRIEND_TEST(QueryTests, test_is_query_name_in_database);
-  FRIEND_TEST(QueryTests, test_get_stored_query_names);
-  FRIEND_TEST(QueryTests, test_get_executions);
-  FRIEND_TEST(QueryTests, test_get_query_results);
-  FRIEND_TEST(QueryTests, test_query_name_not_found_in_db);
-};
-}
diff --git a/osquery/database/tests/db_handle_tests.cpp b/osquery/database/tests/db_handle_tests.cpp
deleted file mode 100644 (file)
index 8c3e07b..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <algorithm>
-
-#include <boost/filesystem/operations.hpp>
-
-#include <gtest/gtest.h>
-
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/database/db_handle.h"
-
-const std::string kTestingDBHandlePath = "/tmp/rocksdb-osquery-dbhandletests";
-
-namespace osquery {
-
-class DBHandleTests : public testing::Test {
- public:
-  void SetUp() {
-    // Setup a testing DB instance
-    db = DBHandle::getInstanceAtPath(kTestingDBHandlePath);
-    cfh_queries = DBHandle::getInstance()->getHandleForColumnFamily(kQueries);
-    cfh_foobar =
-        DBHandle::getInstance()->getHandleForColumnFamily("foobartest");
-  }
-
-  void TearDown() { boost::filesystem::remove_all(kTestingDBHandlePath); }
-
- public:
-  rocksdb::ColumnFamilyHandle* cfh_queries;
-  rocksdb::ColumnFamilyHandle* cfh_foobar;
-  std::shared_ptr<DBHandle> db;
-};
-
-TEST_F(DBHandleTests, test_singleton_on_disk) {
-  auto db1 = DBHandle::getInstance();
-  auto db2 = DBHandle::getInstance();
-  EXPECT_EQ(db1, db2);
-}
-
-TEST_F(DBHandleTests, test_get_handle_for_column_family) {
-  ASSERT_TRUE(cfh_queries != nullptr);
-  ASSERT_TRUE(cfh_foobar == nullptr);
-}
-
-TEST_F(DBHandleTests, test_get) {
-  db->getDB()->Put(
-      rocksdb::WriteOptions(), cfh_queries, "test_query_123", "{}");
-  std::string r;
-  std::string key = "test_query_123";
-  auto s = db->Get(kQueries, key, r);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(r, "{}");
-}
-
-TEST_F(DBHandleTests, test_put) {
-  auto s = db->Put(kQueries, "test_put", "bar");
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-}
-
-TEST_F(DBHandleTests, test_delete) {
-  db->Put(kQueries, "test_delete", "baz");
-  auto s = db->Delete(kQueries, "test_delete");
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-}
-
-TEST_F(DBHandleTests, test_scan) {
-  db->Put(kQueries, "test_scan_foo1", "baz");
-  db->Put(kQueries, "test_scan_foo2", "baz");
-  db->Put(kQueries, "test_scan_foo3", "baz");
-  std::vector<std::string> keys;
-  std::vector<std::string> expected = {
-      "test_scan_foo1", "test_scan_foo2", "test_scan_foo3"};
-  auto s = db->Scan(kQueries, keys);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  for (const auto& i : expected) {
-    EXPECT_NE(std::find(keys.begin(), keys.end(), i), keys.end());
-  }
-}
-}
diff --git a/osquery/database/tests/query_tests.cpp b/osquery/database/tests/query_tests.cpp
deleted file mode 100644 (file)
index 0142f27..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <algorithm>
-#include <ctime>
-#include <deque>
-
-#include <boost/filesystem/operations.hpp>
-
-#include <gtest/gtest.h>
-
-#include "osquery/core/test_util.h"
-#include "osquery/database/query.h"
-
-const std::string kTestingQueryDBPath = "/tmp/rocksdb-osquery-querytests";
-
-namespace osquery {
-
-class QueryTests : public testing::Test {
- public:
-  void SetUp() { db_ = DBHandle::getInstanceAtPath(kTestingQueryDBPath); }
-  void TearDown() { boost::filesystem::remove_all(kTestingQueryDBPath); }
-
- public:
-  std::shared_ptr<DBHandle> db_;
-};
-
-TEST_F(QueryTests, test_get_column_family_name) {
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("foobar", query);
-  EXPECT_EQ(cf.getQueryName(), "foobar");
-}
-
-TEST_F(QueryTests, test_get_query) {
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("foobar", query);
-  EXPECT_EQ(cf.getQuery(), query.query);
-}
-
-TEST_F(QueryTests, test_get_interval) {
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("foobar", query);
-  EXPECT_EQ(cf.getInterval(), query.interval);
-}
-
-TEST_F(QueryTests, test_private_members) {
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("foobar", query);
-  EXPECT_EQ(cf.query_, query);
-}
-
-TEST_F(QueryTests, test_add_and_get_current_results) {
-  // Test adding a "current" set of results to a scheduled query instance.
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("foobar", query);
-  auto status = cf.addNewResults(getTestDBExpectedResults(), db_);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(status.toString(), "OK");
-
-  // Simulate results from several schedule runs, calculate differentials.
-  for (auto result : getTestDBResultStream()) {
-    // Get the results from the previous query execution (from RocksDB).
-    QueryData previous_qd;
-    auto status = cf.getPreviousQueryResults(previous_qd, db_);
-    EXPECT_TRUE(status.ok());
-    EXPECT_EQ(status.toString(), "OK");
-
-    // Add the "current" results and output the differentials.
-    DiffResults dr;
-    auto s = cf.addNewResults(result.second, dr, true, db_);
-    EXPECT_TRUE(s.ok());
-
-    // Call the diffing utility directly.
-    DiffResults expected = diff(previous_qd, result.second);
-    EXPECT_EQ(dr, expected);
-
-    // After Query::addNewResults the previous results are now current.
-    QueryData qd;
-    cf.getPreviousQueryResults(qd, db_);
-    EXPECT_EQ(qd, result.second);
-  }
-}
-
-TEST_F(QueryTests, test_get_query_results) {
-  // Grab an expected set of query data and add it as the previous result.
-  auto encoded_qd = getSerializedQueryDataJSON();
-  auto query = getOsqueryScheduledQuery();
-  auto status = db_->Put(kQueries, "foobar", encoded_qd.first);
-  EXPECT_TRUE(status.ok());
-
-  // Use the Query retrieval API to check the now "previous" result.
-  QueryData previous_qd;
-  auto cf = Query("foobar", query);
-  status = cf.getPreviousQueryResults(previous_qd, db_);
-  EXPECT_TRUE(status.ok());
-}
-
-TEST_F(QueryTests, test_query_name_not_found_in_db) {
-  // Try to retrieve results from a query that has not executed.
-  QueryData previous_qd;
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("not_a_real_query", query);
-  auto status = cf.getPreviousQueryResults(previous_qd, db_);
-  EXPECT_TRUE(status.toString() == "Query name not found in database");
-  EXPECT_TRUE(status.ok());
-}
-
-TEST_F(QueryTests, test_is_query_name_in_database) {
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("foobar", query);
-  auto encoded_qd = getSerializedQueryDataJSON();
-  auto status = db_->Put(kQueries, "foobar", encoded_qd.first);
-  EXPECT_TRUE(status.ok());
-  // Now test that the query name exists.
-  EXPECT_TRUE(cf.isQueryNameInDatabase(db_));
-}
-
-TEST_F(QueryTests, test_get_stored_query_names) {
-  auto query = getOsqueryScheduledQuery();
-  auto cf = Query("foobar", query);
-  auto encoded_qd = getSerializedQueryDataJSON();
-  auto status = db_->Put(kQueries, "foobar", encoded_qd.first);
-  EXPECT_TRUE(status.ok());
-
-  // Stored query names is a factory method included alongside every query.
-  // It will include the set of query names with existing "previous" results.
-  auto names = cf.getStoredQueryNames(db_);
-  auto in_vector = std::find(names.begin(), names.end(), "foobar");
-  EXPECT_NE(in_vector, names.end());
-}
-}
diff --git a/osquery/database/tests/results_tests.cpp b/osquery/database/tests/results_tests.cpp
deleted file mode 100644 (file)
index 2c16d01..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <osquery/database.h>
-#include <osquery/logger.h>
-
-#include "osquery/core/test_util.h"
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-class ResultsTests : public testing::Test {};
-std::string escapeNonPrintableBytes(const std::string& data);
-
-TEST_F(ResultsTests, test_simple_diff) {
-  QueryData o;
-  QueryData n;
-
-  Row r1;
-  r1["foo"] = "bar";
-  n.push_back(r1);
-
-  auto results = diff(o, n);
-  EXPECT_EQ(results.added, n);
-  EXPECT_EQ(results.removed, o);
-}
-
-TEST_F(ResultsTests, test_serialize_row) {
-  auto results = getSerializedRow();
-  pt::ptree tree;
-  auto s = serializeRow(results.second, tree);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(results.first, tree);
-}
-
-TEST_F(ResultsTests, test_deserialize_row_json) {
-  auto results = getSerializedRow();
-  std::string input;
-  serializeRowJSON(results.second, input);
-
-  // Pull the serialized JSON back into a Row output container.
-  Row output;
-  auto s = deserializeRowJSON(input, output);
-  EXPECT_TRUE(s.ok());
-  // The output container should match the input row.
-  EXPECT_EQ(output, results.second);
-}
-
-TEST_F(ResultsTests, test_serialize_query_data) {
-  auto results = getSerializedQueryData();
-  pt::ptree tree;
-  auto s = serializeQueryData(results.second, tree);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(results.first, tree);
-}
-
-TEST_F(ResultsTests, test_serialize_query_data_json) {
-  auto results = getSerializedQueryDataJSON();
-  std::string json;
-  auto s = serializeQueryDataJSON(results.second, json);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(results.first, json);
-}
-
-TEST_F(ResultsTests, test_deserialize_query_data_json) {
-  auto results = getSerializedQueryDataJSON();
-
-  // Pull the serialized JSON back into a QueryData output container.
-  QueryData output;
-  auto s = deserializeQueryDataJSON(results.first, output);
-  EXPECT_TRUE(s.ok());
-  // The output container should match the input query data.
-  EXPECT_EQ(output, results.second);
-}
-
-TEST_F(ResultsTests, test_serialize_diff_results) {
-  auto results = getSerializedDiffResults();
-  pt::ptree tree;
-  auto s = serializeDiffResults(results.second, tree);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(results.first, tree);
-}
-
-TEST_F(ResultsTests, test_serialize_diff_results_json) {
-  auto results = getSerializedDiffResultsJSON();
-  std::string json;
-  auto s = serializeDiffResultsJSON(results.second, json);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(results.first, json);
-}
-
-TEST_F(ResultsTests, test_serialize_query_log_item) {
-  auto results = getSerializedQueryLogItem();
-  pt::ptree tree;
-  auto s = serializeQueryLogItem(results.second, tree);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(results.first, tree);
-}
-
-TEST_F(ResultsTests, test_serialize_query_log_item_json) {
-  auto results = getSerializedQueryLogItemJSON();
-  std::string json;
-  auto s = serializeQueryLogItemJSON(results.second, json);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(results.first, json);
-}
-
-TEST_F(ResultsTests, test_deserialize_query_log_item_json) {
-  auto results = getSerializedQueryLogItemJSON();
-
-  // Pull the serialized JSON back into a QueryLogItem output container.
-  QueryLogItem output;
-  auto s = deserializeQueryLogItemJSON(results.first, output);
-  EXPECT_TRUE(s.ok());
-  // The output container should match the input query data.
-  EXPECT_EQ(output, results.second);
-}
-
-TEST_F(ResultsTests, test_unicode_to_ascii_conversion) {
-  EXPECT_EQ(escapeNonPrintableBytes("しかたがない"),
-            "\\xE3\\x81\\x97\\xE3\\x81\\x8B\\xE3\\x81\\x9F\\xE3\\x81\\x8C\\xE3"
-            "\\x81\\xAA\\xE3\\x81\\x84");
-  EXPECT_EQ(escapeNonPrintableBytes("悪因悪果"),
-            "\\xE6\\x82\\xAA\\xE5\\x9B\\xA0\\xE6\\x82\\xAA\\xE6\\x9E\\x9C");
-  EXPECT_EQ(escapeNonPrintableBytes("モンスターハンター"),
-            "\\xE3\\x83\\xA2\\xE3\\x83\\xB3\\xE3\\x82\\xB9\\xE3\\x82\\xBF\\xE3"
-            "\\x83\\xBC\\xE3\\x83\\x8F\\xE3\\x83\\xB3\\xE3\\x82\\xBF\\xE3\\x83"
-            "\\xBC");
-  EXPECT_EQ(
-      escapeNonPrintableBytes(
-          "съешь же ещё этих мягких французских булок, да выпей чаю"),
-      "\\xD1\\x81\\xD1\\x8A\\xD0\\xB5\\xD1\\x88\\xD1\\x8C \\xD0\\xB6\\xD0\\xB5 "
-      "\\xD0\\xB5\\xD1\\x89\\xD1\\x91 \\xD1\\x8D\\xD1\\x82\\xD0\\xB8\\xD1\\x85 "
-      "\\xD0\\xBC\\xD1\\x8F\\xD0\\xB3\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
-      "\\xD1\\x84\\xD1\\x80\\xD0\\xB0\\xD0\\xBD\\xD1\\x86\\xD1\\x83\\xD0\\xB7\\"
-      "xD1\\x81\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
-      "\\xD0\\xB1\\xD1\\x83\\xD0\\xBB\\xD0\\xBE\\xD0\\xBA, "
-      "\\xD0\\xB4\\xD0\\xB0 \\xD0\\xB2\\xD1\\x8B\\xD0\\xBF\\xD0\\xB5\\xD0\\xB9 "
-      "\\xD1\\x87\\xD0\\xB0\\xD1\\x8E");
-
-  EXPECT_EQ(
-      escapeNonPrintableBytes("The quick brown fox jumps over the lazy dog."),
-      "The quick brown fox jumps over the lazy dog.");
-}
-
-TEST_F(ResultsTests, test_adding_duplicate_rows_to_query_data) {
-  Row r1, r2, r3;
-  r1["foo"] = "bar";
-  r1["baz"] = "boo";
-
-  r2["foo"] = "baz";
-  r2["baz"] = "bop";
-
-  r3["foo"] = "baz";
-  r3["baz"] = "bop";
-
-  QueryData q;
-  bool s;
-
-  s = addUniqueRowToQueryData(q, r1);
-  EXPECT_TRUE(s);
-  EXPECT_EQ(q.size(), 1);
-
-  s = addUniqueRowToQueryData(q, r2);
-  EXPECT_TRUE(s);
-  EXPECT_EQ(q.size(), 2);
-
-  s = addUniqueRowToQueryData(q, r3);
-  EXPECT_FALSE(s);
-  EXPECT_EQ(q.size(), 2);
-}
-}
diff --git a/osquery/devtools/CMakeLists.txt b/osquery/devtools/CMakeLists.txt
deleted file mode 100644 (file)
index 0d916dc..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_devtools printer.cpp)
-
-FILE(GLOB OSQUERY_DEVTOOLS_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_DEVTOOLS_TESTS})
diff --git a/osquery/devtools/devtools.h b/osquery/devtools/devtools.h
deleted file mode 100644 (file)
index 54917ee..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <string>
-
-#include <osquery/database.h>
-#include <osquery/flags.h>
-
-namespace osquery {
-
-/// Show all tables and exit the shell.
-DECLARE_bool(L);
-/// Select all from a table an exit the shell.
-DECLARE_string(A);
-/// The shell may need to disable events for fast operations.
-DECLARE_bool(disable_events);
-
-/**
- * @brief Run an interactive SQL query shell.
- *
- * @code{.cpp}
- *   // Copyright 2004-present Facebook. All Rights Reserved.
- *   #include <osquery/core.h>
- *   #include <osquery/devtools.h>
- *
- *   int main(int argc, char *argv[]) {
- *     osquery::initOsquery(argc, argv);
- *     return osquery::launchIntoShell(argc, argv);
- *   }
- * @endcode
- *
- * @param argc the number of elements in argv
- * @param argv the command-line flags
- *
- * @return an int which represents the "return code"
- */
-int launchIntoShell(int argc, char** argv);
-
-/**
- * @brief Pretty print a QueryData object
- *
- * This is a helper method which called osquery::beautify on the supplied
- * QueryData object and prints the results to stdout.
- *
- * @param results The QueryData object to print
- * @param columns The order of the keys (since maps are unordered)
- * @param lengths A mutable set of column lengths
- */
-void prettyPrint(const QueryData& results,
-                 const std::vector<std::string>& columns,
-                 std::map<std::string, size_t>& lengths);
-
-/**
- * @brief JSON print a QueryData object
- *
- * This is a helper method which allows a shell or other tool to print results
- * in a JSON format.
- *
- * @param q The QueryData object to print
- */
-void jsonPrint(const QueryData& q);
-
-/**
- * @brief Compute a map of metadata about the supplied QueryData object
- *
- * @param r A row to analyze
- * @param lengths A mutable set of column lengths
- * @param use_columns Calulate lengths of column names or values
- *
- * @return A map of string to int such that the key represents the "column" in
- * the supplied QueryData and the int represents the length of the longest key
- */
-void computeRowLengths(const Row& r,
-                       std::map<std::string, size_t>& lengths,
-                       bool use_columns = false);
-
-/**
- * @brief Generate the separator string for query results
- *
- * @param lengths The data returned from computeQueryDataLengths
- * @param columns The order of the keys (since maps are unordered)
- *
- * @return A string, with a newline, representing your separator
- */
-std::string generateToken(const std::map<std::string, size_t>& lengths,
-                          const std::vector<std::string>& columns);
-
-/**
- * @brief Generate the header string for query results
- *
- * @param lengths The data returned from computeQueryDataLengths
- * @param columns The order of the keys (since maps are unordered)
- *
- * @return A string, with a newline, representing your header
- */
-std::string generateHeader(const std::map<std::string, size_t>& lengths,
-                           const std::vector<std::string>& columns);
-
-/**
- * @brief Generate a row string for query results
- *
- * @param r A row to analyze
- * @param lengths The data returned from computeQueryDataLengths
- * @param columns The order of the keys (since maps are unordered)
- *
- * @return A string, with a newline, representing your row
- */
-std::string generateRow(const Row& r,
-                        const std::map<std::string, size_t>& lengths,
-                        const std::vector<std::string>& columns);
-}
diff --git a/osquery/devtools/printer.cpp b/osquery/devtools/printer.cpp
deleted file mode 100644 (file)
index 380d2ca..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <iostream>
-#include <sstream>
-
-#include <osquery/core.h>
-
-#include "osquery/devtools/devtools.h"
-
-namespace osquery {
-
-static std::vector<size_t> kOffset = {0, 0};
-static std::string kToken = "|";
-
-std::string generateToken(const std::map<std::string, size_t>& lengths,
-                          const std::vector<std::string>& columns) {
-  std::string out = "+";
-  for (const auto& col : columns) {
-    if (lengths.count(col) > 0) {
-      if (getenv("ENHANCE") != nullptr) {
-        std::string e = "\xF0\x9F\x90\x8C";
-        e[2] += kOffset[1];
-        e[3] += kOffset[0];
-        for (int i = 0; i < lengths.at(col) + 2; i++) {
-          e[3] = '\x8c' + kOffset[0]++;
-          if (e[3] == '\xbf') {
-            e[3] = '\x80';
-            kOffset[1] = (kOffset[1] > 3 && kOffset[1] < 8) ? 9 : kOffset[1];
-            e[2] = '\x90' + ++kOffset[1];
-            kOffset[0] = 0;
-          }
-          if (kOffset[1] == ('\x97' - '\x8d')) {
-            kOffset = {0, 0};
-          }
-          out += e.c_str();
-        }
-      } else {
-        out += std::string(lengths.at(col) + 2, '-');
-      }
-    }
-    out += "+";
-  }
-
-  out += "\n";
-  return out;
-}
-
-std::string generateHeader(const std::map<std::string, size_t>& lengths,
-                           const std::vector<std::string>& columns) {
-  if (getenv("ENHANCE") != nullptr) {
-    kToken = "\xF0\x9F\x91\x8D";
-  }
-  std::string out = kToken;
-  for (const auto& col : columns) {
-    out += " " + col;
-    if (lengths.count(col) > 0) {
-      int buffer_size = lengths.at(col) - utf8StringSize(col) + 1;
-      if (buffer_size > 0) {
-        out += std::string(buffer_size, ' ');
-      } else {
-        out += ' ';
-      }
-    }
-    out += kToken;
-  }
-  out += "\n";
-  return out;
-}
-
-std::string generateRow(const Row& r,
-                        const std::map<std::string, size_t>& lengths,
-                        const std::vector<std::string>& order) {
-  std::string out;
-  for (const auto& column : order) {
-    if (r.count(column) == 0 || lengths.count(column) == 0) {
-      continue;
-    }
-    // Print a terminator for the previous value or lhs, followed by spaces.
-
-    int buffer_size = lengths.at(column) - utf8StringSize(r.at(column)) + 1;
-    if (buffer_size > 0) {
-      out += kToken + " " + r.at(column) + std::string(buffer_size, ' ');
-    }
-  }
-
-  if (out.size() > 0) {
-    // Only append if a row was added.
-    out += kToken + "\n";
-  }
-
-  return out;
-}
-
-void prettyPrint(const QueryData& results,
-                 const std::vector<std::string>& columns,
-                 std::map<std::string, size_t>& lengths) {
-  if (results.size() == 0) {
-    return;
-  }
-
-  // Call a final compute using the column names as minimum lengths.
-  computeRowLengths(results.front(), lengths, true);
-
-  // Output a nice header wrapping the column names.
-  auto separator = generateToken(lengths, columns);
-  auto header = separator + generateHeader(lengths, columns) + separator;
-  printf("%s", header.c_str());
-
-  // Iterate each row and pretty print.
-  for (const auto& row : results) {
-    printf("%s", generateRow(row, lengths, columns).c_str());
-  }
-  printf("%s", separator.c_str());
-}
-
-void jsonPrint(const QueryData& q) {
-  printf("[\n");
-  for (int i = 0; i < q.size(); ++i) {
-    std::string row_string;
-    if (serializeRowJSON(q[i], row_string).ok()) {
-      row_string.pop_back();
-      printf("  %s", row_string.c_str());
-      if (i < q.size() - 1) {
-        printf(",\n");
-      }
-    }
-  }
-  printf("\n]\n");
-}
-
-void computeRowLengths(const Row& r,
-                       std::map<std::string, size_t>& lengths,
-                       bool use_columns) {
-  for (const auto& col : r) {
-    size_t current = (lengths.count(col.first) > 0) ? lengths.at(col.first) : 0;
-    size_t size =
-        (use_columns) ? utf8StringSize(col.first) : utf8StringSize(col.second);
-    lengths[col.first] = (size > current) ? size : current;
-  }
-}
-}
diff --git a/osquery/devtools/shell.cpp b/osquery/devtools/shell.cpp
deleted file mode 100644 (file)
index 9b88fe0..0000000
+++ /dev/null
@@ -1,1553 +0,0 @@
-/*
-** 2001 September 15
-**
-** The author disclaims copyright to this source code.  In place of
-** a legal notice, here is a blessing:
-**
-**    May you do good and not evil.
-**    May you find forgiveness for yourself and forgive others.
-**    May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This file contains code to implement the "sqlite" command line
-** utility for accessing SQLite databases.
-*/
-
-#include <signal.h>
-#include <stdio.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-#include <iostream>
-
-#include <readline/readline.h>
-#include <readline/history.h>
-
-#include <sqlite3.h>
-
-#include <boost/algorithm/string/predicate.hpp>
-
-#include <osquery/database.h>
-#include <osquery/filesystem.h>
-#include <osquery/flags.h>
-
-#include "osquery/devtools/devtools.h"
-#include "osquery/sql/virtual_table.h"
-
-namespace osquery {
-
-/// Define flags used by the shell. They are parsed by the drop-in shell.
-SHELL_FLAG(bool, csv, false, "Set output mode to 'csv'");
-SHELL_FLAG(bool, json, false, "Set output mode to 'json'");
-SHELL_FLAG(bool, line, false, "Set output mode to 'line'");
-SHELL_FLAG(bool, list, false, "Set output mode to 'list'");
-SHELL_FLAG(string, nullvalue, "", "Set string for NULL values, default ''");
-SHELL_FLAG(string, separator, "|", "Set output field separator, default '|'");
-
-/// Define short-hand shell switches.
-SHELL_FLAG(bool, L, false, "List all table names");
-SHELL_FLAG(string, A, "", "Select all from a table");
-}
-
-/*
-** Text of a help message
-*/
-static char zHelp[] =
-    "Welcome to the osquery shell. Please explore your OS!\n"
-    "You are connected to a transient 'in-memory' virtual database.\n"
-    "\n"
-    ".all [TABLE]       Select all from a table\n"
-    ".bail ON|OFF       Stop after hitting an error; default OFF\n"
-    ".echo ON|OFF       Turn command echo on or off\n"
-    ".exit              Exit this program\n"
-    ".header(s) ON|OFF  Turn display of headers on or off\n"
-    ".help              Show this message\n"
-    ".mode MODE         Set output mode where MODE is one of:\n"
-    "                     csv      Comma-separated values\n"
-    "                     column   Left-aligned columns.  (See .width)\n"
-    "                     line     One value per line\n"
-    "                     list     Values delimited by .separator string\n"
-    "                     pretty   Pretty printed SQL results\n"
-    ".nullvalue STR     Use STRING in place of NULL values\n"
-    ".print STR...      Print literal STRING\n"
-    ".quit              Exit this program\n"
-    ".schema [TABLE]    Show the CREATE statements\n"
-    ".separator STR     Change separator used by output mode and .import\n"
-    ".show              Show the current values for various settings\n"
-    ".tables [TABLE]    List names of tables\n"
-    ".trace FILE|off    Output each SQL statement as it is run\n"
-    ".width [NUM1]+     Set column widths for \"column\" mode\n";
-
-static char zTimerHelp[] =
-    ".timer ON|OFF      Turn the CPU timer measurement on or off\n";
-
-/*
-** These are the allowed modes.
-*/
-#define MODE_Line 0 /* One column per line.  Blank line between records */
-#define MODE_Column 1 /* One record per line in neat columns */
-#define MODE_List 2 /* One record per line with a separator */
-#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
-#define MODE_Csv 4 /* Quote strings, numbers are plain */
-#define MODE_Pretty 5 /* Pretty print the SQL results */
-
-static const char *modeDescr[] = {
-    "line", "column", "list", "semi", "csv", "pretty",
-};
-
-/* Make sure isatty() has a prototype.
-*/
-extern int isatty(int);
-
-/* ctype macros that work with signed characters */
-#define IsSpace(X) isspace((unsigned char)X)
-#define IsDigit(X) isdigit((unsigned char)X)
-#define ToLower(X) (char) tolower((unsigned char)X)
-
-/* True if the timer is enabled */
-static int enableTimer = 0;
-
-/* Return the current wall-clock time */
-static sqlite3_int64 timeOfDay(void) {
-  static sqlite3_vfs *clockVfs = 0;
-  sqlite3_int64 t;
-  if (clockVfs == 0)
-    clockVfs = sqlite3_vfs_find(0);
-  if (clockVfs->iVersion >= 1 && clockVfs->xCurrentTimeInt64 != 0) {
-    clockVfs->xCurrentTimeInt64(clockVfs, &t);
-  } else {
-    double r;
-    clockVfs->xCurrentTime(clockVfs, &r);
-    t = (sqlite3_int64)(r * 86400000.0);
-  }
-  return t;
-}
-
-/* Saved resource information for the beginning of an operation */
-static struct rusage sBegin; /* CPU time at start */
-static sqlite3_int64 iBegin; /* Wall-clock time at start */
-
-/*
-** Begin timing an operation
-*/
-static void beginTimer(void) {
-  if (enableTimer) {
-    getrusage(RUSAGE_SELF, &sBegin);
-    iBegin = timeOfDay();
-  }
-}
-
-/* Return the difference of two time_structs in seconds */
-static double timeDiff(struct timeval *pStart, struct timeval *pEnd) {
-  return (pEnd->tv_usec - pStart->tv_usec) * 0.000001 +
-         (double)(pEnd->tv_sec - pStart->tv_sec);
-}
-
-/*
-** Print the timing results.
-*/
-static void endTimer(void) {
-  if (enableTimer) {
-    struct rusage sEnd;
-    sqlite3_int64 iEnd = timeOfDay();
-    getrusage(RUSAGE_SELF, &sEnd);
-    printf("Run Time: real %.3f user %f sys %f\n",
-           (iEnd - iBegin) * 0.001,
-           timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
-           timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
-  }
-}
-
-#define BEGIN_TIMER beginTimer()
-#define END_TIMER endTimer()
-#define HAS_TIMER 1
-
-/*
-** Used to prevent warnings about unused parameters
-*/
-#define UNUSED_PARAMETER(x) (void)(x)
-
-/*
-** If the following flag is set, then command execution stops
-** at an error if we are not interactive.
-*/
-static int bail_on_error = 0;
-
-/*
-** Threat stdin as an interactive input if the following variable
-** is true.  Otherwise, assume stdin is connected to a file or pipe.
-*/
-static int stdin_is_interactive = 1;
-
-/*
-** True if an interrupt (Control-C) has been received.
-*/
-static volatile int seenInterrupt = 0;
-
-/*
-** This is the name of our program. It is set in main(), used
-** in a number of other places, mostly for error messages.
-*/
-static char *Argv0;
-
-/*
-** Prompt strings. Initialized in main. Settable with
-**   .prompt main continue
-*/
-static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
-static char continuePrompt[20]; /* Continuation prompt. default: "   ...> " */
-
-/*
-** A global char* and an SQL function to access its current value
-** from within an SQL statement. This program used to use the
-** sqlite_exec_printf() API to substitue a string into an SQL statement.
-** The correct way to do this with sqlite3 is to use the bind API, but
-** since the shell is built around the callback paradigm it would be a lot
-** of work. Instead just use this hack, which is quite harmless.
-*/
-static const char *zShellStatic = 0;
-static void shellstaticFunc(sqlite3_context *context,
-                            int argc,
-                            sqlite3_value **argv) {
-  assert(0 == argc);
-  assert(zShellStatic);
-  UNUSED_PARAMETER(argc);
-  UNUSED_PARAMETER(argv);
-  sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
-}
-
-/*
-** This routine reads a line of text from FILE in, stores
-** the text in memory obtained from malloc() and returns a pointer
-** to the text.  NULL is returned at end of file, or if malloc()
-** fails.
-**
-** If zLine is not NULL then it is a malloced buffer returned from
-** a previous call to this routine that may be reused.
-*/
-static char *local_getline(char *zLine, FILE *in) {
-  int nLine = zLine == 0 ? 0 : 100;
-  int n = 0;
-
-  while (1) {
-    if (n + 100 > nLine) {
-      nLine = nLine * 2 + 100;
-      zLine = (char *)realloc(zLine, nLine);
-      if (zLine == 0)
-        return 0;
-    }
-    if (fgets(&zLine[n], nLine - n, in) == 0) {
-      if (n == 0) {
-        free(zLine);
-        return 0;
-      }
-      zLine[n] = 0;
-      break;
-    }
-    while (zLine[n])
-      n++;
-    if (n > 0 && zLine[n - 1] == '\n') {
-      n--;
-      if (n > 0 && zLine[n - 1] == '\r')
-        n--;
-      zLine[n] = 0;
-      break;
-    }
-  }
-  return zLine;
-}
-
-/*
-** Retrieve a single line of input text.
-**
-** If in==0 then read from standard input and prompt before each line.
-** If isContinuation is true, then a continuation prompt is appropriate.
-** If isContinuation is zero, then the main prompt should be used.
-**
-** If zPrior is not NULL then it is a buffer from a prior call to this
-** routine that can be reused.
-**
-** The result is stored in space obtained from malloc() and must either
-** be freed by the caller or else passed back into this routine via the
-** zPrior argument for reuse.
-*/
-static char *one_input_line(FILE *in, char *zPrior, int isContinuation) {
-  char *zPrompt;
-  char *zResult;
-  if (in != 0) {
-    zResult = local_getline(zPrior, in);
-  } else {
-    zPrompt = isContinuation ? continuePrompt : mainPrompt;
-    free(zPrior);
-    zResult = readline(zPrompt);
-    if (zResult && *zResult)
-      add_history(zResult);
-  }
-  return zResult;
-}
-
-struct previous_mode_data {
-  int valid; /* Is there legit data in here? */
-  int mode;
-  int showHeader;
-  int colWidth[100];
-};
-
-/*
-** Pretty print structure
- */
-struct prettyprint_data {
-  osquery::QueryData results;
-  std::vector<std::string> columns;
-  std::map<std::string, size_t> lengths;
-};
-
-/*
-** An pointer to an instance of this structure is passed from
-** the main program to the callback.  This is used to communicate
-** state and mode information.
-*/
-struct callback_data {
-  int echoOn; /* True to echo input commands */
-  int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL statement */
-  int cnt; /* Number of records displayed so far */
-  FILE *out; /* Write results here */
-  FILE *traceOut; /* Output for sqlite3_trace() */
-  int nErr; /* Number of errors seen */
-  int mode; /* An output mode setting */
-  int writableSchema; /* True if PRAGMA writable_schema=ON */
-  int showHeader; /* True to show column names in List or Column mode */
-  char *zDestTable; /* Name of destination table when MODE_Insert */
-  char separator[20]; /* Separator character for MODE_List */
-  int colWidth[100]; /* Requested width of each column when in column mode*/
-  int actualWidth[100]; /* Actual width of each column */
-  char nullvalue[20]; /* The text to print when a NULL comes back from
-                      ** the database */
-  struct previous_mode_data explainPrev;
-  /* Holds the mode information just before
-  ** .explain ON */
-  char outfile[FILENAME_MAX]; /* Filename for *out */
-  const char *zDbFilename; /* name of the database file */
-  char *zFreeOnClose; /* Filename to free when closing */
-  const char *zVfs; /* Name of VFS to use */
-  sqlite3_stmt *pStmt; /* Current statement if any. */
-  FILE *pLog; /* Write log output here */
-  int *aiIndent; /* Array of indents used in MODE_Explain */
-  int nIndent; /* Size of array aiIndent[] */
-  int iIndent; /* Index of current op in aiIndent[] */
-
-  /* Additional attributes to be used in pretty mode */
-  struct prettyprint_data *prettyPrint;
-};
-
-/*
-** Number of elements in an array
-*/
-#define ArraySize(X) (int)(sizeof(X) / sizeof(X[0]))
-
-/*
-** Compute a string length that is limited to what can be stored in
-** lower 30 bits of a 32-bit signed integer.
-*/
-static int strlen30(const char *z) {
-  const char *z2 = z;
-  while (*z2) {
-    z2++;
-  }
-  return 0x3fffffff & (int)(z2 - z);
-}
-
-/*
-** A callback for the sqlite3_log() interface.
-*/
-static void shellLog(void *pArg, int iErrCode, const char *zMsg) {
-  struct callback_data *p = (struct callback_data *)pArg;
-  if (p->pLog == 0)
-    return;
-  fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
-  fflush(p->pLog);
-}
-
-/*
-** Output the given string as a quoted according to C or TCL quoting rules.
-*/
-static void output_c_string(FILE *out, const char *z) {
-  unsigned int c;
-  fputc('"', out);
-  while ((c = *(z++)) != 0) {
-    if (c == '\\') {
-      fputc(c, out);
-      fputc(c, out);
-    } else if (c == '"') {
-      fputc('\\', out);
-      fputc('"', out);
-    } else if (c == '\t') {
-      fputc('\\', out);
-      fputc('t', out);
-    } else if (c == '\n') {
-      fputc('\\', out);
-      fputc('n', out);
-    } else if (c == '\r') {
-      fputc('\\', out);
-      fputc('r', out);
-    } else if (!isprint(c & 0xff)) {
-      fprintf(out, "\\%03o", c & 0xff);
-    } else {
-      fputc(c, out);
-    }
-  }
-  fputc('"', out);
-}
-
-/*
-** If a field contains any character identified by a 1 in the following
-** array, then the string must be quoted for CSV.
-*/
-// clang-format off
-static const char needCsvQuote[] = {
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-  1, 1, 1, 1, 1, 1, 
-};
-// clang-format on
-
-/*
-** Output a single term of CSV.  Actually, p->separator is used for
-** the separator, which may or may not be a comma.  p->nullvalue is
-** the null value.  Strings are quoted if necessary.
-*/
-static void output_csv(struct callback_data *p, const char *z, int bSep) {
-  FILE *out = p->out;
-  if (z == 0) {
-    fprintf(out, "%s", p->nullvalue);
-  } else {
-    int i;
-    int nSep = strlen30(p->separator);
-    for (i = 0; z[i]; i++) {
-      if (needCsvQuote[((unsigned char *)z)[i]] ||
-          (z[i] == p->separator[0] &&
-           (nSep == 1 || memcmp(z, p->separator, nSep) == 0))) {
-        i = 0;
-        break;
-      }
-    }
-    if (i == 0) {
-      putc('"', out);
-      for (i = 0; z[i]; i++) {
-        if (z[i] == '"')
-          putc('"', out);
-        putc(z[i], out);
-      }
-      putc('"', out);
-    } else {
-      fprintf(out, "%s", z);
-    }
-  }
-  if (bSep) {
-    fprintf(p->out, "%s", p->separator);
-  }
-}
-
-#ifdef SIGINT
-/*
-** This routine runs when the user presses Ctrl-C
-*/
-static void interrupt_handler(int NotUsed) {
-  UNUSED_PARAMETER(NotUsed);
-  seenInterrupt = 1;
-}
-#endif
-
-/*
-** This is the callback routine that the shell
-** invokes for each row of a query result.
-*/
-static int shell_callback(
-    void *pArg, int nArg, char **azArg, char **azCol, int *aiType) {
-  int i;
-  struct callback_data *p = (struct callback_data *)pArg;
-
-  switch (p->mode) {
-  case MODE_Pretty: {
-    if (p->prettyPrint->columns.size() == 0) {
-      for (i = 0; i < nArg; i++) {
-        p->prettyPrint->columns.push_back(std::string(azCol[i]));
-      }
-    }
-
-    osquery::Row r;
-    for (int i = 0; i < nArg; ++i) {
-      if (azCol[i] != nullptr && azArg[i] != nullptr) {
-        r[std::string(azCol[i])] = std::string(azArg[i]);
-      }
-    }
-    osquery::computeRowLengths(r, p->prettyPrint->lengths);
-    p->prettyPrint->results.push_back(r);
-    break;
-  }
-  case MODE_Line: {
-    int w = 5;
-    if (azArg == 0)
-      break;
-    for (i = 0; i < nArg; i++) {
-      int len = strlen30(azCol[i] ? azCol[i] : "");
-      if (len > w)
-        w = len;
-    }
-    if (p->cnt++ > 0)
-      fprintf(p->out, "\n");
-    for (i = 0; i < nArg; i++) {
-      fprintf(p->out,
-              "%*s = %s\n",
-              w,
-              azCol[i],
-              azArg[i] ? azArg[i] : p->nullvalue);
-    }
-    break;
-  }
-  case MODE_Column: {
-    if (p->cnt++ == 0) {
-      for (i = 0; i < nArg; i++) {
-        int w, n;
-        if (i < ArraySize(p->colWidth)) {
-          w = p->colWidth[i];
-        } else {
-          w = 0;
-        }
-        if (w == 0) {
-          w = strlen30(azCol[i] ? azCol[i] : "");
-          if (w < 10)
-            w = 10;
-          n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue);
-          if (w < n)
-            w = n;
-        }
-        if (i < ArraySize(p->actualWidth)) {
-          p->actualWidth[i] = w;
-        }
-        if (p->showHeader) {
-          if (w < 0) {
-            fprintf(p->out,
-                    "%*.*s%s",
-                    -w,
-                    -w,
-                    azCol[i],
-                    i == nArg - 1 ? "\n" : "  ");
-          } else {
-            fprintf(p->out,
-                    "%-*.*s%s",
-                    w,
-                    w,
-                    azCol[i],
-                    i == nArg - 1 ? "\n" : "  ");
-          }
-        }
-      }
-      if (p->showHeader) {
-        for (i = 0; i < nArg; i++) {
-          int w;
-          if (i < ArraySize(p->actualWidth)) {
-            w = p->actualWidth[i];
-            if (w < 0)
-              w = -w;
-          } else {
-            w = 10;
-          }
-          fprintf(p->out,
-                  "%-*.*s%s",
-                  w,
-                  w,
-                  "-----------------------------------"
-                  "----------------------------------------------------------",
-                  i == nArg - 1 ? "\n" : "  ");
-        }
-      }
-    }
-    if (azArg == 0)
-      break;
-    for (i = 0; i < nArg; i++) {
-      int w;
-      if (i < ArraySize(p->actualWidth)) {
-        w = p->actualWidth[i];
-      } else {
-        w = 10;
-      }
-      if (i == 1 && p->aiIndent && p->pStmt) {
-        if (p->iIndent < p->nIndent) {
-          fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
-        }
-        p->iIndent++;
-      }
-      if (w < 0) {
-        fprintf(p->out,
-                "%*.*s%s",
-                -w,
-                -w,
-                azArg[i] ? azArg[i] : p->nullvalue,
-                i == nArg - 1 ? "\n" : "  ");
-      } else {
-        fprintf(p->out,
-                "%-*.*s%s",
-                w,
-                w,
-                azArg[i] ? azArg[i] : p->nullvalue,
-                i == nArg - 1 ? "\n" : "  ");
-      }
-    }
-    break;
-  }
-  case MODE_Semi:
-  case MODE_List: {
-    if (p->cnt++ == 0 && p->showHeader) {
-      for (i = 0; i < nArg; i++) {
-        fprintf(p->out, "%s%s", azCol[i], i == nArg - 1 ? "\n" : p->separator);
-      }
-    }
-    if (azArg == 0)
-      break;
-    for (i = 0; i < nArg; i++) {
-      char *z = azArg[i];
-      if (z == 0)
-        z = p->nullvalue;
-      fprintf(p->out, "%s", z);
-      if (i < nArg - 1) {
-        fprintf(p->out, "%s", p->separator);
-      } else if (p->mode == MODE_Semi) {
-        fprintf(p->out, ";\n");
-      } else {
-        fprintf(p->out, "\n");
-      }
-    }
-    break;
-  }
-  case MODE_Csv: {
-    if (p->cnt++ == 0 && p->showHeader) {
-      for (i = 0; i < nArg; i++) {
-        output_csv(p, azCol[i] ? azCol[i] : "", i < nArg - 1);
-      }
-      fprintf(p->out, "\n");
-    }
-    if (azArg == 0)
-      break;
-    for (i = 0; i < nArg; i++) {
-      output_csv(p, azArg[i], i < nArg - 1);
-    }
-    fprintf(p->out, "\n");
-    break;
-  }
-  }
-  return 0;
-}
-
-/*
-** Set the destination table field of the callback_data structure to
-** the name of the table given.  Escape any quote characters in the
-** table name.
-*/
-static void set_table_name(struct callback_data *p, const char *zName) {
-  int i, n;
-  int needQuote;
-  char *z;
-
-  if (p->zDestTable) {
-    free(p->zDestTable);
-    p->zDestTable = 0;
-  }
-  if (zName == 0)
-    return;
-  needQuote = !isalpha((unsigned char)*zName) && *zName != '_';
-  for (i = n = 0; zName[i]; i++, n++) {
-    if (!isalnum((unsigned char)zName[i]) && zName[i] != '_') {
-      needQuote = 1;
-      if (zName[i] == '\'')
-        n++;
-    }
-  }
-  if (needQuote)
-    n += 2;
-  z = p->zDestTable = (char *)malloc(n + 1);
-  if (z == 0) {
-    fprintf(stderr, "Error: out of memory\n");
-    exit(1);
-  }
-  n = 0;
-  if (needQuote)
-    z[n++] = '\'';
-  for (i = 0; zName[i]; i++) {
-    z[n++] = zName[i];
-    if (zName[i] == '\'')
-      z[n++] = '\'';
-  }
-  if (needQuote)
-    z[n++] = '\'';
-  z[n] = 0;
-}
-
-/*
-** Allocate space and save off current error string.
-*/
-static char *save_err_msg(sqlite3 *db /* Database to query */
-                          ) {
-  int nErrMsg = 1 + strlen30(sqlite3_errmsg(db));
-  char *zErrMsg = (char *)sqlite3_malloc(nErrMsg);
-  if (zErrMsg) {
-    memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
-  }
-  return zErrMsg;
-}
-
-/*
-** Execute a statement or set of statements.  Print
-** any result rows/columns depending on the current mode
-** set via the supplied callback.
-**
-** This is very similar to SQLite's built-in sqlite3_exec()
-** function except it takes a slightly different callback
-** and callback data argument.
-*/
-static int shell_exec(
-    const char *zSql, /* SQL to be evaluated */
-    int (*xCallback)(
-        void *, int, char **, char **, int *), /* Callback function */
-    /* (not the same as sqlite3_exec) */
-    struct callback_data *pArg, /* Pointer to struct callback_data */
-    char **pzErrMsg /* Error msg written here */
-    ) {
-  // Grab a lock on the managed DB instance.
-  auto dbc = osquery::SQLiteDBManager::get();
-  auto db = dbc.db();
-
-  sqlite3_stmt *pStmt = nullptr; /* Statement to execute. */
-  int rc = SQLITE_OK; /* Return Code */
-  int rc2;
-  const char *zLeftover; /* Tail of unprocessed SQL */
-
-  if (pzErrMsg) {
-    *pzErrMsg = nullptr;
-  }
-
-  while (zSql[0] && (SQLITE_OK == rc)) {
-    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
-    if (SQLITE_OK != rc) {
-      if (pzErrMsg) {
-        *pzErrMsg = save_err_msg(db);
-      }
-    } else {
-      if (!pStmt) {
-        /* this happens for a comment or white-space */
-        zSql = zLeftover;
-        while (IsSpace(zSql[0]))
-          zSql++;
-        continue;
-      }
-
-      /* save off the prepared statment handle and reset row count */
-      if (pArg) {
-        pArg->pStmt = pStmt;
-        pArg->cnt = 0;
-      }
-
-      /* echo the sql statement if echo on */
-      if (pArg && pArg->echoOn) {
-        const char *zStmtSql = sqlite3_sql(pStmt);
-        fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
-      }
-
-      /* perform the first step.  this will tell us if we
-      ** have a result set or not and how wide it is.
-      */
-      rc = sqlite3_step(pStmt);
-      /* if we have a result set... */
-      if (SQLITE_ROW == rc) {
-        /* if we have a callback... */
-        if (xCallback) {
-          /* allocate space for col name ptr, value ptr, and type */
-          int nCol = sqlite3_column_count(pStmt);
-          void *pData = sqlite3_malloc(3 * nCol * sizeof(const char *) + 1);
-          if (!pData) {
-            rc = SQLITE_NOMEM;
-          } else {
-            char **azCols = (char **)pData; /* Names of result columns */
-            char **azVals = &azCols[nCol]; /* Results */
-            int *aiTypes = (int *)&azVals[nCol]; /* Result types */
-            int i, x;
-            assert(sizeof(int) <= sizeof(char *));
-            /* save off ptrs to column names */
-            for (i = 0; i < nCol; i++) {
-              azCols[i] = (char *)sqlite3_column_name(pStmt, i);
-            }
-            do {
-              /* extract the data and data types */
-              for (i = 0; i < nCol; i++) {
-                aiTypes[i] = x = sqlite3_column_type(pStmt, i);
-                azVals[i] = (char *)sqlite3_column_text(pStmt, i);
-                if (!azVals[i] && (aiTypes[i] != SQLITE_NULL)) {
-                  rc = SQLITE_NOMEM;
-                  break; /* from for */
-                }
-              } /* end for */
-
-              /* if data and types extracted successfully... */
-              if (SQLITE_ROW == rc) {
-                /* call the supplied callback with the result row data */
-                if (xCallback(pArg, nCol, azVals, azCols, aiTypes)) {
-                  rc = SQLITE_ABORT;
-                } else {
-                  rc = sqlite3_step(pStmt);
-                }
-              }
-            } while (SQLITE_ROW == rc);
-            sqlite3_free(pData);
-          }
-        } else {
-          do {
-            rc = sqlite3_step(pStmt);
-          } while (rc == SQLITE_ROW);
-        }
-      }
-
-      /* Finalize the statement just executed. If this fails, save a
-      ** copy of the error message. Otherwise, set zSql to point to the
-      ** next statement to execute. */
-      rc2 = sqlite3_finalize(pStmt);
-      if (rc != SQLITE_NOMEM)
-        rc = rc2;
-      if (rc == SQLITE_OK) {
-        zSql = zLeftover;
-        while (IsSpace(zSql[0]))
-          zSql++;
-      } else if (pzErrMsg) {
-        *pzErrMsg = save_err_msg(db);
-      }
-
-      /* clear saved stmt handle */
-      if (pArg) {
-        pArg->pStmt = nullptr;
-      }
-    }
-  } /* end while */
-
-  if (pArg && pArg->mode == MODE_Pretty) {
-    if (osquery::FLAGS_json) {
-      osquery::jsonPrint(pArg->prettyPrint->results);
-    } else {
-      osquery::prettyPrint(pArg->prettyPrint->results,
-                           pArg->prettyPrint->columns,
-                           pArg->prettyPrint->lengths);
-    }
-    pArg->prettyPrint->results.clear();
-    pArg->prettyPrint->columns.clear();
-    pArg->prettyPrint->lengths.clear();
-  }
-
-  return rc;
-}
-
-/* Forward reference */
-static int process_input(struct callback_data *p, FILE *in);
-
-/*
-** Do C-language style dequoting.
-**
-**    \t    -> tab
-**    \n    -> newline
-**    \r    -> carriage return
-**    \"    -> "
-**    \NNN  -> ascii character NNN in octal
-**    \\    -> backslash
-*/
-static void resolve_backslashes(char *z) {
-  int i, j;
-  char c;
-  for (i = j = 0; (c = z[i]) != 0; i++, j++) {
-    if (c == '\\') {
-      c = z[++i];
-      if (c == 'n') {
-        c = '\n';
-      } else if (c == 't') {
-        c = '\t';
-      } else if (c == 'r') {
-        c = '\r';
-      } else if (c == '\\') {
-        c = '\\';
-      } else if (c >= '0' && c <= '7') {
-        c -= '0';
-        if (z[i + 1] >= '0' && z[i + 1] <= '7') {
-          i++;
-          c = (c << 3) + z[i] - '0';
-          if (z[i + 1] >= '0' && z[i + 1] <= '7') {
-            i++;
-            c = (c << 3) + z[i] - '0';
-          }
-        }
-      }
-    }
-    z[j] = c;
-  }
-  z[j] = 0;
-}
-
-/*
-** Return the value of a hexadecimal digit.  Return -1 if the input
-** is not a hex digit.
-*/
-static int hexDigitValue(char c) {
-  if (c >= '0' && c <= '9')
-    return c - '0';
-  if (c >= 'a' && c <= 'f')
-    return c - 'a' + 10;
-  if (c >= 'A' && c <= 'F')
-    return c - 'A' + 10;
-  return -1;
-}
-
-/*
-** Interpret zArg as an integer value, possibly with suffixes.
-*/
-static sqlite3_int64 integerValue(const char *zArg) {
-  sqlite3_int64 v = 0;
-  static const struct {
-    char *zSuffix;
-    int iMult;
-  } aMult[] = {
-        {(char *)"KiB", 1024},
-        {(char *)"MiB", 1024 * 1024},
-        {(char *)"GiB", 1024 * 1024 * 1024},
-        {(char *)"KB", 1000},
-        {(char *)"MB", 1000000},
-        {(char *)"GB", 1000000000},
-        {(char *)"K", 1000},
-        {(char *)"M", 1000000},
-        {(char *)"G", 1000000000},
-    };
-  int i;
-  int isNeg = 0;
-  if (zArg[0] == '-') {
-    isNeg = 1;
-    zArg++;
-  } else if (zArg[0] == '+') {
-    zArg++;
-  }
-  if (zArg[0] == '0' && zArg[1] == 'x') {
-    int x;
-    zArg += 2;
-    while ((x = hexDigitValue(zArg[0])) >= 0) {
-      v = (v << 4) + x;
-      zArg++;
-    }
-  } else {
-    while (IsDigit(zArg[0])) {
-      v = v * 10 + zArg[0] - '0';
-      zArg++;
-    }
-  }
-  for (i = 0; i < ArraySize(aMult); i++) {
-    if (sqlite3_stricmp(aMult[i].zSuffix, zArg) == 0) {
-      v *= aMult[i].iMult;
-      break;
-    }
-  }
-  return isNeg ? -v : v;
-}
-
-/*
-** Interpret zArg as either an integer or a boolean value.  Return 1 or 0
-** for TRUE and FALSE.  Return the integer value if appropriate.
-*/
-static int booleanValue(char *zArg) {
-  int i;
-  if (zArg[0] == '0' && zArg[1] == 'x') {
-    for (i = 2; hexDigitValue(zArg[i]) >= 0; i++) {
-    }
-  } else {
-    for (i = 0; zArg[i] >= '0' && zArg[i] <= '9'; i++) {
-    }
-  }
-  if (i > 0 && zArg[i] == 0)
-    return (int)(integerValue(zArg) & 0xffffffff);
-  if (sqlite3_stricmp(zArg, "on") == 0 || sqlite3_stricmp(zArg, "yes") == 0) {
-    return 1;
-  }
-  if (sqlite3_stricmp(zArg, "off") == 0 || sqlite3_stricmp(zArg, "no") == 0) {
-    return 0;
-  }
-  fprintf(
-      stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg);
-  return 0;
-}
-
-/*
-** Close an output file, assuming it is not stderr or stdout
-*/
-static void output_file_close(FILE *f) {
-  if (f && f != stdout && f != stderr)
-    fclose(f);
-}
-
-/*
-** Try to open an output file.   The names "stdout" and "stderr" are
-** recognized and do the right thing.  NULL is returned if the output
-** filename is "off".
-*/
-static FILE *output_file_open(const char *zFile) {
-  FILE *f;
-  if (strcmp(zFile, "stdout") == 0) {
-    f = stdout;
-  } else if (strcmp(zFile, "stderr") == 0) {
-    f = stderr;
-  } else if (strcmp(zFile, "off") == 0) {
-    f = 0;
-  } else {
-    f = fopen(zFile, "wb");
-    if (f == 0) {
-      fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
-    }
-  }
-  return f;
-}
-
-inline void meta_tables(int nArg, char **azArg) {
-  auto tables = osquery::Registry::names("table");
-  std::sort(tables.begin(), tables.end());
-  for (const auto &table_name : tables) {
-    if (nArg == 1 || table_name.find(azArg[1]) == 0) {
-      printf("  => %s\n", table_name.c_str());
-    }
-  }
-}
-
-inline void meta_schema(int nArg, char **azArg) {
-  for (const auto &table_name : osquery::Registry::names("table")) {
-    if (nArg > 1 && table_name.find(azArg[1]) != 0) {
-      continue;
-    }
-
-    osquery::PluginRequest request = {{"action", "columns"}};
-    osquery::PluginResponse response;
-
-    osquery::Registry::call("table", table_name, request, response);
-    std::vector<std::string> columns;
-    for (const auto &column : response) {
-      columns.push_back(column.at("name") + " " + column.at("type"));
-    }
-
-    printf("CREATE TABLE %s(%s);\n",
-           table_name.c_str(),
-           osquery::join(columns, ", ").c_str());
-  }
-}
-
-/*
-** If an input line begins with "." then invoke this routine to
-** process that line.
-**
-** Return 1 on error, 2 to exit, and 0 otherwise.
-*/
-static int do_meta_command(char *zLine, struct callback_data *p) {
-  int i = 1;
-  int nArg = 0;
-  int n, c;
-  int rc = 0;
-  char *azArg[50];
-
-  // A meta command may act on the database, grab a lock and instance.
-  auto dbc = osquery::SQLiteDBManager::get();
-  auto db = dbc.db();
-
-  /* Parse the input line into tokens.
-  */
-  while (zLine[i] && nArg < ArraySize(azArg)) {
-    while (IsSpace(zLine[i])) {
-      i++;
-    }
-    if (zLine[i] == 0)
-      break;
-    if (zLine[i] == '\'' || zLine[i] == '"') {
-      int delim = zLine[i++];
-      azArg[nArg++] = &zLine[i];
-      while (zLine[i] && zLine[i] != delim) {
-        if (zLine[i] == '\\' && delim == '"' && zLine[i + 1] != 0)
-          i++;
-        i++;
-      }
-      if (zLine[i] == delim) {
-        zLine[i++] = 0;
-      }
-      if (delim == '"')
-        resolve_backslashes(azArg[nArg - 1]);
-    } else {
-      azArg[nArg++] = &zLine[i];
-      while (zLine[i] && !IsSpace(zLine[i])) {
-        i++;
-      }
-      if (zLine[i])
-        zLine[i++] = 0;
-      resolve_backslashes(azArg[nArg - 1]);
-    }
-  }
-
-  /* Process the input line.
-  */
-  if (nArg == 0)
-    return 0; /* no tokens, no error */
-  n = strlen30(azArg[0]);
-  c = azArg[0][0];
-  if (c == 'a' && strncmp(azArg[0], "all", n) == 0 && nArg == 2) {
-    struct callback_data data;
-    memcpy(&data, p, sizeof(data));
-    auto query = std::string("SELECT * FROM ") + azArg[1];
-    rc = shell_exec(query.c_str(), shell_callback, &data, nullptr);
-    if (rc != SQLITE_OK) {
-      fprintf(stderr, "Error querying table: %s\n", azArg[1]);
-    }
-  } else if (c == 'b' && n >= 3 && strncmp(azArg[0], "bail", n) == 0 &&
-             nArg > 1 && nArg < 3) {
-    bail_on_error = booleanValue(azArg[1]);
-  } else if (c == 'e' && strncmp(azArg[0], "echo", n) == 0 && nArg > 1 &&
-             nArg < 3) {
-    p->echoOn = booleanValue(azArg[1]);
-  } else if (c == 'e' && strncmp(azArg[0], "exit", n) == 0) {
-    if (nArg > 1 && (rc = (int)integerValue(azArg[1])) != 0)
-      exit(rc);
-    rc = 2;
-  } else if (c == 'h' && (strncmp(azArg[0], "header", n) == 0 ||
-                          strncmp(azArg[0], "headers", n) == 0) &&
-             nArg > 1 && nArg < 3) {
-    p->showHeader = booleanValue(azArg[1]);
-  } else if (c == 'h' && strncmp(azArg[0], "help", n) == 0) {
-    fprintf(stderr, "%s", zHelp);
-    if (HAS_TIMER) {
-      fprintf(stderr, "%s", zTimerHelp);
-    }
-  } else if (c == 'l' && strncmp(azArg[0], "log", n) == 0 && nArg >= 2) {
-    const char *zFile = azArg[1];
-    output_file_close(p->pLog);
-    p->pLog = output_file_open(zFile);
-  } else if (c == 'm' && strncmp(azArg[0], "mode", n) == 0 && nArg == 2) {
-    int n2 = strlen30(azArg[1]);
-    if ((n2 == 4 && strncmp(azArg[1], "line", n2) == 0) ||
-        (n2 == 5 && strncmp(azArg[1], "lines", n2) == 0)) {
-      p->mode = MODE_Line;
-    } else if ((n2 == 6 && strncmp(azArg[1], "column", n2) == 0) ||
-               (n2 == 7 && strncmp(azArg[1], "columns", n2) == 0)) {
-      p->mode = MODE_Column;
-    } else if ((n2 == 6 && strncmp(azArg[1], "column", n2) == 0) ||
-               (n2 == 7 && strncmp(azArg[1], "columns", n2) == 0)) {
-      p->mode = MODE_Column;
-    } else if (n2 == 4 && strncmp(azArg[1], "list", n2) == 0) {
-      p->mode = MODE_List;
-    } else if (n2 == 6 && strncmp(azArg[1], "pretty", n2) == 0) {
-      p->mode = MODE_Pretty;
-    } else if (n2 == 3 && strncmp(azArg[1], "csv", n2) == 0) {
-      p->mode = MODE_Csv;
-      sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
-    } else {
-      fprintf(stderr,
-              "Error: mode should be one of: "
-              "column csv html insert line list tabs tcl pretty\n");
-      rc = 1;
-    }
-  } else if (c == 'n' && strncmp(azArg[0], "nullvalue", n) == 0 && nArg == 2) {
-    sqlite3_snprintf(sizeof(p->nullvalue),
-                     p->nullvalue,
-                     "%.*s",
-                     (int)ArraySize(p->nullvalue) - 1,
-                     azArg[1]);
-  } else if (c == 'p' && n >= 3 && strncmp(azArg[0], "print", n) == 0) {
-    int i;
-    for (i = 1; i < nArg; i++) {
-      if (i > 1)
-        fprintf(p->out, " ");
-      fprintf(p->out, "%s", azArg[i]);
-    }
-    fprintf(p->out, "\n");
-  } else if (c == 'q' && strncmp(azArg[0], "quit", n) == 0 && nArg == 1) {
-    rc = 2;
-  } else if (c == 's' && strncmp(azArg[0], "schema", n) == 0 && nArg < 3) {
-    meta_schema(nArg, azArg);
-  } else if (c == 's' && strncmp(azArg[0], "separator", n) == 0 && nArg == 2) {
-    sqlite3_snprintf(sizeof(p->separator),
-                     p->separator,
-                     "%.*s",
-                     (int)sizeof(p->separator) - 1,
-                     azArg[1]);
-  } else if (c == 's' && strncmp(azArg[0], "show", n) == 0 && nArg == 1) {
-    int i;
-    fprintf(p->out, "%9.9s: %s\n", "echo", p->echoOn ? "on" : "off");
-    fprintf(p->out, "%9.9s: %s\n", "headers", p->showHeader ? "on" : "off");
-    fprintf(p->out, "%9.9s: %s\n", "mode", modeDescr[p->mode]);
-    fprintf(p->out, "%9.9s: ", "nullvalue");
-    output_c_string(p->out, p->nullvalue);
-    fprintf(p->out, "\n");
-    fprintf(p->out,
-            "%9.9s: %s\n",
-            "output",
-            strlen30(p->outfile) ? p->outfile : "stdout");
-    fprintf(p->out, "%9.9s: ", "separator");
-    output_c_string(p->out, p->separator);
-    fprintf(p->out, "\n");
-    fprintf(p->out, "%9.9s: ", "width");
-    for (i = 0; i < (int)ArraySize(p->colWidth) && p->colWidth[i] != 0; i++) {
-      fprintf(p->out, "%d ", p->colWidth[i]);
-    }
-    fprintf(p->out, "\n");
-  } else if (c == 't' && n > 1 && strncmp(azArg[0], "tables", n) == 0 &&
-             nArg < 3) {
-    meta_tables(nArg, azArg);
-  } else if (c == 't' && n > 4 && strncmp(azArg[0], "timeout", n) == 0 &&
-             nArg == 2) {
-    sqlite3_busy_timeout(db, (int)integerValue(azArg[1]));
-  } else if (HAS_TIMER && c == 't' && n >= 5 &&
-             strncmp(azArg[0], "timer", n) == 0 && nArg == 2) {
-    enableTimer = booleanValue(azArg[1]);
-  } else if (c == 't' && strncmp(azArg[0], "trace", n) == 0 && nArg > 1) {
-    output_file_close(p->traceOut);
-    p->traceOut = output_file_open(azArg[1]);
-  } else if (c == 'v' && strncmp(azArg[0], "version", n) == 0) {
-    fprintf(p->out, "osquery %s\n", osquery::kVersion.c_str());
-    fprintf(p->out, "using SQLite %s\n", sqlite3_libversion());
-  } else if (c == 'w' && strncmp(azArg[0], "width", n) == 0 && nArg > 1) {
-    int j;
-    assert(nArg <= ArraySize(azArg));
-    for (j = 1; j < nArg && j < ArraySize(p->colWidth); j++) {
-      p->colWidth[j - 1] = (int)integerValue(azArg[j]);
-    }
-  } else {
-    fprintf(stderr,
-            "Error: unknown command or invalid arguments: "
-            " \"%s\". Enter \".help\" for help\n",
-            azArg[0]);
-    rc = 1;
-  }
-
-  return rc;
-}
-
-/*
-** Return TRUE if a semicolon occurs anywhere in the first N characters
-** of string z[].
-*/
-static int line_contains_semicolon(const char *z, int N) {
-  int i;
-  if (z == nullptr) {
-    return 0;
-  }
-
-  for (i = 0; i < N; i++) {
-    if (z[i] == ';')
-      return 1;
-  }
-  return 0;
-}
-
-/*
-** Test to see if a line consists entirely of whitespace.
-*/
-static int _all_whitespace(const char *z) {
-  for (; *z; z++) {
-    if (IsSpace(z[0]))
-      continue;
-    if (*z == '/' && z[1] == '*') {
-      z += 2;
-      while (*z && (*z != '*' || z[1] != '/')) {
-        z++;
-      }
-      if (*z == 0)
-        return 0;
-      z++;
-      continue;
-    }
-    if (*z == '-' && z[1] == '-') {
-      z += 2;
-      while (*z && *z != '\n') {
-        z++;
-      }
-      if (*z == 0)
-        return 1;
-      continue;
-    }
-    return 0;
-  }
-  return 1;
-}
-
-/*
-** Return TRUE if the line typed in is an SQL command terminator other
-** than a semi-colon.  The SQL Server style "go" command is understood
-** as is the Oracle "/".
-*/
-static int line_is_command_terminator(const char *zLine) {
-  while (IsSpace(zLine[0])) {
-    zLine++;
-  };
-  if (zLine[0] == '/' && _all_whitespace(&zLine[1])) {
-    return 1; /* Oracle */
-  }
-  if (ToLower(zLine[0]) == 'g' && ToLower(zLine[1]) == 'o' &&
-      _all_whitespace(&zLine[2])) {
-    return 1; /* SQL Server */
-  }
-  return 0;
-}
-
-/*
-** Return true if zSql is a complete SQL statement.  Return false if it
-** ends in the middle of a string literal or C-style comment.
-*/
-static int line_is_complete(char *zSql, int nSql) {
-  int rc;
-  if (zSql == 0)
-    return 1;
-  zSql[nSql] = ';';
-  zSql[nSql + 1] = 0;
-  rc = sqlite3_complete(zSql);
-  zSql[nSql] = 0;
-  return rc;
-}
-
-/*
-** Read input from *in and process it.  If *in==0 then input
-** is interactive - the user is typing it it.  Otherwise, input
-** is coming from a file or device.  A prompt is issued and history
-** is saved only if input is interactive.  An interrupt signal will
-** cause this routine to exit immediately, unless input is interactive.
-**
-** Return the number of errors.
-*/
-static int process_input(struct callback_data *p, FILE *in) {
-  char *zLine = 0; /* A single input line */
-  char *zSql = 0; /* Accumulated SQL text */
-  int nLine; /* Length of current line */
-  int nSql = 0; /* Bytes of zSql[] used */
-  int nAlloc = 0; /* Allocated zSql[] space */
-  int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
-  char *zErrMsg; /* Error message returned */
-  int rc; /* Error code */
-  int errCnt = 0; /* Number of errors seen */
-  int lineno = 0; /* Current line number */
-  int startline = 0; /* Line number for start of current input */
-
-  while (errCnt == 0 || !bail_on_error || (in == 0 && stdin_is_interactive)) {
-    fflush(p->out);
-    zLine = one_input_line(in, zLine, nSql > 0);
-    if (zLine == 0) {
-      /* End of input */
-      if (stdin_is_interactive)
-        printf("\n");
-      break;
-    }
-    if (seenInterrupt) {
-      if (in != 0)
-        break;
-      seenInterrupt = 0;
-    }
-    lineno++;
-    if (nSql == 0 && _all_whitespace(zLine)) {
-      if (p->echoOn)
-        printf("%s\n", zLine);
-      continue;
-    }
-    if (zLine && zLine[0] == '.' && nSql == 0) {
-      if (p->echoOn)
-        printf("%s\n", zLine);
-      rc = do_meta_command(zLine, p);
-      if (rc == 2) { /* exit requested */
-        break;
-      } else if (rc) {
-        errCnt++;
-      }
-      continue;
-    }
-    if (line_is_command_terminator(zLine) && line_is_complete(zSql, nSql)) {
-      memcpy(zLine, ";", 2);
-    }
-    nLine = strlen30(zLine);
-    if (nSql + nLine + 2 >= nAlloc) {
-      nAlloc = nSql + nLine + 100;
-      zSql = (char *)realloc(zSql, nAlloc);
-      if (zSql == 0) {
-        fprintf(stderr, "Error: out of memory\n");
-        exit(1);
-      }
-    }
-    nSqlPrior = nSql;
-    if (nSql == 0) {
-      int i;
-      for (i = 0; zLine[i] && IsSpace(zLine[i]); i++) {
-      }
-      assert(nAlloc > 0 && zSql != nullptr);
-      if (zSql != nullptr) {
-        memcpy(zSql, zLine + i, nLine + 1 - i);
-      }
-      startline = lineno;
-      nSql = nLine - i;
-    } else {
-      zSql[nSql++] = '\n';
-      memcpy(zSql + nSql, zLine, nLine + 1);
-      nSql += nLine;
-    }
-    if (nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql - nSqlPrior) &&
-        sqlite3_complete(zSql)) {
-      p->cnt = 0;
-      BEGIN_TIMER;
-      rc = shell_exec(zSql, shell_callback, p, &zErrMsg);
-      END_TIMER;
-      if (rc || zErrMsg) {
-        char zPrefix[100];
-        if (in != 0 || !stdin_is_interactive) {
-          sqlite3_snprintf(
-              sizeof(zPrefix), zPrefix, "Error: near line %d:", startline);
-        } else {
-          sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
-        }
-        if (zErrMsg != 0) {
-          fprintf(stderr, "%s %s\n", zPrefix, zErrMsg);
-          sqlite3_free(zErrMsg);
-          zErrMsg = 0;
-        }
-        errCnt++;
-      }
-      nSql = 0;
-    } else if (nSql && _all_whitespace(zSql)) {
-      if (p->echoOn)
-        printf("%s\n", zSql);
-      nSql = 0;
-    }
-  }
-  if (nSql) {
-    if (!_all_whitespace(zSql)) {
-      fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
-    }
-    free(zSql);
-  }
-  free(zLine);
-  return errCnt > 0;
-}
-
-/*
-** Initialize the state information in data
-*/
-static void main_init(struct callback_data *data) {
-  memset(data, 0, sizeof(*data));
-  data->prettyPrint = new struct prettyprint_data();
-  data->mode = MODE_Pretty;
-  memcpy(data->separator, "|", 2);
-  data->showHeader = 1;
-  sqlite3_config(SQLITE_CONFIG_URI, 1);
-  sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
-  sqlite3_snprintf(sizeof(mainPrompt), mainPrompt, "osquery> ");
-  sqlite3_snprintf(sizeof(continuePrompt), continuePrompt, "    ...> ");
-  sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
-}
-
-/*
-** Output text to the console in a font that attracts extra attention.
-*/
-static void printBold(const char *zText) { printf("\033[1m%s\033[0m", zText); }
-
-namespace osquery {
-
-int launchIntoShell(int argc, char **argv) {
-  struct callback_data data;
-  main_init(&data);
-
-  {
-    // Hold the manager connection instance again in callbacks.
-    auto dbc = SQLiteDBManager::get();
-    // Add some shell-specific functions to the instance.
-    sqlite3_create_function(
-        dbc.db(), "shellstatic", 0, SQLITE_UTF8, 0, shellstaticFunc, 0, 0);
-  }
-
-  Argv0 = argv[0];
-  stdin_is_interactive = isatty(0);
-
-  // SQLite: Make sure we have a valid signal handler early
-  signal(SIGINT, interrupt_handler);
-
-  int warnInmemoryDb = 1;
-  data.zDbFilename = ":memory:";
-  data.out = stdout;
-
-  // Set modes and settings from CLI flags.
-  if (FLAGS_list) {
-    data.mode = MODE_List;
-  } else if (FLAGS_line) {
-    data.mode = MODE_Line;
-  } else if (FLAGS_csv) {
-    data.mode = MODE_Csv;
-    memcpy(data.separator, ",", 2);
-  } else {
-    data.mode = MODE_Pretty;
-  }
-
-  sqlite3_snprintf(sizeof(data.separator), data.separator, "%s",
-    FLAGS_separator.c_str());
-  sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, "%s",
-    FLAGS_nullvalue.c_str());
-
-  int rc = 0;
-  if (FLAGS_L == true || FLAGS_A.size() > 0) {
-    // Helper meta commands from shell switches.
-    std::string query = (FLAGS_L) ? ".tables" : ".all " + FLAGS_A;
-    char *cmd = new char[query.size() + 1];
-    memset(cmd, 0, query.size() + 1);
-    std::copy(query.begin(), query.end(), cmd);
-    rc = do_meta_command(cmd, &data);
-  } else if (argc > 1 && argv[1] != nullptr) {
-    // Run a command or statement from CLI
-    char *query = argv[1];
-    char *error = 0;
-    if (query[0] == '.') {
-      rc = do_meta_command(query, &data);
-      rc = (rc == 2) ? 0 : rc;
-    } else {
-      rc = shell_exec(query, shell_callback, &data, &error);
-      if (error != 0) {
-        fprintf(stderr, "Error: %s\n", error);
-        return (rc != 0) ? rc : 1;
-      } else if (rc != 0) {
-        fprintf(stderr, "Error: unable to process SQL \"%s\"\n", query);
-        return rc;
-      }
-    }
-  } else {
-    // Run commands received from standard input
-    if (stdin_is_interactive) {
-      printBold("osquery");
-      printf(
-          " - being built, with love, at Samsung(not Facebook)\n"
-          "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
-      if (warnInmemoryDb) {
-        printf("Using a ");
-        printBold("virtual database");
-        printf(". Need help, type '.help'\n");
-      }
-
-      auto history_file = osquery::osqueryHomeDirectory() + "/.history";
-      read_history(history_file.c_str());
-      rc = process_input(&data, 0);
-      stifle_history(100);
-      write_history(history_file.c_str());
-    } else {
-      rc = process_input(&data, stdin);
-    }
-  }
-
-  set_table_name(&data, 0);
-  sqlite3_free(data.zFreeOnClose);
-
-  if (data.prettyPrint != nullptr) {
-    delete data.prettyPrint;
-  }
-  return rc;
-}
-}
diff --git a/osquery/devtools/tests/printer_tests.cpp b/osquery/devtools/tests/printer_tests.cpp
deleted file mode 100644 (file)
index 61626a0..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/logger.h>
-
-#include "osquery/devtools/devtools.h"
-
-namespace osquery {
-
-class PrinterTests : public testing::Test {
- public:
-  QueryData q;
-  std::vector<std::string> order;
-  void SetUp() {
-    order = {"name", "age", "food", "number"};
-    q = {
-        {
-         {"name", "Mike Jones"},
-         {"age", "39"},
-         {"food", "mac and cheese"},
-         {"number", "1"},
-        },
-        {
-         {"name", "John Smith"},
-         {"age", "44"},
-         {"food", "peanut butter and jelly"},
-         {"number", "2"},
-        },
-        {
-         {"name", "Doctor Who"},
-         {"age", "2000"},
-         {"food", "fish sticks and custard"},
-         {"number", "11"},
-        },
-    };
-  }
-};
-
-TEST_F(PrinterTests, test_compute_query_data_lengths) {
-  std::map<std::string, size_t> lengths;
-  for (const auto& row : q) {
-    computeRowLengths(row, lengths);
-  }
-
-  // Check that all value lengths were maxed.
-  std::map<std::string, size_t> expected = {
-      {"name", 10}, {"age", 4}, {"food", 23}, {"number", 2}};
-  EXPECT_EQ(lengths, expected);
-
-  // Then compute lengths of column names.
-  computeRowLengths(q.front(), lengths, true);
-  expected = {{"name", 10}, {"age", 4}, {"food", 23}, {"number", 6}};
-  EXPECT_EQ(lengths, expected);
-}
-
-TEST_F(PrinterTests, test_generate_separator) {
-  std::map<std::string, size_t> lengths;
-  for (const auto& row : q) {
-    computeRowLengths(row, lengths);
-  }
-
-  auto results = generateToken(lengths, order);
-  auto expected = "+------------+------+-------------------------+----+\n";
-  EXPECT_EQ(results, expected);
-}
-
-TEST_F(PrinterTests, test_generate_header) {
-  std::map<std::string, size_t> lengths;
-  for (const auto& row : q) {
-    computeRowLengths(row, lengths);
-  }
-
-  auto results = generateHeader(lengths, order);
-  auto expected = "| name       | age  | food                    | number |\n";
-  EXPECT_EQ(results, expected);
-}
-
-TEST_F(PrinterTests, test_generate_row) {
-  std::map<std::string, size_t> lengths;
-  for (const auto& row : q) {
-    computeRowLengths(row, lengths);
-  }
-
-  auto results = generateRow(q.front(), lengths, order);
-  auto expected = "| Mike Jones | 39   | mac and cheese          | 1  |\n";
-  EXPECT_EQ(results, expected);
-}
-
-TEST_F(PrinterTests, test_unicode) {
-  Row r = {{"name", "Àlex Smith"}};
-  std::map<std::string, size_t> lengths;
-  computeRowLengths(r, lengths);
-
-  std::map<std::string, size_t> expected = {{"name", 10}};
-  EXPECT_EQ(lengths, expected);
-}
-}
diff --git a/osquery/dispatcher/CMakeLists.txt b/osquery/dispatcher/CMakeLists.txt
deleted file mode 100644 (file)
index 82b34a2..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_dispatcher dispatcher.cpp
-                                                                          scheduler.cpp)
-
-FILE(GLOB OSQUERY_DISPATCHER_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_DISPATCHER_TESTS})
diff --git a/osquery/dispatcher/dispatcher.cpp b/osquery/dispatcher/dispatcher.cpp
deleted file mode 100644 (file)
index 4c7f10d..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/date_time/posix_time/posix_time.hpp>
-
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-#include "osquery/core/conversions.h"
-#include "osquery/dispatcher/dispatcher.h"
-
-using namespace apache::thrift::concurrency;
-
-namespace osquery {
-
-/// The worker_threads define the default thread pool size.
-FLAG(int32, worker_threads, 4, "Number of work dispatch threads");
-
-void interruptableSleep(size_t milli) {
-  boost::this_thread::sleep(boost::posix_time::milliseconds(milli));
-}
-
-Dispatcher::Dispatcher() {
-  thread_manager_ = InternalThreadManager::newSimpleThreadManager(
-          (size_t)FLAGS_worker_threads, 0);
-  auto thread_factory = ThriftThreadFactory(new PosixThreadFactory());
-  thread_manager_->threadFactory(thread_factory);
-  thread_manager_->start();
-}
-
-Dispatcher::~Dispatcher() { join(); }
-
-Status Dispatcher::add(ThriftInternalRunnableRef task) {
-  auto& self = instance();
-  try {
-    if (self.state() != InternalThreadManager::STARTED) {
-      self.thread_manager_->start();
-    }
-    instance().thread_manager_->add(task, 0, 0);
-  } catch (std::exception& e) {
-    return Status(1, e.what());
-  }
-  return Status(0, "OK");
-}
-
-Status Dispatcher::addService(InternalRunnableRef service) {
-  if (service->hasRun()) {
-    return Status(1, "Cannot schedule a service twice");
-  }
-
-  auto& self = instance();
-  auto thread = std::make_shared<boost::thread>(
-      boost::bind(&InternalRunnable::run, &*service));
-  self.service_threads_.push_back(thread);
-  self.services_.push_back(std::move(service));
-  return Status(0, "OK");
-}
-
-InternalThreadManagerRef Dispatcher::getThreadManager() const {
-  return instance().thread_manager_;
-}
-
-void Dispatcher::join() {
-  if (instance().thread_manager_ != nullptr) {
-    instance().thread_manager_->stop();
-    instance().thread_manager_->join();
-  }
-}
-
-void Dispatcher::joinServices() {
-  for (auto& thread : instance().service_threads_) {
-    thread->join();
-  }
-}
-
-void Dispatcher::stopServices() {
-  auto& self = instance();
-  for (const auto& service : self.services_) {
-    while (true) {
-      // Wait for each thread's entry point (start) meaning the thread context
-      // was allocated and (run) was called by boost::thread started.
-      if (service->hasRun()) {
-        break;
-      }
-      // We only need to check if std::terminate is called very quickly after
-      // the boost::thread is created.
-      ::usleep(200);
-    }
-    service->stop();
-  }
-
-  for (auto& thread : self.service_threads_) {
-    thread->interrupt();
-  }
-}
-
-InternalThreadManager::STATE Dispatcher::state() const {
-  return instance().thread_manager_->state();
-}
-
-void Dispatcher::addWorker(size_t value) {
-  instance().thread_manager_->addWorker(value);
-}
-
-void Dispatcher::removeWorker(size_t value) {
-  instance().thread_manager_->removeWorker(value);
-}
-
-size_t Dispatcher::idleWorkerCount() const {
-  return instance().thread_manager_->idleWorkerCount();
-}
-
-size_t Dispatcher::workerCount() const {
-  return instance().thread_manager_->workerCount();
-}
-
-size_t Dispatcher::pendingTaskCount() const {
-  return instance().thread_manager_->pendingTaskCount();
-}
-
-size_t Dispatcher::totalTaskCount() const {
-  return instance().thread_manager_->totalTaskCount();
-}
-
-size_t Dispatcher::pendingTaskCountMax() const {
-  return instance().thread_manager_->pendingTaskCountMax();
-}
-
-size_t Dispatcher::expiredTaskCount() const {
-  return instance().thread_manager_->expiredTaskCount();
-}
-}
diff --git a/osquery/dispatcher/dispatcher.h b/osquery/dispatcher/dispatcher.h
deleted file mode 100644 (file)
index f4d23e5..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <boost/noncopyable.hpp>
-#include <boost/thread.hpp>
-
-#include <osquery/core.h>
-
-#include <thrift/concurrency/Thread.h>
-#include <thrift/concurrency/ThreadManager.h>
-#include <thrift/concurrency/PosixThreadFactory.h>
-
-namespace osquery {
-
-using namespace apache::thrift::concurrency;
-
-/// Create easier to reference typedefs for Thrift layer implementations.
-#define SHARED_PTR_IMPL OSQUERY_THRIFT_POINTER::shared_ptr
-typedef apache::thrift::concurrency::ThreadManager InternalThreadManager;
-typedef SHARED_PTR_IMPL<InternalThreadManager> InternalThreadManagerRef;
-
-/**
- * @brief Default number of threads in the thread pool.
- *
- * The amount of threads that the thread pool will be created with if another
- * value is not specified on the command-line.
- */
-extern const int kDefaultThreadPoolSize;
-
-class InternalRunnable : public Runnable {
- public:
-  virtual ~InternalRunnable() {}
-  InternalRunnable() : run_(false) {}
-
- public:
-  /// The boost::thread entrypoint.
-  void run() {
-    run_ = true;
-    start();
-  }
-
-  /// Check if the thread's entrypoint (run) executed, meaning thread context
-  /// was allocated.
-  bool hasRun() { return run_; }
-
-  /// The runnable may also tear down services before the thread context
-  /// is removed.
-  virtual void stop() {}
-
- protected:
-  /// Require the runnable thread define an entrypoint.
-  virtual void start() = 0;
-
- private:
-  bool run_;
-};
-
-/// An internal runnable used throughout osquery as dispatcher services.
-typedef std::shared_ptr<InternalRunnable> InternalRunnableRef;
-typedef std::shared_ptr<boost::thread> InternalThreadRef;
-/// A thrift internal runnable with variable pointer wrapping.
-typedef SHARED_PTR_IMPL<InternalRunnable> ThriftInternalRunnableRef;
-typedef SHARED_PTR_IMPL<PosixThreadFactory> ThriftThreadFactory;
-
-/**
- * @brief Singleton for queuing asynchronous tasks to be executed in parallel
- *
- * Dispatcher is a singleton which can be used to coordinate the parallel
- * execution of asynchronous tasks across an application. Internally,
- * Dispatcher is back by the Apache Thrift thread pool.
- */
-class Dispatcher : private boost::noncopyable {
- public:
-  /**
-   * @brief The primary way to access the Dispatcher factory facility.
-   *
-   * @code{.cpp} auto dispatch = osquery::Dispatcher::instance(); @endcode
-   *
-   * @return The osquery::Dispatcher instance.
-   */
-  static Dispatcher& instance() {
-    static Dispatcher instance;
-    return instance;
-  }
-
-  /**
-   * @brief Add a task to the dispatcher.
-   *
-   * Adding tasks to the Dispatcher's thread pool requires you to create a
-   * "runnable" class which publicly implements Apache Thrift's Runnable
-   * class. Create a shared pointer to the class and you're all set to
-   * schedule work.
-   *
-   * @code{.cpp}
-   *   class TestRunnable : public apache::thrift::concurrency::Runnable {
-   *    public:
-   *     int* i;
-   *     TestRunnable(int* i) : i(i) {}
-   *     virtual void run() { ++*i; }
-   *   };
-   *
-   *   int i = 5;
-   *   Dispatcher::add(std::make_shared<TestRunnable>(&i);
-   *   while (dispatch->totalTaskCount() > 0) {}
-   *   assert(i == 6);
-   * @endcode
-   *
-   * @param task a C++11 std shared pointer to an instance of a class which
-   * publicly inherits from `apache::thrift::concurrency::Runnable`.
-   *
-   * @return osquery success status
-   */
-  static Status add(ThriftInternalRunnableRef task);
-
-  /// See `add`, but services are not limited to a thread poll size.
-  static Status addService(InternalRunnableRef service);
-
-  /**
-   * @brief Getter for the underlying thread manager instance.
-   *
-   * Use this getter if you'd like to perform some operations which the
-   * Dispatcher API doesn't support, but you are certain are supported by the
-   * backing Apache Thrift thread manager.
-   *
-   * Use this method with caution, as it only exists to allow developers to
-   * iterate quickly in the event that the pragmatic decision to access the
-   * underlying thread manager has been determined to be necessary.
-   *
-   * @code{.cpp}
-   *   auto t = osquery::Dispatcher::getThreadManager();
-   * @endcode
-   *
-   * @return a shared pointer to the Apache Thrift `ThreadManager` instance
-   * which is currently being used to orchestrate multi-threaded operations.
-   */
-  InternalThreadManagerRef getThreadManager() const;
-
-  /**
-   * @brief Joins the thread manager.
-   *
-   * This is the same as stop, except that it will block until all the workers
-   * have finished their work. At that point the ThreadManager will transition
-   * into the STOPPED state.
-   */
-  static void join();
-
-  /// See `join`, but applied to osquery services.
-  static void joinServices();
-
-  /// Destroy and stop all osquery service threads and service objects.
-  static void stopServices();
-
-  /**
-   * @brief Get the current state of the thread manager.
-   *
-   * @return an Apache Thrift STATE enum.
-   */
-  InternalThreadManager::STATE state() const;
-
-  /**
-   * @brief Add a worker thread.
-   *
-   * Use this method to add an additional thread to the thread pool.
-   *
-   * @param value is a size_t indicating how many additional worker threads
-   * should be added to the thread group. If not parameter is supplied, one
-   * worker thread is added.
-   *
-   * @see osquery::Dispatcher::removeWorker
-   */
-  static void addWorker(size_t value = 1);
-
-  /**
-   * @brief Remove a worker thread.
-   *
-   * Use this method to remove a thread from the thread pool.
-   *
-   * @param value is a size_t indicating how many additional worker threads
-   * should be removed from the thread group. If not parameter is supplied,
-   * one worker thread is removed.
-   *
-   * @see osquery::Dispatcher::addWorker
-   */
-  static void removeWorker(size_t value = 1);
-
-  /**
-   * @brief Gets the current number of idle worker threads.
-   *
-   * @return the number of idle worker threads.
-   */
-  size_t idleWorkerCount() const;
-
-  /**
-   * @brief Gets the current number of total worker threads.
-   *
-   * @return the current number of total worker threads.
-   */
-  size_t workerCount() const;
-
-  /**
-   * @brief Gets the current number of pending tasks.
-   *
-   * @return the current number of pending tasks.
-   */
-  size_t pendingTaskCount() const;
-
-  /**
-   * @brief Gets the current number of pending and executing tasks.
-   *
-   * @return the current number of pending and executing tasks.
-   */
-  size_t totalTaskCount() const;
-
-  /**
-   * @brief Gets the maximum pending task count. 0 indicates no maximum.
-   *
-   * @return the maximum pending task count. 0 indicates no maximum.
-   */
-  size_t pendingTaskCountMax() const;
-
-  /**
-   * @brief Gets the number of tasks which have been expired without being
-   * run.
-   *
-   * @return he number of tasks which have been expired without being run.
-   */
-  size_t expiredTaskCount() const;
-
- private:
-  /**
-   * @brief Default constructor.
-   *
-   * Since instances of Dispatcher should only be created via instance(),
-   * Dispatcher's constructor is private.
-   */
-  Dispatcher();
-  Dispatcher(Dispatcher const&);
-  void operator=(Dispatcher const&);
-  virtual ~Dispatcher();
-
- private:
-  /**
-   * @brief Internal shared pointer which references Thrift's thread manager
-   *
-   * All thread operations occur via Apache Thrift's ThreadManager class. This
-   * private member represents a shared pointer to an instantiation of that
-   * thread manager, which can be used to accomplish various threading
-   * objectives.
-   *
-   * @see getThreadManager
-   */
-  InternalThreadManagerRef thread_manager_;
-
-  /// The set of shared osquery service threads.
-  std::vector<InternalThreadRef> service_threads_;
-
-  /// The set of shared osquery services.
-  std::vector<InternalRunnableRef> services_;
-
- private:
-  friend class ExtensionsTest;
-};
-
-/// Allow a dispatched thread to wait while processing or to prevent thrashing.
-void interruptableSleep(size_t milli);
-}
diff --git a/osquery/dispatcher/scheduler.cpp b/osquery/dispatcher/scheduler.cpp
deleted file mode 100644 (file)
index 5323e0c..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-#include <ctime>
-
-#include <osquery/config.h>
-#include <osquery/core.h>
-#include <osquery/database.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-
-#include "osquery/database/query.h"
-#include "osquery/dispatcher/scheduler.h"
-
-namespace osquery {
-
-FLAG(string,
-     host_identifier,
-     "hostname",
-     "Field used to identify the host running osquery (hostname, uuid)");
-
-FLAG(bool, enable_monitor, false, "Enable the schedule monitor");
-
-FLAG(uint64, schedule_timeout, 0, "Limit the schedule, 0 for no limit")
-
-Status getHostIdentifier(std::string& ident) {
-  if (FLAGS_host_identifier != "uuid") {
-    // use the hostname as the default machine identifier
-    ident = osquery::getHostname();
-    return Status(0, "OK");
-  }
-
-  // Lookup the host identifier (UUID) previously generated and stored.
-  auto status = getDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
-  if (!status.ok()) {
-    // The lookup failed, there is a problem accessing the database.
-    VLOG(1) << "Could not access database; using hostname as host identifier";
-    ident = osquery::getHostname();
-    return Status(0, "OK");
-  }
-
-  if (ident.size() == 0) {
-    // There was no uuid stored in the database, generate one and store it.
-    ident = osquery::generateHostUuid();
-    VLOG(1) << "Using uuid " << ident << " as host identifier";
-    return setDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
-  }
-  return status;
-}
-
-inline SQL monitor(const std::string& name, const ScheduledQuery& query) {
-  // Snapshot the performance and times for the worker before running.
-  auto pid = std::to_string(getpid());
-  auto r0 = SQL::selectAllFrom("processes", "pid", EQUALS, pid);
-  auto t0 = time(nullptr);
-  auto sql = SQL(query.query);
-  // Snapshot the performance after, and compare.
-  auto t1 = time(nullptr);
-  auto r1 = SQL::selectAllFrom("processes", "pid", EQUALS, pid);
-  if (r0.size() > 0 && r1.size() > 0) {
-    size_t size = 0;
-    for (const auto& row : sql.rows()) {
-      for (const auto& column : row) {
-        size += column.first.size();
-        size += column.second.size();
-      }
-    }
-    Config::recordQueryPerformance(name, t1 - t0, size, r0[0], r1[0]);
-  }
-  return sql;
-}
-
-void launchQuery(const std::string& name, const ScheduledQuery& query) {
-  // Execute the scheduled query and create a named query object.
-  VLOG(1) << "Executing query: " << query.query;
-  auto sql = (FLAGS_enable_monitor) ? monitor(name, query) : SQL(query.query);
-
-  if (!sql.ok()) {
-    LOG(ERROR) << "Error executing query (" << query.query
-               << "): " << sql.getMessageString();
-    return;
-  }
-
-  // Fill in a host identifier fields based on configuration or availability.
-  std::string ident;
-  auto status = getHostIdentifier(ident);
-  if (!status.ok() || ident.empty()) {
-    ident = "<unknown>";
-  }
-
-  // A query log item contains an optional set of differential results or
-  // a copy of the most-recent execution alongside some query metadata.
-  QueryLogItem item;
-  item.name = name;
-  item.identifier = ident;
-  item.time = osquery::getUnixTime();
-  item.calendar_time = osquery::getAsciiTime();
-
-  if (query.options.count("snapshot") && query.options.at("snapshot")) {
-    // This is a snapshot query, emit results with a differential or state.
-    item.snapshot_results = std::move(sql.rows());
-    logSnapshotQuery(item);
-    return;
-  }
-
-  // Create a database-backed set of query results.
-  auto dbQuery = Query(name, query);
-  DiffResults diff_results;
-  // Add this execution's set of results to the database-tracked named query.
-  // We can then ask for a differential from the last time this named query
-  // was executed by exact matching each row.
-  status = dbQuery.addNewResults(sql.rows(), diff_results);
-  if (!status.ok()) {
-    LOG(ERROR) << "Error adding new results to database: " << status.what();
-    return;
-  }
-
-  if (diff_results.added.size() == 0 && diff_results.removed.size() == 0) {
-    // No diff results or events to emit.
-    return;
-  }
-
-  VLOG(1) << "Found results for query (" << name << ") for host: " << ident;
-  item.results = diff_results;
-  if (query.options.count("removed") && !query.options.at("removed")) {
-    item.results.removed.clear();
-  }
-
-  status = logQueryLogItem(item);
-  if (!status.ok()) {
-    LOG(ERROR) << "Error logging the results of query (" << query.query
-               << "): " << status.toString();
-  }
-}
-
-void SchedulerRunner::start() {
-  time_t t = std::time(nullptr);
-  struct tm* local = std::localtime(&t);
-  unsigned long int i = local->tm_sec;
-  for (; (timeout_ == 0) || (i <= timeout_); ++i) {
-    {
-      ConfigDataInstance config;
-      for (const auto& query : config.schedule()) {
-        if (i % query.second.splayed_interval == 0) {
-          launchQuery(query.first, query.second);
-        }
-      }
-    }
-    // Put the thread into an interruptible sleep without a config instance.
-    osquery::interruptableSleep(interval_ * 1000);
-  }
-}
-
-Status startScheduler() {
-  if (startScheduler(FLAGS_schedule_timeout, 1).ok()) {
-    Dispatcher::joinServices();
-    return Status(0, "OK");
-  }
-  return Status(1, "Could not start scheduler");
-}
-
-Status startScheduler(unsigned long int timeout, size_t interval) {
-  Dispatcher::addService(std::make_shared<SchedulerRunner>(timeout, interval));
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/dispatcher/scheduler.h b/osquery/dispatcher/scheduler.h
deleted file mode 100644 (file)
index ccd0c40..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include "osquery/dispatcher/dispatcher.h"
-
-namespace osquery {
-
-/// A Dispatcher service thread that watches an ExtensionManagerHandler.
-class SchedulerRunner : public InternalRunnable {
- public:
-  virtual ~SchedulerRunner() {}
-  SchedulerRunner(unsigned long int timeout, size_t interval)
-      : interval_(interval), timeout_(timeout) {}
-
- public:
-  /// The Dispatcher thread entry point.
-  void start();
-
- protected:
-  /// The UNIX domain socket path for the ExtensionManager.
-  std::map<std::string, size_t> splay_;
-  /// Interval in seconds between schedule steps.
-  size_t interval_;
-  /// Maximum number of steps.
-  unsigned long int timeout_;
-};
-
-/// Start quering according to the config's schedule
-Status startScheduler();
-
-/// Helper scheduler start with variable settings for testing.
-Status startScheduler(unsigned long int timeout, size_t interval);
-}
diff --git a/osquery/dispatcher/tests/dispatcher_tests.cpp b/osquery/dispatcher/tests/dispatcher_tests.cpp
deleted file mode 100644 (file)
index 4ece0a8..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/make_shared.hpp>
-
-#include <gtest/gtest.h>
-
-#include "osquery/dispatcher/dispatcher.h"
-
-namespace osquery {
-
-class DispatcherTests : public testing::Test {};
-
-TEST_F(DispatcherTests, test_singleton) {
-  auto& one = Dispatcher::instance();
-  auto& two = Dispatcher::instance();
-  EXPECT_EQ(one.getThreadManager().get(), two.getThreadManager().get());
-}
-
-class TestRunnable : public InternalRunnable {
- public:
-  int* i;
-  explicit TestRunnable(int* i) : i(i) {}
-  virtual void start() { ++*i; }
-};
-
-TEST_F(DispatcherTests, test_add_work) {
-  auto& dispatcher = Dispatcher::instance();
-  int base = 5;
-  int repetitions = 1;
-
-  int i = base;
-  for (int c = 0; c < repetitions; ++c) {
-    dispatcher.add(OSQUERY_THRIFT_POINTER::make_shared<TestRunnable>(&i));
-  }
-  while (dispatcher.totalTaskCount() > 0) {
-  }
-
-  EXPECT_EQ(i, base + repetitions);
-}
-}
diff --git a/osquery/distributed/CMakeLists.txt b/osquery/distributed/CMakeLists.txt
deleted file mode 100644 (file)
index f5a2f97..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-ADD_OSQUERY_LIBRARY(osquery_distributed distributed.cpp)
-
-FILE(GLOB OSQUERY_DISTRIBUTED_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_DISTRIBUTED_TESTS})
diff --git a/osquery/distributed/distributed.cpp b/osquery/distributed/distributed.cpp
deleted file mode 100644 (file)
index 5647b1f..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sstream>
-
-#include <boost/property_tree/json_parser.hpp>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-
-#include "osquery/distributed/distributed.h"
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-FLAG(int32,
-     distributed_retries,
-     3,
-     "Times to retry reading/writing distributed queries");
-
-Status MockDistributedProvider::getQueriesJSON(std::string& query_json) {
-  query_json = queriesJSON_;
-  return Status();
-}
-
-Status MockDistributedProvider::writeResultsJSON(const std::string& results) {
-    resultsJSON_ = results;
-    return Status();
-}
-
-Status DistributedQueryHandler::parseQueriesJSON(
-    const std::string& query_json,
-    std::vector<DistributedQueryRequest>& requests) {
-  // Parse the JSON into a ptree
-  pt::ptree tree;
-  try {
-    std::stringstream query_stream(query_json);
-    pt::read_json(query_stream, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return Status(1, std::string("Error loading query JSON: ") + e.what());
-  }
-
-  // Parse the ptree into DistributedQueryRequests
-  std::vector<DistributedQueryRequest> results;
-  for (const auto& node : tree) {
-    const auto& request_tree = node.second;
-    DistributedQueryRequest request;
-    try {
-      request.query = request_tree.get_child("query").get_value<std::string>();
-      request.id = request_tree.get_child("id").get_value<std::string>();
-    } catch (const std::exception& e) {
-      return Status(1, std::string("Error parsing queries: ") + e.what());
-    }
-    results.push_back(request);
-  }
-
-  requests = std::move(results);
-
-  return Status();
-}
-
-SQL DistributedQueryHandler::handleQuery(const std::string& query_string) {
-  SQL query = SQL(query_string);
-  query.annotateHostInfo();
-  return query;
-}
-
-Status DistributedQueryHandler::serializeResults(
-    const std::vector<std::pair<DistributedQueryRequest, SQL> >& results,
-    pt::ptree& tree) {
-  try {
-    pt::ptree& res_tree = tree.put_child("results", pt::ptree());
-    for (const auto& result : results) {
-      DistributedQueryRequest request = result.first;
-      SQL sql = result.second;
-      pt::ptree& child = res_tree.put_child(request.id, pt::ptree());
-      child.put("status", sql.getStatus().getCode());
-      pt::ptree& rows_child = child.put_child("rows", pt::ptree());
-      Status s = serializeQueryData(sql.rows(), rows_child);
-      if (!s.ok()) {
-        return s;
-      }
-    }
-  } catch (const std::exception& e) {
-    return Status(1, std::string("Error serializing results: ") + e.what());
-  }
-  return Status();
-}
-
-Status DistributedQueryHandler::doQueries() {
-  // Get and parse the queries
-  Status status;
-  std::string query_json;
-  int retries = 0;
-  do {
-    status = provider_->getQueriesJSON(query_json);
-    ++retries;
-  } while (!status.ok() && retries <= FLAGS_distributed_retries);
-  if (!status.ok()) {
-    return status;
-  }
-
-  std::vector<DistributedQueryRequest> requests;
-  status = parseQueriesJSON(query_json, requests);
-  if (!status.ok()) {
-    return status;
-  }
-
-  // Run the queries
-  std::vector<std::pair<DistributedQueryRequest, SQL> > query_results;
-  std::set<std::string> successful_query_ids;
-  for (const auto& request : requests) {
-    if (executedRequestIds_.find(request.id) != executedRequestIds_.end()) {
-      // We've already successfully returned results for this request, don't
-      // process it again.
-      continue;
-    }
-    SQL query_result = handleQuery(request.query);
-    if (query_result.ok()) {
-      successful_query_ids.insert(request.id);
-    }
-    query_results.push_back({request, query_result});
-  }
-
-  // Serialize the results
-  pt::ptree serialized_results;
-  serializeResults(query_results, serialized_results);
-  std::string json;
-  try {
-    std::ostringstream ss;
-    pt::write_json(ss, serialized_results, false);
-    json = ss.str();
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return Status(1, e.what());
-  }
-
-  // Write the results
-  retries = 0;
-  do {
-    status = provider_->writeResultsJSON(json);
-    ++retries;
-  } while (!status.ok() && retries <= FLAGS_distributed_retries);
-  if (!status.ok()) {
-    return status;
-  }
-
-  // Only note that the queries were successfully completed if we were actually
-  // able to write the results.
-  executedRequestIds_.insert(successful_query_ids.begin(),
-                             successful_query_ids.end());
-
-  return status;
-}
-}
diff --git a/osquery/distributed/distributed.h b/osquery/distributed/distributed.h
deleted file mode 100644 (file)
index fc53f01..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <set>
-#include <vector>
-
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/sql.h>
-
-namespace osquery {
-
-/**
- * @brief This is an interface for distributed query "providers"
- *
- * Providers implement the communication between the distributed query master
- * and the individual host. A provider may utilize any communications strategy
- * that supports reading and writing JSON (i.e. HTTPS requests, reading from a
- * file, querying a message queue, etc.)
- */
-class IDistributedProvider {
-public:
-  virtual ~IDistributedProvider() {}
-
-  /*
-   * @brief Get the JSON string containing the queries to be executed
-   *
-   * @param query_json A string to fill with the retrieved JSON
-   *
-   * @return osquery::Status indicating success or failure of the operation
-   */
-  virtual Status getQueriesJSON(std::string& query_json) = 0;
-
-  /*
-   * @brief Write the results JSON back to the master
-   *
-   * @param results A string containing the results JSON
-   *
-   * @return osquery::Status indicating success or failure of the operation
-   */
-  virtual Status writeResultsJSON(const std::string& results) = 0;
-};
-
-/**
- * @brief A mocked implementation of IDistributedProvider
- *
- * This implementation is useful for writing unit tests of the
- * DistributedQueryHandler functionality.
- */
-class MockDistributedProvider : public IDistributedProvider {
-public:
- // These methods just read/write the corresponding public members
-  Status getQueriesJSON(std::string& query_json) override;
-  Status writeResultsJSON(const std::string& results) override;
-
-  std::string queriesJSON_;
-  std::string resultsJSON_;
-};
-
-/**
- * @brief Small struct containing the query and ID information for a
- * distributed query
- */
-struct DistributedQueryRequest {
-public:
- explicit DistributedQueryRequest() {}
- explicit DistributedQueryRequest(const std::string& q, const std::string& i)
-     : query(q), id(i) {}
-  std::string query;
-  std::string id;
-};
-
-/**
- * @brief The main handler class for distributed queries
- *
- * This class is responsible for implementing the core functionality of
- * distributed queries. It manages state, uses the provider to read/write from
- * the master, and executes queries.
- */
-class DistributedQueryHandler {
-public:
- /**
-  * @brief Construct a new handler with the given provider
-  *
-  * @param provider The provider used retrieving queries and writing results
-  */
- explicit DistributedQueryHandler(
-     std::unique_ptr<IDistributedProvider> provider)
-     : provider_(std::move(provider)) {}
-
- /**
-  * @brief Retrieve queries, run them, and write results
-  *
-  * This is the core method of DistributedQueryHandler, tying together all the
-  * other components to read the requests from the provider, execute the
-  * queries, and write the results back to the provider.
-  *
-  * @return osquery::Status indicating success or failure of the operation
-  */
- Status doQueries();
-
- /**
-  * @brief Run and annotate an individual query
-  *
-  * @param query_string A string containing the query to be executed
-  *
-  * @return A SQL object containing the (annotated) query results
-  */
- static SQL handleQuery(const std::string& query_string);
-
- /**
-  * @brief Serialize the results of all requests into a ptree
-  *
-  * @param results The vector of requests and results
-  * @param tree The tree to serialize results into
-  *
-  * @return osquery::Status indicating success or failure of the operation
-  */
- static Status serializeResults(
-     const std::vector<std::pair<DistributedQueryRequest, SQL> >& results,
-     boost::property_tree::ptree& tree);
-
- /**
-  * @brief Parse the query JSON into the individual query objects
-  *
-  * @param query_json The JSON string containing the queries
-  * @param requests A vector to fill with the query objects
-  *
-  * @return osquery::Status indicating success or failure of the parsing
-  */
-  static Status parseQueriesJSON(const std::string& query_json,
-                                 std::vector<DistributedQueryRequest>& requests);
-
-private:
-  // The provider used to read and write queries and results
-  std::unique_ptr<IDistributedProvider> provider_;
-
-  // Used to store already executed queries to avoid duplication. (Some master
-  // configurations may asynchronously process the results of requests, so a
-  // request might be seen by the host after it has already been executed.)
-  std::set<std::string> executedRequestIds_;
-};
-
-} // namespace osquery
diff --git a/osquery/distributed/tests/distributed_tests.cpp b/osquery/distributed/tests/distributed_tests.cpp
deleted file mode 100644 (file)
index 81b0607..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <iostream>
-
-#include <boost/property_tree/json_parser.hpp>
-#include <boost/property_tree/ptree.hpp>
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/sql.h>
-
-#include "osquery/distributed/distributed.h"
-#include "osquery/sql/sqlite_util.h"
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-// Distributed tests expect an SQL implementation for queries.
-REGISTER_INTERNAL(SQLiteSQLPlugin, "sql", "sql");
-
-class DistributedTests : public testing::Test {};
-
-TEST_F(DistributedTests, test_test_distributed_provider) {
-  MockDistributedProvider p;
-  std::string query_string = "['foo']";
-  std::string result_string = "['bar']";
-
-  p.queriesJSON_ = query_string;
-  std::string query_json;
-  Status s = p.getQueriesJSON(query_json);
-  ASSERT_EQ(Status(), s);
-  EXPECT_EQ(query_string, query_json);
-
-  s = p.writeResultsJSON(result_string);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(result_string, p.resultsJSON_);
-}
-
-TEST_F(DistributedTests, test_parse_query_json) {
-  std::string request_json = "[{\"query\": \"foo\", \"id\": \"bar\"}]";
-  std::vector<DistributedQueryRequest> requests;
-  Status s = DistributedQueryHandler::parseQueriesJSON(request_json, requests);
-  ASSERT_EQ(Status(), s);
-  EXPECT_EQ(1, requests.size());
-  EXPECT_EQ("foo", requests[0].query);
-  EXPECT_EQ("bar", requests[0].id);
-
-  std::string bad_json =
-      "[{\"query\": \"foo\", \"id\": \"bar\"}, {\"query\": \"b\"}]";
-  requests.clear();
-  s = DistributedQueryHandler::parseQueriesJSON(bad_json, requests);
-  ASSERT_FALSE(s.ok());
-  EXPECT_EQ(0, requests.size());
-}
-
-TEST_F(DistributedTests, test_handle_query) {
-  // Access to the internal SQL implementation is only available in core.
-  SQL query = DistributedQueryHandler::handleQuery("SELECT hour from time");
-  ASSERT_TRUE(query.ok());
-  QueryData rows = query.rows();
-  ASSERT_EQ(1, rows.size());
-  EXPECT_EQ(rows[0]["_source_host"], getHostname());
-
-  query = DistributedQueryHandler::handleQuery("bad query");
-  ASSERT_FALSE(query.ok());
-  rows = query.rows();
-  ASSERT_EQ(0, rows.size());
-}
-
-TEST_F(DistributedTests, test_serialize_results_empty) {
-  DistributedQueryRequest r0("foo", "foo_id");
-  MockSQL q0 = MockSQL();
-  pt::ptree tree;
-
-  DistributedQueryHandler::serializeResults({{r0, q0}}, tree);
-
-  EXPECT_EQ(0, tree.get<int>("results.foo_id.status"));
-  EXPECT_TRUE(tree.get_child("results.foo_id.rows").empty());
-}
-
-TEST_F(DistributedTests, test_serialize_results_basic) {
-  DistributedQueryRequest r0("foo", "foo_id");
-  QueryData rows0 = {
-      {{"foo0", "foo0_val"}, {"bar0", "bar0_val"}},
-      {{"foo1", "foo1_val"}, {"bar1", "bar1_val"}},
-  };
-  MockSQL q0 = MockSQL(rows0);
-  pt::ptree tree;
-
-  DistributedQueryHandler::serializeResults({{r0, q0}}, tree);
-
-  EXPECT_EQ(0, tree.get<int>("results.foo_id.status"));
-
-  const pt::ptree& tree_rows = tree.get_child("results.foo_id.rows");
-  EXPECT_EQ(2, tree_rows.size());
-
-  auto row = tree_rows.begin();
-  EXPECT_EQ("foo0_val", row->second.get<std::string>("foo0"));
-  EXPECT_EQ("bar0_val", row->second.get<std::string>("bar0"));
-  ++row;
-  EXPECT_EQ("foo1_val", row->second.get<std::string>("foo1"));
-  EXPECT_EQ("bar1_val", row->second.get<std::string>("bar1"));
-}
-
-TEST_F(DistributedTests, test_serialize_results_multiple) {
-  DistributedQueryRequest r0("foo", "foo_id");
-  QueryData rows0 = {
-      {{"foo0", "foo0_val"}, {"bar0", "bar0_val"}},
-      {{"foo1", "foo1_val"}, {"bar1", "bar1_val"}},
-  };
-  MockSQL q0 = MockSQL(rows0);
-
-  DistributedQueryRequest r1("bar", "bar_id");
-  MockSQL q1 = MockSQL({}, Status(1, "Fail"));
-
-  pt::ptree tree;
-
-  DistributedQueryHandler::serializeResults({{r0, q0}, {r1, q1}}, tree);
-
-  EXPECT_EQ(0, tree.get<int>("results.foo_id.status"));
-  const pt::ptree& tree_rows = tree.get_child("results.foo_id.rows");
-  EXPECT_EQ(2, tree_rows.size());
-  auto row = tree_rows.begin();
-  EXPECT_EQ("foo0_val", row->second.get<std::string>("foo0"));
-  EXPECT_EQ("bar0_val", row->second.get<std::string>("bar0"));
-  ++row;
-  EXPECT_EQ("foo1_val", row->second.get<std::string>("foo1"));
-  EXPECT_EQ("bar1_val", row->second.get<std::string>("bar1"));
-
-  EXPECT_EQ(1, tree.get<int>("results.bar_id.status"));
-  const pt::ptree& fail_rows = tree.get_child("results.bar_id.rows");
-  EXPECT_EQ(0, fail_rows.size());
-}
-
-TEST_F(DistributedTests, test_do_queries) {
-  // Access to the internal SQL implementation is only available in core.
-  auto provider_raw = new MockDistributedProvider();
-  provider_raw->queriesJSON_ =
-      "[ \
-      {\"query\": \"SELECT hour FROM time\", \"id\": \"hour\"},\
-      {\"query\": \"bad\", \"id\": \"bad\"},\
-      {\"query\": \"SELECT minutes FROM time\", \"id\": \"minutes\"}\
-    ]";
-  std::unique_ptr<MockDistributedProvider>
-    provider(provider_raw);
-  DistributedQueryHandler handler(std::move(provider));
-
-  Status s = handler.doQueries();
-  ASSERT_EQ(Status(), s);
-
-  pt::ptree tree;
-  std::istringstream json_stream(provider_raw->resultsJSON_);
-  ASSERT_NO_THROW(pt::read_json(json_stream, tree));
-
-  {
-    EXPECT_EQ(0, tree.get<int>("results.hour.status"));
-    const pt::ptree& tree_rows = tree.get_child("results.hour.rows");
-    EXPECT_EQ(1, tree_rows.size());
-    auto row = tree_rows.begin();
-    EXPECT_GE(row->second.get<int>("hour"), 0);
-    EXPECT_LE(row->second.get<int>("hour"), 24);
-    EXPECT_EQ(getHostname(), row->second.get<std::string>("_source_host"));
-  }
-
-  {
-    // this query should have failed
-    EXPECT_EQ(1, tree.get<int>("results.bad.status"));
-    const pt::ptree& tree_rows = tree.get_child("results.bad.rows");
-    EXPECT_EQ(0, tree_rows.size());
-  }
-
-  {
-    EXPECT_EQ(0, tree.get<int>("results.minutes.status"));
-    const pt::ptree& tree_rows = tree.get_child("results.minutes.rows");
-    EXPECT_EQ(1, tree_rows.size());
-    auto row = tree_rows.begin();
-    EXPECT_GE(row->second.get<int>("minutes"), 0);
-    EXPECT_LE(row->second.get<int>("minutes"), 60);
-    EXPECT_EQ(getHostname(), row->second.get<std::string>("_source_host"));
-  }
-}
-
-TEST_F(DistributedTests, test_duplicate_request) {
-  // Access to the internal SQL implementation is only available in core.
-  auto provider_raw = new MockDistributedProvider();
-  provider_raw->queriesJSON_ =
-      "[{\"query\": \"SELECT hour FROM time\", \"id\": \"hour\"}]";
-  std::unique_ptr<MockDistributedProvider>
-    provider(provider_raw);
-  DistributedQueryHandler handler(std::move(provider));
-
-  Status s = handler.doQueries();
-  ASSERT_EQ(Status(), s);
-
-  pt::ptree tree;
-  std::istringstream json_stream(provider_raw->resultsJSON_);
-  ASSERT_NO_THROW(pt::read_json(json_stream, tree));
-
-  EXPECT_EQ(0, tree.get<int>("results.hour.status"));
-  const pt::ptree& tree_rows = tree.get_child("results.hour.rows");
-  EXPECT_EQ(1, tree_rows.size());
-
-  auto row = tree_rows.begin();
-  EXPECT_GE(row->second.get<int>("hour"), 0);
-  EXPECT_LE(row->second.get<int>("hour"), 24);
-  EXPECT_EQ(getHostname(), row->second.get<std::string>("_source_host"));
-
-  // The second time, 'hour' should not be executed again
-  s = handler.doQueries();
-  ASSERT_EQ(Status(), s);
-  json_stream.str(provider_raw->resultsJSON_);
-  ASSERT_NO_THROW(pt::read_json(json_stream, tree));
-  EXPECT_EQ(0, tree.get_child("results").size());
-}
-}
diff --git a/osquery/events/CMakeLists.txt b/osquery/events/CMakeLists.txt
deleted file mode 100644 (file)
index e2ee61b..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LINK(udev ip4tc)
-
-ADD_OSQUERY_LIBRARY(osquery_events events.cpp)
-ADD_OSQUERY_LIBRARY(osquery_events_linux linux/inotify.cpp
-                                                                                linux/udev.cpp)
-
-FILE(GLOB OSQUERY_EVENTS_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_EVENTS_TESTS})
-
-FILE(GLOB OSQUERY_LINUX_EVENTS_TESTS "linux/tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_LINUX_EVENTS_TESTS})
diff --git a/osquery/events/events.cpp b/osquery/events/events.cpp
deleted file mode 100644 (file)
index 5b0cd8b..0000000
+++ /dev/null
@@ -1,731 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <exception>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/lexical_cast.hpp>
-
-#include <osquery/core.h>
-#include <osquery/events.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-#include "osquery/core/conversions.h"
-#include "osquery/database/db_handle.h"
-
-namespace osquery {
-
-/// Helper cooloff (ms) macro to prevent thread failure thrashing.
-#define EVENTS_COOLOFF 20
-
-FLAG(bool, disable_events, false, "Disable osquery publish/subscribe system");
-
-FLAG(bool,
-     events_optimize,
-     true,
-     "Optimize subscriber select queries (scheduler only)");
-
-FLAG(int32, events_expiry, 86000, "Timeout to expire event subscriber results");
-
-const std::vector<size_t> kEventTimeLists = {
-    1 * 60 * 60, // 1 hour
-    1 * 60, // 1 minute
-    10, // 10 seconds
-};
-
-void publisherSleep(size_t milli) {
-  boost::this_thread::sleep(boost::posix_time::milliseconds(milli));
-}
-
-QueryData EventSubscriberPlugin::genTable(QueryContext& context) {
-  EventTime start = 0, stop = -1;
-  if (context.constraints["time"].getAll().size() > 0) {
-    // Use the 'time' constraint to optimize backing-store lookups.
-    for (const auto& constraint : context.constraints["time"].getAll()) {
-      EventTime expr = 0;
-      try {
-        expr = boost::lexical_cast<EventTime>(constraint.expr);
-      } catch (const boost::bad_lexical_cast& e) {
-        expr = 0;
-      }
-      if (constraint.op == EQUALS) {
-        stop = start = expr;
-        break;
-      } else if (constraint.op == GREATER_THAN) {
-        start = std::max(start, expr + 1);
-      } else if (constraint.op == GREATER_THAN_OR_EQUALS) {
-        start = std::max(start, expr);
-      } else if (constraint.op == LESS_THAN) {
-        stop = std::min(stop, expr - 1);
-      } else if (constraint.op == LESS_THAN_OR_EQUALS) {
-        stop = std::min(stop, expr);
-      }
-    }
-  } else if (kToolType == OSQUERY_TOOL_DAEMON && FLAGS_events_optimize) {
-    // If the daemon is querying a subscriber without a 'time' constraint and
-    // allows optimization, only emit events since the last query.
-    start = optimize_time_;
-    optimize_time_ = getUnixTime() - 1;
-  }
-
-  return get(start, stop);
-}
-
-void EventPublisherPlugin::fire(const EventContextRef& ec, EventTime time) {
-  EventContextID ec_id;
-
-  if (isEnding()) {
-    // Cannot emit/fire while ending
-    return;
-  }
-
-  {
-    boost::lock_guard<boost::mutex> lock(ec_id_lock_);
-    ec_id = next_ec_id_++;
-  }
-
-  // Fill in EventContext ID and time if needed.
-  if (ec != nullptr) {
-    ec->id = ec_id;
-    if (ec->time == 0) {
-      if (time == 0) {
-        time = getUnixTime();
-      }
-      // Todo: add a check to assure normalized (seconds) time.
-      ec->time = time;
-    }
-  }
-
-  for (const auto& subscription : subscriptions_) {
-    auto es = EventFactory::getEventSubscriber(subscription->subscriber_name);
-    if (es->state() == SUBSCRIBER_RUNNING) {
-      fireCallback(subscription, ec);
-    }
-  }
-}
-
-std::set<std::string> EventSubscriberPlugin::getIndexes(EventTime start,
-                                                        EventTime stop,
-                                                        int list_key) {
-  auto db = DBHandle::getInstance();
-  auto index_key = "indexes." + dbNamespace();
-  std::set<std::string> indexes;
-
-  // Keep track of the tail/head of account time while bin searching.
-  EventTime start_max = stop, stop_min = stop, local_start, local_stop;
-  auto types = kEventTimeLists.size();
-  // List types are sized bins of time containing records for this namespace.
-  for (size_t i = 0; i < types; ++i) {
-    auto size = kEventTimeLists[i];
-    if (list_key > 0 && i != list_key) {
-      // A specific list_type was requested, only return bins of this key.
-      continue;
-    }
-
-    std::string time_list;
-    auto list_type = boost::lexical_cast<std::string>(size);
-    auto status = db->Get(kEvents, index_key + "." + list_type, time_list);
-    if (time_list.length() == 0) {
-      // No events in this binning size.
-      return indexes;
-    }
-
-    if (list_key == 0 && i == (types - 1) && types > 1) {
-      // Relax the requested start/stop bounds.
-      if (start != start_max) {
-        start = (start / size) * size;
-        start_max = ((start / size) + 1) * size;
-        if (start_max < stop) {
-          start_max = start + kEventTimeLists[types - 2];
-        }
-      }
-
-      if (stop != stop_min) {
-        stop = ((stop / size) + 1) * size;
-        stop_min = (stop / size) * size;
-        if (stop_min > start) {
-          stop_min = stop_min - kEventTimeLists[types - 1];
-        }
-      }
-    } else if (list_key > 0 || types == 1) {
-      // Relax the requested bounds to fit the requested/only index.
-      start = (start / size) * size;
-      start_max = ((start_max / size) + 1) * size;
-    }
-
-    // (1) The first iteration will have 1 range (start to start_max=stop).
-    // (2) Intermediate iterations will have 2 (start-start_max, stop-stop_min).
-    // For each iteration the range collapses based on the coverage using
-    // the first bin's start time and the last bin's stop time.
-    // (3) The last iteration's range includes relaxed bounds outside the
-    // requested start to stop range.
-    std::vector<std::string> all_bins, bins, expirations;
-    boost::split(all_bins, time_list, boost::is_any_of(","));
-    for (const auto& bin : all_bins) {
-      // Bins are identified by the binning size step.
-      auto step = boost::lexical_cast<EventTime>(bin);
-      // Check if size * step -> size * (step + 1) is within a range.
-      int bin_start = size * step, bin_stop = size * (step + 1);
-      if (expire_events_ && expire_time_ > 0) {
-        if (bin_stop <= expire_time_) {
-          expirations.push_back(bin);
-        } else if (bin_start < expire_time_) {
-          expireRecords(list_type, bin, false);
-        }
-      }
-
-      if (bin_start >= start && bin_stop <= start_max) {
-        bins.push_back(bin);
-      } else if ((bin_start >= stop_min && bin_stop <= stop) || stop == 0) {
-        bins.push_back(bin);
-      }
-    }
-
-    // Rewrite the index lists and delete each expired item.
-    if (expirations.size() > 0) {
-      expireIndexes(list_type, all_bins, expirations);
-    }
-
-    if (bins.size() != 0) {
-      // If more precision was achieved though this list's binning.
-      local_start = boost::lexical_cast<EventTime>(bins.front()) * size;
-      start_max = (local_start < start_max) ? local_start : start_max;
-      local_stop = (boost::lexical_cast<EventTime>(bins.back()) + 1) * size;
-      stop_min = (local_stop < stop_min) ? local_stop : stop_min;
-    }
-
-    for (const auto& bin : bins) {
-      indexes.insert(list_type + "." + bin);
-    }
-
-    if (start == start_max && stop == stop_min) {
-      break;
-    }
-  }
-
-  // Update the new time that events expire to now - expiry.
-  return indexes;
-}
-
-void EventSubscriberPlugin::expireRecords(const std::string& list_type,
-                                          const std::string& index,
-                                          bool all) {
-  auto db = DBHandle::getInstance();
-  auto record_key = "records." + dbNamespace();
-  auto data_key = "data." + dbNamespace();
-
-  // If the expirations is not removing all records, rewrite the persisting.
-  std::vector<std::string> persisting_records;
-  // Request all records within this list-size + bin offset.
-  auto expired_records = getRecords({list_type + "." + index});
-  for (const auto& record : expired_records) {
-    if (all) {
-      db->Delete(kEvents, data_key + "." + record.first);
-    } else if (record.second > expire_time_) {
-      persisting_records.push_back(record.first + ":" +
-                                   std::to_string(record.second));
-    }
-  }
-
-  // Either drop or overwrite the record list.
-  if (all) {
-    db->Delete(kEvents, record_key + "." + list_type + "." + index);
-  } else {
-    auto new_records = boost::algorithm::join(persisting_records, ",");
-    db->Put(kEvents, record_key + "." + list_type + "." + index, new_records);
-  }
-}
-
-void EventSubscriberPlugin::expireIndexes(
-    const std::string& list_type,
-    const std::vector<std::string>& indexes,
-    const std::vector<std::string>& expirations) {
-  auto db = DBHandle::getInstance();
-  auto index_key = "indexes." + dbNamespace();
-
-  // Construct a mutable list of persisting indexes to rewrite as records.
-  std::vector<std::string> persisting_indexes = indexes;
-  // Remove the records using the list of expired indexes.
-  for (const auto& bin : expirations) {
-    expireRecords(list_type, bin, true);
-    persisting_indexes.erase(
-        std::remove(persisting_indexes.begin(), persisting_indexes.end(), bin),
-        persisting_indexes.end());
-  }
-
-  // Update the list of indexes with the non-expired indexes.
-  auto new_indexes = boost::algorithm::join(persisting_indexes, ",");
-  db->Put(kEvents, index_key + "." + list_type, new_indexes);
-}
-
-std::vector<EventRecord> EventSubscriberPlugin::getRecords(
-    const std::set<std::string>& indexes) {
-  auto db = DBHandle::getInstance();
-  auto record_key = "records." + dbNamespace();
-
-  std::vector<EventRecord> records;
-  for (const auto& index : indexes) {
-    std::string record_value;
-    if (!db->Get(kEvents, record_key + "." + index, record_value).ok()) {
-      return records;
-    }
-
-    if (record_value.length() == 0) {
-      // There are actually no events in this bin, interesting error case.
-      continue;
-    }
-
-    // Each list is tokenized into a record=event_id:time.
-    std::vector<std::string> bin_records;
-    boost::split(bin_records, record_value, boost::is_any_of(",:"));
-    auto bin_it = bin_records.begin();
-    for (; bin_it != bin_records.end(); bin_it++) {
-      std::string eid = *bin_it;
-      EventTime time = boost::lexical_cast<EventTime>(*(++bin_it));
-      records.push_back(std::make_pair(eid, time));
-    }
-  }
-
-  return records;
-}
-
-Status EventSubscriberPlugin::recordEvent(EventID& eid, EventTime time) {
-  Status status;
-  auto db = DBHandle::getInstance();
-  std::string time_value = boost::lexical_cast<std::string>(time);
-
-  // The record is identified by the event type then module name.
-  std::string index_key = "indexes." + dbNamespace();
-  std::string record_key = "records." + dbNamespace();
-  // The list key includes the list type (bin size) and the list ID (bin).
-  std::string list_key;
-  std::string list_id;
-
-  for (auto time_list : kEventTimeLists) {
-    // The list_id is the MOST-Specific key ID, the bin for this list.
-    // If the event time was 13 and the time_list is 5 seconds, lid = 2.
-    list_id = boost::lexical_cast<std::string>(time / time_list);
-    // The list name identifies the 'type' of list.
-    list_key = boost::lexical_cast<std::string>(time_list);
-    // list_key = list_key + "." + list_id;
-
-    {
-      boost::lock_guard<boost::mutex> lock(event_record_lock_);
-      // Append the record (eid, unix_time) to the list bin.
-      std::string record_value;
-      status = db->Get(
-          kEvents, record_key + "." + list_key + "." + list_id, record_value);
-
-      if (record_value.length() == 0) {
-        // This is a new list_id for list_key, append the ID to the indirect
-        // lookup for this list_key.
-        std::string index_value;
-        status = db->Get(kEvents, index_key + "." + list_key, index_value);
-        if (index_value.length() == 0) {
-          // A new index.
-          index_value = list_id;
-        } else {
-          index_value += "," + list_id;
-        }
-        status = db->Put(kEvents, index_key + "." + list_key, index_value);
-        record_value = eid + ":" + time_value;
-      } else {
-        // Tokenize a record using ',' and the EID/time using ':'.
-        record_value += "," + eid + ":" + time_value;
-      }
-      status = db->Put(
-          kEvents, record_key + "." + list_key + "." + list_id, record_value);
-      if (!status.ok()) {
-        LOG(ERROR) << "Could not put Event Record key: " << record_key << "."
-                   << list_key << "." << list_id;
-      }
-    }
-  }
-
-  return Status(0, "OK");
-}
-
-EventID EventSubscriberPlugin::getEventID() {
-  Status status;
-  auto db = DBHandle::getInstance();
-  // First get an event ID from the meta key.
-  std::string eid_key = "eid." + dbNamespace();
-  std::string last_eid_value;
-  std::string eid_value;
-
-  {
-    boost::lock_guard<boost::mutex> lock(event_id_lock_);
-    status = db->Get(kEvents, eid_key, last_eid_value);
-    if (!status.ok()) {
-      last_eid_value = "0";
-    }
-
-    size_t eid = boost::lexical_cast<size_t>(last_eid_value) + 1;
-    eid_value = boost::lexical_cast<std::string>(eid);
-    status = db->Put(kEvents, eid_key, eid_value);
-  }
-
-  if (!status.ok()) {
-    return "0";
-  }
-
-  return eid_value;
-}
-
-QueryData EventSubscriberPlugin::get(EventTime start, EventTime stop) {
-  QueryData results;
-  Status status;
-
-  std::shared_ptr<DBHandle> db;
-  try {
-    db = DBHandle::getInstance();
-  } catch (const std::runtime_error& e) {
-    LOG(ERROR) << "Cannot retrieve subscriber results database is locked";
-    return results;
-  }
-
-  // Get the records for this time range.
-  auto indexes = getIndexes(start, stop);
-  auto records = getRecords(indexes);
-
-  std::vector<EventRecord> mapped_records;
-  for (const auto& record : records) {
-    if (record.second >= start && (record.second <= stop || stop == 0)) {
-      mapped_records.push_back(record);
-    }
-  }
-
-  std::string events_key = "data." + dbNamespace();
-  if (FLAGS_events_expiry > 0) {
-    // Set the expire time to NOW - "configured lifetime".
-    // Index retrieval will apply the constraints checking and auto-expire.
-    expire_time_ = getUnixTime() - FLAGS_events_expiry;
-  }
-
-  // Select mapped_records using event_ids as keys.
-  std::string data_value;
-  for (const auto& record : mapped_records) {
-    Row r;
-    status = db->Get(kEvents, events_key + "." + record.first, data_value);
-    if (data_value.length() == 0) {
-      // THere is no record here, interesting error case.
-      continue;
-    }
-    status = deserializeRowJSON(data_value, r);
-    if (status.ok()) {
-      results.push_back(r);
-    }
-  }
-  return results;
-}
-
-Status EventSubscriberPlugin::add(Row& r, EventTime event_time) {
-  std::shared_ptr<DBHandle> db = nullptr;
-  try {
-    db = DBHandle::getInstance();
-  } catch (const std::runtime_error& e) {
-    return Status(1, e.what());
-  }
-
-  // Get and increment the EID for this module.
-  EventID eid = getEventID();
-  // Without encouraging a missing event time, do not support a 0-time.
-  auto index_time = getUnixTime();
-  if (event_time == 0) {
-    r["time"] = std::to_string(index_time);
-  } else {
-    r["time"] = std::to_string(event_time);
-  }
-
-  // Serialize and store the row data, for query-time retrieval.
-  std::string data;
-  auto status = serializeRowJSON(r, data);
-  if (!status.ok()) {
-    return status;
-  }
-
-  // Store the event data.
-  std::string event_key = "data." + dbNamespace() + "." + eid;
-  status = db->Put(kEvents, event_key, data);
-  // Record the event in the indexing bins, using the index time.
-  recordEvent(eid, event_time);
-  return status;
-}
-
-void EventFactory::delay() {
-  // Caller may disable event publisher threads.
-  if (FLAGS_disable_events) {
-    return;
-  }
-
-  // Create a thread for each event publisher.
-  auto& ef = EventFactory::getInstance();
-  for (const auto& publisher : EventFactory::getInstance().event_pubs_) {
-    auto thread_ = std::make_shared<boost::thread>(
-        boost::bind(&EventFactory::run, publisher.first));
-    ef.threads_.push_back(thread_);
-  }
-}
-
-Status EventFactory::run(EventPublisherID& type_id) {
-  auto& ef = EventFactory::getInstance();
-  if (FLAGS_disable_events) {
-    return Status(0, "Events disabled");
-  }
-
-  // An interesting take on an event dispatched entrypoint.
-  // There is little introspection into the event type.
-  // Assume it can either make use of an entrypoint poller/selector or
-  // take care of async callback registrations in setUp/configure/run
-  // only once and handle event queuing/firing in callbacks.
-  EventPublisherRef publisher = nullptr;
-  try {
-    publisher = ef.getEventPublisher(type_id);
-  } catch (std::out_of_range& e) {
-    return Status(1, "No event type found");
-  }
-
-  if (publisher == nullptr) {
-    return Status(1, "Event publisher is missing");
-  } else if (publisher->hasStarted()) {
-    return Status(1, "Cannot restart an event publisher");
-  }
-  VLOG(1) << "Starting event publisher run loop: " + type_id;
-  publisher->hasStarted(true);
-
-  auto status = Status(0, "OK");
-  while (!publisher->isEnding() && status.ok()) {
-    // Can optionally implement a global cooloff latency here.
-    status = publisher->run();
-    osquery::publisherSleep(EVENTS_COOLOFF);
-  }
-  // The runloop status is not reflective of the event type's.
-  VLOG(1) << "Event publisher " << publisher->type()
-          << " run loop terminated for reason: " << status.getMessage();
-  // Publishers auto tear down when their run loop stops.
-  publisher->tearDown();
-  ef.event_pubs_.erase(type_id);
-  return Status(0, "OK");
-}
-
-// There's no reason for the event factory to keep multiple instances.
-EventFactory& EventFactory::getInstance() {
-  static EventFactory ef;
-  return ef;
-}
-
-Status EventFactory::registerEventPublisher(const PluginRef& pub) {
-  // Try to downcast the plugin to an event publisher.
-  EventPublisherRef specialized_pub;
-  try {
-    auto base_pub = std::dynamic_pointer_cast<EventPublisherPlugin>(pub);
-    specialized_pub = std::static_pointer_cast<BaseEventPublisher>(base_pub);
-  } catch (const std::bad_cast& e) {
-    return Status(1, "Incorrect plugin");
-  }
-
-  if (specialized_pub == nullptr || specialized_pub.get() == nullptr) {
-    return Status(0, "Invalid subscriber");
-  }
-
-  auto& ef = EventFactory::getInstance();
-  auto type_id = specialized_pub->type();
-  if (ef.event_pubs_.count(type_id) != 0) {
-    // This is a duplicate event publisher.
-    return Status(1, "Duplicate publisher type");
-  }
-
-  // Do not set up event publisher if events are disabled.
-  if (!FLAGS_disable_events) {
-    if (!specialized_pub->setUp().ok()) {
-      // Only start event loop if setUp succeeds.
-      return Status(1, "Event publisher setup failed");
-    }
-  }
-
-  ef.event_pubs_[type_id] = specialized_pub;
-  return Status(0, "OK");
-}
-
-Status EventFactory::registerEventSubscriber(const PluginRef& sub) {
-  // Try to downcast the plugin to an event subscriber.
-  EventSubscriberRef specialized_sub;
-  try {
-    auto base_sub = std::dynamic_pointer_cast<EventSubscriberPlugin>(sub);
-    specialized_sub = std::static_pointer_cast<BaseEventSubscriber>(base_sub);
-  } catch (const std::bad_cast& e) {
-    return Status(1, "Incorrect plugin");
-  }
-
-  if (specialized_sub == nullptr || specialized_sub.get() == nullptr) {
-    return Status(1, "Invalid subscriber");
-  }
-
-  // Let the module initialize any Subscriptions.
-  auto status = Status(0, "OK");
-  if (!FLAGS_disable_events) {
-    status = specialized_sub->init();
-  }
-
-  auto& ef = EventFactory::getInstance();
-  ef.event_subs_[specialized_sub->getName()] = specialized_sub;
-
-  // Set state of subscriber.
-  if (!status.ok()) {
-    specialized_sub->state(SUBSCRIBER_FAILED);
-    return Status(1, status.getMessage());
-  } else {
-    specialized_sub->state(SUBSCRIBER_RUNNING);
-    return Status(0, "OK");
-  }
-}
-
-Status EventFactory::addSubscription(EventPublisherID& type_id,
-                                     EventSubscriberID& name_id,
-                                     const SubscriptionContextRef& mc,
-                                     EventCallback cb,
-                                     void* user_data) {
-  auto subscription = Subscription::create(name_id, mc, cb, user_data);
-  return EventFactory::addSubscription(type_id, subscription);
-}
-
-Status EventFactory::addSubscription(EventPublisherID& type_id,
-                                     const SubscriptionRef& subscription) {
-  EventPublisherRef publisher = getInstance().getEventPublisher(type_id);
-  if (publisher == nullptr) {
-    return Status(1, "Unknown event publisher");
-  }
-
-  // The event factory is responsible for configuring the event types.
-  auto status = publisher->addSubscription(subscription);
-  if (!FLAGS_disable_events) {
-    publisher->configure();
-  }
-  return status;
-}
-
-size_t EventFactory::numSubscriptions(EventPublisherID& type_id) {
-  EventPublisherRef publisher;
-  try {
-    publisher = EventFactory::getInstance().getEventPublisher(type_id);
-  } catch (std::out_of_range& e) {
-    return 0;
-  }
-  return publisher->numSubscriptions();
-}
-
-EventPublisherRef EventFactory::getEventPublisher(EventPublisherID& type_id) {
-  if (getInstance().event_pubs_.count(type_id) == 0) {
-    LOG(ERROR) << "Requested unknown event publisher: " + type_id;
-    return nullptr;
-  }
-  return getInstance().event_pubs_.at(type_id);
-}
-
-EventSubscriberRef EventFactory::getEventSubscriber(
-    EventSubscriberID& name_id) {
-  if (!exists(name_id)) {
-    LOG(ERROR) << "Requested unknown event subscriber: " + name_id;
-    return nullptr;
-  }
-  return getInstance().event_subs_.at(name_id);
-}
-
-bool EventFactory::exists(EventSubscriberID& name_id) {
-  return (getInstance().event_subs_.count(name_id) > 0);
-}
-
-Status EventFactory::deregisterEventPublisher(const EventPublisherRef& pub) {
-  return EventFactory::deregisterEventPublisher(pub->type());
-}
-
-Status EventFactory::deregisterEventPublisher(EventPublisherID& type_id) {
-  auto& ef = EventFactory::getInstance();
-  EventPublisherRef publisher;
-  try {
-    publisher = ef.getEventPublisher(type_id);
-  } catch (std::out_of_range& e) {
-    return Status(1, "No event publisher to deregister");
-  }
-
-  if (!FLAGS_disable_events) {
-    publisher->isEnding(true);
-    if (!publisher->hasStarted()) {
-      // If a publisher's run loop was not started, call tearDown since
-      // the setUp happened at publisher registration time.
-      publisher->tearDown();
-      // If the run loop did run the tear down and erase will happen in the
-      // event
-      // thread wrapper when isEnding is next checked.
-      ef.event_pubs_.erase(type_id);
-    } else {
-      publisher->end();
-    }
-  }
-  return Status(0, "OK");
-}
-
-std::vector<std::string> EventFactory::publisherTypes() {
-  std::vector<std::string> types;
-  for (const auto& publisher : getInstance().event_pubs_) {
-    types.push_back(publisher.first);
-  }
-  return types;
-}
-
-std::vector<std::string> EventFactory::subscriberNames() {
-  std::vector<std::string> names;
-  for (const auto& subscriber : getInstance().event_subs_) {
-    names.push_back(subscriber.first);
-  }
-  return names;
-}
-
-void EventFactory::end(bool join) {
-  auto& ef = EventFactory::getInstance();
-
-  // Call deregister on each publisher.
-  for (const auto& publisher : ef.publisherTypes()) {
-    deregisterEventPublisher(publisher);
-  }
-
-  // Stop handling exceptions for the publisher threads.
-  for (const auto& thread : ef.threads_) {
-    if (join) {
-      thread->join();
-    } else {
-      thread->detach();
-    }
-  }
-
-  // A small cool off helps OS API event publisher flushing.
-  if (!FLAGS_disable_events) {
-    ::usleep(400);
-    ef.threads_.clear();
-  }
-}
-
-void attachEvents() {
-  const auto& publishers = Registry::all("event_publisher");
-  for (const auto& publisher : publishers) {
-    EventFactory::registerEventPublisher(publisher.second);
-  }
-
-  const auto& subscribers = Registry::all("event_subscriber");
-  for (const auto& subscriber : subscribers) {
-    auto status = EventFactory::registerEventSubscriber(subscriber.second);
-    if (!status.ok()) {
-      LOG(ERROR) << "Error registering subscriber: " << status.getMessage();
-    }
-  }
-}
-}
diff --git a/osquery/events/linux/inotify.cpp b/osquery/events/linux/inotify.cpp
deleted file mode 100644 (file)
index 69c0845..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sstream>
-
-#include <fnmatch.h>
-#include <linux/limits.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-#include "osquery/events/linux/inotify.h"
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-int kINotifyMLatency = 200;
-
-static const uint32_t BUFFER_SIZE =
-    (10 * ((sizeof(struct inotify_event)) + NAME_MAX + 1));
-
-std::map<int, std::string> kMaskActions = {
-    {IN_ACCESS, "ACCESSED"},
-    {IN_ATTRIB, "ATTRIBUTES_MODIFIED"},
-    {IN_CLOSE_WRITE, "UPDATED"},
-    {IN_CREATE, "CREATED"},
-    {IN_DELETE, "DELETED"},
-    {IN_MODIFY, "UPDATED"},
-    {IN_MOVED_FROM, "MOVED_FROM"},
-    {IN_MOVED_TO, "MOVED_TO"},
-    {IN_OPEN, "OPENED"},
-};
-
-REGISTER(INotifyEventPublisher, "event_publisher", "inotify");
-
-Status INotifyEventPublisher::setUp() {
-  inotify_handle_ = ::inotify_init();
-  // If this does not work throw an exception.
-  if (inotify_handle_ == -1) {
-    return Status(1, "Could not start inotify: inotify_init failed");
-  }
-  return Status(0, "OK");
-}
-
-void INotifyEventPublisher::configure() {
-  for (auto& sub : subscriptions_) {
-    // Anytime a configure is called, try to monitor all subscriptions.
-    // Configure is called as a response to removing/adding subscriptions.
-    // This means recalculating all monitored paths.
-    auto sc = getSubscriptionContext(sub->context);
-    if (sc->discovered_.size() > 0) {
-      continue;
-    }
-
-    sc->discovered_ = sc->path;
-    if (sc->path.find("**") != std::string::npos) {
-      sc->recursive = true;
-      sc->discovered_ = sc->path.substr(0, sc->path.find("**"));
-      sc->path = sc->discovered_;
-    }
-
-    if (sc->path.find('*') != std::string::npos) {
-      // If the wildcard exists within the file (leaf), remove and monitor the
-      // directory instead. Apply a fnmatch on fired events to filter leafs.
-      auto fullpath = fs::path(sc->path);
-      if (fullpath.filename().string().find('*') != std::string::npos) {
-        sc->discovered_ = fullpath.parent_path().string();
-      }
-
-      if (sc->discovered_.find('*') != std::string::npos) {
-        // If a wildcard exists within the tree (stem), resolve at configure
-        // time and monitor each path.
-        std::vector<std::string> paths;
-        resolveFilePattern(sc->discovered_, paths);
-        for (const auto& _path : paths) {
-          addMonitor(_path, sc->recursive);
-        }
-        sc->recursive_match = sc->recursive;
-        continue;
-      }
-    }
-    addMonitor(sc->discovered_, sc->recursive);
-  }
-}
-
-void INotifyEventPublisher::tearDown() {
-  ::close(inotify_handle_);
-  inotify_handle_ = -1;
-}
-
-Status INotifyEventPublisher::restartMonitoring(){
-  if (last_restart_ != 0 && getUnixTime() - last_restart_ < 10) {
-    return Status(1, "Overflow");
-  }
-  last_restart_ = getUnixTime();
-  VLOG(1) << "inotify was overflown, attempting to restart handle";
-  for(const auto& desc : descriptors_){
-    removeMonitor(desc, 1);
-  }
-  path_descriptors_.clear();
-  descriptor_paths_.clear();
-  configure();
-  return Status(0, "OK");
-}
-
-Status INotifyEventPublisher::run() {
-  // Get a while wrapper for free.
-  char buffer[BUFFER_SIZE];
-  fd_set set;
-
-  FD_ZERO(&set);
-  FD_SET(getHandle(), &set);
-
-  struct timeval timeout = {3, 3000};
-  int selector = ::select(getHandle() + 1, &set, nullptr, nullptr, &timeout);
-  if (selector == -1) {
-    LOG(ERROR) << "Could not read inotify handle";
-    return Status(1, "INotify handle failed");
-  }
-
-  if (selector == 0) {
-    // Read timeout.
-    return Status(0, "Continue");
-  }
-  ssize_t record_num = ::read(getHandle(), buffer, BUFFER_SIZE);
-  if (record_num == 0 || record_num == -1) {
-    return Status(1, "INotify read failed");
-  }
-
-  for (char* p = buffer; p < buffer + record_num;) {
-    // Cast the inotify struct, make shared pointer, and append to contexts.
-    auto event = reinterpret_cast<struct inotify_event*>(p);
-    if (event->mask & IN_Q_OVERFLOW) {
-      // The inotify queue was overflown (remove all paths).
-      Status stat = restartMonitoring();
-      if(!stat.ok()){
-        return stat;
-      }
-    }
-
-    if (event->mask & IN_IGNORED) {
-      // This inotify watch was removed.
-      removeMonitor(event->wd, false);
-    } else if (event->mask & IN_MOVE_SELF) {
-      // This inotify path was moved, but is still watched.
-      removeMonitor(event->wd, true);
-    } else if (event->mask & IN_DELETE_SELF) {
-      // A file was moved to replace the watched path.
-      removeMonitor(event->wd, false);
-    } else {
-      auto ec = createEventContextFrom(event);
-      fire(ec);
-    }
-    // Continue to iterate
-    p += (sizeof(struct inotify_event)) + event->len;
-  }
-
-  osquery::publisherSleep(kINotifyMLatency);
-  return Status(0, "Continue");
-}
-
-INotifyEventContextRef INotifyEventPublisher::createEventContextFrom(
-    struct inotify_event* event) {
-  auto shared_event = std::make_shared<struct inotify_event>(*event);
-  auto ec = createEventContext();
-  ec->event = shared_event;
-
-  // Get the pathname the watch fired on.
-  ec->path = descriptor_paths_[event->wd];
-  if (event->len > 1) {
-    ec->path += event->name;
-  }
-
-  for (const auto& action : kMaskActions) {
-    if (event->mask & action.first) {
-      ec->action = action.second;
-      break;
-    }
-  }
-  return ec;
-}
-
-bool INotifyEventPublisher::shouldFire(const INotifySubscriptionContextRef& sc,
-                                       const INotifyEventContextRef& ec) const {
-  if (sc->recursive && !sc->recursive_match) {
-    ssize_t found = ec->path.find(sc->path);
-    if (found != 0) {
-      return false;
-    }
-  } else if (fnmatch((sc->path + "*").c_str(),
-                     ec->path.c_str(),
-                     FNM_PATHNAME | FNM_CASEFOLD |
-                         ((sc->recursive_match) ? FNM_LEADING_DIR : 0)) != 0) {
-    // Only apply a leading-dir match if this is a recursive watch with a
-    // match requirement (an inline wildcard with ending recursive wildcard).
-    return false;
-  }
-  // The subscription may supply a required event mask.
-  if (sc->mask != 0 && !(ec->event->mask & sc->mask)) {
-    return false;
-  }
-
-  // inotify will not monitor recursively, new directories need watches.
-  if(sc->recursive && ec->action == "CREATED" && isDirectory(ec->path)){
-    const_cast<INotifyEventPublisher*>(this)->addMonitor(ec->path + '/', true);
-  }
-
-  return true;
-}
-
-bool INotifyEventPublisher::addMonitor(const std::string& path,
-                                       bool recursive) {
-  if (!isPathMonitored(path)) {
-    int watch = ::inotify_add_watch(getHandle(), path.c_str(), IN_ALL_EVENTS);
-    if (watch == -1) {
-      LOG(ERROR) << "Could not add inotify watch on: " << path;
-      return false;
-    }
-
-    // Keep a list of the watch descriptors
-    descriptors_.push_back(watch);
-    // Keep a map of the path -> watch descriptor
-    path_descriptors_[path] = watch;
-    // Keep a map of the opposite (descriptor -> path)
-    descriptor_paths_[watch] = path;
-  }
-
-  if (recursive && isDirectory(path).ok()) {
-    std::vector<std::string> children;
-    // Get a list of children of this directory (requested recursive watches).
-    listDirectoriesInDirectory(path, children);
-
-    for (const auto& child : children) {
-      addMonitor(child, recursive);
-    }
-  }
-
-  return true;
-}
-
-bool INotifyEventPublisher::removeMonitor(const std::string& path, bool force) {
-  // If force then remove from INotify, otherwise cleanup file descriptors.
-  if (path_descriptors_.find(path) == path_descriptors_.end()) {
-    return false;
-  }
-
-  int watch = path_descriptors_[path];
-  path_descriptors_.erase(path);
-  descriptor_paths_.erase(watch);
-
-  auto position = std::find(descriptors_.begin(), descriptors_.end(), watch);
-  descriptors_.erase(position);
-
-  if (force) {
-    ::inotify_rm_watch(getHandle(), watch);
-  }
-  return true;
-}
-
-bool INotifyEventPublisher::removeMonitor(int watch, bool force) {
-  if (descriptor_paths_.find(watch) == descriptor_paths_.end()) {
-    return false;
-  }
-
-  auto path = descriptor_paths_[watch];
-  return removeMonitor(path, force);
-}
-
-bool INotifyEventPublisher::isPathMonitored(const std::string& path) {
-  boost::filesystem::path parent_path;
-  if (!isDirectory(path).ok()) {
-    if (path_descriptors_.find(path) != path_descriptors_.end()) {
-      // Path is a file, and is directly monitored.
-      return true;
-    }
-    if (!getDirectory(path, parent_path).ok()) {
-      // Could not get parent of unmonitored file.
-      return false;
-    }
-  } else {
-    parent_path = path;
-  }
-
-  // Directory or parent of file monitoring
-  auto path_iterator = path_descriptors_.find(parent_path.string());
-  return (path_iterator != path_descriptors_.end());
-}
-}
diff --git a/osquery/events/linux/inotify.h b/osquery/events/linux/inotify.h
deleted file mode 100644 (file)
index 50d07b8..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <vector>
-
-#include <sys/inotify.h>
-#include <sys/stat.h>
-
-#include <osquery/events.h>
-
-namespace osquery {
-
-extern std::map<int, std::string> kMaskActions;
-
-/**
- * @brief Subscription details for INotifyEventPublisher events.
- *
- * This context is specific to INotifyEventPublisher. It allows the
- * subscribing EventSubscriber to set a path (file or directory) and a
- * limited action mask.
- * Events are passed to the EventSubscriber if they match the context
- * path (or anything within a directory if the path is a directory) and if the
- * event action is part of the mask. If the mask is 0 then all actions are
- * passed to the EventSubscriber.
- */
-struct INotifySubscriptionContext : public SubscriptionContext {
-  /// Subscription the following filesystem path.
-  std::string path;
-  /// Limit the `inotify` actions to the subscription mask (if not 0).
-  uint32_t mask;
-  /// Treat this path as a directory and subscription recursively.
-  bool recursive;
-
-  INotifySubscriptionContext()
-      : mask(0), recursive(false), recursive_match(false) {}
-
-  /**
-   * @brief Helper method to map a string action to `inotify` action mask bit.
-   *
-   * This helper method will set the `mask` value for this SubscriptionContext.
-   *
-   * @param action The string action, a value in kMaskAction%s.
-   */
-  void requireAction(const std::string& action) {
-    for (const auto& bit : kMaskActions) {
-      if (action == bit.second) {
-        mask = mask | bit.first;
-      }
-    }
-  }
-
- private:
-  /// During configure the INotify publisher may modify/optimize the paths.
-  std::string discovered_;
-  /// A configure-time pattern was expanded to match absolute paths.
-  bool recursive_match;
-
- private:
-  friend class INotifyEventPublisher;
-};
-
-/**
- * @brief Event details for INotifyEventPublisher events.
- */
-struct INotifyEventContext : public EventContext {
-  /// The inotify_event structure if the EventSubscriber want to interact.
-  std::shared_ptr<struct inotify_event> event;
-  /// A string path parsed from the inotify_event.
-  std::string path;
-  /// A string action representing the event action `inotify` bit.
-  std::string action;
-  /// A no-op event transaction id.
-  uint32_t transaction_id;
-
-  INotifyEventContext() : event(nullptr), transaction_id(0) {}
-};
-
-typedef std::shared_ptr<INotifyEventContext> INotifyEventContextRef;
-typedef std::shared_ptr<INotifySubscriptionContext>
-    INotifySubscriptionContextRef;
-
-// Thread-safe containers
-typedef std::vector<int> DescriptorVector;
-typedef std::map<std::string, int> PathDescriptorMap;
-typedef std::map<int, std::string> DescriptorPathMap;
-
-/**
- * @brief A Linux `inotify` EventPublisher.
- *
- * This EventPublisher allows EventSubscriber%s to subscription for Linux
- *`inotify` events.
- * Since these events are limited this EventPublisher will optimize the watch
- * descriptors, keep track of the usage, implement optimizations/priority
- * where possible, and abstract file system events to a path/action context.
- *
- * Uses INotifySubscriptionContext and INotifyEventContext for subscriptioning,
- *eventing.
- */
-class INotifyEventPublisher
-    : public EventPublisher<INotifySubscriptionContext, INotifyEventContext> {
-  DECLARE_PUBLISHER("inotify");
-
- public:
-  /// Create an `inotify` handle descriptor.
-  Status setUp();
-  void configure();
-  /// Release the `inotify` handle descriptor.
-  void tearDown();
-
-  Status run();
-
-  INotifyEventPublisher()
-      : EventPublisher(), inotify_handle_(-1), last_restart_(-1) {}
-  /// Check if the application-global `inotify` handle is alive.
-  bool isHandleOpen() { return inotify_handle_ > 0; }
-
- private:
-  INotifyEventContextRef createEventContextFrom(struct inotify_event* event);
-
-  /// Check all added Subscription%s for a path.
-  bool isPathMonitored(const std::string& path);
-
-  /// Add an INotify watch (monitor) on this path.
-  bool addMonitor(const std::string& path, bool recursive);
-
-  /// Remove an INotify watch (monitor) from our tracking.
-  bool removeMonitor(const std::string& path, bool force = false);
-  bool removeMonitor(int watch, bool force = false);
-
-  /// Given a SubscriptionContext and INotifyEventContext match path and action.
-  bool shouldFire(const INotifySubscriptionContextRef& mc,
-                  const INotifyEventContextRef& ec) const;
-
-  /// Get the INotify file descriptor.
-  int getHandle() { return inotify_handle_; }
-
-  /// Get the number of actual INotify active descriptors.
-  int numDescriptors() { return descriptors_.size(); }
-
-  /// If we overflow, try and restart the monitor
-  Status restartMonitoring();
-
-  // Consider an event queue if separating buffering from firing/servicing.
-  DescriptorVector descriptors_;
-
-  /// Map of watched path string to inotify watch file descriptor.
-  PathDescriptorMap path_descriptors_;
-
-  /// Map of inotify watch file descriptor to watched path string.
-  DescriptorPathMap descriptor_paths_;
-
-  /// The inotify file descriptor handle.
-  int inotify_handle_;
-
-  /// Time in seconds of the last inotify restart.
-  int last_restart_;
-
- public:
-  FRIEND_TEST(INotifyTests, test_inotify_optimization);
-};
-}
diff --git a/osquery/events/linux/tests/inotify_tests.cpp b/osquery/events/linux/tests/inotify_tests.cpp
deleted file mode 100644 (file)
index 3557075..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <stdio.h>
-
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
-#include <boost/thread.hpp>
-
-#include <gtest/gtest.h>
-
-#include <osquery/events.h>
-#include <osquery/filesystem.h>
-#include <osquery/tables.h>
-
-#include "osquery/events/linux/inotify.h"
-#include "osquery/core/test_util.h"
-
-namespace osquery {
-
-const std::string kRealTestPath = kTestWorkingDirectory + "inotify-trigger";
-const std::string kRealTestDir = kTestWorkingDirectory + "inotify-triggers";
-const std::string kRealTestDirPath = kRealTestDir + "/1";
-const std::string kRealTestSubDir = kRealTestDir + "/2";
-const std::string kRealTestSubDirPath = kRealTestSubDir + "/1";
-
-int kMaxEventLatency = 3000;
-
-class INotifyTests : public testing::Test {
- protected:
-  void TearDown() {
-    // End the event loops, and join on the threads.
-    boost::filesystem::remove_all(kRealTestPath);
-    boost::filesystem::remove_all(kRealTestDir);
-  }
-
-  void StartEventLoop() {
-    event_pub_ = std::make_shared<INotifyEventPublisher>();
-    auto status = EventFactory::registerEventPublisher(event_pub_);
-    FILE* fd = fopen(kRealTestPath.c_str(), "w");
-    fclose(fd);
-    temp_thread_ = boost::thread(EventFactory::run, "inotify");
-  }
-
-  void StopEventLoop() {
-    while (!event_pub_->hasStarted()) {
-      ::usleep(20);
-    }
-
-    EventFactory::end(true);
-    temp_thread_.join();
-  }
-
-  void SubscriptionAction(const std::string& path,
-                          uint32_t mask = 0,
-                          EventCallback ec = 0) {
-    auto mc = std::make_shared<INotifySubscriptionContext>();
-    mc->path = path;
-    mc->mask = mask;
-
-    EventFactory::addSubscription("inotify", "TestSubscriber", mc, ec);
-  }
-
-  bool WaitForEvents(int max, int num_events = 0) {
-    int delay = 0;
-    while (delay <= max * 1000) {
-      if (num_events > 0 && event_pub_->numEvents() >= num_events) {
-        return true;
-      } else if (num_events == 0 && event_pub_->numEvents() > 0) {
-        return true;
-      }
-      delay += 50;
-      ::usleep(50);
-    }
-    return false;
-  }
-
-  void TriggerEvent(const std::string& path) {
-    FILE* fd = fopen(path.c_str(), "w");
-    fputs("inotify", fd);
-    fclose(fd);
-  }
-
-  std::shared_ptr<INotifyEventPublisher> event_pub_;
-  boost::thread temp_thread_;
-};
-
-TEST_F(INotifyTests, test_register_event_pub) {
-  auto pub = std::make_shared<INotifyEventPublisher>();
-  auto status = EventFactory::registerEventPublisher(pub);
-  EXPECT_TRUE(status.ok());
-
-  // Make sure only one event type exists
-  EXPECT_EQ(EventFactory::numEventPublishers(), 1);
-  // And deregister
-  status = EventFactory::deregisterEventPublisher("inotify");
-  EXPECT_TRUE(status.ok());
-}
-
-TEST_F(INotifyTests, test_inotify_init) {
-  // Handle should not be initialized during ctor.
-  auto event_pub = std::make_shared<INotifyEventPublisher>();
-  EXPECT_FALSE(event_pub->isHandleOpen());
-
-  // Registering the event type initializes inotify.
-  auto status = EventFactory::registerEventPublisher(event_pub);
-  EXPECT_TRUE(status.ok());
-  EXPECT_TRUE(event_pub->isHandleOpen());
-
-  // Similarly deregistering closes the handle.
-  EventFactory::deregisterEventPublisher("inotify");
-  EXPECT_FALSE(event_pub->isHandleOpen());
-}
-
-TEST_F(INotifyTests, test_inotify_add_subscription_missing_path) {
-  auto pub = std::make_shared<INotifyEventPublisher>();
-  EventFactory::registerEventPublisher(pub);
-
-  // This subscription path is fake, and will succeed.
-  auto mc = std::make_shared<INotifySubscriptionContext>();
-  mc->path = "/this/path/is/fake";
-
-  auto subscription = Subscription::create("TestSubscriber", mc);
-  auto status = EventFactory::addSubscription("inotify", subscription);
-  EXPECT_TRUE(status.ok());
-  EventFactory::deregisterEventPublisher("inotify");
-}
-
-TEST_F(INotifyTests, test_inotify_add_subscription_success) {
-  auto pub = std::make_shared<INotifyEventPublisher>();
-  EventFactory::registerEventPublisher(pub);
-
-  // This subscription path *should* be real.
-  auto mc = std::make_shared<INotifySubscriptionContext>();
-  mc->path = "/";
-
-  auto subscription = Subscription::create("TestSubscriber", mc);
-  auto status = EventFactory::addSubscription("inotify", subscription);
-  EXPECT_TRUE(status.ok());
-  EventFactory::deregisterEventPublisher("inotify");
-}
-
-class TestINotifyEventSubscriber
-    : public EventSubscriber<INotifyEventPublisher> {
- public:
-  TestINotifyEventSubscriber() : callback_count_(0) {
-    setName("TestINotifyEventSubscriber");
-  }
-
-  Status init() {
-    callback_count_ = 0;
-    return Status(0, "OK");
-  }
-
-  Status SimpleCallback(const INotifyEventContextRef& ec,
-                        const void* user_data) {
-    callback_count_ += 1;
-    return Status(0, "OK");
-  }
-
-  Status Callback(const INotifyEventContextRef& ec, const void* user_data) {
-    // The following comments are an example Callback routine.
-    // Row r;
-    // r["action"] = ec->action;
-    // r["path"] = ec->path;
-
-    // Normally would call Add here.
-    actions_.push_back(ec->action);
-    callback_count_ += 1;
-    return Status(0, "OK");
-  }
-
-  SCRef GetSubscription(const std::string& path, uint32_t mask = 0) {
-    auto mc = createSubscriptionContext();
-    mc->path = path;
-    mc->mask = mask;
-    return mc;
-  }
-
-  void WaitForEvents(int max, int num_events = 1) {
-    int delay = 0;
-    while (delay < max * 1000) {
-      if (callback_count_ >= num_events) {
-        return;
-      }
-      ::usleep(50);
-      delay += 50;
-    }
-  }
-
-  std::vector<std::string> actions() { return actions_; }
-
-  int count() { return callback_count_; }
-
- public:
-  int callback_count_;
-  std::vector<std::string> actions_;
-
- private:
-  FRIEND_TEST(INotifyTests, test_inotify_fire_event);
-  FRIEND_TEST(INotifyTests, test_inotify_event_action);
-  FRIEND_TEST(INotifyTests, test_inotify_optimization);
-  FRIEND_TEST(INotifyTests, test_inotify_recursion);
-};
-
-TEST_F(INotifyTests, test_inotify_run) {
-  // Assume event type is registered.
-  event_pub_ = std::make_shared<INotifyEventPublisher>();
-  auto status = EventFactory::registerEventPublisher(event_pub_);
-  EXPECT_TRUE(status.ok());
-
-  // Create a temporary file to watch, open writeable
-  FILE* fd = fopen(kRealTestPath.c_str(), "w");
-
-  // Create a subscriber.
-  auto sub = std::make_shared<TestINotifyEventSubscriber>();
-  EventFactory::registerEventSubscriber(sub);
-
-  // Create a subscriptioning context
-  auto mc = std::make_shared<INotifySubscriptionContext>();
-  mc->path = kRealTestPath;
-  status = EventFactory::addSubscription(
-      "inotify", Subscription::create("TestINotifyEventSubscriber", mc));
-  EXPECT_TRUE(status.ok());
-
-  // Create an event loop thread (similar to main)
-  boost::thread temp_thread(EventFactory::run, "inotify");
-  EXPECT_TRUE(event_pub_->numEvents() == 0);
-
-  // Cause an inotify event by writing to the watched path.
-  fputs("inotify", fd);
-  fclose(fd);
-
-  // Wait for the thread's run loop to select.
-  WaitForEvents(kMaxEventLatency);
-/// Result is different in linux distros.
-  EXPECT_TRUE(event_pub_->numEvents() >= 0);
-  EventFactory::end();
-  temp_thread.join();
-}
-
-TEST_F(INotifyTests, test_inotify_fire_event) {
-  // Assume event type is registered.
-  StartEventLoop();
-  auto sub = std::make_shared<TestINotifyEventSubscriber>();
-  sub->init();
-
-  // Create a subscriptioning context, note the added Event to the symbol
-  auto sc = sub->GetSubscription(kRealTestPath, 0);
-  sub->subscribe(&TestINotifyEventSubscriber::SimpleCallback, sc, nullptr);
-
-  TriggerEvent(kRealTestPath);
-  sub->WaitForEvents(kMaxEventLatency);
-
-  // Make sure our expected event fired (aka subscription callback was called).
-  EXPECT_TRUE(sub->count() > 0);
-  StopEventLoop();
-}
-
-TEST_F(INotifyTests, test_inotify_event_action) {
-  // Assume event type is registered.
-  StartEventLoop();
-  auto sub = std::make_shared<TestINotifyEventSubscriber>();
-  sub->init();
-
-  auto sc = sub->GetSubscription(kRealTestPath, 0);
-  sub->subscribe(&TestINotifyEventSubscriber::Callback, sc, nullptr);
-
-  TriggerEvent(kRealTestPath);
-  sub->WaitForEvents(kMaxEventLatency, 4);
-
-  // Make sure the inotify action was expected.
-/// Result is different in linux distros.
-  EXPECT_TRUE(sub->actions().size() >= 0);
-/*
-  EXPECT_EQ(sub->actions().size(), 4);
-  EXPECT_EQ(sub->actions()[0], "UPDATED");
-  EXPECT_EQ(sub->actions()[1], "OPENED");
-  EXPECT_EQ(sub->actions()[2], "UPDATED");
-  EXPECT_EQ(sub->actions()[3], "UPDATED");
-*/
-  StopEventLoop();
-}
-
-TEST_F(INotifyTests, test_inotify_optimization) {
-  // Assume event type is registered.
-  StartEventLoop();
-  boost::filesystem::create_directory(kRealTestDir);
-
-  // Adding a descriptor to a directory will monitor files within.
-  SubscriptionAction(kRealTestDir);
-  EXPECT_TRUE(event_pub_->isPathMonitored(kRealTestDirPath));
-
-  // Adding a subscription to a file within a monitored directory is fine
-  // but this will NOT cause an additional INotify watch.
-  SubscriptionAction(kRealTestDirPath);
-  EXPECT_EQ(event_pub_->numDescriptors(), 1);
-  StopEventLoop();
-}
-
-TEST_F(INotifyTests, test_inotify_recursion) {
-  StartEventLoop();
-
-  auto sub = std::make_shared<TestINotifyEventSubscriber>();
-  sub->init();
-
-  boost::filesystem::create_directory(kRealTestDir);
-  boost::filesystem::create_directory(kRealTestSubDir);
-
-  // Subscribe to the directory inode
-  auto mc = sub->createSubscriptionContext();
-  mc->path = kRealTestDir;
-  mc->recursive = true;
-  sub->subscribe(&TestINotifyEventSubscriber::Callback, mc, nullptr);
-
-  // Trigger on a subdirectory's file.
-  TriggerEvent(kRealTestSubDirPath);
-
-  sub->WaitForEvents(kMaxEventLatency, 1);
-  EXPECT_TRUE(sub->count() > 0);
-  StopEventLoop();
-}
-}
diff --git a/osquery/events/linux/udev.cpp b/osquery/events/linux/udev.cpp
deleted file mode 100644 (file)
index 2696a0c..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/events.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-#include "osquery/events/linux/udev.h"
-
-namespace osquery {
-
-int kUdevMLatency = 200;
-
-REGISTER(UdevEventPublisher, "event_publisher", "udev");
-
-Status UdevEventPublisher::setUp() {
-  // Create the udev object.
-  handle_ = udev_new();
-  if (!handle_) {
-    return Status(1, "Could not create udev object.");
-  }
-
-  // Set up the udev monitor before scanning/polling.
-  monitor_ = udev_monitor_new_from_netlink(handle_, "udev");
-  udev_monitor_enable_receiving(monitor_);
-
-  return Status(0, "OK");
-}
-
-void UdevEventPublisher::configure() {}
-
-void UdevEventPublisher::tearDown() {
-  if (monitor_ != nullptr) {
-    udev_monitor_unref(monitor_);
-  }
-
-  if (handle_ != nullptr) {
-    udev_unref(handle_);
-  }
-}
-
-Status UdevEventPublisher::run() {
-  int fd = udev_monitor_get_fd(monitor_);
-  fd_set set;
-
-  FD_ZERO(&set);
-  FD_SET(fd, &set);
-
-  struct timeval timeout = {3, 3000};
-  int selector = ::select(fd + 1, &set, nullptr, nullptr, &timeout);
-  if (selector == -1) {
-    LOG(ERROR) << "Could not read udev monitor";
-    return Status(1, "udev monitor failed.");
-  }
-
-  if (selector == 0 || !FD_ISSET(fd, &set)) {
-    // Read timeout.
-    return Status(0, "Timeout");
-  }
-
-  struct udev_device *device = udev_monitor_receive_device(monitor_);
-  if (device == nullptr) {
-    LOG(ERROR) << "udev monitor returned invalid device.";
-    return Status(1, "udev monitor failed.");
-  }
-
-  auto ec = createEventContextFrom(device);
-  fire(ec);
-
-  udev_device_unref(device);
-
-  osquery::publisherSleep(kUdevMLatency);
-  return Status(0, "Continue");
-}
-
-std::string UdevEventPublisher::getValue(struct udev_device* device,
-                                         const std::string& property) {
-  auto value = udev_device_get_property_value(device, property.c_str());
-  if (value != nullptr) {
-    return std::string(value);
-  }
-  return "";
-}
-
-std::string UdevEventPublisher::getAttr(struct udev_device* device,
-                                        const std::string& attr) {
-  auto value = udev_device_get_sysattr_value(device, attr.c_str());
-  if (value != nullptr) {
-    return std::string(value);
-  }
-  return "";
-}
-
-UdevEventContextRef UdevEventPublisher::createEventContextFrom(
-    struct udev_device* device) {
-  auto ec = createEventContext();
-  ec->device = device;
-  // Map the action string to the eventing enum.
-  ec->action = UDEV_EVENT_ACTION_UNKNOWN;
-  ec->action_string = std::string(udev_device_get_action(device));
-  if (ec->action_string == "add") {
-    ec->action = UDEV_EVENT_ACTION_ADD;
-  } else if (ec->action_string == "remove") {
-    ec->action = UDEV_EVENT_ACTION_REMOVE;
-  } else if (ec->action_string == "change") {
-    ec->action = UDEV_EVENT_ACTION_CHANGE;
-  }
-
-  // Set the subscription-aware variables for the event.
-  auto value = udev_device_get_subsystem(device);
-  if (value != nullptr) {
-    ec->subsystem = std::string(value);
-  }
-
-  value = udev_device_get_devnode(device);
-  if (value != nullptr) {
-    ec->devnode = std::string(value);
-  }
-
-  value = udev_device_get_devtype(device);
-  if (value != nullptr) {
-    ec->devtype = std::string(value);
-  }
-
-  value = udev_device_get_driver(device);
-  if (value != nullptr) {
-    ec->driver = std::string(value);
-  }
-
-  return ec;
-}
-
-bool UdevEventPublisher::shouldFire(const UdevSubscriptionContextRef& sc,
-                                    const UdevEventContextRef& ec) const {
-  if (sc->action != UDEV_EVENT_ACTION_ALL) {
-    if (sc->action != ec->action) {
-      return false;
-    }
-  }
-
-  if (sc->subsystem.length() != 0 && sc->subsystem != ec->subsystem) {
-    return false;
-  } else if (sc->devnode.length() != 0 && sc->devnode != ec->devnode) {
-    return false;
-  } else if (sc->devtype.length() != 0 && sc->devtype != ec->devtype) {
-    return false;
-  } else if (sc->driver.length() != 0 && sc->driver != ec->driver) {
-    return false;
-  }
-
-  return true;
-}
-}
diff --git a/osquery/events/linux/udev.h b/osquery/events/linux/udev.h
deleted file mode 100644 (file)
index 7ce8deb..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <libudev.h>
-
-#include <osquery/events.h>
-#include <osquery/status.h>
-
-namespace osquery {
-
-enum udev_event_action {
-  UDEV_EVENT_ACTION_ADD = 1,
-  UDEV_EVENT_ACTION_REMOVE = 2,
-  UDEV_EVENT_ACTION_CHANGE = 3,
-  UDEV_EVENT_ACTION_UNKNOWN = 4,
-
-  // Custom subscriber-only catch-all for actions.
-  UDEV_EVENT_ACTION_ALL = 10,
-};
-
-/**
- * @brief Subscriptioning details for UdevEventPublisher events.
- *
- */
-struct UdevSubscriptionContext : public SubscriptionContext {
-  /// The hardware event action, add/remove/change.
-  udev_event_action action;
-
-  /// Restrict to a specific subsystem.
-  std::string subsystem;
-  /// Restrict to a specific devnode.
-  std::string devnode;
-  /// Restrict to a specific devtype.
-  std::string devtype;
-  /// Limit to a specific driver name.
-  std::string driver;
-};
-
-/**
- * @brief Event details for UdevEventPublisher events.
- */
-struct UdevEventContext : public EventContext {
-  /// A pointer to the device object, most subscribers will only use device.
-  struct udev_device* device;
-  /// The udev_event_action identifier.
-  udev_event_action action;
-  /// Action as a string (as given by udev).
-  std::string action_string;
-
-  std::string subsystem;
-  std::string devnode;
-  std::string devtype;
-  std::string driver;
-};
-
-typedef std::shared_ptr<UdevEventContext> UdevEventContextRef;
-typedef std::shared_ptr<UdevSubscriptionContext> UdevSubscriptionContextRef;
-
-/**
- * @brief A Linux `udev` EventPublisher.
- *
- */
-class UdevEventPublisher
-    : public EventPublisher<UdevSubscriptionContext, UdevEventContext> {
-  DECLARE_PUBLISHER("udev");
-
- public:
-  Status setUp();
-  void configure();
-  void tearDown();
-
-  Status run();
-
-  UdevEventPublisher() : EventPublisher() {
-    handle_ = nullptr;
-    monitor_ = nullptr;
-  }
-
-  /**
-   * @brief Return a string representation of a udev property.
-   *
-   * @param device the udev device pointer.
-   * @param property the udev property identifier string.
-   * @return string representation of the property or empty if null.
-   */
-  static std::string getValue(struct udev_device* device,
-                              const std::string& property);
-
-  /**
-   * @brief Return a string representation of a udev system attribute.
-   *
-   * @param device the udev device pointer.
-   * @param property the udev system attribute identifier string.
-   * @return string representation of the attribute or empty if null.
-   */
-  static std::string getAttr(struct udev_device* device,
-                             const std::string& attr);
-
- private:
-  /// udev handle (socket descriptor contained within).
-  struct udev *handle_;
-  struct udev_monitor *monitor_;
-
- private:
-  /// Check subscription details.
-  bool shouldFire(const UdevSubscriptionContextRef& mc,
-                  const UdevEventContextRef& ec) const;
-  /// Helper function to create an EventContext using a udev_device pointer.
-  UdevEventContextRef createEventContextFrom(struct udev_device* device);
-};
-}
diff --git a/osquery/events/tests/events_database_tests.cpp b/osquery/events/tests/events_database_tests.cpp
deleted file mode 100644 (file)
index 68314a7..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/algorithm/string.hpp>
-#include <boost/filesystem/operations.hpp>
-
-#include <gtest/gtest.h>
-
-#include <osquery/events.h>
-#include <osquery/tables.h>
-
-#include "osquery/database/db_handle.h"
-
-namespace osquery {
-
-//const std::string kTestingEventsDBPath = "/tmp/rocksdb-osquery-testevents";
-
-class EventsDatabaseTests : public ::testing::Test {};
-
-class FakeEventPublisher
-    : public EventPublisher<SubscriptionContext, EventContext> {
-  DECLARE_PUBLISHER("FakePublisher");
-};
-
-class FakeEventSubscriber : public EventSubscriber<FakeEventPublisher> {
- public:
-  FakeEventSubscriber() { setName("FakeSubscriber"); }
-  /// Add a fake event at time t
-  Status testAdd(int t) {
-    Row r;
-    r["testing"] = "hello from space";
-    return add(r, t);
-  }
-};
-
-TEST_F(EventsDatabaseTests, test_event_module_id) {
-  auto sub = std::make_shared<FakeEventSubscriber>();
-  sub->doNotExpire();
-
-  // Not normally available outside of EventSubscriber->Add().
-  auto event_id1 = sub->getEventID();
-  EXPECT_EQ(event_id1, "1");
-  auto event_id2 = sub->getEventID();
-  EXPECT_EQ(event_id2, "2");
-}
-
-TEST_F(EventsDatabaseTests, test_event_add) {
-  auto sub = std::make_shared<FakeEventSubscriber>();
-  auto status = sub->testAdd(1);
-  EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsDatabaseTests, test_record_indexing) {
-  auto sub = std::make_shared<FakeEventSubscriber>();
-  auto status = sub->testAdd(2);
-  status = sub->testAdd(11);
-  status = sub->testAdd(61);
-  status = sub->testAdd((1 * 3600) + 1);
-  status = sub->testAdd((2 * 3600) + 1);
-
-  // An "all" range, will pick up everything in the largest index.
-  auto indexes = sub->getIndexes(0, 3 * 3600);
-  auto output = boost::algorithm::join(indexes, ", ");
-  EXPECT_EQ(output, "3600.0, 3600.1, 3600.2");
-
-  // Restrict range to "most specific".
-  indexes = sub->getIndexes(0, 5);
-  output = boost::algorithm::join(indexes, ", ");
-  EXPECT_EQ(output, "10.0");
-
-  // Get a mix of indexes for the lower bounding.
-  indexes = sub->getIndexes(2, (3 * 3600));
-  output = boost::algorithm::join(indexes, ", ");
-  EXPECT_EQ(output, "10.0, 10.1, 3600.1, 3600.2, 60.1");
-
-  // Rare, but test ONLY intermediate indexes.
-  indexes = sub->getIndexes(2, (3 * 3600), 1);
-  output = boost::algorithm::join(indexes, ", ");
-  EXPECT_EQ(output, "60.0, 60.1, 60.120, 60.60");
-
-  // Add specific indexes to the upper bound.
-  status = sub->testAdd((2 * 3600) + 11);
-  status = sub->testAdd((2 * 3600) + 61);
-  indexes = sub->getIndexes(2 * 3600, (2 * 3600) + 62);
-  output = boost::algorithm::join(indexes, ", ");
-  EXPECT_EQ(output, "10.726, 60.120");
-
-  // Request specific lower and upper bounding.
-  indexes = sub->getIndexes(2, (2 * 3600) + 62);
-  output = boost::algorithm::join(indexes, ", ");
-  EXPECT_EQ(output, "10.0, 10.1, 10.726, 3600.1, 60.1, 60.120");
-}
-
-TEST_F(EventsDatabaseTests, test_record_range) {
-  auto sub = std::make_shared<FakeEventSubscriber>();
-
-  // Search within a specific record range.
-  auto indexes = sub->getIndexes(0, 10);
-  auto records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 2); // 1, 2
-
-  // Search within a large bound.
-  indexes = sub->getIndexes(3, 3601);
-  // This will include the 0-10 bucket meaning 1, 2 will show up.
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 5); // 1, 2, 11, 61, 3601
-
-  // Get all of the records.
-  indexes = sub->getIndexes(0, 3 * 3600);
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 8); // 1, 2, 11, 61, 3601, 7201, 7211, 7261
-
-  // stop = 0 is an alias for everything.
-  indexes = sub->getIndexes(0, 0);
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 8);
-}
-
-TEST_F(EventsDatabaseTests, test_record_expiration) {
-  auto sub = std::make_shared<FakeEventSubscriber>();
-
-  // No expiration
-  auto indexes = sub->getIndexes(0, 5000);
-  auto records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 5); // 1, 2, 11, 61, 3601
-
-  sub->expire_events_ = true;
-  sub->expire_time_ = 10;
-  indexes = sub->getIndexes(0, 5000);
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
-
-  indexes = sub->getIndexes(0, 5000, 0);
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
-
-  indexes = sub->getIndexes(0, 5000, 1);
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
-
-  indexes = sub->getIndexes(0, 5000, 2);
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
-
-  // Check that get/deletes did not act on cache.
-  sub->expire_time_ = 0;
-  indexes = sub->getIndexes(0, 5000);
-  records = sub->getRecords(indexes);
-  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
-}
-}
diff --git a/osquery/events/tests/events_tests.cpp b/osquery/events/tests/events_tests.cpp
deleted file mode 100644 (file)
index 5a983b1..0000000
+++ /dev/null
@@ -1,385 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <typeinfo>
-
-#include <boost/filesystem/operations.hpp>
-
-#include <gtest/gtest.h>
-
-#include <osquery/events.h>
-#include <osquery/tables.h>
-
-#include "osquery/database/db_handle.h"
-
-namespace osquery {
-
-const std::string kTestingEventsDBPath = "/tmp/rocksdb-osquery-testevents";
-
-class EventsTests : public ::testing::Test {
- public:
-  void SetUp() {
-    // Setup a testing DB instance
-    DBHandle::getInstanceAtPath(kTestingEventsDBPath);
-  }
-
-  void TearDown() {
-    EventFactory::end();
-    boost::filesystem::remove_all(osquery::kTestingEventsDBPath);
-  }
-};
-
-// The most basic event publisher uses useless Subscription/Event.
-class BasicEventPublisher
-    : public EventPublisher<SubscriptionContext, EventContext> {};
-
-class AnotherBasicEventPublisher
-    : public EventPublisher<SubscriptionContext, EventContext> {};
-
-// Create some semi-useless subscription and event structures.
-struct FakeSubscriptionContext : SubscriptionContext {
-  int require_this_value;
-};
-struct FakeEventContext : EventContext {
-  int required_value;
-};
-
-// Typedef the shared_ptr accessors.
-typedef std::shared_ptr<FakeSubscriptionContext> FakeSubscriptionContextRef;
-typedef std::shared_ptr<FakeEventContext> FakeEventContextRef;
-
-// Now a publisher with a type.
-class FakeEventPublisher
-    : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
-  DECLARE_PUBLISHER("FakePublisher");
-};
-
-class AnotherFakeEventPublisher
-    : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
-  DECLARE_PUBLISHER("AnotherFakePublisher");
-};
-
-TEST_F(EventsTests, test_event_pub) {
-  auto pub = std::make_shared<FakeEventPublisher>();
-  EXPECT_EQ(pub->type(), "FakePublisher");
-
-  // Test type names.
-  auto pub_sub = pub->createSubscriptionContext();
-  EXPECT_EQ(typeid(FakeSubscriptionContext), typeid(*pub_sub));
-}
-
-TEST_F(EventsTests, test_register_event_pub) {
-  auto basic_pub = std::make_shared<BasicEventPublisher>();
-  auto status = EventFactory::registerEventPublisher(basic_pub);
-
-  // This class is the SAME, there was no type override.
-  auto another_basic_pub = std::make_shared<AnotherBasicEventPublisher>();
-  status = EventFactory::registerEventPublisher(another_basic_pub);
-  EXPECT_FALSE(status.ok());
-
-  // This class is different but also uses different types!
-  auto fake_pub = std::make_shared<FakeEventPublisher>();
-  status = EventFactory::registerEventPublisher(fake_pub);
-  EXPECT_TRUE(status.ok());
-
-  // May also register the event_pub instance
-  auto another_fake_pub = std::make_shared<AnotherFakeEventPublisher>();
-  status = EventFactory::registerEventPublisher(another_fake_pub);
-  EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsTests, test_event_pub_types) {
-  auto pub = std::make_shared<FakeEventPublisher>();
-  EXPECT_EQ(pub->type(), "FakePublisher");
-
-  EventFactory::registerEventPublisher(pub);
-  auto pub2 = EventFactory::getEventPublisher("FakePublisher");
-  EXPECT_EQ(pub->type(), pub2->type());
-}
-
-TEST_F(EventsTests, test_create_event_pub) {
-  auto pub = std::make_shared<BasicEventPublisher>();
-  auto status = EventFactory::registerEventPublisher(pub);
-  EXPECT_TRUE(status.ok());
-
-  // Make sure only the first event type was recorded.
-  EXPECT_EQ(EventFactory::numEventPublishers(), 1);
-}
-
-class UniqueEventPublisher
-    : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
-  DECLARE_PUBLISHER("unique");
-};
-
-TEST_F(EventsTests, test_create_using_registry) {
-  // The events API uses attachEvents to move registry event publishers and
-  // subscribers into the events factory.
-  EXPECT_EQ(EventFactory::numEventPublishers(), 0);
-  attachEvents();
-
-  // Store the number of default event publishers (in core).
-  int default_publisher_count = EventFactory::numEventPublishers();
-
-  // Now add another registry item, but do not yet attach it.
-  auto UniqueEventPublisherRegistryItem =
-      Registry::add<UniqueEventPublisher>("event_publisher", "unique");
-  EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count);
-
-  // Now attach and make sure it was added.
-  attachEvents();
-  EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count + 1);
-}
-
-TEST_F(EventsTests, test_create_subscription) {
-  auto pub = std::make_shared<BasicEventPublisher>();
-  EventFactory::registerEventPublisher(pub);
-
-  // Make sure a subscription cannot be added for a non-existent event type.
-  // Note: It normally would not make sense to create a blank subscription.
-  auto subscription = Subscription::create("FakeSubscriber");
-  auto status = EventFactory::addSubscription("FakePublisher", subscription);
-  EXPECT_FALSE(status.ok());
-
-  // In this case we can still add a blank subscription to an existing event
-  // type.
-  status = EventFactory::addSubscription("publisher", subscription);
-  EXPECT_TRUE(status.ok());
-
-  // Make sure the subscription is added.
-  EXPECT_EQ(EventFactory::numSubscriptions("publisher"), 1);
-}
-
-TEST_F(EventsTests, test_multiple_subscriptions) {
-  Status status;
-
-  auto pub = std::make_shared<BasicEventPublisher>();
-  EventFactory::registerEventPublisher(pub);
-
-  auto subscription = Subscription::create("subscriber");
-  status = EventFactory::addSubscription("publisher", subscription);
-  status = EventFactory::addSubscription("publisher", subscription);
-
-  EXPECT_EQ(EventFactory::numSubscriptions("publisher"), 2);
-}
-
-struct TestSubscriptionContext : public SubscriptionContext {
-  int smallest;
-};
-
-class TestEventPublisher
-    : public EventPublisher<TestSubscriptionContext, EventContext> {
-  DECLARE_PUBLISHER("TestPublisher");
-
- public:
-  Status setUp() {
-    smallest_ever_ += 1;
-    return Status(0, "OK");
-  }
-
-  void configure() {
-    int smallest_subscription = smallest_ever_;
-
-    configure_run = true;
-    for (const auto& subscription : subscriptions_) {
-      auto subscription_context = getSubscriptionContext(subscription->context);
-      if (smallest_subscription > subscription_context->smallest) {
-        smallest_subscription = subscription_context->smallest;
-      }
-    }
-
-    smallest_ever_ = smallest_subscription;
-  }
-
-  void tearDown() { smallest_ever_ += 1; }
-
-  TestEventPublisher() : EventPublisher() {
-    smallest_ever_ = 0;
-    configure_run = false;
-  }
-
-  // Custom methods do not make sense, but for testing it exists.
-  int getTestValue() { return smallest_ever_; }
-
- public:
-  bool configure_run;
-
- private:
-  int smallest_ever_;
-};
-
-TEST_F(EventsTests, test_create_custom_event_pub) {
-  auto basic_pub = std::make_shared<BasicEventPublisher>();
-  EventFactory::registerEventPublisher(basic_pub);
-  auto pub = std::make_shared<TestEventPublisher>();
-  auto status = EventFactory::registerEventPublisher(pub);
-
-  // These event types have unique event type IDs
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(EventFactory::numEventPublishers(), 2);
-
-  // Make sure the setUp function was called.
-  EXPECT_EQ(pub->getTestValue(), 1);
-}
-
-TEST_F(EventsTests, test_custom_subscription) {
-  // Step 1, register event type
-  auto pub = std::make_shared<TestEventPublisher>();
-  auto status = EventFactory::registerEventPublisher(pub);
-
-  // Step 2, create and configure a subscription context
-  auto sc = std::make_shared<TestSubscriptionContext>();
-  sc->smallest = -1;
-
-  // Step 3, add the subscription to the event type
-  status = EventFactory::addSubscription("TestPublisher", "TestSubscriber", sc);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(pub->numSubscriptions(), 1);
-
-  // The event type must run configure for each added subscription.
-  EXPECT_TRUE(pub->configure_run);
-  EXPECT_EQ(pub->getTestValue(), -1);
-}
-
-TEST_F(EventsTests, test_tear_down) {
-  auto pub = std::make_shared<TestEventPublisher>();
-  auto status = EventFactory::registerEventPublisher(pub);
-
-  // Make sure set up incremented the test value.
-  EXPECT_EQ(pub->getTestValue(), 1);
-
-  status = EventFactory::deregisterEventPublisher("TestPublisher");
-  EXPECT_TRUE(status.ok());
-
-  // Make sure tear down inremented the test value.
-  EXPECT_EQ(pub->getTestValue(), 2);
-
-  // Once more, now deregistering all event types.
-  status = EventFactory::registerEventPublisher(pub);
-  EXPECT_EQ(pub->getTestValue(), 3);
-  EventFactory::end();
-  EXPECT_EQ(pub->getTestValue(), 4);
-
-  // Make sure the factory state represented.
-  EXPECT_EQ(EventFactory::numEventPublishers(), 0);
-}
-
-static int kBellHathTolled = 0;
-
-Status TestTheeCallback(EventContextRef context, const void* user_data) {
-  kBellHathTolled += 1;
-  return Status(0, "OK");
-}
-
-class FakeEventSubscriber : public EventSubscriber<FakeEventPublisher> {
- public:
-  bool bellHathTolled;
-  bool contextBellHathTolled;
-  bool shouldFireBethHathTolled;
-
-  FakeEventSubscriber() {
-    setName("FakeSubscriber");
-    bellHathTolled = false;
-    contextBellHathTolled = false;
-    shouldFireBethHathTolled = false;
-  }
-
-  Status Callback(const EventContextRef& ec, const void* user_data) {
-    // We don't care about the subscription or the event contexts.
-    bellHathTolled = true;
-    return Status(0, "OK");
-  }
-
-  Status SpecialCallback(const FakeEventContextRef& ec, const void* user_data) {
-    // Now we care that the event context is corrected passed.
-    if (ec->required_value == 42) {
-      contextBellHathTolled = true;
-    }
-    return Status(0, "OK");
-  }
-
-  void lateInit() {
-    auto sub_ctx = createSubscriptionContext();
-    subscribe(&FakeEventSubscriber::Callback, sub_ctx, nullptr);
-  }
-
-  void laterInit() {
-    auto sub_ctx = createSubscriptionContext();
-    sub_ctx->require_this_value = 42;
-    subscribe(&FakeEventSubscriber::SpecialCallback, sub_ctx, nullptr);
-  }
-};
-
-TEST_F(EventsTests, test_event_sub) {
-  auto sub = std::make_shared<FakeEventSubscriber>();
-  EXPECT_EQ(sub->getType(), "FakePublisher");
-  EXPECT_EQ(sub->getName(), "FakeSubscriber");
-}
-
-TEST_F(EventsTests, test_event_sub_subscribe) {
-  auto pub = std::make_shared<FakeEventPublisher>();
-  EventFactory::registerEventPublisher(pub);
-
-  auto sub = std::make_shared<FakeEventSubscriber>();
-  EventFactory::registerEventSubscriber(sub);
-
-  // Don't overload the normal `init` Subscription member.
-  sub->lateInit();
-  EXPECT_EQ(pub->numSubscriptions(), 1);
-
-  auto ec = pub->createEventContext();
-  pub->fire(ec, 0);
-
-  EXPECT_TRUE(sub->bellHathTolled);
-}
-
-TEST_F(EventsTests, test_event_sub_context) {
-  auto pub = std::make_shared<FakeEventPublisher>();
-  EventFactory::registerEventPublisher(pub);
-
-  auto sub = std::make_shared<FakeEventSubscriber>();
-  EventFactory::registerEventSubscriber(sub);
-
-  sub->laterInit();
-  auto ec = pub->createEventContext();
-  ec->required_value = 42;
-  pub->fire(ec, 0);
-
-  EXPECT_TRUE(sub->contextBellHathTolled);
-}
-
-TEST_F(EventsTests, test_fire_event) {
-  Status status;
-
-  auto pub = std::make_shared<BasicEventPublisher>();
-  status = EventFactory::registerEventPublisher(pub);
-
-  auto sub = std::make_shared<FakeEventSubscriber>();
-  auto subscription = Subscription::create("FakeSubscriber");
-  subscription->callback = TestTheeCallback;
-  status = EventFactory::addSubscription("publisher", subscription);
-
-  // The event context creation would normally happen in the event type.
-  auto ec = pub->createEventContext();
-  pub->fire(ec, 0);
-  EXPECT_EQ(kBellHathTolled, 1);
-
-  auto second_subscription = Subscription::create("FakeSubscriber");
-  status = EventFactory::addSubscription("publisher", second_subscription);
-
-  // Now there are two subscriptions (one sans callback).
-  pub->fire(ec, 0);
-  EXPECT_EQ(kBellHathTolled, 2);
-
-  // Now both subscriptions have callbacks.
-  second_subscription->callback = TestTheeCallback;
-  pub->fire(ec, 0);
-  EXPECT_EQ(kBellHathTolled, 4);
-}
-}
diff --git a/osquery/examples/example_extension.cpp b/osquery/examples/example_extension.cpp
deleted file mode 100644 (file)
index b618b96..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/sdk.h>
-
-using namespace osquery;
-
-class ExampleConfigPlugin : public ConfigPlugin {
- public:
-  Status setUp() {
-    LOG(WARNING) << "ExampleConfigPlugin setting up.";
-    return Status(0, "OK");
-  }
-
-  Status genConfig(std::map<std::string, std::string>& config) {
-    config["data"] = "{\"options\": [], \"scheduledQueries\": []}";
-    return Status(0, "OK");
-  }
-};
-
-class ExampleTable : public TablePlugin {
- private:
-  TableColumns columns() const {
-    return {{"example_text", "TEXT"}, {"example_integer", "INTEGER"}};
-  }
-
-  QueryData generate(QueryContext& request) {
-    QueryData results;
-
-    Row r;
-    r["example_text"] = "example";
-    r["example_integer"] = INTEGER(1);
-
-    results.push_back(r);
-    return results;
-  }
-};
-
-REGISTER_EXTERNAL(ExampleConfigPlugin, "config", "example");
-REGISTER_EXTERNAL(ExampleTable, "table", "example");
-
-int main(int argc, char* argv[]) {
-  osquery::Initializer runner(argc, argv, OSQUERY_EXTENSION);
-
-  auto status = startExtension("example", "0.0.1");
-  if (!status.ok()) {
-    LOG(ERROR) << status.getMessage();
-  }
-
-  // Finally shutdown.
-  runner.shutdown();
-  return 0;
-}
diff --git a/osquery/examples/example_module.cpp b/osquery/examples/example_module.cpp
deleted file mode 100644 (file)
index 38a2167..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/sdk.h>
-
-using namespace osquery;
-
-class ExampleTable : public TablePlugin {
- private:
-  TableColumns columns() const {
-    return {{"example_text", "TEXT"}, {"example_integer", "INTEGER"}};
-  }
-
-  QueryData generate(QueryContext& request) {
-    QueryData results;
-
-    Row r;
-    r["example_text"] = "example";
-    r["example_integer"] = INTEGER(1);
-
-    results.push_back(r);
-    return results;
-  }
-};
-
-// Create the module if the environment variable TESTFAIL1 is not defined.
-// This allows the integration tests to, at run time, test the module
-// loading workflow.
-CREATE_MODULE_IF(getenv("TESTFAIL1") == nullptr, "example", "0.0.1", "0.0.0");
-
-void initModule(void) {
-  // Register a plugin from a module if the environment variable TESTFAIL2
-  // is not defined.
-  if (getenv("TESTFAIL2") == nullptr) {
-    REGISTER_MODULE(ExampleTable, "table", "example");
-  }
-}
diff --git a/osquery/extensions/CMakeLists.txt b/osquery/extensions/CMakeLists.txt
deleted file mode 100644 (file)
index fb575ab..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-FIND_PROGRAM(THRIFT_COMPILER thrift /usr/local/bin
-                                                                       /usr/bin
-                                                                       NO_DEFAULT_PATH)
-
-# Generate the thrift intermediate/interface code.
-FILE(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
-ADD_CUSTOM_COMMAND(
-       COMMAND
-               ${THRIFT_COMPILER} --gen cpp:dense "${CMAKE_SOURCE_DIR}/osquery.thrift"
-       DEPENDS
-               "${CMAKE_SOURCE_DIR}/osquery.thrift"
-       WORKING_DIRECTORY
-               "${CMAKE_BINARY_DIR}/generated"
-       OUTPUT
-               ${OSQUERY_THRIFT_GENERATED_FILES})
-
-ADD_OSQUERY_LIBRARY(osquery_extensions ${OSQUERY_THRIFT_GENERATED_FILES}
-                                                                          extensions.cpp
-                                                                          interface.cpp)
-
-FILE(GLOB OSQUERY_EXTENSIONS_TESTS "tests/*.cpp")
-
-# TODO: Resolve failed cases
-#ADD_OSQUERY_TEST(${OSQUERY_EXTENSIONS_TESTS})
diff --git a/osquery/extensions/extensions.cpp b/osquery/extensions/extensions.cpp
deleted file mode 100644 (file)
index 7ca2ea9..0000000
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <csignal>
-
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/events.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/registry.h>
-#include <osquery/sql.h>
-
-#include "osquery/extensions/interface.h"
-#include "osquery/core/watcher.h"
-
-using namespace osquery::extensions;
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-// Millisecond latency between initalizing manager pings.
-const size_t kExtensionInitializeLatencyUS = 20000;
-
-#ifdef __APPLE__
-const std::string kModuleExtension = ".dylib";
-#else
-const std::string kModuleExtension = ".so";
-#endif
-
-CLI_FLAG(bool, disable_extensions, false, "Disable extension API");
-
-CLI_FLAG(string,
-         extensions_socket,
-         "/var/osquery/osquery.em",
-         "Path to the extensions UNIX domain socket")
-
-CLI_FLAG(string,
-         extensions_autoload,
-         "/etc/osquery/extensions.load",
-         "Optional path to a list of autoloaded & managed extensions")
-
-CLI_FLAG(string,
-         extensions_timeout,
-         "3",
-         "Seconds to wait for autoloaded extensions");
-
-CLI_FLAG(string,
-         extensions_interval,
-         "3",
-         "Seconds delay between connectivity checks")
-
-CLI_FLAG(string,
-         modules_autoload,
-         "/etc/osquery/modules.load",
-         "Optional path to a list of autoloaded registry modules")
-
-/**
- * @brief Alias the extensions_socket (used by core) to a simple 'socket'.
- *
- * Extension binaries will more commonly set the path to an extension manager
- * socket. Alias the long switch name to 'socket' for an easier UX.
- *
- * We include timeout and interval, where the 'extensions_' prefix is removed
- * in the alias since we are already within the context of an extension.
- */
-EXTENSION_FLAG_ALIAS(socket, extensions_socket);
-EXTENSION_FLAG_ALIAS(timeout, extensions_timeout);
-EXTENSION_FLAG_ALIAS(interval, extensions_interval);
-
-void ExtensionWatcher::start() {
-  // Watch the manager, if the socket is removed then the extension will die.
-  while (true) {
-    watch();
-    interruptableSleep(interval_);
-  }
-}
-
-void ExtensionWatcher::exitFatal(int return_code) {
-  // Exit the extension.
-  ::exit(return_code);
-}
-
-void ExtensionWatcher::watch() {
-  ExtensionStatus status;
-  try {
-    auto client = EXManagerClient(path_);
-    // Ping the extension manager until it goes down.
-    client.get()->ping(status);
-  } catch (const std::exception& e) {
-    LOG(WARNING) << "Extension watcher ending: osquery core has gone away";
-    exitFatal(0);
-  }
-
-  if (status.code != ExtensionCode::EXT_SUCCESS && fatal_) {
-    exitFatal();
-  }
-}
-
-void ExtensionManagerWatcher::watch() {
-  // Watch the set of extensions, if the socket is removed then the extension
-  // will be deregistered.
-  const auto uuids = Registry::routeUUIDs();
-
-  ExtensionStatus status;
-  for (const auto& uuid : uuids) {
-    try {
-      auto client = EXClient(getExtensionSocket(uuid));
-      // Ping the extension until it goes down.
-      client.get()->ping(status);
-    } catch (const std::exception& e) {
-      failures_[uuid] += 1;
-      continue;
-    }
-
-    if (status.code != ExtensionCode::EXT_SUCCESS) {
-      LOG(INFO) << "Extension UUID " << uuid << " ping failed";
-      failures_[uuid] += 1;
-    } else {
-      failures_[uuid] = 0;
-    }
-  }
-
-  for (const auto& uuid : failures_) {
-    if (uuid.second >= 3) {
-      LOG(INFO) << "Extension UUID " << uuid.first << " has gone away";
-      Registry::removeBroadcast(uuid.first);
-      failures_[uuid.first] = 0;
-    }
-  }
-}
-
-inline Status socketWritable(const fs::path& path) {
-  if (pathExists(path).ok()) {
-    if (!isWritable(path).ok()) {
-      return Status(1, "Cannot write extension socket: " + path.string());
-    }
-
-    if (!remove(path).ok()) {
-      return Status(1, "Cannot remove extension socket: " + path.string());
-    }
-  } else {
-    if (!pathExists(path.parent_path()).ok()) {
-      return Status(1, "Extension socket directory missing: " + path.string());
-    }
-
-    if (!isWritable(path.parent_path()).ok()) {
-      return Status(1, "Cannot write extension socket: " + path.string());
-    }
-  }
-  return Status(0, "OK");
-}
-
-void loadExtensions() {
-  // Disabling extensions will disable autoloading.
-  if (FLAGS_disable_extensions) {
-    return;
-  }
-
-  // Optionally autoload extensions
-  auto status = loadExtensions(FLAGS_extensions_autoload);
-  if (!status.ok()) {
-    VLOG(1) << "Could not autoload extensions: " << status.what();
-  }
-}
-
-void loadModules() {
-  auto status = loadModules(FLAGS_modules_autoload);
-  if (!status.ok()) {
-    VLOG(1) << "Could not autoload modules: " << status.what();
-  }
-}
-
-Status loadExtensions(const std::string& loadfile) {
-  std::string autoload_paths;
-  if (readFile(loadfile, autoload_paths).ok()) {
-    for (auto& path : osquery::split(autoload_paths, "\n")) {
-      boost::trim(path);
-      if (path.size() > 0 && path[0] != '#' && path[0] != ';') {
-        Watcher::addExtensionPath(path);
-      }
-    }
-    return Status(0, "OK");
-  }
-  return Status(1, "Failed reading: " + loadfile);
-}
-
-Status loadModuleFile(const std::string& path) {
-  fs::path module(path);
-  if (safePermissions(module.parent_path().string(), path)) {
-    if (module.extension().string() == kModuleExtension) {
-      // Silently allow module load failures to drop.
-      RegistryModuleLoader loader(module.string());
-      loader.init();
-      return Status(0, "OK");
-    }
-  }
-  return Status(1, "Module check failed");
-}
-
-Status loadModules(const std::string& loadfile) {
-  // Split the search path for modules using a ':' delimiter.
-  std::string autoload_paths;
-  if (readFile(loadfile, autoload_paths).ok()) {
-    auto status = Status(0, "OK");
-    for (auto& module_path : osquery::split(autoload_paths, "\n")) {
-      boost::trim(module_path);
-      auto path_status = loadModuleFile(module_path);
-      if (!path_status.ok()) {
-        status = path_status;
-      }
-    }
-    // Return an aggregate failure if any load fails (invalid search path).
-    return status;
-  }
-  return Status(1, "Failed reading: " + loadfile);
-}
-
-Status extensionPathActive(const std::string& path, bool use_timeout = false) {
-  // Make sure the extension manager path exists, and is writable.
-  size_t delay = 0;
-  // The timeout is given in seconds, but checked interval is microseconds.
-  size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000;
-  if (timeout < kExtensionInitializeLatencyUS * 10) {
-    timeout = kExtensionInitializeLatencyUS * 10;
-  }
-  do {
-    if (pathExists(path) && isWritable(path)) {
-      try {
-        auto client = EXManagerClient(path);
-        return Status(0, "OK");
-      } catch (const std::exception& e) {
-        // Path might exist without a connected extension or extension manager.
-      }
-    }
-    // Only check active once if this check does not allow a timeout.
-    if (!use_timeout || timeout == 0) {
-      break;
-    }
-    // Increase the total wait detail.
-    delay += kExtensionInitializeLatencyUS;
-    ::usleep(kExtensionInitializeLatencyUS);
-  } while (delay < timeout);
-  return Status(1, "Extension socket not available: " + path);
-}
-
-Status startExtension(const std::string& name, const std::string& version) {
-  return startExtension(name, version, "0.0.0");
-}
-
-Status startExtension(const std::string& name,
-                      const std::string& version,
-                      const std::string& min_sdk_version) {
-  Registry::setExternal();
-  // Latency converted to milliseconds, used as a thread interruptible.
-  auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
-  auto status = startExtensionWatcher(FLAGS_extensions_socket, latency, true);
-  if (!status.ok()) {
-    // If the threaded watcher fails to start, fail the extension.
-    return status;
-  }
-
-  status = startExtension(
-// TODO(Sangwan): Sync with upstream code
-      FLAGS_extensions_socket, name, version, min_sdk_version, "1.4.1");
-// HotFix: Below upstream code makes undefined error.
-//   FLAGS_extensions_socket, name, version, min_sdk_version, kSDKVersion);
-  if (!status.ok()) {
-    // If the extension failed to start then the EM is most likely unavailable.
-    return status;
-  }
-
-  try {
-    // The extension does nothing but serve the thrift API.
-    // Join on both the thrift and extension manager watcher services.
-    Dispatcher::joinServices();
-  } catch (const std::exception& e) {
-    // The extension manager may shutdown without notifying the extension.
-    return Status(0, e.what());
-  }
-
-  // An extension will only return on failure.
-  return Status(0, "Extension was shutdown");
-}
-
-Status startExtension(const std::string& manager_path,
-                      const std::string& name,
-                      const std::string& version,
-                      const std::string& min_sdk_version,
-                      const std::string& sdk_version) {
-  // Make sure the extension manager path exists, and is writable.
-  auto status = extensionPathActive(manager_path, true);
-  if (!status.ok()) {
-    return status;
-  }
-
-  // The Registry broadcast is used as the ExtensionRegistry.
-  auto broadcast = Registry::getBroadcast();
-  // The extension will register and provide name, version, sdk details.
-  InternalExtensionInfo info;
-  info.name = name;
-  info.version = version;
-  info.sdk_version = sdk_version;
-  info.min_sdk_version = min_sdk_version;
-
-  // If registration is successful, we will also request the manager's options.
-  InternalOptionList options;
-  // Register the extension's registry broadcast with the manager.
-  ExtensionStatus ext_status;
-  try {
-    auto client = EXManagerClient(manager_path);
-    client.get()->registerExtension(ext_status, info, broadcast);
-    // The main reason for a failed registry is a duplicate extension name
-    // (the extension process is already running), or the extension broadcasts
-    // a duplicate registry item.
-    if (ext_status.code != ExtensionCode::EXT_SUCCESS) {
-      return Status(ext_status.code, ext_status.message);
-    }
-    // Request the core options, mainly to set the active registry plugins for
-    // logger and config.
-    client.get()->options(options);
-  } catch (const std::exception& e) {
-    return Status(1, "Extension register failed: " + std::string(e.what()));
-  }
-
-  // Now that the uuid is known, try to clean up stale socket paths.
-  auto extension_path = getExtensionSocket(ext_status.uuid, manager_path);
-  status = socketWritable(extension_path);
-  if (!status) {
-    return status;
-  }
-
-  // Set the active config and logger plugins. The core will arbitrate if the
-  // plugins are not available in the extension's local registry.
-  Registry::setActive("config", options["config_plugin"].value);
-  Registry::setActive("logger", options["logger_plugin"].value);
-  // Set up all lazy registry plugins and the active config/logger plugin.
-  Registry::setUp();
-
-  // Start the extension's Thrift server
-  Dispatcher::addService(
-      std::make_shared<ExtensionRunner>(manager_path, ext_status.uuid));
-  VLOG(1) << "Extension (" << name << ", " << ext_status.uuid << ", " << version
-          << ", " << sdk_version << ") registered";
-  return Status(0, std::to_string(ext_status.uuid));
-}
-
-Status queryExternal(const std::string& manager_path,
-                     const std::string& query,
-                     QueryData& results) {
-  // Make sure the extension path exists, and is writable.
-  auto status = extensionPathActive(manager_path);
-  if (!status.ok()) {
-    return status;
-  }
-
-  ExtensionResponse response;
-  try {
-    auto client = EXManagerClient(manager_path);
-    client.get()->query(response, query);
-  } catch (const std::exception& e) {
-    return Status(1, "Extension call failed: " + std::string(e.what()));
-  }
-
-  for (const auto& row : response.response) {
-    results.push_back(row);
-  }
-
-  return Status(response.status.code, response.status.message);
-}
-
-Status queryExternal(const std::string& query, QueryData& results) {
-  return queryExternal(FLAGS_extensions_socket, query, results);
-}
-
-Status getQueryColumnsExternal(const std::string& manager_path,
-                               const std::string& query,
-                               TableColumns& columns) {
-  // Make sure the extension path exists, and is writable.
-  auto status = extensionPathActive(manager_path);
-  if (!status.ok()) {
-    return status;
-  }
-
-  ExtensionResponse response;
-  try {
-    auto client = EXManagerClient(manager_path);
-    client.get()->getQueryColumns(response, query);
-  } catch (const std::exception& e) {
-    return Status(1, "Extension call failed: " + std::string(e.what()));
-  }
-
-  // Translate response map: {string: string} to a vector: pair(name, type).
-  for (const auto& column : response.response) {
-    for (const auto& column_detail : column) {
-      columns.push_back(make_pair(column_detail.first, column_detail.second));
-    }
-  }
-
-  return Status(response.status.code, response.status.message);
-}
-
-Status getQueryColumnsExternal(const std::string& query,
-                               TableColumns& columns) {
-  return getQueryColumnsExternal(FLAGS_extensions_socket, query, columns);
-}
-
-Status pingExtension(const std::string& path) {
-  if (FLAGS_disable_extensions) {
-    return Status(1, "Extensions disabled");
-  }
-
-  // Make sure the extension path exists, and is writable.
-  auto status = extensionPathActive(path);
-  if (!status.ok()) {
-    return status;
-  }
-
-  ExtensionStatus ext_status;
-  try {
-    auto client = EXClient(path);
-    client.get()->ping(ext_status);
-  } catch (const std::exception& e) {
-    return Status(1, "Extension call failed: " + std::string(e.what()));
-  }
-
-  return Status(ext_status.code, ext_status.message);
-}
-
-Status getExtensions(ExtensionList& extensions) {
-  if (FLAGS_disable_extensions) {
-    return Status(1, "Extensions disabled");
-  }
-  return getExtensions(FLAGS_extensions_socket, extensions);
-}
-
-Status getExtensions(const std::string& manager_path,
-                     ExtensionList& extensions) {
-  // Make sure the extension path exists, and is writable.
-  auto status = extensionPathActive(manager_path);
-  if (!status.ok()) {
-    return status;
-  }
-
-  InternalExtensionList ext_list;
-  try {
-    auto client = EXManagerClient(manager_path);
-    client.get()->extensions(ext_list);
-  } catch (const std::exception& e) {
-    return Status(1, "Extension call failed: " + std::string(e.what()));
-  }
-
-  // Add the extension manager to the list called (core).
-  extensions[0] = {"core", kVersion, "0.0.0", kSDKVersion};
-
-  // Convert from Thrift-internal list type to RouteUUID/ExtenionInfo type.
-  for (const auto& ext : ext_list) {
-    extensions[ext.first] = {ext.second.name,
-                             ext.second.version,
-                             ext.second.min_sdk_version,
-                             ext.second.sdk_version};
-  }
-
-  return Status(0, "OK");
-}
-
-Status callExtension(const RouteUUID uuid,
-                     const std::string& registry,
-                     const std::string& item,
-                     const PluginRequest& request,
-                     PluginResponse& response) {
-  if (FLAGS_disable_extensions) {
-    return Status(1, "Extensions disabled");
-  }
-  return callExtension(
-      getExtensionSocket(uuid), registry, item, request, response);
-}
-
-Status callExtension(const std::string& extension_path,
-                     const std::string& registry,
-                     const std::string& item,
-                     const PluginRequest& request,
-                     PluginResponse& response) {
-  // Make sure the extension manager path exists, and is writable.
-  auto status = extensionPathActive(extension_path);
-  if (!status.ok()) {
-    return status;
-  }
-
-  ExtensionResponse ext_response;
-  try {
-    auto client = EXClient(extension_path);
-    client.get()->call(ext_response, registry, item, request);
-  }
-  catch (const std::exception& e) {
-    return Status(1, "Extension call failed: " + std::string(e.what()));
-  }
-
-  // Convert from Thrift-internal list type to PluginResponse type.
-  if (ext_response.status.code == ExtensionCode::EXT_SUCCESS) {
-    for (const auto& item : ext_response.response) {
-      response.push_back(item);
-    }
-  }
-  return Status(ext_response.status.code, ext_response.status.message);
-}
-
-Status startExtensionWatcher(const std::string& manager_path,
-                             size_t interval,
-                             bool fatal) {
-  // Make sure the extension manager path exists, and is writable.
-  auto status = extensionPathActive(manager_path, true);
-  if (!status.ok()) {
-    return status;
-  }
-
-  // Start a extension manager watcher, if the manager dies, so should we.
-  Dispatcher::addService(
-      std::make_shared<ExtensionWatcher>(manager_path, interval, fatal));
-  return Status(0, "OK");
-}
-
-Status startExtensionManager() {
-  if (FLAGS_disable_extensions) {
-    return Status(1, "Extensions disabled");
-  }
-  return startExtensionManager(FLAGS_extensions_socket);
-}
-
-Status startExtensionManager(const std::string& manager_path) {
-  // Check if the socket location exists.
-  auto status = socketWritable(manager_path);
-  if (!status.ok()) {
-    return status;
-  }
-
-  // Seconds converted to milliseconds, used as a thread interruptible.
-  auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
-  // Start a extension manager watcher, if the manager dies, so should we.
-  Dispatcher::addService(
-      std::make_shared<ExtensionManagerWatcher>(manager_path, latency));
-
-  // Start the extension manager thread.
-  Dispatcher::addService(
-      std::make_shared<ExtensionManagerRunner>(manager_path));
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/extensions/interface.cpp b/osquery/extensions/interface.cpp
deleted file mode 100644 (file)
index 7099fc4..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-#include "osquery/extensions/interface.h"
-
-using namespace osquery::extensions;
-
-namespace osquery {
-namespace extensions {
-
-void ExtensionHandler::ping(ExtensionStatus& _return) {
-  _return.code = ExtensionCode::EXT_SUCCESS;
-  _return.message = "pong";
-  _return.uuid = uuid_;
-}
-
-void ExtensionHandler::call(ExtensionResponse& _return,
-                            const std::string& registry,
-                            const std::string& item,
-                            const ExtensionPluginRequest& request) {
-  // Call will receive an extension or core's request to call the other's
-  // internal registry call. It is the ONLY actor that resolves registry
-  // item aliases.
-  auto local_item = Registry::getAlias(registry, item);
-
-  PluginResponse response;
-  PluginRequest plugin_request;
-  for (const auto& request_item : request) {
-    // Create a PluginRequest from an ExtensionPluginRequest.
-    plugin_request[request_item.first] = request_item.second;
-  }
-
-  auto status = Registry::call(registry, local_item, plugin_request, response);
-  _return.status.code = status.getCode();
-  _return.status.message = status.getMessage();
-  _return.status.uuid = uuid_;
-
-  if (status.ok()) {
-    for (const auto& response_item : response) {
-      // Translate a PluginResponse to an ExtensionPluginResponse.
-      _return.response.push_back(response_item);
-    }
-  }
-}
-
-void ExtensionManagerHandler::extensions(InternalExtensionList& _return) {
-  refresh();
-  _return = extensions_;
-}
-
-void ExtensionManagerHandler::options(InternalOptionList& _return) {
-  auto flags = Flag::flags();
-  for (const auto& flag : flags) {
-    _return[flag.first].value = flag.second.value;
-    _return[flag.first].default_value = flag.second.default_value;
-    _return[flag.first].type = flag.second.type;
-  }
-}
-
-void ExtensionManagerHandler::registerExtension(
-    ExtensionStatus& _return,
-    const InternalExtensionInfo& info,
-    const ExtensionRegistry& registry) {
-  if (exists(info.name)) {
-    LOG(WARNING) << "Refusing to register duplicate extension " << info.name;
-    _return.code = ExtensionCode::EXT_FAILED;
-    _return.message = "Duplicate extension registered";
-    return;
-  }
-
-  // Every call to registerExtension is assigned a new RouteUUID.
-  RouteUUID uuid = rand();
-  LOG(INFO) << "Registering extension (" << info.name << ", " << uuid
-            << ", version=" << info.version << ", sdk=" << info.sdk_version
-            << ")";
-
-  if (!Registry::addBroadcast(uuid, registry).ok()) {
-    LOG(WARNING) << "Could not add extension (" << info.name << ", " << uuid
-                 << ") broadcast to registry";
-    _return.code = ExtensionCode::EXT_FAILED;
-    _return.message = "Failed adding registry broadcast";
-    return;
-  }
-
-  extensions_[uuid] = info;
-  _return.code = ExtensionCode::EXT_SUCCESS;
-  _return.message = "OK";
-  _return.uuid = uuid;
-}
-
-void ExtensionManagerHandler::deregisterExtension(
-    ExtensionStatus& _return, const ExtensionRouteUUID uuid) {
-  if (extensions_.count(uuid) == 0) {
-    _return.code = ExtensionCode::EXT_FAILED;
-    _return.message = "No extension UUID registered";
-    _return.uuid = 0;
-    return;
-  }
-
-  // On success return the uuid of the now de-registered extension.
-  Registry::removeBroadcast(uuid);
-  extensions_.erase(uuid);
-  _return.code = ExtensionCode::EXT_SUCCESS;
-  _return.uuid = uuid;
-}
-
-void ExtensionManagerHandler::query(ExtensionResponse& _return,
-                                    const std::string& sql) {
-  QueryData results;
-  auto status = osquery::query(sql, results);
-  _return.status.code = status.getCode();
-  _return.status.message = status.getMessage();
-  _return.status.uuid = uuid_;
-
-  if (status.ok()) {
-    for (const auto& row : results) {
-      _return.response.push_back(row);
-    }
-  }
-}
-
-void ExtensionManagerHandler::getQueryColumns(ExtensionResponse& _return,
-                                              const std::string& sql) {
-  TableColumns columns;
-  auto status = osquery::getQueryColumns(sql, columns);
-  _return.status.code = status.getCode();
-  _return.status.message = status.getMessage();
-  _return.status.uuid = uuid_;
-
-  if (status.ok()) {
-    for (const auto& column : columns) {
-      _return.response.push_back({{column.first, column.second}});
-    }
-  }
-}
-
-void ExtensionManagerHandler::refresh() {
-  std::vector<RouteUUID> removed_routes;
-  const auto uuids = Registry::routeUUIDs();
-  for (const auto& ext : extensions_) {
-    // Find extension UUIDs that have gone away.
-    if (std::find(uuids.begin(), uuids.end(), ext.first) == uuids.end()) {
-      removed_routes.push_back(ext.first);
-    }
-  }
-
-  // Remove each from the manager's list of extension metadata.
-  for (const auto& uuid : removed_routes) {
-    extensions_.erase(uuid);
-  }
-}
-
-bool ExtensionManagerHandler::exists(const std::string& name) {
-  refresh();
-
-  // Search the remaining extension list for duplicates.
-  for (const auto& extension : extensions_) {
-    if (extension.second.name == name) {
-      return true;
-    }
-  }
-  return false;
-}
-}
-
-ExtensionRunnerCore::~ExtensionRunnerCore() { remove(path_); }
-
-void ExtensionRunnerCore::stop() {
-  if (server_ != nullptr) {
-    server_->stop();
-  }
-}
-
-void ExtensionRunnerCore::startServer(TProcessorRef processor) {
-  auto transport = TServerTransportRef(new TServerSocket(path_));
-  auto transport_fac = TTransportFactoryRef(new TBufferedTransportFactory());
-  auto protocol_fac = TProtocolFactoryRef(new TBinaryProtocolFactory());
-
-  auto thread_manager_ =
-      ThreadManager::newSimpleThreadManager((size_t)FLAGS_worker_threads, 0);
-  auto thread_fac = ThriftThreadFactory(new PosixThreadFactory());
-  thread_manager_->threadFactory(thread_fac);
-  thread_manager_->start();
-
-  // Start the Thrift server's run loop.
-  server_ = TThreadPoolServerRef(new TThreadPoolServer(
-      processor, transport, transport_fac, protocol_fac, thread_manager_));
-  server_->serve();
-}
-
-void ExtensionRunner::start() {
-  // Create the thrift instances.
-  auto handler = ExtensionHandlerRef(new ExtensionHandler(uuid_));
-  auto processor = TProcessorRef(new ExtensionProcessor(handler));
-
-  VLOG(1) << "Extension service starting: " << path_;
-  try {
-    startServer(processor);
-  } catch (const std::exception& e) {
-    LOG(ERROR) << "Cannot start extension handler: " << path_ << " ("
-               << e.what() << ")";
-  }
-}
-
-void ExtensionManagerRunner::start() {
-  // Create the thrift instances.
-  auto handler = ExtensionManagerHandlerRef(new ExtensionManagerHandler());
-  auto processor = TProcessorRef(new ExtensionManagerProcessor(handler));
-
-  VLOG(1) << "Extension manager service starting: " << path_;
-  try {
-    startServer(processor);
-  } catch (const std::exception& e) {
-    LOG(WARNING) << "Extensions disabled: cannot start extension manager ("
-                 << path_ << ") (" << e.what() << ")";
-  }
-}
-}
diff --git a/osquery/extensions/interface.h b/osquery/extensions/interface.h
deleted file mode 100644 (file)
index 4c489db..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <osquery/extensions.h>
-
-#include "osquery/dispatcher/dispatcher.h"
-
-#include <thrift/server/TThreadPoolServer.h>
-#include <thrift/protocol/TBinaryProtocol.h>
-#include <thrift/transport/TServerSocket.h>
-#include <thrift/transport/TBufferTransports.h>
-#include <thrift/transport/TSocket.h>
-
-#ifdef OSQUERY_THRIFT
-#include "Extension.h"
-#include "ExtensionManager.h"
-#else
-#error "Required -DOSQUERY_THRIFT=/path/to/thrift/gen-cpp"
-#endif
-
-namespace osquery {
-
-using namespace apache::thrift;
-using namespace apache::thrift::protocol;
-using namespace apache::thrift::transport;
-using namespace apache::thrift::server;
-using namespace apache::thrift::concurrency;
-
-/// Create easier to reference typedefs for Thrift layer implementations.
-#define SHARED_PTR_IMPL OSQUERY_THRIFT_POINTER::shared_ptr
-typedef SHARED_PTR_IMPL<TSocket> TSocketRef;
-typedef SHARED_PTR_IMPL<TTransport> TTransportRef;
-typedef SHARED_PTR_IMPL<TProtocol> TProtocolRef;
-
-typedef SHARED_PTR_IMPL<TProcessor> TProcessorRef;
-typedef SHARED_PTR_IMPL<TServerTransport> TServerTransportRef;
-typedef SHARED_PTR_IMPL<TTransportFactory> TTransportFactoryRef;
-typedef SHARED_PTR_IMPL<TProtocolFactory> TProtocolFactoryRef;
-typedef SHARED_PTR_IMPL<PosixThreadFactory> PosixThreadFactoryRef;
-typedef std::shared_ptr<TThreadPoolServer> TThreadPoolServerRef;
-
-namespace extensions {
-
-/**
- * @brief The Thrift API server used by an osquery Extension process.
- *
- * An extension will load and start a thread to serve the ExtensionHandler
- * Thrift runloop. This handler is the implementation of the thrift IDL spec.
- * It implements all the Extension API handlers.
- *
- */
-class ExtensionHandler : virtual public ExtensionIf {
- public:
-  ExtensionHandler() : uuid_(0) {}
-  explicit ExtensionHandler(RouteUUID uuid) : uuid_(uuid) {}
-
-  /// Ping an Extension for status and metrics.
-  void ping(ExtensionStatus& _return);
-
-  /**
-   * @brief The Thrift API used by Registry::call for an extension route.
-   *
-   * @param _return The return response (combo Status and PluginResponse).
-   * @param registry The name of the Extension registry.
-   * @param item The Extension plugin name.
-   * @param request The plugin request.
-   */
-  void call(ExtensionResponse& _return,
-            const std::string& registry,
-            const std::string& item,
-            const ExtensionPluginRequest& request);
-
- protected:
-  /// Transient UUID assigned to the extension after registering.
-  RouteUUID uuid_;
-};
-
-/**
- * @brief The Thrift API server used by an osquery process.
- *
- * An extension will load and start a thread to serve the
- * ExtensionManagerHandler. This listens for extensions and allows them to
- * register their Registry route information. Calls to the registry may then
- * match a route exposed by an extension.
- * This handler is the implementation of the thrift IDL spec.
- * It implements all the ExtensionManager API handlers.
- *
- */
-class ExtensionManagerHandler : virtual public ExtensionManagerIf,
-                                public ExtensionHandler {
- public:
-  ExtensionManagerHandler() {}
-
-  /// Return a list of Route UUIDs and extension metadata.
-  void extensions(InternalExtensionList& _return);
-
-  /**
-   * @brief Return a map of osquery options (Flags, bootstrap CLI flags).
-   *
-   * osquery options are set via command line flags or overridden by a config
-   * options dictionary. There are some CLI-only flags that should never
-   * be overridden. If a bootstrap flag is changed there is undefined behavior
-   * since bootstrap candidates are settings needed before a configuration
-   * plugin is setUp.
-   *
-   * Extensions may broadcast config or logger plugins that need a snapshot
-   * of the current options. The best example is the `config_plugin` bootstrap
-   * flag.
-   */
-  void options(InternalOptionList& _return);
-
-  /**
-   * @brief Request a Route UUID and advertise a set of Registry routes.
-   *
-   * When an Extension starts it must call registerExtension using a well known
-   * ExtensionManager UNIX domain socket path. The ExtensionManager will check
-   * the broadcasted routes for duplicates as well as enforce SDK version
-   * compatibility checks. On success the Extension is returned a Route UUID and
-   * begins to serve the ExtensionHandler Thrift API.
-   *
-   * @param _return The output Status and optional assigned RouteUUID.
-   * @param info The osquery Thrift-internal Extension metadata container.
-   * @param registry The Extension's Registry::getBroadcast information.
-   */
-  void registerExtension(ExtensionStatus& _return,
-                         const InternalExtensionInfo& info,
-                         const ExtensionRegistry& registry);
-
-  /**
-   * @brief Request an Extension removal and removal of Registry routes.
-   *
-   * When an Extension process is graceful killed it should deregister.
-   * Other privileged tools may choose to deregister an Extension by
-   * the transient Extension's Route UUID, obtained using
-   * ExtensionManagerHandler::extensions.
-   *
-   * @param _return The output Status.
-   * @param uuid The assigned Route UUID to deregister.
-   */
-  void deregisterExtension(ExtensionStatus& _return,
-                           const ExtensionRouteUUID uuid);
-
-  /**
-   * @brief Execute an SQL statement in osquery core.
-   *
-   * Extensions do not have access to the internal SQLite implementation.
-   * For complex queries (beyond select all from a table) the statement must
-   * be passed into SQLite.
-   *
-   * @param _return The output Status and QueryData (as response).
-   * @param sql The sql statement.
-   */
-  void query(ExtensionResponse& _return, const std::string& sql);
-
-  /**
-   * @brief Get SQL column information for SQL statements in osquery core.
-   *
-   * Extensions do not have access to the internal SQLite implementation.
-   * For complex queries (beyond metadata for a table) the statement must
-   * be passed into SQLite.
-   *
-   * @param _return The output Status and TableColumns (as response).
-   * @param sql The sql statement.
-   */
-  void getQueryColumns(ExtensionResponse& _return, const std::string& sql);
-
- private:
-  /// Check if an extension exists by the name it registered.
-  bool exists(const std::string& name);
-
-  /// Introspect into the registry, checking if any extension routes have been
-  /// removed.
-  void refresh();
-
-  /// Maintain a map of extension UUID to metadata for tracking deregistration.
-  InternalExtensionList extensions_;
-};
-
-typedef SHARED_PTR_IMPL<ExtensionHandler> ExtensionHandlerRef;
-typedef SHARED_PTR_IMPL<ExtensionManagerHandler> ExtensionManagerHandlerRef;
-}
-
-/// A Dispatcher service thread that watches an ExtensionManagerHandler.
-class ExtensionWatcher : public InternalRunnable {
- public:
-  virtual ~ExtensionWatcher() {}
-  ExtensionWatcher(const std::string& path, size_t interval, bool fatal)
-      : path_(path), interval_(interval), fatal_(fatal) {
-    // Set the interval to a minimum of 200 milliseconds.
-    interval_ = (interval_ < 200) ? 200 : interval_;
-  }
-
- public:
-  /// The Dispatcher thread entry point.
-  void start();
-
-  /// Perform health checks.
-  virtual void watch();
-
- protected:
-  /// Exit the extension process with a fatal if the ExtensionManager dies.
-  void exitFatal(int return_code = 1);
-
- protected:
-  /// The UNIX domain socket path for the ExtensionManager.
-  std::string path_;
-
-  /// The internal in milliseconds to ping the ExtensionManager.
-  size_t interval_;
-
-  /// If the ExtensionManager socket is closed, should the extension exit.
-  bool fatal_;
-};
-
-class ExtensionManagerWatcher : public ExtensionWatcher {
- public:
-  ExtensionManagerWatcher(const std::string& path, size_t interval)
-      : ExtensionWatcher(path, interval, false) {}
-
-  /// Start a specialized health check for an ExtensionManager.
-  void watch();
-
- private:
-  /// Allow extensions to fail for several intervals.
-  std::map<RouteUUID, size_t> failures_;
-};
-
-class ExtensionRunnerCore : public InternalRunnable {
- public:
-  virtual ~ExtensionRunnerCore();
-  ExtensionRunnerCore(const std::string& path)
-      : path_(path), server_(nullptr) {}
-
- public:
-  /// Given a handler transport and protocol start a thrift threaded server.
-  void startServer(TProcessorRef processor);
-
-  // The Dispatcher thread service stop point.
-  void stop();
-
- protected:
-  /// The UNIX domain socket used for requests from the ExtensionManager.
-  std::string path_;
-
-  /// Server instance, will be stopped if thread service is removed.
-  TThreadPoolServerRef server_;
-};
-
-/**
- * @brief A Dispatcher service thread that starts ExtensionHandler.
- *
- * This runner will start a Thrift Extension server, call serve, and wait
- * until the extension exists or the ExtensionManager (core) terminates or
- * deregisters the extension.
- *
- */
-class ExtensionRunner : public ExtensionRunnerCore {
- public:
-  ExtensionRunner(const std::string& manager_path, RouteUUID uuid)
-      : ExtensionRunnerCore(""), uuid_(uuid) {
-    path_ = getExtensionSocket(uuid, manager_path);
-  }
-
- public:
-  void start();
-
-  /// Access the UUID provided by the ExtensionManager.
-  RouteUUID getUUID() { return uuid_; }
-
- private:
-  /// The unique and transient Extension UUID assigned by the ExtensionManager.
-  RouteUUID uuid_;
-};
-
-/**
- * @brief A Dispatcher service thread that starts ExtensionManagerHandler.
- *
- * This runner will start a Thrift ExtensionManager server, call serve, and wait
- * until for extensions to register, or thrift API calls.
- *
- */
-class ExtensionManagerRunner : public ExtensionRunnerCore {
- public:
-  explicit ExtensionManagerRunner(const std::string& manager_path)
-      : ExtensionRunnerCore(manager_path) {}
-
- public:
-  void start();
-};
-
-/// Internal accessor for extension clients.
-class EXInternal {
- public:
-  explicit EXInternal(const std::string& path)
-      : socket_(new TSocket(path)),
-        transport_(new TBufferedTransport(socket_)),
-        protocol_(new TBinaryProtocol(transport_)) {}
-
-  virtual ~EXInternal() { transport_->close(); }
-
- protected:
-  TSocketRef socket_;
-  TTransportRef transport_;
-  TProtocolRef protocol_;
-};
-
-/// Internal accessor for a client to an extension (from an extension manager).
-class EXClient : public EXInternal {
- public:
-  explicit EXClient(const std::string& path) : EXInternal(path) {
-    client_ = std::make_shared<extensions::ExtensionClient>(protocol_);
-    (void)transport_->open();
-  }
-
-  const std::shared_ptr<extensions::ExtensionClient>& get() { return client_; }
-
- private:
-  std::shared_ptr<extensions::ExtensionClient> client_;
-};
-
-/// Internal accessor for a client to an extension manager (from an extension).
-class EXManagerClient : public EXInternal {
- public:
-  explicit EXManagerClient(const std::string& manager_path)
-      : EXInternal(manager_path) {
-    client_ = std::make_shared<extensions::ExtensionManagerClient>(protocol_);
-    (void)transport_->open();
-  }
-
-  const std::shared_ptr<extensions::ExtensionManagerClient>& get() {
-    return client_;
-  }
-
- private:
-  std::shared_ptr<extensions::ExtensionManagerClient> client_;
-};
-}
diff --git a/osquery/extensions/tests/extensions_tests.cpp b/osquery/extensions/tests/extensions_tests.cpp
deleted file mode 100644 (file)
index a76022c..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <stdexcept>
-
-#include <gtest/gtest.h>
-
-#include <osquery/extensions.h>
-#include <osquery/filesystem.h>
-
-#include "osquery/core/test_util.h"
-#include "osquery/extensions/interface.h"
-
-using namespace osquery::extensions;
-
-namespace osquery {
-
-const int kDelayUS = 2000;
-const int kTimeoutUS = 1000000;
-const std::string kTestManagerSocket = kTestWorkingDirectory + "test.em";
-
-class ExtensionsTest : public testing::Test {
- protected:
-  void SetUp() {
-    socket_path = kTestManagerSocket + std::to_string(rand());
-    remove(socket_path);
-    if (pathExists(socket_path).ok()) {
-      throw std::domain_error("Cannot test sockets: " + socket_path);
-    }
-  }
-
-  void TearDown() {
-    Dispatcher::stopServices();
-    Dispatcher::joinServices();
-    remove(socket_path);
-  }
-
-  bool ping(int attempts = 3) {
-    // Calling open will except if the socket does not exist.
-    ExtensionStatus status;
-    for (int i = 0; i < attempts; ++i) {
-      try {
-        EXManagerClient client(socket_path);
-        client.get()->ping(status);
-        return (status.code == ExtensionCode::EXT_SUCCESS);
-      } catch (const std::exception& e) {
-        ::usleep(kDelayUS);
-      }
-    }
-
-    return false;
-  }
-
-  QueryData query(const std::string& sql, int attempts = 3) {
-    // Calling open will except if the socket does not exist.
-    ExtensionResponse response;
-    for (int i = 0; i < attempts; ++i) {
-      try {
-        EXManagerClient client(socket_path);
-        client.get()->query(response, sql);
-      } catch (const std::exception& e) {
-        ::usleep(kDelayUS);
-      }
-    }
-
-    QueryData qd;
-    for (const auto& row : response.response) {
-      qd.push_back(row);
-    }
-
-    return qd;
-  }
-
-  ExtensionList registeredExtensions(int attempts = 3) {
-    ExtensionList extensions;
-    for (int i = 0; i < attempts; ++i) {
-      if (getExtensions(socket_path, extensions).ok()) {
-        break;
-      }
-    }
-
-    return extensions;
-  }
-
-  bool socketExists(const std::string& socket_path) {
-    // Wait until the runnable/thread created the socket.
-    int delay = 0;
-    while (delay < kTimeoutUS) {
-      if (pathExists(socket_path).ok() && isReadable(socket_path).ok()) {
-        return true;
-      }
-      ::usleep(kDelayUS);
-      delay += kDelayUS;
-    }
-    return false;
-  }
-
- public:
-  std::string socket_path;
-};
-
-TEST_F(ExtensionsTest, test_manager_runnable) {
-  // Start a testing extension manager.
-  auto status = startExtensionManager(socket_path);
-  EXPECT_TRUE(status.ok());
-  // Call success if the Unix socket was created.
-  EXPECT_TRUE(socketExists(socket_path));
-}
-
-TEST_F(ExtensionsTest, test_extension_runnable) {
-  auto status = startExtensionManager(socket_path);
-  EXPECT_TRUE(status.ok());
-  // Wait for the extension manager to start.
-  EXPECT_TRUE(socketExists(socket_path));
-
-  // Test the extension manager API 'ping' call.
-  EXPECT_TRUE(ping());
-}
-
-TEST_F(ExtensionsTest, test_extension_start) {
-  auto status = startExtensionManager(socket_path);
-  EXPECT_TRUE(status.ok());
-  EXPECT_TRUE(socketExists(socket_path));
-
-  // Now allow duplicates (for testing, since EM/E are the same).
-  Registry::allowDuplicates(true);
-  status = startExtension(socket_path, "test", "0.1", "0.0.0", "0.0.1");
-  // This will not be false since we are allowing deplicate items.
-  // Otherwise, starting an extension and extensionManager would fatal.
-  ASSERT_TRUE(status.ok());
-
-  // The `startExtension` internal call (exposed for testing) returns the
-  // uuid of the extension in the success status.
-  RouteUUID uuid = (RouteUUID)stoi(status.getMessage(), nullptr, 0);
-
-  // We can test-wait for the extensions's socket to open.
-  EXPECT_TRUE(socketExists(socket_path + "." + std::to_string(uuid)));
-
-  // Then clean up the registry modifications.
-  Registry::removeBroadcast(uuid);
-  Registry::allowDuplicates(false);
-}
-
-class ExtensionPlugin : public Plugin {
- public:
-  Status call(const PluginRequest& request, PluginResponse& response) {
-    for (const auto& request_item : request) {
-      response.push_back({{request_item.first, request_item.second}});
-    }
-    return Status(0, "Test success");
-  }
-};
-
-class TestExtensionPlugin : public ExtensionPlugin {};
-
-CREATE_REGISTRY(ExtensionPlugin, "extension_test");
-
-TEST_F(ExtensionsTest, test_extension_broadcast) {
-  auto status = startExtensionManager(socket_path);
-  EXPECT_TRUE(status.ok());
-  EXPECT_TRUE(socketExists(socket_path));
-
-  // This time we're going to add a plugin to the extension_test registry.
-  Registry::add<TestExtensionPlugin>("extension_test", "test_item");
-
-  // Now we create a registry alias that will be broadcasted but NOT used for
-  // internal call lookups. Aliasing was introduced for testing such that an
-  // EM/E could exist in the same process (the same registry) without having
-  // duplicate registry items in the internal registry list AND extension
-  // registry route table.
-  Registry::addAlias("extension_test", "test_item", "test_alias");
-  Registry::allowDuplicates(true);
-
-  // Before registering the extension there is NO route to "test_alias" since
-  // alias resolutions are performed by the EM.
-  EXPECT_TRUE(Registry::exists("extension_test", "test_item"));
-  EXPECT_FALSE(Registry::exists("extension_test", "test_alias"));
-
-  status = startExtension(socket_path, "test", "0.1", "0.0.0", "0.0.1");
-  EXPECT_TRUE(status.ok());
-
-  RouteUUID uuid;
-  try {
-    uuid = (RouteUUID)stoi(status.getMessage(), nullptr, 0);
-  } catch (const std::exception& e) {
-    EXPECT_TRUE(false);
-    return;
-  }
-
-  auto ext_socket = socket_path + "." + std::to_string(uuid);
-  EXPECT_TRUE(socketExists(ext_socket));
-
-  // Make sure the EM registered the extension (called in start extension).
-  auto extensions = registeredExtensions();
-  // Expect two, since `getExtensions` includes the core.
-  ASSERT_EQ(extensions.size(), 2);
-  EXPECT_EQ(extensions.count(uuid), 1);
-  EXPECT_EQ(extensions.at(uuid).name, "test");
-  EXPECT_EQ(extensions.at(uuid).version, "0.1");
-  EXPECT_EQ(extensions.at(uuid).sdk_version, "0.0.1");
-
-  // We are broadcasting to our own registry in the test, which internally has
-  // a "test_item" aliased to "test_alias", "test_item" is internally callable
-  // but "test_alias" can only be resolved by an EM call.
-  EXPECT_TRUE(Registry::exists("extension_test", "test_item"));
-  // Now "test_alias" exists since it is in the extensions route table.
-  EXPECT_TRUE(Registry::exists("extension_test", "test_alias"));
-
-  PluginResponse response;
-  // This registry call will fail, since "test_alias" cannot be resolved using
-  // a local registry call.
-  status = Registry::call("extension_test", "test_alias", {{}}, response);
-  EXPECT_FALSE(status.ok());
-
-  // The following will be the result of a:
-  //   Registry::call("extension_test", "test_alias", {{}}, response);
-  status = callExtension(ext_socket,
-                         "extension_test",
-                         "test_alias",
-                         {{"test_key", "test_value"}},
-                         response);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(response.size(), 1);
-  EXPECT_EQ(response[0]["test_key"], "test_value");
-
-  Registry::removeBroadcast(uuid);
-  Registry::allowDuplicates(false);
-}
-
-TEST_F(ExtensionsTest, test_extension_module_search) {
-  createMockFileStructure();
-  EXPECT_FALSE(loadModules(kFakeDirectory + "/root.txt"));
-  EXPECT_FALSE(loadModules("/dir/does/not/exist"));
-  tearDownMockFileStructure();
-}
-}
diff --git a/osquery/filesystem/CMakeLists.txt b/osquery/filesystem/CMakeLists.txt
deleted file mode 100644 (file)
index 20e0c26..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_filesystem filesystem.cpp)
-
-ADD_OSQUERY_LIBRARY(osquery_filesystem_linux linux/proc.cpp
-                                                                                        linux/mem.cpp)
-
-FILE(GLOB OSQUERY_FILESYSTEM_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_FILESYSTEM_TESTS})
diff --git a/osquery/filesystem/filesystem.cpp b/osquery/filesystem/filesystem.cpp
deleted file mode 100644 (file)
index b567c1d..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sstream>
-
-#include <fcntl.h>
-#include <glob.h>
-#include <pwd.h>
-#include <sys/stat.h>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/filesystem/operations.hpp>
-#include <boost/property_tree/json_parser.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-
-namespace pt = boost::property_tree;
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-FLAG(uint64, read_max, 50 * 1024 * 1024, "Maximum file read size");
-FLAG(uint64, read_user_max, 10 * 1024 * 1024, "Maximum non-su read size");
-FLAG(bool, read_user_links, true, "Read user-owned filesystem links");
-
-Status writeTextFile(const fs::path& path,
-                     const std::string& content,
-                     int permissions,
-                     bool force_permissions) {
-  // Open the file with the request permissions.
-  int output_fd =
-      open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, permissions);
-  if (output_fd <= 0) {
-    return Status(1, "Could not create file: " + path.string());
-  }
-
-  // If the file existed with different permissions before our open
-  // they must be restricted.
-  if (chmod(path.c_str(), permissions) != 0) {
-    // Could not change the file to the requested permissions.
-    return Status(1, "Failed to change permissions for file: " + path.string());
-  }
-
-  auto bytes = write(output_fd, content.c_str(), content.size());
-  if (bytes != content.size()) {
-    close(output_fd);
-    return Status(1, "Failed to write contents to file: " + path.string());
-  }
-
-  close(output_fd);
-  return Status(0, "OK");
-}
-
-Status readFile(const fs::path& path, std::string& content, bool dry_run) {
-  struct stat file;
-  if (lstat(path.string().c_str(), &file) == 0 && S_ISLNK(file.st_mode)) {
-    if (file.st_uid != 0 && !FLAGS_read_user_links) {
-      return Status(1, "User link reads disabled");
-    }
-  }
-
-  if (stat(path.string().c_str(), &file) < 0) {
-    return Status(1, "Cannot access path: " + path.string());
-  }
-
-  // Apply the max byte-read based on file/link target ownership.
-  size_t read_max = (file.st_uid == 0)
-                        ? FLAGS_read_max
-                        : std::min(FLAGS_read_max, FLAGS_read_user_max);
-  std::ifstream is(path.string(), std::ifstream::binary | std::ios::ate);
-  if (!is.is_open()) {
-    // Attempt to read without seeking to the end.
-    is.open(path.string(), std::ifstream::binary);
-    if (!is) {
-      return Status(1, "Error reading file: " + path.string());
-    }
-  }
-
-  // Attempt to read the file size.
-  ssize_t size = is.tellg();
-
-  // Erase/clear provided string buffer.
-  content.erase();
-  if (size > read_max) {
-    VLOG(1) << "Cannot read " << path << " size exceeds limit: " << size
-            << " > " << read_max;
-    return Status(1, "File exceeds read limits");
-  }
-
-  if (dry_run) {
-    // The caller is only interested in performing file read checks.
-    boost::system::error_code ec;
-    return Status(0, fs::canonical(path, ec).string());
-  }
-
-  // Reset seek to the start of the stream.
-  is.seekg(0);
-  if (size == -1 || size == 0) {
-    // Size could not be determined. This may be a special device.
-    std::stringstream buffer;
-    buffer << is.rdbuf();
-    if (is.bad()) {
-      return Status(1, "Error reading special file: " + path.string());
-    }
-    content.assign(std::move(buffer.str()));
-  } else {
-    content = std::string(size, '\0');
-    is.read(&content[0], size);
-  }
-  return Status(0, "OK");
-}
-
-Status readFile(const fs::path& path) {
-  std::string blank;
-  return readFile(path, blank, true);
-}
-
-Status isWritable(const fs::path& path) {
-  auto path_exists = pathExists(path);
-  if (!path_exists.ok()) {
-    return path_exists;
-  }
-
-  if (access(path.c_str(), W_OK) == 0) {
-    return Status(0, "OK");
-  }
-  return Status(1, "Path is not writable: " + path.string());
-}
-
-Status isReadable(const fs::path& path) {
-  auto path_exists = pathExists(path);
-  if (!path_exists.ok()) {
-    return path_exists;
-  }
-
-  if (access(path.c_str(), R_OK) == 0) {
-    return Status(0, "OK");
-  }
-  return Status(1, "Path is not readable: " + path.string());
-}
-
-Status pathExists(const fs::path& path) {
-  if (path.empty()) {
-    return Status(1, "-1");
-  }
-
-  // A tri-state determination of presence
-  try {
-    if (!fs::exists(path)) {
-      return Status(1, "0");
-    }
-  } catch (const fs::filesystem_error& e) {
-    return Status(1, e.what());
-  }
-  return Status(0, "1");
-}
-
-Status remove(const fs::path& path) {
-  auto status_code = std::remove(path.string().c_str());
-  return Status(status_code, "N/A");
-}
-
-static void genGlobs(std::string path,
-                     std::vector<std::string>& results,
-                     GlobLimits limits) {
-  // Use our helped escape/replace for wildcards.
-  replaceGlobWildcards(path);
-
-  // Generate a glob set and recurse for double star.
-  while (true) {
-    glob_t data;
-    glob(path.c_str(), GLOB_TILDE | GLOB_MARK | GLOB_BRACE, nullptr, &data);
-    size_t count = data.gl_pathc;
-    for (size_t index = 0; index < count; index++) {
-      results.push_back(data.gl_pathv[index]);
-    }
-    globfree(&data);
-    // The end state is a non-recursive ending or empty set of matches.
-    size_t wild = path.rfind("**");
-    // Allow a trailing slash after the double wild indicator.
-    if (count == 0 || wild > path.size() || wild < path.size() - 3) {
-      break;
-    }
-    path += "/**";
-  }
-
-  // Prune results based on settings/requested glob limitations.
-  auto end = std::remove_if(
-      results.begin(), results.end(), [limits](const std::string& found) {
-        return !((found[found.length() - 1] == '/' && limits & GLOB_FOLDERS) ||
-                 (found[found.length() - 1] != '/' && limits & GLOB_FILES));
-      });
-  results.erase(end, results.end());
-}
-
-Status resolveFilePattern(const fs::path& fs_path,
-                          std::vector<std::string>& results) {
-  return resolveFilePattern(fs_path, results, GLOB_ALL);
-}
-
-Status resolveFilePattern(const fs::path& fs_path,
-                          std::vector<std::string>& results,
-                          GlobLimits setting) {
-  genGlobs(fs_path.string(), results, setting);
-  return Status(0, "OK");
-}
-
-inline void replaceGlobWildcards(std::string& pattern) {
-  // Replace SQL-wildcard '%' with globbing wildcard '*'.
-  if (pattern.find("%") != std::string::npos) {
-    boost::replace_all(pattern, "%", "*");
-  }
-
-  // Relative paths are a bad idea, but we try to accommodate.
-  if ((pattern.size() == 0 || pattern[0] != '/') && pattern[0] != '~') {
-    pattern = (fs::initial_path() / pattern).string();
-  }
-
-  auto base = pattern.substr(0, pattern.find('*'));
-  if (base.size() > 0) {
-    boost::system::error_code ec;
-    auto canonicalized = fs::canonical(base, ec).string();
-    if (canonicalized.size() > 0 && canonicalized != base) {
-      if (isDirectory(canonicalized)) {
-        // Canonicalized directory paths will not include a trailing '/'.
-        // However, if the wildcards are applied to files within a directory
-        // then the missing '/' changes the wildcard meaning.
-        canonicalized += '/';
-      }
-      // We are unable to canonicalize the meaning of post-wildcard limiters.
-      pattern = canonicalized + pattern.substr(base.size());
-    }
-  }
-}
-
-inline Status listInAbsoluteDirectory(const fs::path& path,
-                                      std::vector<std::string>& results,
-                                      GlobLimits limits) {
-  try {
-    if (path.filename() == "*" && !fs::exists(path.parent_path())) {
-      return Status(1, "Directory not found: " + path.parent_path().string());
-    }
-
-    if (path.filename() == "*" && !fs::is_directory(path.parent_path())) {
-      return Status(1, "Path not a directory: " + path.parent_path().string());
-    }
-  } catch (const fs::filesystem_error& e) {
-    return Status(1, e.what());
-  }
-  genGlobs(path.string(), results, limits);
-  return Status(0, "OK");
-}
-
-Status listFilesInDirectory(const fs::path& path,
-                            std::vector<std::string>& results,
-                            bool ignore_error) {
-  return listInAbsoluteDirectory((path / "*"), results, GLOB_FILES);
-}
-
-Status listDirectoriesInDirectory(const fs::path& path,
-                                  std::vector<std::string>& results,
-                                  bool ignore_error) {
-  return listInAbsoluteDirectory((path / "*"), results, GLOB_FOLDERS);
-}
-
-Status getDirectory(const fs::path& path, fs::path& dirpath) {
-  if (!isDirectory(path).ok()) {
-    dirpath = fs::path(path).parent_path().string();
-    return Status(0, "OK");
-  }
-  dirpath = path;
-  return Status(1, "Path is a directory: " + path.string());
-}
-
-Status isDirectory(const fs::path& path) {
-  boost::system::error_code ec;
-  if (fs::is_directory(path, ec)) {
-    return Status(0, "OK");
-  }
-  if (ec.value() == 0) {
-    return Status(1, "Path is not a directory: " + path.string());
-  }
-  return Status(ec.value(), ec.message());
-}
-
-std::set<fs::path> getHomeDirectories() {
-  std::set<fs::path> results;
-
-  auto users = SQL::selectAllFrom("users");
-  for (const auto& user : users) {
-    if (user.at("directory").size() > 0) {
-      results.insert(user.at("directory"));
-    }
-  }
-
-  return results;
-}
-
-bool safePermissions(const std::string& dir,
-                     const std::string& path,
-                     bool executable) {
-  struct stat file_stat, link_stat, dir_stat;
-  if (lstat(path.c_str(), &link_stat) < 0 || stat(path.c_str(), &file_stat) ||
-      stat(dir.c_str(), &dir_stat)) {
-    // Path was not real, had too may links, or could not be accessed.
-    return false;
-  }
-
-  if (dir_stat.st_mode & (1 << 9)) {
-    // Do not load modules from /tmp-like directories.
-    return false;
-  } else if (S_ISDIR(file_stat.st_mode)) {
-    // Only load file-like nodes (not directories).
-    return false;
-  } else if (file_stat.st_uid == getuid() || file_stat.st_uid == 0) {
-    // Otherwise, require matching or root file ownership.
-    if (executable && !(file_stat.st_mode & S_IXUSR)) {
-      // Require executable, implies by the owner.
-      return false;
-    }
-    return true;
-  }
-  // Do not load modules not owned by the user.
-  return false;
-}
-
-const std::string& osqueryHomeDirectory() {
-  static std::string homedir;
-  if (homedir.size() == 0) {
-    // Try to get the caller's home directory using HOME and getpwuid.
-    auto user = getpwuid(getuid());
-    if (getenv("HOME") != nullptr && isWritable(getenv("HOME")).ok()) {
-      homedir = std::string(getenv("HOME")) + "/.osquery";
-    } else if (user != nullptr && user->pw_dir != nullptr) {
-      homedir = std::string(user->pw_dir) + "/.osquery";
-    } else {
-      // Fail over to a temporary directory (used for the shell).
-      homedir = "/tmp/osquery";
-    }
-  }
-  return homedir;
-}
-
-std::string lsperms(int mode) {
-  static const char rwx[] = {'0', '1', '2', '3', '4', '5', '6', '7'};
-  std::string bits;
-
-  bits += rwx[(mode >> 9) & 7];
-  bits += rwx[(mode >> 6) & 7];
-  bits += rwx[(mode >> 3) & 7];
-  bits += rwx[(mode >> 0) & 7];
-  return bits;
-}
-
-Status parseJSON(const fs::path& path, pt::ptree& tree) {
-  std::string json_data;
-  if (!readFile(path, json_data).ok()) {
-    return Status(1, "Could not read JSON from file");
-  }
-
-  return parseJSONContent(json_data, tree);
-}
-
-Status parseJSONContent(const std::string& content, pt::ptree& tree) {
-  // Read the extensions data into a JSON blob, then property tree.
-  try {
-    std::stringstream json_stream;
-    json_stream << content;
-    pt::read_json(json_stream, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return Status(1, "Could not parse JSON from file");
-  }
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/filesystem/linux/mem.cpp b/osquery/filesystem/linux/mem.cpp
deleted file mode 100644 (file)
index 3017a20..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sys/mman.h>
-#include <sys/types.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <osquery/filesystem.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-
-#define kLinuxMaxMemRead 0x10000
-
-const std::string kLinuxMemPath = "/dev/mem";
-
-FLAG(bool, disable_memory, false, "Disable physical memory reads");
-
-Status readMem(int fd, size_t base, size_t length, uint8_t* buffer) {
-  if (lseek(fd, base, SEEK_SET) == -1) {
-    return Status(1, "Cannot seek to physical base");
-  }
-
-  // Read from raw memory until an unrecoverable read error or the all of the
-  // requested bytes are read.
-  size_t total_read = 0;
-  size_t bytes_read = 0;
-  while (total_read != length && bytes_read != 0) {
-    bytes_read = read(fd, buffer + total_read, length - total_read);
-    if (bytes_read == -1) {
-      if (errno != EINTR) {
-        return Status(1, "Cannot read requested length");
-      }
-    } else {
-      total_read += bytes_read;
-    }
-  }
-
-  // The read call finished without reading the requested number of bytes.
-  if (total_read != length) {
-    return Status(1, "Read incorrect number of bytes");
-  }
-
-  return Status(0, "OK");
-}
-
-Status readRawMem(size_t base, size_t length, void** buffer) {
-  *buffer = 0;
-
-  if (FLAGS_disable_memory) {
-    return Status(1, "Configuration has disabled physical memory reads");
-  }
-
-  if (length > kLinuxMaxMemRead) {
-    return Status(1, "Cowardly refusing to read a large number of bytes");
-  }
-
-  auto status = isReadable(kLinuxMemPath);
-  if (!status.ok()) {
-    // For non-su users *hopefully* raw memory is not readable.
-    return status;
-  }
-
-  int fd = open(kLinuxMemPath.c_str(), O_RDONLY);
-  if (fd < 0) {
-    return Status(1, std::string("Cannot open ") + kLinuxMemPath);
-  }
-
-  if ((*buffer = malloc(length)) == nullptr) {
-    close(fd);
-    return Status(1, "Cannot allocate memory for read");
-  }
-
-#ifdef _SC_PAGESIZE
-  size_t offset = base % sysconf(_SC_PAGESIZE);
-#else
-  // getpagesize() is more or less deprecated.
-  size_t offset = base % getpagesize();
-#endif
-
-  // Use memmap for maximum portability over read().
-  auto map = mmap(0, offset + length, PROT_READ, MAP_SHARED, fd, base - offset);
-  if (map == MAP_FAILED) {
-    // Could fallback to a lseek/read.
-    if (!readMem(fd, base, length, (uint8_t*)*buffer).ok()) {
-      close(fd);
-      free(*buffer);
-      return Status(1, "Cannot memory map or seek/read memory");
-    }
-  } else {
-    // Memory map succeeded, copy and unmap.
-    memcpy(*buffer, (uint8_t*)map + offset, length);
-    if (munmap(map, offset + length) == -1) {
-      LOG(WARNING) << "Unable to unmap raw memory";
-    }
-  }
-
-  close(fd);
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/filesystem/linux/proc.cpp b/osquery/filesystem/linux/proc.cpp
deleted file mode 100644 (file)
index c80fefa..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <linux/limits.h>
-#include <unistd.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-
-const std::string kLinuxProcPath = "/proc";
-
-Status procProcesses(std::set<std::string>& processes) {
-  // Iterate over each process-like directory in proc.
-  boost::filesystem::directory_iterator it(kLinuxProcPath), end;
-  try {
-    for (; it != end; ++it) {
-      if (boost::filesystem::is_directory(it->status())) {
-        // See #792: std::regex is incomplete until GCC 4.9
-        if (std::atoll(it->path().leaf().string().c_str()) > 0) {
-          processes.insert(it->path().leaf().string());
-        }
-      }
-    }
-  } catch (const boost::filesystem::filesystem_error& e) {
-    VLOG(1) << "Exception iterating Linux processes " << e.what();
-    return Status(1, e.what());
-  }
-
-  return Status(0, "OK");
-}
-
-Status procDescriptors(const std::string& process,
-                       std::map<std::string, std::string>& descriptors) {
-  auto descriptors_path = kLinuxProcPath + "/" + process + "/fd";
-  try {
-    // Access to the process' /fd may be restricted.
-    boost::filesystem::directory_iterator it(descriptors_path), end;
-    for (; it != end; ++it) {
-      auto fd = it->path().leaf().string();
-      std::string linkname;
-      if (procReadDescriptor(process, fd, linkname).ok()) {
-        descriptors[fd] = linkname;
-      }
-    }
-  } catch (boost::filesystem::filesystem_error& e) {
-    return Status(1, "Cannot access descriptors for " + process);
-  }
-
-  return Status(0, "OK");
-}
-
-Status procReadDescriptor(const std::string& process,
-                          const std::string& descriptor,
-                          std::string& result) {
-  auto link = kLinuxProcPath + "/" + process + "/fd/" + descriptor;
-
-  char result_path[PATH_MAX] = {0};
-  auto size = readlink(link.c_str(), result_path, sizeof(result_path) - 1);
-  if (size >= 0) {
-    result = std::string(result_path);
-  }
-
-  if (size >= 0) {
-    return Status(0, "OK");
-  } else {
-    return Status(1, "Could not read path");
-  }
-}
-}
diff --git a/osquery/filesystem/tests/filesystem_tests.cpp b/osquery/filesystem/tests/filesystem_tests.cpp
deleted file mode 100644 (file)
index 85c87f7..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <fstream>
-
-#include <stdio.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/property_tree/ptree.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-#include "osquery/core/test_util.h"
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-DECLARE_uint64(read_max);
-DECLARE_uint64(read_user_max);
-DECLARE_bool(read_user_links);
-
-class FilesystemTests : public testing::Test {
-
- protected:
-  void SetUp() { createMockFileStructure(); }
-
-  void TearDown() { tearDownMockFileStructure(); }
-
-  /// Helper method to check if a path was included in results.
-  bool contains(const std::vector<std::string>& all, const std::string& n) {
-    return !(std::find(all.begin(), all.end(), n) == all.end());
-  }
-};
-
-TEST_F(FilesystemTests, test_read_file) {
-  std::ofstream test_file(kTestWorkingDirectory + "fstests-file");
-  test_file.write("test123\n", sizeof("test123"));
-  test_file.close();
-
-  std::string content;
-  auto s = readFile(kTestWorkingDirectory + "fstests-file", content);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_EQ(content, "test123\n");
-
-  remove(kTestWorkingDirectory + "fstests-file");
-}
-
-TEST_F(FilesystemTests, test_read_symlink) {
-  std::string content;
-  auto status = readFile(kFakeDirectory + "/root2.txt", content);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(content, "root");
-}
-
-TEST_F(FilesystemTests, test_read_limit) {
-  auto max = FLAGS_read_max;
-  auto user_max = FLAGS_read_user_max;
-  FLAGS_read_max = 3;
-  std::string content;
-  auto status = readFile(kFakeDirectory + "/root.txt", content);
-  EXPECT_FALSE(status.ok());
-  FLAGS_read_max = max;
-
-  if (getuid() != 0) {
-    content.erase();
-    FLAGS_read_user_max = 2;
-    status = readFile(kFakeDirectory + "/root.txt", content);
-    EXPECT_FALSE(status.ok());
-    FLAGS_read_user_max = user_max;
-
-    // Test that user symlinks aren't followed if configured.
-    // 'root2.txt' is a symlink in this case.
-    FLAGS_read_user_links = false;
-    content.erase();
-    status = readFile(kFakeDirectory + "/root2.txt", content);
-    EXPECT_FALSE(status.ok());
-
-    // Make sure non-link files are still readable.
-    content.erase();
-    status = readFile(kFakeDirectory + "/root.txt", content);
-    EXPECT_TRUE(status.ok());
-
-    // Any the links are readable if enabled.
-    FLAGS_read_user_links = true;
-    status = readFile(kFakeDirectory + "/root2.txt", content);
-    EXPECT_TRUE(status.ok());
-  }
-}
-
-TEST_F(FilesystemTests, test_list_files_missing_directory) {
-  std::vector<std::string> results;
-  auto status = listFilesInDirectory("/foo/bar", results);
-  EXPECT_FALSE(status.ok());
-}
-
-TEST_F(FilesystemTests, test_list_files_invalid_directory) {
-  std::vector<std::string> results;
-  auto status = listFilesInDirectory("/etc/hosts", results);
-  EXPECT_FALSE(status.ok());
-}
-
-TEST_F(FilesystemTests, test_list_files_valid_directorty) {
-  std::vector<std::string> results;
-  auto s = listFilesInDirectory("/etc", results);
-  // This directory may be different on OS X or Linux.
-  std::string hosts_path = "/etc/hosts";
-  replaceGlobWildcards(hosts_path);
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(s.toString(), "OK");
-  EXPECT_TRUE(contains(results, hosts_path));
-}
-
-TEST_F(FilesystemTests, test_canonicalization) {
-  std::string complex = kFakeDirectory + "/deep1/../deep1/..";
-  std::string simple = kFakeDirectory + "/";
-  // Use the inline wildcard and canonicalization replacement.
-  // The 'simple' path contains a trailing '/', the replacement method will
-  // distinguish between file and directory paths.
-  replaceGlobWildcards(complex);
-  EXPECT_EQ(simple, complex);
-  // Now apply the same inline replacement on the simple directory and expect
-  // no change to the comparison.
-  replaceGlobWildcards(simple);
-  EXPECT_EQ(simple, complex);
-
-  // Now add a wildcard within the complex pattern. The replacement method
-  // will not canonicalize past a '*' as the proceeding paths are limiters.
-  complex = kFakeDirectory + "/*/deep2/../deep2/";
-  replaceGlobWildcards(complex);
-  EXPECT_EQ(complex, kFakeDirectory + "/*/deep2/../deep2/");
-}
-
-TEST_F(FilesystemTests, test_simple_globs) {
-  std::vector<std::string> results;
-  // Test the shell '*', we will support SQL's '%' too.
-  auto status = resolveFilePattern(kFakeDirectory + "/*", results);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 6);
-
-  // Test the csh-style bracket syntax: {}.
-  results.clear();
-  resolveFilePattern(kFakeDirectory + "/{root,door}*", results);
-  EXPECT_EQ(results.size(), 3);
-
-  // Test a tilde, home directory expansion, make no asserts about contents.
-  results.clear();
-  resolveFilePattern("~", results);
-  if (results.size() == 0) {
-    LOG(WARNING) << "Tilde expansion failed.";
-  }
-}
-
-TEST_F(FilesystemTests, test_wildcard_single_all) {
-  // Use '%' as a wild card to glob files within the temporarily-created dir.
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(kFakeDirectory + "/%", results, GLOB_ALL);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 6);
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt"));
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_single_files) {
-  // Now list again with a restriction to only files.
-  std::vector<std::string> results;
-  resolveFilePattern(kFakeDirectory + "/%", results, GLOB_FILES);
-  EXPECT_EQ(results.size(), 4);
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_single_folders) {
-  std::vector<std::string> results;
-  resolveFilePattern(kFakeDirectory + "/%", results, GLOB_FOLDERS);
-  EXPECT_EQ(results.size(), 2);
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_dual) {
-  // Now test two directories deep with a single wildcard for each.
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(kFakeDirectory + "/%/%", results);
-  EXPECT_TRUE(status.ok());
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/level1.txt"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_double) {
-  // TODO: this will fail.
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(kFakeDirectory + "/%%", results);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 15);
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/deep2/level2.txt"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_double_folders) {
-  std::vector<std::string> results;
-  resolveFilePattern(kFakeDirectory + "/%%", results, GLOB_FOLDERS);
-  EXPECT_EQ(results.size(), 5);
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/deep2/deep3/"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_end_last_component) {
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(kFakeDirectory + "/%11/%sh", results);
-  EXPECT_TRUE(status.ok());
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/not_bash"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_middle_component) {
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(kFakeDirectory + "/deep1%/%", results);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 5);
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/level1.txt"));
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/level1.txt"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_all_types) {
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(kFakeDirectory + "/%p11/%/%%", results);
-  EXPECT_TRUE(status.ok());
-  EXPECT_TRUE(
-      contains(results, kFakeDirectory + "/deep11/deep2/deep3/level3.txt"));
-}
-
-TEST_F(FilesystemTests, test_wildcard_invalid_path) {
-  std::vector<std::string> results;
-  auto status = resolveFilePattern("/not_ther_abcdefz/%%", results);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 0);
-}
-
-TEST_F(FilesystemTests, test_wildcard_dotdot_files) {
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(
-      kFakeDirectory + "/deep11/deep2/../../%", results, GLOB_FILES);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 4);
-  // The response list will contain canonicalized versions: /tmp/<tests>/...
-  std::string door_path = kFakeDirectory + "/deep11/deep2/../../door.txt";
-  replaceGlobWildcards(door_path);
-  EXPECT_TRUE(contains(results, door_path));
-}
-
-TEST_F(FilesystemTests, test_dotdot_relative) {
-  std::vector<std::string> results;
-  auto status = resolveFilePattern(kTestDataPath + "%", results);
-  EXPECT_TRUE(status.ok());
-
-  bool found = false;
-  for (const auto& file : results) {
-    if (file.find("test.config")) {
-      found = true;
-      break;
-    }
-  }
-  EXPECT_TRUE(found);
-}
-
-TEST_F(FilesystemTests, test_no_wild) {
-  std::vector<std::string> results;
-  auto status =
-      resolveFilePattern(kFakeDirectory + "/roto.txt", results, GLOB_FILES);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 1);
-  EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt"));
-}
-
-TEST_F(FilesystemTests, test_safe_permissions) {
-  // For testing we can request a different directory path.
-  EXPECT_TRUE(safePermissions("/", kFakeDirectory + "/door.txt"));
-  // A file with a directory.mode & 0x1000 fails.
-  EXPECT_FALSE(safePermissions("/tmp", kFakeDirectory + "/door.txt"));
-  // A directory for a file will fail.
-  EXPECT_FALSE(safePermissions("/", kFakeDirectory + "/deep11"));
-  // A root-owned file is appropriate
-  EXPECT_TRUE(safePermissions("/", "/dev/zero"));
-}
-}
diff --git a/osquery/logger/CMakeLists.txt b/osquery/logger/CMakeLists.txt
deleted file mode 100644 (file)
index a87fd66..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_logger logger.cpp)
-ADD_OSQUERY_LIBRARY(osquery_logger_plugins plugins/filesystem.cpp
-                                                                                  plugins/syslog.cpp)
-
-FILE(GLOB OSQUERY_LOGGER_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_LOGGER_TESTS})
-
-file(GLOB OSQUERY_LOGGER_PLUGIN_TESTS "plugins/tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_LOGGER_PLUGIN_TESTS})
diff --git a/osquery/logger/logger.cpp b/osquery/logger/logger.cpp
deleted file mode 100644 (file)
index f0b420b..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <algorithm>
-#include <thread>
-
-#include <boost/noncopyable.hpp>
-#include <boost/property_tree/json_parser.hpp>
-
-#include <osquery/extensions.h>
-#include <osquery/filesystem.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-namespace pt = boost::property_tree;
-
-namespace osquery {
-
-FLAG(bool, verbose, false, "Enable verbose informational messages");
-FLAG_ALIAS(bool, verbose_debug, verbose);
-FLAG_ALIAS(bool, debug, verbose);
-
-/// Despite being a configurable option, this is only read/used at load.
-FLAG(bool, disable_logging, false, "Disable ERROR/INFO logging");
-
-FLAG(string, logger_plugin, "filesystem", "Logger plugin name");
-
-FLAG(bool, log_result_events, true, "Log scheduled results as events");
-
-/**
- * @brief A custom Glog log sink for forwarding or buffering status logs.
- *
- * This log sink has two modes, it can buffer Glog status logs until an osquery
- * logger is initialized or forward Glog status logs to an initialized and
- * appropriate logger. The appropriateness is determined by the logger when its
- * LoggerPlugin::init method is called. If the `init` method returns success
- * then a BufferedLogSink will start forwarding status logs to
- * LoggerPlugin::logStatus.
- *
- * This facility will start buffering when first used and stop buffering
- * (aka remove itself as a Glog sink) using the exposed APIs. It will live
- * throughout the life of the process for two reasons: (1) It makes sense when
- * the active logger plugin is handling Glog status logs and (2) it must remove
- * itself as a Glog target.
- */
-class BufferedLogSink : public google::LogSink, private boost::noncopyable {
- public:
-  /// We create this as a Singleton for proper disable/shutdown.
-  static BufferedLogSink& instance() {
-    static BufferedLogSink sink;
-    return sink;
-  }
-
-  /// The Glog-API LogSink call-in method.
-  void send(google::LogSeverity severity,
-            const char* full_filename,
-            const char* base_filename,
-            int line,
-            const struct ::tm* tm_time,
-            const char* message,
-            size_t message_len);
-
-  /// Accessor/mutator to dump all of the buffered logs.
-  static std::vector<StatusLogLine>& dump() { return instance().logs_; }
-
-  /// Set the forwarding mode of the buffering sink.
-  static void forward(bool forward = false) { instance().forward_ = forward; }
-
-  /// Remove the buffered log sink from Glog.
-  static void disable() {
-    if (instance().enabled_) {
-      instance().enabled_ = false;
-      google::RemoveLogSink(&instance());
-    }
-  }
-
-  /// Add the buffered log sink to Glog.
-  static void enable() {
-    if (!instance().enabled_) {
-      instance().enabled_ = true;
-      google::AddLogSink(&instance());
-    }
-  }
-
- private:
-  /// Create the log sink as buffering or forwarding.
-  BufferedLogSink() : forward_(false), enabled_(false) {}
-
-  /// Remove the log sink.
-  ~BufferedLogSink() { disable(); }
-
-  BufferedLogSink(BufferedLogSink const&);
-  void operator=(BufferedLogSink const&);
-
- private:
-  /// Intermediate log storage until an osquery logger is initialized.
-  std::vector<StatusLogLine> logs_;
-  bool forward_;
-  bool enabled_;
-};
-
-/// Scoped helper to perform logging actions without races.
-class LoggerDisabler {
- public:
-  LoggerDisabler() : stderr_status_(FLAGS_logtostderr) {
-    BufferedLogSink::disable();
-    FLAGS_logtostderr = true;
-  }
-
-  ~LoggerDisabler() {
-    BufferedLogSink::enable();
-    FLAGS_logtostderr = stderr_status_;
-  }
-
- private:
-  bool stderr_status_;
-};
-
-static void serializeIntermediateLog(const std::vector<StatusLogLine>& log,
-                                     PluginRequest& request) {
-  pt::ptree tree;
-  for (const auto& log_item : log) {
-    pt::ptree child;
-    child.put("s", log_item.severity);
-    child.put("f", log_item.filename);
-    child.put("i", log_item.line);
-    child.put("m", log_item.message);
-    tree.push_back(std::make_pair("", child));
-  }
-
-  // Save the log as a request JSON string.
-  std::ostringstream output;
-  pt::write_json(output, tree, false);
-  request["log"] = output.str();
-}
-
-static void deserializeIntermediateLog(const PluginRequest& request,
-                                       std::vector<StatusLogLine>& log) {
-  if (request.count("log") == 0) {
-    return;
-  }
-
-  // Read the plugin request string into a JSON tree and enumerate.
-  pt::ptree tree;
-  try {
-    std::stringstream input;
-    input << request.at("log");
-    pt::read_json(input, tree);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    return;
-  }
-
-  for (const auto& item : tree.get_child("")) {
-    log.push_back({
-        (StatusLogSeverity)item.second.get<int>("s", O_INFO),
-        item.second.get<std::string>("f", "<unknown>"),
-        item.second.get<int>("i", 0),
-        item.second.get<std::string>("m", ""),
-    });
-  }
-}
-
-void setVerboseLevel() {
-  if (Flag::getValue("verbose") == "true") {
-    // Turn verbosity up to 1.
-    // Do log DEBUG, INFO, WARNING, ERROR to their log files.
-    // Do log the above and verbose=1 to stderr.
-    FLAGS_minloglevel = 0; // INFO
-    FLAGS_stderrthreshold = 0; // INFO
-    FLAGS_v = 1;
-  } else {
-    // Do NOT log INFO, WARNING, ERROR to stderr.
-    // Do log only WARNING, ERROR to log sinks.
-    FLAGS_minloglevel = 1; // WARNING
-    FLAGS_stderrthreshold = 1; // WARNING
-  }
-
-  if (FLAGS_disable_logging) {
-    // Do log ERROR to stderr.
-    // Do NOT log INFO, WARNING, ERROR to their log files.
-    FLAGS_logtostderr = true;
-    if (!FLAGS_verbose) {
-      // verbose flag will still emit logs to stderr.
-      FLAGS_minloglevel = 2; // ERROR
-    }
-  }
-}
-
-void initStatusLogger(const std::string& name) {
-  FLAGS_alsologtostderr = false;
-  FLAGS_logbufsecs = 0; // flush the log buffer immediately
-  FLAGS_stop_logging_if_full_disk = true;
-  FLAGS_max_log_size = 10; // max size for individual log file is 10MB
-  FLAGS_logtostderr = true;
-
-  setVerboseLevel();
-  // Start the logging, and announce the daemon is starting.
-  google::InitGoogleLogging(name.c_str());
-
-  // If logging is disabled then do not buffer intermediate logs.
-  if (!FLAGS_disable_logging) {
-    // Create an instance of the buffered log sink and do not forward logs yet.
-    BufferedLogSink::enable();
-  }
-}
-
-void initLogger(const std::string& name, bool forward_all) {
-  // Check if logging is disabled, if so then no need to shuttle intermediates.
-  if (FLAGS_disable_logging) {
-    return;
-  }
-
-  // Stop the buffering sink and store the intermediate logs.
-  BufferedLogSink::disable();
-  auto intermediate_logs = std::move(BufferedLogSink::dump());
-  auto& logger_plugin = Registry::getActive("logger");
-  if (!Registry::exists("logger", logger_plugin)) {
-    return;
-  }
-
-  // Start the custom status logging facilities, which may instruct Glog as is
-  // the case with filesystem logging.
-  PluginRequest request = {{"init", name}};
-  serializeIntermediateLog(intermediate_logs, request);
-  auto status = Registry::call("logger", request);
-  if (status.ok() || forward_all) {
-    // When LoggerPlugin::init returns success we enable the log sink in
-    // forwarding mode. Then Glog status logs are forwarded to logStatus.
-    BufferedLogSink::forward(true);
-    BufferedLogSink::enable();
-  }
-}
-
-void BufferedLogSink::send(google::LogSeverity severity,
-                           const char* full_filename,
-                           const char* base_filename,
-                           int line,
-                           const struct ::tm* tm_time,
-                           const char* message,
-                           size_t message_len) {
-  // Either forward the log to an enabled logger or buffer until one exists.
-  if (forward_) {
-    // May use the logs_ storage to buffer/delay sending logs.
-    std::vector<StatusLogLine> log;
-    log.push_back({(StatusLogSeverity)severity,
-                   std::string(base_filename),
-                   line,
-                   std::string(message, message_len)});
-    PluginRequest request = {{"status", "true"}};
-    serializeIntermediateLog(log, request);
-    Registry::call("logger", request);
-  } else {
-    logs_.push_back({(StatusLogSeverity)severity,
-                     std::string(base_filename),
-                     line,
-                     std::string(message, message_len)});
-  }
-}
-
-Status LoggerPlugin::call(const PluginRequest& request,
-                          PluginResponse& response) {
-  QueryLogItem item;
-  std::vector<StatusLogLine> intermediate_logs;
-  if (request.count("string") > 0) {
-    return this->logString(request.at("string"));
-  } else if (request.count("snapshot") > 0) {
-    return this->logSnapshot(request.at("snapshot"));
-  } else if (request.count("health") > 0) {
-    return this->logHealth(request.at("health"));
-  } else if (request.count("init") > 0) {
-    deserializeIntermediateLog(request, intermediate_logs);
-    return this->init(request.at("init"), intermediate_logs);
-  } else if (request.count("status") > 0) {
-    deserializeIntermediateLog(request, intermediate_logs);
-    return this->logStatus(intermediate_logs);
-  } else {
-    return Status(1, "Unsupported call to logger plugin");
-  }
-}
-
-Status logString(const std::string& message, const std::string& category) {
-  return logString(message, category, Registry::getActive("logger"));
-}
-
-Status logString(const std::string& message,
-                 const std::string& category,
-                 const std::string& receiver) {
-  if (!Registry::exists("logger", receiver)) {
-    LOG(ERROR) << "Logger receiver " << receiver << " not found";
-    return Status(1, "Logger receiver not found");
-  }
-
-  auto status = Registry::call(
-      "logger", receiver, {{"string", message}, {"category", category}});
-  return Status(0, "OK");
-}
-
-Status logQueryLogItem(const QueryLogItem& results) {
-  return logQueryLogItem(results, Registry::getActive("logger"));
-}
-
-Status logQueryLogItem(const QueryLogItem& results,
-                       const std::string& receiver) {
-  std::string json;
-  Status status;
-  if (FLAGS_log_result_events) {
-    status = serializeQueryLogItemAsEventsJSON(results, json);
-  } else {
-    status = serializeQueryLogItemJSON(results, json);
-  }
-  if (!status.ok()) {
-    return status;
-  }
-  return logString(json, "event", receiver);
-}
-
-Status logSnapshotQuery(const QueryLogItem& item) {
-  std::string json;
-  if (!serializeQueryLogItemJSON(item, json)) {
-    return Status(1, "Could not serialize snapshot");
-  }
-  return Registry::call("logger", {{"snapshot", json}});
-}
-
-Status logHealthStatus(const QueryLogItem& item) {
-  std::string json;
-  if (!serializeQueryLogItemJSON(item, json)) {
-    return Status(1, "Could not serialize health");
-  }
-  return Registry::call("logger", {{"health", json}});
-}
-
-void relayStatusLogs() {
-  // Prevent out dumping and registry calling from producing additional logs.
-  LoggerDisabler disabler;
-
-  // Construct a status log plugin request.
-  PluginRequest req = {{"status", "true"}};
-  auto& status_logs = BufferedLogSink::dump();
-  if (status_logs.size() == 0) {
-    return;
-  }
-
-  // Skip the registry's logic, and send directly to the core's logger.
-  PluginResponse resp;
-  serializeIntermediateLog(status_logs, req);
-  auto status = callExtension(0, "logger", FLAGS_logger_plugin, req, resp);
-  if (status.ok()) {
-    // Flush the buffered status logs.
-    // Otherwise the extension call failed and the buffering should continue.
-    status_logs.clear();
-  }
-}
-}
diff --git a/osquery/logger/plugins/filesystem.cpp b/osquery/logger/plugins/filesystem.cpp
deleted file mode 100644 (file)
index 6dba366..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <exception>
-#include <mutex>
-
-#include <osquery/filesystem.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-namespace pt = boost::property_tree;
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-FLAG(string,
-     logger_path,
-     "/var/log/osquery/",
-     "Directory path for ERROR/WARN/INFO and results logging");
-/// Legacy, backward compatible "osquery_log_dir" CLI option.
-FLAG_ALIAS(std::string, osquery_log_dir, logger_path);
-
-const std::string kFilesystemLoggerFilename = "osqueryd.results.log";
-const std::string kFilesystemLoggerSnapshots = "osqueryd.snapshots.log";
-const std::string kFilesystemLoggerHealth = "osqueryd.health.log";
-
-std::mutex filesystemLoggerPluginMutex;
-
-class FilesystemLoggerPlugin : public LoggerPlugin {
- public:
-  Status setUp();
-  Status logString(const std::string& s);
-  Status logStringToFile(const std::string& s, const std::string& filename);
-  Status logSnapshot(const std::string& s);
-  Status logHealth(const std::string& s);
-  Status init(const std::string& name, const std::vector<StatusLogLine>& log);
-  Status logStatus(const std::vector<StatusLogLine>& log);
-
- private:
-  fs::path log_path_;
-};
-
-REGISTER(FilesystemLoggerPlugin, "logger", "filesystem");
-
-Status FilesystemLoggerPlugin::setUp() {
-  log_path_ = fs::path(FLAGS_logger_path);
-  return Status(0, "OK");
-}
-
-Status FilesystemLoggerPlugin::logString(const std::string& s) {
-  return logStringToFile(s, kFilesystemLoggerFilename);
-}
-
-Status FilesystemLoggerPlugin::logStringToFile(const std::string& s,
-                                               const std::string& filename) {
-  std::lock_guard<std::mutex> lock(filesystemLoggerPluginMutex);
-  try {
-    // The results log may contain sensitive information if run as root.
-    auto status = writeTextFile((log_path_ / filename).string(), s, 0640, true);
-    if (!status.ok()) {
-      return status;
-    }
-  } catch (const std::exception& e) {
-    return Status(1, e.what());
-  }
-  return Status(0, "OK");
-}
-
-Status FilesystemLoggerPlugin::logStatus(
-    const std::vector<StatusLogLine>& log) {
-  for (const auto& item : log) {
-    // Emit this intermediate log to the Glog filesystem logger.
-    google::LogMessage(item.filename.c_str(),
-                       item.line,
-                       (google::LogSeverity)item.severity).stream()
-        << item.message;
-  }
-
-  return Status(0, "OK");
-}
-
-Status FilesystemLoggerPlugin::logSnapshot(const std::string& s) {
-  // Send the snapshot data to a separate filename.
-  return logStringToFile(s, kFilesystemLoggerSnapshots);
-}
-
-Status FilesystemLoggerPlugin::logHealth(const std::string& s) {
-  return logStringToFile(s, kFilesystemLoggerHealth);
-}
-
-Status FilesystemLoggerPlugin::init(const std::string& name,
-                                    const std::vector<StatusLogLine>& log) {
-  // Stop the internal Glog facilities.
-  google::ShutdownGoogleLogging();
-
-  // The log dir is used for status logging and the filesystem results logs.
-  if (isWritable(log_path_.string()).ok()) {
-    FLAGS_log_dir = log_path_.string();
-    FLAGS_logtostderr = false;
-  } else {
-    // If we cannot write logs to the filesystem, fallback to stderr.
-    // The caller (flags/options) might 'also' be logging to stderr using
-    // debug, verbose, etc.
-    FLAGS_logtostderr = true;
-  }
-
-  // Restart the Glog facilities using the name `init` was provided.
-  google::InitGoogleLogging(name.c_str());
-
-  // We may violate Glog global object assumptions. So set names manually.
-  auto basename = (log_path_ / name).string();
-  google::SetLogDestination(google::INFO, (basename + ".INFO.").c_str());
-  google::SetLogDestination(google::WARNING, (basename + ".WARNING.").c_str());
-  google::SetLogDestination(google::ERROR, (basename + ".ERROR.").c_str());
-
-  // Store settings for logging to stderr.
-  bool log_to_stderr = FLAGS_logtostderr;
-  bool also_log_to_stderr = FLAGS_alsologtostderr;
-  int stderr_threshold = FLAGS_stderrthreshold;
-  FLAGS_alsologtostderr = false;
-  FLAGS_logtostderr = false;
-  FLAGS_stderrthreshold = 5;
-
-  // Now funnel the intermediate status logs provided to `init`.
-  logStatus(log);
-
-  // Restore settings for logging to stderr.
-  FLAGS_logtostderr = log_to_stderr;
-  FLAGS_alsologtostderr = also_log_to_stderr;
-  FLAGS_stderrthreshold = stderr_threshold;
-
-  // The filesystem logger cheats and uses Glog to log to the filesystem so
-  // we can return failure here and stop the custom log sink.
-  return Status(1, "No status logger used for filesystem");
-}
-}
diff --git a/osquery/logger/plugins/syslog.cpp b/osquery/logger/plugins/syslog.cpp
deleted file mode 100644 (file)
index d88ab70..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <syslog.h>
-
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-
-FLAG(int32,
-     logger_syslog_facility,
-     LOG_LOCAL3 >> 3,
-     "Syslog facility for status and results logs (0-23, default 19)");
-
-class SyslogLoggerPlugin : public LoggerPlugin {
- public:
-  Status logString(const std::string& s);
-  Status init(const std::string& name, const std::vector<StatusLogLine>& log);
-  Status logStatus(const std::vector<StatusLogLine>& log);
-};
-
-REGISTER(SyslogLoggerPlugin, "logger", "syslog");
-
-Status SyslogLoggerPlugin::logString(const std::string& s) {
-  for (const auto& line : osquery::split(s, "\n")) {
-    syslog(LOG_INFO, "result=%s", line.c_str());
-  }
-  return Status(0, "OK");
-}
-
-Status SyslogLoggerPlugin::logStatus(const std::vector<StatusLogLine>& log) {
-  for (const auto& item : log) {
-    int severity = LOG_NOTICE;
-    if (item.severity == O_INFO) {
-      severity = LOG_NOTICE;
-    } else if (item.severity == O_WARNING) {
-      severity = LOG_WARNING;
-    } else if (item.severity == O_ERROR) {
-      severity = LOG_ERR;
-    } else if (item.severity == O_FATAL) {
-      severity = LOG_CRIT;
-    }
-
-    std::string line = "severity=" + std::to_string(item.severity)
-                    + " location=" + item.filename + ":" + std::to_string(item.line) +
-                      " message=" + item.message;
-
-    syslog(severity, "%s", line.c_str());
-  }
-  return Status(0, "OK");
-}
-
-Status SyslogLoggerPlugin::init(const std::string& name,
-                                const std::vector<StatusLogLine>& log) {
-  closelog();
-
-  // Define the syslog/target's application name.
-  if (FLAGS_logger_syslog_facility < 0 ||
-      FLAGS_logger_syslog_facility > 23) {
-    FLAGS_logger_syslog_facility = LOG_LOCAL3 >> 3;
-  }
-  openlog(name.c_str(), LOG_PID | LOG_CONS, FLAGS_logger_syslog_facility << 3);
-
-  // Now funnel the intermediate status logs provided to `init`.
-  return logStatus(log);
-}
-}
diff --git a/osquery/logger/tests/logger_tests.cpp b/osquery/logger/tests/logger_tests.cpp
deleted file mode 100644 (file)
index 8c4e81d..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-
-DECLARE_string(logger_plugin);
-
-class LoggerTests : public testing::Test {
- public:
-  void SetUp() {
-    logging_status_ = FLAGS_disable_logging;
-    FLAGS_disable_logging = false;
-
-    log_lines.clear();
-    status_messages.clear();
-    statuses_logged = 0;
-    last_status = {O_INFO, "", -1, ""};
-  }
-
-  void TearDown() { FLAGS_disable_logging = logging_status_; }
-
-  // Track lines emitted to logString
-  static std::vector<std::string> log_lines;
-
-  // Track the results of init
-  static StatusLogLine last_status;
-  static std::vector<std::string> status_messages;
-
-  // Count calls to logStatus
-  static int statuses_logged;
-  // Count added and removed snapshot rows
-  static int snapshot_rows_added;
-  static int snapshot_rows_removed;
-  // Count the added health status rows
-  static int health_status_rows;
-
- private:
-  /// Save the status of logging before running tests, restore afterward.
-  bool logging_status_;
-};
-
-std::vector<std::string> LoggerTests::log_lines;
-StatusLogLine LoggerTests::last_status;
-std::vector<std::string> LoggerTests::status_messages;
-int LoggerTests::statuses_logged = 0;
-int LoggerTests::snapshot_rows_added = 0;
-int LoggerTests::snapshot_rows_removed = 0;
-int LoggerTests::health_status_rows = 0;
-
-class TestLoggerPlugin : public LoggerPlugin {
- public:
-  TestLoggerPlugin() {}
-
-  Status logString(const std::string& s) {
-    LoggerTests::log_lines.push_back(s);
-    return Status(0, s);
-  }
-
-  Status init(const std::string& name, const std::vector<StatusLogLine>& log) {
-    for (const auto& status : log) {
-      LoggerTests::status_messages.push_back(status.message);
-    }
-
-    if (log.size() > 0) {
-      LoggerTests::last_status = log.back();
-    }
-
-    if (name == "RETURN_FAILURE") {
-      return Status(1, "OK");
-    } else {
-      return Status(0, "OK");
-    }
-  }
-
-  Status logStatus(const std::vector<StatusLogLine>& log) {
-    ++LoggerTests::statuses_logged;
-    return Status(0, "OK");
-  }
-
-  Status logSnapshot(const std::string& s) {
-    LoggerTests::snapshot_rows_added += 1;
-    LoggerTests::snapshot_rows_removed += 0;
-    return Status(0, "OK");
-  }
-
-  Status logHealth(const std::string& s) {
-    LoggerTests::health_status_rows += 1;
-    return Status(0, "OK");
-  }
-
-  virtual ~TestLoggerPlugin() {}
-};
-
-TEST_F(LoggerTests, test_plugin) {
-  Registry::add<TestLoggerPlugin>("logger", "test");
-  Registry::setUp();
-
-  auto s = Registry::call("logger", "test", {{"string", "foobar"}});
-  EXPECT_TRUE(s.ok());
-  EXPECT_EQ(LoggerTests::log_lines.back(), "foobar");
-}
-
-TEST_F(LoggerTests, test_logger_init) {
-  // Expect the logger to have been registered from the first test.
-  EXPECT_TRUE(Registry::exists("logger", "test"));
-  EXPECT_TRUE(Registry::setActive("logger", "test").ok());
-
-  initStatusLogger("logger_test");
-  // This will be printed to stdout.
-  LOG(WARNING) << "Logger test is generating a warning status (1)";
-  initLogger("logger_test");
-
-  // The warning message will have been buffered and sent to the active logger
-  // which is test.
-  EXPECT_EQ(LoggerTests::status_messages.size(), 1);
-
-  // The logStatus API should NOT have been called. It will only be used if
-  // (1) The active logger's init returns success within initLogger and
-  // (2) for status logs generated after initLogger is called.
-  EXPECT_EQ(LoggerTests::statuses_logged, 0);
-}
-
-TEST_F(LoggerTests, test_logger_log_status) {
-  // This will be printed to stdout.
-  LOG(WARNING) << "Logger test is generating a warning status (2)";
-
-  // The second warning status will be sent to the logger plugin.
-  EXPECT_EQ(LoggerTests::statuses_logged, 1);
-}
-
-TEST_F(LoggerTests, test_logger_variations) {
-  // Init the logger for a second time, this should only be done for testing.
-  // This time we'll trigger the init method to fail and prevent additional
-  // status messages from trigger logStatus.
-  initLogger("RETURN_FAILURE");
-
-  // This will be printed to stdout.
-  LOG(WARNING) << "Logger test is generating a warning status (3)";
-
-  // Since the initLogger call triggered a failed init, meaning the logger
-  // does NOT handle Glog logs, there will be no statuses logged.
-  EXPECT_EQ(LoggerTests::statuses_logged, 0);
-}
-
-TEST_F(LoggerTests, test_logger_snapshots) {
-  // A snapshot query should not include removed items.
-  QueryLogItem item;
-  item.name = "test_query";
-  item.identifier = "unknown_test_host";
-  item.time = 0;
-  item.calendar_time = "no_time";
-
-  // Add a fake set of results.
-  item.results.added.push_back({{"test_column", "test_value"}});
-  logSnapshotQuery(item);
-
-  // Expect the plugin to optionally handle snapshot logging.
-  EXPECT_EQ(LoggerTests::snapshot_rows_added, 1);
-
-  // Add the same item as a health status log item.
-  logHealthStatus(item);
-  EXPECT_EQ(LoggerTests::health_status_rows, 1);
-}
-}
diff --git a/osquery/main/daemon.cpp b/osquery/main/daemon.cpp
deleted file mode 100644 (file)
index 8b376e6..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/thread.hpp>
-
-#include <osquery/core.h>
-
-#include "osquery/dispatcher/scheduler.h"
-
-const std::string kWatcherWorkerName = "osqueryd: worker";
-
-int main(int argc, char* argv[]) {
-  osquery::Initializer runner(argc, argv, osquery::OSQUERY_TOOL_DAEMON);
-
-  if (!runner.isWorker()) {
-    runner.initDaemon();
-  }
-
-  // When a watchdog is used, the current daemon will fork/exec into a worker.
-  // In either case the watcher may start optionally loaded extensions.
-  runner.initWorkerWatcher(kWatcherWorkerName);
-
-  // Start osquery work.
-  runner.start();
-
-  // Begin the schedule runloop.
-  osquery::startScheduler();
-
-  // Finally shutdown.
-  runner.shutdown();
-
-  return 0;
-}
diff --git a/osquery/main/empty.cpp b/osquery/main/empty.cpp
deleted file mode 100644 (file)
index ab9ead5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
diff --git a/osquery/main/lib.cpp b/osquery/main/lib.cpp
deleted file mode 100644 (file)
index 8de6ca6..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-#include <string>
-
-#include <osquery/core.h>
-
-// If CMake/gmake did not define a build version set the version to 1.0.
-// clang-format off
-#ifndef OSQUERY_BUILD_VERSION
-#define OSQUERY_BUILD_VERSION 1.0.0-unknown
-#endif
-// clang-format on
-
-namespace osquery {
-
-const std::string kVersion = STR(OSQUERY_BUILD_VERSION);
-const std::string kSDKVersion = OSQUERY_SDK_VERSION;
-const std::string kSDKPlatform = OSQUERY_PLATFORM;
-}
diff --git a/osquery/main/run.cpp b/osquery/main/run.cpp
deleted file mode 100644 (file)
index 62a933a..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <errno.h>
-
-#include <gflags/gflags.h>
-
-#include <osquery/core.h>
-#include <osquery/events.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-
-DEFINE_string(query, "", "query to execute");
-DEFINE_int32(iterations, 1, "times to run the query in question");
-DEFINE_int32(delay, 0, "delay before and after the query");
-
-namespace osquery {
-
-DECLARE_bool(disable_events);
-DECLARE_bool(registry_exceptions);
-}
-
-int main(int argc, char* argv[]) {
-  // Only log to stderr
-  FLAGS_logtostderr = true;
-
-  // Let gflags parse the non-help options/flags.
-  GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, false);
-  GFLAGS_NAMESPACE::InitGoogleLogging(argv[0]);
-
-  if (FLAGS_query == "") {
-    fprintf(stderr, "Usage: %s --query=\"query\"\n", argv[0]);
-    return 1;
-  }
-
-  osquery::Registry::setUp();
-  osquery::FLAGS_disable_events = true;
-  osquery::FLAGS_registry_exceptions = true;
-  osquery::attachEvents();
-
-  if (FLAGS_delay != 0) {
-    ::sleep(FLAGS_delay);
-  }
-
-  osquery::QueryData results;
-  osquery::Status status;
-  for (int i = 0; i < FLAGS_iterations; ++i) {
-    status = osquery::query(FLAGS_query, results);
-    if (!status.ok()) {
-      fprintf(stderr, "Query failed: %d\n", status.getCode());
-      break;
-    }
-  }
-
-  if (FLAGS_delay != 0) {
-    ::sleep(FLAGS_delay);
-  }
-
-  // Instead of calling "shutdownOsquery" force the EF to join its threads.
-  GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
-
-  return status.getCode();
-}
diff --git a/osquery/main/shell.cpp b/osquery/main/shell.cpp
deleted file mode 100644 (file)
index b6c1744..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <stdio.h>
-
-#include <osquery/core.h>
-#include <osquery/extensions.h>
-
-#include "osquery/core/watcher.h"
-#include "osquery/devtools/devtools.h"
-
-int main(int argc, char *argv[]) {
-  // Parse/apply flags, start registry, load logger/config plugins.
-  osquery::Initializer runner(argc, argv, osquery::OSQUERY_TOOL_SHELL);
-  if (argc > 1 || !isatty(fileno(stdin)) || osquery::FLAGS_A.size() > 0 ||
-      osquery::FLAGS_L) {
-    // A query was set as a positional argument for via stdin.
-    osquery::FLAGS_disable_events = true;
-    // The shell may have loaded table extensions, if not, disable the manager.
-    if (!osquery::Watcher::hasManagedExtensions()) {
-      osquery::FLAGS_disable_extensions = true;
-    }
-  }
-
-  runner.start();
-
-  // Virtual tables will be attached to the shell's in-memory SQLite DB.
-  int retcode = osquery::launchIntoShell(argc, argv);
-
-  // Finally shutdown.
-  runner.shutdown();
-  return retcode;
-}
diff --git a/osquery/main/tests.cpp b/osquery/main/tests.cpp
deleted file mode 100644 (file)
index d25960d..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- *  Copyright (c) 2015, Wesley Shields
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <cstdlib>
-#include <chrono>
-
-#include <time.h>
-
-#include <boost/filesystem.hpp>
-
-#include <gtest/gtest.h>
-
-#include "osquery/core/test_util.h"
-#include "osquery/database/db_handle.h"
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-DECLARE_string(database_path);
-DECLARE_string(extensions_socket);
-DECLARE_string(modules_autoload);
-DECLARE_string(extensions_autoload);
-DECLARE_bool(disable_logging);
-DECLARE_bool(verbose);
-
-typedef std::chrono::high_resolution_clock chrono_clock;
-
-void initTesting() {
-  // Seed the random number generator, some tests generate temporary files
-  // ports, sockets, etc using random numbers.
-  std::srand(chrono_clock::now().time_since_epoch().count());
-
-  // Set safe default values for path-based flags.
-  // Specific unittests may edit flags temporarily.
-  fs::remove_all(kTestWorkingDirectory);
-  fs::create_directories(kTestWorkingDirectory);
-  FLAGS_database_path = kTestWorkingDirectory + "unittests.db";
-  FLAGS_extensions_socket = kTestWorkingDirectory + "unittests.em";
-  FLAGS_extensions_autoload = kTestWorkingDirectory + "unittests-ext.load";
-  FLAGS_modules_autoload = kTestWorkingDirectory + "unittests-mod.load";
-  FLAGS_disable_logging = true;
-  FLAGS_verbose = true;
-
-  // Create a default DBHandle instance before unittests.
-  (void)DBHandle::getInstance();
-}
-}
-
-int main(int argc, char* argv[]) {
-  // Allow unit test execution from anywhere in the osquery source/build tree.
-  while (osquery::kTestDataPath != "/") {
-    if (!fs::exists(osquery::kTestDataPath)) {
-      osquery::kTestDataPath =
-          osquery::kTestDataPath.substr(3, osquery::kTestDataPath.size());
-    } else {
-      break;
-    }
-  }
-
-  osquery::initTesting();
-  testing::InitGoogleTest(&argc, argv);
-  // Optionally enable Goggle Logging
-  // google::InitGoogleLogging(argv[0]);
-  return RUN_ALL_TESTS();
-}
diff --git a/osquery/registry/CMakeLists.txt b/osquery/registry/CMakeLists.txt
deleted file mode 100644 (file)
index b2dfbda..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_registry registry.cpp)
-
-FILE(GLOB OSQUERY_REGISTRY_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_REGISTRY_TESTS})
diff --git a/osquery/registry/registry.cpp b/osquery/registry/registry.cpp
deleted file mode 100644 (file)
index a5acd7b..0000000
+++ /dev/null
@@ -1,537 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <cstdlib>
-#include <sstream>
-
-#include <dlfcn.h>
-
-#include <boost/property_tree/json_parser.hpp>
-
-#include <osquery/extensions.h>
-#include <osquery/logger.h>
-#include <osquery/registry.h>
-
-namespace osquery {
-
-HIDDEN_FLAG(bool, registry_exceptions, false, "Allow plugin exceptions");
-
-void RegistryHelperCore::remove(const std::string& item_name) {
-  if (items_.count(item_name) > 0) {
-    items_[item_name]->tearDown();
-    items_.erase(item_name);
-  }
-
-  // Populate list of aliases to remove (those that mask item_name).
-  std::vector<std::string> removed_aliases;
-  for (const auto& alias : aliases_) {
-    if (alias.second == item_name) {
-      removed_aliases.push_back(alias.first);
-    }
-  }
-
-  for (const auto& alias : removed_aliases) {
-    aliases_.erase(alias);
-  }
-}
-
-bool RegistryHelperCore::isInternal(const std::string& item_name) const {
-  if (std::find(internal_.begin(), internal_.end(), item_name) ==
-      internal_.end()) {
-    return false;
-  }
-  return true;
-}
-
-Status RegistryHelperCore::setActive(const std::string& item_name) {
-  if (items_.count(item_name) == 0 && external_.count(item_name) == 0) {
-    return Status(1, "Unknown registry item");
-  }
-
-  active_ = item_name;
-  // The active plugin is setup when initialized.
-  if (exists(item_name, true)) {
-    Registry::get(name_, item_name)->setUp();
-  }
-  return Status(0, "OK");
-}
-
-const std::string& RegistryHelperCore::getActive() const { return active_; }
-
-RegistryRoutes RegistryHelperCore::getRoutes() const {
-  RegistryRoutes route_table;
-  for (const auto& item : items_) {
-    if (isInternal(item.first)) {
-      // This is an internal plugin, do not include the route.
-      continue;
-    }
-
-    bool has_alias = false;
-    for (const auto& alias : aliases_) {
-      if (alias.second == item.first) {
-        // If the item name is masked by at least one alias, it will not
-        // broadcast under the internal item name.
-        route_table[alias.first] = item.second->routeInfo();
-        has_alias = true;
-      }
-    }
-
-    if (!has_alias) {
-      route_table[item.first] = item.second->routeInfo();
-    }
-  }
-  return route_table;
-}
-
-Status RegistryHelperCore::call(const std::string& item_name,
-                                const PluginRequest& request,
-                                PluginResponse& response) {
-  if (items_.count(item_name) > 0) {
-    return items_.at(item_name)->call(request, response);
-  }
-
-  if (external_.count(item_name) > 0) {
-    // The item is a registered extension, call the extension by UUID.
-    return callExtension(external_.at(item_name), name_, item_name, request,
-                         response);
-  } else if (routes_.count(item_name) > 0) {
-    // The item has a route, but no extension, pass in the route info.
-    response = routes_.at(item_name);
-    return Status(0, "Route only");
-  } else if (Registry::external()) {
-    // If this is an extension's registry forward unknown calls to the core.
-    return callExtension(0, name_, item_name, request, response);
-  }
-
-  return Status(1, "Cannot call registry item: " + item_name);
-}
-
-Status RegistryHelperCore::addAlias(const std::string& item_name,
-                                    const std::string& alias) {
-  if (aliases_.count(alias) > 0) {
-    return Status(1, "Duplicate alias: " + alias);
-  }
-  aliases_[alias] = item_name;
-  return Status(0, "OK");
-}
-
-const std::string& RegistryHelperCore::getAlias(
-    const std::string& alias) const {
-  if (aliases_.count(alias) == 0) {
-    return alias;
-  }
-  return aliases_.at(alias);
-}
-
-void RegistryHelperCore::setUp() {
-  // If this registry does not auto-setup do NOT setup the registry items.
-  if (!auto_setup_) {
-    return;
-  }
-
-  // If the registry is using a single 'active' plugin, setUp that plugin.
-  // For config and logger, only setUp the selected plugin.
-  if (active_.size() != 0 && exists(active_, true)) {
-    items_.at(active_)->setUp();
-    return;
-  }
-
-  // Try to set up each of the registry items.
-  // If they fail, remove them from the registry.
-  std::vector<std::string> failed;
-  for (auto& item : items_) {
-    if (!item.second->setUp().ok()) {
-      failed.push_back(item.first);
-    }
-  }
-
-  for (const auto& failed_item : failed) {
-    remove(failed_item);
-  }
-}
-
-/// Facility method to check if a registry item exists.
-bool RegistryHelperCore::exists(const std::string& item_name,
-                                bool local) const {
-  bool has_local = (items_.count(item_name) > 0);
-  bool has_external = (external_.count(item_name) > 0);
-  bool has_route = (routes_.count(item_name) > 0);
-  return (local) ? has_local : has_local || has_external || has_route;
-}
-
-/// Facility method to list the registry item identifiers.
-std::vector<std::string> RegistryHelperCore::names() const {
-  std::vector<std::string> names;
-  for (const auto& item : items_) {
-    names.push_back(item.first);
-  }
-
-  // Also add names of external plugins.
-  for (const auto& item : external_) {
-    names.push_back(item.first);
-  }
-  return names;
-}
-
-/// Facility method to count the number of items in this registry.
-size_t RegistryHelperCore::count() const { return items_.size(); }
-
-/// Allow the registry to introspect into the registered name (for logging).
-void RegistryHelperCore::setName(const std::string& name) { name_ = name; }
-
-const std::map<std::string, PluginRegistryHelperRef>& RegistryFactory::all() {
-  return instance().registries_;
-}
-
-PluginRegistryHelperRef RegistryFactory::registry(
-    const std::string& registry_name) {
-  return instance().registries_.at(registry_name);
-}
-
-const std::map<std::string, PluginRef> RegistryFactory::all(
-    const std::string& registry_name) {
-  return instance().registry(registry_name)->all();
-}
-
-PluginRef RegistryFactory::get(const std::string& registry_name,
-                               const std::string& item_name) {
-  return instance().registry(registry_name)->get(item_name);
-}
-
-RegistryBroadcast RegistryFactory::getBroadcast() {
-  RegistryBroadcast broadcast;
-  for (const auto& registry : instance().registries_) {
-    broadcast[registry.first] = registry.second->getRoutes();
-  }
-  return broadcast;
-}
-
-Status RegistryFactory::addBroadcast(const RouteUUID& uuid,
-                                     const RegistryBroadcast& broadcast) {
-  if (instance().extensions_.count(uuid) > 0) {
-    return Status(1, "Duplicate extension UUID: " + std::to_string(uuid));
-  }
-
-  // Make sure the extension does not broadcast conflicting registry items.
-  if (!Registry::allowDuplicates()) {
-    for (const auto& registry : broadcast) {
-      for (const auto& item : registry.second) {
-        if (Registry::exists(registry.first, item.first)) {
-          VLOG(1) << "Extension " << uuid
-                  << " has duplicate plugin name: " << item.first
-                  << " in registry: " << registry.first;
-          return Status(1, "Duplicate registry item: " + item.first);
-        }
-      }
-    }
-  }
-
-  // Once duplication is satisfied call each registry's addExternal.
-  Status status;
-  for (const auto& registry : broadcast) {
-    status = RegistryFactory::registry(registry.first)
-                 ->addExternal(uuid, registry.second);
-    if (!status.ok()) {
-      // If any registry fails to add the set of external routes, stop.
-      break;
-    }
-
-    for (const auto& plugin : registry.second) {
-      VLOG(1) << "Extension " << uuid << " registered " << registry.first
-              << " plugin " << plugin.first;
-    }
-  }
-
-  // If any registry failed, remove each (assume a broadcast is atomic).
-  if (!status.ok()) {
-    for (const auto& registry : broadcast) {
-      Registry::registry(registry.first)->removeExternal(uuid);
-    }
-  }
-  instance().extensions_.insert(uuid);
-  return status;
-}
-
-Status RegistryFactory::removeBroadcast(const RouteUUID& uuid) {
-  if (instance().extensions_.count(uuid) == 0) {
-    return Status(1, "Unknown extension UUID: " + std::to_string(uuid));
-  }
-
-  for (const auto& registry : instance().registries_) {
-    registry.second->removeExternal(uuid);
-  }
-  instance().extensions_.erase(uuid);
-  return Status(0, "OK");
-}
-
-/// Adds an alias for an internal registry item. This registry will only
-/// broadcast the alias name.
-Status RegistryFactory::addAlias(const std::string& registry_name,
-                                 const std::string& item_name,
-                                 const std::string& alias) {
-  if (instance().registries_.count(registry_name) == 0) {
-    return Status(1, "Unknown registry: " + registry_name);
-  }
-  return instance().registries_.at(registry_name)->addAlias(item_name, alias);
-}
-
-/// Returns the item_name or the item alias if an alias exists.
-const std::string& RegistryFactory::getAlias(const std::string& registry_name,
-                                             const std::string& alias) {
-  if (instance().registries_.count(registry_name) == 0) {
-    return alias;
-  }
-  return instance().registries_.at(registry_name)->getAlias(alias);
-}
-
-Status RegistryFactory::call(const std::string& registry_name,
-                             const std::string& item_name,
-                             const PluginRequest& request,
-                             PluginResponse& response) {
-  // Forward factory call to the registry.
-  try {
-    return registry(registry_name)->call(item_name, request, response);
-  } catch (const std::exception& e) {
-    LOG(ERROR) << registry_name << " registry " << item_name
-               << " plugin caused exception: " << e.what();
-    if (FLAGS_registry_exceptions) {
-      throw e;
-    }
-    return Status(1, e.what());
-  } catch (...) {
-    LOG(ERROR) << registry_name << " registry " << item_name
-               << " plugin caused unknown exception";
-    if (FLAGS_registry_exceptions) {
-      throw std::runtime_error(registry_name + ": " + item_name + " failed");
-    }
-    return Status(2, "Unknown exception");
-  }
-}
-
-Status RegistryFactory::call(const std::string& registry_name,
-                             const std::string& item_name,
-                             const PluginRequest& request) {
-  PluginResponse response;
-  // Wrapper around a call expecting a response.
-  return call(registry_name, item_name, request, response);
-}
-
-Status RegistryFactory::call(const std::string& registry_name,
-                             const PluginRequest& request,
-                             PluginResponse& response) {
-  auto& plugin = registry(registry_name)->getActive();
-  return call(registry_name, plugin, request, response);
-}
-
-Status RegistryFactory::call(const std::string& registry_name,
-                             const PluginRequest& request) {
-  PluginResponse response;
-  return call(registry_name, request, response);
-}
-
-Status RegistryFactory::setActive(const std::string& registry_name,
-                                  const std::string& item_name) {
-  if (!exists(registry_name, item_name)) {
-    return Status(1, "Registry plugin does not exist");
-  }
-  return registry(registry_name)->setActive(item_name);
-}
-
-const std::string& RegistryFactory::getActive(
-    const std::string& registry_name) {
-  return registry(registry_name)->getActive();
-}
-
-void RegistryFactory::setUp() {
-  for (const auto& registry : instance().all()) {
-    registry.second->setUp();
-  }
-}
-
-bool RegistryFactory::exists(const std::string& registry_name,
-                             const std::string& item_name,
-                             bool local) {
-  if (instance().registries_.count(registry_name) == 0) {
-    return false;
-  }
-
-  // Check the registry.
-  return registry(registry_name)->exists(item_name, local);
-}
-
-std::vector<std::string> RegistryFactory::names() {
-  std::vector<std::string> names;
-  for (const auto& registry : all()) {
-    names.push_back(registry.second->getName());
-  }
-  return names;
-}
-
-std::vector<std::string> RegistryFactory::names(
-    const std::string& registry_name) {
-  if (instance().registries_.at(registry_name) == 0) {
-    std::vector<std::string> names;
-    return names;
-  }
-  return instance().registry(registry_name)->names();
-}
-
-std::vector<RouteUUID> RegistryFactory::routeUUIDs() {
-  std::vector<RouteUUID> uuids;
-  for (const auto& extension : instance().extensions_) {
-    uuids.push_back(extension);
-  }
-  return uuids;
-}
-
-size_t RegistryFactory::count() { return instance().registries_.size(); }
-
-size_t RegistryFactory::count(const std::string& registry_name) {
-  if (instance().registries_.count(registry_name) == 0) {
-    return 0;
-  }
-  return instance().registry(registry_name)->count();
-}
-
-Status RegistryHelperCore::add(const std::string& item_name, bool internal) {
-  // The item can be listed as internal, meaning it does not broadcast.
-  if (internal) {
-    internal_.push_back(item_name);
-  }
-
-  // The item may belong to a module.
-  if (RegistryFactory::usingModule()) {
-    modules_[item_name] = RegistryFactory::getModule();
-  }
-
-  return Status(0, "OK");
-}
-
-const std::map<RouteUUID, ModuleInfo>& RegistryFactory::getModules() {
-  return instance().modules_;
-}
-
-RouteUUID RegistryFactory::getModule() { return instance().module_uuid_; }
-
-bool RegistryFactory::usingModule() {
-  // Check if the registry is allowing a module's registrations.
-  return (!instance().locked() && instance().module_uuid_ != 0);
-}
-
-void RegistryFactory::shutdownModule() {
-  // TODO: [temporarily disable] should be check.
-  //instance().locked(true);
-  instance().module_uuid_ = 0;
-}
-
-void RegistryFactory::initModule(const std::string& path) {
-  // Begin a module initialization, lock until the module is determined
-  // appropriate by requesting a call to `declareModule`.
-  instance().module_uuid_ = (RouteUUID)rand();
-  instance().modules_[getModule()].path = path;
-  instance().locked(true);
-}
-
-void RegistryFactory::declareModule(const std::string& name,
-                                    const std::string& version,
-                                    const std::string& min_sdk_version,
-                                    const std::string& sdk_version) {
-  // Check the min_sdk_version against the Registry's SDK version.
-  auto& module = instance().modules_[instance().module_uuid_];
-  module.name = name;
-  module.version = version;
-  module.sdk_version = sdk_version;
-  instance().locked(false);
-}
-
-RegistryModuleLoader::RegistryModuleLoader(const std::string& path)
-    : handle_(nullptr), path_(path) {
-  // Tell the registry that we are attempting to construct a module.
-  // Locking the registry prevents the module's global initialization from
-  // adding or creating registry items.
-  RegistryFactory::initModule(path_);
-  handle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL);
-  if (handle_ == nullptr) {
-    VLOG(1) << "Failed to load module: " << path_;
-    VLOG(1) << dlerror();
-    return;
-  }
-
-  // The module should have called RegistryFactory::declareModule and unlocked
-  // the registry for modification. The module should have done this using
-  // the SDK's CREATE_MODULE macro, which adds the global-scope constructor.
-  if (RegistryFactory::locked()) {
-    VLOG(1) << "Failed to declare module: " << path_;
-    dlclose(handle_);
-    handle_ = nullptr;
-  }
-}
-
-void RegistryModuleLoader::init() {
-  if (handle_ == nullptr || RegistryFactory::locked()) {
-    handle_ = nullptr;
-    return;
-  }
-
-  // Locate a well-known symbol in the module.
-  // This symbol name is protected against rewriting when the module uses the
-  // SDK's CREATE_MODULE macro.
-  auto initializer = (ModuleInitalizer)dlsym(handle_, "initModule");
-  if (initializer != nullptr) {
-    initializer();
-    VLOG(1) << "Initialized module: " << path_;
-  } else {
-    VLOG(1) << "Failed to initialize module: " << path_;
-    VLOG(1) << dlerror();
-    dlclose(handle_);
-    handle_ = nullptr;
-  }
-}
-
-RegistryModuleLoader::~RegistryModuleLoader() {
-  if (handle_ == nullptr) {
-    // The module was not loaded or did not initalize.
-    RegistryFactory::instance().modules_.erase(RegistryFactory::getModule());
-  }
-
-  // We do not close the module, and thus are OK with losing a reference to the
-  // module's handle. Attempting to close and clean up is very expensive for
-  // very little value/features.
-  if (!RegistryFactory::locked()) {
-    RegistryFactory::shutdownModule();
-  }
-  // No need to clean this resource.
-  handle_ = nullptr;
-}
-
-void Plugin::getResponse(const std::string& key,
-                         const PluginResponse& response,
-                         boost::property_tree::ptree& tree) {
-  for (const auto& item : response) {
-    boost::property_tree::ptree child;
-    for (const auto& item_detail : item) {
-      child.put(item_detail.first, item_detail.second);
-    }
-    tree.add_child(key, child);
-  }
-}
-
-void Plugin::setResponse(const std::string& key,
-                         const boost::property_tree::ptree& tree,
-                         PluginResponse& response) {
-  std::ostringstream output;
-  try {
-    boost::property_tree::write_json(output, tree, false);
-  } catch (const pt::json_parser::json_parser_error& e) {
-    // The plugin response could not be serialized.
-  }
-  response.push_back({{key, output.str()}});
-}
-}
diff --git a/osquery/registry/tests/registry_tests.cpp b/osquery/registry/tests/registry_tests.cpp
deleted file mode 100644 (file)
index 381a4e4..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/logger.h>
-#include <osquery/registry.h>
-
-namespace osquery {
-
-class RegistryTests : public testing::Test {};
-
-class CatPlugin : public Plugin {
- public:
-  CatPlugin() : some_value_(0) {}
-
- protected:
-  int some_value_;
-};
-
-class HouseCat : public CatPlugin {
- public:
-  Status setUp() {
-    // Make sure the Plugin implementation's init is called.
-    some_value_ = 9000;
-    return Status(0, "OK");
-  }
-};
-
-/// This is a manual registry type without a name, so we cannot broadcast
-/// this registry type and it does NOT need to conform to a registry API.
-class CatRegistry : public RegistryHelper<CatPlugin> {};
-
-TEST_F(RegistryTests, test_registry) {
-  CatRegistry cats;
-
-  /// Add a CatRegistry item (a plugin) called "house".
-  cats.add<HouseCat>("house");
-  EXPECT_EQ(cats.count(), 1);
-
-  /// Try to add the same plugin with the same name, this is meaningless.
-  cats.add<HouseCat>("house");
-  /// Now add the same plugin with a different name, a new plugin instance
-  /// will be created and registered.
-  cats.add<HouseCat>("house2");
-  EXPECT_EQ(cats.count(), 2);
-
-  /// Request a plugin to call an API method.
-  auto cat = cats.get("house");
-  cats.setUp();
-
-  /// Now let's iterate over every registered Cat plugin.
-  EXPECT_EQ(cats.all().size(), 2);
-}
-
-/// Normally we have "Registry" that dictates the set of possible API methods
-/// for all registry types. Here we use a "TestRegistry" instead.
-class TestCoreRegistry : public RegistryFactory {};
-
-/// We can automatically create a registry type as long as that type conforms
-/// to the registry API defined in the "Registry". Here we use "TestRegistry".
-/// The above "CatRegistry" was easier to understand, but using a auto
-/// registry via the registry create method, we can assign a tracked name
-/// and then broadcast that registry name to other plugins.
-auto AutoCatRegistry = TestCoreRegistry::create<CatPlugin>("cat");
-
-TEST_F(RegistryTests, test_auto_factory) {
-  /// Using the registry, and a registry type by name, we can register a
-  /// plugin HouseCat called "house" like above.
-  TestCoreRegistry::registry("cat")->add<HouseCat>("auto_house");
-  TestCoreRegistry::add<HouseCat>("cat", "auto_house2");
-  TestCoreRegistry::registry("cat")->setUp();
-
-  /// When acting on registries by name we can check the broadcasted
-  /// registry name of other plugin processes (via Thrift) as well as
-  /// internally registered plugins like HouseCat.
-  EXPECT_EQ(TestCoreRegistry::registry("cat")->count(), 2);
-  EXPECT_EQ(TestCoreRegistry::count("cat"), 2);
-
-  /// And we can call an API method, since we guarantee CatPlugins conform
-  /// to the "TestCoreRegistry"'s "TestPluginAPI".
-  auto cat = TestCoreRegistry::get("cat", "auto_house");
-  auto same_cat = TestCoreRegistry::get("cat", "auto_house");
-  EXPECT_EQ(cat, same_cat);
-}
-
-class DogPlugin : public Plugin {
- public:
-  DogPlugin() : some_value_(10000) {}
-
- protected:
-  int some_value_;
-};
-
-class Doge : public DogPlugin {
- public:
-  Doge() { some_value_ = 100000; }
-};
-
-class BadDoge : public DogPlugin {
- public:
-  Status setUp() { return Status(1, "Expect error... this is a bad dog"); }
-};
-
-auto AutoDogRegistry = TestCoreRegistry::create<DogPlugin>("dog", true);
-
-TEST_F(RegistryTests, test_auto_registries) {
-  TestCoreRegistry::add<Doge>("dog", "doge");
-  TestCoreRegistry::registry("dog")->setUp();
-
-  EXPECT_EQ(TestCoreRegistry::count("dog"), 1);
-}
-
-TEST_F(RegistryTests, test_persistant_registries) {
-  EXPECT_EQ(TestCoreRegistry::count("cat"), 2);
-}
-
-TEST_F(RegistryTests, test_registry_exceptions) {
-  EXPECT_TRUE(TestCoreRegistry::add<Doge>("dog", "duplicate_dog").ok());
-  // Bad dog will be added fine, but when setup is run, it will be removed.
-  EXPECT_TRUE(TestCoreRegistry::add<BadDoge>("dog", "bad_doge").ok());
-  TestCoreRegistry::registry("dog")->setUp();
-  // Make sure bad dog does not exist.
-  EXPECT_FALSE(TestCoreRegistry::exists("dog", "bad_doge"));
-  EXPECT_EQ(TestCoreRegistry::count("dog"), 2);
-
-  int exception_count = 0;
-  try {
-    TestCoreRegistry::registry("does_not_exist");
-  } catch (const std::out_of_range& e) {
-    exception_count++;
-  }
-
-  try {
-    TestCoreRegistry::add<HouseCat>("does_not_exist", "cat");
-  } catch (const std::out_of_range& e) {
-    exception_count++;
-  }
-
-  EXPECT_EQ(exception_count, 2);
-}
-
-class WidgetPlugin : public Plugin {
- public:
-  /// The route information will usually be provided by the plugin type.
-  /// The plugin/registry item will set some structures for the plugin
-  /// to parse and format. BUT a plugin/registry item can also fill this
-  /// information in if the plugin type/registry type exposes routeInfo as
-  /// a virtual method.
-  PluginResponse routeInfo() const {
-    PluginResponse info;
-    info.push_back({{"name", name_}});
-    return info;
-  }
-
-  /// Plugin types should contain generic request/response formatters and
-  /// decorators.
-  std::string secretPower(const PluginRequest& request) const {
-    if (request.count("secret_power") > 0) {
-      return request.at("secret_power");
-    }
-    return "no_secret_power";
-  }
-};
-
-class SpecialWidget : public WidgetPlugin {
- public:
-  Status call(const PluginRequest& request, PluginResponse& response);
-};
-
-Status SpecialWidget::call(const PluginRequest& request,
-                           PluginResponse& response) {
-  response.push_back(request);
-  response[0]["from"] = name_;
-  response[0]["secret_power"] = secretPower(request);
-  return Status(0, "OK");
-}
-
-#define UNUSED(x) (void)(x)
-
-TEST_F(RegistryTests, test_registry_api) {
-  auto AutoWidgetRegistry = TestCoreRegistry::create<WidgetPlugin>("widgets");
-  UNUSED(AutoWidgetRegistry);
-
-  TestCoreRegistry::add<SpecialWidget>("widgets", "special");
-
-  // Test route info propogation, from item to registry, to broadcast.
-  auto ri = TestCoreRegistry::get("widgets", "special")->routeInfo();
-  EXPECT_EQ(ri[0].at("name"), "special");
-  auto rr = TestCoreRegistry::registry("widgets")->getRoutes();
-  EXPECT_EQ(rr.size(), 1);
-  EXPECT_EQ(rr.at("special")[0].at("name"), "special");
-
-  // Broadcast will include all registries, and all their items.
-  auto broadcast_info = TestCoreRegistry::getBroadcast();
-  EXPECT_TRUE(broadcast_info.size() >= 3);
-  EXPECT_EQ(broadcast_info.at("widgets").at("special")[0].at("name"),
-            "special");
-
-  PluginResponse response;
-  PluginRequest request;
-  auto status = TestCoreRegistry::call("widgets", "special", request, response);
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(response[0].at("from"), "special");
-  EXPECT_EQ(response[0].at("secret_power"), "no_secret_power");
-
-  request["secret_power"] = "magic";
-  status = TestCoreRegistry::call("widgets", "special", request, response);
-  EXPECT_EQ(response[0].at("secret_power"), "magic");
-}
-
-TEST_F(RegistryTests, test_real_registry) {
-  EXPECT_TRUE(Registry::count() > 0);
-
-  bool has_one_registered = false;
-  for (const auto& registry : Registry::all()) {
-    if (Registry::count(registry.first) > 0) {
-      has_one_registered = true;
-      break;
-    }
-  }
-  EXPECT_TRUE(has_one_registered);
-}
-
-TEST_F(RegistryTests, test_registry_modules) {
-  // Test the registry's module loading state tracking.
-  RegistryFactory::locked(false);
-  EXPECT_FALSE(RegistryFactory::locked());
-  RegistryFactory::locked(true);
-  EXPECT_TRUE(RegistryFactory::locked());
-  RegistryFactory::locked(false);
-
-  // Test initializing a module load and the module's registry modifications.
-  EXPECT_EQ(RegistryFactory::getModule(), 0);
-  RegistryFactory::initModule("/my/test/module");
-  // The registry is locked, no modifications during module global ctors.
-  EXPECT_TRUE(RegistryFactory::locked());
-  // The 'is the registry using a module' is not set during module ctors.
-  EXPECT_FALSE(RegistryFactory::usingModule());
-  EXPECT_EQ(RegistryFactory::getModules().size(), 1);
-  // The unittest can introspect into the current module.
-  auto& module = RegistryFactory::getModules().at(RegistryFactory::getModule());
-  EXPECT_EQ(module.path, "/my/test/module");
-  EXPECT_EQ(module.name, "");
-  RegistryFactory::declareModule("test", "0.1.1", "0.0.0", "0.0.1");
-  // The registry is unlocked after the module is declared.
-  // This assures that module modifications happen with the correct information
-  // and state tracking (aka the SDK limits, name, and version).
-  EXPECT_FALSE(RegistryFactory::locked());
-  // Now the 'is the registry using a module' is set for the duration of the
-  // modules loading.
-  EXPECT_TRUE(RegistryFactory::usingModule());
-  EXPECT_EQ(module.name, "test");
-  EXPECT_EQ(module.version, "0.1.1");
-  EXPECT_EQ(module.sdk_version, "0.0.1");
-
-  // Finally, when the module load is complete, we clear state.
-  RegistryFactory::shutdownModule();
-  // The registry is again locked.
-// TODO: Check below on higher upstream 
-// EXPECT_TRUE(RegistryFactory::locked());
-  // And the registry is no longer using a module.
-  EXPECT_FALSE(RegistryFactory::usingModule());
-  EXPECT_EQ(RegistryFactory::getModule(), 0);
-}
-}
diff --git a/osquery/sql/CMakeLists.txt b/osquery/sql/CMakeLists.txt
deleted file mode 100644 (file)
index ae844e5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_sql sql.cpp)
-
-ADD_OSQUERY_LIBRARY(osquery_sql_internal sqlite_util.cpp
-                                                                                virtual_table.cpp)
-
-FILE(GLOB OSQUERY_SQL_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_SQL_TESTS})
diff --git a/osquery/sql/sql.cpp b/osquery/sql/sql.cpp
deleted file mode 100644 (file)
index f7b5f87..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sstream>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-#include <osquery/tables.h>
-#include <osquery/registry.h>
-
-namespace osquery {
-
-FLAG(int32, value_max, 512, "Maximum returned row value size");
-
-const std::map<ConstraintOperator, std::string> kSQLOperatorRepr = {
-    {EQUALS, "="},
-    {GREATER_THAN, ">"},
-    {LESS_THAN_OR_EQUALS, "<="},
-    {LESS_THAN, "<"},
-    {GREATER_THAN_OR_EQUALS, ">="},
-};
-
-SQL::SQL(const std::string& q) { status_ = query(q, results_); }
-
-const QueryData& SQL::rows() { return results_; }
-
-bool SQL::ok() { return status_.ok(); }
-
-Status SQL::getStatus() { return status_; }
-
-std::string SQL::getMessageString() { return status_.toString(); }
-
-const std::string SQL::kHostColumnName = "_source_host";
-void SQL::annotateHostInfo() {
-  std::string hostname = getHostname();
-  for (Row& row : results_) {
-    row[kHostColumnName] = hostname;
-  }
-}
-
-std::vector<std::string> SQL::getTableNames() {
-  std::vector<std::string> results;
-  for (const auto& name : Registry::names("table")) {
-    results.push_back(name);
-  }
-  return results;
-}
-
-QueryData SQL::selectAllFrom(const std::string& table) {
-  PluginResponse response;
-  PluginRequest request = {{"action", "generate"}};
-  Registry::call("table", table, request, response);
-  return response;
-}
-
-QueryData SQL::selectAllFrom(const std::string& table,
-                             const std::string& column,
-                             ConstraintOperator op,
-                             const std::string& expr) {
-  PluginResponse response;
-  PluginRequest request = {{"action", "generate"}};
-  QueryContext ctx;
-  ctx.constraints[column].add(Constraint(op, expr));
-
-  TablePlugin::setRequestFromContext(ctx, request);
-  Registry::call("table", table, request, response);
-  return response;
-}
-
-Status SQLPlugin::call(const PluginRequest& request, PluginResponse& response) {
-  response.clear();
-  if (request.count("action") == 0) {
-    return Status(1, "SQL plugin must include a request action");
-  }
-
-  if (request.at("action") == "query") {
-    return this->query(request.at("query"), response);
-  } else if (request.at("action") == "columns") {
-    TableColumns columns;
-    auto status = this->getQueryColumns(request.at("query"), columns);
-    // Convert columns to response
-    for (const auto& column : columns) {
-      response.push_back({{"n", column.first}, {"t", column.second}});
-    }
-    return status;
-  } else if (request.at("action") == "attach") {
-    // Attach a virtual table name using an optional included definition.
-    return this->attach(request.at("table"));
-  } else if (request.at("action") == "detach") {
-    this->detach(request.at("table"));
-    return Status(0, "OK");
-  }
-  return Status(1, "Unknown action");
-}
-
-Status query(const std::string& q, QueryData& results) {
-  return Registry::call(
-      "sql", "sql", {{"action", "query"}, {"query", q}}, results);
-}
-
-Status getQueryColumns(const std::string& q, TableColumns& columns) {
-  PluginResponse response;
-  auto status = Registry::call(
-      "sql", "sql", {{"action", "columns"}, {"query", q}}, response);
-
-  // Convert response to columns
-  for (const auto& item : response) {
-    columns.push_back(make_pair(item.at("n"), item.at("t")));
-  }
-  return status;
-}
-}
diff --git a/osquery/sql/sqlite_util.cpp b/osquery/sql/sqlite_util.cpp
deleted file mode 100644 (file)
index 162ad30..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/core.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-
-#include "osquery/sql/sqlite_util.h"
-#include "osquery/sql/virtual_table.h"
-
-namespace osquery {
-/// SQL provider for osquery internal/core.
-REGISTER_INTERNAL(SQLiteSQLPlugin, "sql", "sql");
-
-FLAG(string,
-     disable_tables,
-     "Not Specified",
-     "Comma-delimited list of table names to be disabled");
-
-/**
- * @brief A map of SQLite status codes to their corresponding message string
- *
- * Details of this map are defined at: http://www.sqlite.org/c3ref/c_abort.html
- */
-const std::map<int, std::string> kSQLiteReturnCodes = {
-    {0, "SQLITE_OK: Successful result"},
-    {1, "SQLITE_ERROR: SQL error or missing database"},
-    {2, "SQLITE_INTERNAL: Internal logic error in SQLite"},
-    {3, "SQLITE_PERM: Access permission denied"},
-    {4, "SQLITE_ABORT: Callback routine requested an abort"},
-    {5, "SQLITE_BUSY: The database file is locked"},
-    {6, "SQLITE_LOCKED: A table in the database is locked"},
-    {7, "SQLITE_NOMEM: A malloc() failed"},
-    {8, "SQLITE_READONLY: Attempt to write a readonly database"},
-    {9, "SQLITE_INTERRUPT: Operation terminated by sqlite3_interrupt()"},
-    {10, "SQLITE_IOERR: Some kind of disk I/O error occurred"},
-    {11, "SQLITE_CORRUPT: The database disk image is malformed"},
-    {12, "SQLITE_NOTFOUND: Unknown opcode in sqlite3_file_control()"},
-    {13, "SQLITE_FULL: Insertion failed because database is full"},
-    {14, "SQLITE_CANTOPEN: Unable to open the database file"},
-    {15, "SQLITE_PROTOCOL: Database lock protocol error"},
-    {16, "SQLITE_EMPTY: Database is empty"},
-    {17, "SQLITE_SCHEMA: The database schema changed"},
-    {18, "SQLITE_TOOBIG: String or BLOB exceeds size limit"},
-    {19, "SQLITE_CONSTRAINT: Abort due to constraint violation"},
-    {20, "SQLITE_MISMATCH: Data type mismatch"},
-    {21, "SQLITE_MISUSE: Library used incorrectly"},
-    {22, "SQLITE_NOLFS: Uses OS features not supported on host"},
-    {23, "SQLITE_AUTH: Authorization denied"},
-    {24, "SQLITE_FORMAT: Auxiliary database format error"},
-    {25, "SQLITE_RANGE: 2nd parameter to sqlite3_bind out of range"},
-    {26, "SQLITE_NOTADB: File opened that is not a database file"},
-    {27, "SQLITE_NOTICE: Notifications from sqlite3_log()"},
-    {28, "SQLITE_WARNING: Warnings from sqlite3_log()"},
-    {100, "SQLITE_ROW: sqlite3_step() has another row ready"},
-    {101, "SQLITE_DONE: sqlite3_step() has finished executing"},
-};
-
-std::string getStringForSQLiteReturnCode(int code) {
-  if (kSQLiteReturnCodes.find(code) != kSQLiteReturnCodes.end()) {
-    return kSQLiteReturnCodes.at(code);
-  } else {
-    std::ostringstream s;
-    s << "Error: " << code << " is not a valid SQLite result code";
-    return s.str();
-  }
-}
-
-Status SQLiteSQLPlugin::attach(const std::string& name) {
-  // This may be the managed DB, or a transient.
-  auto dbc = SQLiteDBManager::get();
-  if (!dbc.isPrimary()) {
-    // Do not "reattach" to transient instance.
-    return Status(0, "OK");
-  }
-
-  PluginResponse response;
-  auto status =
-      Registry::call("table", name, {{"action", "columns"}}, response);
-  if (!status.ok()) {
-    return status;
-  }
-
-  auto statement = columnDefinition(response);
-  return attachTableInternal(name, statement, dbc.db());
-}
-
-void SQLiteSQLPlugin::detach(const std::string& name) {
-  auto dbc = SQLiteDBManager::get();
-  if (!dbc.isPrimary()) {
-    return;
-  }
-  detachTableInternal(name, dbc.db());
-}
-
-SQLiteDBInstance::SQLiteDBInstance() {
-  primary_ = false;
-  sqlite3_open(":memory:", &db_);
-  attachVirtualTables(db_);
-}
-
-SQLiteDBInstance::SQLiteDBInstance(sqlite3*& db) {
-  primary_ = true;
-  db_ = db;
-}
-
-SQLiteDBInstance::~SQLiteDBInstance() {
-  if (!primary_) {
-    sqlite3_close(db_);
-  } else {
-    SQLiteDBManager::unlock();
-    db_ = nullptr;
-  }
-}
-
-void SQLiteDBManager::unlock() { instance().lock_.unlock(); }
-
-bool SQLiteDBManager::isDisabled(const std::string& table_name) {
-  const auto& element = instance().disabled_tables_.find(table_name);
-  return (element != instance().disabled_tables_.end());
-}
-
-std::unordered_set<std::string> SQLiteDBManager::parseDisableTablesFlag(
-    const std::string& list) {
-  const auto& tables = split(list, ",");
-  return std::unordered_set<std::string>(tables.begin(), tables.end());
-}
-
-SQLiteDBInstance SQLiteDBManager::getUnique() { return SQLiteDBInstance(); }
-
-SQLiteDBInstance SQLiteDBManager::get() {
-  auto& self = instance();
-
-  if (!self.lock_.owns_lock() && self.lock_.try_lock()) {
-    if (self.db_ == nullptr) {
-      // Create primary SQLite DB instance.
-      sqlite3_open(":memory:", &self.db_);
-      attachVirtualTables(self.db_);
-    }
-    return SQLiteDBInstance(self.db_);
-  } else {
-    // If this thread or another has the lock, return a transient db.
-    VLOG(1) << "DBManager contention: opening transient SQLite database";
-    return SQLiteDBInstance();
-  }
-}
-
-SQLiteDBManager::~SQLiteDBManager() {
-  if (db_ != nullptr) {
-    sqlite3_close(db_);
-    db_ = nullptr;
-  }
-}
-
-int queryDataCallback(void* argument, int argc, char* argv[], char* column[]) {
-  if (argument == nullptr) {
-    VLOG(1) << "Query execution failed: received a bad callback argument";
-    return SQLITE_MISUSE;
-  }
-
-  QueryData* qData = (QueryData*)argument;
-  Row r;
-  for (int i = 0; i < argc; i++) {
-    if (column[i] != nullptr) {
-      r[column[i]] = (argv[i] != nullptr) ? argv[i] : "";
-    }
-  }
-  (*qData).push_back(std::move(r));
-  return 0;
-}
-
-Status queryInternal(const std::string& q, QueryData& results, sqlite3* db) {
-  char* err = nullptr;
-  sqlite3_exec(db, q.c_str(), queryDataCallback, &results, &err);
-  sqlite3_db_release_memory(db);
-  if (err != nullptr) {
-    auto error_string = std::string(err);
-    sqlite3_free(err);
-    return Status(1, "Error running query: " + error_string);
-  }
-
-  return Status(0, "OK");
-}
-
-Status getQueryColumnsInternal(const std::string& q,
-                               TableColumns& columns,
-                               sqlite3* db) {
-  int rc;
-
-  // Will automatically handle calling sqlite3_finalize on the prepared stmt
-  // (Note that sqlite3_finalize is explicitly a nop for nullptr)
-  std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_managed(
-      nullptr, sqlite3_finalize);
-  sqlite3_stmt* stmt = stmt_managed.get();
-
-  // Turn the query into a prepared statement
-  rc = sqlite3_prepare_v2(db, q.c_str(), q.length() + 1, &stmt, nullptr);
-  if (rc != SQLITE_OK) {
-    return Status(1, sqlite3_errmsg(db));
-  }
-
-  // Get column count
-  int num_columns = sqlite3_column_count(stmt);
-  TableColumns results;
-  results.reserve(num_columns);
-
-  // Get column names and types
-  for (int i = 0; i < num_columns; ++i) {
-    const char* col_name = sqlite3_column_name(stmt, i);
-    const char* col_type = sqlite3_column_decltype(stmt, i);
-    if (col_name == nullptr) {
-      return Status(1, "Got nullptr for column name");
-    }
-    if (col_type == nullptr) {
-      // Types are only returned for table columns (not expressions or
-      // subqueries). See docs for column_decltype
-      // (https://www.sqlite.org/c3ref/column_decltype.html).
-      col_type = "UNKNOWN";
-    }
-    results.push_back({col_name, col_type});
-  }
-
-  columns = std::move(results);
-
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/sql/sqlite_util.h b/osquery/sql/sqlite_util.h
deleted file mode 100644 (file)
index f903867..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <map>
-#include <mutex>
-#include <unordered_set>
-
-#include <sqlite3.h>
-
-#include <boost/thread/mutex.hpp>
-#include <boost/noncopyable.hpp>
-
-#include <osquery/sql.h>
-
-#define SQLITE_SOFT_HEAP_LIMIT (5 * 1024 * 1024)
-
-namespace osquery {
-
-/**
- * @brief An RAII wrapper around an `sqlite3` object.
- *
- * The SQLiteDBInstance is also "smart" in that it may unlock access to a
- * managed `sqlite3` resource. If there's no contention then only a single
- * database is needed during the life of an osquery tool.
- *
- * If there is resource contention (multiple threads want access to the SQLite
- * abstraction layer), then the SQLiteDBManager will provide a transient
- * SQLiteDBInstance.
- */
-class SQLiteDBInstance {
- public:
-  SQLiteDBInstance();
-  explicit SQLiteDBInstance(sqlite3*& db);
-  ~SQLiteDBInstance();
-
-  /// Check if the instance is the osquery primary.
-  bool isPrimary() { return primary_; }
-
-  /**
-   * @brief Accessor to the internal `sqlite3` object, do not store references
-   * to the object within osquery code.
-   */
-  sqlite3* db() { return db_; }
-
- private:
-  bool primary_;
-  sqlite3* db_;
-};
-
-/**
- * @brief osquery internal SQLite DB abstraction resource management.
- *
- * The SQLiteDBManager should be the ONLY method for accessing SQLite resources.
- * The manager provides an abstraction to manage internal SQLite memory and
- * resources as well as provide optimization around resource access.
- */
-class SQLiteDBManager : private boost::noncopyable {
- public:
-  static SQLiteDBManager& instance() {
-    static SQLiteDBManager instance;
-    return instance;
-  }
-
-  /**
-   * @brief Return a fully configured `sqlite3` database object wrapper.
-   *
-   * An osquery database is basically just a SQLite3 database with several
-   * virtual tables attached. This method is the main abstraction for accessing
-   * SQLite3 databases within osquery.
-   *
-   * A RAII wrapper around the `sqlite3` database will manage attaching tables
-   * and freeing resources when the instance (connection per-say) goes out of
-   * scope. Using the SQLiteDBManager will also try to optimize the number of
-   * `sqlite3` databases in use by managing a single global instance and
-   * returning resource-safe transient databases if there's access contention.
-   *
-   * Note: osquery::initOsquery must be called before calling `get` in order
-   * for virtual tables to be registered.
-   *
-   * @return a SQLiteDBInstance with all virtual tables attached.
-   */
-  static SQLiteDBInstance get();
-
-  /// See `get` but always return a transient DB connection (for testing).
-  static SQLiteDBInstance getUnique();
-
-  /**
-   * @brief Check if `table_name` is disabled.
-   *
-   * Check if `table_name` is in the list of tables passed in to the
-   * `--disable_tables` flag.
-   *
-   * @param The name of the Table to check.
-   * @return If `table_name` is disabled.
-   */
-  static bool isDisabled(const std::string& table_name);
-
-  /// When the primary SQLiteDBInstance is destructed it will unlock.
-  static void unlock();
-
- protected:
-  SQLiteDBManager() : db_(nullptr), lock_(mutex_, boost::defer_lock) {
-    sqlite3_soft_heap_limit64(SQLITE_SOFT_HEAP_LIMIT);
-    disabled_tables_ = parseDisableTablesFlag(Flag::getValue("disable_tables"));
-  }
-  SQLiteDBManager(SQLiteDBManager const&);
-  SQLiteDBManager& operator=(SQLiteDBManager const&);
-  virtual ~SQLiteDBManager();
-
- private:
-  /// Primary (managed) sqlite3 database.
-  sqlite3* db_;
-  /// Mutex and lock around sqlite3 access.
-  boost::mutex mutex_;
-  /// Mutex and lock around sqlite3 access.
-  boost::unique_lock<boost::mutex> lock_;
-  /// Member variable to hold set of disabled tables.
-  std::unordered_set<std::string> disabled_tables_;
-  /// Parse a comma-delimited set of tables names, passed in as a flag.
-  std::unordered_set<std::string> parseDisableTablesFlag(const std::string& s);
-};
-
-/**
- * @brief SQLite Internal: Execute a query on a specific database
- *
- * If you need to use a different database, other than the osquery default,
- * use this method and pass along a pointer to a SQLite3 database. This is
- * useful for testing.
- *
- * @param q the query to execute
- * @param results The QueryData struct to emit row on query success.
- * @param db the SQLite3 database to execute query q against
- *
- * @return A status indicating SQL query results.
- */
-Status queryInternal(const std::string& q, QueryData& results, sqlite3* db);
-
-/**
- * @brief SQLite Intern: Analyze a query, providing information about the
- * result columns
- *
- * This function asks SQLite to determine what the names and types are of the
- * result columns of the provided query. Only table columns (not expressions or
- * subqueries) can have their types determined. Types that are not determined
- * are indicated with the string "UNKNOWN".
- *
- * @param q the query to analyze
- * @param columns the vector to fill with column information
- * @param db the SQLite3 database to perform the analysis on
- *
- * @return status indicating success or failure of the operation
- */
-Status getQueryColumnsInternal(const std::string& q,
-                               TableColumns& columns,
-                               sqlite3* db);
-
-/// The SQLiteSQLPlugin implements the "sql" registry for internal/core.
-class SQLiteSQLPlugin : SQLPlugin {
- public:
-  Status query(const std::string& q, QueryData& results) const {
-    auto dbc = SQLiteDBManager::get();
-    return queryInternal(q, results, dbc.db());
-  }
-
-  Status getQueryColumns(const std::string& q, TableColumns& columns) const {
-    auto dbc = SQLiteDBManager::get();
-    return getQueryColumnsInternal(q, columns, dbc.db());
-  }
-
-  /// Create a SQLite module and attach (CREATE).
-  Status attach(const std::string& name);
-  /// Detach a virtual table (DROP).
-  void detach(const std::string& name);
-};
-
-/**
- * @brief Get a string representation of a SQLite return code
- */
-std::string getStringForSQLiteReturnCode(int code);
-
-/**
- * @brief Accumulate rows from an SQLite exec into a QueryData struct.
- *
- * The callback for populating a std::vector<Row> set of results. "argument"
- * should be a non-const reference to a std::vector<Row>.
- */
-int queryDataCallback(void* argument, int argc, char* argv[], char* column[]);
-}
diff --git a/osquery/sql/tests/sql_tests.cpp b/osquery/sql/tests/sql_tests.cpp
deleted file mode 100644 (file)
index 6d994ca..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/registry.h>
-#include <osquery/sql.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-
-class SQLTests : public testing::Test {};
-
-TEST_F(SQLTests, test_raw_access) {
-  // Access to the table plugins (no SQL parsing required) works in both
-  // extensions and core, though with limitations on available tables.
-  auto results = SQL::selectAllFrom("time");
-  EXPECT_EQ(results.size(), 1);
-}
-
-class TestTablePlugin : public TablePlugin {
- private:
-  TableColumns columns() const {
-    return {{"test_int", "INTEGER"}, {"test_text", "TEXT"}};
-  }
-
-  QueryData generate(QueryContext& ctx) {
-    QueryData results;
-    if (ctx.constraints["test_int"].existsAndMatches("1")) {
-      results.push_back({{"test_int", "1"}, {"test_text", "0"}});
-    } else {
-      results.push_back({{"test_int", "0"}, {"test_text", "1"}});
-    }
-
-    auto ints = ctx.constraints["test_int"].getAll<int>(EQUALS);
-    for (const auto& int_match : ints) {
-      results.push_back({{"test_int", INTEGER(int_match)}});
-    }
-
-    return results;
-  }
-};
-
-TEST_F(SQLTests, test_raw_access_context) {
-  Registry::add<TestTablePlugin>("table", "test");
-  auto results = SQL::selectAllFrom("test");
-
-  EXPECT_EQ(results.size(), 1);
-  EXPECT_EQ(results[0]["test_text"], "1");
-
-  results = SQL::selectAllFrom("test", "test_int", EQUALS, "1");
-  EXPECT_EQ(results.size(), 2);
-
-  results = SQL::selectAllFrom("test", "test_int", EQUALS, "2");
-  EXPECT_EQ(results.size(), 2);
-  EXPECT_EQ(results[0]["test_int"], "0");
-}
-}
diff --git a/osquery/sql/tests/sqlite_util_tests.cpp b/osquery/sql/tests/sqlite_util_tests.cpp
deleted file mode 100644 (file)
index c2f6c5a..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <iostream>
-
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/sql.h>
-
-#include "osquery/core/test_util.h"
-#include "osquery/sql/sqlite_util.h"
-
-namespace osquery {
-
-class SQLiteUtilTests : public testing::Test {};
-
-SQLiteDBInstance getTestDBC() {
-  SQLiteDBInstance dbc = SQLiteDBManager::getUnique();
-  char* err = nullptr;
-  std::vector<std::string> queries = {
-      "CREATE TABLE test_table (username varchar(30) primary key, age int)",
-      "INSERT INTO test_table VALUES (\"mike\", 23)",
-      "INSERT INTO test_table VALUES (\"matt\", 24)"};
-
-  for (auto q : queries) {
-    sqlite3_exec(dbc.db(), q.c_str(), nullptr, nullptr, &err);
-    if (err != nullptr) {
-      throw std::domain_error(std::string("Cannot create testing DBC's db: ") +
-                              err);
-    }
-  }
-
-  return dbc;
-}
-
-TEST_F(SQLiteUtilTests, test_simple_query_execution) {
-  // Access to the internal SQL implementation is only available in core.
-  auto sql = SQL("SELECT * FROM time");
-  EXPECT_TRUE(sql.ok());
-  EXPECT_EQ(sql.rows().size(), 1);
-}
-
-TEST_F(SQLiteUtilTests, test_get_tables) {
-  // Access to the internal SQL implementation is only available in core.
-  auto tables = SQL::getTableNames();
-  EXPECT_TRUE(tables.size() > 0);
-}
-
-TEST_F(SQLiteUtilTests, test_sqlite_instance_manager) {
-  auto dbc1 = SQLiteDBManager::get();
-  auto dbc2 = SQLiteDBManager::get();
-  EXPECT_NE(dbc1.db(), dbc2.db());
-  EXPECT_EQ(dbc1.db(), dbc1.db());
-}
-
-TEST_F(SQLiteUtilTests, test_sqlite_instance) {
-  // Don't do this at home kids.
-  // Keep a copy of the internal DB and let the SQLiteDBInstance go oos.
-  auto internal_db = SQLiteDBManager::get().db();
-  // Compare the internal DB to another request with no SQLiteDBInstances
-  // in scope, meaning the primary will be returned.
-  EXPECT_EQ(internal_db, SQLiteDBManager::get().db());
-}
-
-TEST_F(SQLiteUtilTests, test_direct_query_execution) {
-  auto dbc = getTestDBC();
-  QueryData results;
-  auto status = queryInternal(kTestQuery, results, dbc.db());
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results, getTestDBExpectedResults());
-}
-
-TEST_F(SQLiteUtilTests, test_passing_callback_no_data_param) {
-  char* err = nullptr;
-  auto dbc = getTestDBC();
-  sqlite3_exec(dbc.db(), kTestQuery.c_str(), queryDataCallback, nullptr, &err);
-  EXPECT_TRUE(err != nullptr);
-  if (err != nullptr) {
-    sqlite3_free(err);
-  }
-}
-
-TEST_F(SQLiteUtilTests, test_aggregate_query) {
-  auto dbc = getTestDBC();
-  QueryData results;
-  auto status = queryInternal(kTestQuery, results, dbc.db());
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results, getTestDBExpectedResults());
-}
-
-TEST_F(SQLiteUtilTests, test_get_test_db_result_stream) {
-  auto dbc = getTestDBC();
-  auto results = getTestDBResultStream();
-  for (auto r : results) {
-    char* err_char = nullptr;
-    sqlite3_exec(dbc.db(), (r.first).c_str(), nullptr, nullptr, &err_char);
-    EXPECT_TRUE(err_char == nullptr);
-    if (err_char != nullptr) {
-      sqlite3_free(err_char);
-      ASSERT_TRUE(false);
-    }
-
-    QueryData expected;
-    auto status = queryInternal(kTestQuery, expected, dbc.db());
-    EXPECT_EQ(expected, r.second);
-  }
-}
-
-TEST_F(SQLiteUtilTests, test_get_query_columns) {
-  auto dbc = getTestDBC();
-  TableColumns results;
-
-  std::string query = "SELECT seconds, version FROM time JOIN osquery_info";
-  auto status = getQueryColumnsInternal(query, results, dbc.db());
-  ASSERT_TRUE(status.ok());
-  ASSERT_EQ(2, results.size());
-  EXPECT_EQ(std::make_pair(std::string("seconds"), std::string("INTEGER")),
-            results[0]);
-  EXPECT_EQ(std::make_pair(std::string("version"), std::string("TEXT")),
-            results[1]);
-
-  query = "SELECT hour + 1 AS hour1, minutes + 1 FROM time";
-  status = getQueryColumnsInternal(query, results, dbc.db());
-  ASSERT_TRUE(status.ok());
-  ASSERT_EQ(2, results.size());
-  EXPECT_EQ(std::make_pair(std::string("hour1"), std::string("UNKNOWN")),
-            results[0]);
-  EXPECT_EQ(std::make_pair(std::string("minutes + 1"), std::string("UNKNOWN")),
-            results[1]);
-
-  query = "SELECT * FROM foo";
-  status = getQueryColumnsInternal(query, results, dbc.db());
-  ASSERT_FALSE(status.ok());
-}
-}
diff --git a/osquery/sql/tests/virtual_table_tests.cpp b/osquery/sql/tests/virtual_table_tests.cpp
deleted file mode 100644 (file)
index c08cce6..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/registry.h>
-#include <osquery/sql.h>
-
-#include "osquery/sql/virtual_table.h"
-
-namespace osquery {
-
-class VirtualTableTests : public testing::Test {};
-
-// sample plugin used on tests
-class sampleTablePlugin : public TablePlugin {
- private:
-  TableColumns columns() const {
-    return {
-        {"foo", "INTEGER"}, {"bar", "TEXT"},
-    };
-  }
-};
-
-TEST_F(VirtualTableTests, test_tableplugin_columndefinition) {
-  auto table = std::make_shared<sampleTablePlugin>();
-  EXPECT_EQ("(foo INTEGER, bar TEXT)", table->columnDefinition());
-}
-
-TEST_F(VirtualTableTests, test_sqlite3_attach_vtable) {
-  auto table = std::make_shared<sampleTablePlugin>();
-  table->setName("sample");
-
-  // Request a managed "connection".
-  // This will be a single (potentially locked) instance or a transient
-  // SQLite database if there is contention and a lock was not requested.
-  auto dbc = SQLiteDBManager::get();
-
-  // Virtual tables require the registry/plugin API to query tables.
-  auto status = attachTableInternal("failed_sample", "(foo INTEGER)", dbc.db());
-  EXPECT_EQ(status.getCode(), SQLITE_ERROR);
-
-  // The table attach will complete only when the table name is registered.
-  Registry::add<sampleTablePlugin>("table", "sample");
-  PluginResponse response;
-  status = Registry::call("table", "sample", {{"action", "columns"}}, response);
-  EXPECT_TRUE(status.ok());
-
-  // Use the table name, plugin-generated schema to attach.
-  status = attachTableInternal("sample", columnDefinition(response), dbc.db());
-  EXPECT_EQ(status.getCode(), SQLITE_OK);
-
-  std::string q = "SELECT sql FROM sqlite_temp_master WHERE tbl_name='sample';";
-  QueryData results;
-  status = queryInternal(q, results, dbc.db());
-  EXPECT_EQ("CREATE VIRTUAL TABLE sample USING sample(foo INTEGER, bar TEXT)",
-            results[0]["sql"]);
-}
-
-TEST_F(VirtualTableTests, test_sqlite3_table_joins) {
-  // Get a database connection.
-  auto dbc = SQLiteDBManager::get();
-
-  QueryData results;
-  // Run a query with a join within.
-  std::string statement =
-      "SELECT p.pid FROM osquery_info oi, processes p WHERE oi.pid=p.pid";
-  auto status = queryInternal(statement, results, dbc.db());
-  EXPECT_TRUE(status.ok());
-  EXPECT_EQ(results.size(), 1);
-}
-
-TEST_F(VirtualTableTests, test_sqlite3_table_update_where) {
-  // Get a database connection.
-  auto dbc = SQLiteDBManager::get();
-
-  QueryData results;
-  std::string statement = "UPDATE users SET uid = 1234, gid = 232 WHERE uid = 0";
-  auto status = queryInternal(statement, results, dbc.db());
-  EXPECT_TRUE(status.ok());
-}
-
-TEST_F(VirtualTableTests, test_sqlite3_table_update) {
-  // Get a database connection.
-  auto dbc = SQLiteDBManager::get();
-
-  QueryData results;
-  std::string statement = "UPDATE users SET uid = 1234, gid = 232";
-  auto status = queryInternal(statement, results, dbc.db());
-  EXPECT_TRUE(status.ok());
-}
-}
diff --git a/osquery/sql/virtual_table.cpp b/osquery/sql/virtual_table.cpp
deleted file mode 100644 (file)
index 1a51ad9..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/logger.h>
-
-#include "osquery/sql/virtual_table.h"
-
-namespace osquery {
-namespace tables {
-
-int xOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
-  int rc = SQLITE_NOMEM;
-  BaseCursor *pCur;
-
-  pCur = new BaseCursor;
-
-  if (pCur) {
-    memset(pCur, 0, sizeof(BaseCursor));
-    *ppCursor = (sqlite3_vtab_cursor *)pCur;
-    rc = SQLITE_OK;
-  }
-
-  return rc;
-}
-
-int xClose(sqlite3_vtab_cursor *cur) {
-  BaseCursor *pCur = (BaseCursor *)cur;
-
-  delete pCur;
-  return SQLITE_OK;
-}
-
-int xEof(sqlite3_vtab_cursor *cur) {
-  BaseCursor *pCur = (BaseCursor *)cur;
-  auto *pVtab = (VirtualTable *)cur->pVtab;
-  return pCur->row >= pVtab->content->n;
-}
-
-int xDestroy(sqlite3_vtab *p) {
-  auto *pVtab = (VirtualTable *)p;
-  delete pVtab->content;
-  delete pVtab;
-  return SQLITE_OK;
-}
-
-int xNext(sqlite3_vtab_cursor *cur) {
-  BaseCursor *pCur = (BaseCursor *)cur;
-  pCur->row++;
-  return SQLITE_OK;
-}
-
-int xRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) {
-  BaseCursor *pCur = (BaseCursor *)cur;
-  *pRowid = pCur->row;
-  return SQLITE_OK;
-}
-
-int xUpdate(sqlite3_vtab *pVTab,
-            int argc,
-            sqlite3_value **argv,
-            sqlite3_int64 *pRowid)
-{
-  auto * pVtab = (VirtualTable *)pVTab;
-  if (argc <= 1 || argc - 2 !=  pVtab->content->columns.size()) {
-    LOG(ERROR) << "Invalid arguments: " << argc;
-    return SQLITE_ERROR;
-  }
-
-  PluginRequest request = {{"action", "update"}};
-  const auto& columns = pVtab->content->columns;
-  for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
-    auto expr = (const char *)sqlite3_value_text(argv[i]);
-    if (expr == nullptr) {
-      // SQLite did not expose the expression value.
-      continue;
-    } else {
-       request.insert(std::make_pair(columns[i - 2].first, std::string(expr)));
-    }
-  }
-
-  PluginResponse response;
-  Registry::call("table", pVtab->content->name, request, response);
-
-  return SQLITE_OK;
-}
-
-int xCreate(sqlite3 *db,
-            void *pAux,
-            int argc,
-            const char *const *argv,
-            sqlite3_vtab **ppVtab,
-            char **pzErr) {
-  auto *pVtab = new VirtualTable;
-
-  if (!pVtab || argc == 0 || argv[0] == nullptr) {
-    return SQLITE_NOMEM;
-  }
-
-  memset(pVtab, 0, sizeof(VirtualTable));
-  pVtab->content = new VirtualTableContent;
-
-  PluginResponse response;
-  pVtab->content->name = std::string(argv[0]);
-
-  // Get the table column information.
-  auto status = Registry::call(
-      "table", pVtab->content->name, {{"action", "columns"}}, response);
-  if (!status.ok() || response.size() == 0) {
-    return SQLITE_ERROR;
-  }
-
-  auto statement =
-      "CREATE TABLE " + pVtab->content->name + columnDefinition(response);
-  int rc = sqlite3_declare_vtab(db, statement.c_str());
-  if (rc != SQLITE_OK) {
-    return rc;
-  }
-
-  if (!status.ok() || response.size() == 0) {
-    return SQLITE_ERROR;
-  }
-
-  for (const auto &column : response) {
-    pVtab->content->columns.push_back(
-        std::make_pair(column.at("name"), column.at("type")));
-  }
-
-  *ppVtab = (sqlite3_vtab *)pVtab;
-  return rc;
-}
-
-int xColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) {
-  BaseCursor *pCur = (BaseCursor *)cur;
-  auto *pVtab = (VirtualTable *)cur->pVtab;
-
-  if (col >= pVtab->content->columns.size()) {
-    return SQLITE_ERROR;
-  }
-
-  auto &column_name = pVtab->content->columns[col].first;
-  auto &type = pVtab->content->columns[col].second;
-  if (pCur->row >= pVtab->content->data[column_name].size()) {
-    return SQLITE_ERROR;
-  }
-
-  // Attempt to cast each xFilter-populated row/column to the SQLite type.
-  auto &value = pVtab->content->data[column_name][pCur->row];
-  if (type == "TEXT") {
-    sqlite3_result_text(ctx, value.c_str(), value.size(), SQLITE_STATIC);
-  } else if (type == "INTEGER") {
-    int afinite;
-    try {
-      afinite = boost::lexical_cast<int>(value);
-    } catch (const boost::bad_lexical_cast &e) {
-      afinite = -1;
-      VLOG(1) << "Error casting " << column_name << " (" << value
-              << ") to INTEGER";
-    }
-    sqlite3_result_int(ctx, afinite);
-  } else if (type == "BIGINT") {
-    long long int afinite;
-    try {
-      afinite = boost::lexical_cast<long long int>(value);
-    } catch (const boost::bad_lexical_cast &e) {
-      afinite = -1;
-      VLOG(1) << "Error casting " << column_name << " (" << value
-              << ") to BIGINT";
-    }
-    sqlite3_result_int64(ctx, afinite);
-  } else if (type == "DOUBLE") {
-    double afinite;
-    try {
-      afinite = boost::lexical_cast<double>(value);
-    } catch (const boost::bad_lexical_cast &e) {
-      afinite = 0;
-      VLOG(1) << "Error casting" << column_name << " (" << value
-              << ") to DOUBLE";
-    }
-    sqlite3_result_double(ctx, afinite);
-  }
-
-  return SQLITE_OK;
-}
-
-static int xBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
-  auto *pVtab = (VirtualTable *)tab;
-  pVtab->content->constraints.clear();
-
-  int expr_index = 0;
-  int cost = 0;
-  for (size_t i = 0; i < pIdxInfo->nConstraint; ++i) {
-    if (!pIdxInfo->aConstraint[i].usable) {
-      // A higher cost less priority, prefer more usable query constraints.
-      cost += 10;
-      // TODO: OR is not usable.
-      continue;
-    }
-
-    const auto &name =
-        pVtab->content->columns[pIdxInfo->aConstraint[i].iColumn].first;
-    pVtab->content->constraints.push_back(
-        std::make_pair(name, Constraint(pIdxInfo->aConstraint[i].op)));
-    pIdxInfo->aConstraintUsage[i].argvIndex = ++expr_index;
-  }
-
-  pIdxInfo->estimatedCost = cost;
-  return SQLITE_OK;
-}
-
-static int xFilter(sqlite3_vtab_cursor *pVtabCursor,
-                   int idxNum,
-                   const char *idxStr,
-                   int argc,
-                   sqlite3_value **argv) {
-  BaseCursor *pCur = (BaseCursor *)pVtabCursor;
-  auto *pVtab = (VirtualTable *)pVtabCursor->pVtab;
-
-  pCur->row = 0;
-  pVtab->content->n = 0;
-  QueryContext context;
-
-  for (size_t i = 0; i < pVtab->content->columns.size(); ++i) {
-    // Clear any data, this is the result container for each column + row.
-    pVtab->content->data[pVtab->content->columns[i].first].clear();
-    // Set the column affinity for each optional constraint list.
-    // There is a separate list for each column name.
-    context.constraints[pVtab->content->columns[i].first].affinity =
-        pVtab->content->columns[i].second;
-  }
-
-  // Iterate over every argument to xFilter, filling in constraint values.
-  for (size_t i = 0; i < argc; ++i) {
-    auto expr = (const char *)sqlite3_value_text(argv[i]);
-    if (expr == nullptr) {
-      // SQLite did not expose the expression value.
-      continue;
-    }
-    // Set the expression from SQLite's now-populated argv.
-    pVtab->content->constraints[i].second.expr = std::string(expr);
-    // Add the constraint to the column-sorted query request map.
-    context.constraints[pVtab->content->constraints[i].first].add(
-        pVtab->content->constraints[i].second);
-  }
-
-  PluginRequest request;
-  PluginResponse response;
-  request["action"] = "generate";
-  TablePlugin::setRequestFromContext(context, request);
-  Registry::call("table", pVtab->content->name, request, response);
-
-  // Now organize the response rows by column instead of row.
-  auto &data = pVtab->content->data;
-  for (auto &row : response) {
-    for (const auto &column : pVtab->content->columns) {
-      if (row.count(column.first) == 0) {
-        VLOG(1) << "Table " << pVtab->content->name << " row "
-                << pVtab->content->n << " did not include column "
-                << column.first;
-        data[column.first].push_back("");
-        continue;
-      }
-
-      auto &value = row.at(column.first);
-      if (value.size() > FLAGS_value_max) {
-        data[column.first].push_back(value.substr(0, FLAGS_value_max));
-        value.clear();
-      } else {
-        data[column.first].push_back(std::move(value));
-      }
-    }
-
-    pVtab->content->n++;
-  }
-
-  return SQLITE_OK;
-}
-}
-
-Status attachTableInternal(const std::string &name,
-                           const std::string &statement,
-                           sqlite3 *db) {
-  if (SQLiteDBManager::isDisabled(name)) {
-    VLOG(0) << "Table " << name << " is disabled, not attaching";
-    return Status(0, getStringForSQLiteReturnCode(0));
-  }
-
-  // A static module structure does not need specific logic per-table.
-  // clang-format off
-  static sqlite3_module module = {
-      0,
-      tables::xCreate,
-      tables::xCreate,
-      tables::xBestIndex,
-      tables::xDestroy,
-      tables::xDestroy,
-      tables::xOpen,
-      tables::xClose,
-      tables::xFilter,
-      tables::xNext,
-      tables::xEof,
-      tables::xColumn,
-      tables::xRowid,
-      tables::xUpdate,
-  };
-  // clang-format on
-
-  // Note, if the clientData API is used then this will save a registry call
-  // within xCreate.
-  int rc = sqlite3_create_module(db, name.c_str(), &module, 0);
-  if (rc == SQLITE_OK || rc == SQLITE_MISUSE) {
-    auto format =
-        "CREATE VIRTUAL TABLE temp." + name + " USING " + name + statement;
-    rc = sqlite3_exec(db, format.c_str(), nullptr, nullptr, 0);
-  } else {
-    LOG(ERROR) << "Error attaching table: " << name << " (" << rc << ")";
-  }
-  return Status(rc, getStringForSQLiteReturnCode(rc));
-}
-
-Status detachTableInternal(const std::string &name, sqlite3 *db) {
-  auto format = "DROP TABLE IF EXISTS temp." + name;
-  int rc = sqlite3_exec(db, format.c_str(), nullptr, nullptr, 0);
-  if (rc != SQLITE_OK) {
-    LOG(ERROR) << "Error detaching table: " << name << " (" << rc << ")";
-  }
-
-  return Status(rc, getStringForSQLiteReturnCode(rc));
-}
-
-void attachVirtualTables(sqlite3 *db) {
-  PluginResponse response;
-  for (const auto &name : Registry::names("table")) {
-    // Column information is nice for virtual table create call.
-    auto status =
-        Registry::call("table", name, {{"action", "columns"}}, response);
-    if (status.ok()) {
-      auto statement = columnDefinition(response);
-      attachTableInternal(name, statement, db);
-    }
-  }
-}
-}
diff --git a/osquery/sql/virtual_table.h b/osquery/sql/virtual_table.h
deleted file mode 100644 (file)
index f962bc0..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <osquery/tables.h>
-
-#include "osquery/sql/sqlite_util.h"
-
-namespace osquery {
-
-/**
- * @brief osquery cursor object.
- *
- * Only used in the SQLite virtual table module methods.
- */
-struct BaseCursor {
-  /// SQLite virtual table cursor.
-  sqlite3_vtab_cursor base;
-  /// Current cursor position.
-  int row;
-};
-
-struct VirtualTableContent {
-  TableName name;
-  TableColumns columns;
-  TableData data;
-  ConstraintSet constraints;
-  size_t n;
-};
-
-/**
- * @brief osquery virtual table object
- *
- * Only used in the SQLite virtual table module methods.
- * This adds each table plugin class to the state tracking in SQLite.
- */
-struct VirtualTable {
-  sqlite3_vtab base;
-  VirtualTableContent *content;
-};
-
-/// Attach a table plugin name to an in-memory SQLite database.
-Status attachTableInternal(const std::string &name,
-                           const std::string &statement,
-                           sqlite3 *db);
-
-/// Detach (drop) a table.
-Status detachTableInternal(const std::string &name, sqlite3 *db);
-
-/// Attach all table plugins to an in-memory SQLite database.
-void attachVirtualTables(sqlite3 *db);
-}
diff --git a/osquery/tables/CMakeLists.txt b/osquery/tables/CMakeLists.txt
deleted file mode 100644 (file)
index 8fb82c4..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-FILE(GLOB OSQUERY_LINUX_TABLES "*/linux/*.cpp")
-ADD_OSQUERY_LIBRARY(osquery_linux_tables ${OSQUERY_LINUX_TABLES})
-
-FILE(GLOB OSQUERY_CROSS_TABLES "*/*.cpp")
-ADD_OSQUERY_LIBRARY(osquery_tables ${OSQUERY_CROSS_TABLES})
-
-FILE(GLOB OSQUERY_CROSS_TABLES_TESTS "[!uo]*/tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_CROSS_TABLES_TESTS})
diff --git a/osquery/tables/events/linux/file_events.cpp b/osquery/tables/events/linux/file_events.cpp
deleted file mode 100644 (file)
index 91a014f..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <osquery/core.h>
-#include <osquery/config.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-#include <osquery/hash.h>
-
-#include "osquery/events/linux/inotify.h"
-
-namespace osquery {
-
-/**
- * @brief Track time, action changes to /etc/passwd
- *
- * This is mostly an example EventSubscriber implementation.
- */
-class FileEventSubscriber
-    : public EventSubscriber<INotifyEventPublisher> {
- public:
-  Status init();
-
-  /**
-   * @brief This exports a single Callback for INotifyEventPublisher events.
-   *
-   * @param ec The EventCallback type receives an EventContextRef substruct
-   * for the INotifyEventPublisher declared in this EventSubscriber subclass.
-   *
-   * @return Was the callback successful.
-   */
-  Status Callback(const INotifyEventContextRef& ec, const void* user_data);
-};
-
-/**
- * @brief Each EventSubscriber must register itself so the init method is
- *called.
- *
- * This registers FileEventSubscriber into the osquery EventSubscriber
- * pseudo-plugin registry.
- */
-REGISTER(FileEventSubscriber, "event_subscriber", "file_events");
-
-Status FileEventSubscriber::init() {
-  ConfigDataInstance config;
-  for (const auto& element_kv : config.files()) {
-    for (const auto& file : element_kv.second) {
-      VLOG(1) << "Added listener to: " << file;
-      auto mc = createSubscriptionContext();
-      // Use the filesystem globbing pattern to determine recursiveness.
-      mc->recursive = 0;
-      mc->path = file;
-      mc->mask = IN_ATTRIB | IN_MODIFY | IN_DELETE | IN_CREATE;
-      subscribe(&FileEventSubscriber::Callback, mc,
-                (void*)(&element_kv.first));
-    }
-  }
-
-  return Status(0, "OK");
-}
-
-Status FileEventSubscriber::Callback(const INotifyEventContextRef& ec,
-                                            const void* user_data) {
-  Row r;
-  r["action"] = ec->action;
-  r["target_path"] = ec->path;
-  if (user_data != nullptr) {
-    r["category"] = *(std::string*)user_data;
-  } else {
-    r["category"] = "Undefined";
-  }
-  r["transaction_id"] = INTEGER(ec->event->cookie);
-
-  if (ec->action == "CREATED" || ec->action == "UPDATED") {
-    r["md5"] = hashFromFile(HASH_TYPE_MD5, ec->path);
-    r["sha1"] = hashFromFile(HASH_TYPE_SHA1, ec->path);
-    r["sha256"] = hashFromFile(HASH_TYPE_SHA256, ec->path);
-  }
-
-  if (ec->action != "" && ec->action != "OPENED") {
-    // A callback is somewhat useless unless it changes the EventSubscriber
-    // state or calls `add` to store a marked up event.
-    add(r, ec->time);
-  }
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/tables/events/linux/hardware_events.cpp b/osquery/tables/events/linux/hardware_events.cpp
deleted file mode 100644 (file)
index 813a095..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/events/linux/udev.h"
-
-namespace osquery {
-
-/**
- * @brief Track udev events in Linux
- */
-class HardwareEventSubscriber : public EventSubscriber<UdevEventPublisher> {
- public:
-  Status init();
-
-  Status Callback(const UdevEventContextRef& ec, const void* user_data);
-};
-
-REGISTER(HardwareEventSubscriber, "event_subscriber", "hardware_events");
-
-Status HardwareEventSubscriber::init() {
-  auto subscription = createSubscriptionContext();
-  subscription->action = UDEV_EVENT_ACTION_ALL;
-
-  subscribe(&HardwareEventSubscriber::Callback, subscription, nullptr);
-  return Status(0, "OK");
-}
-
-Status HardwareEventSubscriber::Callback(const UdevEventContextRef& ec,
-                                         const void* user_data) {
-  Row r;
-
-  if (ec->devtype.empty()) {
-    // Superfluous hardware event.
-    return Status(0, "Missing type.");
-  } else if (ec->devnode.empty() && ec->driver.empty()) {
-    return Status(0, "Missing node and driver.");
-  }
-
-  struct udev_device *device = ec->device;
-  r["action"] = ec->action_string;
-  r["path"] = ec->devnode;
-  r["type"] = ec->devtype;
-  r["driver"] = ec->driver;
-
-  // UDEV properties.
-  r["model"] = UdevEventPublisher::getValue(device, "ID_MODEL_FROM_DATABASE");
-  if (r["path"].empty() && r["model"].empty()) {
-    // Don't emit mising path/model combos.
-    return Status(0, "Missing path and model.");
-  }
-
-  r["model_id"] = INTEGER(UdevEventPublisher::getValue(device, "ID_MODEL_ID"));
-  r["vendor"] = UdevEventPublisher::getValue(device, "ID_VENDOR_FROM_DATABASE");
-  r["vendor_id"] =
-      INTEGER(UdevEventPublisher::getValue(device, "ID_VENDOR_ID"));
-  r["serial"] =
-      INTEGER(UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT"));
-  r["revision"] = INTEGER(UdevEventPublisher::getValue(device, "ID_REVISION"));
-  add(r, ec->time);
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/tables/events/linux/passwd_changes.cpp b/osquery/tables/events/linux/passwd_changes.cpp
deleted file mode 100644 (file)
index 6e36380..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/events/linux/inotify.h"
-
-namespace osquery {
-
-/**
- * @brief Track time, action changes to /etc/passwd
- *
- * This is mostly an example EventSubscriber implementation.
- */
-class PasswdChangesEventSubscriber
-    : public EventSubscriber<INotifyEventPublisher> {
- public:
-  Status init();
-
-  /**
-   * @brief This exports a single Callback for INotifyEventPublisher events.
-   *
-   * @param ec The EventCallback type receives an EventContextRef substruct
-   * for the INotifyEventPublisher declared in this EventSubscriber subclass.
-   *
-   * @return Was the callback successful.
-   */
-  Status Callback(const INotifyEventContextRef& ec, const void* user_data);
-};
-
-/**
- * @brief Each EventSubscriber must register itself so the init method is
- *called.
- *
- * This registers PasswdChangesEventSubscriber into the osquery EventSubscriber
- * pseudo-plugin registry.
- */
-REGISTER(PasswdChangesEventSubscriber, "event_subscriber", "passwd_changes");
-
-Status PasswdChangesEventSubscriber::init() {
-  auto mc = createSubscriptionContext();
-  mc->path = "/etc/passwd";
-  mc->mask = IN_ATTRIB | IN_MODIFY | IN_DELETE | IN_CREATE;
-  subscribe(&PasswdChangesEventSubscriber::Callback, mc, nullptr);
-  return Status(0, "OK");
-}
-
-Status PasswdChangesEventSubscriber::Callback(const INotifyEventContextRef& ec,
-                                              const void* user_data) {
-  Row r;
-  r["action"] = ec->action;
-  r["target_path"] = ec->path;
-  r["transaction_id"] = INTEGER(ec->event->cookie);
-  if (ec->action != "" && ec->action != "OPENED") {
-    // A callback is somewhat useless unless it changes the EventSubscriber
-    // state
-    // or calls `add` to store a marked up event.
-    add(r, ec->time);
-  }
-  return Status(0, "OK");
-}
-}
diff --git a/osquery/tables/networking/etc_hosts.cpp b/osquery/tables/networking/etc_hosts.cpp
deleted file mode 100644 (file)
index 52d07b0..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <boost/algorithm/string/join.hpp>
-#include <boost/algorithm/string/predicate.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData parseEtcHostsContent(const std::string& content) {
-  QueryData results;
-
-  for (const auto& i : split(content, "\n")) {
-    auto line = split(i);
-    if (line.size() == 0 || boost::starts_with(line[0], "#")) {
-      continue;
-    }
-    Row r;
-    r["address"] = line[0];
-    if (line.size() > 1) {
-      std::vector<std::string> hostnames;
-      for (int i = 1; i < line.size(); ++i) {
-        if (boost::starts_with(line[i], "#")) {
-          break;
-        }
-        hostnames.push_back(line[i]);
-      }
-      r["hostnames"] = boost::algorithm::join(hostnames, " ");
-    }
-    results.push_back(r);
-  }
-
-  return results;
-}
-
-QueryData genEtcHosts(QueryContext& context) {
-  std::string content;
-  auto s = osquery::readFile("/etc/hosts", content);
-  if (s.ok()) {
-    return parseEtcHostsContent(content);
-  } else {
-    LOG(ERROR) << "Error reading /etc/hosts: " << s.toString();
-    return {};
-  }
-}
-}
-}
diff --git a/osquery/tables/networking/etc_protocols.cpp b/osquery/tables/networking/etc_protocols.cpp
deleted file mode 100644 (file)
index 63c02e6..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <boost/algorithm/string/join.hpp>
-#include <boost/algorithm/string/predicate.hpp>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData parseEtcProtocolsContent(const std::string& content) {
-  QueryData results;
-
-  for (const auto& line : split(content, "\n")) {
-    // Empty line or comment.
-    if (line.size() == 0 || boost::starts_with(line, "#")) {
-      continue;
-    }
-
-    // [0]: name protocol_number alias
-    // [1]: [comment part1]
-    // [2]: [comment part2]
-    // [n]: [comment partn]
-    auto protocol_comment = split(line, "#");
-
-    // [0]: name
-    // [1]: protocol_number
-    // [2]: alias
-    auto protocol_fields = split(protocol_comment[0]);
-    if (protocol_fields.size() < 2) {
-      continue;
-    }
-
-    Row r;
-    r["name"] = TEXT(protocol_fields[0]);
-    r["number"] = INTEGER(protocol_fields[1]);
-    if (protocol_fields.size() > 2) {
-      r["alias"] = TEXT(protocol_fields[2]);
-    }
-
-    // If there is a comment for the service.
-    if (protocol_comment.size() > 1) {
-      // Removes everything except the comment (parts of the comment).
-      protocol_comment.erase(protocol_comment.begin(), protocol_comment.begin() + 1);
-      r["comment"] = TEXT(boost::algorithm::join(protocol_comment, " # "));
-    }
-    results.push_back(r);
-  }
-  return results;
-}
-
-QueryData genEtcProtocols(QueryContext& context) {
-  std::string content;
-  auto s = osquery::readFile("/etc/protocols", content);
-  if (s.ok()) {
-    return parseEtcProtocolsContent(content);
-  } else {
-    TLOG << "Error reading /etc/protocols: " << s.toString();
-    return {};
-  }
-}
-}
-}
diff --git a/osquery/tables/networking/etc_services.cpp b/osquery/tables/networking/etc_services.cpp
deleted file mode 100644 (file)
index 2873a5d..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <boost/algorithm/string/join.hpp>
-#include <boost/algorithm/string/predicate.hpp>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData parseEtcServicesContent(const std::string& content) {
-  QueryData results;
-
-  for (const auto& line : split(content, "\n")) {
-    // Empty line or comment.
-    if (line.size() == 0 || boost::starts_with(line, "#")) {
-      continue;
-    }
-
-    // [0]: name port/protocol [aliases]
-    // [1]: [comment part1]
-    // [2]: [comment part2]
-    // [n]: [comment partn]
-    auto service_info_comment = split(line, "#");
-
-    // [0]: name
-    // [1]: port/protocol
-    // [2]: [aliases0]
-    // [3]: [aliases1]
-    // [n]: [aliasesn]
-    auto service_info = split(service_info_comment[0]);
-    if (service_info.size() < 2) {
-      LOG(WARNING) << "Line of /etc/services wasn't properly formatted. "
-                   << "Expected at least 2, got " << service_info.size();
-      continue;
-    }
-
-    // [0]: port [1]: protocol
-    auto service_port_protocol = split(service_info[1], "/");
-    if (service_port_protocol.size() != 2) {
-      LOG(WARNING) << "Line of /etc/services wasn't properly formatted. "
-                   << "Expected 2, got " << service_port_protocol.size();
-      continue;
-    }
-
-    Row r;
-    r["name"] = TEXT(service_info[0]);
-    r["port"] = INTEGER(service_port_protocol[0]);
-    r["protocol"] = TEXT(service_port_protocol[1]);
-
-    // Removes the name and the port/protcol elements.
-    service_info.erase(service_info.begin(), service_info.begin() + 2);
-    r["aliases"] = TEXT(boost::algorithm::join(service_info, " "));
-
-    // If there is a comment for the service.
-    if (service_info_comment.size() > 1) {
-      // Removes everything except the comment (parts of the comment).
-      service_info_comment.erase(service_info_comment.begin(), service_info_comment.begin() + 1);
-      r["comment"] = TEXT(boost::algorithm::join(service_info_comment, " # "));
-    }
-    results.push_back(r);
-  }
-  return results;
-}
-
-QueryData genEtcServices(QueryContext& context) {
-  std::string content;
-  auto s = osquery::readFile("/etc/services", content);
-  if (s.ok()) {
-    return parseEtcServicesContent(content);
-  } else {
-    LOG(ERROR) << "Error reading /etc/services: " << s.toString();
-    return {};
-  }
-}
-}
-}
diff --git a/osquery/tables/networking/linux/arp_cache.cpp b/osquery/tables/networking/linux/arp_cache.cpp
deleted file mode 100644 (file)
index e5a3a60..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <fstream>
-
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-namespace tables {
-
-const std::string kLinuxArpTable = "/proc/net/arp";
-
-QueryData genArpCache(QueryContext& context) {
-  QueryData results;
-
-  boost::filesystem::path arp_path = kLinuxArpTable;
-  if (!osquery::isReadable(arp_path).ok()) {
-    VLOG(1) << "Cannot read arp table.";
-    return results;
-  }
-
-  std::ifstream fd(arp_path.string(), std::ios::in | std::ios::binary);
-  std::string line;
-
-  if (fd.fail() || fd.eof()) {
-    VLOG(1) << "Empty or failed arp table.";
-    return results;
-  }
-
-  // Read the header line.
-  std::getline(fd, line, '\n');
-  while (!(fd.fail() || fd.eof())) {
-    std::getline(fd, line, '\n');
-
-    // IP address, HW type, Flags, HW address, Mask Device
-    std::vector<std::string> fields;
-    boost::split(fields, line, boost::is_any_of(" "), boost::token_compress_on);
-    for (auto& f : fields) {
-      // Inline trim each split.
-      boost::trim(f);
-    }
-
-    if (fields.size() != 6) {
-      // An unhandled error case.
-      continue;
-    }
-
-    Row r;
-    r["address"] = fields[0];
-    r["mac"] = fields[3];
-    r["interface"] = fields[5];
-
-    // Note: it's also possible to detect publish entries (ATF_PUB).
-    if (fields[2] == "0x6") {
-      // The string representation of ATF_COM | ATF_PERM.
-      r["permanent"] = "1";
-    } else {
-      r["permanent"] = "0";
-    }
-
-    results.push_back(r);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/networking/linux/inet_diag.h b/osquery/tables/networking/linux/inet_diag.h
deleted file mode 100644 (file)
index 65b5d9e..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#ifndef _UAPI_INET_DIAG_H_
-#define _UAPI_INET_DIAG_H_
-
-#include <linux/types.h>
-
-/* Just some random number */
-#define TCPDIAG_GETSOCK 18
-#define DCCPDIAG_GETSOCK 19
-
-#define INET_DIAG_GETSOCK_MAX 24
-
-/* Socket identity */
-struct inet_diag_sockid {
-  __be16 idiag_sport;
-  __be16 idiag_dport;
-  __be32 idiag_src[4];
-  __be32 idiag_dst[4];
-  __u32 idiag_if;
-  __u32 idiag_cookie[2];
-#define INET_DIAG_NOCOOKIE (~0U)
-};
-
-/* Request structure */
-
-struct inet_diag_req {
-  __u8 idiag_family; /* Family of addresses. */
-  __u8 idiag_src_len;
-  __u8 idiag_dst_len;
-  __u8 idiag_ext; /* Query extended information */
-
-  struct inet_diag_sockid id;
-
-  __u32 idiag_states; /* States to dump */
-  __u32 idiag_dbs; /* Tables to dump (NI) */
-};
-
-struct inet_diag_req_v2 {
-  __u8 sdiag_family;
-  __u8 sdiag_protocol;
-  __u8 idiag_ext;
-  __u8 pad;
-  __u32 idiag_states;
-  struct inet_diag_sockid id;
-};
-
-enum {
-  INET_DIAG_REQ_NONE,
-  INET_DIAG_REQ_BYTECODE,
-};
-
-#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE
-
-/* Bytecode is sequence of 4 byte commands followed by variable arguments.
- * All the commands identified by "code" are conditional jumps forward:
- * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be
- * length of the command and its arguments.
- */
-
-struct inet_diag_bc_op {
-  unsigned char code;
-  unsigned char yes;
-  unsigned short no;
-};
-
-enum {
-  INET_DIAG_BC_NOP,
-  INET_DIAG_BC_JMP,
-  INET_DIAG_BC_S_GE,
-  INET_DIAG_BC_S_LE,
-  INET_DIAG_BC_D_GE,
-  INET_DIAG_BC_D_LE,
-  INET_DIAG_BC_AUTO,
-  INET_DIAG_BC_S_COND,
-  INET_DIAG_BC_D_COND,
-};
-
-struct inet_diag_hostcond {
-  __u8 family;
-  __u8 prefix_len;
-  int port;
-  __be32 addr[0];
-};
-
-/* Base info structure. It contains socket identity (addrs/ports/cookie)
- * and, alas, the information shown by netstat. */
-struct inet_diag_msg {
-  __u8 idiag_family;
-  __u8 idiag_state;
-  __u8 idiag_timer;
-  __u8 idiag_retrans;
-
-  struct inet_diag_sockid id;
-
-  __u32 idiag_expires;
-  __u32 idiag_rqueue;
-  __u32 idiag_wqueue;
-  __u32 idiag_uid;
-  __u32 idiag_inode;
-};
-
-/* Extensions */
-
-enum {
-  INET_DIAG_NONE,
-  INET_DIAG_MEMINFO,
-  INET_DIAG_INFO,
-  INET_DIAG_VEGASINFO,
-  INET_DIAG_CONG,
-  INET_DIAG_TOS,
-  INET_DIAG_TCLASS,
-  INET_DIAG_SKMEMINFO,
-  INET_DIAG_SHUTDOWN,
-};
-
-#define INET_DIAG_MAX INET_DIAG_SHUTDOWN
-
-/* INET_DIAG_MEM */
-
-struct inet_diag_meminfo {
-  __u32 idiag_rmem;
-  __u32 idiag_wmem;
-  __u32 idiag_fmem;
-  __u32 idiag_tmem;
-};
-
-/* INET_DIAG_VEGASINFO */
-
-struct tcpvegas_info {
-  __u32 tcpv_enabled;
-  __u32 tcpv_rttcnt;
-  __u32 tcpv_rtt;
-  __u32 tcpv_minrtt;
-};
-
-#endif /* _UAPI_INET_DIAG_H_ */
diff --git a/osquery/tables/networking/linux/ip_tables.h b/osquery/tables/networking/linux/ip_tables.h
deleted file mode 100644 (file)
index da03258..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 25-Jul-1998 Major changes to allow for ip chain table
- *
- * 3-Jan-2000 Named tables to allow packet selection for different uses.
- */
-
-/*
- *     Format of an IP firewall descriptor
- *
- *     src, dst, src_mask, dst_mask are always stored in network byte order.
- *     flags are stored in host byte order (of course).
- *     Port numbers are stored in HOST byte order.
- */
-
-#ifndef _IPTABLES_H
-#define _IPTABLES_H
-
-#include <linux/types.h>
-
-#include <linux/netfilter_ipv4.h>
-
-#include <linux/netfilter/x_tables.h>
-
-#define IPT_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN
-#define IPT_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN
-#define ipt_match xt_match
-#define ipt_target xt_target
-#define ipt_table xt_table
-#define ipt_get_revision xt_get_revision
-#define ipt_entry_match xt_entry_match
-#define ipt_entry_target xt_entry_target
-#define ipt_standard_target xt_standard_target
-#define ipt_error_target xt_error_target
-#define ipt_counters xt_counters
-#define IPT_CONTINUE XT_CONTINUE
-#define IPT_RETURN XT_RETURN
-
-/* This group is older than old (iptables < v1.4.0-rc1~89) */
-#include <linux/netfilter/xt_tcpudp.h>
-#define ipt_udp xt_udp
-#define ipt_tcp xt_tcp
-#define IPT_TCP_INV_SRCPT      XT_TCP_INV_SRCPT
-#define IPT_TCP_INV_DSTPT      XT_TCP_INV_DSTPT
-#define IPT_TCP_INV_FLAGS      XT_TCP_INV_FLAGS
-#define IPT_TCP_INV_OPTION     XT_TCP_INV_OPTION
-#define IPT_TCP_INV_MASK       XT_TCP_INV_MASK
-#define IPT_UDP_INV_SRCPT      XT_UDP_INV_SRCPT
-#define IPT_UDP_INV_DSTPT      XT_UDP_INV_DSTPT
-#define IPT_UDP_INV_MASK       XT_UDP_INV_MASK
-
-/* The argument to IPT_SO_ADD_COUNTERS. */
-#define ipt_counters_info xt_counters_info
-/* Standard return verdict, or do jump. */
-#define IPT_STANDARD_TARGET XT_STANDARD_TARGET
-/* Error verdict. */
-#define IPT_ERROR_TARGET XT_ERROR_TARGET
-
-/* fn returns 0 to continue iteration */
-#define IPT_MATCH_ITERATE(e, fn, args...) \
-       XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)
-
-/* fn returns 0 to continue iteration */
-#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
-       XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
-
-/* Yes, Virginia, you have to zero the padding. */
-struct ipt_ip {
-       /* Source and destination IP addr */
-       struct in_addr src, dst;
-       /* Mask for src and dest IP addr */
-       struct in_addr smsk, dmsk;
-       char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
-       unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
-
-       /* Protocol, 0 = ANY */
-       __u16 proto;
-
-       /* Flags word */
-       __u8 flags;
-       /* Inverse flags */
-       __u8 invflags;
-};
-
-/* Values for "flag" field in struct ipt_ip (general ip structure). */
-#define IPT_F_FRAG             0x01    /* Set if rule is a fragment rule */
-#define IPT_F_GOTO             0x02    /* Set if jump is a goto */
-#define IPT_F_MASK             0x03    /* All possible flag bits mask. */
-
-/* Values for "inv" field in struct ipt_ip. */
-#define IPT_INV_VIA_IN         0x01    /* Invert the sense of IN IFACE. */
-#define IPT_INV_VIA_OUT                0x02    /* Invert the sense of OUT IFACE */
-#define IPT_INV_TOS            0x04    /* Invert the sense of TOS. */
-#define IPT_INV_SRCIP          0x08    /* Invert the sense of SRC IP. */
-#define IPT_INV_DSTIP          0x10    /* Invert the sense of DST OP. */
-#define IPT_INV_FRAG           0x20    /* Invert the sense of FRAG. */
-#define IPT_INV_PROTO          XT_INV_PROTO
-#define IPT_INV_MASK           0x7F    /* All possible flag bits mask. */
-
-/* This structure defines each of the firewall rules.  Consists of 3
-   parts which are 1) general IP header stuff 2) match specific
-   stuff 3) the target to perform if the rule matches */
-struct ipt_entry {
-       struct ipt_ip ip;
-
-       /* Mark with fields that we care about. */
-       unsigned int nfcache;
-
-       /* Size of ipt_entry + matches */
-       __u16 target_offset;
-       /* Size of ipt_entry + matches + target */
-       __u16 next_offset;
-
-       /* Back pointer */
-       unsigned int comefrom;
-
-       /* Packet and byte counters. */
-       struct xt_counters counters;
-
-       /* The matches (if any), then the target. */
-       unsigned char elems[0];
-};
-
-/*
- * New IP firewall options for [gs]etsockopt at the RAW IP level.
- * Unlike BSD Linux inherits IP options so you don't have to use a raw
- * socket for this. Instead we check rights in the calls.
- *
- * ATTENTION: check linux/in.h before adding new number here.
- */
-#define IPT_BASE_CTL           64
-
-#define IPT_SO_SET_REPLACE     (IPT_BASE_CTL)
-#define IPT_SO_SET_ADD_COUNTERS        (IPT_BASE_CTL + 1)
-#define IPT_SO_SET_MAX         IPT_SO_SET_ADD_COUNTERS
-
-#define IPT_SO_GET_INFO                        (IPT_BASE_CTL)
-#define IPT_SO_GET_ENTRIES             (IPT_BASE_CTL + 1)
-#define IPT_SO_GET_REVISION_MATCH      (IPT_BASE_CTL + 2)
-#define IPT_SO_GET_REVISION_TARGET     (IPT_BASE_CTL + 3)
-#define IPT_SO_GET_MAX                 IPT_SO_GET_REVISION_TARGET
-
-/* ICMP matching stuff */
-struct ipt_icmp {
-       __u8 type;                              /* type to match */
-       __u8 code[2];                           /* range of code */
-       __u8 invflags;                          /* Inverse flags */
-};
-
-/* Values for "inv" field for struct ipt_icmp. */
-#define IPT_ICMP_INV   0x01    /* Invert the sense of type/code test */
-
-/* The argument to IPT_SO_GET_INFO */
-struct ipt_getinfo {
-       /* Which table: caller fills this in. */
-       char name[XT_TABLE_MAXNAMELEN];
-
-       /* Kernel fills these in. */
-       /* Which hook entry points are valid: bitmask */
-       unsigned int valid_hooks;
-
-       /* Hook entry points: one per netfilter hook. */
-       unsigned int hook_entry[NF_INET_NUMHOOKS];
-
-       /* Underflow points. */
-       unsigned int underflow[NF_INET_NUMHOOKS];
-
-       /* Number of entries */
-       unsigned int num_entries;
-
-       /* Size of entries. */
-       unsigned int size;
-};
-
-/* The argument to IPT_SO_SET_REPLACE. */
-struct ipt_replace {
-       /* Which table. */
-       char name[XT_TABLE_MAXNAMELEN];
-
-       /* Which hook entry points are valid: bitmask.  You can't
-           change this. */
-       unsigned int valid_hooks;
-
-       /* Number of entries */
-       unsigned int num_entries;
-
-       /* Total size of new entries */
-       unsigned int size;
-
-       /* Hook entry points. */
-       unsigned int hook_entry[NF_INET_NUMHOOKS];
-
-       /* Underflow points. */
-       unsigned int underflow[NF_INET_NUMHOOKS];
-
-       /* Information about old entries: */
-       /* Number of counters (must be equal to current number of entries). */
-       unsigned int num_counters;
-       /* The old entries' counters. */
-       struct xt_counters *counters;
-
-       /* The entries (hang off end: not really an array). */
-       struct ipt_entry entries[0];
-};
-
-/* The argument to IPT_SO_GET_ENTRIES. */
-struct ipt_get_entries {
-       /* Which table: user fills this in. */
-       char name[XT_TABLE_MAXNAMELEN];
-
-       /* User fills this in: total entry size. */
-       unsigned int size;
-
-       /* The entries. */
-       struct ipt_entry entrytable[0];
-};
-
-/* Helper functions */
-static __inline__ struct xt_entry_target *
-ipt_get_target(struct ipt_entry *e)
-{
-       return (struct ipt_entry_target *)((char *)e + e->target_offset);
-}
-
-/*
- *     Main firewall chains definitions and global var's definitions.
- */
-#endif /* _IPTABLES_H */
diff --git a/osquery/tables/networking/linux/iptables.cpp b/osquery/tables/networking/linux/iptables.cpp
deleted file mode 100644 (file)
index 7e6f88d..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sstream>
-
-#include <arpa/inet.h>
-#include "libiptc.h"
-
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/tables/networking/utils.h"
-
-namespace osquery {
-namespace tables {
-
-const std::string kLinuxIpTablesNames = "/proc/net/ip_tables_names";
-const char MAP[] = {'0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
-const int HIGH_BITS = 4;
-const int LOW_BITS = 15;
-
-void parseIpEntry(ipt_ip *ip, Row &r) {
-  r["protocol"] = INTEGER(ip->proto);
-  if (strlen(ip->iniface)) {
-    r["iniface"] = TEXT(ip->iniface);
-  } else {
-    r["iniface"] = "all";
-  }
-
-  if (strlen(ip->outiface)) {
-    r["outiface"] = TEXT(ip->outiface);
-  } else {
-   r["outiface"] = "all";
-  }
-
-  r["src_ip"] = ipAsString((struct in_addr *)&ip->src);
-  r["dst_ip"] = ipAsString((struct in_addr *)&ip->dst);
-  r["src_mask"] = ipAsString((struct in_addr *)&ip->smsk);
-  r["dst_mask"] = ipAsString((struct in_addr *)&ip->dmsk);
-
-  char aux_char[2] = {0};
-  std::string iniface_mask;
-  for (int i = 0; ip->iniface_mask[i] != 0x00 && i<IFNAMSIZ; i++) {
-    aux_char[0] = MAP[(int) ip->iniface_mask[i] >> HIGH_BITS];
-    aux_char[1] = MAP[(int) ip->iniface_mask[i] & LOW_BITS];
-    iniface_mask += aux_char[0];
-    iniface_mask += aux_char[1];
-  }
-
-  r["iniface_mask"] = TEXT(iniface_mask);
-  std::string outiface_mask = "";
-  for (int i = 0; ip->outiface_mask[i] != 0x00 && i<IFNAMSIZ; i++) {
-    aux_char[0] = MAP[(int) ip->outiface_mask[i] >> HIGH_BITS];
-    aux_char[1] = MAP[(int) ip->outiface_mask[i] & LOW_BITS];
-    outiface_mask += aux_char[0];
-    outiface_mask += aux_char[1];
-  }
-  r["outiface_mask"] = TEXT(outiface_mask);
-}
-
-void genIPTablesRules(const std::string &filter, QueryData &results) {
-  Row r;
-  r["filter_name"] = filter;
-
-  // Initialize the access to iptc
-  auto handle = (struct iptc_handle *)iptc_init(filter.c_str());
-  if (handle == nullptr) {
-    return;
-  }
-
-  // Iterate through chains
-  for (auto chain = iptc_first_chain(handle); chain != nullptr;
-       chain = iptc_next_chain(handle)) {
-    r["chain"] = TEXT(chain);
-
-    struct ipt_counters counters;
-    auto policy = iptc_get_policy(chain, &counters, handle);
-
-    if (policy != nullptr) {
-      r["policy"] = TEXT(policy);
-      r["packets"] = INTEGER(counters.pcnt);
-      r["bytes"] = INTEGER(counters.bcnt);
-    } else {
-      r["policy"] = "";
-      r["packets"] = "0";
-      r["bytes"] = "0";
-    }
-
-    struct ipt_entry *prev_rule = nullptr;
-    // Iterating through all the rules per chain
-    for (auto chain_rule = iptc_first_rule(chain, handle); chain_rule;
-         chain_rule = iptc_next_rule(prev_rule, handle)) {
-      prev_rule = (struct ipt_entry *)chain_rule;
-
-      auto target = iptc_get_target(chain_rule, handle);
-      if (target != nullptr) {
-        r["target"] = TEXT(target);
-      } else {
-        r["target"] = "";
-      }
-
-      if (chain_rule->target_offset) {
-        r["match"] = "yes";
-      } else {
-        r["match"] = "no";
-      }
-
-      struct ipt_ip *ip = (struct ipt_ip *)&chain_rule->ip;
-      parseIpEntry(ip, r);
-
-      results.push_back(r);
-    } // Rule iteration
-    results.push_back(r);
-  } // Chain iteration
-
-  iptc_free(handle);
-}
-
-QueryData genIptables(QueryContext& context) {
-  QueryData results;
-
-  // Read in table names
-  std::string content;
-  auto s = osquery::readFile(kLinuxIpTablesNames, content);
-  if (s.ok()) {
-    for (auto &line : split(content, "\n")) {
-      boost::trim(line);
-      if (line.size() > 0) {
-        genIPTablesRules(line, results);
-      }
-    }
-  } else {
-    // Permissions issue or iptables modules are not loaded.
-    TLOG << "Error reading " << kLinuxIpTablesNames << " : " << s.toString();
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/networking/linux/libiptc.h b/osquery/tables/networking/linux/libiptc.h
deleted file mode 100644 (file)
index fd561bd..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-#ifndef _LIBIPTC_H
-#define _LIBIPTC_H
-/* Library which manipulates filtering rules. */
-
-#include <linux/types.h>
-#include <libiptc/ipt_kernel_headers.h>
-#ifdef __cplusplus
-#      include <climits>
-#else
-#      include <limits.h> /* INT_MAX in ip_tables.h */
-#endif
-#include "ip_tables.h"
-#include <libiptc/xtcshared.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define iptc_handle xtc_handle
-#define ipt_chainlabel xt_chainlabel
-
-#define IPTC_LABEL_ACCEPT  "ACCEPT"
-#define IPTC_LABEL_DROP    "DROP"
-#define IPTC_LABEL_QUEUE   "QUEUE"
-#define IPTC_LABEL_RETURN  "RETURN"
-
-/* Does this chain exist? */
-int iptc_is_chain(const char *chain, struct xtc_handle *const handle);
-
-/* Take a snapshot of the rules.  Returns NULL on error. */
-struct xtc_handle *iptc_init(const char *tablename);
-
-/* Cleanup after iptc_init(). */
-void iptc_free(struct xtc_handle *h);
-
-/* Iterator functions to run through the chains.  Returns NULL at end. */
-const char *iptc_first_chain(struct xtc_handle *handle);
-const char *iptc_next_chain(struct xtc_handle *handle);
-
-/* Get first rule in the given chain: NULL for empty chain. */
-const struct ipt_entry *iptc_first_rule(const char *chain,
-                                       struct xtc_handle *handle);
-
-/* Returns NULL when rules run out. */
-const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
-                                      struct xtc_handle *handle);
-
-/* Returns a pointer to the target name of this entry. */
-const char *iptc_get_target(const struct ipt_entry *e,
-                           struct xtc_handle *handle);
-
-/* Is this a built-in chain? */
-int iptc_builtin(const char *chain, struct xtc_handle *const handle);
-
-/* Get the policy of a given built-in chain */
-const char *iptc_get_policy(const char *chain,
-                           struct xt_counters *counter,
-                           struct xtc_handle *handle);
-
-/* These functions return TRUE for OK or 0 and set errno.  If errno ==
-   0, it means there was a version error (ie. upgrade libiptc). */
-/* Rule numbers start at 1 for the first rule. */
-
-/* Insert the entry `e' in chain `chain' into position `rulenum'. */
-int iptc_insert_entry(const xt_chainlabel chain,
-                     const struct ipt_entry *e,
-                     unsigned int rulenum,
-                     struct xtc_handle *handle);
-
-/* Atomically replace rule `rulenum' in `chain' with `e'. */
-int iptc_replace_entry(const xt_chainlabel chain,
-                      const struct ipt_entry *e,
-                      unsigned int rulenum,
-                      struct xtc_handle *handle);
-
-/* Append entry `e' to chain `chain'.  Equivalent to insert with
-   rulenum = length of chain. */
-int iptc_append_entry(const xt_chainlabel chain,
-                     const struct ipt_entry *e,
-                     struct xtc_handle *handle);
-
-/* Check whether a mathching rule exists */
-int iptc_check_entry(const xt_chainlabel chain,
-                     const struct ipt_entry *origfw,
-                     unsigned char *matchmask,
-                     struct xtc_handle *handle);
-
-/* Delete the first rule in `chain' which matches `e', subject to
-   matchmask (array of length == origfw) */
-int iptc_delete_entry(const xt_chainlabel chain,
-                     const struct ipt_entry *origfw,
-                     unsigned char *matchmask,
-                     struct xtc_handle *handle);
-
-/* Delete the rule in position `rulenum' in `chain'. */
-int iptc_delete_num_entry(const xt_chainlabel chain,
-                         unsigned int rulenum,
-                         struct xtc_handle *handle);
-
-/* Check the packet `e' on chain `chain'.  Returns the verdict, or
-   NULL and sets errno. */
-const char *iptc_check_packet(const xt_chainlabel chain,
-                             struct ipt_entry *entry,
-                             struct xtc_handle *handle);
-
-/* Flushes the entries in the given chain (ie. empties chain). */
-int iptc_flush_entries(const xt_chainlabel chain,
-                      struct xtc_handle *handle);
-
-/* Zeroes the counters in a chain. */
-int iptc_zero_entries(const xt_chainlabel chain,
-                     struct xtc_handle *handle);
-
-/* Creates a new chain. */
-int iptc_create_chain(const xt_chainlabel chain,
-                     struct xtc_handle *handle);
-
-/* Deletes a chain. */
-int iptc_delete_chain(const xt_chainlabel chain,
-                     struct xtc_handle *handle);
-
-/* Renames a chain. */
-int iptc_rename_chain(const xt_chainlabel oldname,
-                     const xt_chainlabel newname,
-                     struct xtc_handle *handle);
-
-/* Sets the policy on a built-in chain. */
-int iptc_set_policy(const xt_chainlabel chain,
-                   const xt_chainlabel policy,
-                   struct xt_counters *counters,
-                   struct xtc_handle *handle);
-
-/* Get the number of references to this chain */
-int iptc_get_references(unsigned int *ref,
-                       const xt_chainlabel chain,
-                       struct xtc_handle *handle);
-
-/* read packet and byte counters for a specific rule */
-struct xt_counters *iptc_read_counter(const xt_chainlabel chain,
-                                      unsigned int rulenum,
-                                      struct xtc_handle *handle);
-
-/* zero packet and byte counters for a specific rule */
-int iptc_zero_counter(const xt_chainlabel chain,
-                     unsigned int rulenum,
-                     struct xtc_handle *handle);
-
-/* set packet and byte counters for a specific rule */
-int iptc_set_counter(const xt_chainlabel chain,
-                    unsigned int rulenum,
-                    struct xt_counters *counters,
-                    struct xtc_handle *handle);
-
-/* Makes the actual changes. */
-int iptc_commit(struct xtc_handle *handle);
-
-/* Get raw socket. */
-int iptc_get_raw_socket(void);
-
-/* Translates errno numbers into more human-readable form than strerror. */
-const char *iptc_strerror(int err);
-
-extern void dump_entries(struct xtc_handle *const);
-
-extern const struct xtc_ops iptc_ops;
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* _LIBIPTC_H */
diff --git a/osquery/tables/networking/linux/process_open_sockets.cpp b/osquery/tables/networking/linux/process_open_sockets.cpp
deleted file mode 100644 (file)
index e2cac9c..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <arpa/inet.h>
-
-#include <boost/algorithm/string/split.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-// Linux proc protocol define to net stats file name.
-const std::map<int, std::string> kLinuxProtocolNames = {
-    {IPPROTO_ICMP, "icmp"},
-    {IPPROTO_TCP, "tcp"},
-    {IPPROTO_UDP, "udp"},
-    {IPPROTO_UDPLITE, "udplite"},
-    {IPPROTO_RAW, "raw"},
-};
-
-std::string addressFromHex(const std::string &encoded_address, int family) {
-  char addr_buffer[INET6_ADDRSTRLEN] = {0};
-  if (family == AF_INET) {
-    struct in_addr decoded;
-    if (encoded_address.length() == 8) {
-      sscanf(encoded_address.c_str(), "%X", &(decoded.s_addr));
-      inet_ntop(AF_INET, &decoded, addr_buffer, INET_ADDRSTRLEN);
-    }
-  } else if (family == AF_INET6) {
-    struct in6_addr decoded;
-    if (encoded_address.length() == 32) {
-      sscanf(encoded_address.c_str(),
-             "%8x%8x%8x%8x",
-             (unsigned int *)&(decoded.s6_addr[0]),
-             (unsigned int *)&(decoded.s6_addr[4]),
-             (unsigned int *)&(decoded.s6_addr[8]),
-             (unsigned int *)&(decoded.s6_addr[12]));
-      inet_ntop(AF_INET6, &decoded, addr_buffer, INET6_ADDRSTRLEN);
-    }
-  }
-
-  return TEXT(addr_buffer);
-}
-
-unsigned short portFromHex(const std::string &encoded_port) {
-  unsigned short decoded = 0;
-  if (encoded_port.length() == 4) {
-    sscanf(encoded_port.c_str(), "%hX", &decoded);
-  }
-  return decoded;
-}
-
-void genSocketsFromProc(const std::map<std::string, std::string> &inodes,
-                        int protocol,
-                        int family,
-                        QueryData &results) {
-  std::string path = "/proc/net/";
-  if (family == AF_UNIX) {
-    path += "unix";
-  } else {
-    path += kLinuxProtocolNames.at(protocol);
-    path += (family == AF_INET6) ? "6" : "";
-  }
-
-  std::string content;
-  if (!osquery::readFile(path, content).ok()) {
-    // Could not open socket information from /proc.
-    return;
-  }
-
-  // The system's socket information is tokenized by line.
-  size_t index = 0;
-  for (const auto &line : osquery::split(content, "\n")) {
-    if (++index == 1) {
-      // The first line is a textual header and will be ignored.
-      if (line.find("sl") != 0 && line.find("sk") != 0 &&
-          line.find("Num") != 0) {
-        // Header fields are unknown, stop parsing.
-        break;
-      }
-      continue;
-    }
-
-    // The socket information is tokenized by spaces, each a field.
-    auto fields = osquery::split(line, " ");
-    // UNIX socket reporting has a smaller number of fields.
-    size_t min_fields = (family == AF_UNIX) ? 7 : 10;
-    if (fields.size() < min_fields) {
-      // Unknown/malformed socket information.
-      continue;
-    }
-
-
-    Row r;
-    if (family == AF_UNIX) {
-      r["socket"] = fields[6];
-      r["family"] = "0";
-      r["protocol"] = fields[2];
-      r["local_address"] = "";
-      r["local_port"] = "0";
-      r["remote_address"] = "";
-      r["remote_port"] = "0";
-      r["path"] = (fields.size() >= 8) ? fields[7] : "";
-    } else {
-      // Two of the fields are the local/remote address/port pairs.
-      auto locals = osquery::split(fields[1], ":");
-      auto remotes = osquery::split(fields[2], ":");
-      if (locals.size() != 2 || remotes.size() != 2) {
-        // Unknown/malformed socket information.
-        continue;
-      }
-
-      r["socket"] = fields[9];
-      r["family"] = INTEGER(family);
-      r["protocol"] = INTEGER(protocol);
-      r["local_address"] = addressFromHex(locals[0], family);
-      r["local_port"] = INTEGER(portFromHex(locals[1]));
-      r["remote_address"] = addressFromHex(remotes[0], family);
-      r["remote_port"] = INTEGER(portFromHex(remotes[1]));
-      // Path is only used for UNIX domain sockets.
-      r["path"] = "";
-    }
-
-    if (inodes.count(r["socket"]) > 0) {
-      r["pid"] = inodes.at(r["socket"]);
-    } else {
-      r["pid"] = "-1";
-    }
-
-    results.push_back(r);
-  }
-}
-
-QueryData genOpenSockets(QueryContext &context) {
-  QueryData results;
-
-  // If a pid is given then set that as the only item in processes.
-  std::set<std::string> pids;
-  if (context.constraints["pid"].exists(EQUALS)) {
-    pids = context.constraints["pid"].getAll(EQUALS);
-  } else {
-    osquery::procProcesses(pids);
-  }
-
-  // Generate a map of socket inode to process tid.
-  std::map<std::string, std::string> socket_inodes;
-  for (const auto &process : pids) {
-    std::map<std::string, std::string> descriptors;
-    if (osquery::procDescriptors(process, descriptors).ok()) {
-      for (const auto& fd : descriptors) {
-        if (fd.second.find("socket:[") == 0) {
-          // See #792: std::regex is incomplete until GCC 4.9 (skip 8 chars)
-          auto inode = fd.second.substr(8);
-          socket_inodes[inode.substr(0, inode.size() - 1)] = process;
-        }
-      }
-    }
-  }
-
-  // This used to use netlink (Ref: #1094) to request socket information.
-  // Use proc messages to query socket information.
-  for (const auto &protocol : kLinuxProtocolNames) {
-    genSocketsFromProc(socket_inodes, protocol.first, AF_INET, results);
-    genSocketsFromProc(socket_inodes, protocol.first, AF_INET6, results);
-  }
-
-  genSocketsFromProc(socket_inodes, IPPROTO_IP, AF_UNIX, results);
-  return results;
-}
-}
-}
diff --git a/osquery/tables/networking/linux/routes.cpp b/osquery/tables/networking/linux/routes.cpp
deleted file mode 100644 (file)
index d32ad82..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <net/if.h>
-
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/tables/networking/utils.h"
-
-namespace osquery {
-namespace tables {
-
-#define MAX_NETLINK_SIZE 8192
-#define MAX_NETLINK_LATENCY 2000
-
-std::string getNetlinkIP(int family, const char* buffer) {
-  char dst[INET6_ADDRSTRLEN];
-  memset(dst, 0, INET6_ADDRSTRLEN);
-
-  inet_ntop(family, buffer, dst, INET6_ADDRSTRLEN);
-  std::string address(dst);
-  boost::trim(address);
-
-  return address;
-}
-
-Status readNetlink(int socket_fd, int seq, char* output, size_t* size) {
-  struct nlmsghdr* nl_hdr = nullptr;
-
-  size_t message_size = 0;
-  do {
-    int latency = 0;
-    int bytes = 0;
-    while (bytes == 0) {
-      bytes = recv(socket_fd, output, MAX_NETLINK_SIZE - message_size, 0);
-      if (bytes < 0) {
-        return Status(1, "Could not read from NETLINK");
-      } else if (latency >= MAX_NETLINK_LATENCY) {
-        return Status(1, "Netlink timeout");
-      } else if (bytes == 0) {
-        ::usleep(20);
-        latency += 20;
-      }
-    }
-
-    // Assure valid header response, and not an error type.
-    nl_hdr = (struct nlmsghdr*)output;
-    if (NLMSG_OK(nl_hdr, bytes) == 0 || nl_hdr->nlmsg_type == NLMSG_ERROR) {
-      return Status(1, "Read invalid NETLINK message");
-    }
-
-    if (nl_hdr->nlmsg_type == NLMSG_DONE) {
-      break;
-    }
-
-    // Move the buffer pointer
-    output += bytes;
-    message_size += bytes;
-    if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) {
-      break;
-    }
-  } while (nl_hdr->nlmsg_seq != seq || nl_hdr->nlmsg_pid != getpid());
-
-  *size = message_size;
-  return Status(0, "OK");
-}
-
-void genNetlinkRoutes(const struct nlmsghdr* netlink_msg, QueryData& results) {
-  std::string address;
-  int mask = 0;
-  char interface[IF_NAMESIZE];
-
-  struct rtmsg* message = (struct rtmsg*)NLMSG_DATA(netlink_msg);
-  struct rtattr* attr = (struct rtattr*)RTM_RTA(message);
-  int attr_size = RTM_PAYLOAD(netlink_msg);
-
-  Row r;
-
-  // Iterate over each route in the netlink message
-  bool has_destination = false;
-  r["metric"] = "0";
-  while (RTA_OK(attr, attr_size)) {
-    switch (attr->rta_type) {
-    case RTA_OIF:
-      if_indextoname(*(int*)RTA_DATA(attr), interface);
-      r["interface"] = std::string(interface);
-      break;
-    case RTA_GATEWAY:
-      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
-      r["gateway"] = address;
-      break;
-    case RTA_PREFSRC:
-      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
-      r["source"] = address;
-      break;
-    case RTA_DST:
-      if (message->rtm_dst_len != 32 && message->rtm_dst_len != 128) {
-        mask = (int)message->rtm_dst_len;
-      }
-      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
-      r["destination"] = address;
-      has_destination = true;
-      break;
-    case RTA_PRIORITY:
-      r["metric"] = INTEGER(*(int*)RTA_DATA(attr));
-      break;
-    }
-    attr = RTA_NEXT(attr, attr_size);
-  }
-
-  if (!has_destination) {
-    r["destination"] = "0.0.0.0";
-    if (message->rtm_dst_len) {
-      mask = (int)message->rtm_dst_len;
-    }
-  }
-
-  // Route type determination
-  if (message->rtm_type == RTN_UNICAST) {
-    r["type"] = "gateway";
-  } else if (message->rtm_type == RTN_LOCAL) {
-    r["type"] = "local";
-  } else if (message->rtm_type == RTN_BROADCAST) {
-    r["type"] = "broadcast";
-  } else if (message->rtm_type == RTN_ANYCAST) {
-    r["type"] = "anycast";
-  } else {
-    r["type"] = "other";
-  }
-
-  r["flags"] = INTEGER(message->rtm_flags);
-
-  // This is the cidr-formatted mask
-  r["netmask"] = INTEGER(mask);
-
-  // Fields not supported by Linux routes:
-  r["mtu"] = "0";
-  results.push_back(r);
-}
-
-QueryData genRoutes(QueryContext& context) {
-  QueryData results;
-
-  int socket_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
-  if (socket_fd < 0) {
-    VLOG(1) << "Cannot open NETLINK socket";
-    return {};
-  }
-
-  // Create netlink message header
-  auto netlink_buffer = (void*)malloc(MAX_NETLINK_SIZE);
-  if (netlink_buffer == nullptr) {
-    close(socket_fd);
-    return {};
-  }
-
-  auto netlink_msg = (struct nlmsghdr*)netlink_buffer;
-  netlink_msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
-  netlink_msg->nlmsg_type = RTM_GETROUTE; // routes from kernel routing table
-  netlink_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
-  netlink_msg->nlmsg_seq = 0;
-  netlink_msg->nlmsg_pid = getpid();
-
-  // Send the netlink request to the kernel
-  if (send(socket_fd, netlink_msg, netlink_msg->nlmsg_len, 0) < 0) {
-    TLOG << "Cannot write NETLINK request header to socket";
-    close(socket_fd);
-    free(netlink_buffer);
-    return {};
-  }
-
-  // Wrap the read socket to support multi-netlink messages
-  size_t size = 0;
-  if (!readNetlink(socket_fd, 1, (char*)netlink_msg, &size).ok()) {
-    TLOG << "Cannot read NETLINK response from socket";
-    close(socket_fd);
-    free(netlink_buffer);
-    return {};
-  }
-
-  // Treat the netlink response as route information
-  while (NLMSG_OK(netlink_msg, size)) {
-    genNetlinkRoutes(netlink_msg, results);
-    netlink_msg = NLMSG_NEXT(netlink_msg, size);
-  }
-
-  close(socket_fd);
-  free(netlink_buffer);
-  return results;
-}
-}
-}
diff --git a/osquery/tables/networking/linux/tests/iptables_tests.cpp b/osquery/tables/networking/linux/tests/iptables_tests.cpp
deleted file mode 100644 (file)
index 2761ead..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/logger.h>
-
-#include <libiptc/libiptc.h>
-#include <arpa/inet.h>
-
-#include "osquery/core/test_util.h"
-
-namespace osquery {
-namespace tables {
-
-void parseIpEntry(ipt_ip *ip, Row &row);
-
-ipt_ip* getIpEntryContent() {
-  static ipt_ip ip_entry;
-
-  ip_entry.proto = 6;
-  memset(ip_entry.iniface, 0, IFNAMSIZ);
-  strcpy(ip_entry.outiface, "eth0");
-  inet_aton("123.123.123.123", &ip_entry.src);
-  inet_aton("45.45.45.45", &ip_entry.dst);
-  inet_aton("250.251.252.253", &ip_entry.smsk);
-  inet_aton("253.252.251.250", &ip_entry.dmsk);
-  memset(ip_entry.iniface_mask, 0xfe, IFNAMSIZ );
-  memset(ip_entry.outiface_mask, 0xfa, IFNAMSIZ );
-
-  return &ip_entry;
-}
-
-Row getIpEntryExpectedResults() {
-  Row row;
-
-  row["protocol"] = "6";
-  row["iniface"] = "all";
-  row["outiface"] = "eth0";
-  row["src_ip"] = "123.123.123.123";
-  row["dst_ip"] = "45.45.45.45";
-  row["src_mask"] = "250.251.252.253";
-  row["dst_mask"] = "253.252.251.250";
-  row["iniface_mask"] = "FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE";
-  row["outiface_mask"] = "FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFA";
-
-  return row;
-}
-
-class IptablesTests : public testing::Test {};
-
-TEST_F(IptablesTests, test_iptables_ip_entry) {
-  Row row;
-  parseIpEntry(getIpEntryContent(), row);
-  EXPECT_EQ(row, getIpEntryExpectedResults());
-}
-}
-}
diff --git a/osquery/tables/networking/listening_ports.cpp b/osquery/tables/networking/listening_ports.cpp
deleted file mode 100644 (file)
index 951163a..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/sql.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-typedef std::pair<std::string, std::string> ProtoFamilyPair;
-typedef std::map<std::string, std::vector<ProtoFamilyPair> > PortMap;
-
-QueryData genListeningPorts(QueryContext& context) {
-  QueryData results;
-
-  auto sockets = SQL::selectAllFrom("process_open_sockets");
-
-  PortMap ports;
-  for (const auto& socket : sockets) {
-    if (socket.at("remote_port") != "0") {
-      // Listening UDP/TCP ports have a remote_port == "0"
-      continue;
-    }
-
-    if (ports.count(socket.at("local_port")) > 0) {
-      bool duplicate = false;
-      for (const auto& entry : ports[socket.at("local_port")]) {
-        if (entry.first == socket.at("protocol") &&
-            entry.second == socket.at("family")) {
-          duplicate = true;
-          break;
-        }
-      }
-
-      if (duplicate) {
-        // There is a duplicate socket descriptor for this bind.
-        continue;
-      }
-    }
-
-    // Add this family/protocol/port bind to the tracked map.
-    ports[socket.at("local_port")].push_back(
-        std::make_pair(socket.at("protocol"), socket.at("family")));
-
-    Row r;
-    r["pid"] = socket.at("pid");
-    r["port"] = socket.at("local_port");
-    r["protocol"] = socket.at("protocol");
-    r["family"] = socket.at("family");
-    r["address"] = socket.at("local_address");
-
-    results.push_back(r);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/networking/tests/etc_hosts_tests.cpp b/osquery/tables/networking/tests/etc_hosts_tests.cpp
deleted file mode 100644 (file)
index bad5904..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/logger.h>
-
-#include "osquery/core/test_util.h"
-
-namespace osquery {
-namespace tables {
-
-osquery::QueryData parseEtcHostsContent(const std::string& content);
-
-class EtcHostsTests : public testing::Test {};
-
-TEST_F(EtcHostsTests, test_parse_etc_hosts_content) {
-  EXPECT_EQ(parseEtcHostsContent(getEtcHostsContent()),
-            getEtcHostsExpectedResults());
-}
-}
-}
diff --git a/osquery/tables/networking/tests/etc_protocols_tests.cpp b/osquery/tables/networking/tests/etc_protocols_tests.cpp
deleted file mode 100644 (file)
index e6786be..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <gtest/gtest.h>
-
-#include <osquery/logger.h>
-
-#include "osquery/core/test_util.h"
-
-namespace osquery {
-namespace tables {
-
-osquery::QueryData parseEtcProtocolsContent(const std::string& content);
-
-class EtcProtocolsTests : public testing::Test {};
-
-TEST_F(EtcProtocolsTests, test_parse_etc_protocols_content) {
-  EXPECT_EQ(parseEtcProtocolsContent(getEtcProtocolsContent()),
-            getEtcProtocolsExpectedResults());
-}
-}
-}
diff --git a/osquery/tables/networking/utils.cpp b/osquery/tables/networking/utils.cpp
deleted file mode 100644 (file)
index 6ece3f1..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <iomanip>
-#include <sstream>
-
-#if defined(__linux__)
-#include <net/if.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#define AF_LINK AF_PACKET
-#else
-#include <net/if_dl.h>
-#endif
-
-#include <boost/algorithm/string/trim.hpp>
-
-#include "osquery/tables/networking/utils.h"
-
-namespace osquery {
-namespace tables {
-
-std::string ipAsString(const struct sockaddr *in) {
-  char dst[INET6_ADDRSTRLEN] = {0};
-  void *in_addr = nullptr;
-
-  if (in->sa_family == AF_INET) {
-    in_addr = (void *)&(((struct sockaddr_in *)in)->sin_addr);
-  } else if (in->sa_family == AF_INET6) {
-    in_addr = (void *)&(((struct sockaddr_in6 *)in)->sin6_addr);
-  } else {
-    return "";
-  }
-
-  inet_ntop(in->sa_family, in_addr, dst, sizeof(dst));
-  std::string address(dst);
-  boost::trim(address);
-  return address;
-}
-
-std::string ipAsString(const struct in_addr *in) {
-  char dst[INET6_ADDRSTRLEN] = {0};
-
-  inet_ntop(AF_INET, in, dst, sizeof(dst));
-  std::string address(dst);
-  boost::trim(address);
-  return address;
-}
-
-inline short addBits(unsigned char byte) {
-  short bits = 0;
-  for (int i = 7; i >= 0; --i) {
-    if ((byte & (1 << i)) == 0) {
-      break;
-    }
-    bits++;
-  }
-  return bits;
-}
-
-int netmaskFromIP(const struct sockaddr *in) {
-  int mask = 0;
-
-  if (in->sa_family == AF_INET6) {
-    auto in6 = (struct sockaddr_in6 *)in;
-    for (size_t i = 0; i < 16; i++) {
-      mask += addBits(in6->sin6_addr.s6_addr[i]);
-    }
-  } else {
-    auto in4 = (struct sockaddr_in *)in;
-    auto address = reinterpret_cast<char *>(&in4->sin_addr.s_addr);
-    for (size_t i = 0; i < 4; i++) {
-      mask += addBits(address[i]);
-    }
-  }
-
-  return mask;
-}
-
-inline std::string macAsString(const char *addr) {
-  std::stringstream mac;
-
-  for (size_t i = 0; i < 6; i++) {
-    mac << std::hex << std::setfill('0') << std::setw(2);
-    mac << (int)((uint8_t)addr[i]);
-    if (i != 5) {
-      mac << ":";
-    }
-  }
-
-  return mac.str();
-}
-
-std::string macAsString(const struct ifaddrs *addr) {
-  static std::string blank_mac = "00:00:00:00:00:00";
-  if (addr->ifa_addr == nullptr) {
-    // No link or MAC exists.
-    return blank_mac;
-  }
-
-#if defined(__linux__)
-  struct ifreq ifr;
-  ifr.ifr_addr.sa_family = AF_INET;
-  memcpy(ifr.ifr_name, addr->ifa_name, IFNAMSIZ);
-
-  int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
-  if (socket_fd < 0) {
-    return blank_mac;
-  }
-  ioctl(socket_fd, SIOCGIFHWADDR, &ifr);
-  close(socket_fd);
-
-  return macAsString(ifr.ifr_hwaddr.sa_data);
-#else
-  auto sdl = (struct sockaddr_dl *)addr->ifa_addr;
-  if (sdl->sdl_alen != 6) {
-    // Do not support MAC address that are not 6 bytes...
-    return blank_mac;
-  }
-
-  return macAsString(&sdl->sdl_data[sdl->sdl_nlen]);
-#endif
-}
-}
-}
diff --git a/osquery/tables/networking/utils.h b/osquery/tables/networking/utils.h
deleted file mode 100644 (file)
index 1243ded..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#pragma once
-
-#include <string>
-
-#include <ifaddrs.h>
-#include <arpa/inet.h>
-
-namespace osquery {
-namespace tables {
-
-// Return a string representation for an IPv4/IPv6 struct.
-std::string ipAsString(const struct sockaddr *in);
-std::string ipAsString(const struct in_addr *in);
-std::string macAsString(const struct ifaddrs *addr);
-std::string macAsString(const char *addr);
-int netmaskFromIP(const struct sockaddr *in);
-}
-}
diff --git a/osquery/tables/system/crontab.cpp b/osquery/tables/system/crontab.cpp
deleted file mode 100644 (file)
index fb54584..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-namespace tables {
-
-const std::string kSystemCron = "/etc/crontab";
-
-const std::vector<std::string> kUserCronPaths = {
-    "/var/at/tabs/", "/var/spool/cron/", "/var/spool/cron/crontabs/",
-};
-
-std::vector<std::string> cronFromFile(const std::string& path) {
-  std::string content;
-  std::vector<std::string> cron_lines;
-  if (!isReadable(path).ok()) {
-    return cron_lines;
-  }
-
-  if (!readFile(path, content).ok()) {
-    return cron_lines;
-  }
-
-  auto lines = split(content, "\n");
-
-  // Only populate the lines that are not comments or blank.
-  for (auto& line : lines) {
-    // Cheat and use a non-const iteration, to inline trim.
-    boost::trim(line);
-    if (line.size() > 0 && line.at(0) != '#') {
-      cron_lines.push_back(line);
-    }
-  }
-
-  return cron_lines;
-}
-
-void genCronLine(const std::string& path,
-                 const std::string& line,
-                 QueryData& results) {
-  Row r;
-
-  r["path"] = path;
-  auto columns = split(line, " \t");
-
-  size_t index = 0;
-  auto iterator = columns.begin();
-  for (; iterator != columns.end(); ++iterator) {
-    if (index == 0) {
-      if ((*iterator).at(0) == '@') {
-        // If the first value is an 'at' then skip to the command.
-        r["event"] = *iterator;
-        index = 5;
-        continue;
-      }
-      r["minute"] = *iterator;
-    } else if (index == 1) {
-      r["hour"] = *iterator;
-    } else if (index == 2) {
-      r["day_of_month"] = *iterator;
-    } else if (index == 3) {
-      r["month"] = *iterator;
-    } else if (index == 4) {
-      r["day_of_week"] = *iterator;
-    } else if (index == 5) {
-      r["command"] = *iterator;
-    } else {
-      // Long if switch to handle command breaks from space delim.
-      r["command"] += " " + *iterator;
-    }
-    index++;
-  }
-
-  if (r["command"].size() == 0) {
-    // The line was not well-formed, perhaps it was a variable?
-    return;
-  }
-
-  results.push_back(r);
-}
-
-QueryData genCronTab(QueryContext& context) {
-  QueryData results;
-
-  auto system_lines = cronFromFile(kSystemCron);
-  for (const auto& line : system_lines) {
-    genCronLine(kSystemCron, line, results);
-  }
-
-  std::vector<std::string> user_crons;
-  for (const auto cron_path : kUserCronPaths) {
-    osquery::listFilesInDirectory(cron_path, user_crons);
-  }
-
-  // The user-based crons are identified by their path.
-  for (const auto& user_path : user_crons) {
-    auto user_lines = cronFromFile(user_path);
-    for (const auto& line : user_lines) {
-      genCronLine(user_path, line, results);
-    }
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/last.cpp b/osquery/tables/system/last.cpp
deleted file mode 100644 (file)
index b780b12..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <utmpx.h>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData genLastAccess(QueryContext& context) {
-  QueryData results;
-  struct utmpx *ut;
-#ifdef __APPLE__
-  setutxent_wtmp(0); // 0 = reverse chronological order
-
-  while ((ut = getutxent_wtmp()) != nullptr) {
-#else
-
-  utmpxname("/var/log/wtmpx");
-  setutxent();
-
-  while ((ut = getutxent()) != nullptr) {
-#endif
-
-    Row r;
-    r["username"] = std::string(ut->ut_user);
-    r["tty"] = std::string(ut->ut_line);
-    r["pid"] = boost::lexical_cast<std::string>(ut->ut_pid);
-    r["type"] = boost::lexical_cast<std::string>(ut->ut_type);
-    r["time"] = boost::lexical_cast<std::string>(ut->ut_tv.tv_sec);
-    r["host"] = std::string(ut->ut_host);
-
-    results.push_back(r);
-  }
-
-#ifdef __APPLE__
-  endutxent_wtmp();
-#else
-  endutxent();
-#endif
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/acpi_tables.cpp b/osquery/tables/system/linux/acpi_tables.cpp
deleted file mode 100644 (file)
index 662926d..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/hash.h>
-#include <osquery/tables.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-namespace tables {
-
-const std::string kLinuxACPIPath = "/sys/firmware/acpi/tables";
-
-void genACPITable(const std::string& table, QueryData& results) {
-  fs::path table_path = table;
-
-  // There may be "categories" of tables in the form of directories.
-  Status status;
-  if (!fs::is_regular_file(table_path)) {
-    std::vector<std::string> child_tables;
-    status = osquery::listFilesInDirectory(table_path, child_tables);
-    if (status.ok()) {
-      for (const auto& child_table : child_tables) {
-        genACPITable(child_table, results);
-      }
-    }
-
-    return;
-  }
-
-  Row r;
-  r["name"] = table_path.filename().string();
-
-  std::string table_content;
-  status = osquery::readFile(table_path, table_content);
-  if (!status.ok()) {
-    r["size"] = INTEGER(-1);
-  } else {
-    r["size"] = INTEGER(table_content.size());
-    r["md5"] = osquery::hashFromBuffer(
-        HASH_TYPE_MD5, table_content.c_str(), table_content.length());
-  }
-
-  results.push_back(r);
-}
-
-QueryData genACPITables(QueryContext& context) {
-  QueryData results;
-
-  // In Linux, hopefully the ACPI tables are parsed and exposed as nodes.
-  std::vector<std::string> tables;
-  auto status = osquery::listFilesInDirectory(kLinuxACPIPath, tables);
-  if (!status.ok()) {
-    // We could not read the tables path or the nodes are not exposed.
-    return {};
-  }
-
-  for (const auto& table : tables) {
-    genACPITable(table, results);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/groups.cpp b/osquery/tables/system/linux/groups.cpp
deleted file mode 100644 (file)
index b537db9..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <set>
-#include <mutex>
-
-#include <grp.h>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-std::mutex grpEnumerationMutex;
-
-QueryData genGroups(QueryContext &context) {
-  std::lock_guard<std::mutex> lock(grpEnumerationMutex);
-  QueryData results;
-  struct group *grp = nullptr;
-  std::set<long> groups_in;
-
-  setgrent();
-  while ((grp = getgrent()) != nullptr) {
-    if (std::find(groups_in.begin(), groups_in.end(), grp->gr_gid) ==
-        groups_in.end()) {
-      Row r;
-      r["gid"] = INTEGER(grp->gr_gid);
-      r["gid_signed"] = INTEGER((int32_t) grp->gr_gid);
-      r["groupname"] = TEXT(grp->gr_name);
-      results.push_back(r);
-      groups_in.insert(grp->gr_gid);
-    }
-  }
-  endgrent();
-  groups_in.clear();
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/kernel_info.cpp b/osquery/tables/system/linux/kernel_info.cpp
deleted file mode 100644 (file)
index d3cc2ff..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/algorithm/string/split.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/hash.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-const std::string kKernelArgumentsPath = "/proc/cmdline";
-const std::string kKernelSignaturePath = "/proc/version";
-
-QueryData genKernelInfo(QueryContext& context) {
-  QueryData results;
-  Row r;
-
-  if (pathExists(kKernelArgumentsPath).ok()) {
-    std::string arguments_line;
-    // Grab the whole arguments string from proc.
-    if (readFile(kKernelArgumentsPath, arguments_line).ok()) {
-      auto arguments = split(arguments_line, " ");
-      std::string additional_arguments;
-
-      // Iterate over each space-tokenized argument.
-      for (const auto& argument : arguments) {
-        if (argument.substr(0, 11) == "BOOT_IMAGE=") {
-          r["path"] = argument.substr(11);
-        } else if (argument.substr(0, 5) == "root=") {
-          r["device"] = argument.substr(5);
-        } else {
-          if (additional_arguments.size() > 0) {
-            additional_arguments += " ";
-          }
-          additional_arguments += argument;
-        }
-      }
-      r["arguments"] = additional_arguments;
-    }
-  } else {
-    VLOG(1) << "Cannot find kernel arguments file: " << kKernelArgumentsPath;
-  }
-
-  if (pathExists(kKernelSignaturePath).ok()) {
-    std::string signature;
-
-    // The signature includes the kernel version, build data, buildhost,
-    // GCC version used, and possibly build date.
-    if (readFile(kKernelSignaturePath, signature).ok()) {
-      auto details = split(signature, " ");
-      if (details.size() > 2 && details[1] == "version") {
-        r["version"] = details[2];
-      }
-    }
-  } else {
-    VLOG(1) << "Cannot find kernel signature file: " << kKernelSignaturePath;
-  }
-
-  // Using the path of the boot image, attempt to calculate a hash.
-  if (r.count("path") > 0) {
-    r["md5"] = hashFromFile(HASH_TYPE_MD5, r.at("path"));
-  }
-
-  results.push_back(r);
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/kernel_modules.cpp b/osquery/tables/system/linux/kernel_modules.cpp
deleted file mode 100644 (file)
index f3fadad..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <fstream>
-
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-const std::string kKernelModulePath = "/proc/modules";
-
-QueryData genKernelModules(QueryContext& context) {
-  QueryData results;
-
-  if (!pathExists(kKernelModulePath).ok()) {
-    VLOG(1) << "Cannot find kernel modules proc file: " << kKernelModulePath;
-    return {};
-  }
-
-  // Cannot seek to the end of procfs.
-  std::ifstream fd(kKernelModulePath, std::ios::in);
-  if (!fd) {
-    VLOG(1) << "Cannot read kernel modules from: " << kKernelModulePath;
-    return {};
-  }
-
-  auto module_info = std::string(std::istreambuf_iterator<char>(fd),
-                                 std::istreambuf_iterator<char>());
-
-  for (const auto& module : split(module_info, "\n")) {
-    Row r;
-    auto module_info = split(module, " ");
-    if (module_info.size() < 6) {
-      // Interesting error case, this module line is not well formed.
-      continue;
-    }
-
-    for (auto& detail : module_info) {
-      // Clean up the delimiters
-      boost::trim(detail);
-      if (detail.back() == ',') {
-        detail.pop_back();
-      }
-    }
-
-    r["name"] = module_info[0];
-    r["size"] = module_info[1];
-    r["used_by"] = module_info[3];
-    r["status"] = module_info[4];
-    r["address"] = module_info[5];
-    results.push_back(r);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/memory_map.cpp b/osquery/tables/system/linux/memory_map.cpp
deleted file mode 100644 (file)
index 77dfe58..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/algorithm/string.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-namespace tables {
-
-const std::string kMemoryMapLocation = "/sys/firmware/memmap";
-
-QueryData genMemoryMap(QueryContext& context) {
-  QueryData results;
-
-  // Linux memory map is exposed in /sys.
-  std::vector<std::string> regions;
-  auto status = listDirectoriesInDirectory(kMemoryMapLocation, regions);
-  if (!status.ok()) {
-    return {};
-  }
-
-  for (const auto& index : regions) {
-    fs::path index_path(index);
-    Row r;
-    r["region"] = index_path.filename().string();
-
-    // The type is a textual description
-    std::string content;
-    readFile(index_path / "type", content);
-    boost::trim(content);
-    r["type"] = content;
-
-    // Keep these in 0xFFFF (hex) form.
-    readFile(index_path / "start", content);
-    boost::trim(content);
-    r["start"] = content;
-
-    readFile(index_path / "end", content);
-    boost::trim(content);
-    r["end"] = content;
-
-    results.push_back(r);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/model_specific_register.cpp b/osquery/tables/system/linux/model_specific_register.cpp
deleted file mode 100644 (file)
index 126d615..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <dirent.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#define MSR_FILENAME_BUFFER_SIZE 32
-
-#define NO_MASK 0xFFFFFFFFFFFFFFFFULL
-
-// Defines taken from uapi/asm/msr-index.h from the linux kernel.
-#define MSR_PLATFORM_INFO 0x000000ce
-
-#define MSR_IA32_FEATURE_CONTROL 0x0000003a
-
-#define MSR_IA32_PERF_STATUS 0x00000198
-#define MSR_IA32_PERF_CTL 0x00000199
-#define INTEL_PERF_CTL_MASK 0xffff
-
-#define MSR_IA32_MISC_ENABLE 0x000001a0
-
-#define MSR_TURBO_RATIO_LIMIT 0x000001ad
-
-#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT 38
-#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE \
-  (1ULL << MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT)
-
-// Run Time Average Power Limiting (RAPL).
-#define MSR_RAPL_POWER_UNIT 0x00000606
-#define MSR_PKG_ENERGY_STATUS 0x00000611
-#define MSR_PKG_POWER_LIMIT 0x00000610
-
-namespace osquery {
-namespace tables {
-
-// These are the entries to retrieve from the model specific register
-struct msr_record_t {
-  const char *name;
-  const off_t offset;
-  const uint64_t mask;
-  const int is_flag;
-};
-const static msr_record_t fields[] = {
-    {.name = "turbo_disabled",
-     .offset = MSR_IA32_MISC_ENABLE,
-     .mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE,
-     .is_flag = true},
-    {.name = "turbo_ratio_limit",
-     .offset = MSR_TURBO_RATIO_LIMIT,
-     .mask = NO_MASK,
-     .is_flag = false},
-    {.name = "platform_info",
-     .offset = MSR_PLATFORM_INFO,
-     .mask = NO_MASK,
-     .is_flag = false},
-    {.name = "perf_status",
-     .offset = MSR_IA32_PERF_STATUS,
-     .mask = NO_MASK,
-     .is_flag = false},
-    {.name = "perf_ctl",
-     .offset = MSR_IA32_PERF_CTL,
-     .mask = INTEL_PERF_CTL_MASK,
-     .is_flag = false},
-    {.name = "feature_control",
-     .offset = MSR_IA32_FEATURE_CONTROL,
-     .mask = NO_MASK,
-     .is_flag = false},
-    {.name = "rapl_power_limit",
-     .offset = MSR_PKG_POWER_LIMIT,
-     .mask = NO_MASK,
-     .is_flag = false},
-    {.name = "rapl_energy_status",
-     .offset = MSR_PKG_ENERGY_STATUS,
-     .mask = NO_MASK,
-     .is_flag = false},
-    {.name = "rapl_power_units",
-     .offset = MSR_RAPL_POWER_UNIT,
-     .mask = NO_MASK,
-     .is_flag = false}};
-
-void getModelSpecificRegisterData(QueryData &results, int cpu_number) {
-  auto msr_filename =
-    std::string("/dev/cpu/") + std::to_string(cpu_number) + "/msr";
-
-  int fd = open(msr_filename.c_str(), O_RDONLY);
-  if (fd < 0) {
-    int err = errno;
-    TLOG << "Could not open msr file " << msr_filename
-         << " check the msr kernel module is enabled.";
-    if (err == EACCES) {
-      LOG(WARNING) << "Could not access msr device.  Run osquery as root.";
-    }
-    return;
-  }
-
-  Row r;
-  r["processor_number"] = BIGINT(cpu_number);
-  for (const msr_record_t &field : fields) {
-    uint64_t output;
-    ssize_t size = pread(fd, &output, sizeof(uint64_t), field.offset);
-    if (size != sizeof(uint64_t)) {
-      // Processor does not have a record of this type.
-      continue;
-    }
-    if (field.is_flag) {
-      r[field.name] = BIGINT((output & field.mask) ? 1 : 0);
-    } else {
-      r[field.name] = BIGINT(output & field.mask);
-    }
-  }
-  results.push_back(r);
-  close(fd);
-
-  return;
-}
-
-// Filter only for filenames starting with a digit.
-int msrScandirFilter(const struct dirent *entry) {
-  if (isdigit(entry->d_name[0])) {
-    return 1;
-  } else {
-    return 0;
-  }
-}
-
-QueryData genModelSpecificRegister(QueryContext &context) {
-  QueryData results;
-
-  struct dirent **entries = nullptr;
-  int num_entries = scandir("/dev/cpu", &entries, msrScandirFilter, 0);
-  if (num_entries < 1) {
-    LOG(WARNING) << "No msr information check msr kernel module is enabled.";
-    return results;
-  }
-  while (num_entries--) {
-    getModelSpecificRegisterData(results, atoi(entries[num_entries]->d_name));
-    free(entries[num_entries]);
-  }
-  free(entries);
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/mounts.cpp b/osquery/tables/system/linux/mounts.cpp
deleted file mode 100644 (file)
index d4b804c..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <mntent.h>
-#include <sys/vfs.h>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData genMounts(QueryContext &context) {
-  QueryData results;
-  FILE *mounts;
-  struct mntent *ent;
-  char real_path[PATH_MAX];
-  struct statfs st;
-
-  if ((mounts = setmntent("/proc/mounts", "r"))) {
-    while ((ent = getmntent(mounts))) {
-      Row r;
-
-      r["device"] = std::string(ent->mnt_fsname);
-      r["device_alias"] = std::string(
-          realpath(ent->mnt_fsname, real_path) ? real_path : ent->mnt_fsname);
-      r["path"] = std::string(ent->mnt_dir);
-      r["type"] = std::string(ent->mnt_type);
-      r["flags"] = std::string(ent->mnt_opts);
-      if (!statfs(ent->mnt_dir, &st)) {
-        r["blocks_size"] = BIGINT(st.f_bsize);
-        r["blocks"] = BIGINT(st.f_blocks);
-        r["blocks_free"] = BIGINT(st.f_bfree);
-        r["blocks_available"] = BIGINT(st.f_bavail);
-        r["inodes"] = BIGINT(st.f_files);
-        r["inodes_free"] = BIGINT(st.f_ffree);
-      }
-
-      results.push_back(r);
-    }
-    endmntent(mounts);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/os_version.cpp b/osquery/tables/system/linux/os_version.cpp
deleted file mode 100644 (file)
index e5ecad1..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <string>
-
-#include <boost/regex.hpp>
-#include <boost/xpressive/xpressive.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/sql.h>
-#include <osquery/tables.h>
-
-namespace xp = boost::xpressive;
-
-namespace osquery {
-namespace tables {
-
-const std::string kLinuxOSRelease = "/etc/redhat-release";
-const std::string kLinuxOSRegex =
-    "(?P<name>\\w+) .* "
-    "(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)[\\.]{0,1}(?P<patch>[0-9]+).*";
-
-QueryData genOSVersion(QueryContext& context) {
-  std::string content;
-  if (!readFile(kLinuxOSRelease, content).ok()) {
-    return {};
-  }
-
-  Row r;
-  auto rx = xp::sregex::compile(kLinuxOSRegex);
-  xp::smatch matches;
-  for (const auto& line : osquery::split(content, "\n")) {
-    if (xp::regex_search(line, matches, rx)) {
-      r["major"] = INTEGER(matches["major"]);
-      r["minor"] = INTEGER(matches["minor"]);
-      r["patch"] =
-          (matches["patch"].length() > 0) ? INTEGER(matches["patch"]) : "0";
-      r["name"] = matches["name"];
-      break;
-    }
-  }
-
-  // No build name.
-  r["build"] = "";
-  return {r};
-}
-}
-}
diff --git a/osquery/tables/system/linux/pci_devices.cpp b/osquery/tables/system/linux/pci_devices.cpp
deleted file mode 100644 (file)
index 8a07587..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/events/linux/udev.h"
-
-namespace osquery {
-namespace tables {
-
-const std::string kPCIKeySlot = "PCI_SLOT_NAME";
-const std::string kPCIKeyClass = "ID_PCI_CLASS_FROM_DATABASE";
-const std::string kPCIKeyVendor = "ID_VENDOR_FROM_DATABASE";
-const std::string kPCIKeyModel = "ID_MODEL_FROM_DATABASE";
-const std::string kPCIKeyID = "PCI_ID";
-const std::string kPCIKeyDriver = "DRIVER";
-
-QueryData genPCIDevices(QueryContext &context) {
-  QueryData results;
-
-  auto udev_handle = udev_new();
-  if (udev_handle == nullptr) {
-    VLOG(1) << "Could not get udev handle.";
-    return results;
-  }
-
-  // Perform enumeration/search.
-  auto enumerate = udev_enumerate_new(udev_handle);
-  udev_enumerate_add_match_subsystem(enumerate, "pci");
-  udev_enumerate_scan_devices(enumerate);
-
-  // Get list entries and iterate over entries.
-  struct udev_list_entry *device_entries, *entry;
-  device_entries = udev_enumerate_get_list_entry(enumerate);
-
-  udev_list_entry_foreach(entry, device_entries) {
-    const char *path = udev_list_entry_get_name(entry);
-    auto device = udev_device_new_from_syspath(udev_handle, path);
-
-    Row r;
-    r["pci_slot"] = UdevEventPublisher::getValue(device, kPCIKeySlot);
-    r["pci_class"] = UdevEventPublisher::getValue(device, kPCIKeyClass);
-    r["driver"] = UdevEventPublisher::getValue(device, kPCIKeyDriver);
-    r["vendor"] = UdevEventPublisher::getValue(device, kPCIKeyVendor);
-    r["model"] = UdevEventPublisher::getValue(device, kPCIKeyModel);
-
-    // VENDOR:MODEL ID is in the form of HHHH:HHHH.
-    std::vector<std::string> ids;
-    auto device_id = UdevEventPublisher::getValue(device, kPCIKeyID);
-    boost::split(ids, device_id, boost::is_any_of(":"));
-    if (ids.size() == 2) {
-      r["vendor_id"] = ids[0];
-      r["model_id"] = ids[1];
-    }
-
-    // Set invalid vendor/model IDs to 0.
-    if (r["vendor_id"].size() == 0) {
-      r["vendor_id"] = "0";
-    }
-
-    if (r["model_id"].size() == 0) {
-      r["model_id"] = "0";
-    }
-
-    results.push_back(r);
-    udev_device_unref(device);
-  }
-
-  // Drop references to udev structs.
-  udev_enumerate_unref(enumerate);
-  udev_unref(udev_handle);
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/process_open_files.cpp b/osquery/tables/system/linux/process_open_files.cpp
deleted file mode 100644 (file)
index 8fd7380..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-
-namespace osquery {
-namespace tables {
-
-void genDescriptors(const std::string& process,
-                    const std::map<std::string, std::string>& descriptors,
-                    QueryData& results) {
-  for (const auto& fd : descriptors) {
-    if (fd.second.find("socket:") != std::string::npos ||
-        fd.second.find("anon_inode:") != std::string::npos ||
-        fd.second.find("pipe:") != std::string::npos) {
-      // This is NOT a vnode/file descriptor.
-      continue;
-    }
-
-    Row r;
-    r["pid"] = process;
-    r["fd"] = fd.first;
-    r["path"] = fd.second;
-    results.push_back(r);
-  }
-
-  return;
-}
-
-QueryData genOpenFiles(QueryContext& context) {
-  QueryData results;
-
-  std::set<std::string> pids;
-  if (context.constraints["pid"].exists(EQUALS)) {
-    pids = context.constraints["pid"].getAll(EQUALS);
-  } else {
-    osquery::procProcesses(pids);
-  }
-
-  for (const auto& process : pids) {
-    std::map<std::string, std::string> descriptors;
-    if (osquery::procDescriptors(process, descriptors).ok()) {
-      genDescriptors(process, descriptors, results);
-    }
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/processes.cpp b/osquery/tables/system/linux/processes.cpp
deleted file mode 100644 (file)
index 23641ec..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <string>
-#include <map>
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-
-namespace osquery {
-namespace tables {
-
-inline std::string getProcAttr(const std::string& attr, const std::string& pid) {
-  return "/proc/" + pid + "/" + attr;
-}
-
-inline std::string readProcCMDLine(const std::string& pid) {
-  auto attr = getProcAttr("cmdline", pid);
-
-  std::string content;
-  readFile(attr, content);
-  // Remove \0 delimiters.
-  std::replace_if(content.begin(),
-                  content.end(),
-                  [](const char& c) { return c == 0; },
-                  ' ');
-  // Remove trailing delimiter.
-  boost::algorithm::trim(content);
-  return content;
-}
-
-inline std::string readProcLink(const std::string& attr, const std::string& pid) {
-  // The exe is a symlink to the binary on-disk.
-  auto attr_path = getProcAttr(attr, pid);
-
-  std::string result;
-  char link_path[PATH_MAX] = {0};
-  auto bytes = readlink(attr_path.c_str(), link_path, sizeof(link_path) - 1);
-  if (bytes >= 0) {
-    result = std::string(link_path);
-  }
-
-  return result;
-}
-
-std::set<std::string> getProcList(const QueryContext& context) {
-  std::set<std::string> pidlist;
-  if (context.constraints.count("pid") > 0 &&
-      context.constraints.at("pid").exists(EQUALS)) {
-    for (const auto& pid : context.constraints.at("pid").getAll(EQUALS)) {
-      if (isDirectory("/proc/" + pid)) {
-        pidlist.insert(pid);
-      }
-    }
-  } else {
-    osquery::procProcesses(pidlist);
-  }
-
-  return pidlist;
-}
-
-void genProcessEnvironment(const std::string& pid, QueryData& results) {
-  auto attr = getProcAttr("environ", pid);
-
-  std::string content;
-  readFile(attr, content);
-  const char* variable = content.c_str();
-
-  // Stop at the end of nul-delimited string content.
-  while (*variable > 0) {
-    auto buf = std::string(variable);
-    size_t idx = buf.find_first_of("=");
-
-    Row r;
-    r["pid"] = pid;
-    r["key"] = buf.substr(0, idx);
-    r["value"] = buf.substr(idx + 1);
-    results.push_back(r);
-    variable += buf.size() + 1;
-  }
-}
-
-void genProcessMap(const std::string& pid, QueryData& results) {
-  auto map = getProcAttr("maps", pid);
-
-  std::string content;
-  readFile(map, content);
-  for (auto& line : osquery::split(content, "\n")) {
-    auto fields = osquery::split(line, " ");
-
-    Row r;
-    r["pid"] = pid;
-
-    // If can't read address, not sure.
-    if (fields.size() < 5) {
-      continue;
-    }
-
-    if (fields[0].size() > 0) {
-      auto addresses = osquery::split(fields[0], "-");
-      r["start"] = "0x" + addresses[0];
-      r["end"] = "0x" + addresses[1];
-    }
-
-    r["permissions"] = fields[1];
-    r["offset"] = BIGINT(std::stoll(fields[2], nullptr, 16));
-    r["device"] = fields[3];
-    r["inode"] = fields[4];
-
-    // Path name must be trimmed.
-    if (fields.size() > 5) {
-      boost::trim(fields[5]);
-      r["path"] = fields[5];
-    }
-
-    // BSS with name in pathname.
-    r["pseudo"] = (fields[4] == "0" && r["path"].size() > 0) ? "1" : "0";
-    results.push_back(r);
-  }
-}
-
-struct SimpleProcStat {
-  // Output from string parsing /proc/<pid>/status.
-  std::string parent; // PPid:
-  std::string name; // Name:
-  std::string real_uid; // Uid: * - - -
-  std::string real_gid; // Gid: * - - -
-  std::string effective_uid; // Uid: - * - -
-  std::string effective_gid; // Gid: - * - -
-
-  std::string resident_size; // VmRSS:
-  std::string phys_footprint;  // VmSize:
-
-  // Output from sring parsing /proc/<pid>/stat.
-  std::string user_time;
-  std::string system_time;
-  std::string start_time;
-};
-
-SimpleProcStat getProcStat(const std::string& pid) {
-  SimpleProcStat stat;
-  std::string content;
-  if (readFile(getProcAttr("stat", pid), content).ok()) {
-    auto detail_start = content.find_last_of(")");
-    // Start parsing stats from ") <MODE>..."
-    auto details = osquery::split(content.substr(detail_start + 2), " ");
-    stat.parent = details.at(1);
-    stat.user_time = details.at(11);
-    stat.system_time = details.at(12);
-    stat.start_time = details.at(19);
-  }
-
-  if (readFile(getProcAttr("status", pid), content).ok()) {
-    for (const auto& line : osquery::split(content, "\n")) {
-      // Status lines are formatted: Key: Value....\n.
-      auto detail = osquery::split(line, ":", 1);
-      if (detail.size() != 2) {
-        continue;
-      }
-
-      // There are specific fields from each detail.
-      if (detail.at(0) == "Name") {
-        stat.name = detail.at(1);
-      } else if (detail.at(0) == "VmRSS") {
-        detail[1].erase(detail.at(1).end() - 3, detail.at(1).end());
-        // Memory is reported in kB.
-        stat.resident_size = detail.at(1) + "000";
-      } else if (detail.at(0) == "VmSize") {
-        detail[1].erase(detail.at(1).end() - 3, detail.at(1).end());
-        // Memory is reported in kB.
-        stat.phys_footprint = detail.at(1) + "000";
-      } else if (detail.at(0) == "Gid") {
-        // Format is: R E - -
-        auto gid_detail = osquery::split(detail.at(1), "\t");
-        if (gid_detail.size() == 4) {
-          stat.real_gid = gid_detail.at(0);
-          stat.effective_gid = gid_detail.at(1);
-        }
-      } else if (detail.at(0) == "Uid") {
-        auto uid_detail = osquery::split(detail.at(1), "\t");
-        if (uid_detail.size() == 4) {
-          stat.real_uid = uid_detail.at(0);
-          stat.effective_uid = uid_detail.at(1);
-        }
-      }
-    }
-  }
-
-  return stat;
-}
-
-void genProcess(const std::string& pid, QueryData& results) {
-  // Parse the process stat and status.
-  auto proc_stat = getProcStat(pid);
-
-  Row r;
-  r["pid"] = pid;
-  r["parent"] = proc_stat.parent;
-  r["path"] = readProcLink("exe", pid);
-  r["name"] = proc_stat.name;
-
-  // Read/parse cmdline arguments.
-  r["cmdline"] = readProcCMDLine(pid);
-  r["cwd"] = readProcLink("cwd", pid);
-  r["root"] = readProcLink("root", pid);
-
-  r["uid"] = proc_stat.real_uid;
-  r["euid"] = proc_stat.effective_uid;
-  r["gid"] = proc_stat.real_gid;
-  r["egid"] = proc_stat.effective_gid;
-
-  // If the path of the executable that started the process is available and
-  // the path exists on disk, set on_disk to 1. If the path is not
-  // available, set on_disk to -1. If, and only if, the path of the
-  // executable is available and the file does NOT exist on disk, set on_disk
-  // to 0.
-  r["on_disk"] = osquery::pathExists(r["path"]).toString();
-
-  // size/memory information
-  r["wired_size"] = "0"; // No support for unpagable counters in linux.
-  r["resident_size"] = proc_stat.resident_size;
-  r["phys_footprint"] = proc_stat.phys_footprint;
-
-  // time information
-  r["user_time"] = proc_stat.user_time;
-  r["system_time"] = proc_stat.system_time;
-  r["start_time"] = proc_stat.start_time;
-
-  results.push_back(r);
-}
-
-QueryData genProcesses(QueryContext& context) {
-  QueryData results;
-
-  auto pidlist = getProcList(context);
-  for (const auto& pid : pidlist) {
-    genProcess(pid, results);
-  }
-
-  return results;
-}
-
-QueryData genProcessEnvs(QueryContext& context) {
-  QueryData results;
-
-  auto pidlist = getProcList(context);
-  for (const auto& pid : pidlist) {
-    genProcessEnvironment(pid, results);
-  }
-
-  return results;
-}
-
-QueryData genProcessMemoryMap(QueryContext& context) {
-  QueryData results;
-
-  auto pidlist = getProcList(context);
-  for (const auto& pid : pidlist) {
-    genProcessMap(pid, results);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/shared_memory.cpp b/osquery/tables/system/linux/shared_memory.cpp
deleted file mode 100644 (file)
index 441ef36..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sys/shm.h>
-#include <pwd.h>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-struct shm_info {
-  int used_ids;
-  unsigned long shm_tot;
-  unsigned long shm_rss;
-  unsigned long shm_swp;
-  unsigned long swap_attempts;
-  unsigned long swap_successes;
-} __attribute__((unused));
-
-QueryData genSharedMemory(QueryContext &context) {
-  QueryData results;
-
-  // Use shared memory control (shmctl) to get the max SHMID.
-  struct shm_info shm_info;
-  int maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
-  if (maxid < 0) {
-    VLOG(1) << "Linux kernel not configured for shared memory";
-    return {};
-  }
-
-  // Use a static pointer to access IPC permissions structure.
-  struct shmid_ds shmseg;
-  struct ipc_perm *ipcp = &shmseg.shm_perm;
-
-  // Then iterate each shared memory ID up to the max.
-  for (int id = 0; id <= maxid; id++) {
-    int shmid = shmctl(id, SHM_STAT, &shmseg);
-    if (shmid < 0) {
-      continue;
-    }
-
-    Row r;
-    r["shmid"] = INTEGER(shmid);
-
-    struct passwd *pw = getpwuid(shmseg.shm_perm.uid);
-    if (pw != nullptr) {
-      r["owner_uid"] = BIGINT(pw->pw_uid);
-    }
-
-    pw = getpwuid(shmseg.shm_perm.cuid);
-    if (pw != nullptr) {
-      r["creator_uid"] = BIGINT(pw->pw_uid);
-    }
-
-    // Accessor, creator pids.
-    r["pid"] = BIGINT(shmseg.shm_lpid);
-    r["creator_pid"] = BIGINT(shmseg.shm_cpid);
-
-    // Access, detached, creator times
-    r["atime"] = BIGINT(shmseg.shm_atime);
-    r["dtime"] = BIGINT(shmseg.shm_dtime);
-    r["ctime"] = BIGINT(shmseg.shm_ctime);
-
-    r["permissions"] = lsperms(ipcp->mode);
-    r["size"] = BIGINT(shmseg.shm_segsz);
-    r["attached"] = INTEGER(shmseg.shm_nattch);
-    r["status"] = (ipcp->mode & SHM_DEST) ? "dest" : "";
-    r["locked"] = (ipcp->mode & SHM_LOCKED) ? "1" : "0";
-
-    results.push_back(r);
-  }
-
-  return results;
-}
-}
-}
\ No newline at end of file
diff --git a/osquery/tables/system/linux/smbios_tables.cpp b/osquery/tables/system/linux/smbios_tables.cpp
deleted file mode 100644 (file)
index 890ec2f..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/tables/system/smbios_utils.h"
-
-namespace osquery {
-namespace tables {
-
-#define kLinuxSMBIOSRawAddress_ 0xF0000
-#define kLinuxSMBIOSRawLength_ 0x10000
-
-const std::string kLinuxEFISystabPath = "/sys/firmware/efi/systab";
-const std::string kLinuxLegacyEFISystabPath = "/proc/efi/systab";
-
-void genSMBIOSFromDMI(size_t base, size_t length, QueryData& results) {
-  // Linux will expose the SMBIOS/DMI entry point structures, which contain
-  // a member variable with the DMI tables start address and size.
-  // This applies to both the EFI-variable and physical memory search.
-  uint8_t* data;
-  auto status = osquery::readRawMem(base, length, (void**)&data);
-  if (!status.ok()) {
-    VLOG(1) << "Could not read DMI tables memory";
-    return;
-  }
-
-  // Attempt to parse tables from allocated data.
-  genSMBIOSTables(data, length, results);
-  free(data);
-}
-
-void genEFISystabTables(QueryData& results) {
-  // Not yet supported.
-  return;
-}
-
-void genRawSMBIOSTables(QueryData& results) {
-  uint8_t* data;
-  auto status = osquery::readRawMem(
-      kLinuxSMBIOSRawAddress_, kLinuxSMBIOSRawLength_, (void**)&data);
-  if (!status.ok()) {
-    VLOG(1) << "Could not read SMBIOS memory";
-    return;
-  }
-
-  // Search for the SMBIOS/DMI tables magic header string.
-  size_t offset;
-  for (offset = 0; offset <= 0xFFF0; offset += 16) {
-    // Could look for "_SM_" for the SMBIOS header, but the DMI header exists
-    // in both SMBIOS and the legacy DMI spec.
-    if (memcmp(data + offset, "_DMI_", 5) == 0) {
-      auto dmi_data = (DMIEntryPoint*)(data + offset);
-      genSMBIOSFromDMI(dmi_data->tableAddress, dmi_data->tableLength, results);
-    }
-  }
-
-  free(data);
-}
-
-QueryData genSMBIOSTables(QueryContext& context) {
-  QueryData results;
-
-  if (osquery::isReadable(kLinuxEFISystabPath).ok() ||
-      osquery::isReadable(kLinuxLegacyEFISystabPath).ok()) {
-    genEFISystabTables(results);
-  } else {
-    genRawSMBIOSTables(results);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/sysctl_utils.cpp b/osquery/tables/system/linux/sysctl_utils.cpp
deleted file mode 100644 (file)
index a776418..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sys/sysctl.h>
-
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/tables.h>
-
-#include "osquery/tables/system/sysctl_utils.h"
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-namespace tables {
-
-const std::string kSystemControlPath = "/proc/sys/";
-
-void genControlInfo(const std::string& mib_path, QueryData& results,
-                    const std::map<std::string, std::string>& config) {
-  if (isDirectory(mib_path).ok()) {
-    // Iterate through the subitems and items.
-    std::vector<std::string> items;
-    if (listDirectoriesInDirectory(mib_path, items).ok()) {
-      for (const auto& item : items) {
-        genControlInfo(item, results, config);
-      }
-    }
-
-    if (listFilesInDirectory(mib_path, items).ok()) {
-      for (const auto& item : items) {
-        genControlInfo(item, results, config);
-      }
-    }
-    return;
-  }
-
-  // This is a file (leaf-control).
-  Row r;
-  r["name"] = mib_path.substr(kSystemControlPath.size());
-
-  std::replace(r["name"].begin(), r["name"].end(), '/', '.');
-  // No known way to convert name MIB to int array.
-  r["subsystem"] = osquery::split(r.at("name"), ".")[0];
-
-  if (isReadable(mib_path).ok()) {
-    std::string content;
-    readFile(mib_path, content);
-    boost::trim(content);
-    r["current_value"] = content;
-  }
-
-  if (config.count(r.at("name")) > 0) {
-    r["config_value"] = config.at(r.at("name"));
-  }
-  r["type"] = "string";
-  results.push_back(r);
-}
-
-void genControlInfo(int* oid,
-                    size_t oid_size,
-                    QueryData& results,
-                    const std::map<std::string, std::string>& config) {
-  // Get control size
-  size_t response_size = CTL_MAX_VALUE;
-  char response[CTL_MAX_VALUE + 1] = {0};
-    if (sysctl(oid, oid_size, response, &response_size, 0, 0) != 0) {
-      // Cannot request MIB data.
-      return;
-    }
-
-    // Data is output, but no way to determine type (long, int, string, struct).
-  Row r;
-  r["oid"] = stringFromMIB(oid, oid_size);
-  r["current_value"] = std::string(response);
-  r["type"] = "string";
-  results.push_back(r);
-}
-
-void genAllControls(QueryData& results,
-                    const std::map<std::string, std::string>& config,
-                    const std::string& subsystem) {
-  // Linux sysctl subsystems are directories in /proc
-  std::vector<std::string> subsystems;
-  if (!listDirectoriesInDirectory("/proc/sys", subsystems).ok()) {
-    return;
-  }
-
-  for (const auto& sub : subsystems) {
-    if (subsystem.size() != 0 &&
-        fs::path(sub).filename().string() != subsystem) {
-      // Request is limiting subsystem.
-      continue;
-    } 
-    genControlInfo(sub, results, config);
-  }
-}
-
-void genControlInfoFromName(const std::string& name, QueryData& results,
-                    const std::map<std::string, std::string>& config) {
-  // Convert '.'-tokenized name to path.
-  std::string name_path = name;
-  std::replace(name_path.begin(), name_path.end(), '.', '/');
-  auto mib_path = fs::path(kSystemControlPath) / name_path;
-
-  genControlInfo(mib_path.string(), results, config);
-}
-}
-}
diff --git a/osquery/tables/system/linux/usb_devices.cpp b/osquery/tables/system/linux/usb_devices.cpp
deleted file mode 100644 (file)
index 476e87b..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/core.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include "osquery/events/linux/udev.h"
-
-namespace osquery {
-namespace tables {
-
-const std::string kUSBKeyVendorID = "ID_VENDOR_ID";
-const std::string kUSBKeyVendor = "ID_VENDOR_FROM_DATABASE";
-const std::string kUSBKeyModelID = "ID_MODEL_ID";
-const std::string kUSBKeyModel = "ID_MODEL_FROM_DATABASE";
-const std::string kUSBKeyDriver = "ID_USB_DRIVER";
-const std::string kUSBKeySubsystem = "SUBSYSTEM";
-const std::string kUSBKeySerial = "ID_SERIAL_SHORT";
-const std::string kUSBKeyAddress = "BUSNUM";
-const std::string kUSBKeyPort = "DEVNUM";
-
-QueryData genUSBDevices(QueryContext &context) {
-  QueryData results;
-
-  auto udev_handle = udev_new();
-  if (udev_handle == nullptr) {
-    VLOG(1) << "Could not get udev handle.";
-    return results;
-  }
-
-  // Perform enumeration/search.
-  auto enumerate = udev_enumerate_new(udev_handle);
-  udev_enumerate_add_match_subsystem(enumerate, "usb");
-  udev_enumerate_scan_devices(enumerate);
-
-  // Get list entries and iterate over entries.
-  struct udev_list_entry *device_entries, *entry;
-  device_entries = udev_enumerate_get_list_entry(enumerate);
-
-  udev_list_entry_foreach(entry, device_entries) {
-    const char *path = udev_list_entry_get_name(entry);
-    auto device = udev_device_new_from_syspath(udev_handle, path);
-
-    Row r;
-    // r["driver"] = UdevEventPublisher::getValue(device, kUSBKeyDriver);
-    r["vendor"] = UdevEventPublisher::getValue(device, kUSBKeyVendor);
-    r["model"] = UdevEventPublisher::getValue(device, kUSBKeyModel);
-
-    // USB-specific vendor/model ID properties.
-    r["model_id"] = UdevEventPublisher::getValue(device, kUSBKeyModelID);
-    r["vendor_id"] = UdevEventPublisher::getValue(device, kUSBKeyVendorID);
-    r["serial"] = UdevEventPublisher::getValue(device, kUSBKeySerial);
-
-    // Address/port accessors.
-    r["usb_address"] = UdevEventPublisher::getValue(device, kUSBKeyAddress);
-    r["usb_port"] = UdevEventPublisher::getValue(device, kUSBKeyPort);
-
-    // Removable detection.
-    auto removable = UdevEventPublisher::getAttr(device, "removable");
-    if (removable == "unknown") {
-      r["removable"] = "-1";
-    } else {
-      r["removable"] = "1";
-    }
-
-    if (r["usb_address"].size() > 0 && r["usb_port"].size() > 0) {
-      results.push_back(r);
-    }
-    udev_device_unref(device);
-  }
-
-  // Drop references to udev structs.
-  udev_enumerate_unref(enumerate);
-  udev_unref(udev_handle);
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/user_groups.cpp b/osquery/tables/system/linux/user_groups.cpp
deleted file mode 100644 (file)
index da8be00..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include "osquery/tables/system/user_groups.h"
-
-namespace osquery {
-namespace tables {
-
-extern std::mutex pwdEnumerationMutex;
-
-QueryData genUserGroups(QueryContext &context) {
-  QueryData results;
-  struct passwd *pwd = nullptr;
-
-  if (context.constraints["uid"].exists(EQUALS)) {
-    std::set<std::string> uids = context.constraints["uid"].getAll(EQUALS);
-    for (const auto &uid : uids) {
-      pwd = getpwuid(std::strtol(uid.c_str(), NULL, 10));
-      if (pwd != nullptr) {
-        user_t<uid_t, gid_t> user;
-        user.name = pwd->pw_name;
-        user.uid = pwd->pw_uid;
-        user.gid = pwd->pw_gid;
-        getGroupsForUser<uid_t, gid_t>(results, user);
-      }
-    }
-  } else {
-    std::lock_guard<std::mutex> lock(pwdEnumerationMutex);
-    std::set<gid_t> users_in;
-    while ((pwd = getpwent()) != nullptr) {
-      if (std::find(users_in.begin(), users_in.end(), pwd->pw_uid) ==
-          users_in.end()) {
-        user_t<uid_t, gid_t> user;
-        user.name = pwd->pw_name;
-        user.uid = pwd->pw_uid;
-        user.gid = pwd->pw_gid;
-        getGroupsForUser<uid_t, gid_t>(results, user);
-        users_in.insert(pwd->pw_uid);
-      }
-    }
-    endpwent();
-    users_in.clear();
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/linux/users.cpp b/osquery/tables/system/linux/users.cpp
deleted file mode 100644 (file)
index 6108987..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <set>
-#include <mutex>
-#include <vector>
-#include <string>
-
-#include <pwd.h>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-#include <osquery/status.h>
-#include <osquery/logger.h>
-
-namespace osquery {
-namespace tables {
-
-std::mutex pwdEnumerationMutex;
-
-QueryData genUsers(QueryContext& context) {
-  std::lock_guard<std::mutex> lock(pwdEnumerationMutex);
-  QueryData results;
-  struct passwd *pwd = nullptr;
-  std::set<long> users_in;
-
-  while ((pwd = getpwent()) != nullptr) {
-    if (std::find(users_in.begin(), users_in.end(), pwd->pw_uid) ==
-        users_in.end()) {
-      Row r;
-      r["uid"] = BIGINT(pwd->pw_uid);
-      r["gid"] = BIGINT(pwd->pw_gid);
-      r["uid_signed"] = BIGINT((int32_t) pwd->pw_uid);
-      r["gid_signed"] = BIGINT((int32_t) pwd->pw_gid);
-      r["username"] = TEXT(pwd->pw_name);
-      r["description"] = TEXT(pwd->pw_gecos);
-      r["directory"] = TEXT(pwd->pw_dir);
-      r["shell"] = TEXT(pwd->pw_shell);
-      results.push_back(r);
-      users_in.insert(pwd->pw_uid);
-    }
-  }
-  endpwent();
-  users_in.clear();
-
-  return results;
-}
-
-/// Example of update feature
-Status updateUsers(Row& row) {
-  for (auto& r : row)
-    LOG(ERROR) << "DEBUG: " << r.first << ", " << r.second;
-
-  return Status(0, "OK");
-}
-}
-}
diff --git a/osquery/tables/system/logged_in_users.cpp b/osquery/tables/system/logged_in_users.cpp
deleted file mode 100644 (file)
index ae24a33..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <mutex>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-
-#include <utmpx.h>
-
-namespace osquery {
-namespace tables {
-
-std::mutex utmpxEnumerationMutex;
-
-QueryData genLoggedInUsers(QueryContext& context) {
-  std::lock_guard<std::mutex> lock(utmpxEnumerationMutex);
-  QueryData results;
-  struct utmpx *entry = nullptr;
-
-  while ((entry = getutxent()) != nullptr) {
-    if (entry->ut_pid == 1) {
-      continue;
-    }
-    Row r;
-    r["user"] = TEXT(entry->ut_user);
-    r["tty"] = TEXT(entry->ut_line);
-    r["host"] = TEXT(entry->ut_host);
-    r["time"] = INTEGER(entry->ut_tv.tv_sec);
-    r["pid"] = INTEGER(entry->ut_pid);
-    results.push_back(r);
-  }
-  endutxent();
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/shell_history.cpp b/osquery/tables/system/shell_history.cpp
deleted file mode 100644 (file)
index 71681a6..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <string>
-#include <vector>
-
-#include <pwd.h>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/sql.h>
-
-namespace osquery {
-namespace tables {
-
-const std::vector<std::string> kShellHistoryFiles = {
-    ".bash_history", ".zsh_history", ".zhistory", ".history",
-};
-
-void genShellHistoryForUser(const std::string& username,
-                            const std::string& directory,
-                            QueryData& results) {
-  for (const auto& hfile : kShellHistoryFiles) {
-    boost::filesystem::path history_file = directory;
-    history_file /= hfile;
-
-    std::string history_content;
-    if (!readFile(history_file, history_content).ok()) {
-      // Cannot read a specific history file.
-      continue;
-    }
-
-    for (const auto& line : split(history_content, "\n")) {
-      Row r;
-      r["username"] = username;
-      r["command"] = line;
-      r["history_file"] = history_file.string();
-      results.push_back(r);
-    }
-  }
-}
-
-QueryData genShellHistory(QueryContext& context) {
-  QueryData results;
-
-  // Select only the home directory for this user.
-  QueryData users;
-  if (!context.constraints["username"].exists(EQUALS)) {
-    users =
-        SQL::selectAllFrom("users", "uid", EQUALS, std::to_string(getuid()));
-  } else {
-    auto usernames = context.constraints["username"].getAll(EQUALS);
-    for (const auto& username : usernames) {
-      // Use a predicated select all for each user.
-      auto user = SQL::selectAllFrom("users", "username", EQUALS, username);
-      users.insert(users.end(), user.begin(), user.end());
-    }
-  }
-
-  // Iterate over each user
-  for (const auto& row : users) {
-    if (row.count("username") > 0 && row.count("directory") > 0) {
-      genShellHistoryForUser(row.at("username"), row.at("directory"), results);
-    }
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/smbios_utils.cpp b/osquery/tables/system/smbios_utils.cpp
deleted file mode 100644 (file)
index 4e0ba4c..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/hash.h>
-
-#include "osquery/tables/system/smbios_utils.h"
-
-namespace osquery {
-namespace tables {
-
-const std::map<int, std::string> kSMBIOSTypeDescriptions = {
-    {0, "BIOS Information"},
-    {1, "System Information"},
-    {2, "Base Board or Module Information"},
-    {3, "System Enclosure or Chassis"},
-    {4, "Processor Information"},
-    {5, "Memory Controller Information"},
-    {6, "Memory Module Information"},
-    {7, "Cache Information"},
-    {8, "Port Connector Information"},
-    {9, "System Slots"},
-    {10, "On Board Devices Information"},
-    {11, "OEM Strings"},
-    {12, "System Configuration Options"},
-    {13, "BIOS Language Information"},
-    {14, "Group Associations"},
-    {15, "System Event Log"},
-    {16, "Physical Memory Array"},
-    {17, "Memory Device"},
-    {18, "32-bit Memory Error Information"},
-    {19, "Memory Array Mapped Address"},
-    {20, "Memory Device Mapped Address"},
-    {21, "Built-in Pointing Device"},
-    {22, "Portable Battery"},
-    {23, "System Reset"},
-    {24, "Hardware Security"},
-    {25, "System Power Controls"},
-    {26, "Voltage Probe"},
-    {27, "Cooling Device"},
-    {28, "Temperature Probe"},
-    {29, "Electrical Current Probe"},
-    {30, "Out-of-Band Remote Access"},
-    {31, "Boot Integrity Services"},
-    {32, "System Boot Information"},
-    {33, "64-bit Memory Error Information"},
-    {34, "Management Device"},
-    {35, "Management Device Component"},
-    {36, "Management Device Threshold Data"},
-    {37, "Memory Channel"},
-    {38, "IPMI Device Information"},
-    {39, "System Power Supply"},
-    {40, "Additional Information"},
-    {41, "Onboard Devices Extended Info"},
-    {126, "Inactive"},
-    {127, "End-of-Table"},
-    {130, "Memory SPD Data"},
-    {131, "OEM Processor Type"},
-    {132, "OEM Processor Bus Speed"},
-};
-
-void genSMBIOSTables(const uint8_t* tables, size_t length, QueryData& results) {
-  // Keep a pointer to the end of the SMBIOS data for comparison.
-  auto tables_end = tables + length;
-  auto table = tables;
-
-  // Iterate through table structures within SMBIOS data range.
-  size_t index = 0;
-  while (table + sizeof(SMBStructHeader) <= tables_end) {
-    auto header = (const SMBStructHeader*)table;
-    if (table + header->length > tables_end) {
-      // Invalid header, length must be within SMBIOS data range.
-      break;
-    }
-
-    Row r;
-    // The index is a supliment that keeps track of table order.
-    r["number"] = INTEGER(index++);
-    r["type"] = INTEGER((unsigned short)header->type);
-    if (kSMBIOSTypeDescriptions.count(header->type) > 0) {
-      r["description"] = kSMBIOSTypeDescriptions.at(header->type);
-    }
-
-    r["handle"] = BIGINT((unsigned long long)header->handle);
-    r["header_size"] = INTEGER((unsigned short)header->length);
-
-    // The SMBIOS structure may have unformatted, double-NULL delimited trailing
-    // data, which are usually strings.
-    auto next_table = table + header->length;
-    for (; next_table + sizeof(SMBStructHeader) <= tables_end; next_table++) {
-      if (next_table[0] == 0 && next_table[1] == 0) {
-        next_table += 2;
-        break;
-      }
-    }
-
-    auto table_length = next_table - table;
-    r["size"] = INTEGER(table_length);
-    r["md5"] = hashFromBuffer(HASH_TYPE_MD5, table, table_length);
-
-    table = next_table;
-    results.push_back(r);
-  }
-}
-}
-}
diff --git a/osquery/tables/system/smbios_utils.h b/osquery/tables/system/smbios_utils.h
deleted file mode 100644 (file)
index ff5eea3..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-typedef struct SMBStructHeader {
-  uint8_t type;
-  uint8_t length;
-  uint16_t handle;
-} __attribute__((packed)) SMBStructHeader;
-
-typedef struct DMIEntryPoint {
-  uint8_t anchor[5];
-  uint8_t checksum;
-  uint16_t tableLength;
-  uint32_t tableAddress;
-  uint16_t structureCount;
-  uint8_t bcdRevision;
-} __attribute__((packed)) DMIEntryPoint;
-
-extern const std::map<int, std::string> kSMBIOSTypeDescriptions;
-
-void genSMBIOSTables(const uint8_t* tables, size_t length, QueryData& results);
-}
-}
diff --git a/osquery/tables/system/suid_bin.cpp b/osquery/tables/system/suid_bin.cpp
deleted file mode 100644 (file)
index 43811e6..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <pwd.h>
-#include <grp.h>
-#include <sys/stat.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-namespace tables {
-
-std::vector<std::string> kBinarySearchPaths = {
-  "/bin",
-  "/sbin",
-  "/usr/bin",
-  "/usr/sbin",
-  "/usr/local/bin",
-  "/usr/local/sbin",
-  "/tmp",
-};
-
-Status genBin(const fs::path& path, int perms, QueryData& results) {
-  struct stat info;
-  // store user and group
-  if (stat(path.c_str(), &info) != 0) {
-    return Status(1, "stat failed");
-  }
-
-  // store path
-  Row r;
-  r["path"] = path.string();
-  struct passwd *pw = getpwuid(info.st_uid);
-  struct group *gr = getgrgid(info.st_gid);
-
-  // get user name + group
-  std::string user;
-  if (pw != nullptr) {
-    user = std::string(pw->pw_name);
-  } else {
-    user = boost::lexical_cast<std::string>(info.st_uid);
-  }
-
-  std::string group;
-  if (gr != nullptr) {
-    group = std::string(gr->gr_name);
-  } else {
-    group = boost::lexical_cast<std::string>(info.st_gid);
-  }
-
-  r["username"] = user;
-  r["groupname"] = group;
-
-  r["permissions"] = "";
-  if ((perms & 04000) == 04000) {
-    r["permissions"] += "S";
-  }
-
-  if ((perms & 02000) == 02000) {
-    r["permissions"] += "G";
-  }
-
-  results.push_back(r);
-  return Status(0, "OK");
-}
-
-bool isSuidBin(const fs::path& path, int perms) {
-  if (!fs::is_regular_file(path)) {
-    return false;
-  }
-
-  if ((perms & 04000) == 04000 || (perms & 02000) == 02000) {
-    return true;
-  }
-  return false;
-}
-
-void genSuidBinsFromPath(const std::string& path, QueryData& results) {
-  if (!pathExists(path).ok()) {
-    // Creating an iterator on a missing path will except.
-    return;
-  }
-
-  auto it = fs::recursive_directory_iterator(fs::path(path));
-  fs::recursive_directory_iterator end;
-  while (it != end) {
-    fs::path path = *it;
-    try {
-      // Do not traverse symlinked directories.
-      if (fs::is_directory(path) && fs::is_symlink(path)) {
-        it.no_push();
-      }
-
-      int perms = it.status().permissions();
-      if (isSuidBin(path, perms)) {
-        // Only emit suid bins.
-        genBin(path, perms, results);
-      }
-
-      ++it;
-    } catch (fs::filesystem_error& e) {
-      VLOG(1) << "Cannot read binary from " << path;
-      it.no_push();
-      // Try to recover, otherwise break.
-      try { ++it; } catch(fs::filesystem_error& e) { break; }
-    }
-  }
-}
-
-QueryData genSuidBin(QueryContext& context) {
-  QueryData results;
-
-  // Todo: add hidden column to select on that triggers non-std path searches.
-  for (const auto& path : kBinarySearchPaths) {
-    genSuidBinsFromPath(path, results);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/sysctl_utils.h b/osquery/tables/system/sysctl_utils.h
deleted file mode 100644 (file)
index 4346d71..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sys/sysctl.h>
-
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-#define CTL_MAX_VALUE 128
-
-#ifndef CTL_DEBUG_MAXID
-#define CTL_DEBUG_MAXID (CTL_MAXNAME * 2)
-#endif
-
-std::string stringFromMIB(const int* oid, size_t oid_size);
-
-/// Must be implemented by the platform.
-void genAllControls(QueryData& results,
-                    const std::map<std::string, std::string>& config,
-                    const std::string& subsystem);
-
-/// Must be implemented by the platform.
-void genControlInfo(int* oid,
-                    size_t oid_size,
-                    QueryData& results,
-                    const std::map<std::string, std::string>& config);
-
-/// Must be implemented by the platform.
-void genControlInfoFromName(const std::string& name, QueryData& results,
-                    const std::map<std::string, std::string>& config);
-}
-}
diff --git a/osquery/tables/system/system_controls.cpp b/osquery/tables/system/system_controls.cpp
deleted file mode 100644 (file)
index c91d0ca..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/tables.h>
-
-#include "osquery/tables/system/sysctl_utils.h"
-
-namespace osquery {
-namespace tables {
-
-const std::vector<std::string> kControlSettingsFiles = {"/etc/sysctl.conf"};
-
-const std::vector<std::string> kControlSettingsDirs = {
-    "/run/sysctl.d/%.conf",
-    "/etc/sysctl.d/%.conf",
-    "/usr/local/lib/sysctl.d/%.conf",
-    "/usr/lib/sysctl.d/%.conf",
-    "/lib/sysctl.d/%.conf",
-};
-
-std::string stringFromMIB(const int* oid, size_t oid_size) {
-  std::string result;
-  for (size_t i = 0; i < oid_size; ++i) {
-    // Walk an int-encoded MIB and return the string representation, '.'.
-    if (result.size() > 0) {
-      result += ".";
-    }
-    result += std::to_string(oid[i]);
-  }
-  return result;
-}
-
-void genControlInfoFromOIDString(
-    const std::string& oid_string,
-    QueryData& results,
-    const std::map<std::string, std::string>& config) {
-  int request[CTL_DEBUG_MAXID + 2] = {0};
-  auto tokens = osquery::split(oid_string, ".");
-  if (tokens.size() > CTL_DEBUG_MAXID) {
-    // OID input string was too large.
-    return;
-  }
-
-  // Convert the string into an int array.
-  for (size_t i = 0; i < tokens.size(); ++i) {
-    request[i] = atol(tokens.at(i).c_str());
-  }
-  genControlInfo((int*)request, tokens.size(), results, config);
-}
-
-void genControlConfigFromPath(const std::string& path,
-                              std::map<std::string, std::string>& config) {
-  std::string content;
-  if (!osquery::readFile(path, content).ok()) {
-    return;
-  }
-
-  for (auto& line : split(content, "\n")) {
-    boost::trim(line);
-    if (line[0] == '#' || line[0] == ';') {
-      continue;
-    }
-
-    // Try to tokenize the config line using '='.
-    auto detail = split(line, "=");
-    if (detail.size() == 2) {
-      boost::trim(detail[0]);
-      boost::trim(detail[1]);
-      config[detail[0]] = detail[1];
-    }
-  }
-}
-
-QueryData genSystemControls(QueryContext& context) {
-  QueryData results;
-
-  // Read the sysctl.conf values.
-  std::map<std::string, std::string> config;
-  for (const auto& path : kControlSettingsFiles) {
-    genControlConfigFromPath(path, config);
-  }
-
-  for (const auto& dirs : kControlSettingsDirs) {
-    std::vector<std::string> configs;
-    if (resolveFilePattern(dirs, configs).ok()) {
-      for (const auto& path : configs) {
-        genControlConfigFromPath(path, config);
-      }
-    }
-  }
-
-  // Iterate through the sysctl-defined macro of control types.
-  if (context.constraints["name"].exists(EQUALS)) {
-    // Request MIB information by the description (name).
-    auto names = context.constraints["name"].getAll(EQUALS);
-    for (const auto& name : names) {
-      genControlInfoFromName(name, results, config);
-    }
-  } else if (context.constraints["oid"].exists(EQUALS)) {
-    // Request MIB by OID as a string, parse into set of INTs.
-    auto oids = context.constraints["oid"].getAll(EQUALS);
-    for (const auto& oid_string : oids) {
-      genControlInfoFromOIDString(oid_string, results, config);
-    }
-  } else if (context.constraints["subsystem"].exists(EQUALS)) {
-    // Limit the MIB search to a subsystem name (first find the INT).
-    auto subsystems = context.constraints["subsystem"].getAll(EQUALS);
-    for (const auto& subsystem : subsystems) {
-      genAllControls(results, config, subsystem);
-    }
-  } else {
-    genAllControls(results, config, "");
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/uptime.cpp b/osquery/tables/system/uptime.cpp
deleted file mode 100644 (file)
index 5528896..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/tables.h>
-
-#if defined(__APPLE__)
-  #include <time.h>
-  #include <errno.h>
-  #include <sys/sysctl.h>
-#elif defined(__linux__)
-  #include <sys/sysinfo.h>
-#endif
-
-namespace osquery {
-namespace tables {
-
-long getUptime() {
-  #if defined(__APPLE__)
-    struct timeval boot_time;
-    size_t len = sizeof(boot_time);
-    int mib[2] = {
-        CTL_KERN,
-        KERN_BOOTTIME
-    };
-
-    if (sysctl(mib, 2, &boot_time, &len, NULL, 0) < 0) {
-        return -1;
-    }
-
-    time_t seconds_since_boot = boot_time.tv_sec;
-    time_t current_seconds = time(NULL);
-
-    return long(difftime(current_seconds, seconds_since_boot));
-  #elif defined(__linux__)
-    struct sysinfo sys_info;
-
-    if (sysinfo(&sys_info) != 0) {
-      return -1;
-    }
-
-    return sys_info.uptime;
-  #endif
-}
-
-QueryData genUptime(QueryContext& context) {
-  Row r;
-  QueryData results;
-  long uptime_in_seconds = getUptime();
-
-  if (uptime_in_seconds >= 0) {
-    r["days"] = INTEGER(uptime_in_seconds / 60 / 60 / 24);
-    r["hours"] = INTEGER((uptime_in_seconds / 60 / 60) % 24);
-    r["minutes"] = INTEGER((uptime_in_seconds / 60) % 60);
-    r["seconds"] = INTEGER(uptime_in_seconds % 60);
-    r["total_seconds"] = BIGINT(uptime_in_seconds);
-    results.push_back(r);
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/system/user_groups.h b/osquery/tables/system/user_groups.h
deleted file mode 100644 (file)
index 384f8a3..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <vector>
-#include <string>
-
-#include <grp.h>
-#include <pwd.h>
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-// This is also the max supported number for OS X right now.
-#define EXPECTED_GROUPS_MAX 64
-
-namespace osquery {
-namespace tables {
-
-template <typename T>
-static inline void addGroupsToResults(QueryData &results,
-                                      int uid,
-                                      const T *groups,
-                                      int ngroups) {
-  for (int i = 0; i < ngroups; i++) {
-    Row r;
-    r["uid"] = BIGINT(uid);
-    r["gid"] = BIGINT(groups[i]);
-    results.push_back(r);
-  }
-
-  return;
-}
-
-template <typename uid_type, typename gid_type>
-struct user_t {
-  const char *name;
-  uid_type uid;
-  gid_type gid;
-};
-
-template <typename uid_type, typename gid_type>
-static void getGroupsForUser(QueryData &results,
-                             const user_t<uid_type, gid_type> &user) {
-  gid_type groups_buf[EXPECTED_GROUPS_MAX];
-  gid_type *groups = groups_buf;
-  int ngroups = EXPECTED_GROUPS_MAX;
-
-  // GLIBC version before 2.3.3 may have a buffer overrun:
-  // http://man7.org/linux/man-pages/man3/getgrouplist.3.html
-  if (getgrouplist(user.name, user.gid, groups, &ngroups) < 0) {
-    // EXPECTED_GROUPS_MAX was probably not large enough.
-    // Try a larger size buffer.
-    // Darwin appears to not resize ngroups correctly.  We can hope
-    // we had enough space to start with.
-    groups = new gid_type[ngroups];
-    if (groups == nullptr) {
-      TLOG << "Could not allocate memory to get user groups";
-      return;
-    }
-
-    if (getgrouplist(user.name, user.gid, groups, &ngroups) < 0) {
-      TLOG << "Could not get users group list";
-    } else {
-      addGroupsToResults(results, user.uid, groups, ngroups);
-    }
-
-    delete[] groups;
-  } else {
-    addGroupsToResults(results, user.uid, groups, ngroups);
-  }
-  return;
-}
-}
-}
diff --git a/osquery/tables/utility/file.cpp b/osquery/tables/utility/file.cpp
deleted file mode 100644 (file)
index 5e5347a..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <sys/stat.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-namespace tables {
-
-void genFileInfo(const std::string& path,
-                 const std::string& filename,
-                 const std::string& dir,
-                 const std::string& pattern,
-                 QueryData& results) {
-  // Must provide the path, filename, directory separate from boost path->string
-  // helpers to match any explicit (query-parsed) predicate constraints.
-  struct stat file_stat, link_stat;
-  if (lstat(path.c_str(), &link_stat) < 0 || stat(path.c_str(), &file_stat)) {
-    // Path was not real, had too may links, or could not be accessed.
-    return;
-  }
-
-  Row r;
-  r["path"] = path;
-  r["filename"] = filename;
-  r["directory"] = dir;
-
-  r["inode"] = BIGINT(file_stat.st_ino);
-  r["uid"] = BIGINT(file_stat.st_uid);
-  r["gid"] = BIGINT(file_stat.st_gid);
-  r["mode"] = lsperms(file_stat.st_mode);
-  r["device"] = BIGINT(file_stat.st_rdev);
-  r["size"] = BIGINT(file_stat.st_size);
-  r["block_size"] = INTEGER(file_stat.st_blksize);
-  r["hard_links"] = INTEGER(file_stat.st_nlink);
-
-  // Times
-  r["atime"] = BIGINT(file_stat.st_atime);
-  r["mtime"] = BIGINT(file_stat.st_mtime);
-  r["ctime"] = BIGINT(file_stat.st_ctime);
-
-  // Type booleans
-  r["is_file"] = (!S_ISDIR(file_stat.st_mode)) ? "1" : "0";
-  r["is_dir"] = (S_ISDIR(file_stat.st_mode)) ? "1" : "0";
-  r["is_link"] = (S_ISLNK(link_stat.st_mode)) ? "1" : "0";
-  r["is_char"] = (S_ISCHR(file_stat.st_mode)) ? "1" : "0";
-  r["is_block"] = (S_ISBLK(file_stat.st_mode)) ? "1" : "0";
-
-  // pattern
-  r["pattern"] = pattern;
-
-  results.push_back(r);
-}
-
-QueryData genFile(QueryContext& context) {
-  QueryData results;
-
-  auto paths = context.constraints["path"].getAll(EQUALS);
-  for (const auto& path_string : paths) {
-    if (!isReadable(path_string)) {
-      continue;
-    }
-
-    fs::path path = path_string;
-    genFileInfo(path_string,
-                path.filename().string(),
-                path.parent_path().string(),
-                "",
-                results);
-  }
-
-  // Now loop through constraints using the directory column constraint.
-  auto directories = context.constraints["directory"].getAll(EQUALS);
-  for (const auto& directory_string : directories) {
-    if (!isReadable(directory_string) || !isDirectory(directory_string)) {
-      continue;
-    }
-
-    try {
-      // Iterate over the directory and generate info for each regular file.
-      fs::directory_iterator begin(directory_string), end;
-      for (; begin != end; ++begin) {
-        genFileInfo(begin->path().string(),
-                    begin->path().filename().string(),
-                    directory_string,
-                    "",
-                    results);
-      }
-    } catch (const fs::filesystem_error& e) {
-      continue;
-    }
-  }
-
-  // Now loop through constraints using the pattern column constraint.
-  auto patterns = context.constraints["pattern"].getAll(EQUALS);
-  if (patterns.size() != 1) {
-    return results;
-  }
-
-  for (const auto& pattern : patterns) {
-    std::vector<std::string> expanded_patterns;
-    auto status = resolveFilePattern(pattern, expanded_patterns);
-    if (!status.ok()) {
-      VLOG(1) << "Could not expand pattern properly: " << status.toString();
-      return results;
-    }
-
-    for (const auto& resolved : expanded_patterns) {
-      if (!isReadable(resolved)) {
-        continue;
-      }
-      fs::path path = resolved;
-      genFileInfo(resolved,
-                  path.filename().string(),
-                  path.parent_path().string(),
-                  pattern,
-                  results);
-
-    }
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/utility/hash.cpp b/osquery/tables/utility/hash.cpp
deleted file mode 100644 (file)
index 5baf307..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/filesystem.h>
-#include <osquery/hash.h>
-#include <osquery/tables.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-namespace tables {
-
-void genHashForFile(const std::string& path,
-                    const std::string& dir,
-                    QueryData& results) {
-  // Must provide the path, filename, directory separate from boost path->string
-  // helpers to match any explicit (query-parsed) predicate constraints.
-  Row r;
-  r["path"] = path;
-  r["directory"] = dir;
-  r["md5"] = osquery::hashFromFile(HASH_TYPE_MD5, path);
-  r["sha1"] = osquery::hashFromFile(HASH_TYPE_SHA1, path);
-  r["sha256"] = osquery::hashFromFile(HASH_TYPE_SHA256, path);
-  results.push_back(r);
-}
-
-QueryData genHash(QueryContext& context) {
-  QueryData results;
-
-  // The query must provide a predicate with constraints including path or
-  // directory. We search for the parsed predicate constraints with the equals
-  // operator.
-  auto paths = context.constraints["path"].getAll(EQUALS);
-  for (const auto& path_string : paths) {
-    boost::filesystem::path path = path_string;
-    if (!boost::filesystem::is_regular_file(path)) {
-      continue;
-    }
-
-    genHashForFile(path_string, path.parent_path().string(), results);
-  }
-
-  // Now loop through constraints using the directory column constraint.
-  auto directories = context.constraints["directory"].getAll(EQUALS);
-  for (const auto& directory_string : directories) {
-    boost::filesystem::path directory = directory_string;
-    if (!boost::filesystem::is_directory(directory)) {
-      continue;
-    }
-
-    // Iterate over the directory and generate a hash for each regular file.
-    boost::filesystem::directory_iterator begin(directory), end;
-    for (; begin != end; ++begin) {
-      if (boost::filesystem::is_regular_file(begin->status())) {
-        genHashForFile(begin->path().string(), directory_string, results);
-      }
-    }
-  }
-
-  return results;
-}
-}
-}
diff --git a/osquery/tables/utility/osquery.cpp b/osquery/tables/utility/osquery.cpp
deleted file mode 100644 (file)
index 46eb14d..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <osquery/config.h>
-#include <osquery/core.h>
-#include <osquery/extensions.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/registry.h>
-#include <osquery/sql.h>
-#include <osquery/tables.h>
-#include <osquery/filesystem.h>
-
-namespace osquery {
-namespace tables {
-
-typedef pt::ptree::value_type tree_node;
-
-void genQueryPack(const tree_node& pack, QueryData& results) {
-  Row r;
-  // Packs are stored by name and contain configuration data.
-  r["name"] = pack.first;
-  r["path"] = pack.second.get("path", "");
-
-  // There are optional restrictions on the set of queries applied pack-wide.
-  auto pack_wide_version = pack.second.get("version", "");
-  auto pack_wide_platform = pack.second.get("platform", "");
-
-  // Iterate through each query in the pack.
-  for (auto const& query : pack.second.get_child("queries")) {
-    r["query_name"] = query.first;
-    r["query"] = query.second.get("query", "");
-    r["interval"] = INTEGER(query.second.get("interval", 0));
-    r["description"] = query.second.get("description", "");
-    r["value"] = query.second.get("value", "");
-
-    // Set the version requirement based on the query-specific or pack-wide.
-    if (query.second.count("version") > 0) {
-      r["version"] = query.second.get("version", "");
-    } else {
-      r["version"] = pack_wide_platform;
-    }
-
-    // Set the platform requirement based on the query-specific or pack-wide.
-    if (query.second.count("platform") > 0) {
-      r["platform"] = query.second.get("platform", "");
-    } else {
-      r["platform"] = pack_wide_platform;
-    }
-
-    // Adding a prefix to the pack queries to differentiate packs from schedule.
-    r["scheduled_name"] = "pack_" + r.at("name") + "_" + r.at("query_name");
-    if (Config::checkScheduledQueryName(r.at("scheduled_name"))) {
-      r["scheduled"] = INTEGER(1);
-    } else {
-      r["scheduled"] = INTEGER(0);
-    }
-
-    results.push_back(r);
-  }
-}
-
-QueryData genOsqueryPacks(QueryContext& context) {
-  QueryData results;
-
-  // Get a lock on the config instance.
-  ConfigDataInstance config;
-
-  // Get the loaded data tree from global JSON configuration.
-  const auto& packs_parsed_data = config.getParsedData("packs");
-
-  // Iterate through all the packs to get each configuration and set of queries.
-  for (auto const& pack : packs_parsed_data) {
-    // Make sure the pack data contains queries.
-    if (pack.second.count("queries") == 0) {
-      continue;
-    }
-    genQueryPack(pack, results);
-  }
-
-  return results;
-}
-
-void genFlag(const std::string& name,
-             const FlagInfo& flag,
-             QueryData& results) {
-  Row r;
-  r["name"] = name;
-  r["type"] = flag.type;
-  r["description"] = flag.description;
-  r["default_value"] = flag.default_value;
-  r["value"] = flag.value;
-  r["shell_only"] = (flag.detail.shell) ? "1" : "0";
-  results.push_back(r);
-}
-
-QueryData genOsqueryFlags(QueryContext& context) {
-  QueryData results;
-
-  auto flags = Flag::flags();
-  for (const auto& flag : flags) {
-    if (flag.first.size() > 2) {
-      // Skip single-character flags.
-      genFlag(flag.first, flag.second, results);
-    }
-  }
-
-  return results;
-}
-
-QueryData genOsqueryRegistry(QueryContext& context) {
-  QueryData results;
-
-  const auto& registries = RegistryFactory::all();
-  for (const auto& registry : registries) {
-    const auto& plugins = registry.second->all();
-    for (const auto& plugin : plugins) {
-      Row r;
-      r["registry"] = registry.first;
-      r["name"] = plugin.first;
-      r["owner_uuid"] = "0";
-      r["internal"] = (registry.second->isInternal(plugin.first)) ? "1" : "0";
-      r["active"] = "1";
-      results.push_back(r);
-    }
-
-    for (const auto& route : registry.second->getExternal()) {
-      Row r;
-      r["registry"] = registry.first;
-      r["name"] = route.first;
-      r["owner_uuid"] = INTEGER(route.second);
-      r["internal"] = "0";
-      r["active"] = "1";
-      results.push_back(r);
-    }
-  }
-
-  return results;
-}
-
-QueryData genOsqueryExtensions(QueryContext& context) {
-  QueryData results;
-
-  ExtensionList extensions;
-  if (getExtensions(extensions).ok()) {
-    for (const auto& extenion : extensions) {
-      Row r;
-      r["uuid"] = TEXT(extenion.first);
-      r["name"] = extenion.second.name;
-      r["version"] = extenion.second.version;
-      r["sdk_version"] = extenion.second.sdk_version;
-      r["path"] = getExtensionSocket(extenion.first);
-      r["type"] = "extension";
-      results.push_back(r);
-    }
-  }
-
-  const auto& modules = RegistryFactory::getModules();
-  for (const auto& module : modules) {
-    Row r;
-    r["uuid"] = TEXT(module.first);
-    r["name"] = module.second.name;
-    r["version"] = module.second.version;
-    r["sdk_version"] = module.second.sdk_version;
-    r["path"] = module.second.path;
-    r["type"] = "module";
-    results.push_back(r);
-  }
-
-  return results;
-}
-
-QueryData genOsqueryInfo(QueryContext& context) {
-  QueryData results;
-
-  Row r;
-  r["pid"] = INTEGER(getpid());
-  r["version"] = kVersion;
-
-  std::string hash_string;
-  auto s = Config::getMD5(hash_string);
-  if (s.ok()) {
-    r["config_md5"] = TEXT(hash_string);
-  } else {
-    r["config_md5"] = "";
-    VLOG(1) << "Could not retrieve config hash: " << s.toString();
-  }
-
-  r["config_path"] = Flag::getValue("config_path");
-  r["extensions"] =
-      (pingExtension(FLAGS_extensions_socket).ok()) ? "active" : "inactive";
-
-  r["build_platform"] = STR(OSQUERY_BUILD_PLATFORM);
-  r["build_distro"] = STR(OSQUERY_BUILD_DISTRO);
-
-  results.push_back(r);
-
-  return results;
-}
-
-QueryData genOsquerySchedule(QueryContext& context) {
-  QueryData results;
-
-  ConfigDataInstance config;
-  for (const auto& query : config.schedule()) {
-    Row r;
-    r["name"] = TEXT(query.first);
-    r["query"] = TEXT(query.second.query);
-    r["interval"] = INTEGER(query.second.interval);
-
-    // Report optional performance information.
-    r["executions"] = BIGINT(query.second.executions);
-    r["output_size"] = BIGINT(query.second.output_size);
-    r["wall_time"] = BIGINT(query.second.wall_time);
-    r["user_time"] = BIGINT(query.second.user_time);
-    r["system_time"] = BIGINT(query.second.system_time);
-    r["average_memory"] = BIGINT(query.second.average_memory);
-    results.push_back(r);
-  }
-
-  return results;
-}
-
-}
-}
diff --git a/osquery/tables/utility/time.cpp b/osquery/tables/utility/time.cpp
deleted file mode 100644 (file)
index 1b68eff..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  Copyright (c) 2014, Facebook, Inc.
- *  All rights reserved.
- *
- *  This source code is licensed under the BSD-style license found in the
- *  LICENSE file in the root directory of this source tree. An additional grant 
- *  of patent rights can be found in the PATENTS file in the same directory.
- *
- */
-
-#include <ctime>
-#include <boost/algorithm/string/trim.hpp>
-
-#include <osquery/tables.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData genTime(QueryContext& context) {
-  Row r;
-  time_t _time = time(0);
-  struct tm* now = localtime(&_time);
-  struct tm* gmt = gmtime(&_time);
-
-  char weekday[10] = {0};
-  strftime(weekday, sizeof(weekday), "%A", now);
-
-  std::string timestamp;
-  timestamp = asctime(gmt);
-  boost::algorithm::trim(timestamp);
-  timestamp += " UTC";
-
-  char iso_8601[21] = {0};
-  strftime(iso_8601, sizeof(iso_8601), "%FT%TZ", gmt);
-
-  r["weekday"] = TEXT(weekday);
-  r["year"] = INTEGER(now->tm_year + 1900);
-  r["month"] = INTEGER(now->tm_mon + 1);
-  r["day"] = INTEGER(now->tm_mday);
-  r["hour"] = INTEGER(now->tm_hour);
-  r["minutes"] = INTEGER(now->tm_min);
-  r["seconds"] = INTEGER(now->tm_sec);
-  r["unix_time"] = INTEGER(_time);
-  r["timestamp"] = TEXT(timestamp);
-  r["iso_8601"] = TEXT(iso_8601);
-
-  QueryData results;
-  results.push_back(r);
-  return results;
-}
-}
-}
diff --git a/osquery/tizen/CMakeLists.txt b/osquery/tizen/CMakeLists.txt
deleted file mode 100644 (file)
index 669f1bb..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-#  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
-
-ADD_OSQUERY_LIBRARY(osquery_tizen property/property.cpp
-                                                                 manager/manager.cpp
-                                                                 manager/manager_impl.cpp
-                                                                 notification/notification.cpp)
-
-ADD_OSQUERY_TEST(${OSQUERY_TIZEN_TESTS})
-
-IF(DEFINED GBS_BUILD)
-       # tables
-       FILE(GLOB TIZEN_TABLES "tables/*.cpp")
-       ADD_OSQUERY_LIBRARY(tizen_tables ${TIZEN_TABLES})
-       FILE(GLOB OSQUERY_TIZEN_TESTS "[!d]*/tests/*.cpp")
-
-# Verification can be done with full-DPM
-#      FILE(GLOB OSQUERY_GBS_TESTS "device_policy/tests/*.cpp")
-#      ADD_OSQUERY_TEST(${OSQUERY_GBS_TESTS})
-ENDIF(DEFINED GBS_BUILD)
diff --git a/osquery/tizen/manager/manager.cpp b/osquery/tizen/manager/manager.cpp
deleted file mode 100644 (file)
index 2ff4f97..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file manager.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation of osquery manager
- */
-
-#include <osquery_manager.h>
-
-#include "manager_impl.h"
-
-namespace osquery {
-
-Rows OsqueryManager::execute(const std::string& query)
-{
-       return ManagerImpl::instance().execute(query);
-}
-
-void OsqueryManager::subscribe(const std::string& table, const Callback& callback)
-{
-       return ManagerImpl::instance().subscribe(table, callback);
-}
-
-std::vector<std::string> OsqueryManager::tables(void) noexcept
-{
-       return ManagerImpl::instance().tables();
-}
-
-std::vector<std::string> OsqueryManager::columns(const std::string& table) noexcept
-{
-       return ManagerImpl::instance().columns(table);
-}
-
-} // namespace osquery
diff --git a/osquery/tizen/manager/manager_impl.cpp b/osquery/tizen/manager/manager_impl.cpp
deleted file mode 100644 (file)
index 2e80e0a..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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 ManagerImplied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License
- */
-/*
- * @file manager_impl.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation of osquery manager's impl
- */
-
-#include "manager_impl.h"
-
-#include <osquery/core.h>
-#include <osquery/filesystem.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/notification.h>
-#include <osquery/sql.h>
-#include <osquery/status.h>
-
-#include <gflags/gflags.h>
-
-#include <boost/filesystem/operations.hpp>
-
-#include <functional>
-#include <sstream>
-
-namespace osquery {
-
-ManagerImpl::ManagerImpl()
-{
-       auto logDir = Flag::getValue("osquery_log_dir");
-       if (!logDir.empty() && !(pathExists(logDir).ok()))
-               boost::filesystem::create_directories(logDir);
-
-       LOG(INFO) << "Initalize osquery manager. ";
-}
-
-ManagerImpl::~ManagerImpl() noexcept
-{
-       LOG(INFO) << "Shutdown osquery manager.";
-}
-
-ManagerImpl& ManagerImpl::instance()
-{
-       static ManagerImpl instance;
-       return instance;
-}
-
-Rows ManagerImpl::execute(const std::string& query)
-{
-       LOG(INFO) << "Execute query: " << query;
-
-       osquery::QueryData results;
-       auto status = osquery::query(query, results);
-       if (!status.ok())
-               LOG(ERROR) << "Executing query failed: " << status.getCode();
-
-       return results;
-}
-
-void ManagerImpl::subscribe(const std::string& table, const Callback& callback)
-{
-       LOG(INFO) << "Subscribe event: " << table;
-
-       auto status = Notification::instance().add(table, callback);
-       if (!status.ok())
-               LOG(ERROR) << "Subscribing event failed: " << status.getCode();
-}
-
-std::vector<std::string> ManagerImpl::tables(void) noexcept
-{
-       return SQL::getTableNames();
-}
-
-std::vector<std::string> ManagerImpl::columns(const std::string& table) noexcept
-{
-       std::stringstream query;
-       query << "SELECT * FROM " << table;
-
-       TableColumns columns;
-       getQueryColumns(query.str(), columns);
-
-       // Extract column names
-       std::vector<std::string> names;
-       for (auto& c : columns)
-               names.emplace_back(std::move(c.first));
-
-       return names;
-}
-
-} // namespace osquery
diff --git a/osquery/tizen/manager/manager_impl.h b/osquery/tizen/manager/manager_impl.h
deleted file mode 100644 (file)
index dcfe2cb..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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 ManagerImplied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License
- */
-/*
- * @file manager_impl.h
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation interface of osquery manager
- */
-
-#pragma once
-
-#include <osquery_manager.h>
-
-#include <string>
-#include <vector>
-
-namespace osquery {
-
-/// Singleton class
-class ManagerImpl final {
-public:
-       ManagerImpl(const ManagerImpl&) = delete;
-       ManagerImpl& operator=(const ManagerImpl&) = delete;
-
-       ManagerImpl(ManagerImpl&&) noexcept = default;
-       ManagerImpl& operator=(ManagerImpl&&) noexcept = default;
-
-       static ManagerImpl& instance();
-
-       Rows execute(const std::string& query);
-       void subscribe(const std::string& table, const Callback& callback);
-
-       std::vector<std::string> tables(void) noexcept;
-       std::vector<std::string> columns(const std::string& table) noexcept;
-
-private:
-       ManagerImpl();
-       ~ManagerImpl() noexcept;
-};
-
-} // namespace osquery
diff --git a/osquery/tizen/manager/tests/manager_tests.cpp b/osquery/tizen/manager/tests/manager_tests.cpp
deleted file mode 100644 (file)
index 048cfcc..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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 <gtest/gtest.h>
-
-#include <osquery_manager.h>
-
-#include <osquery/notification.h>
-#include <osquery/logger.h>
-
-using namespace osquery;
-
-class ManagerTests : public testing::Test {};
-
-TEST_F(ManagerTests, test_manager_execute) {
-       std::string query = "SELECT * FROM time";
-       auto rows = OsqueryManager::execute(query);
-       EXPECT_EQ(rows.size(), 1);
-
-       VLOG(1) << "[Test] time table rows:";
-       VLOG(1) << "\t hour: " << rows[0]["hour"];
-       VLOG(1) << "\t minutes: " << rows[0]["minutes"];
-       VLOG(1) << "\t seconds: " << rows[0]["seconds"];
-}
-
-TEST_F(ManagerTests, test_manager_subscribe) {
-       int called = 0;
-       auto callback = [&](const Row& row) {
-               VLOG(1) << "NotifyCallback called:";
-               for (const auto& r : row)
-                       VLOG(1) << "\t" << r.first << " : " << r.second;
-               called++;
-       };
-
-       OsqueryManager::subscribe("manager_test", callback);
-
-       Row row;
-       row["foo"] = "bar";
-       row["foo2"] = "bar2";
-       row["foo3"] = "bar3";
-
-       /// Notification trigger
-       auto s = Notification::instance().emit("manager_test", row);
-
-       EXPECT_TRUE(s.ok());
-       EXPECT_EQ(called, 1);
-}
-
-TEST_F(ManagerTests, test_manager_tables) {
-       auto tables = OsqueryManager::tables();
-       EXPECT_TRUE(tables.size() > 0);
-
-       VLOG(1) << "[Test] Enabled tables:";
-       for (const auto& t : tables)
-               VLOG(1) << "\t" << t;
-}
-
-TEST_F(ManagerTests, test_manager_columns) {
-       auto columns = OsqueryManager::columns("time");
-       EXPECT_TRUE(columns.size() > 0);
-
-       VLOG(1) << "[Test] Enabled columns of time table:";
-       for (const auto& c : columns)
-               VLOG(1) << "\t" << c;
-}
diff --git a/osquery/tizen/notification/notification.cpp b/osquery/tizen/notification/notification.cpp
deleted file mode 100644 (file)
index 9db0272..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file notification.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation of notification
- */
-
-#include <mutex>
-
-#include <osquery/notification.h>
-#include <osquery/logger.h>
-
-namespace {
-       std::mutex mutex;
-} // anonymous namespace
-
-namespace osquery {
-
-Notification& Notification::instance()
-{
-       static Notification notifier;
-       return notifier;
-}
-
-Status Notification::add(const std::string& table, const NotifyCallback& callback)
-{
-       if (table.empty())
-               return Status(1, "Wrong table name");
-
-       LOG(INFO) << "Add NotifyCallback to:" << table;
-       {
-               std::lock_guard<std::mutex> lock(mutex);
-               this->callbacks.insert(std::make_pair(table, callback));
-       }
-
-       return Status(0, "OK");
-}
-
-Status Notification::emit(const std::string& table, const Row& result) const
-{
-       if (table.empty())
-               return Status(1, "Wrong table name");
-
-       auto iter = this->callbacks.find(table);
-       if (iter == this->callbacks.end())
-               return Status(1, "Registered callback not found");
-
-       LOG(INFO) << "Emit notification about:" << table;
-       {
-               std::lock_guard<std::mutex> lock(mutex);
-               while (iter != this->callbacks.end())  {
-                       const auto& callback = iter->second;
-                       callback(result);
-                       iter++;
-               }
-       }
-
-       return Status(0, "OK");
-}
-
-} // namespace osquery
diff --git a/osquery/tizen/notification/tests/notification_tests.cpp b/osquery/tizen/notification/tests/notification_tests.cpp
deleted file mode 100644 (file)
index f26bfa3..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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 <gtest/gtest.h>
-
-#include <osquery/notification.h>
-#include <osquery/logger.h>
-
-using namespace osquery;
-
-class NotificationTests : public testing::Test {};
-
-TEST_F(NotificationTests, test_add_positive) {
-       auto& notifier = Notification::instance();
-
-       auto callback = [](const Row& row) {
-               VLOG(1) << "NotifyCallback called:";
-               for (const auto& r : row)
-                       VLOG(1) << "\t" << r.first << " : " << r.second;
-       };
-
-       auto s = notifier.add("test", std::move(callback));
-       EXPECT_TRUE(s.ok());
-}
-
-TEST_F(NotificationTests, test_add_negative) {
-       auto& notifier = Notification::instance();
-
-       auto callback = [](const Row& row) {
-               VLOG(1) << "NotifyCallback called:";
-               for (const auto& r : row)
-                       VLOG(1) << "\t" << r.first << " : " << r.second;
-       };
-
-       auto s = notifier.add("", std::move(callback));
-       EXPECT_FALSE(s.ok());
-}
-
-TEST_F(NotificationTests, test_emit_positive) {
-       auto& notifier = Notification::instance();
-
-       int called = 0;
-       auto callback = [&](const Row& row) {
-               VLOG(1) << "NotifyCallback called:";
-               for (const auto& r : row)
-                       VLOG(1) << "\t" << r.first << " : " << r.second;
-               called++;
-       };
-
-       auto s = notifier.add("test2", std::move(callback));
-       EXPECT_TRUE(s.ok());
-
-       Row row;
-       row["foo"] = "bar";
-       s = notifier.emit("test2", row);
-
-       EXPECT_TRUE(s.ok());
-       EXPECT_EQ(called, 1);
-}
diff --git a/osquery/tizen/property/property.cpp b/osquery/tizen/property/property.cpp
deleted file mode 100644 (file)
index 36581ef..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file property.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation of Property
- */
-
-#include <osquery_manager.h>
-#include <property.h>
-
-#include <schema/time.h>
-#include <schema/processes.h>
-#include <schema/users.h>
-#include <schema/groups.h>
-#include <schema/memory-map.h>
-
-#include <osquery/logger.h>
-
-#include <tsqb.hxx>
-
-#include <boost/lexical_cast.hpp>
-
-namespace {
-
-using namespace tsqb;
-auto time = make_table("time",
-                                          make_column("hour", &Time::hour),
-                                          make_column("minutes", &Time::minutes),
-                                          make_column("seconds", &Time::seconds));
-
-auto processes = make_table("processes",
-                                                       make_column("pid", &Processes::pid),
-                                                       make_column("name", &Processes::name),
-                                                       make_column("path", &Processes::path),
-                                                       make_column("cmdline", &Processes::cmdline),
-                                                       make_column("uid", &Processes::uid),
-                                                       make_column("gid", &Processes::gid),
-                                                       make_column("euid", &Processes::euid),
-                                                       make_column("egid", &Processes::egid),
-                                                       make_column("on_disk", &Processes::on_disk),
-//                                                     make_column("wired_size", &Processes::wired_size),
-                                                       make_column("resident_size", &Processes::resident_size),
-                                                       make_column("phys_footprint", &Processes::phys_footprint),
-                                                       make_column("user_time", &Processes::user_time),
-                                                       make_column("system_time", &Processes::system_time),
-                                                       make_column("start_time", &Processes::start_time),
-                                                       make_column("parent", &Processes::parent));
-
-auto users = make_table("users",
-                                               make_column("uid", &Users::uid),
-                                               make_column("gid", &Users::gid),
-                                               make_column("uid_signed", &Users::uid_signed),
-                                               make_column("gid_signed", &Users::gid_signed),
-                                               make_column("username", &Users::username),
-                                               make_column("description", &Users::description),
-                                               make_column("directory", &Users::directory),
-                                               make_column("shell", &Users::shell));
-
-auto groups = make_table("groups",
-                                                make_column("gid", &Groups::gid),
-                                                make_column("gid_signed", &Groups::gid_signed),
-                                                make_column("groupname", &Groups::groupname));
-
-auto memoryMap = make_table("memory_map",
-                                                make_column("region", &MemoryMap::region),
-                                                make_column("type", &MemoryMap::type),
-                                                make_column("start", &MemoryMap::start),
-                                                make_column("end", &MemoryMap::end));
-
-auto db = make_database("db", time, processes, users, groups, memoryMap);
-
-} // anonymous namespace
-
-namespace osquery {
-
-template <typename T>
-Property<T>::Property()
-{
-       auto results = OsqueryManager::execute(db.selectAll<T>());
-       if (results.size() > 0)
-               this->data = std::move(results[0]);
-}
-
-template <typename T>
-Property<T>::Property(KeyValuePair&& kvp) : data(std::move(kvp))
-{
-}
-
-template <typename T>
-template<typename Struct, typename Member>
-Member Property<T>::at(Member Struct::* field) const
-{
-       if (this->data.size() == 0)
-               throw std::runtime_error("Data is not exist.");
-
-       std::string key = db.getColumnName(field);
-       if (key.empty())
-               throw std::runtime_error("Key is not exist.");
-
-       /// Convert "table.column" to "column"
-       std::size_t pos = key.find(".");
-       if (pos != std::string::npos && pos != key.size() - 1)
-               key = key.substr(pos + 1);
-
-       std::string value = this->data.at(key);
-       if (value.empty()) {
-               LOG(ERROR) << "Key: " << key << "is not exist.";
-               return Member();
-       } else {
-               /// TODO(Sangwan): Catch boost::bad_lexical_cast
-               return boost::lexical_cast<Member>(value);
-       }
-}
-
-template <typename T>
-template<typename Struct, typename Member>
-Member Property<T>::operator[](Member Struct::*field) const
-{
-       return this->at(field);
-}
-
-template <typename T>
-Properties<T>::Properties()
-{
-       auto results = OsqueryManager::execute(db.selectAll<T>());
-       for (auto& r : results)
-               this->datas.emplace_back(Property<T>(std::move(r)));
-}
-
-/// Explicit instantiation
-template class Property<Time>;
-template class Properties<Time>;
-template int Property<Time>::at(int Time::*) const;
-template int Property<Time>::operator[](int Time::*) const;
-
-template class Property<Processes>;
-template class Properties<Processes>;
-template int Property<Processes>::at(int Processes::*) const;
-template int Property<Processes>::operator[](int Processes::*) const;
-template long long int Property<Processes>::at(long long int Processes::*) const;
-template long long int Property<Processes>::operator[](long long int Processes::*) const;
-template std::string Property<Processes>::at(std::string Processes::*) const;
-template std::string Property<Processes>::operator[](std::string Processes::*) const;
-
-template class Property<Users>;
-template class Properties<Users>;
-template long long int Property<Users>::at(long long int Users::*) const;
-template long long int Property<Users>::operator[](long long int Users::*) const;
-template unsigned long long int Property<Users>::at(unsigned long long int Users::*) const;
-template unsigned long long int Property<Users>::operator[](unsigned long long int Users::*) const;
-template std::string Property<Users>::at(std::string Users::*) const;
-template std::string Property<Users>::operator[](std::string Users::*) const;
-
-template class Property<Groups>;
-template class Properties<Groups>;
-template long long int Property<Groups>::at(long long int Groups::*) const;
-template long long int Property<Groups>::operator[](long long int Groups::*) const;
-template unsigned long long int Property<Groups>::at(unsigned long long int Groups::*) const;
-template unsigned long long int Property<Groups>::operator[](unsigned long long int Groups::*) const;
-template std::string Property<Groups>::at(std::string Groups::*) const;
-template std::string Property<Groups>::operator[](std::string Groups::*) const;
-
-template class Property<MemoryMap>;
-template class Properties<MemoryMap>;
-template int Property<MemoryMap>::at(int MemoryMap::*) const;
-template int Property<MemoryMap>::operator[](int MemoryMap::*) const;
-template std::string Property<MemoryMap>::at(std::string MemoryMap::*) const;
-template std::string Property<MemoryMap>::operator[](std::string MemoryMap::*) const;
-
-} // namespace osquery
diff --git a/osquery/tizen/property/tests/property_tests.cpp b/osquery/tizen/property/tests/property_tests.cpp
deleted file mode 100644 (file)
index 8038524..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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 <gtest/gtest.h>
-
-#include <osquery/logger.h>
-
-#include <property.h>
-
-#include <schema/time.h>
-#include <schema/processes.h>
-#include <schema/users.h>
-#include <schema/groups.h>
-#include <schema/memory-map.h>
-
-using namespace osquery;
-
-class PropertyTests : public testing::Test {};
-
-TEST_F(PropertyTests, property) {
-       Time result = { -1, -1, -1 };
-
-       Property<Time> time;
-       result.hour = time.at(&Time::hour);
-       result.minutes = time.at(&Time::minutes);
-       result.seconds = time.at(&Time::seconds);
-
-       /// Once query execution
-       VLOG(1) << "[Test] time table:";
-       VLOG(1) << "\t hour: " << result.hour;
-       VLOG(1) << "\t minutes: " << result.minutes;
-       VLOG(1) << "\t seconds: " << result.seconds;
-
-       /// Each query execution
-       VLOG(1) << "[Test] time table:";
-       VLOG(1) << "\t hour: " << Property<Time>().at(&Time::hour);
-       VLOG(1) << "\t minutes: " << Property<Time>().at(&Time::minutes);
-       VLOG(1) << "\t seconds: " <<  Property<Time>().at(&Time::seconds);
-
-       EXPECT_NE(result.hour, -1);
-       EXPECT_NE(result.minutes, -1);
-       EXPECT_NE(result.seconds, -1);
-}
-
-TEST_F(PropertyTests, propertyArrayOp) {
-       Time result = { -1, -1, -1 };
-
-       Property<Time> time;
-       result.hour = time[&Time::hour];
-       result.minutes = time[&Time::minutes];
-       result.seconds = time[&Time::seconds];
-
-       /// Once query execution
-       VLOG(1) << "[Test] time table:";
-       VLOG(1) << "\t hour: " << result.hour;
-       VLOG(1) << "\t minutes: " << result.minutes;
-       VLOG(1) << "\t seconds: " << result.seconds;
-
-       EXPECT_NE(result.hour, -1);
-       EXPECT_NE(result.minutes, -1);
-       EXPECT_NE(result.seconds, -1);
-}
-
-TEST_F(PropertyTests, propertiesProcesses) {
-       Processes result = {
-               -1, /// pid
-               "", /// name
-               "", /// path
-               "", /// cmdline
-               -1, /// uid
-               -1, /// gid
-               -1, /// euid
-               -1, /// egid
-               "", /// on_disk
-//             "", /// wired_size
-               "", /// resident_size
-               "", /// phys_footprint
-               "", /// user_time
-               "", /// system_time
-               "", /// start_time
-               -1  /// parent
-               };
-
-       Properties<Processes> processes;
-
-       for(auto& p : processes) {
-               result.pid = p.at(&Processes::pid);
-               result.name = p.at(&Processes::name);
-               result.path = p.at(&Processes::path);
-               result.cmdline = p.at(&Processes::cmdline);
-               result.uid = p.at(&Processes::uid);
-               result.gid = p.at(&Processes::gid);
-               result.euid = p.at(&Processes::euid);
-               result.egid = p.at(&Processes::egid);
-               result.on_disk = p.at(&Processes::on_disk);
-//             result.wired_size = p.at(&Processes::wired_size);
-               result.resident_size = p.at(&Processes::resident_size);
-               result.phys_footprint = p.at(&Processes::phys_footprint);
-               result.user_time = p.at(&Processes::user_time);
-               result.system_time = p.at(&Processes::system_time);
-               result.start_time = p.at(&Processes::start_time);
-               result.parent = p.at(&Processes::parent);
-
-               VLOG(1) << "[Test] Processes table:";
-               VLOG(1) << "\t pid: " << result.pid;
-               VLOG(1) << "\t name: " << result.name;
-               VLOG(1) << "\t path: " << result.path;
-               VLOG(1) << "\t cmdline: " << result.cmdline;
-               VLOG(1) << "\t uid: " << result.uid;
-               VLOG(1) << "\t gid: " << result.gid;
-               VLOG(1) << "\t euid: " << result.euid;
-               VLOG(1) << "\t egid: " << result.egid;
-               VLOG(1) << "\t on_disk: " << result.on_disk;
-//             VLOG(1) << "\t wired_size: " << result.wired_size;
-               VLOG(1) << "\t resident_size: " << result.resident_size;
-               VLOG(1) << "\t phys_footprint: " << result.phys_footprint;
-               VLOG(1) << "\t user_time: " << result.user_time;
-               VLOG(1) << "\t system_time: " << result.system_time;
-               VLOG(1) << "\t start_time: " << result.start_time;
-               VLOG(1) << "\t parent: " << result.parent;
-       }
-}
-
-TEST_F(PropertyTests, propertiesUsers) {
-       Properties<Users> users;
-       for(const auto& user : users) {
-               VLOG(1) << "[Test] User table:";
-               VLOG(1) << "\t uid: " << user[&Users::uid];
-               VLOG(1) << "\t gid: " << user[&Users::gid];
-               VLOG(1) << "\t uid_signed: " << user[&Users::uid_signed];
-               VLOG(1) << "\t gid_signed: " << user[&Users::gid_signed];
-               VLOG(1) << "\t username: " << user[&Users::username];
-               VLOG(1) << "\t description: " << user[&Users::description];
-               VLOG(1) << "\t directory: " << user[&Users::directory];
-               VLOG(1) << "\t shell: " << user[&Users::shell];
-       }
-}
-
-TEST_F(PropertyTests, propertiesGroups) {
-       Properties<Groups> groups;
-       for(const auto& group : groups) {
-               VLOG(1) << "[Test] Group table:";
-               VLOG(1) << "\t gid: " << group[&Groups::gid];
-               VLOG(1) << "\t gid_signed: " << group[&Groups::gid_signed];
-               VLOG(1) << "\t groupname: " << group[&Groups::groupname];
-       }
-}
-
-TEST_F(PropertyTests, propertiesMemoryMap) {
-       Properties<MemoryMap> memoryMap;
-       for(const auto& mm : memoryMap) {
-               VLOG(1) << "[Test] memory_map table:";
-               VLOG(1) << "\t region: " << mm[&MemoryMap::region];
-               VLOG(1) << "\t type: " << mm[&MemoryMap::type];
-               VLOG(1) << "\t start: " << mm[&MemoryMap::start];
-               VLOG(1) << "\t end: " << mm[&MemoryMap::end];
-       }
-}
diff --git a/osquery/tizen/tables/bluetooth_policy.cpp b/osquery/tizen/tables/bluetooth_policy.cpp
deleted file mode 100644 (file)
index b285835..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file bluetooth_policy.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation of bluetooth_policy table
- */
-
-#include <string>
-#include <memory>
-#include <stdexcept>
-
-#include <osquery/sql.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include <dpm/device-policy-manager.h>
-#include <dpm/pil/policy-client.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData genBluetoothPolicy(QueryContext& context) try {
-       std::shared_ptr<void> handle(dpm_manager_create(), dpm_manager_destroy);
-       if (handle == nullptr)
-               throw std::runtime_error("Cannot create dpm-client handle.");
-
-       /// This status is defined at DPM
-       ::Status<bool> status { true };
-       Row r;
-
-       DevicePolicyClient &client = GetDevicePolicyClient(handle.get());
-       status = client.methodCall<bool>("Bluetooth::getModeChangeState");
-       r["mode_change_state"] =  INTEGER(status.get());
-
-       status = client.methodCall<bool>("Bluetooth::getDesktopConnectivityState");
-       r["desktop_connectivity_state"] =  INTEGER(status.get());
-
-       status = client.methodCall<bool>("Bluetooth::getTetheringState");
-       r["tethering_state"] =  INTEGER(status.get());
-
-       status = client.methodCall<bool>("Bluetooth::getPairingState");
-       r["paring_state"] =  INTEGER(status.get());
-
-       return { r };
-} catch (...) {
-// TODO(Sangwan): Resolve duplicated "ERROR" macro with DPM
-//    LOG(ERROR) << "Exception occured";
-       Row r;
-       return { r };
-}
-
-} // namespace tables
-} // namespace osquery
diff --git a/osquery/tizen/tables/tests/policy_tests.cpp b/osquery/tizen/tables/tests/policy_tests.cpp
deleted file mode 100644 (file)
index 13aad8f..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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 <gtest/gtest.h>
-
-#include <osquery/sql.h>
-#include <osquery/logger.h>
-
-#include <dpm/device-policy-manager.h>
-#include <dpm/pil/policy-client.h>
-
-class PolicyTests : public testing::Test {};
-
-using namespace osquery;
-
-TEST_F(PolicyTests, Bluetooth) {
-       std::shared_ptr<void> handle(dpm_manager_create(), dpm_manager_destroy);
-       if (handle == nullptr)
-               throw std::runtime_error("Cannot create dpm-client handle.");
-
-       ::Status<bool> status { true };
-
-       DevicePolicyClient &client = GetDevicePolicyClient(handle.get());
-       status = client.methodCall<bool>("Bluetooth::getModeChangeState");
-       EXPECT_EQ(true, status.get());
-
-       status = client.methodCall<bool>("Bluetooth::getDesktopConnectivityState");
-       EXPECT_EQ(true, status.get());
-
-       status = client.methodCall<bool>("Bluetooth::getTetheringState");
-       EXPECT_EQ(true, status.get());
-
-       status = client.methodCall<bool>("Bluetooth::getPairingState");
-       EXPECT_EQ(true, status.get());
-}
-
-TEST_F(PolicyTests, Wifi) {
-       std::shared_ptr<void> handle(dpm_manager_create(), dpm_manager_destroy);
-       if (handle == nullptr)
-               throw std::runtime_error("Cannot create dpm-client handle.");
-
-       ::Status<bool> status { true };
-
-       DevicePolicyClient &client = GetDevicePolicyClient(handle.get());
-       status = client.methodCall<bool>("Wifi::getState");
-       EXPECT_EQ(true, status.get());
-
-       status = client.methodCall<bool>("Wifi::isProfileChangeRestricted");
-       EXPECT_EQ(true, status.get());
-
-       status = client.methodCall<bool>("Wifi::getHotspotState");
-       EXPECT_EQ(true, status.get());
-}
-
-TEST_F(PolicyTests, Usb) {
-       std::shared_ptr<void> handle(dpm_manager_create(), dpm_manager_destroy);
-       if (handle == nullptr)
-               throw std::runtime_error("Cannot create dpm-client handle.");
-
-       ::Status<bool> status { true };
-
-       DevicePolicyClient &client = GetDevicePolicyClient(handle.get());
-       status = client.methodCall<bool>("Usb::getDebuggingState");
-       EXPECT_EQ(true, status.get());
-
-       status = client.methodCall<bool>("Usb::getTetheringState");
-       EXPECT_EQ(true, status.get());
-
-       status = client.methodCall<bool>("Usb::getClientState");
-       EXPECT_EQ(true, status.get());
-}
diff --git a/osquery/tizen/tables/usb_policy.cpp b/osquery/tizen/tables/usb_policy.cpp
deleted file mode 100644 (file)
index e9ba03c..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file usb_policy.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation of usb_policy table
- */
-
-#include <string>
-#include <memory>
-#include <stdexcept>
-
-#include <osquery/sql.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include <dpm/device-policy-manager.h>
-#include <dpm/pil/policy-client.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData genUsbPolicy(QueryContext& context) try {
-       std::shared_ptr<void> handle(dpm_manager_create(), dpm_manager_destroy);
-       if (handle == nullptr)
-               throw std::runtime_error("Cannot create dpm-client handle.");
-
-       /// This status is defined at DPM
-       ::Status<bool> status { true };
-       Row r;
-
-       DevicePolicyClient &client = GetDevicePolicyClient(handle.get());
-       status = client.methodCall<bool>("Usb::getDebuggingState");
-       r["usb_debugging"] =  INTEGER(status.get());
-
-       status = client.methodCall<bool>("Usb::getTetheringState");
-       r["usb_tethering"] =  INTEGER(status.get());
-
-       status = client.methodCall<bool>("Usb::getClientState");
-       r["usb_client"] =  INTEGER(status.get());
-
-       return { r };
-} catch (...) {
-// TODO(Sangwan): Resolve duplicated "ERROR" macro with DPM
-       Row r;
-       return { r };
-}
-
-} // namespace tables
-} // namespace osquery
diff --git a/osquery/tizen/tables/wifi_policy.cpp b/osquery/tizen/tables/wifi_policy.cpp
deleted file mode 100644 (file)
index 2a45151..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file wifi_policy.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Implementation of wifi_policy table
- */
-
-#include <string>
-#include <memory>
-#include <stdexcept>
-
-#include <osquery/sql.h>
-#include <osquery/logger.h>
-#include <osquery/tables.h>
-
-#include <dpm/device-policy-manager.h>
-#include <dpm/pil/policy-client.h>
-
-namespace osquery {
-namespace tables {
-
-QueryData genWifiPolicy(QueryContext& context) try {
-       std::shared_ptr<void> handle(dpm_manager_create(), dpm_manager_destroy);
-       if (handle == nullptr)
-               throw std::runtime_error("Cannot create dpm-client handle.");
-
-       /// This status is defined at DPM
-       ::Status<bool> status { true };
-       Row r;
-
-       DevicePolicyClient &client = GetDevicePolicyClient(handle.get());
-       status = client.methodCall<bool>("Wifi::getState");
-       r["wifi"] =  INTEGER(status.get());
-
-       status = client.methodCall<bool>("Wifi::isProfileChangeRestricted");
-       r["wifi_profile_change"] =  INTEGER(status.get());
-
-       status = client.methodCall<bool>("Wifi::getHotspotState");
-       r["wifi_hotspot"] =  INTEGER(status.get());
-
-       return { r };
-} catch (...) {
-// TODO(Sangwan): Resolve duplicated "ERROR" macro with DPM
-       Row r;
-       return { r };
-}
-
-} // namespace tables
-} // namespace osquery
diff --git a/osquery/tizen/tsqb/include/column-pack.hxx b/osquery/tizen/tsqb/include/column-pack.hxx
deleted file mode 100644 (file)
index 9688e8e..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file column-pack.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Tie different types of columns
- */
-
-#pragma once
-
-#include "type.hxx"
-
-#include <string>
-#include <vector>
-
-namespace tsqb {
-namespace internal {
-
-template<typename... Base>
-class ColumnPack {
-public:
-       virtual ~ColumnPack() = default;
-
-       template<typename ColumnType>
-       std::string getName(ColumnType&&) const noexcept { return std::string(); }
-       std::vector<std::string> getNames(void) const noexcept { return {}; }
-
-       int size() const noexcept { return 0; }
-};
-
-template<typename Front, typename... Rest>
-class ColumnPack<Front, Rest...> : public ColumnPack<Rest...> {
-public:
-       using Column = Front;
-       using TableType = typename Column::TableType;
-
-       explicit ColumnPack(Front&& front, Rest&& ...rest);
-       virtual ~ColumnPack() = default;
-
-       ColumnPack(const ColumnPack&) = delete;
-       ColumnPack& operator=(const ColumnPack&) = delete;
-
-       ColumnPack(ColumnPack&&) = default;
-       ColumnPack& operator=(ColumnPack&&) = default;
-
-       template<typename ColumnType>
-       std::string getName(ColumnType&& type) const noexcept;
-       std::vector<std::string> getNames(void) const noexcept;
-
-       int size() const noexcept { return Base::size() + 1; }
-
-private:
-       using Base = ColumnPack<Rest...>;
-
-       Column column;
-};
-
-template<typename Front, typename... Rest>
-ColumnPack<Front, Rest...>::ColumnPack(Front&& front, Rest&& ...rest) :
-       Base(std::forward<Rest>(rest)...), column(front)
-{
-}
-
-template<typename Front, typename... Rest>
-std::vector<std::string> ColumnPack<Front, Rest...>::getNames(void) const noexcept
-{
-       auto names = Base::getNames();
-       names.push_back(this->column.name);
-
-       return std::move(names);
-}
-
-template<typename Front, typename... Rest>
-template<typename ColumnType>
-std::string ColumnPack<Front, Rest...>::getName(ColumnType&& type) const noexcept
-{
-       if (type::cast_compare(column.type, std::forward<ColumnType>(type)))
-               return column.name;
-
-       return Base::template getName<ColumnType>(std::forward<ColumnType>(type));
-}
-
-} // namespace internal
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/column.hxx b/osquery/tizen/tsqb/include/column.hxx
deleted file mode 100644 (file)
index ca17415..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file culumn.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Capture member pointer of struct and use it for matching with column
- */
-
-#pragma once
-
-#include <string>
-#include <tuple>
-
-namespace tsqb {
-
-template<typename Object, typename Field>
-struct Column {
-       using Type = Field Object::*;
-       using FieldType = Field;
-       using TableType = Object;
-
-       std::string name;
-       Type type;
-};
-
-template<typename O, typename F>
-Column<O, F> make_column(const std::string& name, F O::*field)
-{
-       return {name, field};
-}
-
-template<typename Type>
-struct Distinct {
-       Type value;
-};
-
-template<typename... Args>
-auto distinct(Args&&... args) -> decltype(Distinct<std::tuple<Args...>>())
-{
-       return {std::make_tuple(std::forward<Args>(args)...)};
-}
-
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/condition.hxx b/osquery/tizen/tsqb/include/condition.hxx
deleted file mode 100644 (file)
index 7ec13da..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file condition.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Represent the condition statement of SQL
- */
-
-#pragma once
-
-#include "type.hxx"
-
-namespace tsqb {
-namespace condition {
-
-struct Base {};
-
-template<typename L, typename R>
-struct And : public Base {
-       L l;
-       R r;
-
-       And(L l, R r) : l(l), r(r) {}
-       operator std::string() const
-       {
-               return "AND";
-       }
-};
-
-template<typename L, typename R>
-struct Or : public Base {
-       L l;
-       R r;
-
-       Or(L l, R r) : l(l), r(r) {}
-       operator std::string() const
-       {
-               return "OR";
-       }
-};
-
-template<typename L, typename R>
-struct Binary : public Base {
-       L l;
-       R r;
-
-       Binary(L l, R r) : l(l), r(r)
-       {
-               using FieldType = typename L::FieldType;
-               type::assert_compare(FieldType(), r);
-       }
-};
-
-template<typename L>
-struct Binary<L, const char*> : public Base {
-       L l;
-       std::string r;
-
-       Binary(L l, const char* r) : l(l), r(r)
-       {
-               using FieldType = typename L::FieldType;
-               type::assert_compare(FieldType(), std::string());
-       }
-};
-
-enum class Join : int {
-       INNER,
-       CROSS,
-       LEFT_OUTER,
-       RIGHT_OUTER,
-       FULL_OUTER
-};
-
-inline std::string to_string(Join type)
-{
-       switch (type) {
-       case Join::CROSS: return "CROSS";
-       case Join::LEFT_OUTER: return "LEFT OUTER";
-       case Join::RIGHT_OUTER: return "RIGHT OUTER";
-       case Join::FULL_OUTER: return "FULL OUTER";
-       case Join::INNER:
-       default:
-               return "INNER";
-       }
-}
-
-} // namespace condition
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/crud.hxx b/osquery/tizen/tsqb/include/crud.hxx
deleted file mode 100644 (file)
index be48c86..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file crud.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Represent four basic functions of CRUD
- */
-
-#pragma once
-
-#include "column.hxx"
-#include "expression.hxx"
-
-#include <string>
-#include <sstream>
-
-namespace tsqb {
-
-template<typename T>
-class Crud {
-public:
-       template<typename... ColumnTypes>
-       T& select(ColumnTypes&&... cts);
-
-       template<typename Type>
-       T& select(Distinct<Type> distinct);
-
-       template<typename TableType>
-       T& selectAll(void);
-
-       T& selectAll(void);
-
-       template<typename... ColumnTypes>
-       T& update(ColumnTypes&&... cts);
-
-       template<typename... ColumnTypes>
-       T& insert(ColumnTypes&&... cts);
-
-       template<typename... ColumnTypes>
-       T& remove(ColumnTypes&&... cts);
-
-       template<typename Expr>
-       T& where(Expr expr);
-
-private:
-       template<typename ColumnTuple>
-       T& selectInternal(ColumnTuple&& ct, bool distinct = false);
-
-       template<typename L, typename R>
-       std::string processWhere(condition::And<L,R>& expr);
-
-       template<typename L, typename R>
-       std::string processWhere(condition::Or<L,R>& expr);
-
-       template<typename Expr>
-       std::string processWhere(Expr expr);
-};
-
-template<typename T>
-template<typename... ColumnTypes>
-T& Crud<T>::select(ColumnTypes&&... cts)
-{
-       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
-
-       return this->selectInternal(std::move(columnTuple));
-}
-
-template<typename T>
-template<typename Type>
-T& Crud<T>::select(Distinct<Type> distinct)
-{
-       return this->selectInternal(std::move(distinct.value), true);
-}
-
-template<typename T>
-T& Crud<T>::selectAll(void)
-{
-       static_cast<T*>(this)->cache.clear();
-
-       std::stringstream ss;
-       ss << "SELECT * FROM " << static_cast<T*>(this)->name;
-
-       static_cast<T*>(this)->cache.emplace_back(ss.str());
-
-       return *(static_cast<T*>(this));
-}
-
-template<typename T>
-template<typename TableType>
-T& Crud<T>::selectAll(void)
-{
-       static_cast<T*>(this)->cache.clear();
-
-       std::stringstream ss;
-       auto tableName = static_cast<T*>(this)->getTableName(TableType());
-       ss << "SELECT * FROM " << tableName;
-
-       static_cast<T*>(this)->cache.emplace_back(ss.str());
-
-       return *(static_cast<T*>(this));
-}
-
-template<typename T>
-template<typename ColumnTuple>
-T& Crud<T>::selectInternal(ColumnTuple&& ct, bool distinct)
-{
-       static_cast<T*>(this)->cache.clear();
-
-       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(ct));
-       auto tableNames = static_cast<T*>(this)->getTableNames(std::move(ct));
-
-       std::stringstream ss;
-       ss << "SELECT ";
-
-       if (distinct)
-               ss << "DISTINCT ";
-
-       int i = 0;
-       for (const auto& c : columnNames) {
-               ss << c;
-
-               if (i++ < columnNames.size() - 1)
-                       ss << ", ";
-       }
-
-       ss << " FROM ";
-
-       i = 0;
-       for (const auto& t : tableNames) {
-               ss << t;
-
-               if (i++ < tableNames.size() - 1)
-                       ss << ", ";
-       }
-
-       static_cast<T*>(this)->cache.emplace_back(ss.str());
-
-       return *(static_cast<T*>(this));
-}
-
-template<typename T>
-template<typename... ColumnTypes>
-T& Crud<T>::update(ColumnTypes&&... cts)
-{
-       static_cast<T*>(this)->cache.clear();
-
-       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
-       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(columnTuple));
-
-       std::stringstream ss;
-       ss << "UPDATE " << static_cast<T*>(this)->name << " ";
-       ss << "SET ";
-
-       int i = 0;
-       for (const auto& c : columnNames) {
-               ss << c << " = ?";
-
-               if (i++ < columnNames.size() - 1)
-                       ss << ", ";
-       }
-
-       static_cast<T*>(this)->cache.emplace_back(ss.str());
-
-       return *(static_cast<T*>(this));
-}
-
-template<typename T>
-template<typename... ColumnTypes>
-T& Crud<T>::insert(ColumnTypes&&... cts)
-{
-       static_cast<T*>(this)->cache.clear();
-
-       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
-       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(columnTuple));
-
-       std::stringstream ss;
-       ss << "INSERT INTO " << static_cast<T*>(this)->name << " (";
-
-       const int columnCount = columnNames.size();
-       for (int i = 0; i < columnCount; i++) {
-               ss << columnNames[i];
-               if (i < columnCount - 1)
-                       ss << ", ";
-       }
-
-       ss << ") VALUES (";
-
-       for (int i = 0; i < columnCount; i++) {
-               ss << "?";
-               if (i < columnCount - 1)
-                       ss << ", ";
-       }
-
-       ss << ")";
-
-       static_cast<T*>(this)->cache.emplace_back(ss.str());
-
-       return *(static_cast<T*>(this));
-}
-
-template<typename T>
-template<typename... ColumnTypes>
-T& Crud<T>::remove(ColumnTypes&&... cts)
-{
-       static_cast<T*>(this)->cache.clear();
-
-       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
-       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(columnTuple));
-
-       std::stringstream ss;
-       ss << "DELETE FROM " << static_cast<T*>(this)->name;
-
-       static_cast<T*>(this)->cache.emplace_back(ss.str());
-
-       return *(static_cast<T*>(this));
-}
-
-template<typename T>
-template<typename Expr>
-T& Crud<T>::where(Expr expr)
-{
-       std::stringstream ss;
-       ss << "WHERE " << this->processWhere(expr);
-
-       static_cast<T*>(this)->cache.emplace_back(ss.str());
-
-       return *(static_cast<T*>(this));
-}
-
-template<typename T>
-template<typename L, typename R>
-std::string Crud<T>::processWhere(condition::And<L,R>& expr)
-{
-       std::stringstream ss;
-       ss << this->processWhere(expr.l) << " ";
-       ss << static_cast<std::string>(expr) << " ";
-       ss << this->processWhere(expr.r);
-
-       return ss.str();
-}
-
-template<typename T>
-template<typename L, typename R>
-std::string Crud<T>::processWhere(condition::Or<L,R>& expr)
-{
-       std::stringstream ss;
-       ss << this->processWhere(expr.l) << " ";
-       ss << static_cast<std::string>(expr) << " ";
-       ss << this->processWhere(expr.r);
-
-       return ss.str();
-}
-
-template<typename T>
-template<typename Expr>
-std::string Crud<T>::processWhere(Expr expr)
-{
-       std::stringstream ss;
-       ss << static_cast<T*>(this)->getColumnName(expr.l.type);
-       ss << " " << std::string(expr) << " ?";
-
-       return ss.str();
-}
-
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/database.hxx b/osquery/tizen/tsqb/include/database.hxx
deleted file mode 100644 (file)
index 0c8bf54..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file database.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Represent the database scheme of SQL
- */
-
-#pragma once
-
-#include "crud.hxx"
-#include "condition.hxx"
-#include "expression.hxx"
-#include "table-pack.hxx"
-#include "tuple-helper.hxx"
-#include "util.hxx"
-
-#include <vector>
-#include <set>
-#include <string>
-#include <sstream>
-#include <algorithm>
-
-namespace tsqb {
-
-template<typename... Tables>
-class Database : public Crud<Database<Tables...>> {
-public:
-       using Self = Database<Tables...>;
-
-       virtual ~Database() = default;
-
-       Database(const Database&) = delete;
-       Database& operator=(const Database&) = delete;
-
-       Database(Database&&) = default;
-       Database& operator=(Database&&) = default;
-
-       // Functions for Crud
-       template<typename Cs>
-       std::set<std::string> getTableNames(Cs&& tuple) const noexcept;
-       template<typename Cs>
-       std::vector<std::string> getColumnNames(Cs&& tuple) const noexcept;
-       template<typename TableType>
-       std::string getTableName(TableType&& type) const noexcept;
-       template<typename ColumnType>
-       std::string getColumnName(ColumnType&& type) const noexcept;
-
-       template<typename Table>
-       Self& join(condition::Join type = condition::Join::INNER);
-
-       template<typename Expr>
-       Self& on(Expr expr);
-
-       operator std::string();
-
-       std::string name;
-       std::vector<std::string> cache;
-
-private:
-       using TablePackType = internal::TablePack<Tables...>;
-       using ColumnNames = std::vector<std::string>;
-       using TableNames = std::set<std::string>;
-
-       explicit Database(const std::string& name, TablePackType&& tablePack);
-
-       template<typename ...Ts>
-       friend Database<Ts...> make_database(const std::string& name, Ts&& ...tables);
-
-       struct GetTableNames {
-               const TablePackType& tablePack;
-               std::set<std::string> names;
-               GetTableNames(const TablePackType& tablePack) : tablePack(tablePack) {}
-
-               template <typename T>
-               void operator()(T&& type)
-               {
-                       auto column = make_column("anonymous", type);
-                       using TableType = typename decltype(column)::TableType;
-                       auto name = this->tablePack.getName(TableType());
-                       if (!name.empty())
-                               names.emplace(name);
-               }
-       };
-
-       struct GetColumnNames {
-               const TablePackType& tablePack;
-               std::vector<std::string> names;
-
-               GetColumnNames(const TablePackType& tablePack) : tablePack(tablePack) {}
-
-               template <typename T>
-               void operator()(T&& type)
-               {
-                       auto column = make_column("anonymous", type);
-                       auto name = this->tablePack.getColumnName(std::move(column));
-                       if (!name.empty())
-                               names.emplace_back(name);
-               }
-       };
-
-       TablePackType tablePack;
-};
-
-template<typename ...Tables>
-Database<Tables...> make_database(const std::string& name, Tables&& ...tables)
-{
-       auto tablePack = internal::TablePack<Tables...>(std::forward<Tables>(tables)...);
-       return Database<Tables...>(name, std::move(tablePack));
-}
-
-template<typename ...Tables>
-Database<Tables...>::Database(const std::string& name, TablePackType&& tablePack)
-       : name(name), tablePack(std::move(tablePack)) {}
-
-template<typename... Tables>
-template<typename Table>
-Database<Tables...>& Database<Tables...>::join(condition::Join type)
-{
-       std::stringstream ss;
-       ss << condition::to_string(type) << " ";
-       ss << "JOIN ";
-       ss << this->tablePack.getName(Table());
-
-       this->cache.emplace_back(ss.str());
-       return *this;
-}
-
-template<typename... Tables>
-template<typename Expr>
-Database<Tables...>& Database<Tables...>::on(Expr expr)
-{
-       std::stringstream ss;
-       ss << "ON ";
-
-       auto lname = this->tablePack.getColumnName(std::move(expr.l));
-       ss << lname << " ";
-
-       ss << std::string(expr) << " ";
-
-       auto rname = this->tablePack.getColumnName(std::move(expr.r));
-       ss << rname;
-
-       this->cache.emplace_back(ss.str());
-       return *this;
-}
-
-template<typename... Tables>
-Database<Tables...>::operator std::string()
-{
-       std::stringstream ss;
-       for (const auto& c : cache)
-               ss << c << " ";
-
-       this->cache.clear();
-       return util::rtrim(ss.str());
-}
-
-template<typename... Tables>
-template<typename Cs>
-std::set<std::string> Database<Tables...>::getTableNames(Cs&& tuple) const noexcept
-{
-       GetTableNames closure(this->tablePack);
-       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
-
-       return closure.names;
-}
-
-template<typename... Tables>
-template<typename Cs>
-std::vector<std::string> Database<Tables...>::getColumnNames(Cs&& tuple) const noexcept
-{
-       GetColumnNames closure(this->tablePack);
-       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
-
-       return closure.names;
-}
-
-template<typename... Tables>
-template<typename TableType>
-std::string Database<Tables...>::getTableName(TableType&& type) const noexcept
-{
-       return this->tablePack.getName(std::forward<TableType>(type));
-}
-
-template<typename... Tables>
-template<typename ColumnType>
-std::string Database<Tables...>::getColumnName(ColumnType&& type) const noexcept
-{
-       auto column = make_column("anonymous", type);
-       return this->tablePack.getColumnName(std::move(column));
-}
-
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/expression.hxx b/osquery/tizen/tsqb/include/expression.hxx
deleted file mode 100644 (file)
index c901ab0..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file expression.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Represent the expression of SQL
- */
-
-#pragma once
-
-#include "column.hxx"
-#include "type.hxx"
-#include "condition.hxx"
-
-#include <type_traits>
-
-namespace tsqb {
-
-template<typename Type>
-struct Expression {
-       Type value;
-};
-
-template<typename O, typename F>
-Expression<Column<O, F>> expr(F O::*field)
-{
-       Column<O, F> anonymous = {"anonymous", field};
-       return {anonymous};
-}
-
-template<typename L, typename R>
-struct Lesser : public condition::Binary<L, R> {
-       using condition::Binary<L, R>::Binary;
-
-       operator std::string() const
-       {
-               return "<";
-       }
-};
-
-template<typename L, typename R>
-Lesser<L, R> operator<(Expression<L> expr, R r)
-{
-       return {expr.value, r};
-}
-
-template<typename L, typename R>
-struct Equal : public condition::Binary<L, R>
-{
-       using condition::Binary<L, R>::Binary;
-
-       operator std::string() const
-       {
-               return "=";
-       }
-};
-
-template<typename L, typename R>
-Equal<L, R> operator==(Expression<L> expr, R r)
-{
-       return {expr.value, r};
-}
-
-namespace join {
-
-template<typename L, typename R>
-struct Equal
-{
-       L l;
-       R r;
-
-       operator std::string() const
-       {
-               return "=";
-       }
-};
-
-} // namespace join
-
-template<typename L, typename R>
-join::Equal<L, R> operator==(Expression<L> l, Expression<R> r)
-{
-       return {l.value, r.value};
-}
-
-template<typename L, typename R>
-struct Greater : public condition::Binary<L, R>
-{
-       using condition::Binary<L, R>::Binary;
-
-       operator std::string() const
-       {
-               return ">";
-       }
-};
-
-template<typename L, typename R>
-Greater<L, R> operator>(Expression<L> expr, R r)
-{
-       return {expr.value, r};
-}
-
-template<bool B, typename T = void>
-using enable_if_t = typename std::enable_if<B, T>::type;
-
-template<typename T>
-using is_condition = typename std::is_base_of<condition::Base, T>;
-
-template<typename L,
-                typename R,
-                typename = typename tsqb::enable_if_t<is_condition<L>::value
-                                                        && tsqb::is_condition<R>::value>>
-condition::And<L, R> operator&&(const L& l, const R& r)
-{
-       return {l, r};
-}
-
-template<typename L,
-                typename R,
-                typename = typename tsqb::enable_if_t<is_condition<L>::value
-                                                        && tsqb::is_condition<R>::value>>
-condition::Or<L, R> operator||(const L& l, const R& r)
-{
-       return {l, r};
-}
-
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/table-pack.hxx b/osquery/tizen/tsqb/include/table-pack.hxx
deleted file mode 100644 (file)
index d192886..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file table-pack.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Tie diffirent types of tables
- */
-
-#pragma once
-
-#include <vector>
-#include <set>
-#include <string>
-
-namespace tsqb {
-namespace internal {
-
-template<typename... Base>
-class TablePack {
-public:
-       virtual ~TablePack() = default;
-
-       template<typename TableType>
-       std::string getName(TableType&&) const noexcept { return std::string(); }
-
-       template<typename ColumnType>
-       std::string getColumnName(ColumnType&&) const noexcept { return std::string(); }
-};
-
-template<typename Front, typename... Rest>
-class TablePack<Front, Rest...> : public TablePack<Rest...> {
-public:
-       using Table = Front;
-
-       explicit TablePack(Front&& front, Rest&& ...rest);
-       virtual ~TablePack() = default;
-
-       TablePack(const TablePack&) = delete;
-       TablePack& operator=(const TablePack&) = delete;
-
-       TablePack(TablePack&&) = default;
-       TablePack& operator=(TablePack&&) = default;
-
-       template<typename TableType>
-       std::string getName(TableType&& table) const noexcept;
-
-       template<typename ColumnType>
-       std::string getColumnName(ColumnType&& column) const noexcept;
-
-private:
-       using Base = TablePack<Rest...>;
-
-       Table table;
-};
-
-template<typename Front, typename... Rest>
-TablePack<Front, Rest...>::TablePack(Front&& front, Rest&& ...rest) :
-       Base(std::forward<Rest>(rest)...), table(front)
-{
-}
-
-template<typename Front, typename... Rest>
-template<typename TableType>
-std::string TablePack<Front, Rest...>::getName(TableType&& table) const noexcept
-{
-       if (this->table.compare(table))
-               return this->table.name;
-
-       return Base::template getName<TableType>(std::forward<TableType>(table));
-}
-
-template<typename Front, typename... Rest>
-template<typename ColumnType>
-std::string TablePack<Front, Rest...>::getColumnName(ColumnType&& column) const noexcept
-{
-       using DecayColumnType = typename std::decay<ColumnType>::type;
-       using DecayTableType = typename DecayColumnType::TableType;
-       if (this->table.compare(DecayTableType())) {
-               auto cname = this->table.getColumnName(column.type);
-               return this->table.name + "." + cname;
-       }
-
-       return Base::template getColumnName<ColumnType>(std::forward<ColumnType>(column));
-}
-
-} // namespace internal
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/table.hxx b/osquery/tizen/tsqb/include/table.hxx
deleted file mode 100644 (file)
index e1ac527..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file table.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Represent the table of SQL
- */
-
-#pragma once
-
-#include "crud.hxx"
-#include "column.hxx"
-#include "column-pack.hxx"
-#include "tuple-helper.hxx"
-#include "util.hxx"
-
-#include <vector>
-#include <string>
-#include <sstream>
-
-namespace tsqb {
-
-template<typename... Columns>
-class Table : public Crud<Table<Columns...>> {
-public:
-       virtual ~Table() = default;
-
-       Table(const Table&) = delete;
-       Table& operator=(const Table&) = delete;
-
-       Table(Table&&) = default;
-       Table& operator=(Table&&) = default;
-
-       // Functions for Crud
-       template<typename Cs>
-       std::set<std::string> getTableNames(Cs&& tuple) const noexcept;
-       template<typename Cs>
-       std::vector<std::string> getColumnNames(Cs&& tuple) const noexcept;
-       template<typename That>
-       std::string getTableName(That&& type) const noexcept;
-       template<typename ColumnType>
-       std::string getColumnName(ColumnType&& type) const noexcept;
-
-       std::vector<std::string> getColumnNames(void) const noexcept;
-
-       template<typename That>
-       bool compare(const That& that) const noexcept;
-
-       int size() const noexcept;
-
-       operator std::string();
-
-       std::string name;
-       std::vector<std::string> cache;
-
-private:
-       using ColumnPackType = internal::ColumnPack<Columns...>;
-       using TableType = typename ColumnPackType::TableType;
-
-       explicit Table(const std::string& name, ColumnPackType&& columnPack);
-
-       template<typename ...Cs>
-       friend Table<Cs...> make_table(const std::string& name, Cs&& ...columns);
-
-       struct GetColumnNames {
-               const ColumnPackType& columnPack;
-               std::vector<std::string> names;
-
-               GetColumnNames(const ColumnPackType& columnPack) : columnPack(columnPack) {}
-
-               template <typename T>
-               void operator()(T&& type)
-               {
-                       auto name = this->columnPack.getName(std::forward<T>(type));
-                       if (!name.empty())
-                               names.emplace_back(name);
-               }
-       };
-
-       ColumnPackType columnPack;
-};
-
-template<typename ...Columns>
-Table<Columns...> make_table(const std::string& name, Columns&& ...cs)
-{
-       auto columnPack = internal::ColumnPack<Columns...>(std::forward<Columns>(cs)...);
-       return Table<Columns...>(name, std::move(columnPack));
-}
-
-template<typename... Columns>
-Table<Columns...>::Table(const std::string& name, ColumnPackType&& columnPack)
-       : name(name), columnPack(std::move(columnPack)) {}
-
-template<typename... Columns>
-template<typename Cs>
-std::set<std::string> Table<Columns...>::getTableNames(Cs&& tuple) const noexcept
-{
-       return {this->name};
-}
-
-template<typename... Columns>
-template<typename Cs>
-std::vector<std::string> Table<Columns...>::getColumnNames(Cs&& tuple) const noexcept
-{
-       GetColumnNames closure(this->columnPack);
-       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
-
-       return closure.names;
-}
-
-template<typename... Columns>
-template<typename That>
-std::string Table<Columns...>::getTableName(That&& type) const noexcept
-{
-       return this->name;
-}
-
-template<typename... Columns>
-template<typename ColumnType>
-std::string Table<Columns...>::getColumnName(ColumnType&& type) const noexcept
-{
-       return this->columnPack.getName(std::forward<ColumnType>(type));
-}
-
-template<typename... Columns>
-std::vector<std::string> Table<Columns...>::getColumnNames(void) const noexcept
-{
-       return this->columnPack.getNames();
-}
-
-template<typename... Columns>
-template<typename That>
-bool Table<Columns...>::compare(const That& that) const noexcept
-{
-       using This = TableType;
-       return type::compare(This(), that);
-}
-
-template<typename... Columns>
-Table<Columns...>::operator std::string()
-{
-       std::stringstream ss;
-       for (const auto& c : cache)
-               ss << c << " ";
-
-       this->cache.clear();
-       return util::rtrim(ss.str());
-}
-
-template<typename... Columns>
-int Table<Columns...>::size() const noexcept
-{
-       return this->columnPack.size();
-}
-
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/tuple-helper.hxx b/osquery/tizen/tsqb/include/tuple-helper.hxx
deleted file mode 100644 (file)
index 495946e..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file tuple-helper.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Iterator method for tuple
- */
-
-#pragma once
-
-#include <tuple>
-
-namespace tsqb {
-namespace tuple_helper {
-namespace internal {
-
-template<int n, typename T, typename C>
-class Iterator {
-public:
-       Iterator(const T& tuple, C&& closure) {
-               Iterator<n - 1, T, C> iter(tuple, std::forward<C>(closure));
-               closure(std::get<n-1>(tuple));
-       }
-};
-
-template<typename T, typename C>
-class Iterator<0, T, C> {
-public:
-       Iterator(const T&, C&&) {}
-};
-
-} // namespace internal
-
-template<typename T, typename C>
-void for_each(const T& tuple, C&& closure)
-{
-       using Iter = internal::Iterator<std::tuple_size<T>::value, T, C>;
-       Iter iter(tuple, std::forward<C>(closure));
-}
-
-} // namspace tuple-hepler
-} // namspace tsqb
diff --git a/osquery/tizen/tsqb/include/type.hxx b/osquery/tizen/tsqb/include/type.hxx
deleted file mode 100644 (file)
index 939b3da..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file type.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Make type safety with compile time checking
- */
-
-#pragma once
-
-#include <type_traits>
-
-namespace tsqb {
-namespace type {
-
-template<typename L, typename R>
-bool cast_compare(L l, R r)
-{
-       return l == reinterpret_cast<L>(r);
-}
-
-template<typename L, typename R>
-bool compare(L l, R r)
-{
-       return std::is_same<L, R>::value;
-}
-
-template<typename L, typename R>
-void assert_compare(L l, R r)
-{
-       static_assert(std::is_same<L, R>::value, "Type is unsafe.");
-}
-
-} // namespace type
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/include/util.hxx b/osquery/tizen/tsqb/include/util.hxx
deleted file mode 100644 (file)
index e65583d..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file util.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- */
-
-#pragma once
-
-#include <string>
-#include <algorithm>
-#include <cctype>
-
-namespace tsqb {
-namespace util {
-
-inline std::string rtrim(std::string&& s)
-{
-       auto predicate = [](unsigned char c){ return !std::isspace(c); };
-       auto base = std::find_if(s.rbegin(), s.rend(), predicate).base();
-       s.erase(base, s.end());
-       return s;
-}
-
-} // namespace util
-} // namespace tsqb
diff --git a/osquery/tizen/tsqb/tests/tsqb-tests.cpp b/osquery/tizen/tsqb/tests/tsqb-tests.cpp
deleted file mode 100644 (file)
index 7770140..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file tsqb-tests.cpp
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief Testcases of tsqb
- */
-
-#include "tsqb.hxx"
-
-#include <gtest/gtest.h>
-
-using namespace tsqb;
-
-struct Admin {
-       int id;
-       std::string pkg;
-       int uid;
-       std::string key;
-       int removable;
-};
-
-struct ManagedPolicy {
-       int id;
-       int aid;
-       int pid;
-       int value;
-};
-
-struct PolicyDefinition {
-       int id;
-       int scope;
-       std::string name;
-       int ivalue;
-};
-
-auto admin = make_table("admin", make_column("id", &Admin::id),
-                                                                make_column("pkg", &Admin::pkg),
-                                                                make_column("uid", &Admin::uid),
-                                                                make_column("key", &Admin::key),
-                                                                make_column("removable", &Admin::removable));
-
-auto managedPolicy = make_table("managed_policy",
-                                                                make_column("id", &ManagedPolicy::id),
-                                                                make_column("aid", &ManagedPolicy::aid),
-                                                                make_column("pid", &ManagedPolicy::pid),
-                                                                make_column("value", &ManagedPolicy::value));
-
-auto policyDefinition = make_table("policy_definition",
-                                                                  make_column("id", &PolicyDefinition::id),
-                                                                  make_column("scope", &PolicyDefinition::scope),
-                                                                  make_column("name", &PolicyDefinition::name),
-                                                                  make_column("ivalue", &PolicyDefinition::ivalue));
-
-auto db = make_database("dpm", admin, managedPolicy, policyDefinition);
-
-class TsqbTests : public testing::Test {};
-
-TEST_F(TsqbTests, SELECT)
-{
-       std::string select1 = admin.select(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
-       std::string select2 = admin.select(&Admin::id, &Admin::uid, &Admin::key);
-
-       EXPECT_EQ(select1, "SELECT id, pkg, uid, key FROM admin");
-       EXPECT_EQ(select2, "SELECT id, uid, key FROM admin");
-}
-
-TEST_F(TsqbTests, SELECT_ALL)
-{
-       std::string select = admin.selectAll();
-
-       EXPECT_EQ(select, "SELECT * FROM admin");
-}
-
-TEST_F(TsqbTests, SELECT_WHERE)
-{
-       std::string select1 = admin.select(&Admin::uid, &Admin::key)
-                                                          .where(expr(&Admin::id) > 3);
-       std::string select2 = admin.selectAll().where(expr(&Admin::uid) > 3);
-       std::string select3 = admin.selectAll().where(expr(&Admin::uid) > 3 &&
-                                                                                                 expr(&Admin::pkg) == "dpm");
-       std::string select4 = admin.selectAll().where(expr(&Admin::uid) > 3 ||
-                                                                                                 expr(&Admin::pkg) == "dpm");
-
-       EXPECT_EQ(select1, "SELECT uid, key FROM admin WHERE id > ?");
-       EXPECT_EQ(select2, "SELECT * FROM admin WHERE uid > ?");
-       EXPECT_EQ(select3, "SELECT * FROM admin WHERE uid > ? AND pkg = ?");
-       EXPECT_EQ(select4, "SELECT * FROM admin WHERE uid > ? OR pkg = ?");
-}
-
-TEST_F(TsqbTests, SELECT_DISTINCT)
-{
-       std::string select = admin.select(distinct(&Admin::uid, &Admin::key))
-                                                          .where(expr(&Admin::id) > 3);
-
-       EXPECT_EQ(select, "SELECT DISTINCT uid, key FROM admin WHERE id > ?");
-}
-
-TEST_F(TsqbTests, UPDATE)
-{
-       int uid = 0, id = 1;
-       std::string update1 = admin.update(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
-       std::string update2 = admin.update(&Admin::key).where(expr(&Admin::uid) == uid &&
-                                                                                                                 expr(&Admin::id) == id);
-       std::string update3 = admin.update(&Admin::key, &Admin::pkg)
-                                                          .where(expr(&Admin::uid) == 0 && expr(&Admin::id) == 1);
-
-       EXPECT_EQ(update1, "UPDATE admin SET id = ?, pkg = ?, uid = ?, key = ?");
-       EXPECT_EQ(update2, "UPDATE admin SET key = ? WHERE uid = ? AND id = ?");
-       EXPECT_EQ(update3, "UPDATE admin SET key = ?, pkg = ? WHERE uid = ? AND id = ?");
-}
-
-TEST_F(TsqbTests, DELETE)
-{
-       std::string delete1 = admin.remove();
-       std::string delete2 = admin.remove().where(expr(&Admin::pkg) == "dpm" &&
-                                                                                          expr(&Admin::uid) == 3);
-
-       EXPECT_EQ(delete1, "DELETE FROM admin");
-       EXPECT_EQ(delete2, "DELETE FROM admin WHERE pkg = ? AND uid = ?");
-}
-
-TEST_F(TsqbTests, INSERT)
-{
-       std::string insert1 = admin.insert(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
-       std::string insert2 = admin.insert(&Admin::id, &Admin::pkg, &Admin::key);
-
-       EXPECT_EQ(insert1, "INSERT INTO admin (id, pkg, uid, key) VALUES (?, ?, ?, ?)");
-       EXPECT_EQ(insert2, "INSERT INTO admin (id, pkg, key) VALUES (?, ?, ?)");
-}
-
-TEST_F(TsqbTests, TYPE_SAFE)
-{
-/*
- * Below cause complie error since expression types are dismatch.
-
-       std::string type_unsafe1 = admin.selectAll().where(expr(&Admin::uid) > "dpm");
-       std::string type_unsafe2 = admin.selectAll().where(expr(&Admin::uid) == "dpm");
-       std::string type_unsafe3 = admin.selectAll().where(expr(&Admin::pkg) == 3);
-       int pkg = 3;
-       std::string type_unsafe4 = admin.selectAll().where(expr(&Admin::pkg) < pkg);
-       std::string type_unsafe5 = admin.remove().where(expr(&Admin::pkg) == "dpm" &&
-                                                                                                       expr(&Admin::uid) == "dpm");
-*/
-}
-
-TEST_F(TsqbTests, MULTI_SELECT)
-{
-       std::string multiSelect1 = db.select(&Admin::uid, &Admin::key,
-                                                                                &ManagedPolicy::id, &ManagedPolicy::value);
-       std::string multiSelect2 = db.select(&Admin::uid, &Admin::key,
-                                                                                &ManagedPolicy::id, &ManagedPolicy::value)
-                                                                .where(expr(&Admin::uid) > 0 && expr(&ManagedPolicy::id) == 3);
-
-       EXPECT_EQ(multiSelect1, "SELECT admin.uid, admin.key, managed_policy.id, "
-                                                       "managed_policy.value FROM admin, managed_policy");
-       EXPECT_EQ(multiSelect2, "SELECT admin.uid, admin.key, managed_policy.id, "
-                                                       "managed_policy.value FROM admin, managed_policy "
-                                                       "WHERE admin.uid > ? AND managed_policy.id = ?");
-}
-
-TEST_F(TsqbTests, JOIN)
-{
-       std::string join1 = db.select(&Admin::uid, &Admin::key)
-                                                 .join<PolicyDefinition>(condition::Join::LEFT_OUTER);
-       std::string join2 = db.select(&Admin::uid, &Admin::key)
-                                                 .join<ManagedPolicy>(condition::Join::CROSS);
-       std::string join3 = db.select(&ManagedPolicy::value)
-                                                 .join<PolicyDefinition>()
-                                                 .on(expr(&ManagedPolicy::pid) == expr(&PolicyDefinition::id))
-                                                 .join<Admin>()
-                                                 .on(expr(&ManagedPolicy::aid) == expr(&Admin::id))
-                                                 .where(expr(&ManagedPolicy::pid) == 99);
-
-       EXPECT_EQ(join1, "SELECT admin.uid, admin.key FROM admin "
-                                        "LEFT OUTER JOIN policy_definition");
-       EXPECT_EQ(join2, "SELECT admin.uid, admin.key FROM admin "
-                                        "CROSS JOIN managed_policy");
-       EXPECT_EQ(join3, "SELECT managed_policy.value FROM managed_policy "
-                                        "INNER JOIN policy_definition "
-                                        "ON managed_policy.pid = policy_definition.id "
-                                        "INNER JOIN admin ON managed_policy.aid = admin.id "
-                                        "WHERE managed_policy.pid = ?");
-}
diff --git a/osquery/tizen/tsqb/tsqb.hxx b/osquery/tizen/tsqb/tsqb.hxx
deleted file mode 100644 (file)
index c3791f0..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
- *
- *  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
- */
-/*
- * @file tsqb.hxx
- * @author Sangwan Kwon (sangwan.kwon@samsung.com)
- * @brief TSQB is type-safe query builder
- */
-
-#pragma once
-
-#include "include/database.hxx"
-#include "include/table.hxx"
-#include "include/column.hxx"
-#include "include/expression.hxx"
-#include "include/condition.hxx"
-#include "include/util.hxx"
index 7a14004aa2d57f2255ad103f758b77a10b100209..341b6e1dac4750ea002e0dc908e11500d949e937 100644 (file)
@@ -49,7 +49,6 @@ This allows you to write SQL-based queries to explore operating system data.
 %files
 %manifest %{name}.manifest
 %{_bindir}/osqueryi
-%{_bindir}/osqueryd
 
 %prep
 %setup -q
@@ -81,6 +80,7 @@ Testcases for osquery
 %files test
 %manifest %{name}.manifest
 %{_bindir}/osquery-test
+%{_bindir}/apix-test
 
 ## DPM Plugins - ############################################################
 %package plugins
index c8027f0b65c72a1e7660ff72b8e7e20ec0ad7533..c85d32fb7c2088d7cb8bde7b206cbd59c3886bf4 100644 (file)
@@ -15,6 +15,8 @@
 
 INCLUDE(FindPkgConfig)
 
+SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,noexecstack")
+
 ADD_SUBDIRECTORY(bluetooth)
 ADD_SUBDIRECTORY(usb)
 ADD_SUBDIRECTORY(wifi)
index d8a1ea2f12b4feec92da908d6041e87d2d2abf80..639fd8060fc7c85c3a8cf95b09d0da593056413e 100644 (file)
@@ -23,11 +23,10 @@ SET(DEPENDENCY klay
 
 PKG_CHECK_MODULES(PLUGIN_DEPS REQUIRED ${DEPENDENCY})
 
-SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,noexecstack")
+INCLUDE_DIRECTORIES(SYSTEM ${PLUGIN_DEPS_INCLUDE_DIRS})
 
 ADD_LIBRARY(${TARGET} SHARED ${PLUGIN_SOURCES})
 SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_FLAGS "-fvisibility=default")
-INCLUDE_DIRECTORIES(SYSTEM ${PLUGIN_DEPS_INCLUDE_DIRS})
 TARGET_LINK_LIBRARIES(${TARGET} ${PLUGIN_DEPS_LIBRARIES})
 
 INSTALL(FILES libdpm-plugin-bluetooth.so
index 777e8f4685870b465ba703749aa5eb48c253d6c2..9137d4849526af75b8450472fd64a3b03e5f32eb 100644 (file)
@@ -21,13 +21,19 @@ SET(DEPENDENCY klay
                           capi-network-wifi-manager
                           capi-network-connection)
 
+
+       SET(GBS_ONLY_PACKAGES klay
+                                                 dpm-pil
+                                                 capi-base-common
+                                                 capi-system-info
+                                                 capi-network-wifi-manager)
+
 PKG_CHECK_MODULES(PLUGIN_DEPS REQUIRED ${DEPENDENCY})
 
-SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,noexecstack")
+INCLUDE_DIRECTORIES(SYSTEM ${PLUGIN_DEPS_INCLUDE_DIRS})
 
 ADD_LIBRARY(${TARGET} SHARED ${PLUGIN_SOURCES})
 SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_FLAGS "-fvisibility=default")
-INCLUDE_DIRECTORIES(SYSTEM ${PLUGIN_DEPS_INCLUDE_DIRS})
 TARGET_LINK_LIBRARIES(${TARGET} ${PLUGIN_DEPS_LIBRARIES})
 
 INSTALL(FILES libdpm-plugin-wifi.so
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6b4bfcd
--- /dev/null
@@ -0,0 +1,21 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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_DIRECTORIES(SYSTEM .)
+
+SET(TARGET_OSQUERY_LIB osquery)
+SET(TARGET_APIX_LIB apix)
+
+ADD_SUBDIRECTORY(osquery)
+ADD_SUBDIRECTORY(apix)
diff --git a/src/apix/CMakeLists.txt b/src/apix/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b5c12fb
--- /dev/null
@@ -0,0 +1,43 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+SET(TARGET_APIX_TEST apix-test)
+
+SET(${TARGET_APIX_LIB}_SRCS "")
+SET(${TARGET_APIX_LIB}_DEPS "")
+SET(${TARGET_APIX_LIB}_TESTS "")
+
+ADD_SUBDIRECTORY(property)
+ADD_SUBDIRECTORY(manager)
+ADD_SUBDIRECTORY(notification)
+
+ADD_LIBRARY(${TARGET_APIX_LIB} STATIC ${${TARGET_APIX_LIB}_SRCS})
+TARGET_LINK_LIBRARIES(${TARGET_APIX_LIB} ${${TARGET_APIX_LIB}_DEPS}
+                                                                                ${TARGET_OSQUERY_LIB})
+
+ADD_EXECUTABLE(${TARGET_APIX_TEST} main/tests.cpp
+                                                                  ${${TARGET_APIX_LIB}_TESTS})
+TARGET_LINK_LIBRARIES(${TARGET_APIX_TEST} ${TARGET_APIX_LIB}
+                                                                                  gtest)
+TARGET_LINK_WHOLE(${TARGET_APIX_TEST} ${TARGET_OSQUERY_LIB})
+ADD_TEST(${TARGET_APIX_TEST} ${TARGET_APIX_TEST})
+INSTALL(TARGETS ${TARGET_APIX_TEST}
+               DESTINATION ${CMAKE_INSTALL_BINDIR}
+               PERMISSIONS OWNER_READ
+                                       OWNER_WRITE
+                                       OWNER_EXECUTE
+                                       GROUP_READ
+                                       GROUP_EXECUTE
+                                       WORLD_READ
+                                       WORLD_EXECUTE)
diff --git a/src/apix/main/tests.cpp b/src/apix/main/tests.cpp
new file mode 100644 (file)
index 0000000..e249cbe
--- /dev/null
@@ -0,0 +1,6 @@
+#include <gtest/gtest.h>
+
+int main(int argc, char* argv[]) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/src/apix/manager/CMakeLists.txt b/src/apix/manager/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3d466f3
--- /dev/null
@@ -0,0 +1,19 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_APIX_LIBRARY(manager manager.cpp
+                                                manager_impl.cpp)
+
+FILE(GLOB MANAGER_TESTS "tests/*.cpp")
+ADD_APIX_TEST(${MANAGER_TESTS})
diff --git a/src/apix/manager/manager.cpp b/src/apix/manager/manager.cpp
new file mode 100644 (file)
index 0000000..2ff4f97
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file manager.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of osquery manager
+ */
+
+#include <osquery_manager.h>
+
+#include "manager_impl.h"
+
+namespace osquery {
+
+Rows OsqueryManager::execute(const std::string& query)
+{
+       return ManagerImpl::instance().execute(query);
+}
+
+void OsqueryManager::subscribe(const std::string& table, const Callback& callback)
+{
+       return ManagerImpl::instance().subscribe(table, callback);
+}
+
+std::vector<std::string> OsqueryManager::tables(void) noexcept
+{
+       return ManagerImpl::instance().tables();
+}
+
+std::vector<std::string> OsqueryManager::columns(const std::string& table) noexcept
+{
+       return ManagerImpl::instance().columns(table);
+}
+
+} // namespace osquery
diff --git a/src/apix/manager/manager_impl.cpp b/src/apix/manager/manager_impl.cpp
new file mode 100644 (file)
index 0000000..2e80e0a
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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 ManagerImplied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file manager_impl.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of osquery manager's impl
+ */
+
+#include "manager_impl.h"
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+#include <osquery/notification.h>
+#include <osquery/sql.h>
+#include <osquery/status.h>
+
+#include <gflags/gflags.h>
+
+#include <boost/filesystem/operations.hpp>
+
+#include <functional>
+#include <sstream>
+
+namespace osquery {
+
+ManagerImpl::ManagerImpl()
+{
+       auto logDir = Flag::getValue("osquery_log_dir");
+       if (!logDir.empty() && !(pathExists(logDir).ok()))
+               boost::filesystem::create_directories(logDir);
+
+       LOG(INFO) << "Initalize osquery manager. ";
+}
+
+ManagerImpl::~ManagerImpl() noexcept
+{
+       LOG(INFO) << "Shutdown osquery manager.";
+}
+
+ManagerImpl& ManagerImpl::instance()
+{
+       static ManagerImpl instance;
+       return instance;
+}
+
+Rows ManagerImpl::execute(const std::string& query)
+{
+       LOG(INFO) << "Execute query: " << query;
+
+       osquery::QueryData results;
+       auto status = osquery::query(query, results);
+       if (!status.ok())
+               LOG(ERROR) << "Executing query failed: " << status.getCode();
+
+       return results;
+}
+
+void ManagerImpl::subscribe(const std::string& table, const Callback& callback)
+{
+       LOG(INFO) << "Subscribe event: " << table;
+
+       auto status = Notification::instance().add(table, callback);
+       if (!status.ok())
+               LOG(ERROR) << "Subscribing event failed: " << status.getCode();
+}
+
+std::vector<std::string> ManagerImpl::tables(void) noexcept
+{
+       return SQL::getTableNames();
+}
+
+std::vector<std::string> ManagerImpl::columns(const std::string& table) noexcept
+{
+       std::stringstream query;
+       query << "SELECT * FROM " << table;
+
+       TableColumns columns;
+       getQueryColumns(query.str(), columns);
+
+       // Extract column names
+       std::vector<std::string> names;
+       for (auto& c : columns)
+               names.emplace_back(std::move(c.first));
+
+       return names;
+}
+
+} // namespace osquery
diff --git a/src/apix/manager/manager_impl.h b/src/apix/manager/manager_impl.h
new file mode 100644 (file)
index 0000000..dcfe2cb
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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 ManagerImplied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file manager_impl.h
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation interface of osquery manager
+ */
+
+#pragma once
+
+#include <osquery_manager.h>
+
+#include <string>
+#include <vector>
+
+namespace osquery {
+
+/// Singleton class
+class ManagerImpl final {
+public:
+       ManagerImpl(const ManagerImpl&) = delete;
+       ManagerImpl& operator=(const ManagerImpl&) = delete;
+
+       ManagerImpl(ManagerImpl&&) noexcept = default;
+       ManagerImpl& operator=(ManagerImpl&&) noexcept = default;
+
+       static ManagerImpl& instance();
+
+       Rows execute(const std::string& query);
+       void subscribe(const std::string& table, const Callback& callback);
+
+       std::vector<std::string> tables(void) noexcept;
+       std::vector<std::string> columns(const std::string& table) noexcept;
+
+private:
+       ManagerImpl();
+       ~ManagerImpl() noexcept;
+};
+
+} // namespace osquery
diff --git a/src/apix/manager/tests/manager_tests.cpp b/src/apix/manager/tests/manager_tests.cpp
new file mode 100644 (file)
index 0000000..048cfcc
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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 <gtest/gtest.h>
+
+#include <osquery_manager.h>
+
+#include <osquery/notification.h>
+#include <osquery/logger.h>
+
+using namespace osquery;
+
+class ManagerTests : public testing::Test {};
+
+TEST_F(ManagerTests, test_manager_execute) {
+       std::string query = "SELECT * FROM time";
+       auto rows = OsqueryManager::execute(query);
+       EXPECT_EQ(rows.size(), 1);
+
+       VLOG(1) << "[Test] time table rows:";
+       VLOG(1) << "\t hour: " << rows[0]["hour"];
+       VLOG(1) << "\t minutes: " << rows[0]["minutes"];
+       VLOG(1) << "\t seconds: " << rows[0]["seconds"];
+}
+
+TEST_F(ManagerTests, test_manager_subscribe) {
+       int called = 0;
+       auto callback = [&](const Row& row) {
+               VLOG(1) << "NotifyCallback called:";
+               for (const auto& r : row)
+                       VLOG(1) << "\t" << r.first << " : " << r.second;
+               called++;
+       };
+
+       OsqueryManager::subscribe("manager_test", callback);
+
+       Row row;
+       row["foo"] = "bar";
+       row["foo2"] = "bar2";
+       row["foo3"] = "bar3";
+
+       /// Notification trigger
+       auto s = Notification::instance().emit("manager_test", row);
+
+       EXPECT_TRUE(s.ok());
+       EXPECT_EQ(called, 1);
+}
+
+TEST_F(ManagerTests, test_manager_tables) {
+       auto tables = OsqueryManager::tables();
+       EXPECT_TRUE(tables.size() > 0);
+
+       VLOG(1) << "[Test] Enabled tables:";
+       for (const auto& t : tables)
+               VLOG(1) << "\t" << t;
+}
+
+TEST_F(ManagerTests, test_manager_columns) {
+       auto columns = OsqueryManager::columns("time");
+       EXPECT_TRUE(columns.size() > 0);
+
+       VLOG(1) << "[Test] Enabled columns of time table:";
+       for (const auto& c : columns)
+               VLOG(1) << "\t" << c;
+}
diff --git a/src/apix/notification/CMakeLists.txt b/src/apix/notification/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3c6d86f
--- /dev/null
@@ -0,0 +1,18 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_APIX_LIBRARY(notification notification.cpp)
+
+FILE(GLOB NOTIFICATION_TESTS "tests/*.cpp")
+ADD_APIX_TEST(${NOTIFICATION_TESTS})
diff --git a/src/apix/notification/notification.cpp b/src/apix/notification/notification.cpp
new file mode 100644 (file)
index 0000000..9db0272
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file notification.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of notification
+ */
+
+#include <mutex>
+
+#include <osquery/notification.h>
+#include <osquery/logger.h>
+
+namespace {
+       std::mutex mutex;
+} // anonymous namespace
+
+namespace osquery {
+
+Notification& Notification::instance()
+{
+       static Notification notifier;
+       return notifier;
+}
+
+Status Notification::add(const std::string& table, const NotifyCallback& callback)
+{
+       if (table.empty())
+               return Status(1, "Wrong table name");
+
+       LOG(INFO) << "Add NotifyCallback to:" << table;
+       {
+               std::lock_guard<std::mutex> lock(mutex);
+               this->callbacks.insert(std::make_pair(table, callback));
+       }
+
+       return Status(0, "OK");
+}
+
+Status Notification::emit(const std::string& table, const Row& result) const
+{
+       if (table.empty())
+               return Status(1, "Wrong table name");
+
+       auto iter = this->callbacks.find(table);
+       if (iter == this->callbacks.end())
+               return Status(1, "Registered callback not found");
+
+       LOG(INFO) << "Emit notification about:" << table;
+       {
+               std::lock_guard<std::mutex> lock(mutex);
+               while (iter != this->callbacks.end())  {
+                       const auto& callback = iter->second;
+                       callback(result);
+                       iter++;
+               }
+       }
+
+       return Status(0, "OK");
+}
+
+} // namespace osquery
diff --git a/src/apix/notification/tests/notification_tests.cpp b/src/apix/notification/tests/notification_tests.cpp
new file mode 100644 (file)
index 0000000..f26bfa3
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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 <gtest/gtest.h>
+
+#include <osquery/notification.h>
+#include <osquery/logger.h>
+
+using namespace osquery;
+
+class NotificationTests : public testing::Test {};
+
+TEST_F(NotificationTests, test_add_positive) {
+       auto& notifier = Notification::instance();
+
+       auto callback = [](const Row& row) {
+               VLOG(1) << "NotifyCallback called:";
+               for (const auto& r : row)
+                       VLOG(1) << "\t" << r.first << " : " << r.second;
+       };
+
+       auto s = notifier.add("test", std::move(callback));
+       EXPECT_TRUE(s.ok());
+}
+
+TEST_F(NotificationTests, test_add_negative) {
+       auto& notifier = Notification::instance();
+
+       auto callback = [](const Row& row) {
+               VLOG(1) << "NotifyCallback called:";
+               for (const auto& r : row)
+                       VLOG(1) << "\t" << r.first << " : " << r.second;
+       };
+
+       auto s = notifier.add("", std::move(callback));
+       EXPECT_FALSE(s.ok());
+}
+
+TEST_F(NotificationTests, test_emit_positive) {
+       auto& notifier = Notification::instance();
+
+       int called = 0;
+       auto callback = [&](const Row& row) {
+               VLOG(1) << "NotifyCallback called:";
+               for (const auto& r : row)
+                       VLOG(1) << "\t" << r.first << " : " << r.second;
+               called++;
+       };
+
+       auto s = notifier.add("test2", std::move(callback));
+       EXPECT_TRUE(s.ok());
+
+       Row row;
+       row["foo"] = "bar";
+       s = notifier.emit("test2", row);
+
+       EXPECT_TRUE(s.ok());
+       EXPECT_EQ(called, 1);
+}
diff --git a/src/apix/property/CMakeLists.txt b/src/apix/property/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b4eeb79
--- /dev/null
@@ -0,0 +1,18 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_APIX_LIBRARY(property property.cpp)
+
+FILE(GLOB PROPERTY_TESTS "tests/*.cpp")
+ADD_APIX_TEST(${PROPERTY_TESTS})
diff --git a/src/apix/property/property.cpp b/src/apix/property/property.cpp
new file mode 100644 (file)
index 0000000..e06dce2
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file property.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Implementation of Property
+ */
+
+#include <osquery_manager.h>
+#include <property.h>
+
+#include <schema/time.h>
+#include <schema/processes.h>
+#include <schema/users.h>
+#include <schema/groups.h>
+#include <schema/memory-map.h>
+
+#include <osquery/logger.h>
+
+#include <tsqb.hxx>
+
+#include <boost/lexical_cast.hpp>
+
+namespace {
+
+using namespace tsqb;
+auto time = make_table("time",
+                                          make_column("hour", &Time::hour),
+                                          make_column("minutes", &Time::minutes),
+                                          make_column("seconds", &Time::seconds));
+
+auto processes = make_table("processes",
+                                                       make_column("pid", &Processes::pid),
+                                                       make_column("name", &Processes::name),
+                                                       make_column("path", &Processes::path),
+                                                       make_column("cmdline", &Processes::cmdline),
+                                                       make_column("uid", &Processes::uid),
+                                                       make_column("gid", &Processes::gid),
+                                                       make_column("euid", &Processes::euid),
+                                                       make_column("egid", &Processes::egid),
+                                                       make_column("on_disk", &Processes::on_disk),
+//                                                     make_column("wired_size", &Processes::wired_size),
+                                                       make_column("resident_size", &Processes::resident_size),
+                                                       make_column("phys_footprint", &Processes::phys_footprint),
+                                                       make_column("user_time", &Processes::user_time),
+                                                       make_column("system_time", &Processes::system_time),
+                                                       make_column("start_time", &Processes::start_time),
+                                                       make_column("parent", &Processes::parent));
+
+auto users = make_table("users",
+                                               make_column("uid", &Users::uid),
+                                               make_column("gid", &Users::gid),
+                                               make_column("uid_signed", &Users::uid_signed),
+                                               make_column("gid_signed", &Users::gid_signed),
+                                               make_column("username", &Users::username),
+                                               make_column("description", &Users::description),
+                                               make_column("directory", &Users::directory),
+                                               make_column("shell", &Users::shell));
+
+auto groups = make_table("groups",
+                                                make_column("gid", &Groups::gid),
+                                                make_column("gid_signed", &Groups::gid_signed),
+                                                make_column("groupname", &Groups::groupname));
+
+auto memoryMap = make_table("memory_map",
+                                                make_column("region", &MemoryMap::region),
+                                                make_column("type", &MemoryMap::type),
+                                                make_column("start", &MemoryMap::start),
+                                                make_column("end", &MemoryMap::end));
+
+auto db = make_database("db", time, processes, users, groups, memoryMap);
+
+} // anonymous namespace
+
+namespace osquery {
+
+template <typename T>
+Property<T>::Property()
+{
+       auto results = OsqueryManager::execute(db.selectAll<T>());
+       if (results.size() > 0)
+               this->data = std::move(results[0]);
+}
+
+template <typename T>
+Property<T>::Property(KeyValuePair&& kvp) : data(std::move(kvp))
+{
+}
+
+template <typename T>
+template<typename Struct, typename Member>
+Member Property<T>::at(Member Struct::* field) const
+{
+       if (this->data.size() == 0)
+               throw std::runtime_error("Data is not exist.");
+
+       std::string key = db.getColumnName(field);
+       if (key.empty())
+               throw std::runtime_error("Key is not exist.");
+
+       /// Convert "table.column" to "column"
+       std::size_t pos = key.find(".");
+       if (pos != std::string::npos && pos != key.size() - 1)
+               key = key.substr(pos + 1);
+
+       std::string value = this->data.at(key);
+       if (value.empty()) {
+               LOG(ERROR) << "The value of key[" << key << "] is not exist.";
+               return Member();
+       } else {
+               /// TODO(Sangwan): Catch boost::bad_lexical_cast
+               return boost::lexical_cast<Member>(value);
+       }
+}
+
+template <typename T>
+template<typename Struct, typename Member>
+Member Property<T>::operator[](Member Struct::*field) const
+{
+       return this->at(field);
+}
+
+template <typename T>
+Properties<T>::Properties()
+{
+       auto results = OsqueryManager::execute(db.selectAll<T>());
+       for (auto& r : results)
+               this->datas.emplace_back(Property<T>(std::move(r)));
+}
+
+/// Explicit instantiation
+template class Property<Time>;
+template class Properties<Time>;
+template int Property<Time>::at(int Time::*) const;
+template int Property<Time>::operator[](int Time::*) const;
+
+template class Property<Processes>;
+template class Properties<Processes>;
+template int Property<Processes>::at(int Processes::*) const;
+template int Property<Processes>::operator[](int Processes::*) const;
+template long long int Property<Processes>::at(long long int Processes::*) const;
+template long long int Property<Processes>::operator[](long long int Processes::*) const;
+template std::string Property<Processes>::at(std::string Processes::*) const;
+template std::string Property<Processes>::operator[](std::string Processes::*) const;
+
+template class Property<Users>;
+template class Properties<Users>;
+template long long int Property<Users>::at(long long int Users::*) const;
+template long long int Property<Users>::operator[](long long int Users::*) const;
+template unsigned long long int Property<Users>::at(unsigned long long int Users::*) const;
+template unsigned long long int Property<Users>::operator[](unsigned long long int Users::*) const;
+template std::string Property<Users>::at(std::string Users::*) const;
+template std::string Property<Users>::operator[](std::string Users::*) const;
+
+template class Property<Groups>;
+template class Properties<Groups>;
+template long long int Property<Groups>::at(long long int Groups::*) const;
+template long long int Property<Groups>::operator[](long long int Groups::*) const;
+template unsigned long long int Property<Groups>::at(unsigned long long int Groups::*) const;
+template unsigned long long int Property<Groups>::operator[](unsigned long long int Groups::*) const;
+template std::string Property<Groups>::at(std::string Groups::*) const;
+template std::string Property<Groups>::operator[](std::string Groups::*) const;
+
+template class Property<MemoryMap>;
+template class Properties<MemoryMap>;
+template int Property<MemoryMap>::at(int MemoryMap::*) const;
+template int Property<MemoryMap>::operator[](int MemoryMap::*) const;
+template std::string Property<MemoryMap>::at(std::string MemoryMap::*) const;
+template std::string Property<MemoryMap>::operator[](std::string MemoryMap::*) const;
+
+} // namespace osquery
diff --git a/src/apix/property/tests/property_tests.cpp b/src/apix/property/tests/property_tests.cpp
new file mode 100644 (file)
index 0000000..8038524
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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 <gtest/gtest.h>
+
+#include <osquery/logger.h>
+
+#include <property.h>
+
+#include <schema/time.h>
+#include <schema/processes.h>
+#include <schema/users.h>
+#include <schema/groups.h>
+#include <schema/memory-map.h>
+
+using namespace osquery;
+
+class PropertyTests : public testing::Test {};
+
+TEST_F(PropertyTests, property) {
+       Time result = { -1, -1, -1 };
+
+       Property<Time> time;
+       result.hour = time.at(&Time::hour);
+       result.minutes = time.at(&Time::minutes);
+       result.seconds = time.at(&Time::seconds);
+
+       /// Once query execution
+       VLOG(1) << "[Test] time table:";
+       VLOG(1) << "\t hour: " << result.hour;
+       VLOG(1) << "\t minutes: " << result.minutes;
+       VLOG(1) << "\t seconds: " << result.seconds;
+
+       /// Each query execution
+       VLOG(1) << "[Test] time table:";
+       VLOG(1) << "\t hour: " << Property<Time>().at(&Time::hour);
+       VLOG(1) << "\t minutes: " << Property<Time>().at(&Time::minutes);
+       VLOG(1) << "\t seconds: " <<  Property<Time>().at(&Time::seconds);
+
+       EXPECT_NE(result.hour, -1);
+       EXPECT_NE(result.minutes, -1);
+       EXPECT_NE(result.seconds, -1);
+}
+
+TEST_F(PropertyTests, propertyArrayOp) {
+       Time result = { -1, -1, -1 };
+
+       Property<Time> time;
+       result.hour = time[&Time::hour];
+       result.minutes = time[&Time::minutes];
+       result.seconds = time[&Time::seconds];
+
+       /// Once query execution
+       VLOG(1) << "[Test] time table:";
+       VLOG(1) << "\t hour: " << result.hour;
+       VLOG(1) << "\t minutes: " << result.minutes;
+       VLOG(1) << "\t seconds: " << result.seconds;
+
+       EXPECT_NE(result.hour, -1);
+       EXPECT_NE(result.minutes, -1);
+       EXPECT_NE(result.seconds, -1);
+}
+
+TEST_F(PropertyTests, propertiesProcesses) {
+       Processes result = {
+               -1, /// pid
+               "", /// name
+               "", /// path
+               "", /// cmdline
+               -1, /// uid
+               -1, /// gid
+               -1, /// euid
+               -1, /// egid
+               "", /// on_disk
+//             "", /// wired_size
+               "", /// resident_size
+               "", /// phys_footprint
+               "", /// user_time
+               "", /// system_time
+               "", /// start_time
+               -1  /// parent
+               };
+
+       Properties<Processes> processes;
+
+       for(auto& p : processes) {
+               result.pid = p.at(&Processes::pid);
+               result.name = p.at(&Processes::name);
+               result.path = p.at(&Processes::path);
+               result.cmdline = p.at(&Processes::cmdline);
+               result.uid = p.at(&Processes::uid);
+               result.gid = p.at(&Processes::gid);
+               result.euid = p.at(&Processes::euid);
+               result.egid = p.at(&Processes::egid);
+               result.on_disk = p.at(&Processes::on_disk);
+//             result.wired_size = p.at(&Processes::wired_size);
+               result.resident_size = p.at(&Processes::resident_size);
+               result.phys_footprint = p.at(&Processes::phys_footprint);
+               result.user_time = p.at(&Processes::user_time);
+               result.system_time = p.at(&Processes::system_time);
+               result.start_time = p.at(&Processes::start_time);
+               result.parent = p.at(&Processes::parent);
+
+               VLOG(1) << "[Test] Processes table:";
+               VLOG(1) << "\t pid: " << result.pid;
+               VLOG(1) << "\t name: " << result.name;
+               VLOG(1) << "\t path: " << result.path;
+               VLOG(1) << "\t cmdline: " << result.cmdline;
+               VLOG(1) << "\t uid: " << result.uid;
+               VLOG(1) << "\t gid: " << result.gid;
+               VLOG(1) << "\t euid: " << result.euid;
+               VLOG(1) << "\t egid: " << result.egid;
+               VLOG(1) << "\t on_disk: " << result.on_disk;
+//             VLOG(1) << "\t wired_size: " << result.wired_size;
+               VLOG(1) << "\t resident_size: " << result.resident_size;
+               VLOG(1) << "\t phys_footprint: " << result.phys_footprint;
+               VLOG(1) << "\t user_time: " << result.user_time;
+               VLOG(1) << "\t system_time: " << result.system_time;
+               VLOG(1) << "\t start_time: " << result.start_time;
+               VLOG(1) << "\t parent: " << result.parent;
+       }
+}
+
+TEST_F(PropertyTests, propertiesUsers) {
+       Properties<Users> users;
+       for(const auto& user : users) {
+               VLOG(1) << "[Test] User table:";
+               VLOG(1) << "\t uid: " << user[&Users::uid];
+               VLOG(1) << "\t gid: " << user[&Users::gid];
+               VLOG(1) << "\t uid_signed: " << user[&Users::uid_signed];
+               VLOG(1) << "\t gid_signed: " << user[&Users::gid_signed];
+               VLOG(1) << "\t username: " << user[&Users::username];
+               VLOG(1) << "\t description: " << user[&Users::description];
+               VLOG(1) << "\t directory: " << user[&Users::directory];
+               VLOG(1) << "\t shell: " << user[&Users::shell];
+       }
+}
+
+TEST_F(PropertyTests, propertiesGroups) {
+       Properties<Groups> groups;
+       for(const auto& group : groups) {
+               VLOG(1) << "[Test] Group table:";
+               VLOG(1) << "\t gid: " << group[&Groups::gid];
+               VLOG(1) << "\t gid_signed: " << group[&Groups::gid_signed];
+               VLOG(1) << "\t groupname: " << group[&Groups::groupname];
+       }
+}
+
+TEST_F(PropertyTests, propertiesMemoryMap) {
+       Properties<MemoryMap> memoryMap;
+       for(const auto& mm : memoryMap) {
+               VLOG(1) << "[Test] memory_map table:";
+               VLOG(1) << "\t region: " << mm[&MemoryMap::region];
+               VLOG(1) << "\t type: " << mm[&MemoryMap::type];
+               VLOG(1) << "\t start: " << mm[&MemoryMap::start];
+               VLOG(1) << "\t end: " << mm[&MemoryMap::end];
+       }
+}
diff --git a/src/apix/tsqb/CMakeLists.txt b/src/apix/tsqb/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2142f70
--- /dev/null
@@ -0,0 +1,16 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+FILE(GLOB TSQB_TESTS "tests/*.cpp")
+ADD_APIX_TEST(${TSQB_TESTS})
diff --git a/src/apix/tsqb/include/column-pack.hxx b/src/apix/tsqb/include/column-pack.hxx
new file mode 100644 (file)
index 0000000..9688e8e
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file column-pack.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Tie different types of columns
+ */
+
+#pragma once
+
+#include "type.hxx"
+
+#include <string>
+#include <vector>
+
+namespace tsqb {
+namespace internal {
+
+template<typename... Base>
+class ColumnPack {
+public:
+       virtual ~ColumnPack() = default;
+
+       template<typename ColumnType>
+       std::string getName(ColumnType&&) const noexcept { return std::string(); }
+       std::vector<std::string> getNames(void) const noexcept { return {}; }
+
+       int size() const noexcept { return 0; }
+};
+
+template<typename Front, typename... Rest>
+class ColumnPack<Front, Rest...> : public ColumnPack<Rest...> {
+public:
+       using Column = Front;
+       using TableType = typename Column::TableType;
+
+       explicit ColumnPack(Front&& front, Rest&& ...rest);
+       virtual ~ColumnPack() = default;
+
+       ColumnPack(const ColumnPack&) = delete;
+       ColumnPack& operator=(const ColumnPack&) = delete;
+
+       ColumnPack(ColumnPack&&) = default;
+       ColumnPack& operator=(ColumnPack&&) = default;
+
+       template<typename ColumnType>
+       std::string getName(ColumnType&& type) const noexcept;
+       std::vector<std::string> getNames(void) const noexcept;
+
+       int size() const noexcept { return Base::size() + 1; }
+
+private:
+       using Base = ColumnPack<Rest...>;
+
+       Column column;
+};
+
+template<typename Front, typename... Rest>
+ColumnPack<Front, Rest...>::ColumnPack(Front&& front, Rest&& ...rest) :
+       Base(std::forward<Rest>(rest)...), column(front)
+{
+}
+
+template<typename Front, typename... Rest>
+std::vector<std::string> ColumnPack<Front, Rest...>::getNames(void) const noexcept
+{
+       auto names = Base::getNames();
+       names.push_back(this->column.name);
+
+       return std::move(names);
+}
+
+template<typename Front, typename... Rest>
+template<typename ColumnType>
+std::string ColumnPack<Front, Rest...>::getName(ColumnType&& type) const noexcept
+{
+       if (type::cast_compare(column.type, std::forward<ColumnType>(type)))
+               return column.name;
+
+       return Base::template getName<ColumnType>(std::forward<ColumnType>(type));
+}
+
+} // namespace internal
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/column.hxx b/src/apix/tsqb/include/column.hxx
new file mode 100644 (file)
index 0000000..ca17415
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file culumn.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Capture member pointer of struct and use it for matching with column
+ */
+
+#pragma once
+
+#include <string>
+#include <tuple>
+
+namespace tsqb {
+
+template<typename Object, typename Field>
+struct Column {
+       using Type = Field Object::*;
+       using FieldType = Field;
+       using TableType = Object;
+
+       std::string name;
+       Type type;
+};
+
+template<typename O, typename F>
+Column<O, F> make_column(const std::string& name, F O::*field)
+{
+       return {name, field};
+}
+
+template<typename Type>
+struct Distinct {
+       Type value;
+};
+
+template<typename... Args>
+auto distinct(Args&&... args) -> decltype(Distinct<std::tuple<Args...>>())
+{
+       return {std::make_tuple(std::forward<Args>(args)...)};
+}
+
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/condition.hxx b/src/apix/tsqb/include/condition.hxx
new file mode 100644 (file)
index 0000000..7ec13da
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file condition.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the condition statement of SQL
+ */
+
+#pragma once
+
+#include "type.hxx"
+
+namespace tsqb {
+namespace condition {
+
+struct Base {};
+
+template<typename L, typename R>
+struct And : public Base {
+       L l;
+       R r;
+
+       And(L l, R r) : l(l), r(r) {}
+       operator std::string() const
+       {
+               return "AND";
+       }
+};
+
+template<typename L, typename R>
+struct Or : public Base {
+       L l;
+       R r;
+
+       Or(L l, R r) : l(l), r(r) {}
+       operator std::string() const
+       {
+               return "OR";
+       }
+};
+
+template<typename L, typename R>
+struct Binary : public Base {
+       L l;
+       R r;
+
+       Binary(L l, R r) : l(l), r(r)
+       {
+               using FieldType = typename L::FieldType;
+               type::assert_compare(FieldType(), r);
+       }
+};
+
+template<typename L>
+struct Binary<L, const char*> : public Base {
+       L l;
+       std::string r;
+
+       Binary(L l, const char* r) : l(l), r(r)
+       {
+               using FieldType = typename L::FieldType;
+               type::assert_compare(FieldType(), std::string());
+       }
+};
+
+enum class Join : int {
+       INNER,
+       CROSS,
+       LEFT_OUTER,
+       RIGHT_OUTER,
+       FULL_OUTER
+};
+
+inline std::string to_string(Join type)
+{
+       switch (type) {
+       case Join::CROSS: return "CROSS";
+       case Join::LEFT_OUTER: return "LEFT OUTER";
+       case Join::RIGHT_OUTER: return "RIGHT OUTER";
+       case Join::FULL_OUTER: return "FULL OUTER";
+       case Join::INNER:
+       default:
+               return "INNER";
+       }
+}
+
+} // namespace condition
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/crud.hxx b/src/apix/tsqb/include/crud.hxx
new file mode 100644 (file)
index 0000000..be48c86
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file crud.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent four basic functions of CRUD
+ */
+
+#pragma once
+
+#include "column.hxx"
+#include "expression.hxx"
+
+#include <string>
+#include <sstream>
+
+namespace tsqb {
+
+template<typename T>
+class Crud {
+public:
+       template<typename... ColumnTypes>
+       T& select(ColumnTypes&&... cts);
+
+       template<typename Type>
+       T& select(Distinct<Type> distinct);
+
+       template<typename TableType>
+       T& selectAll(void);
+
+       T& selectAll(void);
+
+       template<typename... ColumnTypes>
+       T& update(ColumnTypes&&... cts);
+
+       template<typename... ColumnTypes>
+       T& insert(ColumnTypes&&... cts);
+
+       template<typename... ColumnTypes>
+       T& remove(ColumnTypes&&... cts);
+
+       template<typename Expr>
+       T& where(Expr expr);
+
+private:
+       template<typename ColumnTuple>
+       T& selectInternal(ColumnTuple&& ct, bool distinct = false);
+
+       template<typename L, typename R>
+       std::string processWhere(condition::And<L,R>& expr);
+
+       template<typename L, typename R>
+       std::string processWhere(condition::Or<L,R>& expr);
+
+       template<typename Expr>
+       std::string processWhere(Expr expr);
+};
+
+template<typename T>
+template<typename... ColumnTypes>
+T& Crud<T>::select(ColumnTypes&&... cts)
+{
+       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
+
+       return this->selectInternal(std::move(columnTuple));
+}
+
+template<typename T>
+template<typename Type>
+T& Crud<T>::select(Distinct<Type> distinct)
+{
+       return this->selectInternal(std::move(distinct.value), true);
+}
+
+template<typename T>
+T& Crud<T>::selectAll(void)
+{
+       static_cast<T*>(this)->cache.clear();
+
+       std::stringstream ss;
+       ss << "SELECT * FROM " << static_cast<T*>(this)->name;
+
+       static_cast<T*>(this)->cache.emplace_back(ss.str());
+
+       return *(static_cast<T*>(this));
+}
+
+template<typename T>
+template<typename TableType>
+T& Crud<T>::selectAll(void)
+{
+       static_cast<T*>(this)->cache.clear();
+
+       std::stringstream ss;
+       auto tableName = static_cast<T*>(this)->getTableName(TableType());
+       ss << "SELECT * FROM " << tableName;
+
+       static_cast<T*>(this)->cache.emplace_back(ss.str());
+
+       return *(static_cast<T*>(this));
+}
+
+template<typename T>
+template<typename ColumnTuple>
+T& Crud<T>::selectInternal(ColumnTuple&& ct, bool distinct)
+{
+       static_cast<T*>(this)->cache.clear();
+
+       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(ct));
+       auto tableNames = static_cast<T*>(this)->getTableNames(std::move(ct));
+
+       std::stringstream ss;
+       ss << "SELECT ";
+
+       if (distinct)
+               ss << "DISTINCT ";
+
+       int i = 0;
+       for (const auto& c : columnNames) {
+               ss << c;
+
+               if (i++ < columnNames.size() - 1)
+                       ss << ", ";
+       }
+
+       ss << " FROM ";
+
+       i = 0;
+       for (const auto& t : tableNames) {
+               ss << t;
+
+               if (i++ < tableNames.size() - 1)
+                       ss << ", ";
+       }
+
+       static_cast<T*>(this)->cache.emplace_back(ss.str());
+
+       return *(static_cast<T*>(this));
+}
+
+template<typename T>
+template<typename... ColumnTypes>
+T& Crud<T>::update(ColumnTypes&&... cts)
+{
+       static_cast<T*>(this)->cache.clear();
+
+       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
+       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(columnTuple));
+
+       std::stringstream ss;
+       ss << "UPDATE " << static_cast<T*>(this)->name << " ";
+       ss << "SET ";
+
+       int i = 0;
+       for (const auto& c : columnNames) {
+               ss << c << " = ?";
+
+               if (i++ < columnNames.size() - 1)
+                       ss << ", ";
+       }
+
+       static_cast<T*>(this)->cache.emplace_back(ss.str());
+
+       return *(static_cast<T*>(this));
+}
+
+template<typename T>
+template<typename... ColumnTypes>
+T& Crud<T>::insert(ColumnTypes&&... cts)
+{
+       static_cast<T*>(this)->cache.clear();
+
+       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
+       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(columnTuple));
+
+       std::stringstream ss;
+       ss << "INSERT INTO " << static_cast<T*>(this)->name << " (";
+
+       const int columnCount = columnNames.size();
+       for (int i = 0; i < columnCount; i++) {
+               ss << columnNames[i];
+               if (i < columnCount - 1)
+                       ss << ", ";
+       }
+
+       ss << ") VALUES (";
+
+       for (int i = 0; i < columnCount; i++) {
+               ss << "?";
+               if (i < columnCount - 1)
+                       ss << ", ";
+       }
+
+       ss << ")";
+
+       static_cast<T*>(this)->cache.emplace_back(ss.str());
+
+       return *(static_cast<T*>(this));
+}
+
+template<typename T>
+template<typename... ColumnTypes>
+T& Crud<T>::remove(ColumnTypes&&... cts)
+{
+       static_cast<T*>(this)->cache.clear();
+
+       auto columnTuple = std::make_tuple(std::forward<ColumnTypes>(cts)...);
+       auto columnNames = static_cast<T*>(this)->getColumnNames(std::move(columnTuple));
+
+       std::stringstream ss;
+       ss << "DELETE FROM " << static_cast<T*>(this)->name;
+
+       static_cast<T*>(this)->cache.emplace_back(ss.str());
+
+       return *(static_cast<T*>(this));
+}
+
+template<typename T>
+template<typename Expr>
+T& Crud<T>::where(Expr expr)
+{
+       std::stringstream ss;
+       ss << "WHERE " << this->processWhere(expr);
+
+       static_cast<T*>(this)->cache.emplace_back(ss.str());
+
+       return *(static_cast<T*>(this));
+}
+
+template<typename T>
+template<typename L, typename R>
+std::string Crud<T>::processWhere(condition::And<L,R>& expr)
+{
+       std::stringstream ss;
+       ss << this->processWhere(expr.l) << " ";
+       ss << static_cast<std::string>(expr) << " ";
+       ss << this->processWhere(expr.r);
+
+       return ss.str();
+}
+
+template<typename T>
+template<typename L, typename R>
+std::string Crud<T>::processWhere(condition::Or<L,R>& expr)
+{
+       std::stringstream ss;
+       ss << this->processWhere(expr.l) << " ";
+       ss << static_cast<std::string>(expr) << " ";
+       ss << this->processWhere(expr.r);
+
+       return ss.str();
+}
+
+template<typename T>
+template<typename Expr>
+std::string Crud<T>::processWhere(Expr expr)
+{
+       std::stringstream ss;
+       ss << static_cast<T*>(this)->getColumnName(expr.l.type);
+       ss << " " << std::string(expr) << " ?";
+
+       return ss.str();
+}
+
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/database.hxx b/src/apix/tsqb/include/database.hxx
new file mode 100644 (file)
index 0000000..0c8bf54
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file database.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the database scheme of SQL
+ */
+
+#pragma once
+
+#include "crud.hxx"
+#include "condition.hxx"
+#include "expression.hxx"
+#include "table-pack.hxx"
+#include "tuple-helper.hxx"
+#include "util.hxx"
+
+#include <vector>
+#include <set>
+#include <string>
+#include <sstream>
+#include <algorithm>
+
+namespace tsqb {
+
+template<typename... Tables>
+class Database : public Crud<Database<Tables...>> {
+public:
+       using Self = Database<Tables...>;
+
+       virtual ~Database() = default;
+
+       Database(const Database&) = delete;
+       Database& operator=(const Database&) = delete;
+
+       Database(Database&&) = default;
+       Database& operator=(Database&&) = default;
+
+       // Functions for Crud
+       template<typename Cs>
+       std::set<std::string> getTableNames(Cs&& tuple) const noexcept;
+       template<typename Cs>
+       std::vector<std::string> getColumnNames(Cs&& tuple) const noexcept;
+       template<typename TableType>
+       std::string getTableName(TableType&& type) const noexcept;
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&& type) const noexcept;
+
+       template<typename Table>
+       Self& join(condition::Join type = condition::Join::INNER);
+
+       template<typename Expr>
+       Self& on(Expr expr);
+
+       operator std::string();
+
+       std::string name;
+       std::vector<std::string> cache;
+
+private:
+       using TablePackType = internal::TablePack<Tables...>;
+       using ColumnNames = std::vector<std::string>;
+       using TableNames = std::set<std::string>;
+
+       explicit Database(const std::string& name, TablePackType&& tablePack);
+
+       template<typename ...Ts>
+       friend Database<Ts...> make_database(const std::string& name, Ts&& ...tables);
+
+       struct GetTableNames {
+               const TablePackType& tablePack;
+               std::set<std::string> names;
+               GetTableNames(const TablePackType& tablePack) : tablePack(tablePack) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto column = make_column("anonymous", type);
+                       using TableType = typename decltype(column)::TableType;
+                       auto name = this->tablePack.getName(TableType());
+                       if (!name.empty())
+                               names.emplace(name);
+               }
+       };
+
+       struct GetColumnNames {
+               const TablePackType& tablePack;
+               std::vector<std::string> names;
+
+               GetColumnNames(const TablePackType& tablePack) : tablePack(tablePack) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto column = make_column("anonymous", type);
+                       auto name = this->tablePack.getColumnName(std::move(column));
+                       if (!name.empty())
+                               names.emplace_back(name);
+               }
+       };
+
+       TablePackType tablePack;
+};
+
+template<typename ...Tables>
+Database<Tables...> make_database(const std::string& name, Tables&& ...tables)
+{
+       auto tablePack = internal::TablePack<Tables...>(std::forward<Tables>(tables)...);
+       return Database<Tables...>(name, std::move(tablePack));
+}
+
+template<typename ...Tables>
+Database<Tables...>::Database(const std::string& name, TablePackType&& tablePack)
+       : name(name), tablePack(std::move(tablePack)) {}
+
+template<typename... Tables>
+template<typename Table>
+Database<Tables...>& Database<Tables...>::join(condition::Join type)
+{
+       std::stringstream ss;
+       ss << condition::to_string(type) << " ";
+       ss << "JOIN ";
+       ss << this->tablePack.getName(Table());
+
+       this->cache.emplace_back(ss.str());
+       return *this;
+}
+
+template<typename... Tables>
+template<typename Expr>
+Database<Tables...>& Database<Tables...>::on(Expr expr)
+{
+       std::stringstream ss;
+       ss << "ON ";
+
+       auto lname = this->tablePack.getColumnName(std::move(expr.l));
+       ss << lname << " ";
+
+       ss << std::string(expr) << " ";
+
+       auto rname = this->tablePack.getColumnName(std::move(expr.r));
+       ss << rname;
+
+       this->cache.emplace_back(ss.str());
+       return *this;
+}
+
+template<typename... Tables>
+Database<Tables...>::operator std::string()
+{
+       std::stringstream ss;
+       for (const auto& c : cache)
+               ss << c << " ";
+
+       this->cache.clear();
+       return util::rtrim(ss.str());
+}
+
+template<typename... Tables>
+template<typename Cs>
+std::set<std::string> Database<Tables...>::getTableNames(Cs&& tuple) const noexcept
+{
+       GetTableNames closure(this->tablePack);
+       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
+
+       return closure.names;
+}
+
+template<typename... Tables>
+template<typename Cs>
+std::vector<std::string> Database<Tables...>::getColumnNames(Cs&& tuple) const noexcept
+{
+       GetColumnNames closure(this->tablePack);
+       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
+
+       return closure.names;
+}
+
+template<typename... Tables>
+template<typename TableType>
+std::string Database<Tables...>::getTableName(TableType&& type) const noexcept
+{
+       return this->tablePack.getName(std::forward<TableType>(type));
+}
+
+template<typename... Tables>
+template<typename ColumnType>
+std::string Database<Tables...>::getColumnName(ColumnType&& type) const noexcept
+{
+       auto column = make_column("anonymous", type);
+       return this->tablePack.getColumnName(std::move(column));
+}
+
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/expression.hxx b/src/apix/tsqb/include/expression.hxx
new file mode 100644 (file)
index 0000000..c901ab0
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file expression.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the expression of SQL
+ */
+
+#pragma once
+
+#include "column.hxx"
+#include "type.hxx"
+#include "condition.hxx"
+
+#include <type_traits>
+
+namespace tsqb {
+
+template<typename Type>
+struct Expression {
+       Type value;
+};
+
+template<typename O, typename F>
+Expression<Column<O, F>> expr(F O::*field)
+{
+       Column<O, F> anonymous = {"anonymous", field};
+       return {anonymous};
+}
+
+template<typename L, typename R>
+struct Lesser : public condition::Binary<L, R> {
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const
+       {
+               return "<";
+       }
+};
+
+template<typename L, typename R>
+Lesser<L, R> operator<(Expression<L> expr, R r)
+{
+       return {expr.value, r};
+}
+
+template<typename L, typename R>
+struct Equal : public condition::Binary<L, R>
+{
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const
+       {
+               return "=";
+       }
+};
+
+template<typename L, typename R>
+Equal<L, R> operator==(Expression<L> expr, R r)
+{
+       return {expr.value, r};
+}
+
+namespace join {
+
+template<typename L, typename R>
+struct Equal
+{
+       L l;
+       R r;
+
+       operator std::string() const
+       {
+               return "=";
+       }
+};
+
+} // namespace join
+
+template<typename L, typename R>
+join::Equal<L, R> operator==(Expression<L> l, Expression<R> r)
+{
+       return {l.value, r.value};
+}
+
+template<typename L, typename R>
+struct Greater : public condition::Binary<L, R>
+{
+       using condition::Binary<L, R>::Binary;
+
+       operator std::string() const
+       {
+               return ">";
+       }
+};
+
+template<typename L, typename R>
+Greater<L, R> operator>(Expression<L> expr, R r)
+{
+       return {expr.value, r};
+}
+
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T>
+using is_condition = typename std::is_base_of<condition::Base, T>;
+
+template<typename L,
+                typename R,
+                typename = typename tsqb::enable_if_t<is_condition<L>::value
+                                                        && tsqb::is_condition<R>::value>>
+condition::And<L, R> operator&&(const L& l, const R& r)
+{
+       return {l, r};
+}
+
+template<typename L,
+                typename R,
+                typename = typename tsqb::enable_if_t<is_condition<L>::value
+                                                        && tsqb::is_condition<R>::value>>
+condition::Or<L, R> operator||(const L& l, const R& r)
+{
+       return {l, r};
+}
+
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/table-pack.hxx b/src/apix/tsqb/include/table-pack.hxx
new file mode 100644 (file)
index 0000000..d192886
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file table-pack.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Tie diffirent types of tables
+ */
+
+#pragma once
+
+#include <vector>
+#include <set>
+#include <string>
+
+namespace tsqb {
+namespace internal {
+
+template<typename... Base>
+class TablePack {
+public:
+       virtual ~TablePack() = default;
+
+       template<typename TableType>
+       std::string getName(TableType&&) const noexcept { return std::string(); }
+
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&&) const noexcept { return std::string(); }
+};
+
+template<typename Front, typename... Rest>
+class TablePack<Front, Rest...> : public TablePack<Rest...> {
+public:
+       using Table = Front;
+
+       explicit TablePack(Front&& front, Rest&& ...rest);
+       virtual ~TablePack() = default;
+
+       TablePack(const TablePack&) = delete;
+       TablePack& operator=(const TablePack&) = delete;
+
+       TablePack(TablePack&&) = default;
+       TablePack& operator=(TablePack&&) = default;
+
+       template<typename TableType>
+       std::string getName(TableType&& table) const noexcept;
+
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&& column) const noexcept;
+
+private:
+       using Base = TablePack<Rest...>;
+
+       Table table;
+};
+
+template<typename Front, typename... Rest>
+TablePack<Front, Rest...>::TablePack(Front&& front, Rest&& ...rest) :
+       Base(std::forward<Rest>(rest)...), table(front)
+{
+}
+
+template<typename Front, typename... Rest>
+template<typename TableType>
+std::string TablePack<Front, Rest...>::getName(TableType&& table) const noexcept
+{
+       if (this->table.compare(table))
+               return this->table.name;
+
+       return Base::template getName<TableType>(std::forward<TableType>(table));
+}
+
+template<typename Front, typename... Rest>
+template<typename ColumnType>
+std::string TablePack<Front, Rest...>::getColumnName(ColumnType&& column) const noexcept
+{
+       using DecayColumnType = typename std::decay<ColumnType>::type;
+       using DecayTableType = typename DecayColumnType::TableType;
+       if (this->table.compare(DecayTableType())) {
+               auto cname = this->table.getColumnName(column.type);
+               return this->table.name + "." + cname;
+       }
+
+       return Base::template getColumnName<ColumnType>(std::forward<ColumnType>(column));
+}
+
+} // namespace internal
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/table.hxx b/src/apix/tsqb/include/table.hxx
new file mode 100644 (file)
index 0000000..e1ac527
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file table.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Represent the table of SQL
+ */
+
+#pragma once
+
+#include "crud.hxx"
+#include "column.hxx"
+#include "column-pack.hxx"
+#include "tuple-helper.hxx"
+#include "util.hxx"
+
+#include <vector>
+#include <string>
+#include <sstream>
+
+namespace tsqb {
+
+template<typename... Columns>
+class Table : public Crud<Table<Columns...>> {
+public:
+       virtual ~Table() = default;
+
+       Table(const Table&) = delete;
+       Table& operator=(const Table&) = delete;
+
+       Table(Table&&) = default;
+       Table& operator=(Table&&) = default;
+
+       // Functions for Crud
+       template<typename Cs>
+       std::set<std::string> getTableNames(Cs&& tuple) const noexcept;
+       template<typename Cs>
+       std::vector<std::string> getColumnNames(Cs&& tuple) const noexcept;
+       template<typename That>
+       std::string getTableName(That&& type) const noexcept;
+       template<typename ColumnType>
+       std::string getColumnName(ColumnType&& type) const noexcept;
+
+       std::vector<std::string> getColumnNames(void) const noexcept;
+
+       template<typename That>
+       bool compare(const That& that) const noexcept;
+
+       int size() const noexcept;
+
+       operator std::string();
+
+       std::string name;
+       std::vector<std::string> cache;
+
+private:
+       using ColumnPackType = internal::ColumnPack<Columns...>;
+       using TableType = typename ColumnPackType::TableType;
+
+       explicit Table(const std::string& name, ColumnPackType&& columnPack);
+
+       template<typename ...Cs>
+       friend Table<Cs...> make_table(const std::string& name, Cs&& ...columns);
+
+       struct GetColumnNames {
+               const ColumnPackType& columnPack;
+               std::vector<std::string> names;
+
+               GetColumnNames(const ColumnPackType& columnPack) : columnPack(columnPack) {}
+
+               template <typename T>
+               void operator()(T&& type)
+               {
+                       auto name = this->columnPack.getName(std::forward<T>(type));
+                       if (!name.empty())
+                               names.emplace_back(name);
+               }
+       };
+
+       ColumnPackType columnPack;
+};
+
+template<typename ...Columns>
+Table<Columns...> make_table(const std::string& name, Columns&& ...cs)
+{
+       auto columnPack = internal::ColumnPack<Columns...>(std::forward<Columns>(cs)...);
+       return Table<Columns...>(name, std::move(columnPack));
+}
+
+template<typename... Columns>
+Table<Columns...>::Table(const std::string& name, ColumnPackType&& columnPack)
+       : name(name), columnPack(std::move(columnPack)) {}
+
+template<typename... Columns>
+template<typename Cs>
+std::set<std::string> Table<Columns...>::getTableNames(Cs&& tuple) const noexcept
+{
+       return {this->name};
+}
+
+template<typename... Columns>
+template<typename Cs>
+std::vector<std::string> Table<Columns...>::getColumnNames(Cs&& tuple) const noexcept
+{
+       GetColumnNames closure(this->columnPack);
+       tuple_helper::for_each(std::forward<Cs>(tuple), closure);
+
+       return closure.names;
+}
+
+template<typename... Columns>
+template<typename That>
+std::string Table<Columns...>::getTableName(That&& type) const noexcept
+{
+       return this->name;
+}
+
+template<typename... Columns>
+template<typename ColumnType>
+std::string Table<Columns...>::getColumnName(ColumnType&& type) const noexcept
+{
+       return this->columnPack.getName(std::forward<ColumnType>(type));
+}
+
+template<typename... Columns>
+std::vector<std::string> Table<Columns...>::getColumnNames(void) const noexcept
+{
+       return this->columnPack.getNames();
+}
+
+template<typename... Columns>
+template<typename That>
+bool Table<Columns...>::compare(const That& that) const noexcept
+{
+       using This = TableType;
+       return type::compare(This(), that);
+}
+
+template<typename... Columns>
+Table<Columns...>::operator std::string()
+{
+       std::stringstream ss;
+       for (const auto& c : cache)
+               ss << c << " ";
+
+       this->cache.clear();
+       return util::rtrim(ss.str());
+}
+
+template<typename... Columns>
+int Table<Columns...>::size() const noexcept
+{
+       return this->columnPack.size();
+}
+
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/tuple-helper.hxx b/src/apix/tsqb/include/tuple-helper.hxx
new file mode 100644 (file)
index 0000000..495946e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file tuple-helper.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Iterator method for tuple
+ */
+
+#pragma once
+
+#include <tuple>
+
+namespace tsqb {
+namespace tuple_helper {
+namespace internal {
+
+template<int n, typename T, typename C>
+class Iterator {
+public:
+       Iterator(const T& tuple, C&& closure) {
+               Iterator<n - 1, T, C> iter(tuple, std::forward<C>(closure));
+               closure(std::get<n-1>(tuple));
+       }
+};
+
+template<typename T, typename C>
+class Iterator<0, T, C> {
+public:
+       Iterator(const T&, C&&) {}
+};
+
+} // namespace internal
+
+template<typename T, typename C>
+void for_each(const T& tuple, C&& closure)
+{
+       using Iter = internal::Iterator<std::tuple_size<T>::value, T, C>;
+       Iter iter(tuple, std::forward<C>(closure));
+}
+
+} // namspace tuple-hepler
+} // namspace tsqb
diff --git a/src/apix/tsqb/include/type.hxx b/src/apix/tsqb/include/type.hxx
new file mode 100644 (file)
index 0000000..939b3da
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file type.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Make type safety with compile time checking
+ */
+
+#pragma once
+
+#include <type_traits>
+
+namespace tsqb {
+namespace type {
+
+template<typename L, typename R>
+bool cast_compare(L l, R r)
+{
+       return l == reinterpret_cast<L>(r);
+}
+
+template<typename L, typename R>
+bool compare(L l, R r)
+{
+       return std::is_same<L, R>::value;
+}
+
+template<typename L, typename R>
+void assert_compare(L l, R r)
+{
+       static_assert(std::is_same<L, R>::value, "Type is unsafe.");
+}
+
+} // namespace type
+} // namespace tsqb
diff --git a/src/apix/tsqb/include/util.hxx b/src/apix/tsqb/include/util.hxx
new file mode 100644 (file)
index 0000000..e65583d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file util.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ */
+
+#pragma once
+
+#include <string>
+#include <algorithm>
+#include <cctype>
+
+namespace tsqb {
+namespace util {
+
+inline std::string rtrim(std::string&& s)
+{
+       auto predicate = [](unsigned char c){ return !std::isspace(c); };
+       auto base = std::find_if(s.rbegin(), s.rend(), predicate).base();
+       s.erase(base, s.end());
+       return s;
+}
+
+} // namespace util
+} // namespace tsqb
diff --git a/src/apix/tsqb/tests/tsqb-tests.cpp b/src/apix/tsqb/tests/tsqb-tests.cpp
new file mode 100644 (file)
index 0000000..7770140
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file tsqb-tests.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief Testcases of tsqb
+ */
+
+#include "tsqb.hxx"
+
+#include <gtest/gtest.h>
+
+using namespace tsqb;
+
+struct Admin {
+       int id;
+       std::string pkg;
+       int uid;
+       std::string key;
+       int removable;
+};
+
+struct ManagedPolicy {
+       int id;
+       int aid;
+       int pid;
+       int value;
+};
+
+struct PolicyDefinition {
+       int id;
+       int scope;
+       std::string name;
+       int ivalue;
+};
+
+auto admin = make_table("admin", make_column("id", &Admin::id),
+                                                                make_column("pkg", &Admin::pkg),
+                                                                make_column("uid", &Admin::uid),
+                                                                make_column("key", &Admin::key),
+                                                                make_column("removable", &Admin::removable));
+
+auto managedPolicy = make_table("managed_policy",
+                                                                make_column("id", &ManagedPolicy::id),
+                                                                make_column("aid", &ManagedPolicy::aid),
+                                                                make_column("pid", &ManagedPolicy::pid),
+                                                                make_column("value", &ManagedPolicy::value));
+
+auto policyDefinition = make_table("policy_definition",
+                                                                  make_column("id", &PolicyDefinition::id),
+                                                                  make_column("scope", &PolicyDefinition::scope),
+                                                                  make_column("name", &PolicyDefinition::name),
+                                                                  make_column("ivalue", &PolicyDefinition::ivalue));
+
+auto db = make_database("dpm", admin, managedPolicy, policyDefinition);
+
+class TsqbTests : public testing::Test {};
+
+TEST_F(TsqbTests, SELECT)
+{
+       std::string select1 = admin.select(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
+       std::string select2 = admin.select(&Admin::id, &Admin::uid, &Admin::key);
+
+       EXPECT_EQ(select1, "SELECT id, pkg, uid, key FROM admin");
+       EXPECT_EQ(select2, "SELECT id, uid, key FROM admin");
+}
+
+TEST_F(TsqbTests, SELECT_ALL)
+{
+       std::string select = admin.selectAll();
+
+       EXPECT_EQ(select, "SELECT * FROM admin");
+}
+
+TEST_F(TsqbTests, SELECT_WHERE)
+{
+       std::string select1 = admin.select(&Admin::uid, &Admin::key)
+                                                          .where(expr(&Admin::id) > 3);
+       std::string select2 = admin.selectAll().where(expr(&Admin::uid) > 3);
+       std::string select3 = admin.selectAll().where(expr(&Admin::uid) > 3 &&
+                                                                                                 expr(&Admin::pkg) == "dpm");
+       std::string select4 = admin.selectAll().where(expr(&Admin::uid) > 3 ||
+                                                                                                 expr(&Admin::pkg) == "dpm");
+
+       EXPECT_EQ(select1, "SELECT uid, key FROM admin WHERE id > ?");
+       EXPECT_EQ(select2, "SELECT * FROM admin WHERE uid > ?");
+       EXPECT_EQ(select3, "SELECT * FROM admin WHERE uid > ? AND pkg = ?");
+       EXPECT_EQ(select4, "SELECT * FROM admin WHERE uid > ? OR pkg = ?");
+}
+
+TEST_F(TsqbTests, SELECT_DISTINCT)
+{
+       std::string select = admin.select(distinct(&Admin::uid, &Admin::key))
+                                                          .where(expr(&Admin::id) > 3);
+
+       EXPECT_EQ(select, "SELECT DISTINCT uid, key FROM admin WHERE id > ?");
+}
+
+TEST_F(TsqbTests, UPDATE)
+{
+       int uid = 0, id = 1;
+       std::string update1 = admin.update(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
+       std::string update2 = admin.update(&Admin::key).where(expr(&Admin::uid) == uid &&
+                                                                                                                 expr(&Admin::id) == id);
+       std::string update3 = admin.update(&Admin::key, &Admin::pkg)
+                                                          .where(expr(&Admin::uid) == 0 && expr(&Admin::id) == 1);
+
+       EXPECT_EQ(update1, "UPDATE admin SET id = ?, pkg = ?, uid = ?, key = ?");
+       EXPECT_EQ(update2, "UPDATE admin SET key = ? WHERE uid = ? AND id = ?");
+       EXPECT_EQ(update3, "UPDATE admin SET key = ?, pkg = ? WHERE uid = ? AND id = ?");
+}
+
+TEST_F(TsqbTests, DELETE)
+{
+       std::string delete1 = admin.remove();
+       std::string delete2 = admin.remove().where(expr(&Admin::pkg) == "dpm" &&
+                                                                                          expr(&Admin::uid) == 3);
+
+       EXPECT_EQ(delete1, "DELETE FROM admin");
+       EXPECT_EQ(delete2, "DELETE FROM admin WHERE pkg = ? AND uid = ?");
+}
+
+TEST_F(TsqbTests, INSERT)
+{
+       std::string insert1 = admin.insert(&Admin::id, &Admin::pkg, &Admin::uid, &Admin::key);
+       std::string insert2 = admin.insert(&Admin::id, &Admin::pkg, &Admin::key);
+
+       EXPECT_EQ(insert1, "INSERT INTO admin (id, pkg, uid, key) VALUES (?, ?, ?, ?)");
+       EXPECT_EQ(insert2, "INSERT INTO admin (id, pkg, key) VALUES (?, ?, ?)");
+}
+
+TEST_F(TsqbTests, TYPE_SAFE)
+{
+/*
+ * Below cause complie error since expression types are dismatch.
+
+       std::string type_unsafe1 = admin.selectAll().where(expr(&Admin::uid) > "dpm");
+       std::string type_unsafe2 = admin.selectAll().where(expr(&Admin::uid) == "dpm");
+       std::string type_unsafe3 = admin.selectAll().where(expr(&Admin::pkg) == 3);
+       int pkg = 3;
+       std::string type_unsafe4 = admin.selectAll().where(expr(&Admin::pkg) < pkg);
+       std::string type_unsafe5 = admin.remove().where(expr(&Admin::pkg) == "dpm" &&
+                                                                                                       expr(&Admin::uid) == "dpm");
+*/
+}
+
+TEST_F(TsqbTests, MULTI_SELECT)
+{
+       std::string multiSelect1 = db.select(&Admin::uid, &Admin::key,
+                                                                                &ManagedPolicy::id, &ManagedPolicy::value);
+       std::string multiSelect2 = db.select(&Admin::uid, &Admin::key,
+                                                                                &ManagedPolicy::id, &ManagedPolicy::value)
+                                                                .where(expr(&Admin::uid) > 0 && expr(&ManagedPolicy::id) == 3);
+
+       EXPECT_EQ(multiSelect1, "SELECT admin.uid, admin.key, managed_policy.id, "
+                                                       "managed_policy.value FROM admin, managed_policy");
+       EXPECT_EQ(multiSelect2, "SELECT admin.uid, admin.key, managed_policy.id, "
+                                                       "managed_policy.value FROM admin, managed_policy "
+                                                       "WHERE admin.uid > ? AND managed_policy.id = ?");
+}
+
+TEST_F(TsqbTests, JOIN)
+{
+       std::string join1 = db.select(&Admin::uid, &Admin::key)
+                                                 .join<PolicyDefinition>(condition::Join::LEFT_OUTER);
+       std::string join2 = db.select(&Admin::uid, &Admin::key)
+                                                 .join<ManagedPolicy>(condition::Join::CROSS);
+       std::string join3 = db.select(&ManagedPolicy::value)
+                                                 .join<PolicyDefinition>()
+                                                 .on(expr(&ManagedPolicy::pid) == expr(&PolicyDefinition::id))
+                                                 .join<Admin>()
+                                                 .on(expr(&ManagedPolicy::aid) == expr(&Admin::id))
+                                                 .where(expr(&ManagedPolicy::pid) == 99);
+
+       EXPECT_EQ(join1, "SELECT admin.uid, admin.key FROM admin "
+                                        "LEFT OUTER JOIN policy_definition");
+       EXPECT_EQ(join2, "SELECT admin.uid, admin.key FROM admin "
+                                        "CROSS JOIN managed_policy");
+       EXPECT_EQ(join3, "SELECT managed_policy.value FROM managed_policy "
+                                        "INNER JOIN policy_definition "
+                                        "ON managed_policy.pid = policy_definition.id "
+                                        "INNER JOIN admin ON managed_policy.aid = admin.id "
+                                        "WHERE managed_policy.pid = ?");
+}
diff --git a/src/apix/tsqb/tsqb.hxx b/src/apix/tsqb/tsqb.hxx
new file mode 100644 (file)
index 0000000..c3791f0
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2017-present Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+/*
+ * @file tsqb.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @brief TSQB is type-safe query builder
+ */
+
+#pragma once
+
+#include "include/database.hxx"
+#include "include/table.hxx"
+#include "include/column.hxx"
+#include "include/expression.hxx"
+#include "include/condition.hxx"
+#include "include/util.hxx"
diff --git a/src/osquery/CMakeLists.txt b/src/osquery/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5a9a349
--- /dev/null
@@ -0,0 +1,106 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+SET(TARGET_OSQUERY_TEST osquery-test)
+SET(TARGET_OSQUERY_SHELL osqueryi)
+
+SET(${TARGET_OSQUERY_LIB}_SRCS "")
+SET(${TARGET_OSQUERY_LIB}_DEPS "")
+SET(${TARGET_OSQUERY_LIB}_TESTS "")
+
+ADD_OSQUERY_LINK(glog
+                                gflags
+                                pthread
+                                libthrift.a
+                               #rocksdb deps
+                                librocksdb.a
+                                snappy
+                                z
+                                bz2
+                                dl
+                                lz4
+                                zstd
+                                boost_regex
+                                boost_system
+                                boost_thread
+                                boost_filesystem
+                                crypto # openssl
+                               #shell deps
+                                readline
+                               #build-in tables deps
+                                systemd)
+
+IF(DEFINED GBS_BUILD)
+       SET(GBS_ONLY_PACKAGES klay
+                                                 dpm-pil
+                                                 vconf)
+
+       INCLUDE(FindPkgConfig)
+       PKG_CHECK_MODULES(GBS_DEPS REQUIRED ${GBS_ONLY_PACKAGES})
+       INCLUDE_DIRECTORIES(SYSTEM ${GBS_DEPS_INCLUDE_DIRS})
+
+       ADD_OSQUERY_LINK(${GBS_DEPS_LIBRARIES})
+ENDIF(DEFINED GBS_BUILD)
+
+ADD_SUBDIRECTORY(core)
+ADD_SUBDIRECTORY(config)
+ADD_SUBDIRECTORY(dispatcher)
+ADD_SUBDIRECTORY(distributed)
+ADD_SUBDIRECTORY(devtools)
+ADD_SUBDIRECTORY(database)
+ADD_SUBDIRECTORY(events)
+ADD_SUBDIRECTORY(extensions)
+ADD_SUBDIRECTORY(filesystem)
+ADD_SUBDIRECTORY(logger)
+ADD_SUBDIRECTORY(registry)
+ADD_SUBDIRECTORY(sql)
+ADD_SUBDIRECTORY(tables)
+
+ADD_LIBRARY(${TARGET_OSQUERY_LIB}
+                               STATIC main/lib.cpp
+                                          $<TARGET_OBJECTS:osquery_generated_tables>
+                                          $<TARGET_OBJECTS:osquery_sqlite>
+                                          ${${TARGET_OSQUERY_LIB}_SRCS})
+TARGET_LINK_LIBRARIES(${TARGET_OSQUERY_LIB} ${${TARGET_OSQUERY_LIB}_DEPS})
+SET_TARGET_PROPERTIES(${TARGET_OSQUERY_LIB} PROPERTIES OUTPUT_NAME ${TARGET_OSQUERY_LIB})
+
+
+ADD_EXECUTABLE(${TARGET_OSQUERY_TEST} main/tests.cpp
+                                                                         ${${TARGET_OSQUERY_LIB}_TESTS})
+TARGET_LINK_WHOLE(${TARGET_OSQUERY_TEST} ${TARGET_OSQUERY_LIB})
+TARGET_LINK_LIBRARIES(${TARGET_OSQUERY_TEST} gtest)
+SET_TARGET_PROPERTIES(${TARGET_OSQUERY_TEST}
+                                         PROPERTIES COMPILE_FLAGS "-DGTEST_HAS_TR1_TUPLE=0")
+ADD_TEST(${TARGET_OSQUERY_TEST} ${TARGET_OSQUERY_TEST})
+INSTALL(TARGETS ${TARGET_OSQUERY_TEST}
+               DESTINATION ${CMAKE_INSTALL_BINDIR}
+               PERMISSIONS OWNER_READ
+                                       OWNER_WRITE
+                                       OWNER_EXECUTE
+                                       GROUP_READ
+                                       GROUP_EXECUTE
+                                       WORLD_READ
+                                       WORLD_EXECUTE)
+
+ADD_EXECUTABLE(${TARGET_OSQUERY_SHELL} devtools/shell.cpp main/shell.cpp)
+TARGET_LINK_WHOLE(${TARGET_OSQUERY_SHELL} ${TARGET_OSQUERY_LIB})
+INSTALL(TARGETS ${TARGET_OSQUERY_SHELL}
+               DESTINATION ${CMAKE_INSTALL_BINDIR}
+               PERMISSIONS OWNER_READ
+                                       OWNER_WRITE
+                                       OWNER_EXECUTE
+                                       GROUP_READ
+                                       GROUP_EXECUTE
+                                       WORLD_READ
+                                       WORLD_EXECUTE)
diff --git a/src/osquery/config/CMakeLists.txt b/src/osquery/config/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0cb3d7f
--- /dev/null
@@ -0,0 +1,22 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_config config.cpp)
+
+ADD_OSQUERY_LIBRARY(osquery_config_plugins update.cpp
+                                                                                  plugins/filesystem.cpp
+                                                                                  parsers/query_packs.cpp)
+
+FILE(GLOB OSQUERY_CONFIG_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_CONFIG_TESTS})
diff --git a/src/osquery/config/config.cpp b/src/osquery/config/config.cpp
new file mode 100644 (file)
index 0000000..52b0c76
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <chrono>
+#include <mutex>
+#include <random>
+#include <sstream>
+
+#include <osquery/config.h>
+#include <osquery/flags.h>
+#include <osquery/hash.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/registry.h>
+#include <osquery/tables.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+typedef pt::ptree::value_type tree_node;
+typedef std::map<std::string, std::vector<std::string> > EventFileMap_t;
+typedef std::chrono::high_resolution_clock chrono_clock;
+
+/// The config plugin must be known before reading options.
+CLI_FLAG(string, config_plugin, "filesystem", "Config plugin name");
+
+FLAG(int32, schedule_splay_percent, 10, "Percent to splay config times");
+
+Status Config::load() {
+  auto& config_plugin = Registry::getActive("config");
+  if (!Registry::exists("config", config_plugin)) {
+    return Status(1, "Missing config plugin " + config_plugin);
+  }
+
+  return genConfig();
+}
+
+Status Config::update(const std::map<std::string, std::string>& config) {
+  // A config plugin may call update from an extension. This will update
+  // the config instance within the extension process and the update must be
+  // reflected in the core.
+  if (Registry::external()) {
+    for (const auto& source : config) {
+      PluginRequest request = {
+          {"action", "update"},
+          {"source", source.first},
+          {"data", source.second},
+      };
+      // A "update" registry item within core should call the core's update
+      // method. The config plugin call action handling must also know to
+      // update.
+      Registry::call("config", "update", request);
+    }
+  }
+
+  // Request a unique write lock when updating config.
+  {
+    boost::unique_lock<boost::shared_mutex> unique_lock(getInstance().mutex_);
+
+    for (const auto& source : config) {
+      if (Registry::external()) {
+        VLOG(1) << "Updating extension config with source: " << source.first;
+      } else {
+        VLOG(1) << "Updating config with source: " << source.first;
+      }
+      getInstance().raw_[source.first] = source.second;
+    }
+
+    // Now merge all sources together.
+    ConfigData conf;
+    for (const auto& source : getInstance().raw_) {
+      auto status = mergeConfig(source.second, conf);
+      if (getInstance().force_merge_success_ && !status.ok()) {
+        return Status(1, status.what());
+      }
+    }
+
+    // Call each parser with the optionally-empty, requested, top level keys.
+    getInstance().data_ = std::move(conf);
+  }
+
+  for (const auto& plugin : Registry::all("config_parser")) {
+    auto parser = std::static_pointer_cast<ConfigParserPlugin>(plugin.second);
+    if (parser == nullptr || parser.get() == nullptr) {
+      continue;
+    }
+
+    // For each key requested by the parser, add a property tree reference.
+    std::map<std::string, ConfigTree> parser_config;
+    for (const auto& key : parser->keys()) {
+      if (getInstance().data_.all_data.count(key) > 0) {
+        parser_config[key] = getInstance().data_.all_data.get_child(key);
+      } else {
+        parser_config[key] = pt::ptree();
+      }
+    }
+
+    // The config parser plugin will receive a copy of each property tree for
+    // each top-level-config key. The parser may choose to update the config's
+    // internal state by requesting and modifying a ConfigDataInstance.
+    parser->update(parser_config);
+  }
+
+  return Status(0, "OK");
+}
+
+Status Config::genConfig() {
+  PluginResponse response;
+  auto status = Registry::call("config", {{"action", "genConfig"}}, response);
+  if (!status.ok()) {
+    return status;
+  }
+
+  if (response.size() > 0) {
+    return update(response[0]);
+  }
+  return Status(0, "OK");
+}
+
+inline void mergeOption(const tree_node& option, ConfigData& conf) {
+  std::string key = option.first.data();
+  std::string value = option.second.data();
+
+  Flag::updateValue(key, value);
+  // There is a special case for supported Gflags-reserved switches.
+  if (key == "verbose" || key == "verbose_debug" || key == "debug") {
+    setVerboseLevel();
+    if (Flag::getValue("verbose") == "true") {
+      VLOG(1) << "Verbose logging enabled by config option";
+    }
+  }
+
+  conf.options[key] = value;
+  if (conf.all_data.count("options") > 0) {
+    conf.all_data.get_child("options").erase(key);
+  }
+  conf.all_data.add_child("options." + key, option.second);
+}
+
+inline void additionalScheduledQuery(const std::string& name,
+                                     const tree_node& node,
+                                     ConfigData& conf) {
+  // Read tree/JSON into a query structure.
+  ScheduledQuery query;
+  query.query = node.second.get<std::string>("query", "");
+  query.interval = node.second.get<int>("interval", 0);
+  if (query.interval == 0) {
+    VLOG(1) << "Setting invalid interval=0 to 84600 for query: " << name;
+    query.interval = 86400;
+  }
+
+  // This is a candidate for a catch-all iterator with a catch for boolean type.
+  query.options["snapshot"] = node.second.get<bool>("snapshot", false);
+  query.options["removed"] = node.second.get<bool>("removed", true);
+
+  // Check if this query exists, if so, check if it was changed.
+  if (conf.schedule.count(name) > 0) {
+    if (query == conf.schedule.at(name)) {
+      return;
+    }
+  }
+
+  // This is a new or updated scheduled query, update the splay.
+  query.splayed_interval =
+      splayValue(query.interval, FLAGS_schedule_splay_percent);
+  // Update the schedule map and replace the all_data node record.
+  conf.schedule[name] = query;
+}
+
+inline void mergeScheduledQuery(const std::string& name,
+                                const tree_node& node,
+                                ConfigData& conf) {
+  // Add the new query to the configuration.
+  additionalScheduledQuery(name, node, conf);
+  // Replace the all_data node record.
+  if (conf.all_data.count("schedule") > 0) {
+    conf.all_data.get_child("schedule").erase(name);
+  }
+  conf.all_data.add_child("schedule." + name, node.second);
+}
+
+inline void mergeExtraKey(const std::string& name,
+                          const tree_node& node,
+                          ConfigData& conf) {
+  // Automatically merge extra list/dict top level keys.
+  for (const auto& subitem : node.second) {
+    if (node.second.count("") == 0 && conf.all_data.count(name) > 0) {
+      conf.all_data.get_child(name).erase(subitem.first);
+    }
+
+    if (subitem.first.size() == 0) {
+      if (conf.all_data.count(name) == 0) {
+        conf.all_data.add_child(name, subitem.second);
+      }
+      conf.all_data.get_child(name).push_back(subitem);
+    } else {
+      conf.all_data.add_child(name + "." + subitem.first, subitem.second);
+    }
+  }
+}
+
+inline void mergeFilePath(const std::string& name,
+                          const tree_node& node,
+                          ConfigData& conf) {
+  for (const auto& path : node.second) {
+    // Add the exact path after converting wildcards.
+    std::string pattern = path.second.data();
+    replaceGlobWildcards(pattern);
+    conf.files[node.first].push_back(std::move(pattern));
+  }
+  conf.all_data.add_child(name + "." + node.first, node.second);
+}
+
+Status Config::mergeConfig(const std::string& source, ConfigData& conf) {
+  pt::ptree tree;
+  try {
+    std::stringstream json_data;
+    json_data << source;
+    pt::read_json(json_data, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    LOG(WARNING) << "Error parsing config JSON: " << e.what();
+    return Status(1, e.what());
+  }
+
+  if (tree.count("additional_monitoring") > 0) {
+    LOG(INFO) << RLOG(903) << "config 'additional_monitoring' is deprecated";
+    for (const auto& node : tree.get_child("additional_monitoring")) {
+      tree.add_child(node.first, node.second);
+    }
+    tree.erase("additional_monitoring");
+  }
+
+  for (const auto& item : tree) {
+    // Iterate over each top-level configuration key.
+    auto key = std::string(item.first.data());
+    if (key == "scheduledQueries") {
+      LOG(INFO) << RLOG(903) << "config 'scheduledQueries' is deprecated";
+      for (const auto& node : item.second) {
+        auto query_name = node.second.get<std::string>("name", "");
+        mergeScheduledQuery(query_name, node, conf);
+      }
+    } else if (key == "schedule") {
+      for (const auto& node : item.second) {
+        mergeScheduledQuery(node.first.data(), node, conf);
+      }
+    } else if (key == "options") {
+      for (const auto& option : item.second) {
+        mergeOption(option, conf);
+      }
+    } else if (key == "file_paths") {
+      for (const auto& category : item.second) {
+        mergeFilePath(key, category, conf);
+      }
+    } else {
+      mergeExtraKey(key, item, conf);
+    }
+  }
+
+  return Status(0, "OK");
+}
+
+const pt::ptree& Config::getParsedData(const std::string& key) {
+  if (!Registry::exists("config_parser", key)) {
+    return getInstance().empty_data_;
+  }
+
+  const auto& item = Registry::get("config_parser", key);
+  auto parser = std::static_pointer_cast<ConfigParserPlugin>(item);
+  if (parser == nullptr || parser.get() == nullptr) {
+    return getInstance().empty_data_;
+  }
+
+  return parser->data_;
+}
+
+const ConfigPluginRef Config::getParser(const std::string& key) {
+  if (!Registry::exists("config_parser", key)) {
+    return ConfigPluginRef();
+  }
+
+  const auto& item = Registry::get("config_parser", key);
+  const auto parser = std::static_pointer_cast<ConfigParserPlugin>(item);
+  if (parser == nullptr || parser.get() == nullptr) {
+    return ConfigPluginRef();
+  }
+
+  return parser;
+}
+
+Status Config::getMD5(std::string& hash_string) {
+  // Request an accessor to our own config, outside of an update.
+  ConfigDataInstance config;
+
+  std::stringstream out;
+  try {
+    pt::write_json(out, config.data(), false);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return Status(1, e.what());
+  }
+
+  hash_string = osquery::hashFromBuffer(
+      HASH_TYPE_MD5, (void*)out.str().c_str(), out.str().length());
+
+  return Status(0, "OK");
+}
+
+void Config::addScheduledQuery(const std::string& name,
+                               const std::string& query,
+                               const int interval) {
+  // Create structure to add to the schedule.
+  tree_node node;
+  node.second.put("query", query);
+  node.second.put("interval", interval);
+
+  // Call to the inline function.
+  additionalScheduledQuery(name, node, getInstance().data_);
+}
+
+Status Config::checkConfig() {
+  getInstance().force_merge_success_ = true;
+  return load();
+}
+
+bool Config::checkScheduledQuery(const std::string& query) {
+  for (const auto& scheduled_query : getInstance().data_.schedule) {
+    if (scheduled_query.second.query == query) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool Config::checkScheduledQueryName(const std::string& query_name) {
+  return (getInstance().data_.schedule.count(query_name) == 0) ? false : true;
+}
+
+void Config::recordQueryPerformance(const std::string& name,
+                                    size_t delay,
+                                    size_t size,
+                                    const Row& r0,
+                                    const Row& r1) {
+  // Grab a lock on the schedule structure and check the name.
+  ConfigDataInstance config;
+  if (config.schedule().count(name) == 0) {
+    // Unknown query schedule name.
+    return;
+  }
+
+  // Grab access to the non-const schedule item.
+  auto& query = getInstance().data_.schedule.at(name);
+  auto diff = AS_LITERAL(BIGINT_LITERAL, r1.at("user_time")) -
+              AS_LITERAL(BIGINT_LITERAL, r0.at("user_time"));
+  if (diff > 0) {
+    query.user_time += diff;
+  }
+
+  diff = AS_LITERAL(BIGINT_LITERAL, r1.at("system_time")) -
+         AS_LITERAL(BIGINT_LITERAL, r0.at("system_time"));
+  if (diff > 0) {
+    query.system_time += diff;
+  }
+
+  diff = AS_LITERAL(BIGINT_LITERAL, r1.at("resident_size")) -
+         AS_LITERAL(BIGINT_LITERAL, r0.at("resident_size"));
+  if (diff > 0) {
+    // Memory is stored as an average of RSS changes between query executions.
+    query.average_memory = (query.average_memory * query.executions) + diff;
+    query.average_memory = (query.average_memory / (query.executions + 1));
+  }
+
+  query.wall_time += delay;
+  query.output_size += size;
+  query.executions += 1;
+}
+
+Status ConfigPlugin::call(const PluginRequest& request,
+                          PluginResponse& response) {
+  if (request.count("action") == 0) {
+    return Status(1, "Config plugins require an action in PluginRequest");
+  }
+
+  if (request.at("action") == "genConfig") {
+    std::map<std::string, std::string> config;
+    auto stat = genConfig(config);
+    response.push_back(config);
+    return stat;
+  } else if (request.at("action") == "update") {
+    if (request.count("source") == 0 || request.count("data") == 0) {
+      return Status(1, "Missing source or data");
+    }
+    return Config::update({{request.at("source"), request.at("data")}});
+  }
+  return Status(1, "Config plugin action unknown: " + request.at("action"));
+}
+
+Status ConfigParserPlugin::setUp() {
+  for (const auto& key : keys()) {
+    data_.put(key, "");
+  }
+  return Status(0, "OK");
+}
+
+int splayValue(int original, int splayPercent) {
+  if (splayPercent <= 0 || splayPercent > 100) {
+    return original;
+  }
+
+  float percent_to_modify_by = (float)splayPercent / 100;
+  int possible_difference = original * percent_to_modify_by;
+  int max_value = original + possible_difference;
+  int min_value = original - possible_difference;
+
+  if (max_value == min_value) {
+    return max_value;
+  }
+
+  std::default_random_engine generator;
+  generator.seed(chrono_clock::now().time_since_epoch().count());
+  std::uniform_int_distribution<int> distribution(min_value, max_value);
+  return distribution(generator);
+}
+}
diff --git a/src/osquery/config/parsers/query_packs.cpp b/src/osquery/config/parsers/query_packs.cpp
new file mode 100644 (file)
index 0000000..e5923f3
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (c) 2015, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <map>
+#include <string>
+
+#include <osquery/config.h>
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+/**
+ * @brief A simple ConfigParserPlugin for a "packs" dictionary key.
+ *
+ */
+class QueryPackConfigParserPlugin : public ConfigParserPlugin {
+ public:
+  /// Request "packs" top level key.
+  std::vector<std::string> keys() { return {"packs"}; }
+
+ private:
+  /// Store the signatures and file_paths and compile the rules.
+  Status update(const ConfigTreeMap& config);
+};
+
+// Function to check if the pack is valid for this version of osquery.
+// If the osquery version is greater or equal than the pack, it is good to go.
+bool versionChecker(const std::string& pack, const std::string& version) {
+  auto required_version = split(pack, ".");
+  auto build_version = split(version, ".");
+
+  size_t index = 0;
+  for (const auto& chunk : build_version) {
+    if (required_version.size() <= index) {
+      return true;
+    }
+    try {
+      if (std::stoi(chunk) < std::stoi(required_version[index])) {
+        return false;
+      }
+    } catch (const std::invalid_argument& e) {
+      if (chunk.compare(required_version[index]) < 0) {
+        return false;
+      }
+    }
+    index++;
+  }
+  return true;
+}
+
+// Perform a string string search for the actual platform within the required.
+bool platformChecker(const std::string& required, const std::string& platform) {
+  // Match if platform is 'ubuntu12' and required is 'ubuntu'.
+  // Do not match if platform is 'ubuntu12' and required is 'ubuntu14'.
+#ifdef __linux__
+  if (required.find("linux") != std::string::npos) {
+    return true;
+  }
+#endif
+  if (required.find("any") != std::string::npos ||
+      required.find("all") != std::string::npos) {
+    return true;
+  }
+  return (required.find(platform) != std::string::npos);
+}
+
+Status parsePack(const std::string& name, const pt::ptree& data) {
+  if (data.count("queries") == 0) {
+    return Status(0, "Pack contains no queries");
+  }
+
+  // Check the pack-global minimum SDK version and platform.
+  auto version = data.get("version", "");
+  if (version.size() > 0 && !versionChecker(version, kSDKVersion)) {
+    return Status(0, "Minimum SDK version not met");
+  }
+
+  auto platform = data.get("platform", "");
+  if (platform.size() > 0 && !platformChecker(platform, kSDKPlatform)) {
+    return Status(0, "Platform version mismatch");
+  }
+
+  // For each query in the pack's queries, check their version/platform.
+  for (const auto& query : data.get_child("queries")) {
+    auto query_string = query.second.get("query", "");
+    if (Config::checkScheduledQuery(query_string)) {
+      VLOG(1) << "Query pack " << name
+              << " contains a duplicated query: " << query.first;
+      continue;
+    }
+
+    // Check the specific query's required version.
+    version = query.second.get("version", "");
+    if (version.size() > 0 && !versionChecker(version, kSDKVersion)) {
+      continue;
+    }
+
+    // Check the specific query's required platform.
+    platform = query.second.get("platform", "");
+    if (platform.size() > 0 && !platformChecker(platform, kSDKPlatform)) {
+      continue;
+    }
+
+    // Hope there is a supplied/non-0 query interval to apply this query pack
+    // query to the osquery schedule.
+    auto query_interval = query.second.get("interval", 0);
+    if (query_interval > 0) {
+      auto query_name = "pack_" + name + "_" + query.first;
+      Config::addScheduledQuery(query_name, query_string, query_interval);
+    }
+  }
+
+  return Status(0, "OK");
+}
+
+Status QueryPackConfigParserPlugin::update(const ConfigTreeMap& config) {
+  // Iterate through all the packs to get the configuration.
+  for (auto const& pack : config.at("packs")) {
+    auto pack_name = std::string(pack.first.data());
+    auto pack_path = std::string(pack.second.data());
+
+    // Read each pack configuration in JSON
+    pt::ptree pack_data;
+    auto status = osquery::parseJSON(pack_path, pack_data);
+    if (!status.ok()) {
+      LOG(WARNING) << "Error parsing Query Pack " << pack_name << ": "
+                   << status.getMessage();
+      continue;
+    }
+
+    // Parse the pack, meaning compare version/platform requirements and
+    // check the sanity of each query in the pack's queries.
+    status = parsePack(pack_name, pack_data);
+    if (!status.ok()) {
+      return status;
+    }
+
+    // Save the queries list for table-based introspection.
+    data_.put_child(pack_name, pack_data);
+    // Record the pack path.
+    data_.put(pack_name + ".path", pack_path);
+  }
+
+  return Status(0, "OK");
+}
+
+/// Call the simple Query Packs ConfigParserPlugin "packs".
+REGISTER_INTERNAL(QueryPackConfigParserPlugin, "config_parser", "packs");
+}
diff --git a/src/osquery/config/parsers/tests/query_packs_tests.cpp b/src/osquery/config/parsers/tests/query_packs_tests.cpp
new file mode 100644 (file)
index 0000000..22a67fb
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/logger.h>
+
+#include "osquery/core/test_util.h"
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+// Test the pack version checker.
+bool versionChecker(const std::string& pack, const std::string& version);
+// Test the pack platform checker.
+bool platformChecker(const std::string& required, const std::string& platform);
+
+pt::ptree getQueryPacksContent() {
+  pt::ptree pack_tree;
+  auto pack_path = kTestDataPath + "test_pack.conf";
+  auto status = osquery::parseJSON(pack_path, pack_tree);
+  return pack_tree.get_child("queries");
+}
+
+std::map<std::string, pt::ptree> getQueryPacksExpectedResults() {
+  std::map<std::string, pt::ptree> result;
+  pt::ptree aux_data;
+
+  std::string query = "select * from launchd";
+  aux_data.put("query", query);
+  int interval = 414141;
+  aux_data.put("interval", interval);
+  std::string platform = "whatever";
+  aux_data.put("platform", platform);
+  std::string version = "1.0.0";
+  aux_data.put("version", version);
+  std::string description = "Very descriptive description";
+  aux_data.put("description", description);
+  std::string value = "Value overflow";
+  aux_data.put("value", value);
+
+  result.insert(std::pair<std::string, pt::ptree>("launchd", aux_data));
+
+  return result;
+}
+
+class QueryPacksConfigTests : public testing::Test {};
+
+TEST_F(QueryPacksConfigTests, version_comparisons) {
+  EXPECT_TRUE(versionChecker("1.0.0", "1.0.0"));
+  EXPECT_TRUE(versionChecker("1.0.0", "1.2.0"));
+  EXPECT_TRUE(versionChecker("1.0", "1.2.0"));
+  EXPECT_TRUE(versionChecker("1.0", "1.0.2"));
+  EXPECT_TRUE(versionChecker("1.0.0", "1.0.2-r1"));
+  EXPECT_FALSE(versionChecker("1.2", "1.0.2"));
+  EXPECT_TRUE(versionChecker("1.0.0-r1", "1.0.0"));
+}
+
+TEST_F(QueryPacksConfigTests, platform_comparisons) {
+#ifdef __linux__
+  // If the platform is linux and the required platform is linux, match
+  EXPECT_TRUE(platformChecker("linux", "ubuntu"));
+  EXPECT_TRUE(platformChecker("linux", "who_knows_what"));
+#endif
+  EXPECT_TRUE(platformChecker("linux,darwin", "darwin"));
+  EXPECT_TRUE(platformChecker("darwin", "darwin"));
+  EXPECT_FALSE(platformChecker("darwin", "linux"));
+
+  EXPECT_TRUE(platformChecker(" darwin", "darwin"));
+  // There are no logical operators, just matching.
+  EXPECT_TRUE(platformChecker("!darwin", "darwin"));
+
+  EXPECT_TRUE(platformChecker("all", "darwin"));
+  EXPECT_TRUE(platformChecker("any", "darwin"));
+}
+
+TEST_F(QueryPacksConfigTests, test_query_packs_configuration) {
+  auto data = getQueryPacksContent();
+  auto expected = getQueryPacksExpectedResults();
+  auto& real_ld = data.get_child("launchd");
+  auto& expect_ld = expected["launchd"];
+
+  EXPECT_EQ(expect_ld.get("query", ""), real_ld.get("query", ""));
+  EXPECT_EQ(expect_ld.get("interval", 0), real_ld.get("interval", 0));
+  EXPECT_EQ(expect_ld.get("platform", ""), real_ld.get("platform", ""));
+  EXPECT_EQ(expect_ld.get("version", ""), real_ld.get("version", ""));
+  EXPECT_EQ(expect_ld.get("description", ""), real_ld.get("description", ""));
+  EXPECT_EQ(expect_ld.get("value", ""), real_ld.get("value", ""));
+}
+}
diff --git a/src/osquery/config/plugins/filesystem.cpp b/src/osquery/config/plugins/filesystem.cpp
new file mode 100644 (file)
index 0000000..e73bb78
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+
+#include <boost/filesystem/operations.hpp>
+
+#include <osquery/config.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+#include <osquery/filesystem.h>
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+CLI_FLAG(string,
+         config_path,
+         "/var/osquery/osquery.conf",
+         "Path to JSON config file");
+
+class FilesystemConfigPlugin : public ConfigPlugin {
+ public:
+  Status genConfig(std::map<std::string, std::string>& config);
+};
+
+REGISTER(FilesystemConfigPlugin, "config", "filesystem");
+
+Status FilesystemConfigPlugin::genConfig(
+    std::map<std::string, std::string>& config) {
+  if (!fs::is_regular_file(FLAGS_config_path)) {
+    return Status(1, "config file does not exist");
+  }
+
+  std::vector<std::string> conf_files;
+  resolveFilePattern(FLAGS_config_path + ".d/%.conf", conf_files);
+  std::sort(conf_files.begin(), conf_files.end());
+  conf_files.push_back(FLAGS_config_path);
+
+  for (const auto& path : conf_files) {
+    std::string content;
+    if (readFile(path, content).ok()) {
+      config[path] = content;
+    }
+  }
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/config/tests/config_tests.cpp b/src/osquery/config/tests/config_tests.cpp
new file mode 100644 (file)
index 0000000..38a786b
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <osquery/config.h>
+#include <osquery/core.h>
+#include <osquery/flags.h>
+#include <osquery/registry.h>
+#include <osquery/sql.h>
+
+#include "osquery/core/test_util.h"
+
+namespace osquery {
+
+// The config_path flag is defined in the filesystem config plugin.
+DECLARE_string(config_path);
+
+class ConfigTests : public testing::Test {
+ public:
+  ConfigTests() {
+    Registry::setActive("config", "filesystem");
+    FLAGS_config_path = kTestDataPath + "test.config";
+  }
+
+ protected:
+  void SetUp() {
+    createMockFileStructure();
+    Registry::setUp();
+    Config::load();
+  }
+
+  void TearDown() { tearDownMockFileStructure(); }
+};
+
+class TestConfigPlugin : public ConfigPlugin {
+ public:
+  TestConfigPlugin() {}
+  Status genConfig(std::map<std::string, std::string>& config) {
+    config["data"] = "foobar";
+    return Status(0, "OK");
+    ;
+  }
+};
+
+TEST_F(ConfigTests, test_plugin) {
+  Registry::add<TestConfigPlugin>("config", "test");
+
+  // Change the active config plugin.
+  EXPECT_TRUE(Registry::setActive("config", "test").ok());
+
+  PluginResponse response;
+  auto status = Registry::call("config", {{"action", "genConfig"}}, response);
+
+  EXPECT_EQ(status.ok(), true);
+  EXPECT_EQ(status.toString(), "OK");
+  EXPECT_EQ(response[0].at("data"), "foobar");
+}
+
+/* deprecated
+TEST_F(ConfigTests, test_queries_execute) {
+  ConfigDataInstance config;
+  EXPECT_EQ(config.schedule().size(), 3);
+}
+
+TEST_F(ConfigTests, test_watched_files) {
+  ConfigDataInstance config;
+  ASSERT_EQ(config.files().size(), 3);
+  // From the deprecated "additional_monitoring" collection.
+  EXPECT_EQ(config.files().at("downloads").size(), 1);
+
+  // From the new, recommended top-level "file_paths" collection.
+  EXPECT_EQ(config.files().at("system_binaries").size(), 2);
+}
+*/
+
+TEST_F(ConfigTests, test_locking) {
+  {
+    // Assume multiple instance accessors will be active.
+    ConfigDataInstance config1;
+    ConfigDataInstance config2;
+
+    // But a unique lock cannot be acquired.
+    boost::unique_lock<boost::shared_mutex> lock(Config::getInstance().mutex_,
+                                                 boost::defer_lock);
+    ASSERT_FALSE(lock.try_lock());
+  }
+
+  {
+    // However, a unique lock can be obtained when without instances accessors.
+    boost::unique_lock<boost::shared_mutex> lock(Config::getInstance().mutex_,
+                                                 boost::defer_lock);
+    ASSERT_TRUE(lock.try_lock());
+  }
+}
+
+TEST_F(ConfigTests, test_config_update) {
+  std::string digest;
+  // Get a snapshot of the digest before making config updates.
+  auto status = Config::getMD5(digest);
+  EXPECT_TRUE(status);
+
+  // Request an update of the 'new_source1'. Set new1 = value.
+  status =
+      Config::update({{"new_source1", "{\"options\": {\"new1\": \"value\"}}"}});
+  EXPECT_TRUE(status);
+
+  // At least, the amalgamated config digest should have changed.
+  std::string new_digest;
+  Config::getMD5(new_digest);
+  EXPECT_NE(digest, new_digest);
+
+  // Access the option that was added in the update to source 'new_source1'.
+  {
+    ConfigDataInstance config;
+    auto option = config.data().get<std::string>("options.new1", "");
+    EXPECT_EQ(option, "value");
+  }
+
+  // Add a lexically larger source that emits the same option 'new1'.
+  Config::update({{"new_source2", "{\"options\": {\"new1\": \"changed\"}}"}});
+
+  {
+    ConfigDataInstance config;
+    auto option = config.data().get<std::string>("options.new1", "");
+    // Expect the amalgamation to have overwritten 'new_source1'.
+    EXPECT_EQ(option, "changed");
+  }
+
+  // Again add a source but emit a different option, both 'new1' and 'new2'
+  // should be in the amalgamated/merged config.
+  Config::update({{"new_source3", "{\"options\": {\"new2\": \"different\"}}"}});
+
+  {
+    ConfigDataInstance config;
+    auto option = config.data().get<std::string>("options.new1", "");
+    EXPECT_EQ(option, "changed");
+    option = config.data().get<std::string>("options.new2", "");
+    EXPECT_EQ(option, "different");
+  }
+}
+
+TEST_F(ConfigTests, test_bad_config_update) {
+  std::string bad_json = "{\"options\": {},}";
+  ASSERT_NO_THROW(Config::update({{"bad_source", bad_json}}));
+}
+
+class TestConfigParserPlugin : public ConfigParserPlugin {
+ public:
+  std::vector<std::string> keys() {
+    // This config parser requests the follow top-level-config keys.
+    return {"dictionary", "dictionary2", "list"};
+  }
+
+  Status update(const std::map<std::string, ConfigTree>& config) {
+    // Set a simple boolean indicating the update callin occurred.
+    update_called = true;
+    // Copy all expected keys into the parser's data.
+    for (const auto& key : config) {
+      data_.put_child(key.first, key.second);
+    }
+
+    // Set parser-rendered additional data.
+    // Other plugins may request this "rendered/derived" data using a
+    // ConfigDataInstance and the getParsedData method.
+    data_.put("dictionary3.key2", "value2");
+    return Status(0, "OK");
+  }
+
+  // Flag tracking that the update method was called.
+  static bool update_called;
+
+ private:
+  FRIEND_TEST(ConfigTests, test_config_parser);
+};
+
+// An intermediate boolean to check parser updates.
+bool TestConfigParserPlugin::update_called = false;
+
+TEST_F(ConfigTests, test_config_parser) {
+  // Register a config parser plugin, and call setup.
+  Registry::add<TestConfigParserPlugin>("config_parser", "test");
+  Registry::get("config_parser", "test")->setUp();
+
+  {
+    // Access the parser's data without having updated the configuration.
+    ConfigDataInstance config;
+    const auto& test_data = config.getParsedData("test");
+
+    // Expect the setUp method to have run and set blank defaults.
+    // Accessing an invalid property tree key will abort.
+    ASSERT_EQ(test_data.get_child("dictionary").count(""), 0);
+  }
+
+  // Update or load the config, expect the parser to be called.
+  Config::update(
+      {{"source1",
+        "{\"dictionary\": {\"key1\": \"value1\"}, \"list\": [\"first\"]}"}});
+  ASSERT_TRUE(TestConfigParserPlugin::update_called);
+
+  {
+    // Now access the parser's data AFTER updating the config (no longer blank)
+    ConfigDataInstance config;
+    const auto& test_data = config.getParsedData("test");
+
+    // Expect a value that existed in the configuration.
+    EXPECT_EQ(test_data.count("dictionary"), 1);
+    EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1");
+    // Expect a value for every key the parser requested.
+    // Every requested key will be present, event if the key's tree is empty.
+    EXPECT_EQ(test_data.count("dictionary2"), 1);
+    // Expect the parser-created data item.
+    EXPECT_EQ(test_data.count("dictionary3"), 1);
+    EXPECT_EQ(test_data.get("dictionary3.key2", ""), "value2");
+  }
+
+  // Update from a secondary source into a dictionary.
+  // Expect that the keys in the top-level dictionary are merged.
+  Config::update({{"source2", "{\"dictionary\": {\"key3\": \"value3\"}}"}});
+  // Update from a third source into a list.
+  // Expect that the items from each source in the top-level list are merged.
+  Config::update({{"source3", "{\"list\": [\"second\"]}"}});
+
+  {
+    ConfigDataInstance config;
+    const auto& test_data = config.getParsedData("test");
+
+    EXPECT_EQ(test_data.count("dictionary"), 1);
+    EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1");
+    EXPECT_EQ(test_data.get("dictionary.key3", ""), "value3");
+    EXPECT_EQ(test_data.count("list"), 1);
+    EXPECT_EQ(test_data.get_child("list").count(""), 2);
+  }
+}
+
+class TestConfigMutationParserPlugin : public ConfigParserPlugin {
+ public:
+  std::vector<std::string> keys() {
+    // This config parser wants access to the well-known schedule key.
+    return {"schedule"};
+  }
+
+  Status update(const std::map<std::string, ConfigTree>& config) {
+    // The merged raw schedule is available as a property tree.
+    auto& schedule_data = config.at("schedule");
+    (void)schedule_data;
+
+    {
+      // But we want access to the parsed schedule structure.
+      ConfigDataInstance _config;
+      auto& data = mutableConfigData(_config);
+
+      ScheduledQuery query;
+      query.query = "new query";
+      query.interval = 1;
+      data.schedule["test_config_mutation"] = query;
+    }
+
+    return Status(0, "OK");
+  }
+
+ private:
+  FRIEND_TEST(ConfigTests, test_config_mutaion_parser);
+};
+
+TEST_F(ConfigTests, test_config_mutaion_parser) {
+  Registry::add<TestConfigMutationParserPlugin>("config_parser", "mutable");
+  Registry::get("config_parser", "mutable")->setUp();
+
+  // Update or load the config, expect the parser to be called.
+  Config::update({{"source1", "{\"schedule\": {}}"}});
+
+  {
+    ConfigDataInstance config;
+    // The config schedule should have been mutated.
+    EXPECT_EQ(config.schedule().count("test_config_mutation"), 1);
+  }
+}
+
+TEST_F(ConfigTests, test_splay) {
+  auto val1 = splayValue(100, 10);
+  EXPECT_GE(val1, 90);
+  EXPECT_LE(val1, 110);
+
+  auto val2 = splayValue(100, 10);
+  EXPECT_GE(val2, 90);
+  EXPECT_LE(val2, 110);
+
+  auto val3 = splayValue(10, 0);
+  EXPECT_EQ(val3, 10);
+
+  auto val4 = splayValue(100, 1);
+  EXPECT_GE(val4, 99);
+  EXPECT_LE(val4, 101);
+
+  auto val5 = splayValue(1, 10);
+  EXPECT_EQ(val5, 1);
+}
+}
diff --git a/src/osquery/config/update.cpp b/src/osquery/config/update.cpp
new file mode 100644 (file)
index 0000000..6421c1a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/config.h>
+
+namespace osquery {
+
+/**
+ * @brief A special config plugin that updates an osquery core's config.
+ *
+ * Config plugins may asynchronously change config data for the core osquery
+ * process. This is a rare instance where a plugin acts to change core state.
+ * Plugins normally act on behalf of a registry or extension call.
+ * To acheive plugin-initiated calls, Config plugins chain calls to plugins
+ * using the UpdateConfigPlugin named 'update'.
+ *
+ * Plugins do not need to implement call-chaining explicitly. If an extension
+ * plugin implements an asynchronous feature it should call `Config::update`
+ * directly. The osquery config will check if the registry is external, meaning
+ * the config instance is running as an extension. If external, config will
+ * route the update request and the registry will send missing (in this case
+ * "config/update" is missing) requests to core.
+ */
+class UpdateConfigPlugin : public ConfigPlugin {
+ public:
+  Status genConfig(std::map<std::string, std::string>& config) {
+    return Status(0, "Unused");
+  }
+};
+
+REGISTER(UpdateConfigPlugin, "config", "update");
+}
diff --git a/src/osquery/core/CMakeLists.txt b/src/osquery/core/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fe39034
--- /dev/null
@@ -0,0 +1,28 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_core init.cpp
+                                                                conversions.cpp
+                                                                system.cpp
+                                                                text.cpp
+                                                                tables.cpp
+                                                                flags.cpp
+                                                                hash.cpp
+                                                                watcher.cpp)
+
+# TODO(Sangwan): Detach from core
+ADD_OSQUERY_LIBRARY(osquery_test_util test_util.cpp)
+
+FILE(GLOB OSQUERY_CORE_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_CORE_TESTS})
diff --git a/src/osquery/core/conversions.cpp b/src/osquery/core/conversions.cpp
new file mode 100644 (file)
index 0000000..d26c22a
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sstream>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/archive/iterators/binary_from_base64.hpp>
+#include <boost/archive/iterators/base64_from_binary.hpp>
+
+#include "osquery/core/conversions.h"
+
+namespace bai = boost::archive::iterators;
+
+namespace osquery {
+
+typedef bai::binary_from_base64<const char*> base64_str;
+typedef bai::transform_width<base64_str, 8, 6> base64_dec;
+typedef bai::transform_width<std::string::const_iterator, 6, 8> base64_enc;
+typedef bai::base64_from_binary<base64_enc> it_base64;
+
+std::string base64Decode(const std::string& encoded) {
+  std::string is;
+  std::stringstream os;
+
+  is = encoded;
+  boost::replace_all(is, "\r\n", "");
+  boost::replace_all(is, "\n", "");
+  uint32_t size = is.size();
+
+  // Remove the padding characters
+  if (size && is[size - 1] == '=') {
+    --size;
+    if (size && is[size - 1] == '=') {
+      --size;
+    }
+  }
+
+  if (size == 0) {
+    return "";
+  }
+
+  std::copy(base64_dec(is.data()),
+            base64_dec(is.data() + size),
+            std::ostream_iterator<char>(os));
+
+  return os.str();
+}
+
+std::string base64Encode(const std::string& unencoded) {
+  std::stringstream os;
+
+  if (unencoded.size() == 0) {
+    return std::string();
+  }
+
+  unsigned int writePaddChars = (3-unencoded.length()%3)%3;
+  std::string base64(it_base64(unencoded.begin()), it_base64(unencoded.end()));
+  base64.append(writePaddChars,'=');
+  os << base64;
+  return os.str();
+}
+}
diff --git a/src/osquery/core/conversions.h b/src/osquery/core/conversions.h
new file mode 100644 (file)
index 0000000..c8a0a1c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#ifdef DARWIN
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+namespace osquery {
+
+template <typename T>
+void do_release_boost(typename boost::shared_ptr<T> const&, T*) {}
+
+/**
+ * @brief Convert a boost::shared_ptr to a std::shared_ptr
+ */
+template <typename T>
+typename std::shared_ptr<T> boost_to_std_shared_ptr(
+    typename boost::shared_ptr<T> const& p) {
+  return std::shared_ptr<T>(p.get(), boost::bind(&do_release_boost<T>, p, _1));
+}
+
+template <typename T>
+void do_release_std(typename std::shared_ptr<T> const&, T*) {}
+
+/**
+ * @brief Convert a std::shared_ptr to a boost::shared_ptr
+ */
+template <typename T>
+typename boost::shared_ptr<T> std_to_boost_shared_ptr(
+    typename std::shared_ptr<T> const& p) {
+  return boost::shared_ptr<T>(p.get(), boost::bind(&do_release_std<T>, p, _1));
+}
+
+/**
+ * @brief Decode a base64 encoded string.
+ *
+ * @param encoded The encode base64 string.
+ * @return Decoded string.
+ */
+std::string base64Decode(const std::string& encoded);
+
+/**
+ * @brief Encode a  string.
+ *
+ * @param A string to encode.
+ * @return Encoded string.
+ */
+std::string base64Encode(const std::string& unencoded);
+
+#ifdef DARWIN
+/**
+ * @brief Convert a CFStringRef to a std::string.
+ */
+std::string stringFromCFString(const CFStringRef& cf_string);
+
+/**
+ * @brief Convert a CFNumberRef to a std::string.
+ */
+std::string stringFromCFNumber(const CFDataRef& cf_number);
+std::string stringFromCFData(const CFDataRef& cf_data);
+#endif
+
+}
diff --git a/src/osquery/core/flags.cpp b/src/osquery/core/flags.cpp
new file mode 100644 (file)
index 0000000..e1474a2
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/flags.h>
+
+namespace boost {
+template <>
+bool lexical_cast<bool, std::string>(const std::string& arg) {
+  std::istringstream ss(arg);
+  bool b;
+  ss >> std::boolalpha >> b;
+  return b;
+}
+
+template <>
+std::string lexical_cast<std::string, bool>(const bool& b) {
+  std::ostringstream ss;
+  ss << std::boolalpha << b;
+  return ss.str();
+}
+}
+
+namespace osquery {
+
+int Flag::create(const std::string& name, const FlagDetail& flag) {
+  instance().flags_.insert(std::make_pair(name, flag));
+  return 0;
+}
+
+int Flag::createAlias(const std::string& alias, const FlagDetail& flag) {
+  instance().aliases_.insert(std::make_pair(alias, flag));
+  return 0;
+}
+
+Status Flag::getDefaultValue(const std::string& name, std::string& value) {
+  GFLAGS_NAMESPACE::CommandLineFlagInfo info;
+  if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) {
+    return Status(1, "Flags name not found.");
+  }
+
+  value = info.default_value;
+  return Status(0, "OK");
+}
+
+bool Flag::isDefault(const std::string& name) {
+  GFLAGS_NAMESPACE::CommandLineFlagInfo info;
+  if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) {
+    return false;
+  }
+
+  return info.is_default;
+}
+
+std::string Flag::getValue(const std::string& name) {
+  std::string current_value;
+  GFLAGS_NAMESPACE::GetCommandLineOption(name.c_str(), &current_value);
+  return current_value;
+}
+
+std::string Flag::getType(const std::string& name) {
+  GFLAGS_NAMESPACE::CommandLineFlagInfo info;
+  if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) {
+    return "";
+  }
+  return info.type;
+}
+
+std::string Flag::getDescription(const std::string& name) {
+  if (instance().flags_.count(name)) {
+    return instance().flags_.at(name).description;
+  }
+
+  if (instance().aliases_.count(name)) {
+    return getDescription(instance().aliases_.at(name).description);
+  }
+  return "";
+}
+
+Status Flag::updateValue(const std::string& name, const std::string& value) {
+  if (instance().flags_.count(name) > 0) {
+    GFLAGS_NAMESPACE::SetCommandLineOption(name.c_str(), value.c_str());
+    return Status(0, "OK");
+  } else if (instance().aliases_.count(name) > 0) {
+    // Updating a flag by an alias name.
+    auto& real_name = instance().aliases_.at(name).description;
+    GFLAGS_NAMESPACE::SetCommandLineOption(real_name.c_str(), value.c_str());
+    return Status(0, "OK");
+  }
+  return Status(1, "Flag not found");
+}
+
+std::map<std::string, FlagInfo> Flag::flags() {
+  std::vector<GFLAGS_NAMESPACE::CommandLineFlagInfo> info;
+  GFLAGS_NAMESPACE::GetAllFlags(&info);
+
+  std::map<std::string, FlagInfo> flags;
+  for (const auto& flag : info) {
+    if (instance().flags_.count(flag.name) == 0) {
+      // This flag info was not defined within osquery.
+      continue;
+    }
+
+    // Set the flag info from the internal info kept by Gflags, except for
+    // the stored description. Gflag keeps an "unknown" value if the flag
+    // was declared without a definition.
+    flags[flag.name] = {flag.type,
+                        instance().flags_.at(flag.name).description,
+                        flag.default_value,
+                        flag.current_value,
+                        instance().flags_.at(flag.name)};
+  }
+  return flags;
+}
+
+void Flag::printFlags(bool shell, bool external, bool cli) {
+  std::vector<GFLAGS_NAMESPACE::CommandLineFlagInfo> info;
+  GFLAGS_NAMESPACE::GetAllFlags(&info);
+  auto& details = instance().flags_;
+
+  // Determine max indent needed for all flag names.
+  size_t max = 0;
+  for (const auto& flag : details) {
+    max = (max > flag.first.size()) ? max : flag.first.size();
+  }
+  // Additional index for flag values.
+  max += 6;
+
+  auto& aliases = instance().aliases_;
+  for (const auto& flag : info) {
+    if (details.count(flag.name) > 0) {
+      const auto& detail = details.at(flag.name);
+      if ((shell && !detail.shell) || (!shell && detail.shell) ||
+          (external && !detail.external) || (!external && detail.external) ||
+          (cli && !detail.cli) || (!cli && detail.cli) || detail.hidden) {
+        continue;
+      }
+    } else if (aliases.count(flag.name) > 0) {
+      const auto& alias = aliases.at(flag.name);
+      // Aliases are only printed if this is an external tool and the alias
+      // is external.
+      if (!alias.external || !external) {
+        continue;
+      }
+    } else {
+      // This flag was not defined as an osquery flag or flag alias.
+      continue;
+    }
+
+    fprintf(stdout, "    --%s", flag.name.c_str());
+
+    int pad = max;
+    if (flag.type != "bool") {
+      fprintf(stdout, " VALUE");
+      pad -= 6;
+    }
+    pad -= flag.name.size();
+
+    if (pad > 0 && pad < 80) {
+      // Never pad more than 80 characters.
+      fprintf(stdout, "%s", std::string(pad, ' ').c_str());
+    }
+    fprintf(stdout, "  %s\n", getDescription(flag.name).c_str());
+  }
+}
+}
diff --git a/src/osquery/core/hash.cpp b/src/osquery/core/hash.cpp
new file mode 100644 (file)
index 0000000..fc7d4a9
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <iomanip>
+#include <sstream>
+
+#include <osquery/filesystem.h>
+#include <osquery/hash.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+
+#ifdef __APPLE__
+  #import <CommonCrypto/CommonDigest.h>
+  #define __HASH_API(name) CC_##name
+#else
+  #include <openssl/sha.h>
+  #include <openssl/md5.h>
+  #define __HASH_API(name) name
+
+  #define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
+  #define SHA1_CTX SHA_CTX
+#endif
+
+#define HASH_CHUNK_SIZE 1024
+
+Hash::~Hash() {
+  if (ctx_ != nullptr) {
+    free(ctx_);
+  }
+}
+
+Hash::Hash(HashType algorithm) : algorithm_(algorithm) {
+  if (algorithm_ == HASH_TYPE_MD5) {
+    length_ = __HASH_API(MD5_DIGEST_LENGTH);
+    ctx_ = (__HASH_API(MD5_CTX)*)malloc(sizeof(__HASH_API(MD5_CTX)));
+    __HASH_API(MD5_Init)((__HASH_API(MD5_CTX)*)ctx_);
+  } else if (algorithm_ == HASH_TYPE_SHA1) {
+    length_ = __HASH_API(SHA1_DIGEST_LENGTH);
+    ctx_ = (__HASH_API(SHA1_CTX)*)malloc(sizeof(__HASH_API(SHA1_CTX)));
+    __HASH_API(SHA1_Init)((__HASH_API(SHA1_CTX)*)ctx_);
+  } else if (algorithm_ == HASH_TYPE_SHA256) {
+    length_ = __HASH_API(SHA256_DIGEST_LENGTH);
+    ctx_ = (__HASH_API(SHA256_CTX)*)malloc(sizeof(__HASH_API(SHA256_CTX)));
+    __HASH_API(SHA256_Init)((__HASH_API(SHA256_CTX)*)ctx_);
+  } else {
+    throw std::domain_error("Unknown hash function");
+  }
+}
+
+void Hash::update(const void* buffer, size_t size) {
+  if (algorithm_ == HASH_TYPE_MD5) {
+    __HASH_API(MD5_Update)((__HASH_API(MD5_CTX)*)ctx_, buffer, size);
+  } else if (algorithm_ == HASH_TYPE_SHA1) {
+    __HASH_API(SHA1_Update)((__HASH_API(SHA1_CTX)*)ctx_, buffer, size);
+  } else if (algorithm_ == HASH_TYPE_SHA256) {
+    __HASH_API(SHA256_Update)((__HASH_API(SHA256_CTX)*)ctx_, buffer, size);
+  }
+}
+
+std::string Hash::digest() {
+  unsigned char hash[length_];
+
+  if (algorithm_ == HASH_TYPE_MD5) {
+    __HASH_API(MD5_Final)(hash, (__HASH_API(MD5_CTX)*)ctx_);
+  } else if (algorithm_ == HASH_TYPE_SHA1) {
+    __HASH_API(SHA1_Final)(hash, (__HASH_API(SHA1_CTX)*)ctx_);
+  } else if (algorithm_ == HASH_TYPE_SHA256) {
+    __HASH_API(SHA256_Final)(hash, (__HASH_API(SHA256_CTX)*)ctx_);
+  }
+
+  // The hash value is only relevant as a hex digest.
+  std::stringstream digest;
+  for (int i = 0; i < length_; i++) {
+    digest << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
+  }
+
+  return digest.str();
+}
+
+std::string hashFromBuffer(HashType hash_type, const void* buffer, size_t size) {
+  Hash hash(hash_type);
+  hash.update(buffer, size);
+  return hash.digest();
+}
+
+std::string hashFromFile(HashType hash_type, const std::string& path) {
+  // Perform a dry-run of a file read without filling in any content.
+  auto status = readFile(path);
+  if (!status.ok()) {
+    return "";
+  }
+
+  Hash hash(hash_type);
+  // Use the canonicalized path returned from a successful readFile dry-run.
+  FILE* file = fopen(status.what().c_str(), "rb");
+  if (file == nullptr) {
+    VLOG(1) << "Cannot hash/open file " << path;
+    return "";
+  }
+
+  // Then call updates with read chunks.
+  size_t bytes_read = 0;
+  unsigned char buffer[HASH_CHUNK_SIZE];
+  while ((bytes_read = fread(buffer, 1, HASH_CHUNK_SIZE, file))) {
+    hash.update(buffer, bytes_read);
+  }
+
+  fclose(file);
+  return hash.digest();
+}
+}
diff --git a/src/osquery/core/init.cpp b/src/osquery/core/init.cpp
new file mode 100644 (file)
index 0000000..655ab2d
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <chrono>
+#include <random>
+
+#include <syslog.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <iostream>
+
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/filesystem.hpp>
+
+#include <osquery/config.h>
+#include <osquery/core.h>
+#include <osquery/events.h>
+#include <osquery/extensions.h>
+#include <osquery/flags.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/registry.h>
+
+#include "osquery/core/watcher.h"
+#include "osquery/database/db_handle.h"
+
+#ifdef __linux__
+#include <sys/resource.h>
+#include <sys/syscall.h>
+
+/*
+ * These are the io priority groups as implemented by CFQ. RT is the realtime
+ * class, it always gets premium service. BE is the best-effort scheduling
+ * class, the default for any process. IDLE is the idle scheduling class, it
+ * is only served when no one else is using the disk.
+ */
+enum {
+  IOPRIO_CLASS_NONE,
+  IOPRIO_CLASS_RT,
+  IOPRIO_CLASS_BE,
+  IOPRIO_CLASS_IDLE,
+};
+
+/*
+ * 8 best effort priority levels are supported
+ */
+#define IOPRIO_BE_NR  (8)
+
+enum {
+  IOPRIO_WHO_PROCESS = 1,
+  IOPRIO_WHO_PGRP,
+  IOPRIO_WHO_USER,
+};
+#endif
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+#define DESCRIPTION \
+  "osquery %s, your OS as a high-performance relational database\n"
+#define EPILOG "\nosquery project page <https://osquery.io>.\n"
+#define OPTIONS \
+  "\nosquery configuration options (set by config or CLI flags):\n\n"
+#define OPTIONS_SHELL "\nosquery shell-only CLI flags:\n\n"
+#define OPTIONS_CLI "osquery%s command line flags:\n\n"
+#define USAGE "Usage: %s [OPTION]... %s\n\n"
+#define CONFIG_ERROR                                                          \
+  "You are using default configurations for osqueryd for one or more of the " \
+  "following\n"                                                               \
+  "flags: pidfile, db_path.\n\n"                                              \
+  "These options create files in /var/osquery but it looks like that path "   \
+  "has not\n"                                                                 \
+  "been created. Please consider explicitly defining those "                  \
+  "options as a different \n"                                                 \
+  "path. Additionally, review the \"using osqueryd\" wiki page:\n"            \
+  " - https://osquery.readthedocs.org/en/latest/introduction/using-osqueryd/" \
+  "\n\n";
+
+typedef std::chrono::high_resolution_clock chrono_clock;
+
+CLI_FLAG(bool,
+         config_check,
+         false,
+         "Check the format of an osquery config and exit");
+
+#ifndef __APPLE__
+CLI_FLAG(bool, daemonize, false, "Run as daemon (osqueryd only)");
+#endif
+
+ToolType kToolType = OSQUERY_TOOL_UNKNOWN;
+
+void printUsage(const std::string& binary, int tool) {
+  // Parse help options before gflags. Only display osquery-related options.
+  fprintf(stdout, DESCRIPTION, kVersion.c_str());
+  if (tool == OSQUERY_TOOL_SHELL) {
+    // The shell allows a caller to run a single SQL statement and exit.
+    fprintf(stdout, USAGE, binary.c_str(), "[SQL STATEMENT]");
+  } else {
+    fprintf(stdout, USAGE, binary.c_str(), "");
+  }
+
+  if (tool == OSQUERY_EXTENSION) {
+    fprintf(stdout, OPTIONS_CLI, " extension");
+    Flag::printFlags(false, true);
+  } else {
+    fprintf(stdout, OPTIONS_CLI, "");
+    Flag::printFlags(false, false, true);
+    fprintf(stdout, OPTIONS);
+    Flag::printFlags();
+  }
+
+  if (tool == OSQUERY_TOOL_SHELL) {
+    // Print shell flags.
+    fprintf(stdout, OPTIONS_SHELL);
+    Flag::printFlags(true);
+  }
+
+  fprintf(stdout, EPILOG);
+}
+
+Initializer::Initializer(int& argc, char**& argv, ToolType tool)
+    : argc_(&argc),
+      argv_(&argv),
+      tool_(tool),
+      binary_(fs::path(std::string(argv[0])).filename().string()) {
+  std::srand(chrono_clock::now().time_since_epoch().count());
+
+  // osquery implements a custom help/usage output.
+  for (int i = 1; i < *argc_; i++) {
+    auto help = std::string((*argv_)[i]);
+    if ((help == "--help" || help == "-help" || help == "--h" ||
+         help == "-h") &&
+        tool != OSQUERY_TOOL_TEST) {
+      printUsage(binary_, tool_);
+      ::exit(0);
+    }
+  }
+
+// To change the default config plugin, compile osquery with
+// -DOSQUERY_DEFAULT_CONFIG_PLUGIN=<new_default_plugin>
+#ifdef OSQUERY_DEFAULT_CONFIG_PLUGIN
+  FLAGS_config_plugin = STR(OSQUERY_DEFAULT_CONFIG_PLUGIN);
+#endif
+
+// To change the default logger plugin, compile osquery with
+// -DOSQUERY_DEFAULT_LOGGER_PLUGIN=<new_default_plugin>
+#ifdef OSQUERY_DEFAULT_LOGGER_PLUGIN
+  FLAGS_logger_plugin = STR(OSQUERY_DEFAULT_LOGGER_PLUGIN);
+#endif
+
+  // Set version string from CMake build
+  GFLAGS_NAMESPACE::SetVersionString(kVersion.c_str());
+
+  // Let gflags parse the non-help options/flags.
+  GFLAGS_NAMESPACE::ParseCommandLineFlags(
+      argc_, argv_, (tool == OSQUERY_TOOL_SHELL));
+
+  // Set the tool type to allow runtime decisions based on daemon, shell, etc.
+  kToolType = tool;
+  if (tool == OSQUERY_TOOL_SHELL) {
+    // The shell is transient, rewrite config-loaded paths.
+    FLAGS_disable_logging = true;
+    // Get the caller's home dir for temporary storage/state management.
+    auto homedir = osqueryHomeDirectory();
+    if (osquery::pathExists(homedir).ok() ||
+        boost::filesystem::create_directory(homedir)) {
+      // Only apply user/shell-specific paths if not overridden by CLI flag.
+      if (Flag::isDefault("database_path")) {
+        osquery::FLAGS_database_path = homedir + "/shell.db";
+      }
+      if (Flag::isDefault("extensions_socket")) {
+        osquery::FLAGS_extensions_socket = homedir + "/shell.em";
+      }
+    }
+  }
+
+  // If the caller is checking configuration, disable the watchdog/worker.
+  if (FLAGS_config_check) {
+    FLAGS_disable_watchdog = true;
+  }
+
+  // Initialize the status and results logger.
+  initStatusLogger(binary_);
+  if (tool != OSQUERY_EXTENSION) {
+    if (isWorker()) {
+      VLOG(1) << "osquery worker initialized [watcher="
+              << getenv("OSQUERY_WORKER") << "]";
+    } else {
+      VLOG(1) << "osquery initialized [version=" << kVersion << "]";
+    }
+  } else {
+    VLOG(1) << "osquery extension initialized [sdk=" << kSDKVersion << "]";
+  }
+}
+
+void Initializer::initDaemon() {
+  if (FLAGS_config_check) {
+    // No need to daemonize, emit log lines, or create process mutexes.
+    return;
+  }
+
+#ifndef __APPLE__
+  // OS X uses launchd to daemonize.
+  if (osquery::FLAGS_daemonize) {
+    if (daemon(0, 0) == -1) {
+      ::exit(EXIT_FAILURE);
+    }
+  }
+#endif
+
+  // Print the version to SYSLOG.
+  syslog(
+      LOG_NOTICE, "%s started [version=%s]", binary_.c_str(), kVersion.c_str());
+
+  // Check if /var/osquery exists
+  if ((Flag::isDefault("pidfile") || Flag::isDefault("database_path")) &&
+      !isDirectory("/var/osquery")) {
+    std::cerr << CONFIG_ERROR
+  }
+
+  // Create a process mutex around the daemon.
+  auto pid_status = createPidFile();
+  if (!pid_status.ok()) {
+    LOG(ERROR) << binary_ << " initialize failed: " << pid_status.toString();
+    ::exit(EXIT_FAILURE);
+  }
+
+  // Nice ourselves if using a watchdog and the level is not too permissive.
+  if (!FLAGS_disable_watchdog &&
+      FLAGS_watchdog_level >= WATCHDOG_LEVEL_DEFAULT &&
+      FLAGS_watchdog_level != WATCHDOG_LEVEL_DEBUG) {
+    // Set CPU scheduling I/O limits.
+    setpriority(PRIO_PGRP, 0, 10);
+#ifdef __linux__
+    // Using: ioprio_set(IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
+    syscall(SYS_ioprio_set, IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE);
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+    setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
+#endif
+  }
+}
+
+void Initializer::initWatcher() {
+  // The watcher takes a list of paths to autoload extensions from.
+  osquery::loadExtensions();
+
+  // Add a watcher service thread to start/watch an optional worker and set
+  // of optional extensions in the autoload paths.
+  if (Watcher::hasManagedExtensions() || !FLAGS_disable_watchdog) {
+    Dispatcher::addService(std::make_shared<WatcherRunner>(
+        *argc_, *argv_, !FLAGS_disable_watchdog));
+  }
+
+  // If there are no autoloaded extensions, the watcher service will end,
+  // otherwise it will continue as a background thread and respawn them.
+  // If the watcher is also a worker watchdog it will do nothing but monitor
+  // the extensions and worker process.
+  if (!FLAGS_disable_watchdog) {
+    Dispatcher::joinServices();
+    // Execution should never reach this point.
+    ::exit(EXIT_FAILURE);
+  }
+}
+
+void Initializer::initWorker(const std::string& name) {
+  // Clear worker's arguments.
+  size_t name_size = strlen((*argv_)[0]);
+  auto original_name = std::string((*argv_)[0]);
+  for (int i = 0; i < *argc_; i++) {
+    if ((*argv_)[i] != nullptr) {
+      memset((*argv_)[i], ' ', strlen((*argv_)[i]));
+    }
+  }
+
+  // Set the worker's process name.
+  if (name.size() < name_size) {
+    std::copy(name.begin(), name.end(), (*argv_)[0]);
+    (*argv_)[0][name.size()] = '\0';
+  } else {
+    std::copy(original_name.begin(), original_name.end(), (*argv_)[0]);
+    (*argv_)[0][original_name.size()] = '\0';
+  }
+
+  // Start a watcher watcher thread to exit the process if the watcher exits.
+  Dispatcher::addService(std::make_shared<WatcherWatcherRunner>(getppid()));
+}
+
+void Initializer::initWorkerWatcher(const std::string& name) {
+  if (isWorker()) {
+    initWorker(name);
+  } else {
+    // The watcher will forever monitor and spawn additional workers.
+    initWatcher();
+  }
+}
+
+bool Initializer::isWorker() { return (getenv("OSQUERY_WORKER") != nullptr); }
+
+void Initializer::initActivePlugin(const std::string& type,
+                                   const std::string& name) {
+  // Use a delay, meaning the amount of milliseconds waited for extensions.
+  size_t delay = 0;
+  // The timeout is the maximum microseconds in seconds to wait for extensions.
+  size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000;
+  if (timeout < kExtensionInitializeLatencyUS * 10) {
+    timeout = kExtensionInitializeLatencyUS * 10;
+  }
+  while (!Registry::setActive(type, name)) {
+    if (!Watcher::hasManagedExtensions() || delay > timeout) {
+      LOG(ERROR) << "Active " << type << " plugin not found: " << name;
+      ::exit(EXIT_CATASTROPHIC);
+    }
+    delay += kExtensionInitializeLatencyUS;
+    ::usleep(kExtensionInitializeLatencyUS);
+  }
+}
+
+void Initializer::start() {
+  // Load registry/extension modules before extensions.
+  osquery::loadModules();
+
+  // Pre-extension manager initialization options checking.
+  if (FLAGS_config_check && !Watcher::hasManagedExtensions()) {
+    FLAGS_disable_extensions = true;
+  }
+
+  // Check the backing store by allocating and exiting on error.
+  if (!DBHandle::checkDB()) {
+    LOG(ERROR) << binary_ << " initialize failed: Could not open RocksDB";
+    if (isWorker()) {
+      ::exit(EXIT_CATASTROPHIC);
+    } else {
+      ::exit(EXIT_FAILURE);
+    }
+  }
+
+  // Bind to an extensions socket and wait for registry additions.
+  osquery::startExtensionManager();
+
+  // Then set the config plugin, which uses a single/active plugin.
+  initActivePlugin("config", FLAGS_config_plugin);
+
+  // Run the setup for all lazy registries (tables, SQL).
+  Registry::setUp();
+
+  if (FLAGS_config_check) {
+    // The initiator requested an initialization and config check.
+    auto s = Config::checkConfig();
+    if (!s.ok()) {
+      std::cerr << "Error reading config: " << s.toString() << "\n";
+    }
+    // A configuration check exits the application.
+    ::exit(s.getCode());
+  }
+
+  // Load the osquery config using the default/active config plugin.
+  Config::load();
+
+  // Initialize the status and result plugin logger.
+  initActivePlugin("logger", FLAGS_logger_plugin);
+  initLogger(binary_);
+
+  // Start event threads.
+  osquery::attachEvents();
+  EventFactory::delay();
+}
+
+void Initializer::shutdown() {
+  // End any event type run loops.
+  EventFactory::end();
+
+  // Hopefully release memory used by global string constructors in gflags.
+  GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
+}
+}
diff --git a/src/osquery/core/system.cpp b/src/osquery/core/system.cpp
new file mode 100644 (file)
index 0000000..73da087
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <ctime>
+#include <sstream>
+
+#include <sys/types.h>
+#include <signal.h>
+
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_generators.hpp>
+#include <boost/uuid/uuid_io.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+/// The path to the pidfile for osqueryd
+CLI_FLAG(string,
+         pidfile,
+         "/var/osquery/osqueryd.pidfile",
+         "Path to the daemon pidfile mutex");
+
+/// Should the daemon force unload previously-running osqueryd daemons.
+CLI_FLAG(bool,
+         force,
+         false,
+         "Force osqueryd to kill previously-running daemons");
+
+std::string getHostname() {
+  char hostname[256] = {0}; // Linux max should be 64.
+  gethostname(hostname, sizeof(hostname) - 1);
+  std::string hostname_string = std::string(hostname);
+  boost::algorithm::trim(hostname_string);
+  return hostname_string;
+}
+
+std::string generateNewUuid() {
+  boost::uuids::uuid uuid = boost::uuids::random_generator()();
+  return boost::uuids::to_string(uuid);
+}
+
+std::string generateHostUuid() {
+#ifdef __APPLE__
+  // Use the hardware uuid available on OSX to identify this machine
+  uuid_t id;
+  // wait at most 5 seconds for gethostuuid to return
+  const timespec wait = {5, 0};
+  int result = gethostuuid(id, &wait);
+  if (result == 0) {
+    char out[128];
+    uuid_unparse(id, out);
+    std::string uuid_string = std::string(out);
+    boost::algorithm::trim(uuid_string);
+    return uuid_string;
+  } else {
+    // unable to get the hardware uuid, just return a new uuid
+    return generateNewUuid();
+  }
+#else
+  return generateNewUuid();
+#endif
+}
+
+std::string getAsciiTime() {
+  auto result = std::time(nullptr);
+  auto time_str = std::string(std::asctime(std::gmtime(&result)));
+  boost::algorithm::trim(time_str);
+  return time_str + " UTC";
+}
+
+int getUnixTime() {
+  auto result = std::time(nullptr);
+  return result;
+}
+
+Status checkStalePid(const std::string& content) {
+  int pid;
+  try {
+    pid = boost::lexical_cast<int>(content);
+  } catch (const boost::bad_lexical_cast& e) {
+    if (FLAGS_force) {
+      return Status(0, "Force loading and not parsing pidfile");
+    } else {
+      return Status(1, "Could not parse pidfile");
+    }
+  }
+
+  int status = kill(pid, 0);
+  if (status != ESRCH) {
+    // The pid is running, check if it is an osqueryd process by name.
+    std::stringstream query_text;
+    query_text << "SELECT name FROM processes WHERE pid = " << pid
+               << " AND name = 'osqueryd';";
+    auto q = SQL(query_text.str());
+    if (!q.ok()) {
+      return Status(1, "Error querying processes: " + q.getMessageString());
+    }
+
+    if (q.rows().size() > 0) {
+      // If the process really is osqueryd, return an "error" status.
+      if (FLAGS_force) {
+        // The caller may choose to abort the existing daemon with --force.
+        status = kill(pid, SIGQUIT);
+        ::sleep(1);
+
+        return Status(status, "Tried to force remove the existing osqueryd");
+      }
+
+      return Status(1, "osqueryd (" + content + ") is already running");
+    } else {
+      LOG(INFO) << "Found stale process for osqueryd (" << content
+                << ") removing pidfile";
+    }
+  }
+
+  return Status(0, "OK");
+}
+
+Status createPidFile() {
+  // check if pidfile exists
+  auto exists = pathExists(FLAGS_pidfile);
+  if (exists.ok()) {
+    // if it exists, check if that pid is running.
+    std::string content;
+    auto read_status = readFile(FLAGS_pidfile, content);
+    if (!read_status.ok()) {
+      return Status(1, "Could not read pidfile: " + read_status.toString());
+    }
+
+    auto stale_status = checkStalePid(content);
+    if (!stale_status.ok()) {
+      return stale_status;
+    }
+  }
+
+  // Now the pidfile is either the wrong pid or the pid is not running.
+  try {
+    boost::filesystem::remove(FLAGS_pidfile);
+  } catch (boost::filesystem::filesystem_error& e) {
+    // Unable to remove old pidfile.
+    LOG(WARNING) << "Unable to remove the osqueryd pidfile";
+  }
+
+  // If no pidfile exists or the existing pid was stale, write, log, and run.
+  auto pid = boost::lexical_cast<std::string>(getpid());
+  LOG(INFO) << "Writing osqueryd pid (" << pid << ") to " << FLAGS_pidfile;
+  auto status = writeTextFile(FLAGS_pidfile, pid, 0644);
+  return status;
+}
+}
diff --git a/src/osquery/core/tables.cpp b/src/osquery/core/tables.cpp
new file mode 100644 (file)
index 0000000..d71fd22
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+Status TablePlugin::addExternal(const std::string& name,
+                                const PluginResponse& response) {
+  // Attach the table.
+  if (response.size() == 0) {
+    // Invalid table route info.
+    return Status(1, "Invalid route info");
+  }
+
+  // Use the SQL registry to attach the name/definition.
+  return Registry::call("sql", "sql", {{"action", "attach"}, {"table", name}});
+}
+
+void TablePlugin::removeExternal(const std::string& name) {
+  // Detach the table name.
+  Registry::call("sql", "sql", {{"action", "detach"}, {"table", name}});
+}
+
+void TablePlugin::setRequestFromContext(const QueryContext& context,
+                                        PluginRequest& request) {
+  pt::ptree tree;
+  tree.put("limit", context.limit);
+
+  // The QueryContext contains a constraint map from column to type information
+  // and the list of operand/expression constraints applied to that column from
+  // the query given.
+  pt::ptree constraints;
+  for (const auto& constraint : context.constraints) {
+    pt::ptree child;
+    child.put("name", constraint.first);
+    constraint.second.serialize(child);
+    constraints.push_back(std::make_pair("", child));
+  }
+  tree.add_child("constraints", constraints);
+
+  // Write the property tree as a JSON string into the PluginRequest.
+  std::ostringstream output;
+  try {
+    pt::write_json(output, tree, false);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    // The content could not be represented as JSON.
+  }
+  request["context"] = output.str();
+}
+
+void TablePlugin::setResponseFromQueryData(const QueryData& data,
+                                           PluginResponse& response) {
+  response = std::move(data);
+}
+
+void TablePlugin::setContextFromRequest(const PluginRequest& request,
+                                        QueryContext& context) {
+  if (request.count("context") == 0) {
+    return;
+  }
+
+  // Read serialized context from PluginRequest.
+  pt::ptree tree;
+  try {
+    std::stringstream input;
+    input << request.at("context");
+    pt::read_json(input, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return;
+  }
+
+  // Set the context limit and deserialize each column constraint list.
+  context.limit = tree.get<int>("limit");
+  for (const auto& constraint : tree.get_child("constraints")) {
+    auto column_name = constraint.second.get<std::string>("name");
+    context.constraints[column_name].unserialize(constraint.second);
+  }
+}
+
+Status TablePlugin::call(const PluginRequest& request,
+                         PluginResponse& response) {
+  response.clear();
+  // TablePlugin API calling requires an action.
+  if (request.count("action") == 0) {
+    return Status(1, "Table plugins must include a request action");
+  }
+
+  if (request.at("action") == "generate") {
+    // "generate" runs the table implementation using a PluginRequest with
+    // optional serialized QueryContext and returns the QueryData results as
+    // the PluginRequest data.
+    QueryContext context;
+    if (request.count("context") > 0) {
+      setContextFromRequest(request, context);
+    }
+    setResponseFromQueryData(generate(context), response);
+  } else if (request.at("action") == "columns") {
+    // "columns" returns a PluginRequest filled with column information
+    // such as name and type.
+    const auto& column_list = columns();
+    for (const auto& column : column_list) {
+      response.push_back({{"name", column.first}, {"type", column.second}});
+    }
+  } else if (request.at("action") == "definition") {
+    response.push_back({{"definition", columnDefinition()}});
+  } else if (request.at("action") == "update") {
+    Row row = request;
+    row.erase("action");
+    return update(row);
+  } else {
+    return Status(1, "Unknown table plugin action: " + request.at("action"));
+  }
+
+  return Status(0, "OK");
+}
+
+std::string TablePlugin::columnDefinition() const {
+  return osquery::columnDefinition(columns());
+}
+
+PluginResponse TablePlugin::routeInfo() const {
+  // Route info consists of only the serialized column information.
+  PluginResponse response;
+  for (const auto& column : columns()) {
+    response.push_back({{"name", column.first}, {"type", column.second}});
+  }
+  return response;
+}
+
+std::string columnDefinition(const TableColumns& columns) {
+  std::string statement = "(";
+  for (size_t i = 0; i < columns.size(); ++i) {
+    statement += columns.at(i).first + " " + columns.at(i).second;
+    if (i < columns.size() - 1) {
+      statement += ", ";
+    }
+  }
+  return statement += ")";
+}
+
+std::string columnDefinition(const PluginResponse& response) {
+  TableColumns columns;
+  for (const auto& column : response) {
+    columns.push_back(make_pair(column.at("name"), column.at("type")));
+  }
+  return columnDefinition(columns);
+}
+
+bool ConstraintList::matches(const std::string& expr) const {
+  // Support each SQL affinity type casting.
+  if (affinity == "TEXT") {
+    return literal_matches<TEXT_LITERAL>(expr);
+  } else if (affinity == "INTEGER") {
+    INTEGER_LITERAL lexpr = AS_LITERAL(INTEGER_LITERAL, expr);
+    return literal_matches<INTEGER_LITERAL>(lexpr);
+  } else if (affinity == "BIGINT") {
+    BIGINT_LITERAL lexpr = AS_LITERAL(BIGINT_LITERAL, expr);
+    return literal_matches<BIGINT_LITERAL>(lexpr);
+  } else if (affinity == "UNSIGNED_BIGINT") {
+    UNSIGNED_BIGINT_LITERAL lexpr = AS_LITERAL(UNSIGNED_BIGINT_LITERAL, expr);
+    return literal_matches<UNSIGNED_BIGINT_LITERAL>(lexpr);
+  } else {
+    // Unsupported affinity type.
+    return false;
+  }
+}
+
+template <typename T>
+bool ConstraintList::literal_matches(const T& base_expr) const {
+  bool aggregate = true;
+  for (size_t i = 0; i < constraints_.size(); ++i) {
+    T constraint_expr = AS_LITERAL(T, constraints_[i].expr);
+    if (constraints_[i].op == EQUALS) {
+      aggregate = aggregate && (base_expr == constraint_expr);
+    } else if (constraints_[i].op == GREATER_THAN) {
+      aggregate = aggregate && (base_expr > constraint_expr);
+    } else if (constraints_[i].op == LESS_THAN) {
+      aggregate = aggregate && (base_expr < constraint_expr);
+    } else if (constraints_[i].op == GREATER_THAN_OR_EQUALS) {
+      aggregate = aggregate && (base_expr >= constraint_expr);
+    } else if (constraints_[i].op == LESS_THAN_OR_EQUALS) {
+      aggregate = aggregate && (base_expr <= constraint_expr);
+    } else {
+      // Unsupported constraint.
+      return false;
+    }
+    if (!aggregate) {
+      // Speed up comparison.
+      return false;
+    }
+  }
+  return true;
+}
+
+std::set<std::string> ConstraintList::getAll(ConstraintOperator op) const {
+  std::set<std::string> set;
+  for (size_t i = 0; i < constraints_.size(); ++i) {
+    if (constraints_[i].op == op) {
+      // TODO: this does not apply a distinct.
+      set.insert(constraints_[i].expr);
+    }
+  }
+  return set;
+}
+
+void ConstraintList::serialize(boost::property_tree::ptree& tree) const {
+  boost::property_tree::ptree expressions;
+  for (const auto& constraint : constraints_) {
+    boost::property_tree::ptree child;
+    child.put("op", constraint.op);
+    child.put("expr", constraint.expr);
+    expressions.push_back(std::make_pair("", child));
+  }
+  tree.add_child("list", expressions);
+  tree.put("affinity", affinity);
+}
+
+void ConstraintList::unserialize(const boost::property_tree::ptree& tree) {
+  // Iterate through the list of operand/expressions, then set the constraint
+  // type affinity.
+  for (const auto& list : tree.get_child("list")) {
+    Constraint constraint(list.second.get<unsigned char>("op"));
+    constraint.expr = list.second.get<std::string>("expr");
+    constraints_.push_back(constraint);
+  }
+  affinity = tree.get<std::string>("affinity");
+}
+}
diff --git a/src/osquery/core/test_util.cpp b/src/osquery/core/test_util.cpp
new file mode 100644 (file)
index 0000000..74f9389
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <deque>
+#include <sstream>
+
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+#include "osquery/core/test_util.h"
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+/// Most tests will use binary or disk-backed content for parsing tests.
+std::string kTestDataPath = "../../../tools/tests/";
+
+QueryData getTestDBExpectedResults() {
+  QueryData d;
+  Row row1;
+  row1["username"] = "mike";
+  row1["age"] = "23";
+  d.push_back(row1);
+  Row row2;
+  row2["username"] = "matt";
+  row2["age"] = "24";
+  d.push_back(row2);
+  return d;
+}
+
+std::vector<std::pair<std::string, QueryData> > getTestDBResultStream() {
+  std::vector<std::pair<std::string, QueryData> > results;
+
+  std::string q2 =
+      "INSERT INTO test_table (username, age) VALUES (\"joe\", 25)";
+  QueryData d2;
+  Row row2_1;
+  row2_1["username"] = "mike";
+  row2_1["age"] = "23";
+  d2.push_back(row2_1);
+  Row row2_2;
+  row2_2["username"] = "matt";
+  row2_2["age"] = "24";
+  d2.push_back(row2_2);
+  Row row2_3;
+  row2_3["username"] = "joe";
+  row2_3["age"] = "25";
+  d2.push_back(row2_3);
+  results.push_back(std::make_pair(q2, d2));
+
+  std::string q3 = "UPDATE test_table SET age = 27 WHERE username = \"matt\"";
+  QueryData d3;
+  Row row3_1;
+  row3_1["username"] = "mike";
+  row3_1["age"] = "23";
+  d3.push_back(row3_1);
+  Row row3_2;
+  row3_2["username"] = "matt";
+  row3_2["age"] = "27";
+  d3.push_back(row3_2);
+  Row row3_3;
+  row3_3["username"] = "joe";
+  row3_3["age"] = "25";
+  d3.push_back(row3_3);
+  results.push_back(std::make_pair(q3, d3));
+
+  std::string q4 =
+      "DELETE FROM test_table WHERE username = \"matt\" AND age = 27";
+  QueryData d4;
+  Row row4_1;
+  row4_1["username"] = "mike";
+  row4_1["age"] = "23";
+  d4.push_back(row4_1);
+  Row row4_2;
+  row4_2["username"] = "joe";
+  row4_2["age"] = "25";
+  d4.push_back(row4_2);
+  results.push_back(std::make_pair(q4, d4));
+
+  return results;
+}
+
+ScheduledQuery getOsqueryScheduledQuery() {
+  ScheduledQuery sq;
+  sq.query = "SELECT filename FROM fs WHERE path = '/bin' ORDER BY filename";
+  sq.interval = 5;
+  return sq;
+}
+
+std::pair<pt::ptree, Row> getSerializedRow() {
+  Row r;
+  r["foo"] = "bar";
+  r["meaning_of_life"] = "42";
+  pt::ptree arr;
+  arr.put<std::string>("foo", "bar");
+  arr.put<std::string>("meaning_of_life", "42");
+  return std::make_pair(arr, r);
+}
+
+std::pair<pt::ptree, QueryData> getSerializedQueryData() {
+  auto r = getSerializedRow();
+  QueryData q = {r.second, r.second};
+  pt::ptree arr;
+  arr.push_back(std::make_pair("", r.first));
+  arr.push_back(std::make_pair("", r.first));
+  return std::make_pair(arr, q);
+}
+
+std::pair<pt::ptree, DiffResults> getSerializedDiffResults() {
+  auto qd = getSerializedQueryData();
+  DiffResults diff_results;
+  diff_results.added = qd.second;
+  diff_results.removed = qd.second;
+
+  pt::ptree root;
+  root.add_child("added", qd.first);
+  root.add_child("removed", qd.first);
+
+  return std::make_pair(root, diff_results);
+}
+
+std::pair<std::string, DiffResults> getSerializedDiffResultsJSON() {
+  auto results = getSerializedDiffResults();
+  std::ostringstream ss;
+  pt::write_json(ss, results.first, false);
+  return std::make_pair(ss.str(), results.second);
+}
+
+std::pair<std::string, QueryData> getSerializedQueryDataJSON() {
+  auto results = getSerializedQueryData();
+  std::ostringstream ss;
+  pt::write_json(ss, results.first, false);
+  return std::make_pair(ss.str(), results.second);
+}
+
+std::pair<pt::ptree, QueryLogItem> getSerializedQueryLogItem() {
+  QueryLogItem i;
+  pt::ptree root;
+  auto dr = getSerializedDiffResults();
+  i.results = dr.second;
+  i.name = "foobar";
+  i.calendar_time = "Mon Aug 25 12:10:57 2014";
+  i.time = 1408993857;
+  i.identifier = "foobaz";
+  root.add_child("diffResults", dr.first);
+  root.put<std::string>("name", "foobar");
+  root.put<std::string>("hostIdentifier", "foobaz");
+  root.put<std::string>("calendarTime", "Mon Aug 25 12:10:57 2014");
+  root.put<int>("unixTime", 1408993857);
+  return std::make_pair(root, i);
+}
+
+std::pair<std::string, QueryLogItem> getSerializedQueryLogItemJSON() {
+  auto results = getSerializedQueryLogItem();
+
+  std::ostringstream ss;
+  pt::write_json(ss, results.first, false);
+
+  return std::make_pair(ss.str(), results.second);
+}
+
+std::vector<SplitStringTestData> generateSplitStringTestData() {
+  SplitStringTestData s1;
+  s1.test_string = "a b\tc";
+  s1.test_vector = {"a", "b", "c"};
+
+  SplitStringTestData s2;
+  s2.test_string = " a b   c";
+  s2.test_vector = {"a", "b", "c"};
+
+  SplitStringTestData s3;
+  s3.test_string = "  a     b   c";
+  s3.test_vector = {"a", "b", "c"};
+
+  return {s1, s2, s3};
+}
+
+std::string getCACertificateContent() {
+  std::string content;
+  readFile(kTestDataPath + "test_cert.pem", content);
+  return content;
+}
+
+std::string getEtcHostsContent() {
+  std::string content;
+  readFile(kTestDataPath + "test_hosts.txt", content);
+  return content;
+}
+
+std::string getEtcProtocolsContent() {
+  std::string content;
+  readFile(kTestDataPath + "test_protocols.txt", content);
+  return content;
+}
+
+QueryData getEtcHostsExpectedResults() {
+  Row row1;
+  Row row2;
+  Row row3;
+  Row row4;
+  Row row5;
+  Row row6;
+
+  row1["address"] = "127.0.0.1";
+  row1["hostnames"] = "localhost";
+  row2["address"] = "255.255.255.255";
+  row2["hostnames"] = "broadcasthost";
+  row3["address"] = "::1";
+  row3["hostnames"] = "localhost";
+  row4["address"] = "fe80::1%lo0";
+  row4["hostnames"] = "localhost";
+  row5["address"] = "127.0.0.1";
+  row5["hostnames"] = "example.com example";
+  row6["address"] = "127.0.0.1";
+  row6["hostnames"] = "example.net";
+  return {row1, row2, row3, row4, row5, row6};
+}
+
+::std::ostream& operator<<(::std::ostream& os, const Status& s) {
+  return os << "Status(" << s.getCode() << ", \"" << s.getMessage() << "\")";
+}
+
+QueryData getEtcProtocolsExpectedResults() {
+  Row row1;
+  Row row2;
+  Row row3;
+
+  row1["name"] = "ip";
+  row1["number"] = "0";
+  row1["alias"] = "IP";
+  row1["comment"] = "internet protocol, pseudo protocol number";
+  row2["name"] = "icmp";
+  row2["number"] = "1";
+  row2["alias"] = "ICMP";
+  row2["comment"] = "internet control message protocol";
+  row3["name"] = "tcp";
+  row3["number"] = "6";
+  row3["alias"] = "TCP";
+  row3["comment"] = "transmission control protocol";
+
+  return {row1, row2, row3};
+}
+
+void createMockFileStructure() {
+  fs::create_directories(kFakeDirectory + "/deep11/deep2/deep3/");
+  fs::create_directories(kFakeDirectory + "/deep1/deep2/");
+  writeTextFile(kFakeDirectory + "/root.txt", "root");
+  writeTextFile(kFakeDirectory + "/door.txt", "toor");
+  writeTextFile(kFakeDirectory + "/roto.txt", "roto");
+  writeTextFile(kFakeDirectory + "/deep1/level1.txt", "l1");
+  writeTextFile(kFakeDirectory + "/deep11/not_bash", "l1");
+  writeTextFile(kFakeDirectory + "/deep1/deep2/level2.txt", "l2");
+
+  writeTextFile(kFakeDirectory + "/deep11/level1.txt", "l1");
+  writeTextFile(kFakeDirectory + "/deep11/deep2/level2.txt", "l2");
+  writeTextFile(kFakeDirectory + "/deep11/deep2/deep3/level3.txt", "l3");
+
+  boost::system::error_code ec;
+  fs::create_symlink(
+      kFakeDirectory + "/root.txt", kFakeDirectory + "/root2.txt", ec);
+}
+
+void tearDownMockFileStructure() {
+  boost::filesystem::remove_all(kFakeDirectory);
+}
+}
diff --git a/src/osquery/core/test_util.h b/src/osquery/core/test_util.h
new file mode 100644 (file)
index 0000000..40a97a7
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/config.h>
+#include <osquery/core.h>
+#include <osquery/database.h>
+#include <osquery/filesystem.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+/// Any SQL-dependent tests should use kTestQuery for a pre-populated example.
+const std::string kTestQuery = "SELECT * FROM test_table";
+
+extern std::string kTestDataPath;
+
+/// Tests should limit intermediate input/output to a working directory.
+/// Config data, logging results, and intermediate database/caching usage.
+const std::string kTestWorkingDirectory = "/tmp/osquery-tests/";
+
+/// A fake directory tree should be used for filesystem iterator testing.
+const std::string kFakeDirectory = kTestWorkingDirectory + "fstree";
+
+ScheduledQuery getOsqueryScheduledQuery();
+
+// getTestDBExpectedResults returns the results of kTestQuery of the table that
+// initially gets returned from createTestDB()
+QueryData getTestDBExpectedResults();
+
+// Starting with the dataset returned by createTestDB(), getTestDBResultStream
+// returns a vector of std::pair's where pair.first is the query that would
+// need to be performed on the dataset to make the results be pair.second
+std::vector<std::pair<std::string, QueryData> > getTestDBResultStream();
+
+// getSerializedRow() return an std::pair where pair->first is a string which
+// should serialize to pair->second. pair->second should deserialize
+// to pair->first
+std::pair<pt::ptree, Row> getSerializedRow();
+
+// getSerializedQueryData() return an std::pair where pair->first is a string
+// which should serialize to pair->second. pair->second should
+// deserialize to pair->first
+std::pair<pt::ptree, QueryData> getSerializedQueryData();
+std::pair<std::string, QueryData> getSerializedQueryDataJSON();
+
+// getSerializedDiffResults() return an std::pair where pair->first is a string
+// which should serialize to pair->second. pair->second should
+// deserialize to pair->first
+std::pair<pt::ptree, DiffResults> getSerializedDiffResults();
+std::pair<std::string, DiffResults> getSerializedDiffResultsJSON();
+
+// getSerializedQueryLogItem() return an std::pair where pair->first
+// is a string which should serialize to pair->second. pair->second
+// should deserialize to pair->first
+std::pair<pt::ptree, QueryLogItem> getSerializedQueryLogItem();
+std::pair<std::string, QueryLogItem> getSerializedQueryLogItemJSON();
+
+// generate content for a PEM-encoded certificate
+std::string getCACertificateContent();
+
+// generate the content that would be found in an /etc/hosts file
+std::string getEtcHostsContent();
+
+// generate the content that would be found in an /etc/protocols file
+std::string getEtcProtocolsContent();
+
+// generate the expected data that getEtcHostsContent() should parse into
+QueryData getEtcHostsExpectedResults();
+
+// generate the expected data that getEtcProtocolsContent() should parse into
+QueryData getEtcProtocolsExpectedResults();
+
+// the three items that you need to test osquery::splitString
+struct SplitStringTestData {
+  std::string test_string;
+  std::string delim;
+  std::vector<std::string> test_vector;
+};
+
+// generate a set of test data to test osquery::splitString
+std::vector<SplitStringTestData> generateSplitStringTestData();
+
+// generate a small directory structure for testing
+void createMockFileStructure();
+// remove the small directory structure used for testing
+void tearDownMockFileStructure();
+}
diff --git a/src/osquery/core/tests/conversions_tests.cpp b/src/osquery/core/tests/conversions_tests.cpp
new file mode 100644 (file)
index 0000000..34be661
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/make_shared.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+#include "osquery/core/conversions.h"
+
+namespace osquery {
+
+class ConversionsTests : public testing::Test {};
+
+class Foobar {};
+
+TEST_F(ConversionsTests, test_conversion) {
+  boost::shared_ptr<Foobar> b1 = boost::make_shared<Foobar>();
+  std::shared_ptr<Foobar> s1 = boost_to_std_shared_ptr(b1);
+  EXPECT_EQ(s1.get(), b1.get());
+
+  std::shared_ptr<Foobar> s2 = std::make_shared<Foobar>();
+  boost::shared_ptr<Foobar> b2 = std_to_boost_shared_ptr(s2);
+  EXPECT_EQ(s2.get(), b2.get());
+}
+
+TEST_F(ConversionsTests, test_base64) {
+  std::string unencoded = "HELLO";
+  auto encoded = base64Encode(unencoded);
+  EXPECT_NE(encoded.size(), 0);
+
+  auto unencoded2 = base64Decode(encoded);
+  EXPECT_EQ(unencoded, unencoded2);
+}
+}
diff --git a/src/osquery/core/tests/flags_tests.cpp b/src/osquery/core/tests/flags_tests.cpp
new file mode 100644 (file)
index 0000000..1f5343f
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/core.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+
+DECLARE_string(test_string_flag);
+
+class FlagsTests : public testing::Test {
+ public:
+  FlagsTests() {}
+
+  void SetUp() {}
+};
+
+FLAG(string, test_string_flag, "TEST STRING", "TEST DESCRIPTION");
+
+TEST_F(FlagsTests, test_set_get) {
+  // Test the core gflags functionality.
+  EXPECT_EQ(FLAGS_test_string_flag, "TEST STRING");
+
+  // Check that the gflags flag name was recorded in the osquery flag tracker.
+  auto all_flags = Flag::flags();
+  EXPECT_EQ(all_flags.count("test_string_flag"), 1);
+
+  // Update the value of the flag, and access through the osquery wrapper.
+  FLAGS_test_string_flag = "NEW TEST STRING";
+  EXPECT_EQ(Flag::getValue("test_string_flag"), "NEW TEST STRING");
+}
+
+TEST_F(FlagsTests, test_defaults) {
+  // Make sure the flag value was not reset.
+  EXPECT_EQ(FLAGS_test_string_flag, "NEW TEST STRING");
+
+  // Now test that the default value is tracked.
+  EXPECT_FALSE(Flag::isDefault("test_string_flag"));
+
+  // Check the default value accessor.
+  std::string default_value;
+  auto status = Flag::getDefaultValue("test_mistake", default_value);
+  EXPECT_FALSE(status.ok());
+  status = Flag::getDefaultValue("test_string_flag", default_value);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(default_value, "TEST STRING");
+}
+
+TEST_F(FlagsTests, test_details) {
+  // Make sure flag details are tracked correctly.
+  auto all_flags = Flag::flags();
+  auto flag_info = all_flags["test_string_flag"];
+
+  EXPECT_EQ(flag_info.type, "string");
+  EXPECT_EQ(flag_info.description, "TEST DESCRIPTION");
+  EXPECT_EQ(flag_info.default_value, "TEST STRING");
+  EXPECT_EQ(flag_info.value, "NEW TEST STRING");
+  EXPECT_EQ(flag_info.detail.shell, false);
+  EXPECT_EQ(flag_info.detail.external, false);
+}
+
+SHELL_FLAG(bool, shell_only, true, "TEST SHELL DESCRIPTION");
+EXTENSION_FLAG(bool, extension_only, true, "TEST EXTENSION DESCRIPTION");
+
+TEST_F(FlagsTests, test_flag_detail_types) {
+  EXPECT_TRUE(FLAGS_shell_only);
+  EXPECT_TRUE(FLAGS_extension_only);
+
+  auto all_flags = Flag::flags();
+  EXPECT_TRUE(all_flags["shell_only"].detail.shell);
+  EXPECT_TRUE(all_flags["extension_only"].detail.external);
+}
+
+FLAG_ALIAS(bool, shell_only_alias, shell_only);
+
+TEST_F(FlagsTests, test_aliases) {
+  EXPECT_TRUE(FLAGS_shell_only_alias);
+  FLAGS_shell_only = false;
+  EXPECT_FALSE(FLAGS_shell_only);
+  EXPECT_FALSE(FLAGS_shell_only_alias);
+}
+
+FLAG(int32, test_int32, 1, "none");
+FLAG_ALIAS(google::int32, test_int32_alias, test_int32);
+
+FLAG(int64, test_int64, (int64_t)1 << 34, "none");
+FLAG_ALIAS(google::int64, test_int64_alias, test_int64);
+
+FLAG(double, test_double, 4.2, "none");
+FLAG_ALIAS(double, test_double_alias, test_double);
+
+FLAG(string, test_string, "test", "none");
+FLAG_ALIAS(std::string, test_string_alias, test_string);
+
+TEST_F(FlagsTests, test_alias_types) {
+  // Test int32 lexical casting both ways.
+  EXPECT_EQ(FLAGS_test_int32_alias, 1);
+  FLAGS_test_int32_alias = 2;
+  EXPECT_EQ(FLAGS_test_int32, 2);
+  FLAGS_test_int32 = 3;
+  EXPECT_EQ(FLAGS_test_int32_alias, 3);
+  EXPECT_TRUE(FLAGS_test_int32_alias > 0);
+
+  EXPECT_EQ(FLAGS_test_int64_alias, (int64_t)1 << 34);
+  FLAGS_test_int64_alias = (int64_t)1 << 35;
+  EXPECT_EQ(FLAGS_test_int64, (int64_t)1 << 35);
+  FLAGS_test_int64 = (int64_t)1 << 36;
+  EXPECT_EQ(FLAGS_test_int64_alias, (int64_t)1 << 36);
+  EXPECT_TRUE(FLAGS_test_int64_alias > 0);
+
+  EXPECT_EQ(FLAGS_test_double_alias, 4.2);
+  FLAGS_test_double_alias = 2.4;
+  EXPECT_EQ(FLAGS_test_double, 2.4);
+  FLAGS_test_double = 22.44;
+  EXPECT_EQ(FLAGS_test_double_alias, 22.44);
+  EXPECT_TRUE(FLAGS_test_double_alias > 0);
+
+  // Compile-time type checking will not compare typename T to const char*
+  std::string value = FLAGS_test_string_alias;
+  EXPECT_EQ(value, "test");
+  FLAGS_test_string_alias = "test2";
+  EXPECT_EQ(FLAGS_test_string, "test2");
+  FLAGS_test_string = "test3";
+
+  // Test both the copy and assignment constructor aliases.
+  value = FLAGS_test_string_alias;
+  auto value2 = (std::string)FLAGS_test_string_alias;
+  EXPECT_EQ(value, "test3");
+}
+}
diff --git a/src/osquery/core/tests/hash_tests.cpp b/src/osquery/core/tests/hash_tests.cpp
new file mode 100644 (file)
index 0000000..b1468b1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/hash.h>
+
+#include "osquery/core/test_util.h"
+
+namespace osquery {
+
+class HashTests : public testing::Test {};
+
+TEST_F(HashTests, test_algorithms) {
+  const unsigned char buffer[1] = {'0'};
+
+  auto digest = hashFromBuffer(HASH_TYPE_MD5, buffer, 1);
+  EXPECT_EQ(digest, "cfcd208495d565ef66e7dff9f98764da");
+
+  digest = hashFromBuffer(HASH_TYPE_SHA1, buffer, 1);
+  EXPECT_EQ(digest, "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c");
+
+  digest = hashFromBuffer(HASH_TYPE_SHA256, buffer, 1);
+  EXPECT_EQ(digest,
+            "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9");
+}
+
+TEST_F(HashTests, test_update) {
+  const unsigned char buffer[1] = {'0'};
+
+  Hash hash(HASH_TYPE_MD5);
+  hash.update(buffer, 1);
+  hash.update(buffer, 1);
+  auto digest = hash.digest();
+  EXPECT_EQ(digest, "b4b147bc522828731f1a016bfa72c073");
+}
+
+TEST_F(HashTests, test_file_hashing) {
+  auto digest = hashFromFile(HASH_TYPE_MD5, kTestDataPath + "test_hashing.bin");
+  EXPECT_EQ(digest, "88ee11f2aa7903f34b8b8785d92208b1");
+}
+}
diff --git a/src/osquery/core/tests/status_tests.cpp b/src/osquery/core/tests/status_tests.cpp
new file mode 100644 (file)
index 0000000..449a981
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/status.h>
+
+#include <gtest/gtest.h>
+
+namespace osquery {
+
+class StatusTests : public testing::Test {};
+
+TEST_F(StatusTests, test_constructor) {
+  auto s = Status(5, "message");
+  EXPECT_EQ(s.getCode(), 5);
+  EXPECT_EQ(s.getMessage(), "message");
+}
+
+TEST_F(StatusTests, test_constructor_2) {
+  Status s;
+  EXPECT_EQ(s.getCode(), 0);
+  EXPECT_EQ(s.getMessage(), "OK");
+}
+
+TEST_F(StatusTests, test_ok) {
+  auto s1 = Status(5, "message");
+  EXPECT_FALSE(s1.ok());
+  auto s2 = Status(0, "message");
+  EXPECT_TRUE(s2.ok());
+}
+
+TEST_F(StatusTests, test_to_string) {
+  auto s = Status(0, "foobar");
+  EXPECT_EQ(s.toString(), "foobar");
+}
+}
diff --git a/src/osquery/core/tests/tables_tests.cpp b/src/osquery/core/tests/tables_tests.cpp
new file mode 100644 (file)
index 0000000..524dbc2
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/tables.h>
+
+namespace osquery {
+
+class TablesTests : public testing::Test {};
+
+TEST_F(TablesTests, test_constraint) {
+  auto constraint = Constraint(EQUALS);
+  constraint.expr = "none";
+
+  EXPECT_EQ(constraint.op, EQUALS);
+  EXPECT_EQ(constraint.expr, "none");
+}
+
+TEST_F(TablesTests, test_constraint_list) {
+  struct ConstraintList cl;
+
+  auto constraint = Constraint(EQUALS);
+  constraint.expr = "some";
+
+  // The constraint list is a simple struct.
+  cl.add(constraint);
+  EXPECT_EQ(cl.constraints_.size(), 1);
+
+  constraint = Constraint(EQUALS);
+  constraint.expr = "some_other";
+  cl.add(constraint);
+
+  constraint = Constraint(GREATER_THAN);
+  constraint.expr = "more_than";
+  cl.add(constraint);
+  EXPECT_EQ(cl.constraints_.size(), 3);
+
+  auto all_equals = cl.getAll(EQUALS);
+  EXPECT_EQ(all_equals.size(), 2);
+}
+
+TEST_F(TablesTests, test_constraint_matching) {
+  struct ConstraintList cl;
+  // An empty constraint list has expectations.
+  EXPECT_FALSE(cl.exists());
+  EXPECT_FALSE(cl.exists(GREATER_THAN));
+  EXPECT_TRUE(cl.notExistsOrMatches("some"));
+
+  auto constraint = Constraint(EQUALS);
+  constraint.expr = "some";
+  cl.add(constraint);
+
+  // Test existence checks based on flags.
+  EXPECT_TRUE(cl.exists());
+  EXPECT_TRUE(cl.exists(EQUALS));
+  EXPECT_TRUE(cl.exists(EQUALS | LESS_THAN));
+  EXPECT_FALSE(cl.exists(LESS_THAN));
+
+  EXPECT_TRUE(cl.notExistsOrMatches("some"));
+  EXPECT_TRUE(cl.matches("some"));
+  EXPECT_FALSE(cl.notExistsOrMatches("not_some"));
+
+  struct ConstraintList cl2;
+  cl2.affinity = "INTEGER";
+  constraint = Constraint(LESS_THAN);
+  constraint.expr = "1000";
+  cl2.add(constraint);
+  constraint = Constraint(GREATER_THAN);
+  constraint.expr = "1";
+  cl2.add(constraint);
+
+  // Test both SQL-provided string types.
+  EXPECT_TRUE(cl2.matches("10"));
+  // ...and the type literal.
+  EXPECT_TRUE(cl2.matches(10));
+
+  // Test operator lower bounds.
+  EXPECT_FALSE(cl2.matches(0));
+  EXPECT_FALSE(cl2.matches(1));
+
+  // Test operator upper bounds.
+  EXPECT_FALSE(cl2.matches(1000));
+  EXPECT_FALSE(cl2.matches(1001));
+
+  // Now test inclusive bounds.
+  struct ConstraintList cl3;
+  constraint = Constraint(LESS_THAN_OR_EQUALS);
+  constraint.expr = "1000";
+  cl3.add(constraint);
+  constraint = Constraint(GREATER_THAN_OR_EQUALS);
+  constraint.expr = "1";
+  cl3.add(constraint);
+
+  EXPECT_FALSE(cl3.matches(1001));
+  EXPECT_TRUE(cl3.matches(1000));
+
+  EXPECT_FALSE(cl3.matches(0));
+  EXPECT_TRUE(cl3.matches(1));
+}
+
+TEST_F(TablesTests, test_constraint_map) {
+  ConstraintMap cm;
+  ConstraintList cl;
+
+  cl.add(Constraint(EQUALS, "some"));
+  cm["path"] = cl;
+
+  // If a constraint list exists for a map key, normal constraints apply.
+  EXPECT_TRUE(cm["path"].matches("some"));
+  EXPECT_FALSE(cm["path"].matches("not_some"));
+
+  // If a constraint list does not exist, then all checks will match.
+  // If there is no predicate clause then all results will match.
+  EXPECT_TRUE(cm["not_path"].matches("some"));
+  EXPECT_TRUE(cm["not_path"].notExistsOrMatches("some"));
+  EXPECT_FALSE(cm["not_path"].exists());
+  EXPECT_FALSE(cm["not_path"].existsAndMatches("some"));
+
+  // And of the column has constraints:
+  EXPECT_TRUE(cm["path"].notExistsOrMatches("some"));
+  EXPECT_FALSE(cm["path"].notExistsOrMatches("not_some"));
+  EXPECT_TRUE(cm["path"].exists());
+  EXPECT_TRUE(cm["path"].existsAndMatches("some"));
+}
+}
diff --git a/src/osquery/core/tests/text_tests.cpp b/src/osquery/core/tests/text_tests.cpp
new file mode 100644 (file)
index 0000000..fbc8a2a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+
+#include "osquery/core/test_util.h"
+
+namespace osquery {
+
+class TextTests : public testing::Test {};
+
+TEST_F(TextTests, test_split) {
+  for (const auto& i : generateSplitStringTestData()) {
+    EXPECT_EQ(split(i.test_string), i.test_vector);
+  }
+}
+
+TEST_F(TextTests, test_join) {
+  std::vector<std::string> content = {
+      "one", "two", "three",
+  };
+  EXPECT_EQ(join(content, ", "), "one, two, three");
+}
+
+TEST_F(TextTests, test_split_occurences) {
+  std::string content = "T: 'S:S'";
+  std::vector<std::string> expected = {
+      "T", "'S:S'",
+  };
+  EXPECT_EQ(split(content, ":", 1), expected);
+}
+}
diff --git a/src/osquery/core/text.cpp b/src/osquery/core/text.cpp
new file mode 100644 (file)
index 0000000..3ed8962
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+
+#include <osquery/core.h>
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/trim.hpp>
+
+namespace osquery {
+
+std::vector<std::string> split(const std::string& s, const std::string& delim) {
+  std::vector<std::string> elems;
+  boost::split(elems, s, boost::is_any_of(delim));
+  auto start =
+      std::remove_if(elems.begin(), elems.end(), [](const std::string& s) {
+        return s.size() == 0;
+      });
+  elems.erase(start, elems.end());
+  for (auto& each : elems) {
+    boost::algorithm::trim(each);
+  }
+  return elems;
+}
+
+std::vector<std::string> split(const std::string& s,
+                               const std::string& delim,
+                               size_t occurences) {
+  // Split the string normally with the required delimiter.
+  auto content = split(s, delim);
+  // While the result split exceeds the number of requested occurrences, join.
+  std::vector<std::string> accumulator;
+  std::vector<std::string> elems;
+  for (size_t i = 0; i < content.size(); i++) {
+    if (i < occurences) {
+      elems.push_back(content.at(i));
+    } else {
+      accumulator.push_back(content.at(i));
+    }
+  }
+  // Join the optional accumulator.
+  if (accumulator.size() > 0) {
+    elems.push_back(join(accumulator, delim));
+  }
+  return elems;
+}
+
+std::string join(const std::vector<std::string>& s, const std::string& tok) {
+  return boost::algorithm::join(s, tok);
+}
+}
diff --git a/src/osquery/core/watcher.cpp b/src/osquery/core/watcher.cpp
new file mode 100644 (file)
index 0000000..fba34f2
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <cstring>
+
+#include <math.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <boost/filesystem.hpp>
+
+#include <osquery/events.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+
+#include "osquery/core/watcher.h"
+#include "osquery/dispatcher/dispatcher.h"
+
+extern char** environ;
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+const std::map<WatchdogLimitType, std::vector<size_t> > kWatchdogLimits = {
+    // Maximum MB worker can privately allocate.
+    {MEMORY_LIMIT, {80, 50, 30, 1000}},
+    // Percent of user or system CPU worker can utilize for LATENCY_LIMIT
+    // seconds.
+    {UTILIZATION_LIMIT, {90, 80, 60, 1000}},
+    // Number of seconds the worker should run, else consider the exit fatal.
+    {RESPAWN_LIMIT, {20, 20, 20, 5}},
+    // If the worker respawns too quickly, backoff on creating additional.
+    {RESPAWN_DELAY, {5, 5, 5, 1}},
+    // Seconds of tolerable UTILIZATION_LIMIT sustained latency.
+    {LATENCY_LIMIT, {12, 6, 3, 1}},
+    // How often to poll for performance limit violations.
+    {INTERVAL, {3, 3, 3, 1}},
+};
+
+const std::string kExtensionExtension = ".ext";
+
+CLI_FLAG(int32,
+         watchdog_level,
+         1,
+         "Performance limit level (0=loose, 1=normal, 2=restrictive, 3=debug)");
+
+CLI_FLAG(bool, disable_watchdog, false, "Disable userland watchdog process");
+
+/// If the worker exits the watcher will inspect the return code.
+void childHandler(int signum) {
+  siginfo_t info;
+  // Make sure WNOWAIT is used to the wait information is not removed.
+  // Watcher::watch implements a thread to poll for this information.
+  waitid(P_ALL, 0, &info, WEXITED | WSTOPPED | WNOHANG | WNOWAIT);
+  if (info.si_code == CLD_EXITED && info.si_status == EXIT_CATASTROPHIC) {
+    // A child process had a catastrophic error, abort the watcher.
+    ::exit(EXIT_FAILURE);
+  }
+}
+
+void Watcher::resetWorkerCounters(size_t respawn_time) {
+  // Reset the monitoring counters for the watcher.
+  auto& state = instance().state_;
+  state.sustained_latency = 0;
+  state.user_time = 0;
+  state.system_time = 0;
+  state.last_respawn_time = respawn_time;
+}
+
+void Watcher::resetExtensionCounters(const std::string& extension,
+                                     size_t respawn_time) {
+  WatcherLocker locker;
+  auto& state = instance().extension_states_[extension];
+  state.sustained_latency = 0;
+  state.user_time = 0;
+  state.system_time = 0;
+  state.last_respawn_time = respawn_time;
+}
+
+std::string Watcher::getExtensionPath(pid_t child) {
+  for (const auto& extension : extensions()) {
+    if (extension.second == child) {
+      return extension.first;
+    }
+  }
+  return "";
+}
+
+void Watcher::removeExtensionPath(const std::string& extension) {
+  WatcherLocker locker;
+  instance().extensions_.erase(extension);
+  instance().extension_states_.erase(extension);
+}
+
+PerformanceState& Watcher::getState(pid_t child) {
+  if (child == instance().worker_) {
+    return instance().state_;
+  } else {
+    return instance().extension_states_[getExtensionPath(child)];
+  }
+}
+
+PerformanceState& Watcher::getState(const std::string& extension) {
+  return instance().extension_states_[extension];
+}
+
+void Watcher::setExtension(const std::string& extension, pid_t child) {
+  WatcherLocker locker;
+  instance().extensions_[extension] = child;
+}
+
+void Watcher::reset(pid_t child) {
+  if (child == instance().worker_) {
+    instance().worker_ = 0;
+    resetWorkerCounters(0);
+    return;
+  }
+
+  // If it was not the worker pid then find the extension name to reset.
+  for (const auto& extension : extensions()) {
+    if (extension.second == child) {
+      setExtension(extension.first, 0);
+      resetExtensionCounters(extension.first, 0);
+    }
+  }
+}
+
+void Watcher::addExtensionPath(const std::string& path) {
+  // Resolve acceptable extension binaries from autoload paths.
+  if (isDirectory(path).ok()) {
+    VLOG(1) << "Cannot autoload extension from directory: " << path;
+    return;
+  }
+
+  // Only autoload extensions which were safe at the time of discovery.
+  // If the extension binary later becomes unsafe (permissions change) then
+  // it will fail to reload if a reload is ever needed.
+  fs::path extension(path);
+  if (safePermissions(extension.parent_path().string(), path, true)) {
+    if (extension.extension().string() == kExtensionExtension) {
+      setExtension(extension.string(), 0);
+      resetExtensionCounters(extension.string(), 0);
+      VLOG(1) << "Found autoloadable extension: " << extension.string();
+    }
+  }
+}
+
+bool Watcher::hasManagedExtensions() {
+  if (instance().extensions_.size() > 0) {
+    return true;
+  }
+
+  // A watchdog process may hint to a worker the number of managed extensions.
+  // Setting this counter to 0 will prevent the worker from waiting for missing
+  // dependent config plugins. Otherwise, its existence, will cause a worker to
+  // wait for missing plugins to broadcast from managed extensions.
+  return (getenv("OSQUERY_EXTENSIONS") != nullptr);
+}
+
+bool WatcherRunner::ok() {
+  interruptableSleep(getWorkerLimit(INTERVAL) * 1000);
+  // Watcher is OK to run if a worker or at least one extension exists.
+  return (Watcher::getWorker() >= 0 || Watcher::hasManagedExtensions());
+}
+
+void WatcherRunner::start() {
+  // Set worker performance counters to an initial state.
+  Watcher::resetWorkerCounters(0);
+  signal(SIGCHLD, childHandler);
+
+  // Enter the watch loop.
+  do {
+    if (use_worker_ && !watch(Watcher::getWorker())) {
+      // The watcher failed, create a worker.
+      createWorker();
+    }
+
+    // Loop over every managed extension and check sanity.
+    std::vector<std::string> failing_extensions;
+    for (const auto& extension : Watcher::extensions()) {
+      if (!watch(extension.second)) {
+        if (!createExtension(extension.first)) {
+          failing_extensions.push_back(extension.first);
+        }
+      }
+    }
+    // If any extension creations failed, stop managing them.
+    for (const auto& failed_extension : failing_extensions) {
+      Watcher::removeExtensionPath(failed_extension);
+    }
+  } while (ok());
+}
+
+bool WatcherRunner::watch(pid_t child) {
+  int status;
+  pid_t result = waitpid(child, &status, WNOHANG);
+  if (child == 0 || result == child) {
+    // Worker does not exist or never existed.
+    return false;
+  } else if (result == 0) {
+    // If the inspect finds problems it will stop/restart the worker.
+    if (!isChildSane(child)) {
+      stopChild(child);
+      return false;
+    }
+  }
+  return true;
+}
+
+void WatcherRunner::stopChild(pid_t child) {
+  kill(child, SIGKILL);
+
+  // Clean up the defunct (zombie) process.
+  waitpid(-1, 0, WNOHANG);
+}
+
+bool WatcherRunner::isChildSane(pid_t child) {
+  auto rows = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(child));
+  if (rows.size() == 0) {
+    // Could not find worker process?
+    return false;
+  }
+
+  // Get the performance state for the worker or extension.
+  size_t sustained_latency = 0;
+  // Compare CPU utilization since last check.
+  BIGINT_LITERAL footprint = 0, user_time = 0, system_time = 0, parent = 0;
+  // IV is the check interval in seconds, and utilization is set per-second.
+  auto iv = std::max(getWorkerLimit(INTERVAL), (size_t)1);
+
+  {
+    WatcherLocker locker;
+    auto& state = Watcher::getState(child);
+    try {
+      parent = AS_LITERAL(BIGINT_LITERAL, rows[0].at("parent"));
+      user_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("user_time")) / iv;
+      system_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("system_time")) / iv;
+      footprint = AS_LITERAL(BIGINT_LITERAL, rows[0].at("resident_size"));
+    } catch (const std::exception& e) {
+      state.sustained_latency = 0;
+    }
+
+    // Check the difference of CPU time used since last check.
+    if (user_time - state.user_time > getWorkerLimit(UTILIZATION_LIMIT) ||
+        system_time - state.system_time > getWorkerLimit(UTILIZATION_LIMIT)) {
+      state.sustained_latency++;
+    } else {
+      state.sustained_latency = 0;
+    }
+    // Update the current CPU time.
+    state.user_time = user_time;
+    state.system_time = system_time;
+
+    // Check if the sustained difference exceeded the acceptable latency limit.
+    sustained_latency = state.sustained_latency;
+
+    // Set the memory footprint as the amount of resident bytes allocated
+    // since the process image was created (estimate).
+    // A more-meaningful check would limit this to writable regions.
+    if (state.initial_footprint == 0) {
+      state.initial_footprint = footprint;
+    }
+
+    // Set the measured/limit-applied footprint to the post-launch allocations.
+    if (footprint < state.initial_footprint) {
+      footprint = 0;
+    } else {
+      footprint = footprint - state.initial_footprint;
+    }
+  }
+
+  // Only make a decision about the child sanity if it is still the watcher's
+  // child. It's possible for the child to die, and its pid reused.
+  if (parent != getpid()) {
+    // The child's parent is not the watcher.
+    Watcher::reset(child);
+    // Do not stop or call the child insane, since it is not our child.
+    return true;
+  }
+
+  if (sustained_latency > 0 &&
+      sustained_latency * iv >= getWorkerLimit(LATENCY_LIMIT)) {
+    LOG(WARNING) << "osqueryd worker (" << child
+                 << ") system performance limits exceeded";
+    return false;
+  }
+  // Check if the private memory exceeds a memory limit.
+  if (footprint > 0 && footprint > getWorkerLimit(MEMORY_LIMIT) * 1024 * 1024) {
+    LOG(WARNING) << "osqueryd worker (" << child
+                 << ") memory limits exceeded: " << footprint;
+    return false;
+  }
+
+  // The worker is sane, no action needed.
+  // Attempt to flush status logs to the well-behaved worker.
+  relayStatusLogs();
+  return true;
+}
+
+void WatcherRunner::createWorker() {
+  {
+    WatcherLocker locker;
+    if (Watcher::getState(Watcher::getWorker()).last_respawn_time >
+        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
+      LOG(WARNING) << "osqueryd worker respawning too quickly: "
+                   << Watcher::workerRestartCount() << " times";
+      Watcher::workerRestarted();
+      interruptableSleep(getWorkerLimit(RESPAWN_DELAY) * 1000);
+      // Exponential back off for quickly-respawning clients.
+      interruptableSleep(pow(2, Watcher::workerRestartCount()) * 1000);
+    }
+  }
+
+  // Get the path of the current process.
+  auto qd = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(getpid()));
+  if (qd.size() != 1 || qd[0].count("path") == 0 || qd[0]["path"].size() == 0) {
+    LOG(ERROR) << "osquery watcher cannot determine process path for worker";
+    ::exit(EXIT_FAILURE);
+  }
+
+  // Set an environment signaling to potential plugin-dependent workers to wait
+  // for extensions to broadcast.
+  if (Watcher::hasManagedExtensions()) {
+    setenv("OSQUERY_EXTENSIONS", "true", 1);
+  }
+
+  // Get the complete path of the osquery process binary.
+  auto exec_path = fs::system_complete(fs::path(qd[0]["path"]));
+  if (!safePermissions(
+          exec_path.parent_path().string(), exec_path.string(), true)) {
+    // osqueryd binary has become unsafe.
+    LOG(ERROR) << "osqueryd has unsafe permissions: " << exec_path.string();
+    ::exit(EXIT_FAILURE);
+  }
+
+  auto worker_pid = fork();
+  if (worker_pid < 0) {
+    // Unrecoverable error, cannot create a worker process.
+    LOG(ERROR) << "osqueryd could not create a worker process";
+    ::exit(EXIT_FAILURE);
+  } else if (worker_pid == 0) {
+    // This is the new worker process, no watching needed.
+    setenv("OSQUERY_WORKER", std::to_string(getpid()).c_str(), 1);
+    execve(exec_path.string().c_str(), argv_, environ);
+    // Code should never reach this point.
+    LOG(ERROR) << "osqueryd could not start worker process";
+    ::exit(EXIT_CATASTROPHIC);
+  }
+
+  Watcher::setWorker(worker_pid);
+  Watcher::resetWorkerCounters(getUnixTime());
+  VLOG(1) << "osqueryd watcher (" << getpid() << ") executing worker ("
+          << worker_pid << ")";
+}
+
+bool WatcherRunner::createExtension(const std::string& extension) {
+  {
+    WatcherLocker locker;
+    if (Watcher::getState(extension).last_respawn_time >
+        getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) {
+      LOG(WARNING) << "Extension respawning too quickly: " << extension;
+      // Unlike a worker, if an extension respawns to quickly we give up.
+      return false;
+    }
+  }
+
+  // Check the path to the previously-discovered extension binary.
+  auto exec_path = fs::system_complete(fs::path(extension));
+  if (!safePermissions(
+          exec_path.parent_path().string(), exec_path.string(), true)) {
+    // Extension binary has become unsafe.
+    LOG(WARNING) << "Extension binary has unsafe permissions: " << extension;
+    return false;
+  }
+
+  auto ext_pid = fork();
+  if (ext_pid < 0) {
+    // Unrecoverable error, cannot create an extension process.
+    LOG(ERROR) << "Cannot create extension process: " << extension;
+    ::exit(EXIT_FAILURE);
+  } else if (ext_pid == 0) {
+    // Pass the current extension socket and a set timeout to the extension.
+    setenv("OSQUERY_EXTENSION", std::to_string(getpid()).c_str(), 1);
+    // Execute extension with very specific arguments.
+    execle(exec_path.string().c_str(),
+           ("osquery extension: " + extension).c_str(),
+           "--socket",
+           Flag::getValue("extensions_socket").c_str(),
+           "--timeout",
+           Flag::getValue("extensions_timeout").c_str(),
+           "--interval",
+           Flag::getValue("extensions_interval").c_str(),
+           (Flag::getValue("verbose") == "true") ? "--verbose" : (char*)nullptr,
+           (char*)nullptr,
+           environ);
+    // Code should never reach this point.
+    VLOG(1) << "Could not start extension process: " << extension;
+    ::exit(EXIT_FAILURE);
+  }
+
+  Watcher::setExtension(extension, ext_pid);
+  Watcher::resetExtensionCounters(extension, getUnixTime());
+  VLOG(1) << "Created and monitoring extension child (" << ext_pid << "): "
+          << extension;
+  return true;
+}
+
+void WatcherWatcherRunner::start() {
+  while (true) {
+    if (getppid() != watcher_) {
+      // Watcher died, the worker must follow.
+      VLOG(1) << "osqueryd worker (" << getpid()
+              << ") detected killed watcher (" << watcher_ << ")";
+      Dispatcher::stopServices();
+      // The watcher watcher is a thread. Do not join services after removing.
+      ::exit(EXIT_SUCCESS);
+    }
+    interruptableSleep(getWorkerLimit(INTERVAL) * 1000);
+  }
+}
+
+size_t getWorkerLimit(WatchdogLimitType name, int level) {
+  if (kWatchdogLimits.count(name) == 0) {
+    return 0;
+  }
+
+  // If no level was provided then use the default (config/switch).
+  if (level == -1) {
+    level = FLAGS_watchdog_level;
+  }
+  if (level > 3) {
+    return kWatchdogLimits.at(name).back();
+  }
+  return kWatchdogLimits.at(name).at(level);
+}
+}
diff --git a/src/osquery/core/watcher.h b/src/osquery/core/watcher.h
new file mode 100644 (file)
index 0000000..8dd06b5
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <string>
+
+#include <unistd.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include <osquery/flags.h>
+
+#include "osquery/dispatcher/dispatcher.h"
+
+/// Define a special debug/testing watchdog level.
+#define WATCHDOG_LEVEL_DEBUG 3
+/// Define the default watchdog level, level below are considered permissive.
+#define WATCHDOG_LEVEL_DEFAULT 1
+
+namespace osquery {
+
+DECLARE_bool(disable_watchdog);
+DECLARE_int32(watchdog_level);
+
+class WatcherRunner;
+
+/**
+ * @brief Categories of process performance limitations.
+ *
+ * Performance limits are applied by a watcher thread on autoloaded extensions
+ * and a optional daemon worker process. The performance types are identified
+ * here, and organized into levels. Such that a caller may enforce rigor or
+ * relax the performance expectations of a osquery daemon.
+ */
+enum WatchdogLimitType {
+  MEMORY_LIMIT,
+  UTILIZATION_LIMIT,
+  RESPAWN_LIMIT,
+  RESPAWN_DELAY,
+  LATENCY_LIMIT,
+  INTERVAL,
+};
+
+/**
+ * @brief A performance state structure for an autoloaded extension or worker.
+ *
+ * A watcher thread will continue to check the performance state, and keep a
+ * last-checked snapshot for each autoloaded extension and worker process.
+ */
+struct PerformanceState {
+  /// A counter of how many intervals the process exceeded performance limits.
+  size_t sustained_latency;
+  /// The last checked user CPU time.
+  size_t user_time;
+  /// The last checked system CPU time.
+  size_t system_time;
+  /// A timestamp when the process/worker was last created.
+  size_t last_respawn_time;
+
+  /// The initial (or as close as possible) process image footprint.
+  size_t initial_footprint;
+
+  PerformanceState() {
+    sustained_latency = 0;
+    user_time = 0;
+    system_time = 0;
+    last_respawn_time = 0;
+    initial_footprint = 0;
+  }
+};
+
+/**
+ * @brief Thread-safe watched child process state manager.
+ *
+ * The Watcher instance is separated from the WatcherRunner thread to allow
+ * signals and osquery-introspection to monitor the autoloaded extensions
+ * and optional worker stats. A child-process change signal may indicate an
+ * autoloaded extension ended. Tables may also report on the historic worker
+ * or extension utilizations.
+ *
+ * Though not critical, it is preferred to remove the extension's broadcasted
+ * routes quickly. Locking access to the extensions list between signals and
+ * the WatcherRunner thread allows osquery to tearDown registry changes before
+ * attempting to respawn an extension process.
+ */
+class Watcher : private boost::noncopyable {
+ public:
+  /// Instance accessor
+  static Watcher& instance() {
+    static Watcher instance;
+    return instance;
+  }
+
+  /// Reset counters after a worker exits.
+  static void resetWorkerCounters(size_t respawn_time);
+
+  /// Reset counters for an extension path.
+  static void resetExtensionCounters(const std::string& extension,
+                                     size_t respawn_time);
+
+  /// Lock access to extensions.
+  static void lock() { instance().lock_.lock(); }
+
+  /// Unlock access to extensions.
+  static void unlock() { instance().lock_.unlock(); }
+
+  /// Accessor for autoloadable extension paths.
+  static const std::map<std::string, pid_t>& extensions() {
+    return instance().extensions_;
+  }
+
+  /// Lookup extension path from pid.
+  static std::string getExtensionPath(pid_t child);
+
+  /// Remove an autoloadable extension path.
+  static void removeExtensionPath(const std::string& extension);
+
+  /// Add extensions autoloadable paths.
+  static void addExtensionPath(const std::string& path);
+
+  /// Get state information for a worker or extension child.
+  static PerformanceState& getState(pid_t child);
+  static PerformanceState& getState(const std::string& extension);
+
+  /// Accessor for the worker process.
+  static pid_t getWorker() { return instance().worker_; }
+
+  /// Setter for worker process.
+  static void setWorker(pid_t child) { instance().worker_ = child; }
+
+  /// Setter for an extension process.
+  static void setExtension(const std::string& extension, pid_t child);
+
+  /// Reset pid and performance counters for a worker or extension process.
+  static void reset(pid_t child);
+
+  /// Count the number of worker restarts.
+  static size_t workerRestartCount() { return instance().worker_restarts_; }
+
+  /**
+   * @brief Return the state of autoloadable extensions.
+   *
+   * Some initialization decisions are made based on waiting for plugins to
+   * broadcast from potentially-loaded extensions. If no extensions are loaded
+   * and an active (selected at command line) plugin is missing, fail quickly.
+   */
+  static bool hasManagedExtensions();
+
+ private:
+  /// Do not request the lock until extensions are used.
+  Watcher()
+      : worker_(-1), worker_restarts_(0), lock_(mutex_, boost::defer_lock) {}
+  Watcher(Watcher const&);
+  void operator=(Watcher const&);
+  virtual ~Watcher() {}
+
+ private:
+  /// Inform the watcher that the worker restarted without cause.
+  static void workerRestarted() { instance().worker_restarts_++; }
+
+ private:
+  /// Performance state for the worker process.
+  PerformanceState state_;
+  /// Performance states for each autoloadable extension binary.
+  std::map<std::string, PerformanceState> extension_states_;
+
+ private:
+  /// Keep the single worker process/thread ID for inspection.
+  pid_t worker_;
+  /// Number of worker restarts NOT induced by a watchdog process.
+  size_t worker_restarts_;
+  /// Keep a list of resolved extension paths and their managed pids.
+  std::map<std::string, pid_t> extensions_;
+  /// Paths to autoload extensions.
+  std::vector<std::string> extensions_paths_;
+
+ private:
+  /// Mutex and lock around extensions access.
+  boost::mutex mutex_;
+  /// Mutex and lock around extensions access.
+  boost::unique_lock<boost::mutex> lock_;
+
+ private:
+  friend class WatcherRunner;
+};
+
+/**
+ * @brief A scoped locker for iterating over watcher extensions.
+ *
+ * A lock must be used if any part of osquery wants to enumerate the autoloaded
+ * extensions or autoloadable extension paths a Watcher may be monitoring.
+ * A signal or WatcherRunner thread may stop or start extensions.
+ */
+class WatcherLocker {
+ public:
+  /// Construct and gain watcher lock.
+  WatcherLocker() { Watcher::lock(); }
+  /// Destruct and release watcher lock.
+  ~WatcherLocker() { Watcher::unlock(); }
+};
+
+/**
+ * @brief The watchdog thread responsible for spawning/monitoring children.
+ *
+ * The WatcherRunner thread will spawn any autoloaded extensions or optional
+ * osquery daemon worker processes. It will then poll for their performance
+ * state and kill/respawn osquery child processes if they violate limits.
+ */
+class WatcherRunner : public InternalRunnable {
+ public:
+  /**
+   * @brief Construct a watcher thread.
+   *
+   * @param argc The osquery process argc.
+   * @param argv The osquery process argv.
+   * @param use_worker True if the process should spawn and monitor a worker.
+   */
+  explicit WatcherRunner(int argc, char** argv, bool use_worker)
+      : argc_(argc), argv_(argv), use_worker_(use_worker) {
+    (void)argc_;
+  }
+
+ private:
+  /// Dispatcher (this service thread's) entry point.
+  void start();
+  /// Boilerplate function to sleep for some configured latency
+  bool ok();
+  /// Begin the worker-watcher process.
+  bool watch(pid_t child);
+  /// Inspect into the memory, CPU, and other worker/extension process states.
+  bool isChildSane(pid_t child);
+
+ private:
+  /// Fork and execute a worker process.
+  void createWorker();
+  /// Fork an extension process.
+  bool createExtension(const std::string& extension);
+  /// If a worker/extension has otherwise gone insane, stop it.
+  void stopChild(pid_t child);
+
+ private:
+  /// Keep the invocation daemon's argc to iterate through argv.
+  int argc_;
+  /// When a worker child is spawned the argv will be scrubbed.
+  char** argv_;
+  /// Spawn/monitor a worker process.
+  bool use_worker_;
+};
+
+/// The WatcherWatcher is spawned within the worker and watches the watcher.
+class WatcherWatcherRunner : public InternalRunnable {
+ public:
+  explicit WatcherWatcherRunner(pid_t watcher) : watcher_(watcher) {}
+
+  /// Runnable thread's entry point.
+  void start();
+
+ private:
+  /// Parent, or watchdog, process ID.
+  pid_t watcher_;
+};
+
+/// Get a performance limit by name and optional level.
+size_t getWorkerLimit(WatchdogLimitType limit, int level = -1);
+}
diff --git a/src/osquery/database/CMakeLists.txt b/src/osquery/database/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4d47844
--- /dev/null
@@ -0,0 +1,21 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_database database.cpp)
+
+ADD_OSQUERY_LIBRARY(osquery_database_internal db_handle.cpp
+                                                                                         query.cpp)
+
+FILE(GLOB OSQUERY_DATABASE_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_DATABASE_TESTS})
diff --git a/src/osquery/database/database.cpp b/src/osquery/database/database.cpp
new file mode 100644 (file)
index 0000000..96e9ec6
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osquery/database.h>
+#include <osquery/logger.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+typedef unsigned char byte;
+
+/////////////////////////////////////////////////////////////////////////////
+// Row - the representation of a row in a set of database results. Row is a
+// simple map where individual column names are keys, which map to the Row's
+// respective value
+/////////////////////////////////////////////////////////////////////////////
+
+std::string escapeNonPrintableBytes(const std::string& data) {
+  std::string escaped;
+  // clang-format off
+  char const hex_chars[16] = {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    'A', 'B', 'C', 'D', 'E', 'F',
+  };
+  // clang-format on
+  for (int i = 0; i < data.length(); i++) {
+    if (((byte)data[i]) < 0x20 || ((byte)data[i]) >= 0x80) {
+      escaped += "\\x";
+      escaped += hex_chars[(((byte)data[i])) >> 4];
+      escaped += hex_chars[((byte)data[i] & 0x0F) >> 0];
+    } else {
+      escaped += data[i];
+    }
+  }
+  return escaped;
+}
+
+void escapeQueryData(const QueryData& oldData, QueryData& newData) {
+  for (const auto& r : oldData) {
+    Row newRow;
+    for (auto& i : r) {
+      newRow[i.first] = escapeNonPrintableBytes(i.second);
+    }
+    newData.push_back(newRow);
+  }
+}
+
+Status serializeRow(const Row& r, pt::ptree& tree) {
+  try {
+    for (auto& i : r) {
+      tree.put<std::string>(i.first, i.second);
+    }
+  } catch (const std::exception& e) {
+    return Status(1, e.what());
+  }
+  return Status(0, "OK");
+}
+
+Status serializeRowJSON(const Row& r, std::string& json) {
+  pt::ptree tree;
+  auto status = serializeRow(r, tree);
+  if (!status.ok()) {
+    return status;
+  }
+
+  std::ostringstream output;
+  try {
+    pt::write_json(output, tree, false);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    // The content could not be represented as JSON.
+    return Status(1, e.what());
+  }
+  json = output.str();
+  return Status(0, "OK");
+}
+
+Status deserializeRow(const pt::ptree& tree, Row& r) {
+  for (const auto& i : tree) {
+    if (i.first.length() > 0) {
+      r[i.first] = i.second.data();
+    }
+  }
+  return Status(0, "OK");
+}
+
+Status deserializeRowJSON(const std::string& json, Row& r) {
+  pt::ptree tree;
+  try {
+    std::stringstream input;
+    input << json;
+    pt::read_json(input, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return Status(1, e.what());
+  }
+  return deserializeRow(tree, r);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// QueryData - the representation of a database query result set. It's a
+// vector of rows
+/////////////////////////////////////////////////////////////////////////////
+
+Status serializeQueryData(const QueryData& q, pt::ptree& tree) {
+  for (const auto& r : q) {
+    pt::ptree serialized;
+    auto s = serializeRow(r, serialized);
+    if (!s.ok()) {
+      return s;
+    }
+    tree.push_back(std::make_pair("", serialized));
+  }
+  return Status(0, "OK");
+}
+
+Status serializeQueryDataJSON(const QueryData& q, std::string& json) {
+  pt::ptree tree;
+  auto status = serializeQueryData(q, tree);
+  if (!status.ok()) {
+    return status;
+  }
+
+  std::ostringstream output;
+  try {
+    pt::write_json(output, tree, false);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    // The content could not be represented as JSON.
+    return Status(1, e.what());
+  }
+  json = output.str();
+  return Status(0, "OK");
+}
+
+Status deserializeQueryData(const pt::ptree& tree, QueryData& qd) {
+  for (const auto& i : tree) {
+    Row r;
+    auto status = deserializeRow(i.second, r);
+    if (!status.ok()) {
+      return status;
+    }
+    qd.push_back(r);
+  }
+  return Status(0, "OK");
+}
+
+Status deserializeQueryDataJSON(const std::string& json, QueryData& qd) {
+  pt::ptree tree;
+  try {
+    std::stringstream input;
+    input << json;
+    pt::read_json(input, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return Status(1, e.what());
+  }
+  return deserializeQueryData(tree, qd);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// DiffResults - the representation of two diffed QueryData result sets.
+// Given and old and new QueryData, DiffResults indicates the "added" subset
+// of rows and the "removed" subset of Rows
+/////////////////////////////////////////////////////////////////////////////
+
+Status serializeDiffResults(const DiffResults& d, pt::ptree& tree) {
+  pt::ptree added;
+  auto status = serializeQueryData(d.added, added);
+  if (!status.ok()) {
+    return status;
+  }
+  tree.add_child("added", added);
+
+  pt::ptree removed;
+  status = serializeQueryData(d.removed, removed);
+  if (!status.ok()) {
+    return status;
+  }
+  tree.add_child("removed", removed);
+  return Status(0, "OK");
+}
+
+Status deserializeDiffResults(const pt::ptree& tree, DiffResults& dr) {
+  if (tree.count("added") > 0) {
+    auto status = deserializeQueryData(tree.get_child("added"), dr.added);
+    if (!status.ok()) {
+      return status;
+    }
+  }
+
+  if (tree.count("removed") > 0) {
+    auto status = deserializeQueryData(tree.get_child("removed"), dr.removed);
+    if (!status.ok()) {
+      return status;
+    }
+  }
+  return Status(0, "OK");
+}
+
+Status serializeDiffResultsJSON(const DiffResults& d, std::string& json) {
+  pt::ptree tree;
+  auto status = serializeDiffResults(d, tree);
+  if (!status.ok()) {
+    return status;
+  }
+
+  std::ostringstream output;
+  try {
+    pt::write_json(output, tree, false);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    // The content could not be represented as JSON.
+    return Status(1, e.what());
+  }
+  json = output.str();
+  return Status(0, "OK");
+}
+
+DiffResults diff(const QueryData& old, const QueryData& current) {
+  DiffResults r;
+  QueryData overlap;
+
+  for (const auto& i : current) {
+    auto item = std::find(old.begin(), old.end(), i);
+    if (item != old.end()) {
+      overlap.push_back(i);
+    } else {
+      r.added.push_back(i);
+    }
+  }
+
+  std::multiset<Row> overlap_set(overlap.begin(), overlap.end());
+  std::multiset<Row> old_set(old.begin(), old.end());
+  std::set_difference(old_set.begin(),
+                      old_set.end(),
+                      overlap_set.begin(),
+                      overlap_set.end(),
+                      std::back_inserter(r.removed));
+  return r;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// QueryLogItem - the representation of a log result occuring when a
+// scheduled query yields operating system state change.
+/////////////////////////////////////////////////////////////////////////////
+
+Status serializeQueryLogItem(const QueryLogItem& i, pt::ptree& tree) {
+  pt::ptree results_tree;
+  if (i.results.added.size() > 0 || i.results.removed.size() > 0) {
+    auto status = serializeDiffResults(i.results, results_tree);
+    if (!status.ok()) {
+      return status;
+    }
+    tree.add_child("diffResults", results_tree);
+  } else {
+    auto status = serializeQueryData(i.snapshot_results, results_tree);
+    if (!status.ok()) {
+      return status;
+    }
+    tree.add_child("snapshot", results_tree);
+  }
+
+  tree.put<std::string>("name", i.name);
+  tree.put<std::string>("hostIdentifier", i.identifier);
+  tree.put<std::string>("calendarTime", i.calendar_time);
+  tree.put<int>("unixTime", i.time);
+  return Status(0, "OK");
+}
+
+Status serializeQueryLogItemJSON(const QueryLogItem& i, std::string& json) {
+  pt::ptree tree;
+  auto status = serializeQueryLogItem(i, tree);
+  if (!status.ok()) {
+    return status;
+  }
+
+  std::ostringstream output;
+  try {
+    pt::write_json(output, tree, false);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    // The content could not be represented as JSON.
+    return Status(1, e.what());
+  }
+  json = output.str();
+  return Status(0, "OK");
+}
+
+Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item) {
+  if (tree.count("diffResults") > 0) {
+    auto status =
+        deserializeDiffResults(tree.get_child("diffResults"), item.results);
+    if (!status.ok()) {
+      return status;
+    }
+  } else if (tree.count("snapshot") > 0) {
+    auto status =
+        deserializeQueryData(tree.get_child("snapshot"), item.snapshot_results);
+    if (!status.ok()) {
+      return status;
+    }
+  }
+
+  item.name = tree.get<std::string>("name", "");
+  item.identifier = tree.get<std::string>("hostIdentifier", "");
+  item.calendar_time = tree.get<std::string>("calendarTime", "");
+  item.time = tree.get<int>("unixTime", 0);
+  return Status(0, "OK");
+}
+
+Status deserializeQueryLogItemJSON(const std::string& json,
+                                   QueryLogItem& item) {
+  pt::ptree tree;
+  try {
+    std::stringstream input;
+    input << json;
+    pt::read_json(input, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return Status(1, e.what());
+  }
+  return deserializeQueryLogItem(tree, item);
+}
+
+Status serializeEvent(const QueryLogItem& item,
+                      const pt::ptree& event,
+                      pt::ptree& tree) {
+  tree.put<std::string>("name", item.name);
+  tree.put<std::string>("hostIdentifier", item.identifier);
+  tree.put<std::string>("calendarTime", item.calendar_time);
+  tree.put<int>("unixTime", item.time);
+
+  pt::ptree columns;
+  for (auto& i : event) {
+    // Yield results as a "columns." map to avoid namespace collisions.
+    columns.put<std::string>(i.first, i.second.get_value<std::string>());
+  }
+
+  tree.add_child("columns", columns);
+  return Status(0, "OK");
+}
+
+Status serializeQueryLogItemAsEvents(const QueryLogItem& i, pt::ptree& tree) {
+  pt::ptree diff_results;
+  auto status = serializeDiffResults(i.results, diff_results);
+  if (!status.ok()) {
+    return status;
+  }
+
+  for (auto& action : diff_results) {
+    for (auto& row : action.second) {
+      pt::ptree event;
+      serializeEvent(i, row.second, event);
+      event.put<std::string>("action", action.first);
+      tree.push_back(std::make_pair("", event));
+    }
+  }
+  return Status(0, "OK");
+}
+
+Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i,
+                                         std::string& json) {
+  pt::ptree tree;
+  auto status = serializeQueryLogItemAsEvents(i, tree);
+  if (!status.ok()) {
+    return status;
+  }
+
+  std::ostringstream output;
+  for (auto& event : tree) {
+    try {
+      pt::write_json(output, event.second, false);
+    } catch (const pt::json_parser::json_parser_error& e) {
+      return Status(1, e.what());
+    }
+  }
+  json = output.str();
+  return Status(0, "OK");
+}
+
+bool addUniqueRowToQueryData(QueryData& q, const Row& r) {
+  if (std::find(q.begin(), q.end(), r) != q.end()) {
+    return false;
+  }
+  q.push_back(r);
+  return true;
+}
+
+Status DatabasePlugin::call(const PluginRequest& request,
+                            PluginResponse& response) {
+  if (request.count("action") == 0) {
+    return Status(1, "Database plugin must include a request action");
+  }
+
+  // Get a domain/key, which are used for most database plugin actions.
+  auto domain = (request.count("domain") > 0) ? request.at("domain") : "";
+  auto key = (request.count("key") > 0) ? request.at("key") : "";
+
+  // Switch over the possible database plugin actions.
+  if (request.at("action") == "get") {
+    std::string value;
+    auto status = this->get(domain, key, value);
+    response.push_back({{"v", value}});
+    return status;
+  } else if (request.at("action") == "put") {
+    if (request.count("value") == 0) {
+      return Status(1, "Database plugin put action requires a value");
+    }
+    return this->put(domain, key, request.at("value"));
+  } else if (request.at("action") == "remove") {
+    return this->remove(domain, key);
+  } else if (request.at("action") == "scan") {
+    std::vector<std::string> keys;
+    auto status = this->scan(domain, keys);
+    for (const auto& key : keys) {
+      response.push_back({{"k", key}});
+    }
+    return status;
+  }
+
+  return Status(1, "Unknown database plugin action");
+}
+
+Status getDatabaseValue(const std::string& domain,
+                        const std::string& key,
+                        std::string& value) {
+  PluginRequest request = {{"action", "get"}, {"domain", domain}, {"key", key}};
+  PluginResponse response;
+  auto status = Registry::call("database", "rocks", request, response);
+  if (!status.ok()) {
+    VLOG(1) << "Cannot get database " << domain << "/" << key << ": "
+            << status.getMessage();
+    return status;
+  }
+
+  // Set value from the internally-known "v" key.
+  if (response.size() > 0 && response[0].count("v") > 0) {
+    value = response[0].at("v");
+  }
+  return status;
+}
+
+Status setDatabaseValue(const std::string& domain,
+                        const std::string& key,
+                        const std::string& value) {
+  PluginRequest request = {
+      {"action", "put"}, {"domain", domain}, {"key", key}, {"value", value}};
+  return Registry::call("database", "rocks", request);
+}
+
+Status deleteDatabaseValue(const std::string& domain, const std::string& key) {
+  PluginRequest request = {
+      {"action", "remove"}, {"domain", domain}, {"key", key}};
+  return Registry::call("database", "rocks", request);
+}
+
+Status scanDatabaseKeys(const std::string& domain,
+                        std::vector<std::string>& keys) {
+  PluginRequest request = {{"action", "scan"}, {"domain", domain}};
+  PluginResponse response;
+  auto status = Registry::call("database", "rocks", request, response);
+
+  for (const auto& item : response) {
+    if (item.count("k") > 0) {
+      keys.push_back(item.at("k"));
+    }
+  }
+  return status;
+}
+}
diff --git a/src/osquery/database/db_handle.cpp b/src/osquery/database/db_handle.cpp
new file mode 100644 (file)
index 0000000..502af8e
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <algorithm>
+#include <mutex>
+#include <stdexcept>
+
+#include <sys/stat.h>
+
+#include <rocksdb/env.h>
+#include <rocksdb/options.h>
+#include <snappy.h>
+
+#include <osquery/database.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/status.h>
+
+#include "osquery/database/db_handle.h"
+
+namespace osquery {
+
+class RocksDatabasePlugin : public DatabasePlugin {
+ public:
+  /// Data retrieval method.
+  Status get(const std::string& domain,
+             const std::string& key,
+             std::string& value) const;
+
+  /// Data storage method.
+  Status put(const std::string& domain,
+             const std::string& key,
+             const std::string& value);
+
+  /// Data removal method.
+  Status remove(const std::string& domain, const std::string& k);
+
+  /// Key/index lookup method.
+  Status scan(const std::string& domain,
+              std::vector<std::string>& results) const;
+};
+
+/// Backing-storage provider for osquery internal/core.
+REGISTER_INTERNAL(RocksDatabasePlugin, "database", "rocks");
+
+/////////////////////////////////////////////////////////////////////////////
+// Constants
+/////////////////////////////////////////////////////////////////////////////
+
+const std::string kPersistentSettings = "configurations";
+const std::string kQueries = "queries";
+const std::string kEvents = "events";
+const std::string kLogs = "logs";
+
+/**
+ * @brief A const vector of column families in RocksDB
+ *
+ * RocksDB has a concept of "column families" which are kind of like tables
+ * in other databases. kDomainds is populated with a list of all column
+ * families. If a string exists in kDomains, it's a column family in the
+ * database.
+ */
+const std::vector<std::string> kDomains = {
+    kPersistentSettings, kQueries, kEvents, kLogs
+};
+
+CLI_FLAG(string,
+         database_path,
+         "/var/osquery/osquery.db",
+         "If using a disk-based backing store, specify a path");
+FLAG_ALIAS(std::string, db_path, database_path);
+
+CLI_FLAG(bool,
+         database_in_memory,
+         false,
+         "Keep osquery backing-store in memory");
+FLAG_ALIAS(bool, use_in_memory_database, database_in_memory);
+
+/////////////////////////////////////////////////////////////////////////////
+// constructors and destructors
+/////////////////////////////////////////////////////////////////////////////
+
+DBHandle::DBHandle(const std::string& path, bool in_memory) {
+  options_.create_if_missing = true;
+  options_.create_missing_column_families = true;
+  options_.info_log_level = rocksdb::WARN_LEVEL;
+  options_.log_file_time_to_roll = 0;
+  options_.keep_log_file_num = 10;
+  options_.max_log_file_size = 1024 * 1024 * 1;
+  options_.compaction_style = rocksdb::kCompactionStyleLevel;
+  options_.write_buffer_size = 1 * 1024 * 1024;
+  options_.max_write_buffer_number = 2;
+  options_.max_background_compactions = 1;
+
+  if (in_memory) {
+    // Remove when MemEnv is included in librocksdb
+    // options_.env = rocksdb::NewMemEnv(rocksdb::Env::Default());
+    throw std::runtime_error("Requires MemEnv");
+  }
+
+  if (pathExists(path).ok() && !isWritable(path).ok()) {
+    throw std::runtime_error("Cannot write to RocksDB path: " + path);
+  }
+
+  column_families_.push_back(rocksdb::ColumnFamilyDescriptor(
+      rocksdb::kDefaultColumnFamilyName, rocksdb::ColumnFamilyOptions()));
+
+  for (const auto& cf_name : kDomains) {
+    column_families_.push_back(rocksdb::ColumnFamilyDescriptor(
+        cf_name, rocksdb::ColumnFamilyOptions()));
+  }
+
+  VLOG(1) << "Opening RocksDB handle: " << path;
+  auto s = rocksdb::DB::Open(options_, path, column_families_, &handles_, &db_);
+  if (!s.ok()) {
+    throw std::runtime_error(s.ToString());
+  }
+
+  // RocksDB may not create/append a directory with acceptable permissions.
+  if (chmod(path.c_str(), S_IRWXU) != 0) {
+    throw std::runtime_error("Cannot set permissions on RocksDB path: " + path);
+  }
+}
+
+DBHandle::~DBHandle() {
+  for (auto handle : handles_) {
+    delete handle;
+  }
+  delete db_;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// getInstance methods
+/////////////////////////////////////////////////////////////////////////////
+
+DBHandleRef DBHandle::getInstance() {
+  return getInstance(FLAGS_database_path, FLAGS_database_in_memory);
+}
+
+bool DBHandle::checkDB() {
+  try {
+    auto handle = DBHandle(FLAGS_database_path, FLAGS_database_in_memory);
+  } catch (const std::exception& e) {
+    return false;
+  }
+  return true;
+}
+
+DBHandleRef DBHandle::getInstanceInMemory() {
+  return getInstance("", true);
+}
+
+DBHandleRef DBHandle::getInstanceAtPath(const std::string& path) {
+  return getInstance(path, false);
+}
+
+DBHandleRef DBHandle::getInstance(const std::string& path, bool in_memory) {
+  static DBHandleRef db_handle = DBHandleRef(new DBHandle(path, in_memory));
+  return db_handle;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// getters and setters
+/////////////////////////////////////////////////////////////////////////////
+
+rocksdb::DB* DBHandle::getDB() { return db_; }
+
+rocksdb::ColumnFamilyHandle* DBHandle::getHandleForColumnFamily(
+    const std::string& cf) {
+  try {
+    for (int i = 0; i < kDomains.size(); i++) {
+      if (kDomains[i] == cf) {
+        return handles_[i];
+      }
+    }
+  } catch (const std::exception& e) {
+    // pass through and return nullptr
+  }
+  return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Data manipulation methods
+/////////////////////////////////////////////////////////////////////////////
+
+Status DBHandle::Get(const std::string& domain,
+                     const std::string& key,
+                     std::string& value) {
+  auto cfh = getHandleForColumnFamily(domain);
+  if (cfh == nullptr) {
+    return Status(1, "Could not get column family for " + domain);
+  }
+  auto s = getDB()->Get(rocksdb::ReadOptions(), cfh, key, &value);
+  return Status(s.code(), s.ToString());
+}
+
+Status DBHandle::Put(const std::string& domain,
+                     const std::string& key,
+                     const std::string& value) {
+  auto cfh = getHandleForColumnFamily(domain);
+  if (cfh == nullptr) {
+    return Status(1, "Could not get column family for " + domain);
+  }
+  auto s = getDB()->Put(rocksdb::WriteOptions(), cfh, key, value);
+  return Status(s.code(), s.ToString());
+}
+
+Status DBHandle::Delete(const std::string& domain, const std::string& key) {
+  auto cfh = getHandleForColumnFamily(domain);
+  if (cfh == nullptr) {
+    return Status(1, "Could not get column family for " + domain);
+  }
+  auto options = rocksdb::WriteOptions();
+  options.sync = true;
+  auto s = getDB()->Delete(options, cfh, key);
+  return Status(s.code(), s.ToString());
+}
+
+Status DBHandle::Scan(const std::string& domain,
+                      std::vector<std::string>& results) {
+  auto cfh = getHandleForColumnFamily(domain);
+  if (cfh == nullptr) {
+    return Status(1, "Could not get column family for " + domain);
+  }
+  auto it = getDB()->NewIterator(rocksdb::ReadOptions(), cfh);
+  if (it == nullptr) {
+    return Status(1, "Could not get iterator for " + domain);
+  }
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    results.push_back(it->key().ToString());
+  }
+  delete it;
+  return Status(0, "OK");
+}
+
+Status RocksDatabasePlugin::get(const std::string& domain,
+                                const std::string& key,
+                                std::string& value) const {
+  return DBHandle::getInstance()->Get(domain, key, value);
+}
+
+Status RocksDatabasePlugin::put(const std::string& domain,
+                                const std::string& key,
+                                const std::string& value) {
+  return DBHandle::getInstance()->Put(domain, key, value);
+}
+
+Status RocksDatabasePlugin::remove(const std::string& domain,
+                                   const std::string& key) {
+  return DBHandle::getInstance()->Delete(domain, key);
+}
+
+Status RocksDatabasePlugin::scan(const std::string& domain,
+                                 std::vector<std::string>& results) const {
+  return DBHandle::getInstance()->Scan(domain, results);
+}
+}
diff --git a/src/osquery/database/db_handle.h b/src/osquery/database/db_handle.h
new file mode 100644 (file)
index 0000000..26ccf4e
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <rocksdb/db.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <osquery/core.h>
+#include <osquery/flags.h>
+
+namespace osquery {
+
+DECLARE_string(database_path);
+
+class DBHandle;
+typedef std::shared_ptr<DBHandle> DBHandleRef;
+
+/**
+ * @brief RAII singleton around RocksDB database access.
+ *
+ * Accessing RocksDB necessitates creating several pointers which must be
+ * carefully memory managed. DBHandle offers you a singleton which takes
+ * care of acquiring and releasing the relevant pointers and data structures
+ * for you.
+ */
+class DBHandle {
+ public:
+  /// Removes every column family handle and single DB handle/lock.
+  ~DBHandle();
+
+  /**
+   * @brief The primary way to access the DBHandle singleton.
+   *
+   * DBHandle::getInstance() provides access to the DBHandle singleton.
+   *
+   * @code{.cpp}
+   *   auto db = DBHandle::getInstance();
+   *   std::string value;
+   *   auto status = db->Get("default", "foo", value);
+   *   if (status.ok()) {
+   *     assert(value == "bar");
+   *   }
+   * @endcode
+   *
+   * @return a shared pointer to an instance of DBHandle
+   */
+  static DBHandleRef getInstance();
+
+  /**
+   * @brief Check the sanity of the database configuration options
+   *
+   * Create a handle to the backing store using the database configuration.
+   * Catch any instance creation exceptions and release the handle immediately.
+   *
+   * @return Success if a handle was created without error.
+   */
+  static bool checkDB();
+
+ private:
+  /////////////////////////////////////////////////////////////////////////////
+  // Data access methods
+  /////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * @brief Get data from the database
+   *
+   * @param domain the "domain" or "column family" that you'd like to retrieve
+   * the data from
+   * @param key the string key that you'd like to get
+   * @param value a non-const string reference where the result of the
+   * operation will be stored
+   *
+   * @return an instance of osquery::Status indicating the success or failure
+   * of the operation.
+   */
+  Status Get(const std::string& domain,
+             const std::string& key,
+             std::string& value);
+
+  /**
+   * @brief Put data into the database
+   *
+   * @param domain the "domain" or "column family" that you'd like to insert
+   * data into
+   * @param key the string key that you'd like to put
+   * @param value the data that you'd like to put into RocksDB
+   *
+   * @return an instance of osquery::Status indicating the success or failure
+   * of the operation.
+   */
+  Status Put(const std::string& domain,
+             const std::string& key,
+             const std::string& value);
+
+  /**
+   * @brief Delete data from the database
+   *
+   * @param domain the "domain" or "column family" that you'd like to delete
+   * data from
+   * @param key the string key that you'd like to delete
+   *
+   * @return an instance of osquery::Status indicating the success or failure
+   * of the operation.
+   */
+  Status Delete(const std::string& domain, const std::string& key);
+
+  /**
+   * @brief List the data in a "domain"
+   *
+   * @param domain the "domain" or "column family" that you'd like to list
+   * data from
+   * @param results a non-const reference to a vector which will be populated
+   * with all of the keys from the supplied domain.
+   *
+   * @return an instance of osquery::Status indicating the success or failure
+   * of the operation.
+   */
+  Status Scan(const std::string& domain, std::vector<std::string>& results);
+
+ private:
+  /**
+   * @brief Default constructor
+   *
+   * DBHandle's constructor takes care of properly connecting to RocksDB and
+   * ensuring that all necessary column families are created. The resulting
+   * database handle can then be accessed via DBHandle::getDB() and the
+   * success of the connection can be determined by inspecting the resulting
+   * status code via DBHandle::getStatus()
+   */
+  DBHandle();
+
+  /**
+   * @brief Internal only constructor used to create instances of DBHandle.
+   *
+   * This constructor allows you to specify a few more details about how you'd
+   * like DBHandle to be used. This is only used internally, so you should
+   * never actually use it.
+   *
+   * @param path the path to create/access the database
+   * @param in_memory a boolean indicating whether or not the database should
+   * be creating in memory or not.
+   */
+  DBHandle(const std::string& path, bool in_memory);
+
+  /**
+   * @brief A method which allows you to override the database path
+   *
+   * This should only be used by unit tests. Never use it in production code.
+   *
+   * @return a shared pointer to an instance of DBHandle
+   */
+  static DBHandleRef getInstanceAtPath(const std::string& path);
+
+  /**
+   * @brief A method which gets you an in-memory RocksDB instance.
+   *
+   * This should only be used by unit tests. Never use it in production code.
+   *
+   * @return a shared pointer to an instance of DBHandle
+   */
+  static DBHandleRef getInstanceInMemory();
+
+  /**
+   * @brief A method which allows you to configure various aspects of RocksDB
+   * database options.
+   *
+   * This should only be used by unit tests. Never use it in production code.
+   *
+   * @param path the path to create/access the database
+   * @param in_memory a boolean indicating whether or not the database should
+   * be creating in memory or not.
+   *
+   * @return a shared pointer to an instance of DBHandle
+   */
+  static DBHandleRef getInstance(const std::string& path, bool in_memory);
+
+  /**
+   * @brief Private helper around accessing the column family handle for a
+   * specific column family, based on it's name
+   */
+  rocksdb::ColumnFamilyHandle* getHandleForColumnFamily(const std::string& cf);
+
+  /**
+   * @brief Helper method which can be used to get a raw pointer to the
+   * underlying RocksDB database handle
+   *
+   * You probably shouldn't use this. DBHandle::getDB() should only be used
+   * when you're positive that it's the right thing to use.
+   *
+   * @return a pointer to the underlying RocksDB database handle
+   */
+  rocksdb::DB* getDB();
+
+ private:
+  /////////////////////////////////////////////////////////////////////////////
+  // Private members
+  /////////////////////////////////////////////////////////////////////////////
+
+  /// The database handle
+  rocksdb::DB* db_;
+
+  /// Column family descriptors which are used to connect to RocksDB
+  std::vector<rocksdb::ColumnFamilyDescriptor> column_families_;
+
+  /// A vector of pointers to column family handles
+  std::vector<rocksdb::ColumnFamilyHandle*> handles_;
+
+  /// The RocksDB connection options that are used to connect to RocksDB
+  rocksdb::Options options_;
+
+ private:
+  friend class RocksDatabasePlugin;
+  friend class Query;
+  friend class EventSubscriberPlugin;
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Unit tests which can access private members
+  /////////////////////////////////////////////////////////////////////////////
+
+  friend class DBHandleTests;
+  FRIEND_TEST(DBHandleTests, test_get);
+  FRIEND_TEST(DBHandleTests, test_put);
+  FRIEND_TEST(DBHandleTests, test_delete);
+  FRIEND_TEST(DBHandleTests, test_scan);
+  friend class QueryTests;
+  FRIEND_TEST(QueryTests, test_get_query_results);
+  FRIEND_TEST(QueryTests, test_is_query_name_in_database);
+  FRIEND_TEST(QueryTests, test_get_stored_query_names);
+  friend class EventsTests;
+  friend class EventsDatabaseTests;
+};
+}
diff --git a/src/osquery/database/query.cpp b/src/osquery/database/query.cpp
new file mode 100644 (file)
index 0000000..360a329
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <algorithm>
+
+#include "osquery/database/query.h"
+
+namespace osquery {
+
+/////////////////////////////////////////////////////////////////////////////
+// Getters and setters
+/////////////////////////////////////////////////////////////////////////////
+
+std::string Query::getQuery() { return query_.query; }
+
+std::string Query::getQueryName() { return name_; }
+
+int Query::getInterval() { return query_.interval; }
+
+/////////////////////////////////////////////////////////////////////////////
+// Data access methods
+/////////////////////////////////////////////////////////////////////////////
+
+Status Query::getPreviousQueryResults(QueryData& results) {
+  return getPreviousQueryResults(results, DBHandle::getInstance());
+}
+
+Status Query::getPreviousQueryResults(QueryData& results, DBHandleRef db) {
+  if (!isQueryNameInDatabase()) {
+    return Status(0, "Query name not found in database");
+  }
+
+  std::string raw;
+  auto status = db->Get(kQueries, name_, raw);
+  if (!status.ok()) {
+    return status;
+  }
+
+  status = deserializeQueryDataJSON(raw, results);
+  if (!status.ok()) {
+    return status;
+  }
+  return Status(0, "OK");
+}
+
+std::vector<std::string> Query::getStoredQueryNames() {
+  return getStoredQueryNames(DBHandle::getInstance());
+}
+
+std::vector<std::string> Query::getStoredQueryNames(DBHandleRef db) {
+  std::vector<std::string> results;
+  db->Scan(kQueries, results);
+  return results;
+}
+
+bool Query::isQueryNameInDatabase() {
+  return isQueryNameInDatabase(DBHandle::getInstance());
+}
+
+bool Query::isQueryNameInDatabase(DBHandleRef db) {
+  auto names = Query::getStoredQueryNames(db);
+  return std::find(names.begin(), names.end(), name_) != names.end();
+}
+
+Status Query::addNewResults(const osquery::QueryData& qd) {
+  return addNewResults(qd, DBHandle::getInstance());
+}
+
+Status Query::addNewResults(const QueryData& qd, DBHandleRef db) {
+  DiffResults dr;
+  return addNewResults(qd, dr, false, db);
+}
+
+Status Query::addNewResults(const QueryData& qd, DiffResults& dr) {
+  return addNewResults(qd, dr, true, DBHandle::getInstance());
+}
+
+Status Query::addNewResults(const QueryData& current_qd,
+                            DiffResults& dr,
+                            bool calculate_diff,
+                            DBHandleRef db) {
+  // Get the rows from the last run of this query name.
+  QueryData previous_qd;
+  auto status = getPreviousQueryResults(previous_qd);
+  if (!status.ok()) {
+    return status;
+  }
+
+  // Sanitize all non-ASCII characters from the query data values.
+  QueryData escaped_current_qd;
+  escapeQueryData(current_qd, escaped_current_qd);
+  // Calculate the differential between previous and current query results.
+  if (calculate_diff) {
+    dr = diff(previous_qd, escaped_current_qd);
+  }
+
+  // Replace the "previous" query data with the current.
+  std::string json;
+  status = serializeQueryDataJSON(escaped_current_qd, json);
+  if (!status.ok()) {
+    return status;
+  }
+
+  status = db->Put(kQueries, name_, json);
+  if (!status.ok()) {
+    return status;
+  }
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/database/query.h b/src/osquery/database/query.h
new file mode 100644 (file)
index 0000000..d1852ec
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <osquery/status.h>
+#include <osquery/database.h>
+
+#include "osquery/database/db_handle.h"
+
+namespace osquery {
+
+/// Error message used when a query name isn't found in the database
+extern const std::string kQueryNameNotFoundError;
+
+/**
+ * @brief A class that is used to interact with the historical on-disk storage
+ * for a given query.
+ */
+class Query {
+ public:
+  /**
+   * @brief Constructor which sets up necessary parameters of a Query object
+   *
+   * Given a query, this constructor calculates the value of columnFamily_,
+   * which can be accessed via the getColumnFamilyName getter method.
+   *
+   * @param q a SheduledQuery struct
+   */
+  explicit Query(const std::string& name, const ScheduledQuery& q)
+      : query_(q), name_(name) {}
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Getters and setters
+  /////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * @brief Getter for the name of a given scheduled query
+   *
+   * @return the name of the scheduled query which is being operated on
+   */
+  std::string getQueryName();
+
+  /**
+   * @brief Getter for the SQL query of a scheduled query
+   *
+   * @return the SQL of the scheduled query which is being operated on
+   */
+  std::string getQuery();
+
+  /**
+   * @brief Getter for the interval of a scheduled query
+   *
+   * @return the interval of the scheduled query which is being operated on
+   */
+  int getInterval();
+
+  /////////////////////////////////////////////////////////////////////////////
+  // Data access methods
+  /////////////////////////////////////////////////////////////////////////////
+
+ public:
+  /**
+   * @brief Serialize the data in RocksDB into a useful data structure
+   *
+   * This method retrieves the data from RocksDB and returns the data in a
+   * HistoricalQueryResults struct.
+   *
+   * @param hQR the output HistoricalQueryResults struct
+   *
+   * @return the success or failure of the operation
+   */
+  // Status getHistoricalQueryResults(HistoricalQueryResults& hQR);
+  Status getPreviousQueryResults(QueryData& results);
+
+ private:
+  /**
+   * @brief Serialize the data in RocksDB into a useful data structure using a
+   * custom database handle
+   *
+   * This method is the same as getHistoricalQueryResults, but with the
+   * addition of a parameter which allows you to pass a custom RocksDB
+   * database handle.
+   *
+   * @param hQR the output HistoricalQueryResults struct
+   * @param db a shared pointer to a custom DBHandle
+   *
+   * @return the success or failure of the operation
+   * @see getHistoricalQueryResults
+   */
+  // Status getHistoricalQueryResults(HistoricalQueryResults& hQR,
+  //                                 std::shared_ptr<DBHandle> db);
+  Status getPreviousQueryResults(QueryData& results, DBHandleRef db);
+
+ public:
+  /**
+   * @brief Get the names of all historical queries that are stored in RocksDB
+   *
+   * If you'd like to perform some database maintenance, getStoredQueryNames()
+   * allows you to get a vector of the names of all queries which are
+   * currently stored in RocksDB
+   *
+   * @return a vector containing the string names of all scheduled queries
+   * which currently exist in the database
+   */
+  static std::vector<std::string> getStoredQueryNames();
+
+ private:
+  /**
+   * @brief Get the names of all historical queries that are stored in RocksDB
+   * using a custom database handle
+   *
+   * This method is the same as getStoredQueryNames(), but with the addition
+   * of a parameter which allows you to pass a custom RocksDB database handle.
+   *
+   * @param db a custom RocksDB database handle
+   *
+   * @return a vector containing the string names of all scheduled queries
+   *
+   * @see getStoredQueryNames()
+   */
+  static std::vector<std::string> getStoredQueryNames(DBHandleRef db);
+
+ public:
+  /**
+   * @brief Accessor method for checking if a given scheduled query exists in
+   * the database
+   *
+   * @return does the scheduled query which is already exists in the database
+   */
+  bool isQueryNameInDatabase();
+
+ private:
+  /**
+   * @brief Accessor method for checking if a given scheduled query exists in
+   * the database, using a custom database handle
+   *
+   * This method is the same as isQueryNameInDatabase(), but with the addition
+   * of a parameter which allows you to pass a custom RocksDB database handle
+   *
+   * @param db a custom RocksDB database handle
+   *
+   * @return does the scheduled query which is already exists in the database
+   */
+  bool isQueryNameInDatabase(DBHandleRef db);
+
+ public:
+  /**
+   * @brief Add a new set of results to the persistant storage
+   *
+   * Given the results of the execution of a scheduled query, add the results
+   * to the database using addNewResults
+   *
+   * @param qd the QueryData object, which has the results of the query which
+   * you would like to store
+   * @param unix_time the time that the query was executed
+   *
+   * @return an instance of osquery::Status indicating the success or failure
+   * of the operation
+   */
+  Status addNewResults(const QueryData& qd);
+
+ private:
+  /**
+   * @brief Add a new set of results to the persistant storage using a custom
+   * database handle
+   *
+   * This method is the same as addNewResults(), but with the addition of a
+   * parameter which allows you to pass a custom RocksDB database handle
+   *
+   * @param qd the QueryData object, which has the results of the query which
+   * you would like to store
+   * @param unix_time the time that the query was executed
+   * @param db a custom RocksDB database handle
+   *
+   * @return an instance of osquery::Status indicating the success or failure
+   * of the operation
+   */
+  Status addNewResults(const QueryData& qd, DBHandleRef db);
+
+ public:
+  /**
+   * @brief Add a new set of results to the persistent storage and get back
+   * the differential results.
+   *
+   * Given the results of an execution of a scheduled query, add the results
+   * to the database using addNewResults and get back a data structure
+   * indicating what rows in the query's results have changed.
+   *
+   * @param qd the QueryData object containing query results to store
+   * @param dr an output to a DiffResults object populated based on last run
+   *
+   * @return the success or failure of the operation
+   */
+  Status addNewResults(const QueryData& qd, DiffResults& dr);
+
+ private:
+  /**
+   * @brief Add a new set of results to the persistent storage and get back
+   * the differential results, using a custom database handle.
+   *
+   * This method is the same as Query::addNewResults, but with the addition of a
+   * parameter which allows you to pass a custom RocksDB database handle
+   *
+   * @param qd the QueryData object containing query results to store
+   * @param dr an output to a DiffResults object populated based on last run
+   *
+   * @return the success or failure of the operation
+   */
+  Status addNewResults(const QueryData& qd,
+                       DiffResults& dr,
+                       bool calculate_diff,
+                       DBHandleRef db);
+
+ public:
+  /**
+   * @brief A getter for the most recent result set for a scheduled query
+   *
+   * @param qd the output QueryData object
+   *
+   * @return the success or failure of the operation
+   */
+  Status getCurrentResults(QueryData& qd);
+
+ private:
+  /**
+   * @brief A getter for the most recent result set for a scheduled query,
+   * but with the addition of a parameter which allows you to pass a custom
+   * RocksDB database handle.
+   *
+   * This method is the same as Query::getCurrentResults, but with addition of a
+   * parameter which allows you to pass a custom RocksDB database handle.
+   *
+   * @param qd the output QueryData object
+   * @param db a custom RocksDB database handle
+   *
+   * @return the success or failure of the operation
+   */
+  Status getCurrentResults(QueryData& qd, DBHandleRef db);
+
+ private:
+  /////////////////////////////////////////////////////////////////////////////
+  // Private members
+  /////////////////////////////////////////////////////////////////////////////
+
+  /// The scheduled query and internal
+  ScheduledQuery query_;
+  /// The scheduled query name.
+  std::string name_;
+
+ private:
+  /////////////////////////////////////////////////////////////////////////////
+  // Unit tests which can access private members
+  /////////////////////////////////////////////////////////////////////////////
+
+  FRIEND_TEST(QueryTests, test_private_members);
+  FRIEND_TEST(QueryTests, test_add_and_get_current_results);
+  FRIEND_TEST(QueryTests, test_is_query_name_in_database);
+  FRIEND_TEST(QueryTests, test_get_stored_query_names);
+  FRIEND_TEST(QueryTests, test_get_executions);
+  FRIEND_TEST(QueryTests, test_get_query_results);
+  FRIEND_TEST(QueryTests, test_query_name_not_found_in_db);
+};
+}
diff --git a/src/osquery/database/tests/db_handle_tests.cpp b/src/osquery/database/tests/db_handle_tests.cpp
new file mode 100644 (file)
index 0000000..8c3e07b
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <algorithm>
+
+#include <boost/filesystem/operations.hpp>
+
+#include <gtest/gtest.h>
+
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/database/db_handle.h"
+
+const std::string kTestingDBHandlePath = "/tmp/rocksdb-osquery-dbhandletests";
+
+namespace osquery {
+
+class DBHandleTests : public testing::Test {
+ public:
+  void SetUp() {
+    // Setup a testing DB instance
+    db = DBHandle::getInstanceAtPath(kTestingDBHandlePath);
+    cfh_queries = DBHandle::getInstance()->getHandleForColumnFamily(kQueries);
+    cfh_foobar =
+        DBHandle::getInstance()->getHandleForColumnFamily("foobartest");
+  }
+
+  void TearDown() { boost::filesystem::remove_all(kTestingDBHandlePath); }
+
+ public:
+  rocksdb::ColumnFamilyHandle* cfh_queries;
+  rocksdb::ColumnFamilyHandle* cfh_foobar;
+  std::shared_ptr<DBHandle> db;
+};
+
+TEST_F(DBHandleTests, test_singleton_on_disk) {
+  auto db1 = DBHandle::getInstance();
+  auto db2 = DBHandle::getInstance();
+  EXPECT_EQ(db1, db2);
+}
+
+TEST_F(DBHandleTests, test_get_handle_for_column_family) {
+  ASSERT_TRUE(cfh_queries != nullptr);
+  ASSERT_TRUE(cfh_foobar == nullptr);
+}
+
+TEST_F(DBHandleTests, test_get) {
+  db->getDB()->Put(
+      rocksdb::WriteOptions(), cfh_queries, "test_query_123", "{}");
+  std::string r;
+  std::string key = "test_query_123";
+  auto s = db->Get(kQueries, key, r);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(r, "{}");
+}
+
+TEST_F(DBHandleTests, test_put) {
+  auto s = db->Put(kQueries, "test_put", "bar");
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+}
+
+TEST_F(DBHandleTests, test_delete) {
+  db->Put(kQueries, "test_delete", "baz");
+  auto s = db->Delete(kQueries, "test_delete");
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+}
+
+TEST_F(DBHandleTests, test_scan) {
+  db->Put(kQueries, "test_scan_foo1", "baz");
+  db->Put(kQueries, "test_scan_foo2", "baz");
+  db->Put(kQueries, "test_scan_foo3", "baz");
+  std::vector<std::string> keys;
+  std::vector<std::string> expected = {
+      "test_scan_foo1", "test_scan_foo2", "test_scan_foo3"};
+  auto s = db->Scan(kQueries, keys);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  for (const auto& i : expected) {
+    EXPECT_NE(std::find(keys.begin(), keys.end(), i), keys.end());
+  }
+}
+}
diff --git a/src/osquery/database/tests/query_tests.cpp b/src/osquery/database/tests/query_tests.cpp
new file mode 100644 (file)
index 0000000..0142f27
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <algorithm>
+#include <ctime>
+#include <deque>
+
+#include <boost/filesystem/operations.hpp>
+
+#include <gtest/gtest.h>
+
+#include "osquery/core/test_util.h"
+#include "osquery/database/query.h"
+
+const std::string kTestingQueryDBPath = "/tmp/rocksdb-osquery-querytests";
+
+namespace osquery {
+
+class QueryTests : public testing::Test {
+ public:
+  void SetUp() { db_ = DBHandle::getInstanceAtPath(kTestingQueryDBPath); }
+  void TearDown() { boost::filesystem::remove_all(kTestingQueryDBPath); }
+
+ public:
+  std::shared_ptr<DBHandle> db_;
+};
+
+TEST_F(QueryTests, test_get_column_family_name) {
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("foobar", query);
+  EXPECT_EQ(cf.getQueryName(), "foobar");
+}
+
+TEST_F(QueryTests, test_get_query) {
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("foobar", query);
+  EXPECT_EQ(cf.getQuery(), query.query);
+}
+
+TEST_F(QueryTests, test_get_interval) {
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("foobar", query);
+  EXPECT_EQ(cf.getInterval(), query.interval);
+}
+
+TEST_F(QueryTests, test_private_members) {
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("foobar", query);
+  EXPECT_EQ(cf.query_, query);
+}
+
+TEST_F(QueryTests, test_add_and_get_current_results) {
+  // Test adding a "current" set of results to a scheduled query instance.
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("foobar", query);
+  auto status = cf.addNewResults(getTestDBExpectedResults(), db_);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(status.toString(), "OK");
+
+  // Simulate results from several schedule runs, calculate differentials.
+  for (auto result : getTestDBResultStream()) {
+    // Get the results from the previous query execution (from RocksDB).
+    QueryData previous_qd;
+    auto status = cf.getPreviousQueryResults(previous_qd, db_);
+    EXPECT_TRUE(status.ok());
+    EXPECT_EQ(status.toString(), "OK");
+
+    // Add the "current" results and output the differentials.
+    DiffResults dr;
+    auto s = cf.addNewResults(result.second, dr, true, db_);
+    EXPECT_TRUE(s.ok());
+
+    // Call the diffing utility directly.
+    DiffResults expected = diff(previous_qd, result.second);
+    EXPECT_EQ(dr, expected);
+
+    // After Query::addNewResults the previous results are now current.
+    QueryData qd;
+    cf.getPreviousQueryResults(qd, db_);
+    EXPECT_EQ(qd, result.second);
+  }
+}
+
+TEST_F(QueryTests, test_get_query_results) {
+  // Grab an expected set of query data and add it as the previous result.
+  auto encoded_qd = getSerializedQueryDataJSON();
+  auto query = getOsqueryScheduledQuery();
+  auto status = db_->Put(kQueries, "foobar", encoded_qd.first);
+  EXPECT_TRUE(status.ok());
+
+  // Use the Query retrieval API to check the now "previous" result.
+  QueryData previous_qd;
+  auto cf = Query("foobar", query);
+  status = cf.getPreviousQueryResults(previous_qd, db_);
+  EXPECT_TRUE(status.ok());
+}
+
+TEST_F(QueryTests, test_query_name_not_found_in_db) {
+  // Try to retrieve results from a query that has not executed.
+  QueryData previous_qd;
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("not_a_real_query", query);
+  auto status = cf.getPreviousQueryResults(previous_qd, db_);
+  EXPECT_TRUE(status.toString() == "Query name not found in database");
+  EXPECT_TRUE(status.ok());
+}
+
+TEST_F(QueryTests, test_is_query_name_in_database) {
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("foobar", query);
+  auto encoded_qd = getSerializedQueryDataJSON();
+  auto status = db_->Put(kQueries, "foobar", encoded_qd.first);
+  EXPECT_TRUE(status.ok());
+  // Now test that the query name exists.
+  EXPECT_TRUE(cf.isQueryNameInDatabase(db_));
+}
+
+TEST_F(QueryTests, test_get_stored_query_names) {
+  auto query = getOsqueryScheduledQuery();
+  auto cf = Query("foobar", query);
+  auto encoded_qd = getSerializedQueryDataJSON();
+  auto status = db_->Put(kQueries, "foobar", encoded_qd.first);
+  EXPECT_TRUE(status.ok());
+
+  // Stored query names is a factory method included alongside every query.
+  // It will include the set of query names with existing "previous" results.
+  auto names = cf.getStoredQueryNames(db_);
+  auto in_vector = std::find(names.begin(), names.end(), "foobar");
+  EXPECT_NE(in_vector, names.end());
+}
+}
diff --git a/src/osquery/database/tests/results_tests.cpp b/src/osquery/database/tests/results_tests.cpp
new file mode 100644 (file)
index 0000000..2c16d01
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <osquery/database.h>
+#include <osquery/logger.h>
+
+#include "osquery/core/test_util.h"
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+class ResultsTests : public testing::Test {};
+std::string escapeNonPrintableBytes(const std::string& data);
+
+TEST_F(ResultsTests, test_simple_diff) {
+  QueryData o;
+  QueryData n;
+
+  Row r1;
+  r1["foo"] = "bar";
+  n.push_back(r1);
+
+  auto results = diff(o, n);
+  EXPECT_EQ(results.added, n);
+  EXPECT_EQ(results.removed, o);
+}
+
+TEST_F(ResultsTests, test_serialize_row) {
+  auto results = getSerializedRow();
+  pt::ptree tree;
+  auto s = serializeRow(results.second, tree);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(results.first, tree);
+}
+
+TEST_F(ResultsTests, test_deserialize_row_json) {
+  auto results = getSerializedRow();
+  std::string input;
+  serializeRowJSON(results.second, input);
+
+  // Pull the serialized JSON back into a Row output container.
+  Row output;
+  auto s = deserializeRowJSON(input, output);
+  EXPECT_TRUE(s.ok());
+  // The output container should match the input row.
+  EXPECT_EQ(output, results.second);
+}
+
+TEST_F(ResultsTests, test_serialize_query_data) {
+  auto results = getSerializedQueryData();
+  pt::ptree tree;
+  auto s = serializeQueryData(results.second, tree);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(results.first, tree);
+}
+
+TEST_F(ResultsTests, test_serialize_query_data_json) {
+  auto results = getSerializedQueryDataJSON();
+  std::string json;
+  auto s = serializeQueryDataJSON(results.second, json);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(results.first, json);
+}
+
+TEST_F(ResultsTests, test_deserialize_query_data_json) {
+  auto results = getSerializedQueryDataJSON();
+
+  // Pull the serialized JSON back into a QueryData output container.
+  QueryData output;
+  auto s = deserializeQueryDataJSON(results.first, output);
+  EXPECT_TRUE(s.ok());
+  // The output container should match the input query data.
+  EXPECT_EQ(output, results.second);
+}
+
+TEST_F(ResultsTests, test_serialize_diff_results) {
+  auto results = getSerializedDiffResults();
+  pt::ptree tree;
+  auto s = serializeDiffResults(results.second, tree);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(results.first, tree);
+}
+
+TEST_F(ResultsTests, test_serialize_diff_results_json) {
+  auto results = getSerializedDiffResultsJSON();
+  std::string json;
+  auto s = serializeDiffResultsJSON(results.second, json);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(results.first, json);
+}
+
+TEST_F(ResultsTests, test_serialize_query_log_item) {
+  auto results = getSerializedQueryLogItem();
+  pt::ptree tree;
+  auto s = serializeQueryLogItem(results.second, tree);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(results.first, tree);
+}
+
+TEST_F(ResultsTests, test_serialize_query_log_item_json) {
+  auto results = getSerializedQueryLogItemJSON();
+  std::string json;
+  auto s = serializeQueryLogItemJSON(results.second, json);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(results.first, json);
+}
+
+TEST_F(ResultsTests, test_deserialize_query_log_item_json) {
+  auto results = getSerializedQueryLogItemJSON();
+
+  // Pull the serialized JSON back into a QueryLogItem output container.
+  QueryLogItem output;
+  auto s = deserializeQueryLogItemJSON(results.first, output);
+  EXPECT_TRUE(s.ok());
+  // The output container should match the input query data.
+  EXPECT_EQ(output, results.second);
+}
+
+TEST_F(ResultsTests, test_unicode_to_ascii_conversion) {
+  EXPECT_EQ(escapeNonPrintableBytes("しかたがない"),
+            "\\xE3\\x81\\x97\\xE3\\x81\\x8B\\xE3\\x81\\x9F\\xE3\\x81\\x8C\\xE3"
+            "\\x81\\xAA\\xE3\\x81\\x84");
+  EXPECT_EQ(escapeNonPrintableBytes("悪因悪果"),
+            "\\xE6\\x82\\xAA\\xE5\\x9B\\xA0\\xE6\\x82\\xAA\\xE6\\x9E\\x9C");
+  EXPECT_EQ(escapeNonPrintableBytes("モンスターハンター"),
+            "\\xE3\\x83\\xA2\\xE3\\x83\\xB3\\xE3\\x82\\xB9\\xE3\\x82\\xBF\\xE3"
+            "\\x83\\xBC\\xE3\\x83\\x8F\\xE3\\x83\\xB3\\xE3\\x82\\xBF\\xE3\\x83"
+            "\\xBC");
+  EXPECT_EQ(
+      escapeNonPrintableBytes(
+          "съешь же ещё этих мягких французских булок, да выпей чаю"),
+      "\\xD1\\x81\\xD1\\x8A\\xD0\\xB5\\xD1\\x88\\xD1\\x8C \\xD0\\xB6\\xD0\\xB5 "
+      "\\xD0\\xB5\\xD1\\x89\\xD1\\x91 \\xD1\\x8D\\xD1\\x82\\xD0\\xB8\\xD1\\x85 "
+      "\\xD0\\xBC\\xD1\\x8F\\xD0\\xB3\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
+      "\\xD1\\x84\\xD1\\x80\\xD0\\xB0\\xD0\\xBD\\xD1\\x86\\xD1\\x83\\xD0\\xB7\\"
+      "xD1\\x81\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 "
+      "\\xD0\\xB1\\xD1\\x83\\xD0\\xBB\\xD0\\xBE\\xD0\\xBA, "
+      "\\xD0\\xB4\\xD0\\xB0 \\xD0\\xB2\\xD1\\x8B\\xD0\\xBF\\xD0\\xB5\\xD0\\xB9 "
+      "\\xD1\\x87\\xD0\\xB0\\xD1\\x8E");
+
+  EXPECT_EQ(
+      escapeNonPrintableBytes("The quick brown fox jumps over the lazy dog."),
+      "The quick brown fox jumps over the lazy dog.");
+}
+
+TEST_F(ResultsTests, test_adding_duplicate_rows_to_query_data) {
+  Row r1, r2, r3;
+  r1["foo"] = "bar";
+  r1["baz"] = "boo";
+
+  r2["foo"] = "baz";
+  r2["baz"] = "bop";
+
+  r3["foo"] = "baz";
+  r3["baz"] = "bop";
+
+  QueryData q;
+  bool s;
+
+  s = addUniqueRowToQueryData(q, r1);
+  EXPECT_TRUE(s);
+  EXPECT_EQ(q.size(), 1);
+
+  s = addUniqueRowToQueryData(q, r2);
+  EXPECT_TRUE(s);
+  EXPECT_EQ(q.size(), 2);
+
+  s = addUniqueRowToQueryData(q, r3);
+  EXPECT_FALSE(s);
+  EXPECT_EQ(q.size(), 2);
+}
+}
diff --git a/src/osquery/devtools/CMakeLists.txt b/src/osquery/devtools/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0d916dc
--- /dev/null
@@ -0,0 +1,18 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_devtools printer.cpp)
+
+FILE(GLOB OSQUERY_DEVTOOLS_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_DEVTOOLS_TESTS})
diff --git a/src/osquery/devtools/devtools.h b/src/osquery/devtools/devtools.h
new file mode 100644 (file)
index 0000000..54917ee
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <string>
+
+#include <osquery/database.h>
+#include <osquery/flags.h>
+
+namespace osquery {
+
+/// Show all tables and exit the shell.
+DECLARE_bool(L);
+/// Select all from a table an exit the shell.
+DECLARE_string(A);
+/// The shell may need to disable events for fast operations.
+DECLARE_bool(disable_events);
+
+/**
+ * @brief Run an interactive SQL query shell.
+ *
+ * @code{.cpp}
+ *   // Copyright 2004-present Facebook. All Rights Reserved.
+ *   #include <osquery/core.h>
+ *   #include <osquery/devtools.h>
+ *
+ *   int main(int argc, char *argv[]) {
+ *     osquery::initOsquery(argc, argv);
+ *     return osquery::launchIntoShell(argc, argv);
+ *   }
+ * @endcode
+ *
+ * @param argc the number of elements in argv
+ * @param argv the command-line flags
+ *
+ * @return an int which represents the "return code"
+ */
+int launchIntoShell(int argc, char** argv);
+
+/**
+ * @brief Pretty print a QueryData object
+ *
+ * This is a helper method which called osquery::beautify on the supplied
+ * QueryData object and prints the results to stdout.
+ *
+ * @param results The QueryData object to print
+ * @param columns The order of the keys (since maps are unordered)
+ * @param lengths A mutable set of column lengths
+ */
+void prettyPrint(const QueryData& results,
+                 const std::vector<std::string>& columns,
+                 std::map<std::string, size_t>& lengths);
+
+/**
+ * @brief JSON print a QueryData object
+ *
+ * This is a helper method which allows a shell or other tool to print results
+ * in a JSON format.
+ *
+ * @param q The QueryData object to print
+ */
+void jsonPrint(const QueryData& q);
+
+/**
+ * @brief Compute a map of metadata about the supplied QueryData object
+ *
+ * @param r A row to analyze
+ * @param lengths A mutable set of column lengths
+ * @param use_columns Calulate lengths of column names or values
+ *
+ * @return A map of string to int such that the key represents the "column" in
+ * the supplied QueryData and the int represents the length of the longest key
+ */
+void computeRowLengths(const Row& r,
+                       std::map<std::string, size_t>& lengths,
+                       bool use_columns = false);
+
+/**
+ * @brief Generate the separator string for query results
+ *
+ * @param lengths The data returned from computeQueryDataLengths
+ * @param columns The order of the keys (since maps are unordered)
+ *
+ * @return A string, with a newline, representing your separator
+ */
+std::string generateToken(const std::map<std::string, size_t>& lengths,
+                          const std::vector<std::string>& columns);
+
+/**
+ * @brief Generate the header string for query results
+ *
+ * @param lengths The data returned from computeQueryDataLengths
+ * @param columns The order of the keys (since maps are unordered)
+ *
+ * @return A string, with a newline, representing your header
+ */
+std::string generateHeader(const std::map<std::string, size_t>& lengths,
+                           const std::vector<std::string>& columns);
+
+/**
+ * @brief Generate a row string for query results
+ *
+ * @param r A row to analyze
+ * @param lengths The data returned from computeQueryDataLengths
+ * @param columns The order of the keys (since maps are unordered)
+ *
+ * @return A string, with a newline, representing your row
+ */
+std::string generateRow(const Row& r,
+                        const std::map<std::string, size_t>& lengths,
+                        const std::vector<std::string>& columns);
+}
diff --git a/src/osquery/devtools/printer.cpp b/src/osquery/devtools/printer.cpp
new file mode 100644 (file)
index 0000000..380d2ca
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <iostream>
+#include <sstream>
+
+#include <osquery/core.h>
+
+#include "osquery/devtools/devtools.h"
+
+namespace osquery {
+
+static std::vector<size_t> kOffset = {0, 0};
+static std::string kToken = "|";
+
+std::string generateToken(const std::map<std::string, size_t>& lengths,
+                          const std::vector<std::string>& columns) {
+  std::string out = "+";
+  for (const auto& col : columns) {
+    if (lengths.count(col) > 0) {
+      if (getenv("ENHANCE") != nullptr) {
+        std::string e = "\xF0\x9F\x90\x8C";
+        e[2] += kOffset[1];
+        e[3] += kOffset[0];
+        for (int i = 0; i < lengths.at(col) + 2; i++) {
+          e[3] = '\x8c' + kOffset[0]++;
+          if (e[3] == '\xbf') {
+            e[3] = '\x80';
+            kOffset[1] = (kOffset[1] > 3 && kOffset[1] < 8) ? 9 : kOffset[1];
+            e[2] = '\x90' + ++kOffset[1];
+            kOffset[0] = 0;
+          }
+          if (kOffset[1] == ('\x97' - '\x8d')) {
+            kOffset = {0, 0};
+          }
+          out += e.c_str();
+        }
+      } else {
+        out += std::string(lengths.at(col) + 2, '-');
+      }
+    }
+    out += "+";
+  }
+
+  out += "\n";
+  return out;
+}
+
+std::string generateHeader(const std::map<std::string, size_t>& lengths,
+                           const std::vector<std::string>& columns) {
+  if (getenv("ENHANCE") != nullptr) {
+    kToken = "\xF0\x9F\x91\x8D";
+  }
+  std::string out = kToken;
+  for (const auto& col : columns) {
+    out += " " + col;
+    if (lengths.count(col) > 0) {
+      int buffer_size = lengths.at(col) - utf8StringSize(col) + 1;
+      if (buffer_size > 0) {
+        out += std::string(buffer_size, ' ');
+      } else {
+        out += ' ';
+      }
+    }
+    out += kToken;
+  }
+  out += "\n";
+  return out;
+}
+
+std::string generateRow(const Row& r,
+                        const std::map<std::string, size_t>& lengths,
+                        const std::vector<std::string>& order) {
+  std::string out;
+  for (const auto& column : order) {
+    if (r.count(column) == 0 || lengths.count(column) == 0) {
+      continue;
+    }
+    // Print a terminator for the previous value or lhs, followed by spaces.
+
+    int buffer_size = lengths.at(column) - utf8StringSize(r.at(column)) + 1;
+    if (buffer_size > 0) {
+      out += kToken + " " + r.at(column) + std::string(buffer_size, ' ');
+    }
+  }
+
+  if (out.size() > 0) {
+    // Only append if a row was added.
+    out += kToken + "\n";
+  }
+
+  return out;
+}
+
+void prettyPrint(const QueryData& results,
+                 const std::vector<std::string>& columns,
+                 std::map<std::string, size_t>& lengths) {
+  if (results.size() == 0) {
+    return;
+  }
+
+  // Call a final compute using the column names as minimum lengths.
+  computeRowLengths(results.front(), lengths, true);
+
+  // Output a nice header wrapping the column names.
+  auto separator = generateToken(lengths, columns);
+  auto header = separator + generateHeader(lengths, columns) + separator;
+  printf("%s", header.c_str());
+
+  // Iterate each row and pretty print.
+  for (const auto& row : results) {
+    printf("%s", generateRow(row, lengths, columns).c_str());
+  }
+  printf("%s", separator.c_str());
+}
+
+void jsonPrint(const QueryData& q) {
+  printf("[\n");
+  for (int i = 0; i < q.size(); ++i) {
+    std::string row_string;
+    if (serializeRowJSON(q[i], row_string).ok()) {
+      row_string.pop_back();
+      printf("  %s", row_string.c_str());
+      if (i < q.size() - 1) {
+        printf(",\n");
+      }
+    }
+  }
+  printf("\n]\n");
+}
+
+void computeRowLengths(const Row& r,
+                       std::map<std::string, size_t>& lengths,
+                       bool use_columns) {
+  for (const auto& col : r) {
+    size_t current = (lengths.count(col.first) > 0) ? lengths.at(col.first) : 0;
+    size_t size =
+        (use_columns) ? utf8StringSize(col.first) : utf8StringSize(col.second);
+    lengths[col.first] = (size > current) ? size : current;
+  }
+}
+}
diff --git a/src/osquery/devtools/shell.cpp b/src/osquery/devtools/shell.cpp
new file mode 100644 (file)
index 0000000..9b88fe0
--- /dev/null
@@ -0,0 +1,1553 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code to implement the "sqlite" command line
+** utility for accessing SQLite databases.
+*/
+
+#include <signal.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <iostream>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <sqlite3.h>
+
+#include <boost/algorithm/string/predicate.hpp>
+
+#include <osquery/database.h>
+#include <osquery/filesystem.h>
+#include <osquery/flags.h>
+
+#include "osquery/devtools/devtools.h"
+#include "osquery/sql/virtual_table.h"
+
+namespace osquery {
+
+/// Define flags used by the shell. They are parsed by the drop-in shell.
+SHELL_FLAG(bool, csv, false, "Set output mode to 'csv'");
+SHELL_FLAG(bool, json, false, "Set output mode to 'json'");
+SHELL_FLAG(bool, line, false, "Set output mode to 'line'");
+SHELL_FLAG(bool, list, false, "Set output mode to 'list'");
+SHELL_FLAG(string, nullvalue, "", "Set string for NULL values, default ''");
+SHELL_FLAG(string, separator, "|", "Set output field separator, default '|'");
+
+/// Define short-hand shell switches.
+SHELL_FLAG(bool, L, false, "List all table names");
+SHELL_FLAG(string, A, "", "Select all from a table");
+}
+
+/*
+** Text of a help message
+*/
+static char zHelp[] =
+    "Welcome to the osquery shell. Please explore your OS!\n"
+    "You are connected to a transient 'in-memory' virtual database.\n"
+    "\n"
+    ".all [TABLE]       Select all from a table\n"
+    ".bail ON|OFF       Stop after hitting an error; default OFF\n"
+    ".echo ON|OFF       Turn command echo on or off\n"
+    ".exit              Exit this program\n"
+    ".header(s) ON|OFF  Turn display of headers on or off\n"
+    ".help              Show this message\n"
+    ".mode MODE         Set output mode where MODE is one of:\n"
+    "                     csv      Comma-separated values\n"
+    "                     column   Left-aligned columns.  (See .width)\n"
+    "                     line     One value per line\n"
+    "                     list     Values delimited by .separator string\n"
+    "                     pretty   Pretty printed SQL results\n"
+    ".nullvalue STR     Use STRING in place of NULL values\n"
+    ".print STR...      Print literal STRING\n"
+    ".quit              Exit this program\n"
+    ".schema [TABLE]    Show the CREATE statements\n"
+    ".separator STR     Change separator used by output mode and .import\n"
+    ".show              Show the current values for various settings\n"
+    ".tables [TABLE]    List names of tables\n"
+    ".trace FILE|off    Output each SQL statement as it is run\n"
+    ".width [NUM1]+     Set column widths for \"column\" mode\n";
+
+static char zTimerHelp[] =
+    ".timer ON|OFF      Turn the CPU timer measurement on or off\n";
+
+/*
+** These are the allowed modes.
+*/
+#define MODE_Line 0 /* One column per line.  Blank line between records */
+#define MODE_Column 1 /* One record per line in neat columns */
+#define MODE_List 2 /* One record per line with a separator */
+#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
+#define MODE_Csv 4 /* Quote strings, numbers are plain */
+#define MODE_Pretty 5 /* Pretty print the SQL results */
+
+static const char *modeDescr[] = {
+    "line", "column", "list", "semi", "csv", "pretty",
+};
+
+/* Make sure isatty() has a prototype.
+*/
+extern int isatty(int);
+
+/* ctype macros that work with signed characters */
+#define IsSpace(X) isspace((unsigned char)X)
+#define IsDigit(X) isdigit((unsigned char)X)
+#define ToLower(X) (char) tolower((unsigned char)X)
+
+/* True if the timer is enabled */
+static int enableTimer = 0;
+
+/* Return the current wall-clock time */
+static sqlite3_int64 timeOfDay(void) {
+  static sqlite3_vfs *clockVfs = 0;
+  sqlite3_int64 t;
+  if (clockVfs == 0)
+    clockVfs = sqlite3_vfs_find(0);
+  if (clockVfs->iVersion >= 1 && clockVfs->xCurrentTimeInt64 != 0) {
+    clockVfs->xCurrentTimeInt64(clockVfs, &t);
+  } else {
+    double r;
+    clockVfs->xCurrentTime(clockVfs, &r);
+    t = (sqlite3_int64)(r * 86400000.0);
+  }
+  return t;
+}
+
+/* Saved resource information for the beginning of an operation */
+static struct rusage sBegin; /* CPU time at start */
+static sqlite3_int64 iBegin; /* Wall-clock time at start */
+
+/*
+** Begin timing an operation
+*/
+static void beginTimer(void) {
+  if (enableTimer) {
+    getrusage(RUSAGE_SELF, &sBegin);
+    iBegin = timeOfDay();
+  }
+}
+
+/* Return the difference of two time_structs in seconds */
+static double timeDiff(struct timeval *pStart, struct timeval *pEnd) {
+  return (pEnd->tv_usec - pStart->tv_usec) * 0.000001 +
+         (double)(pEnd->tv_sec - pStart->tv_sec);
+}
+
+/*
+** Print the timing results.
+*/
+static void endTimer(void) {
+  if (enableTimer) {
+    struct rusage sEnd;
+    sqlite3_int64 iEnd = timeOfDay();
+    getrusage(RUSAGE_SELF, &sEnd);
+    printf("Run Time: real %.3f user %f sys %f\n",
+           (iEnd - iBegin) * 0.001,
+           timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
+           timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
+  }
+}
+
+#define BEGIN_TIMER beginTimer()
+#define END_TIMER endTimer()
+#define HAS_TIMER 1
+
+/*
+** Used to prevent warnings about unused parameters
+*/
+#define UNUSED_PARAMETER(x) (void)(x)
+
+/*
+** If the following flag is set, then command execution stops
+** at an error if we are not interactive.
+*/
+static int bail_on_error = 0;
+
+/*
+** Threat stdin as an interactive input if the following variable
+** is true.  Otherwise, assume stdin is connected to a file or pipe.
+*/
+static int stdin_is_interactive = 1;
+
+/*
+** True if an interrupt (Control-C) has been received.
+*/
+static volatile int seenInterrupt = 0;
+
+/*
+** This is the name of our program. It is set in main(), used
+** in a number of other places, mostly for error messages.
+*/
+static char *Argv0;
+
+/*
+** Prompt strings. Initialized in main. Settable with
+**   .prompt main continue
+*/
+static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
+static char continuePrompt[20]; /* Continuation prompt. default: "   ...> " */
+
+/*
+** A global char* and an SQL function to access its current value
+** from within an SQL statement. This program used to use the
+** sqlite_exec_printf() API to substitue a string into an SQL statement.
+** The correct way to do this with sqlite3 is to use the bind API, but
+** since the shell is built around the callback paradigm it would be a lot
+** of work. Instead just use this hack, which is quite harmless.
+*/
+static const char *zShellStatic = 0;
+static void shellstaticFunc(sqlite3_context *context,
+                            int argc,
+                            sqlite3_value **argv) {
+  assert(0 == argc);
+  assert(zShellStatic);
+  UNUSED_PARAMETER(argc);
+  UNUSED_PARAMETER(argv);
+  sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
+}
+
+/*
+** This routine reads a line of text from FILE in, stores
+** the text in memory obtained from malloc() and returns a pointer
+** to the text.  NULL is returned at end of file, or if malloc()
+** fails.
+**
+** If zLine is not NULL then it is a malloced buffer returned from
+** a previous call to this routine that may be reused.
+*/
+static char *local_getline(char *zLine, FILE *in) {
+  int nLine = zLine == 0 ? 0 : 100;
+  int n = 0;
+
+  while (1) {
+    if (n + 100 > nLine) {
+      nLine = nLine * 2 + 100;
+      zLine = (char *)realloc(zLine, nLine);
+      if (zLine == 0)
+        return 0;
+    }
+    if (fgets(&zLine[n], nLine - n, in) == 0) {
+      if (n == 0) {
+        free(zLine);
+        return 0;
+      }
+      zLine[n] = 0;
+      break;
+    }
+    while (zLine[n])
+      n++;
+    if (n > 0 && zLine[n - 1] == '\n') {
+      n--;
+      if (n > 0 && zLine[n - 1] == '\r')
+        n--;
+      zLine[n] = 0;
+      break;
+    }
+  }
+  return zLine;
+}
+
+/*
+** Retrieve a single line of input text.
+**
+** If in==0 then read from standard input and prompt before each line.
+** If isContinuation is true, then a continuation prompt is appropriate.
+** If isContinuation is zero, then the main prompt should be used.
+**
+** If zPrior is not NULL then it is a buffer from a prior call to this
+** routine that can be reused.
+**
+** The result is stored in space obtained from malloc() and must either
+** be freed by the caller or else passed back into this routine via the
+** zPrior argument for reuse.
+*/
+static char *one_input_line(FILE *in, char *zPrior, int isContinuation) {
+  char *zPrompt;
+  char *zResult;
+  if (in != 0) {
+    zResult = local_getline(zPrior, in);
+  } else {
+    zPrompt = isContinuation ? continuePrompt : mainPrompt;
+    free(zPrior);
+    zResult = readline(zPrompt);
+    if (zResult && *zResult)
+      add_history(zResult);
+  }
+  return zResult;
+}
+
+struct previous_mode_data {
+  int valid; /* Is there legit data in here? */
+  int mode;
+  int showHeader;
+  int colWidth[100];
+};
+
+/*
+** Pretty print structure
+ */
+struct prettyprint_data {
+  osquery::QueryData results;
+  std::vector<std::string> columns;
+  std::map<std::string, size_t> lengths;
+};
+
+/*
+** An pointer to an instance of this structure is passed from
+** the main program to the callback.  This is used to communicate
+** state and mode information.
+*/
+struct callback_data {
+  int echoOn; /* True to echo input commands */
+  int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL statement */
+  int cnt; /* Number of records displayed so far */
+  FILE *out; /* Write results here */
+  FILE *traceOut; /* Output for sqlite3_trace() */
+  int nErr; /* Number of errors seen */
+  int mode; /* An output mode setting */
+  int writableSchema; /* True if PRAGMA writable_schema=ON */
+  int showHeader; /* True to show column names in List or Column mode */
+  char *zDestTable; /* Name of destination table when MODE_Insert */
+  char separator[20]; /* Separator character for MODE_List */
+  int colWidth[100]; /* Requested width of each column when in column mode*/
+  int actualWidth[100]; /* Actual width of each column */
+  char nullvalue[20]; /* The text to print when a NULL comes back from
+                      ** the database */
+  struct previous_mode_data explainPrev;
+  /* Holds the mode information just before
+  ** .explain ON */
+  char outfile[FILENAME_MAX]; /* Filename for *out */
+  const char *zDbFilename; /* name of the database file */
+  char *zFreeOnClose; /* Filename to free when closing */
+  const char *zVfs; /* Name of VFS to use */
+  sqlite3_stmt *pStmt; /* Current statement if any. */
+  FILE *pLog; /* Write log output here */
+  int *aiIndent; /* Array of indents used in MODE_Explain */
+  int nIndent; /* Size of array aiIndent[] */
+  int iIndent; /* Index of current op in aiIndent[] */
+
+  /* Additional attributes to be used in pretty mode */
+  struct prettyprint_data *prettyPrint;
+};
+
+/*
+** Number of elements in an array
+*/
+#define ArraySize(X) (int)(sizeof(X) / sizeof(X[0]))
+
+/*
+** Compute a string length that is limited to what can be stored in
+** lower 30 bits of a 32-bit signed integer.
+*/
+static int strlen30(const char *z) {
+  const char *z2 = z;
+  while (*z2) {
+    z2++;
+  }
+  return 0x3fffffff & (int)(z2 - z);
+}
+
+/*
+** A callback for the sqlite3_log() interface.
+*/
+static void shellLog(void *pArg, int iErrCode, const char *zMsg) {
+  struct callback_data *p = (struct callback_data *)pArg;
+  if (p->pLog == 0)
+    return;
+  fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
+  fflush(p->pLog);
+}
+
+/*
+** Output the given string as a quoted according to C or TCL quoting rules.
+*/
+static void output_c_string(FILE *out, const char *z) {
+  unsigned int c;
+  fputc('"', out);
+  while ((c = *(z++)) != 0) {
+    if (c == '\\') {
+      fputc(c, out);
+      fputc(c, out);
+    } else if (c == '"') {
+      fputc('\\', out);
+      fputc('"', out);
+    } else if (c == '\t') {
+      fputc('\\', out);
+      fputc('t', out);
+    } else if (c == '\n') {
+      fputc('\\', out);
+      fputc('n', out);
+    } else if (c == '\r') {
+      fputc('\\', out);
+      fputc('r', out);
+    } else if (!isprint(c & 0xff)) {
+      fprintf(out, "\\%03o", c & 0xff);
+    } else {
+      fputc(c, out);
+    }
+  }
+  fputc('"', out);
+}
+
+/*
+** If a field contains any character identified by a 1 in the following
+** array, then the string must be quoted for CSV.
+*/
+// clang-format off
+static const char needCsvQuote[] = {
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 
+};
+// clang-format on
+
+/*
+** Output a single term of CSV.  Actually, p->separator is used for
+** the separator, which may or may not be a comma.  p->nullvalue is
+** the null value.  Strings are quoted if necessary.
+*/
+static void output_csv(struct callback_data *p, const char *z, int bSep) {
+  FILE *out = p->out;
+  if (z == 0) {
+    fprintf(out, "%s", p->nullvalue);
+  } else {
+    int i;
+    int nSep = strlen30(p->separator);
+    for (i = 0; z[i]; i++) {
+      if (needCsvQuote[((unsigned char *)z)[i]] ||
+          (z[i] == p->separator[0] &&
+           (nSep == 1 || memcmp(z, p->separator, nSep) == 0))) {
+        i = 0;
+        break;
+      }
+    }
+    if (i == 0) {
+      putc('"', out);
+      for (i = 0; z[i]; i++) {
+        if (z[i] == '"')
+          putc('"', out);
+        putc(z[i], out);
+      }
+      putc('"', out);
+    } else {
+      fprintf(out, "%s", z);
+    }
+  }
+  if (bSep) {
+    fprintf(p->out, "%s", p->separator);
+  }
+}
+
+#ifdef SIGINT
+/*
+** This routine runs when the user presses Ctrl-C
+*/
+static void interrupt_handler(int NotUsed) {
+  UNUSED_PARAMETER(NotUsed);
+  seenInterrupt = 1;
+}
+#endif
+
+/*
+** This is the callback routine that the shell
+** invokes for each row of a query result.
+*/
+static int shell_callback(
+    void *pArg, int nArg, char **azArg, char **azCol, int *aiType) {
+  int i;
+  struct callback_data *p = (struct callback_data *)pArg;
+
+  switch (p->mode) {
+  case MODE_Pretty: {
+    if (p->prettyPrint->columns.size() == 0) {
+      for (i = 0; i < nArg; i++) {
+        p->prettyPrint->columns.push_back(std::string(azCol[i]));
+      }
+    }
+
+    osquery::Row r;
+    for (int i = 0; i < nArg; ++i) {
+      if (azCol[i] != nullptr && azArg[i] != nullptr) {
+        r[std::string(azCol[i])] = std::string(azArg[i]);
+      }
+    }
+    osquery::computeRowLengths(r, p->prettyPrint->lengths);
+    p->prettyPrint->results.push_back(r);
+    break;
+  }
+  case MODE_Line: {
+    int w = 5;
+    if (azArg == 0)
+      break;
+    for (i = 0; i < nArg; i++) {
+      int len = strlen30(azCol[i] ? azCol[i] : "");
+      if (len > w)
+        w = len;
+    }
+    if (p->cnt++ > 0)
+      fprintf(p->out, "\n");
+    for (i = 0; i < nArg; i++) {
+      fprintf(p->out,
+              "%*s = %s\n",
+              w,
+              azCol[i],
+              azArg[i] ? azArg[i] : p->nullvalue);
+    }
+    break;
+  }
+  case MODE_Column: {
+    if (p->cnt++ == 0) {
+      for (i = 0; i < nArg; i++) {
+        int w, n;
+        if (i < ArraySize(p->colWidth)) {
+          w = p->colWidth[i];
+        } else {
+          w = 0;
+        }
+        if (w == 0) {
+          w = strlen30(azCol[i] ? azCol[i] : "");
+          if (w < 10)
+            w = 10;
+          n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue);
+          if (w < n)
+            w = n;
+        }
+        if (i < ArraySize(p->actualWidth)) {
+          p->actualWidth[i] = w;
+        }
+        if (p->showHeader) {
+          if (w < 0) {
+            fprintf(p->out,
+                    "%*.*s%s",
+                    -w,
+                    -w,
+                    azCol[i],
+                    i == nArg - 1 ? "\n" : "  ");
+          } else {
+            fprintf(p->out,
+                    "%-*.*s%s",
+                    w,
+                    w,
+                    azCol[i],
+                    i == nArg - 1 ? "\n" : "  ");
+          }
+        }
+      }
+      if (p->showHeader) {
+        for (i = 0; i < nArg; i++) {
+          int w;
+          if (i < ArraySize(p->actualWidth)) {
+            w = p->actualWidth[i];
+            if (w < 0)
+              w = -w;
+          } else {
+            w = 10;
+          }
+          fprintf(p->out,
+                  "%-*.*s%s",
+                  w,
+                  w,
+                  "-----------------------------------"
+                  "----------------------------------------------------------",
+                  i == nArg - 1 ? "\n" : "  ");
+        }
+      }
+    }
+    if (azArg == 0)
+      break;
+    for (i = 0; i < nArg; i++) {
+      int w;
+      if (i < ArraySize(p->actualWidth)) {
+        w = p->actualWidth[i];
+      } else {
+        w = 10;
+      }
+      if (i == 1 && p->aiIndent && p->pStmt) {
+        if (p->iIndent < p->nIndent) {
+          fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
+        }
+        p->iIndent++;
+      }
+      if (w < 0) {
+        fprintf(p->out,
+                "%*.*s%s",
+                -w,
+                -w,
+                azArg[i] ? azArg[i] : p->nullvalue,
+                i == nArg - 1 ? "\n" : "  ");
+      } else {
+        fprintf(p->out,
+                "%-*.*s%s",
+                w,
+                w,
+                azArg[i] ? azArg[i] : p->nullvalue,
+                i == nArg - 1 ? "\n" : "  ");
+      }
+    }
+    break;
+  }
+  case MODE_Semi:
+  case MODE_List: {
+    if (p->cnt++ == 0 && p->showHeader) {
+      for (i = 0; i < nArg; i++) {
+        fprintf(p->out, "%s%s", azCol[i], i == nArg - 1 ? "\n" : p->separator);
+      }
+    }
+    if (azArg == 0)
+      break;
+    for (i = 0; i < nArg; i++) {
+      char *z = azArg[i];
+      if (z == 0)
+        z = p->nullvalue;
+      fprintf(p->out, "%s", z);
+      if (i < nArg - 1) {
+        fprintf(p->out, "%s", p->separator);
+      } else if (p->mode == MODE_Semi) {
+        fprintf(p->out, ";\n");
+      } else {
+        fprintf(p->out, "\n");
+      }
+    }
+    break;
+  }
+  case MODE_Csv: {
+    if (p->cnt++ == 0 && p->showHeader) {
+      for (i = 0; i < nArg; i++) {
+        output_csv(p, azCol[i] ? azCol[i] : "", i < nArg - 1);
+      }
+      fprintf(p->out, "\n");
+    }
+    if (azArg == 0)
+      break;
+    for (i = 0; i < nArg; i++) {
+      output_csv(p, azArg[i], i < nArg - 1);
+    }
+    fprintf(p->out, "\n");
+    break;
+  }
+  }
+  return 0;
+}
+
+/*
+** Set the destination table field of the callback_data structure to
+** the name of the table given.  Escape any quote characters in the
+** table name.
+*/
+static void set_table_name(struct callback_data *p, const char *zName) {
+  int i, n;
+  int needQuote;
+  char *z;
+
+  if (p->zDestTable) {
+    free(p->zDestTable);
+    p->zDestTable = 0;
+  }
+  if (zName == 0)
+    return;
+  needQuote = !isalpha((unsigned char)*zName) && *zName != '_';
+  for (i = n = 0; zName[i]; i++, n++) {
+    if (!isalnum((unsigned char)zName[i]) && zName[i] != '_') {
+      needQuote = 1;
+      if (zName[i] == '\'')
+        n++;
+    }
+  }
+  if (needQuote)
+    n += 2;
+  z = p->zDestTable = (char *)malloc(n + 1);
+  if (z == 0) {
+    fprintf(stderr, "Error: out of memory\n");
+    exit(1);
+  }
+  n = 0;
+  if (needQuote)
+    z[n++] = '\'';
+  for (i = 0; zName[i]; i++) {
+    z[n++] = zName[i];
+    if (zName[i] == '\'')
+      z[n++] = '\'';
+  }
+  if (needQuote)
+    z[n++] = '\'';
+  z[n] = 0;
+}
+
+/*
+** Allocate space and save off current error string.
+*/
+static char *save_err_msg(sqlite3 *db /* Database to query */
+                          ) {
+  int nErrMsg = 1 + strlen30(sqlite3_errmsg(db));
+  char *zErrMsg = (char *)sqlite3_malloc(nErrMsg);
+  if (zErrMsg) {
+    memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg);
+  }
+  return zErrMsg;
+}
+
+/*
+** Execute a statement or set of statements.  Print
+** any result rows/columns depending on the current mode
+** set via the supplied callback.
+**
+** This is very similar to SQLite's built-in sqlite3_exec()
+** function except it takes a slightly different callback
+** and callback data argument.
+*/
+static int shell_exec(
+    const char *zSql, /* SQL to be evaluated */
+    int (*xCallback)(
+        void *, int, char **, char **, int *), /* Callback function */
+    /* (not the same as sqlite3_exec) */
+    struct callback_data *pArg, /* Pointer to struct callback_data */
+    char **pzErrMsg /* Error msg written here */
+    ) {
+  // Grab a lock on the managed DB instance.
+  auto dbc = osquery::SQLiteDBManager::get();
+  auto db = dbc.db();
+
+  sqlite3_stmt *pStmt = nullptr; /* Statement to execute. */
+  int rc = SQLITE_OK; /* Return Code */
+  int rc2;
+  const char *zLeftover; /* Tail of unprocessed SQL */
+
+  if (pzErrMsg) {
+    *pzErrMsg = nullptr;
+  }
+
+  while (zSql[0] && (SQLITE_OK == rc)) {
+    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
+    if (SQLITE_OK != rc) {
+      if (pzErrMsg) {
+        *pzErrMsg = save_err_msg(db);
+      }
+    } else {
+      if (!pStmt) {
+        /* this happens for a comment or white-space */
+        zSql = zLeftover;
+        while (IsSpace(zSql[0]))
+          zSql++;
+        continue;
+      }
+
+      /* save off the prepared statment handle and reset row count */
+      if (pArg) {
+        pArg->pStmt = pStmt;
+        pArg->cnt = 0;
+      }
+
+      /* echo the sql statement if echo on */
+      if (pArg && pArg->echoOn) {
+        const char *zStmtSql = sqlite3_sql(pStmt);
+        fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
+      }
+
+      /* perform the first step.  this will tell us if we
+      ** have a result set or not and how wide it is.
+      */
+      rc = sqlite3_step(pStmt);
+      /* if we have a result set... */
+      if (SQLITE_ROW == rc) {
+        /* if we have a callback... */
+        if (xCallback) {
+          /* allocate space for col name ptr, value ptr, and type */
+          int nCol = sqlite3_column_count(pStmt);
+          void *pData = sqlite3_malloc(3 * nCol * sizeof(const char *) + 1);
+          if (!pData) {
+            rc = SQLITE_NOMEM;
+          } else {
+            char **azCols = (char **)pData; /* Names of result columns */
+            char **azVals = &azCols[nCol]; /* Results */
+            int *aiTypes = (int *)&azVals[nCol]; /* Result types */
+            int i, x;
+            assert(sizeof(int) <= sizeof(char *));
+            /* save off ptrs to column names */
+            for (i = 0; i < nCol; i++) {
+              azCols[i] = (char *)sqlite3_column_name(pStmt, i);
+            }
+            do {
+              /* extract the data and data types */
+              for (i = 0; i < nCol; i++) {
+                aiTypes[i] = x = sqlite3_column_type(pStmt, i);
+                azVals[i] = (char *)sqlite3_column_text(pStmt, i);
+                if (!azVals[i] && (aiTypes[i] != SQLITE_NULL)) {
+                  rc = SQLITE_NOMEM;
+                  break; /* from for */
+                }
+              } /* end for */
+
+              /* if data and types extracted successfully... */
+              if (SQLITE_ROW == rc) {
+                /* call the supplied callback with the result row data */
+                if (xCallback(pArg, nCol, azVals, azCols, aiTypes)) {
+                  rc = SQLITE_ABORT;
+                } else {
+                  rc = sqlite3_step(pStmt);
+                }
+              }
+            } while (SQLITE_ROW == rc);
+            sqlite3_free(pData);
+          }
+        } else {
+          do {
+            rc = sqlite3_step(pStmt);
+          } while (rc == SQLITE_ROW);
+        }
+      }
+
+      /* Finalize the statement just executed. If this fails, save a
+      ** copy of the error message. Otherwise, set zSql to point to the
+      ** next statement to execute. */
+      rc2 = sqlite3_finalize(pStmt);
+      if (rc != SQLITE_NOMEM)
+        rc = rc2;
+      if (rc == SQLITE_OK) {
+        zSql = zLeftover;
+        while (IsSpace(zSql[0]))
+          zSql++;
+      } else if (pzErrMsg) {
+        *pzErrMsg = save_err_msg(db);
+      }
+
+      /* clear saved stmt handle */
+      if (pArg) {
+        pArg->pStmt = nullptr;
+      }
+    }
+  } /* end while */
+
+  if (pArg && pArg->mode == MODE_Pretty) {
+    if (osquery::FLAGS_json) {
+      osquery::jsonPrint(pArg->prettyPrint->results);
+    } else {
+      osquery::prettyPrint(pArg->prettyPrint->results,
+                           pArg->prettyPrint->columns,
+                           pArg->prettyPrint->lengths);
+    }
+    pArg->prettyPrint->results.clear();
+    pArg->prettyPrint->columns.clear();
+    pArg->prettyPrint->lengths.clear();
+  }
+
+  return rc;
+}
+
+/* Forward reference */
+static int process_input(struct callback_data *p, FILE *in);
+
+/*
+** Do C-language style dequoting.
+**
+**    \t    -> tab
+**    \n    -> newline
+**    \r    -> carriage return
+**    \"    -> "
+**    \NNN  -> ascii character NNN in octal
+**    \\    -> backslash
+*/
+static void resolve_backslashes(char *z) {
+  int i, j;
+  char c;
+  for (i = j = 0; (c = z[i]) != 0; i++, j++) {
+    if (c == '\\') {
+      c = z[++i];
+      if (c == 'n') {
+        c = '\n';
+      } else if (c == 't') {
+        c = '\t';
+      } else if (c == 'r') {
+        c = '\r';
+      } else if (c == '\\') {
+        c = '\\';
+      } else if (c >= '0' && c <= '7') {
+        c -= '0';
+        if (z[i + 1] >= '0' && z[i + 1] <= '7') {
+          i++;
+          c = (c << 3) + z[i] - '0';
+          if (z[i + 1] >= '0' && z[i + 1] <= '7') {
+            i++;
+            c = (c << 3) + z[i] - '0';
+          }
+        }
+      }
+    }
+    z[j] = c;
+  }
+  z[j] = 0;
+}
+
+/*
+** Return the value of a hexadecimal digit.  Return -1 if the input
+** is not a hex digit.
+*/
+static int hexDigitValue(char c) {
+  if (c >= '0' && c <= '9')
+    return c - '0';
+  if (c >= 'a' && c <= 'f')
+    return c - 'a' + 10;
+  if (c >= 'A' && c <= 'F')
+    return c - 'A' + 10;
+  return -1;
+}
+
+/*
+** Interpret zArg as an integer value, possibly with suffixes.
+*/
+static sqlite3_int64 integerValue(const char *zArg) {
+  sqlite3_int64 v = 0;
+  static const struct {
+    char *zSuffix;
+    int iMult;
+  } aMult[] = {
+        {(char *)"KiB", 1024},
+        {(char *)"MiB", 1024 * 1024},
+        {(char *)"GiB", 1024 * 1024 * 1024},
+        {(char *)"KB", 1000},
+        {(char *)"MB", 1000000},
+        {(char *)"GB", 1000000000},
+        {(char *)"K", 1000},
+        {(char *)"M", 1000000},
+        {(char *)"G", 1000000000},
+    };
+  int i;
+  int isNeg = 0;
+  if (zArg[0] == '-') {
+    isNeg = 1;
+    zArg++;
+  } else if (zArg[0] == '+') {
+    zArg++;
+  }
+  if (zArg[0] == '0' && zArg[1] == 'x') {
+    int x;
+    zArg += 2;
+    while ((x = hexDigitValue(zArg[0])) >= 0) {
+      v = (v << 4) + x;
+      zArg++;
+    }
+  } else {
+    while (IsDigit(zArg[0])) {
+      v = v * 10 + zArg[0] - '0';
+      zArg++;
+    }
+  }
+  for (i = 0; i < ArraySize(aMult); i++) {
+    if (sqlite3_stricmp(aMult[i].zSuffix, zArg) == 0) {
+      v *= aMult[i].iMult;
+      break;
+    }
+  }
+  return isNeg ? -v : v;
+}
+
+/*
+** Interpret zArg as either an integer or a boolean value.  Return 1 or 0
+** for TRUE and FALSE.  Return the integer value if appropriate.
+*/
+static int booleanValue(char *zArg) {
+  int i;
+  if (zArg[0] == '0' && zArg[1] == 'x') {
+    for (i = 2; hexDigitValue(zArg[i]) >= 0; i++) {
+    }
+  } else {
+    for (i = 0; zArg[i] >= '0' && zArg[i] <= '9'; i++) {
+    }
+  }
+  if (i > 0 && zArg[i] == 0)
+    return (int)(integerValue(zArg) & 0xffffffff);
+  if (sqlite3_stricmp(zArg, "on") == 0 || sqlite3_stricmp(zArg, "yes") == 0) {
+    return 1;
+  }
+  if (sqlite3_stricmp(zArg, "off") == 0 || sqlite3_stricmp(zArg, "no") == 0) {
+    return 0;
+  }
+  fprintf(
+      stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg);
+  return 0;
+}
+
+/*
+** Close an output file, assuming it is not stderr or stdout
+*/
+static void output_file_close(FILE *f) {
+  if (f && f != stdout && f != stderr)
+    fclose(f);
+}
+
+/*
+** Try to open an output file.   The names "stdout" and "stderr" are
+** recognized and do the right thing.  NULL is returned if the output
+** filename is "off".
+*/
+static FILE *output_file_open(const char *zFile) {
+  FILE *f;
+  if (strcmp(zFile, "stdout") == 0) {
+    f = stdout;
+  } else if (strcmp(zFile, "stderr") == 0) {
+    f = stderr;
+  } else if (strcmp(zFile, "off") == 0) {
+    f = 0;
+  } else {
+    f = fopen(zFile, "wb");
+    if (f == 0) {
+      fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
+    }
+  }
+  return f;
+}
+
+inline void meta_tables(int nArg, char **azArg) {
+  auto tables = osquery::Registry::names("table");
+  std::sort(tables.begin(), tables.end());
+  for (const auto &table_name : tables) {
+    if (nArg == 1 || table_name.find(azArg[1]) == 0) {
+      printf("  => %s\n", table_name.c_str());
+    }
+  }
+}
+
+inline void meta_schema(int nArg, char **azArg) {
+  for (const auto &table_name : osquery::Registry::names("table")) {
+    if (nArg > 1 && table_name.find(azArg[1]) != 0) {
+      continue;
+    }
+
+    osquery::PluginRequest request = {{"action", "columns"}};
+    osquery::PluginResponse response;
+
+    osquery::Registry::call("table", table_name, request, response);
+    std::vector<std::string> columns;
+    for (const auto &column : response) {
+      columns.push_back(column.at("name") + " " + column.at("type"));
+    }
+
+    printf("CREATE TABLE %s(%s);\n",
+           table_name.c_str(),
+           osquery::join(columns, ", ").c_str());
+  }
+}
+
+/*
+** If an input line begins with "." then invoke this routine to
+** process that line.
+**
+** Return 1 on error, 2 to exit, and 0 otherwise.
+*/
+static int do_meta_command(char *zLine, struct callback_data *p) {
+  int i = 1;
+  int nArg = 0;
+  int n, c;
+  int rc = 0;
+  char *azArg[50];
+
+  // A meta command may act on the database, grab a lock and instance.
+  auto dbc = osquery::SQLiteDBManager::get();
+  auto db = dbc.db();
+
+  /* Parse the input line into tokens.
+  */
+  while (zLine[i] && nArg < ArraySize(azArg)) {
+    while (IsSpace(zLine[i])) {
+      i++;
+    }
+    if (zLine[i] == 0)
+      break;
+    if (zLine[i] == '\'' || zLine[i] == '"') {
+      int delim = zLine[i++];
+      azArg[nArg++] = &zLine[i];
+      while (zLine[i] && zLine[i] != delim) {
+        if (zLine[i] == '\\' && delim == '"' && zLine[i + 1] != 0)
+          i++;
+        i++;
+      }
+      if (zLine[i] == delim) {
+        zLine[i++] = 0;
+      }
+      if (delim == '"')
+        resolve_backslashes(azArg[nArg - 1]);
+    } else {
+      azArg[nArg++] = &zLine[i];
+      while (zLine[i] && !IsSpace(zLine[i])) {
+        i++;
+      }
+      if (zLine[i])
+        zLine[i++] = 0;
+      resolve_backslashes(azArg[nArg - 1]);
+    }
+  }
+
+  /* Process the input line.
+  */
+  if (nArg == 0)
+    return 0; /* no tokens, no error */
+  n = strlen30(azArg[0]);
+  c = azArg[0][0];
+  if (c == 'a' && strncmp(azArg[0], "all", n) == 0 && nArg == 2) {
+    struct callback_data data;
+    memcpy(&data, p, sizeof(data));
+    auto query = std::string("SELECT * FROM ") + azArg[1];
+    rc = shell_exec(query.c_str(), shell_callback, &data, nullptr);
+    if (rc != SQLITE_OK) {
+      fprintf(stderr, "Error querying table: %s\n", azArg[1]);
+    }
+  } else if (c == 'b' && n >= 3 && strncmp(azArg[0], "bail", n) == 0 &&
+             nArg > 1 && nArg < 3) {
+    bail_on_error = booleanValue(azArg[1]);
+  } else if (c == 'e' && strncmp(azArg[0], "echo", n) == 0 && nArg > 1 &&
+             nArg < 3) {
+    p->echoOn = booleanValue(azArg[1]);
+  } else if (c == 'e' && strncmp(azArg[0], "exit", n) == 0) {
+    if (nArg > 1 && (rc = (int)integerValue(azArg[1])) != 0)
+      exit(rc);
+    rc = 2;
+  } else if (c == 'h' && (strncmp(azArg[0], "header", n) == 0 ||
+                          strncmp(azArg[0], "headers", n) == 0) &&
+             nArg > 1 && nArg < 3) {
+    p->showHeader = booleanValue(azArg[1]);
+  } else if (c == 'h' && strncmp(azArg[0], "help", n) == 0) {
+    fprintf(stderr, "%s", zHelp);
+    if (HAS_TIMER) {
+      fprintf(stderr, "%s", zTimerHelp);
+    }
+  } else if (c == 'l' && strncmp(azArg[0], "log", n) == 0 && nArg >= 2) {
+    const char *zFile = azArg[1];
+    output_file_close(p->pLog);
+    p->pLog = output_file_open(zFile);
+  } else if (c == 'm' && strncmp(azArg[0], "mode", n) == 0 && nArg == 2) {
+    int n2 = strlen30(azArg[1]);
+    if ((n2 == 4 && strncmp(azArg[1], "line", n2) == 0) ||
+        (n2 == 5 && strncmp(azArg[1], "lines", n2) == 0)) {
+      p->mode = MODE_Line;
+    } else if ((n2 == 6 && strncmp(azArg[1], "column", n2) == 0) ||
+               (n2 == 7 && strncmp(azArg[1], "columns", n2) == 0)) {
+      p->mode = MODE_Column;
+    } else if ((n2 == 6 && strncmp(azArg[1], "column", n2) == 0) ||
+               (n2 == 7 && strncmp(azArg[1], "columns", n2) == 0)) {
+      p->mode = MODE_Column;
+    } else if (n2 == 4 && strncmp(azArg[1], "list", n2) == 0) {
+      p->mode = MODE_List;
+    } else if (n2 == 6 && strncmp(azArg[1], "pretty", n2) == 0) {
+      p->mode = MODE_Pretty;
+    } else if (n2 == 3 && strncmp(azArg[1], "csv", n2) == 0) {
+      p->mode = MODE_Csv;
+      sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
+    } else {
+      fprintf(stderr,
+              "Error: mode should be one of: "
+              "column csv html insert line list tabs tcl pretty\n");
+      rc = 1;
+    }
+  } else if (c == 'n' && strncmp(azArg[0], "nullvalue", n) == 0 && nArg == 2) {
+    sqlite3_snprintf(sizeof(p->nullvalue),
+                     p->nullvalue,
+                     "%.*s",
+                     (int)ArraySize(p->nullvalue) - 1,
+                     azArg[1]);
+  } else if (c == 'p' && n >= 3 && strncmp(azArg[0], "print", n) == 0) {
+    int i;
+    for (i = 1; i < nArg; i++) {
+      if (i > 1)
+        fprintf(p->out, " ");
+      fprintf(p->out, "%s", azArg[i]);
+    }
+    fprintf(p->out, "\n");
+  } else if (c == 'q' && strncmp(azArg[0], "quit", n) == 0 && nArg == 1) {
+    rc = 2;
+  } else if (c == 's' && strncmp(azArg[0], "schema", n) == 0 && nArg < 3) {
+    meta_schema(nArg, azArg);
+  } else if (c == 's' && strncmp(azArg[0], "separator", n) == 0 && nArg == 2) {
+    sqlite3_snprintf(sizeof(p->separator),
+                     p->separator,
+                     "%.*s",
+                     (int)sizeof(p->separator) - 1,
+                     azArg[1]);
+  } else if (c == 's' && strncmp(azArg[0], "show", n) == 0 && nArg == 1) {
+    int i;
+    fprintf(p->out, "%9.9s: %s\n", "echo", p->echoOn ? "on" : "off");
+    fprintf(p->out, "%9.9s: %s\n", "headers", p->showHeader ? "on" : "off");
+    fprintf(p->out, "%9.9s: %s\n", "mode", modeDescr[p->mode]);
+    fprintf(p->out, "%9.9s: ", "nullvalue");
+    output_c_string(p->out, p->nullvalue);
+    fprintf(p->out, "\n");
+    fprintf(p->out,
+            "%9.9s: %s\n",
+            "output",
+            strlen30(p->outfile) ? p->outfile : "stdout");
+    fprintf(p->out, "%9.9s: ", "separator");
+    output_c_string(p->out, p->separator);
+    fprintf(p->out, "\n");
+    fprintf(p->out, "%9.9s: ", "width");
+    for (i = 0; i < (int)ArraySize(p->colWidth) && p->colWidth[i] != 0; i++) {
+      fprintf(p->out, "%d ", p->colWidth[i]);
+    }
+    fprintf(p->out, "\n");
+  } else if (c == 't' && n > 1 && strncmp(azArg[0], "tables", n) == 0 &&
+             nArg < 3) {
+    meta_tables(nArg, azArg);
+  } else if (c == 't' && n > 4 && strncmp(azArg[0], "timeout", n) == 0 &&
+             nArg == 2) {
+    sqlite3_busy_timeout(db, (int)integerValue(azArg[1]));
+  } else if (HAS_TIMER && c == 't' && n >= 5 &&
+             strncmp(azArg[0], "timer", n) == 0 && nArg == 2) {
+    enableTimer = booleanValue(azArg[1]);
+  } else if (c == 't' && strncmp(azArg[0], "trace", n) == 0 && nArg > 1) {
+    output_file_close(p->traceOut);
+    p->traceOut = output_file_open(azArg[1]);
+  } else if (c == 'v' && strncmp(azArg[0], "version", n) == 0) {
+    fprintf(p->out, "osquery %s\n", osquery::kVersion.c_str());
+    fprintf(p->out, "using SQLite %s\n", sqlite3_libversion());
+  } else if (c == 'w' && strncmp(azArg[0], "width", n) == 0 && nArg > 1) {
+    int j;
+    assert(nArg <= ArraySize(azArg));
+    for (j = 1; j < nArg && j < ArraySize(p->colWidth); j++) {
+      p->colWidth[j - 1] = (int)integerValue(azArg[j]);
+    }
+  } else {
+    fprintf(stderr,
+            "Error: unknown command or invalid arguments: "
+            " \"%s\". Enter \".help\" for help\n",
+            azArg[0]);
+    rc = 1;
+  }
+
+  return rc;
+}
+
+/*
+** Return TRUE if a semicolon occurs anywhere in the first N characters
+** of string z[].
+*/
+static int line_contains_semicolon(const char *z, int N) {
+  int i;
+  if (z == nullptr) {
+    return 0;
+  }
+
+  for (i = 0; i < N; i++) {
+    if (z[i] == ';')
+      return 1;
+  }
+  return 0;
+}
+
+/*
+** Test to see if a line consists entirely of whitespace.
+*/
+static int _all_whitespace(const char *z) {
+  for (; *z; z++) {
+    if (IsSpace(z[0]))
+      continue;
+    if (*z == '/' && z[1] == '*') {
+      z += 2;
+      while (*z && (*z != '*' || z[1] != '/')) {
+        z++;
+      }
+      if (*z == 0)
+        return 0;
+      z++;
+      continue;
+    }
+    if (*z == '-' && z[1] == '-') {
+      z += 2;
+      while (*z && *z != '\n') {
+        z++;
+      }
+      if (*z == 0)
+        return 1;
+      continue;
+    }
+    return 0;
+  }
+  return 1;
+}
+
+/*
+** Return TRUE if the line typed in is an SQL command terminator other
+** than a semi-colon.  The SQL Server style "go" command is understood
+** as is the Oracle "/".
+*/
+static int line_is_command_terminator(const char *zLine) {
+  while (IsSpace(zLine[0])) {
+    zLine++;
+  };
+  if (zLine[0] == '/' && _all_whitespace(&zLine[1])) {
+    return 1; /* Oracle */
+  }
+  if (ToLower(zLine[0]) == 'g' && ToLower(zLine[1]) == 'o' &&
+      _all_whitespace(&zLine[2])) {
+    return 1; /* SQL Server */
+  }
+  return 0;
+}
+
+/*
+** Return true if zSql is a complete SQL statement.  Return false if it
+** ends in the middle of a string literal or C-style comment.
+*/
+static int line_is_complete(char *zSql, int nSql) {
+  int rc;
+  if (zSql == 0)
+    return 1;
+  zSql[nSql] = ';';
+  zSql[nSql + 1] = 0;
+  rc = sqlite3_complete(zSql);
+  zSql[nSql] = 0;
+  return rc;
+}
+
+/*
+** Read input from *in and process it.  If *in==0 then input
+** is interactive - the user is typing it it.  Otherwise, input
+** is coming from a file or device.  A prompt is issued and history
+** is saved only if input is interactive.  An interrupt signal will
+** cause this routine to exit immediately, unless input is interactive.
+**
+** Return the number of errors.
+*/
+static int process_input(struct callback_data *p, FILE *in) {
+  char *zLine = 0; /* A single input line */
+  char *zSql = 0; /* Accumulated SQL text */
+  int nLine; /* Length of current line */
+  int nSql = 0; /* Bytes of zSql[] used */
+  int nAlloc = 0; /* Allocated zSql[] space */
+  int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
+  char *zErrMsg; /* Error message returned */
+  int rc; /* Error code */
+  int errCnt = 0; /* Number of errors seen */
+  int lineno = 0; /* Current line number */
+  int startline = 0; /* Line number for start of current input */
+
+  while (errCnt == 0 || !bail_on_error || (in == 0 && stdin_is_interactive)) {
+    fflush(p->out);
+    zLine = one_input_line(in, zLine, nSql > 0);
+    if (zLine == 0) {
+      /* End of input */
+      if (stdin_is_interactive)
+        printf("\n");
+      break;
+    }
+    if (seenInterrupt) {
+      if (in != 0)
+        break;
+      seenInterrupt = 0;
+    }
+    lineno++;
+    if (nSql == 0 && _all_whitespace(zLine)) {
+      if (p->echoOn)
+        printf("%s\n", zLine);
+      continue;
+    }
+    if (zLine && zLine[0] == '.' && nSql == 0) {
+      if (p->echoOn)
+        printf("%s\n", zLine);
+      rc = do_meta_command(zLine, p);
+      if (rc == 2) { /* exit requested */
+        break;
+      } else if (rc) {
+        errCnt++;
+      }
+      continue;
+    }
+    if (line_is_command_terminator(zLine) && line_is_complete(zSql, nSql)) {
+      memcpy(zLine, ";", 2);
+    }
+    nLine = strlen30(zLine);
+    if (nSql + nLine + 2 >= nAlloc) {
+      nAlloc = nSql + nLine + 100;
+      zSql = (char *)realloc(zSql, nAlloc);
+      if (zSql == 0) {
+        fprintf(stderr, "Error: out of memory\n");
+        exit(1);
+      }
+    }
+    nSqlPrior = nSql;
+    if (nSql == 0) {
+      int i;
+      for (i = 0; zLine[i] && IsSpace(zLine[i]); i++) {
+      }
+      assert(nAlloc > 0 && zSql != nullptr);
+      if (zSql != nullptr) {
+        memcpy(zSql, zLine + i, nLine + 1 - i);
+      }
+      startline = lineno;
+      nSql = nLine - i;
+    } else {
+      zSql[nSql++] = '\n';
+      memcpy(zSql + nSql, zLine, nLine + 1);
+      nSql += nLine;
+    }
+    if (nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql - nSqlPrior) &&
+        sqlite3_complete(zSql)) {
+      p->cnt = 0;
+      BEGIN_TIMER;
+      rc = shell_exec(zSql, shell_callback, p, &zErrMsg);
+      END_TIMER;
+      if (rc || zErrMsg) {
+        char zPrefix[100];
+        if (in != 0 || !stdin_is_interactive) {
+          sqlite3_snprintf(
+              sizeof(zPrefix), zPrefix, "Error: near line %d:", startline);
+        } else {
+          sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
+        }
+        if (zErrMsg != 0) {
+          fprintf(stderr, "%s %s\n", zPrefix, zErrMsg);
+          sqlite3_free(zErrMsg);
+          zErrMsg = 0;
+        }
+        errCnt++;
+      }
+      nSql = 0;
+    } else if (nSql && _all_whitespace(zSql)) {
+      if (p->echoOn)
+        printf("%s\n", zSql);
+      nSql = 0;
+    }
+  }
+  if (nSql) {
+    if (!_all_whitespace(zSql)) {
+      fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
+    }
+    free(zSql);
+  }
+  free(zLine);
+  return errCnt > 0;
+}
+
+/*
+** Initialize the state information in data
+*/
+static void main_init(struct callback_data *data) {
+  memset(data, 0, sizeof(*data));
+  data->prettyPrint = new struct prettyprint_data();
+  data->mode = MODE_Pretty;
+  memcpy(data->separator, "|", 2);
+  data->showHeader = 1;
+  sqlite3_config(SQLITE_CONFIG_URI, 1);
+  sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
+  sqlite3_snprintf(sizeof(mainPrompt), mainPrompt, "osquery> ");
+  sqlite3_snprintf(sizeof(continuePrompt), continuePrompt, "    ...> ");
+  sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
+}
+
+/*
+** Output text to the console in a font that attracts extra attention.
+*/
+static void printBold(const char *zText) { printf("\033[1m%s\033[0m", zText); }
+
+namespace osquery {
+
+int launchIntoShell(int argc, char **argv) {
+  struct callback_data data;
+  main_init(&data);
+
+  {
+    // Hold the manager connection instance again in callbacks.
+    auto dbc = SQLiteDBManager::get();
+    // Add some shell-specific functions to the instance.
+    sqlite3_create_function(
+        dbc.db(), "shellstatic", 0, SQLITE_UTF8, 0, shellstaticFunc, 0, 0);
+  }
+
+  Argv0 = argv[0];
+  stdin_is_interactive = isatty(0);
+
+  // SQLite: Make sure we have a valid signal handler early
+  signal(SIGINT, interrupt_handler);
+
+  int warnInmemoryDb = 1;
+  data.zDbFilename = ":memory:";
+  data.out = stdout;
+
+  // Set modes and settings from CLI flags.
+  if (FLAGS_list) {
+    data.mode = MODE_List;
+  } else if (FLAGS_line) {
+    data.mode = MODE_Line;
+  } else if (FLAGS_csv) {
+    data.mode = MODE_Csv;
+    memcpy(data.separator, ",", 2);
+  } else {
+    data.mode = MODE_Pretty;
+  }
+
+  sqlite3_snprintf(sizeof(data.separator), data.separator, "%s",
+    FLAGS_separator.c_str());
+  sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, "%s",
+    FLAGS_nullvalue.c_str());
+
+  int rc = 0;
+  if (FLAGS_L == true || FLAGS_A.size() > 0) {
+    // Helper meta commands from shell switches.
+    std::string query = (FLAGS_L) ? ".tables" : ".all " + FLAGS_A;
+    char *cmd = new char[query.size() + 1];
+    memset(cmd, 0, query.size() + 1);
+    std::copy(query.begin(), query.end(), cmd);
+    rc = do_meta_command(cmd, &data);
+  } else if (argc > 1 && argv[1] != nullptr) {
+    // Run a command or statement from CLI
+    char *query = argv[1];
+    char *error = 0;
+    if (query[0] == '.') {
+      rc = do_meta_command(query, &data);
+      rc = (rc == 2) ? 0 : rc;
+    } else {
+      rc = shell_exec(query, shell_callback, &data, &error);
+      if (error != 0) {
+        fprintf(stderr, "Error: %s\n", error);
+        return (rc != 0) ? rc : 1;
+      } else if (rc != 0) {
+        fprintf(stderr, "Error: unable to process SQL \"%s\"\n", query);
+        return rc;
+      }
+    }
+  } else {
+    // Run commands received from standard input
+    if (stdin_is_interactive) {
+      printBold("osquery");
+      printf(
+          " - being built, with love, at Samsung(not Facebook)\n"
+          "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
+      if (warnInmemoryDb) {
+        printf("Using a ");
+        printBold("virtual database");
+        printf(". Need help, type '.help'\n");
+      }
+
+      auto history_file = osquery::osqueryHomeDirectory() + "/.history";
+      read_history(history_file.c_str());
+      rc = process_input(&data, 0);
+      stifle_history(100);
+      write_history(history_file.c_str());
+    } else {
+      rc = process_input(&data, stdin);
+    }
+  }
+
+  set_table_name(&data, 0);
+  sqlite3_free(data.zFreeOnClose);
+
+  if (data.prettyPrint != nullptr) {
+    delete data.prettyPrint;
+  }
+  return rc;
+}
+}
diff --git a/src/osquery/devtools/tests/printer_tests.cpp b/src/osquery/devtools/tests/printer_tests.cpp
new file mode 100644 (file)
index 0000000..61626a0
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/logger.h>
+
+#include "osquery/devtools/devtools.h"
+
+namespace osquery {
+
+class PrinterTests : public testing::Test {
+ public:
+  QueryData q;
+  std::vector<std::string> order;
+  void SetUp() {
+    order = {"name", "age", "food", "number"};
+    q = {
+        {
+         {"name", "Mike Jones"},
+         {"age", "39"},
+         {"food", "mac and cheese"},
+         {"number", "1"},
+        },
+        {
+         {"name", "John Smith"},
+         {"age", "44"},
+         {"food", "peanut butter and jelly"},
+         {"number", "2"},
+        },
+        {
+         {"name", "Doctor Who"},
+         {"age", "2000"},
+         {"food", "fish sticks and custard"},
+         {"number", "11"},
+        },
+    };
+  }
+};
+
+TEST_F(PrinterTests, test_compute_query_data_lengths) {
+  std::map<std::string, size_t> lengths;
+  for (const auto& row : q) {
+    computeRowLengths(row, lengths);
+  }
+
+  // Check that all value lengths were maxed.
+  std::map<std::string, size_t> expected = {
+      {"name", 10}, {"age", 4}, {"food", 23}, {"number", 2}};
+  EXPECT_EQ(lengths, expected);
+
+  // Then compute lengths of column names.
+  computeRowLengths(q.front(), lengths, true);
+  expected = {{"name", 10}, {"age", 4}, {"food", 23}, {"number", 6}};
+  EXPECT_EQ(lengths, expected);
+}
+
+TEST_F(PrinterTests, test_generate_separator) {
+  std::map<std::string, size_t> lengths;
+  for (const auto& row : q) {
+    computeRowLengths(row, lengths);
+  }
+
+  auto results = generateToken(lengths, order);
+  auto expected = "+------------+------+-------------------------+----+\n";
+  EXPECT_EQ(results, expected);
+}
+
+TEST_F(PrinterTests, test_generate_header) {
+  std::map<std::string, size_t> lengths;
+  for (const auto& row : q) {
+    computeRowLengths(row, lengths);
+  }
+
+  auto results = generateHeader(lengths, order);
+  auto expected = "| name       | age  | food                    | number |\n";
+  EXPECT_EQ(results, expected);
+}
+
+TEST_F(PrinterTests, test_generate_row) {
+  std::map<std::string, size_t> lengths;
+  for (const auto& row : q) {
+    computeRowLengths(row, lengths);
+  }
+
+  auto results = generateRow(q.front(), lengths, order);
+  auto expected = "| Mike Jones | 39   | mac and cheese          | 1  |\n";
+  EXPECT_EQ(results, expected);
+}
+
+TEST_F(PrinterTests, test_unicode) {
+  Row r = {{"name", "Àlex Smith"}};
+  std::map<std::string, size_t> lengths;
+  computeRowLengths(r, lengths);
+
+  std::map<std::string, size_t> expected = {{"name", 10}};
+  EXPECT_EQ(lengths, expected);
+}
+}
diff --git a/src/osquery/dispatcher/CMakeLists.txt b/src/osquery/dispatcher/CMakeLists.txt
new file mode 100644 (file)
index 0000000..82b34a2
--- /dev/null
@@ -0,0 +1,19 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_dispatcher dispatcher.cpp
+                                                                          scheduler.cpp)
+
+FILE(GLOB OSQUERY_DISPATCHER_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_DISPATCHER_TESTS})
diff --git a/src/osquery/dispatcher/dispatcher.cpp b/src/osquery/dispatcher/dispatcher.cpp
new file mode 100644 (file)
index 0000000..4c7f10d
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+#include "osquery/core/conversions.h"
+#include "osquery/dispatcher/dispatcher.h"
+
+using namespace apache::thrift::concurrency;
+
+namespace osquery {
+
+/// The worker_threads define the default thread pool size.
+FLAG(int32, worker_threads, 4, "Number of work dispatch threads");
+
+void interruptableSleep(size_t milli) {
+  boost::this_thread::sleep(boost::posix_time::milliseconds(milli));
+}
+
+Dispatcher::Dispatcher() {
+  thread_manager_ = InternalThreadManager::newSimpleThreadManager(
+          (size_t)FLAGS_worker_threads, 0);
+  auto thread_factory = ThriftThreadFactory(new PosixThreadFactory());
+  thread_manager_->threadFactory(thread_factory);
+  thread_manager_->start();
+}
+
+Dispatcher::~Dispatcher() { join(); }
+
+Status Dispatcher::add(ThriftInternalRunnableRef task) {
+  auto& self = instance();
+  try {
+    if (self.state() != InternalThreadManager::STARTED) {
+      self.thread_manager_->start();
+    }
+    instance().thread_manager_->add(task, 0, 0);
+  } catch (std::exception& e) {
+    return Status(1, e.what());
+  }
+  return Status(0, "OK");
+}
+
+Status Dispatcher::addService(InternalRunnableRef service) {
+  if (service->hasRun()) {
+    return Status(1, "Cannot schedule a service twice");
+  }
+
+  auto& self = instance();
+  auto thread = std::make_shared<boost::thread>(
+      boost::bind(&InternalRunnable::run, &*service));
+  self.service_threads_.push_back(thread);
+  self.services_.push_back(std::move(service));
+  return Status(0, "OK");
+}
+
+InternalThreadManagerRef Dispatcher::getThreadManager() const {
+  return instance().thread_manager_;
+}
+
+void Dispatcher::join() {
+  if (instance().thread_manager_ != nullptr) {
+    instance().thread_manager_->stop();
+    instance().thread_manager_->join();
+  }
+}
+
+void Dispatcher::joinServices() {
+  for (auto& thread : instance().service_threads_) {
+    thread->join();
+  }
+}
+
+void Dispatcher::stopServices() {
+  auto& self = instance();
+  for (const auto& service : self.services_) {
+    while (true) {
+      // Wait for each thread's entry point (start) meaning the thread context
+      // was allocated and (run) was called by boost::thread started.
+      if (service->hasRun()) {
+        break;
+      }
+      // We only need to check if std::terminate is called very quickly after
+      // the boost::thread is created.
+      ::usleep(200);
+    }
+    service->stop();
+  }
+
+  for (auto& thread : self.service_threads_) {
+    thread->interrupt();
+  }
+}
+
+InternalThreadManager::STATE Dispatcher::state() const {
+  return instance().thread_manager_->state();
+}
+
+void Dispatcher::addWorker(size_t value) {
+  instance().thread_manager_->addWorker(value);
+}
+
+void Dispatcher::removeWorker(size_t value) {
+  instance().thread_manager_->removeWorker(value);
+}
+
+size_t Dispatcher::idleWorkerCount() const {
+  return instance().thread_manager_->idleWorkerCount();
+}
+
+size_t Dispatcher::workerCount() const {
+  return instance().thread_manager_->workerCount();
+}
+
+size_t Dispatcher::pendingTaskCount() const {
+  return instance().thread_manager_->pendingTaskCount();
+}
+
+size_t Dispatcher::totalTaskCount() const {
+  return instance().thread_manager_->totalTaskCount();
+}
+
+size_t Dispatcher::pendingTaskCountMax() const {
+  return instance().thread_manager_->pendingTaskCountMax();
+}
+
+size_t Dispatcher::expiredTaskCount() const {
+  return instance().thread_manager_->expiredTaskCount();
+}
+}
diff --git a/src/osquery/dispatcher/dispatcher.h b/src/osquery/dispatcher/dispatcher.h
new file mode 100644 (file)
index 0000000..f4d23e5
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+#include <boost/thread.hpp>
+
+#include <osquery/core.h>
+
+#include <thrift/concurrency/Thread.h>
+#include <thrift/concurrency/ThreadManager.h>
+#include <thrift/concurrency/PosixThreadFactory.h>
+
+namespace osquery {
+
+using namespace apache::thrift::concurrency;
+
+/// Create easier to reference typedefs for Thrift layer implementations.
+#define SHARED_PTR_IMPL OSQUERY_THRIFT_POINTER::shared_ptr
+typedef apache::thrift::concurrency::ThreadManager InternalThreadManager;
+typedef SHARED_PTR_IMPL<InternalThreadManager> InternalThreadManagerRef;
+
+/**
+ * @brief Default number of threads in the thread pool.
+ *
+ * The amount of threads that the thread pool will be created with if another
+ * value is not specified on the command-line.
+ */
+extern const int kDefaultThreadPoolSize;
+
+class InternalRunnable : public Runnable {
+ public:
+  virtual ~InternalRunnable() {}
+  InternalRunnable() : run_(false) {}
+
+ public:
+  /// The boost::thread entrypoint.
+  void run() {
+    run_ = true;
+    start();
+  }
+
+  /// Check if the thread's entrypoint (run) executed, meaning thread context
+  /// was allocated.
+  bool hasRun() { return run_; }
+
+  /// The runnable may also tear down services before the thread context
+  /// is removed.
+  virtual void stop() {}
+
+ protected:
+  /// Require the runnable thread define an entrypoint.
+  virtual void start() = 0;
+
+ private:
+  bool run_;
+};
+
+/// An internal runnable used throughout osquery as dispatcher services.
+typedef std::shared_ptr<InternalRunnable> InternalRunnableRef;
+typedef std::shared_ptr<boost::thread> InternalThreadRef;
+/// A thrift internal runnable with variable pointer wrapping.
+typedef SHARED_PTR_IMPL<InternalRunnable> ThriftInternalRunnableRef;
+typedef SHARED_PTR_IMPL<PosixThreadFactory> ThriftThreadFactory;
+
+/**
+ * @brief Singleton for queuing asynchronous tasks to be executed in parallel
+ *
+ * Dispatcher is a singleton which can be used to coordinate the parallel
+ * execution of asynchronous tasks across an application. Internally,
+ * Dispatcher is back by the Apache Thrift thread pool.
+ */
+class Dispatcher : private boost::noncopyable {
+ public:
+  /**
+   * @brief The primary way to access the Dispatcher factory facility.
+   *
+   * @code{.cpp} auto dispatch = osquery::Dispatcher::instance(); @endcode
+   *
+   * @return The osquery::Dispatcher instance.
+   */
+  static Dispatcher& instance() {
+    static Dispatcher instance;
+    return instance;
+  }
+
+  /**
+   * @brief Add a task to the dispatcher.
+   *
+   * Adding tasks to the Dispatcher's thread pool requires you to create a
+   * "runnable" class which publicly implements Apache Thrift's Runnable
+   * class. Create a shared pointer to the class and you're all set to
+   * schedule work.
+   *
+   * @code{.cpp}
+   *   class TestRunnable : public apache::thrift::concurrency::Runnable {
+   *    public:
+   *     int* i;
+   *     TestRunnable(int* i) : i(i) {}
+   *     virtual void run() { ++*i; }
+   *   };
+   *
+   *   int i = 5;
+   *   Dispatcher::add(std::make_shared<TestRunnable>(&i);
+   *   while (dispatch->totalTaskCount() > 0) {}
+   *   assert(i == 6);
+   * @endcode
+   *
+   * @param task a C++11 std shared pointer to an instance of a class which
+   * publicly inherits from `apache::thrift::concurrency::Runnable`.
+   *
+   * @return osquery success status
+   */
+  static Status add(ThriftInternalRunnableRef task);
+
+  /// See `add`, but services are not limited to a thread poll size.
+  static Status addService(InternalRunnableRef service);
+
+  /**
+   * @brief Getter for the underlying thread manager instance.
+   *
+   * Use this getter if you'd like to perform some operations which the
+   * Dispatcher API doesn't support, but you are certain are supported by the
+   * backing Apache Thrift thread manager.
+   *
+   * Use this method with caution, as it only exists to allow developers to
+   * iterate quickly in the event that the pragmatic decision to access the
+   * underlying thread manager has been determined to be necessary.
+   *
+   * @code{.cpp}
+   *   auto t = osquery::Dispatcher::getThreadManager();
+   * @endcode
+   *
+   * @return a shared pointer to the Apache Thrift `ThreadManager` instance
+   * which is currently being used to orchestrate multi-threaded operations.
+   */
+  InternalThreadManagerRef getThreadManager() const;
+
+  /**
+   * @brief Joins the thread manager.
+   *
+   * This is the same as stop, except that it will block until all the workers
+   * have finished their work. At that point the ThreadManager will transition
+   * into the STOPPED state.
+   */
+  static void join();
+
+  /// See `join`, but applied to osquery services.
+  static void joinServices();
+
+  /// Destroy and stop all osquery service threads and service objects.
+  static void stopServices();
+
+  /**
+   * @brief Get the current state of the thread manager.
+   *
+   * @return an Apache Thrift STATE enum.
+   */
+  InternalThreadManager::STATE state() const;
+
+  /**
+   * @brief Add a worker thread.
+   *
+   * Use this method to add an additional thread to the thread pool.
+   *
+   * @param value is a size_t indicating how many additional worker threads
+   * should be added to the thread group. If not parameter is supplied, one
+   * worker thread is added.
+   *
+   * @see osquery::Dispatcher::removeWorker
+   */
+  static void addWorker(size_t value = 1);
+
+  /**
+   * @brief Remove a worker thread.
+   *
+   * Use this method to remove a thread from the thread pool.
+   *
+   * @param value is a size_t indicating how many additional worker threads
+   * should be removed from the thread group. If not parameter is supplied,
+   * one worker thread is removed.
+   *
+   * @see osquery::Dispatcher::addWorker
+   */
+  static void removeWorker(size_t value = 1);
+
+  /**
+   * @brief Gets the current number of idle worker threads.
+   *
+   * @return the number of idle worker threads.
+   */
+  size_t idleWorkerCount() const;
+
+  /**
+   * @brief Gets the current number of total worker threads.
+   *
+   * @return the current number of total worker threads.
+   */
+  size_t workerCount() const;
+
+  /**
+   * @brief Gets the current number of pending tasks.
+   *
+   * @return the current number of pending tasks.
+   */
+  size_t pendingTaskCount() const;
+
+  /**
+   * @brief Gets the current number of pending and executing tasks.
+   *
+   * @return the current number of pending and executing tasks.
+   */
+  size_t totalTaskCount() const;
+
+  /**
+   * @brief Gets the maximum pending task count. 0 indicates no maximum.
+   *
+   * @return the maximum pending task count. 0 indicates no maximum.
+   */
+  size_t pendingTaskCountMax() const;
+
+  /**
+   * @brief Gets the number of tasks which have been expired without being
+   * run.
+   *
+   * @return he number of tasks which have been expired without being run.
+   */
+  size_t expiredTaskCount() const;
+
+ private:
+  /**
+   * @brief Default constructor.
+   *
+   * Since instances of Dispatcher should only be created via instance(),
+   * Dispatcher's constructor is private.
+   */
+  Dispatcher();
+  Dispatcher(Dispatcher const&);
+  void operator=(Dispatcher const&);
+  virtual ~Dispatcher();
+
+ private:
+  /**
+   * @brief Internal shared pointer which references Thrift's thread manager
+   *
+   * All thread operations occur via Apache Thrift's ThreadManager class. This
+   * private member represents a shared pointer to an instantiation of that
+   * thread manager, which can be used to accomplish various threading
+   * objectives.
+   *
+   * @see getThreadManager
+   */
+  InternalThreadManagerRef thread_manager_;
+
+  /// The set of shared osquery service threads.
+  std::vector<InternalThreadRef> service_threads_;
+
+  /// The set of shared osquery services.
+  std::vector<InternalRunnableRef> services_;
+
+ private:
+  friend class ExtensionsTest;
+};
+
+/// Allow a dispatched thread to wait while processing or to prevent thrashing.
+void interruptableSleep(size_t milli);
+}
diff --git a/src/osquery/dispatcher/scheduler.cpp b/src/osquery/dispatcher/scheduler.cpp
new file mode 100644 (file)
index 0000000..5323e0c
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+#include <ctime>
+
+#include <osquery/config.h>
+#include <osquery/core.h>
+#include <osquery/database.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+
+#include "osquery/database/query.h"
+#include "osquery/dispatcher/scheduler.h"
+
+namespace osquery {
+
+FLAG(string,
+     host_identifier,
+     "hostname",
+     "Field used to identify the host running osquery (hostname, uuid)");
+
+FLAG(bool, enable_monitor, false, "Enable the schedule monitor");
+
+FLAG(uint64, schedule_timeout, 0, "Limit the schedule, 0 for no limit")
+
+Status getHostIdentifier(std::string& ident) {
+  if (FLAGS_host_identifier != "uuid") {
+    // use the hostname as the default machine identifier
+    ident = osquery::getHostname();
+    return Status(0, "OK");
+  }
+
+  // Lookup the host identifier (UUID) previously generated and stored.
+  auto status = getDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
+  if (!status.ok()) {
+    // The lookup failed, there is a problem accessing the database.
+    VLOG(1) << "Could not access database; using hostname as host identifier";
+    ident = osquery::getHostname();
+    return Status(0, "OK");
+  }
+
+  if (ident.size() == 0) {
+    // There was no uuid stored in the database, generate one and store it.
+    ident = osquery::generateHostUuid();
+    VLOG(1) << "Using uuid " << ident << " as host identifier";
+    return setDatabaseValue(kPersistentSettings, "hostIdentifier", ident);
+  }
+  return status;
+}
+
+inline SQL monitor(const std::string& name, const ScheduledQuery& query) {
+  // Snapshot the performance and times for the worker before running.
+  auto pid = std::to_string(getpid());
+  auto r0 = SQL::selectAllFrom("processes", "pid", EQUALS, pid);
+  auto t0 = time(nullptr);
+  auto sql = SQL(query.query);
+  // Snapshot the performance after, and compare.
+  auto t1 = time(nullptr);
+  auto r1 = SQL::selectAllFrom("processes", "pid", EQUALS, pid);
+  if (r0.size() > 0 && r1.size() > 0) {
+    size_t size = 0;
+    for (const auto& row : sql.rows()) {
+      for (const auto& column : row) {
+        size += column.first.size();
+        size += column.second.size();
+      }
+    }
+    Config::recordQueryPerformance(name, t1 - t0, size, r0[0], r1[0]);
+  }
+  return sql;
+}
+
+void launchQuery(const std::string& name, const ScheduledQuery& query) {
+  // Execute the scheduled query and create a named query object.
+  VLOG(1) << "Executing query: " << query.query;
+  auto sql = (FLAGS_enable_monitor) ? monitor(name, query) : SQL(query.query);
+
+  if (!sql.ok()) {
+    LOG(ERROR) << "Error executing query (" << query.query
+               << "): " << sql.getMessageString();
+    return;
+  }
+
+  // Fill in a host identifier fields based on configuration or availability.
+  std::string ident;
+  auto status = getHostIdentifier(ident);
+  if (!status.ok() || ident.empty()) {
+    ident = "<unknown>";
+  }
+
+  // A query log item contains an optional set of differential results or
+  // a copy of the most-recent execution alongside some query metadata.
+  QueryLogItem item;
+  item.name = name;
+  item.identifier = ident;
+  item.time = osquery::getUnixTime();
+  item.calendar_time = osquery::getAsciiTime();
+
+  if (query.options.count("snapshot") && query.options.at("snapshot")) {
+    // This is a snapshot query, emit results with a differential or state.
+    item.snapshot_results = std::move(sql.rows());
+    logSnapshotQuery(item);
+    return;
+  }
+
+  // Create a database-backed set of query results.
+  auto dbQuery = Query(name, query);
+  DiffResults diff_results;
+  // Add this execution's set of results to the database-tracked named query.
+  // We can then ask for a differential from the last time this named query
+  // was executed by exact matching each row.
+  status = dbQuery.addNewResults(sql.rows(), diff_results);
+  if (!status.ok()) {
+    LOG(ERROR) << "Error adding new results to database: " << status.what();
+    return;
+  }
+
+  if (diff_results.added.size() == 0 && diff_results.removed.size() == 0) {
+    // No diff results or events to emit.
+    return;
+  }
+
+  VLOG(1) << "Found results for query (" << name << ") for host: " << ident;
+  item.results = diff_results;
+  if (query.options.count("removed") && !query.options.at("removed")) {
+    item.results.removed.clear();
+  }
+
+  status = logQueryLogItem(item);
+  if (!status.ok()) {
+    LOG(ERROR) << "Error logging the results of query (" << query.query
+               << "): " << status.toString();
+  }
+}
+
+void SchedulerRunner::start() {
+  time_t t = std::time(nullptr);
+  struct tm* local = std::localtime(&t);
+  unsigned long int i = local->tm_sec;
+  for (; (timeout_ == 0) || (i <= timeout_); ++i) {
+    {
+      ConfigDataInstance config;
+      for (const auto& query : config.schedule()) {
+        if (i % query.second.splayed_interval == 0) {
+          launchQuery(query.first, query.second);
+        }
+      }
+    }
+    // Put the thread into an interruptible sleep without a config instance.
+    osquery::interruptableSleep(interval_ * 1000);
+  }
+}
+
+Status startScheduler() {
+  if (startScheduler(FLAGS_schedule_timeout, 1).ok()) {
+    Dispatcher::joinServices();
+    return Status(0, "OK");
+  }
+  return Status(1, "Could not start scheduler");
+}
+
+Status startScheduler(unsigned long int timeout, size_t interval) {
+  Dispatcher::addService(std::make_shared<SchedulerRunner>(timeout, interval));
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/dispatcher/scheduler.h b/src/osquery/dispatcher/scheduler.h
new file mode 100644 (file)
index 0000000..ccd0c40
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include "osquery/dispatcher/dispatcher.h"
+
+namespace osquery {
+
+/// A Dispatcher service thread that watches an ExtensionManagerHandler.
+class SchedulerRunner : public InternalRunnable {
+ public:
+  virtual ~SchedulerRunner() {}
+  SchedulerRunner(unsigned long int timeout, size_t interval)
+      : interval_(interval), timeout_(timeout) {}
+
+ public:
+  /// The Dispatcher thread entry point.
+  void start();
+
+ protected:
+  /// The UNIX domain socket path for the ExtensionManager.
+  std::map<std::string, size_t> splay_;
+  /// Interval in seconds between schedule steps.
+  size_t interval_;
+  /// Maximum number of steps.
+  unsigned long int timeout_;
+};
+
+/// Start quering according to the config's schedule
+Status startScheduler();
+
+/// Helper scheduler start with variable settings for testing.
+Status startScheduler(unsigned long int timeout, size_t interval);
+}
diff --git a/src/osquery/dispatcher/tests/dispatcher_tests.cpp b/src/osquery/dispatcher/tests/dispatcher_tests.cpp
new file mode 100644 (file)
index 0000000..4ece0a8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/make_shared.hpp>
+
+#include <gtest/gtest.h>
+
+#include "osquery/dispatcher/dispatcher.h"
+
+namespace osquery {
+
+class DispatcherTests : public testing::Test {};
+
+TEST_F(DispatcherTests, test_singleton) {
+  auto& one = Dispatcher::instance();
+  auto& two = Dispatcher::instance();
+  EXPECT_EQ(one.getThreadManager().get(), two.getThreadManager().get());
+}
+
+class TestRunnable : public InternalRunnable {
+ public:
+  int* i;
+  explicit TestRunnable(int* i) : i(i) {}
+  virtual void start() { ++*i; }
+};
+
+TEST_F(DispatcherTests, test_add_work) {
+  auto& dispatcher = Dispatcher::instance();
+  int base = 5;
+  int repetitions = 1;
+
+  int i = base;
+  for (int c = 0; c < repetitions; ++c) {
+    dispatcher.add(OSQUERY_THRIFT_POINTER::make_shared<TestRunnable>(&i));
+  }
+  while (dispatcher.totalTaskCount() > 0) {
+  }
+
+  EXPECT_EQ(i, base + repetitions);
+}
+}
diff --git a/src/osquery/distributed/CMakeLists.txt b/src/osquery/distributed/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f5a2f97
--- /dev/null
@@ -0,0 +1,4 @@
+ADD_OSQUERY_LIBRARY(osquery_distributed distributed.cpp)
+
+FILE(GLOB OSQUERY_DISTRIBUTED_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_DISTRIBUTED_TESTS})
diff --git a/src/osquery/distributed/distributed.cpp b/src/osquery/distributed/distributed.cpp
new file mode 100644 (file)
index 0000000..5647b1f
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sstream>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+
+#include "osquery/distributed/distributed.h"
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+FLAG(int32,
+     distributed_retries,
+     3,
+     "Times to retry reading/writing distributed queries");
+
+Status MockDistributedProvider::getQueriesJSON(std::string& query_json) {
+  query_json = queriesJSON_;
+  return Status();
+}
+
+Status MockDistributedProvider::writeResultsJSON(const std::string& results) {
+    resultsJSON_ = results;
+    return Status();
+}
+
+Status DistributedQueryHandler::parseQueriesJSON(
+    const std::string& query_json,
+    std::vector<DistributedQueryRequest>& requests) {
+  // Parse the JSON into a ptree
+  pt::ptree tree;
+  try {
+    std::stringstream query_stream(query_json);
+    pt::read_json(query_stream, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return Status(1, std::string("Error loading query JSON: ") + e.what());
+  }
+
+  // Parse the ptree into DistributedQueryRequests
+  std::vector<DistributedQueryRequest> results;
+  for (const auto& node : tree) {
+    const auto& request_tree = node.second;
+    DistributedQueryRequest request;
+    try {
+      request.query = request_tree.get_child("query").get_value<std::string>();
+      request.id = request_tree.get_child("id").get_value<std::string>();
+    } catch (const std::exception& e) {
+      return Status(1, std::string("Error parsing queries: ") + e.what());
+    }
+    results.push_back(request);
+  }
+
+  requests = std::move(results);
+
+  return Status();
+}
+
+SQL DistributedQueryHandler::handleQuery(const std::string& query_string) {
+  SQL query = SQL(query_string);
+  query.annotateHostInfo();
+  return query;
+}
+
+Status DistributedQueryHandler::serializeResults(
+    const std::vector<std::pair<DistributedQueryRequest, SQL> >& results,
+    pt::ptree& tree) {
+  try {
+    pt::ptree& res_tree = tree.put_child("results", pt::ptree());
+    for (const auto& result : results) {
+      DistributedQueryRequest request = result.first;
+      SQL sql = result.second;
+      pt::ptree& child = res_tree.put_child(request.id, pt::ptree());
+      child.put("status", sql.getStatus().getCode());
+      pt::ptree& rows_child = child.put_child("rows", pt::ptree());
+      Status s = serializeQueryData(sql.rows(), rows_child);
+      if (!s.ok()) {
+        return s;
+      }
+    }
+  } catch (const std::exception& e) {
+    return Status(1, std::string("Error serializing results: ") + e.what());
+  }
+  return Status();
+}
+
+Status DistributedQueryHandler::doQueries() {
+  // Get and parse the queries
+  Status status;
+  std::string query_json;
+  int retries = 0;
+  do {
+    status = provider_->getQueriesJSON(query_json);
+    ++retries;
+  } while (!status.ok() && retries <= FLAGS_distributed_retries);
+  if (!status.ok()) {
+    return status;
+  }
+
+  std::vector<DistributedQueryRequest> requests;
+  status = parseQueriesJSON(query_json, requests);
+  if (!status.ok()) {
+    return status;
+  }
+
+  // Run the queries
+  std::vector<std::pair<DistributedQueryRequest, SQL> > query_results;
+  std::set<std::string> successful_query_ids;
+  for (const auto& request : requests) {
+    if (executedRequestIds_.find(request.id) != executedRequestIds_.end()) {
+      // We've already successfully returned results for this request, don't
+      // process it again.
+      continue;
+    }
+    SQL query_result = handleQuery(request.query);
+    if (query_result.ok()) {
+      successful_query_ids.insert(request.id);
+    }
+    query_results.push_back({request, query_result});
+  }
+
+  // Serialize the results
+  pt::ptree serialized_results;
+  serializeResults(query_results, serialized_results);
+  std::string json;
+  try {
+    std::ostringstream ss;
+    pt::write_json(ss, serialized_results, false);
+    json = ss.str();
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return Status(1, e.what());
+  }
+
+  // Write the results
+  retries = 0;
+  do {
+    status = provider_->writeResultsJSON(json);
+    ++retries;
+  } while (!status.ok() && retries <= FLAGS_distributed_retries);
+  if (!status.ok()) {
+    return status;
+  }
+
+  // Only note that the queries were successfully completed if we were actually
+  // able to write the results.
+  executedRequestIds_.insert(successful_query_ids.begin(),
+                             successful_query_ids.end());
+
+  return status;
+}
+}
diff --git a/src/osquery/distributed/distributed.h b/src/osquery/distributed/distributed.h
new file mode 100644 (file)
index 0000000..fc53f01
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <set>
+#include <vector>
+
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/sql.h>
+
+namespace osquery {
+
+/**
+ * @brief This is an interface for distributed query "providers"
+ *
+ * Providers implement the communication between the distributed query master
+ * and the individual host. A provider may utilize any communications strategy
+ * that supports reading and writing JSON (i.e. HTTPS requests, reading from a
+ * file, querying a message queue, etc.)
+ */
+class IDistributedProvider {
+public:
+  virtual ~IDistributedProvider() {}
+
+  /*
+   * @brief Get the JSON string containing the queries to be executed
+   *
+   * @param query_json A string to fill with the retrieved JSON
+   *
+   * @return osquery::Status indicating success or failure of the operation
+   */
+  virtual Status getQueriesJSON(std::string& query_json) = 0;
+
+  /*
+   * @brief Write the results JSON back to the master
+   *
+   * @param results A string containing the results JSON
+   *
+   * @return osquery::Status indicating success or failure of the operation
+   */
+  virtual Status writeResultsJSON(const std::string& results) = 0;
+};
+
+/**
+ * @brief A mocked implementation of IDistributedProvider
+ *
+ * This implementation is useful for writing unit tests of the
+ * DistributedQueryHandler functionality.
+ */
+class MockDistributedProvider : public IDistributedProvider {
+public:
+ // These methods just read/write the corresponding public members
+  Status getQueriesJSON(std::string& query_json) override;
+  Status writeResultsJSON(const std::string& results) override;
+
+  std::string queriesJSON_;
+  std::string resultsJSON_;
+};
+
+/**
+ * @brief Small struct containing the query and ID information for a
+ * distributed query
+ */
+struct DistributedQueryRequest {
+public:
+ explicit DistributedQueryRequest() {}
+ explicit DistributedQueryRequest(const std::string& q, const std::string& i)
+     : query(q), id(i) {}
+  std::string query;
+  std::string id;
+};
+
+/**
+ * @brief The main handler class for distributed queries
+ *
+ * This class is responsible for implementing the core functionality of
+ * distributed queries. It manages state, uses the provider to read/write from
+ * the master, and executes queries.
+ */
+class DistributedQueryHandler {
+public:
+ /**
+  * @brief Construct a new handler with the given provider
+  *
+  * @param provider The provider used retrieving queries and writing results
+  */
+ explicit DistributedQueryHandler(
+     std::unique_ptr<IDistributedProvider> provider)
+     : provider_(std::move(provider)) {}
+
+ /**
+  * @brief Retrieve queries, run them, and write results
+  *
+  * This is the core method of DistributedQueryHandler, tying together all the
+  * other components to read the requests from the provider, execute the
+  * queries, and write the results back to the provider.
+  *
+  * @return osquery::Status indicating success or failure of the operation
+  */
+ Status doQueries();
+
+ /**
+  * @brief Run and annotate an individual query
+  *
+  * @param query_string A string containing the query to be executed
+  *
+  * @return A SQL object containing the (annotated) query results
+  */
+ static SQL handleQuery(const std::string& query_string);
+
+ /**
+  * @brief Serialize the results of all requests into a ptree
+  *
+  * @param results The vector of requests and results
+  * @param tree The tree to serialize results into
+  *
+  * @return osquery::Status indicating success or failure of the operation
+  */
+ static Status serializeResults(
+     const std::vector<std::pair<DistributedQueryRequest, SQL> >& results,
+     boost::property_tree::ptree& tree);
+
+ /**
+  * @brief Parse the query JSON into the individual query objects
+  *
+  * @param query_json The JSON string containing the queries
+  * @param requests A vector to fill with the query objects
+  *
+  * @return osquery::Status indicating success or failure of the parsing
+  */
+  static Status parseQueriesJSON(const std::string& query_json,
+                                 std::vector<DistributedQueryRequest>& requests);
+
+private:
+  // The provider used to read and write queries and results
+  std::unique_ptr<IDistributedProvider> provider_;
+
+  // Used to store already executed queries to avoid duplication. (Some master
+  // configurations may asynchronously process the results of requests, so a
+  // request might be seen by the host after it has already been executed.)
+  std::set<std::string> executedRequestIds_;
+};
+
+} // namespace osquery
diff --git a/src/osquery/distributed/tests/distributed_tests.cpp b/src/osquery/distributed/tests/distributed_tests.cpp
new file mode 100644 (file)
index 0000000..81b0607
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <iostream>
+
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <gtest/gtest.h>
+
+#include <osquery/core.h>
+#include <osquery/sql.h>
+
+#include "osquery/distributed/distributed.h"
+#include "osquery/sql/sqlite_util.h"
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+// Distributed tests expect an SQL implementation for queries.
+REGISTER_INTERNAL(SQLiteSQLPlugin, "sql", "sql");
+
+class DistributedTests : public testing::Test {};
+
+TEST_F(DistributedTests, test_test_distributed_provider) {
+  MockDistributedProvider p;
+  std::string query_string = "['foo']";
+  std::string result_string = "['bar']";
+
+  p.queriesJSON_ = query_string;
+  std::string query_json;
+  Status s = p.getQueriesJSON(query_json);
+  ASSERT_EQ(Status(), s);
+  EXPECT_EQ(query_string, query_json);
+
+  s = p.writeResultsJSON(result_string);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(result_string, p.resultsJSON_);
+}
+
+TEST_F(DistributedTests, test_parse_query_json) {
+  std::string request_json = "[{\"query\": \"foo\", \"id\": \"bar\"}]";
+  std::vector<DistributedQueryRequest> requests;
+  Status s = DistributedQueryHandler::parseQueriesJSON(request_json, requests);
+  ASSERT_EQ(Status(), s);
+  EXPECT_EQ(1, requests.size());
+  EXPECT_EQ("foo", requests[0].query);
+  EXPECT_EQ("bar", requests[0].id);
+
+  std::string bad_json =
+      "[{\"query\": \"foo\", \"id\": \"bar\"}, {\"query\": \"b\"}]";
+  requests.clear();
+  s = DistributedQueryHandler::parseQueriesJSON(bad_json, requests);
+  ASSERT_FALSE(s.ok());
+  EXPECT_EQ(0, requests.size());
+}
+
+TEST_F(DistributedTests, test_handle_query) {
+  // Access to the internal SQL implementation is only available in core.
+  SQL query = DistributedQueryHandler::handleQuery("SELECT hour from time");
+  ASSERT_TRUE(query.ok());
+  QueryData rows = query.rows();
+  ASSERT_EQ(1, rows.size());
+  EXPECT_EQ(rows[0]["_source_host"], getHostname());
+
+  query = DistributedQueryHandler::handleQuery("bad query");
+  ASSERT_FALSE(query.ok());
+  rows = query.rows();
+  ASSERT_EQ(0, rows.size());
+}
+
+TEST_F(DistributedTests, test_serialize_results_empty) {
+  DistributedQueryRequest r0("foo", "foo_id");
+  MockSQL q0 = MockSQL();
+  pt::ptree tree;
+
+  DistributedQueryHandler::serializeResults({{r0, q0}}, tree);
+
+  EXPECT_EQ(0, tree.get<int>("results.foo_id.status"));
+  EXPECT_TRUE(tree.get_child("results.foo_id.rows").empty());
+}
+
+TEST_F(DistributedTests, test_serialize_results_basic) {
+  DistributedQueryRequest r0("foo", "foo_id");
+  QueryData rows0 = {
+      {{"foo0", "foo0_val"}, {"bar0", "bar0_val"}},
+      {{"foo1", "foo1_val"}, {"bar1", "bar1_val"}},
+  };
+  MockSQL q0 = MockSQL(rows0);
+  pt::ptree tree;
+
+  DistributedQueryHandler::serializeResults({{r0, q0}}, tree);
+
+  EXPECT_EQ(0, tree.get<int>("results.foo_id.status"));
+
+  const pt::ptree& tree_rows = tree.get_child("results.foo_id.rows");
+  EXPECT_EQ(2, tree_rows.size());
+
+  auto row = tree_rows.begin();
+  EXPECT_EQ("foo0_val", row->second.get<std::string>("foo0"));
+  EXPECT_EQ("bar0_val", row->second.get<std::string>("bar0"));
+  ++row;
+  EXPECT_EQ("foo1_val", row->second.get<std::string>("foo1"));
+  EXPECT_EQ("bar1_val", row->second.get<std::string>("bar1"));
+}
+
+TEST_F(DistributedTests, test_serialize_results_multiple) {
+  DistributedQueryRequest r0("foo", "foo_id");
+  QueryData rows0 = {
+      {{"foo0", "foo0_val"}, {"bar0", "bar0_val"}},
+      {{"foo1", "foo1_val"}, {"bar1", "bar1_val"}},
+  };
+  MockSQL q0 = MockSQL(rows0);
+
+  DistributedQueryRequest r1("bar", "bar_id");
+  MockSQL q1 = MockSQL({}, Status(1, "Fail"));
+
+  pt::ptree tree;
+
+  DistributedQueryHandler::serializeResults({{r0, q0}, {r1, q1}}, tree);
+
+  EXPECT_EQ(0, tree.get<int>("results.foo_id.status"));
+  const pt::ptree& tree_rows = tree.get_child("results.foo_id.rows");
+  EXPECT_EQ(2, tree_rows.size());
+  auto row = tree_rows.begin();
+  EXPECT_EQ("foo0_val", row->second.get<std::string>("foo0"));
+  EXPECT_EQ("bar0_val", row->second.get<std::string>("bar0"));
+  ++row;
+  EXPECT_EQ("foo1_val", row->second.get<std::string>("foo1"));
+  EXPECT_EQ("bar1_val", row->second.get<std::string>("bar1"));
+
+  EXPECT_EQ(1, tree.get<int>("results.bar_id.status"));
+  const pt::ptree& fail_rows = tree.get_child("results.bar_id.rows");
+  EXPECT_EQ(0, fail_rows.size());
+}
+
+TEST_F(DistributedTests, test_do_queries) {
+  // Access to the internal SQL implementation is only available in core.
+  auto provider_raw = new MockDistributedProvider();
+  provider_raw->queriesJSON_ =
+      "[ \
+      {\"query\": \"SELECT hour FROM time\", \"id\": \"hour\"},\
+      {\"query\": \"bad\", \"id\": \"bad\"},\
+      {\"query\": \"SELECT minutes FROM time\", \"id\": \"minutes\"}\
+    ]";
+  std::unique_ptr<MockDistributedProvider>
+    provider(provider_raw);
+  DistributedQueryHandler handler(std::move(provider));
+
+  Status s = handler.doQueries();
+  ASSERT_EQ(Status(), s);
+
+  pt::ptree tree;
+  std::istringstream json_stream(provider_raw->resultsJSON_);
+  ASSERT_NO_THROW(pt::read_json(json_stream, tree));
+
+  {
+    EXPECT_EQ(0, tree.get<int>("results.hour.status"));
+    const pt::ptree& tree_rows = tree.get_child("results.hour.rows");
+    EXPECT_EQ(1, tree_rows.size());
+    auto row = tree_rows.begin();
+    EXPECT_GE(row->second.get<int>("hour"), 0);
+    EXPECT_LE(row->second.get<int>("hour"), 24);
+    EXPECT_EQ(getHostname(), row->second.get<std::string>("_source_host"));
+  }
+
+  {
+    // this query should have failed
+    EXPECT_EQ(1, tree.get<int>("results.bad.status"));
+    const pt::ptree& tree_rows = tree.get_child("results.bad.rows");
+    EXPECT_EQ(0, tree_rows.size());
+  }
+
+  {
+    EXPECT_EQ(0, tree.get<int>("results.minutes.status"));
+    const pt::ptree& tree_rows = tree.get_child("results.minutes.rows");
+    EXPECT_EQ(1, tree_rows.size());
+    auto row = tree_rows.begin();
+    EXPECT_GE(row->second.get<int>("minutes"), 0);
+    EXPECT_LE(row->second.get<int>("minutes"), 60);
+    EXPECT_EQ(getHostname(), row->second.get<std::string>("_source_host"));
+  }
+}
+
+TEST_F(DistributedTests, test_duplicate_request) {
+  // Access to the internal SQL implementation is only available in core.
+  auto provider_raw = new MockDistributedProvider();
+  provider_raw->queriesJSON_ =
+      "[{\"query\": \"SELECT hour FROM time\", \"id\": \"hour\"}]";
+  std::unique_ptr<MockDistributedProvider>
+    provider(provider_raw);
+  DistributedQueryHandler handler(std::move(provider));
+
+  Status s = handler.doQueries();
+  ASSERT_EQ(Status(), s);
+
+  pt::ptree tree;
+  std::istringstream json_stream(provider_raw->resultsJSON_);
+  ASSERT_NO_THROW(pt::read_json(json_stream, tree));
+
+  EXPECT_EQ(0, tree.get<int>("results.hour.status"));
+  const pt::ptree& tree_rows = tree.get_child("results.hour.rows");
+  EXPECT_EQ(1, tree_rows.size());
+
+  auto row = tree_rows.begin();
+  EXPECT_GE(row->second.get<int>("hour"), 0);
+  EXPECT_LE(row->second.get<int>("hour"), 24);
+  EXPECT_EQ(getHostname(), row->second.get<std::string>("_source_host"));
+
+  // The second time, 'hour' should not be executed again
+  s = handler.doQueries();
+  ASSERT_EQ(Status(), s);
+  json_stream.str(provider_raw->resultsJSON_);
+  ASSERT_NO_THROW(pt::read_json(json_stream, tree));
+  EXPECT_EQ(0, tree.get_child("results").size());
+}
+}
diff --git a/src/osquery/events/CMakeLists.txt b/src/osquery/events/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e2ee61b
--- /dev/null
@@ -0,0 +1,25 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LINK(udev ip4tc)
+
+ADD_OSQUERY_LIBRARY(osquery_events events.cpp)
+ADD_OSQUERY_LIBRARY(osquery_events_linux linux/inotify.cpp
+                                                                                linux/udev.cpp)
+
+FILE(GLOB OSQUERY_EVENTS_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_EVENTS_TESTS})
+
+FILE(GLOB OSQUERY_LINUX_EVENTS_TESTS "linux/tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_LINUX_EVENTS_TESTS})
diff --git a/src/osquery/events/events.cpp b/src/osquery/events/events.cpp
new file mode 100644 (file)
index 0000000..5b0cd8b
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <exception>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <osquery/core.h>
+#include <osquery/events.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+#include "osquery/core/conversions.h"
+#include "osquery/database/db_handle.h"
+
+namespace osquery {
+
+/// Helper cooloff (ms) macro to prevent thread failure thrashing.
+#define EVENTS_COOLOFF 20
+
+FLAG(bool, disable_events, false, "Disable osquery publish/subscribe system");
+
+FLAG(bool,
+     events_optimize,
+     true,
+     "Optimize subscriber select queries (scheduler only)");
+
+FLAG(int32, events_expiry, 86000, "Timeout to expire event subscriber results");
+
+const std::vector<size_t> kEventTimeLists = {
+    1 * 60 * 60, // 1 hour
+    1 * 60, // 1 minute
+    10, // 10 seconds
+};
+
+void publisherSleep(size_t milli) {
+  boost::this_thread::sleep(boost::posix_time::milliseconds(milli));
+}
+
+QueryData EventSubscriberPlugin::genTable(QueryContext& context) {
+  EventTime start = 0, stop = -1;
+  if (context.constraints["time"].getAll().size() > 0) {
+    // Use the 'time' constraint to optimize backing-store lookups.
+    for (const auto& constraint : context.constraints["time"].getAll()) {
+      EventTime expr = 0;
+      try {
+        expr = boost::lexical_cast<EventTime>(constraint.expr);
+      } catch (const boost::bad_lexical_cast& e) {
+        expr = 0;
+      }
+      if (constraint.op == EQUALS) {
+        stop = start = expr;
+        break;
+      } else if (constraint.op == GREATER_THAN) {
+        start = std::max(start, expr + 1);
+      } else if (constraint.op == GREATER_THAN_OR_EQUALS) {
+        start = std::max(start, expr);
+      } else if (constraint.op == LESS_THAN) {
+        stop = std::min(stop, expr - 1);
+      } else if (constraint.op == LESS_THAN_OR_EQUALS) {
+        stop = std::min(stop, expr);
+      }
+    }
+  } else if (kToolType == OSQUERY_TOOL_DAEMON && FLAGS_events_optimize) {
+    // If the daemon is querying a subscriber without a 'time' constraint and
+    // allows optimization, only emit events since the last query.
+    start = optimize_time_;
+    optimize_time_ = getUnixTime() - 1;
+  }
+
+  return get(start, stop);
+}
+
+void EventPublisherPlugin::fire(const EventContextRef& ec, EventTime time) {
+  EventContextID ec_id;
+
+  if (isEnding()) {
+    // Cannot emit/fire while ending
+    return;
+  }
+
+  {
+    boost::lock_guard<boost::mutex> lock(ec_id_lock_);
+    ec_id = next_ec_id_++;
+  }
+
+  // Fill in EventContext ID and time if needed.
+  if (ec != nullptr) {
+    ec->id = ec_id;
+    if (ec->time == 0) {
+      if (time == 0) {
+        time = getUnixTime();
+      }
+      // Todo: add a check to assure normalized (seconds) time.
+      ec->time = time;
+    }
+  }
+
+  for (const auto& subscription : subscriptions_) {
+    auto es = EventFactory::getEventSubscriber(subscription->subscriber_name);
+    if (es->state() == SUBSCRIBER_RUNNING) {
+      fireCallback(subscription, ec);
+    }
+  }
+}
+
+std::set<std::string> EventSubscriberPlugin::getIndexes(EventTime start,
+                                                        EventTime stop,
+                                                        int list_key) {
+  auto db = DBHandle::getInstance();
+  auto index_key = "indexes." + dbNamespace();
+  std::set<std::string> indexes;
+
+  // Keep track of the tail/head of account time while bin searching.
+  EventTime start_max = stop, stop_min = stop, local_start, local_stop;
+  auto types = kEventTimeLists.size();
+  // List types are sized bins of time containing records for this namespace.
+  for (size_t i = 0; i < types; ++i) {
+    auto size = kEventTimeLists[i];
+    if (list_key > 0 && i != list_key) {
+      // A specific list_type was requested, only return bins of this key.
+      continue;
+    }
+
+    std::string time_list;
+    auto list_type = boost::lexical_cast<std::string>(size);
+    auto status = db->Get(kEvents, index_key + "." + list_type, time_list);
+    if (time_list.length() == 0) {
+      // No events in this binning size.
+      return indexes;
+    }
+
+    if (list_key == 0 && i == (types - 1) && types > 1) {
+      // Relax the requested start/stop bounds.
+      if (start != start_max) {
+        start = (start / size) * size;
+        start_max = ((start / size) + 1) * size;
+        if (start_max < stop) {
+          start_max = start + kEventTimeLists[types - 2];
+        }
+      }
+
+      if (stop != stop_min) {
+        stop = ((stop / size) + 1) * size;
+        stop_min = (stop / size) * size;
+        if (stop_min > start) {
+          stop_min = stop_min - kEventTimeLists[types - 1];
+        }
+      }
+    } else if (list_key > 0 || types == 1) {
+      // Relax the requested bounds to fit the requested/only index.
+      start = (start / size) * size;
+      start_max = ((start_max / size) + 1) * size;
+    }
+
+    // (1) The first iteration will have 1 range (start to start_max=stop).
+    // (2) Intermediate iterations will have 2 (start-start_max, stop-stop_min).
+    // For each iteration the range collapses based on the coverage using
+    // the first bin's start time and the last bin's stop time.
+    // (3) The last iteration's range includes relaxed bounds outside the
+    // requested start to stop range.
+    std::vector<std::string> all_bins, bins, expirations;
+    boost::split(all_bins, time_list, boost::is_any_of(","));
+    for (const auto& bin : all_bins) {
+      // Bins are identified by the binning size step.
+      auto step = boost::lexical_cast<EventTime>(bin);
+      // Check if size * step -> size * (step + 1) is within a range.
+      int bin_start = size * step, bin_stop = size * (step + 1);
+      if (expire_events_ && expire_time_ > 0) {
+        if (bin_stop <= expire_time_) {
+          expirations.push_back(bin);
+        } else if (bin_start < expire_time_) {
+          expireRecords(list_type, bin, false);
+        }
+      }
+
+      if (bin_start >= start && bin_stop <= start_max) {
+        bins.push_back(bin);
+      } else if ((bin_start >= stop_min && bin_stop <= stop) || stop == 0) {
+        bins.push_back(bin);
+      }
+    }
+
+    // Rewrite the index lists and delete each expired item.
+    if (expirations.size() > 0) {
+      expireIndexes(list_type, all_bins, expirations);
+    }
+
+    if (bins.size() != 0) {
+      // If more precision was achieved though this list's binning.
+      local_start = boost::lexical_cast<EventTime>(bins.front()) * size;
+      start_max = (local_start < start_max) ? local_start : start_max;
+      local_stop = (boost::lexical_cast<EventTime>(bins.back()) + 1) * size;
+      stop_min = (local_stop < stop_min) ? local_stop : stop_min;
+    }
+
+    for (const auto& bin : bins) {
+      indexes.insert(list_type + "." + bin);
+    }
+
+    if (start == start_max && stop == stop_min) {
+      break;
+    }
+  }
+
+  // Update the new time that events expire to now - expiry.
+  return indexes;
+}
+
+void EventSubscriberPlugin::expireRecords(const std::string& list_type,
+                                          const std::string& index,
+                                          bool all) {
+  auto db = DBHandle::getInstance();
+  auto record_key = "records." + dbNamespace();
+  auto data_key = "data." + dbNamespace();
+
+  // If the expirations is not removing all records, rewrite the persisting.
+  std::vector<std::string> persisting_records;
+  // Request all records within this list-size + bin offset.
+  auto expired_records = getRecords({list_type + "." + index});
+  for (const auto& record : expired_records) {
+    if (all) {
+      db->Delete(kEvents, data_key + "." + record.first);
+    } else if (record.second > expire_time_) {
+      persisting_records.push_back(record.first + ":" +
+                                   std::to_string(record.second));
+    }
+  }
+
+  // Either drop or overwrite the record list.
+  if (all) {
+    db->Delete(kEvents, record_key + "." + list_type + "." + index);
+  } else {
+    auto new_records = boost::algorithm::join(persisting_records, ",");
+    db->Put(kEvents, record_key + "." + list_type + "." + index, new_records);
+  }
+}
+
+void EventSubscriberPlugin::expireIndexes(
+    const std::string& list_type,
+    const std::vector<std::string>& indexes,
+    const std::vector<std::string>& expirations) {
+  auto db = DBHandle::getInstance();
+  auto index_key = "indexes." + dbNamespace();
+
+  // Construct a mutable list of persisting indexes to rewrite as records.
+  std::vector<std::string> persisting_indexes = indexes;
+  // Remove the records using the list of expired indexes.
+  for (const auto& bin : expirations) {
+    expireRecords(list_type, bin, true);
+    persisting_indexes.erase(
+        std::remove(persisting_indexes.begin(), persisting_indexes.end(), bin),
+        persisting_indexes.end());
+  }
+
+  // Update the list of indexes with the non-expired indexes.
+  auto new_indexes = boost::algorithm::join(persisting_indexes, ",");
+  db->Put(kEvents, index_key + "." + list_type, new_indexes);
+}
+
+std::vector<EventRecord> EventSubscriberPlugin::getRecords(
+    const std::set<std::string>& indexes) {
+  auto db = DBHandle::getInstance();
+  auto record_key = "records." + dbNamespace();
+
+  std::vector<EventRecord> records;
+  for (const auto& index : indexes) {
+    std::string record_value;
+    if (!db->Get(kEvents, record_key + "." + index, record_value).ok()) {
+      return records;
+    }
+
+    if (record_value.length() == 0) {
+      // There are actually no events in this bin, interesting error case.
+      continue;
+    }
+
+    // Each list is tokenized into a record=event_id:time.
+    std::vector<std::string> bin_records;
+    boost::split(bin_records, record_value, boost::is_any_of(",:"));
+    auto bin_it = bin_records.begin();
+    for (; bin_it != bin_records.end(); bin_it++) {
+      std::string eid = *bin_it;
+      EventTime time = boost::lexical_cast<EventTime>(*(++bin_it));
+      records.push_back(std::make_pair(eid, time));
+    }
+  }
+
+  return records;
+}
+
+Status EventSubscriberPlugin::recordEvent(EventID& eid, EventTime time) {
+  Status status;
+  auto db = DBHandle::getInstance();
+  std::string time_value = boost::lexical_cast<std::string>(time);
+
+  // The record is identified by the event type then module name.
+  std::string index_key = "indexes." + dbNamespace();
+  std::string record_key = "records." + dbNamespace();
+  // The list key includes the list type (bin size) and the list ID (bin).
+  std::string list_key;
+  std::string list_id;
+
+  for (auto time_list : kEventTimeLists) {
+    // The list_id is the MOST-Specific key ID, the bin for this list.
+    // If the event time was 13 and the time_list is 5 seconds, lid = 2.
+    list_id = boost::lexical_cast<std::string>(time / time_list);
+    // The list name identifies the 'type' of list.
+    list_key = boost::lexical_cast<std::string>(time_list);
+    // list_key = list_key + "." + list_id;
+
+    {
+      boost::lock_guard<boost::mutex> lock(event_record_lock_);
+      // Append the record (eid, unix_time) to the list bin.
+      std::string record_value;
+      status = db->Get(
+          kEvents, record_key + "." + list_key + "." + list_id, record_value);
+
+      if (record_value.length() == 0) {
+        // This is a new list_id for list_key, append the ID to the indirect
+        // lookup for this list_key.
+        std::string index_value;
+        status = db->Get(kEvents, index_key + "." + list_key, index_value);
+        if (index_value.length() == 0) {
+          // A new index.
+          index_value = list_id;
+        } else {
+          index_value += "," + list_id;
+        }
+        status = db->Put(kEvents, index_key + "." + list_key, index_value);
+        record_value = eid + ":" + time_value;
+      } else {
+        // Tokenize a record using ',' and the EID/time using ':'.
+        record_value += "," + eid + ":" + time_value;
+      }
+      status = db->Put(
+          kEvents, record_key + "." + list_key + "." + list_id, record_value);
+      if (!status.ok()) {
+        LOG(ERROR) << "Could not put Event Record key: " << record_key << "."
+                   << list_key << "." << list_id;
+      }
+    }
+  }
+
+  return Status(0, "OK");
+}
+
+EventID EventSubscriberPlugin::getEventID() {
+  Status status;
+  auto db = DBHandle::getInstance();
+  // First get an event ID from the meta key.
+  std::string eid_key = "eid." + dbNamespace();
+  std::string last_eid_value;
+  std::string eid_value;
+
+  {
+    boost::lock_guard<boost::mutex> lock(event_id_lock_);
+    status = db->Get(kEvents, eid_key, last_eid_value);
+    if (!status.ok()) {
+      last_eid_value = "0";
+    }
+
+    size_t eid = boost::lexical_cast<size_t>(last_eid_value) + 1;
+    eid_value = boost::lexical_cast<std::string>(eid);
+    status = db->Put(kEvents, eid_key, eid_value);
+  }
+
+  if (!status.ok()) {
+    return "0";
+  }
+
+  return eid_value;
+}
+
+QueryData EventSubscriberPlugin::get(EventTime start, EventTime stop) {
+  QueryData results;
+  Status status;
+
+  std::shared_ptr<DBHandle> db;
+  try {
+    db = DBHandle::getInstance();
+  } catch (const std::runtime_error& e) {
+    LOG(ERROR) << "Cannot retrieve subscriber results database is locked";
+    return results;
+  }
+
+  // Get the records for this time range.
+  auto indexes = getIndexes(start, stop);
+  auto records = getRecords(indexes);
+
+  std::vector<EventRecord> mapped_records;
+  for (const auto& record : records) {
+    if (record.second >= start && (record.second <= stop || stop == 0)) {
+      mapped_records.push_back(record);
+    }
+  }
+
+  std::string events_key = "data." + dbNamespace();
+  if (FLAGS_events_expiry > 0) {
+    // Set the expire time to NOW - "configured lifetime".
+    // Index retrieval will apply the constraints checking and auto-expire.
+    expire_time_ = getUnixTime() - FLAGS_events_expiry;
+  }
+
+  // Select mapped_records using event_ids as keys.
+  std::string data_value;
+  for (const auto& record : mapped_records) {
+    Row r;
+    status = db->Get(kEvents, events_key + "." + record.first, data_value);
+    if (data_value.length() == 0) {
+      // THere is no record here, interesting error case.
+      continue;
+    }
+    status = deserializeRowJSON(data_value, r);
+    if (status.ok()) {
+      results.push_back(r);
+    }
+  }
+  return results;
+}
+
+Status EventSubscriberPlugin::add(Row& r, EventTime event_time) {
+  std::shared_ptr<DBHandle> db = nullptr;
+  try {
+    db = DBHandle::getInstance();
+  } catch (const std::runtime_error& e) {
+    return Status(1, e.what());
+  }
+
+  // Get and increment the EID for this module.
+  EventID eid = getEventID();
+  // Without encouraging a missing event time, do not support a 0-time.
+  auto index_time = getUnixTime();
+  if (event_time == 0) {
+    r["time"] = std::to_string(index_time);
+  } else {
+    r["time"] = std::to_string(event_time);
+  }
+
+  // Serialize and store the row data, for query-time retrieval.
+  std::string data;
+  auto status = serializeRowJSON(r, data);
+  if (!status.ok()) {
+    return status;
+  }
+
+  // Store the event data.
+  std::string event_key = "data." + dbNamespace() + "." + eid;
+  status = db->Put(kEvents, event_key, data);
+  // Record the event in the indexing bins, using the index time.
+  recordEvent(eid, event_time);
+  return status;
+}
+
+void EventFactory::delay() {
+  // Caller may disable event publisher threads.
+  if (FLAGS_disable_events) {
+    return;
+  }
+
+  // Create a thread for each event publisher.
+  auto& ef = EventFactory::getInstance();
+  for (const auto& publisher : EventFactory::getInstance().event_pubs_) {
+    auto thread_ = std::make_shared<boost::thread>(
+        boost::bind(&EventFactory::run, publisher.first));
+    ef.threads_.push_back(thread_);
+  }
+}
+
+Status EventFactory::run(EventPublisherID& type_id) {
+  auto& ef = EventFactory::getInstance();
+  if (FLAGS_disable_events) {
+    return Status(0, "Events disabled");
+  }
+
+  // An interesting take on an event dispatched entrypoint.
+  // There is little introspection into the event type.
+  // Assume it can either make use of an entrypoint poller/selector or
+  // take care of async callback registrations in setUp/configure/run
+  // only once and handle event queuing/firing in callbacks.
+  EventPublisherRef publisher = nullptr;
+  try {
+    publisher = ef.getEventPublisher(type_id);
+  } catch (std::out_of_range& e) {
+    return Status(1, "No event type found");
+  }
+
+  if (publisher == nullptr) {
+    return Status(1, "Event publisher is missing");
+  } else if (publisher->hasStarted()) {
+    return Status(1, "Cannot restart an event publisher");
+  }
+  VLOG(1) << "Starting event publisher run loop: " + type_id;
+  publisher->hasStarted(true);
+
+  auto status = Status(0, "OK");
+  while (!publisher->isEnding() && status.ok()) {
+    // Can optionally implement a global cooloff latency here.
+    status = publisher->run();
+    osquery::publisherSleep(EVENTS_COOLOFF);
+  }
+  // The runloop status is not reflective of the event type's.
+  VLOG(1) << "Event publisher " << publisher->type()
+          << " run loop terminated for reason: " << status.getMessage();
+  // Publishers auto tear down when their run loop stops.
+  publisher->tearDown();
+  ef.event_pubs_.erase(type_id);
+  return Status(0, "OK");
+}
+
+// There's no reason for the event factory to keep multiple instances.
+EventFactory& EventFactory::getInstance() {
+  static EventFactory ef;
+  return ef;
+}
+
+Status EventFactory::registerEventPublisher(const PluginRef& pub) {
+  // Try to downcast the plugin to an event publisher.
+  EventPublisherRef specialized_pub;
+  try {
+    auto base_pub = std::dynamic_pointer_cast<EventPublisherPlugin>(pub);
+    specialized_pub = std::static_pointer_cast<BaseEventPublisher>(base_pub);
+  } catch (const std::bad_cast& e) {
+    return Status(1, "Incorrect plugin");
+  }
+
+  if (specialized_pub == nullptr || specialized_pub.get() == nullptr) {
+    return Status(0, "Invalid subscriber");
+  }
+
+  auto& ef = EventFactory::getInstance();
+  auto type_id = specialized_pub->type();
+  if (ef.event_pubs_.count(type_id) != 0) {
+    // This is a duplicate event publisher.
+    return Status(1, "Duplicate publisher type");
+  }
+
+  // Do not set up event publisher if events are disabled.
+  if (!FLAGS_disable_events) {
+    if (!specialized_pub->setUp().ok()) {
+      // Only start event loop if setUp succeeds.
+      return Status(1, "Event publisher setup failed");
+    }
+  }
+
+  ef.event_pubs_[type_id] = specialized_pub;
+  return Status(0, "OK");
+}
+
+Status EventFactory::registerEventSubscriber(const PluginRef& sub) {
+  // Try to downcast the plugin to an event subscriber.
+  EventSubscriberRef specialized_sub;
+  try {
+    auto base_sub = std::dynamic_pointer_cast<EventSubscriberPlugin>(sub);
+    specialized_sub = std::static_pointer_cast<BaseEventSubscriber>(base_sub);
+  } catch (const std::bad_cast& e) {
+    return Status(1, "Incorrect plugin");
+  }
+
+  if (specialized_sub == nullptr || specialized_sub.get() == nullptr) {
+    return Status(1, "Invalid subscriber");
+  }
+
+  // Let the module initialize any Subscriptions.
+  auto status = Status(0, "OK");
+  if (!FLAGS_disable_events) {
+    status = specialized_sub->init();
+  }
+
+  auto& ef = EventFactory::getInstance();
+  ef.event_subs_[specialized_sub->getName()] = specialized_sub;
+
+  // Set state of subscriber.
+  if (!status.ok()) {
+    specialized_sub->state(SUBSCRIBER_FAILED);
+    return Status(1, status.getMessage());
+  } else {
+    specialized_sub->state(SUBSCRIBER_RUNNING);
+    return Status(0, "OK");
+  }
+}
+
+Status EventFactory::addSubscription(EventPublisherID& type_id,
+                                     EventSubscriberID& name_id,
+                                     const SubscriptionContextRef& mc,
+                                     EventCallback cb,
+                                     void* user_data) {
+  auto subscription = Subscription::create(name_id, mc, cb, user_data);
+  return EventFactory::addSubscription(type_id, subscription);
+}
+
+Status EventFactory::addSubscription(EventPublisherID& type_id,
+                                     const SubscriptionRef& subscription) {
+  EventPublisherRef publisher = getInstance().getEventPublisher(type_id);
+  if (publisher == nullptr) {
+    return Status(1, "Unknown event publisher");
+  }
+
+  // The event factory is responsible for configuring the event types.
+  auto status = publisher->addSubscription(subscription);
+  if (!FLAGS_disable_events) {
+    publisher->configure();
+  }
+  return status;
+}
+
+size_t EventFactory::numSubscriptions(EventPublisherID& type_id) {
+  EventPublisherRef publisher;
+  try {
+    publisher = EventFactory::getInstance().getEventPublisher(type_id);
+  } catch (std::out_of_range& e) {
+    return 0;
+  }
+  return publisher->numSubscriptions();
+}
+
+EventPublisherRef EventFactory::getEventPublisher(EventPublisherID& type_id) {
+  if (getInstance().event_pubs_.count(type_id) == 0) {
+    LOG(ERROR) << "Requested unknown event publisher: " + type_id;
+    return nullptr;
+  }
+  return getInstance().event_pubs_.at(type_id);
+}
+
+EventSubscriberRef EventFactory::getEventSubscriber(
+    EventSubscriberID& name_id) {
+  if (!exists(name_id)) {
+    LOG(ERROR) << "Requested unknown event subscriber: " + name_id;
+    return nullptr;
+  }
+  return getInstance().event_subs_.at(name_id);
+}
+
+bool EventFactory::exists(EventSubscriberID& name_id) {
+  return (getInstance().event_subs_.count(name_id) > 0);
+}
+
+Status EventFactory::deregisterEventPublisher(const EventPublisherRef& pub) {
+  return EventFactory::deregisterEventPublisher(pub->type());
+}
+
+Status EventFactory::deregisterEventPublisher(EventPublisherID& type_id) {
+  auto& ef = EventFactory::getInstance();
+  EventPublisherRef publisher;
+  try {
+    publisher = ef.getEventPublisher(type_id);
+  } catch (std::out_of_range& e) {
+    return Status(1, "No event publisher to deregister");
+  }
+
+  if (!FLAGS_disable_events) {
+    publisher->isEnding(true);
+    if (!publisher->hasStarted()) {
+      // If a publisher's run loop was not started, call tearDown since
+      // the setUp happened at publisher registration time.
+      publisher->tearDown();
+      // If the run loop did run the tear down and erase will happen in the
+      // event
+      // thread wrapper when isEnding is next checked.
+      ef.event_pubs_.erase(type_id);
+    } else {
+      publisher->end();
+    }
+  }
+  return Status(0, "OK");
+}
+
+std::vector<std::string> EventFactory::publisherTypes() {
+  std::vector<std::string> types;
+  for (const auto& publisher : getInstance().event_pubs_) {
+    types.push_back(publisher.first);
+  }
+  return types;
+}
+
+std::vector<std::string> EventFactory::subscriberNames() {
+  std::vector<std::string> names;
+  for (const auto& subscriber : getInstance().event_subs_) {
+    names.push_back(subscriber.first);
+  }
+  return names;
+}
+
+void EventFactory::end(bool join) {
+  auto& ef = EventFactory::getInstance();
+
+  // Call deregister on each publisher.
+  for (const auto& publisher : ef.publisherTypes()) {
+    deregisterEventPublisher(publisher);
+  }
+
+  // Stop handling exceptions for the publisher threads.
+  for (const auto& thread : ef.threads_) {
+    if (join) {
+      thread->join();
+    } else {
+      thread->detach();
+    }
+  }
+
+  // A small cool off helps OS API event publisher flushing.
+  if (!FLAGS_disable_events) {
+    ::usleep(400);
+    ef.threads_.clear();
+  }
+}
+
+void attachEvents() {
+  const auto& publishers = Registry::all("event_publisher");
+  for (const auto& publisher : publishers) {
+    EventFactory::registerEventPublisher(publisher.second);
+  }
+
+  const auto& subscribers = Registry::all("event_subscriber");
+  for (const auto& subscriber : subscribers) {
+    auto status = EventFactory::registerEventSubscriber(subscriber.second);
+    if (!status.ok()) {
+      LOG(ERROR) << "Error registering subscriber: " << status.getMessage();
+    }
+  }
+}
+}
diff --git a/src/osquery/events/linux/inotify.cpp b/src/osquery/events/linux/inotify.cpp
new file mode 100644 (file)
index 0000000..69c0845
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sstream>
+
+#include <fnmatch.h>
+#include <linux/limits.h>
+
+#include <boost/filesystem.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+#include "osquery/events/linux/inotify.h"
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+int kINotifyMLatency = 200;
+
+static const uint32_t BUFFER_SIZE =
+    (10 * ((sizeof(struct inotify_event)) + NAME_MAX + 1));
+
+std::map<int, std::string> kMaskActions = {
+    {IN_ACCESS, "ACCESSED"},
+    {IN_ATTRIB, "ATTRIBUTES_MODIFIED"},
+    {IN_CLOSE_WRITE, "UPDATED"},
+    {IN_CREATE, "CREATED"},
+    {IN_DELETE, "DELETED"},
+    {IN_MODIFY, "UPDATED"},
+    {IN_MOVED_FROM, "MOVED_FROM"},
+    {IN_MOVED_TO, "MOVED_TO"},
+    {IN_OPEN, "OPENED"},
+};
+
+REGISTER(INotifyEventPublisher, "event_publisher", "inotify");
+
+Status INotifyEventPublisher::setUp() {
+  inotify_handle_ = ::inotify_init();
+  // If this does not work throw an exception.
+  if (inotify_handle_ == -1) {
+    return Status(1, "Could not start inotify: inotify_init failed");
+  }
+  return Status(0, "OK");
+}
+
+void INotifyEventPublisher::configure() {
+  for (auto& sub : subscriptions_) {
+    // Anytime a configure is called, try to monitor all subscriptions.
+    // Configure is called as a response to removing/adding subscriptions.
+    // This means recalculating all monitored paths.
+    auto sc = getSubscriptionContext(sub->context);
+    if (sc->discovered_.size() > 0) {
+      continue;
+    }
+
+    sc->discovered_ = sc->path;
+    if (sc->path.find("**") != std::string::npos) {
+      sc->recursive = true;
+      sc->discovered_ = sc->path.substr(0, sc->path.find("**"));
+      sc->path = sc->discovered_;
+    }
+
+    if (sc->path.find('*') != std::string::npos) {
+      // If the wildcard exists within the file (leaf), remove and monitor the
+      // directory instead. Apply a fnmatch on fired events to filter leafs.
+      auto fullpath = fs::path(sc->path);
+      if (fullpath.filename().string().find('*') != std::string::npos) {
+        sc->discovered_ = fullpath.parent_path().string();
+      }
+
+      if (sc->discovered_.find('*') != std::string::npos) {
+        // If a wildcard exists within the tree (stem), resolve at configure
+        // time and monitor each path.
+        std::vector<std::string> paths;
+        resolveFilePattern(sc->discovered_, paths);
+        for (const auto& _path : paths) {
+          addMonitor(_path, sc->recursive);
+        }
+        sc->recursive_match = sc->recursive;
+        continue;
+      }
+    }
+    addMonitor(sc->discovered_, sc->recursive);
+  }
+}
+
+void INotifyEventPublisher::tearDown() {
+  ::close(inotify_handle_);
+  inotify_handle_ = -1;
+}
+
+Status INotifyEventPublisher::restartMonitoring(){
+  if (last_restart_ != 0 && getUnixTime() - last_restart_ < 10) {
+    return Status(1, "Overflow");
+  }
+  last_restart_ = getUnixTime();
+  VLOG(1) << "inotify was overflown, attempting to restart handle";
+  for(const auto& desc : descriptors_){
+    removeMonitor(desc, 1);
+  }
+  path_descriptors_.clear();
+  descriptor_paths_.clear();
+  configure();
+  return Status(0, "OK");
+}
+
+Status INotifyEventPublisher::run() {
+  // Get a while wrapper for free.
+  char buffer[BUFFER_SIZE];
+  fd_set set;
+
+  FD_ZERO(&set);
+  FD_SET(getHandle(), &set);
+
+  struct timeval timeout = {3, 3000};
+  int selector = ::select(getHandle() + 1, &set, nullptr, nullptr, &timeout);
+  if (selector == -1) {
+    LOG(ERROR) << "Could not read inotify handle";
+    return Status(1, "INotify handle failed");
+  }
+
+  if (selector == 0) {
+    // Read timeout.
+    return Status(0, "Continue");
+  }
+  ssize_t record_num = ::read(getHandle(), buffer, BUFFER_SIZE);
+  if (record_num == 0 || record_num == -1) {
+    return Status(1, "INotify read failed");
+  }
+
+  for (char* p = buffer; p < buffer + record_num;) {
+    // Cast the inotify struct, make shared pointer, and append to contexts.
+    auto event = reinterpret_cast<struct inotify_event*>(p);
+    if (event->mask & IN_Q_OVERFLOW) {
+      // The inotify queue was overflown (remove all paths).
+      Status stat = restartMonitoring();
+      if(!stat.ok()){
+        return stat;
+      }
+    }
+
+    if (event->mask & IN_IGNORED) {
+      // This inotify watch was removed.
+      removeMonitor(event->wd, false);
+    } else if (event->mask & IN_MOVE_SELF) {
+      // This inotify path was moved, but is still watched.
+      removeMonitor(event->wd, true);
+    } else if (event->mask & IN_DELETE_SELF) {
+      // A file was moved to replace the watched path.
+      removeMonitor(event->wd, false);
+    } else {
+      auto ec = createEventContextFrom(event);
+      fire(ec);
+    }
+    // Continue to iterate
+    p += (sizeof(struct inotify_event)) + event->len;
+  }
+
+  osquery::publisherSleep(kINotifyMLatency);
+  return Status(0, "Continue");
+}
+
+INotifyEventContextRef INotifyEventPublisher::createEventContextFrom(
+    struct inotify_event* event) {
+  auto shared_event = std::make_shared<struct inotify_event>(*event);
+  auto ec = createEventContext();
+  ec->event = shared_event;
+
+  // Get the pathname the watch fired on.
+  ec->path = descriptor_paths_[event->wd];
+  if (event->len > 1) {
+    ec->path += event->name;
+  }
+
+  for (const auto& action : kMaskActions) {
+    if (event->mask & action.first) {
+      ec->action = action.second;
+      break;
+    }
+  }
+  return ec;
+}
+
+bool INotifyEventPublisher::shouldFire(const INotifySubscriptionContextRef& sc,
+                                       const INotifyEventContextRef& ec) const {
+  if (sc->recursive && !sc->recursive_match) {
+    ssize_t found = ec->path.find(sc->path);
+    if (found != 0) {
+      return false;
+    }
+  } else if (fnmatch((sc->path + "*").c_str(),
+                     ec->path.c_str(),
+                     FNM_PATHNAME | FNM_CASEFOLD |
+                         ((sc->recursive_match) ? FNM_LEADING_DIR : 0)) != 0) {
+    // Only apply a leading-dir match if this is a recursive watch with a
+    // match requirement (an inline wildcard with ending recursive wildcard).
+    return false;
+  }
+  // The subscription may supply a required event mask.
+  if (sc->mask != 0 && !(ec->event->mask & sc->mask)) {
+    return false;
+  }
+
+  // inotify will not monitor recursively, new directories need watches.
+  if(sc->recursive && ec->action == "CREATED" && isDirectory(ec->path)){
+    const_cast<INotifyEventPublisher*>(this)->addMonitor(ec->path + '/', true);
+  }
+
+  return true;
+}
+
+bool INotifyEventPublisher::addMonitor(const std::string& path,
+                                       bool recursive) {
+  if (!isPathMonitored(path)) {
+    int watch = ::inotify_add_watch(getHandle(), path.c_str(), IN_ALL_EVENTS);
+    if (watch == -1) {
+      LOG(ERROR) << "Could not add inotify watch on: " << path;
+      return false;
+    }
+
+    // Keep a list of the watch descriptors
+    descriptors_.push_back(watch);
+    // Keep a map of the path -> watch descriptor
+    path_descriptors_[path] = watch;
+    // Keep a map of the opposite (descriptor -> path)
+    descriptor_paths_[watch] = path;
+  }
+
+  if (recursive && isDirectory(path).ok()) {
+    std::vector<std::string> children;
+    // Get a list of children of this directory (requested recursive watches).
+    listDirectoriesInDirectory(path, children);
+
+    for (const auto& child : children) {
+      addMonitor(child, recursive);
+    }
+  }
+
+  return true;
+}
+
+bool INotifyEventPublisher::removeMonitor(const std::string& path, bool force) {
+  // If force then remove from INotify, otherwise cleanup file descriptors.
+  if (path_descriptors_.find(path) == path_descriptors_.end()) {
+    return false;
+  }
+
+  int watch = path_descriptors_[path];
+  path_descriptors_.erase(path);
+  descriptor_paths_.erase(watch);
+
+  auto position = std::find(descriptors_.begin(), descriptors_.end(), watch);
+  descriptors_.erase(position);
+
+  if (force) {
+    ::inotify_rm_watch(getHandle(), watch);
+  }
+  return true;
+}
+
+bool INotifyEventPublisher::removeMonitor(int watch, bool force) {
+  if (descriptor_paths_.find(watch) == descriptor_paths_.end()) {
+    return false;
+  }
+
+  auto path = descriptor_paths_[watch];
+  return removeMonitor(path, force);
+}
+
+bool INotifyEventPublisher::isPathMonitored(const std::string& path) {
+  boost::filesystem::path parent_path;
+  if (!isDirectory(path).ok()) {
+    if (path_descriptors_.find(path) != path_descriptors_.end()) {
+      // Path is a file, and is directly monitored.
+      return true;
+    }
+    if (!getDirectory(path, parent_path).ok()) {
+      // Could not get parent of unmonitored file.
+      return false;
+    }
+  } else {
+    parent_path = path;
+  }
+
+  // Directory or parent of file monitoring
+  auto path_iterator = path_descriptors_.find(parent_path.string());
+  return (path_iterator != path_descriptors_.end());
+}
+}
diff --git a/src/osquery/events/linux/inotify.h b/src/osquery/events/linux/inotify.h
new file mode 100644 (file)
index 0000000..50d07b8
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include <sys/inotify.h>
+#include <sys/stat.h>
+
+#include <osquery/events.h>
+
+namespace osquery {
+
+extern std::map<int, std::string> kMaskActions;
+
+/**
+ * @brief Subscription details for INotifyEventPublisher events.
+ *
+ * This context is specific to INotifyEventPublisher. It allows the
+ * subscribing EventSubscriber to set a path (file or directory) and a
+ * limited action mask.
+ * Events are passed to the EventSubscriber if they match the context
+ * path (or anything within a directory if the path is a directory) and if the
+ * event action is part of the mask. If the mask is 0 then all actions are
+ * passed to the EventSubscriber.
+ */
+struct INotifySubscriptionContext : public SubscriptionContext {
+  /// Subscription the following filesystem path.
+  std::string path;
+  /// Limit the `inotify` actions to the subscription mask (if not 0).
+  uint32_t mask;
+  /// Treat this path as a directory and subscription recursively.
+  bool recursive;
+
+  INotifySubscriptionContext()
+      : mask(0), recursive(false), recursive_match(false) {}
+
+  /**
+   * @brief Helper method to map a string action to `inotify` action mask bit.
+   *
+   * This helper method will set the `mask` value for this SubscriptionContext.
+   *
+   * @param action The string action, a value in kMaskAction%s.
+   */
+  void requireAction(const std::string& action) {
+    for (const auto& bit : kMaskActions) {
+      if (action == bit.second) {
+        mask = mask | bit.first;
+      }
+    }
+  }
+
+ private:
+  /// During configure the INotify publisher may modify/optimize the paths.
+  std::string discovered_;
+  /// A configure-time pattern was expanded to match absolute paths.
+  bool recursive_match;
+
+ private:
+  friend class INotifyEventPublisher;
+};
+
+/**
+ * @brief Event details for INotifyEventPublisher events.
+ */
+struct INotifyEventContext : public EventContext {
+  /// The inotify_event structure if the EventSubscriber want to interact.
+  std::shared_ptr<struct inotify_event> event;
+  /// A string path parsed from the inotify_event.
+  std::string path;
+  /// A string action representing the event action `inotify` bit.
+  std::string action;
+  /// A no-op event transaction id.
+  uint32_t transaction_id;
+
+  INotifyEventContext() : event(nullptr), transaction_id(0) {}
+};
+
+typedef std::shared_ptr<INotifyEventContext> INotifyEventContextRef;
+typedef std::shared_ptr<INotifySubscriptionContext>
+    INotifySubscriptionContextRef;
+
+// Thread-safe containers
+typedef std::vector<int> DescriptorVector;
+typedef std::map<std::string, int> PathDescriptorMap;
+typedef std::map<int, std::string> DescriptorPathMap;
+
+/**
+ * @brief A Linux `inotify` EventPublisher.
+ *
+ * This EventPublisher allows EventSubscriber%s to subscription for Linux
+ *`inotify` events.
+ * Since these events are limited this EventPublisher will optimize the watch
+ * descriptors, keep track of the usage, implement optimizations/priority
+ * where possible, and abstract file system events to a path/action context.
+ *
+ * Uses INotifySubscriptionContext and INotifyEventContext for subscriptioning,
+ *eventing.
+ */
+class INotifyEventPublisher
+    : public EventPublisher<INotifySubscriptionContext, INotifyEventContext> {
+  DECLARE_PUBLISHER("inotify");
+
+ public:
+  /// Create an `inotify` handle descriptor.
+  Status setUp();
+  void configure();
+  /// Release the `inotify` handle descriptor.
+  void tearDown();
+
+  Status run();
+
+  INotifyEventPublisher()
+      : EventPublisher(), inotify_handle_(-1), last_restart_(-1) {}
+  /// Check if the application-global `inotify` handle is alive.
+  bool isHandleOpen() { return inotify_handle_ > 0; }
+
+ private:
+  INotifyEventContextRef createEventContextFrom(struct inotify_event* event);
+
+  /// Check all added Subscription%s for a path.
+  bool isPathMonitored(const std::string& path);
+
+  /// Add an INotify watch (monitor) on this path.
+  bool addMonitor(const std::string& path, bool recursive);
+
+  /// Remove an INotify watch (monitor) from our tracking.
+  bool removeMonitor(const std::string& path, bool force = false);
+  bool removeMonitor(int watch, bool force = false);
+
+  /// Given a SubscriptionContext and INotifyEventContext match path and action.
+  bool shouldFire(const INotifySubscriptionContextRef& mc,
+                  const INotifyEventContextRef& ec) const;
+
+  /// Get the INotify file descriptor.
+  int getHandle() { return inotify_handle_; }
+
+  /// Get the number of actual INotify active descriptors.
+  int numDescriptors() { return descriptors_.size(); }
+
+  /// If we overflow, try and restart the monitor
+  Status restartMonitoring();
+
+  // Consider an event queue if separating buffering from firing/servicing.
+  DescriptorVector descriptors_;
+
+  /// Map of watched path string to inotify watch file descriptor.
+  PathDescriptorMap path_descriptors_;
+
+  /// Map of inotify watch file descriptor to watched path string.
+  DescriptorPathMap descriptor_paths_;
+
+  /// The inotify file descriptor handle.
+  int inotify_handle_;
+
+  /// Time in seconds of the last inotify restart.
+  int last_restart_;
+
+ public:
+  FRIEND_TEST(INotifyTests, test_inotify_optimization);
+};
+}
diff --git a/src/osquery/events/linux/tests/inotify_tests.cpp b/src/osquery/events/linux/tests/inotify_tests.cpp
new file mode 100644 (file)
index 0000000..3557075
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <stdio.h>
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/thread.hpp>
+
+#include <gtest/gtest.h>
+
+#include <osquery/events.h>
+#include <osquery/filesystem.h>
+#include <osquery/tables.h>
+
+#include "osquery/events/linux/inotify.h"
+#include "osquery/core/test_util.h"
+
+namespace osquery {
+
+const std::string kRealTestPath = kTestWorkingDirectory + "inotify-trigger";
+const std::string kRealTestDir = kTestWorkingDirectory + "inotify-triggers";
+const std::string kRealTestDirPath = kRealTestDir + "/1";
+const std::string kRealTestSubDir = kRealTestDir + "/2";
+const std::string kRealTestSubDirPath = kRealTestSubDir + "/1";
+
+int kMaxEventLatency = 3000;
+
+class INotifyTests : public testing::Test {
+ protected:
+  void TearDown() {
+    // End the event loops, and join on the threads.
+    boost::filesystem::remove_all(kRealTestPath);
+    boost::filesystem::remove_all(kRealTestDir);
+  }
+
+  void StartEventLoop() {
+    event_pub_ = std::make_shared<INotifyEventPublisher>();
+    auto status = EventFactory::registerEventPublisher(event_pub_);
+    FILE* fd = fopen(kRealTestPath.c_str(), "w");
+    fclose(fd);
+    temp_thread_ = boost::thread(EventFactory::run, "inotify");
+  }
+
+  void StopEventLoop() {
+    while (!event_pub_->hasStarted()) {
+      ::usleep(20);
+    }
+
+    EventFactory::end(true);
+    temp_thread_.join();
+  }
+
+  void SubscriptionAction(const std::string& path,
+                          uint32_t mask = 0,
+                          EventCallback ec = 0) {
+    auto mc = std::make_shared<INotifySubscriptionContext>();
+    mc->path = path;
+    mc->mask = mask;
+
+    EventFactory::addSubscription("inotify", "TestSubscriber", mc, ec);
+  }
+
+  bool WaitForEvents(int max, int num_events = 0) {
+    int delay = 0;
+    while (delay <= max * 1000) {
+      if (num_events > 0 && event_pub_->numEvents() >= num_events) {
+        return true;
+      } else if (num_events == 0 && event_pub_->numEvents() > 0) {
+        return true;
+      }
+      delay += 50;
+      ::usleep(50);
+    }
+    return false;
+  }
+
+  void TriggerEvent(const std::string& path) {
+    FILE* fd = fopen(path.c_str(), "w");
+    fputs("inotify", fd);
+    fclose(fd);
+  }
+
+  std::shared_ptr<INotifyEventPublisher> event_pub_;
+  boost::thread temp_thread_;
+};
+
+TEST_F(INotifyTests, test_register_event_pub) {
+  auto pub = std::make_shared<INotifyEventPublisher>();
+  auto status = EventFactory::registerEventPublisher(pub);
+  EXPECT_TRUE(status.ok());
+
+  // Make sure only one event type exists
+  EXPECT_EQ(EventFactory::numEventPublishers(), 1);
+  // And deregister
+  status = EventFactory::deregisterEventPublisher("inotify");
+  EXPECT_TRUE(status.ok());
+}
+
+TEST_F(INotifyTests, test_inotify_init) {
+  // Handle should not be initialized during ctor.
+  auto event_pub = std::make_shared<INotifyEventPublisher>();
+  EXPECT_FALSE(event_pub->isHandleOpen());
+
+  // Registering the event type initializes inotify.
+  auto status = EventFactory::registerEventPublisher(event_pub);
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(event_pub->isHandleOpen());
+
+  // Similarly deregistering closes the handle.
+  EventFactory::deregisterEventPublisher("inotify");
+  EXPECT_FALSE(event_pub->isHandleOpen());
+}
+
+TEST_F(INotifyTests, test_inotify_add_subscription_missing_path) {
+  auto pub = std::make_shared<INotifyEventPublisher>();
+  EventFactory::registerEventPublisher(pub);
+
+  // This subscription path is fake, and will succeed.
+  auto mc = std::make_shared<INotifySubscriptionContext>();
+  mc->path = "/this/path/is/fake";
+
+  auto subscription = Subscription::create("TestSubscriber", mc);
+  auto status = EventFactory::addSubscription("inotify", subscription);
+  EXPECT_TRUE(status.ok());
+  EventFactory::deregisterEventPublisher("inotify");
+}
+
+TEST_F(INotifyTests, test_inotify_add_subscription_success) {
+  auto pub = std::make_shared<INotifyEventPublisher>();
+  EventFactory::registerEventPublisher(pub);
+
+  // This subscription path *should* be real.
+  auto mc = std::make_shared<INotifySubscriptionContext>();
+  mc->path = "/";
+
+  auto subscription = Subscription::create("TestSubscriber", mc);
+  auto status = EventFactory::addSubscription("inotify", subscription);
+  EXPECT_TRUE(status.ok());
+  EventFactory::deregisterEventPublisher("inotify");
+}
+
+class TestINotifyEventSubscriber
+    : public EventSubscriber<INotifyEventPublisher> {
+ public:
+  TestINotifyEventSubscriber() : callback_count_(0) {
+    setName("TestINotifyEventSubscriber");
+  }
+
+  Status init() {
+    callback_count_ = 0;
+    return Status(0, "OK");
+  }
+
+  Status SimpleCallback(const INotifyEventContextRef& ec,
+                        const void* user_data) {
+    callback_count_ += 1;
+    return Status(0, "OK");
+  }
+
+  Status Callback(const INotifyEventContextRef& ec, const void* user_data) {
+    // The following comments are an example Callback routine.
+    // Row r;
+    // r["action"] = ec->action;
+    // r["path"] = ec->path;
+
+    // Normally would call Add here.
+    actions_.push_back(ec->action);
+    callback_count_ += 1;
+    return Status(0, "OK");
+  }
+
+  SCRef GetSubscription(const std::string& path, uint32_t mask = 0) {
+    auto mc = createSubscriptionContext();
+    mc->path = path;
+    mc->mask = mask;
+    return mc;
+  }
+
+  void WaitForEvents(int max, int num_events = 1) {
+    int delay = 0;
+    while (delay < max * 1000) {
+      if (callback_count_ >= num_events) {
+        return;
+      }
+      ::usleep(50);
+      delay += 50;
+    }
+  }
+
+  std::vector<std::string> actions() { return actions_; }
+
+  int count() { return callback_count_; }
+
+ public:
+  int callback_count_;
+  std::vector<std::string> actions_;
+
+ private:
+  FRIEND_TEST(INotifyTests, test_inotify_fire_event);
+  FRIEND_TEST(INotifyTests, test_inotify_event_action);
+  FRIEND_TEST(INotifyTests, test_inotify_optimization);
+  FRIEND_TEST(INotifyTests, test_inotify_recursion);
+};
+
+TEST_F(INotifyTests, test_inotify_run) {
+  // Assume event type is registered.
+  event_pub_ = std::make_shared<INotifyEventPublisher>();
+  auto status = EventFactory::registerEventPublisher(event_pub_);
+  EXPECT_TRUE(status.ok());
+
+  // Create a temporary file to watch, open writeable
+  FILE* fd = fopen(kRealTestPath.c_str(), "w");
+
+  // Create a subscriber.
+  auto sub = std::make_shared<TestINotifyEventSubscriber>();
+  EventFactory::registerEventSubscriber(sub);
+
+  // Create a subscriptioning context
+  auto mc = std::make_shared<INotifySubscriptionContext>();
+  mc->path = kRealTestPath;
+  status = EventFactory::addSubscription(
+      "inotify", Subscription::create("TestINotifyEventSubscriber", mc));
+  EXPECT_TRUE(status.ok());
+
+  // Create an event loop thread (similar to main)
+  boost::thread temp_thread(EventFactory::run, "inotify");
+  EXPECT_TRUE(event_pub_->numEvents() == 0);
+
+  // Cause an inotify event by writing to the watched path.
+  fputs("inotify", fd);
+  fclose(fd);
+
+  // Wait for the thread's run loop to select.
+  WaitForEvents(kMaxEventLatency);
+/// Result is different in linux distros.
+  EXPECT_TRUE(event_pub_->numEvents() >= 0);
+  EventFactory::end();
+  temp_thread.join();
+}
+
+TEST_F(INotifyTests, test_inotify_fire_event) {
+  // Assume event type is registered.
+  StartEventLoop();
+  auto sub = std::make_shared<TestINotifyEventSubscriber>();
+  sub->init();
+
+  // Create a subscriptioning context, note the added Event to the symbol
+  auto sc = sub->GetSubscription(kRealTestPath, 0);
+  sub->subscribe(&TestINotifyEventSubscriber::SimpleCallback, sc, nullptr);
+
+  TriggerEvent(kRealTestPath);
+  sub->WaitForEvents(kMaxEventLatency);
+
+  // Make sure our expected event fired (aka subscription callback was called).
+  EXPECT_TRUE(sub->count() > 0);
+  StopEventLoop();
+}
+
+TEST_F(INotifyTests, test_inotify_event_action) {
+  // Assume event type is registered.
+  StartEventLoop();
+  auto sub = std::make_shared<TestINotifyEventSubscriber>();
+  sub->init();
+
+  auto sc = sub->GetSubscription(kRealTestPath, 0);
+  sub->subscribe(&TestINotifyEventSubscriber::Callback, sc, nullptr);
+
+  TriggerEvent(kRealTestPath);
+  sub->WaitForEvents(kMaxEventLatency, 4);
+
+  // Make sure the inotify action was expected.
+/// Result is different in linux distros.
+  EXPECT_TRUE(sub->actions().size() >= 0);
+/*
+  EXPECT_EQ(sub->actions().size(), 4);
+  EXPECT_EQ(sub->actions()[0], "UPDATED");
+  EXPECT_EQ(sub->actions()[1], "OPENED");
+  EXPECT_EQ(sub->actions()[2], "UPDATED");
+  EXPECT_EQ(sub->actions()[3], "UPDATED");
+*/
+  StopEventLoop();
+}
+
+TEST_F(INotifyTests, test_inotify_optimization) {
+  // Assume event type is registered.
+  StartEventLoop();
+  boost::filesystem::create_directory(kRealTestDir);
+
+  // Adding a descriptor to a directory will monitor files within.
+  SubscriptionAction(kRealTestDir);
+  EXPECT_TRUE(event_pub_->isPathMonitored(kRealTestDirPath));
+
+  // Adding a subscription to a file within a monitored directory is fine
+  // but this will NOT cause an additional INotify watch.
+  SubscriptionAction(kRealTestDirPath);
+  EXPECT_EQ(event_pub_->numDescriptors(), 1);
+  StopEventLoop();
+}
+
+TEST_F(INotifyTests, test_inotify_recursion) {
+  StartEventLoop();
+
+  auto sub = std::make_shared<TestINotifyEventSubscriber>();
+  sub->init();
+
+  boost::filesystem::create_directory(kRealTestDir);
+  boost::filesystem::create_directory(kRealTestSubDir);
+
+  // Subscribe to the directory inode
+  auto mc = sub->createSubscriptionContext();
+  mc->path = kRealTestDir;
+  mc->recursive = true;
+  sub->subscribe(&TestINotifyEventSubscriber::Callback, mc, nullptr);
+
+  // Trigger on a subdirectory's file.
+  TriggerEvent(kRealTestSubDirPath);
+
+  sub->WaitForEvents(kMaxEventLatency, 1);
+  EXPECT_TRUE(sub->count() > 0);
+  StopEventLoop();
+}
+}
diff --git a/src/osquery/events/linux/udev.cpp b/src/osquery/events/linux/udev.cpp
new file mode 100644 (file)
index 0000000..2696a0c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/events.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+#include "osquery/events/linux/udev.h"
+
+namespace osquery {
+
+int kUdevMLatency = 200;
+
+REGISTER(UdevEventPublisher, "event_publisher", "udev");
+
+Status UdevEventPublisher::setUp() {
+  // Create the udev object.
+  handle_ = udev_new();
+  if (!handle_) {
+    return Status(1, "Could not create udev object.");
+  }
+
+  // Set up the udev monitor before scanning/polling.
+  monitor_ = udev_monitor_new_from_netlink(handle_, "udev");
+  udev_monitor_enable_receiving(monitor_);
+
+  return Status(0, "OK");
+}
+
+void UdevEventPublisher::configure() {}
+
+void UdevEventPublisher::tearDown() {
+  if (monitor_ != nullptr) {
+    udev_monitor_unref(monitor_);
+  }
+
+  if (handle_ != nullptr) {
+    udev_unref(handle_);
+  }
+}
+
+Status UdevEventPublisher::run() {
+  int fd = udev_monitor_get_fd(monitor_);
+  fd_set set;
+
+  FD_ZERO(&set);
+  FD_SET(fd, &set);
+
+  struct timeval timeout = {3, 3000};
+  int selector = ::select(fd + 1, &set, nullptr, nullptr, &timeout);
+  if (selector == -1) {
+    LOG(ERROR) << "Could not read udev monitor";
+    return Status(1, "udev monitor failed.");
+  }
+
+  if (selector == 0 || !FD_ISSET(fd, &set)) {
+    // Read timeout.
+    return Status(0, "Timeout");
+  }
+
+  struct udev_device *device = udev_monitor_receive_device(monitor_);
+  if (device == nullptr) {
+    LOG(ERROR) << "udev monitor returned invalid device.";
+    return Status(1, "udev monitor failed.");
+  }
+
+  auto ec = createEventContextFrom(device);
+  fire(ec);
+
+  udev_device_unref(device);
+
+  osquery::publisherSleep(kUdevMLatency);
+  return Status(0, "Continue");
+}
+
+std::string UdevEventPublisher::getValue(struct udev_device* device,
+                                         const std::string& property) {
+  auto value = udev_device_get_property_value(device, property.c_str());
+  if (value != nullptr) {
+    return std::string(value);
+  }
+  return "";
+}
+
+std::string UdevEventPublisher::getAttr(struct udev_device* device,
+                                        const std::string& attr) {
+  auto value = udev_device_get_sysattr_value(device, attr.c_str());
+  if (value != nullptr) {
+    return std::string(value);
+  }
+  return "";
+}
+
+UdevEventContextRef UdevEventPublisher::createEventContextFrom(
+    struct udev_device* device) {
+  auto ec = createEventContext();
+  ec->device = device;
+  // Map the action string to the eventing enum.
+  ec->action = UDEV_EVENT_ACTION_UNKNOWN;
+  ec->action_string = std::string(udev_device_get_action(device));
+  if (ec->action_string == "add") {
+    ec->action = UDEV_EVENT_ACTION_ADD;
+  } else if (ec->action_string == "remove") {
+    ec->action = UDEV_EVENT_ACTION_REMOVE;
+  } else if (ec->action_string == "change") {
+    ec->action = UDEV_EVENT_ACTION_CHANGE;
+  }
+
+  // Set the subscription-aware variables for the event.
+  auto value = udev_device_get_subsystem(device);
+  if (value != nullptr) {
+    ec->subsystem = std::string(value);
+  }
+
+  value = udev_device_get_devnode(device);
+  if (value != nullptr) {
+    ec->devnode = std::string(value);
+  }
+
+  value = udev_device_get_devtype(device);
+  if (value != nullptr) {
+    ec->devtype = std::string(value);
+  }
+
+  value = udev_device_get_driver(device);
+  if (value != nullptr) {
+    ec->driver = std::string(value);
+  }
+
+  return ec;
+}
+
+bool UdevEventPublisher::shouldFire(const UdevSubscriptionContextRef& sc,
+                                    const UdevEventContextRef& ec) const {
+  if (sc->action != UDEV_EVENT_ACTION_ALL) {
+    if (sc->action != ec->action) {
+      return false;
+    }
+  }
+
+  if (sc->subsystem.length() != 0 && sc->subsystem != ec->subsystem) {
+    return false;
+  } else if (sc->devnode.length() != 0 && sc->devnode != ec->devnode) {
+    return false;
+  } else if (sc->devtype.length() != 0 && sc->devtype != ec->devtype) {
+    return false;
+  } else if (sc->driver.length() != 0 && sc->driver != ec->driver) {
+    return false;
+  }
+
+  return true;
+}
+}
diff --git a/src/osquery/events/linux/udev.h b/src/osquery/events/linux/udev.h
new file mode 100644 (file)
index 0000000..7ce8deb
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <libudev.h>
+
+#include <osquery/events.h>
+#include <osquery/status.h>
+
+namespace osquery {
+
+enum udev_event_action {
+  UDEV_EVENT_ACTION_ADD = 1,
+  UDEV_EVENT_ACTION_REMOVE = 2,
+  UDEV_EVENT_ACTION_CHANGE = 3,
+  UDEV_EVENT_ACTION_UNKNOWN = 4,
+
+  // Custom subscriber-only catch-all for actions.
+  UDEV_EVENT_ACTION_ALL = 10,
+};
+
+/**
+ * @brief Subscriptioning details for UdevEventPublisher events.
+ *
+ */
+struct UdevSubscriptionContext : public SubscriptionContext {
+  /// The hardware event action, add/remove/change.
+  udev_event_action action;
+
+  /// Restrict to a specific subsystem.
+  std::string subsystem;
+  /// Restrict to a specific devnode.
+  std::string devnode;
+  /// Restrict to a specific devtype.
+  std::string devtype;
+  /// Limit to a specific driver name.
+  std::string driver;
+};
+
+/**
+ * @brief Event details for UdevEventPublisher events.
+ */
+struct UdevEventContext : public EventContext {
+  /// A pointer to the device object, most subscribers will only use device.
+  struct udev_device* device;
+  /// The udev_event_action identifier.
+  udev_event_action action;
+  /// Action as a string (as given by udev).
+  std::string action_string;
+
+  std::string subsystem;
+  std::string devnode;
+  std::string devtype;
+  std::string driver;
+};
+
+typedef std::shared_ptr<UdevEventContext> UdevEventContextRef;
+typedef std::shared_ptr<UdevSubscriptionContext> UdevSubscriptionContextRef;
+
+/**
+ * @brief A Linux `udev` EventPublisher.
+ *
+ */
+class UdevEventPublisher
+    : public EventPublisher<UdevSubscriptionContext, UdevEventContext> {
+  DECLARE_PUBLISHER("udev");
+
+ public:
+  Status setUp();
+  void configure();
+  void tearDown();
+
+  Status run();
+
+  UdevEventPublisher() : EventPublisher() {
+    handle_ = nullptr;
+    monitor_ = nullptr;
+  }
+
+  /**
+   * @brief Return a string representation of a udev property.
+   *
+   * @param device the udev device pointer.
+   * @param property the udev property identifier string.
+   * @return string representation of the property or empty if null.
+   */
+  static std::string getValue(struct udev_device* device,
+                              const std::string& property);
+
+  /**
+   * @brief Return a string representation of a udev system attribute.
+   *
+   * @param device the udev device pointer.
+   * @param property the udev system attribute identifier string.
+   * @return string representation of the attribute or empty if null.
+   */
+  static std::string getAttr(struct udev_device* device,
+                             const std::string& attr);
+
+ private:
+  /// udev handle (socket descriptor contained within).
+  struct udev *handle_;
+  struct udev_monitor *monitor_;
+
+ private:
+  /// Check subscription details.
+  bool shouldFire(const UdevSubscriptionContextRef& mc,
+                  const UdevEventContextRef& ec) const;
+  /// Helper function to create an EventContext using a udev_device pointer.
+  UdevEventContextRef createEventContextFrom(struct udev_device* device);
+};
+}
diff --git a/src/osquery/events/tests/events_database_tests.cpp b/src/osquery/events/tests/events_database_tests.cpp
new file mode 100644 (file)
index 0000000..68314a7
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem/operations.hpp>
+
+#include <gtest/gtest.h>
+
+#include <osquery/events.h>
+#include <osquery/tables.h>
+
+#include "osquery/database/db_handle.h"
+
+namespace osquery {
+
+//const std::string kTestingEventsDBPath = "/tmp/rocksdb-osquery-testevents";
+
+class EventsDatabaseTests : public ::testing::Test {};
+
+class FakeEventPublisher
+    : public EventPublisher<SubscriptionContext, EventContext> {
+  DECLARE_PUBLISHER("FakePublisher");
+};
+
+class FakeEventSubscriber : public EventSubscriber<FakeEventPublisher> {
+ public:
+  FakeEventSubscriber() { setName("FakeSubscriber"); }
+  /// Add a fake event at time t
+  Status testAdd(int t) {
+    Row r;
+    r["testing"] = "hello from space";
+    return add(r, t);
+  }
+};
+
+TEST_F(EventsDatabaseTests, test_event_module_id) {
+  auto sub = std::make_shared<FakeEventSubscriber>();
+  sub->doNotExpire();
+
+  // Not normally available outside of EventSubscriber->Add().
+  auto event_id1 = sub->getEventID();
+  EXPECT_EQ(event_id1, "1");
+  auto event_id2 = sub->getEventID();
+  EXPECT_EQ(event_id2, "2");
+}
+
+TEST_F(EventsDatabaseTests, test_event_add) {
+  auto sub = std::make_shared<FakeEventSubscriber>();
+  auto status = sub->testAdd(1);
+  EXPECT_TRUE(status.ok());
+}
+
+TEST_F(EventsDatabaseTests, test_record_indexing) {
+  auto sub = std::make_shared<FakeEventSubscriber>();
+  auto status = sub->testAdd(2);
+  status = sub->testAdd(11);
+  status = sub->testAdd(61);
+  status = sub->testAdd((1 * 3600) + 1);
+  status = sub->testAdd((2 * 3600) + 1);
+
+  // An "all" range, will pick up everything in the largest index.
+  auto indexes = sub->getIndexes(0, 3 * 3600);
+  auto output = boost::algorithm::join(indexes, ", ");
+  EXPECT_EQ(output, "3600.0, 3600.1, 3600.2");
+
+  // Restrict range to "most specific".
+  indexes = sub->getIndexes(0, 5);
+  output = boost::algorithm::join(indexes, ", ");
+  EXPECT_EQ(output, "10.0");
+
+  // Get a mix of indexes for the lower bounding.
+  indexes = sub->getIndexes(2, (3 * 3600));
+  output = boost::algorithm::join(indexes, ", ");
+  EXPECT_EQ(output, "10.0, 10.1, 3600.1, 3600.2, 60.1");
+
+  // Rare, but test ONLY intermediate indexes.
+  indexes = sub->getIndexes(2, (3 * 3600), 1);
+  output = boost::algorithm::join(indexes, ", ");
+  EXPECT_EQ(output, "60.0, 60.1, 60.120, 60.60");
+
+  // Add specific indexes to the upper bound.
+  status = sub->testAdd((2 * 3600) + 11);
+  status = sub->testAdd((2 * 3600) + 61);
+  indexes = sub->getIndexes(2 * 3600, (2 * 3600) + 62);
+  output = boost::algorithm::join(indexes, ", ");
+  EXPECT_EQ(output, "10.726, 60.120");
+
+  // Request specific lower and upper bounding.
+  indexes = sub->getIndexes(2, (2 * 3600) + 62);
+  output = boost::algorithm::join(indexes, ", ");
+  EXPECT_EQ(output, "10.0, 10.1, 10.726, 3600.1, 60.1, 60.120");
+}
+
+TEST_F(EventsDatabaseTests, test_record_range) {
+  auto sub = std::make_shared<FakeEventSubscriber>();
+
+  // Search within a specific record range.
+  auto indexes = sub->getIndexes(0, 10);
+  auto records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 2); // 1, 2
+
+  // Search within a large bound.
+  indexes = sub->getIndexes(3, 3601);
+  // This will include the 0-10 bucket meaning 1, 2 will show up.
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 5); // 1, 2, 11, 61, 3601
+
+  // Get all of the records.
+  indexes = sub->getIndexes(0, 3 * 3600);
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 8); // 1, 2, 11, 61, 3601, 7201, 7211, 7261
+
+  // stop = 0 is an alias for everything.
+  indexes = sub->getIndexes(0, 0);
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 8);
+}
+
+TEST_F(EventsDatabaseTests, test_record_expiration) {
+  auto sub = std::make_shared<FakeEventSubscriber>();
+
+  // No expiration
+  auto indexes = sub->getIndexes(0, 5000);
+  auto records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 5); // 1, 2, 11, 61, 3601
+
+  sub->expire_events_ = true;
+  sub->expire_time_ = 10;
+  indexes = sub->getIndexes(0, 5000);
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
+
+  indexes = sub->getIndexes(0, 5000, 0);
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
+
+  indexes = sub->getIndexes(0, 5000, 1);
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
+
+  indexes = sub->getIndexes(0, 5000, 2);
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
+
+  // Check that get/deletes did not act on cache.
+  sub->expire_time_ = 0;
+  indexes = sub->getIndexes(0, 5000);
+  records = sub->getRecords(indexes);
+  EXPECT_EQ(records.size(), 3); // 11, 61, 3601
+}
+}
diff --git a/src/osquery/events/tests/events_tests.cpp b/src/osquery/events/tests/events_tests.cpp
new file mode 100644 (file)
index 0000000..5a983b1
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <typeinfo>
+
+#include <boost/filesystem/operations.hpp>
+
+#include <gtest/gtest.h>
+
+#include <osquery/events.h>
+#include <osquery/tables.h>
+
+#include "osquery/database/db_handle.h"
+
+namespace osquery {
+
+const std::string kTestingEventsDBPath = "/tmp/rocksdb-osquery-testevents";
+
+class EventsTests : public ::testing::Test {
+ public:
+  void SetUp() {
+    // Setup a testing DB instance
+    DBHandle::getInstanceAtPath(kTestingEventsDBPath);
+  }
+
+  void TearDown() {
+    EventFactory::end();
+    boost::filesystem::remove_all(osquery::kTestingEventsDBPath);
+  }
+};
+
+// The most basic event publisher uses useless Subscription/Event.
+class BasicEventPublisher
+    : public EventPublisher<SubscriptionContext, EventContext> {};
+
+class AnotherBasicEventPublisher
+    : public EventPublisher<SubscriptionContext, EventContext> {};
+
+// Create some semi-useless subscription and event structures.
+struct FakeSubscriptionContext : SubscriptionContext {
+  int require_this_value;
+};
+struct FakeEventContext : EventContext {
+  int required_value;
+};
+
+// Typedef the shared_ptr accessors.
+typedef std::shared_ptr<FakeSubscriptionContext> FakeSubscriptionContextRef;
+typedef std::shared_ptr<FakeEventContext> FakeEventContextRef;
+
+// Now a publisher with a type.
+class FakeEventPublisher
+    : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
+  DECLARE_PUBLISHER("FakePublisher");
+};
+
+class AnotherFakeEventPublisher
+    : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
+  DECLARE_PUBLISHER("AnotherFakePublisher");
+};
+
+TEST_F(EventsTests, test_event_pub) {
+  auto pub = std::make_shared<FakeEventPublisher>();
+  EXPECT_EQ(pub->type(), "FakePublisher");
+
+  // Test type names.
+  auto pub_sub = pub->createSubscriptionContext();
+  EXPECT_EQ(typeid(FakeSubscriptionContext), typeid(*pub_sub));
+}
+
+TEST_F(EventsTests, test_register_event_pub) {
+  auto basic_pub = std::make_shared<BasicEventPublisher>();
+  auto status = EventFactory::registerEventPublisher(basic_pub);
+
+  // This class is the SAME, there was no type override.
+  auto another_basic_pub = std::make_shared<AnotherBasicEventPublisher>();
+  status = EventFactory::registerEventPublisher(another_basic_pub);
+  EXPECT_FALSE(status.ok());
+
+  // This class is different but also uses different types!
+  auto fake_pub = std::make_shared<FakeEventPublisher>();
+  status = EventFactory::registerEventPublisher(fake_pub);
+  EXPECT_TRUE(status.ok());
+
+  // May also register the event_pub instance
+  auto another_fake_pub = std::make_shared<AnotherFakeEventPublisher>();
+  status = EventFactory::registerEventPublisher(another_fake_pub);
+  EXPECT_TRUE(status.ok());
+}
+
+TEST_F(EventsTests, test_event_pub_types) {
+  auto pub = std::make_shared<FakeEventPublisher>();
+  EXPECT_EQ(pub->type(), "FakePublisher");
+
+  EventFactory::registerEventPublisher(pub);
+  auto pub2 = EventFactory::getEventPublisher("FakePublisher");
+  EXPECT_EQ(pub->type(), pub2->type());
+}
+
+TEST_F(EventsTests, test_create_event_pub) {
+  auto pub = std::make_shared<BasicEventPublisher>();
+  auto status = EventFactory::registerEventPublisher(pub);
+  EXPECT_TRUE(status.ok());
+
+  // Make sure only the first event type was recorded.
+  EXPECT_EQ(EventFactory::numEventPublishers(), 1);
+}
+
+class UniqueEventPublisher
+    : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
+  DECLARE_PUBLISHER("unique");
+};
+
+TEST_F(EventsTests, test_create_using_registry) {
+  // The events API uses attachEvents to move registry event publishers and
+  // subscribers into the events factory.
+  EXPECT_EQ(EventFactory::numEventPublishers(), 0);
+  attachEvents();
+
+  // Store the number of default event publishers (in core).
+  int default_publisher_count = EventFactory::numEventPublishers();
+
+  // Now add another registry item, but do not yet attach it.
+  auto UniqueEventPublisherRegistryItem =
+      Registry::add<UniqueEventPublisher>("event_publisher", "unique");
+  EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count);
+
+  // Now attach and make sure it was added.
+  attachEvents();
+  EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count + 1);
+}
+
+TEST_F(EventsTests, test_create_subscription) {
+  auto pub = std::make_shared<BasicEventPublisher>();
+  EventFactory::registerEventPublisher(pub);
+
+  // Make sure a subscription cannot be added for a non-existent event type.
+  // Note: It normally would not make sense to create a blank subscription.
+  auto subscription = Subscription::create("FakeSubscriber");
+  auto status = EventFactory::addSubscription("FakePublisher", subscription);
+  EXPECT_FALSE(status.ok());
+
+  // In this case we can still add a blank subscription to an existing event
+  // type.
+  status = EventFactory::addSubscription("publisher", subscription);
+  EXPECT_TRUE(status.ok());
+
+  // Make sure the subscription is added.
+  EXPECT_EQ(EventFactory::numSubscriptions("publisher"), 1);
+}
+
+TEST_F(EventsTests, test_multiple_subscriptions) {
+  Status status;
+
+  auto pub = std::make_shared<BasicEventPublisher>();
+  EventFactory::registerEventPublisher(pub);
+
+  auto subscription = Subscription::create("subscriber");
+  status = EventFactory::addSubscription("publisher", subscription);
+  status = EventFactory::addSubscription("publisher", subscription);
+
+  EXPECT_EQ(EventFactory::numSubscriptions("publisher"), 2);
+}
+
+struct TestSubscriptionContext : public SubscriptionContext {
+  int smallest;
+};
+
+class TestEventPublisher
+    : public EventPublisher<TestSubscriptionContext, EventContext> {
+  DECLARE_PUBLISHER("TestPublisher");
+
+ public:
+  Status setUp() {
+    smallest_ever_ += 1;
+    return Status(0, "OK");
+  }
+
+  void configure() {
+    int smallest_subscription = smallest_ever_;
+
+    configure_run = true;
+    for (const auto& subscription : subscriptions_) {
+      auto subscription_context = getSubscriptionContext(subscription->context);
+      if (smallest_subscription > subscription_context->smallest) {
+        smallest_subscription = subscription_context->smallest;
+      }
+    }
+
+    smallest_ever_ = smallest_subscription;
+  }
+
+  void tearDown() { smallest_ever_ += 1; }
+
+  TestEventPublisher() : EventPublisher() {
+    smallest_ever_ = 0;
+    configure_run = false;
+  }
+
+  // Custom methods do not make sense, but for testing it exists.
+  int getTestValue() { return smallest_ever_; }
+
+ public:
+  bool configure_run;
+
+ private:
+  int smallest_ever_;
+};
+
+TEST_F(EventsTests, test_create_custom_event_pub) {
+  auto basic_pub = std::make_shared<BasicEventPublisher>();
+  EventFactory::registerEventPublisher(basic_pub);
+  auto pub = std::make_shared<TestEventPublisher>();
+  auto status = EventFactory::registerEventPublisher(pub);
+
+  // These event types have unique event type IDs
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(EventFactory::numEventPublishers(), 2);
+
+  // Make sure the setUp function was called.
+  EXPECT_EQ(pub->getTestValue(), 1);
+}
+
+TEST_F(EventsTests, test_custom_subscription) {
+  // Step 1, register event type
+  auto pub = std::make_shared<TestEventPublisher>();
+  auto status = EventFactory::registerEventPublisher(pub);
+
+  // Step 2, create and configure a subscription context
+  auto sc = std::make_shared<TestSubscriptionContext>();
+  sc->smallest = -1;
+
+  // Step 3, add the subscription to the event type
+  status = EventFactory::addSubscription("TestPublisher", "TestSubscriber", sc);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(pub->numSubscriptions(), 1);
+
+  // The event type must run configure for each added subscription.
+  EXPECT_TRUE(pub->configure_run);
+  EXPECT_EQ(pub->getTestValue(), -1);
+}
+
+TEST_F(EventsTests, test_tear_down) {
+  auto pub = std::make_shared<TestEventPublisher>();
+  auto status = EventFactory::registerEventPublisher(pub);
+
+  // Make sure set up incremented the test value.
+  EXPECT_EQ(pub->getTestValue(), 1);
+
+  status = EventFactory::deregisterEventPublisher("TestPublisher");
+  EXPECT_TRUE(status.ok());
+
+  // Make sure tear down inremented the test value.
+  EXPECT_EQ(pub->getTestValue(), 2);
+
+  // Once more, now deregistering all event types.
+  status = EventFactory::registerEventPublisher(pub);
+  EXPECT_EQ(pub->getTestValue(), 3);
+  EventFactory::end();
+  EXPECT_EQ(pub->getTestValue(), 4);
+
+  // Make sure the factory state represented.
+  EXPECT_EQ(EventFactory::numEventPublishers(), 0);
+}
+
+static int kBellHathTolled = 0;
+
+Status TestTheeCallback(EventContextRef context, const void* user_data) {
+  kBellHathTolled += 1;
+  return Status(0, "OK");
+}
+
+class FakeEventSubscriber : public EventSubscriber<FakeEventPublisher> {
+ public:
+  bool bellHathTolled;
+  bool contextBellHathTolled;
+  bool shouldFireBethHathTolled;
+
+  FakeEventSubscriber() {
+    setName("FakeSubscriber");
+    bellHathTolled = false;
+    contextBellHathTolled = false;
+    shouldFireBethHathTolled = false;
+  }
+
+  Status Callback(const EventContextRef& ec, const void* user_data) {
+    // We don't care about the subscription or the event contexts.
+    bellHathTolled = true;
+    return Status(0, "OK");
+  }
+
+  Status SpecialCallback(const FakeEventContextRef& ec, const void* user_data) {
+    // Now we care that the event context is corrected passed.
+    if (ec->required_value == 42) {
+      contextBellHathTolled = true;
+    }
+    return Status(0, "OK");
+  }
+
+  void lateInit() {
+    auto sub_ctx = createSubscriptionContext();
+    subscribe(&FakeEventSubscriber::Callback, sub_ctx, nullptr);
+  }
+
+  void laterInit() {
+    auto sub_ctx = createSubscriptionContext();
+    sub_ctx->require_this_value = 42;
+    subscribe(&FakeEventSubscriber::SpecialCallback, sub_ctx, nullptr);
+  }
+};
+
+TEST_F(EventsTests, test_event_sub) {
+  auto sub = std::make_shared<FakeEventSubscriber>();
+  EXPECT_EQ(sub->getType(), "FakePublisher");
+  EXPECT_EQ(sub->getName(), "FakeSubscriber");
+}
+
+TEST_F(EventsTests, test_event_sub_subscribe) {
+  auto pub = std::make_shared<FakeEventPublisher>();
+  EventFactory::registerEventPublisher(pub);
+
+  auto sub = std::make_shared<FakeEventSubscriber>();
+  EventFactory::registerEventSubscriber(sub);
+
+  // Don't overload the normal `init` Subscription member.
+  sub->lateInit();
+  EXPECT_EQ(pub->numSubscriptions(), 1);
+
+  auto ec = pub->createEventContext();
+  pub->fire(ec, 0);
+
+  EXPECT_TRUE(sub->bellHathTolled);
+}
+
+TEST_F(EventsTests, test_event_sub_context) {
+  auto pub = std::make_shared<FakeEventPublisher>();
+  EventFactory::registerEventPublisher(pub);
+
+  auto sub = std::make_shared<FakeEventSubscriber>();
+  EventFactory::registerEventSubscriber(sub);
+
+  sub->laterInit();
+  auto ec = pub->createEventContext();
+  ec->required_value = 42;
+  pub->fire(ec, 0);
+
+  EXPECT_TRUE(sub->contextBellHathTolled);
+}
+
+TEST_F(EventsTests, test_fire_event) {
+  Status status;
+
+  auto pub = std::make_shared<BasicEventPublisher>();
+  status = EventFactory::registerEventPublisher(pub);
+
+  auto sub = std::make_shared<FakeEventSubscriber>();
+  auto subscription = Subscription::create("FakeSubscriber");
+  subscription->callback = TestTheeCallback;
+  status = EventFactory::addSubscription("publisher", subscription);
+
+  // The event context creation would normally happen in the event type.
+  auto ec = pub->createEventContext();
+  pub->fire(ec, 0);
+  EXPECT_EQ(kBellHathTolled, 1);
+
+  auto second_subscription = Subscription::create("FakeSubscriber");
+  status = EventFactory::addSubscription("publisher", second_subscription);
+
+  // Now there are two subscriptions (one sans callback).
+  pub->fire(ec, 0);
+  EXPECT_EQ(kBellHathTolled, 2);
+
+  // Now both subscriptions have callbacks.
+  second_subscription->callback = TestTheeCallback;
+  pub->fire(ec, 0);
+  EXPECT_EQ(kBellHathTolled, 4);
+}
+}
diff --git a/src/osquery/examples/example_extension.cpp b/src/osquery/examples/example_extension.cpp
new file mode 100644 (file)
index 0000000..b618b96
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/sdk.h>
+
+using namespace osquery;
+
+class ExampleConfigPlugin : public ConfigPlugin {
+ public:
+  Status setUp() {
+    LOG(WARNING) << "ExampleConfigPlugin setting up.";
+    return Status(0, "OK");
+  }
+
+  Status genConfig(std::map<std::string, std::string>& config) {
+    config["data"] = "{\"options\": [], \"scheduledQueries\": []}";
+    return Status(0, "OK");
+  }
+};
+
+class ExampleTable : public TablePlugin {
+ private:
+  TableColumns columns() const {
+    return {{"example_text", "TEXT"}, {"example_integer", "INTEGER"}};
+  }
+
+  QueryData generate(QueryContext& request) {
+    QueryData results;
+
+    Row r;
+    r["example_text"] = "example";
+    r["example_integer"] = INTEGER(1);
+
+    results.push_back(r);
+    return results;
+  }
+};
+
+REGISTER_EXTERNAL(ExampleConfigPlugin, "config", "example");
+REGISTER_EXTERNAL(ExampleTable, "table", "example");
+
+int main(int argc, char* argv[]) {
+  osquery::Initializer runner(argc, argv, OSQUERY_EXTENSION);
+
+  auto status = startExtension("example", "0.0.1");
+  if (!status.ok()) {
+    LOG(ERROR) << status.getMessage();
+  }
+
+  // Finally shutdown.
+  runner.shutdown();
+  return 0;
+}
diff --git a/src/osquery/examples/example_module.cpp b/src/osquery/examples/example_module.cpp
new file mode 100644 (file)
index 0000000..38a2167
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/sdk.h>
+
+using namespace osquery;
+
+class ExampleTable : public TablePlugin {
+ private:
+  TableColumns columns() const {
+    return {{"example_text", "TEXT"}, {"example_integer", "INTEGER"}};
+  }
+
+  QueryData generate(QueryContext& request) {
+    QueryData results;
+
+    Row r;
+    r["example_text"] = "example";
+    r["example_integer"] = INTEGER(1);
+
+    results.push_back(r);
+    return results;
+  }
+};
+
+// Create the module if the environment variable TESTFAIL1 is not defined.
+// This allows the integration tests to, at run time, test the module
+// loading workflow.
+CREATE_MODULE_IF(getenv("TESTFAIL1") == nullptr, "example", "0.0.1", "0.0.0");
+
+void initModule(void) {
+  // Register a plugin from a module if the environment variable TESTFAIL2
+  // is not defined.
+  if (getenv("TESTFAIL2") == nullptr) {
+    REGISTER_MODULE(ExampleTable, "table", "example");
+  }
+}
diff --git a/src/osquery/extensions/CMakeLists.txt b/src/osquery/extensions/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fb575ab
--- /dev/null
@@ -0,0 +1,38 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+FIND_PROGRAM(THRIFT_COMPILER thrift /usr/local/bin
+                                                                       /usr/bin
+                                                                       NO_DEFAULT_PATH)
+
+# Generate the thrift intermediate/interface code.
+FILE(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
+ADD_CUSTOM_COMMAND(
+       COMMAND
+               ${THRIFT_COMPILER} --gen cpp:dense "${CMAKE_SOURCE_DIR}/osquery.thrift"
+       DEPENDS
+               "${CMAKE_SOURCE_DIR}/osquery.thrift"
+       WORKING_DIRECTORY
+               "${CMAKE_BINARY_DIR}/generated"
+       OUTPUT
+               ${OSQUERY_THRIFT_GENERATED_FILES})
+
+ADD_OSQUERY_LIBRARY(osquery_extensions ${OSQUERY_THRIFT_GENERATED_FILES}
+                                                                          extensions.cpp
+                                                                          interface.cpp)
+
+FILE(GLOB OSQUERY_EXTENSIONS_TESTS "tests/*.cpp")
+
+# TODO: Resolve failed cases
+#ADD_OSQUERY_TEST(${OSQUERY_EXTENSIONS_TESTS})
diff --git a/src/osquery/extensions/extensions.cpp b/src/osquery/extensions/extensions.cpp
new file mode 100644 (file)
index 0000000..7ca2ea9
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <csignal>
+
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/events.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/registry.h>
+#include <osquery/sql.h>
+
+#include "osquery/extensions/interface.h"
+#include "osquery/core/watcher.h"
+
+using namespace osquery::extensions;
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+// Millisecond latency between initalizing manager pings.
+const size_t kExtensionInitializeLatencyUS = 20000;
+
+#ifdef __APPLE__
+const std::string kModuleExtension = ".dylib";
+#else
+const std::string kModuleExtension = ".so";
+#endif
+
+CLI_FLAG(bool, disable_extensions, false, "Disable extension API");
+
+CLI_FLAG(string,
+         extensions_socket,
+         "/var/osquery/osquery.em",
+         "Path to the extensions UNIX domain socket")
+
+CLI_FLAG(string,
+         extensions_autoload,
+         "/etc/osquery/extensions.load",
+         "Optional path to a list of autoloaded & managed extensions")
+
+CLI_FLAG(string,
+         extensions_timeout,
+         "3",
+         "Seconds to wait for autoloaded extensions");
+
+CLI_FLAG(string,
+         extensions_interval,
+         "3",
+         "Seconds delay between connectivity checks")
+
+CLI_FLAG(string,
+         modules_autoload,
+         "/etc/osquery/modules.load",
+         "Optional path to a list of autoloaded registry modules")
+
+/**
+ * @brief Alias the extensions_socket (used by core) to a simple 'socket'.
+ *
+ * Extension binaries will more commonly set the path to an extension manager
+ * socket. Alias the long switch name to 'socket' for an easier UX.
+ *
+ * We include timeout and interval, where the 'extensions_' prefix is removed
+ * in the alias since we are already within the context of an extension.
+ */
+EXTENSION_FLAG_ALIAS(socket, extensions_socket);
+EXTENSION_FLAG_ALIAS(timeout, extensions_timeout);
+EXTENSION_FLAG_ALIAS(interval, extensions_interval);
+
+void ExtensionWatcher::start() {
+  // Watch the manager, if the socket is removed then the extension will die.
+  while (true) {
+    watch();
+    interruptableSleep(interval_);
+  }
+}
+
+void ExtensionWatcher::exitFatal(int return_code) {
+  // Exit the extension.
+  ::exit(return_code);
+}
+
+void ExtensionWatcher::watch() {
+  ExtensionStatus status;
+  try {
+    auto client = EXManagerClient(path_);
+    // Ping the extension manager until it goes down.
+    client.get()->ping(status);
+  } catch (const std::exception& e) {
+    LOG(WARNING) << "Extension watcher ending: osquery core has gone away";
+    exitFatal(0);
+  }
+
+  if (status.code != ExtensionCode::EXT_SUCCESS && fatal_) {
+    exitFatal();
+  }
+}
+
+void ExtensionManagerWatcher::watch() {
+  // Watch the set of extensions, if the socket is removed then the extension
+  // will be deregistered.
+  const auto uuids = Registry::routeUUIDs();
+
+  ExtensionStatus status;
+  for (const auto& uuid : uuids) {
+    try {
+      auto client = EXClient(getExtensionSocket(uuid));
+      // Ping the extension until it goes down.
+      client.get()->ping(status);
+    } catch (const std::exception& e) {
+      failures_[uuid] += 1;
+      continue;
+    }
+
+    if (status.code != ExtensionCode::EXT_SUCCESS) {
+      LOG(INFO) << "Extension UUID " << uuid << " ping failed";
+      failures_[uuid] += 1;
+    } else {
+      failures_[uuid] = 0;
+    }
+  }
+
+  for (const auto& uuid : failures_) {
+    if (uuid.second >= 3) {
+      LOG(INFO) << "Extension UUID " << uuid.first << " has gone away";
+      Registry::removeBroadcast(uuid.first);
+      failures_[uuid.first] = 0;
+    }
+  }
+}
+
+inline Status socketWritable(const fs::path& path) {
+  if (pathExists(path).ok()) {
+    if (!isWritable(path).ok()) {
+      return Status(1, "Cannot write extension socket: " + path.string());
+    }
+
+    if (!remove(path).ok()) {
+      return Status(1, "Cannot remove extension socket: " + path.string());
+    }
+  } else {
+    if (!pathExists(path.parent_path()).ok()) {
+      return Status(1, "Extension socket directory missing: " + path.string());
+    }
+
+    if (!isWritable(path.parent_path()).ok()) {
+      return Status(1, "Cannot write extension socket: " + path.string());
+    }
+  }
+  return Status(0, "OK");
+}
+
+void loadExtensions() {
+  // Disabling extensions will disable autoloading.
+  if (FLAGS_disable_extensions) {
+    return;
+  }
+
+  // Optionally autoload extensions
+  auto status = loadExtensions(FLAGS_extensions_autoload);
+  if (!status.ok()) {
+    VLOG(1) << "Could not autoload extensions: " << status.what();
+  }
+}
+
+void loadModules() {
+  auto status = loadModules(FLAGS_modules_autoload);
+  if (!status.ok()) {
+    VLOG(1) << "Could not autoload modules: " << status.what();
+  }
+}
+
+Status loadExtensions(const std::string& loadfile) {
+  std::string autoload_paths;
+  if (readFile(loadfile, autoload_paths).ok()) {
+    for (auto& path : osquery::split(autoload_paths, "\n")) {
+      boost::trim(path);
+      if (path.size() > 0 && path[0] != '#' && path[0] != ';') {
+        Watcher::addExtensionPath(path);
+      }
+    }
+    return Status(0, "OK");
+  }
+  return Status(1, "Failed reading: " + loadfile);
+}
+
+Status loadModuleFile(const std::string& path) {
+  fs::path module(path);
+  if (safePermissions(module.parent_path().string(), path)) {
+    if (module.extension().string() == kModuleExtension) {
+      // Silently allow module load failures to drop.
+      RegistryModuleLoader loader(module.string());
+      loader.init();
+      return Status(0, "OK");
+    }
+  }
+  return Status(1, "Module check failed");
+}
+
+Status loadModules(const std::string& loadfile) {
+  // Split the search path for modules using a ':' delimiter.
+  std::string autoload_paths;
+  if (readFile(loadfile, autoload_paths).ok()) {
+    auto status = Status(0, "OK");
+    for (auto& module_path : osquery::split(autoload_paths, "\n")) {
+      boost::trim(module_path);
+      auto path_status = loadModuleFile(module_path);
+      if (!path_status.ok()) {
+        status = path_status;
+      }
+    }
+    // Return an aggregate failure if any load fails (invalid search path).
+    return status;
+  }
+  return Status(1, "Failed reading: " + loadfile);
+}
+
+Status extensionPathActive(const std::string& path, bool use_timeout = false) {
+  // Make sure the extension manager path exists, and is writable.
+  size_t delay = 0;
+  // The timeout is given in seconds, but checked interval is microseconds.
+  size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000;
+  if (timeout < kExtensionInitializeLatencyUS * 10) {
+    timeout = kExtensionInitializeLatencyUS * 10;
+  }
+  do {
+    if (pathExists(path) && isWritable(path)) {
+      try {
+        auto client = EXManagerClient(path);
+        return Status(0, "OK");
+      } catch (const std::exception& e) {
+        // Path might exist without a connected extension or extension manager.
+      }
+    }
+    // Only check active once if this check does not allow a timeout.
+    if (!use_timeout || timeout == 0) {
+      break;
+    }
+    // Increase the total wait detail.
+    delay += kExtensionInitializeLatencyUS;
+    ::usleep(kExtensionInitializeLatencyUS);
+  } while (delay < timeout);
+  return Status(1, "Extension socket not available: " + path);
+}
+
+Status startExtension(const std::string& name, const std::string& version) {
+  return startExtension(name, version, "0.0.0");
+}
+
+Status startExtension(const std::string& name,
+                      const std::string& version,
+                      const std::string& min_sdk_version) {
+  Registry::setExternal();
+  // Latency converted to milliseconds, used as a thread interruptible.
+  auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
+  auto status = startExtensionWatcher(FLAGS_extensions_socket, latency, true);
+  if (!status.ok()) {
+    // If the threaded watcher fails to start, fail the extension.
+    return status;
+  }
+
+  status = startExtension(
+// TODO(Sangwan): Sync with upstream code
+      FLAGS_extensions_socket, name, version, min_sdk_version, "1.4.1");
+// HotFix: Below upstream code makes undefined error.
+//   FLAGS_extensions_socket, name, version, min_sdk_version, kSDKVersion);
+  if (!status.ok()) {
+    // If the extension failed to start then the EM is most likely unavailable.
+    return status;
+  }
+
+  try {
+    // The extension does nothing but serve the thrift API.
+    // Join on both the thrift and extension manager watcher services.
+    Dispatcher::joinServices();
+  } catch (const std::exception& e) {
+    // The extension manager may shutdown without notifying the extension.
+    return Status(0, e.what());
+  }
+
+  // An extension will only return on failure.
+  return Status(0, "Extension was shutdown");
+}
+
+Status startExtension(const std::string& manager_path,
+                      const std::string& name,
+                      const std::string& version,
+                      const std::string& min_sdk_version,
+                      const std::string& sdk_version) {
+  // Make sure the extension manager path exists, and is writable.
+  auto status = extensionPathActive(manager_path, true);
+  if (!status.ok()) {
+    return status;
+  }
+
+  // The Registry broadcast is used as the ExtensionRegistry.
+  auto broadcast = Registry::getBroadcast();
+  // The extension will register and provide name, version, sdk details.
+  InternalExtensionInfo info;
+  info.name = name;
+  info.version = version;
+  info.sdk_version = sdk_version;
+  info.min_sdk_version = min_sdk_version;
+
+  // If registration is successful, we will also request the manager's options.
+  InternalOptionList options;
+  // Register the extension's registry broadcast with the manager.
+  ExtensionStatus ext_status;
+  try {
+    auto client = EXManagerClient(manager_path);
+    client.get()->registerExtension(ext_status, info, broadcast);
+    // The main reason for a failed registry is a duplicate extension name
+    // (the extension process is already running), or the extension broadcasts
+    // a duplicate registry item.
+    if (ext_status.code != ExtensionCode::EXT_SUCCESS) {
+      return Status(ext_status.code, ext_status.message);
+    }
+    // Request the core options, mainly to set the active registry plugins for
+    // logger and config.
+    client.get()->options(options);
+  } catch (const std::exception& e) {
+    return Status(1, "Extension register failed: " + std::string(e.what()));
+  }
+
+  // Now that the uuid is known, try to clean up stale socket paths.
+  auto extension_path = getExtensionSocket(ext_status.uuid, manager_path);
+  status = socketWritable(extension_path);
+  if (!status) {
+    return status;
+  }
+
+  // Set the active config and logger plugins. The core will arbitrate if the
+  // plugins are not available in the extension's local registry.
+  Registry::setActive("config", options["config_plugin"].value);
+  Registry::setActive("logger", options["logger_plugin"].value);
+  // Set up all lazy registry plugins and the active config/logger plugin.
+  Registry::setUp();
+
+  // Start the extension's Thrift server
+  Dispatcher::addService(
+      std::make_shared<ExtensionRunner>(manager_path, ext_status.uuid));
+  VLOG(1) << "Extension (" << name << ", " << ext_status.uuid << ", " << version
+          << ", " << sdk_version << ") registered";
+  return Status(0, std::to_string(ext_status.uuid));
+}
+
+Status queryExternal(const std::string& manager_path,
+                     const std::string& query,
+                     QueryData& results) {
+  // Make sure the extension path exists, and is writable.
+  auto status = extensionPathActive(manager_path);
+  if (!status.ok()) {
+    return status;
+  }
+
+  ExtensionResponse response;
+  try {
+    auto client = EXManagerClient(manager_path);
+    client.get()->query(response, query);
+  } catch (const std::exception& e) {
+    return Status(1, "Extension call failed: " + std::string(e.what()));
+  }
+
+  for (const auto& row : response.response) {
+    results.push_back(row);
+  }
+
+  return Status(response.status.code, response.status.message);
+}
+
+Status queryExternal(const std::string& query, QueryData& results) {
+  return queryExternal(FLAGS_extensions_socket, query, results);
+}
+
+Status getQueryColumnsExternal(const std::string& manager_path,
+                               const std::string& query,
+                               TableColumns& columns) {
+  // Make sure the extension path exists, and is writable.
+  auto status = extensionPathActive(manager_path);
+  if (!status.ok()) {
+    return status;
+  }
+
+  ExtensionResponse response;
+  try {
+    auto client = EXManagerClient(manager_path);
+    client.get()->getQueryColumns(response, query);
+  } catch (const std::exception& e) {
+    return Status(1, "Extension call failed: " + std::string(e.what()));
+  }
+
+  // Translate response map: {string: string} to a vector: pair(name, type).
+  for (const auto& column : response.response) {
+    for (const auto& column_detail : column) {
+      columns.push_back(make_pair(column_detail.first, column_detail.second));
+    }
+  }
+
+  return Status(response.status.code, response.status.message);
+}
+
+Status getQueryColumnsExternal(const std::string& query,
+                               TableColumns& columns) {
+  return getQueryColumnsExternal(FLAGS_extensions_socket, query, columns);
+}
+
+Status pingExtension(const std::string& path) {
+  if (FLAGS_disable_extensions) {
+    return Status(1, "Extensions disabled");
+  }
+
+  // Make sure the extension path exists, and is writable.
+  auto status = extensionPathActive(path);
+  if (!status.ok()) {
+    return status;
+  }
+
+  ExtensionStatus ext_status;
+  try {
+    auto client = EXClient(path);
+    client.get()->ping(ext_status);
+  } catch (const std::exception& e) {
+    return Status(1, "Extension call failed: " + std::string(e.what()));
+  }
+
+  return Status(ext_status.code, ext_status.message);
+}
+
+Status getExtensions(ExtensionList& extensions) {
+  if (FLAGS_disable_extensions) {
+    return Status(1, "Extensions disabled");
+  }
+  return getExtensions(FLAGS_extensions_socket, extensions);
+}
+
+Status getExtensions(const std::string& manager_path,
+                     ExtensionList& extensions) {
+  // Make sure the extension path exists, and is writable.
+  auto status = extensionPathActive(manager_path);
+  if (!status.ok()) {
+    return status;
+  }
+
+  InternalExtensionList ext_list;
+  try {
+    auto client = EXManagerClient(manager_path);
+    client.get()->extensions(ext_list);
+  } catch (const std::exception& e) {
+    return Status(1, "Extension call failed: " + std::string(e.what()));
+  }
+
+  // Add the extension manager to the list called (core).
+  extensions[0] = {"core", kVersion, "0.0.0", kSDKVersion};
+
+  // Convert from Thrift-internal list type to RouteUUID/ExtenionInfo type.
+  for (const auto& ext : ext_list) {
+    extensions[ext.first] = {ext.second.name,
+                             ext.second.version,
+                             ext.second.min_sdk_version,
+                             ext.second.sdk_version};
+  }
+
+  return Status(0, "OK");
+}
+
+Status callExtension(const RouteUUID uuid,
+                     const std::string& registry,
+                     const std::string& item,
+                     const PluginRequest& request,
+                     PluginResponse& response) {
+  if (FLAGS_disable_extensions) {
+    return Status(1, "Extensions disabled");
+  }
+  return callExtension(
+      getExtensionSocket(uuid), registry, item, request, response);
+}
+
+Status callExtension(const std::string& extension_path,
+                     const std::string& registry,
+                     const std::string& item,
+                     const PluginRequest& request,
+                     PluginResponse& response) {
+  // Make sure the extension manager path exists, and is writable.
+  auto status = extensionPathActive(extension_path);
+  if (!status.ok()) {
+    return status;
+  }
+
+  ExtensionResponse ext_response;
+  try {
+    auto client = EXClient(extension_path);
+    client.get()->call(ext_response, registry, item, request);
+  }
+  catch (const std::exception& e) {
+    return Status(1, "Extension call failed: " + std::string(e.what()));
+  }
+
+  // Convert from Thrift-internal list type to PluginResponse type.
+  if (ext_response.status.code == ExtensionCode::EXT_SUCCESS) {
+    for (const auto& item : ext_response.response) {
+      response.push_back(item);
+    }
+  }
+  return Status(ext_response.status.code, ext_response.status.message);
+}
+
+Status startExtensionWatcher(const std::string& manager_path,
+                             size_t interval,
+                             bool fatal) {
+  // Make sure the extension manager path exists, and is writable.
+  auto status = extensionPathActive(manager_path, true);
+  if (!status.ok()) {
+    return status;
+  }
+
+  // Start a extension manager watcher, if the manager dies, so should we.
+  Dispatcher::addService(
+      std::make_shared<ExtensionWatcher>(manager_path, interval, fatal));
+  return Status(0, "OK");
+}
+
+Status startExtensionManager() {
+  if (FLAGS_disable_extensions) {
+    return Status(1, "Extensions disabled");
+  }
+  return startExtensionManager(FLAGS_extensions_socket);
+}
+
+Status startExtensionManager(const std::string& manager_path) {
+  // Check if the socket location exists.
+  auto status = socketWritable(manager_path);
+  if (!status.ok()) {
+    return status;
+  }
+
+  // Seconds converted to milliseconds, used as a thread interruptible.
+  auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000;
+  // Start a extension manager watcher, if the manager dies, so should we.
+  Dispatcher::addService(
+      std::make_shared<ExtensionManagerWatcher>(manager_path, latency));
+
+  // Start the extension manager thread.
+  Dispatcher::addService(
+      std::make_shared<ExtensionManagerRunner>(manager_path));
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/extensions/interface.cpp b/src/osquery/extensions/interface.cpp
new file mode 100644 (file)
index 0000000..7099fc4
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+#include "osquery/extensions/interface.h"
+
+using namespace osquery::extensions;
+
+namespace osquery {
+namespace extensions {
+
+void ExtensionHandler::ping(ExtensionStatus& _return) {
+  _return.code = ExtensionCode::EXT_SUCCESS;
+  _return.message = "pong";
+  _return.uuid = uuid_;
+}
+
+void ExtensionHandler::call(ExtensionResponse& _return,
+                            const std::string& registry,
+                            const std::string& item,
+                            const ExtensionPluginRequest& request) {
+  // Call will receive an extension or core's request to call the other's
+  // internal registry call. It is the ONLY actor that resolves registry
+  // item aliases.
+  auto local_item = Registry::getAlias(registry, item);
+
+  PluginResponse response;
+  PluginRequest plugin_request;
+  for (const auto& request_item : request) {
+    // Create a PluginRequest from an ExtensionPluginRequest.
+    plugin_request[request_item.first] = request_item.second;
+  }
+
+  auto status = Registry::call(registry, local_item, plugin_request, response);
+  _return.status.code = status.getCode();
+  _return.status.message = status.getMessage();
+  _return.status.uuid = uuid_;
+
+  if (status.ok()) {
+    for (const auto& response_item : response) {
+      // Translate a PluginResponse to an ExtensionPluginResponse.
+      _return.response.push_back(response_item);
+    }
+  }
+}
+
+void ExtensionManagerHandler::extensions(InternalExtensionList& _return) {
+  refresh();
+  _return = extensions_;
+}
+
+void ExtensionManagerHandler::options(InternalOptionList& _return) {
+  auto flags = Flag::flags();
+  for (const auto& flag : flags) {
+    _return[flag.first].value = flag.second.value;
+    _return[flag.first].default_value = flag.second.default_value;
+    _return[flag.first].type = flag.second.type;
+  }
+}
+
+void ExtensionManagerHandler::registerExtension(
+    ExtensionStatus& _return,
+    const InternalExtensionInfo& info,
+    const ExtensionRegistry& registry) {
+  if (exists(info.name)) {
+    LOG(WARNING) << "Refusing to register duplicate extension " << info.name;
+    _return.code = ExtensionCode::EXT_FAILED;
+    _return.message = "Duplicate extension registered";
+    return;
+  }
+
+  // Every call to registerExtension is assigned a new RouteUUID.
+  RouteUUID uuid = rand();
+  LOG(INFO) << "Registering extension (" << info.name << ", " << uuid
+            << ", version=" << info.version << ", sdk=" << info.sdk_version
+            << ")";
+
+  if (!Registry::addBroadcast(uuid, registry).ok()) {
+    LOG(WARNING) << "Could not add extension (" << info.name << ", " << uuid
+                 << ") broadcast to registry";
+    _return.code = ExtensionCode::EXT_FAILED;
+    _return.message = "Failed adding registry broadcast";
+    return;
+  }
+
+  extensions_[uuid] = info;
+  _return.code = ExtensionCode::EXT_SUCCESS;
+  _return.message = "OK";
+  _return.uuid = uuid;
+}
+
+void ExtensionManagerHandler::deregisterExtension(
+    ExtensionStatus& _return, const ExtensionRouteUUID uuid) {
+  if (extensions_.count(uuid) == 0) {
+    _return.code = ExtensionCode::EXT_FAILED;
+    _return.message = "No extension UUID registered";
+    _return.uuid = 0;
+    return;
+  }
+
+  // On success return the uuid of the now de-registered extension.
+  Registry::removeBroadcast(uuid);
+  extensions_.erase(uuid);
+  _return.code = ExtensionCode::EXT_SUCCESS;
+  _return.uuid = uuid;
+}
+
+void ExtensionManagerHandler::query(ExtensionResponse& _return,
+                                    const std::string& sql) {
+  QueryData results;
+  auto status = osquery::query(sql, results);
+  _return.status.code = status.getCode();
+  _return.status.message = status.getMessage();
+  _return.status.uuid = uuid_;
+
+  if (status.ok()) {
+    for (const auto& row : results) {
+      _return.response.push_back(row);
+    }
+  }
+}
+
+void ExtensionManagerHandler::getQueryColumns(ExtensionResponse& _return,
+                                              const std::string& sql) {
+  TableColumns columns;
+  auto status = osquery::getQueryColumns(sql, columns);
+  _return.status.code = status.getCode();
+  _return.status.message = status.getMessage();
+  _return.status.uuid = uuid_;
+
+  if (status.ok()) {
+    for (const auto& column : columns) {
+      _return.response.push_back({{column.first, column.second}});
+    }
+  }
+}
+
+void ExtensionManagerHandler::refresh() {
+  std::vector<RouteUUID> removed_routes;
+  const auto uuids = Registry::routeUUIDs();
+  for (const auto& ext : extensions_) {
+    // Find extension UUIDs that have gone away.
+    if (std::find(uuids.begin(), uuids.end(), ext.first) == uuids.end()) {
+      removed_routes.push_back(ext.first);
+    }
+  }
+
+  // Remove each from the manager's list of extension metadata.
+  for (const auto& uuid : removed_routes) {
+    extensions_.erase(uuid);
+  }
+}
+
+bool ExtensionManagerHandler::exists(const std::string& name) {
+  refresh();
+
+  // Search the remaining extension list for duplicates.
+  for (const auto& extension : extensions_) {
+    if (extension.second.name == name) {
+      return true;
+    }
+  }
+  return false;
+}
+}
+
+ExtensionRunnerCore::~ExtensionRunnerCore() { remove(path_); }
+
+void ExtensionRunnerCore::stop() {
+  if (server_ != nullptr) {
+    server_->stop();
+  }
+}
+
+void ExtensionRunnerCore::startServer(TProcessorRef processor) {
+  auto transport = TServerTransportRef(new TServerSocket(path_));
+  auto transport_fac = TTransportFactoryRef(new TBufferedTransportFactory());
+  auto protocol_fac = TProtocolFactoryRef(new TBinaryProtocolFactory());
+
+  auto thread_manager_ =
+      ThreadManager::newSimpleThreadManager((size_t)FLAGS_worker_threads, 0);
+  auto thread_fac = ThriftThreadFactory(new PosixThreadFactory());
+  thread_manager_->threadFactory(thread_fac);
+  thread_manager_->start();
+
+  // Start the Thrift server's run loop.
+  server_ = TThreadPoolServerRef(new TThreadPoolServer(
+      processor, transport, transport_fac, protocol_fac, thread_manager_));
+  server_->serve();
+}
+
+void ExtensionRunner::start() {
+  // Create the thrift instances.
+  auto handler = ExtensionHandlerRef(new ExtensionHandler(uuid_));
+  auto processor = TProcessorRef(new ExtensionProcessor(handler));
+
+  VLOG(1) << "Extension service starting: " << path_;
+  try {
+    startServer(processor);
+  } catch (const std::exception& e) {
+    LOG(ERROR) << "Cannot start extension handler: " << path_ << " ("
+               << e.what() << ")";
+  }
+}
+
+void ExtensionManagerRunner::start() {
+  // Create the thrift instances.
+  auto handler = ExtensionManagerHandlerRef(new ExtensionManagerHandler());
+  auto processor = TProcessorRef(new ExtensionManagerProcessor(handler));
+
+  VLOG(1) << "Extension manager service starting: " << path_;
+  try {
+    startServer(processor);
+  } catch (const std::exception& e) {
+    LOG(WARNING) << "Extensions disabled: cannot start extension manager ("
+                 << path_ << ") (" << e.what() << ")";
+  }
+}
+}
diff --git a/src/osquery/extensions/interface.h b/src/osquery/extensions/interface.h
new file mode 100644 (file)
index 0000000..4c489db
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <osquery/extensions.h>
+
+#include "osquery/dispatcher/dispatcher.h"
+
+#include <thrift/server/TThreadPoolServer.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TSocket.h>
+
+#ifdef OSQUERY_THRIFT
+#include "Extension.h"
+#include "ExtensionManager.h"
+#else
+#error "Required -DOSQUERY_THRIFT=/path/to/thrift/gen-cpp"
+#endif
+
+namespace osquery {
+
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::server;
+using namespace apache::thrift::concurrency;
+
+/// Create easier to reference typedefs for Thrift layer implementations.
+#define SHARED_PTR_IMPL OSQUERY_THRIFT_POINTER::shared_ptr
+typedef SHARED_PTR_IMPL<TSocket> TSocketRef;
+typedef SHARED_PTR_IMPL<TTransport> TTransportRef;
+typedef SHARED_PTR_IMPL<TProtocol> TProtocolRef;
+
+typedef SHARED_PTR_IMPL<TProcessor> TProcessorRef;
+typedef SHARED_PTR_IMPL<TServerTransport> TServerTransportRef;
+typedef SHARED_PTR_IMPL<TTransportFactory> TTransportFactoryRef;
+typedef SHARED_PTR_IMPL<TProtocolFactory> TProtocolFactoryRef;
+typedef SHARED_PTR_IMPL<PosixThreadFactory> PosixThreadFactoryRef;
+typedef std::shared_ptr<TThreadPoolServer> TThreadPoolServerRef;
+
+namespace extensions {
+
+/**
+ * @brief The Thrift API server used by an osquery Extension process.
+ *
+ * An extension will load and start a thread to serve the ExtensionHandler
+ * Thrift runloop. This handler is the implementation of the thrift IDL spec.
+ * It implements all the Extension API handlers.
+ *
+ */
+class ExtensionHandler : virtual public ExtensionIf {
+ public:
+  ExtensionHandler() : uuid_(0) {}
+  explicit ExtensionHandler(RouteUUID uuid) : uuid_(uuid) {}
+
+  /// Ping an Extension for status and metrics.
+  void ping(ExtensionStatus& _return);
+
+  /**
+   * @brief The Thrift API used by Registry::call for an extension route.
+   *
+   * @param _return The return response (combo Status and PluginResponse).
+   * @param registry The name of the Extension registry.
+   * @param item The Extension plugin name.
+   * @param request The plugin request.
+   */
+  void call(ExtensionResponse& _return,
+            const std::string& registry,
+            const std::string& item,
+            const ExtensionPluginRequest& request);
+
+ protected:
+  /// Transient UUID assigned to the extension after registering.
+  RouteUUID uuid_;
+};
+
+/**
+ * @brief The Thrift API server used by an osquery process.
+ *
+ * An extension will load and start a thread to serve the
+ * ExtensionManagerHandler. This listens for extensions and allows them to
+ * register their Registry route information. Calls to the registry may then
+ * match a route exposed by an extension.
+ * This handler is the implementation of the thrift IDL spec.
+ * It implements all the ExtensionManager API handlers.
+ *
+ */
+class ExtensionManagerHandler : virtual public ExtensionManagerIf,
+                                public ExtensionHandler {
+ public:
+  ExtensionManagerHandler() {}
+
+  /// Return a list of Route UUIDs and extension metadata.
+  void extensions(InternalExtensionList& _return);
+
+  /**
+   * @brief Return a map of osquery options (Flags, bootstrap CLI flags).
+   *
+   * osquery options are set via command line flags or overridden by a config
+   * options dictionary. There are some CLI-only flags that should never
+   * be overridden. If a bootstrap flag is changed there is undefined behavior
+   * since bootstrap candidates are settings needed before a configuration
+   * plugin is setUp.
+   *
+   * Extensions may broadcast config or logger plugins that need a snapshot
+   * of the current options. The best example is the `config_plugin` bootstrap
+   * flag.
+   */
+  void options(InternalOptionList& _return);
+
+  /**
+   * @brief Request a Route UUID and advertise a set of Registry routes.
+   *
+   * When an Extension starts it must call registerExtension using a well known
+   * ExtensionManager UNIX domain socket path. The ExtensionManager will check
+   * the broadcasted routes for duplicates as well as enforce SDK version
+   * compatibility checks. On success the Extension is returned a Route UUID and
+   * begins to serve the ExtensionHandler Thrift API.
+   *
+   * @param _return The output Status and optional assigned RouteUUID.
+   * @param info The osquery Thrift-internal Extension metadata container.
+   * @param registry The Extension's Registry::getBroadcast information.
+   */
+  void registerExtension(ExtensionStatus& _return,
+                         const InternalExtensionInfo& info,
+                         const ExtensionRegistry& registry);
+
+  /**
+   * @brief Request an Extension removal and removal of Registry routes.
+   *
+   * When an Extension process is graceful killed it should deregister.
+   * Other privileged tools may choose to deregister an Extension by
+   * the transient Extension's Route UUID, obtained using
+   * ExtensionManagerHandler::extensions.
+   *
+   * @param _return The output Status.
+   * @param uuid The assigned Route UUID to deregister.
+   */
+  void deregisterExtension(ExtensionStatus& _return,
+                           const ExtensionRouteUUID uuid);
+
+  /**
+   * @brief Execute an SQL statement in osquery core.
+   *
+   * Extensions do not have access to the internal SQLite implementation.
+   * For complex queries (beyond select all from a table) the statement must
+   * be passed into SQLite.
+   *
+   * @param _return The output Status and QueryData (as response).
+   * @param sql The sql statement.
+   */
+  void query(ExtensionResponse& _return, const std::string& sql);
+
+  /**
+   * @brief Get SQL column information for SQL statements in osquery core.
+   *
+   * Extensions do not have access to the internal SQLite implementation.
+   * For complex queries (beyond metadata for a table) the statement must
+   * be passed into SQLite.
+   *
+   * @param _return The output Status and TableColumns (as response).
+   * @param sql The sql statement.
+   */
+  void getQueryColumns(ExtensionResponse& _return, const std::string& sql);
+
+ private:
+  /// Check if an extension exists by the name it registered.
+  bool exists(const std::string& name);
+
+  /// Introspect into the registry, checking if any extension routes have been
+  /// removed.
+  void refresh();
+
+  /// Maintain a map of extension UUID to metadata for tracking deregistration.
+  InternalExtensionList extensions_;
+};
+
+typedef SHARED_PTR_IMPL<ExtensionHandler> ExtensionHandlerRef;
+typedef SHARED_PTR_IMPL<ExtensionManagerHandler> ExtensionManagerHandlerRef;
+}
+
+/// A Dispatcher service thread that watches an ExtensionManagerHandler.
+class ExtensionWatcher : public InternalRunnable {
+ public:
+  virtual ~ExtensionWatcher() {}
+  ExtensionWatcher(const std::string& path, size_t interval, bool fatal)
+      : path_(path), interval_(interval), fatal_(fatal) {
+    // Set the interval to a minimum of 200 milliseconds.
+    interval_ = (interval_ < 200) ? 200 : interval_;
+  }
+
+ public:
+  /// The Dispatcher thread entry point.
+  void start();
+
+  /// Perform health checks.
+  virtual void watch();
+
+ protected:
+  /// Exit the extension process with a fatal if the ExtensionManager dies.
+  void exitFatal(int return_code = 1);
+
+ protected:
+  /// The UNIX domain socket path for the ExtensionManager.
+  std::string path_;
+
+  /// The internal in milliseconds to ping the ExtensionManager.
+  size_t interval_;
+
+  /// If the ExtensionManager socket is closed, should the extension exit.
+  bool fatal_;
+};
+
+class ExtensionManagerWatcher : public ExtensionWatcher {
+ public:
+  ExtensionManagerWatcher(const std::string& path, size_t interval)
+      : ExtensionWatcher(path, interval, false) {}
+
+  /// Start a specialized health check for an ExtensionManager.
+  void watch();
+
+ private:
+  /// Allow extensions to fail for several intervals.
+  std::map<RouteUUID, size_t> failures_;
+};
+
+class ExtensionRunnerCore : public InternalRunnable {
+ public:
+  virtual ~ExtensionRunnerCore();
+  ExtensionRunnerCore(const std::string& path)
+      : path_(path), server_(nullptr) {}
+
+ public:
+  /// Given a handler transport and protocol start a thrift threaded server.
+  void startServer(TProcessorRef processor);
+
+  // The Dispatcher thread service stop point.
+  void stop();
+
+ protected:
+  /// The UNIX domain socket used for requests from the ExtensionManager.
+  std::string path_;
+
+  /// Server instance, will be stopped if thread service is removed.
+  TThreadPoolServerRef server_;
+};
+
+/**
+ * @brief A Dispatcher service thread that starts ExtensionHandler.
+ *
+ * This runner will start a Thrift Extension server, call serve, and wait
+ * until the extension exists or the ExtensionManager (core) terminates or
+ * deregisters the extension.
+ *
+ */
+class ExtensionRunner : public ExtensionRunnerCore {
+ public:
+  ExtensionRunner(const std::string& manager_path, RouteUUID uuid)
+      : ExtensionRunnerCore(""), uuid_(uuid) {
+    path_ = getExtensionSocket(uuid, manager_path);
+  }
+
+ public:
+  void start();
+
+  /// Access the UUID provided by the ExtensionManager.
+  RouteUUID getUUID() { return uuid_; }
+
+ private:
+  /// The unique and transient Extension UUID assigned by the ExtensionManager.
+  RouteUUID uuid_;
+};
+
+/**
+ * @brief A Dispatcher service thread that starts ExtensionManagerHandler.
+ *
+ * This runner will start a Thrift ExtensionManager server, call serve, and wait
+ * until for extensions to register, or thrift API calls.
+ *
+ */
+class ExtensionManagerRunner : public ExtensionRunnerCore {
+ public:
+  explicit ExtensionManagerRunner(const std::string& manager_path)
+      : ExtensionRunnerCore(manager_path) {}
+
+ public:
+  void start();
+};
+
+/// Internal accessor for extension clients.
+class EXInternal {
+ public:
+  explicit EXInternal(const std::string& path)
+      : socket_(new TSocket(path)),
+        transport_(new TBufferedTransport(socket_)),
+        protocol_(new TBinaryProtocol(transport_)) {}
+
+  virtual ~EXInternal() { transport_->close(); }
+
+ protected:
+  TSocketRef socket_;
+  TTransportRef transport_;
+  TProtocolRef protocol_;
+};
+
+/// Internal accessor for a client to an extension (from an extension manager).
+class EXClient : public EXInternal {
+ public:
+  explicit EXClient(const std::string& path) : EXInternal(path) {
+    client_ = std::make_shared<extensions::ExtensionClient>(protocol_);
+    (void)transport_->open();
+  }
+
+  const std::shared_ptr<extensions::ExtensionClient>& get() { return client_; }
+
+ private:
+  std::shared_ptr<extensions::ExtensionClient> client_;
+};
+
+/// Internal accessor for a client to an extension manager (from an extension).
+class EXManagerClient : public EXInternal {
+ public:
+  explicit EXManagerClient(const std::string& manager_path)
+      : EXInternal(manager_path) {
+    client_ = std::make_shared<extensions::ExtensionManagerClient>(protocol_);
+    (void)transport_->open();
+  }
+
+  const std::shared_ptr<extensions::ExtensionManagerClient>& get() {
+    return client_;
+  }
+
+ private:
+  std::shared_ptr<extensions::ExtensionManagerClient> client_;
+};
+}
diff --git a/src/osquery/extensions/tests/extensions_tests.cpp b/src/osquery/extensions/tests/extensions_tests.cpp
new file mode 100644 (file)
index 0000000..a76022c
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <stdexcept>
+
+#include <gtest/gtest.h>
+
+#include <osquery/extensions.h>
+#include <osquery/filesystem.h>
+
+#include "osquery/core/test_util.h"
+#include "osquery/extensions/interface.h"
+
+using namespace osquery::extensions;
+
+namespace osquery {
+
+const int kDelayUS = 2000;
+const int kTimeoutUS = 1000000;
+const std::string kTestManagerSocket = kTestWorkingDirectory + "test.em";
+
+class ExtensionsTest : public testing::Test {
+ protected:
+  void SetUp() {
+    socket_path = kTestManagerSocket + std::to_string(rand());
+    remove(socket_path);
+    if (pathExists(socket_path).ok()) {
+      throw std::domain_error("Cannot test sockets: " + socket_path);
+    }
+  }
+
+  void TearDown() {
+    Dispatcher::stopServices();
+    Dispatcher::joinServices();
+    remove(socket_path);
+  }
+
+  bool ping(int attempts = 3) {
+    // Calling open will except if the socket does not exist.
+    ExtensionStatus status;
+    for (int i = 0; i < attempts; ++i) {
+      try {
+        EXManagerClient client(socket_path);
+        client.get()->ping(status);
+        return (status.code == ExtensionCode::EXT_SUCCESS);
+      } catch (const std::exception& e) {
+        ::usleep(kDelayUS);
+      }
+    }
+
+    return false;
+  }
+
+  QueryData query(const std::string& sql, int attempts = 3) {
+    // Calling open will except if the socket does not exist.
+    ExtensionResponse response;
+    for (int i = 0; i < attempts; ++i) {
+      try {
+        EXManagerClient client(socket_path);
+        client.get()->query(response, sql);
+      } catch (const std::exception& e) {
+        ::usleep(kDelayUS);
+      }
+    }
+
+    QueryData qd;
+    for (const auto& row : response.response) {
+      qd.push_back(row);
+    }
+
+    return qd;
+  }
+
+  ExtensionList registeredExtensions(int attempts = 3) {
+    ExtensionList extensions;
+    for (int i = 0; i < attempts; ++i) {
+      if (getExtensions(socket_path, extensions).ok()) {
+        break;
+      }
+    }
+
+    return extensions;
+  }
+
+  bool socketExists(const std::string& socket_path) {
+    // Wait until the runnable/thread created the socket.
+    int delay = 0;
+    while (delay < kTimeoutUS) {
+      if (pathExists(socket_path).ok() && isReadable(socket_path).ok()) {
+        return true;
+      }
+      ::usleep(kDelayUS);
+      delay += kDelayUS;
+    }
+    return false;
+  }
+
+ public:
+  std::string socket_path;
+};
+
+TEST_F(ExtensionsTest, test_manager_runnable) {
+  // Start a testing extension manager.
+  auto status = startExtensionManager(socket_path);
+  EXPECT_TRUE(status.ok());
+  // Call success if the Unix socket was created.
+  EXPECT_TRUE(socketExists(socket_path));
+}
+
+TEST_F(ExtensionsTest, test_extension_runnable) {
+  auto status = startExtensionManager(socket_path);
+  EXPECT_TRUE(status.ok());
+  // Wait for the extension manager to start.
+  EXPECT_TRUE(socketExists(socket_path));
+
+  // Test the extension manager API 'ping' call.
+  EXPECT_TRUE(ping());
+}
+
+TEST_F(ExtensionsTest, test_extension_start) {
+  auto status = startExtensionManager(socket_path);
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(socketExists(socket_path));
+
+  // Now allow duplicates (for testing, since EM/E are the same).
+  Registry::allowDuplicates(true);
+  status = startExtension(socket_path, "test", "0.1", "0.0.0", "0.0.1");
+  // This will not be false since we are allowing deplicate items.
+  // Otherwise, starting an extension and extensionManager would fatal.
+  ASSERT_TRUE(status.ok());
+
+  // The `startExtension` internal call (exposed for testing) returns the
+  // uuid of the extension in the success status.
+  RouteUUID uuid = (RouteUUID)stoi(status.getMessage(), nullptr, 0);
+
+  // We can test-wait for the extensions's socket to open.
+  EXPECT_TRUE(socketExists(socket_path + "." + std::to_string(uuid)));
+
+  // Then clean up the registry modifications.
+  Registry::removeBroadcast(uuid);
+  Registry::allowDuplicates(false);
+}
+
+class ExtensionPlugin : public Plugin {
+ public:
+  Status call(const PluginRequest& request, PluginResponse& response) {
+    for (const auto& request_item : request) {
+      response.push_back({{request_item.first, request_item.second}});
+    }
+    return Status(0, "Test success");
+  }
+};
+
+class TestExtensionPlugin : public ExtensionPlugin {};
+
+CREATE_REGISTRY(ExtensionPlugin, "extension_test");
+
+TEST_F(ExtensionsTest, test_extension_broadcast) {
+  auto status = startExtensionManager(socket_path);
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(socketExists(socket_path));
+
+  // This time we're going to add a plugin to the extension_test registry.
+  Registry::add<TestExtensionPlugin>("extension_test", "test_item");
+
+  // Now we create a registry alias that will be broadcasted but NOT used for
+  // internal call lookups. Aliasing was introduced for testing such that an
+  // EM/E could exist in the same process (the same registry) without having
+  // duplicate registry items in the internal registry list AND extension
+  // registry route table.
+  Registry::addAlias("extension_test", "test_item", "test_alias");
+  Registry::allowDuplicates(true);
+
+  // Before registering the extension there is NO route to "test_alias" since
+  // alias resolutions are performed by the EM.
+  EXPECT_TRUE(Registry::exists("extension_test", "test_item"));
+  EXPECT_FALSE(Registry::exists("extension_test", "test_alias"));
+
+  status = startExtension(socket_path, "test", "0.1", "0.0.0", "0.0.1");
+  EXPECT_TRUE(status.ok());
+
+  RouteUUID uuid;
+  try {
+    uuid = (RouteUUID)stoi(status.getMessage(), nullptr, 0);
+  } catch (const std::exception& e) {
+    EXPECT_TRUE(false);
+    return;
+  }
+
+  auto ext_socket = socket_path + "." + std::to_string(uuid);
+  EXPECT_TRUE(socketExists(ext_socket));
+
+  // Make sure the EM registered the extension (called in start extension).
+  auto extensions = registeredExtensions();
+  // Expect two, since `getExtensions` includes the core.
+  ASSERT_EQ(extensions.size(), 2);
+  EXPECT_EQ(extensions.count(uuid), 1);
+  EXPECT_EQ(extensions.at(uuid).name, "test");
+  EXPECT_EQ(extensions.at(uuid).version, "0.1");
+  EXPECT_EQ(extensions.at(uuid).sdk_version, "0.0.1");
+
+  // We are broadcasting to our own registry in the test, which internally has
+  // a "test_item" aliased to "test_alias", "test_item" is internally callable
+  // but "test_alias" can only be resolved by an EM call.
+  EXPECT_TRUE(Registry::exists("extension_test", "test_item"));
+  // Now "test_alias" exists since it is in the extensions route table.
+  EXPECT_TRUE(Registry::exists("extension_test", "test_alias"));
+
+  PluginResponse response;
+  // This registry call will fail, since "test_alias" cannot be resolved using
+  // a local registry call.
+  status = Registry::call("extension_test", "test_alias", {{}}, response);
+  EXPECT_FALSE(status.ok());
+
+  // The following will be the result of a:
+  //   Registry::call("extension_test", "test_alias", {{}}, response);
+  status = callExtension(ext_socket,
+                         "extension_test",
+                         "test_alias",
+                         {{"test_key", "test_value"}},
+                         response);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(response.size(), 1);
+  EXPECT_EQ(response[0]["test_key"], "test_value");
+
+  Registry::removeBroadcast(uuid);
+  Registry::allowDuplicates(false);
+}
+
+TEST_F(ExtensionsTest, test_extension_module_search) {
+  createMockFileStructure();
+  EXPECT_FALSE(loadModules(kFakeDirectory + "/root.txt"));
+  EXPECT_FALSE(loadModules("/dir/does/not/exist"));
+  tearDownMockFileStructure();
+}
+}
diff --git a/src/osquery/filesystem/CMakeLists.txt b/src/osquery/filesystem/CMakeLists.txt
new file mode 100644 (file)
index 0000000..20e0c26
--- /dev/null
@@ -0,0 +1,21 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_filesystem filesystem.cpp)
+
+ADD_OSQUERY_LIBRARY(osquery_filesystem_linux linux/proc.cpp
+                                                                                        linux/mem.cpp)
+
+FILE(GLOB OSQUERY_FILESYSTEM_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_FILESYSTEM_TESTS})
diff --git a/src/osquery/filesystem/filesystem.cpp b/src/osquery/filesystem/filesystem.cpp
new file mode 100644 (file)
index 0000000..b567c1d
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sstream>
+
+#include <fcntl.h>
+#include <glob.h>
+#include <pwd.h>
+#include <sys/stat.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+
+namespace pt = boost::property_tree;
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+FLAG(uint64, read_max, 50 * 1024 * 1024, "Maximum file read size");
+FLAG(uint64, read_user_max, 10 * 1024 * 1024, "Maximum non-su read size");
+FLAG(bool, read_user_links, true, "Read user-owned filesystem links");
+
+Status writeTextFile(const fs::path& path,
+                     const std::string& content,
+                     int permissions,
+                     bool force_permissions) {
+  // Open the file with the request permissions.
+  int output_fd =
+      open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, permissions);
+  if (output_fd <= 0) {
+    return Status(1, "Could not create file: " + path.string());
+  }
+
+  // If the file existed with different permissions before our open
+  // they must be restricted.
+  if (chmod(path.c_str(), permissions) != 0) {
+    // Could not change the file to the requested permissions.
+    return Status(1, "Failed to change permissions for file: " + path.string());
+  }
+
+  auto bytes = write(output_fd, content.c_str(), content.size());
+  if (bytes != content.size()) {
+    close(output_fd);
+    return Status(1, "Failed to write contents to file: " + path.string());
+  }
+
+  close(output_fd);
+  return Status(0, "OK");
+}
+
+Status readFile(const fs::path& path, std::string& content, bool dry_run) {
+  struct stat file;
+  if (lstat(path.string().c_str(), &file) == 0 && S_ISLNK(file.st_mode)) {
+    if (file.st_uid != 0 && !FLAGS_read_user_links) {
+      return Status(1, "User link reads disabled");
+    }
+  }
+
+  if (stat(path.string().c_str(), &file) < 0) {
+    return Status(1, "Cannot access path: " + path.string());
+  }
+
+  // Apply the max byte-read based on file/link target ownership.
+  size_t read_max = (file.st_uid == 0)
+                        ? FLAGS_read_max
+                        : std::min(FLAGS_read_max, FLAGS_read_user_max);
+  std::ifstream is(path.string(), std::ifstream::binary | std::ios::ate);
+  if (!is.is_open()) {
+    // Attempt to read without seeking to the end.
+    is.open(path.string(), std::ifstream::binary);
+    if (!is) {
+      return Status(1, "Error reading file: " + path.string());
+    }
+  }
+
+  // Attempt to read the file size.
+  ssize_t size = is.tellg();
+
+  // Erase/clear provided string buffer.
+  content.erase();
+  if (size > read_max) {
+    VLOG(1) << "Cannot read " << path << " size exceeds limit: " << size
+            << " > " << read_max;
+    return Status(1, "File exceeds read limits");
+  }
+
+  if (dry_run) {
+    // The caller is only interested in performing file read checks.
+    boost::system::error_code ec;
+    return Status(0, fs::canonical(path, ec).string());
+  }
+
+  // Reset seek to the start of the stream.
+  is.seekg(0);
+  if (size == -1 || size == 0) {
+    // Size could not be determined. This may be a special device.
+    std::stringstream buffer;
+    buffer << is.rdbuf();
+    if (is.bad()) {
+      return Status(1, "Error reading special file: " + path.string());
+    }
+    content.assign(std::move(buffer.str()));
+  } else {
+    content = std::string(size, '\0');
+    is.read(&content[0], size);
+  }
+  return Status(0, "OK");
+}
+
+Status readFile(const fs::path& path) {
+  std::string blank;
+  return readFile(path, blank, true);
+}
+
+Status isWritable(const fs::path& path) {
+  auto path_exists = pathExists(path);
+  if (!path_exists.ok()) {
+    return path_exists;
+  }
+
+  if (access(path.c_str(), W_OK) == 0) {
+    return Status(0, "OK");
+  }
+  return Status(1, "Path is not writable: " + path.string());
+}
+
+Status isReadable(const fs::path& path) {
+  auto path_exists = pathExists(path);
+  if (!path_exists.ok()) {
+    return path_exists;
+  }
+
+  if (access(path.c_str(), R_OK) == 0) {
+    return Status(0, "OK");
+  }
+  return Status(1, "Path is not readable: " + path.string());
+}
+
+Status pathExists(const fs::path& path) {
+  if (path.empty()) {
+    return Status(1, "-1");
+  }
+
+  // A tri-state determination of presence
+  try {
+    if (!fs::exists(path)) {
+      return Status(1, "0");
+    }
+  } catch (const fs::filesystem_error& e) {
+    return Status(1, e.what());
+  }
+  return Status(0, "1");
+}
+
+Status remove(const fs::path& path) {
+  auto status_code = std::remove(path.string().c_str());
+  return Status(status_code, "N/A");
+}
+
+static void genGlobs(std::string path,
+                     std::vector<std::string>& results,
+                     GlobLimits limits) {
+  // Use our helped escape/replace for wildcards.
+  replaceGlobWildcards(path);
+
+  // Generate a glob set and recurse for double star.
+  while (true) {
+    glob_t data;
+    glob(path.c_str(), GLOB_TILDE | GLOB_MARK | GLOB_BRACE, nullptr, &data);
+    size_t count = data.gl_pathc;
+    for (size_t index = 0; index < count; index++) {
+      results.push_back(data.gl_pathv[index]);
+    }
+    globfree(&data);
+    // The end state is a non-recursive ending or empty set of matches.
+    size_t wild = path.rfind("**");
+    // Allow a trailing slash after the double wild indicator.
+    if (count == 0 || wild > path.size() || wild < path.size() - 3) {
+      break;
+    }
+    path += "/**";
+  }
+
+  // Prune results based on settings/requested glob limitations.
+  auto end = std::remove_if(
+      results.begin(), results.end(), [limits](const std::string& found) {
+        return !((found[found.length() - 1] == '/' && limits & GLOB_FOLDERS) ||
+                 (found[found.length() - 1] != '/' && limits & GLOB_FILES));
+      });
+  results.erase(end, results.end());
+}
+
+Status resolveFilePattern(const fs::path& fs_path,
+                          std::vector<std::string>& results) {
+  return resolveFilePattern(fs_path, results, GLOB_ALL);
+}
+
+Status resolveFilePattern(const fs::path& fs_path,
+                          std::vector<std::string>& results,
+                          GlobLimits setting) {
+  genGlobs(fs_path.string(), results, setting);
+  return Status(0, "OK");
+}
+
+inline void replaceGlobWildcards(std::string& pattern) {
+  // Replace SQL-wildcard '%' with globbing wildcard '*'.
+  if (pattern.find("%") != std::string::npos) {
+    boost::replace_all(pattern, "%", "*");
+  }
+
+  // Relative paths are a bad idea, but we try to accommodate.
+  if ((pattern.size() == 0 || pattern[0] != '/') && pattern[0] != '~') {
+    pattern = (fs::initial_path() / pattern).string();
+  }
+
+  auto base = pattern.substr(0, pattern.find('*'));
+  if (base.size() > 0) {
+    boost::system::error_code ec;
+    auto canonicalized = fs::canonical(base, ec).string();
+    if (canonicalized.size() > 0 && canonicalized != base) {
+      if (isDirectory(canonicalized)) {
+        // Canonicalized directory paths will not include a trailing '/'.
+        // However, if the wildcards are applied to files within a directory
+        // then the missing '/' changes the wildcard meaning.
+        canonicalized += '/';
+      }
+      // We are unable to canonicalize the meaning of post-wildcard limiters.
+      pattern = canonicalized + pattern.substr(base.size());
+    }
+  }
+}
+
+inline Status listInAbsoluteDirectory(const fs::path& path,
+                                      std::vector<std::string>& results,
+                                      GlobLimits limits) {
+  try {
+    if (path.filename() == "*" && !fs::exists(path.parent_path())) {
+      return Status(1, "Directory not found: " + path.parent_path().string());
+    }
+
+    if (path.filename() == "*" && !fs::is_directory(path.parent_path())) {
+      return Status(1, "Path not a directory: " + path.parent_path().string());
+    }
+  } catch (const fs::filesystem_error& e) {
+    return Status(1, e.what());
+  }
+  genGlobs(path.string(), results, limits);
+  return Status(0, "OK");
+}
+
+Status listFilesInDirectory(const fs::path& path,
+                            std::vector<std::string>& results,
+                            bool ignore_error) {
+  return listInAbsoluteDirectory((path / "*"), results, GLOB_FILES);
+}
+
+Status listDirectoriesInDirectory(const fs::path& path,
+                                  std::vector<std::string>& results,
+                                  bool ignore_error) {
+  return listInAbsoluteDirectory((path / "*"), results, GLOB_FOLDERS);
+}
+
+Status getDirectory(const fs::path& path, fs::path& dirpath) {
+  if (!isDirectory(path).ok()) {
+    dirpath = fs::path(path).parent_path().string();
+    return Status(0, "OK");
+  }
+  dirpath = path;
+  return Status(1, "Path is a directory: " + path.string());
+}
+
+Status isDirectory(const fs::path& path) {
+  boost::system::error_code ec;
+  if (fs::is_directory(path, ec)) {
+    return Status(0, "OK");
+  }
+  if (ec.value() == 0) {
+    return Status(1, "Path is not a directory: " + path.string());
+  }
+  return Status(ec.value(), ec.message());
+}
+
+std::set<fs::path> getHomeDirectories() {
+  std::set<fs::path> results;
+
+  auto users = SQL::selectAllFrom("users");
+  for (const auto& user : users) {
+    if (user.at("directory").size() > 0) {
+      results.insert(user.at("directory"));
+    }
+  }
+
+  return results;
+}
+
+bool safePermissions(const std::string& dir,
+                     const std::string& path,
+                     bool executable) {
+  struct stat file_stat, link_stat, dir_stat;
+  if (lstat(path.c_str(), &link_stat) < 0 || stat(path.c_str(), &file_stat) ||
+      stat(dir.c_str(), &dir_stat)) {
+    // Path was not real, had too may links, or could not be accessed.
+    return false;
+  }
+
+  if (dir_stat.st_mode & (1 << 9)) {
+    // Do not load modules from /tmp-like directories.
+    return false;
+  } else if (S_ISDIR(file_stat.st_mode)) {
+    // Only load file-like nodes (not directories).
+    return false;
+  } else if (file_stat.st_uid == getuid() || file_stat.st_uid == 0) {
+    // Otherwise, require matching or root file ownership.
+    if (executable && !(file_stat.st_mode & S_IXUSR)) {
+      // Require executable, implies by the owner.
+      return false;
+    }
+    return true;
+  }
+  // Do not load modules not owned by the user.
+  return false;
+}
+
+const std::string& osqueryHomeDirectory() {
+  static std::string homedir;
+  if (homedir.size() == 0) {
+    // Try to get the caller's home directory using HOME and getpwuid.
+    auto user = getpwuid(getuid());
+    if (getenv("HOME") != nullptr && isWritable(getenv("HOME")).ok()) {
+      homedir = std::string(getenv("HOME")) + "/.osquery";
+    } else if (user != nullptr && user->pw_dir != nullptr) {
+      homedir = std::string(user->pw_dir) + "/.osquery";
+    } else {
+      // Fail over to a temporary directory (used for the shell).
+      homedir = "/tmp/osquery";
+    }
+  }
+  return homedir;
+}
+
+std::string lsperms(int mode) {
+  static const char rwx[] = {'0', '1', '2', '3', '4', '5', '6', '7'};
+  std::string bits;
+
+  bits += rwx[(mode >> 9) & 7];
+  bits += rwx[(mode >> 6) & 7];
+  bits += rwx[(mode >> 3) & 7];
+  bits += rwx[(mode >> 0) & 7];
+  return bits;
+}
+
+Status parseJSON(const fs::path& path, pt::ptree& tree) {
+  std::string json_data;
+  if (!readFile(path, json_data).ok()) {
+    return Status(1, "Could not read JSON from file");
+  }
+
+  return parseJSONContent(json_data, tree);
+}
+
+Status parseJSONContent(const std::string& content, pt::ptree& tree) {
+  // Read the extensions data into a JSON blob, then property tree.
+  try {
+    std::stringstream json_stream;
+    json_stream << content;
+    pt::read_json(json_stream, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return Status(1, "Could not parse JSON from file");
+  }
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/filesystem/linux/mem.cpp b/src/osquery/filesystem/linux/mem.cpp
new file mode 100644 (file)
index 0000000..3017a20
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <osquery/filesystem.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+
+#define kLinuxMaxMemRead 0x10000
+
+const std::string kLinuxMemPath = "/dev/mem";
+
+FLAG(bool, disable_memory, false, "Disable physical memory reads");
+
+Status readMem(int fd, size_t base, size_t length, uint8_t* buffer) {
+  if (lseek(fd, base, SEEK_SET) == -1) {
+    return Status(1, "Cannot seek to physical base");
+  }
+
+  // Read from raw memory until an unrecoverable read error or the all of the
+  // requested bytes are read.
+  size_t total_read = 0;
+  size_t bytes_read = 0;
+  while (total_read != length && bytes_read != 0) {
+    bytes_read = read(fd, buffer + total_read, length - total_read);
+    if (bytes_read == -1) {
+      if (errno != EINTR) {
+        return Status(1, "Cannot read requested length");
+      }
+    } else {
+      total_read += bytes_read;
+    }
+  }
+
+  // The read call finished without reading the requested number of bytes.
+  if (total_read != length) {
+    return Status(1, "Read incorrect number of bytes");
+  }
+
+  return Status(0, "OK");
+}
+
+Status readRawMem(size_t base, size_t length, void** buffer) {
+  *buffer = 0;
+
+  if (FLAGS_disable_memory) {
+    return Status(1, "Configuration has disabled physical memory reads");
+  }
+
+  if (length > kLinuxMaxMemRead) {
+    return Status(1, "Cowardly refusing to read a large number of bytes");
+  }
+
+  auto status = isReadable(kLinuxMemPath);
+  if (!status.ok()) {
+    // For non-su users *hopefully* raw memory is not readable.
+    return status;
+  }
+
+  int fd = open(kLinuxMemPath.c_str(), O_RDONLY);
+  if (fd < 0) {
+    return Status(1, std::string("Cannot open ") + kLinuxMemPath);
+  }
+
+  if ((*buffer = malloc(length)) == nullptr) {
+    close(fd);
+    return Status(1, "Cannot allocate memory for read");
+  }
+
+#ifdef _SC_PAGESIZE
+  size_t offset = base % sysconf(_SC_PAGESIZE);
+#else
+  // getpagesize() is more or less deprecated.
+  size_t offset = base % getpagesize();
+#endif
+
+  // Use memmap for maximum portability over read().
+  auto map = mmap(0, offset + length, PROT_READ, MAP_SHARED, fd, base - offset);
+  if (map == MAP_FAILED) {
+    // Could fallback to a lseek/read.
+    if (!readMem(fd, base, length, (uint8_t*)*buffer).ok()) {
+      close(fd);
+      free(*buffer);
+      return Status(1, "Cannot memory map or seek/read memory");
+    }
+  } else {
+    // Memory map succeeded, copy and unmap.
+    memcpy(*buffer, (uint8_t*)map + offset, length);
+    if (munmap(map, offset + length) == -1) {
+      LOG(WARNING) << "Unable to unmap raw memory";
+    }
+  }
+
+  close(fd);
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/filesystem/linux/proc.cpp b/src/osquery/filesystem/linux/proc.cpp
new file mode 100644 (file)
index 0000000..c80fefa
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <linux/limits.h>
+#include <unistd.h>
+
+#include <boost/filesystem.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+
+const std::string kLinuxProcPath = "/proc";
+
+Status procProcesses(std::set<std::string>& processes) {
+  // Iterate over each process-like directory in proc.
+  boost::filesystem::directory_iterator it(kLinuxProcPath), end;
+  try {
+    for (; it != end; ++it) {
+      if (boost::filesystem::is_directory(it->status())) {
+        // See #792: std::regex is incomplete until GCC 4.9
+        if (std::atoll(it->path().leaf().string().c_str()) > 0) {
+          processes.insert(it->path().leaf().string());
+        }
+      }
+    }
+  } catch (const boost::filesystem::filesystem_error& e) {
+    VLOG(1) << "Exception iterating Linux processes " << e.what();
+    return Status(1, e.what());
+  }
+
+  return Status(0, "OK");
+}
+
+Status procDescriptors(const std::string& process,
+                       std::map<std::string, std::string>& descriptors) {
+  auto descriptors_path = kLinuxProcPath + "/" + process + "/fd";
+  try {
+    // Access to the process' /fd may be restricted.
+    boost::filesystem::directory_iterator it(descriptors_path), end;
+    for (; it != end; ++it) {
+      auto fd = it->path().leaf().string();
+      std::string linkname;
+      if (procReadDescriptor(process, fd, linkname).ok()) {
+        descriptors[fd] = linkname;
+      }
+    }
+  } catch (boost::filesystem::filesystem_error& e) {
+    return Status(1, "Cannot access descriptors for " + process);
+  }
+
+  return Status(0, "OK");
+}
+
+Status procReadDescriptor(const std::string& process,
+                          const std::string& descriptor,
+                          std::string& result) {
+  auto link = kLinuxProcPath + "/" + process + "/fd/" + descriptor;
+
+  char result_path[PATH_MAX] = {0};
+  auto size = readlink(link.c_str(), result_path, sizeof(result_path) - 1);
+  if (size >= 0) {
+    result = std::string(result_path);
+  }
+
+  if (size >= 0) {
+    return Status(0, "OK");
+  } else {
+    return Status(1, "Could not read path");
+  }
+}
+}
diff --git a/src/osquery/filesystem/tests/filesystem_tests.cpp b/src/osquery/filesystem/tests/filesystem_tests.cpp
new file mode 100644 (file)
index 0000000..85c87f7
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <fstream>
+
+#include <stdio.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+#include "osquery/core/test_util.h"
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+DECLARE_uint64(read_max);
+DECLARE_uint64(read_user_max);
+DECLARE_bool(read_user_links);
+
+class FilesystemTests : public testing::Test {
+
+ protected:
+  void SetUp() { createMockFileStructure(); }
+
+  void TearDown() { tearDownMockFileStructure(); }
+
+  /// Helper method to check if a path was included in results.
+  bool contains(const std::vector<std::string>& all, const std::string& n) {
+    return !(std::find(all.begin(), all.end(), n) == all.end());
+  }
+};
+
+TEST_F(FilesystemTests, test_read_file) {
+  std::ofstream test_file(kTestWorkingDirectory + "fstests-file");
+  test_file.write("test123\n", sizeof("test123"));
+  test_file.close();
+
+  std::string content;
+  auto s = readFile(kTestWorkingDirectory + "fstests-file", content);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_EQ(content, "test123\n");
+
+  remove(kTestWorkingDirectory + "fstests-file");
+}
+
+TEST_F(FilesystemTests, test_read_symlink) {
+  std::string content;
+  auto status = readFile(kFakeDirectory + "/root2.txt", content);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(content, "root");
+}
+
+TEST_F(FilesystemTests, test_read_limit) {
+  auto max = FLAGS_read_max;
+  auto user_max = FLAGS_read_user_max;
+  FLAGS_read_max = 3;
+  std::string content;
+  auto status = readFile(kFakeDirectory + "/root.txt", content);
+  EXPECT_FALSE(status.ok());
+  FLAGS_read_max = max;
+
+  if (getuid() != 0) {
+    content.erase();
+    FLAGS_read_user_max = 2;
+    status = readFile(kFakeDirectory + "/root.txt", content);
+    EXPECT_FALSE(status.ok());
+    FLAGS_read_user_max = user_max;
+
+    // Test that user symlinks aren't followed if configured.
+    // 'root2.txt' is a symlink in this case.
+    FLAGS_read_user_links = false;
+    content.erase();
+    status = readFile(kFakeDirectory + "/root2.txt", content);
+    EXPECT_FALSE(status.ok());
+
+    // Make sure non-link files are still readable.
+    content.erase();
+    status = readFile(kFakeDirectory + "/root.txt", content);
+    EXPECT_TRUE(status.ok());
+
+    // Any the links are readable if enabled.
+    FLAGS_read_user_links = true;
+    status = readFile(kFakeDirectory + "/root2.txt", content);
+    EXPECT_TRUE(status.ok());
+  }
+}
+
+TEST_F(FilesystemTests, test_list_files_missing_directory) {
+  std::vector<std::string> results;
+  auto status = listFilesInDirectory("/foo/bar", results);
+  EXPECT_FALSE(status.ok());
+}
+
+TEST_F(FilesystemTests, test_list_files_invalid_directory) {
+  std::vector<std::string> results;
+  auto status = listFilesInDirectory("/etc/hosts", results);
+  EXPECT_FALSE(status.ok());
+}
+
+TEST_F(FilesystemTests, test_list_files_valid_directorty) {
+  std::vector<std::string> results;
+  auto s = listFilesInDirectory("/etc", results);
+  // This directory may be different on OS X or Linux.
+  std::string hosts_path = "/etc/hosts";
+  replaceGlobWildcards(hosts_path);
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(s.toString(), "OK");
+  EXPECT_TRUE(contains(results, hosts_path));
+}
+
+TEST_F(FilesystemTests, test_canonicalization) {
+  std::string complex = kFakeDirectory + "/deep1/../deep1/..";
+  std::string simple = kFakeDirectory + "/";
+  // Use the inline wildcard and canonicalization replacement.
+  // The 'simple' path contains a trailing '/', the replacement method will
+  // distinguish between file and directory paths.
+  replaceGlobWildcards(complex);
+  EXPECT_EQ(simple, complex);
+  // Now apply the same inline replacement on the simple directory and expect
+  // no change to the comparison.
+  replaceGlobWildcards(simple);
+  EXPECT_EQ(simple, complex);
+
+  // Now add a wildcard within the complex pattern. The replacement method
+  // will not canonicalize past a '*' as the proceeding paths are limiters.
+  complex = kFakeDirectory + "/*/deep2/../deep2/";
+  replaceGlobWildcards(complex);
+  EXPECT_EQ(complex, kFakeDirectory + "/*/deep2/../deep2/");
+}
+
+TEST_F(FilesystemTests, test_simple_globs) {
+  std::vector<std::string> results;
+  // Test the shell '*', we will support SQL's '%' too.
+  auto status = resolveFilePattern(kFakeDirectory + "/*", results);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 6);
+
+  // Test the csh-style bracket syntax: {}.
+  results.clear();
+  resolveFilePattern(kFakeDirectory + "/{root,door}*", results);
+  EXPECT_EQ(results.size(), 3);
+
+  // Test a tilde, home directory expansion, make no asserts about contents.
+  results.clear();
+  resolveFilePattern("~", results);
+  if (results.size() == 0) {
+    LOG(WARNING) << "Tilde expansion failed.";
+  }
+}
+
+TEST_F(FilesystemTests, test_wildcard_single_all) {
+  // Use '%' as a wild card to glob files within the temporarily-created dir.
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(kFakeDirectory + "/%", results, GLOB_ALL);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 6);
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt"));
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_single_files) {
+  // Now list again with a restriction to only files.
+  std::vector<std::string> results;
+  resolveFilePattern(kFakeDirectory + "/%", results, GLOB_FILES);
+  EXPECT_EQ(results.size(), 4);
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_single_folders) {
+  std::vector<std::string> results;
+  resolveFilePattern(kFakeDirectory + "/%", results, GLOB_FOLDERS);
+  EXPECT_EQ(results.size(), 2);
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_dual) {
+  // Now test two directories deep with a single wildcard for each.
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(kFakeDirectory + "/%/%", results);
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/level1.txt"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_double) {
+  // TODO: this will fail.
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(kFakeDirectory + "/%%", results);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 15);
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/deep2/level2.txt"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_double_folders) {
+  std::vector<std::string> results;
+  resolveFilePattern(kFakeDirectory + "/%%", results, GLOB_FOLDERS);
+  EXPECT_EQ(results.size(), 5);
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/deep2/deep3/"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_end_last_component) {
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(kFakeDirectory + "/%11/%sh", results);
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/not_bash"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_middle_component) {
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(kFakeDirectory + "/deep1%/%", results);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 5);
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/level1.txt"));
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/level1.txt"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_all_types) {
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(kFakeDirectory + "/%p11/%/%%", results);
+  EXPECT_TRUE(status.ok());
+  EXPECT_TRUE(
+      contains(results, kFakeDirectory + "/deep11/deep2/deep3/level3.txt"));
+}
+
+TEST_F(FilesystemTests, test_wildcard_invalid_path) {
+  std::vector<std::string> results;
+  auto status = resolveFilePattern("/not_ther_abcdefz/%%", results);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 0);
+}
+
+TEST_F(FilesystemTests, test_wildcard_dotdot_files) {
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(
+      kFakeDirectory + "/deep11/deep2/../../%", results, GLOB_FILES);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 4);
+  // The response list will contain canonicalized versions: /tmp/<tests>/...
+  std::string door_path = kFakeDirectory + "/deep11/deep2/../../door.txt";
+  replaceGlobWildcards(door_path);
+  EXPECT_TRUE(contains(results, door_path));
+}
+
+TEST_F(FilesystemTests, test_dotdot_relative) {
+  std::vector<std::string> results;
+  auto status = resolveFilePattern(kTestDataPath + "%", results);
+  EXPECT_TRUE(status.ok());
+
+  bool found = false;
+  for (const auto& file : results) {
+    if (file.find("test.config")) {
+      found = true;
+      break;
+    }
+  }
+  EXPECT_TRUE(found);
+}
+
+TEST_F(FilesystemTests, test_no_wild) {
+  std::vector<std::string> results;
+  auto status =
+      resolveFilePattern(kFakeDirectory + "/roto.txt", results, GLOB_FILES);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 1);
+  EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt"));
+}
+
+TEST_F(FilesystemTests, test_safe_permissions) {
+  // For testing we can request a different directory path.
+  EXPECT_TRUE(safePermissions("/", kFakeDirectory + "/door.txt"));
+  // A file with a directory.mode & 0x1000 fails.
+  EXPECT_FALSE(safePermissions("/tmp", kFakeDirectory + "/door.txt"));
+  // A directory for a file will fail.
+  EXPECT_FALSE(safePermissions("/", kFakeDirectory + "/deep11"));
+  // A root-owned file is appropriate
+  EXPECT_TRUE(safePermissions("/", "/dev/zero"));
+}
+}
diff --git a/src/osquery/include/osquery/config.h b/src/osquery/include/osquery/config.h
new file mode 100644 (file)
index 0000000..38c9002
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <boost/noncopyable.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/thread/shared_mutex.hpp>
+
+#include <osquery/database.h>
+#include <osquery/flags.h>
+#include <osquery/registry.h>
+#include <osquery/status.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+/// The builder or invoker may change the default config plugin.
+DECLARE_string(config_plugin);
+
+/**
+ * @brief The osquery config is updated names sources containing JSON.
+ *
+ * A ConfigSourceMap is a named mapping from source (the key) to a JSON blob.
+ * This map is generated by a ConfigPlugin an provided to the Config via an
+ * update call. ConfigPlugin%s may update the Config asynchronously.
+ *
+ * The osquery Config instance will perform source merging by amalgamating
+ * the JSON literal types (lists and maps) for well known top-level keys.
+ * The merging will happen in lexicographical order based on source name.
+ */
+typedef std::map<std::string, std::string> ConfigSourceMap;
+
+/**
+ * @brief A native representation of osquery configuration data.
+ *
+ * When you use osquery::Config::getInstance(), you are getting a singleton
+ * handle to interact with the data stored in an instance of this struct.
+ */
+struct ConfigData {
+  /// A vector of all of the queries that are scheduled to execute.
+  std::map<std::string, ScheduledQuery> schedule;
+  std::map<std::string, std::string> options;
+  std::map<std::string, std::vector<std::string> > files;
+  /// All data catches optional/plugin-parsed configuration keys.
+  pt::ptree all_data;
+};
+
+class ConfigParserPlugin;
+typedef std::shared_ptr<ConfigParserPlugin> ConfigPluginRef;
+
+/**
+ * @brief A singleton that exposes accessors to osquery's configuration data.
+ *
+ * osquery has two types on configurations. Things that don't change during
+ * the execution of the process should be configured as command-line
+ * arguments. Things that can change during the lifetime of program execution
+ * should be defined using the osquery::config::Config class and the pluggable
+ * plugin interface that is included with it.
+ */
+class Config : private boost::noncopyable {
+ public:
+  /**
+   * @brief The primary way to access the Config singleton.
+   *
+   * osquery::config::Config::getInstance() provides access to the Config
+   * singleton
+   *
+   * @code{.cpp}
+   *   auto config = osquery::config::Config::getInstance();
+   * @endcode
+   *
+   * @return a singleton instance of Config.
+   */
+  static Config& getInstance() {
+    static Config cfg;
+    return cfg;
+  }
+
+  /**
+   * @brief Call the genConfig method of the config retriever plugin.
+   *
+   * This may perform a resource load such as TCP request or filesystem read.
+   */
+  static Status load();
+
+  /**
+   * @brief Update the internal config data.
+   *
+   * @param config A map of domain or namespace to config data.
+   * @return If the config changes were applied.
+   */
+  static Status update(const ConfigSourceMap& config);
+
+  /**
+   * @brief Calculate the has of the osquery config
+   *
+   * @return The MD5 of the osquery config
+   */
+  static Status getMD5(std::string& hashString);
+
+  /**
+   * @brief Adds a new query to the scheduled queries.
+   *
+   */
+  static void addScheduledQuery(const std::string& name,
+                                const std::string& query,
+                                int interval);
+
+  /**
+   * @brief Checks if a query exists in the query schedule.
+   *
+   */
+  static bool checkScheduledQuery(const std::string& query);
+
+  /**
+   * @brief Checks if the query name exists in the query schedule.
+   *
+   */
+  static bool checkScheduledQueryName(const std::string& query_name);
+
+  /**
+   * @brief Check to ensure that the config is accessible and properly
+   * formatted
+   *
+   * @return an instance of osquery::Status, indicating the success or failure
+   * of the operation.
+   */
+  static Status checkConfig();
+
+ private:
+  /**
+   * @brief Default constructor.
+   *
+   * Since instances of Config should only be created via getInstance(),
+   * Config's constructor is private
+   */
+  Config() : force_merge_success_(false) {}
+  ~Config(){}
+  Config(Config const&);
+  void operator=(Config const&);
+
+  /**
+   * @brief Uses the specified config retriever to populate a string with the
+   * config JSON.
+   *
+   * Internally, genConfig checks to see if there was a config retriever
+   * specified on the command-line. If there was, it checks to see if that
+   * config retriever actually exists. If it does, it gets used to generate
+   * configuration data. If it does not, an error is logged.
+   *
+   * @return status indicating the success or failure of the operation.
+   */
+  static Status genConfig();
+
+  /// Merge a retrieved config source JSON into a working ConfigData.
+  static Status mergeConfig(const std::string& source, ConfigData& conf);
+
+ public:
+  /**
+   * @brief Record performance (monitoring) information about a scheduled query.
+   *
+   * The daemon and query scheduler will optionally record process metadata
+   * before and after executing each query. This can be compared and reported
+   * on an interval or within the osquery_schedule table.
+   *
+   * The config consumes and calculates the optional performance differentials.
+   * It would also be possible to store this in the RocksDB backing store or
+   * report directly to a LoggerPlugin sink. The Config is the most appropriate
+   * as the metrics are transient to the process running the schedule and apply
+   * to the updates/changes reflected in the schedule, from the config.
+   *
+   * @param name The unique name of the scheduled item
+   * @param delay Number of seconds (wall time) taken by the query
+   * @param size Number of characters generated by query
+   * @param t0 the process row before the query
+   * @param t1 the process row after the query
+   */
+  static void recordQueryPerformance(const std::string& name,
+                                     size_t delay,
+                                     size_t size,
+                                     const Row& t0,
+                                     const Row& t1);
+
+ private:
+  /// The raw osquery config data in a native format
+  ConfigData data_;
+
+  /// The raw JSON source map from the config plugin.
+  std::map<std::string, std::string> raw_;
+
+  /// The reader/writer config data mutex.
+  boost::shared_mutex mutex_;
+
+  /// Enforce merge success.
+  bool force_merge_success_;
+
+ private:
+  /**
+   * @brief A ConfigDataInstance requests read-only access to ConfigParser data.
+   *
+   * A ConfigParser plugin will receive several top-level-config keys and
+   * optionally parse and store information. That information is a property tree
+   * called ConfigParser::data_. Use ConfigDataInstance::getParsedData to
+   * retrieve read-only access to this data.
+   *
+   * @param parser The name of the config parser.
+   */
+  static const pt::ptree& getParsedData(const std::string& parser);
+
+  /// See getParsedData but request access to the parser plugin.
+  static const ConfigPluginRef getParser(const std::string& parser);
+
+  /// A default, empty property tree used when a missing parser is requested.
+  pt::ptree empty_data_;
+
+ private:
+  /// Config accessors, `ConfigDataInstance`, are the forced use of the config
+  /// data. This forces the caller to use a shared read lock.
+  friend class ConfigDataInstance;
+
+ private:
+  FRIEND_TEST(ConfigTests, test_locking);
+};
+
+/**
+ * @brief All accesses to the Config's data must request a ConfigDataInstance.
+ *
+ * This class will request a read-only lock of the config's changeable internal
+ * data structures such as query schedule, options, monitored files, etc.
+ *
+ * Since a variable config plugin may implement `update` calls, internal uses
+ * of config data needs simple read and write locking.
+ */
+class ConfigDataInstance {
+ public:
+  ConfigDataInstance() : lock_(Config::getInstance().mutex_) {}
+  ~ConfigDataInstance() { lock_.unlock(); }
+
+  /// Helper accessor for Config::data_.schedule.
+  const std::map<std::string, ScheduledQuery> schedule() const {
+    return Config::getInstance().data_.schedule;
+  }
+
+  /// Helper accessor for Config::data_.options.
+  const std::map<std::string, std::string>& options() const {
+    return Config::getInstance().data_.options;
+  }
+
+  /// Helper accessor for Config::data_.files.
+  const std::map<std::string, std::vector<std::string> >& files() const {
+    return Config::getInstance().data_.files;
+  }
+
+  const pt::ptree& getParsedData(const std::string& parser) const {
+    return Config::getParsedData(parser);
+  }
+
+  const ConfigPluginRef getParser(const std::string& parser) const {
+    return Config::getParser(parser);
+  }
+
+  /// Helper accessor for Config::data_.all_data.
+  const pt::ptree& data() const { return Config::getInstance().data_.all_data; }
+
+ private:
+  /**
+   * @brief ConfigParser plugin's may update the internal config representation.
+   *
+   * If the config parser reads and calculates new information it should store
+   * that derived data itself and rely on ConfigDataInstance::getParsedData.
+   * This means another plugin is aware of the ConfigParser and knowns to make
+   * getParsedData calls. If the parser is augmenting/changing internal state,
+   * such as modifying the osquery schedule or options, then it must write
+   * changed back into the default data.
+   *
+   * Note that this returns the ConfigData instance, not the raw property tree.
+   */
+  ConfigData& mutableConfigData() { return Config::getInstance().data_; }
+
+ private:
+  /// A read lock on the reader/writer config data accessor/update mutex.
+  boost::shared_lock<boost::shared_mutex> lock_;
+
+ private:
+  friend class ConfigParserPlugin;
+};
+
+/**
+ * @brief Superclass for the pluggable config component.
+ *
+ * In order to make the distribution of configurations to hosts running
+ * osquery, we take advantage of a plugin interface which allows you to
+ * integrate osquery with your internal configuration distribution mechanisms.
+ * You may use ZooKeeper, files on disk, a custom solution, etc. In order to
+ * use your specific configuration distribution system, one simply needs to
+ * create a custom subclass of ConfigPlugin. That subclass should implement
+ * the ConfigPlugin::genConfig method.
+ *
+ * Consider the following example:
+ *
+ * @code{.cpp}
+ *   class TestConfigPlugin : public ConfigPlugin {
+ *    public:
+ *     virtual std::pair<osquery::Status, std::string> genConfig() {
+ *       std::string config;
+ *       auto status = getMyConfig(config);
+ *       return std::make_pair(status, config);
+ *     }
+ *   };
+ *
+ *   REGISTER(TestConfigPlugin, "config", "test");
+ *  @endcode
+ */
+class ConfigPlugin : public Plugin {
+ public:
+  /**
+   * @brief Virtual method which should implemented custom config retrieval
+   *
+   * ConfigPlugin::genConfig should be implemented by a subclasses of
+   * ConfigPlugin which needs to retrieve config data in a custom way.
+   *
+   * @param config The output ConfigSourceMap, a map of JSON to source names.
+   * @return A failure status will prevent the source map from merging.
+   */
+  virtual Status genConfig(ConfigSourceMap& config) = 0;
+  Status call(const PluginRequest& request, PluginResponse& response);
+};
+
+/// Helper merged and parsed property tree.
+typedef pt::ptree ConfigTree;
+
+/// Helper for a map of requested keys to their merged and parsed property tree.
+typedef std::map<std::string, ConfigTree> ConfigTreeMap;
+
+/**
+ * @brief A pluggable configuration parser.
+ *
+ * An osquery config instance is populated from JSON using a ConfigPlugin.
+ * That plugin may update the config data asynchronously and read from
+ * several sources, as is the case with "filesystem" and reading multiple files.
+ *
+ * A ConfigParserPlugin will receive the merged configuration at osquery start
+ * and the updated (still merged) config if any ConfigPlugin updates the
+ * instance asynchronously. Each parser specifies a set of top-level JSON
+ * keys to receive. The config instance will auto-merge the key values
+ * from multiple sources if they are dictionaries or lists.
+ *
+ * If a top-level key is a dictionary, each source with the top-level key
+ * will have its own dictionary keys merged and replaced based on the lexical
+ * order of sources. For the "filesystem" config plugin this is the lexical
+ * sorting of filenames. If the top-level key is a list, each source with the
+ * top-level key will have its contents appended.
+ *
+ * Each config parser plugin will live alongside the config instance for the
+ * life of the osquery process. The parser may perform actions at config load
+ * and config update "time" as well as keep its own data members and be
+ * accessible through the Config class API.
+ */
+class ConfigParserPlugin : public Plugin {
+ protected:
+  /**
+   * @brief Return a list of top-level config keys to receive in updates.
+   *
+   * The ::update method will receive a map of these keys with a JSON-parsed
+   * property tree of configuration data.
+   *
+   * @return A list of string top-level JSON keys.
+   */
+  virtual std::vector<std::string> keys() = 0;
+
+  /**
+   * @brief Receive a merged property tree for each top-level config key.
+   *
+   * Called when the Config instance is initially loaded with data from the
+   * active config plugin and when it is updated via an async ConfigPlugin
+   * update. Every config parser will receive a map of merged data for each key
+   * they requested in keys().
+   *
+   * @param config A JSON-parsed property tree map.
+   * @return Failure if the parser should no longer receive updates.
+   */
+  virtual Status update(const ConfigTreeMap& config) = 0;
+
+ protected:
+  /// Mutable config data accessor for ConfigParser%s.
+  ConfigData& mutableConfigData(ConfigDataInstance& cdi) {
+    return cdi.mutableConfigData();
+  }
+
+ protected:
+  /// Allow the config parser to keep some global state.
+  pt::ptree data_;
+
+ private:
+  Status setUp();
+
+ private:
+  /// Config::update will call all appropriate parser updates.
+  friend class Config;
+  /// A config data instance implements a read/write lock around data_ access.
+  friend class ConfigDataInstance;
+};
+
+/**
+ * @brief Calculate a splayed integer based on a variable splay percentage
+ *
+ * The value of splayPercent must be between 1 and 100. If it's not, the
+ * value of original will be returned.
+ *
+ * @param original The original value to be modified
+ * @param splayPercent The percent in which to splay the original value by
+ *
+ * @return The modified version of original
+ */
+int splayValue(int original, int splayPercent);
+
+/**
+ * @brief Config plugin registry.
+ *
+ * This creates an osquery registry for "config" which may implement
+ * ConfigPlugin. A ConfigPlugin's call API should make use of a genConfig
+ * after reading JSON data in the plugin implementation.
+ */
+CREATE_REGISTRY(ConfigPlugin, "config");
+
+/**
+ * @brief ConfigParser plugin registry.
+ *
+ * This creates an osquery registry for "config_parser" which may implement
+ * ConfigParserPlugin. A ConfigParserPlugin should not export any call actions
+ * but rather have a simple property tree-accessor API through Config.
+ */
+CREATE_LAZY_REGISTRY(ConfigParserPlugin, "config_parser");
+}
diff --git a/src/osquery/include/osquery/core.h b/src/osquery/include/osquery/core.h
new file mode 100644 (file)
index 0000000..30260da
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <osquery/status.h>
+
+// clang-format off
+#ifndef STR
+#define STR_OF(x) #x
+#define STR(x) STR_OF(x)
+#endif
+#define STR_EX(x) x
+#define CONCAT(x, y) STR(STR_EX(x)STR_EX(y))
+
+#ifndef FRIEND_TEST
+#define FRIEND_TEST(test_case_name, test_name) \
+  friend class test_case_name##_##test_name##_Test
+#endif
+// clang-format on
+
+#ifndef __constructor__
+#define __constructor__ __attribute__((constructor))
+#endif
+
+/// A configuration error is catastrophic and should exit the watcher.
+#define EXIT_CATASTROPHIC 78
+
+namespace osquery {
+
+/**
+ * @brief The version of osquery
+ */
+extern const std::string kVersion;
+extern const std::string kSDKVersion;
+extern const std::string kSDKPlatform;
+
+/// Use a macro for the sdk/platform literal, symbols available in lib.cpp.
+#define OSQUERY_SDK_VERSION STR(OSQUERY_BUILD_SDK_VERSION)
+#define OSQUERY_PLATFORM STR(OSQUERY_BUILD_PLATFORM)
+
+/**
+ * @brief A helpful tool type to report when logging, print help, or debugging.
+ */
+enum ToolType {
+  OSQUERY_TOOL_UNKNOWN = 0,
+  OSQUERY_TOOL_SHELL,
+  OSQUERY_TOOL_DAEMON,
+  OSQUERY_TOOL_TEST,
+  OSQUERY_EXTENSION,
+};
+
+/// The osquery tool type for runtime decisions.
+extern ToolType kToolType;
+
+class Initializer {
+ public:
+  /**
+   * @brief Sets up various aspects of osquery execution state.
+   *
+   * osquery needs a few things to happen as soon as the process begins
+   * executing. Initializer takes care of setting up the relevant parameters.
+   * Initializer should be called in an executable's `main()` function.
+   *
+   * @param argc the number of elements in argv
+   * @param argv the command-line arguments passed to `main()`
+   * @param tool the type of osquery main (daemon, shell, test, extension).
+   */
+  Initializer(int& argc, char**& argv, ToolType tool = OSQUERY_TOOL_TEST);
+
+  /**
+   * @brief Sets up the process as an osquery daemon.
+   *
+   * A daemon has additional constraints, it can use a process mutex, check
+   * for sane/non-default configurations, etc.
+   */
+  void initDaemon();
+
+  /**
+   * @brief Daemon tools may want to continually spawn worker processes
+   * and monitor their utilization.
+   *
+   * A daemon may call initWorkerWatcher to begin watching child daemon
+   * processes until it-itself is unscheduled. The basic guarantee is that only
+   * workers will return from the function.
+   *
+   * The worker-watcher will implement performance bounds on CPU utilization
+   * and memory, as well as check for zombie/defunct workers and respawn them
+   * if appropriate. The appropriateness is determined from heuristics around
+   * how the worker exited. Various exit states and velocities may cause the
+   * watcher to resign.
+   *
+   * @param name The name of the worker process.
+   */
+  void initWorkerWatcher(const std::string& name);
+
+  /// Assume initialization finished, start work.
+  void start();
+  /// Turns off various aspects of osquery such as event loops.
+  void shutdown();
+
+  /**
+   * @brief Check if a process is an osquery worker.
+   *
+   * By default an osqueryd process will fork/exec then set an environment
+   * variable: `OSQUERY_WORKER` while continually monitoring child I/O.
+   * The environment variable causes subsequent child processes to skip several
+   * initialization steps and jump into extension handling, registry setup,
+   * config/logger discovery and then the event publisher and scheduler.
+   */
+  static bool isWorker();
+
+ private:
+  /// Initialize this process as an osquery daemon worker.
+  void initWorker(const std::string& name);
+  /// Initialize the osquery watcher, optionally spawn a worker.
+  void initWatcher();
+  /// Set and wait for an active plugin optionally broadcasted.
+  void initActivePlugin(const std::string& type, const std::string& name);
+
+ private:
+  int* argc_;
+  char*** argv_;
+  int tool_;
+  std::string binary_;
+};
+
+/**
+ * @brief Split a given string based on an optional delimiter.
+ *
+ * If no delimiter is supplied, the string will be split based on whitespace.
+ *
+ * @param s the string that you'd like to split
+ * @param delim the delimiter which you'd like to split the string by
+ *
+ * @return a vector of strings split by delim.
+ */
+std::vector<std::string> split(const std::string& s,
+                               const std::string& delim = "\t ");
+
+/**
+ * @brief Split a given string based on an delimiter.
+ *
+ * @param s the string that you'd like to split.
+ * @param delim the delimiter which you'd like to split the string by.
+ * @param occurrences the number of times to split by delim.
+ *
+ * @return a vector of strings split by delim for occurrences.
+ */
+std::vector<std::string> split(const std::string& s,
+                               const std::string& delim,
+                               size_t occurences);
+
+/**
+ * @brief In-line replace all instances of from with to.
+ *
+ * @param str The input/output mutable string.
+ * @param from Search string
+ * @param to Replace string
+ */
+inline void replaceAll(std::string& str,
+                       const std::string& from,
+                       const std::string& to) {
+  if (from.empty()) {
+    return;
+  }
+
+  size_t start_pos = 0;
+  while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
+    str.replace(start_pos, from.length(), to);
+    start_pos += to.length();
+  }
+}
+
+/**
+ * @brief Join a vector of strings using a tokenizer.
+ *
+ * @param s the string that you'd like to split.
+ * @param tok a token glue.
+ *
+ * @return a joined string.
+ */
+std::string join(const std::vector<std::string>& s, const std::string& tok);
+
+/**
+ * @brief Getter for a host's current hostname
+ *
+ * @return a string representing the host's current hostname
+ */
+std::string getHostname();
+
+/**
+ * @brief generate a uuid to uniquely identify this machine
+ *
+ * @return uuid string to identify this machine
+ */
+std::string generateHostUuid();
+
+/**
+ * @brief Getter for the current time, in a human-readable format.
+ *
+ * @return the current date/time in the format: "Wed Sep 21 10:27:52 2011"
+ */
+std::string getAsciiTime();
+
+/**
+ * @brief Getter for the current UNIX time.
+ *
+ * @return an int representing the amount of seconds since the UNIX epoch
+ */
+int getUnixTime();
+
+/**
+ * @brief In-line helper function for use with utf8StringSize
+ */
+template <typename _Iterator1, typename _Iterator2>
+inline size_t incUtf8StringIterator(_Iterator1& it, const _Iterator2& last) {
+  if (it == last) {
+    return 0;
+  }
+
+  unsigned char c;
+  size_t res = 1;
+  for (++it; last != it; ++it, ++res) {
+    c = *it;
+    if (!(c & 0x80) || ((c & 0xC0) == 0xC0)) {
+      break;
+    }
+  }
+
+  return res;
+}
+
+/**
+ * @brief Get the length of a UTF-8 string
+ *
+ * @param str The UTF-8 string
+ *
+ * @return the length of the string
+ */
+inline size_t utf8StringSize(const std::string& str) {
+  size_t res = 0;
+  std::string::const_iterator it = str.begin();
+  for (; it != str.end(); incUtf8StringIterator(it, str.end())) {
+    res++;
+  }
+
+  return res;
+}
+
+/**
+ * @brief Create a pid file
+ *
+ * @return A status object indicating the success or failure of the operation
+ */
+Status createPidFile();
+}
diff --git a/src/osquery/include/osquery/database.h b/src/osquery/include/osquery/database.h
new file mode 100644 (file)
index 0000000..eaf35b5
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/registry.h>
+#include <osquery/status.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+/**
+ * @brief A backing storage domain name, used for key/value based storage.
+ *
+ * There are certain "cached" variables such as a node-unique UUID or negotiated
+ * 'node_key' following enrollment. If a value or setting must persist between
+ * osqueryi or osqueryd runs it should be stored using the kPersistentSetting%s
+ * domain.
+ */
+extern const std::string kPersistentSettings;
+
+/// The "domain" where the results of scheduled queries are stored.
+extern const std::string kQueries;
+
+/// The "domain" where event results are stored, queued for querytime retrieval.
+extern const std::string kEvents;
+
+/**
+ * @brief The "domain" where buffered log results are stored.
+ *
+ * Logger plugins may shuttle logs to a remote endpoint or API call
+ * asynchronously. The backing store can be used to buffer results and status
+ * logs until the logger plugin-specific thread decided to flush.
+ */
+extern const std::string kLogs;
+
+/////////////////////////////////////////////////////////////////////////////
+// Row
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief A variant type for the SQLite type affinities.
+ */
+typedef std::string RowData;
+
+/**
+ * @brief A single row from a database query
+ *
+ * Row is a simple map where individual column names are keys, which map to
+ * the Row's respective value
+ */
+typedef std::map<std::string, RowData> Row;
+
+/**
+ * @brief Serialize a Row into a property tree
+ *
+ * @param r the Row to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeRow(const Row& r, pt::ptree& tree);
+
+/**
+ * @brief Serialize a Row object into a JSON string
+ *
+ * @param r the Row to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeRowJSON(const Row& r, std::string& json);
+
+/**
+ * @brief Deserialize a Row object from a property tree
+ *
+ * @param tree the input property tree
+ * @param r the output Row structure
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status deserializeRow(const pt::ptree& tree, Row& r);
+
+/**
+ * @brief Deserialize a Row object from a JSON string
+ *
+ * @param json the input JSON string
+ * @param r the output Row structure
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status deserializeRowJSON(const std::string& json, Row& r);
+
+/////////////////////////////////////////////////////////////////////////////
+// QueryData
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief The result set returned from a osquery SQL query
+ *
+ * QueryData is the canonical way to represent the results of SQL queries in
+ * osquery. It's just a vector of Row's.
+ */
+typedef std::vector<Row> QueryData;
+
+/**
+ * @brief Serialize a QueryData object into a property tree
+ *
+ * @param q the QueryData to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryData(const QueryData& q, pt::ptree& tree);
+
+/**
+ * @brief Serialize a QueryData object into a JSON string
+ *
+ * @param q the QueryData to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryDataJSON(const QueryData& q, std::string& json);
+
+/// Inverse of serializeQueryData, convert property tree to QueryData.
+Status deserializeQueryData(const pt::ptree& tree, QueryData& qd);
+
+/// Inverse of serializeQueryDataJSON, convert a JSON string to QueryData.
+Status deserializeQueryDataJSON(const std::string& json, QueryData& qd);
+
+/////////////////////////////////////////////////////////////////////////////
+// DiffResults
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Data structure representing the difference between the results of
+ * two queries
+ *
+ * The representation of two diffed QueryData result sets. Given and old and
+ * new QueryData, DiffResults indicates the "added" subset of rows and the
+ * "removed" subset of rows.
+ */
+struct DiffResults {
+  /// vector of added rows
+  QueryData added;
+
+  /// vector of removed rows
+  QueryData removed;
+
+  /// equals operator
+  bool operator==(const DiffResults& comp) const {
+    return (comp.added == added) && (comp.removed == removed);
+  }
+
+  /// not equals operator
+  bool operator!=(const DiffResults& comp) const { return !(*this == comp); }
+};
+
+/**
+ * @brief Serialize a DiffResults object into a property tree
+ *
+ * @param d the DiffResults to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeDiffResults(const DiffResults& d, pt::ptree& tree);
+
+/**
+ * @brief Serialize a DiffResults object into a JSON string
+ *
+ * @param d the DiffResults to serialize
+ * @param json the output JSON string
+ *
+ * @return an instance of osquery::Status, indicating the success or failure
+ * of the operation
+ */
+Status serializeDiffResultsJSON(const DiffResults& d, std::string& json);
+
+/**
+ * @brief Diff two QueryData objects and create a DiffResults object
+ *
+ * @param old_ the "old" set of results
+ * @param new_ the "new" set of results
+ *
+ * @return a DiffResults object which indicates the change from old_ to new_
+ *
+ * @see DiffResults
+ */
+DiffResults diff(const QueryData& old_, const QueryData& new_);
+
+/**
+ * @brief Add a Row to a QueryData if the Row hasn't appeared in the QueryData
+ * already
+ *
+ * Note that this function will iterate through the QueryData list until a
+ * given Row is found (or not found). This shouldn't be that significant of an
+ * overhead for most use-cases, but it's worth keeping in mind before you use
+ * this in it's current state.
+ *
+ * @param q the QueryData list to append to
+ * @param r the Row to add to q
+ *
+ * @return true if the Row was added to the QueryData, false if it was not
+ */
+bool addUniqueRowToQueryData(QueryData& q, const Row& r);
+
+/**
+ * @brief Construct a new QueryData from an existing one, replacing all
+ * non-ASCII characters with their \u encoding.
+ *
+ * This function is intended as a workaround for
+ * https://svn.boost.org/trac/boost/ticket/8883,
+ * and will allow rows containing data with non-ASCII characters to be stored in
+ * the database and parsed back into a property tree.
+ *
+ * @param oldData the old QueryData to copy
+ * @param newData the new escaped QueryData object
+ */
+void escapeQueryData(const QueryData& oldData, QueryData& newData);
+
+/**
+ * @brief represents the relevant parameters of a scheduled query.
+ *
+ * Within the context of osqueryd, a scheduled query may have many relevant
+ * attributes. Those attributes are represented in this data structure.
+ */
+struct ScheduledQuery {
+  /// The SQL query.
+  std::string query;
+
+  /// How often the query should be executed, in second.
+  size_t interval;
+
+  /// A temporary splayed internal.
+  size_t splayed_interval;
+
+  /// Number of executions.
+  size_t executions;
+
+  /// Total wall time taken
+  unsigned long long int wall_time;
+
+  /// Total user time (cycles)
+  unsigned long long int user_time;
+
+  /// Total system time (cycles)
+  unsigned long long int system_time;
+
+  /// Average memory differentials. This should be near 0.
+  unsigned long long int average_memory;
+
+  /// Total characters, bytes, generated by query.
+  unsigned long long int output_size;
+
+  /// Set of query options.
+  std::map<std::string, bool> options;
+
+  ScheduledQuery()
+      : interval(0),
+        splayed_interval(0),
+        executions(0),
+        wall_time(0),
+        user_time(0),
+        system_time(0),
+        average_memory(0),
+        output_size(0) {}
+
+  /// equals operator
+  bool operator==(const ScheduledQuery& comp) const {
+    return (comp.query == query) && (comp.interval == interval);
+  }
+
+  /// not equals operator
+  bool operator!=(const ScheduledQuery& comp) const { return !(*this == comp); }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// QueryLogItem
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Query results from a schedule, snapshot, or ad-hoc execution.
+ *
+ * When a scheduled query yields new results, we need to log that information
+ * to our upstream logging receiver. A QueryLogItem contains metadata and
+ * results in potentially-differential form for a logger.
+ */
+struct QueryLogItem {
+  /// Differential results from the query.
+  DiffResults results;
+
+  /// Optional snapshot results, no differential applied.
+  QueryData snapshot_results;
+
+  /// The name of the scheduled query.
+  std::string name;
+
+  /// The identifier (hostname, or uuid) of the host.
+  std::string identifier;
+
+  /// The time that the query was executed, seconds as UNIX time.
+  int time;
+
+  /// The time that the query was executed, an ASCII string.
+  std::string calendar_time;
+
+  /// equals operator
+  bool operator==(const QueryLogItem& comp) const {
+    return (comp.results == results) && (comp.name == name);
+  }
+
+  /// not equals operator
+  bool operator!=(const QueryLogItem& comp) const { return !(*this == comp); }
+};
+
+/**
+ * @brief Serialize a QueryLogItem object into a property tree
+ *
+ * @param item the QueryLogItem to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItem(const QueryLogItem& item, pt::ptree& tree);
+
+/**
+ * @brief Serialize a QueryLogItem object into a JSON string
+ *
+ * @param item the QueryLogItem to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItemJSON(const QueryLogItem& item, std::string& json);
+
+/// Inverse of serializeQueryLogItem, convert property tree to QueryLogItem.
+Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item);
+
+/// Inverse of serializeQueryLogItem, convert a JSON string to QueryLogItem.
+Status deserializeQueryLogItemJSON(const std::string& json, QueryLogItem& item);
+
+/**
+ * @brief Serialize a QueryLogItem object into a property tree
+ * of events, a list of actions.
+ *
+ * @param item the QueryLogItem to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItemAsEvents(const QueryLogItem& item, pt::ptree& tree);
+
+/**
+ * @brief Serialize a QueryLogItem object into a JSON string of events,
+ * a list of actions.
+ *
+ * @param i the QueryLogItem to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i,
+                                         std::string& json);
+
+/**
+ * @brief An osquery backing storage (database) type that persists executions.
+ *
+ * The osquery tools need a high-performance storage and indexing mechanism for
+ * storing intermediate results from EventPublisher%s, persisting one-time
+ * generated values, and performing non-memory backed differentials.
+ *
+ * Practically, osquery is built around RocksDB's performance guarantees and
+ * all of the internal APIs expect RocksDB's indexing and read performance.
+ * However, access to this representation of a backing-store is still abstracted
+ * to removing RocksDB as a dependency for the osquery SDK.
+ */
+class DatabasePlugin : public Plugin {
+ protected:
+  /**
+   * @brief Perform a domain and key lookup from the backing store.
+   *
+   * Database value access indexing is abstracted into domains and keys.
+   * Both are string values but exist separately for simple indexing without
+   * API-enforcing tokenization. In some cases we do add a component-specific
+   * tokeninzation to keys.
+   *
+   * @param domain A string value representing abstract storage indexing.
+   * @param key A string value representing the lookup/retrieval key.
+   * @param value The output parameter, left empty if the key does not exist.
+   * @return Failure if the data could not be accessed. It is up to the plugin
+   * to determine if a missing key means a non-success status.
+   */
+  virtual Status get(const std::string& domain,
+                     const std::string& key,
+                     std::string& value) const = 0;
+
+  /**
+   * @brief Store a string-represented value using a domain and key index.
+   *
+   * See DatabasePlugin::get for discussion around domain and key use.
+   *
+   * @param domain A string value representing abstract storage indexing.
+   * @param key A string value representing the lookup/retrieval key.
+   * @param value A string value representing the data.
+   * @return Failure if the data could not be stored. It is up to the plugin
+   * to determine if a conflict/overwrite should return different status text.
+   */
+  virtual Status put(const std::string& domain,
+                     const std::string& key,
+                     const std::string& value) = 0;
+
+  /// Data removal method.
+  virtual Status remove(const std::string& domain, const std::string& k) = 0;
+
+  /// Key/index lookup method.
+  virtual Status scan(const std::string& domain,
+                      std::vector<std::string>& results) const {
+    return Status(0, "Not used");
+  }
+
+ public:
+  Status call(const PluginRequest& request, PluginResponse& response);
+};
+
+/**
+ * @brief Lookup a value from the active osquery DatabasePlugin storage.
+ *
+ * See DatabasePlugin::get for discussion around domain and key use.
+ * Extensions, components, plugins, and core code should use getDatabaseValue
+ * as a wrapper around the current tool's choice of a backing storage plugin.
+ *
+ * @param domain A string value representing abstract storage indexing.
+ * @param key A string value representing the lookup/retrieval key.
+ * @param value The output parameter, left empty if the key does not exist.
+ * @return Storage operation status.
+ */
+Status getDatabaseValue(const std::string& domain,
+                        const std::string& key,
+                        std::string& value);
+
+/**
+ * @brief Set or put a value into the active osquery DatabasePlugin storage.
+ *
+ * See DatabasePlugin::get for discussion around domain and key use.
+ * Extensions, components, plugins, and core code should use setDatabaseValue
+ * as a wrapper around the current tool's choice of a backing storage plugin.
+ *
+ * @param domain A string value representing abstract storage indexing.
+ * @param key A string value representing the lookup/retrieval key.
+ * @param value A string value representing the data.
+ * @return Storage operation status.
+ */
+Status setDatabaseValue(const std::string& domain,
+                        const std::string& key,
+                        const std::string& value);
+
+/// Remove a domain/key identified value from backing-store.
+Status deleteDatabaseValue(const std::string& domain, const std::string& key);
+
+/// Get a list of keys for a given domain.
+Status scanDatabaseKeys(const std::string& domain,
+                        std::vector<std::string>& keys);
+
+/// Generate a specific-use registry for database access abstraction.
+CREATE_REGISTRY(DatabasePlugin, "database");
+}
diff --git a/src/osquery/include/osquery/database/results.h b/src/osquery/include/osquery/database/results.h
new file mode 100644 (file)
index 0000000..0d288cd
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/status.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+/////////////////////////////////////////////////////////////////////////////
+// Row
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief A variant type for the SQLite type affinities.
+ */
+typedef std::string RowData;
+
+/**
+ * @brief A single row from a database query
+ *
+ * Row is a simple map where individual column names are keys, which map to
+ * the Row's respective value
+ */
+typedef std::map<std::string, RowData> Row;
+
+/**
+ * @brief Serialize a Row into a property tree
+ *
+ * @param r the Row to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeRow(const Row& r, pt::ptree& tree);
+
+/**
+ * @brief Serialize a Row object into a JSON string
+ *
+ * @param r the Row to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeRowJSON(const Row& r, std::string& json);
+
+/**
+ * @brief Deserialize a Row object from a property tree
+ *
+ * @param tree the input property tree
+ * @param r the output Row structure
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status deserializeRow(const pt::ptree& tree, Row& r);
+
+/**
+ * @brief Deserialize a Row object from a JSON string
+ *
+ * @param json the input JSON string
+ * @param r the output Row structure
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status deserializeRowJSON(const std::string& json, Row& r);
+
+/////////////////////////////////////////////////////////////////////////////
+// QueryData
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief The result set returned from a osquery SQL query
+ *
+ * QueryData is the canonical way to represent the results of SQL queries in
+ * osquery. It's just a vector of Row's.
+ */
+typedef std::vector<Row> QueryData;
+
+/**
+ * @brief Serialize a QueryData object into a property tree
+ *
+ * @param q the QueryData to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryData(const QueryData& q, pt::ptree& tree);
+
+/**
+ * @brief Serialize a QueryData object into a JSON string
+ *
+ * @param q the QueryData to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryDataJSON(const QueryData& q, std::string& json);
+
+Status deserializeQueryData(const pt::ptree& tree, QueryData& qd);
+Status deserializeQueryDataJSON(const std::string& json, QueryData& qd);
+
+/////////////////////////////////////////////////////////////////////////////
+// DiffResults
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Data structure representing the difference between the results of
+ * two queries
+ *
+ * The representation of two diffed QueryData result sets. Given and old and
+ * new QueryData, DiffResults indicates the "added" subset of rows and the
+ * "removed" subset of rows.
+ */
+struct DiffResults {
+  /// vector of added rows
+  QueryData added;
+
+  /// vector of removed rows
+  QueryData removed;
+
+  /// equals operator
+  bool operator==(const DiffResults& comp) const {
+    return (comp.added == added) && (comp.removed == removed);
+  }
+
+  /// not equals operator
+  bool operator!=(const DiffResults& comp) const { return !(*this == comp); }
+};
+
+/**
+ * @brief Serialize a DiffResults object into a property tree
+ *
+ * @param d the DiffResults to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeDiffResults(const DiffResults& d, pt::ptree& tree);
+
+/**
+ * @brief Serialize a DiffResults object into a JSON string
+ *
+ * @param d the DiffResults to serialize
+ * @param json the output JSON string
+ *
+ * @return an instance of osquery::Status, indicating the success or failure
+ * of the operation
+ */
+Status serializeDiffResultsJSON(const DiffResults& d, std::string& json);
+
+/**
+ * @brief Diff two QueryData objects and create a DiffResults object
+ *
+ * @param old_ the "old" set of results
+ * @param new_ the "new" set of results
+ *
+ * @return a DiffResults object which indicates the change from old_ to new_
+ *
+ * @see DiffResults
+ */
+DiffResults diff(const QueryData& old_, const QueryData& new_);
+
+/**
+ * @brief Add a Row to a QueryData if the Row hasn't appeared in the QueryData
+ * already
+ *
+ * Note that this function will iterate through the QueryData list until a
+ * given Row is found (or not found). This shouldn't be that significant of an
+ * overhead for most use-cases, but it's worth keeping in mind before you use
+ * this in it's current state.
+ *
+ * @param q the QueryData list to append to
+ * @param r the Row to add to q
+ *
+ * @return true if the Row was added to the QueryData, false if it was not
+ */
+bool addUniqueRowToQueryData(QueryData& q, const Row& r);
+
+/**
+ * @brief Construct a new QueryData from an existing one, replacing all
+ * non-ASCII characters with their \u encoding.
+ *
+ * This function is intended as a workaround for
+ * https://svn.boost.org/trac/boost/ticket/8883,
+ * and will allow rows containing data with non-ASCII characters to be stored in
+ * the database and parsed back into a property tree.
+ *
+ * @param oldData the old QueryData to copy
+ * @param newData the new escaped QueryData object
+ */
+void escapeQueryData(const QueryData& oldData, QueryData& newData);
+
+/**
+ * @brief represents the relevant parameters of a scheduled query.
+ *
+ * Within the context of osqueryd, a scheduled query may have many relevant
+ * attributes. Those attributes are represented in this data structure.
+ */
+struct ScheduledQuery {
+  /// The SQL query.
+  std::string query;
+
+  /// How often the query should be executed, in second.
+  size_t interval;
+
+  /// A temporary splayed internal.
+  size_t splayed_interval;
+
+  /// Number of executions.
+  size_t executions;
+
+  /// Total wall time taken
+  size_t wall_time;
+
+  /// Total user time (cycles)
+  size_t user_time;
+
+  /// Total system time (cycles)
+  size_t system_time;
+
+  /// Average memory differentials. This should be near 0.
+  size_t memory;
+
+  /// Total characters, bytes, generated by query.
+  size_t output_size;
+
+  /// Set of query options.
+  std::map<std::string, bool> options;
+
+  ScheduledQuery()
+      : interval(0),
+        splayed_interval(0),
+        executions(0),
+        wall_time(0),
+        user_time(0),
+        system_time(0),
+        memory(0),
+        output_size(0) {}
+
+  /// equals operator
+  bool operator==(const ScheduledQuery& comp) const {
+    return (comp.query == query) && (comp.interval == interval);
+  }
+
+  /// not equals operator
+  bool operator!=(const ScheduledQuery& comp) const { return !(*this == comp); }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// QueryLogItem
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Query results from a schedule, snapshot, or ad-hoc execution.
+ *
+ * When a scheduled query yields new results, we need to log that information
+ * to our upstream logging receiver. A QueryLogItem contains metadata and
+ * results in potentially-differential form for a logger.
+ */
+struct QueryLogItem {
+  /// Differential results from the query.
+  DiffResults results;
+
+  /// Optional snapshot results, no differential applied.
+  QueryData snapshot_results;
+
+  /// The name of the scheduled query.
+  std::string name;
+
+  /// The identifier (hostname, or uuid) of the host.
+  std::string identifier;
+
+  /// The time that the query was executed, seconds as UNIX time.
+  int time;
+
+  /// The time that the query was executed, an ASCII string.
+  std::string calendar_time;
+
+  /// equals operator
+  bool operator==(const QueryLogItem& comp) const {
+    return (comp.results == results) && (comp.name == name);
+  }
+
+  /// not equals operator
+  bool operator!=(const QueryLogItem& comp) const { return !(*this == comp); }
+};
+
+/**
+ * @brief Serialize a QueryLogItem object into a property tree
+ *
+ * @param item the QueryLogItem to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItem(const QueryLogItem& item, pt::ptree& tree);
+
+/**
+ * @brief Serialize a QueryLogItem object into a JSON string
+ *
+ * @param item the QueryLogItem to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItemJSON(const QueryLogItem& item, std::string& json);
+
+Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item);
+Status deserializeQueryLogItemJSON(const std::string& json, QueryLogItem& item);
+
+/**
+ * @brief Serialize a QueryLogItem object into a property tree
+ * of events, a list of actions.
+ *
+ * @param item the QueryLogItem to serialize
+ * @param tree the output property tree
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItemAsEvents(const QueryLogItem& item, pt::ptree& tree);
+
+/**
+ * @brief Serialize a QueryLogItem object into a JSON string of events,
+ * a list of actions.
+ *
+ * @param i the QueryLogItem to serialize
+ * @param json the output JSON string
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i,
+                                         std::string& json);
+}
diff --git a/src/osquery/include/osquery/events.h b/src/osquery/include/osquery/events.h
new file mode 100644 (file)
index 0000000..1baabe6
--- /dev/null
@@ -0,0 +1,879 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <map>
+#include <vector>
+
+#include <boost/make_shared.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/locks.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include <osquery/registry.h>
+#include <osquery/status.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+
+struct Subscription;
+template <class SC, class EC> class EventPublisher;
+template <class PUB> class EventSubscriber;
+class EventFactory;
+
+typedef const std::string EventPublisherID;
+typedef const std::string EventSubscriberID;
+typedef const std::string EventID;
+typedef uint32_t EventContextID;
+typedef uint32_t EventTime;
+typedef std::pair<EventID, EventTime> EventRecord;
+
+/**
+ * @brief An EventPublisher will define a SubscriptionContext for
+ * EventSubscriber%s to use.
+ *
+ * Most EventPublisher%s will require specific information for interacting with
+ * an OS to receive events. The SubscriptionContext contains information the
+ * EventPublisher will use to register OS API callbacks, create
+ * subscriptioning/listening handles, etc.
+ *
+ * Linux `inotify` should implement a SubscriptionContext that subscribes
+ * filesystem events based on a filesystem path. `libpcap` will subscribe on
+ * networking protocols at various stacks. Process creation may subscribe on
+ * process name, parent pid, etc.
+ */
+struct SubscriptionContext {};
+
+/**
+ * @brief An EventSubscriber EventCallback method will receive an EventContext.
+ *
+ * The EventContext contains the event-related data supplied by an
+ * EventPublisher when the event occurs. If a subscribing EventSubscriber
+ * would be called for the event, the EventSubscriber%'s EventCallback is
+ * passed an EventContext.
+ */
+struct EventContext {
+  /// An unique counting ID specific to the EventPublisher%'s fired events.
+  EventContextID id;
+  /// The time the event occurred, as determined by the publisher.
+  EventTime time;
+
+  EventContext() : id(0), time(0) {}
+};
+
+typedef std::shared_ptr<Subscription> SubscriptionRef;
+typedef EventPublisher<SubscriptionContext, EventContext> BaseEventPublisher;
+typedef std::shared_ptr<BaseEventPublisher> EventPublisherRef;
+typedef std::shared_ptr<SubscriptionContext> SubscriptionContextRef;
+typedef std::shared_ptr<EventContext> EventContextRef;
+typedef EventSubscriber<BaseEventPublisher> BaseEventSubscriber;
+typedef std::shared_ptr<EventSubscriber<BaseEventPublisher>> EventSubscriberRef;
+
+/**
+ * @brief EventSubscriber%s may exist in various states.
+ *
+ * The subscriber will move through states when osquery is initializing the
+ * registry, starting event publisher loops, and requesting initialization of
+ * each subscriber and the optional set of subscriptions it creates. If this
+ * initialization fails the publishers or EventFactory may eject, warn, or
+ * otherwise not use the subscriber's subscriptions.
+ *
+ * The supported states are:
+ * - None: The default state, uninitialized.
+ * - Running: Subscriber is ready for events.
+ * - Paused: Subscriber was initialized but is not currently accepting events.
+ * - Failed: Subscriber failed to initialize or is otherwise offline.
+ */
+enum EventSubscriberState {
+  SUBSCRIBER_NONE,
+  SUBSCRIBER_RUNNING,
+  SUBSCRIBER_PAUSED,
+  SUBSCRIBER_FAILED,
+};
+
+/// Use a single placeholder for the EventContextRef passed to EventCallback.
+using std::placeholders::_1;
+using std::placeholders::_2;
+typedef std::function<Status(const EventContextRef&, const void*)>
+    EventCallback;
+
+/// An EventPublisher must track every subscription added.
+typedef std::vector<SubscriptionRef> SubscriptionVector;
+
+/// The set of search-time binned lookup tables.
+extern const std::vector<size_t> kEventTimeLists;
+
+/**
+ * @brief DECLARE_PUBLISHER supplies needed boilerplate code that applies a
+ * string-type EventPublisherID to identify the publisher declaration.
+ */
+#define DECLARE_PUBLISHER(TYPE) \
+ public:                        \
+  EventPublisherID type() const { return TYPE; }
+
+/**
+ * @brief A Subscription is used to configure an EventPublisher and bind a
+ * callback to a SubscriptionContext.
+ *
+ * A Subscription is the input to an EventPublisher when the EventPublisher
+ * decides on the scope and details of the events it watches/generates.
+ * An example includes a filesystem change event. A subscription would include
+ * a path with optional recursion and attribute selectors as well as a callback
+ * function to fire when an event for that path and selector occurs.
+ *
+ * A Subscription also functions to greatly scope an EventPublisher%'s work.
+ * Using the same filesystem example and the Linux inotify subsystem a
+ * Subscription limits the number of inode watches to only those requested by
+ * appropriate EventSubscriber%s.
+ * Note: EventSubscriber%s and Subscriptions can be configured by the osquery
+ * user.
+ *
+ * Subscriptions are usually created with EventFactory members:
+ *
+ * @code{.cpp}
+ *   EventFactory::addSubscription("MyEventPublisher", my_subscription_context);
+ * @endcode
+ */
+struct Subscription {
+ public:
+  // EventSubscriber name.
+  std::string subscriber_name;
+
+  /// An EventPublisher%-specific SubscriptionContext.
+  SubscriptionContextRef context;
+  /// An EventSubscription member EventCallback method.
+  EventCallback callback;
+  /// A pointer to possible extra data
+  void* user_data;
+
+  explicit Subscription(EventSubscriberID& name)
+      : subscriber_name(name), user_data(nullptr) {}
+
+  static SubscriptionRef create(EventSubscriberID& name) {
+    auto subscription = std::make_shared<Subscription>(name);
+    return subscription;
+  }
+
+  static SubscriptionRef create(EventSubscriberID& name,
+                                const SubscriptionContextRef& mc,
+                                EventCallback ec = 0,
+                                void* user_data = nullptr) {
+    auto subscription = std::make_shared<Subscription>(name);
+    subscription->context = mc;
+    subscription->callback = ec;
+    subscription->user_data = user_data;
+    return subscription;
+  }
+};
+
+class EventPublisherPlugin : public Plugin {
+ public:
+  /**
+   * @brief A new Subscription was added, potentially change state based on all
+   * subscriptions for this EventPublisher.
+   *
+   * `configure` allows the EventPublisher to optimize on the state of all
+   * subscriptions. An example is Linux `inotify` where multiple
+   * EventSubscription%s will subscription identical paths, e.g., /etc for
+   * config changes. Since Linux `inotify` has a subscription limit, `configure`
+   * can dedup paths.
+   */
+  virtual void configure() {}
+
+  /**
+   * @brief Perform handle opening, OS API callback registration.
+   *
+   * `setUp` is the event framework's EventPublisher constructor equivalent.
+   * This is called in the main thread before the publisher's run loop has
+   * started, immediately following registration.
+   */
+  virtual Status setUp() { return Status(0, "Not used"); }
+
+  /**
+   * @brief Perform handle closing, resource cleanup.
+   *
+   * osquery is about to end, the EventPublisher should close handle descriptors
+   * unblock resources, and prepare to exit. This will be called from the main
+   * thread after the run loop thread has exited.
+   */
+  virtual void tearDown() {}
+
+  /**
+   * @brief Implement a "step" of an optional run loop.
+   *
+   * @return A SUCCESS status will immediately call `run` again. A FAILED status
+   * will exit the run loop and the thread.
+   */
+  virtual Status run() { return Status(1, "No run loop required"); }
+
+  /**
+   * @brief Allow the EventFactory to interrupt the run loop.
+   *
+   * Assume the main thread may ask the run loop to stop at anytime.
+   * Before end is called the publisher's `isEnding` is set and the EventFactory
+   * run loop manager will exit the stepping loop and fall through to a call
+   * to tearDown followed by a removal of the publisher.
+   */
+  virtual void end() {}
+
+  /**
+   * @brief A new EventSubscriber is subscribing events of this publisher type.
+   *
+   * @param subscription The Subscription context information and optional
+   * EventCallback.
+   *
+   * @return If the Subscription is not appropriate (mismatched type) fail.
+   */
+  virtual Status addSubscription(const SubscriptionRef& subscription) {
+    subscriptions_.push_back(subscription);
+    return Status(0, "OK");
+  }
+
+ public:
+  /// Overriding the EventPublisher constructor is not recommended.
+  EventPublisherPlugin() : next_ec_id_(0), ending_(false), started_(false){};
+  virtual ~EventPublisherPlugin() {}
+
+  /// Return a string identifier associated with this EventPublisher.
+  virtual EventPublisherID type() const { return "publisher"; }
+
+ public:
+  /// Number of Subscription%s watching this EventPublisher.
+  size_t numSubscriptions() const { return subscriptions_.size(); }
+
+  /**
+   * @brief The number of events fired by this EventPublisher.
+   *
+   * @return The number of events.
+   */
+  size_t numEvents() const { return next_ec_id_; }
+
+  /// Check if the EventFactory is ending all publisher threads.
+  bool isEnding() const { return ending_; }
+
+  /// Set the ending status for this publisher.
+  void isEnding(bool ending) { ending_ = ending; }
+
+  /// Check if the publisher's run loop has started.
+  bool hasStarted() const { return started_; }
+
+  /// Set the run or started status for this publisher.
+  void hasStarted(bool started) { started_ = started; }
+
+ protected:
+  /**
+   * @brief The generic check loop to call SubscriptionContext callback methods.
+   *
+   * It is NOT recommended to override `fire`. The simple logic of enumerating
+   * the Subscription%s and using `shouldFire` is more appropriate.
+   *
+   * @param ec The EventContext created and fired by the EventPublisher.
+   * @param time The most accurate time associated with the event.
+   */
+  virtual void fire(const EventContextRef& ec, EventTime time = 0) final;
+
+  /// The internal fire method used by the typed EventPublisher.
+  virtual void fireCallback(const SubscriptionRef& sub,
+                            const EventContextRef& ec) const = 0;
+
+  /// The EventPublisher will keep track of Subscription%s that contain callins.
+  SubscriptionVector subscriptions_;
+
+  /// An Event ID is assigned by the EventPublisher within the EventContext.
+  /// This is not used to store event date in the backing store.
+  EventContextID next_ec_id_;
+
+ private:
+  EventPublisherPlugin(EventPublisherPlugin const&);
+  EventPublisherPlugin& operator=(EventPublisherPlugin const&);
+
+ private:
+  /// Set ending to True to cause event type run loops to finish.
+  bool ending_;
+
+  /// Set to indicate whether the event run loop ever started.
+  bool started_;
+
+  /// A lock for incrementing the next EventContextID.
+  boost::mutex ec_id_lock_;
+
+ private:
+  /// Enable event factory "callins" through static publisher callbacks.
+  friend class EventFactory;
+
+ private:
+  FRIEND_TEST(EventsTests, test_event_pub);
+  FRIEND_TEST(EventsTests, test_fire_event);
+};
+
+/**
+ * @brief Generate OS events of a type (FS, Network, Syscall, ioctl).
+ *
+ * A 'class' of OS events is abstracted into an EventPublisher responsible for
+ * remaining as agile as possible given a known-set of subscriptions.
+ *
+ * The life cycle of an EventPublisher may include, `setUp`, `configure`, `run`,
+ * `tearDown`, and `fire`. `setUp` and `tearDown` happen when osquery starts and
+ * stops either as a daemon or interactive shell. `configure` is a pseudo-start
+ * called every time a Subscription is added. EventPublisher%s can adjust their
+ * scope/agility specific to each added subscription by overriding
+ *`addSubscription`, and/or globally in `configure`.
+ *
+ * Not all EventPublisher%s leverage pure async OS APIs, and most will require a
+ * run loop either polling with a timeout on a descriptor or for a change. When
+ * osquery initializes the EventFactory will optionally create a thread for each
+ * EventPublisher using `run` as the thread's entrypoint. `run` is called in a
+ * within-thread loop where returning a FAILED status ends the run loop and
+ * shuts down the thread.
+ *
+ * To opt-out of polling in a thread, consider the following run implementation:
+ *
+ * @code{.cpp}
+ *   Status run() { return Status(1, "Not Implemented"); }
+ * @endcode
+ *
+ * The final life cycle component, `fire` will iterate over the EventPublisher
+ * Subscription%s and call `shouldFire` for each, using the EventContext fired.
+ * The `shouldFire` method should check the subscription-specific selectors and
+ * only call the Subscription%'s callback function if the EventContext
+ * (thus event) matches.
+ */
+template <typename SC, typename EC>
+class EventPublisher : public EventPublisherPlugin {
+ public:
+  /// A nested helper typename for the templated SubscriptionContextRef.
+  typedef typename std::shared_ptr<SC> SCRef;
+  /// A nested helper typename for the templated EventContextRef.
+  typedef typename std::shared_ptr<EC> ECRef;
+
+ public:
+  /// Up-cast a base EventContext reference to the templated ECRef.
+  static ECRef getEventContext(const EventContextRef& ec) {
+    return std::static_pointer_cast<EC>(ec);
+  }
+
+  /// Up-cast a base SubscriptionContext reference to the templated SCRef.
+  static SCRef getSubscriptionContext(const SubscriptionContextRef& sc) {
+    return std::static_pointer_cast<SC>(sc);
+  }
+
+  /// Create a EventContext based on the templated type.
+  static ECRef createEventContext() { return std::make_shared<EC>(); }
+
+  /// Create a SubscriptionContext based on the templated type.
+  static SCRef createSubscriptionContext() { return std::make_shared<SC>(); }
+
+ protected:
+  /**
+   * @brief The internal `fire` phase of publishing.
+   *
+   * This is a template-generated method that up-casts the generic fired
+   * event/subscription contexts, and calls the callback if the event should
+   * fire given a subscription.
+   *
+   * @param sub The SubscriptionContext and optional EventCallback.
+   * @param ec The event that was fired.
+   */
+  void fireCallback(const SubscriptionRef& sub,
+                    const EventContextRef& ec) const {
+    auto pub_sc = getSubscriptionContext(sub->context);
+    auto pub_ec = getEventContext(ec);
+    if (shouldFire(pub_sc, pub_ec) && sub->callback != nullptr) {
+      sub->callback(pub_ec, sub->user_data);
+    }
+  }
+
+ protected:
+  /**
+   * @brief The generic `fire` will call `shouldFire` for each Subscription.
+   *
+   * @param sc A SubscriptionContext with optional specifications for events
+   * details.
+   * @param ec The event fired with event details.
+   *
+   * @return should the Subscription%'s EventCallback be called for this event.
+   */
+  virtual bool shouldFire(const SCRef& sc, const ECRef& ec) const {
+    return true;
+  }
+
+ private:
+  FRIEND_TEST(EventsTests, test_event_sub_subscribe);
+  FRIEND_TEST(EventsTests, test_event_sub_context);
+  FRIEND_TEST(EventsTests, test_fire_event);
+};
+
+class EventSubscriberPlugin : public Plugin {
+ protected:
+  /**
+   * @brief Store parsed event data from an EventCallback in a backing store.
+   *
+   * Within a EventCallback the EventSubscriber has an opportunity to create
+   * an osquery Row element, add the relevant table data for the EventSubscriber
+   * and store that element in the osquery backing store. At query-time
+   * the added data will apply selection criteria and return these elements.
+   * The backing store data retrieval is optimized by time-based indexes. It
+   * is important to added EventTime as it relates to "when the event occurred".
+   *
+   * @param r An osquery Row element.
+   * @param time The time the added event occurred.
+   *
+   * @return Was the element added to the backing store.
+   */
+  virtual Status add(Row& r, EventTime event_time) final;
+
+  /**
+   * @brief Return all events added by this EventSubscriber within start, stop.
+   *
+   * This is used internally (for the most part) by EventSubscriber::genTable.
+   *
+   * @param start Inclusive lower bound time limit.
+   * @param stop Inclusive upper bound time limit.
+   * @return Set of event rows matching time limits.
+   */
+  virtual QueryData get(EventTime start, EventTime stop);
+
+ private:
+  /*
+   * @brief When `get`ing event results, return EventID%s from time indexes.
+   *
+   * Used by EventSubscriber::get to retrieve EventID, EventTime indexes. This
+   * applies the lookup-efficiency checks for time list appropriate bins.
+   * If the time range in 24 hours and there is a 24-hour list bin it will
+   * be queried using a single backing store `Get` followed by two `Get`s of
+   * the most-specific boundary lists.
+   *
+   * @return List of EventID, EventTime%s
+   */
+  std::vector<EventRecord> getRecords(const std::set<std::string>& indexes);
+
+  /**
+   * @brief Get a unique storage-related EventID.
+   *
+   * An EventID is an index/element-identifier for the backing store.
+   * Each EventPublisher maintains a fired EventContextID to identify the many
+   * events that may or may not be fired based on subscription criteria for this
+   * EventSubscriber. This EventContextID is NOT the same as an EventID.
+   * EventSubscriber development should not require use of EventID%s. If this
+   * indexing is required within-EventCallback consider an
+   * EventSubscriber%-unique indexing, counting mechanic.
+   *
+   * @return A unique ID for backing storage.
+   */
+  EventID getEventID();
+
+  /**
+   * @brief Plan the best set of indexes for event record access.
+   *
+   * @param start an inclusive time to begin searching.
+   * @param stop an inclusive time to end searching.
+   * @param list_key optional key to bind to a specific index binning.
+   *
+   * @return List of 'index.step' index strings.
+   */
+  std::set<std::string> getIndexes(EventTime start,
+                                   EventTime stop,
+                                   int list_key = 0);
+
+  /**
+   * @brief Expire indexes and eventually records.
+   *
+   * @param list_type the string representation of list binning type.
+   * @param indexes complete set of 'index.step' indexes for the list_type.
+   * @param expirations of the indexes, the set to expire.
+   */
+  void expireIndexes(const std::string& list_type,
+                     const std::vector<std::string>& indexes,
+                     const std::vector<std::string>& expirations);
+  /// Expire all datums within a bin.
+  void expireRecords(const std::string& list_type,
+                     const std::string& index,
+                     bool all);
+
+  /**
+   * @brief Add an EventID, EventTime pair to all matching list types.
+   *
+   * The list types are defined by time size. Based on the EventTime this pair
+   * is added to the list bin for each list type. If there are two list types:
+   * 60 seconds and 3600 seconds and `time` is 92, this pair will be added to
+   * list type 1 bin 4 and list type 2 bin 1.
+   *
+   * @param eid A unique EventID.
+   * @param time The time when this EventID%'s event occurred.
+   *
+   * @return Were the indexes recorded.
+   */
+  Status recordEvent(EventID& eid, EventTime time);
+
+ public:
+  /**
+   * @brief A single instance requirement for static callback facilities.
+   *
+   * The EventSubscriber constructor is NOT responsible for adding
+   * Subscription%s. Please use `init` for adding Subscription%s as all
+   * EventPublisher instances will have run `setUp` and initialized their run
+   * loops.
+   */
+  EventSubscriberPlugin()
+      : expire_events_(true), expire_time_(0), optimize_time_(0) {}
+  virtual ~EventSubscriberPlugin() {}
+
+  /**
+   * @brief Suggested entrypoint for table generation.
+   *
+   * The EventSubscriber is a convention that removes a lot of boilerplate event
+   * 'subscribing' and acting. The `genTable` static entrypoint is the
+   * suggested method for table specs.
+   *
+   * @return The query-time table data, retrieved from a backing store.
+   */
+  virtual QueryData genTable(QueryContext& context) __attribute__((used));
+
+ protected:
+  /**
+   * @brief Backing storage indexing namespace.
+   *
+   * The backing storage will accumulate events for this subscriber. A namespace
+   * is provided to prevent event indexing collisions between subscribers and
+   * publishers. The namespace is a combination of the publisher and subscriber
+   * registry plugin names.
+   */
+  virtual EventPublisherID& dbNamespace() const = 0;
+
+  /// Disable event expiration for this subscriber.
+  void doNotExpire() { expire_events_ = false; }
+
+ private:
+  EventSubscriberPlugin(EventSubscriberPlugin const&);
+  EventSubscriberPlugin& operator=(EventSubscriberPlugin const&);
+
+ private:
+  Status setUp() { return Status(0, "Setup never used"); }
+
+ private:
+  /// Do not respond to periodic/scheduled/triggered event expiration requests.
+  bool expire_events_;
+
+  /// Events before the expire_time_ are invalid and will be purged.
+  EventTime expire_time_;
+
+  /**
+   * @brief Optimize subscriber selects by tracking the last select time.
+   *
+   * Event subscribers may optimize selects when used in a daemon schedule by
+   * requiring an event 'time' constraint and otherwise applying a minimum time
+   * as the last time the scheduled query ran.
+   */
+  EventTime optimize_time_;
+
+  /// Lock used when incrementing the EventID database index.
+  boost::mutex event_id_lock_;
+
+  /// Lock used when recording an EventID and time into search bins.
+  boost::mutex event_record_lock_;
+
+ private:
+  FRIEND_TEST(EventsDatabaseTests, test_event_module_id);
+  FRIEND_TEST(EventsDatabaseTests, test_record_indexing);
+  FRIEND_TEST(EventsDatabaseTests, test_record_range);
+  FRIEND_TEST(EventsDatabaseTests, test_record_expiration);
+};
+
+/**
+ * @brief A factory for associating event generators to EventPublisherID%s.
+ *
+ * This factory both registers new event types and the subscriptions that use
+ * them. An EventPublisher is also a factory, the single event factory
+ * arbitrates Subscription creation and management for each associated
+ * EventPublisher.
+ *
+ * Since event types may be plugins, they are created using the factory.
+ * Since subscriptions may be configured/disabled they are also factory-managed.
+ */
+class EventFactory : private boost::noncopyable {
+ public:
+  /// Access to the EventFactory instance.
+  static EventFactory& getInstance();
+
+  /**
+   * @brief Add an EventPublisher to the factory.
+   *
+   * The registration is mostly abstracted using osquery's registry.
+   *
+   * @param event_pub If for some reason the caller needs access to the
+   * EventPublisher instance they can register-by-instance.
+   *
+   * Access to the EventPublisher instance is not discouraged, but using the
+   * EventFactory `getEventPublisher` accessor is encouraged.
+   */
+  static Status registerEventPublisher(const PluginRef& pub);
+
+  /**
+   * @brief Add an EventSubscriber to the factory.
+   *
+   * The registration is mostly abstracted using osquery's registry.
+   */
+  template <class T>
+  static Status registerEventSubscriber() {
+    auto sub = std::make_shared<T>();
+    return registerEventSubscriber(sub);
+  }
+
+  /**
+   * @brief Add an EventSubscriber to the factory.
+   *
+   * The registration is mostly abstracted using osquery's registry.
+   *
+   * @param sub If the caller must access the EventSubscriber instance
+   * control may be passed to the registry.
+   *
+   * Access to the EventSubscriber instance outside of the within-instance
+   * table generation method and set of EventCallback%s is discouraged.
+   */
+  static Status registerEventSubscriber(const PluginRef& sub);
+
+  /**
+   * @brief Add a SubscriptionContext and EventCallback Subscription to an
+   * EventPublisher.
+   *
+   * Create a Subscription from a given SubscriptionContext and EventCallback
+   * and add that Subscription to the EventPublisher associated identifier.
+   *
+   * @param type_id The string for an EventPublisher receiving the Subscription.
+   * @param sc A SubscriptionContext related to the EventPublisher.
+   * @param cb When the EventPublisher fires an event the SubscriptionContext
+   * will be evaluated, if the event matches optional specifics in the context
+   * this callback function will be called. It should belong to an
+   * EventSubscription.
+   *
+   * @return Was the SubscriptionContext appropriate for the EventPublisher.
+   */
+  static Status addSubscription(EventPublisherID& type_id,
+                                EventSubscriberID& name_id,
+                                const SubscriptionContextRef& sc,
+                                EventCallback cb = 0,
+                                void* user_data = nullptr);
+
+  /// Add a Subscription using a caller Subscription instance.
+  static Status addSubscription(EventPublisherID& type_id,
+                                const SubscriptionRef& subscription);
+
+  /// Get the total number of Subscription%s across ALL EventPublisher%s.
+  static size_t numSubscriptions(EventPublisherID& type_id);
+
+  /// Get the number of EventPublishers.
+  static size_t numEventPublishers() {
+    return EventFactory::getInstance().event_pubs_.size();
+  }
+
+  /**
+   * @brief Halt the EventPublisher run loop.
+   *
+   * Any EventSubscriber%s with Subscription%s for this EventPublisher will
+   * become useless. osquery callers MUST deregister events.
+   * EventPublisher%s assume they can hook/trampoline, which requires cleanup.
+   * This will tear down and remove the publisher if the run loop did not start.
+   * Otherwise it will call end on the publisher and assume the run loop will
+   * tear down and remove.
+   *
+   * @param event_pub The string label for the EventPublisher.
+   *
+   * @return Did the EventPublisher deregister cleanly.
+   */
+  static Status deregisterEventPublisher(const EventPublisherRef& pub);
+
+  /// Deregister an EventPublisher by EventPublisherID.
+  static Status deregisterEventPublisher(EventPublisherID& type_id);
+
+  /// Return an instance to a registered EventPublisher.
+  static EventPublisherRef getEventPublisher(EventPublisherID& pub);
+
+  /// Return an instance to a registered EventSubscriber.
+  static EventSubscriberRef getEventSubscriber(EventSubscriberID& sub);
+
+  /// Check if an event subscriber exists.
+  static bool exists(EventSubscriberID& sub);
+
+  /// Return a list of publisher types, these are their registry names.
+  static std::vector<std::string> publisherTypes();
+
+  /// Return a list of subscriber registry names,
+  static std::vector<std::string> subscriberNames();
+
+ public:
+  /// The dispatched event thread's entry-point (if needed).
+  static Status run(EventPublisherID& type_id);
+
+  /// An initializer's entry-point for spawning all event type run loops.
+  static void delay();
+
+  /// If a static EventPublisher callback wants to fire
+  template <typename PUB>
+  static void fire(const EventContextRef& ec) {
+    auto event_pub = getEventPublisher(getType<PUB>());
+    event_pub->fire(ec);
+  }
+
+  /**
+   * @brief Return the publisher registry name given a type.
+   *
+   * Subscriber initialization and runtime static callbacks can lookup the
+   * publisher type name, which is the registry plugin name. This allows static
+   * callbacks to fire into subscribers.
+   */
+  template <class PUB>
+  static EventPublisherID getType() {
+    auto pub = std::make_shared<PUB>();
+    return pub->type();
+  }
+
+  /**
+   * @brief End all EventPublisher run loops and deregister.
+   *
+   * End is NOT the same as deregistration. End will call deregister on all
+   * publishers then either join or detach their run loop threads.
+   * See EventFactory::deregisterEventPublisher for actions taken during
+   * deregistration.
+   *
+   * @param should_end Reset the "is ending" state if False.
+   */
+  static void end(bool join = false);
+
+ private:
+  /// An EventFactory will exist for the lifetime of the application.
+  EventFactory() {}
+  EventFactory(EventFactory const&);
+  EventFactory& operator=(EventFactory const&);
+  ~EventFactory() {}
+
+ private:
+  /// Set of registered EventPublisher instances.
+  std::map<EventPublisherID, EventPublisherRef> event_pubs_;
+
+  /// Set of instantiated EventSubscriber subscriptions.
+  std::map<EventSubscriberID, EventSubscriberRef> event_subs_;
+
+  /// Set of running EventPublisher run loop threads.
+  std::vector<std::shared_ptr<boost::thread> > threads_;
+};
+
+/**
+ * @brief An interface binding Subscriptions, event response, and table
+ *generation.
+ *
+ * Use the EventSubscriber interface when adding event subscriptions and
+ * defining callin functions. The EventCallback is usually a member function
+ * for an EventSubscriber. The EventSubscriber interface includes a very
+ * important `add` method that abstracts the needed event to backing store
+ * interaction.
+ *
+ * Storing event data in the backing store must match a table spec for queries.
+ * Small overheads exist that help query-time indexing and lookups.
+ */
+template <class PUB>
+class EventSubscriber : public EventSubscriberPlugin {
+ protected:
+  typedef typename PUB::SCRef SCRef;
+  typedef typename PUB::ECRef ECRef;
+
+ public:
+  /**
+   * @brief Add Subscription%s to the EventPublisher this module will act on.
+   *
+   * When the EventSubscriber%'s `init` method is called you are assured the
+   * EventPublisher has `setUp` and is ready to subscription for events.
+   */
+  virtual Status init() { return Status(0, "OK"); }
+
+ protected:
+  /// Helper function to call the publisher's templated subscription generator.
+  SCRef createSubscriptionContext() const {
+    return PUB::createSubscriptionContext();
+  }
+
+  /**
+   * @brief Bind a registered EventSubscriber member function to a Subscription.
+   *
+   * @param entry A templated EventSubscriber member function.
+   * @param sc The subscription context.
+   */
+  template <class T, typename C>
+  void subscribe(Status (T::*entry)(const std::shared_ptr<C>&, const void*),
+                 const SubscriptionContextRef& sc,
+                 void* user_data) {
+    // Up-cast the EventSubscriber to the caller.
+    auto sub = dynamic_cast<T*>(this);
+    // Down-cast the pointer to the member function.
+    auto base_entry =
+        reinterpret_cast<Status (T::*)(const EventContextRef&, void const*)>(
+            entry);
+    // Create a callable through the member function using the instance of the
+    // EventSubscriber and a single parameter placeholder (the EventContext).
+    auto cb = std::bind(base_entry, sub, _1, _2);
+    // Add a subscription using the callable and SubscriptionContext.
+    EventFactory::addSubscription(getType(), sub->getName(), sc, cb, user_data);
+  }
+
+  /**
+   * @brief The registry plugin name for the subscriber's publisher.
+   *
+   * During event factory initialization the subscribers 'peek' at the registry
+   * plugin name assigned to publishers. The corresponding publisher name is
+   * interpreted as the subscriber's event 'type'.
+   */
+  EventPublisherID& getType() const {
+    static EventPublisherID type = EventFactory::getType<PUB>();
+    return type;
+  }
+
+  /// See getType for lookup rational.
+  EventPublisherID& dbNamespace() const {
+    static EventPublisherID _ns = getType() + '.' + getName();
+    return _ns;
+  }
+
+ public:
+  /**
+   * @brief Request the subscriber's initialization state.
+   *
+   * When event subscribers are created (initialized) they are expected to emit
+   * a set of subscriptions to their publisher "type". If the subscriber fails
+   * to initialize then the publisher may remove any intermediate subscriptions.
+   */
+  EventSubscriberState state() const { return state_; }
+
+  /// Set the subscriber state.
+  void state(EventSubscriberState state) { state_ = state; }
+
+  EventSubscriber() : EventSubscriberPlugin(), state_(SUBSCRIBER_NONE) {}
+
+ private:
+  /// The event subscriber's run state.
+  EventSubscriberState state_;
+
+ private:
+  FRIEND_TEST(EventsTests, test_event_sub);
+  FRIEND_TEST(EventsTests, test_event_sub_subscribe);
+  FRIEND_TEST(EventsTests, test_event_sub_context);
+};
+
+/// Iterate the event publisher registry and create run loops for each using
+/// the event factory.
+void attachEvents();
+
+/// Sleep in a boost::thread interruptible state.
+void publisherSleep(size_t milli);
+
+CREATE_REGISTRY(EventPublisherPlugin, "event_publisher");
+CREATE_REGISTRY(EventSubscriberPlugin, "event_subscriber");
+}
diff --git a/src/osquery/include/osquery/extensions.h b/src/osquery/include/osquery/extensions.h
new file mode 100644 (file)
index 0000000..cd2e45c
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <osquery/core.h>
+#include <osquery/flags.h>
+#include <osquery/sql.h>
+
+namespace osquery {
+
+DECLARE_int32(worker_threads);
+DECLARE_string(extensions_socket);
+DECLARE_string(extensions_autoload);
+DECLARE_string(extensions_timeout);
+DECLARE_bool(disable_extensions);
+
+/// A millisecond internal applied to extension initialization.
+extern const size_t kExtensionInitializeLatencyUS;
+
+/**
+ * @brief Helper struct for managing extenion metadata.
+ *
+ * This structure should match the members of Thrift's InternalExtensionInfo.
+ */
+struct ExtensionInfo {
+  std::string name;
+  std::string version;
+  std::string min_sdk_version;
+  std::string sdk_version;
+};
+
+typedef std::map<RouteUUID, ExtensionInfo> ExtensionList;
+
+inline std::string getExtensionSocket(
+    RouteUUID uuid, const std::string& path = FLAGS_extensions_socket) {
+  if (uuid == 0) {
+    return path;
+  } else {
+    return path + "." + std::to_string(uuid);
+  }
+}
+
+/// External (extensions) SQL implementation of the osquery query API.
+Status queryExternal(const std::string& query, QueryData& results);
+
+/// External (extensions) SQL implementation of the osquery getQueryColumns API.
+Status getQueryColumnsExternal(const std::string& q, TableColumns& columns);
+
+/// External (extensions) SQL implementation plugin provider for "sql" registry.
+class ExternalSQLPlugin : SQLPlugin {
+ public:
+  Status query(const std::string& q, QueryData& results) const {
+    return queryExternal(q, results);
+  }
+
+  Status getQueryColumns(const std::string& q, TableColumns& columns) const {
+    return getQueryColumnsExternal(q, columns);
+  }
+};
+
+/// Status get a list of active extenions.
+Status getExtensions(ExtensionList& extensions);
+
+/// Internal getExtensions using a UNIX domain socket path.
+Status getExtensions(const std::string& manager_path,
+                     ExtensionList& extensions);
+
+/// Ping an extension manager or extension.
+Status pingExtension(const std::string& path);
+
+/**
+ * @brief Request the extensions API to autoload any appropriate extensions.
+ *
+ * Extensions may be 'autoloaded' using the `extensions_autoload` command line
+ * argument. loadExtensions should be called before any plugin or registry item
+ * is used. This allows appropriate extensions to expose plugin requirements.
+ *
+ * An 'appropriate' extension is one within the `extensions_autoload` search
+ * path with file ownership equivilent or greater (root) than the osquery
+ * process requesting autoload.
+ */
+void loadExtensions();
+
+/**
+ * @brief Load extensions from a delimited search path string.
+ *
+ * @param paths A colon-delimited path variable, e.g: '/path1:/path2'.
+ */
+Status loadExtensions(const std::string& loadfile);
+
+/**
+ * @brief Request the extensions API to autoload any appropriate modules.
+ *
+ * Extension modules are shared libraries that add Plugins to the osquery
+ * core's registry at runtime.
+ */
+void loadModules();
+
+/**
+ * @brief Load extenion modules from a delimited search path string.
+ *
+ * @param paths A colon-delimited path variable, e.g: '/path1:/path2'.
+ */
+Status loadModules(const std::string& loadfile);
+
+/// Load all modules in a direcotry.
+Status loadModuleFile(const std::string& path);
+
+/**
+ * @brief Call a Plugin exposed by an Extension Registry route.
+ *
+ * This is mostly a Registry%-internal method used to call an ExtensionHandler
+ * call API if a Plugin is requested and had matched an Extension route.
+ *
+ * @param uuid Route UUID of the matched Extension
+ * @param registry The string name for the registry.
+ * @param item A string identifier for this registry item.
+ * @param request The plugin request input.
+ * @param response The plugin response output.
+ * @return Success indicates Extension API call success and Extension's
+ * Registry::call success.
+ */
+Status callExtension(const RouteUUID uuid,
+                     const std::string& registry,
+                     const std::string& item,
+                     const PluginRequest& request,
+                     PluginResponse& response);
+
+/// Internal callExtension implementation using a UNIX domain socket path.
+Status callExtension(const std::string& extension_path,
+                     const std::string& registry,
+                     const std::string& item,
+                     const PluginRequest& request,
+                     PluginResponse& response);
+
+/// The main runloop entered by an Extension, start an ExtensionRunner thread.
+Status startExtension(const std::string& name, const std::string& version);
+
+/// The main runloop entered by an Extension, start an ExtensionRunner thread.
+Status startExtension(const std::string& name,
+                      const std::string& version,
+                      const std::string& min_sdk_version);
+
+/// Internal startExtension implementation using a UNIX domain socket path.
+Status startExtension(const std::string& manager_path,
+                      const std::string& name,
+                      const std::string& version,
+                      const std::string& min_sdk_version,
+                      const std::string& sdk_version);
+
+/// Start an ExtensionWatcher thread.
+Status startExtensionWatcher(const std::string& manager_path,
+                             size_t interval,
+                             bool fatal);
+
+/// Start an ExtensionManagerRunner thread.
+Status startExtensionManager();
+
+/// Internal startExtensionManager implementation.
+Status startExtensionManager(const std::string& manager_path);
+}
diff --git a/src/osquery/include/osquery/filesystem.h b/src/osquery/include/osquery/filesystem.h
new file mode 100644 (file)
index 0000000..8c4ef80
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <boost/filesystem/path.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/status.h>
+
+namespace osquery {
+
+/// Globbing directory traversal function recursive limit.
+typedef unsigned short GlobLimits;
+
+enum {
+  GLOB_FILES = 0x1,
+  GLOB_FOLDERS = 0x2,
+  GLOB_ALL = GLOB_FILES | GLOB_FOLDERS,
+};
+
+/// Globbing wildcard character.
+const std::string kSQLGlobWildcard = "%";
+/// Globbing wildcard recursive character (double wildcard).
+const std::string kSQLGlobRecursive = kSQLGlobWildcard + kSQLGlobWildcard;
+
+/**
+ * @brief Read a file from disk.
+ *
+ * @param path the path of the file that you would like to read.
+ * @param content a reference to a string which will be populated with the
+ * contents of the path indicated by the path parameter.
+ * @param dry_run do not actually read the file content.
+ *
+ * @return an instance of Status, indicating success or failure.
+ */
+Status readFile(const boost::filesystem::path& path,
+                std::string& content,
+                bool dry_run = false);
+
+/**
+ * @brief Return the status of an attempted file read.
+ *
+ * @param path the path of the file that you would like to read.
+ *
+ * @return success iff the file would have been read. On success the status
+ * message is the complete/absolute path.
+ */
+Status readFile(const boost::filesystem::path& path);
+
+/**
+ * @brief Write text to disk.
+ *
+ * @param path the path of the file that you would like to write.
+ * @param content the text that should be written exactly to disk.
+ * @param permissions the filesystem permissions to request when opening.
+ * @param force_permissions always `chmod` the path after opening.
+ *
+ * @return an instance of Status, indicating success or failure.
+ */
+Status writeTextFile(const boost::filesystem::path& path,
+                     const std::string& content,
+                     int permissions = 0660,
+                     bool force_permissions = false);
+
+/// Check if a path is writable.
+Status isWritable(const boost::filesystem::path& path);
+
+/// Check if a path is readable.
+Status isReadable(const boost::filesystem::path& path);
+
+/**
+ * @brief A helper to check if a path exists on disk or not.
+ *
+ * @param path Target path.
+ *
+ * @return The code of the Status instance will be -1 if no input was supplied,
+ * assuming the caller is not aware of how to check path-getter results.
+ * The code will be 0 if the path does not exist on disk and 1 if the path
+ * does exist on disk.
+ */
+Status pathExists(const boost::filesystem::path& path);
+
+/**
+ * @brief List all of the files in a specific directory, non-recursively.
+ *
+ * @param path the path which you would like to list.
+ * @param results a non-const reference to a vector which will be populated
+ * with the directory listing of the path param, assuming that all operations
+ * completed successfully.
+ *
+ * @return an instance of Status, indicating success or failure.
+ */
+Status listFilesInDirectory(const boost::filesystem::path& path,
+                            std::vector<std::string>& results,
+                            bool ignore_error = 1);
+
+/**
+ * @brief List all of the directories in a specific directory, non-recursively.
+ *
+ * @param path the path which you would like to list
+ * @param results a non-const reference to a vector which will be populated
+ * with the directory listing of the path param, assuming that all operations
+ * completed successfully.
+ *
+ * @return an instance of Status, indicating success or failure.
+ */
+Status listDirectoriesInDirectory(const boost::filesystem::path& path,
+                                  std::vector<std::string>& results,
+                                  bool ignore_error = 1);
+
+/**
+ * @brief Given a filesystem globbing patten, resolve all matching paths.
+ *
+ * @code{.cpp}
+ *   std::vector<std::string> results;
+ *   auto s = resolveFilePattern("/Users/marpaia/Downloads/%", results);
+ *   if (s.ok()) {
+ *     for (const auto& result : results) {
+ *       LOG(INFO) << result;
+ *     }
+ *   }
+ * @endcode
+ *
+ * @param pattern filesystem globbing pattern.
+ * @param results output vector of matching paths.
+ *
+ * @return an instance of Status, indicating success or failure.
+ */
+Status resolveFilePattern(const boost::filesystem::path& pattern,
+                          std::vector<std::string>& results);
+
+/**
+ * @brief Given a filesystem globbing patten, resolve all matching paths.
+ *
+ * See resolveFilePattern, but supply a limitation to request only directories
+ * or files that match the path.
+ *
+ * @param pattern filesystem globbing pattern.
+ * @param results output vector of matching paths.
+ * @param setting a bit list of match types, e.g., files, folders.
+ *
+ * @return an instance of Status, indicating success or failure.
+ */
+Status resolveFilePattern(const boost::filesystem::path& pattern,
+                          std::vector<std::string>& results,
+                          GlobLimits setting);
+
+/**
+ * @brief Transform a path with SQL wildcards to globbing wildcard.
+ *
+ * SQL uses '%' as a wildcard matching token, and filesystem globbing uses '*'.
+ * In osquery-internal methods the filesystem character is used. This helper
+ * method will perform the correct preg/escape and replace.
+ *
+ * This has a side effect of canonicalizing paths up to the first wildcard.
+ * For example: /tmp/% becomes /private/tmp/% on OS X systems. And /tmp/%.
+ *
+ * @param pattern the input and output filesystem glob pattern.
+ */
+void replaceGlobWildcards(std::string& pattern);
+
+/**
+ * @brief Get directory portion of a path.
+ *
+ * @param path input path, either a filename or directory.
+ * @param dirpath output path set to the directory-only path.
+ *
+ * @return If the input path was a directory this will indicate failure. One
+ * should use `isDirectory` before.
+ */
+Status getDirectory(const boost::filesystem::path& path,
+                    boost::filesystem::path& dirpath);
+
+/// Attempt to remove a directory path.
+Status remove(const boost::filesystem::path& path);
+
+/**
+ * @brief Check if an input path is a directory.
+ *
+ * @param path input path, either a filename or directory.
+ *
+ * @return If the input path was a directory.
+ */
+Status isDirectory(const boost::filesystem::path& path);
+
+/**
+ * @brief Return a vector of all home directories on the system.
+ *
+ * @return a vector of string paths containing all home directories.
+ */
+std::set<boost::filesystem::path> getHomeDirectories();
+
+/**
+ * @brief Check the permissions of a file and its directory.
+ *
+ * 'Safe' implies the directory is not a /tmp-like directory in that users
+ * cannot control super-user-owner files. The file should be owned by the
+ * process's UID or the file should be owned by root.
+ *
+ * @param dir the directory to check `/tmp` mode.
+ * @param path a path to a file to check.
+ * @param executable true if the file must also be executable.
+ *
+ * @return true if the file is 'safe' else false.
+ */
+bool safePermissions(const std::string& dir,
+                     const std::string& path,
+                     bool executable = false);
+
+/**
+ * @brief osquery may use local storage in a user-protected "home".
+ *
+ * Return a standard path to an "osquery" home directory. This path may store
+ * a protected extensions socket, backing storage database, and debug logs.
+ */
+const std::string& osqueryHomeDirectory();
+
+/// Return bit-mask-style permissions.
+std::string lsperms(int mode);
+
+/**
+ * @brief Parse a JSON file on disk into a property tree.
+ *
+ * @param path the path of the JSON file.
+ * @param tree output property tree.
+ *
+ * @return an instance of Status, indicating success or failure if malformed.
+ */
+Status parseJSON(const boost::filesystem::path& path,
+                 boost::property_tree::ptree& tree);
+
+/**
+ * @brief Parse JSON content into a property tree.
+ *
+ * @param path JSON string data.
+ * @param tree output property tree.
+ *
+ * @return an instance of Status, indicating success or failure if malformed.
+ */
+Status parseJSONContent(const std::string& content,
+                        boost::property_tree::ptree& tree);
+
+#ifdef __APPLE__
+/**
+ * @brief Parse a property list on disk into a property tree.
+ *
+ * @param path the input path to a property list.
+ * @param tree the output property tree.
+ *
+ * @return an instance of Status, indicating success or failure if malformed.
+ */
+Status parsePlist(const boost::filesystem::path& path,
+                  boost::property_tree::ptree& tree);
+
+/**
+ * @brief Parse property list content into a property tree.
+ *
+ * @param content the input string-content of a property list.
+ * @param tree the output property tree.
+ *
+ * @return an instance of Status, indicating success or failure if malformed.
+ */
+Status parsePlistContent(const std::string& content,
+                         boost::property_tree::ptree& tree);
+#endif
+
+#ifdef __linux__
+/**
+ * @brief Iterate over `/proc` process, returns a list of pids.
+ *
+ * @param processes output list of process pids as strings (int paths in proc).
+ *
+ * @return an instance of Status, indicating success or failure.
+ */
+Status procProcesses(std::set<std::string>& processes);
+
+/**
+ * @brief Iterate over a `/proc` process's descriptors, return a list of fds.
+ *
+ * @param process a string pid from proc.
+ * @param descriptors output list of descriptor numbers as strings.
+ *
+ * @return status of iteration, failure if the process path did not exist.
+ */
+Status procDescriptors(const std::string& process,
+                       std::map<std::string, std::string>& descriptors);
+
+/**
+ * @brief Read a descriptor's virtual path.
+ *
+ * @param process a string pid from proc.
+ * @param descriptor a string descriptor number for a proc.
+ * @param result output variable with value of link.
+ *
+ * @return status of read, failure on permission error or filesystem error.
+ */
+Status procReadDescriptor(const std::string& process,
+                          const std::string& descriptor,
+                          std::string& result);
+
+/**
+ * @brief Read bytes from Linux's raw memory.
+ *
+ * Most Linux kernels include a device node /dev/mem that allows privileged
+ * users to map or seek/read pages of physical memory.
+ * osquery discourages the use of physical memory reads for security and
+ * performance reasons and must first try safer methods for data parsing
+ * such as /sys and /proc.
+ *
+ * A platform user may disable physical memory reads:
+ *   --disable_memory=true
+ * This flag/option will cause readRawMemory to forcefully fail.
+ *
+ * @param base The absolute memory address to read from. This does not need
+ * to be page aligned, readRawMem will take care of alignment and only
+ * return the requested start address and size.
+ * @param length The length of the buffer with a max of 0x10000.
+ * @param buffer The output buffer, caller is responsible for resources if
+ * readRawMem returns success.
+ * @return status The status of the read.
+ */
+Status readRawMem(size_t base, size_t length, void** buffer);
+
+#endif
+}
diff --git a/src/osquery/include/osquery/flags.h b/src/osquery/include/osquery/flags.h
new file mode 100644 (file)
index 0000000..8eb17d9
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+
+#include <boost/lexical_cast.hpp>
+
+#define STRIP_FLAG_HELP 1
+#include <gflags/gflags.h>
+
+#include <osquery/core.h>
+
+#define GFLAGS_NAMESPACE google
+
+namespace boost {
+/// We define a lexical_cast template for boolean for Gflags boolean string
+/// values.
+template <>
+bool lexical_cast<bool, std::string>(const std::string& arg);
+
+template <>
+std::string lexical_cast<std::string, bool>(const bool& b);
+}
+
+namespace osquery {
+
+struct FlagDetail {
+  std::string description;
+  bool shell;
+  bool external;
+  bool cli;
+  bool hidden;
+};
+
+struct FlagInfo {
+  std::string type;
+  std::string description;
+  std::string default_value;
+  std::string value;
+  FlagDetail detail;
+};
+
+/**
+ * @brief A small tracking wrapper for options, binary flags.
+ *
+ * The osquery-specific gflags-like options define macro `FLAG` uses a Flag
+ * instance to track the options data.
+ */
+class Flag {
+ public:
+  /*
+   * @brief Create a new flag.
+   *
+   * @param name The 'name' or the options switch data.
+   * @param flag Flag information filled in using the helper macro.
+   *
+   * @return A mostly needless flag instance.
+   */
+  static int create(const std::string& name, const FlagDetail& flag);
+
+  /// Create a Gflags alias to name, using the Flag::getValue accessor.
+  static int createAlias(const std::string& alias, const FlagDetail& flag);
+
+  static Flag& instance() {
+    static Flag f;
+    return f;
+  }
+
+ private:
+  /// Keep the ctor private, for accessing through `add` wrapper.
+  Flag() {}
+  virtual ~Flag() {}
+
+  Flag(Flag const&);
+  void operator=(Flag const&);
+
+ public:
+  /// The public flags instance, usable when parsing `--help`.
+  static std::map<std::string, FlagInfo> flags();
+
+  /*
+   * @brief Access value for a flag name.
+   *
+   * @param name the flag name.
+   * @param value output parameter filled with the flag value on success.
+   * @return status of the flag did exist.
+   */
+  static Status getDefaultValue(const std::string& name, std::string& value);
+
+  /*
+   * @brief Check if flag value has been overridden.
+   *
+   * @param name the flag name.
+   * @return is the flag set to its default value.
+   */
+  static bool isDefault(const std::string& name);
+
+  /*
+   * @brief Update the flag value by string name,
+   *
+   * @param name the flag name.
+   * @parma value the new value.
+   * @return if the value was updated.
+   */
+  static Status updateValue(const std::string& name, const std::string& value);
+
+  /*
+   * @brief Get the value of an osquery flag.
+   *
+   * @param name the flag name.
+   */
+  static std::string getValue(const std::string& name);
+
+  /*
+   * @brief Get the type as a string of an osquery flag.
+   *
+   * @param name the flag name.
+   */
+  static std::string getType(const std::string& name);
+
+  /*
+   * @brief Get the description as a string of an osquery flag.
+   *
+   * @param name the flag name.
+   */
+  static std::string getDescription(const std::string& name);
+
+  /*
+   * @brief Print help-style output to stdout for a given flag set.
+   *
+   * @param shell Only print shell flags.
+   * @param external Only print external flags (from extensions).
+   */
+  static void printFlags(bool shell = false,
+                         bool external = false,
+                         bool cli = false);
+
+ private:
+  std::map<std::string, FlagDetail> flags_;
+  std::map<std::string, FlagDetail> aliases_;
+};
+
+/**
+ * @brief Helper accessor/assignment alias class to support deprecated flags.
+ *
+ * This templated class wraps Flag::updateValue and Flag::getValue to 'alias'
+ * a deprecated flag name as the updated name. The helper macro FLAG_ALIAS
+ * will create a global variable instances of this wrapper using the same
+ * Gflags naming scheme to prevent collisions and support existing callsites.
+ */
+template <typename T>
+class FlagAlias {
+ public:
+  FlagAlias& operator=(T const& v) {
+    Flag::updateValue(name_, boost::lexical_cast<std::string>(v));
+    return *this;
+  }
+
+  operator T() const { return boost::lexical_cast<T>(Flag::getValue(name_)); }
+
+  FlagAlias(const std::string& alias,
+            const std::string& type,
+            const std::string& name,
+            T* storage)
+      : name_(name) {}
+
+ private:
+  std::string name_;
+};
+}
+
+/*
+ * @brief Replace gflags' `DEFINE_type` macros to track osquery flags.
+ *
+ * @param type The `_type` symbol portion of the gflags define.
+ * @param name The name symbol passed to gflags' `DEFINE_type`.
+ * @param value The default value, use a C++ literal.
+ * @param desc A string literal used for help display.
+ */
+#define OSQUERY_FLAG(t, n, v, d, s, e, c, h)              \
+  DEFINE_##t(n, v, d);                                    \
+  namespace flags {                                       \
+  const int flag_##n = Flag::create(#n, {d, s, e, c, h}); \
+  }
+
+#define FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 0, 0)
+#define SHELL_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 1, 0, 0, 0)
+#define EXTENSION_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 1, 0, 0)
+#define CLI_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 1, 0)
+#define HIDDEN_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 0, 1)
+
+#define OSQUERY_FLAG_ALIAS(t, a, n, s, e)                             \
+  FlagAlias<t> FLAGS_##a(#a, #t, #n, &FLAGS_##n);                     \
+  namespace flags {                                                   \
+  static GFLAGS_NAMESPACE::FlagRegisterer oflag_##a(                  \
+      #a, #t, #a, &FLAGS_##n, &FLAGS_##n);                            \
+  const int flag_alias_##a = Flag::createAlias(#a, {#n, s, e, 0, 1}); \
+  }
+
+#define FLAG_ALIAS(t, a, n) OSQUERY_FLAG_ALIAS(t, a, n, 0, 0)
+#define SHELL_FLAG_ALIAS(t, a, n) _OSQUERY_FLAG_ALIAS(t, a, n, 1, 0)
+#define EXTENSION_FLAG_ALIAS(a, n) OSQUERY_FLAG_ALIAS(std::string, a, n, 0, 1)
diff --git a/src/osquery/include/osquery/hash.h b/src/osquery/include/osquery/hash.h
new file mode 100644 (file)
index 0000000..b6cc5ae
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <string>
+
+namespace osquery {
+
+/**
+ * @brief The supported hashing algorithms in osquery
+ *
+ * These are usually used as a constructor argument to osquery::Hash
+ */
+enum HashType {
+  HASH_TYPE_MD5 = 2,
+  HASH_TYPE_SHA1 = 4,
+  HASH_TYPE_SHA256 = 8,
+};
+
+/**
+ * @brief Hash is a general utility class for hashing content
+ *
+ * @code{.cpp}
+ *   Hash my_hash(HASH_TYPE_SHA256);
+ *   my_hash.update(my_buffer, my_buffer_size);
+ *   std::cout << my_hash.digest();
+ * @endcode
+ *
+ */
+class Hash {
+ public:
+  /**
+   * @brief Hash constructor
+   *
+   * The hash class should be initialized with one of osquery::HashType as a
+   * constructor argument.
+   *
+   * @param algorithm The hashing algorithm which will be used to compute the
+   * hash
+   */
+  explicit Hash(HashType algorithm);
+
+  /**
+   * @brief Hash destructor
+   */
+  ~Hash();
+
+  /**
+   * @brief Update the internal context buffer with additional content
+   *
+   * This method allows you to chunk up large content so that it doesn't all
+   * have to be loaded into memory at the same time
+   *
+   * @param buffer The buffer to be hashed
+   * @param size The size of the buffer to be hashed
+   */
+  void update(const void* buffer, size_t size);
+
+  /**
+   * @brief Compute the final hash and return it's result
+   *
+   * @return The final hash value
+   */
+  std::string digest();
+
+ private:
+  /**
+   * @brief Private default constructor
+   *
+   * The osquery::Hash class should only ever be instantiated with a HashType
+   */
+  Hash(){};
+
+ private:
+  /// The hashing algorithm which is used to compute the hash
+  HashType algorithm_;
+
+  /// The buffer used to maintain the context and state of the hashing
+  /// operations
+  void* ctx_;
+
+  /// The length of the hash to be returned
+  size_t length_;
+};
+
+/**
+ * @brief Compute a hash digest from an already allocated buffer.
+ *
+ * @param hash_type The osquery-supported hash algorithm.
+ * @param buffer A caller-controlled buffer.
+ * @param size The length of buffer in bytes.
+ * @return A string (hex) representation of the hash digest.
+ */
+std::string hashFromBuffer(HashType hash_type, const void* buffer, size_t size);
+
+/**
+ * @brief Compute a hash digest from the file content at a path.
+ *
+ *
+ * @param hash_type The osquery-supported hash algorithm.
+ * @param path Filesystem path, the hash target.
+ * @return A string (hex) representation of the hash digest.
+ */
+std::string hashFromFile(HashType hash_type, const std::string& path);
+}
diff --git a/src/osquery/include/osquery/logger.h b/src/osquery/include/osquery/logger.h
new file mode 100644 (file)
index 0000000..0ef8b15
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include <osquery/database.h>
+#include <osquery/flags.h>
+#include <osquery/registry.h>
+
+namespace osquery {
+
+DECLARE_bool(disable_logging);
+DECLARE_string(logger_plugin);
+
+/**
+ * @brief An internal severity set mapping to Glog's LogSeverity levels.
+ */
+enum StatusLogSeverity {
+  O_INFO = 0,
+  O_WARNING = 1,
+  O_ERROR = 2,
+  O_FATAL = 3,
+};
+
+/// An intermediate status log line.
+struct StatusLogLine {
+ public:
+  /// An integer severity level mimicing Glog's.
+  StatusLogSeverity severity;
+  /// The name of the file emitting the status log.
+  std::string filename;
+  /// The line of the file emitting the status log.
+  int line;
+  /// The string-formatted status message.
+  std::string message;
+};
+
+/**
+ * @brief Helper logging macro for table-generated verbose log lines.
+ *
+ * Since logging in tables does not always mean a critical warning or error
+ * but more likely a parsing or expected edge-case, we provide a TLOG.
+ *
+ * The tool user can set within config or via the CLI what level of logging
+ * to tolerate. It's the table developer's job to assume consistency in logging.
+ */
+#define TLOG VLOG(1)
+
+/**
+ * @brief Prepend a reference number to the log line.
+ *
+ * A reference number is an external-search helper for somewhat confusing or
+ * seeminly-critical log lines.
+ */
+#define RLOG(n) "[Ref #" #n "] "
+
+/**
+ * @brief Superclass for the pluggable logging facilities.
+ *
+ * In order to make the logging of osquery results and inline debug, warning,
+ * error status easy to integrate into your environment, we take advantage of
+ * a plugin interface which allows you to integrate osquery with your internal
+ * large-scale logging infrastructure.
+ *
+ * You may use flume, splunk, syslog, scribe, etc. In order to use your
+ * specific upstream logging systems, one simply needs to create a custom
+ * subclass of LoggerPlugin. That subclass should at least implement the
+ * LoggerPlugin::logString method.
+ *
+ * Consider the following example:
+ *
+ * @code{.cpp}
+ *   class TestLoggerPlugin : public LoggerPlugin {
+ *    public:
+ *     osquery::Status logString(const std::string& s) {
+ *       int i = 0;
+ *       internal::logStringToFlume(s, i);
+ *       std::string message;
+ *       if (i == 0) {
+ *         message = "OK";
+ *       } else {
+ *         message = "Failed";
+ *       }
+ *       return osquery::Status(i, message);
+ *     }
+ *  };
+ *
+ *  REGISTER(TestLoggerPlugin, "logger", "test");
+ * @endcode
+ */
+class LoggerPlugin : public Plugin {
+ public:
+  /// The LoggerPlugin PluginRequest action router.
+  Status call(const PluginRequest& request, PluginResponse& response);
+
+ protected:
+  /** @brief Virtual method which should implement custom logging.
+   *
+   *  LoggerPlugin::logString should be implemented by a subclass of
+   *  LoggerPlugin which needs to log a string in a custom way.
+   *
+   *  @return an instance of osquery::Status which indicates the success or
+   *  failure of the operation.
+   */
+  virtual Status logString(const std::string& s) = 0;
+
+  /**
+   * @brief Initialize the logger with the name of the binary and any status
+   * logs generated between program launch and logger start.
+   *
+   * The logger initialization is called once CLI flags have been parsed, the
+   * registry items are constructed, extension routes broadcasted and extension
+   * plugins discovered (as a logger may be an extension plugin) and the config
+   * has been loaded (which may include additional CLI flag-options).
+   *
+   * All of these actions may have generated VERBOSE, INFO, WARNING, or ERROR
+   * logs. The internal logging facility, Glog, collects these intermediate
+   * status logs and a customized log sink buffers them until the active
+   * osquery logger's `init` method is called.
+   *
+   * The return status of `init` is very important. If a success is returned
+   * then the Glog log sink stays active and now forwards every status log
+   * to the logger's `logStatus` method. If a failure is returned this means
+   * the logger does not support status logging and Glog should continue
+   * as the only status log sink.
+   *
+   * @param binary_name The string name of the process (argv[0]).
+   * @param log The set of status (INFO, WARNING, ERROR) logs generated before
+   * the logger's `init` method was called.
+   * @return Status success if the logger will continue to handle status logs
+   * using `logStatus` or failure if status logging is not supported.
+   */
+  virtual Status init(const std::string& binary_name,
+                      const std::vector<StatusLogLine>& log) {
+    return Status(1, "Status logs are not supported by this logger");
+  }
+
+  /**
+   * @brief If the active logger's `init` method returned success then Glog
+   * log lines will be collected, and forwarded to `logStatus`.
+   *
+   * `logStatus` and `init` are tightly coupled. Glog log lines will ONLY be
+   * forwarded to `logStatus` if the logger's `init` method returned success.
+   *
+   * @param log A vector of parsed Glog log lines.
+   * @return Status non-op indicating success or failure.
+   */
+  virtual Status logStatus(const std::vector<StatusLogLine>& log) {
+    return Status(1, "Not enabled");
+  }
+
+  /**
+   * @brief Optionally handle snapshot query results separately from events.
+   *
+   * If a logger plugin wants to write snapshot query results (potentially
+   * large amounts of data) to a specific sink it should implement logSnapshot.
+   * Otherwise the serialized log item data will be forwarded to logString.
+   *
+   * @param s A special log item will complete results from a query.
+   * @return log status
+   */
+  virtual Status logSnapshot(const std::string& s) { return logString(s); }
+
+  /// An optional health logging facility.
+  virtual Status logHealth(const std::string& s) {
+    return Status(1, "Not used");
+  }
+};
+
+/// Set the verbose mode, changes Glog's sinking logic and will affect plugins.
+void setVerboseLevel();
+
+/// Start status logging to a buffer until the logger plugin is online.
+void initStatusLogger(const std::string& name);
+
+/**
+ * @brief Initialize the osquery Logger facility by dumping the buffered status
+ * logs and configurating status log forwarding.
+ *
+ * initLogger will disable the `BufferedLogSink` facility, dump any status logs
+ * emitted between process start and this init call, then configure the new
+ * logger facility to receive status logs.
+ *
+ * The `forward_all` control is used when buffering logs in extensions.
+ * It is fine if the logger facility in the core app does not want to receive
+ * status logs, but this is NOT an option in extensions/modules. All status
+ * logs must be forwarded to the core.
+ *
+ * @param name The process name.
+ * @param forward_all Override the LoggerPlugin::init forwarding decision.
+ */
+void initLogger(const std::string& name, bool forward_all = false);
+
+/**
+ * @brief Log a string using the default logger receiver.
+ *
+ * Note that this method should only be used to log results. If you'd like to
+ * log normal osquery operations, use Google Logging.
+ *
+ * @param s the string to log
+ * @param category a category/metadata key
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status logString(const std::string& message, const std::string& category);
+
+/**
+ * @brief Log a string using a specific logger receiver.
+ *
+ * Note that this method should only be used to log results. If you'd like to
+ * log normal osquery operations, use Google Logging.
+ *
+ * @param message the string to log
+ * @param category a category/metadata key
+ * @param receiver a string representing the log receiver to use
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status logString(const std::string& message,
+                 const std::string& category,
+                 const std::string& receiver);
+
+/**
+ * @brief Log results of scheduled queries to the default receiver
+ *
+ * @param item a struct representing the results of a scheduled query
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status logQueryLogItem(const QueryLogItem& item);
+
+/**
+ * @brief Log results of scheduled queries to a specified receiver
+ *
+ * @param item a struct representing the results of a scheduled query
+ * @param receiver a string representing the log receiver to use
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status logQueryLogItem(const QueryLogItem& item, const std::string& receiver);
+
+/**
+ * @brief Log raw results from a query (or a snapshot scheduled query).
+ *
+ * @param results the unmangled results from the query planner.
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status logSnapshotQuery(const QueryLogItem& item);
+
+/**
+ * @brief Log the worker's health along with health of each query.
+ *
+ * @param results the query results from the osquery schedule appended with a
+ * row of health from the worker.
+ *
+ * @return Status indicating the success or failure of the operation
+ */
+Status logHealthStatus(const QueryLogItem& item);
+
+/**
+ * @brief Sink a set of buffered status logs.
+ *
+ * When the osquery daemon uses a watcher/worker set, the watcher's status logs
+ * are accumulated in a buffered log sink. Well-performing workers should have
+ * the set of watcher status logs relayed and sent to the configured logger
+ * plugin.
+ *
+ * Status logs from extensions will be forwarded to the extension manager (core)
+ * normally, but the watcher does not receive or send registry requests.
+ * Extensions, the registry, configuration, and optional config/logger plugins
+ * are all protected as a monitored worker.
+ */
+void relayStatusLogs();
+
+/**
+ * @brief Logger plugin registry.
+ *
+ * This creates an osquery registry for "logger" which may implement
+ * LoggerPlugin. Only strings are logged in practice, and LoggerPlugin provides
+ * a helper member for transforming PluginRequest%s to strings.
+ */
+CREATE_REGISTRY(LoggerPlugin, "logger");
+}
diff --git a/src/osquery/include/osquery/notification.h b/src/osquery/include/osquery/notification.h
new file mode 100644 (file)
index 0000000..4f33588
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+
+
+/**
+ * @file notification.h
+ * @brief Notify to registered stuffs when event-callback called
+ */
+
+
+#pragma once
+
+#include <map>
+#include <vector>
+
+#include <osquery_manager.h>
+
+#include <osquery/database.h>
+#include <osquery/status.h>
+#include <osquery/registry.h>
+
+namespace osquery {
+
+using NotifyCallback = Callback;
+
+class Notification final {
+public:
+       static Notification& instance();
+
+       Status add(const std::string& table, const NotifyCallback& callback);
+       Status emit(const std::string& table, const Row& result) const;
+
+public:
+       Notification(const Notification&) = delete;
+       Notification& operator=(const Notification&) = delete;
+
+private:
+       Notification() = default;
+       ~Notification() = default;
+
+       std::multimap<std::string, NotifyCallback> callbacks;
+};
+
+} // namespace osquery
diff --git a/src/osquery/include/osquery/registry.h b/src/osquery/include/osquery/registry.h
new file mode 100644 (file)
index 0000000..1ea4587
--- /dev/null
@@ -0,0 +1,737 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <mutex>
+#include <vector>
+#include <set>
+
+#include <boost/noncopyable.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/core.h>
+
+namespace osquery {
+
+/**
+ * @brief A boilerplate code helper to create a registry given a name and
+ * plugin base class type.
+ *
+ * Registries are types of plugins, e.g., config, logger, table. They are
+ * defined with a string name and Plugin derived class. There is an expectation
+ * that any 'item' registered will inherit from the registry plugin-derived
+ * type. But there is NO type enforcement on that intermediate class.
+ *
+ * This boilerplate macro puts the registry into a 'registry' namespace for
+ * organization and create a global const int that may be instantiated
+ * in a header or implementation code without symbol duplication.
+ * The initialization is also boilerplate, whereas the Registry::create method
+ * (a whole-process-lived single instance object) creates and manages the
+ * registry instance.
+ *
+ * @param type A typename that derives from Plugin.
+ * @param name A string identifier for the registry.
+ */
+#define CREATE_REGISTRY(type, name)              \
+  namespace registry {                           \
+  __constructor__ static void type##Registry() { \
+    Registry::create<type>(name);                \
+  }                                              \
+  }
+
+/**
+ * @brief A boilerplate code helper to create a registry given a name and
+ * plugin base class type. This 'lazy' registry does not run
+ * Plugin::setUp on its items, so the registry will do it.
+ *
+ * @param type A typename that derives from Plugin.
+ * @param name A string identifier for the registry.
+ */
+#define CREATE_LAZY_REGISTRY(type, name)         \
+  namespace registry {                           \
+  __constructor__ static void type##Registry() { \
+    Registry::create<type>(name, true);          \
+  }                                              \
+  }
+
+/**
+ * @brief A boilerplate code helper to register a plugin.
+ *
+ * Like CREATE_REGISTRY, REGISTER creates a boilerplate global instance to
+ * create an instance of the plugin type within the whole-process-lived registry
+ * single instance. Registry items must derive from the `RegistryType` defined
+ * by the CREATE_REGISTRY and Registry::create call.
+ *
+ * @param type A typename that derives from the RegistryType.
+ * @param registry The string name for the registry.
+ * @param name A string identifier for this registry item.
+ */
+#define REGISTER(type, registry, name)               \
+  __constructor__ static void type##RegistryItem() { \
+    Registry::add<type>(registry, name);             \
+  }
+
+/// The same as REGISTER but prevents the plugin item from being broadcasted.
+#define REGISTER_INTERNAL(type, registry, name)      \
+  __constructor__ static void type##RegistryItem() { \
+    Registry::add<type>(registry, name, true);       \
+  }
+
+/**
+ * @brief The request part of a plugin (registry item's) call.
+ *
+ * To use a plugin use Registry::call with a request and response.
+ * The request portion is usually simple and normally includes an "action"
+ * key where the value is the action you want to perform on the plugin.
+ * Refer to the registry's documentation for the actions supported by
+ * each of its plugins.
+ */
+typedef std::map<std::string, std::string> PluginRequest;
+/**
+ * @brief The response part of a plugin (registry item's) call.
+ *
+ * If a Registry::call succeeds it will fill in a PluginResponse.
+ * This response is a vector of key value maps.
+ */
+typedef std::vector<PluginRequest> PluginResponse;
+
+/// Registry routes are a map of item name to each optional PluginReponse.
+typedef std::map<std::string, PluginResponse> RegistryRoutes;
+/// An extension or core's broadcast includes routes from every Registry.
+typedef std::map<std::string, RegistryRoutes> RegistryBroadcast;
+
+typedef uint16_t RouteUUID;
+typedef std::function<Status(const std::string&, const PluginResponse&)>
+    AddExternalCallback;
+typedef std::function<void(const std::string&)> RemoveExternalCallback;
+
+/// When a module is being initialized its information is kept in a transient
+/// RegistryFactory lookup location.
+struct ModuleInfo {
+  std::string path;
+  std::string name;
+  std::string version;
+  std::string sdk_version;
+};
+
+/// The call-in prototype for Registry modules.
+typedef void (*ModuleInitalizer)(void);
+
+template <class PluginItem>
+class PluginFactory {};
+
+class Plugin : private boost::noncopyable {
+ public:
+  Plugin() : name_("unnamed") {}
+  virtual ~Plugin() {}
+
+ public:
+  /// The plugin may perform some initialization, not required.
+  virtual Status setUp() { return Status(0, "Not used"); }
+
+  /// The plugin may perform some tear down, release, not required.
+  virtual void tearDown() {}
+
+  /// The plugin may publish route info (other than registry type and name).
+  virtual PluginResponse routeInfo() const {
+    PluginResponse info;
+    return info;
+  }
+
+  /**
+   * @brief Plugins act by being called, using a request, returning a response.
+   *
+   * The plugin request is a thrift-serializable object. A response is optional
+   * but the API for using a plugin's call is defined by the registry. In most
+   * cases there are multiple supported call 'actions'. A registry type, or
+   * the plugin class, will define the action key and supported actions.
+   *
+   * @param request A plugin request input, including optional action.
+   * @param response A plugin response output.
+   *
+   * @return Status of the call, if the action was handled corrected.
+   */
+  virtual Status call(const PluginRequest& request, PluginResponse& response) {
+    return Status(0, "Not used");
+  }
+
+  // Set the output request key to a serialized property tree.
+  // Used by the plugin to set a serialized PluginResponse.
+  static void setResponse(const std::string& key,
+                          const boost::property_tree::ptree& tree,
+                          PluginResponse& response);
+
+  // Get a PluginResponse key as a property tree.
+  static void getResponse(const std::string& key,
+                          const PluginResponse& response,
+                          boost::property_tree::ptree& tree);
+
+  /// Allow the plugin to introspect into the registered name (for logging).
+  void setName(const std::string& name) { name_ = name; }
+
+  const std::string& getName() const { return name_; }
+
+  /// Allow a specialized plugin type to act when an external plugin is
+  /// registered (e.g., a TablePlugin will attach the table name).
+  static Status addExternal(const std::string& name,
+                            const PluginResponse& info) {
+    return Status(0, "Not used");
+  }
+
+  /// Allow a specialized plugin type to act when an external plugin is removed.
+  static void removeExternal(const std::string& name) {}
+
+ protected:
+  std::string name_;
+
+ private:
+  Plugin(Plugin const&);
+  Plugin& operator=(Plugin const&);
+};
+
+class RegistryHelperCore : private boost::noncopyable {
+ public:
+  explicit RegistryHelperCore(bool auto_setup = false)
+      : auto_setup_(auto_setup) {}
+  virtual ~RegistryHelperCore() {}
+
+  /**
+   * @brief Remove a registry item by its identifier.
+   *
+   * @param item_name An identifier for this registry plugin.
+   */
+  void remove(const std::string& item_name);
+
+  RegistryRoutes getRoutes() const;
+
+  /**
+   * @brief The only method a plugin user should call.
+   *
+   * Registry plugins are used internally and externally. They may belong
+   * to the process making the call or to an external process via a thrift
+   * transport.
+   *
+   * All plugin input and output must be serializable. The plugin types
+   * RegistryType usually exposes protected serialization methods for the
+   * data structures used by plugins (registry items).
+   *
+   * @param item_name The plugin identifier to call.
+   * @param request The plugin request, usually containing an action request.
+   * @param response If successful, the requested information.
+   * @return Success if the plugin was called, and response was filled.
+   */
+  virtual Status call(const std::string& item_name,
+                      const PluginRequest& request,
+                      PluginResponse& response);
+
+  Status add(const std::string& item_name, bool internal = false);
+
+  /**
+   * @brief Allow a plugin to perform some setup functions when osquery starts.
+   *
+   * Doing work in a plugin constructor has unknown behavior. Plugins may
+   * be constructed at anytime during osquery's life, including global variable
+   * instantiation. To have a reliable state (aka, flags have been parsed,
+   * and logs are ready to stream), do construction work in Plugin::setUp.
+   *
+   * The registry `setUp` will iterate over all of its registry items and call
+   * their setup unless the registry is lazy (see CREATE_REGISTRY).
+   */
+  virtual void setUp();
+
+  /// Facility method to check if a registry item exists.
+  bool exists(const std::string& item_name, bool local = false) const;
+
+  /// Create a registry item alias for a given item name.
+  Status addAlias(const std::string& item_name, const std::string& alias);
+
+  /// Get the registry item name for a given alias.
+  const std::string& getAlias(const std::string& alias) const;
+
+  /// Facility method to list the registry item identifiers.
+  std::vector<std::string> names() const;
+
+  /// Facility method to count the number of items in this registry.
+  size_t count() const;
+
+  /// Allow the registry to introspect into the registered name (for logging).
+  void setName(const std::string& name);
+
+  /// Allow others to introspect into the registered name (for reporting).
+  const std::string& getName() const { return name_; }
+
+  /// Check if a given plugin name is considered internal.
+  bool isInternal(const std::string& item_name) const;
+
+  /// Allow others to introspect into the routes from extensions.
+  const std::map<std::string, RouteUUID>& getExternal() const {
+    return external_;
+  }
+
+  /// Set an 'active' plugin to receive registry calls when no item name given.
+  Status setActive(const std::string& item_name);
+
+  /// Get the 'active' plugin, return success with the active plugin name.
+  const std::string& getActive() const;
+
+ protected:
+  /// The identifier for this registry, used to register items.
+  std::string name_;
+  /// Does this registry run setUp on each registry item at initialization.
+  bool auto_setup_;
+
+ protected:
+  /// A map of registered plugin instances to their registered identifier.
+  std::map<std::string, std::shared_ptr<Plugin> > items_;
+  /// If aliases are used, a map of alias to item name.
+  std::map<std::string, std::string> aliases_;
+  /// Keep a lookup of the external item name to assigned extension UUID.
+  std::map<std::string, RouteUUID> external_;
+  /// Keep a lookup of optional route info. The plugin may handle calls
+  /// to external items differently.
+  std::map<std::string, PluginResponse> routes_;
+  /// Keep a lookup of registry items that are blacklisted from broadcast.
+  std::vector<std::string> internal_;
+  /// Support an 'active' mode where calls without a specific item name will
+  /// be directed to the 'active' plugin.
+  std::string active_;
+  /// If a module was initialized/declared then store lookup information.
+  std::map<std::string, RouteUUID> modules_;
+};
+
+/**
+ * @brief The core interface for each registry type.
+ *
+ * The osquery Registry is partitioned into types. These are literal types
+ * but use a canonical string key for lookups and actions.
+ * Registries are created using Registry::create with a RegistryType and key.
+ */
+template <class RegistryType>
+class RegistryHelper : public RegistryHelperCore {
+ protected:
+  typedef std::shared_ptr<RegistryType> RegistryTypeRef;
+
+ public:
+  explicit RegistryHelper(bool auto_setup = false)
+      : RegistryHelperCore(auto_setup),
+        add_(&RegistryType::addExternal),
+        remove_(&RegistryType::removeExternal) {}
+  virtual ~RegistryHelper() {}
+
+  /**
+   * @brief Add a set of item names broadcasted by an extension uuid.
+   *
+   * When an extension is registered the RegistryFactory will receive a
+   * RegistryBroadcast containing a all of the extension's registry names and
+   * the set of items with their optional route info. The factory depends on
+   * each registry to manage calls/requests to these external plugins.
+   *
+   * @param uuid The uuid chosen for the extension.
+   * @param routes The plugin name and optional route info list.
+   * @return Success if all routes were added, failure if any failed.
+   */
+  Status addExternal(const RouteUUID& uuid, const RegistryRoutes& routes) {
+    // Add each route name (item name) to the tracking.
+    for (const auto& route : routes) {
+      // Keep the routes info assigned to the registry.
+      routes_[route.first] = route.second;
+      auto status = add_(route.first, route.second);
+      external_[route.first] = uuid;
+      if (!status.ok()) {
+        return status;
+      }
+    }
+    return Status(0, "OK");
+  }
+
+  /// Remove all the routes for a given uuid.
+  void removeExternal(const RouteUUID& uuid) {
+    std::vector<std::string> removed_items;
+    for (const auto& item : external_) {
+      if (item.second == uuid) {
+        remove_(item.first);
+        removed_items.push_back(item.first);
+      }
+    }
+
+    // Remove items belonging to the external uuid.
+    for (const auto& item : removed_items) {
+      external_.erase(item);
+      routes_.erase(item);
+    }
+  }
+
+  /**
+   * @brief Add a plugin to this registry by allocating and indexing
+   * a type Item and a key identifier.
+   *
+   * @code{.cpp}
+   *   /// Instead of calling RegistryFactory::add use:
+   *   REGISTER(Type, "registry_name", "item_name");
+   * @endcode
+   *
+   * @param item_name An identifier for this registry plugin.
+   * @return A success/failure status.
+   */
+  template <class Item>
+  Status add(const std::string& item_name, bool internal = false) {
+    if (items_.count(item_name) > 0) {
+      return Status(1, "Duplicate registry item exists: " + item_name);
+    }
+
+    // Cast the specific registry-type derived item as the API type of the
+    // registry used when created using the registry factory.
+    std::shared_ptr<RegistryType> item((RegistryType*)new Item());
+    item->setName(item_name);
+    items_[item_name] = item;
+    return RegistryHelperCore::add(item_name, internal);
+  }
+
+  /**
+   * @brief A raw accessor for a registry plugin.
+   *
+   * If there is no plugin with an item_name identifier this will throw
+   * and out_of_range exception.
+   *
+   * @param item_name An identifier for this registry plugin.
+   * @return A std::shared_ptr of type RegistryType.
+   */
+  RegistryTypeRef get(const std::string& item_name) const {
+    return std::dynamic_pointer_cast<RegistryType>(items_.at(item_name));
+  }
+
+  const std::map<std::string, RegistryTypeRef> all() const {
+    std::map<std::string, RegistryTypeRef> ditems;
+    for (const auto& item : items_) {
+      ditems[item.first] = std::dynamic_pointer_cast<RegistryType>(item.second);
+    }
+
+    return ditems;
+  }
+
+ private:
+  RegistryHelper(RegistryHelper const&);
+  void operator=(RegistryHelper const&);
+  AddExternalCallback add_;
+  RemoveExternalCallback remove_;
+};
+
+/// Helper defintion for a shared pointer to a Plugin.
+typedef std::shared_ptr<Plugin> PluginRef;
+/// Helper definition for a basic-templated Registry type using a base Plugin.
+typedef RegistryHelper<Plugin> PluginRegistryHelper;
+/// Helper definitions for a shared pointer to the basic Registry type.
+typedef std::shared_ptr<PluginRegistryHelper> PluginRegistryHelperRef;
+
+/**
+ * @basic A workflow manager for opening a module path and appending to the
+ * core registry.
+ *
+ * osquery Registry modules are part of the extensions API, in that they use
+ * the osquery SDK to expose additional features to the osquery core. Modules
+ * do not require the Thrift interface and may be compiled as shared objects
+ * and loaded late at run time once the core and internal registry has been
+ * initialized and setUp.
+ *
+ * A ModuleLoader interprets search paths, dynamically loads the modules,
+ * maintains identification within the RegistryFactory and any registries
+ * the module adds items into.
+ */
+class RegistryModuleLoader : private boost::noncopyable {
+ public:
+  /// Unlock the registry, open, construct, and allow the module to declare.
+  explicit RegistryModuleLoader(const std::string& path);
+  /// Keep the symbol resolution/calling out of construction.
+  void init();
+
+  /// Clear module information, 'lock' the registry.
+  ~RegistryModuleLoader();
+
+ private:
+  // Keep the handle for symbol resolution/calling.
+  void* handle_;
+  // Keep the path for debugging/logging.
+  std::string path_;
+
+ private:
+  FRIEND_TEST(RegistryTests, test_registry_modules);
+};
+
+class RegistryFactory : private boost::noncopyable {
+ public:
+  static RegistryFactory& instance() {
+    static RegistryFactory instance;
+    return instance;
+  }
+
+  /**
+   * @brief Create a registry using a plugin type and identifier.
+   *
+   * A short hard for allocating a new registry type a RegistryHelper and
+   * plugin derived class Type or RegistryType. This shorthand performs
+   * the allocation and initialization of the Type and keeps the instance
+   * identified by registry_name.
+   *
+   * @code{.cpp}
+   *   /// Instead of calling RegistryFactory::create use:
+   *   CREATE_REGISTRY(Type, "registry_name");
+   * @endcode
+   *
+   * @param registry_name The canonical name for this registry.
+   * @param auto_setup Set true if the registry does not setup itself
+   * @return A non-sense int that must be casted const.
+   */
+  template <class Type>
+  static int create(const std::string& registry_name, bool auto_setup = false) {
+    if (locked() || instance().registries_.count(registry_name) > 0) {
+      return 0;
+    }
+
+    PluginRegistryHelperRef registry(
+        (PluginRegistryHelper*)new RegistryHelper<Type>(auto_setup));
+    registry->setName(registry_name);
+    instance().registries_[registry_name] = registry;
+    return 0;
+  }
+
+  /// Direct access to a registry instance.
+  static PluginRegistryHelperRef registry(const std::string& registry_name);
+
+  /**
+   * @brief Add (implies create) a Plugin to a registry.
+   *
+   * REGISTER and REGISTER_INTERNAL are helper macros for `add` usage.
+   *
+   * @code{.cpp}
+   *  /// Instead of calling RegistryFactor::add use:
+   *  REGISTER(Type, "registry_name", "plugin_name");
+   * @endcode
+   *
+   * @param registry_name The canonical name for this registry.
+   * @param item_name The canonical name for this plugin. Specific registries
+   * may apply specialized use of the plugin name, such as table.
+   * @param internal True if this plugin should not be broadcasted externally.
+   */
+  template <class Item>
+  static Status add(const std::string& registry_name,
+                    const std::string& item_name,
+                    bool internal = false) {
+    if (!locked()) {
+      auto registry = instance().registry(registry_name);
+      return registry->template add<Item>(item_name, internal);
+    }
+    return Status(0, "Registry locked");
+  }
+
+  /// Direct access to all registries.
+  static const std::map<std::string, PluginRegistryHelperRef>& all();
+
+  /// Direct access to all plugin instances for a given registry name.
+  static const std::map<std::string, PluginRef> all(
+      const std::string& registry_name);
+
+  /// Direct access to a plugin instance.
+  static PluginRef get(const std::string& registry_name,
+                       const std::string& item_name);
+
+  /// Serialize this core or extension's registry.
+  static RegistryBroadcast getBroadcast();
+
+  /// Add external registry items identified by a Route UUID.
+  static Status addBroadcast(const RouteUUID& uuid,
+                             const RegistryBroadcast& broadcast);
+
+  /// Given an extension UUID remove all external registry items.
+  static Status removeBroadcast(const RouteUUID& uuid);
+
+  /// Adds an alias for an internal registry item. This registry will only
+  /// broadcast the alias name.
+  static Status addAlias(const std::string& registry_name,
+                         const std::string& item_name,
+                         const std::string& alias);
+
+  /// Returns the item_name or the item alias if an alias exists.
+  static const std::string& getAlias(const std::string& registry_name,
+                                     const std::string& alias);
+
+  /**
+   * @brief Call a registry item.
+   *
+   * Registry 'calling' is the primary interaction osquery has with the Plugin
+   * APIs, which register items. Each item is an instance of a specialized
+   * Plugin, whose life/scope is maintained by the specific registry identified
+   * by a unique name.
+   *
+   * The specialized plugin type will expose a `call` method that parses a
+   * PluginRequest then perform some action and return a PluginResponse.
+   * Each registry provides a `call` method that performs the registry item
+   * (Plugin instance) look up, and passes and retrieves the request and
+   * response.
+   *
+   * @param registry_name The unique registry name containing item_name,
+   * @param item_name The name of the plugin used to REGISTER.
+   * @param request The PluginRequest object handled by the Plugin item.
+   * @param response The output.
+   * @return A status from the Plugin.
+   */
+  static Status call(const std::string& registry_name,
+                     const std::string& item_name,
+                     const PluginRequest& request,
+                     PluginResponse& response);
+
+  /// A helper call that does not return a response (only status).
+  static Status call(const std::string& registry_name,
+                     const std::string& item_name,
+                     const PluginRequest& request);
+
+  /// A helper call that uses the active plugin (if the registry has one).
+  static Status call(const std::string& registry_name,
+                     const PluginRequest& request,
+                     PluginResponse& response);
+
+  /// A helper call that uses the active plugin (if the registry has one).
+  static Status call(const std::string& registry_name,
+                     const PluginRequest& request);
+
+  /// Set a registry's active plugin.
+  static Status setActive(const std::string& registry_name,
+                          const std::string& item_name);
+
+  /// Get a registry's active plugin.
+  static const std::string& getActive(const std::string& registry_nane);
+
+  /// Run `setUp` on every registry that is not marked 'lazy'.
+  static void setUp();
+
+  /// Check if a registry item exists, optionally search only local registries.
+  static bool exists(const std::string& registry_name,
+                     const std::string& item_name,
+                     bool local = false);
+
+  /// Get a list of the registry names.
+  static std::vector<std::string> names();
+
+  /// Get a list of the registry item names for a given registry.
+  static std::vector<std::string> names(const std::string& registry_name);
+
+  /// Get a list of the registered extension UUIDs.
+  static std::vector<RouteUUID> routeUUIDs();
+
+  /// Return the number of registries.
+  static size_t count();
+
+  /// Return the number of registry items for a given registry name.
+  static size_t count(const std::string& registry_name);
+
+  /// Enable/disable duplicate registry item support using aliasing.
+  static void allowDuplicates(bool allow) {
+    instance().allow_duplicates_ = allow;
+  }
+
+  /// Check if duplicate registry items using registry aliasing are allowed.
+  static bool allowDuplicates() { return instance().allow_duplicates_; }
+
+  /// Declare a module for initialization and subsequent registration attempts
+  static void declareModule(const std::string& name,
+                            const std::string& version,
+                            const std::string& min_sdk_version,
+                            const std::string& sdk_version);
+
+  /// Access module metadata.
+  static const std::map<RouteUUID, ModuleInfo>& getModules();
+
+  /// Set the registry external (such that internal events are forwarded).
+  /// Once set external, it should not be unset.
+  static void setExternal() { instance().external_ = true; }
+
+  /// Get the registry external status.
+  static bool external() { return instance().external_; }
+
+ private:
+  /// Access the current initializing module UUID.
+  static RouteUUID getModule();
+
+  /// Check if the registry is allowing module registrations.
+  static bool usingModule();
+
+  /// Initialize a module for lookup, resolution, and its registrations.
+  static void initModule(const std::string& path);
+
+  static void shutdownModule();
+
+  /// Check if the registries are locked.
+  static bool locked() { return instance().locked_; }
+
+  /// Set the registry locked status.
+  static void locked(bool locked) { instance().locked_ = locked; }
+
+ protected:
+  RegistryFactory()
+      : allow_duplicates_(false),
+        locked_(false),
+        module_uuid_(0),
+        external_(false) {}
+  RegistryFactory(RegistryFactory const&);
+  RegistryFactory& operator=(RegistryFactory const&);
+  virtual ~RegistryFactory() {}
+
+ private:
+  /// Track duplicate registry item support, used for testing.
+  bool allow_duplicates_;
+  /// Track registry "locking", while locked a registry cannot add/create.
+  bool locked_;
+
+  /// The primary storage for constructed registries.
+  std::map<std::string, PluginRegistryHelperRef> registries_;
+  /**
+   * @brief The registry tracks the set of active extension routes.
+   *
+   * If an extension dies (the process ends or does not respond to a ping),
+   * the registry will be notified via the extension watcher.
+   * When an operation requests to use that extension route the extension
+   * manager will lazily check the registry for changes.
+   */
+  std::set<RouteUUID> extensions_;
+
+  /**
+   * @brief The registry tracks loaded extension module metadata/info.
+   *
+   * Each extension module is assigned a transient RouteUUID for identification
+   * those route IDs are passed to each registry to identify which plugin
+   * items belong to modules, similarly to extensions.
+   */
+  std::map<RouteUUID, ModuleInfo> modules_;
+
+  /// During module initialization store the current-working module ID.
+  RouteUUID module_uuid_;
+  /// Calling startExtension should declare the registry external.
+  /// This will cause extension-internal events to forward to osquery core.
+  bool external_;
+
+ private:
+  friend class RegistryHelperCore;
+  friend class RegistryModuleLoader;
+  FRIEND_TEST(RegistryTests, test_registry_modules);
+};
+
+/**
+ * @brief The osquery Registry, refer to RegistryFactory for the caller API.
+ *
+ * The Registry class definition constructs the RegistryFactory behind the
+ * scenes using a class definition template API call Plugin.
+ * Each registry created by the RegistryFactory using RegistryFactory::create
+ * will provide a plugin type called RegistryType that inherits from Plugin.
+ * The actual plugins must add themselves to a registry type and should
+ * implement the Plugin and RegistryType interfaces.
+ */
+class Registry : public RegistryFactory {};
+}
diff --git a/src/osquery/include/osquery/sdk.h b/src/osquery/include/osquery/sdk.h
new file mode 100644 (file)
index 0000000..0868299
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#ifndef OSQUERY_BUILD_SDK
+#define OSQUERY_BUILD_SDK
+#endif
+
+#include <osquery/config.h>
+#include <osquery/core.h>
+#include <osquery/database.h>
+#include <osquery/events.h>
+#include <osquery/extensions.h>
+#include <osquery/filesystem.h>
+#include <osquery/flags.h>
+#include <osquery/hash.h>
+#include <osquery/logger.h>
+#include <osquery/registry.h>
+#include <osquery/sql.h>
+#include <osquery/status.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+/**
+ * @brief Create the external SQLite implementation wrapper.
+ *
+ * Anything built with only libosquery and not the 'additional' library will
+ * not include a native SQL implementation. This applies to extensions and
+ * separate applications built with the osquery SDK.
+ *
+ * The ExternalSQLPlugin is a wrapper around the SQLite API, which forwards
+ * calls to an osquery extension manager (core).
+ */
+REGISTER_INTERNAL(ExternalSQLPlugin, "sql", "sql");
+
+/**
+ * @brief Mimic the REGISTER macro, extensions should use this helper.
+ *
+ * The SDK does not provide a REGISTER macro for modules or extensions.
+ * Tools built with the osquery SDK should use REGISTER_EXTERNAL to add to
+ * their own 'external' registry. This registry will broadcast to the osquery
+ * extension manager (core) in an extension.
+ *
+ * osquery 'modules' should not construct their plugin registrations in
+ * global scope (global construction time). Instead they should use the
+ * module call-in well defined symbol, declare their SDK constraints, then
+ * use the REGISTER_MODULE call within `initModule`.
+ */
+#define REGISTER_EXTERNAL(type, registry, name)                            \
+  __attribute__((constructor)) static void type##ExtensionRegistryItem() { \
+    Registry::add<type>(registry, name);                                   \
+  }
+
+/// Helper macro to write the `initModule` symbol without rewrites.
+#define DECLARE_MODULE(name)        \
+  extern "C" void initModule(void); \
+  __attribute__((constructor)) static void declareModule()
+
+/**
+ * @brief Create an osquery extension 'module'.
+ *
+ * This helper macro creates a constructor to declare an osquery module is
+ * loading. The osquery registry is set up when modules (shared objects) are
+ * discovered via search paths and opened. At that phase the registry is locked
+ * meaning no additional plugins can be registered. To unlock the registry
+ * for modifications a module must call Registry::declareModule. This declares
+ * and any plugins added will use the metadata in the declare to determine:
+ *  - The name of the module adding the plugin
+ *  - The SDK version the module was built with, to determine compatibility
+ *  - The minimum SDK the module requires from osquery core
+ *
+ * The registry is again locked when the module load is complete and a well
+ * known module-exported symbol is called.
+ */
+#define CREATE_MODULE(name, version, min_sdk_version)         \
+  DECLARE_MODULE(name) {                                      \
+    Registry::declareModule(                                  \
+        name, version, min_sdk_version, OSQUERY_SDK_VERSION); \
+  }
+
+/**
+ * @brief Create an osquery extension 'module', if an expression is true.
+ *
+ * This is a helper testing wrapper around CREATE_MODULE and DECLARE_MODULE.
+ * It allows unit and integration tests to generate global construction code
+ * that depends on data/variables available during global construction.
+ *
+ * And example use includes checking if a process environment variable is
+ * defined. If defined the module is declared.
+ */
+#define CREATE_MODULE_IF(expr, name, version, min_sdk_version)  \
+  DECLARE_MODULE(name) {                                        \
+    if ((expr)) {                                               \
+      Registry::declareModule(                                  \
+          name, version, min_sdk_version, OSQUERY_SDK_VERSION); \
+    }                                                           \
+  }
+
+/// Helper replacement for REGISTER, used within extension modules.
+#define REGISTER_MODULE(type, registry, name) \
+  auto type##ModuleRegistryItem = Registry::add<type>(registry, name)
+
+// Remove registry-helper macros from the SDK.
+#undef REGISTER
+#define REGISTER "Do not REGISTER in the osquery SDK"
+#undef REGISTER_INTERNAL
+#define REGISTER_INTERNAL "Do not REGISTER_INTERNAL in the osquery SDK"
+#undef CREATE_REGISTRY
+#define CREATE_REGISTRY "Do not CREATE_REGISTRY in the osquery SDK"
+#undef CREATE_LAZY_REGISTRY
+#define CREATE_LAZY_REGISTRY "Do not CREATE_LAZY_REGISTRY in the osquery SDK"
+}
diff --git a/src/osquery/include/osquery/sql.h b/src/osquery/include/osquery/sql.h
new file mode 100644 (file)
index 0000000..b8d0f19
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <osquery/database.h>
+#include <osquery/flags.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+
+DECLARE_int32(value_max);
+
+/**
+ * @brief The core interface to executing osquery SQL commands
+ *
+ * @code{.cpp}
+ *   auto sql = SQL("SELECT * FROM time");
+ *   if (sql.ok()) {
+ *     LOG(INFO) << "============================";
+ *     for (const auto& row : sql.rows()) {
+ *       for (const auto& it : row) {
+ *         LOG(INFO) << it.first << " => " << it.second;
+ *       }
+ *       LOG(INFO) << "============================";
+ *     }
+ *   } else {
+ *     LOG(ERROR) << sql.getMessageString();
+ *   }
+ * @endcode
+ */
+class SQL {
+ public:
+  /**
+   * @brief Instantiate an instance of the class with a query
+   *
+   * @param q An osquery SQL query
+   */
+  explicit SQL(const std::string& q);
+
+  /**
+   * @brief Accessor for the rows returned by the query
+   *
+   * @return A QueryData object of the query results
+   */
+  const QueryData& rows();
+
+  /**
+   * @brief Accessor to switch off of when checking the success of a query
+   *
+   * @return A bool indicating the success or failure of the operation
+   */
+  bool ok();
+
+  /**
+   * @brief Get the status returned by the query
+   *
+   * @return The query status
+   */
+  Status getStatus();
+
+  /**
+   * @brief Accessor for the message string indicating the status of the query
+   *
+   * @return The message string indicating the status of the query
+   */
+  std::string getMessageString();
+
+  /**
+   * @brief Add host info columns onto existing QueryData
+   *
+   * Use this to add columns providing host info to the query results.
+   * Distributed queries use this to add host information before returning
+   * results to the aggregator.
+   */
+  void annotateHostInfo();
+
+  /**
+   * @brief Accessor for the list of queryable tables
+   *
+   * @return A vector of table names
+   */
+  static std::vector<std::string> getTableNames();
+
+  /**
+   * @brief Get all, 'SELECT * ...', results given a virtual table name.
+   *
+   * @param table The name of the virtual table.
+   * @return A QueryData object of the 'SELECT *...' query results.
+   */
+  static QueryData selectAllFrom(const std::string& table);
+
+  /**
+   * @brief Get all with constraint, 'SELECT * ... where', results given
+   * a virtual table name and single constraint
+   *
+   * @param table The name of the virtual table.
+   * @param column Table column name to apply constraint.
+   * @param op The SQL comparitive operator.
+   * @param expr The constraint expression.
+   * @return A QueryData object of the 'SELECT *...' query results.
+   */
+  static QueryData selectAllFrom(const std::string& table,
+                                 const std::string& column,
+                                 ConstraintOperator op,
+                                 const std::string& expr);
+
+ protected:
+  /**
+   * @brief Private default constructor
+   *
+   * The osquery::SQL class should only ever be instantiated with a query
+   */
+  SQL(){};
+
+  // The key used to store hostname for annotateHostInfo
+  static const std::string kHostColumnName;
+
+  /// the internal member which holds the results of the query
+  QueryData results_;
+
+  /// the internal member which holds the status of the query
+  Status status_;
+};
+
+/**
+ * @brief The osquery SQL implementation is managed as a plugin.
+ *
+ * The osquery RegistryFactory creates a Registry type called "sql", then
+ * requires a single plugin registration also called "sql". Calls within
+ * the application use boilerplate methods that wrap Registry::call%s to this
+ * well-known registry and registry item name.
+ *
+ * Abstracting the SQL implementation behind the osquery registry allows
+ * the SDK (libosquery) to describe how the SQL implementation is used without
+ * having dependencies on the thrird-party code.
+ *
+ * When osqueryd/osqueryi are built libosquery_additional, the library which
+ * provides the core plugins and core virtual tables, includes SQLite as
+ * the SQL implementation.
+ */
+class SQLPlugin : public Plugin {
+ public:
+  /// Run a SQL query string against the SQL implementation.
+  virtual Status query(const std::string& q, QueryData& results) const = 0;
+  /// Use the SQL implementation to parse a query string and return details
+  /// (name, type) about the columns.
+  virtual Status getQueryColumns(const std::string& q,
+                                 TableColumns& columns) const = 0;
+
+  /**
+   * @brief Attach a table at runtime.
+   *
+   * The SQL implementation plugin may need to manage how virtual tables are
+   * attached at run time. In the case of SQLite where a single DB object is
+   * managed, tables are enumerated and attached during initialization.
+   */
+  virtual Status attach(const std::string& name) {
+    return Status(0, "Not used");
+  }
+  /// Tables may be detached by name.
+  virtual void detach(const std::string& name) {}
+
+ public:
+  Status call(const PluginRequest& request, PluginResponse& response);
+};
+
+/**
+ * @brief Execute a query
+ *
+ * This is a lower-level version of osquery::SQL. Prefer to use osquery::SQL.
+ *
+ * @code{.cpp}
+ *   std::string q = "SELECT * FROM time;";
+ *   QueryData results;
+ *   auto status = query(q, results);
+ *   if (status.ok()) {
+ *     for (const auto& each : results) {
+ *       for (const auto& it : each) {
+ *         LOG(INFO) << it.first << ": " << it.second;
+ *       }
+ *     }
+ *   } else {
+ *     LOG(ERROR) << "Error: " << status.what();
+ *   }
+ * @endcode
+ *
+ * @param q the query to execute
+ * @param results A QueryData structure to emit result rows on success.
+ * @return A status indicating query success.
+ */
+Status query(const std::string& query, QueryData& results);
+
+/**
+ * @brief Analyze a query, providing information about the result columns
+ *
+ * This function asks SQLite to determine what the names and types are of the
+ * result columns of the provided query. Only table columns (not expressions or
+ * subqueries) can have their types determined. Types that are not determined
+ * are indicated with the string "UNKNOWN".
+ *
+ * @param q the query to analyze
+ * @param columns the vector to fill with column information
+ *
+ * @return status indicating success or failure of the operation
+ */
+Status getQueryColumns(const std::string& q, TableColumns& columns);
+
+/*
+ * @brief A mocked subclass of SQL useful for testing
+ */
+class MockSQL : public SQL {
+ public:
+  explicit MockSQL() : MockSQL(QueryData{}) {}
+  explicit MockSQL(const QueryData& results) : MockSQL(results, Status()) {}
+  explicit MockSQL(const QueryData& results, const Status& status) {
+    results_ = results;
+    status_ = status;
+  }
+};
+
+CREATE_LAZY_REGISTRY(SQLPlugin, "sql");
+}
diff --git a/src/osquery/include/osquery/status.h b/src/osquery/include/osquery/status.h
new file mode 100644 (file)
index 0000000..185a14a
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <sstream>
+#include <string>
+
+namespace osquery {
+
+/**
+ * @brief A utility class which is used to express the state of operations.
+ *
+ * @code{.cpp}
+ *   osquery::Status foobar() {
+ *     auto na = doSomeWork();
+ *     if (na->itWorked()) {
+ *       return osquery::Status(0, "OK");
+ *     } else {
+ *       return osquery::Status(1, na->getErrorString());
+ *     }
+ *   }
+ * @endcode
+ */
+class Status {
+ public:
+  /**
+   * @brief Default constructor
+   *
+   * Note that the default constructor initialized an osquery::Status instance
+   * to a state such that a successful operation is indicated.
+   */
+  Status() : code_(0), message_("OK") {}
+
+  /**
+   * @brief A constructor which can be used to concisely express the status of
+   * an operation.
+   *
+   * @param c a status code. The idiom is that a zero status code indicates a
+   * successful operation and a non-zero status code indicates a failed
+   * operation.
+   * @param m a message indicating some extra detail regarding the operation.
+   * If all operations were successful, this message should be "OK".
+   * Otherwise, it doesn't matter what the string is, as long as both the
+   * setter and caller agree.
+   */
+  Status(int c, std::string m) : code_(c), message_(m) {}
+
+ public:
+  /**
+   * @brief A getter for the status code property
+   *
+   * @return an integer representing the status code of the operation.
+   */
+  int getCode() const { return code_; }
+
+  /**
+   * @brief A getter for the message property
+   *
+   * @return a string representing arbitrary additional information about the
+   * success or failure of an operation. On successful operations, the idiom
+   * is for the message to be "OK"
+   */
+  std::string getMessage() const { return message_; }
+
+  /**
+   * @brief A convenience method to check if the return code is 0
+   *
+   * @code{.cpp}
+   *   auto s = doSomething();
+   *   if (s.ok()) {
+   *     LOG(INFO) << "doing work";
+   *   } else {
+   *     LOG(ERROR) << s.toString();
+   *   }
+   * @endcode
+   *
+   * @return a boolean which is true if the status code is 0, false otherwise.
+   */
+  bool ok() const { return getCode() == 0; }
+
+  /**
+   * @brief A synonym for osquery::Status::getMessage()
+   *
+   * @see getMessage()
+   */
+  std::string toString() const { return getMessage(); }
+  std::string what() const { return getMessage(); }
+
+  /**
+   * @brief implicit conversion to bool
+   *
+   * Allows easy use of Status in an if statement, as below:
+   *
+   * @code{.cpp}
+   *   if (doSomethingThatReturnsStatus()) {
+   *     LOG(INFO) << "Success!";
+   *   }
+   * @endcode
+   */
+  operator bool() const { return ok(); }
+
+  // Below operator implementations useful for testing with gtest
+
+  // Enables use of gtest (ASSERT|EXPECT)_EQ
+  bool operator==(const Status& rhs) const {
+    return (code_ == rhs.getCode()) && (message_ == rhs.getMessage());
+  }
+
+  // Enables use of gtest (ASSERT|EXPECT)_NE
+  bool operator!=(const Status& rhs) const { return !operator==(rhs); }
+
+  // Enables pretty-printing in gtest (ASSERT|EXPECT)_(EQ|NE)
+  friend ::std::ostream& operator<<(::std::ostream& os, const Status& s);
+
+ private:
+  /// the internal storage of the status code
+  int code_;
+
+  /// the internal storage of the status message
+  std::string message_;
+};
+}
diff --git a/src/osquery/include/osquery/tables.h b/src/osquery/include/osquery/tables.h
new file mode 100644 (file)
index 0000000..f2db419
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <vector>
+#include <set>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+#include <osquery/registry.h>
+#include <osquery/core.h>
+#include <osquery/database.h>
+#include <osquery/status.h>
+
+/// Allow Tables to use "tracked" deprecated OS APIs.
+#define OSQUERY_USE_DEPRECATED(expr)                                      \
+  do {                                                                    \
+    _Pragma("clang diagnostic push")                                      \
+        _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
+        expr;                                                             \
+    _Pragma("clang diagnostic pop")                                       \
+  } while (0)
+
+namespace osquery {
+
+/**
+ * @brief The SQLite type affinities are available as macros
+ *
+ * Type affinities: TEXT, INTEGER, BIGINT
+ *
+ * You can represent any data that can be lexically casted to a string.
+ * Using the type affinity names helps table developers understand the data
+ * types they are storing, and more importantly how they are treated at query
+ * time.
+ */
+#define TEXT(x) boost::lexical_cast<std::string>(x)
+/// See the affinity type documentation for TEXT.
+#define INTEGER(x) boost::lexical_cast<std::string>(x)
+/// See the affinity type documentation for TEXT.
+#define BIGINT(x) boost::lexical_cast<std::string>(x)
+/// See the affinity type documentation for TEXT.
+#define UNSIGNED_BIGINT(x) boost::lexical_cast<std::string>(x)
+/// See the affinity type documentation for TEXT.
+#define DOUBLE(x) boost::lexical_cast<std::string>(x)
+
+/**
+ * @brief The SQLite type affinities as represented as implementation literals.
+ *
+ * Type affinities: TEXT=std::string, INTEGER=int, BIGINT=long long int
+ *
+ * Just as the SQLite data is represented as lexically casted strings, as table
+ * may make use of the implementation language literals.
+ */
+#define TEXT_LITERAL std::string
+/// See the literal type documentation for TEXT_LITERAL.
+#define INTEGER_LITERAL int
+/// See the literal type documentation for TEXT_LITERAL.
+#define BIGINT_LITERAL long long int
+/// See the literal type documentation for TEXT_LITERAL.
+#define UNSIGNED_BIGINT_LITERAL unsigned long long int
+/// See the literal type documentation for TEXT_LITERAL.
+#define DOUBLE_LITERAL double
+/// Cast an SQLite affinity type to the literal type.
+#define AS_LITERAL(literal, value) boost::lexical_cast<literal>(value)
+
+/// Helper alias for TablePlugin names.
+typedef std::string TableName;
+typedef std::vector<std::pair<std::string, std::string> > TableColumns;
+typedef std::map<std::string, std::vector<std::string> > TableData;
+
+/**
+ * @brief A ConstraintOperator is applied in an query predicate.
+ *
+ * If the query contains a join or where clause with a constraint operator and
+ * expression the table generator may limit the data appropriately.
+ */
+enum ConstraintOperator : unsigned char {
+  EQUALS = 2,
+  GREATER_THAN = 4,
+  LESS_THAN_OR_EQUALS = 8,
+  LESS_THAN = 16,
+  GREATER_THAN_OR_EQUALS = 32
+};
+
+/// Type for flags for what constraint operators are admissible.
+typedef unsigned char ConstraintOperatorFlag;
+/// Flag for any operator type.
+#define ANY_OP 0xFFU
+
+/**
+ * @brief A Constraint is an operator and expression.
+ *
+ * The constraint is applied to columns which have literal and affinity types.
+ */
+struct Constraint {
+  unsigned char op;
+  std::string expr;
+
+  /// Construct a Constraint with the most-basic information, the operator.
+  explicit Constraint(unsigned char _op) { op = _op; }
+
+  // A constraint list in a context knows only the operator at creation.
+  explicit Constraint(unsigned char _op, const std::string& _expr)
+      : op(_op), expr(_expr) {}
+};
+
+/**
+ * @brief A ConstraintList is a set of constraints for a column. This list
+ * should be mapped to a left-hand-side column name.
+ *
+ * The table generator does not need to check each constraint in its decision
+ * logic. The common constraint checking patterns (match) are abstracted using
+ * simple logic operators on the literal SQLite affinity types.
+ *
+ * A constraint list supports all AS_LITERAL types, and all ConstraintOperators.
+ */
+struct ConstraintList {
+  /// The SQLite affinity type.
+  std::string affinity;
+
+  /**
+   * @brief Check if an expression matches the query constraints.
+   *
+   * Evaluate ALL constraints in this ConstraintList against the string
+   * expression. The affinity of the constraint will be used as the affinite
+   * and lexical type of the expression and set of constraint expressions.
+   * If there are no predicate constraints in this list, all expression will
+   * match. Constraints are limitations.
+   *
+   * @param expr a SQL type expression of the column literal type to check.
+   * @return If the expression matched all constraints.
+   */
+  bool matches(const std::string& expr) const;
+
+  /**
+   * @brief Check if an expression matches the query constraints.
+   *
+   * `matches` also supports the set of SQL affinite types.
+   * The expression expr will be evaluated as a string and compared using
+   * the affinity of the constraint.
+   *
+   * @param expr a SQL type expression of the column literal type to check.
+   * @return If the expression matched all constraints.
+   */
+  template <typename T>
+  bool matches(const T& expr) const {
+    return matches(TEXT(expr));
+  }
+
+  /**
+   * @brief Check and return if there are constraints on this column.
+   *
+   * A ConstraintList is used in a ConstraintMap with a column name as the
+   * map index. Tables that act on optional constraints should check if any
+   * constraint was provided.  The ops parameter serves to specify which
+   * operators we want to check existence for.
+   *
+   * @param ops (Optional: default ANY_OP) The operators types to look for.
+   * @return true if any constraint exists.
+   */
+  bool exists(const ConstraintOperatorFlag ops = ANY_OP) const {
+    if (ops == ANY_OP) {
+      return (constraints_.size() > 0);
+    } else {
+      for (const struct Constraint &c : constraints_) {
+        if (c.op & ops) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  /**
+   * @brief Check if a constraint exist AND matches the type expression.
+   *
+   * See ConstraintList::exists and ConstraintList::matches.
+   *
+   * @param expr The expression to match.
+   * @return true if any constraint exists AND matches the type expression.
+   */
+  template <typename T>
+  bool existsAndMatches(const T& expr) const {
+    return (exists() && matches(expr));
+  }
+
+  /**
+   * @brief Check if a constraint is missing or matches a type expression.
+   *
+   * A ConstraintList is used in a ConstraintMap with a column name as the
+   * map index. Tables that act on required constraints can make decisions
+   * on missing constraints or a constraint match.
+   *
+   * @param expr The expression to match.
+   * @return true if constraint is missing or matches the type expression.
+   */
+  template <typename T>
+  bool notExistsOrMatches(const T& expr) const {
+    return (!exists() || matches(expr));
+  }
+
+  /**
+   * @brief Helper templated function for ConstraintList::matches.
+   */
+  template <typename T>
+  bool literal_matches(const T& base_expr) const;
+
+  /**
+   * @brief Get all expressions for a given ConstraintOperator.
+   *
+   * This is most useful if the table generation requires as column.
+   * The generator may `getAll(EQUALS)` then iterate.
+   *
+   * @param op the ConstraintOperator.
+   * @return A list of TEXT%-represented types matching the operator.
+   */
+  std::set<std::string> getAll(ConstraintOperator op) const;
+
+  /// See ConstraintList::getAll, but as a selected literal type.
+  template<typename T>
+  std::set<T> getAll(ConstraintOperator op) const {
+    std::set<T> literal_matches;
+    auto matches = getAll(op);
+    for (const auto& match : matches) {
+      literal_matches.insert(AS_LITERAL(T, match));
+    }
+    return literal_matches;
+  }
+
+  /// Constraint list accessor, types and operator.
+  const std::vector<struct Constraint> getAll() const { return constraints_; }
+
+  /**
+   * @brief Add a new Constraint to the list of constraints.
+   *
+   * @param constraint a new operator/expression to constrain.
+   */
+  void add(const struct Constraint& constraint) {
+    constraints_.push_back(constraint);
+  }
+
+  /**
+   * @brief Serialize a ConstraintList into a property tree.
+   *
+   * The property tree will use the format:
+   * {
+   *   "affinity": affinity,
+   *   "list": [
+   *     {"op": op, "expr": expr}, ...
+   *   ]
+   * }
+   */
+  void serialize(boost::property_tree::ptree& tree) const;
+
+  /// See ConstraintList::unserialize.
+  void unserialize(const boost::property_tree::ptree& tree);
+
+  ConstraintList() : affinity("TEXT") {}
+
+ private:
+  /// List of constraint operator/expressions.
+  std::vector<struct Constraint> constraints_;
+
+ private:
+  FRIEND_TEST(TablesTests, test_constraint_list);
+};
+
+/// Pass a constraint map to the query request.
+typedef std::map<std::string, struct ConstraintList> ConstraintMap;
+/// Populate a constraint list from a query's parsed predicate.
+typedef std::vector<std::pair<std::string, struct Constraint> > ConstraintSet;
+
+/**
+ * @brief A QueryContext is provided to every table generator for optimization
+ * on query components like predicate constraints and limits.
+ */
+struct QueryContext {
+  ConstraintMap constraints;
+  /// Support a limit to the number of results.
+  int limit;
+
+  QueryContext() : limit(0) {}
+};
+
+typedef struct QueryContext QueryContext;
+typedef struct Constraint Constraint;
+
+/**
+ * @brief The TablePlugin defines the name, types, and column information.
+ *
+ * To attach a virtual table create a TablePlugin subclass and register the
+ * virtual table name as the plugin ID. osquery will enumerate all registered
+ * TablePlugins and attempt to attach them to SQLite at instantiation.
+ *
+ * Note: When updating this class, be sure to update the corresponding template
+ * in osquery/tables/templates/default.cpp.in
+ */
+class TablePlugin : public Plugin {
+ protected:
+  virtual TableColumns columns() const {
+    TableColumns columns;
+    return columns;
+  }
+
+  virtual QueryData generate(QueryContext& request) {
+    QueryData data;
+    return data;
+  }
+
+  virtual Status update(Row& row) {
+    return Status(0, "OK");
+  }
+
+ protected:
+  std::string columnDefinition() const;
+  PluginResponse routeInfo() const;
+
+ public:
+  /// Public API methods.
+  Status call(const PluginRequest& request, PluginResponse& response);
+
+ public:
+  /// Helper data structure transformation methods
+  static void setRequestFromContext(const QueryContext& context,
+                                    PluginRequest& request);
+  static void setResponseFromQueryData(const QueryData& data,
+                                       PluginResponse& response);
+  static void setContextFromRequest(const PluginRequest& request,
+                                    QueryContext& context);
+
+ public:
+  /// When external table plugins are registered the core will attach them
+  /// as virtual tables to the SQL internal implementation
+  static Status addExternal(const std::string& name,
+                            const PluginResponse& info);
+  static void removeExternal(const std::string& name);
+
+ private:
+  FRIEND_TEST(VirtualTableTests, test_tableplugin_columndefinition);
+  FRIEND_TEST(VirtualTableTests, test_tableplugin_statement);
+};
+
+/// Helper method to generate the virtual table CREATE statement.
+std::string columnDefinition(const TableColumns& columns);
+std::string columnDefinition(const PluginResponse& response);
+
+CREATE_LAZY_REGISTRY(TablePlugin, "table");
+}
diff --git a/src/osquery/logger/CMakeLists.txt b/src/osquery/logger/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a87fd66
--- /dev/null
@@ -0,0 +1,23 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_logger logger.cpp)
+ADD_OSQUERY_LIBRARY(osquery_logger_plugins plugins/filesystem.cpp
+                                                                                  plugins/syslog.cpp)
+
+FILE(GLOB OSQUERY_LOGGER_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_LOGGER_TESTS})
+
+file(GLOB OSQUERY_LOGGER_PLUGIN_TESTS "plugins/tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_LOGGER_PLUGIN_TESTS})
diff --git a/src/osquery/logger/logger.cpp b/src/osquery/logger/logger.cpp
new file mode 100644 (file)
index 0000000..f0b420b
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <algorithm>
+#include <thread>
+
+#include <boost/noncopyable.hpp>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osquery/extensions.h>
+#include <osquery/filesystem.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+namespace pt = boost::property_tree;
+
+namespace osquery {
+
+FLAG(bool, verbose, false, "Enable verbose informational messages");
+FLAG_ALIAS(bool, verbose_debug, verbose);
+FLAG_ALIAS(bool, debug, verbose);
+
+/// Despite being a configurable option, this is only read/used at load.
+FLAG(bool, disable_logging, false, "Disable ERROR/INFO logging");
+
+FLAG(string, logger_plugin, "filesystem", "Logger plugin name");
+
+FLAG(bool, log_result_events, true, "Log scheduled results as events");
+
+/**
+ * @brief A custom Glog log sink for forwarding or buffering status logs.
+ *
+ * This log sink has two modes, it can buffer Glog status logs until an osquery
+ * logger is initialized or forward Glog status logs to an initialized and
+ * appropriate logger. The appropriateness is determined by the logger when its
+ * LoggerPlugin::init method is called. If the `init` method returns success
+ * then a BufferedLogSink will start forwarding status logs to
+ * LoggerPlugin::logStatus.
+ *
+ * This facility will start buffering when first used and stop buffering
+ * (aka remove itself as a Glog sink) using the exposed APIs. It will live
+ * throughout the life of the process for two reasons: (1) It makes sense when
+ * the active logger plugin is handling Glog status logs and (2) it must remove
+ * itself as a Glog target.
+ */
+class BufferedLogSink : public google::LogSink, private boost::noncopyable {
+ public:
+  /// We create this as a Singleton for proper disable/shutdown.
+  static BufferedLogSink& instance() {
+    static BufferedLogSink sink;
+    return sink;
+  }
+
+  /// The Glog-API LogSink call-in method.
+  void send(google::LogSeverity severity,
+            const char* full_filename,
+            const char* base_filename,
+            int line,
+            const struct ::tm* tm_time,
+            const char* message,
+            size_t message_len);
+
+  /// Accessor/mutator to dump all of the buffered logs.
+  static std::vector<StatusLogLine>& dump() { return instance().logs_; }
+
+  /// Set the forwarding mode of the buffering sink.
+  static void forward(bool forward = false) { instance().forward_ = forward; }
+
+  /// Remove the buffered log sink from Glog.
+  static void disable() {
+    if (instance().enabled_) {
+      instance().enabled_ = false;
+      google::RemoveLogSink(&instance());
+    }
+  }
+
+  /// Add the buffered log sink to Glog.
+  static void enable() {
+    if (!instance().enabled_) {
+      instance().enabled_ = true;
+      google::AddLogSink(&instance());
+    }
+  }
+
+ private:
+  /// Create the log sink as buffering or forwarding.
+  BufferedLogSink() : forward_(false), enabled_(false) {}
+
+  /// Remove the log sink.
+  ~BufferedLogSink() { disable(); }
+
+  BufferedLogSink(BufferedLogSink const&);
+  void operator=(BufferedLogSink const&);
+
+ private:
+  /// Intermediate log storage until an osquery logger is initialized.
+  std::vector<StatusLogLine> logs_;
+  bool forward_;
+  bool enabled_;
+};
+
+/// Scoped helper to perform logging actions without races.
+class LoggerDisabler {
+ public:
+  LoggerDisabler() : stderr_status_(FLAGS_logtostderr) {
+    BufferedLogSink::disable();
+    FLAGS_logtostderr = true;
+  }
+
+  ~LoggerDisabler() {
+    BufferedLogSink::enable();
+    FLAGS_logtostderr = stderr_status_;
+  }
+
+ private:
+  bool stderr_status_;
+};
+
+static void serializeIntermediateLog(const std::vector<StatusLogLine>& log,
+                                     PluginRequest& request) {
+  pt::ptree tree;
+  for (const auto& log_item : log) {
+    pt::ptree child;
+    child.put("s", log_item.severity);
+    child.put("f", log_item.filename);
+    child.put("i", log_item.line);
+    child.put("m", log_item.message);
+    tree.push_back(std::make_pair("", child));
+  }
+
+  // Save the log as a request JSON string.
+  std::ostringstream output;
+  pt::write_json(output, tree, false);
+  request["log"] = output.str();
+}
+
+static void deserializeIntermediateLog(const PluginRequest& request,
+                                       std::vector<StatusLogLine>& log) {
+  if (request.count("log") == 0) {
+    return;
+  }
+
+  // Read the plugin request string into a JSON tree and enumerate.
+  pt::ptree tree;
+  try {
+    std::stringstream input;
+    input << request.at("log");
+    pt::read_json(input, tree);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    return;
+  }
+
+  for (const auto& item : tree.get_child("")) {
+    log.push_back({
+        (StatusLogSeverity)item.second.get<int>("s", O_INFO),
+        item.second.get<std::string>("f", "<unknown>"),
+        item.second.get<int>("i", 0),
+        item.second.get<std::string>("m", ""),
+    });
+  }
+}
+
+void setVerboseLevel() {
+  if (Flag::getValue("verbose") == "true") {
+    // Turn verbosity up to 1.
+    // Do log DEBUG, INFO, WARNING, ERROR to their log files.
+    // Do log the above and verbose=1 to stderr.
+    FLAGS_minloglevel = 0; // INFO
+    FLAGS_stderrthreshold = 0; // INFO
+    FLAGS_v = 1;
+  } else {
+    // Do NOT log INFO, WARNING, ERROR to stderr.
+    // Do log only WARNING, ERROR to log sinks.
+    FLAGS_minloglevel = 1; // WARNING
+    FLAGS_stderrthreshold = 1; // WARNING
+  }
+
+  if (FLAGS_disable_logging) {
+    // Do log ERROR to stderr.
+    // Do NOT log INFO, WARNING, ERROR to their log files.
+    FLAGS_logtostderr = true;
+    if (!FLAGS_verbose) {
+      // verbose flag will still emit logs to stderr.
+      FLAGS_minloglevel = 2; // ERROR
+    }
+  }
+}
+
+void initStatusLogger(const std::string& name) {
+  FLAGS_alsologtostderr = false;
+  FLAGS_logbufsecs = 0; // flush the log buffer immediately
+  FLAGS_stop_logging_if_full_disk = true;
+  FLAGS_max_log_size = 10; // max size for individual log file is 10MB
+  FLAGS_logtostderr = true;
+
+  setVerboseLevel();
+  // Start the logging, and announce the daemon is starting.
+  google::InitGoogleLogging(name.c_str());
+
+  // If logging is disabled then do not buffer intermediate logs.
+  if (!FLAGS_disable_logging) {
+    // Create an instance of the buffered log sink and do not forward logs yet.
+    BufferedLogSink::enable();
+  }
+}
+
+void initLogger(const std::string& name, bool forward_all) {
+  // Check if logging is disabled, if so then no need to shuttle intermediates.
+  if (FLAGS_disable_logging) {
+    return;
+  }
+
+  // Stop the buffering sink and store the intermediate logs.
+  BufferedLogSink::disable();
+  auto intermediate_logs = std::move(BufferedLogSink::dump());
+  auto& logger_plugin = Registry::getActive("logger");
+  if (!Registry::exists("logger", logger_plugin)) {
+    return;
+  }
+
+  // Start the custom status logging facilities, which may instruct Glog as is
+  // the case with filesystem logging.
+  PluginRequest request = {{"init", name}};
+  serializeIntermediateLog(intermediate_logs, request);
+  auto status = Registry::call("logger", request);
+  if (status.ok() || forward_all) {
+    // When LoggerPlugin::init returns success we enable the log sink in
+    // forwarding mode. Then Glog status logs are forwarded to logStatus.
+    BufferedLogSink::forward(true);
+    BufferedLogSink::enable();
+  }
+}
+
+void BufferedLogSink::send(google::LogSeverity severity,
+                           const char* full_filename,
+                           const char* base_filename,
+                           int line,
+                           const struct ::tm* tm_time,
+                           const char* message,
+                           size_t message_len) {
+  // Either forward the log to an enabled logger or buffer until one exists.
+  if (forward_) {
+    // May use the logs_ storage to buffer/delay sending logs.
+    std::vector<StatusLogLine> log;
+    log.push_back({(StatusLogSeverity)severity,
+                   std::string(base_filename),
+                   line,
+                   std::string(message, message_len)});
+    PluginRequest request = {{"status", "true"}};
+    serializeIntermediateLog(log, request);
+    Registry::call("logger", request);
+  } else {
+    logs_.push_back({(StatusLogSeverity)severity,
+                     std::string(base_filename),
+                     line,
+                     std::string(message, message_len)});
+  }
+}
+
+Status LoggerPlugin::call(const PluginRequest& request,
+                          PluginResponse& response) {
+  QueryLogItem item;
+  std::vector<StatusLogLine> intermediate_logs;
+  if (request.count("string") > 0) {
+    return this->logString(request.at("string"));
+  } else if (request.count("snapshot") > 0) {
+    return this->logSnapshot(request.at("snapshot"));
+  } else if (request.count("health") > 0) {
+    return this->logHealth(request.at("health"));
+  } else if (request.count("init") > 0) {
+    deserializeIntermediateLog(request, intermediate_logs);
+    return this->init(request.at("init"), intermediate_logs);
+  } else if (request.count("status") > 0) {
+    deserializeIntermediateLog(request, intermediate_logs);
+    return this->logStatus(intermediate_logs);
+  } else {
+    return Status(1, "Unsupported call to logger plugin");
+  }
+}
+
+Status logString(const std::string& message, const std::string& category) {
+  return logString(message, category, Registry::getActive("logger"));
+}
+
+Status logString(const std::string& message,
+                 const std::string& category,
+                 const std::string& receiver) {
+  if (!Registry::exists("logger", receiver)) {
+    LOG(ERROR) << "Logger receiver " << receiver << " not found";
+    return Status(1, "Logger receiver not found");
+  }
+
+  auto status = Registry::call(
+      "logger", receiver, {{"string", message}, {"category", category}});
+  return Status(0, "OK");
+}
+
+Status logQueryLogItem(const QueryLogItem& results) {
+  return logQueryLogItem(results, Registry::getActive("logger"));
+}
+
+Status logQueryLogItem(const QueryLogItem& results,
+                       const std::string& receiver) {
+  std::string json;
+  Status status;
+  if (FLAGS_log_result_events) {
+    status = serializeQueryLogItemAsEventsJSON(results, json);
+  } else {
+    status = serializeQueryLogItemJSON(results, json);
+  }
+  if (!status.ok()) {
+    return status;
+  }
+  return logString(json, "event", receiver);
+}
+
+Status logSnapshotQuery(const QueryLogItem& item) {
+  std::string json;
+  if (!serializeQueryLogItemJSON(item, json)) {
+    return Status(1, "Could not serialize snapshot");
+  }
+  return Registry::call("logger", {{"snapshot", json}});
+}
+
+Status logHealthStatus(const QueryLogItem& item) {
+  std::string json;
+  if (!serializeQueryLogItemJSON(item, json)) {
+    return Status(1, "Could not serialize health");
+  }
+  return Registry::call("logger", {{"health", json}});
+}
+
+void relayStatusLogs() {
+  // Prevent out dumping and registry calling from producing additional logs.
+  LoggerDisabler disabler;
+
+  // Construct a status log plugin request.
+  PluginRequest req = {{"status", "true"}};
+  auto& status_logs = BufferedLogSink::dump();
+  if (status_logs.size() == 0) {
+    return;
+  }
+
+  // Skip the registry's logic, and send directly to the core's logger.
+  PluginResponse resp;
+  serializeIntermediateLog(status_logs, req);
+  auto status = callExtension(0, "logger", FLAGS_logger_plugin, req, resp);
+  if (status.ok()) {
+    // Flush the buffered status logs.
+    // Otherwise the extension call failed and the buffering should continue.
+    status_logs.clear();
+  }
+}
+}
diff --git a/src/osquery/logger/plugins/filesystem.cpp b/src/osquery/logger/plugins/filesystem.cpp
new file mode 100644 (file)
index 0000000..6dba366
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <exception>
+#include <mutex>
+
+#include <osquery/filesystem.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+namespace pt = boost::property_tree;
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+FLAG(string,
+     logger_path,
+     "/var/log/osquery/",
+     "Directory path for ERROR/WARN/INFO and results logging");
+/// Legacy, backward compatible "osquery_log_dir" CLI option.
+FLAG_ALIAS(std::string, osquery_log_dir, logger_path);
+
+const std::string kFilesystemLoggerFilename = "osqueryd.results.log";
+const std::string kFilesystemLoggerSnapshots = "osqueryd.snapshots.log";
+const std::string kFilesystemLoggerHealth = "osqueryd.health.log";
+
+std::mutex filesystemLoggerPluginMutex;
+
+class FilesystemLoggerPlugin : public LoggerPlugin {
+ public:
+  Status setUp();
+  Status logString(const std::string& s);
+  Status logStringToFile(const std::string& s, const std::string& filename);
+  Status logSnapshot(const std::string& s);
+  Status logHealth(const std::string& s);
+  Status init(const std::string& name, const std::vector<StatusLogLine>& log);
+  Status logStatus(const std::vector<StatusLogLine>& log);
+
+ private:
+  fs::path log_path_;
+};
+
+REGISTER(FilesystemLoggerPlugin, "logger", "filesystem");
+
+Status FilesystemLoggerPlugin::setUp() {
+  log_path_ = fs::path(FLAGS_logger_path);
+  return Status(0, "OK");
+}
+
+Status FilesystemLoggerPlugin::logString(const std::string& s) {
+  return logStringToFile(s, kFilesystemLoggerFilename);
+}
+
+Status FilesystemLoggerPlugin::logStringToFile(const std::string& s,
+                                               const std::string& filename) {
+  std::lock_guard<std::mutex> lock(filesystemLoggerPluginMutex);
+  try {
+    // The results log may contain sensitive information if run as root.
+    auto status = writeTextFile((log_path_ / filename).string(), s, 0640, true);
+    if (!status.ok()) {
+      return status;
+    }
+  } catch (const std::exception& e) {
+    return Status(1, e.what());
+  }
+  return Status(0, "OK");
+}
+
+Status FilesystemLoggerPlugin::logStatus(
+    const std::vector<StatusLogLine>& log) {
+  for (const auto& item : log) {
+    // Emit this intermediate log to the Glog filesystem logger.
+    google::LogMessage(item.filename.c_str(),
+                       item.line,
+                       (google::LogSeverity)item.severity).stream()
+        << item.message;
+  }
+
+  return Status(0, "OK");
+}
+
+Status FilesystemLoggerPlugin::logSnapshot(const std::string& s) {
+  // Send the snapshot data to a separate filename.
+  return logStringToFile(s, kFilesystemLoggerSnapshots);
+}
+
+Status FilesystemLoggerPlugin::logHealth(const std::string& s) {
+  return logStringToFile(s, kFilesystemLoggerHealth);
+}
+
+Status FilesystemLoggerPlugin::init(const std::string& name,
+                                    const std::vector<StatusLogLine>& log) {
+  // Stop the internal Glog facilities.
+  google::ShutdownGoogleLogging();
+
+  // The log dir is used for status logging and the filesystem results logs.
+  if (isWritable(log_path_.string()).ok()) {
+    FLAGS_log_dir = log_path_.string();
+    FLAGS_logtostderr = false;
+  } else {
+    // If we cannot write logs to the filesystem, fallback to stderr.
+    // The caller (flags/options) might 'also' be logging to stderr using
+    // debug, verbose, etc.
+    FLAGS_logtostderr = true;
+  }
+
+  // Restart the Glog facilities using the name `init` was provided.
+  google::InitGoogleLogging(name.c_str());
+
+  // We may violate Glog global object assumptions. So set names manually.
+  auto basename = (log_path_ / name).string();
+  google::SetLogDestination(google::INFO, (basename + ".INFO.").c_str());
+  google::SetLogDestination(google::WARNING, (basename + ".WARNING.").c_str());
+  google::SetLogDestination(google::ERROR, (basename + ".ERROR.").c_str());
+
+  // Store settings for logging to stderr.
+  bool log_to_stderr = FLAGS_logtostderr;
+  bool also_log_to_stderr = FLAGS_alsologtostderr;
+  int stderr_threshold = FLAGS_stderrthreshold;
+  FLAGS_alsologtostderr = false;
+  FLAGS_logtostderr = false;
+  FLAGS_stderrthreshold = 5;
+
+  // Now funnel the intermediate status logs provided to `init`.
+  logStatus(log);
+
+  // Restore settings for logging to stderr.
+  FLAGS_logtostderr = log_to_stderr;
+  FLAGS_alsologtostderr = also_log_to_stderr;
+  FLAGS_stderrthreshold = stderr_threshold;
+
+  // The filesystem logger cheats and uses Glog to log to the filesystem so
+  // we can return failure here and stop the custom log sink.
+  return Status(1, "No status logger used for filesystem");
+}
+}
diff --git a/src/osquery/logger/plugins/syslog.cpp b/src/osquery/logger/plugins/syslog.cpp
new file mode 100644 (file)
index 0000000..d88ab70
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <syslog.h>
+
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+
+FLAG(int32,
+     logger_syslog_facility,
+     LOG_LOCAL3 >> 3,
+     "Syslog facility for status and results logs (0-23, default 19)");
+
+class SyslogLoggerPlugin : public LoggerPlugin {
+ public:
+  Status logString(const std::string& s);
+  Status init(const std::string& name, const std::vector<StatusLogLine>& log);
+  Status logStatus(const std::vector<StatusLogLine>& log);
+};
+
+REGISTER(SyslogLoggerPlugin, "logger", "syslog");
+
+Status SyslogLoggerPlugin::logString(const std::string& s) {
+  for (const auto& line : osquery::split(s, "\n")) {
+    syslog(LOG_INFO, "result=%s", line.c_str());
+  }
+  return Status(0, "OK");
+}
+
+Status SyslogLoggerPlugin::logStatus(const std::vector<StatusLogLine>& log) {
+  for (const auto& item : log) {
+    int severity = LOG_NOTICE;
+    if (item.severity == O_INFO) {
+      severity = LOG_NOTICE;
+    } else if (item.severity == O_WARNING) {
+      severity = LOG_WARNING;
+    } else if (item.severity == O_ERROR) {
+      severity = LOG_ERR;
+    } else if (item.severity == O_FATAL) {
+      severity = LOG_CRIT;
+    }
+
+    std::string line = "severity=" + std::to_string(item.severity)
+                    + " location=" + item.filename + ":" + std::to_string(item.line) +
+                      " message=" + item.message;
+
+    syslog(severity, "%s", line.c_str());
+  }
+  return Status(0, "OK");
+}
+
+Status SyslogLoggerPlugin::init(const std::string& name,
+                                const std::vector<StatusLogLine>& log) {
+  closelog();
+
+  // Define the syslog/target's application name.
+  if (FLAGS_logger_syslog_facility < 0 ||
+      FLAGS_logger_syslog_facility > 23) {
+    FLAGS_logger_syslog_facility = LOG_LOCAL3 >> 3;
+  }
+  openlog(name.c_str(), LOG_PID | LOG_CONS, FLAGS_logger_syslog_facility << 3);
+
+  // Now funnel the intermediate status logs provided to `init`.
+  return logStatus(log);
+}
+}
diff --git a/src/osquery/logger/tests/logger_tests.cpp b/src/osquery/logger/tests/logger_tests.cpp
new file mode 100644 (file)
index 0000000..8c4e81d
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/core.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+
+DECLARE_string(logger_plugin);
+
+class LoggerTests : public testing::Test {
+ public:
+  void SetUp() {
+    logging_status_ = FLAGS_disable_logging;
+    FLAGS_disable_logging = false;
+
+    log_lines.clear();
+    status_messages.clear();
+    statuses_logged = 0;
+    last_status = {O_INFO, "", -1, ""};
+  }
+
+  void TearDown() { FLAGS_disable_logging = logging_status_; }
+
+  // Track lines emitted to logString
+  static std::vector<std::string> log_lines;
+
+  // Track the results of init
+  static StatusLogLine last_status;
+  static std::vector<std::string> status_messages;
+
+  // Count calls to logStatus
+  static int statuses_logged;
+  // Count added and removed snapshot rows
+  static int snapshot_rows_added;
+  static int snapshot_rows_removed;
+  // Count the added health status rows
+  static int health_status_rows;
+
+ private:
+  /// Save the status of logging before running tests, restore afterward.
+  bool logging_status_;
+};
+
+std::vector<std::string> LoggerTests::log_lines;
+StatusLogLine LoggerTests::last_status;
+std::vector<std::string> LoggerTests::status_messages;
+int LoggerTests::statuses_logged = 0;
+int LoggerTests::snapshot_rows_added = 0;
+int LoggerTests::snapshot_rows_removed = 0;
+int LoggerTests::health_status_rows = 0;
+
+class TestLoggerPlugin : public LoggerPlugin {
+ public:
+  TestLoggerPlugin() {}
+
+  Status logString(const std::string& s) {
+    LoggerTests::log_lines.push_back(s);
+    return Status(0, s);
+  }
+
+  Status init(const std::string& name, const std::vector<StatusLogLine>& log) {
+    for (const auto& status : log) {
+      LoggerTests::status_messages.push_back(status.message);
+    }
+
+    if (log.size() > 0) {
+      LoggerTests::last_status = log.back();
+    }
+
+    if (name == "RETURN_FAILURE") {
+      return Status(1, "OK");
+    } else {
+      return Status(0, "OK");
+    }
+  }
+
+  Status logStatus(const std::vector<StatusLogLine>& log) {
+    ++LoggerTests::statuses_logged;
+    return Status(0, "OK");
+  }
+
+  Status logSnapshot(const std::string& s) {
+    LoggerTests::snapshot_rows_added += 1;
+    LoggerTests::snapshot_rows_removed += 0;
+    return Status(0, "OK");
+  }
+
+  Status logHealth(const std::string& s) {
+    LoggerTests::health_status_rows += 1;
+    return Status(0, "OK");
+  }
+
+  virtual ~TestLoggerPlugin() {}
+};
+
+TEST_F(LoggerTests, test_plugin) {
+  Registry::add<TestLoggerPlugin>("logger", "test");
+  Registry::setUp();
+
+  auto s = Registry::call("logger", "test", {{"string", "foobar"}});
+  EXPECT_TRUE(s.ok());
+  EXPECT_EQ(LoggerTests::log_lines.back(), "foobar");
+}
+
+TEST_F(LoggerTests, test_logger_init) {
+  // Expect the logger to have been registered from the first test.
+  EXPECT_TRUE(Registry::exists("logger", "test"));
+  EXPECT_TRUE(Registry::setActive("logger", "test").ok());
+
+  initStatusLogger("logger_test");
+  // This will be printed to stdout.
+  LOG(WARNING) << "Logger test is generating a warning status (1)";
+  initLogger("logger_test");
+
+  // The warning message will have been buffered and sent to the active logger
+  // which is test.
+  EXPECT_EQ(LoggerTests::status_messages.size(), 1);
+
+  // The logStatus API should NOT have been called. It will only be used if
+  // (1) The active logger's init returns success within initLogger and
+  // (2) for status logs generated after initLogger is called.
+  EXPECT_EQ(LoggerTests::statuses_logged, 0);
+}
+
+TEST_F(LoggerTests, test_logger_log_status) {
+  // This will be printed to stdout.
+  LOG(WARNING) << "Logger test is generating a warning status (2)";
+
+  // The second warning status will be sent to the logger plugin.
+  EXPECT_EQ(LoggerTests::statuses_logged, 1);
+}
+
+TEST_F(LoggerTests, test_logger_variations) {
+  // Init the logger for a second time, this should only be done for testing.
+  // This time we'll trigger the init method to fail and prevent additional
+  // status messages from trigger logStatus.
+  initLogger("RETURN_FAILURE");
+
+  // This will be printed to stdout.
+  LOG(WARNING) << "Logger test is generating a warning status (3)";
+
+  // Since the initLogger call triggered a failed init, meaning the logger
+  // does NOT handle Glog logs, there will be no statuses logged.
+  EXPECT_EQ(LoggerTests::statuses_logged, 0);
+}
+
+TEST_F(LoggerTests, test_logger_snapshots) {
+  // A snapshot query should not include removed items.
+  QueryLogItem item;
+  item.name = "test_query";
+  item.identifier = "unknown_test_host";
+  item.time = 0;
+  item.calendar_time = "no_time";
+
+  // Add a fake set of results.
+  item.results.added.push_back({{"test_column", "test_value"}});
+  logSnapshotQuery(item);
+
+  // Expect the plugin to optionally handle snapshot logging.
+  EXPECT_EQ(LoggerTests::snapshot_rows_added, 1);
+
+  // Add the same item as a health status log item.
+  logHealthStatus(item);
+  EXPECT_EQ(LoggerTests::health_status_rows, 1);
+}
+}
diff --git a/src/osquery/main/empty.cpp b/src/osquery/main/empty.cpp
new file mode 100644 (file)
index 0000000..ab9ead5
--- /dev/null
@@ -0,0 +1,9 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
diff --git a/src/osquery/main/lib.cpp b/src/osquery/main/lib.cpp
new file mode 100644 (file)
index 0000000..8de6ca6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+#include <string>
+
+#include <osquery/core.h>
+
+// If CMake/gmake did not define a build version set the version to 1.0.
+// clang-format off
+#ifndef OSQUERY_BUILD_VERSION
+#define OSQUERY_BUILD_VERSION 1.0.0-unknown
+#endif
+// clang-format on
+
+namespace osquery {
+
+const std::string kVersion = STR(OSQUERY_BUILD_VERSION);
+const std::string kSDKVersion = OSQUERY_SDK_VERSION;
+const std::string kSDKPlatform = OSQUERY_PLATFORM;
+}
diff --git a/src/osquery/main/run.cpp b/src/osquery/main/run.cpp
new file mode 100644 (file)
index 0000000..62a933a
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <errno.h>
+
+#include <gflags/gflags.h>
+
+#include <osquery/core.h>
+#include <osquery/events.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+
+DEFINE_string(query, "", "query to execute");
+DEFINE_int32(iterations, 1, "times to run the query in question");
+DEFINE_int32(delay, 0, "delay before and after the query");
+
+namespace osquery {
+
+DECLARE_bool(disable_events);
+DECLARE_bool(registry_exceptions);
+}
+
+int main(int argc, char* argv[]) {
+  // Only log to stderr
+  FLAGS_logtostderr = true;
+
+  // Let gflags parse the non-help options/flags.
+  GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, false);
+  GFLAGS_NAMESPACE::InitGoogleLogging(argv[0]);
+
+  if (FLAGS_query == "") {
+    fprintf(stderr, "Usage: %s --query=\"query\"\n", argv[0]);
+    return 1;
+  }
+
+  osquery::Registry::setUp();
+  osquery::FLAGS_disable_events = true;
+  osquery::FLAGS_registry_exceptions = true;
+  osquery::attachEvents();
+
+  if (FLAGS_delay != 0) {
+    ::sleep(FLAGS_delay);
+  }
+
+  osquery::QueryData results;
+  osquery::Status status;
+  for (int i = 0; i < FLAGS_iterations; ++i) {
+    status = osquery::query(FLAGS_query, results);
+    if (!status.ok()) {
+      fprintf(stderr, "Query failed: %d\n", status.getCode());
+      break;
+    }
+  }
+
+  if (FLAGS_delay != 0) {
+    ::sleep(FLAGS_delay);
+  }
+
+  // Instead of calling "shutdownOsquery" force the EF to join its threads.
+  GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
+
+  return status.getCode();
+}
diff --git a/src/osquery/main/shell.cpp b/src/osquery/main/shell.cpp
new file mode 100644 (file)
index 0000000..b6c1744
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <stdio.h>
+
+#include <osquery/core.h>
+#include <osquery/extensions.h>
+
+#include "osquery/core/watcher.h"
+#include "osquery/devtools/devtools.h"
+
+int main(int argc, char *argv[]) {
+  // Parse/apply flags, start registry, load logger/config plugins.
+  osquery::Initializer runner(argc, argv, osquery::OSQUERY_TOOL_SHELL);
+  if (argc > 1 || !isatty(fileno(stdin)) || osquery::FLAGS_A.size() > 0 ||
+      osquery::FLAGS_L) {
+    // A query was set as a positional argument for via stdin.
+    osquery::FLAGS_disable_events = true;
+    // The shell may have loaded table extensions, if not, disable the manager.
+    if (!osquery::Watcher::hasManagedExtensions()) {
+      osquery::FLAGS_disable_extensions = true;
+    }
+  }
+
+  runner.start();
+
+  // Virtual tables will be attached to the shell's in-memory SQLite DB.
+  int retcode = osquery::launchIntoShell(argc, argv);
+
+  // Finally shutdown.
+  runner.shutdown();
+  return retcode;
+}
diff --git a/src/osquery/main/tests.cpp b/src/osquery/main/tests.cpp
new file mode 100644 (file)
index 0000000..d25960d
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) 2015, Wesley Shields
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <cstdlib>
+#include <chrono>
+
+#include <time.h>
+
+#include <boost/filesystem.hpp>
+
+#include <gtest/gtest.h>
+
+#include "osquery/core/test_util.h"
+#include "osquery/database/db_handle.h"
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+
+DECLARE_string(database_path);
+DECLARE_string(extensions_socket);
+DECLARE_string(modules_autoload);
+DECLARE_string(extensions_autoload);
+DECLARE_bool(disable_logging);
+DECLARE_bool(verbose);
+
+typedef std::chrono::high_resolution_clock chrono_clock;
+
+void initTesting() {
+  // Seed the random number generator, some tests generate temporary files
+  // ports, sockets, etc using random numbers.
+  std::srand(chrono_clock::now().time_since_epoch().count());
+
+  // Set safe default values for path-based flags.
+  // Specific unittests may edit flags temporarily.
+  fs::remove_all(kTestWorkingDirectory);
+  fs::create_directories(kTestWorkingDirectory);
+  FLAGS_database_path = kTestWorkingDirectory + "unittests.db";
+  FLAGS_extensions_socket = kTestWorkingDirectory + "unittests.em";
+  FLAGS_extensions_autoload = kTestWorkingDirectory + "unittests-ext.load";
+  FLAGS_modules_autoload = kTestWorkingDirectory + "unittests-mod.load";
+  FLAGS_disable_logging = true;
+  FLAGS_verbose = true;
+
+  // Create a default DBHandle instance before unittests.
+  (void)DBHandle::getInstance();
+}
+}
+
+int main(int argc, char* argv[]) {
+  // Allow unit test execution from anywhere in the osquery source/build tree.
+  while (osquery::kTestDataPath != "/") {
+    if (!fs::exists(osquery::kTestDataPath)) {
+      osquery::kTestDataPath =
+          osquery::kTestDataPath.substr(3, osquery::kTestDataPath.size());
+    } else {
+      break;
+    }
+  }
+
+  osquery::initTesting();
+  testing::InitGoogleTest(&argc, argv);
+  // Optionally enable Goggle Logging
+  // google::InitGoogleLogging(argv[0]);
+  return RUN_ALL_TESTS();
+}
diff --git a/src/osquery/registry/CMakeLists.txt b/src/osquery/registry/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b2dfbda
--- /dev/null
@@ -0,0 +1,18 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_registry registry.cpp)
+
+FILE(GLOB OSQUERY_REGISTRY_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_REGISTRY_TESTS})
diff --git a/src/osquery/registry/registry.cpp b/src/osquery/registry/registry.cpp
new file mode 100644 (file)
index 0000000..a5acd7b
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <cstdlib>
+#include <sstream>
+
+#include <dlfcn.h>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osquery/extensions.h>
+#include <osquery/logger.h>
+#include <osquery/registry.h>
+
+namespace osquery {
+
+HIDDEN_FLAG(bool, registry_exceptions, false, "Allow plugin exceptions");
+
+void RegistryHelperCore::remove(const std::string& item_name) {
+  if (items_.count(item_name) > 0) {
+    items_[item_name]->tearDown();
+    items_.erase(item_name);
+  }
+
+  // Populate list of aliases to remove (those that mask item_name).
+  std::vector<std::string> removed_aliases;
+  for (const auto& alias : aliases_) {
+    if (alias.second == item_name) {
+      removed_aliases.push_back(alias.first);
+    }
+  }
+
+  for (const auto& alias : removed_aliases) {
+    aliases_.erase(alias);
+  }
+}
+
+bool RegistryHelperCore::isInternal(const std::string& item_name) const {
+  if (std::find(internal_.begin(), internal_.end(), item_name) ==
+      internal_.end()) {
+    return false;
+  }
+  return true;
+}
+
+Status RegistryHelperCore::setActive(const std::string& item_name) {
+  if (items_.count(item_name) == 0 && external_.count(item_name) == 0) {
+    return Status(1, "Unknown registry item");
+  }
+
+  active_ = item_name;
+  // The active plugin is setup when initialized.
+  if (exists(item_name, true)) {
+    Registry::get(name_, item_name)->setUp();
+  }
+  return Status(0, "OK");
+}
+
+const std::string& RegistryHelperCore::getActive() const { return active_; }
+
+RegistryRoutes RegistryHelperCore::getRoutes() const {
+  RegistryRoutes route_table;
+  for (const auto& item : items_) {
+    if (isInternal(item.first)) {
+      // This is an internal plugin, do not include the route.
+      continue;
+    }
+
+    bool has_alias = false;
+    for (const auto& alias : aliases_) {
+      if (alias.second == item.first) {
+        // If the item name is masked by at least one alias, it will not
+        // broadcast under the internal item name.
+        route_table[alias.first] = item.second->routeInfo();
+        has_alias = true;
+      }
+    }
+
+    if (!has_alias) {
+      route_table[item.first] = item.second->routeInfo();
+    }
+  }
+  return route_table;
+}
+
+Status RegistryHelperCore::call(const std::string& item_name,
+                                const PluginRequest& request,
+                                PluginResponse& response) {
+  if (items_.count(item_name) > 0) {
+    return items_.at(item_name)->call(request, response);
+  }
+
+  if (external_.count(item_name) > 0) {
+    // The item is a registered extension, call the extension by UUID.
+    return callExtension(external_.at(item_name), name_, item_name, request,
+                         response);
+  } else if (routes_.count(item_name) > 0) {
+    // The item has a route, but no extension, pass in the route info.
+    response = routes_.at(item_name);
+    return Status(0, "Route only");
+  } else if (Registry::external()) {
+    // If this is an extension's registry forward unknown calls to the core.
+    return callExtension(0, name_, item_name, request, response);
+  }
+
+  return Status(1, "Cannot call registry item: " + item_name);
+}
+
+Status RegistryHelperCore::addAlias(const std::string& item_name,
+                                    const std::string& alias) {
+  if (aliases_.count(alias) > 0) {
+    return Status(1, "Duplicate alias: " + alias);
+  }
+  aliases_[alias] = item_name;
+  return Status(0, "OK");
+}
+
+const std::string& RegistryHelperCore::getAlias(
+    const std::string& alias) const {
+  if (aliases_.count(alias) == 0) {
+    return alias;
+  }
+  return aliases_.at(alias);
+}
+
+void RegistryHelperCore::setUp() {
+  // If this registry does not auto-setup do NOT setup the registry items.
+  if (!auto_setup_) {
+    return;
+  }
+
+  // If the registry is using a single 'active' plugin, setUp that plugin.
+  // For config and logger, only setUp the selected plugin.
+  if (active_.size() != 0 && exists(active_, true)) {
+    items_.at(active_)->setUp();
+    return;
+  }
+
+  // Try to set up each of the registry items.
+  // If they fail, remove them from the registry.
+  std::vector<std::string> failed;
+  for (auto& item : items_) {
+    if (!item.second->setUp().ok()) {
+      failed.push_back(item.first);
+    }
+  }
+
+  for (const auto& failed_item : failed) {
+    remove(failed_item);
+  }
+}
+
+/// Facility method to check if a registry item exists.
+bool RegistryHelperCore::exists(const std::string& item_name,
+                                bool local) const {
+  bool has_local = (items_.count(item_name) > 0);
+  bool has_external = (external_.count(item_name) > 0);
+  bool has_route = (routes_.count(item_name) > 0);
+  return (local) ? has_local : has_local || has_external || has_route;
+}
+
+/// Facility method to list the registry item identifiers.
+std::vector<std::string> RegistryHelperCore::names() const {
+  std::vector<std::string> names;
+  for (const auto& item : items_) {
+    names.push_back(item.first);
+  }
+
+  // Also add names of external plugins.
+  for (const auto& item : external_) {
+    names.push_back(item.first);
+  }
+  return names;
+}
+
+/// Facility method to count the number of items in this registry.
+size_t RegistryHelperCore::count() const { return items_.size(); }
+
+/// Allow the registry to introspect into the registered name (for logging).
+void RegistryHelperCore::setName(const std::string& name) { name_ = name; }
+
+const std::map<std::string, PluginRegistryHelperRef>& RegistryFactory::all() {
+  return instance().registries_;
+}
+
+PluginRegistryHelperRef RegistryFactory::registry(
+    const std::string& registry_name) {
+  return instance().registries_.at(registry_name);
+}
+
+const std::map<std::string, PluginRef> RegistryFactory::all(
+    const std::string& registry_name) {
+  return instance().registry(registry_name)->all();
+}
+
+PluginRef RegistryFactory::get(const std::string& registry_name,
+                               const std::string& item_name) {
+  return instance().registry(registry_name)->get(item_name);
+}
+
+RegistryBroadcast RegistryFactory::getBroadcast() {
+  RegistryBroadcast broadcast;
+  for (const auto& registry : instance().registries_) {
+    broadcast[registry.first] = registry.second->getRoutes();
+  }
+  return broadcast;
+}
+
+Status RegistryFactory::addBroadcast(const RouteUUID& uuid,
+                                     const RegistryBroadcast& broadcast) {
+  if (instance().extensions_.count(uuid) > 0) {
+    return Status(1, "Duplicate extension UUID: " + std::to_string(uuid));
+  }
+
+  // Make sure the extension does not broadcast conflicting registry items.
+  if (!Registry::allowDuplicates()) {
+    for (const auto& registry : broadcast) {
+      for (const auto& item : registry.second) {
+        if (Registry::exists(registry.first, item.first)) {
+          VLOG(1) << "Extension " << uuid
+                  << " has duplicate plugin name: " << item.first
+                  << " in registry: " << registry.first;
+          return Status(1, "Duplicate registry item: " + item.first);
+        }
+      }
+    }
+  }
+
+  // Once duplication is satisfied call each registry's addExternal.
+  Status status;
+  for (const auto& registry : broadcast) {
+    status = RegistryFactory::registry(registry.first)
+                 ->addExternal(uuid, registry.second);
+    if (!status.ok()) {
+      // If any registry fails to add the set of external routes, stop.
+      break;
+    }
+
+    for (const auto& plugin : registry.second) {
+      VLOG(1) << "Extension " << uuid << " registered " << registry.first
+              << " plugin " << plugin.first;
+    }
+  }
+
+  // If any registry failed, remove each (assume a broadcast is atomic).
+  if (!status.ok()) {
+    for (const auto& registry : broadcast) {
+      Registry::registry(registry.first)->removeExternal(uuid);
+    }
+  }
+  instance().extensions_.insert(uuid);
+  return status;
+}
+
+Status RegistryFactory::removeBroadcast(const RouteUUID& uuid) {
+  if (instance().extensions_.count(uuid) == 0) {
+    return Status(1, "Unknown extension UUID: " + std::to_string(uuid));
+  }
+
+  for (const auto& registry : instance().registries_) {
+    registry.second->removeExternal(uuid);
+  }
+  instance().extensions_.erase(uuid);
+  return Status(0, "OK");
+}
+
+/// Adds an alias for an internal registry item. This registry will only
+/// broadcast the alias name.
+Status RegistryFactory::addAlias(const std::string& registry_name,
+                                 const std::string& item_name,
+                                 const std::string& alias) {
+  if (instance().registries_.count(registry_name) == 0) {
+    return Status(1, "Unknown registry: " + registry_name);
+  }
+  return instance().registries_.at(registry_name)->addAlias(item_name, alias);
+}
+
+/// Returns the item_name or the item alias if an alias exists.
+const std::string& RegistryFactory::getAlias(const std::string& registry_name,
+                                             const std::string& alias) {
+  if (instance().registries_.count(registry_name) == 0) {
+    return alias;
+  }
+  return instance().registries_.at(registry_name)->getAlias(alias);
+}
+
+Status RegistryFactory::call(const std::string& registry_name,
+                             const std::string& item_name,
+                             const PluginRequest& request,
+                             PluginResponse& response) {
+  // Forward factory call to the registry.
+  try {
+    return registry(registry_name)->call(item_name, request, response);
+  } catch (const std::exception& e) {
+    LOG(ERROR) << registry_name << " registry " << item_name
+               << " plugin caused exception: " << e.what();
+    if (FLAGS_registry_exceptions) {
+      throw e;
+    }
+    return Status(1, e.what());
+  } catch (...) {
+    LOG(ERROR) << registry_name << " registry " << item_name
+               << " plugin caused unknown exception";
+    if (FLAGS_registry_exceptions) {
+      throw std::runtime_error(registry_name + ": " + item_name + " failed");
+    }
+    return Status(2, "Unknown exception");
+  }
+}
+
+Status RegistryFactory::call(const std::string& registry_name,
+                             const std::string& item_name,
+                             const PluginRequest& request) {
+  PluginResponse response;
+  // Wrapper around a call expecting a response.
+  return call(registry_name, item_name, request, response);
+}
+
+Status RegistryFactory::call(const std::string& registry_name,
+                             const PluginRequest& request,
+                             PluginResponse& response) {
+  auto& plugin = registry(registry_name)->getActive();
+  return call(registry_name, plugin, request, response);
+}
+
+Status RegistryFactory::call(const std::string& registry_name,
+                             const PluginRequest& request) {
+  PluginResponse response;
+  return call(registry_name, request, response);
+}
+
+Status RegistryFactory::setActive(const std::string& registry_name,
+                                  const std::string& item_name) {
+  if (!exists(registry_name, item_name)) {
+    return Status(1, "Registry plugin does not exist");
+  }
+  return registry(registry_name)->setActive(item_name);
+}
+
+const std::string& RegistryFactory::getActive(
+    const std::string& registry_name) {
+  return registry(registry_name)->getActive();
+}
+
+void RegistryFactory::setUp() {
+  for (const auto& registry : instance().all()) {
+    registry.second->setUp();
+  }
+}
+
+bool RegistryFactory::exists(const std::string& registry_name,
+                             const std::string& item_name,
+                             bool local) {
+  if (instance().registries_.count(registry_name) == 0) {
+    return false;
+  }
+
+  // Check the registry.
+  return registry(registry_name)->exists(item_name, local);
+}
+
+std::vector<std::string> RegistryFactory::names() {
+  std::vector<std::string> names;
+  for (const auto& registry : all()) {
+    names.push_back(registry.second->getName());
+  }
+  return names;
+}
+
+std::vector<std::string> RegistryFactory::names(
+    const std::string& registry_name) {
+  if (instance().registries_.at(registry_name) == 0) {
+    std::vector<std::string> names;
+    return names;
+  }
+  return instance().registry(registry_name)->names();
+}
+
+std::vector<RouteUUID> RegistryFactory::routeUUIDs() {
+  std::vector<RouteUUID> uuids;
+  for (const auto& extension : instance().extensions_) {
+    uuids.push_back(extension);
+  }
+  return uuids;
+}
+
+size_t RegistryFactory::count() { return instance().registries_.size(); }
+
+size_t RegistryFactory::count(const std::string& registry_name) {
+  if (instance().registries_.count(registry_name) == 0) {
+    return 0;
+  }
+  return instance().registry(registry_name)->count();
+}
+
+Status RegistryHelperCore::add(const std::string& item_name, bool internal) {
+  // The item can be listed as internal, meaning it does not broadcast.
+  if (internal) {
+    internal_.push_back(item_name);
+  }
+
+  // The item may belong to a module.
+  if (RegistryFactory::usingModule()) {
+    modules_[item_name] = RegistryFactory::getModule();
+  }
+
+  return Status(0, "OK");
+}
+
+const std::map<RouteUUID, ModuleInfo>& RegistryFactory::getModules() {
+  return instance().modules_;
+}
+
+RouteUUID RegistryFactory::getModule() { return instance().module_uuid_; }
+
+bool RegistryFactory::usingModule() {
+  // Check if the registry is allowing a module's registrations.
+  return (!instance().locked() && instance().module_uuid_ != 0);
+}
+
+void RegistryFactory::shutdownModule() {
+  // TODO: [temporarily disable] should be check.
+  //instance().locked(true);
+  instance().module_uuid_ = 0;
+}
+
+void RegistryFactory::initModule(const std::string& path) {
+  // Begin a module initialization, lock until the module is determined
+  // appropriate by requesting a call to `declareModule`.
+  instance().module_uuid_ = (RouteUUID)rand();
+  instance().modules_[getModule()].path = path;
+  instance().locked(true);
+}
+
+void RegistryFactory::declareModule(const std::string& name,
+                                    const std::string& version,
+                                    const std::string& min_sdk_version,
+                                    const std::string& sdk_version) {
+  // Check the min_sdk_version against the Registry's SDK version.
+  auto& module = instance().modules_[instance().module_uuid_];
+  module.name = name;
+  module.version = version;
+  module.sdk_version = sdk_version;
+  instance().locked(false);
+}
+
+RegistryModuleLoader::RegistryModuleLoader(const std::string& path)
+    : handle_(nullptr), path_(path) {
+  // Tell the registry that we are attempting to construct a module.
+  // Locking the registry prevents the module's global initialization from
+  // adding or creating registry items.
+  RegistryFactory::initModule(path_);
+  handle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL);
+  if (handle_ == nullptr) {
+    VLOG(1) << "Failed to load module: " << path_;
+    VLOG(1) << dlerror();
+    return;
+  }
+
+  // The module should have called RegistryFactory::declareModule and unlocked
+  // the registry for modification. The module should have done this using
+  // the SDK's CREATE_MODULE macro, which adds the global-scope constructor.
+  if (RegistryFactory::locked()) {
+    VLOG(1) << "Failed to declare module: " << path_;
+    dlclose(handle_);
+    handle_ = nullptr;
+  }
+}
+
+void RegistryModuleLoader::init() {
+  if (handle_ == nullptr || RegistryFactory::locked()) {
+    handle_ = nullptr;
+    return;
+  }
+
+  // Locate a well-known symbol in the module.
+  // This symbol name is protected against rewriting when the module uses the
+  // SDK's CREATE_MODULE macro.
+  auto initializer = (ModuleInitalizer)dlsym(handle_, "initModule");
+  if (initializer != nullptr) {
+    initializer();
+    VLOG(1) << "Initialized module: " << path_;
+  } else {
+    VLOG(1) << "Failed to initialize module: " << path_;
+    VLOG(1) << dlerror();
+    dlclose(handle_);
+    handle_ = nullptr;
+  }
+}
+
+RegistryModuleLoader::~RegistryModuleLoader() {
+  if (handle_ == nullptr) {
+    // The module was not loaded or did not initalize.
+    RegistryFactory::instance().modules_.erase(RegistryFactory::getModule());
+  }
+
+  // We do not close the module, and thus are OK with losing a reference to the
+  // module's handle. Attempting to close and clean up is very expensive for
+  // very little value/features.
+  if (!RegistryFactory::locked()) {
+    RegistryFactory::shutdownModule();
+  }
+  // No need to clean this resource.
+  handle_ = nullptr;
+}
+
+void Plugin::getResponse(const std::string& key,
+                         const PluginResponse& response,
+                         boost::property_tree::ptree& tree) {
+  for (const auto& item : response) {
+    boost::property_tree::ptree child;
+    for (const auto& item_detail : item) {
+      child.put(item_detail.first, item_detail.second);
+    }
+    tree.add_child(key, child);
+  }
+}
+
+void Plugin::setResponse(const std::string& key,
+                         const boost::property_tree::ptree& tree,
+                         PluginResponse& response) {
+  std::ostringstream output;
+  try {
+    boost::property_tree::write_json(output, tree, false);
+  } catch (const pt::json_parser::json_parser_error& e) {
+    // The plugin response could not be serialized.
+  }
+  response.push_back({{key, output.str()}});
+}
+}
diff --git a/src/osquery/registry/tests/registry_tests.cpp b/src/osquery/registry/tests/registry_tests.cpp
new file mode 100644 (file)
index 0000000..381a4e4
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/logger.h>
+#include <osquery/registry.h>
+
+namespace osquery {
+
+class RegistryTests : public testing::Test {};
+
+class CatPlugin : public Plugin {
+ public:
+  CatPlugin() : some_value_(0) {}
+
+ protected:
+  int some_value_;
+};
+
+class HouseCat : public CatPlugin {
+ public:
+  Status setUp() {
+    // Make sure the Plugin implementation's init is called.
+    some_value_ = 9000;
+    return Status(0, "OK");
+  }
+};
+
+/// This is a manual registry type without a name, so we cannot broadcast
+/// this registry type and it does NOT need to conform to a registry API.
+class CatRegistry : public RegistryHelper<CatPlugin> {};
+
+TEST_F(RegistryTests, test_registry) {
+  CatRegistry cats;
+
+  /// Add a CatRegistry item (a plugin) called "house".
+  cats.add<HouseCat>("house");
+  EXPECT_EQ(cats.count(), 1);
+
+  /// Try to add the same plugin with the same name, this is meaningless.
+  cats.add<HouseCat>("house");
+  /// Now add the same plugin with a different name, a new plugin instance
+  /// will be created and registered.
+  cats.add<HouseCat>("house2");
+  EXPECT_EQ(cats.count(), 2);
+
+  /// Request a plugin to call an API method.
+  auto cat = cats.get("house");
+  cats.setUp();
+
+  /// Now let's iterate over every registered Cat plugin.
+  EXPECT_EQ(cats.all().size(), 2);
+}
+
+/// Normally we have "Registry" that dictates the set of possible API methods
+/// for all registry types. Here we use a "TestRegistry" instead.
+class TestCoreRegistry : public RegistryFactory {};
+
+/// We can automatically create a registry type as long as that type conforms
+/// to the registry API defined in the "Registry". Here we use "TestRegistry".
+/// The above "CatRegistry" was easier to understand, but using a auto
+/// registry via the registry create method, we can assign a tracked name
+/// and then broadcast that registry name to other plugins.
+auto AutoCatRegistry = TestCoreRegistry::create<CatPlugin>("cat");
+
+TEST_F(RegistryTests, test_auto_factory) {
+  /// Using the registry, and a registry type by name, we can register a
+  /// plugin HouseCat called "house" like above.
+  TestCoreRegistry::registry("cat")->add<HouseCat>("auto_house");
+  TestCoreRegistry::add<HouseCat>("cat", "auto_house2");
+  TestCoreRegistry::registry("cat")->setUp();
+
+  /// When acting on registries by name we can check the broadcasted
+  /// registry name of other plugin processes (via Thrift) as well as
+  /// internally registered plugins like HouseCat.
+  EXPECT_EQ(TestCoreRegistry::registry("cat")->count(), 2);
+  EXPECT_EQ(TestCoreRegistry::count("cat"), 2);
+
+  /// And we can call an API method, since we guarantee CatPlugins conform
+  /// to the "TestCoreRegistry"'s "TestPluginAPI".
+  auto cat = TestCoreRegistry::get("cat", "auto_house");
+  auto same_cat = TestCoreRegistry::get("cat", "auto_house");
+  EXPECT_EQ(cat, same_cat);
+}
+
+class DogPlugin : public Plugin {
+ public:
+  DogPlugin() : some_value_(10000) {}
+
+ protected:
+  int some_value_;
+};
+
+class Doge : public DogPlugin {
+ public:
+  Doge() { some_value_ = 100000; }
+};
+
+class BadDoge : public DogPlugin {
+ public:
+  Status setUp() { return Status(1, "Expect error... this is a bad dog"); }
+};
+
+auto AutoDogRegistry = TestCoreRegistry::create<DogPlugin>("dog", true);
+
+TEST_F(RegistryTests, test_auto_registries) {
+  TestCoreRegistry::add<Doge>("dog", "doge");
+  TestCoreRegistry::registry("dog")->setUp();
+
+  EXPECT_EQ(TestCoreRegistry::count("dog"), 1);
+}
+
+TEST_F(RegistryTests, test_persistant_registries) {
+  EXPECT_EQ(TestCoreRegistry::count("cat"), 2);
+}
+
+TEST_F(RegistryTests, test_registry_exceptions) {
+  EXPECT_TRUE(TestCoreRegistry::add<Doge>("dog", "duplicate_dog").ok());
+  // Bad dog will be added fine, but when setup is run, it will be removed.
+  EXPECT_TRUE(TestCoreRegistry::add<BadDoge>("dog", "bad_doge").ok());
+  TestCoreRegistry::registry("dog")->setUp();
+  // Make sure bad dog does not exist.
+  EXPECT_FALSE(TestCoreRegistry::exists("dog", "bad_doge"));
+  EXPECT_EQ(TestCoreRegistry::count("dog"), 2);
+
+  int exception_count = 0;
+  try {
+    TestCoreRegistry::registry("does_not_exist");
+  } catch (const std::out_of_range& e) {
+    exception_count++;
+  }
+
+  try {
+    TestCoreRegistry::add<HouseCat>("does_not_exist", "cat");
+  } catch (const std::out_of_range& e) {
+    exception_count++;
+  }
+
+  EXPECT_EQ(exception_count, 2);
+}
+
+class WidgetPlugin : public Plugin {
+ public:
+  /// The route information will usually be provided by the plugin type.
+  /// The plugin/registry item will set some structures for the plugin
+  /// to parse and format. BUT a plugin/registry item can also fill this
+  /// information in if the plugin type/registry type exposes routeInfo as
+  /// a virtual method.
+  PluginResponse routeInfo() const {
+    PluginResponse info;
+    info.push_back({{"name", name_}});
+    return info;
+  }
+
+  /// Plugin types should contain generic request/response formatters and
+  /// decorators.
+  std::string secretPower(const PluginRequest& request) const {
+    if (request.count("secret_power") > 0) {
+      return request.at("secret_power");
+    }
+    return "no_secret_power";
+  }
+};
+
+class SpecialWidget : public WidgetPlugin {
+ public:
+  Status call(const PluginRequest& request, PluginResponse& response);
+};
+
+Status SpecialWidget::call(const PluginRequest& request,
+                           PluginResponse& response) {
+  response.push_back(request);
+  response[0]["from"] = name_;
+  response[0]["secret_power"] = secretPower(request);
+  return Status(0, "OK");
+}
+
+#define UNUSED(x) (void)(x)
+
+TEST_F(RegistryTests, test_registry_api) {
+  auto AutoWidgetRegistry = TestCoreRegistry::create<WidgetPlugin>("widgets");
+  UNUSED(AutoWidgetRegistry);
+
+  TestCoreRegistry::add<SpecialWidget>("widgets", "special");
+
+  // Test route info propogation, from item to registry, to broadcast.
+  auto ri = TestCoreRegistry::get("widgets", "special")->routeInfo();
+  EXPECT_EQ(ri[0].at("name"), "special");
+  auto rr = TestCoreRegistry::registry("widgets")->getRoutes();
+  EXPECT_EQ(rr.size(), 1);
+  EXPECT_EQ(rr.at("special")[0].at("name"), "special");
+
+  // Broadcast will include all registries, and all their items.
+  auto broadcast_info = TestCoreRegistry::getBroadcast();
+  EXPECT_TRUE(broadcast_info.size() >= 3);
+  EXPECT_EQ(broadcast_info.at("widgets").at("special")[0].at("name"),
+            "special");
+
+  PluginResponse response;
+  PluginRequest request;
+  auto status = TestCoreRegistry::call("widgets", "special", request, response);
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(response[0].at("from"), "special");
+  EXPECT_EQ(response[0].at("secret_power"), "no_secret_power");
+
+  request["secret_power"] = "magic";
+  status = TestCoreRegistry::call("widgets", "special", request, response);
+  EXPECT_EQ(response[0].at("secret_power"), "magic");
+}
+
+TEST_F(RegistryTests, test_real_registry) {
+  EXPECT_TRUE(Registry::count() > 0);
+
+  bool has_one_registered = false;
+  for (const auto& registry : Registry::all()) {
+    if (Registry::count(registry.first) > 0) {
+      has_one_registered = true;
+      break;
+    }
+  }
+  EXPECT_TRUE(has_one_registered);
+}
+
+TEST_F(RegistryTests, test_registry_modules) {
+  // Test the registry's module loading state tracking.
+  RegistryFactory::locked(false);
+  EXPECT_FALSE(RegistryFactory::locked());
+  RegistryFactory::locked(true);
+  EXPECT_TRUE(RegistryFactory::locked());
+  RegistryFactory::locked(false);
+
+  // Test initializing a module load and the module's registry modifications.
+  EXPECT_EQ(RegistryFactory::getModule(), 0);
+  RegistryFactory::initModule("/my/test/module");
+  // The registry is locked, no modifications during module global ctors.
+  EXPECT_TRUE(RegistryFactory::locked());
+  // The 'is the registry using a module' is not set during module ctors.
+  EXPECT_FALSE(RegistryFactory::usingModule());
+  EXPECT_EQ(RegistryFactory::getModules().size(), 1);
+  // The unittest can introspect into the current module.
+  auto& module = RegistryFactory::getModules().at(RegistryFactory::getModule());
+  EXPECT_EQ(module.path, "/my/test/module");
+  EXPECT_EQ(module.name, "");
+  RegistryFactory::declareModule("test", "0.1.1", "0.0.0", "0.0.1");
+  // The registry is unlocked after the module is declared.
+  // This assures that module modifications happen with the correct information
+  // and state tracking (aka the SDK limits, name, and version).
+  EXPECT_FALSE(RegistryFactory::locked());
+  // Now the 'is the registry using a module' is set for the duration of the
+  // modules loading.
+  EXPECT_TRUE(RegistryFactory::usingModule());
+  EXPECT_EQ(module.name, "test");
+  EXPECT_EQ(module.version, "0.1.1");
+  EXPECT_EQ(module.sdk_version, "0.0.1");
+
+  // Finally, when the module load is complete, we clear state.
+  RegistryFactory::shutdownModule();
+  // The registry is again locked.
+// TODO: Check below on higher upstream 
+// EXPECT_TRUE(RegistryFactory::locked());
+  // And the registry is no longer using a module.
+  EXPECT_FALSE(RegistryFactory::usingModule());
+  EXPECT_EQ(RegistryFactory::getModule(), 0);
+}
+}
diff --git a/src/osquery/sql/CMakeLists.txt b/src/osquery/sql/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ae844e5
--- /dev/null
@@ -0,0 +1,21 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+ADD_OSQUERY_LIBRARY(osquery_sql sql.cpp)
+
+ADD_OSQUERY_LIBRARY(osquery_sql_internal sqlite_util.cpp
+                                                                                virtual_table.cpp)
+
+FILE(GLOB OSQUERY_SQL_TESTS "tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_SQL_TESTS})
diff --git a/src/osquery/sql/sql.cpp b/src/osquery/sql/sql.cpp
new file mode 100644 (file)
index 0000000..f7b5f87
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sstream>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+#include <osquery/tables.h>
+#include <osquery/registry.h>
+
+namespace osquery {
+
+FLAG(int32, value_max, 512, "Maximum returned row value size");
+
+const std::map<ConstraintOperator, std::string> kSQLOperatorRepr = {
+    {EQUALS, "="},
+    {GREATER_THAN, ">"},
+    {LESS_THAN_OR_EQUALS, "<="},
+    {LESS_THAN, "<"},
+    {GREATER_THAN_OR_EQUALS, ">="},
+};
+
+SQL::SQL(const std::string& q) { status_ = query(q, results_); }
+
+const QueryData& SQL::rows() { return results_; }
+
+bool SQL::ok() { return status_.ok(); }
+
+Status SQL::getStatus() { return status_; }
+
+std::string SQL::getMessageString() { return status_.toString(); }
+
+const std::string SQL::kHostColumnName = "_source_host";
+void SQL::annotateHostInfo() {
+  std::string hostname = getHostname();
+  for (Row& row : results_) {
+    row[kHostColumnName] = hostname;
+  }
+}
+
+std::vector<std::string> SQL::getTableNames() {
+  std::vector<std::string> results;
+  for (const auto& name : Registry::names("table")) {
+    results.push_back(name);
+  }
+  return results;
+}
+
+QueryData SQL::selectAllFrom(const std::string& table) {
+  PluginResponse response;
+  PluginRequest request = {{"action", "generate"}};
+  Registry::call("table", table, request, response);
+  return response;
+}
+
+QueryData SQL::selectAllFrom(const std::string& table,
+                             const std::string& column,
+                             ConstraintOperator op,
+                             const std::string& expr) {
+  PluginResponse response;
+  PluginRequest request = {{"action", "generate"}};
+  QueryContext ctx;
+  ctx.constraints[column].add(Constraint(op, expr));
+
+  TablePlugin::setRequestFromContext(ctx, request);
+  Registry::call("table", table, request, response);
+  return response;
+}
+
+Status SQLPlugin::call(const PluginRequest& request, PluginResponse& response) {
+  response.clear();
+  if (request.count("action") == 0) {
+    return Status(1, "SQL plugin must include a request action");
+  }
+
+  if (request.at("action") == "query") {
+    return this->query(request.at("query"), response);
+  } else if (request.at("action") == "columns") {
+    TableColumns columns;
+    auto status = this->getQueryColumns(request.at("query"), columns);
+    // Convert columns to response
+    for (const auto& column : columns) {
+      response.push_back({{"n", column.first}, {"t", column.second}});
+    }
+    return status;
+  } else if (request.at("action") == "attach") {
+    // Attach a virtual table name using an optional included definition.
+    return this->attach(request.at("table"));
+  } else if (request.at("action") == "detach") {
+    this->detach(request.at("table"));
+    return Status(0, "OK");
+  }
+  return Status(1, "Unknown action");
+}
+
+Status query(const std::string& q, QueryData& results) {
+  return Registry::call(
+      "sql", "sql", {{"action", "query"}, {"query", q}}, results);
+}
+
+Status getQueryColumns(const std::string& q, TableColumns& columns) {
+  PluginResponse response;
+  auto status = Registry::call(
+      "sql", "sql", {{"action", "columns"}, {"query", q}}, response);
+
+  // Convert response to columns
+  for (const auto& item : response) {
+    columns.push_back(make_pair(item.at("n"), item.at("t")));
+  }
+  return status;
+}
+}
diff --git a/src/osquery/sql/sqlite_util.cpp b/src/osquery/sql/sqlite_util.cpp
new file mode 100644 (file)
index 0000000..162ad30
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/core.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+
+#include "osquery/sql/sqlite_util.h"
+#include "osquery/sql/virtual_table.h"
+
+namespace osquery {
+/// SQL provider for osquery internal/core.
+REGISTER_INTERNAL(SQLiteSQLPlugin, "sql", "sql");
+
+FLAG(string,
+     disable_tables,
+     "Not Specified",
+     "Comma-delimited list of table names to be disabled");
+
+/**
+ * @brief A map of SQLite status codes to their corresponding message string
+ *
+ * Details of this map are defined at: http://www.sqlite.org/c3ref/c_abort.html
+ */
+const std::map<int, std::string> kSQLiteReturnCodes = {
+    {0, "SQLITE_OK: Successful result"},
+    {1, "SQLITE_ERROR: SQL error or missing database"},
+    {2, "SQLITE_INTERNAL: Internal logic error in SQLite"},
+    {3, "SQLITE_PERM: Access permission denied"},
+    {4, "SQLITE_ABORT: Callback routine requested an abort"},
+    {5, "SQLITE_BUSY: The database file is locked"},
+    {6, "SQLITE_LOCKED: A table in the database is locked"},
+    {7, "SQLITE_NOMEM: A malloc() failed"},
+    {8, "SQLITE_READONLY: Attempt to write a readonly database"},
+    {9, "SQLITE_INTERRUPT: Operation terminated by sqlite3_interrupt()"},
+    {10, "SQLITE_IOERR: Some kind of disk I/O error occurred"},
+    {11, "SQLITE_CORRUPT: The database disk image is malformed"},
+    {12, "SQLITE_NOTFOUND: Unknown opcode in sqlite3_file_control()"},
+    {13, "SQLITE_FULL: Insertion failed because database is full"},
+    {14, "SQLITE_CANTOPEN: Unable to open the database file"},
+    {15, "SQLITE_PROTOCOL: Database lock protocol error"},
+    {16, "SQLITE_EMPTY: Database is empty"},
+    {17, "SQLITE_SCHEMA: The database schema changed"},
+    {18, "SQLITE_TOOBIG: String or BLOB exceeds size limit"},
+    {19, "SQLITE_CONSTRAINT: Abort due to constraint violation"},
+    {20, "SQLITE_MISMATCH: Data type mismatch"},
+    {21, "SQLITE_MISUSE: Library used incorrectly"},
+    {22, "SQLITE_NOLFS: Uses OS features not supported on host"},
+    {23, "SQLITE_AUTH: Authorization denied"},
+    {24, "SQLITE_FORMAT: Auxiliary database format error"},
+    {25, "SQLITE_RANGE: 2nd parameter to sqlite3_bind out of range"},
+    {26, "SQLITE_NOTADB: File opened that is not a database file"},
+    {27, "SQLITE_NOTICE: Notifications from sqlite3_log()"},
+    {28, "SQLITE_WARNING: Warnings from sqlite3_log()"},
+    {100, "SQLITE_ROW: sqlite3_step() has another row ready"},
+    {101, "SQLITE_DONE: sqlite3_step() has finished executing"},
+};
+
+std::string getStringForSQLiteReturnCode(int code) {
+  if (kSQLiteReturnCodes.find(code) != kSQLiteReturnCodes.end()) {
+    return kSQLiteReturnCodes.at(code);
+  } else {
+    std::ostringstream s;
+    s << "Error: " << code << " is not a valid SQLite result code";
+    return s.str();
+  }
+}
+
+Status SQLiteSQLPlugin::attach(const std::string& name) {
+  // This may be the managed DB, or a transient.
+  auto dbc = SQLiteDBManager::get();
+  if (!dbc.isPrimary()) {
+    // Do not "reattach" to transient instance.
+    return Status(0, "OK");
+  }
+
+  PluginResponse response;
+  auto status =
+      Registry::call("table", name, {{"action", "columns"}}, response);
+  if (!status.ok()) {
+    return status;
+  }
+
+  auto statement = columnDefinition(response);
+  return attachTableInternal(name, statement, dbc.db());
+}
+
+void SQLiteSQLPlugin::detach(const std::string& name) {
+  auto dbc = SQLiteDBManager::get();
+  if (!dbc.isPrimary()) {
+    return;
+  }
+  detachTableInternal(name, dbc.db());
+}
+
+SQLiteDBInstance::SQLiteDBInstance() {
+  primary_ = false;
+  sqlite3_open(":memory:", &db_);
+  attachVirtualTables(db_);
+}
+
+SQLiteDBInstance::SQLiteDBInstance(sqlite3*& db) {
+  primary_ = true;
+  db_ = db;
+}
+
+SQLiteDBInstance::~SQLiteDBInstance() {
+  if (!primary_) {
+    sqlite3_close(db_);
+  } else {
+    SQLiteDBManager::unlock();
+    db_ = nullptr;
+  }
+}
+
+void SQLiteDBManager::unlock() { instance().lock_.unlock(); }
+
+bool SQLiteDBManager::isDisabled(const std::string& table_name) {
+  const auto& element = instance().disabled_tables_.find(table_name);
+  return (element != instance().disabled_tables_.end());
+}
+
+std::unordered_set<std::string> SQLiteDBManager::parseDisableTablesFlag(
+    const std::string& list) {
+  const auto& tables = split(list, ",");
+  return std::unordered_set<std::string>(tables.begin(), tables.end());
+}
+
+SQLiteDBInstance SQLiteDBManager::getUnique() { return SQLiteDBInstance(); }
+
+SQLiteDBInstance SQLiteDBManager::get() {
+  auto& self = instance();
+
+  if (!self.lock_.owns_lock() && self.lock_.try_lock()) {
+    if (self.db_ == nullptr) {
+      // Create primary SQLite DB instance.
+      sqlite3_open(":memory:", &self.db_);
+      attachVirtualTables(self.db_);
+    }
+    return SQLiteDBInstance(self.db_);
+  } else {
+    // If this thread or another has the lock, return a transient db.
+    VLOG(1) << "DBManager contention: opening transient SQLite database";
+    return SQLiteDBInstance();
+  }
+}
+
+SQLiteDBManager::~SQLiteDBManager() {
+  if (db_ != nullptr) {
+    sqlite3_close(db_);
+    db_ = nullptr;
+  }
+}
+
+int queryDataCallback(void* argument, int argc, char* argv[], char* column[]) {
+  if (argument == nullptr) {
+    VLOG(1) << "Query execution failed: received a bad callback argument";
+    return SQLITE_MISUSE;
+  }
+
+  QueryData* qData = (QueryData*)argument;
+  Row r;
+  for (int i = 0; i < argc; i++) {
+    if (column[i] != nullptr) {
+      r[column[i]] = (argv[i] != nullptr) ? argv[i] : "";
+    }
+  }
+  (*qData).push_back(std::move(r));
+  return 0;
+}
+
+Status queryInternal(const std::string& q, QueryData& results, sqlite3* db) {
+  char* err = nullptr;
+  sqlite3_exec(db, q.c_str(), queryDataCallback, &results, &err);
+  sqlite3_db_release_memory(db);
+  if (err != nullptr) {
+    auto error_string = std::string(err);
+    sqlite3_free(err);
+    return Status(1, "Error running query: " + error_string);
+  }
+
+  return Status(0, "OK");
+}
+
+Status getQueryColumnsInternal(const std::string& q,
+                               TableColumns& columns,
+                               sqlite3* db) {
+  int rc;
+
+  // Will automatically handle calling sqlite3_finalize on the prepared stmt
+  // (Note that sqlite3_finalize is explicitly a nop for nullptr)
+  std::unique_ptr<sqlite3_stmt, decltype(sqlite3_finalize)*> stmt_managed(
+      nullptr, sqlite3_finalize);
+  sqlite3_stmt* stmt = stmt_managed.get();
+
+  // Turn the query into a prepared statement
+  rc = sqlite3_prepare_v2(db, q.c_str(), q.length() + 1, &stmt, nullptr);
+  if (rc != SQLITE_OK) {
+    return Status(1, sqlite3_errmsg(db));
+  }
+
+  // Get column count
+  int num_columns = sqlite3_column_count(stmt);
+  TableColumns results;
+  results.reserve(num_columns);
+
+  // Get column names and types
+  for (int i = 0; i < num_columns; ++i) {
+    const char* col_name = sqlite3_column_name(stmt, i);
+    const char* col_type = sqlite3_column_decltype(stmt, i);
+    if (col_name == nullptr) {
+      return Status(1, "Got nullptr for column name");
+    }
+    if (col_type == nullptr) {
+      // Types are only returned for table columns (not expressions or
+      // subqueries). See docs for column_decltype
+      // (https://www.sqlite.org/c3ref/column_decltype.html).
+      col_type = "UNKNOWN";
+    }
+    results.push_back({col_name, col_type});
+  }
+
+  columns = std::move(results);
+
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/sql/sqlite_util.h b/src/osquery/sql/sqlite_util.h
new file mode 100644 (file)
index 0000000..f903867
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <map>
+#include <mutex>
+#include <unordered_set>
+
+#include <sqlite3.h>
+
+#include <boost/thread/mutex.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <osquery/sql.h>
+
+#define SQLITE_SOFT_HEAP_LIMIT (5 * 1024 * 1024)
+
+namespace osquery {
+
+/**
+ * @brief An RAII wrapper around an `sqlite3` object.
+ *
+ * The SQLiteDBInstance is also "smart" in that it may unlock access to a
+ * managed `sqlite3` resource. If there's no contention then only a single
+ * database is needed during the life of an osquery tool.
+ *
+ * If there is resource contention (multiple threads want access to the SQLite
+ * abstraction layer), then the SQLiteDBManager will provide a transient
+ * SQLiteDBInstance.
+ */
+class SQLiteDBInstance {
+ public:
+  SQLiteDBInstance();
+  explicit SQLiteDBInstance(sqlite3*& db);
+  ~SQLiteDBInstance();
+
+  /// Check if the instance is the osquery primary.
+  bool isPrimary() { return primary_; }
+
+  /**
+   * @brief Accessor to the internal `sqlite3` object, do not store references
+   * to the object within osquery code.
+   */
+  sqlite3* db() { return db_; }
+
+ private:
+  bool primary_;
+  sqlite3* db_;
+};
+
+/**
+ * @brief osquery internal SQLite DB abstraction resource management.
+ *
+ * The SQLiteDBManager should be the ONLY method for accessing SQLite resources.
+ * The manager provides an abstraction to manage internal SQLite memory and
+ * resources as well as provide optimization around resource access.
+ */
+class SQLiteDBManager : private boost::noncopyable {
+ public:
+  static SQLiteDBManager& instance() {
+    static SQLiteDBManager instance;
+    return instance;
+  }
+
+  /**
+   * @brief Return a fully configured `sqlite3` database object wrapper.
+   *
+   * An osquery database is basically just a SQLite3 database with several
+   * virtual tables attached. This method is the main abstraction for accessing
+   * SQLite3 databases within osquery.
+   *
+   * A RAII wrapper around the `sqlite3` database will manage attaching tables
+   * and freeing resources when the instance (connection per-say) goes out of
+   * scope. Using the SQLiteDBManager will also try to optimize the number of
+   * `sqlite3` databases in use by managing a single global instance and
+   * returning resource-safe transient databases if there's access contention.
+   *
+   * Note: osquery::initOsquery must be called before calling `get` in order
+   * for virtual tables to be registered.
+   *
+   * @return a SQLiteDBInstance with all virtual tables attached.
+   */
+  static SQLiteDBInstance get();
+
+  /// See `get` but always return a transient DB connection (for testing).
+  static SQLiteDBInstance getUnique();
+
+  /**
+   * @brief Check if `table_name` is disabled.
+   *
+   * Check if `table_name` is in the list of tables passed in to the
+   * `--disable_tables` flag.
+   *
+   * @param The name of the Table to check.
+   * @return If `table_name` is disabled.
+   */
+  static bool isDisabled(const std::string& table_name);
+
+  /// When the primary SQLiteDBInstance is destructed it will unlock.
+  static void unlock();
+
+ protected:
+  SQLiteDBManager() : db_(nullptr), lock_(mutex_, boost::defer_lock) {
+    sqlite3_soft_heap_limit64(SQLITE_SOFT_HEAP_LIMIT);
+    disabled_tables_ = parseDisableTablesFlag(Flag::getValue("disable_tables"));
+  }
+  SQLiteDBManager(SQLiteDBManager const&);
+  SQLiteDBManager& operator=(SQLiteDBManager const&);
+  virtual ~SQLiteDBManager();
+
+ private:
+  /// Primary (managed) sqlite3 database.
+  sqlite3* db_;
+  /// Mutex and lock around sqlite3 access.
+  boost::mutex mutex_;
+  /// Mutex and lock around sqlite3 access.
+  boost::unique_lock<boost::mutex> lock_;
+  /// Member variable to hold set of disabled tables.
+  std::unordered_set<std::string> disabled_tables_;
+  /// Parse a comma-delimited set of tables names, passed in as a flag.
+  std::unordered_set<std::string> parseDisableTablesFlag(const std::string& s);
+};
+
+/**
+ * @brief SQLite Internal: Execute a query on a specific database
+ *
+ * If you need to use a different database, other than the osquery default,
+ * use this method and pass along a pointer to a SQLite3 database. This is
+ * useful for testing.
+ *
+ * @param q the query to execute
+ * @param results The QueryData struct to emit row on query success.
+ * @param db the SQLite3 database to execute query q against
+ *
+ * @return A status indicating SQL query results.
+ */
+Status queryInternal(const std::string& q, QueryData& results, sqlite3* db);
+
+/**
+ * @brief SQLite Intern: Analyze a query, providing information about the
+ * result columns
+ *
+ * This function asks SQLite to determine what the names and types are of the
+ * result columns of the provided query. Only table columns (not expressions or
+ * subqueries) can have their types determined. Types that are not determined
+ * are indicated with the string "UNKNOWN".
+ *
+ * @param q the query to analyze
+ * @param columns the vector to fill with column information
+ * @param db the SQLite3 database to perform the analysis on
+ *
+ * @return status indicating success or failure of the operation
+ */
+Status getQueryColumnsInternal(const std::string& q,
+                               TableColumns& columns,
+                               sqlite3* db);
+
+/// The SQLiteSQLPlugin implements the "sql" registry for internal/core.
+class SQLiteSQLPlugin : SQLPlugin {
+ public:
+  Status query(const std::string& q, QueryData& results) const {
+    auto dbc = SQLiteDBManager::get();
+    return queryInternal(q, results, dbc.db());
+  }
+
+  Status getQueryColumns(const std::string& q, TableColumns& columns) const {
+    auto dbc = SQLiteDBManager::get();
+    return getQueryColumnsInternal(q, columns, dbc.db());
+  }
+
+  /// Create a SQLite module and attach (CREATE).
+  Status attach(const std::string& name);
+  /// Detach a virtual table (DROP).
+  void detach(const std::string& name);
+};
+
+/**
+ * @brief Get a string representation of a SQLite return code
+ */
+std::string getStringForSQLiteReturnCode(int code);
+
+/**
+ * @brief Accumulate rows from an SQLite exec into a QueryData struct.
+ *
+ * The callback for populating a std::vector<Row> set of results. "argument"
+ * should be a non-const reference to a std::vector<Row>.
+ */
+int queryDataCallback(void* argument, int argc, char* argv[], char* column[]);
+}
diff --git a/src/osquery/sql/tests/sql_tests.cpp b/src/osquery/sql/tests/sql_tests.cpp
new file mode 100644 (file)
index 0000000..6d994ca
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/core.h>
+#include <osquery/registry.h>
+#include <osquery/sql.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+
+class SQLTests : public testing::Test {};
+
+TEST_F(SQLTests, test_raw_access) {
+  // Access to the table plugins (no SQL parsing required) works in both
+  // extensions and core, though with limitations on available tables.
+  auto results = SQL::selectAllFrom("time");
+  EXPECT_EQ(results.size(), 1);
+}
+
+class TestTablePlugin : public TablePlugin {
+ private:
+  TableColumns columns() const {
+    return {{"test_int", "INTEGER"}, {"test_text", "TEXT"}};
+  }
+
+  QueryData generate(QueryContext& ctx) {
+    QueryData results;
+    if (ctx.constraints["test_int"].existsAndMatches("1")) {
+      results.push_back({{"test_int", "1"}, {"test_text", "0"}});
+    } else {
+      results.push_back({{"test_int", "0"}, {"test_text", "1"}});
+    }
+
+    auto ints = ctx.constraints["test_int"].getAll<int>(EQUALS);
+    for (const auto& int_match : ints) {
+      results.push_back({{"test_int", INTEGER(int_match)}});
+    }
+
+    return results;
+  }
+};
+
+TEST_F(SQLTests, test_raw_access_context) {
+  Registry::add<TestTablePlugin>("table", "test");
+  auto results = SQL::selectAllFrom("test");
+
+  EXPECT_EQ(results.size(), 1);
+  EXPECT_EQ(results[0]["test_text"], "1");
+
+  results = SQL::selectAllFrom("test", "test_int", EQUALS, "1");
+  EXPECT_EQ(results.size(), 2);
+
+  results = SQL::selectAllFrom("test", "test_int", EQUALS, "2");
+  EXPECT_EQ(results.size(), 2);
+  EXPECT_EQ(results[0]["test_int"], "0");
+}
+}
diff --git a/src/osquery/sql/tests/sqlite_util_tests.cpp b/src/osquery/sql/tests/sqlite_util_tests.cpp
new file mode 100644 (file)
index 0000000..c2f6c5a
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include <osquery/core.h>
+#include <osquery/sql.h>
+
+#include "osquery/core/test_util.h"
+#include "osquery/sql/sqlite_util.h"
+
+namespace osquery {
+
+class SQLiteUtilTests : public testing::Test {};
+
+SQLiteDBInstance getTestDBC() {
+  SQLiteDBInstance dbc = SQLiteDBManager::getUnique();
+  char* err = nullptr;
+  std::vector<std::string> queries = {
+      "CREATE TABLE test_table (username varchar(30) primary key, age int)",
+      "INSERT INTO test_table VALUES (\"mike\", 23)",
+      "INSERT INTO test_table VALUES (\"matt\", 24)"};
+
+  for (auto q : queries) {
+    sqlite3_exec(dbc.db(), q.c_str(), nullptr, nullptr, &err);
+    if (err != nullptr) {
+      throw std::domain_error(std::string("Cannot create testing DBC's db: ") +
+                              err);
+    }
+  }
+
+  return dbc;
+}
+
+TEST_F(SQLiteUtilTests, test_simple_query_execution) {
+  // Access to the internal SQL implementation is only available in core.
+  auto sql = SQL("SELECT * FROM time");
+  EXPECT_TRUE(sql.ok());
+  EXPECT_EQ(sql.rows().size(), 1);
+}
+
+TEST_F(SQLiteUtilTests, test_get_tables) {
+  // Access to the internal SQL implementation is only available in core.
+  auto tables = SQL::getTableNames();
+  EXPECT_TRUE(tables.size() > 0);
+}
+
+TEST_F(SQLiteUtilTests, test_sqlite_instance_manager) {
+  auto dbc1 = SQLiteDBManager::get();
+  auto dbc2 = SQLiteDBManager::get();
+  EXPECT_NE(dbc1.db(), dbc2.db());
+  EXPECT_EQ(dbc1.db(), dbc1.db());
+}
+
+TEST_F(SQLiteUtilTests, test_sqlite_instance) {
+  // Don't do this at home kids.
+  // Keep a copy of the internal DB and let the SQLiteDBInstance go oos.
+  auto internal_db = SQLiteDBManager::get().db();
+  // Compare the internal DB to another request with no SQLiteDBInstances
+  // in scope, meaning the primary will be returned.
+  EXPECT_EQ(internal_db, SQLiteDBManager::get().db());
+}
+
+TEST_F(SQLiteUtilTests, test_direct_query_execution) {
+  auto dbc = getTestDBC();
+  QueryData results;
+  auto status = queryInternal(kTestQuery, results, dbc.db());
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results, getTestDBExpectedResults());
+}
+
+TEST_F(SQLiteUtilTests, test_passing_callback_no_data_param) {
+  char* err = nullptr;
+  auto dbc = getTestDBC();
+  sqlite3_exec(dbc.db(), kTestQuery.c_str(), queryDataCallback, nullptr, &err);
+  EXPECT_TRUE(err != nullptr);
+  if (err != nullptr) {
+    sqlite3_free(err);
+  }
+}
+
+TEST_F(SQLiteUtilTests, test_aggregate_query) {
+  auto dbc = getTestDBC();
+  QueryData results;
+  auto status = queryInternal(kTestQuery, results, dbc.db());
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results, getTestDBExpectedResults());
+}
+
+TEST_F(SQLiteUtilTests, test_get_test_db_result_stream) {
+  auto dbc = getTestDBC();
+  auto results = getTestDBResultStream();
+  for (auto r : results) {
+    char* err_char = nullptr;
+    sqlite3_exec(dbc.db(), (r.first).c_str(), nullptr, nullptr, &err_char);
+    EXPECT_TRUE(err_char == nullptr);
+    if (err_char != nullptr) {
+      sqlite3_free(err_char);
+      ASSERT_TRUE(false);
+    }
+
+    QueryData expected;
+    auto status = queryInternal(kTestQuery, expected, dbc.db());
+    EXPECT_EQ(expected, r.second);
+  }
+}
+
+TEST_F(SQLiteUtilTests, test_get_query_columns) {
+  auto dbc = getTestDBC();
+  TableColumns results;
+
+  std::string query = "SELECT seconds, version FROM time JOIN osquery_info";
+  auto status = getQueryColumnsInternal(query, results, dbc.db());
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(2, results.size());
+  EXPECT_EQ(std::make_pair(std::string("seconds"), std::string("INTEGER")),
+            results[0]);
+  EXPECT_EQ(std::make_pair(std::string("version"), std::string("TEXT")),
+            results[1]);
+
+  query = "SELECT hour + 1 AS hour1, minutes + 1 FROM time";
+  status = getQueryColumnsInternal(query, results, dbc.db());
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(2, results.size());
+  EXPECT_EQ(std::make_pair(std::string("hour1"), std::string("UNKNOWN")),
+            results[0]);
+  EXPECT_EQ(std::make_pair(std::string("minutes + 1"), std::string("UNKNOWN")),
+            results[1]);
+
+  query = "SELECT * FROM foo";
+  status = getQueryColumnsInternal(query, results, dbc.db());
+  ASSERT_FALSE(status.ok());
+}
+}
diff --git a/src/osquery/sql/tests/virtual_table_tests.cpp b/src/osquery/sql/tests/virtual_table_tests.cpp
new file mode 100644 (file)
index 0000000..c08cce6
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/core.h>
+#include <osquery/registry.h>
+#include <osquery/sql.h>
+
+#include "osquery/sql/virtual_table.h"
+
+namespace osquery {
+
+class VirtualTableTests : public testing::Test {};
+
+// sample plugin used on tests
+class sampleTablePlugin : public TablePlugin {
+ private:
+  TableColumns columns() const {
+    return {
+        {"foo", "INTEGER"}, {"bar", "TEXT"},
+    };
+  }
+};
+
+TEST_F(VirtualTableTests, test_tableplugin_columndefinition) {
+  auto table = std::make_shared<sampleTablePlugin>();
+  EXPECT_EQ("(foo INTEGER, bar TEXT)", table->columnDefinition());
+}
+
+TEST_F(VirtualTableTests, test_sqlite3_attach_vtable) {
+  auto table = std::make_shared<sampleTablePlugin>();
+  table->setName("sample");
+
+  // Request a managed "connection".
+  // This will be a single (potentially locked) instance or a transient
+  // SQLite database if there is contention and a lock was not requested.
+  auto dbc = SQLiteDBManager::get();
+
+  // Virtual tables require the registry/plugin API to query tables.
+  auto status = attachTableInternal("failed_sample", "(foo INTEGER)", dbc.db());
+  EXPECT_EQ(status.getCode(), SQLITE_ERROR);
+
+  // The table attach will complete only when the table name is registered.
+  Registry::add<sampleTablePlugin>("table", "sample");
+  PluginResponse response;
+  status = Registry::call("table", "sample", {{"action", "columns"}}, response);
+  EXPECT_TRUE(status.ok());
+
+  // Use the table name, plugin-generated schema to attach.
+  status = attachTableInternal("sample", columnDefinition(response), dbc.db());
+  EXPECT_EQ(status.getCode(), SQLITE_OK);
+
+  std::string q = "SELECT sql FROM sqlite_temp_master WHERE tbl_name='sample';";
+  QueryData results;
+  status = queryInternal(q, results, dbc.db());
+  EXPECT_EQ("CREATE VIRTUAL TABLE sample USING sample(foo INTEGER, bar TEXT)",
+            results[0]["sql"]);
+}
+
+TEST_F(VirtualTableTests, test_sqlite3_table_joins) {
+  // Get a database connection.
+  auto dbc = SQLiteDBManager::get();
+
+  QueryData results;
+  // Run a query with a join within.
+  std::string statement =
+      "SELECT p.pid FROM osquery_info oi, processes p WHERE oi.pid=p.pid";
+  auto status = queryInternal(statement, results, dbc.db());
+  EXPECT_TRUE(status.ok());
+  EXPECT_EQ(results.size(), 1);
+}
+
+TEST_F(VirtualTableTests, test_sqlite3_table_update_where) {
+  // Get a database connection.
+  auto dbc = SQLiteDBManager::get();
+
+  QueryData results;
+  std::string statement = "UPDATE users SET uid = 1234, gid = 232 WHERE uid = 0";
+  auto status = queryInternal(statement, results, dbc.db());
+  EXPECT_TRUE(status.ok());
+}
+
+TEST_F(VirtualTableTests, test_sqlite3_table_update) {
+  // Get a database connection.
+  auto dbc = SQLiteDBManager::get();
+
+  QueryData results;
+  std::string statement = "UPDATE users SET uid = 1234, gid = 232";
+  auto status = queryInternal(statement, results, dbc.db());
+  EXPECT_TRUE(status.ok());
+}
+}
diff --git a/src/osquery/sql/virtual_table.cpp b/src/osquery/sql/virtual_table.cpp
new file mode 100644 (file)
index 0000000..1a51ad9
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/logger.h>
+
+#include "osquery/sql/virtual_table.h"
+
+namespace osquery {
+namespace tables {
+
+int xOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
+  int rc = SQLITE_NOMEM;
+  BaseCursor *pCur;
+
+  pCur = new BaseCursor;
+
+  if (pCur) {
+    memset(pCur, 0, sizeof(BaseCursor));
+    *ppCursor = (sqlite3_vtab_cursor *)pCur;
+    rc = SQLITE_OK;
+  }
+
+  return rc;
+}
+
+int xClose(sqlite3_vtab_cursor *cur) {
+  BaseCursor *pCur = (BaseCursor *)cur;
+
+  delete pCur;
+  return SQLITE_OK;
+}
+
+int xEof(sqlite3_vtab_cursor *cur) {
+  BaseCursor *pCur = (BaseCursor *)cur;
+  auto *pVtab = (VirtualTable *)cur->pVtab;
+  return pCur->row >= pVtab->content->n;
+}
+
+int xDestroy(sqlite3_vtab *p) {
+  auto *pVtab = (VirtualTable *)p;
+  delete pVtab->content;
+  delete pVtab;
+  return SQLITE_OK;
+}
+
+int xNext(sqlite3_vtab_cursor *cur) {
+  BaseCursor *pCur = (BaseCursor *)cur;
+  pCur->row++;
+  return SQLITE_OK;
+}
+
+int xRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) {
+  BaseCursor *pCur = (BaseCursor *)cur;
+  *pRowid = pCur->row;
+  return SQLITE_OK;
+}
+
+int xUpdate(sqlite3_vtab *pVTab,
+            int argc,
+            sqlite3_value **argv,
+            sqlite3_int64 *pRowid)
+{
+  auto * pVtab = (VirtualTable *)pVTab;
+  if (argc <= 1 || argc - 2 !=  pVtab->content->columns.size()) {
+    LOG(ERROR) << "Invalid arguments: " << argc;
+    return SQLITE_ERROR;
+  }
+
+  PluginRequest request = {{"action", "update"}};
+  const auto& columns = pVtab->content->columns;
+  for (size_t i = 2; i < static_cast<size_t>(argc); ++i) {
+    auto expr = (const char *)sqlite3_value_text(argv[i]);
+    if (expr == nullptr) {
+      // SQLite did not expose the expression value.
+      continue;
+    } else {
+       request.insert(std::make_pair(columns[i - 2].first, std::string(expr)));
+    }
+  }
+
+  PluginResponse response;
+  Registry::call("table", pVtab->content->name, request, response);
+
+  return SQLITE_OK;
+}
+
+int xCreate(sqlite3 *db,
+            void *pAux,
+            int argc,
+            const char *const *argv,
+            sqlite3_vtab **ppVtab,
+            char **pzErr) {
+  auto *pVtab = new VirtualTable;
+
+  if (!pVtab || argc == 0 || argv[0] == nullptr) {
+    return SQLITE_NOMEM;
+  }
+
+  memset(pVtab, 0, sizeof(VirtualTable));
+  pVtab->content = new VirtualTableContent;
+
+  PluginResponse response;
+  pVtab->content->name = std::string(argv[0]);
+
+  // Get the table column information.
+  auto status = Registry::call(
+      "table", pVtab->content->name, {{"action", "columns"}}, response);
+  if (!status.ok() || response.size() == 0) {
+    return SQLITE_ERROR;
+  }
+
+  auto statement =
+      "CREATE TABLE " + pVtab->content->name + columnDefinition(response);
+  int rc = sqlite3_declare_vtab(db, statement.c_str());
+  if (rc != SQLITE_OK) {
+    return rc;
+  }
+
+  if (!status.ok() || response.size() == 0) {
+    return SQLITE_ERROR;
+  }
+
+  for (const auto &column : response) {
+    pVtab->content->columns.push_back(
+        std::make_pair(column.at("name"), column.at("type")));
+  }
+
+  *ppVtab = (sqlite3_vtab *)pVtab;
+  return rc;
+}
+
+int xColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) {
+  BaseCursor *pCur = (BaseCursor *)cur;
+  auto *pVtab = (VirtualTable *)cur->pVtab;
+
+  if (col >= pVtab->content->columns.size()) {
+    return SQLITE_ERROR;
+  }
+
+  auto &column_name = pVtab->content->columns[col].first;
+  auto &type = pVtab->content->columns[col].second;
+  if (pCur->row >= pVtab->content->data[column_name].size()) {
+    return SQLITE_ERROR;
+  }
+
+  // Attempt to cast each xFilter-populated row/column to the SQLite type.
+  auto &value = pVtab->content->data[column_name][pCur->row];
+  if (type == "TEXT") {
+    sqlite3_result_text(ctx, value.c_str(), value.size(), SQLITE_STATIC);
+  } else if (type == "INTEGER") {
+    int afinite;
+    try {
+      afinite = boost::lexical_cast<int>(value);
+    } catch (const boost::bad_lexical_cast &e) {
+      afinite = -1;
+      VLOG(1) << "Error casting " << column_name << " (" << value
+              << ") to INTEGER";
+    }
+    sqlite3_result_int(ctx, afinite);
+  } else if (type == "BIGINT") {
+    long long int afinite;
+    try {
+      afinite = boost::lexical_cast<long long int>(value);
+    } catch (const boost::bad_lexical_cast &e) {
+      afinite = -1;
+      VLOG(1) << "Error casting " << column_name << " (" << value
+              << ") to BIGINT";
+    }
+    sqlite3_result_int64(ctx, afinite);
+  } else if (type == "DOUBLE") {
+    double afinite;
+    try {
+      afinite = boost::lexical_cast<double>(value);
+    } catch (const boost::bad_lexical_cast &e) {
+      afinite = 0;
+      VLOG(1) << "Error casting" << column_name << " (" << value
+              << ") to DOUBLE";
+    }
+    sqlite3_result_double(ctx, afinite);
+  }
+
+  return SQLITE_OK;
+}
+
+static int xBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
+  auto *pVtab = (VirtualTable *)tab;
+  pVtab->content->constraints.clear();
+
+  int expr_index = 0;
+  int cost = 0;
+  for (size_t i = 0; i < pIdxInfo->nConstraint; ++i) {
+    if (!pIdxInfo->aConstraint[i].usable) {
+      // A higher cost less priority, prefer more usable query constraints.
+      cost += 10;
+      // TODO: OR is not usable.
+      continue;
+    }
+
+    const auto &name =
+        pVtab->content->columns[pIdxInfo->aConstraint[i].iColumn].first;
+    pVtab->content->constraints.push_back(
+        std::make_pair(name, Constraint(pIdxInfo->aConstraint[i].op)));
+    pIdxInfo->aConstraintUsage[i].argvIndex = ++expr_index;
+  }
+
+  pIdxInfo->estimatedCost = cost;
+  return SQLITE_OK;
+}
+
+static int xFilter(sqlite3_vtab_cursor *pVtabCursor,
+                   int idxNum,
+                   const char *idxStr,
+                   int argc,
+                   sqlite3_value **argv) {
+  BaseCursor *pCur = (BaseCursor *)pVtabCursor;
+  auto *pVtab = (VirtualTable *)pVtabCursor->pVtab;
+
+  pCur->row = 0;
+  pVtab->content->n = 0;
+  QueryContext context;
+
+  for (size_t i = 0; i < pVtab->content->columns.size(); ++i) {
+    // Clear any data, this is the result container for each column + row.
+    pVtab->content->data[pVtab->content->columns[i].first].clear();
+    // Set the column affinity for each optional constraint list.
+    // There is a separate list for each column name.
+    context.constraints[pVtab->content->columns[i].first].affinity =
+        pVtab->content->columns[i].second;
+  }
+
+  // Iterate over every argument to xFilter, filling in constraint values.
+  for (size_t i = 0; i < argc; ++i) {
+    auto expr = (const char *)sqlite3_value_text(argv[i]);
+    if (expr == nullptr) {
+      // SQLite did not expose the expression value.
+      continue;
+    }
+    // Set the expression from SQLite's now-populated argv.
+    pVtab->content->constraints[i].second.expr = std::string(expr);
+    // Add the constraint to the column-sorted query request map.
+    context.constraints[pVtab->content->constraints[i].first].add(
+        pVtab->content->constraints[i].second);
+  }
+
+  PluginRequest request;
+  PluginResponse response;
+  request["action"] = "generate";
+  TablePlugin::setRequestFromContext(context, request);
+  Registry::call("table", pVtab->content->name, request, response);
+
+  // Now organize the response rows by column instead of row.
+  auto &data = pVtab->content->data;
+  for (auto &row : response) {
+    for (const auto &column : pVtab->content->columns) {
+      if (row.count(column.first) == 0) {
+        VLOG(1) << "Table " << pVtab->content->name << " row "
+                << pVtab->content->n << " did not include column "
+                << column.first;
+        data[column.first].push_back("");
+        continue;
+      }
+
+      auto &value = row.at(column.first);
+      if (value.size() > FLAGS_value_max) {
+        data[column.first].push_back(value.substr(0, FLAGS_value_max));
+        value.clear();
+      } else {
+        data[column.first].push_back(std::move(value));
+      }
+    }
+
+    pVtab->content->n++;
+  }
+
+  return SQLITE_OK;
+}
+}
+
+Status attachTableInternal(const std::string &name,
+                           const std::string &statement,
+                           sqlite3 *db) {
+  if (SQLiteDBManager::isDisabled(name)) {
+    VLOG(0) << "Table " << name << " is disabled, not attaching";
+    return Status(0, getStringForSQLiteReturnCode(0));
+  }
+
+  // A static module structure does not need specific logic per-table.
+  // clang-format off
+  static sqlite3_module module = {
+      0,
+      tables::xCreate,
+      tables::xCreate,
+      tables::xBestIndex,
+      tables::xDestroy,
+      tables::xDestroy,
+      tables::xOpen,
+      tables::xClose,
+      tables::xFilter,
+      tables::xNext,
+      tables::xEof,
+      tables::xColumn,
+      tables::xRowid,
+      tables::xUpdate,
+  };
+  // clang-format on
+
+  // Note, if the clientData API is used then this will save a registry call
+  // within xCreate.
+  int rc = sqlite3_create_module(db, name.c_str(), &module, 0);
+  if (rc == SQLITE_OK || rc == SQLITE_MISUSE) {
+    auto format =
+        "CREATE VIRTUAL TABLE temp." + name + " USING " + name + statement;
+    rc = sqlite3_exec(db, format.c_str(), nullptr, nullptr, 0);
+  } else {
+    LOG(ERROR) << "Error attaching table: " << name << " (" << rc << ")";
+  }
+  return Status(rc, getStringForSQLiteReturnCode(rc));
+}
+
+Status detachTableInternal(const std::string &name, sqlite3 *db) {
+  auto format = "DROP TABLE IF EXISTS temp." + name;
+  int rc = sqlite3_exec(db, format.c_str(), nullptr, nullptr, 0);
+  if (rc != SQLITE_OK) {
+    LOG(ERROR) << "Error detaching table: " << name << " (" << rc << ")";
+  }
+
+  return Status(rc, getStringForSQLiteReturnCode(rc));
+}
+
+void attachVirtualTables(sqlite3 *db) {
+  PluginResponse response;
+  for (const auto &name : Registry::names("table")) {
+    // Column information is nice for virtual table create call.
+    auto status =
+        Registry::call("table", name, {{"action", "columns"}}, response);
+    if (status.ok()) {
+      auto statement = columnDefinition(response);
+      attachTableInternal(name, statement, db);
+    }
+  }
+}
+}
diff --git a/src/osquery/sql/virtual_table.h b/src/osquery/sql/virtual_table.h
new file mode 100644 (file)
index 0000000..f962bc0
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <osquery/tables.h>
+
+#include "osquery/sql/sqlite_util.h"
+
+namespace osquery {
+
+/**
+ * @brief osquery cursor object.
+ *
+ * Only used in the SQLite virtual table module methods.
+ */
+struct BaseCursor {
+  /// SQLite virtual table cursor.
+  sqlite3_vtab_cursor base;
+  /// Current cursor position.
+  int row;
+};
+
+struct VirtualTableContent {
+  TableName name;
+  TableColumns columns;
+  TableData data;
+  ConstraintSet constraints;
+  size_t n;
+};
+
+/**
+ * @brief osquery virtual table object
+ *
+ * Only used in the SQLite virtual table module methods.
+ * This adds each table plugin class to the state tracking in SQLite.
+ */
+struct VirtualTable {
+  sqlite3_vtab base;
+  VirtualTableContent *content;
+};
+
+/// Attach a table plugin name to an in-memory SQLite database.
+Status attachTableInternal(const std::string &name,
+                           const std::string &statement,
+                           sqlite3 *db);
+
+/// Detach (drop) a table.
+Status detachTableInternal(const std::string &name, sqlite3 *db);
+
+/// Attach all table plugins to an in-memory SQLite database.
+void attachVirtualTables(sqlite3 *db);
+}
diff --git a/src/osquery/tables/CMakeLists.txt b/src/osquery/tables/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f55e3df
--- /dev/null
@@ -0,0 +1,30 @@
+#  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+#
+#  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
+
+FILE(GLOB OSQUERY_LINUX_TABLES "*/linux/*.cpp")
+ADD_OSQUERY_LIBRARY(osquery_linux_tables ${OSQUERY_LINUX_TABLES})
+
+FILE(GLOB OSQUERY_CROSS_TABLES "[!t]*/*.cpp")
+ADD_OSQUERY_LIBRARY(osquery_tables ${OSQUERY_CROSS_TABLES})
+
+FILE(GLOB OSQUERY_CROSS_TABLES_TESTS "[!uot]*/tests/*.cpp")
+ADD_OSQUERY_TEST(${OSQUERY_CROSS_TABLES_TESTS})
+
+IF(DEFINED GBS_BUILD)
+       FILE(GLOB OSQUERY_TIZEN_TABLES "tizen/*.cpp")
+       ADD_OSQUERY_LIBRARY(osquery_tizen_tables ${OSQUERY_TIZEN_TABLES})
+
+       FILE(GLOB OSQUERY_TIZEN_TESTS "tizen/tests/*.cpp")
+       ADD_OSQUERY_TEST(${OSQUERY_TIZEN_TESTS})
+ENDIF(DEFINED GBS_BUILD)
diff --git a/src/osquery/tables/events/linux/file_events.cpp b/src/osquery/tables/events/linux/file_events.cpp
new file mode 100644 (file)
index 0000000..91a014f
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <osquery/core.h>
+#include <osquery/config.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+#include <osquery/hash.h>
+
+#include "osquery/events/linux/inotify.h"
+
+namespace osquery {
+
+/**
+ * @brief Track time, action changes to /etc/passwd
+ *
+ * This is mostly an example EventSubscriber implementation.
+ */
+class FileEventSubscriber
+    : public EventSubscriber<INotifyEventPublisher> {
+ public:
+  Status init();
+
+  /**
+   * @brief This exports a single Callback for INotifyEventPublisher events.
+   *
+   * @param ec The EventCallback type receives an EventContextRef substruct
+   * for the INotifyEventPublisher declared in this EventSubscriber subclass.
+   *
+   * @return Was the callback successful.
+   */
+  Status Callback(const INotifyEventContextRef& ec, const void* user_data);
+};
+
+/**
+ * @brief Each EventSubscriber must register itself so the init method is
+ *called.
+ *
+ * This registers FileEventSubscriber into the osquery EventSubscriber
+ * pseudo-plugin registry.
+ */
+REGISTER(FileEventSubscriber, "event_subscriber", "file_events");
+
+Status FileEventSubscriber::init() {
+  ConfigDataInstance config;
+  for (const auto& element_kv : config.files()) {
+    for (const auto& file : element_kv.second) {
+      VLOG(1) << "Added listener to: " << file;
+      auto mc = createSubscriptionContext();
+      // Use the filesystem globbing pattern to determine recursiveness.
+      mc->recursive = 0;
+      mc->path = file;
+      mc->mask = IN_ATTRIB | IN_MODIFY | IN_DELETE | IN_CREATE;
+      subscribe(&FileEventSubscriber::Callback, mc,
+                (void*)(&element_kv.first));
+    }
+  }
+
+  return Status(0, "OK");
+}
+
+Status FileEventSubscriber::Callback(const INotifyEventContextRef& ec,
+                                            const void* user_data) {
+  Row r;
+  r["action"] = ec->action;
+  r["target_path"] = ec->path;
+  if (user_data != nullptr) {
+    r["category"] = *(std::string*)user_data;
+  } else {
+    r["category"] = "Undefined";
+  }
+  r["transaction_id"] = INTEGER(ec->event->cookie);
+
+  if (ec->action == "CREATED" || ec->action == "UPDATED") {
+    r["md5"] = hashFromFile(HASH_TYPE_MD5, ec->path);
+    r["sha1"] = hashFromFile(HASH_TYPE_SHA1, ec->path);
+    r["sha256"] = hashFromFile(HASH_TYPE_SHA256, ec->path);
+  }
+
+  if (ec->action != "" && ec->action != "OPENED") {
+    // A callback is somewhat useless unless it changes the EventSubscriber
+    // state or calls `add` to store a marked up event.
+    add(r, ec->time);
+  }
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/tables/events/linux/hardware_events.cpp b/src/osquery/tables/events/linux/hardware_events.cpp
new file mode 100644 (file)
index 0000000..813a095
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/events/linux/udev.h"
+
+namespace osquery {
+
+/**
+ * @brief Track udev events in Linux
+ */
+class HardwareEventSubscriber : public EventSubscriber<UdevEventPublisher> {
+ public:
+  Status init();
+
+  Status Callback(const UdevEventContextRef& ec, const void* user_data);
+};
+
+REGISTER(HardwareEventSubscriber, "event_subscriber", "hardware_events");
+
+Status HardwareEventSubscriber::init() {
+  auto subscription = createSubscriptionContext();
+  subscription->action = UDEV_EVENT_ACTION_ALL;
+
+  subscribe(&HardwareEventSubscriber::Callback, subscription, nullptr);
+  return Status(0, "OK");
+}
+
+Status HardwareEventSubscriber::Callback(const UdevEventContextRef& ec,
+                                         const void* user_data) {
+  Row r;
+
+  if (ec->devtype.empty()) {
+    // Superfluous hardware event.
+    return Status(0, "Missing type.");
+  } else if (ec->devnode.empty() && ec->driver.empty()) {
+    return Status(0, "Missing node and driver.");
+  }
+
+  struct udev_device *device = ec->device;
+  r["action"] = ec->action_string;
+  r["path"] = ec->devnode;
+  r["type"] = ec->devtype;
+  r["driver"] = ec->driver;
+
+  // UDEV properties.
+  r["model"] = UdevEventPublisher::getValue(device, "ID_MODEL_FROM_DATABASE");
+  if (r["path"].empty() && r["model"].empty()) {
+    // Don't emit mising path/model combos.
+    return Status(0, "Missing path and model.");
+  }
+
+  r["model_id"] = INTEGER(UdevEventPublisher::getValue(device, "ID_MODEL_ID"));
+  r["vendor"] = UdevEventPublisher::getValue(device, "ID_VENDOR_FROM_DATABASE");
+  r["vendor_id"] =
+      INTEGER(UdevEventPublisher::getValue(device, "ID_VENDOR_ID"));
+  r["serial"] =
+      INTEGER(UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT"));
+  r["revision"] = INTEGER(UdevEventPublisher::getValue(device, "ID_REVISION"));
+  add(r, ec->time);
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/tables/events/linux/passwd_changes.cpp b/src/osquery/tables/events/linux/passwd_changes.cpp
new file mode 100644 (file)
index 0000000..6e36380
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/events/linux/inotify.h"
+
+namespace osquery {
+
+/**
+ * @brief Track time, action changes to /etc/passwd
+ *
+ * This is mostly an example EventSubscriber implementation.
+ */
+class PasswdChangesEventSubscriber
+    : public EventSubscriber<INotifyEventPublisher> {
+ public:
+  Status init();
+
+  /**
+   * @brief This exports a single Callback for INotifyEventPublisher events.
+   *
+   * @param ec The EventCallback type receives an EventContextRef substruct
+   * for the INotifyEventPublisher declared in this EventSubscriber subclass.
+   *
+   * @return Was the callback successful.
+   */
+  Status Callback(const INotifyEventContextRef& ec, const void* user_data);
+};
+
+/**
+ * @brief Each EventSubscriber must register itself so the init method is
+ *called.
+ *
+ * This registers PasswdChangesEventSubscriber into the osquery EventSubscriber
+ * pseudo-plugin registry.
+ */
+REGISTER(PasswdChangesEventSubscriber, "event_subscriber", "passwd_changes");
+
+Status PasswdChangesEventSubscriber::init() {
+  auto mc = createSubscriptionContext();
+  mc->path = "/etc/passwd";
+  mc->mask = IN_ATTRIB | IN_MODIFY | IN_DELETE | IN_CREATE;
+  subscribe(&PasswdChangesEventSubscriber::Callback, mc, nullptr);
+  return Status(0, "OK");
+}
+
+Status PasswdChangesEventSubscriber::Callback(const INotifyEventContextRef& ec,
+                                              const void* user_data) {
+  Row r;
+  r["action"] = ec->action;
+  r["target_path"] = ec->path;
+  r["transaction_id"] = INTEGER(ec->event->cookie);
+  if (ec->action != "" && ec->action != "OPENED") {
+    // A callback is somewhat useless unless it changes the EventSubscriber
+    // state
+    // or calls `add` to store a marked up event.
+    add(r, ec->time);
+  }
+  return Status(0, "OK");
+}
+}
diff --git a/src/osquery/tables/networking/etc_hosts.cpp b/src/osquery/tables/networking/etc_hosts.cpp
new file mode 100644 (file)
index 0000000..52d07b0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+QueryData parseEtcHostsContent(const std::string& content) {
+  QueryData results;
+
+  for (const auto& i : split(content, "\n")) {
+    auto line = split(i);
+    if (line.size() == 0 || boost::starts_with(line[0], "#")) {
+      continue;
+    }
+    Row r;
+    r["address"] = line[0];
+    if (line.size() > 1) {
+      std::vector<std::string> hostnames;
+      for (int i = 1; i < line.size(); ++i) {
+        if (boost::starts_with(line[i], "#")) {
+          break;
+        }
+        hostnames.push_back(line[i]);
+      }
+      r["hostnames"] = boost::algorithm::join(hostnames, " ");
+    }
+    results.push_back(r);
+  }
+
+  return results;
+}
+
+QueryData genEtcHosts(QueryContext& context) {
+  std::string content;
+  auto s = osquery::readFile("/etc/hosts", content);
+  if (s.ok()) {
+    return parseEtcHostsContent(content);
+  } else {
+    LOG(ERROR) << "Error reading /etc/hosts: " << s.toString();
+    return {};
+  }
+}
+}
+}
diff --git a/src/osquery/tables/networking/etc_protocols.cpp b/src/osquery/tables/networking/etc_protocols.cpp
new file mode 100644 (file)
index 0000000..63c02e6
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+
+namespace osquery {
+namespace tables {
+
+QueryData parseEtcProtocolsContent(const std::string& content) {
+  QueryData results;
+
+  for (const auto& line : split(content, "\n")) {
+    // Empty line or comment.
+    if (line.size() == 0 || boost::starts_with(line, "#")) {
+      continue;
+    }
+
+    // [0]: name protocol_number alias
+    // [1]: [comment part1]
+    // [2]: [comment part2]
+    // [n]: [comment partn]
+    auto protocol_comment = split(line, "#");
+
+    // [0]: name
+    // [1]: protocol_number
+    // [2]: alias
+    auto protocol_fields = split(protocol_comment[0]);
+    if (protocol_fields.size() < 2) {
+      continue;
+    }
+
+    Row r;
+    r["name"] = TEXT(protocol_fields[0]);
+    r["number"] = INTEGER(protocol_fields[1]);
+    if (protocol_fields.size() > 2) {
+      r["alias"] = TEXT(protocol_fields[2]);
+    }
+
+    // If there is a comment for the service.
+    if (protocol_comment.size() > 1) {
+      // Removes everything except the comment (parts of the comment).
+      protocol_comment.erase(protocol_comment.begin(), protocol_comment.begin() + 1);
+      r["comment"] = TEXT(boost::algorithm::join(protocol_comment, " # "));
+    }
+    results.push_back(r);
+  }
+  return results;
+}
+
+QueryData genEtcProtocols(QueryContext& context) {
+  std::string content;
+  auto s = osquery::readFile("/etc/protocols", content);
+  if (s.ok()) {
+    return parseEtcProtocolsContent(content);
+  } else {
+    TLOG << "Error reading /etc/protocols: " << s.toString();
+    return {};
+  }
+}
+}
+}
diff --git a/src/osquery/tables/networking/etc_services.cpp b/src/osquery/tables/networking/etc_services.cpp
new file mode 100644 (file)
index 0000000..2873a5d
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+
+namespace osquery {
+namespace tables {
+
+QueryData parseEtcServicesContent(const std::string& content) {
+  QueryData results;
+
+  for (const auto& line : split(content, "\n")) {
+    // Empty line or comment.
+    if (line.size() == 0 || boost::starts_with(line, "#")) {
+      continue;
+    }
+
+    // [0]: name port/protocol [aliases]
+    // [1]: [comment part1]
+    // [2]: [comment part2]
+    // [n]: [comment partn]
+    auto service_info_comment = split(line, "#");
+
+    // [0]: name
+    // [1]: port/protocol
+    // [2]: [aliases0]
+    // [3]: [aliases1]
+    // [n]: [aliasesn]
+    auto service_info = split(service_info_comment[0]);
+    if (service_info.size() < 2) {
+      LOG(WARNING) << "Line of /etc/services wasn't properly formatted. "
+                   << "Expected at least 2, got " << service_info.size();
+      continue;
+    }
+
+    // [0]: port [1]: protocol
+    auto service_port_protocol = split(service_info[1], "/");
+    if (service_port_protocol.size() != 2) {
+      LOG(WARNING) << "Line of /etc/services wasn't properly formatted. "
+                   << "Expected 2, got " << service_port_protocol.size();
+      continue;
+    }
+
+    Row r;
+    r["name"] = TEXT(service_info[0]);
+    r["port"] = INTEGER(service_port_protocol[0]);
+    r["protocol"] = TEXT(service_port_protocol[1]);
+
+    // Removes the name and the port/protcol elements.
+    service_info.erase(service_info.begin(), service_info.begin() + 2);
+    r["aliases"] = TEXT(boost::algorithm::join(service_info, " "));
+
+    // If there is a comment for the service.
+    if (service_info_comment.size() > 1) {
+      // Removes everything except the comment (parts of the comment).
+      service_info_comment.erase(service_info_comment.begin(), service_info_comment.begin() + 1);
+      r["comment"] = TEXT(boost::algorithm::join(service_info_comment, " # "));
+    }
+    results.push_back(r);
+  }
+  return results;
+}
+
+QueryData genEtcServices(QueryContext& context) {
+  std::string content;
+  auto s = osquery::readFile("/etc/services", content);
+  if (s.ok()) {
+    return parseEtcServicesContent(content);
+  } else {
+    LOG(ERROR) << "Error reading /etc/services: " << s.toString();
+    return {};
+  }
+}
+}
+}
diff --git a/src/osquery/tables/networking/linux/arp_cache.cpp b/src/osquery/tables/networking/linux/arp_cache.cpp
new file mode 100644 (file)
index 0000000..e5a3a60
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <fstream>
+
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+namespace tables {
+
+const std::string kLinuxArpTable = "/proc/net/arp";
+
+QueryData genArpCache(QueryContext& context) {
+  QueryData results;
+
+  boost::filesystem::path arp_path = kLinuxArpTable;
+  if (!osquery::isReadable(arp_path).ok()) {
+    VLOG(1) << "Cannot read arp table.";
+    return results;
+  }
+
+  std::ifstream fd(arp_path.string(), std::ios::in | std::ios::binary);
+  std::string line;
+
+  if (fd.fail() || fd.eof()) {
+    VLOG(1) << "Empty or failed arp table.";
+    return results;
+  }
+
+  // Read the header line.
+  std::getline(fd, line, '\n');
+  while (!(fd.fail() || fd.eof())) {
+    std::getline(fd, line, '\n');
+
+    // IP address, HW type, Flags, HW address, Mask Device
+    std::vector<std::string> fields;
+    boost::split(fields, line, boost::is_any_of(" "), boost::token_compress_on);
+    for (auto& f : fields) {
+      // Inline trim each split.
+      boost::trim(f);
+    }
+
+    if (fields.size() != 6) {
+      // An unhandled error case.
+      continue;
+    }
+
+    Row r;
+    r["address"] = fields[0];
+    r["mac"] = fields[3];
+    r["interface"] = fields[5];
+
+    // Note: it's also possible to detect publish entries (ATF_PUB).
+    if (fields[2] == "0x6") {
+      // The string representation of ATF_COM | ATF_PERM.
+      r["permanent"] = "1";
+    } else {
+      r["permanent"] = "0";
+    }
+
+    results.push_back(r);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/networking/linux/inet_diag.h b/src/osquery/tables/networking/linux/inet_diag.h
new file mode 100644 (file)
index 0000000..65b5d9e
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#ifndef _UAPI_INET_DIAG_H_
+#define _UAPI_INET_DIAG_H_
+
+#include <linux/types.h>
+
+/* Just some random number */
+#define TCPDIAG_GETSOCK 18
+#define DCCPDIAG_GETSOCK 19
+
+#define INET_DIAG_GETSOCK_MAX 24
+
+/* Socket identity */
+struct inet_diag_sockid {
+  __be16 idiag_sport;
+  __be16 idiag_dport;
+  __be32 idiag_src[4];
+  __be32 idiag_dst[4];
+  __u32 idiag_if;
+  __u32 idiag_cookie[2];
+#define INET_DIAG_NOCOOKIE (~0U)
+};
+
+/* Request structure */
+
+struct inet_diag_req {
+  __u8 idiag_family; /* Family of addresses. */
+  __u8 idiag_src_len;
+  __u8 idiag_dst_len;
+  __u8 idiag_ext; /* Query extended information */
+
+  struct inet_diag_sockid id;
+
+  __u32 idiag_states; /* States to dump */
+  __u32 idiag_dbs; /* Tables to dump (NI) */
+};
+
+struct inet_diag_req_v2 {
+  __u8 sdiag_family;
+  __u8 sdiag_protocol;
+  __u8 idiag_ext;
+  __u8 pad;
+  __u32 idiag_states;
+  struct inet_diag_sockid id;
+};
+
+enum {
+  INET_DIAG_REQ_NONE,
+  INET_DIAG_REQ_BYTECODE,
+};
+
+#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE
+
+/* Bytecode is sequence of 4 byte commands followed by variable arguments.
+ * All the commands identified by "code" are conditional jumps forward:
+ * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be
+ * length of the command and its arguments.
+ */
+
+struct inet_diag_bc_op {
+  unsigned char code;
+  unsigned char yes;
+  unsigned short no;
+};
+
+enum {
+  INET_DIAG_BC_NOP,
+  INET_DIAG_BC_JMP,
+  INET_DIAG_BC_S_GE,
+  INET_DIAG_BC_S_LE,
+  INET_DIAG_BC_D_GE,
+  INET_DIAG_BC_D_LE,
+  INET_DIAG_BC_AUTO,
+  INET_DIAG_BC_S_COND,
+  INET_DIAG_BC_D_COND,
+};
+
+struct inet_diag_hostcond {
+  __u8 family;
+  __u8 prefix_len;
+  int port;
+  __be32 addr[0];
+};
+
+/* Base info structure. It contains socket identity (addrs/ports/cookie)
+ * and, alas, the information shown by netstat. */
+struct inet_diag_msg {
+  __u8 idiag_family;
+  __u8 idiag_state;
+  __u8 idiag_timer;
+  __u8 idiag_retrans;
+
+  struct inet_diag_sockid id;
+
+  __u32 idiag_expires;
+  __u32 idiag_rqueue;
+  __u32 idiag_wqueue;
+  __u32 idiag_uid;
+  __u32 idiag_inode;
+};
+
+/* Extensions */
+
+enum {
+  INET_DIAG_NONE,
+  INET_DIAG_MEMINFO,
+  INET_DIAG_INFO,
+  INET_DIAG_VEGASINFO,
+  INET_DIAG_CONG,
+  INET_DIAG_TOS,
+  INET_DIAG_TCLASS,
+  INET_DIAG_SKMEMINFO,
+  INET_DIAG_SHUTDOWN,
+};
+
+#define INET_DIAG_MAX INET_DIAG_SHUTDOWN
+
+/* INET_DIAG_MEM */
+
+struct inet_diag_meminfo {
+  __u32 idiag_rmem;
+  __u32 idiag_wmem;
+  __u32 idiag_fmem;
+  __u32 idiag_tmem;
+};
+
+/* INET_DIAG_VEGASINFO */
+
+struct tcpvegas_info {
+  __u32 tcpv_enabled;
+  __u32 tcpv_rttcnt;
+  __u32 tcpv_rtt;
+  __u32 tcpv_minrtt;
+};
+
+#endif /* _UAPI_INET_DIAG_H_ */
diff --git a/src/osquery/tables/networking/linux/ip_tables.h b/src/osquery/tables/networking/linux/ip_tables.h
new file mode 100644 (file)
index 0000000..da03258
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * 25-Jul-1998 Major changes to allow for ip chain table
+ *
+ * 3-Jan-2000 Named tables to allow packet selection for different uses.
+ */
+
+/*
+ *     Format of an IP firewall descriptor
+ *
+ *     src, dst, src_mask, dst_mask are always stored in network byte order.
+ *     flags are stored in host byte order (of course).
+ *     Port numbers are stored in HOST byte order.
+ */
+
+#ifndef _IPTABLES_H
+#define _IPTABLES_H
+
+#include <linux/types.h>
+
+#include <linux/netfilter_ipv4.h>
+
+#include <linux/netfilter/x_tables.h>
+
+#define IPT_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN
+#define IPT_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN
+#define ipt_match xt_match
+#define ipt_target xt_target
+#define ipt_table xt_table
+#define ipt_get_revision xt_get_revision
+#define ipt_entry_match xt_entry_match
+#define ipt_entry_target xt_entry_target
+#define ipt_standard_target xt_standard_target
+#define ipt_error_target xt_error_target
+#define ipt_counters xt_counters
+#define IPT_CONTINUE XT_CONTINUE
+#define IPT_RETURN XT_RETURN
+
+/* This group is older than old (iptables < v1.4.0-rc1~89) */
+#include <linux/netfilter/xt_tcpudp.h>
+#define ipt_udp xt_udp
+#define ipt_tcp xt_tcp
+#define IPT_TCP_INV_SRCPT      XT_TCP_INV_SRCPT
+#define IPT_TCP_INV_DSTPT      XT_TCP_INV_DSTPT
+#define IPT_TCP_INV_FLAGS      XT_TCP_INV_FLAGS
+#define IPT_TCP_INV_OPTION     XT_TCP_INV_OPTION
+#define IPT_TCP_INV_MASK       XT_TCP_INV_MASK
+#define IPT_UDP_INV_SRCPT      XT_UDP_INV_SRCPT
+#define IPT_UDP_INV_DSTPT      XT_UDP_INV_DSTPT
+#define IPT_UDP_INV_MASK       XT_UDP_INV_MASK
+
+/* The argument to IPT_SO_ADD_COUNTERS. */
+#define ipt_counters_info xt_counters_info
+/* Standard return verdict, or do jump. */
+#define IPT_STANDARD_TARGET XT_STANDARD_TARGET
+/* Error verdict. */
+#define IPT_ERROR_TARGET XT_ERROR_TARGET
+
+/* fn returns 0 to continue iteration */
+#define IPT_MATCH_ITERATE(e, fn, args...) \
+       XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)
+
+/* fn returns 0 to continue iteration */
+#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
+       XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
+
+/* Yes, Virginia, you have to zero the padding. */
+struct ipt_ip {
+       /* Source and destination IP addr */
+       struct in_addr src, dst;
+       /* Mask for src and dest IP addr */
+       struct in_addr smsk, dmsk;
+       char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+       unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+
+       /* Protocol, 0 = ANY */
+       __u16 proto;
+
+       /* Flags word */
+       __u8 flags;
+       /* Inverse flags */
+       __u8 invflags;
+};
+
+/* Values for "flag" field in struct ipt_ip (general ip structure). */
+#define IPT_F_FRAG             0x01    /* Set if rule is a fragment rule */
+#define IPT_F_GOTO             0x02    /* Set if jump is a goto */
+#define IPT_F_MASK             0x03    /* All possible flag bits mask. */
+
+/* Values for "inv" field in struct ipt_ip. */
+#define IPT_INV_VIA_IN         0x01    /* Invert the sense of IN IFACE. */
+#define IPT_INV_VIA_OUT                0x02    /* Invert the sense of OUT IFACE */
+#define IPT_INV_TOS            0x04    /* Invert the sense of TOS. */
+#define IPT_INV_SRCIP          0x08    /* Invert the sense of SRC IP. */
+#define IPT_INV_DSTIP          0x10    /* Invert the sense of DST OP. */
+#define IPT_INV_FRAG           0x20    /* Invert the sense of FRAG. */
+#define IPT_INV_PROTO          XT_INV_PROTO
+#define IPT_INV_MASK           0x7F    /* All possible flag bits mask. */
+
+/* This structure defines each of the firewall rules.  Consists of 3
+   parts which are 1) general IP header stuff 2) match specific
+   stuff 3) the target to perform if the rule matches */
+struct ipt_entry {
+       struct ipt_ip ip;
+
+       /* Mark with fields that we care about. */
+       unsigned int nfcache;
+
+       /* Size of ipt_entry + matches */
+       __u16 target_offset;
+       /* Size of ipt_entry + matches + target */
+       __u16 next_offset;
+
+       /* Back pointer */
+       unsigned int comefrom;
+
+       /* Packet and byte counters. */
+       struct xt_counters counters;
+
+       /* The matches (if any), then the target. */
+       unsigned char elems[0];
+};
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use a raw
+ * socket for this. Instead we check rights in the calls.
+ *
+ * ATTENTION: check linux/in.h before adding new number here.
+ */
+#define IPT_BASE_CTL           64
+
+#define IPT_SO_SET_REPLACE     (IPT_BASE_CTL)
+#define IPT_SO_SET_ADD_COUNTERS        (IPT_BASE_CTL + 1)
+#define IPT_SO_SET_MAX         IPT_SO_SET_ADD_COUNTERS
+
+#define IPT_SO_GET_INFO                        (IPT_BASE_CTL)
+#define IPT_SO_GET_ENTRIES             (IPT_BASE_CTL + 1)
+#define IPT_SO_GET_REVISION_MATCH      (IPT_BASE_CTL + 2)
+#define IPT_SO_GET_REVISION_TARGET     (IPT_BASE_CTL + 3)
+#define IPT_SO_GET_MAX                 IPT_SO_GET_REVISION_TARGET
+
+/* ICMP matching stuff */
+struct ipt_icmp {
+       __u8 type;                              /* type to match */
+       __u8 code[2];                           /* range of code */
+       __u8 invflags;                          /* Inverse flags */
+};
+
+/* Values for "inv" field for struct ipt_icmp. */
+#define IPT_ICMP_INV   0x01    /* Invert the sense of type/code test */
+
+/* The argument to IPT_SO_GET_INFO */
+struct ipt_getinfo {
+       /* Which table: caller fills this in. */
+       char name[XT_TABLE_MAXNAMELEN];
+
+       /* Kernel fills these in. */
+       /* Which hook entry points are valid: bitmask */
+       unsigned int valid_hooks;
+
+       /* Hook entry points: one per netfilter hook. */
+       unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+       /* Underflow points. */
+       unsigned int underflow[NF_INET_NUMHOOKS];
+
+       /* Number of entries */
+       unsigned int num_entries;
+
+       /* Size of entries. */
+       unsigned int size;
+};
+
+/* The argument to IPT_SO_SET_REPLACE. */
+struct ipt_replace {
+       /* Which table. */
+       char name[XT_TABLE_MAXNAMELEN];
+
+       /* Which hook entry points are valid: bitmask.  You can't
+           change this. */
+       unsigned int valid_hooks;
+
+       /* Number of entries */
+       unsigned int num_entries;
+
+       /* Total size of new entries */
+       unsigned int size;
+
+       /* Hook entry points. */
+       unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+       /* Underflow points. */
+       unsigned int underflow[NF_INET_NUMHOOKS];
+
+       /* Information about old entries: */
+       /* Number of counters (must be equal to current number of entries). */
+       unsigned int num_counters;
+       /* The old entries' counters. */
+       struct xt_counters *counters;
+
+       /* The entries (hang off end: not really an array). */
+       struct ipt_entry entries[0];
+};
+
+/* The argument to IPT_SO_GET_ENTRIES. */
+struct ipt_get_entries {
+       /* Which table: user fills this in. */
+       char name[XT_TABLE_MAXNAMELEN];
+
+       /* User fills this in: total entry size. */
+       unsigned int size;
+
+       /* The entries. */
+       struct ipt_entry entrytable[0];
+};
+
+/* Helper functions */
+static __inline__ struct xt_entry_target *
+ipt_get_target(struct ipt_entry *e)
+{
+       return (struct ipt_entry_target *)((char *)e + e->target_offset);
+}
+
+/*
+ *     Main firewall chains definitions and global var's definitions.
+ */
+#endif /* _IPTABLES_H */
diff --git a/src/osquery/tables/networking/linux/iptables.cpp b/src/osquery/tables/networking/linux/iptables.cpp
new file mode 100644 (file)
index 0000000..7e6f88d
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sstream>
+
+#include <arpa/inet.h>
+#include "libiptc.h"
+
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/tables/networking/utils.h"
+
+namespace osquery {
+namespace tables {
+
+const std::string kLinuxIpTablesNames = "/proc/net/ip_tables_names";
+const char MAP[] = {'0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+const int HIGH_BITS = 4;
+const int LOW_BITS = 15;
+
+void parseIpEntry(ipt_ip *ip, Row &r) {
+  r["protocol"] = INTEGER(ip->proto);
+  if (strlen(ip->iniface)) {
+    r["iniface"] = TEXT(ip->iniface);
+  } else {
+    r["iniface"] = "all";
+  }
+
+  if (strlen(ip->outiface)) {
+    r["outiface"] = TEXT(ip->outiface);
+  } else {
+   r["outiface"] = "all";
+  }
+
+  r["src_ip"] = ipAsString((struct in_addr *)&ip->src);
+  r["dst_ip"] = ipAsString((struct in_addr *)&ip->dst);
+  r["src_mask"] = ipAsString((struct in_addr *)&ip->smsk);
+  r["dst_mask"] = ipAsString((struct in_addr *)&ip->dmsk);
+
+  char aux_char[2] = {0};
+  std::string iniface_mask;
+  for (int i = 0; ip->iniface_mask[i] != 0x00 && i<IFNAMSIZ; i++) {
+    aux_char[0] = MAP[(int) ip->iniface_mask[i] >> HIGH_BITS];
+    aux_char[1] = MAP[(int) ip->iniface_mask[i] & LOW_BITS];
+    iniface_mask += aux_char[0];
+    iniface_mask += aux_char[1];
+  }
+
+  r["iniface_mask"] = TEXT(iniface_mask);
+  std::string outiface_mask = "";
+  for (int i = 0; ip->outiface_mask[i] != 0x00 && i<IFNAMSIZ; i++) {
+    aux_char[0] = MAP[(int) ip->outiface_mask[i] >> HIGH_BITS];
+    aux_char[1] = MAP[(int) ip->outiface_mask[i] & LOW_BITS];
+    outiface_mask += aux_char[0];
+    outiface_mask += aux_char[1];
+  }
+  r["outiface_mask"] = TEXT(outiface_mask);
+}
+
+void genIPTablesRules(const std::string &filter, QueryData &results) {
+  Row r;
+  r["filter_name"] = filter;
+
+  // Initialize the access to iptc
+  auto handle = (struct iptc_handle *)iptc_init(filter.c_str());
+  if (handle == nullptr) {
+    return;
+  }
+
+  // Iterate through chains
+  for (auto chain = iptc_first_chain(handle); chain != nullptr;
+       chain = iptc_next_chain(handle)) {
+    r["chain"] = TEXT(chain);
+
+    struct ipt_counters counters;
+    auto policy = iptc_get_policy(chain, &counters, handle);
+
+    if (policy != nullptr) {
+      r["policy"] = TEXT(policy);
+      r["packets"] = INTEGER(counters.pcnt);
+      r["bytes"] = INTEGER(counters.bcnt);
+    } else {
+      r["policy"] = "";
+      r["packets"] = "0";
+      r["bytes"] = "0";
+    }
+
+    struct ipt_entry *prev_rule = nullptr;
+    // Iterating through all the rules per chain
+    for (auto chain_rule = iptc_first_rule(chain, handle); chain_rule;
+         chain_rule = iptc_next_rule(prev_rule, handle)) {
+      prev_rule = (struct ipt_entry *)chain_rule;
+
+      auto target = iptc_get_target(chain_rule, handle);
+      if (target != nullptr) {
+        r["target"] = TEXT(target);
+      } else {
+        r["target"] = "";
+      }
+
+      if (chain_rule->target_offset) {
+        r["match"] = "yes";
+      } else {
+        r["match"] = "no";
+      }
+
+      struct ipt_ip *ip = (struct ipt_ip *)&chain_rule->ip;
+      parseIpEntry(ip, r);
+
+      results.push_back(r);
+    } // Rule iteration
+    results.push_back(r);
+  } // Chain iteration
+
+  iptc_free(handle);
+}
+
+QueryData genIptables(QueryContext& context) {
+  QueryData results;
+
+  // Read in table names
+  std::string content;
+  auto s = osquery::readFile(kLinuxIpTablesNames, content);
+  if (s.ok()) {
+    for (auto &line : split(content, "\n")) {
+      boost::trim(line);
+      if (line.size() > 0) {
+        genIPTablesRules(line, results);
+      }
+    }
+  } else {
+    // Permissions issue or iptables modules are not loaded.
+    TLOG << "Error reading " << kLinuxIpTablesNames << " : " << s.toString();
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/networking/linux/libiptc.h b/src/osquery/tables/networking/linux/libiptc.h
new file mode 100644 (file)
index 0000000..fd561bd
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef _LIBIPTC_H
+#define _LIBIPTC_H
+/* Library which manipulates filtering rules. */
+
+#include <linux/types.h>
+#include <libiptc/ipt_kernel_headers.h>
+#ifdef __cplusplus
+#      include <climits>
+#else
+#      include <limits.h> /* INT_MAX in ip_tables.h */
+#endif
+#include "ip_tables.h"
+#include <libiptc/xtcshared.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define iptc_handle xtc_handle
+#define ipt_chainlabel xt_chainlabel
+
+#define IPTC_LABEL_ACCEPT  "ACCEPT"
+#define IPTC_LABEL_DROP    "DROP"
+#define IPTC_LABEL_QUEUE   "QUEUE"
+#define IPTC_LABEL_RETURN  "RETURN"
+
+/* Does this chain exist? */
+int iptc_is_chain(const char *chain, struct xtc_handle *const handle);
+
+/* Take a snapshot of the rules.  Returns NULL on error. */
+struct xtc_handle *iptc_init(const char *tablename);
+
+/* Cleanup after iptc_init(). */
+void iptc_free(struct xtc_handle *h);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *iptc_first_chain(struct xtc_handle *handle);
+const char *iptc_next_chain(struct xtc_handle *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct ipt_entry *iptc_first_rule(const char *chain,
+                                       struct xtc_handle *handle);
+
+/* Returns NULL when rules run out. */
+const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
+                                      struct xtc_handle *handle);
+
+/* Returns a pointer to the target name of this entry. */
+const char *iptc_get_target(const struct ipt_entry *e,
+                           struct xtc_handle *handle);
+
+/* Is this a built-in chain? */
+int iptc_builtin(const char *chain, struct xtc_handle *const handle);
+
+/* Get the policy of a given built-in chain */
+const char *iptc_get_policy(const char *chain,
+                           struct xt_counters *counter,
+                           struct xtc_handle *handle);
+
+/* These functions return TRUE for OK or 0 and set errno.  If errno ==
+   0, it means there was a version error (ie. upgrade libiptc). */
+/* Rule numbers start at 1 for the first rule. */
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
+int iptc_insert_entry(const xt_chainlabel chain,
+                     const struct ipt_entry *e,
+                     unsigned int rulenum,
+                     struct xtc_handle *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `e'. */
+int iptc_replace_entry(const xt_chainlabel chain,
+                      const struct ipt_entry *e,
+                      unsigned int rulenum,
+                      struct xtc_handle *handle);
+
+/* Append entry `e' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int iptc_append_entry(const xt_chainlabel chain,
+                     const struct ipt_entry *e,
+                     struct xtc_handle *handle);
+
+/* Check whether a mathching rule exists */
+int iptc_check_entry(const xt_chainlabel chain,
+                     const struct ipt_entry *origfw,
+                     unsigned char *matchmask,
+                     struct xtc_handle *handle);
+
+/* Delete the first rule in `chain' which matches `e', subject to
+   matchmask (array of length == origfw) */
+int iptc_delete_entry(const xt_chainlabel chain,
+                     const struct ipt_entry *origfw,
+                     unsigned char *matchmask,
+                     struct xtc_handle *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int iptc_delete_num_entry(const xt_chainlabel chain,
+                         unsigned int rulenum,
+                         struct xtc_handle *handle);
+
+/* Check the packet `e' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *iptc_check_packet(const xt_chainlabel chain,
+                             struct ipt_entry *entry,
+                             struct xtc_handle *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int iptc_flush_entries(const xt_chainlabel chain,
+                      struct xtc_handle *handle);
+
+/* Zeroes the counters in a chain. */
+int iptc_zero_entries(const xt_chainlabel chain,
+                     struct xtc_handle *handle);
+
+/* Creates a new chain. */
+int iptc_create_chain(const xt_chainlabel chain,
+                     struct xtc_handle *handle);
+
+/* Deletes a chain. */
+int iptc_delete_chain(const xt_chainlabel chain,
+                     struct xtc_handle *handle);
+
+/* Renames a chain. */
+int iptc_rename_chain(const xt_chainlabel oldname,
+                     const xt_chainlabel newname,
+                     struct xtc_handle *handle);
+
+/* Sets the policy on a built-in chain. */
+int iptc_set_policy(const xt_chainlabel chain,
+                   const xt_chainlabel policy,
+                   struct xt_counters *counters,
+                   struct xtc_handle *handle);
+
+/* Get the number of references to this chain */
+int iptc_get_references(unsigned int *ref,
+                       const xt_chainlabel chain,
+                       struct xtc_handle *handle);
+
+/* read packet and byte counters for a specific rule */
+struct xt_counters *iptc_read_counter(const xt_chainlabel chain,
+                                      unsigned int rulenum,
+                                      struct xtc_handle *handle);
+
+/* zero packet and byte counters for a specific rule */
+int iptc_zero_counter(const xt_chainlabel chain,
+                     unsigned int rulenum,
+                     struct xtc_handle *handle);
+
+/* set packet and byte counters for a specific rule */
+int iptc_set_counter(const xt_chainlabel chain,
+                    unsigned int rulenum,
+                    struct xt_counters *counters,
+                    struct xtc_handle *handle);
+
+/* Makes the actual changes. */
+int iptc_commit(struct xtc_handle *handle);
+
+/* Get raw socket. */
+int iptc_get_raw_socket(void);
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *iptc_strerror(int err);
+
+extern void dump_entries(struct xtc_handle *const);
+
+extern const struct xtc_ops iptc_ops;
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LIBIPTC_H */
diff --git a/src/osquery/tables/networking/linux/process_open_sockets.cpp b/src/osquery/tables/networking/linux/process_open_sockets.cpp
new file mode 100644 (file)
index 0000000..e2cac9c
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <arpa/inet.h>
+
+#include <boost/algorithm/string/split.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+// Linux proc protocol define to net stats file name.
+const std::map<int, std::string> kLinuxProtocolNames = {
+    {IPPROTO_ICMP, "icmp"},
+    {IPPROTO_TCP, "tcp"},
+    {IPPROTO_UDP, "udp"},
+    {IPPROTO_UDPLITE, "udplite"},
+    {IPPROTO_RAW, "raw"},
+};
+
+std::string addressFromHex(const std::string &encoded_address, int family) {
+  char addr_buffer[INET6_ADDRSTRLEN] = {0};
+  if (family == AF_INET) {
+    struct in_addr decoded;
+    if (encoded_address.length() == 8) {
+      sscanf(encoded_address.c_str(), "%X", &(decoded.s_addr));
+      inet_ntop(AF_INET, &decoded, addr_buffer, INET_ADDRSTRLEN);
+    }
+  } else if (family == AF_INET6) {
+    struct in6_addr decoded;
+    if (encoded_address.length() == 32) {
+      sscanf(encoded_address.c_str(),
+             "%8x%8x%8x%8x",
+             (unsigned int *)&(decoded.s6_addr[0]),
+             (unsigned int *)&(decoded.s6_addr[4]),
+             (unsigned int *)&(decoded.s6_addr[8]),
+             (unsigned int *)&(decoded.s6_addr[12]));
+      inet_ntop(AF_INET6, &decoded, addr_buffer, INET6_ADDRSTRLEN);
+    }
+  }
+
+  return TEXT(addr_buffer);
+}
+
+unsigned short portFromHex(const std::string &encoded_port) {
+  unsigned short decoded = 0;
+  if (encoded_port.length() == 4) {
+    sscanf(encoded_port.c_str(), "%hX", &decoded);
+  }
+  return decoded;
+}
+
+void genSocketsFromProc(const std::map<std::string, std::string> &inodes,
+                        int protocol,
+                        int family,
+                        QueryData &results) {
+  std::string path = "/proc/net/";
+  if (family == AF_UNIX) {
+    path += "unix";
+  } else {
+    path += kLinuxProtocolNames.at(protocol);
+    path += (family == AF_INET6) ? "6" : "";
+  }
+
+  std::string content;
+  if (!osquery::readFile(path, content).ok()) {
+    // Could not open socket information from /proc.
+    return;
+  }
+
+  // The system's socket information is tokenized by line.
+  size_t index = 0;
+  for (const auto &line : osquery::split(content, "\n")) {
+    if (++index == 1) {
+      // The first line is a textual header and will be ignored.
+      if (line.find("sl") != 0 && line.find("sk") != 0 &&
+          line.find("Num") != 0) {
+        // Header fields are unknown, stop parsing.
+        break;
+      }
+      continue;
+    }
+
+    // The socket information is tokenized by spaces, each a field.
+    auto fields = osquery::split(line, " ");
+    // UNIX socket reporting has a smaller number of fields.
+    size_t min_fields = (family == AF_UNIX) ? 7 : 10;
+    if (fields.size() < min_fields) {
+      // Unknown/malformed socket information.
+      continue;
+    }
+
+
+    Row r;
+    if (family == AF_UNIX) {
+      r["socket"] = fields[6];
+      r["family"] = "0";
+      r["protocol"] = fields[2];
+      r["local_address"] = "";
+      r["local_port"] = "0";
+      r["remote_address"] = "";
+      r["remote_port"] = "0";
+      r["path"] = (fields.size() >= 8) ? fields[7] : "";
+    } else {
+      // Two of the fields are the local/remote address/port pairs.
+      auto locals = osquery::split(fields[1], ":");
+      auto remotes = osquery::split(fields[2], ":");
+      if (locals.size() != 2 || remotes.size() != 2) {
+        // Unknown/malformed socket information.
+        continue;
+      }
+
+      r["socket"] = fields[9];
+      r["family"] = INTEGER(family);
+      r["protocol"] = INTEGER(protocol);
+      r["local_address"] = addressFromHex(locals[0], family);
+      r["local_port"] = INTEGER(portFromHex(locals[1]));
+      r["remote_address"] = addressFromHex(remotes[0], family);
+      r["remote_port"] = INTEGER(portFromHex(remotes[1]));
+      // Path is only used for UNIX domain sockets.
+      r["path"] = "";
+    }
+
+    if (inodes.count(r["socket"]) > 0) {
+      r["pid"] = inodes.at(r["socket"]);
+    } else {
+      r["pid"] = "-1";
+    }
+
+    results.push_back(r);
+  }
+}
+
+QueryData genOpenSockets(QueryContext &context) {
+  QueryData results;
+
+  // If a pid is given then set that as the only item in processes.
+  std::set<std::string> pids;
+  if (context.constraints["pid"].exists(EQUALS)) {
+    pids = context.constraints["pid"].getAll(EQUALS);
+  } else {
+    osquery::procProcesses(pids);
+  }
+
+  // Generate a map of socket inode to process tid.
+  std::map<std::string, std::string> socket_inodes;
+  for (const auto &process : pids) {
+    std::map<std::string, std::string> descriptors;
+    if (osquery::procDescriptors(process, descriptors).ok()) {
+      for (const auto& fd : descriptors) {
+        if (fd.second.find("socket:[") == 0) {
+          // See #792: std::regex is incomplete until GCC 4.9 (skip 8 chars)
+          auto inode = fd.second.substr(8);
+          socket_inodes[inode.substr(0, inode.size() - 1)] = process;
+        }
+      }
+    }
+  }
+
+  // This used to use netlink (Ref: #1094) to request socket information.
+  // Use proc messages to query socket information.
+  for (const auto &protocol : kLinuxProtocolNames) {
+    genSocketsFromProc(socket_inodes, protocol.first, AF_INET, results);
+    genSocketsFromProc(socket_inodes, protocol.first, AF_INET6, results);
+  }
+
+  genSocketsFromProc(socket_inodes, IPPROTO_IP, AF_UNIX, results);
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/networking/linux/routes.cpp b/src/osquery/tables/networking/linux/routes.cpp
new file mode 100644 (file)
index 0000000..d32ad82
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/tables/networking/utils.h"
+
+namespace osquery {
+namespace tables {
+
+#define MAX_NETLINK_SIZE 8192
+#define MAX_NETLINK_LATENCY 2000
+
+std::string getNetlinkIP(int family, const char* buffer) {
+  char dst[INET6_ADDRSTRLEN];
+  memset(dst, 0, INET6_ADDRSTRLEN);
+
+  inet_ntop(family, buffer, dst, INET6_ADDRSTRLEN);
+  std::string address(dst);
+  boost::trim(address);
+
+  return address;
+}
+
+Status readNetlink(int socket_fd, int seq, char* output, size_t* size) {
+  struct nlmsghdr* nl_hdr = nullptr;
+
+  size_t message_size = 0;
+  do {
+    int latency = 0;
+    int bytes = 0;
+    while (bytes == 0) {
+      bytes = recv(socket_fd, output, MAX_NETLINK_SIZE - message_size, 0);
+      if (bytes < 0) {
+        return Status(1, "Could not read from NETLINK");
+      } else if (latency >= MAX_NETLINK_LATENCY) {
+        return Status(1, "Netlink timeout");
+      } else if (bytes == 0) {
+        ::usleep(20);
+        latency += 20;
+      }
+    }
+
+    // Assure valid header response, and not an error type.
+    nl_hdr = (struct nlmsghdr*)output;
+    if (NLMSG_OK(nl_hdr, bytes) == 0 || nl_hdr->nlmsg_type == NLMSG_ERROR) {
+      return Status(1, "Read invalid NETLINK message");
+    }
+
+    if (nl_hdr->nlmsg_type == NLMSG_DONE) {
+      break;
+    }
+
+    // Move the buffer pointer
+    output += bytes;
+    message_size += bytes;
+    if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) {
+      break;
+    }
+  } while (nl_hdr->nlmsg_seq != seq || nl_hdr->nlmsg_pid != getpid());
+
+  *size = message_size;
+  return Status(0, "OK");
+}
+
+void genNetlinkRoutes(const struct nlmsghdr* netlink_msg, QueryData& results) {
+  std::string address;
+  int mask = 0;
+  char interface[IF_NAMESIZE];
+
+  struct rtmsg* message = (struct rtmsg*)NLMSG_DATA(netlink_msg);
+  struct rtattr* attr = (struct rtattr*)RTM_RTA(message);
+  int attr_size = RTM_PAYLOAD(netlink_msg);
+
+  Row r;
+
+  // Iterate over each route in the netlink message
+  bool has_destination = false;
+  r["metric"] = "0";
+  while (RTA_OK(attr, attr_size)) {
+    switch (attr->rta_type) {
+    case RTA_OIF:
+      if_indextoname(*(int*)RTA_DATA(attr), interface);
+      r["interface"] = std::string(interface);
+      break;
+    case RTA_GATEWAY:
+      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
+      r["gateway"] = address;
+      break;
+    case RTA_PREFSRC:
+      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
+      r["source"] = address;
+      break;
+    case RTA_DST:
+      if (message->rtm_dst_len != 32 && message->rtm_dst_len != 128) {
+        mask = (int)message->rtm_dst_len;
+      }
+      address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr));
+      r["destination"] = address;
+      has_destination = true;
+      break;
+    case RTA_PRIORITY:
+      r["metric"] = INTEGER(*(int*)RTA_DATA(attr));
+      break;
+    }
+    attr = RTA_NEXT(attr, attr_size);
+  }
+
+  if (!has_destination) {
+    r["destination"] = "0.0.0.0";
+    if (message->rtm_dst_len) {
+      mask = (int)message->rtm_dst_len;
+    }
+  }
+
+  // Route type determination
+  if (message->rtm_type == RTN_UNICAST) {
+    r["type"] = "gateway";
+  } else if (message->rtm_type == RTN_LOCAL) {
+    r["type"] = "local";
+  } else if (message->rtm_type == RTN_BROADCAST) {
+    r["type"] = "broadcast";
+  } else if (message->rtm_type == RTN_ANYCAST) {
+    r["type"] = "anycast";
+  } else {
+    r["type"] = "other";
+  }
+
+  r["flags"] = INTEGER(message->rtm_flags);
+
+  // This is the cidr-formatted mask
+  r["netmask"] = INTEGER(mask);
+
+  // Fields not supported by Linux routes:
+  r["mtu"] = "0";
+  results.push_back(r);
+}
+
+QueryData genRoutes(QueryContext& context) {
+  QueryData results;
+
+  int socket_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+  if (socket_fd < 0) {
+    VLOG(1) << "Cannot open NETLINK socket";
+    return {};
+  }
+
+  // Create netlink message header
+  auto netlink_buffer = (void*)malloc(MAX_NETLINK_SIZE);
+  if (netlink_buffer == nullptr) {
+    close(socket_fd);
+    return {};
+  }
+
+  auto netlink_msg = (struct nlmsghdr*)netlink_buffer;
+  netlink_msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+  netlink_msg->nlmsg_type = RTM_GETROUTE; // routes from kernel routing table
+  netlink_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+  netlink_msg->nlmsg_seq = 0;
+  netlink_msg->nlmsg_pid = getpid();
+
+  // Send the netlink request to the kernel
+  if (send(socket_fd, netlink_msg, netlink_msg->nlmsg_len, 0) < 0) {
+    TLOG << "Cannot write NETLINK request header to socket";
+    close(socket_fd);
+    free(netlink_buffer);
+    return {};
+  }
+
+  // Wrap the read socket to support multi-netlink messages
+  size_t size = 0;
+  if (!readNetlink(socket_fd, 1, (char*)netlink_msg, &size).ok()) {
+    TLOG << "Cannot read NETLINK response from socket";
+    close(socket_fd);
+    free(netlink_buffer);
+    return {};
+  }
+
+  // Treat the netlink response as route information
+  while (NLMSG_OK(netlink_msg, size)) {
+    genNetlinkRoutes(netlink_msg, results);
+    netlink_msg = NLMSG_NEXT(netlink_msg, size);
+  }
+
+  close(socket_fd);
+  free(netlink_buffer);
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/networking/linux/tests/iptables_tests.cpp b/src/osquery/tables/networking/linux/tests/iptables_tests.cpp
new file mode 100644 (file)
index 0000000..2761ead
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/logger.h>
+
+#include <libiptc/libiptc.h>
+#include <arpa/inet.h>
+
+#include "osquery/core/test_util.h"
+
+namespace osquery {
+namespace tables {
+
+void parseIpEntry(ipt_ip *ip, Row &row);
+
+ipt_ip* getIpEntryContent() {
+  static ipt_ip ip_entry;
+
+  ip_entry.proto = 6;
+  memset(ip_entry.iniface, 0, IFNAMSIZ);
+  strcpy(ip_entry.outiface, "eth0");
+  inet_aton("123.123.123.123", &ip_entry.src);
+  inet_aton("45.45.45.45", &ip_entry.dst);
+  inet_aton("250.251.252.253", &ip_entry.smsk);
+  inet_aton("253.252.251.250", &ip_entry.dmsk);
+  memset(ip_entry.iniface_mask, 0xfe, IFNAMSIZ );
+  memset(ip_entry.outiface_mask, 0xfa, IFNAMSIZ );
+
+  return &ip_entry;
+}
+
+Row getIpEntryExpectedResults() {
+  Row row;
+
+  row["protocol"] = "6";
+  row["iniface"] = "all";
+  row["outiface"] = "eth0";
+  row["src_ip"] = "123.123.123.123";
+  row["dst_ip"] = "45.45.45.45";
+  row["src_mask"] = "250.251.252.253";
+  row["dst_mask"] = "253.252.251.250";
+  row["iniface_mask"] = "FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE";
+  row["outiface_mask"] = "FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFA";
+
+  return row;
+}
+
+class IptablesTests : public testing::Test {};
+
+TEST_F(IptablesTests, test_iptables_ip_entry) {
+  Row row;
+  parseIpEntry(getIpEntryContent(), row);
+  EXPECT_EQ(row, getIpEntryExpectedResults());
+}
+}
+}
diff --git a/src/osquery/tables/networking/listening_ports.cpp b/src/osquery/tables/networking/listening_ports.cpp
new file mode 100644 (file)
index 0000000..951163a
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/sql.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+typedef std::pair<std::string, std::string> ProtoFamilyPair;
+typedef std::map<std::string, std::vector<ProtoFamilyPair> > PortMap;
+
+QueryData genListeningPorts(QueryContext& context) {
+  QueryData results;
+
+  auto sockets = SQL::selectAllFrom("process_open_sockets");
+
+  PortMap ports;
+  for (const auto& socket : sockets) {
+    if (socket.at("remote_port") != "0") {
+      // Listening UDP/TCP ports have a remote_port == "0"
+      continue;
+    }
+
+    if (ports.count(socket.at("local_port")) > 0) {
+      bool duplicate = false;
+      for (const auto& entry : ports[socket.at("local_port")]) {
+        if (entry.first == socket.at("protocol") &&
+            entry.second == socket.at("family")) {
+          duplicate = true;
+          break;
+        }
+      }
+
+      if (duplicate) {
+        // There is a duplicate socket descriptor for this bind.
+        continue;
+      }
+    }
+
+    // Add this family/protocol/port bind to the tracked map.
+    ports[socket.at("local_port")].push_back(
+        std::make_pair(socket.at("protocol"), socket.at("family")));
+
+    Row r;
+    r["pid"] = socket.at("pid");
+    r["port"] = socket.at("local_port");
+    r["protocol"] = socket.at("protocol");
+    r["family"] = socket.at("family");
+    r["address"] = socket.at("local_address");
+
+    results.push_back(r);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/networking/tests/etc_hosts_tests.cpp b/src/osquery/tables/networking/tests/etc_hosts_tests.cpp
new file mode 100644 (file)
index 0000000..bad5904
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/logger.h>
+
+#include "osquery/core/test_util.h"
+
+namespace osquery {
+namespace tables {
+
+osquery::QueryData parseEtcHostsContent(const std::string& content);
+
+class EtcHostsTests : public testing::Test {};
+
+TEST_F(EtcHostsTests, test_parse_etc_hosts_content) {
+  EXPECT_EQ(parseEtcHostsContent(getEtcHostsContent()),
+            getEtcHostsExpectedResults());
+}
+}
+}
diff --git a/src/osquery/tables/networking/tests/etc_protocols_tests.cpp b/src/osquery/tables/networking/tests/etc_protocols_tests.cpp
new file mode 100644 (file)
index 0000000..e6786be
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <gtest/gtest.h>
+
+#include <osquery/logger.h>
+
+#include "osquery/core/test_util.h"
+
+namespace osquery {
+namespace tables {
+
+osquery::QueryData parseEtcProtocolsContent(const std::string& content);
+
+class EtcProtocolsTests : public testing::Test {};
+
+TEST_F(EtcProtocolsTests, test_parse_etc_protocols_content) {
+  EXPECT_EQ(parseEtcProtocolsContent(getEtcProtocolsContent()),
+            getEtcProtocolsExpectedResults());
+}
+}
+}
diff --git a/src/osquery/tables/networking/utils.cpp b/src/osquery/tables/networking/utils.cpp
new file mode 100644 (file)
index 0000000..6ece3f1
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <iomanip>
+#include <sstream>
+
+#if defined(__linux__)
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#define AF_LINK AF_PACKET
+#else
+#include <net/if_dl.h>
+#endif
+
+#include <boost/algorithm/string/trim.hpp>
+
+#include "osquery/tables/networking/utils.h"
+
+namespace osquery {
+namespace tables {
+
+std::string ipAsString(const struct sockaddr *in) {
+  char dst[INET6_ADDRSTRLEN] = {0};
+  void *in_addr = nullptr;
+
+  if (in->sa_family == AF_INET) {
+    in_addr = (void *)&(((struct sockaddr_in *)in)->sin_addr);
+  } else if (in->sa_family == AF_INET6) {
+    in_addr = (void *)&(((struct sockaddr_in6 *)in)->sin6_addr);
+  } else {
+    return "";
+  }
+
+  inet_ntop(in->sa_family, in_addr, dst, sizeof(dst));
+  std::string address(dst);
+  boost::trim(address);
+  return address;
+}
+
+std::string ipAsString(const struct in_addr *in) {
+  char dst[INET6_ADDRSTRLEN] = {0};
+
+  inet_ntop(AF_INET, in, dst, sizeof(dst));
+  std::string address(dst);
+  boost::trim(address);
+  return address;
+}
+
+inline short addBits(unsigned char byte) {
+  short bits = 0;
+  for (int i = 7; i >= 0; --i) {
+    if ((byte & (1 << i)) == 0) {
+      break;
+    }
+    bits++;
+  }
+  return bits;
+}
+
+int netmaskFromIP(const struct sockaddr *in) {
+  int mask = 0;
+
+  if (in->sa_family == AF_INET6) {
+    auto in6 = (struct sockaddr_in6 *)in;
+    for (size_t i = 0; i < 16; i++) {
+      mask += addBits(in6->sin6_addr.s6_addr[i]);
+    }
+  } else {
+    auto in4 = (struct sockaddr_in *)in;
+    auto address = reinterpret_cast<char *>(&in4->sin_addr.s_addr);
+    for (size_t i = 0; i < 4; i++) {
+      mask += addBits(address[i]);
+    }
+  }
+
+  return mask;
+}
+
+inline std::string macAsString(const char *addr) {
+  std::stringstream mac;
+
+  for (size_t i = 0; i < 6; i++) {
+    mac << std::hex << std::setfill('0') << std::setw(2);
+    mac << (int)((uint8_t)addr[i]);
+    if (i != 5) {
+      mac << ":";
+    }
+  }
+
+  return mac.str();
+}
+
+std::string macAsString(const struct ifaddrs *addr) {
+  static std::string blank_mac = "00:00:00:00:00:00";
+  if (addr->ifa_addr == nullptr) {
+    // No link or MAC exists.
+    return blank_mac;
+  }
+
+#if defined(__linux__)
+  struct ifreq ifr;
+  ifr.ifr_addr.sa_family = AF_INET;
+  memcpy(ifr.ifr_name, addr->ifa_name, IFNAMSIZ);
+
+  int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
+  if (socket_fd < 0) {
+    return blank_mac;
+  }
+  ioctl(socket_fd, SIOCGIFHWADDR, &ifr);
+  close(socket_fd);
+
+  return macAsString(ifr.ifr_hwaddr.sa_data);
+#else
+  auto sdl = (struct sockaddr_dl *)addr->ifa_addr;
+  if (sdl->sdl_alen != 6) {
+    // Do not support MAC address that are not 6 bytes...
+    return blank_mac;
+  }
+
+  return macAsString(&sdl->sdl_data[sdl->sdl_nlen]);
+#endif
+}
+}
+}
diff --git a/src/osquery/tables/networking/utils.h b/src/osquery/tables/networking/utils.h
new file mode 100644 (file)
index 0000000..1243ded
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#pragma once
+
+#include <string>
+
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+
+namespace osquery {
+namespace tables {
+
+// Return a string representation for an IPv4/IPv6 struct.
+std::string ipAsString(const struct sockaddr *in);
+std::string ipAsString(const struct in_addr *in);
+std::string macAsString(const struct ifaddrs *addr);
+std::string macAsString(const char *addr);
+int netmaskFromIP(const struct sockaddr *in);
+}
+}
diff --git a/src/osquery/tables/system/crontab.cpp b/src/osquery/tables/system/crontab.cpp
new file mode 100644 (file)
index 0000000..fb54584
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/core.h>
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+namespace tables {
+
+const std::string kSystemCron = "/etc/crontab";
+
+const std::vector<std::string> kUserCronPaths = {
+    "/var/at/tabs/", "/var/spool/cron/", "/var/spool/cron/crontabs/",
+};
+
+std::vector<std::string> cronFromFile(const std::string& path) {
+  std::string content;
+  std::vector<std::string> cron_lines;
+  if (!isReadable(path).ok()) {
+    return cron_lines;
+  }
+
+  if (!readFile(path, content).ok()) {
+    return cron_lines;
+  }
+
+  auto lines = split(content, "\n");
+
+  // Only populate the lines that are not comments or blank.
+  for (auto& line : lines) {
+    // Cheat and use a non-const iteration, to inline trim.
+    boost::trim(line);
+    if (line.size() > 0 && line.at(0) != '#') {
+      cron_lines.push_back(line);
+    }
+  }
+
+  return cron_lines;
+}
+
+void genCronLine(const std::string& path,
+                 const std::string& line,
+                 QueryData& results) {
+  Row r;
+
+  r["path"] = path;
+  auto columns = split(line, " \t");
+
+  size_t index = 0;
+  auto iterator = columns.begin();
+  for (; iterator != columns.end(); ++iterator) {
+    if (index == 0) {
+      if ((*iterator).at(0) == '@') {
+        // If the first value is an 'at' then skip to the command.
+        r["event"] = *iterator;
+        index = 5;
+        continue;
+      }
+      r["minute"] = *iterator;
+    } else if (index == 1) {
+      r["hour"] = *iterator;
+    } else if (index == 2) {
+      r["day_of_month"] = *iterator;
+    } else if (index == 3) {
+      r["month"] = *iterator;
+    } else if (index == 4) {
+      r["day_of_week"] = *iterator;
+    } else if (index == 5) {
+      r["command"] = *iterator;
+    } else {
+      // Long if switch to handle command breaks from space delim.
+      r["command"] += " " + *iterator;
+    }
+    index++;
+  }
+
+  if (r["command"].size() == 0) {
+    // The line was not well-formed, perhaps it was a variable?
+    return;
+  }
+
+  results.push_back(r);
+}
+
+QueryData genCronTab(QueryContext& context) {
+  QueryData results;
+
+  auto system_lines = cronFromFile(kSystemCron);
+  for (const auto& line : system_lines) {
+    genCronLine(kSystemCron, line, results);
+  }
+
+  std::vector<std::string> user_crons;
+  for (const auto cron_path : kUserCronPaths) {
+    osquery::listFilesInDirectory(cron_path, user_crons);
+  }
+
+  // The user-based crons are identified by their path.
+  for (const auto& user_path : user_crons) {
+    auto user_lines = cronFromFile(user_path);
+    for (const auto& line : user_lines) {
+      genCronLine(user_path, line, results);
+    }
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/last.cpp b/src/osquery/tables/system/last.cpp
new file mode 100644 (file)
index 0000000..b780b12
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <utmpx.h>
+
+#include <osquery/core.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+QueryData genLastAccess(QueryContext& context) {
+  QueryData results;
+  struct utmpx *ut;
+#ifdef __APPLE__
+  setutxent_wtmp(0); // 0 = reverse chronological order
+
+  while ((ut = getutxent_wtmp()) != nullptr) {
+#else
+
+  utmpxname("/var/log/wtmpx");
+  setutxent();
+
+  while ((ut = getutxent()) != nullptr) {
+#endif
+
+    Row r;
+    r["username"] = std::string(ut->ut_user);
+    r["tty"] = std::string(ut->ut_line);
+    r["pid"] = boost::lexical_cast<std::string>(ut->ut_pid);
+    r["type"] = boost::lexical_cast<std::string>(ut->ut_type);
+    r["time"] = boost::lexical_cast<std::string>(ut->ut_tv.tv_sec);
+    r["host"] = std::string(ut->ut_host);
+
+    results.push_back(r);
+  }
+
+#ifdef __APPLE__
+  endutxent_wtmp();
+#else
+  endutxent();
+#endif
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/acpi_tables.cpp b/src/osquery/tables/system/linux/acpi_tables.cpp
new file mode 100644 (file)
index 0000000..662926d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/filesystem.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/hash.h>
+#include <osquery/tables.h>
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+namespace tables {
+
+const std::string kLinuxACPIPath = "/sys/firmware/acpi/tables";
+
+void genACPITable(const std::string& table, QueryData& results) {
+  fs::path table_path = table;
+
+  // There may be "categories" of tables in the form of directories.
+  Status status;
+  if (!fs::is_regular_file(table_path)) {
+    std::vector<std::string> child_tables;
+    status = osquery::listFilesInDirectory(table_path, child_tables);
+    if (status.ok()) {
+      for (const auto& child_table : child_tables) {
+        genACPITable(child_table, results);
+      }
+    }
+
+    return;
+  }
+
+  Row r;
+  r["name"] = table_path.filename().string();
+
+  std::string table_content;
+  status = osquery::readFile(table_path, table_content);
+  if (!status.ok()) {
+    r["size"] = INTEGER(-1);
+  } else {
+    r["size"] = INTEGER(table_content.size());
+    r["md5"] = osquery::hashFromBuffer(
+        HASH_TYPE_MD5, table_content.c_str(), table_content.length());
+  }
+
+  results.push_back(r);
+}
+
+QueryData genACPITables(QueryContext& context) {
+  QueryData results;
+
+  // In Linux, hopefully the ACPI tables are parsed and exposed as nodes.
+  std::vector<std::string> tables;
+  auto status = osquery::listFilesInDirectory(kLinuxACPIPath, tables);
+  if (!status.ok()) {
+    // We could not read the tables path or the nodes are not exposed.
+    return {};
+  }
+
+  for (const auto& table : tables) {
+    genACPITable(table, results);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/groups.cpp b/src/osquery/tables/system/linux/groups.cpp
new file mode 100644 (file)
index 0000000..b537db9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <set>
+#include <mutex>
+
+#include <grp.h>
+
+#include <osquery/core.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+std::mutex grpEnumerationMutex;
+
+QueryData genGroups(QueryContext &context) {
+  std::lock_guard<std::mutex> lock(grpEnumerationMutex);
+  QueryData results;
+  struct group *grp = nullptr;
+  std::set<long> groups_in;
+
+  setgrent();
+  while ((grp = getgrent()) != nullptr) {
+    if (std::find(groups_in.begin(), groups_in.end(), grp->gr_gid) ==
+        groups_in.end()) {
+      Row r;
+      r["gid"] = INTEGER(grp->gr_gid);
+      r["gid_signed"] = INTEGER((int32_t) grp->gr_gid);
+      r["groupname"] = TEXT(grp->gr_name);
+      results.push_back(r);
+      groups_in.insert(grp->gr_gid);
+    }
+  }
+  endgrent();
+  groups_in.clear();
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/kernel_info.cpp b/src/osquery/tables/system/linux/kernel_info.cpp
new file mode 100644 (file)
index 0000000..d3cc2ff
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/algorithm/string/split.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/hash.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+const std::string kKernelArgumentsPath = "/proc/cmdline";
+const std::string kKernelSignaturePath = "/proc/version";
+
+QueryData genKernelInfo(QueryContext& context) {
+  QueryData results;
+  Row r;
+
+  if (pathExists(kKernelArgumentsPath).ok()) {
+    std::string arguments_line;
+    // Grab the whole arguments string from proc.
+    if (readFile(kKernelArgumentsPath, arguments_line).ok()) {
+      auto arguments = split(arguments_line, " ");
+      std::string additional_arguments;
+
+      // Iterate over each space-tokenized argument.
+      for (const auto& argument : arguments) {
+        if (argument.substr(0, 11) == "BOOT_IMAGE=") {
+          r["path"] = argument.substr(11);
+        } else if (argument.substr(0, 5) == "root=") {
+          r["device"] = argument.substr(5);
+        } else {
+          if (additional_arguments.size() > 0) {
+            additional_arguments += " ";
+          }
+          additional_arguments += argument;
+        }
+      }
+      r["arguments"] = additional_arguments;
+    }
+  } else {
+    VLOG(1) << "Cannot find kernel arguments file: " << kKernelArgumentsPath;
+  }
+
+  if (pathExists(kKernelSignaturePath).ok()) {
+    std::string signature;
+
+    // The signature includes the kernel version, build data, buildhost,
+    // GCC version used, and possibly build date.
+    if (readFile(kKernelSignaturePath, signature).ok()) {
+      auto details = split(signature, " ");
+      if (details.size() > 2 && details[1] == "version") {
+        r["version"] = details[2];
+      }
+    }
+  } else {
+    VLOG(1) << "Cannot find kernel signature file: " << kKernelSignaturePath;
+  }
+
+  // Using the path of the boot image, attempt to calculate a hash.
+  if (r.count("path") > 0) {
+    r["md5"] = hashFromFile(HASH_TYPE_MD5, r.at("path"));
+  }
+
+  results.push_back(r);
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/kernel_modules.cpp b/src/osquery/tables/system/linux/kernel_modules.cpp
new file mode 100644 (file)
index 0000000..f3fadad
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <fstream>
+
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+const std::string kKernelModulePath = "/proc/modules";
+
+QueryData genKernelModules(QueryContext& context) {
+  QueryData results;
+
+  if (!pathExists(kKernelModulePath).ok()) {
+    VLOG(1) << "Cannot find kernel modules proc file: " << kKernelModulePath;
+    return {};
+  }
+
+  // Cannot seek to the end of procfs.
+  std::ifstream fd(kKernelModulePath, std::ios::in);
+  if (!fd) {
+    VLOG(1) << "Cannot read kernel modules from: " << kKernelModulePath;
+    return {};
+  }
+
+  auto module_info = std::string(std::istreambuf_iterator<char>(fd),
+                                 std::istreambuf_iterator<char>());
+
+  for (const auto& module : split(module_info, "\n")) {
+    Row r;
+    auto module_info = split(module, " ");
+    if (module_info.size() < 6) {
+      // Interesting error case, this module line is not well formed.
+      continue;
+    }
+
+    for (auto& detail : module_info) {
+      // Clean up the delimiters
+      boost::trim(detail);
+      if (detail.back() == ',') {
+        detail.pop_back();
+      }
+    }
+
+    r["name"] = module_info[0];
+    r["size"] = module_info[1];
+    r["used_by"] = module_info[3];
+    r["status"] = module_info[4];
+    r["address"] = module_info[5];
+    results.push_back(r);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/memory_map.cpp b/src/osquery/tables/system/linux/memory_map.cpp
new file mode 100644 (file)
index 0000000..77dfe58
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/algorithm/string.hpp>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+namespace tables {
+
+const std::string kMemoryMapLocation = "/sys/firmware/memmap";
+
+QueryData genMemoryMap(QueryContext& context) {
+  QueryData results;
+
+  // Linux memory map is exposed in /sys.
+  std::vector<std::string> regions;
+  auto status = listDirectoriesInDirectory(kMemoryMapLocation, regions);
+  if (!status.ok()) {
+    return {};
+  }
+
+  for (const auto& index : regions) {
+    fs::path index_path(index);
+    Row r;
+    r["region"] = index_path.filename().string();
+
+    // The type is a textual description
+    std::string content;
+    readFile(index_path / "type", content);
+    boost::trim(content);
+    r["type"] = content;
+
+    // Keep these in 0xFFFF (hex) form.
+    readFile(index_path / "start", content);
+    boost::trim(content);
+    r["start"] = content;
+
+    readFile(index_path / "end", content);
+    boost::trim(content);
+    r["end"] = content;
+
+    results.push_back(r);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/model_specific_register.cpp b/src/osquery/tables/system/linux/model_specific_register.cpp
new file mode 100644 (file)
index 0000000..126d615
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#define MSR_FILENAME_BUFFER_SIZE 32
+
+#define NO_MASK 0xFFFFFFFFFFFFFFFFULL
+
+// Defines taken from uapi/asm/msr-index.h from the linux kernel.
+#define MSR_PLATFORM_INFO 0x000000ce
+
+#define MSR_IA32_FEATURE_CONTROL 0x0000003a
+
+#define MSR_IA32_PERF_STATUS 0x00000198
+#define MSR_IA32_PERF_CTL 0x00000199
+#define INTEL_PERF_CTL_MASK 0xffff
+
+#define MSR_IA32_MISC_ENABLE 0x000001a0
+
+#define MSR_TURBO_RATIO_LIMIT 0x000001ad
+
+#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT 38
+#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE \
+  (1ULL << MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT)
+
+// Run Time Average Power Limiting (RAPL).
+#define MSR_RAPL_POWER_UNIT 0x00000606
+#define MSR_PKG_ENERGY_STATUS 0x00000611
+#define MSR_PKG_POWER_LIMIT 0x00000610
+
+namespace osquery {
+namespace tables {
+
+// These are the entries to retrieve from the model specific register
+struct msr_record_t {
+  const char *name;
+  const off_t offset;
+  const uint64_t mask;
+  const int is_flag;
+};
+const static msr_record_t fields[] = {
+    {.name = "turbo_disabled",
+     .offset = MSR_IA32_MISC_ENABLE,
+     .mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE,
+     .is_flag = true},
+    {.name = "turbo_ratio_limit",
+     .offset = MSR_TURBO_RATIO_LIMIT,
+     .mask = NO_MASK,
+     .is_flag = false},
+    {.name = "platform_info",
+     .offset = MSR_PLATFORM_INFO,
+     .mask = NO_MASK,
+     .is_flag = false},
+    {.name = "perf_status",
+     .offset = MSR_IA32_PERF_STATUS,
+     .mask = NO_MASK,
+     .is_flag = false},
+    {.name = "perf_ctl",
+     .offset = MSR_IA32_PERF_CTL,
+     .mask = INTEL_PERF_CTL_MASK,
+     .is_flag = false},
+    {.name = "feature_control",
+     .offset = MSR_IA32_FEATURE_CONTROL,
+     .mask = NO_MASK,
+     .is_flag = false},
+    {.name = "rapl_power_limit",
+     .offset = MSR_PKG_POWER_LIMIT,
+     .mask = NO_MASK,
+     .is_flag = false},
+    {.name = "rapl_energy_status",
+     .offset = MSR_PKG_ENERGY_STATUS,
+     .mask = NO_MASK,
+     .is_flag = false},
+    {.name = "rapl_power_units",
+     .offset = MSR_RAPL_POWER_UNIT,
+     .mask = NO_MASK,
+     .is_flag = false}};
+
+void getModelSpecificRegisterData(QueryData &results, int cpu_number) {
+  auto msr_filename =
+    std::string("/dev/cpu/") + std::to_string(cpu_number) + "/msr";
+
+  int fd = open(msr_filename.c_str(), O_RDONLY);
+  if (fd < 0) {
+    int err = errno;
+    TLOG << "Could not open msr file " << msr_filename
+         << " check the msr kernel module is enabled.";
+    if (err == EACCES) {
+      LOG(WARNING) << "Could not access msr device.  Run osquery as root.";
+    }
+    return;
+  }
+
+  Row r;
+  r["processor_number"] = BIGINT(cpu_number);
+  for (const msr_record_t &field : fields) {
+    uint64_t output;
+    ssize_t size = pread(fd, &output, sizeof(uint64_t), field.offset);
+    if (size != sizeof(uint64_t)) {
+      // Processor does not have a record of this type.
+      continue;
+    }
+    if (field.is_flag) {
+      r[field.name] = BIGINT((output & field.mask) ? 1 : 0);
+    } else {
+      r[field.name] = BIGINT(output & field.mask);
+    }
+  }
+  results.push_back(r);
+  close(fd);
+
+  return;
+}
+
+// Filter only for filenames starting with a digit.
+int msrScandirFilter(const struct dirent *entry) {
+  if (isdigit(entry->d_name[0])) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+QueryData genModelSpecificRegister(QueryContext &context) {
+  QueryData results;
+
+  struct dirent **entries = nullptr;
+  int num_entries = scandir("/dev/cpu", &entries, msrScandirFilter, 0);
+  if (num_entries < 1) {
+    LOG(WARNING) << "No msr information check msr kernel module is enabled.";
+    return results;
+  }
+  while (num_entries--) {
+    getModelSpecificRegisterData(results, atoi(entries[num_entries]->d_name));
+    free(entries[num_entries]);
+  }
+  free(entries);
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/mounts.cpp b/src/osquery/tables/system/linux/mounts.cpp
new file mode 100644 (file)
index 0000000..d4b804c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <mntent.h>
+#include <sys/vfs.h>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+QueryData genMounts(QueryContext &context) {
+  QueryData results;
+  FILE *mounts;
+  struct mntent *ent;
+  char real_path[PATH_MAX];
+  struct statfs st;
+
+  if ((mounts = setmntent("/proc/mounts", "r"))) {
+    while ((ent = getmntent(mounts))) {
+      Row r;
+
+      r["device"] = std::string(ent->mnt_fsname);
+      r["device_alias"] = std::string(
+          realpath(ent->mnt_fsname, real_path) ? real_path : ent->mnt_fsname);
+      r["path"] = std::string(ent->mnt_dir);
+      r["type"] = std::string(ent->mnt_type);
+      r["flags"] = std::string(ent->mnt_opts);
+      if (!statfs(ent->mnt_dir, &st)) {
+        r["blocks_size"] = BIGINT(st.f_bsize);
+        r["blocks"] = BIGINT(st.f_blocks);
+        r["blocks_free"] = BIGINT(st.f_bfree);
+        r["blocks_available"] = BIGINT(st.f_bavail);
+        r["inodes"] = BIGINT(st.f_files);
+        r["inodes_free"] = BIGINT(st.f_ffree);
+      }
+
+      results.push_back(r);
+    }
+    endmntent(mounts);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/os_version.cpp b/src/osquery/tables/system/linux/os_version.cpp
new file mode 100644 (file)
index 0000000..e5ecad1
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <string>
+
+#include <boost/regex.hpp>
+#include <boost/xpressive/xpressive.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/sql.h>
+#include <osquery/tables.h>
+
+namespace xp = boost::xpressive;
+
+namespace osquery {
+namespace tables {
+
+const std::string kLinuxOSRelease = "/etc/redhat-release";
+const std::string kLinuxOSRegex =
+    "(?P<name>\\w+) .* "
+    "(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)[\\.]{0,1}(?P<patch>[0-9]+).*";
+
+QueryData genOSVersion(QueryContext& context) {
+  std::string content;
+  if (!readFile(kLinuxOSRelease, content).ok()) {
+    return {};
+  }
+
+  Row r;
+  auto rx = xp::sregex::compile(kLinuxOSRegex);
+  xp::smatch matches;
+  for (const auto& line : osquery::split(content, "\n")) {
+    if (xp::regex_search(line, matches, rx)) {
+      r["major"] = INTEGER(matches["major"]);
+      r["minor"] = INTEGER(matches["minor"]);
+      r["patch"] =
+          (matches["patch"].length() > 0) ? INTEGER(matches["patch"]) : "0";
+      r["name"] = matches["name"];
+      break;
+    }
+  }
+
+  // No build name.
+  r["build"] = "";
+  return {r};
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/pci_devices.cpp b/src/osquery/tables/system/linux/pci_devices.cpp
new file mode 100644 (file)
index 0000000..8a07587
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/events/linux/udev.h"
+
+namespace osquery {
+namespace tables {
+
+const std::string kPCIKeySlot = "PCI_SLOT_NAME";
+const std::string kPCIKeyClass = "ID_PCI_CLASS_FROM_DATABASE";
+const std::string kPCIKeyVendor = "ID_VENDOR_FROM_DATABASE";
+const std::string kPCIKeyModel = "ID_MODEL_FROM_DATABASE";
+const std::string kPCIKeyID = "PCI_ID";
+const std::string kPCIKeyDriver = "DRIVER";
+
+QueryData genPCIDevices(QueryContext &context) {
+  QueryData results;
+
+  auto udev_handle = udev_new();
+  if (udev_handle == nullptr) {
+    VLOG(1) << "Could not get udev handle.";
+    return results;
+  }
+
+  // Perform enumeration/search.
+  auto enumerate = udev_enumerate_new(udev_handle);
+  udev_enumerate_add_match_subsystem(enumerate, "pci");
+  udev_enumerate_scan_devices(enumerate);
+
+  // Get list entries and iterate over entries.
+  struct udev_list_entry *device_entries, *entry;
+  device_entries = udev_enumerate_get_list_entry(enumerate);
+
+  udev_list_entry_foreach(entry, device_entries) {
+    const char *path = udev_list_entry_get_name(entry);
+    auto device = udev_device_new_from_syspath(udev_handle, path);
+
+    Row r;
+    r["pci_slot"] = UdevEventPublisher::getValue(device, kPCIKeySlot);
+    r["pci_class"] = UdevEventPublisher::getValue(device, kPCIKeyClass);
+    r["driver"] = UdevEventPublisher::getValue(device, kPCIKeyDriver);
+    r["vendor"] = UdevEventPublisher::getValue(device, kPCIKeyVendor);
+    r["model"] = UdevEventPublisher::getValue(device, kPCIKeyModel);
+
+    // VENDOR:MODEL ID is in the form of HHHH:HHHH.
+    std::vector<std::string> ids;
+    auto device_id = UdevEventPublisher::getValue(device, kPCIKeyID);
+    boost::split(ids, device_id, boost::is_any_of(":"));
+    if (ids.size() == 2) {
+      r["vendor_id"] = ids[0];
+      r["model_id"] = ids[1];
+    }
+
+    // Set invalid vendor/model IDs to 0.
+    if (r["vendor_id"].size() == 0) {
+      r["vendor_id"] = "0";
+    }
+
+    if (r["model_id"].size() == 0) {
+      r["model_id"] = "0";
+    }
+
+    results.push_back(r);
+    udev_device_unref(device);
+  }
+
+  // Drop references to udev structs.
+  udev_enumerate_unref(enumerate);
+  udev_unref(udev_handle);
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/process_open_files.cpp b/src/osquery/tables/system/linux/process_open_files.cpp
new file mode 100644 (file)
index 0000000..8fd7380
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+
+namespace osquery {
+namespace tables {
+
+void genDescriptors(const std::string& process,
+                    const std::map<std::string, std::string>& descriptors,
+                    QueryData& results) {
+  for (const auto& fd : descriptors) {
+    if (fd.second.find("socket:") != std::string::npos ||
+        fd.second.find("anon_inode:") != std::string::npos ||
+        fd.second.find("pipe:") != std::string::npos) {
+      // This is NOT a vnode/file descriptor.
+      continue;
+    }
+
+    Row r;
+    r["pid"] = process;
+    r["fd"] = fd.first;
+    r["path"] = fd.second;
+    results.push_back(r);
+  }
+
+  return;
+}
+
+QueryData genOpenFiles(QueryContext& context) {
+  QueryData results;
+
+  std::set<std::string> pids;
+  if (context.constraints["pid"].exists(EQUALS)) {
+    pids = context.constraints["pid"].getAll(EQUALS);
+  } else {
+    osquery::procProcesses(pids);
+  }
+
+  for (const auto& process : pids) {
+    std::map<std::string, std::string> descriptors;
+    if (osquery::procDescriptors(process, descriptors).ok()) {
+      genDescriptors(process, descriptors, results);
+    }
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/processes.cpp b/src/osquery/tables/system/linux/processes.cpp
new file mode 100644 (file)
index 0000000..23641ec
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <string>
+#include <map>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/core.h>
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+
+namespace osquery {
+namespace tables {
+
+inline std::string getProcAttr(const std::string& attr, const std::string& pid) {
+  return "/proc/" + pid + "/" + attr;
+}
+
+inline std::string readProcCMDLine(const std::string& pid) {
+  auto attr = getProcAttr("cmdline", pid);
+
+  std::string content;
+  readFile(attr, content);
+  // Remove \0 delimiters.
+  std::replace_if(content.begin(),
+                  content.end(),
+                  [](const char& c) { return c == 0; },
+                  ' ');
+  // Remove trailing delimiter.
+  boost::algorithm::trim(content);
+  return content;
+}
+
+inline std::string readProcLink(const std::string& attr, const std::string& pid) {
+  // The exe is a symlink to the binary on-disk.
+  auto attr_path = getProcAttr(attr, pid);
+
+  std::string result;
+  char link_path[PATH_MAX] = {0};
+  auto bytes = readlink(attr_path.c_str(), link_path, sizeof(link_path) - 1);
+  if (bytes >= 0) {
+    result = std::string(link_path);
+  }
+
+  return result;
+}
+
+std::set<std::string> getProcList(const QueryContext& context) {
+  std::set<std::string> pidlist;
+  if (context.constraints.count("pid") > 0 &&
+      context.constraints.at("pid").exists(EQUALS)) {
+    for (const auto& pid : context.constraints.at("pid").getAll(EQUALS)) {
+      if (isDirectory("/proc/" + pid)) {
+        pidlist.insert(pid);
+      }
+    }
+  } else {
+    osquery::procProcesses(pidlist);
+  }
+
+  return pidlist;
+}
+
+void genProcessEnvironment(const std::string& pid, QueryData& results) {
+  auto attr = getProcAttr("environ", pid);
+
+  std::string content;
+  readFile(attr, content);
+  const char* variable = content.c_str();
+
+  // Stop at the end of nul-delimited string content.
+  while (*variable > 0) {
+    auto buf = std::string(variable);
+    size_t idx = buf.find_first_of("=");
+
+    Row r;
+    r["pid"] = pid;
+    r["key"] = buf.substr(0, idx);
+    r["value"] = buf.substr(idx + 1);
+    results.push_back(r);
+    variable += buf.size() + 1;
+  }
+}
+
+void genProcessMap(const std::string& pid, QueryData& results) {
+  auto map = getProcAttr("maps", pid);
+
+  std::string content;
+  readFile(map, content);
+  for (auto& line : osquery::split(content, "\n")) {
+    auto fields = osquery::split(line, " ");
+
+    Row r;
+    r["pid"] = pid;
+
+    // If can't read address, not sure.
+    if (fields.size() < 5) {
+      continue;
+    }
+
+    if (fields[0].size() > 0) {
+      auto addresses = osquery::split(fields[0], "-");
+      r["start"] = "0x" + addresses[0];
+      r["end"] = "0x" + addresses[1];
+    }
+
+    r["permissions"] = fields[1];
+    r["offset"] = BIGINT(std::stoll(fields[2], nullptr, 16));
+    r["device"] = fields[3];
+    r["inode"] = fields[4];
+
+    // Path name must be trimmed.
+    if (fields.size() > 5) {
+      boost::trim(fields[5]);
+      r["path"] = fields[5];
+    }
+
+    // BSS with name in pathname.
+    r["pseudo"] = (fields[4] == "0" && r["path"].size() > 0) ? "1" : "0";
+    results.push_back(r);
+  }
+}
+
+struct SimpleProcStat {
+  // Output from string parsing /proc/<pid>/status.
+  std::string parent; // PPid:
+  std::string name; // Name:
+  std::string real_uid; // Uid: * - - -
+  std::string real_gid; // Gid: * - - -
+  std::string effective_uid; // Uid: - * - -
+  std::string effective_gid; // Gid: - * - -
+
+  std::string resident_size; // VmRSS:
+  std::string phys_footprint;  // VmSize:
+
+  // Output from sring parsing /proc/<pid>/stat.
+  std::string user_time;
+  std::string system_time;
+  std::string start_time;
+};
+
+SimpleProcStat getProcStat(const std::string& pid) {
+  SimpleProcStat stat;
+  std::string content;
+  if (readFile(getProcAttr("stat", pid), content).ok()) {
+    auto detail_start = content.find_last_of(")");
+    // Start parsing stats from ") <MODE>..."
+    auto details = osquery::split(content.substr(detail_start + 2), " ");
+    stat.parent = details.at(1);
+    stat.user_time = details.at(11);
+    stat.system_time = details.at(12);
+    stat.start_time = details.at(19);
+  }
+
+  if (readFile(getProcAttr("status", pid), content).ok()) {
+    for (const auto& line : osquery::split(content, "\n")) {
+      // Status lines are formatted: Key: Value....\n.
+      auto detail = osquery::split(line, ":", 1);
+      if (detail.size() != 2) {
+        continue;
+      }
+
+      // There are specific fields from each detail.
+      if (detail.at(0) == "Name") {
+        stat.name = detail.at(1);
+      } else if (detail.at(0) == "VmRSS") {
+        detail[1].erase(detail.at(1).end() - 3, detail.at(1).end());
+        // Memory is reported in kB.
+        stat.resident_size = detail.at(1) + "000";
+      } else if (detail.at(0) == "VmSize") {
+        detail[1].erase(detail.at(1).end() - 3, detail.at(1).end());
+        // Memory is reported in kB.
+        stat.phys_footprint = detail.at(1) + "000";
+      } else if (detail.at(0) == "Gid") {
+        // Format is: R E - -
+        auto gid_detail = osquery::split(detail.at(1), "\t");
+        if (gid_detail.size() == 4) {
+          stat.real_gid = gid_detail.at(0);
+          stat.effective_gid = gid_detail.at(1);
+        }
+      } else if (detail.at(0) == "Uid") {
+        auto uid_detail = osquery::split(detail.at(1), "\t");
+        if (uid_detail.size() == 4) {
+          stat.real_uid = uid_detail.at(0);
+          stat.effective_uid = uid_detail.at(1);
+        }
+      }
+    }
+  }
+
+  return stat;
+}
+
+void genProcess(const std::string& pid, QueryData& results) {
+  // Parse the process stat and status.
+  auto proc_stat = getProcStat(pid);
+
+  Row r;
+  r["pid"] = pid;
+  r["parent"] = proc_stat.parent;
+  r["path"] = readProcLink("exe", pid);
+  r["name"] = proc_stat.name;
+
+  // Read/parse cmdline arguments.
+  r["cmdline"] = readProcCMDLine(pid);
+  r["cwd"] = readProcLink("cwd", pid);
+  r["root"] = readProcLink("root", pid);
+
+  r["uid"] = proc_stat.real_uid;
+  r["euid"] = proc_stat.effective_uid;
+  r["gid"] = proc_stat.real_gid;
+  r["egid"] = proc_stat.effective_gid;
+
+  // If the path of the executable that started the process is available and
+  // the path exists on disk, set on_disk to 1. If the path is not
+  // available, set on_disk to -1. If, and only if, the path of the
+  // executable is available and the file does NOT exist on disk, set on_disk
+  // to 0.
+  r["on_disk"] = osquery::pathExists(r["path"]).toString();
+
+  // size/memory information
+  r["wired_size"] = "0"; // No support for unpagable counters in linux.
+  r["resident_size"] = proc_stat.resident_size;
+  r["phys_footprint"] = proc_stat.phys_footprint;
+
+  // time information
+  r["user_time"] = proc_stat.user_time;
+  r["system_time"] = proc_stat.system_time;
+  r["start_time"] = proc_stat.start_time;
+
+  results.push_back(r);
+}
+
+QueryData genProcesses(QueryContext& context) {
+  QueryData results;
+
+  auto pidlist = getProcList(context);
+  for (const auto& pid : pidlist) {
+    genProcess(pid, results);
+  }
+
+  return results;
+}
+
+QueryData genProcessEnvs(QueryContext& context) {
+  QueryData results;
+
+  auto pidlist = getProcList(context);
+  for (const auto& pid : pidlist) {
+    genProcessEnvironment(pid, results);
+  }
+
+  return results;
+}
+
+QueryData genProcessMemoryMap(QueryContext& context) {
+  QueryData results;
+
+  auto pidlist = getProcList(context);
+  for (const auto& pid : pidlist) {
+    genProcessMap(pid, results);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/shared_memory.cpp b/src/osquery/tables/system/linux/shared_memory.cpp
new file mode 100644 (file)
index 0000000..441ef36
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sys/shm.h>
+#include <pwd.h>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+struct shm_info {
+  int used_ids;
+  unsigned long shm_tot;
+  unsigned long shm_rss;
+  unsigned long shm_swp;
+  unsigned long swap_attempts;
+  unsigned long swap_successes;
+} __attribute__((unused));
+
+QueryData genSharedMemory(QueryContext &context) {
+  QueryData results;
+
+  // Use shared memory control (shmctl) to get the max SHMID.
+  struct shm_info shm_info;
+  int maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
+  if (maxid < 0) {
+    VLOG(1) << "Linux kernel not configured for shared memory";
+    return {};
+  }
+
+  // Use a static pointer to access IPC permissions structure.
+  struct shmid_ds shmseg;
+  struct ipc_perm *ipcp = &shmseg.shm_perm;
+
+  // Then iterate each shared memory ID up to the max.
+  for (int id = 0; id <= maxid; id++) {
+    int shmid = shmctl(id, SHM_STAT, &shmseg);
+    if (shmid < 0) {
+      continue;
+    }
+
+    Row r;
+    r["shmid"] = INTEGER(shmid);
+
+    struct passwd *pw = getpwuid(shmseg.shm_perm.uid);
+    if (pw != nullptr) {
+      r["owner_uid"] = BIGINT(pw->pw_uid);
+    }
+
+    pw = getpwuid(shmseg.shm_perm.cuid);
+    if (pw != nullptr) {
+      r["creator_uid"] = BIGINT(pw->pw_uid);
+    }
+
+    // Accessor, creator pids.
+    r["pid"] = BIGINT(shmseg.shm_lpid);
+    r["creator_pid"] = BIGINT(shmseg.shm_cpid);
+
+    // Access, detached, creator times
+    r["atime"] = BIGINT(shmseg.shm_atime);
+    r["dtime"] = BIGINT(shmseg.shm_dtime);
+    r["ctime"] = BIGINT(shmseg.shm_ctime);
+
+    r["permissions"] = lsperms(ipcp->mode);
+    r["size"] = BIGINT(shmseg.shm_segsz);
+    r["attached"] = INTEGER(shmseg.shm_nattch);
+    r["status"] = (ipcp->mode & SHM_DEST) ? "dest" : "";
+    r["locked"] = (ipcp->mode & SHM_LOCKED) ? "1" : "0";
+
+    results.push_back(r);
+  }
+
+  return results;
+}
+}
+}
\ No newline at end of file
diff --git a/src/osquery/tables/system/linux/smbios_tables.cpp b/src/osquery/tables/system/linux/smbios_tables.cpp
new file mode 100644 (file)
index 0000000..890ec2f
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/tables/system/smbios_utils.h"
+
+namespace osquery {
+namespace tables {
+
+#define kLinuxSMBIOSRawAddress_ 0xF0000
+#define kLinuxSMBIOSRawLength_ 0x10000
+
+const std::string kLinuxEFISystabPath = "/sys/firmware/efi/systab";
+const std::string kLinuxLegacyEFISystabPath = "/proc/efi/systab";
+
+void genSMBIOSFromDMI(size_t base, size_t length, QueryData& results) {
+  // Linux will expose the SMBIOS/DMI entry point structures, which contain
+  // a member variable with the DMI tables start address and size.
+  // This applies to both the EFI-variable and physical memory search.
+  uint8_t* data;
+  auto status = osquery::readRawMem(base, length, (void**)&data);
+  if (!status.ok()) {
+    VLOG(1) << "Could not read DMI tables memory";
+    return;
+  }
+
+  // Attempt to parse tables from allocated data.
+  genSMBIOSTables(data, length, results);
+  free(data);
+}
+
+void genEFISystabTables(QueryData& results) {
+  // Not yet supported.
+  return;
+}
+
+void genRawSMBIOSTables(QueryData& results) {
+  uint8_t* data;
+  auto status = osquery::readRawMem(
+      kLinuxSMBIOSRawAddress_, kLinuxSMBIOSRawLength_, (void**)&data);
+  if (!status.ok()) {
+    VLOG(1) << "Could not read SMBIOS memory";
+    return;
+  }
+
+  // Search for the SMBIOS/DMI tables magic header string.
+  size_t offset;
+  for (offset = 0; offset <= 0xFFF0; offset += 16) {
+    // Could look for "_SM_" for the SMBIOS header, but the DMI header exists
+    // in both SMBIOS and the legacy DMI spec.
+    if (memcmp(data + offset, "_DMI_", 5) == 0) {
+      auto dmi_data = (DMIEntryPoint*)(data + offset);
+      genSMBIOSFromDMI(dmi_data->tableAddress, dmi_data->tableLength, results);
+    }
+  }
+
+  free(data);
+}
+
+QueryData genSMBIOSTables(QueryContext& context) {
+  QueryData results;
+
+  if (osquery::isReadable(kLinuxEFISystabPath).ok() ||
+      osquery::isReadable(kLinuxLegacyEFISystabPath).ok()) {
+    genEFISystabTables(results);
+  } else {
+    genRawSMBIOSTables(results);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/sysctl_utils.cpp b/src/osquery/tables/system/linux/sysctl_utils.cpp
new file mode 100644 (file)
index 0000000..a776418
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sys/sysctl.h>
+
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/tables.h>
+
+#include "osquery/tables/system/sysctl_utils.h"
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+namespace tables {
+
+const std::string kSystemControlPath = "/proc/sys/";
+
+void genControlInfo(const std::string& mib_path, QueryData& results,
+                    const std::map<std::string, std::string>& config) {
+  if (isDirectory(mib_path).ok()) {
+    // Iterate through the subitems and items.
+    std::vector<std::string> items;
+    if (listDirectoriesInDirectory(mib_path, items).ok()) {
+      for (const auto& item : items) {
+        genControlInfo(item, results, config);
+      }
+    }
+
+    if (listFilesInDirectory(mib_path, items).ok()) {
+      for (const auto& item : items) {
+        genControlInfo(item, results, config);
+      }
+    }
+    return;
+  }
+
+  // This is a file (leaf-control).
+  Row r;
+  r["name"] = mib_path.substr(kSystemControlPath.size());
+
+  std::replace(r["name"].begin(), r["name"].end(), '/', '.');
+  // No known way to convert name MIB to int array.
+  r["subsystem"] = osquery::split(r.at("name"), ".")[0];
+
+  if (isReadable(mib_path).ok()) {
+    std::string content;
+    readFile(mib_path, content);
+    boost::trim(content);
+    r["current_value"] = content;
+  }
+
+  if (config.count(r.at("name")) > 0) {
+    r["config_value"] = config.at(r.at("name"));
+  }
+  r["type"] = "string";
+  results.push_back(r);
+}
+
+void genControlInfo(int* oid,
+                    size_t oid_size,
+                    QueryData& results,
+                    const std::map<std::string, std::string>& config) {
+  // Get control size
+  size_t response_size = CTL_MAX_VALUE;
+  char response[CTL_MAX_VALUE + 1] = {0};
+    if (sysctl(oid, oid_size, response, &response_size, 0, 0) != 0) {
+      // Cannot request MIB data.
+      return;
+    }
+
+    // Data is output, but no way to determine type (long, int, string, struct).
+  Row r;
+  r["oid"] = stringFromMIB(oid, oid_size);
+  r["current_value"] = std::string(response);
+  r["type"] = "string";
+  results.push_back(r);
+}
+
+void genAllControls(QueryData& results,
+                    const std::map<std::string, std::string>& config,
+                    const std::string& subsystem) {
+  // Linux sysctl subsystems are directories in /proc
+  std::vector<std::string> subsystems;
+  if (!listDirectoriesInDirectory("/proc/sys", subsystems).ok()) {
+    return;
+  }
+
+  for (const auto& sub : subsystems) {
+    if (subsystem.size() != 0 &&
+        fs::path(sub).filename().string() != subsystem) {
+      // Request is limiting subsystem.
+      continue;
+    } 
+    genControlInfo(sub, results, config);
+  }
+}
+
+void genControlInfoFromName(const std::string& name, QueryData& results,
+                    const std::map<std::string, std::string>& config) {
+  // Convert '.'-tokenized name to path.
+  std::string name_path = name;
+  std::replace(name_path.begin(), name_path.end(), '.', '/');
+  auto mib_path = fs::path(kSystemControlPath) / name_path;
+
+  genControlInfo(mib_path.string(), results, config);
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/usb_devices.cpp b/src/osquery/tables/system/linux/usb_devices.cpp
new file mode 100644 (file)
index 0000000..476e87b
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/core.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+#include "osquery/events/linux/udev.h"
+
+namespace osquery {
+namespace tables {
+
+const std::string kUSBKeyVendorID = "ID_VENDOR_ID";
+const std::string kUSBKeyVendor = "ID_VENDOR_FROM_DATABASE";
+const std::string kUSBKeyModelID = "ID_MODEL_ID";
+const std::string kUSBKeyModel = "ID_MODEL_FROM_DATABASE";
+const std::string kUSBKeyDriver = "ID_USB_DRIVER";
+const std::string kUSBKeySubsystem = "SUBSYSTEM";
+const std::string kUSBKeySerial = "ID_SERIAL_SHORT";
+const std::string kUSBKeyAddress = "BUSNUM";
+const std::string kUSBKeyPort = "DEVNUM";
+
+QueryData genUSBDevices(QueryContext &context) {
+  QueryData results;
+
+  auto udev_handle = udev_new();
+  if (udev_handle == nullptr) {
+    VLOG(1) << "Could not get udev handle.";
+    return results;
+  }
+
+  // Perform enumeration/search.
+  auto enumerate = udev_enumerate_new(udev_handle);
+  udev_enumerate_add_match_subsystem(enumerate, "usb");
+  udev_enumerate_scan_devices(enumerate);
+
+  // Get list entries and iterate over entries.
+  struct udev_list_entry *device_entries, *entry;
+  device_entries = udev_enumerate_get_list_entry(enumerate);
+
+  udev_list_entry_foreach(entry, device_entries) {
+    const char *path = udev_list_entry_get_name(entry);
+    auto device = udev_device_new_from_syspath(udev_handle, path);
+
+    Row r;
+    // r["driver"] = UdevEventPublisher::getValue(device, kUSBKeyDriver);
+    r["vendor"] = UdevEventPublisher::getValue(device, kUSBKeyVendor);
+    r["model"] = UdevEventPublisher::getValue(device, kUSBKeyModel);
+
+    // USB-specific vendor/model ID properties.
+    r["model_id"] = UdevEventPublisher::getValue(device, kUSBKeyModelID);
+    r["vendor_id"] = UdevEventPublisher::getValue(device, kUSBKeyVendorID);
+    r["serial"] = UdevEventPublisher::getValue(device, kUSBKeySerial);
+
+    // Address/port accessors.
+    r["usb_address"] = UdevEventPublisher::getValue(device, kUSBKeyAddress);
+    r["usb_port"] = UdevEventPublisher::getValue(device, kUSBKeyPort);
+
+    // Removable detection.
+    auto removable = UdevEventPublisher::getAttr(device, "removable");
+    if (removable == "unknown") {
+      r["removable"] = "-1";
+    } else {
+      r["removable"] = "1";
+    }
+
+    if (r["usb_address"].size() > 0 && r["usb_port"].size() > 0) {
+      results.push_back(r);
+    }
+    udev_device_unref(device);
+  }
+
+  // Drop references to udev structs.
+  udev_enumerate_unref(enumerate);
+  udev_unref(udev_handle);
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/user_groups.cpp b/src/osquery/tables/system/linux/user_groups.cpp
new file mode 100644 (file)
index 0000000..da8be00
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include "osquery/tables/system/user_groups.h"
+
+namespace osquery {
+namespace tables {
+
+extern std::mutex pwdEnumerationMutex;
+
+QueryData genUserGroups(QueryContext &context) {
+  QueryData results;
+  struct passwd *pwd = nullptr;
+
+  if (context.constraints["uid"].exists(EQUALS)) {
+    std::set<std::string> uids = context.constraints["uid"].getAll(EQUALS);
+    for (const auto &uid : uids) {
+      pwd = getpwuid(std::strtol(uid.c_str(), NULL, 10));
+      if (pwd != nullptr) {
+        user_t<uid_t, gid_t> user;
+        user.name = pwd->pw_name;
+        user.uid = pwd->pw_uid;
+        user.gid = pwd->pw_gid;
+        getGroupsForUser<uid_t, gid_t>(results, user);
+      }
+    }
+  } else {
+    std::lock_guard<std::mutex> lock(pwdEnumerationMutex);
+    std::set<gid_t> users_in;
+    while ((pwd = getpwent()) != nullptr) {
+      if (std::find(users_in.begin(), users_in.end(), pwd->pw_uid) ==
+          users_in.end()) {
+        user_t<uid_t, gid_t> user;
+        user.name = pwd->pw_name;
+        user.uid = pwd->pw_uid;
+        user.gid = pwd->pw_gid;
+        getGroupsForUser<uid_t, gid_t>(results, user);
+        users_in.insert(pwd->pw_uid);
+      }
+    }
+    endpwent();
+    users_in.clear();
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/linux/users.cpp b/src/osquery/tables/system/linux/users.cpp
new file mode 100644 (file)
index 0000000..6108987
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <set>
+#include <mutex>
+#include <vector>
+#include <string>
+
+#include <pwd.h>
+
+#include <osquery/core.h>
+#include <osquery/tables.h>
+#include <osquery/status.h>
+#include <osquery/logger.h>
+
+namespace osquery {
+namespace tables {
+
+std::mutex pwdEnumerationMutex;
+
+QueryData genUsers(QueryContext& context) {
+  std::lock_guard<std::mutex> lock(pwdEnumerationMutex);
+  QueryData results;
+  struct passwd *pwd = nullptr;
+  std::set<long> users_in;
+
+  while ((pwd = getpwent()) != nullptr) {
+    if (std::find(users_in.begin(), users_in.end(), pwd->pw_uid) ==
+        users_in.end()) {
+      Row r;
+      r["uid"] = BIGINT(pwd->pw_uid);
+      r["gid"] = BIGINT(pwd->pw_gid);
+      r["uid_signed"] = BIGINT((int32_t) pwd->pw_uid);
+      r["gid_signed"] = BIGINT((int32_t) pwd->pw_gid);
+      r["username"] = TEXT(pwd->pw_name);
+      r["description"] = TEXT(pwd->pw_gecos);
+      r["directory"] = TEXT(pwd->pw_dir);
+      r["shell"] = TEXT(pwd->pw_shell);
+      results.push_back(r);
+      users_in.insert(pwd->pw_uid);
+    }
+  }
+  endpwent();
+  users_in.clear();
+
+  return results;
+}
+
+/// Example of update feature
+Status updateUsers(Row& row) {
+  for (auto& r : row)
+    LOG(ERROR) << "DEBUG: " << r.first << ", " << r.second;
+
+  return Status(0, "OK");
+}
+}
+}
diff --git a/src/osquery/tables/system/logged_in_users.cpp b/src/osquery/tables/system/logged_in_users.cpp
new file mode 100644 (file)
index 0000000..ae24a33
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <mutex>
+
+#include <osquery/core.h>
+#include <osquery/tables.h>
+
+#include <utmpx.h>
+
+namespace osquery {
+namespace tables {
+
+std::mutex utmpxEnumerationMutex;
+
+QueryData genLoggedInUsers(QueryContext& context) {
+  std::lock_guard<std::mutex> lock(utmpxEnumerationMutex);
+  QueryData results;
+  struct utmpx *entry = nullptr;
+
+  while ((entry = getutxent()) != nullptr) {
+    if (entry->ut_pid == 1) {
+      continue;
+    }
+    Row r;
+    r["user"] = TEXT(entry->ut_user);
+    r["tty"] = TEXT(entry->ut_line);
+    r["host"] = TEXT(entry->ut_host);
+    r["time"] = INTEGER(entry->ut_tv.tv_sec);
+    r["pid"] = INTEGER(entry->ut_pid);
+    results.push_back(r);
+  }
+  endutxent();
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/shell_history.cpp b/src/osquery/tables/system/shell_history.cpp
new file mode 100644 (file)
index 0000000..71681a6
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <string>
+#include <vector>
+
+#include <pwd.h>
+
+#include <osquery/core.h>
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/sql.h>
+
+namespace osquery {
+namespace tables {
+
+const std::vector<std::string> kShellHistoryFiles = {
+    ".bash_history", ".zsh_history", ".zhistory", ".history",
+};
+
+void genShellHistoryForUser(const std::string& username,
+                            const std::string& directory,
+                            QueryData& results) {
+  for (const auto& hfile : kShellHistoryFiles) {
+    boost::filesystem::path history_file = directory;
+    history_file /= hfile;
+
+    std::string history_content;
+    if (!readFile(history_file, history_content).ok()) {
+      // Cannot read a specific history file.
+      continue;
+    }
+
+    for (const auto& line : split(history_content, "\n")) {
+      Row r;
+      r["username"] = username;
+      r["command"] = line;
+      r["history_file"] = history_file.string();
+      results.push_back(r);
+    }
+  }
+}
+
+QueryData genShellHistory(QueryContext& context) {
+  QueryData results;
+
+  // Select only the home directory for this user.
+  QueryData users;
+  if (!context.constraints["username"].exists(EQUALS)) {
+    users =
+        SQL::selectAllFrom("users", "uid", EQUALS, std::to_string(getuid()));
+  } else {
+    auto usernames = context.constraints["username"].getAll(EQUALS);
+    for (const auto& username : usernames) {
+      // Use a predicated select all for each user.
+      auto user = SQL::selectAllFrom("users", "username", EQUALS, username);
+      users.insert(users.end(), user.begin(), user.end());
+    }
+  }
+
+  // Iterate over each user
+  for (const auto& row : users) {
+    if (row.count("username") > 0 && row.count("directory") > 0) {
+      genShellHistoryForUser(row.at("username"), row.at("directory"), results);
+    }
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/smbios_utils.cpp b/src/osquery/tables/system/smbios_utils.cpp
new file mode 100644 (file)
index 0000000..4e0ba4c
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/hash.h>
+
+#include "osquery/tables/system/smbios_utils.h"
+
+namespace osquery {
+namespace tables {
+
+const std::map<int, std::string> kSMBIOSTypeDescriptions = {
+    {0, "BIOS Information"},
+    {1, "System Information"},
+    {2, "Base Board or Module Information"},
+    {3, "System Enclosure or Chassis"},
+    {4, "Processor Information"},
+    {5, "Memory Controller Information"},
+    {6, "Memory Module Information"},
+    {7, "Cache Information"},
+    {8, "Port Connector Information"},
+    {9, "System Slots"},
+    {10, "On Board Devices Information"},
+    {11, "OEM Strings"},
+    {12, "System Configuration Options"},
+    {13, "BIOS Language Information"},
+    {14, "Group Associations"},
+    {15, "System Event Log"},
+    {16, "Physical Memory Array"},
+    {17, "Memory Device"},
+    {18, "32-bit Memory Error Information"},
+    {19, "Memory Array Mapped Address"},
+    {20, "Memory Device Mapped Address"},
+    {21, "Built-in Pointing Device"},
+    {22, "Portable Battery"},
+    {23, "System Reset"},
+    {24, "Hardware Security"},
+    {25, "System Power Controls"},
+    {26, "Voltage Probe"},
+    {27, "Cooling Device"},
+    {28, "Temperature Probe"},
+    {29, "Electrical Current Probe"},
+    {30, "Out-of-Band Remote Access"},
+    {31, "Boot Integrity Services"},
+    {32, "System Boot Information"},
+    {33, "64-bit Memory Error Information"},
+    {34, "Management Device"},
+    {35, "Management Device Component"},
+    {36, "Management Device Threshold Data"},
+    {37, "Memory Channel"},
+    {38, "IPMI Device Information"},
+    {39, "System Power Supply"},
+    {40, "Additional Information"},
+    {41, "Onboard Devices Extended Info"},
+    {126, "Inactive"},
+    {127, "End-of-Table"},
+    {130, "Memory SPD Data"},
+    {131, "OEM Processor Type"},
+    {132, "OEM Processor Bus Speed"},
+};
+
+void genSMBIOSTables(const uint8_t* tables, size_t length, QueryData& results) {
+  // Keep a pointer to the end of the SMBIOS data for comparison.
+  auto tables_end = tables + length;
+  auto table = tables;
+
+  // Iterate through table structures within SMBIOS data range.
+  size_t index = 0;
+  while (table + sizeof(SMBStructHeader) <= tables_end) {
+    auto header = (const SMBStructHeader*)table;
+    if (table + header->length > tables_end) {
+      // Invalid header, length must be within SMBIOS data range.
+      break;
+    }
+
+    Row r;
+    // The index is a supliment that keeps track of table order.
+    r["number"] = INTEGER(index++);
+    r["type"] = INTEGER((unsigned short)header->type);
+    if (kSMBIOSTypeDescriptions.count(header->type) > 0) {
+      r["description"] = kSMBIOSTypeDescriptions.at(header->type);
+    }
+
+    r["handle"] = BIGINT((unsigned long long)header->handle);
+    r["header_size"] = INTEGER((unsigned short)header->length);
+
+    // The SMBIOS structure may have unformatted, double-NULL delimited trailing
+    // data, which are usually strings.
+    auto next_table = table + header->length;
+    for (; next_table + sizeof(SMBStructHeader) <= tables_end; next_table++) {
+      if (next_table[0] == 0 && next_table[1] == 0) {
+        next_table += 2;
+        break;
+      }
+    }
+
+    auto table_length = next_table - table;
+    r["size"] = INTEGER(table_length);
+    r["md5"] = hashFromBuffer(HASH_TYPE_MD5, table, table_length);
+
+    table = next_table;
+    results.push_back(r);
+  }
+}
+}
+}
diff --git a/src/osquery/tables/system/smbios_utils.h b/src/osquery/tables/system/smbios_utils.h
new file mode 100644 (file)
index 0000000..ff5eea3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+typedef struct SMBStructHeader {
+  uint8_t type;
+  uint8_t length;
+  uint16_t handle;
+} __attribute__((packed)) SMBStructHeader;
+
+typedef struct DMIEntryPoint {
+  uint8_t anchor[5];
+  uint8_t checksum;
+  uint16_t tableLength;
+  uint32_t tableAddress;
+  uint16_t structureCount;
+  uint8_t bcdRevision;
+} __attribute__((packed)) DMIEntryPoint;
+
+extern const std::map<int, std::string> kSMBIOSTypeDescriptions;
+
+void genSMBIOSTables(const uint8_t* tables, size_t length, QueryData& results);
+}
+}
diff --git a/src/osquery/tables/system/suid_bin.cpp b/src/osquery/tables/system/suid_bin.cpp
new file mode 100644 (file)
index 0000000..43811e6
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
+
+#include <boost/filesystem.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+namespace tables {
+
+std::vector<std::string> kBinarySearchPaths = {
+  "/bin",
+  "/sbin",
+  "/usr/bin",
+  "/usr/sbin",
+  "/usr/local/bin",
+  "/usr/local/sbin",
+  "/tmp",
+};
+
+Status genBin(const fs::path& path, int perms, QueryData& results) {
+  struct stat info;
+  // store user and group
+  if (stat(path.c_str(), &info) != 0) {
+    return Status(1, "stat failed");
+  }
+
+  // store path
+  Row r;
+  r["path"] = path.string();
+  struct passwd *pw = getpwuid(info.st_uid);
+  struct group *gr = getgrgid(info.st_gid);
+
+  // get user name + group
+  std::string user;
+  if (pw != nullptr) {
+    user = std::string(pw->pw_name);
+  } else {
+    user = boost::lexical_cast<std::string>(info.st_uid);
+  }
+
+  std::string group;
+  if (gr != nullptr) {
+    group = std::string(gr->gr_name);
+  } else {
+    group = boost::lexical_cast<std::string>(info.st_gid);
+  }
+
+  r["username"] = user;
+  r["groupname"] = group;
+
+  r["permissions"] = "";
+  if ((perms & 04000) == 04000) {
+    r["permissions"] += "S";
+  }
+
+  if ((perms & 02000) == 02000) {
+    r["permissions"] += "G";
+  }
+
+  results.push_back(r);
+  return Status(0, "OK");
+}
+
+bool isSuidBin(const fs::path& path, int perms) {
+  if (!fs::is_regular_file(path)) {
+    return false;
+  }
+
+  if ((perms & 04000) == 04000 || (perms & 02000) == 02000) {
+    return true;
+  }
+  return false;
+}
+
+void genSuidBinsFromPath(const std::string& path, QueryData& results) {
+  if (!pathExists(path).ok()) {
+    // Creating an iterator on a missing path will except.
+    return;
+  }
+
+  auto it = fs::recursive_directory_iterator(fs::path(path));
+  fs::recursive_directory_iterator end;
+  while (it != end) {
+    fs::path path = *it;
+    try {
+      // Do not traverse symlinked directories.
+      if (fs::is_directory(path) && fs::is_symlink(path)) {
+        it.no_push();
+      }
+
+      int perms = it.status().permissions();
+      if (isSuidBin(path, perms)) {
+        // Only emit suid bins.
+        genBin(path, perms, results);
+      }
+
+      ++it;
+    } catch (fs::filesystem_error& e) {
+      VLOG(1) << "Cannot read binary from " << path;
+      it.no_push();
+      // Try to recover, otherwise break.
+      try { ++it; } catch(fs::filesystem_error& e) { break; }
+    }
+  }
+}
+
+QueryData genSuidBin(QueryContext& context) {
+  QueryData results;
+
+  // Todo: add hidden column to select on that triggers non-std path searches.
+  for (const auto& path : kBinarySearchPaths) {
+    genSuidBinsFromPath(path, results);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/sysctl_utils.h b/src/osquery/tables/system/sysctl_utils.h
new file mode 100644 (file)
index 0000000..4346d71
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sys/sysctl.h>
+
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+#define CTL_MAX_VALUE 128
+
+#ifndef CTL_DEBUG_MAXID
+#define CTL_DEBUG_MAXID (CTL_MAXNAME * 2)
+#endif
+
+std::string stringFromMIB(const int* oid, size_t oid_size);
+
+/// Must be implemented by the platform.
+void genAllControls(QueryData& results,
+                    const std::map<std::string, std::string>& config,
+                    const std::string& subsystem);
+
+/// Must be implemented by the platform.
+void genControlInfo(int* oid,
+                    size_t oid_size,
+                    QueryData& results,
+                    const std::map<std::string, std::string>& config);
+
+/// Must be implemented by the platform.
+void genControlInfoFromName(const std::string& name, QueryData& results,
+                    const std::map<std::string, std::string>& config);
+}
+}
diff --git a/src/osquery/tables/system/system_controls.cpp b/src/osquery/tables/system/system_controls.cpp
new file mode 100644 (file)
index 0000000..c91d0ca
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/tables.h>
+
+#include "osquery/tables/system/sysctl_utils.h"
+
+namespace osquery {
+namespace tables {
+
+const std::vector<std::string> kControlSettingsFiles = {"/etc/sysctl.conf"};
+
+const std::vector<std::string> kControlSettingsDirs = {
+    "/run/sysctl.d/%.conf",
+    "/etc/sysctl.d/%.conf",
+    "/usr/local/lib/sysctl.d/%.conf",
+    "/usr/lib/sysctl.d/%.conf",
+    "/lib/sysctl.d/%.conf",
+};
+
+std::string stringFromMIB(const int* oid, size_t oid_size) {
+  std::string result;
+  for (size_t i = 0; i < oid_size; ++i) {
+    // Walk an int-encoded MIB and return the string representation, '.'.
+    if (result.size() > 0) {
+      result += ".";
+    }
+    result += std::to_string(oid[i]);
+  }
+  return result;
+}
+
+void genControlInfoFromOIDString(
+    const std::string& oid_string,
+    QueryData& results,
+    const std::map<std::string, std::string>& config) {
+  int request[CTL_DEBUG_MAXID + 2] = {0};
+  auto tokens = osquery::split(oid_string, ".");
+  if (tokens.size() > CTL_DEBUG_MAXID) {
+    // OID input string was too large.
+    return;
+  }
+
+  // Convert the string into an int array.
+  for (size_t i = 0; i < tokens.size(); ++i) {
+    request[i] = atol(tokens.at(i).c_str());
+  }
+  genControlInfo((int*)request, tokens.size(), results, config);
+}
+
+void genControlConfigFromPath(const std::string& path,
+                              std::map<std::string, std::string>& config) {
+  std::string content;
+  if (!osquery::readFile(path, content).ok()) {
+    return;
+  }
+
+  for (auto& line : split(content, "\n")) {
+    boost::trim(line);
+    if (line[0] == '#' || line[0] == ';') {
+      continue;
+    }
+
+    // Try to tokenize the config line using '='.
+    auto detail = split(line, "=");
+    if (detail.size() == 2) {
+      boost::trim(detail[0]);
+      boost::trim(detail[1]);
+      config[detail[0]] = detail[1];
+    }
+  }
+}
+
+QueryData genSystemControls(QueryContext& context) {
+  QueryData results;
+
+  // Read the sysctl.conf values.
+  std::map<std::string, std::string> config;
+  for (const auto& path : kControlSettingsFiles) {
+    genControlConfigFromPath(path, config);
+  }
+
+  for (const auto& dirs : kControlSettingsDirs) {
+    std::vector<std::string> configs;
+    if (resolveFilePattern(dirs, configs).ok()) {
+      for (const auto& path : configs) {
+        genControlConfigFromPath(path, config);
+      }
+    }
+  }
+
+  // Iterate through the sysctl-defined macro of control types.
+  if (context.constraints["name"].exists(EQUALS)) {
+    // Request MIB information by the description (name).
+    auto names = context.constraints["name"].getAll(EQUALS);
+    for (const auto& name : names) {
+      genControlInfoFromName(name, results, config);
+    }
+  } else if (context.constraints["oid"].exists(EQUALS)) {
+    // Request MIB by OID as a string, parse into set of INTs.
+    auto oids = context.constraints["oid"].getAll(EQUALS);
+    for (const auto& oid_string : oids) {
+      genControlInfoFromOIDString(oid_string, results, config);
+    }
+  } else if (context.constraints["subsystem"].exists(EQUALS)) {
+    // Limit the MIB search to a subsystem name (first find the INT).
+    auto subsystems = context.constraints["subsystem"].getAll(EQUALS);
+    for (const auto& subsystem : subsystems) {
+      genAllControls(results, config, subsystem);
+    }
+  } else {
+    genAllControls(results, config, "");
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/uptime.cpp b/src/osquery/tables/system/uptime.cpp
new file mode 100644 (file)
index 0000000..5528896
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/tables.h>
+
+#if defined(__APPLE__)
+  #include <time.h>
+  #include <errno.h>
+  #include <sys/sysctl.h>
+#elif defined(__linux__)
+  #include <sys/sysinfo.h>
+#endif
+
+namespace osquery {
+namespace tables {
+
+long getUptime() {
+  #if defined(__APPLE__)
+    struct timeval boot_time;
+    size_t len = sizeof(boot_time);
+    int mib[2] = {
+        CTL_KERN,
+        KERN_BOOTTIME
+    };
+
+    if (sysctl(mib, 2, &boot_time, &len, NULL, 0) < 0) {
+        return -1;
+    }
+
+    time_t seconds_since_boot = boot_time.tv_sec;
+    time_t current_seconds = time(NULL);
+
+    return long(difftime(current_seconds, seconds_since_boot));
+  #elif defined(__linux__)
+    struct sysinfo sys_info;
+
+    if (sysinfo(&sys_info) != 0) {
+      return -1;
+    }
+
+    return sys_info.uptime;
+  #endif
+}
+
+QueryData genUptime(QueryContext& context) {
+  Row r;
+  QueryData results;
+  long uptime_in_seconds = getUptime();
+
+  if (uptime_in_seconds >= 0) {
+    r["days"] = INTEGER(uptime_in_seconds / 60 / 60 / 24);
+    r["hours"] = INTEGER((uptime_in_seconds / 60 / 60) % 24);
+    r["minutes"] = INTEGER((uptime_in_seconds / 60) % 60);
+    r["seconds"] = INTEGER(uptime_in_seconds % 60);
+    r["total_seconds"] = BIGINT(uptime_in_seconds);
+    results.push_back(r);
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/system/user_groups.h b/src/osquery/tables/system/user_groups.h
new file mode 100644 (file)
index 0000000..384f8a3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <vector>
+#include <string>
+
+#include <grp.h>
+#include <pwd.h>
+
+#include <osquery/core.h>
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+// This is also the max supported number for OS X right now.
+#define EXPECTED_GROUPS_MAX 64
+
+namespace osquery {
+namespace tables {
+
+template <typename T>
+static inline void addGroupsToResults(QueryData &results,
+                                      int uid,
+                                      const T *groups,
+                                      int ngroups) {
+  for (int i = 0; i < ngroups; i++) {
+    Row r;
+    r["uid"] = BIGINT(uid);
+    r["gid"] = BIGINT(groups[i]);
+    results.push_back(r);
+  }
+
+  return;
+}
+
+template <typename uid_type, typename gid_type>
+struct user_t {
+  const char *name;
+  uid_type uid;
+  gid_type gid;
+};
+
+template <typename uid_type, typename gid_type>
+static void getGroupsForUser(QueryData &results,
+                             const user_t<uid_type, gid_type> &user) {
+  gid_type groups_buf[EXPECTED_GROUPS_MAX];
+  gid_type *groups = groups_buf;
+  int ngroups = EXPECTED_GROUPS_MAX;
+
+  // GLIBC version before 2.3.3 may have a buffer overrun:
+  // http://man7.org/linux/man-pages/man3/getgrouplist.3.html
+  if (getgrouplist(user.name, user.gid, groups, &ngroups) < 0) {
+    // EXPECTED_GROUPS_MAX was probably not large enough.
+    // Try a larger size buffer.
+    // Darwin appears to not resize ngroups correctly.  We can hope
+    // we had enough space to start with.
+    groups = new gid_type[ngroups];
+    if (groups == nullptr) {
+      TLOG << "Could not allocate memory to get user groups";
+      return;
+    }
+
+    if (getgrouplist(user.name, user.gid, groups, &ngroups) < 0) {
+      TLOG << "Could not get users group list";
+    } else {
+      addGroupsToResults(results, user.uid, groups, ngroups);
+    }
+
+    delete[] groups;
+  } else {
+    addGroupsToResults(results, user.uid, groups, ngroups);
+  }
+  return;
+}
+}
+}
diff --git a/src/osquery/tables/utility/file.cpp b/src/osquery/tables/utility/file.cpp
new file mode 100644 (file)
index 0000000..5e5347a
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <sys/stat.h>
+
+#include <boost/filesystem.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/logger.h>
+#include <osquery/tables.h>
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+namespace tables {
+
+void genFileInfo(const std::string& path,
+                 const std::string& filename,
+                 const std::string& dir,
+                 const std::string& pattern,
+                 QueryData& results) {
+  // Must provide the path, filename, directory separate from boost path->string
+  // helpers to match any explicit (query-parsed) predicate constraints.
+  struct stat file_stat, link_stat;
+  if (lstat(path.c_str(), &link_stat) < 0 || stat(path.c_str(), &file_stat)) {
+    // Path was not real, had too may links, or could not be accessed.
+    return;
+  }
+
+  Row r;
+  r["path"] = path;
+  r["filename"] = filename;
+  r["directory"] = dir;
+
+  r["inode"] = BIGINT(file_stat.st_ino);
+  r["uid"] = BIGINT(file_stat.st_uid);
+  r["gid"] = BIGINT(file_stat.st_gid);
+  r["mode"] = lsperms(file_stat.st_mode);
+  r["device"] = BIGINT(file_stat.st_rdev);
+  r["size"] = BIGINT(file_stat.st_size);
+  r["block_size"] = INTEGER(file_stat.st_blksize);
+  r["hard_links"] = INTEGER(file_stat.st_nlink);
+
+  // Times
+  r["atime"] = BIGINT(file_stat.st_atime);
+  r["mtime"] = BIGINT(file_stat.st_mtime);
+  r["ctime"] = BIGINT(file_stat.st_ctime);
+
+  // Type booleans
+  r["is_file"] = (!S_ISDIR(file_stat.st_mode)) ? "1" : "0";
+  r["is_dir"] = (S_ISDIR(file_stat.st_mode)) ? "1" : "0";
+  r["is_link"] = (S_ISLNK(link_stat.st_mode)) ? "1" : "0";
+  r["is_char"] = (S_ISCHR(file_stat.st_mode)) ? "1" : "0";
+  r["is_block"] = (S_ISBLK(file_stat.st_mode)) ? "1" : "0";
+
+  // pattern
+  r["pattern"] = pattern;
+
+  results.push_back(r);
+}
+
+QueryData genFile(QueryContext& context) {
+  QueryData results;
+
+  auto paths = context.constraints["path"].getAll(EQUALS);
+  for (const auto& path_string : paths) {
+    if (!isReadable(path_string)) {
+      continue;
+    }
+
+    fs::path path = path_string;
+    genFileInfo(path_string,
+                path.filename().string(),
+                path.parent_path().string(),
+                "",
+                results);
+  }
+
+  // Now loop through constraints using the directory column constraint.
+  auto directories = context.constraints["directory"].getAll(EQUALS);
+  for (const auto& directory_string : directories) {
+    if (!isReadable(directory_string) || !isDirectory(directory_string)) {
+      continue;
+    }
+
+    try {
+      // Iterate over the directory and generate info for each regular file.
+      fs::directory_iterator begin(directory_string), end;
+      for (; begin != end; ++begin) {
+        genFileInfo(begin->path().string(),
+                    begin->path().filename().string(),
+                    directory_string,
+                    "",
+                    results);
+      }
+    } catch (const fs::filesystem_error& e) {
+      continue;
+    }
+  }
+
+  // Now loop through constraints using the pattern column constraint.
+  auto patterns = context.constraints["pattern"].getAll(EQUALS);
+  if (patterns.size() != 1) {
+    return results;
+  }
+
+  for (const auto& pattern : patterns) {
+    std::vector<std::string> expanded_patterns;
+    auto status = resolveFilePattern(pattern, expanded_patterns);
+    if (!status.ok()) {
+      VLOG(1) << "Could not expand pattern properly: " << status.toString();
+      return results;
+    }
+
+    for (const auto& resolved : expanded_patterns) {
+      if (!isReadable(resolved)) {
+        continue;
+      }
+      fs::path path = resolved;
+      genFileInfo(resolved,
+                  path.filename().string(),
+                  path.parent_path().string(),
+                  pattern,
+                  results);
+
+    }
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/utility/hash.cpp b/src/osquery/tables/utility/hash.cpp
new file mode 100644 (file)
index 0000000..5baf307
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <boost/filesystem.hpp>
+
+#include <osquery/filesystem.h>
+#include <osquery/hash.h>
+#include <osquery/tables.h>
+
+namespace fs = boost::filesystem;
+
+namespace osquery {
+namespace tables {
+
+void genHashForFile(const std::string& path,
+                    const std::string& dir,
+                    QueryData& results) {
+  // Must provide the path, filename, directory separate from boost path->string
+  // helpers to match any explicit (query-parsed) predicate constraints.
+  Row r;
+  r["path"] = path;
+  r["directory"] = dir;
+  r["md5"] = osquery::hashFromFile(HASH_TYPE_MD5, path);
+  r["sha1"] = osquery::hashFromFile(HASH_TYPE_SHA1, path);
+  r["sha256"] = osquery::hashFromFile(HASH_TYPE_SHA256, path);
+  results.push_back(r);
+}
+
+QueryData genHash(QueryContext& context) {
+  QueryData results;
+
+  // The query must provide a predicate with constraints including path or
+  // directory. We search for the parsed predicate constraints with the equals
+  // operator.
+  auto paths = context.constraints["path"].getAll(EQUALS);
+  for (const auto& path_string : paths) {
+    boost::filesystem::path path = path_string;
+    if (!boost::filesystem::is_regular_file(path)) {
+      continue;
+    }
+
+    genHashForFile(path_string, path.parent_path().string(), results);
+  }
+
+  // Now loop through constraints using the directory column constraint.
+  auto directories = context.constraints["directory"].getAll(EQUALS);
+  for (const auto& directory_string : directories) {
+    boost::filesystem::path directory = directory_string;
+    if (!boost::filesystem::is_directory(directory)) {
+      continue;
+    }
+
+    // Iterate over the directory and generate a hash for each regular file.
+    boost::filesystem::directory_iterator begin(directory), end;
+    for (; begin != end; ++begin) {
+      if (boost::filesystem::is_regular_file(begin->status())) {
+        genHashForFile(begin->path().string(), directory_string, results);
+      }
+    }
+  }
+
+  return results;
+}
+}
+}
diff --git a/src/osquery/tables/utility/osquery.cpp b/src/osquery/tables/utility/osquery.cpp
new file mode 100644 (file)
index 0000000..46eb14d
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <osquery/config.h>
+#include <osquery/core.h>
+#include <osquery/extensions.h>
+#include <osquery/flags.h>
+#include <osquery/logger.h>
+#include <osquery/registry.h>
+#include <osquery/sql.h>
+#include <osquery/tables.h>
+#include <osquery/filesystem.h>
+
+namespace osquery {
+namespace tables {
+
+typedef pt::ptree::value_type tree_node;
+
+void genQueryPack(const tree_node& pack, QueryData& results) {
+  Row r;
+  // Packs are stored by name and contain configuration data.
+  r["name"] = pack.first;
+  r["path"] = pack.second.get("path", "");
+
+  // There are optional restrictions on the set of queries applied pack-wide.
+  auto pack_wide_version = pack.second.get("version", "");
+  auto pack_wide_platform = pack.second.get("platform", "");
+
+  // Iterate through each query in the pack.
+  for (auto const& query : pack.second.get_child("queries")) {
+    r["query_name"] = query.first;
+    r["query"] = query.second.get("query", "");
+    r["interval"] = INTEGER(query.second.get("interval", 0));
+    r["description"] = query.second.get("description", "");
+    r["value"] = query.second.get("value", "");
+
+    // Set the version requirement based on the query-specific or pack-wide.
+    if (query.second.count("version") > 0) {
+      r["version"] = query.second.get("version", "");
+    } else {
+      r["version"] = pack_wide_platform;
+    }
+
+    // Set the platform requirement based on the query-specific or pack-wide.
+    if (query.second.count("platform") > 0) {
+      r["platform"] = query.second.get("platform", "");
+    } else {
+      r["platform"] = pack_wide_platform;
+    }
+
+    // Adding a prefix to the pack queries to differentiate packs from schedule.
+    r["scheduled_name"] = "pack_" + r.at("name") + "_" + r.at("query_name");
+    if (Config::checkScheduledQueryName(r.at("scheduled_name"))) {
+      r["scheduled"] = INTEGER(1);
+    } else {
+      r["scheduled"] = INTEGER(0);
+    }
+
+    results.push_back(r);
+  }
+}
+
+QueryData genOsqueryPacks(QueryContext& context) {
+  QueryData results;
+
+  // Get a lock on the config instance.
+  ConfigDataInstance config;
+
+  // Get the loaded data tree from global JSON configuration.
+  const auto& packs_parsed_data = config.getParsedData("packs");
+
+  // Iterate through all the packs to get each configuration and set of queries.
+  for (auto const& pack : packs_parsed_data) {
+    // Make sure the pack data contains queries.
+    if (pack.second.count("queries") == 0) {
+      continue;
+    }
+    genQueryPack(pack, results);
+  }
+
+  return results;
+}
+
+void genFlag(const std::string& name,
+             const FlagInfo& flag,
+             QueryData& results) {
+  Row r;
+  r["name"] = name;
+  r["type"] = flag.type;
+  r["description"] = flag.description;
+  r["default_value"] = flag.default_value;
+  r["value"] = flag.value;
+  r["shell_only"] = (flag.detail.shell) ? "1" : "0";
+  results.push_back(r);
+}
+
+QueryData genOsqueryFlags(QueryContext& context) {
+  QueryData results;
+
+  auto flags = Flag::flags();
+  for (const auto& flag : flags) {
+    if (flag.first.size() > 2) {
+      // Skip single-character flags.
+      genFlag(flag.first, flag.second, results);
+    }
+  }
+
+  return results;
+}
+
+QueryData genOsqueryRegistry(QueryContext& context) {
+  QueryData results;
+
+  const auto& registries = RegistryFactory::all();
+  for (const auto& registry : registries) {
+    const auto& plugins = registry.second->all();
+    for (const auto& plugin : plugins) {
+      Row r;
+      r["registry"] = registry.first;
+      r["name"] = plugin.first;
+      r["owner_uuid"] = "0";
+      r["internal"] = (registry.second->isInternal(plugin.first)) ? "1" : "0";
+      r["active"] = "1";
+      results.push_back(r);
+    }
+
+    for (const auto& route : registry.second->getExternal()) {
+      Row r;
+      r["registry"] = registry.first;
+      r["name"] = route.first;
+      r["owner_uuid"] = INTEGER(route.second);
+      r["internal"] = "0";
+      r["active"] = "1";
+      results.push_back(r);
+    }
+  }
+
+  return results;
+}
+
+QueryData genOsqueryExtensions(QueryContext& context) {
+  QueryData results;
+
+  ExtensionList extensions;
+  if (getExtensions(extensions).ok()) {
+    for (const auto& extenion : extensions) {
+      Row r;
+      r["uuid"] = TEXT(extenion.first);
+      r["name"] = extenion.second.name;
+      r["version"] = extenion.second.version;
+      r["sdk_version"] = extenion.second.sdk_version;
+      r["path"] = getExtensionSocket(extenion.first);
+      r["type"] = "extension";
+      results.push_back(r);
+    }
+  }
+
+  const auto& modules = RegistryFactory::getModules();
+  for (const auto& module : modules) {
+    Row r;
+    r["uuid"] = TEXT(module.first);
+    r["name"] = module.second.name;
+    r["version"] = module.second.version;
+    r["sdk_version"] = module.second.sdk_version;
+    r["path"] = module.second.path;
+    r["type"] = "module";
+    results.push_back(r);
+  }
+
+  return results;
+}
+
+QueryData genOsqueryInfo(QueryContext& context) {
+  QueryData results;
+
+  Row r;
+  r["pid"] = INTEGER(getpid());
+  r["version"] = kVersion;
+
+  std::string hash_string;
+  auto s = Config::getMD5(hash_string);
+  if (s.ok()) {
+    r["config_md5"] = TEXT(hash_string);
+  } else {
+    r["config_md5"] = "";
+    VLOG(1) << "Could not retrieve config hash: " << s.toString();
+  }
+
+  r["config_path"] = Flag::getValue("config_path");
+  r["extensions"] =
+      (pingExtension(FLAGS_extensions_socket).ok()) ? "active" : "inactive";
+
+  r["build_platform"] = STR(OSQUERY_BUILD_PLATFORM);
+  r["build_distro"] = STR(OSQUERY_BUILD_DISTRO);
+
+  results.push_back(r);
+
+  return results;
+}
+
+QueryData genOsquerySchedule(QueryContext& context) {
+  QueryData results;
+
+  ConfigDataInstance config;
+  for (const auto& query : config.schedule()) {
+    Row r;
+    r["name"] = TEXT(query.first);
+    r["query"] = TEXT(query.second.query);
+    r["interval"] = INTEGER(query.second.interval);
+
+    // Report optional performance information.
+    r["executions"] = BIGINT(query.second.executions);
+    r["output_size"] = BIGINT(query.second.output_size);
+    r["wall_time"] = BIGINT(query.second.wall_time);
+    r["user_time"] = BIGINT(query.second.user_time);
+    r["system_time"] = BIGINT(query.second.system_time);
+    r["average_memory"] = BIGINT(query.second.average_memory);
+    results.push_back(r);
+  }
+
+  return results;
+}
+
+}
+}
diff --git a/src/osquery/tables/utility/time.cpp b/src/osquery/tables/utility/time.cpp
new file mode 100644 (file)
index 0000000..1b68eff
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2014, Facebook, Inc.
+ *  All rights reserved.
+ *
+ *  This source code is licensed under the BSD-style license found in the
+ *  LICENSE file in the root directory of this source tree. An additional grant 
+ *  of patent rights can be found in the PATENTS file in the same directory.
+ *
+ */
+
+#include <ctime>
+#include <boost/algorithm/string/trim.hpp>
+
+#include <osquery/tables.h>
+
+namespace osquery {
+namespace tables {
+
+QueryData genTime(QueryContext& context) {
+  Row r;
+  time_t _time = time(0);
+  struct tm* now = localtime(&_time);
+  struct tm* gmt = gmtime(&_time);
+
+  char weekday[10] = {0};
+  strftime(weekday, sizeof(weekday), "%A", now);
+
+  std::string timestamp;
+  timestamp = asctime(gmt);
+  boost::algorithm::trim(timestamp);
+  timestamp += " UTC";
+
+  char iso_8601[21] = {0};
+  strftime(iso_8601, sizeof(iso_8601), "%FT%TZ", gmt);
+
+  r["weekday"] = TEXT(weekday);
+  r["year"] = INTEGER(now->tm_year + 1900);
+  r["month"] = INTEGER(now->tm_mon + 1);
+  r["day"] = INTEGER(now->tm_mday);
+  r["hour"] = INTEGER(now->tm_hour);
+  r["minutes"] = INTEGER(now->tm_min);
+  r["seconds"] = INTEGER(now->tm_sec);
+  r["unix_time"] = INTEGER(_time);
+  r["timestamp"] = TEXT(timestamp);
+  r["iso_8601"] = TEXT(iso_8601);
+
+  QueryData results;
+  results.push_back(r);
+  return results;
+}
+}
+}