Merge pull request #1525 from theopolis/process_adds

Add state, group, and nice to processes
This commit is contained in:
Teddy Reed 2015-09-24 14:43:17 -07:00
commit 64c18a70a9
11 changed files with 136 additions and 85 deletions

View File

@ -139,7 +139,7 @@ PluginResponse TablePlugin::routeInfo() const {
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;
statement += "`" + columns.at(i).first + "` " + columns.at(i).second;
if (i < columns.size() - 1) {
statement += ", ";
}

View File

@ -32,7 +32,7 @@ class sampleTablePlugin : public TablePlugin {
TEST_F(VirtualTableTests, test_tableplugin_columndefinition) {
auto table = std::make_shared<sampleTablePlugin>();
EXPECT_EQ("(foo INTEGER, bar TEXT)", table->columnDefinition());
EXPECT_EQ("(`foo` INTEGER, `bar` TEXT)", table->columnDefinition());
}
TEST_F(VirtualTableTests, test_sqlite3_attach_vtable) {
@ -61,7 +61,8 @@ TEST_F(VirtualTableTests, test_sqlite3_attach_vtable) {
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)",
EXPECT_EQ(
"CREATE VIRTUAL TABLE sample USING sample(`foo` INTEGER, `bar` TEXT)",
results[0]["sql"]);
}

View File

@ -78,8 +78,14 @@ ADD_OSQUERY_LIBRARY_ADDITIONAL(osquery_tables
${OSQUERY_CROSS_EVENTS_TABLES}
)
file(GLOB OSQUERY_CROSS_TABLES_TESTS "[!uo]*/tests/*.cpp")
ADD_OSQUERY_TABLE_TEST(${OSQUERY_CROSS_TABLES_TESTS})
file(GLOB OSQUERY_CROSS_TABLES_TESTS "[!uo]*/tests/*/*.cpp")
file(GLOB OSQUERY_CATEGORY_TABLE_TESTS "[!uo]*/tests/*.cpp")
file(GLOB OSQUERY_TABLE_TESTS "tests/*.cpp")
ADD_OSQUERY_TABLE_TEST(
${OSQUERY_CROSS_TABLES_TESTS}
${OSQUERY_CATEGORY_TABLE_TESTS}
${OSQUERY_TABLE_TESTS}
)
file(GLOB OSQUERY_UTILITY_TABLES "utility/*.cpp")
ADD_OSQUERY_LIBRARY_CORE(osquery_tables_utility

View File

@ -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());
}
}
}

View File

