29#include <system_error>
37 ProcPath(
int pid,
const std::string& entry =
"")
39 return "/proc/" + std::to_string(pid) + entry;
43 BaseName(
const std::string& path)
45 const auto lastSeparator = path.rfind(
'/');
46 return lastSeparator == std::string::npos ? path : path.substr(lastSeparator + 1);
54 PidFromProcEntry(
const std::string& entry)
57 !std::all_of(entry.begin(), entry.end(), [](
unsigned char c)
58 { return std::isdigit(c) != 0; }))
65 return std::stoi(entry);
67 catch (
const std::exception&)
86 std::ifstream stat(ProcPath(pid,
"/stat"));
93 if (!std::getline(stat, line))
98 const auto commEnd = line.rfind(
')');
99 if (commEnd == std::string::npos || commEnd + 2 >= line.size())
104 return line[commEnd + 2] ==
'Z';
116 if (!std::filesystem::exists(ProcPath(pid), ec))
137 return arguments.empty() ? std::string{} : BaseName(arguments.front());
140 std::vector<std::string>
148 std::ifstream cmdline(ProcPath(pid,
"/cmdline"), std::ios::binary);
149 if (!cmdline.is_open())
156 std::vector<std::string> arguments;
157 std::string argument;
158 while (std::getline(cmdline, argument,
'\0'))
160 arguments.push_back(argument);
163 if (arguments.empty() || arguments.front().empty())
171 std::vector<ProcessInfo>
174 std::vector<ProcessInfo> processes;
177 std::filesystem::directory_iterator entries(
"/proc", ec);
183 for (
const auto& entry : entries)
185 const int pid = PidFromProcEntry(entry.path().filename().string());
194 if (arguments.empty())
199 std::string name = BaseName(arguments.front());
200 processes.push_back(
ProcessInfo{pid, std::move(name), std::move(arguments)});
206 std::optional<std::string>
209 static const std::string prefix =
"--Ice.Config=";
210 for (
const std::string& argument : arguments)
212 if (argument.rfind(prefix, 0) == 0)
214 return argument.substr(prefix.size());
221 IdentifyProcess(
int pid,
const std::string& executableName,
const std::string& configPath)
223 if (pid <= 0 || executableName.empty())
229 if (arguments.empty())
237 if (BaseName(arguments.front()) != executableName)
244 if (!configPath.empty())
247 if (configArgument.has_value() &&
248 configArgument->find(configPath) == std::string::npos)
258 WaitForExit(
int pid, std::chrono::milliseconds timeout, std::chrono::milliseconds pollInterval)
260 const auto deadline = std::chrono::steady_clock::now() + timeout;
263 if (std::chrono::steady_clock::now() >= deadline)
267 std::this_thread::sleep_for(pollInterval);
Identity
Whether the process behind a pid still is the one we think it is.
@ Unverifiable
argv could not be read, so nothing can be concluded either way.
@ Matches
argv says this is the expected executable, and it does not claim a foreign config.
@ Differs
argv says this is a different program, or the same program serving a different config.
std::string GetProcessName(int pid)
Reads the name of the executable behind pid (the basename of argv[0]).
Identity IdentifyProcess(int pid, const std::string &executableName, const std::string &configPath)
Checks whether the process with the given pid is still the application it is supposed to be.
std::optional< std::string > FindIceConfigArgument(const std::vector< std::string > &arguments)
bool WaitForExit(int pid, std::chrono::milliseconds timeout, std::chrono::milliseconds pollInterval)
Blocks until the process with the given pid has disappeared, at most for timeout.
std::vector< std::string > GetProcessArguments(int pid)
Reads the full argument vector of pid from /proc/<pid>/cmdline.
std::vector< ProcessInfo > ScanProcesses()
Walks /proc and returns every process whose argument vector could be read.
A live process as seen by ScanProcesses().