Skip to content

Commit

Permalink
Enhancements to initial scan of /proc, for supportability
Browse files Browse the repository at this point in the history
- Support terminating scan after specified timeout
- Support periodic log messages to report progress
- API to specify timeout, log interval, and log function
- Add last PID and total FDs processed, to /proc scan progress messages
- Enhance scap_open args and logic to record debug_log_fn and parameters
- Reworked /proc scan to reduce complexity and nesting depth
- Pass through API to specify log/timeout parameters to libscap /proc scan

Signed-off-by: Joseph Pittman <[email protected]>
  • Loading branch information
jcpittman144 authored and mstemm committed Oct 28, 2022
1 parent a6efb4a commit 89a8a08
Show file tree
Hide file tree
Showing 12 changed files with 386 additions and 16 deletions.
81 changes: 81 additions & 0 deletions userspace/libscap/clock_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __CLOCK_HELPERS_H
#define __CLOCK_HELPERS_H

#define SCAP_GET_CUR_TS_MS_CONTEXT_INIT ((uint64_t)0)
#define SCAP_GET_CUR_TS_MS_CONTEXT_ERROR_FLAG ((uint64_t)0x8000000000000000)
#define SCAP_GET_CUR_TS_MS_CONTEXT_PREV_VALUE_MASK ((uint64_t)0x7fffffffffffffff)

#define S_TO_MS(_sec) (((uint64_t)_sec) * (uint64_t)1000)
#define NS_TO_MS(_ns) (((uint64_t)_ns) / ((uint64_t)(1000 * 1000)))

/**
* Return monotonically increasing time in ms.
* Caller initializes context to SCAP_GET_CUR_TS_MS_CONTEXT_INIT,
* Function uses and updates context, to recognize and handle the
* following cases:
* - failed clock_gettime() system call
* - non-monotonic behavior of CLOCK_MONOTONIC
* - time values that cannot be represented in uint64_t number of msec
*/
static __always_inline uint64_t scap_get_monotonic_ts_ms(uint64_t* context)
{
// Record previously reported time; will be 0 for first call.
uint64_t prev_time = ((*context) & SCAP_GET_CUR_TS_MS_CONTEXT_PREV_VALUE_MASK);

// If context indicates error already detected, just return the
// last reported time
if ((*context) & SCAP_GET_CUR_TS_MS_CONTEXT_ERROR_FLAG)
{
return prev_time;
}

// Fetch current monotonic time from kernel
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
{
// System call failed.
// Set error flag
*context |= SCAP_GET_CUR_TS_MS_CONTEXT_ERROR_FLAG;

// Return previously reported time, now frozen
return prev_time;
}

// Form new time
uint64_t new_time = S_TO_MS(ts.tv_sec) + NS_TO_MS(ts.tv_nsec);

// Check for overflow or non-monotonic behavior
if ((new_time & SCAP_GET_CUR_TS_MS_CONTEXT_ERROR_FLAG) ||
(new_time < prev_time))
{
// System call failed.
// Set error flag
*context |= SCAP_GET_CUR_TS_MS_CONTEXT_ERROR_FLAG;

// Return previously reported time, now frozen
return prev_time;
}

// New time looks OK.
// Store it into the context, and return it.
*context = new_time;
return new_time;
}

#endif /* __CLOCK_HELPERS_H */
41 changes: 41 additions & 0 deletions userspace/libscap/debug_log_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright (C) 2022 The Falco Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __DEBUG_LOG_HELPERS_H
#define __DEBUG_LOG_HELPERS_H

#include "scap.h"
#include <stdarg.h>

/**
* If debug_log_fn has been established in the handle, call that function
* to log a debug message.
*/
static void scap_debug_log(scap_t* handle, const char* fmt, ...)
{
if (handle->m_debug_log_fn != NULL)
{
char buf[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);

(*handle->m_debug_log_fn)(buf);
}
}

#endif /* __DEBUG_LOG_HELPERS_H */
9 changes: 8 additions & 1 deletion userspace/libscap/scap-int.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ struct scap
// If the schema version is unavailable for whatever reason,
// it's equivalent to version 0.0.0
uint64_t m_schema_version;