@ -17,11 +17,17 @@
namespace osquery {
namespace tables {
osquery::QueryData parseEtcHostsContent(const std::string& content);
osquery::QueryData parseEtcProtocolsContent(const std::string& content);
class EtcProtocolsTests : public testing::Test {};
class NetworkingTablesTests : public testing::Test {};
TEST_F(EtcProtocolsTests, test_parse_etc_protocols_content) {
TEST_F(NetworkingTablesTests, test_parse_etc_hosts_content) {
EXPECT_EQ(parseEtcHostsContent(getEtcHostsContent()),
getEtcHostsExpectedResults());
}
TEST_F(NetworkingTablesTests, test_parse_etc_protocols_content) {
EXPECT_EQ(parseEtcProtocolsContent(getEtcProtocolsContent()),
getEtcProtocolsExpectedResults());
}

View File

@ -81,26 +81,6 @@ std::set<int> getProcList(const QueryContext &context) {
return pidlist;
}
std::map<int, int> getParentMap(const std::set<int> &pidlist) {
std::map<int, int> pidmap;
struct kinfo_proc proc;
size_t size = sizeof(proc);
for (const auto &pid : pidlist) {
int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
if (sysctl((int *)name, 4, &proc, &size, nullptr, 0) == -1) {
break;
}
if (size > 0) {
pidmap[pid] = (int)proc.kp_eproc.e_ppid;
}
}
return pidmap;
}
inline std::string getProcPath(int pid) {
char path[PROC_PIDPATHINFO_MAXSIZE] = {0};
int bufsize = proc_pidpath(pid, path, sizeof(path));
@ -112,21 +92,44 @@ inline std::string getProcPath(int pid) {
}
struct proc_cred {
uint32_t parent{0};
uint32_t group{0};
uint32_t status{0};
uint32_t nice{0};
struct {
uid_t uid;
gid_t gid;
uid_t uid{0};
gid_t gid{0};
} real, effective;
};
inline bool getProcCred(int pid, proc_cred &cred) {
struct proc_bsdshortinfo bsdinfo;
struct proc_bsdinfo bsdinfo;
struct proc_bsdshortinfo bsdinfo_short;
if (proc_pidinfo(pid, PROC_PIDT_SHORTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo)) ==
sizeof(bsdinfo)) {
cred.real.uid = bsdinfo.pbsi_ruid;
cred.real.gid = bsdinfo.pbsi_rgid;
cred.effective.uid = bsdinfo.pbsi_uid;
cred.effective.gid = bsdinfo.pbsi_gid;
if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &bsdinfo, PROC_PIDTBSDINFO_SIZE) ==
PROC_PIDTBSDINFO_SIZE) {
cred.parent = bsdinfo.pbi_ppid;
cred.group = bsdinfo.pbi_pgid;
cred.status = bsdinfo.pbi_status;
cred.nice = bsdinfo.pbi_nice;
cred.real.uid = bsdinfo.pbi_ruid;
cred.real.gid = bsdinfo.pbi_rgid;
cred.effective.uid = bsdinfo.pbi_uid;
cred.effective.gid = bsdinfo.pbi_gid;
return true;
} else if (proc_pidinfo(pid,
PROC_PIDT_SHORTBSDINFO,
0,
&bsdinfo_short,
PROC_PIDT_SHORTBSDINFO_SIZE) ==
PROC_PIDT_SHORTBSDINFO_SIZE) {
cred.parent = bsdinfo_short.pbsi_ppid;
cred.group = bsdinfo_short.pbsi_pgid;
cred.status = bsdinfo_short.pbsi_status;
cred.real.uid = bsdinfo_short.pbsi_ruid;
cred.real.gid = bsdinfo_short.pbsi_rgid;
cred.effective.uid = bsdinfo_short.pbsi_uid;
cred.effective.gid = bsdinfo_short.pbsi_gid;
return true;
}
return false;
@ -227,22 +230,11 @@ QueryData genProcesses(QueryContext &context) {
}
auto pidlist = getProcList(context);
auto parent_pid = getParentMap(pidlist);
int argmax = genMaxArgs();
for (auto &pid : pidlist) {
Row r;
r["pid"] = INTEGER(pid);
// Find the parent process.
const auto parent_it = parent_pid.find(pid);
if (parent_it != parent_pid.end()) {
r["parent"] = INTEGER(parent_it->second);
} else {
// On OS X a missing parent path means the pid did not exist.
continue;
}
r["path"] = getProcPath(pid);
// OS X proc_name only returns 16 bytes, use the basename of the path.
r["name"] = fs::path(r["path"]).filename().string();
@ -259,11 +251,20 @@ QueryData genProcesses(QueryContext &context) {
proc_cred cred;
if (getProcCred(pid, cred)) {
r["parent"] = BIGINT(cred.parent);
r["group"] = BIGINT(cred.group);
// Use Linux diction for process status/state.
r["state"] = INTEGER(cred.status);
r["nice"] = INTEGER(cred.nice);
r["uid"] = BIGINT(cred.real.uid);
r["gid"] = BIGINT(cred.real.gid);
r["euid"] = BIGINT(cred.effective.uid);
r["egid"] = BIGINT(cred.effective.gid);
} else {
r["parent"] = "0";
r["group"] = "0";
r["state"] = "0";
r["nice"] = "0";
r["uid"] = "-1";
r["gid"] = "-1";
r["euid"] = "-1";

View File

@ -26,8 +26,7 @@ namespace tables {
const std::string kLinuxOSRelease = "/etc/redhat-release";
const std::string kLinuxOSRegex =
"(?P<name>[\\w+\\s]+) .* "
"(?P<major>[0-9]+)\\.(?P<minor>[0-9]+) "
"(?P<patch>\\(\\w+\\))";
"(?P<major>[0-9]+)\\.(?P<minor>[0-9]+)\\.?(?P<patch>\\w+)?";
#else
const std::string kLinuxOSRelease = "/etc/os-release";
const std::string kLinuxOSRegex =

View File

@ -134,7 +134,6 @@ void genProcessMap(const std::string& pid, QueryData& results) {
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: * - - -
@ -145,6 +144,11 @@ struct SimpleProcStat {
std::string phys_footprint; // VmSize:
// Output from sring parsing /proc/<pid>/stat.
std::string state;
std::string parent;
std::string group;
std::string nice;
std::string user_time;
std::string system_time;
std::string start_time;
@ -157,9 +161,12 @@ SimpleProcStat getProcStat(const std::string& pid) {
auto detail_start = content.find_last_of(")");
// Start parsing stats from ") <MODE>..."
auto details = osquery::split(content.substr(detail_start + 2), " ");
stat.state = details.at(0);
stat.parent = details.at(1);
stat.group = details.at(2);
stat.user_time = details.at(11);
stat.system_time = details.at(12);
stat.nice = details.at(16);
stat.start_time = TEXT(AS_LITERAL(BIGINT_LITERAL, details.at(19)) / 100);
}
@ -211,6 +218,9 @@ void genProcess(const std::string& pid, QueryData& results) {
r["parent"] = proc_stat.parent;
r["path"] = readProcLink("exe", pid);
r["name"] = proc_stat.name;
r["group"] = proc_stat.group;
r["state"] = proc_stat.state;
r["nice"] = proc_stat.nice;
// Read/parse cmdline arguments.
r["cmdline"] = readProcCMDLine(pid);

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in 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/tables.h>
#include "osquery/core/test_util.h"
namespace osquery {
namespace tables {
QueryData genOSVersion(QueryContext& context);
class SystemsTablesTests : public testing::Test {};
TEST_F(SystemsTablesTests, test_os_version) {
QueryContext context;
auto result = genOSVersion(context);
EXPECT_EQ(result.size(), 1);
// Make sure major and minor contain data (a missing value of -1 is an error).
EXPECT_FALSE(result[0]["major"].empty());
EXPECT_FALSE(result[0]["minor"].empty());
// The OS name should be filled in too.
EXPECT_FALSE(result[0]["name"].empty());
}
}
}

View File

@ -38,6 +38,11 @@ QueryData genOsqueryEvents(QueryContext& context) {
r["events"] = INTEGER(pubref->numEvents());
r["restarts"] = INTEGER(pubref->restartCount());
r["active"] = (pubref->hasStarted() && !pubref->isEnding()) ? "1" : "0";
} else {
r["subscriptions"] = "0";
r["events"] = "0";
r["restarts"] = "0";
r["active"] = "-1";
}
results.push_back(r);
}
@ -47,6 +52,8 @@ QueryData genOsqueryEvents(QueryContext& context) {
Row r;
r["name"] = subscriber;
r["type"] = "subscriber";
// Subscribers will never 'restart'.
r["restarts"] = "0";
auto subref = EventFactory::getEventSubscriber(subscriber);
if (subref != nullptr) {
@ -54,10 +61,12 @@ QueryData genOsqueryEvents(QueryContext& context) {
r["subscriptions"] = INTEGER(subref->numSubscriptions());
r["events"] = INTEGER(subref->numEvents());
// Subscribers will never 'restart'.
r["restarts"] = "0";
// Subscribers are always active, even if their publisher is not.
r["active"] = (subref->state() == SUBSCRIBER_RUNNING) ? "1" : "0";
} else {
r["subscriptions"] = "0";
r["events"] = "0";
r["active"] = "-1";
}
results.push_back(r);
}
@ -211,6 +220,13 @@ QueryData genOsquerySchedule(QueryContext& context) {
r["name"] = TEXT(name);
r["query"] = TEXT(query.query);
r["interval"] = INTEGER(query.interval);
// Set default (0) values for each query if it has not yet executed.
r["executions"] = "0";
r["output_size"] = "0";
r["wall_time"] = "0";
r["user_time"] = "0";
r["system_time"] = "0";
r["average_memory"] = "0";
// Report optional performance information.
Config::getInstance().getPerformanceStats(

View File

@ -1,14 +1,15 @@
table_name("processes")
description("All running processes on the host system.")
schema([
Column("pid", INTEGER, "Process (or thread) ID", index=True),
Column("pid", BIGINT, "Process (or thread) ID", index=True),
Column("name", TEXT, "The process path or shorthand argv[0]"),
Column("path", TEXT, "Path to executed binary"),
Column("cmdline", TEXT, "Complete argv"),
Column("state", TEXT, "Process state"),
Column("cwd", TEXT, "Process current working directory"),
Column("root", TEXT, "Process virtual root directory"),
Column("uid", BIGINT, "Unsigned user ID"),
Column("gid", BIGINT, "Unsgiend groud ID"),
Column("gid", BIGINT, "Unsigned group ID"),
Column("euid", BIGINT, "Unsigned effective user ID"),
Column("egid", BIGINT, "Unsigned effective group ID"),
Column("on_disk", INTEGER,
@ -20,7 +21,9 @@ schema([
Column("system_time", BIGINT, "CPU time spent in kernel space"),
Column("start_time", BIGINT,
"Process start in seconds since boot (non-sleeping)"),
Column("parent", INTEGER, "Process parent's PID"),
Column("parent", BIGINT, "Process parent's PID"),
Column("group", BIGINT, "Process group"),
Column("nice", INTEGER, "Process nice level (-20 to 20, default 0)"),
])
implementation("system/processes@genProcesses")
examples([