258
+ − 1 #include "animone/fd/proc.h"
+ − 2 #include "animone.h"
+ − 3 #include "animone/util.h"
+ − 4
+ − 5 #include <filesystem>
+ − 6 #include <fstream>
+ − 7 #include <sstream>
+ − 8 #include <string>
+ − 9
+ − 10 #include <fcntl.h>
+ − 11 #include <sys/stat.h>
+ − 12 #include <unistd.h>
+ − 13
+ − 14 static constexpr std::string_view PROC_LOCATION = "/proc";
+ − 15
+ − 16 namespace animone::internal::proc {
+ − 17
+ − 18 static bool IsRegularFile(std::string link) {
+ − 19 struct stat sb;
+ − 20 if (stat(link.c_str(), &sb) == -1)
+ − 21 return false;
+ − 22
+ − 23 return S_ISREG(sb.st_mode);
+ − 24 }
+ − 25
+ − 26 static bool AreFlagsOk(pid_t pid, int fd) {
+ − 27 const std::filesystem::path path =
+ − 28 std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fdinfo" / std::to_string(fd);
+ − 29
+ − 30 std::ifstream file(path);
+ − 31 if (!file)
+ − 32 return false;
+ − 33
+ − 34 int flags = 0;
+ − 35 for (std::string line; std::getline(file, line);)
+ − 36 if (line.find("flags:", 0) == 0)
+ − 37 flags = util::StringToInt(line.substr(line.find_last_not_of("0123456789") + 1));
+ − 38
+ − 39 if (flags & O_WRONLY || flags & O_RDWR)
+ − 40 return false;
+ − 41
+ − 42 return true;
+ − 43 }
+ − 44
+ − 45 static bool GetFilenameFromFd(std::string link, std::string& out) {
+ − 46 /* /proc is a "virtual filesystem", so we have to guess the path size. yippee! */
+ − 47 constexpr size_t OUT_MAX = (1ul << 15); // 32KiB
+ − 48 out.resize(32);
+ − 49
+ − 50 ssize_t exe_used = 0;
+ − 51 do {
+ − 52 out.resize(out.length() * 2);
+ − 53
+ − 54 exe_used = readlink(link.c_str(), &out.front(), out.length());
+ − 55 if (exe_used == (ssize_t)-1 || exe_used < (ssize_t)1)
+ − 56 return false; // we got a bad result. SAD!
+ − 57 } while (out.length() < OUT_MAX && exe_used >= static_cast<ssize_t>(out.length()));
+ − 58
+ − 59 out.resize(out.find('\0'));
+ − 60
+ − 61 return true;
+ − 62 }
+ − 63
+ − 64 static bool IsSystemFile(const std::string& path) {
+ − 65 static constexpr std::array<std::string_view, 9> invalid_paths = {"/boot", "/dev", "/bin", "/usr", "/opt",
+ − 66 "/proc", "/var", "/etc", "/dev"};
+ − 67
+ − 68 for (const auto& invalid_path : invalid_paths) {
+ − 69 if (!path.rfind(invalid_path, 0)) {
+ − 70 return true;
+ − 71 }
+ − 72 }
+ − 73
+ − 74 return false;
+ − 75 }
+ − 76
+ − 77 bool GetProcessName(pid_t pid, std::string& result) {
+ − 78 const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "comm";
+ − 79
+ − 80 if (!util::ReadFile(path, result))
+ − 81 return false;
+ − 82
+ − 83 result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
+ − 84 return true;
+ − 85 }
+ − 86
+ − 87 bool EnumerateOpenProcesses(process_proc_t process_proc) {
+ − 88 bool success = false;
+ − 89
+ − 90 for (const auto& dir : std::filesystem::directory_iterator{PROC_LOCATION}) {
+ − 91 Process proc;
+ − 92
+ − 93 try {
+ − 94 proc.pid = util::StringToInt(dir.path().stem());
+ − 95 success = true;
+ − 96 } catch (std::invalid_argument const& ex) {
+ − 97 continue;
+ − 98 }
+ − 99
+ − 100 if (!GetProcessName(proc.pid, proc.name))
+ − 101 continue;
+ − 102
+ − 103 if (!process_proc(proc))
+ − 104 return false;
+ − 105 }
+ − 106
+ − 107 return success;
+ − 108 }
+ − 109
+ − 110 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+ − 111 if (!open_file_proc)
+ − 112 return false;
+ − 113
+ − 114 for (const auto& pid : pids) {
+ − 115 const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fd";
+ − 116
+ − 117 for (const auto& dir : std::filesystem::directory_iterator{path}) {
+ − 118 if (!AreFlagsOk(pid, util::StringToInt(dir.path().stem())))
+ − 119 continue;
+ − 120
+ − 121 std::string name;
+ − 122 if (!GetFilenameFromFd(dir.path(), name))
+ − 123 continue;
+ − 124
+ − 125 if (!IsRegularFile(name))
+ − 126 continue;
+ − 127
+ − 128 if (IsSystemFile(name))
+ − 129 continue;
+ − 130
+ − 131 if (!open_file_proc({pid, name}))
+ − 132 return false;
+ − 133 }
+ − 134 }
+ − 135 return true;
+ − 136 }
+ − 137
+ − 138 } // namespace animia::internal::proc