Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(scap): move machine/agent info to generic platform #1204

Merged
merged 8 commits into from
Jul 18, 2023
2 changes: 1 addition & 1 deletion driver/capture_macro.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ or GPL2.txt for full copies of the license.
#define BPF_HTTP_PREFIX 0x50545448

/* Convert seconds to nanoseconds */
#define SECOND_TO_NS 1000000000
#define SECOND_TO_NS 1000000000ULL
9 changes: 9 additions & 0 deletions userspace/libscap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ list(APPEND targetfiles
scap_userlist.c
scap_suppress.c)

if(CMAKE_SYSTEM_NAME MATCHES "Linux")
list(APPEND targetfiles linux/scap_machine_info.c)
elseif(WIN32)
list(APPEND targetfiles win32/scap_machine_info.c)
elseif(APPLE)
list(APPEND targetfiles macos/scap_machine_info.c)
endif()


if(CMAKE_SYSTEM_NAME MATCHES "Linux")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}) # temporary
include_directories(${PROJECT_BINARY_DIR}/driver/src)
Expand Down
143 changes: 0 additions & 143 deletions userspace/libscap/linux/scap_linux_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ limitations under the License.
#include <sys/time.h>
#include <unistd.h>

#define SECOND_TO_NS 1000000000

static int32_t scap_linux_close_platform(struct scap_platform* platform)
{
struct scap_linux_platform* linux_platform = (struct scap_linux_platform*)platform;
Expand All @@ -54,151 +52,12 @@ static void scap_linux_free_platform(struct scap_platform* platform)
free(platform);
}

