#include "process.h" #include #include #include #include #include #include #include "file.h" #include "stringhelper.h" namespace fs = std::filesystem; bool Reichwein::Process::is_running(pid_t pid) { // 3rd position is status for pid fs::path pid_file{"/proc/" + std::to_string(pid) + "/stat"}; if (!fs::exists(pid_file)) return false; std::string s{Reichwein::File::getFile(pid_file)}; auto pos0{s.find(' ', 0)}; pos0 = s.find(' ', pos0 + 1); pos0++; auto pos1{s.find(' ', pos0 + 1)}; std::string state{s.substr(pos0, pos1 - pos0)}; return state == "R" || state == "S"; } int Reichwein::Process::number_of_threads(pid_t pid) { // 20th position is the number of threads for pid fs::path pid_file{"/proc/" + std::to_string(pid) + "/stat"}; if (!fs::exists(pid_file)) return false; std::string s{Reichwein::File::getFile(pid_file)}; size_t pos0{}; for (int i = 0; i < 19; i++) { pos0 = s.find(' ', pos0 + 1); if (pos0 == std::string::npos) throw std::runtime_error("Bad format in /proc/" + std::to_string(pid) + "/stat"); } size_t pos1{s.find(' ', pos0 + 1)}; std::string number_of_threads_s{s.substr(pos0, pos1 - pos0)}; return std::stoi(number_of_threads_s); } // tcp: tcp or tcp6 bool Reichwein::Process::tcp_is_pid_listening_on(const std::string& tcp, pid_t pid, int port) { std::string filename{fmt::format("/proc/{}/net/{}", pid, tcp)}; std::ifstream f{filename, std::ios::in}; // e.g.: // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode // 0: 00000000:C799 00000000:0000 0A 00000000:00000000 00:00000000 00000000 107 0 21869 1 00000000335416a4 100 0 0 10 0 std::string s; std::getline(f, s); // skip head line while (std::getline(f, s)) { boost::algorithm::trim_left(s); size_t pos_space1{s.find(' ')}; if (pos_space1 == std::string::npos) throw std::runtime_error("Expected first space in " + filename); size_t pos_colon1{s.find(':', pos_space1 + 1)}; if (pos_colon1 == std::string::npos) throw std::runtime_error("Expected first colon in " + filename); size_t pos_space2{s.find(' ', pos_colon1 + 1)}; if (pos_space2 == std::string::npos) throw std::runtime_error("Expected second space in " + filename); std::string port_s{s.substr(pos_colon1 + 1, pos_space2 - (pos_colon1 + 1))}; auto current_port{std::stoul(port_s, nullptr, 16)}; if (current_port != port) continue; // now, we are in a line related to matching local port size_t pos_space3{s.find(' ', pos_space2 + 1)}; if (pos_space3 == std::string::npos) throw std::runtime_error("Expected third space in " + filename); size_t pos_space4{s.find(' ', pos_space3 + 1)}; if (pos_space4 == std::string::npos) throw std::runtime_error("Expected fourth space in " + filename); std::string state_s{s.substr(pos_space3 + 1, pos_space4 - (pos_space3 + 1))}; if (state_s == "0A") // listening state TCP_LISTEN, from /include/net/tcp_states.h return true; } return false; // not found } bool Reichwein::Process::is_pid_listening_on(pid_t pid, int port) { return tcp_is_pid_listening_on("tcp", pid, port) || tcp_is_pid_listening_on("tcp6", pid, port); } bool Reichwein::Process::unix_is_pid_listening_on(pid_t pid, const std::string& path) { std::string filename{fmt::format("/proc/{}/net/unix", pid)}; std::ifstream f{filename, std::ios::in}; // e.g.: // Num RefCount Protocol Flags Type St Inode Path // 000000009ce259a6: 00000003 00000000 00000000 0001 03 29015 // 000000003a686108: 00000003 00000000 00000000 0001 03 27263 /run/user/1000/pulse/native // 000000006d62b584: 00000002 00000000 00010000 0001 01 6126359 @/tmp/dbus-b8l6vAYN std::string s; std::getline(f, s); // skip head line while (std::getline(f, s)) { boost::algorithm::trim_left(s); std::vector columns{Reichwein::Stringhelper::split(s, " ")}; if (columns.size() < 8) continue; std::string current_path = columns[7]; if (current_path[0] == '@') current_path = current_path.substr(1); if (current_path != path) continue; std::string state = columns[5]; // I would expect "0A" here, since net/unix/af_unix.c also uses // TCP_LISTEN etc. as in TCP sockets /proc/{}/net/tcp. // But practically, I always get "01" if (state == "0A" || state == "01") // listening state TCP_LISTEN, from /include/net/tcp_states.h return true; } return false; // not found } void Reichwein::Process::wait_for_pid_listening_on(pid_t pid, int port) { while (!is_pid_listening_on(pid, port)) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } void Reichwein::Process::wait_for_pid_listening_on(pid_t pid, const std::string& path) { while (!unix_is_pid_listening_on(pid, path)) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }