osquery-1/osquery/core/system.cpp
mike@arpaia.co 0846b6ddd5 Fixing pidfile creation bug
If osqueryd was killed and another process was started with osqueryd's
old pid before a new osqueryd could start, osqueryd would encounter a
bug where osqueryd would never start.

This executes an osquery query to the processes table to make sure that
the name of the process is "osqueryd". Of course, you could perhaps
denial of service osqueryd this way, but that would require root
filesystem access (assuming that the last version of osqueryd was
ran as root). Thoughts?
2014-12-08 23:52:38 -08:00

174 lines
5.1 KiB
C++

// Copyright 2004-present Facebook. All Rights Reserved.
#include <sstream>
#include <sys/types.h>
#include <signal.h>
#if !defined(__FreeBSD__)
#include <uuid/uuid.h>
#endif
#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 <glog/logging.h>
#include <osquery/core.h>
#include <osquery/database/db_handle.h>
#include <osquery/filesystem.h>
#include <osquery/sql.h>
namespace fs = boost::filesystem;
namespace osquery {
/// The path to the pidfile for osqueryd
DEFINE_osquery_flag(string,
pidfile,
"/var/osquery/osqueryd.pidfile",
"The path to the pidfile for osqueryd.");
std::string getHostname() {
char hostname[256]; // Linux max should be 64.
memset(hostname, 0, 256);
gethostname(hostname, 255);
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
char uuid[128];
memset(uuid, 0, 128);
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() {
std::time_t result = std::time(NULL);
std::string time_str = std::string(std::asctime(std::localtime(&result)));
boost::algorithm::trim(time_str);
return time_str;
}
int getUnixTime() {
std::time_t result = std::time(NULL);
return result;
}
std::vector<fs::path> getHomeDirectories() {
auto sql = SQL(
"SELECT DISTINCT directory FROM users WHERE directory != '/var/empty';");
std::vector<fs::path> results;
if (sql.ok()) {
for (const auto& row : sql.rows()) {
results.push_back(row.at("directory"));
}
} else {
LOG(ERROR)
<< "Error executing query to return users: " << sql.getMessageString();
}
return results;
}
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());
}
int osqueryd_pid;
try {
osqueryd_pid = stoi(content);
} catch (const std::invalid_argument& e) {
return Status(
1,
std::string("Could not convert pidfile content to an int: ") +
std::string(e.what()));
}
if (kill(osqueryd_pid, 0) == 0) {
// if the pid is running, check if it's osqueryd
std::stringstream query_text;
query_text << "SELECT name FROM processes WHERE pid = " << osqueryd_pid
<< ";";
auto q = SQL(query_text.str());
if (!q.ok()) {
return Status(
1, "Error querying the processes table: " + q.getMessageString());
}
try {
if (q.rows().size() == 1 && q.rows().front()["name"] == "osqueryd") {
// if the process really is osqueryd, return an "error" status
return Status(1, "osqueryd is already running");
} else {
// if it's not osqueryd, some other process has the pid. delete it
// anyway
LOG(INFO) << "found process running with same pid but it's not "
<< "osqueryd, deleting it";
goto delete_pidfile;
}
} catch (const std::exception& e) {
return Status(1,
"An exception was thrown checking the query results: " +
std::string(e.what()));
}
} else if (errno == ESRCH) {
// if the pid isn't running, overwrite the pidfile
delete_pidfile:
try {
boost::filesystem::remove(FLAGS_pidfile);
} catch (boost::filesystem::filesystem_error& e) {
// Unable to remove old pidfile.
LOG(WARNING) << "Unable to remove the old pidfile";
}
goto write_new_pidfile;
} else {
return Status(
1,
std::string(
"An unknown error occured checking if the pid is running: ") +
std::string(strerror(errno)));
}
} else {
// if it doesn't exist, write a pid file and return a "success" status
write_new_pidfile:
auto current_pid = boost::lexical_cast<std::string>(getpid());
LOG(INFO) << "Writing pid (" << current_pid << ") to " << FLAGS_pidfile;
auto write_status = writeTextFile(FLAGS_pidfile, current_pid, 0755);
return write_status;
}
}
}