static void scap_linux_retrieve_agent_info(scap_agent_info* agent_info)
{
agent_info->start_ts_epoch = 0;
agent_info->start_time = 0;

/* Info 1:
*
* Get epoch timestamp based on procfs stat, only used for (constant) agent start time reporting.
*/
struct stat st = {0};
if(stat("/proc/self/cmdline", &st) == 0)
{
agent_info->start_ts_epoch = st.st_ctim.tv_sec * (uint64_t) SECOND_TO_NS + st.st_ctim.tv_nsec;
}

/* Info 2:
*
* Get /proc/self/stat start_time (22nd item) to calculate subsequent snapshots of the elapsed time
* of the agent for CPU usage calculations, e.g. sysinfo uptime - /proc/self/stat start_time.
*/
FILE* f;
if((f = fopen("/proc/self/stat", "r")))
{
unsigned long long stat_start_time = 0; // unit: USER_HZ / jiffies / clock ticks
long hz = 100;
#ifdef _SC_CLK_TCK
if ((hz = sysconf(_SC_CLK_TCK)) < 0)
{
hz = 100;
ASSERT(false);
}
#endif
if(fscanf(f, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*d %*d %*u %llu", &stat_start_time))
{
agent_info->start_time = (double)stat_start_time / hz; // unit: seconds as type (double)
}
fclose(f);
}

/* Info 3:
*
* Kernel release `uname -r` of the machine the agent is running on.
*/

struct utsname uts;
uname(&uts);
snprintf(agent_info->uname_r, sizeof(agent_info->uname_r), "%s", uts.release);
}

static uint64_t scap_linux_get_host_boot_time_ns(char* last_err)
{
uint64_t btime = 0;
char proc_stat[PPM_MAX_PATH_SIZE];
char line[512];

/* Get boot time from btime value in /proc/stat
* ref: https://github.com/falcosecurity/libs/issues/932
* /proc/uptime and btime in /proc/stat are fed by the same kernel sources.
*
* Multiple ways to get boot time:
* btime in /proc/stat
* calculation via clock_gettime(CLOCK_REALTIME - CLOCK_BOOTTIME)
* calculation via time(NULL) - sysinfo().uptime
*
* Maintainers preferred btime in /proc/stat because:
* value does not depend on calculation using current timestamp
* btime is "static" and doesn't change once set
* btime is available in kernels from 2008
* CLOCK_BOOTTIME is available in kernels from 2011 (2.6.38
*
* By scraping btime from /proc/stat,
* it is both the heaviest and most likely to succeed
*/
snprintf(proc_stat, sizeof(proc_stat), "%s/proc/stat", scap_get_host_root());
FILE* f = fopen(proc_stat, "r");
if (f == NULL)
{
ASSERT(false);
return 0;
}

while(fgets(line, sizeof(line), f) != NULL)
{
if(sscanf(line, "btime %" PRIu64, &btime) == 1)
{
fclose(f);
return btime * (uint64_t) SECOND_TO_NS;
}
}
fclose(f);
ASSERT(false);
return 0;
}

static void scap_get_bpf_stats_enabled(scap_machine_info* machine_info)
{
machine_info->flags &= ~PPM_BPF_STATS_ENABLED;
FILE* f;
if((f = fopen("/proc/sys/kernel/bpf_stats_enabled", "r")))
{
uint32_t bpf_stats_enabled = 0;
if(fscanf(f, "%u", &bpf_stats_enabled) == 1)
{
if (bpf_stats_enabled != 0)
{
machine_info->flags |= PPM_BPF_STATS_ENABLED;
}
}
fclose(f);
}
}

static void scap_gethostname(char* buf, size_t size)
{
char *env_hostname = getenv(SCAP_HOSTNAME_ENV_VAR);
if(env_hostname != NULL)
{
snprintf(buf, size, "%s", env_hostname);
}
else
{
gethostname(buf, size);
}
}

int32_t scap_linux_init_platform(struct scap_platform* platform, char* lasterr, struct scap_engine_handle engine, struct scap_open_args* oargs)
{
int rc;
struct scap_linux_platform* linux_platform = (struct scap_linux_platform*)platform;
linux_platform->m_lasterr = lasterr;

linux_platform->m_engine = engine;

platform->m_machine_info.num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
platform->m_machine_info.memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
scap_gethostname(platform->m_machine_info.hostname, sizeof(platform->m_machine_info.hostname));
platform->m_machine_info.boot_ts_epoch = scap_linux_get_host_boot_time_ns(lasterr);
if(platform->m_machine_info.boot_ts_epoch == 0)
{
return SCAP_FAILURE;
}
scap_get_bpf_stats_enabled(&platform->m_machine_info);
platform->m_machine_info.reserved3 = 0;
platform->m_machine_info.reserved4 = 0;

linux_platform->m_proc_scan_timeout_ms = oargs->proc_scan_timeout_ms;
linux_platform->m_proc_scan_log_interval_ms = oargs->proc_scan_log_interval_ms;
linux_platform->m_debug_log_fn = oargs->debug_log_fn;
Expand Down Expand Up @@ -230,8 +89,6 @@ int32_t scap_linux_init_platform(struct scap_platform* platform, char* lasterr,
return rc;
}

scap_linux_retrieve_agent_info(&platform->m_agent_info);

return SCAP_SUCCESS;
}

Expand Down
171 changes: 171 additions & 0 deletions userspace/libscap/linux/scap_machine_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
Copyright (C) 2023 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.

*/

#include "scap_machine_info.h"
#include "scap_os_machine_info.h"
#include "scap_limits.h"
#include "scap_assert.h"
#include "scap.h"

#include <stdbool.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <unistd.h>

#define SECOND_TO_NS 1000000000

void scap_os_get_agent_info(scap_agent_info* agent_info)
{
agent_info->start_ts_epoch = 0;
agent_info->start_time = 0;

/* Info 1:
*
* Get epoch timestamp based on procfs stat, only used for (constant) agent start time reporting.
*/
struct stat st = {0};
if(stat("/proc/self/cmdline", &st) == 0)
{
agent_info->start_ts_epoch = st.st_ctim.tv_sec * SECOND_TO_NS + st.st_ctim.tv_nsec;
}

/* Info 2:
*
* Get /proc/self/stat start_time (22nd item) to calculate subsequent snapshots of the elapsed time
* of the agent for CPU usage calculations, e.g. sysinfo uptime - /proc/self/stat start_time.
*/
FILE* f;
if((f = fopen("/proc/self/stat", "r")))
{
unsigned long long stat_start_time = 0; // unit: USER_HZ / jiffies / clock ticks
long hz = 100;
#ifdef _SC_CLK_TCK
if ((hz = sysconf(_SC_CLK_TCK)) < 0)
{
hz = 100;
ASSERT(false);
}
#endif
if(fscanf(f, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*d %*d %*u %llu", &stat_start_time))
{
agent_info->start_time = (double)stat_start_time / hz; // unit: seconds as type (double)
}
fclose(f);
}

/* Info 3:
*
* Kernel release `uname -r` of the machine the agent is running on.
*/

struct utsname uts;
uname(&uts);
snprintf(agent_info->uname_r, sizeof(agent_info->uname_r), "%s", uts.release);
}

static uint64_t scap_linux_get_host_boot_time_ns(char* last_err)
{
uint64_t btime = 0;
char proc_stat[SCAP_MAX_PATH_SIZE];
char line[512];

/* Get boot time from btime value in /proc/stat
* ref: https://github.com/falcosecurity/libs/issues/932
* /proc/uptime and btime in /proc/stat are fed by the same kernel sources.
*
* Multiple ways to get boot time:
* btime in /proc/stat
* calculation via clock_gettime(CLOCK_REALTIME - CLOCK_BOOTTIME)
* calculation via time(NULL) - sysinfo().uptime
*
* Maintainers preferred btime in /proc/stat because:
* value does not depend on calculation using current timestamp
* btime is "static" and doesn't change once set
* btime is available in kernels from 2008
* CLOCK_BOOTTIME is available in kernels from 2011 (2.6.38
*
* By scraping btime from /proc/stat,
* it is both the heaviest and most likely to succeed
*/
snprintf(proc_stat, sizeof(proc_stat), "%s/proc/stat", scap_get_host_root());
FILE* f = fopen(proc_stat, "r");
if (f == NULL)
{
ASSERT(false);
return 0;
}

while(fgets(line, sizeof(line), f) != NULL)
{
if(sscanf(line, "btime %" PRIu64, &btime) == 1)
{
fclose(f);
return btime * SECOND_TO_NS;
}
}
fclose(f);
ASSERT(false);
return 0;
}

static void scap_get_bpf_stats_enabled(scap_machine_info* machine_info)
{
machine_info->flags &= ~PPM_BPF_STATS_ENABLED;
FILE* f;
if((f = fopen("/proc/sys/kernel/bpf_stats_enabled", "r")))
{
uint32_t bpf_stats_enabled = 0;
if(fscanf(f, "%u", &bpf_stats_enabled) == 1)
{
if (bpf_stats_enabled != 0)
{
machine_info->flags |= PPM_BPF_STATS_ENABLED;
}
}
fclose(f);
}
}

static void scap_gethostname(char* buf, size_t size)
{
char *env_hostname = getenv(SCAP_HOSTNAME_ENV_VAR);
if(env_hostname != NULL)
{
snprintf(buf, size, "%s", env_hostname);
}
else
{
gethostname(buf, size);
}
}

int32_t scap_os_get_machine_info(scap_machine_info* machine_info, char* lasterr)
{
machine_info->num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
machine_info->memory_size_bytes = (uint64_t)sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
scap_gethostname(machine_info->hostname, sizeof(machine_info->hostname));
machine_info->boot_ts_epoch = scap_linux_get_host_boot_time_ns(lasterr);
if(machine_info->boot_ts_epoch == 0)
{
return SCAP_FAILURE;
}
scap_get_bpf_stats_enabled(machine_info);

return SCAP_SUCCESS;
}

8 changes: 4 additions & 4 deletions userspace/libscap/linux/scap_procs.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ int32_t scap_proc_fill_pidns_start_ts(char* error, struct scap_threadinfo* tinfo
snprintf(proc_cmdline_pidns, sizeof(proc_cmdline_pidns), "%sroot/proc/1/cmdline", procdirname);
if(stat(proc_cmdline_pidns, &targetstat) == 0)
{
tinfo->pidns_init_start_ts = targetstat.st_ctim.tv_sec * (uint64_t) SECOND_TO_NS + targetstat.st_ctim.tv_nsec;
tinfo->pidns_init_start_ts = targetstat.st_ctim.tv_sec * SECOND_TO_NS + targetstat.st_ctim.tv_nsec;
return SCAP_SUCCESS;
}
else
Expand Down Expand Up @@ -480,8 +480,8 @@ int32_t scap_proc_fill_exe_ino_ctime_mtime(char* error, struct scap_threadinfo*
if(stat(exetarget, &targetstat) == 0)
{
tinfo->exe_ino = targetstat.st_ino;
tinfo->exe_ino_ctime = targetstat.st_ctim.tv_sec * (uint64_t) SECOND_TO_NS + targetstat.st_ctim.tv_nsec;
tinfo->exe_ino_mtime = targetstat.st_mtim.tv_sec * (uint64_t) SECOND_TO_NS + targetstat.st_mtim.tv_nsec;
tinfo->exe_ino_ctime = targetstat.st_ctim.tv_sec * SECOND_TO_NS + targetstat.st_ctim.tv_nsec;
tinfo->exe_ino_mtime = targetstat.st_mtim.tv_sec * SECOND_TO_NS + targetstat.st_mtim.tv_nsec;
}

return SCAP_SUCCESS;
Expand Down Expand Up @@ -822,7 +822,7 @@ static int32_t scap_proc_add_from_proc(struct scap_linux_platform* linux_platfor
snprintf(proc_cmdline, sizeof(proc_cmdline), "%scmdline", dir_name);
if(stat(proc_cmdline, &dirstat) == 0)
{
tinfo->clone_ts = dirstat.st_ctim.tv_sec * (uint64_t) SECOND_TO_NS + dirstat.st_ctim.tv_nsec;
tinfo->clone_ts = dirstat.st_ctim.tv_sec * SECOND_TO_NS + dirstat.st_ctim.tv_nsec;
}

// If tid is different from pid, assume this is a thread and that the FDs are shared, and set the
Expand Down
Loading
Loading