// /proc scan parameters
uint64_t m_proc_scan_timeout_ms;
uint64_t m_proc_scan_log_interval_ms;

// Function which may be called to log a debug event
void(*m_debug_log_fn)(const char* msg);
};

typedef enum ppm_dumper_type
Expand Down Expand Up @@ -195,7 +202,7 @@ int32_t scap_add_fd_to_proc_table(scap_t* handle, scap_threadinfo* pi, scap_fdin
// Remove the given fd from the process table of the process pointed by pi
void scap_fd_remove(scap_t* handle, scap_threadinfo* pi, int64_t fd);
// read the file descriptors for a given process directory
int32_t scap_fd_scan_fd_dir(scap_t* handle, char * procdir, scap_threadinfo* pi, struct scap_ns_socket_list** sockets_by_ns, char *error);
int32_t scap_fd_scan_fd_dir(scap_t* handle, char * procdir, scap_threadinfo* pi, struct scap_ns_socket_list** sockets_by_ns, uint64_t* num_fds_ret, char *error);
// scan fd information for a specific thread from engine vtable. src_tinfo is a pointer to a threadinfo returned by the engine
int32_t scap_fd_scan_vtable(scap_t *handle, const scap_threadinfo *src_tinfo, scap_threadinfo *dst_tinfo, char *error);
// read tcp or udp sockets from the proc filesystem
Expand Down
52 changes: 45 additions & 7 deletions userspace/libscap/scap.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ scap_t* scap_open_udig_int(char *error, int32_t *rc,
proc_entry_callback proc_callback,
void* proc_callback_context,
bool import_users,
const char **suppressed_comms)
const char **suppressed_comms,
void(*debug_log_fn)(const char* msg),
uint64_t proc_scan_timeout_ms,
uint64_t proc_scan_log_interval_ms)
{
snprintf(error, SCAP_LASTERR_SIZE, "udig capture not supported on %s", PLATFORM_NAME);
*rc = SCAP_NOT_SUPPORTED;
Expand Down Expand Up @@ -125,6 +128,10 @@ scap_t* scap_open_live_int(char *error, int32_t *rc, scap_open_args* oargs)
// Preliminary initializations
//
handle->m_mode = SCAP_MODE_LIVE;
handle->m_debug_log_fn = oargs->debug_log_fn;
handle->m_proc_scan_timeout_ms = oargs->proc_scan_timeout_ms;
handle->m_proc_scan_log_interval_ms = oargs->proc_scan_log_interval_ms;

if(strncmp(oargs->engine_name, BPF_ENGINE, BPF_ENGINE_LEN) == 0)
{
handle->m_vtable = &scap_bpf_engine;
Expand Down Expand Up @@ -277,7 +284,10 @@ scap_t* scap_open_udig_int(char *error, int32_t *rc,
proc_entry_callback proc_callback,
void* proc_callback_context,
bool import_users,
const char **suppressed_comms)
const char **suppressed_comms,
void(*debug_log_fn)(const char* msg),
uint64_t proc_scan_timeout_ms,
uint64_t proc_scan_log_interval_ms)
{
char filename[SCAP_MAX_PATH_SIZE];
scap_t* handle = NULL;
Expand All @@ -297,6 +307,9 @@ scap_t* scap_open_udig_int(char *error, int32_t *rc,
// Preliminary initializations
//
handle->m_mode = SCAP_MODE_LIVE;
handle->m_debug_log_fn = debug_log_fn;
handle->m_proc_scan_timeout_ms = proc_scan_timeout_ms;
handle->m_proc_scan_log_interval_ms = proc_scan_log_interval_ms;
handle->m_ncpus = 1;

handle->m_vtable = &scap_udig_engine;
Expand Down Expand Up @@ -488,6 +501,10 @@ scap_t* scap_open_test_input_int(char *error, int32_t *rc, scap_open_args *oargs
handle->m_proclist.m_proc_callback_context = oargs->proc_callback_context;
handle->m_proclist.m_proclist = NULL;

handle->m_debug_log_fn = oargs->debug_log_fn;
handle->m_proc_scan_timeout_ms = oargs->proc_scan_timeout_ms;
handle->m_proc_scan_log_interval_ms = oargs->proc_scan_log_interval_ms;

if ((*rc = copy_comms(handle, oargs->suppressed_comms)) != SCAP_SUCCESS)
{
scap_close(handle);
Expand Down Expand Up @@ -577,6 +594,10 @@ scap_t* scap_open_gvisor_int(char *error, int32_t *rc, scap_open_args *oargs)
handle->m_proclist.m_proc_callback_context = oargs->proc_callback_context;
handle->m_proclist.m_proclist = NULL;

handle->m_debug_log_fn = oargs->debug_log_fn;
handle->m_proc_scan_timeout_ms = oargs->proc_scan_timeout_ms;
handle->m_proc_scan_log_interval_ms = oargs->proc_scan_log_interval_ms;

if ((*rc = copy_comms(handle, oargs->suppressed_comms)) != SCAP_SUCCESS)
{
scap_close(handle);
Expand Down Expand Up @@ -614,7 +635,7 @@ scap_t* scap_open_offline_int(scap_open_args* oargs, int* rc, char* error)
//
// Allocate the handle
//
handle = (scap_t*)malloc(sizeof(scap_t));
handle = (scap_t*)calloc(sizeof(scap_t), 1);
if(!handle)
{
snprintf(error, SCAP_LASTERR_SIZE, "error allocating the scap_t structure");
Expand Down Expand Up @@ -689,7 +710,10 @@ scap_t* scap_open_offline_int(scap_open_args* oargs, int* rc, char* error)
scap_t* scap_open_nodriver_int(char *error, int32_t *rc,
proc_entry_callback proc_callback,
void* proc_callback_context,
bool import_users)
bool import_users,
void(*debug_log_fn)(const char* msg),
uint64_t proc_scan_timeout_ms,
uint64_t proc_scan_log_interval_ms)
{
#if !defined(HAS_CAPTURE)
snprintf(error, SCAP_LASTERR_SIZE, "live capture not supported on %s", PLATFORM_NAME);
Expand All @@ -702,7 +726,7 @@ scap_t* scap_open_nodriver_int(char *error, int32_t *rc,
//
// Allocate the handle
//
handle = (scap_t*)malloc(sizeof(scap_t));
handle = (scap_t*)calloc(sizeof(scap_t), 1);
if(!handle)
{
snprintf(error, SCAP_LASTERR_SIZE, "error allocating the scap_t structure");
Expand All @@ -729,6 +753,10 @@ scap_t* scap_open_nodriver_int(char *error, int32_t *rc,
handle->m_proclist.m_proc_callback_context = proc_callback_context;
handle->m_proclist.m_proclist = NULL;

handle->m_debug_log_fn = debug_log_fn;
handle->m_proc_scan_timeout_ms = proc_scan_timeout_ms;
handle->m_proc_scan_log_interval_ms = proc_scan_log_interval_ms;

//
// Extract machine information
//
Expand Down Expand Up @@ -849,6 +877,10 @@ scap_t* scap_open_plugin_int(char *error, int32_t *rc, scap_open_args* oargs)
handle->m_proclist.m_proc_callback_context = NULL;
handle->m_proclist.m_proclist = NULL;

handle->m_debug_log_fn = oargs->debug_log_fn;
handle->m_proc_scan_timeout_ms = oargs->proc_scan_timeout_ms;
handle->m_proc_scan_log_interval_ms = oargs->proc_scan_log_interval_ms;

//
// Extract machine information
//
Expand Down Expand Up @@ -909,7 +941,10 @@ scap_t* scap_open(scap_open_args* oargs, char *error, int32_t *rc)
return scap_open_udig_int(error, rc, oargs->proc_callback,
oargs->proc_callback_context,
oargs->import_users,
oargs->suppressed_comms);
oargs->suppressed_comms,
oargs->debug_log_fn,
oargs->proc_scan_timeout_ms,
oargs->proc_scan_log_interval_ms);
}
else if(strncmp(engine_name, GVISOR_ENGINE, GVISOR_ENGINE_LEN) == 0)
{
Expand All @@ -929,7 +964,10 @@ scap_t* scap_open(scap_open_args* oargs, char *error, int32_t *rc)
{
return scap_open_nodriver_int(error, rc, oargs->proc_callback,
oargs->proc_callback_context,
oargs->import_users);
oargs->import_users,
oargs->debug_log_fn,
oargs->proc_scan_timeout_ms,
oargs->proc_scan_log_interval_ms);
}
else if(strncmp(engine_name, SOURCE_PLUGIN_ENGINE, SOURCE_PLUGIN_ENGINE_LEN) == 0)
{
Expand Down
19 changes: 19 additions & 0 deletions userspace/libscap/scap.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ struct iovec;
//
#define DEFAULT_DRIVER_BUFFER_BYTES_DIM 8 * 1024 * 1024

//
// Value for proc_scan_timeout_ms field in scap_open_args, to specify
// that scan should run to completion without any timeout imposed
//
#define SCAP_PROC_SCAN_TIMEOUT_NONE 0

//
// Value for proc_scan_log_interval_ms field in scap_open_args, to specify
// that no progress logging should be performed
//
#define SCAP_PROC_SCAN_LOG_NONE 0


/*!
\brief Statistics about an in progress capture
*/
Expand Down Expand Up @@ -316,6 +329,12 @@ typedef struct {
UT_hash_handle hh; ///< makes this structure hashable
} scap_mountinfo;

typedef void (*proc_entry_callback)(void* context,
scap_t* handle,
int64_t tid,
scap_threadinfo* tinfo,
scap_fdinfo* fdinfo);

//
// The following stuff is byte aligned because we save it to disk.
//
Expand Down
13 changes: 12 additions & 1 deletion userspace/libscap/scap_fds.c
Original file line number Diff line number Diff line change
Expand Up @@ -1579,7 +1579,7 @@ char * decode_st_mode(struct stat* sb)
//
// Scan the directory containing the fd's of a proc /proc/x/fd
//
int32_t scap_fd_scan_fd_dir(scap_t *handle, char *procdir, scap_threadinfo *tinfo, struct scap_ns_socket_list **sockets_by_ns, char *error)
int32_t scap_fd_scan_fd_dir(scap_t *handle, char *procdir, scap_threadinfo *tinfo, struct scap_ns_socket_list **sockets_by_ns, uint64_t* num_fds_ret, char *error)
{
DIR *dir_p;
struct dirent *dir_entry_p;
Expand All @@ -1594,6 +1594,11 @@ int32_t scap_fd_scan_fd_dir(scap_t *handle, char *procdir, scap_threadinfo *tinf
ssize_t r;
uint16_t fd_added = 0;

if (num_fds_ret != NULL)
{
*num_fds_ret = 0;
}

snprintf(fd_dir_name, SCAP_MAX_PATH_SIZE, "%sfd", procdir);
dir_p = opendir(fd_dir_name);
if(dir_p == NULL)
Expand Down Expand Up @@ -1717,6 +1722,12 @@ int32_t scap_fd_scan_fd_dir(scap_t *handle, char *procdir, scap_threadinfo *tinf
}
}
closedir(dir_p);

if (num_fds_ret != NULL)
{
*num_fds_ret = fd_added;
}

return res;
}

Expand Down
3 changes: 3 additions & 0 deletions userspace/libscap/scap_open.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ extern "C"
// values via scap_suppress_events_comm().
interesting_ppm_sc_set ppm_sc_of_interest; ///< syscalls of interest.
interesting_tp_set tp_of_interest; ///< tp of interest. If left empty, no tracepoints will be attached
void(*debug_log_fn)(const char* msg); //< Function which SCAP may use to log a debug message
uint64_t proc_scan_timeout_ms; //< Timeout in msec, after which so-far-successful scan of /proc should be cut short with success return
uint64_t proc_scan_log_interval_ms; //< Interval for logging progress messages from /proc scan
void* engine_params; ///< engine-specific params.
} scap_open_args;

Expand Down
Loading

0 comments on commit 89a8a08

Please sign in to comment.