comparison dep/animia/src/fd/proc.cc @ 215:031a257ee019

dep/animia: fd/proc: use std::filesystem, etc. changes i.e. don't use std::stoi so we don't get exceptions...
author Paper <mrpapersonic@gmail.com>
date Sun, 07 Jan 2024 11:44:40 -0500
parents bc1ae1810855
children 0a5b6a088886
comparison
equal deleted inserted replaced
214:8d35061e7505 215:031a257ee019
1 #include "animia/fd/proc.h" 1 #include "animia/fd/proc.h"
2 #include "animia.h" 2 #include "animia.h"
3 #include "animia/util.h" 3 #include "animia/util.h"
4 4
5 #include <algorithm>
6 #include <cstring>
7 #include <filesystem> 5 #include <filesystem>
8 #include <fstream> 6 #include <fstream>
9 #include <sstream> 7 #include <sstream>
10 #include <string> 8 #include <string>
11 #include <unordered_map>
12 #include <vector>
13 9
14 #include <dirent.h> 10 #include <sys/stat.h>
15 #include <fcntl.h> 11 #include <fcntl.h>
16 #include <sys/stat.h>
17 #include <unistd.h> 12 #include <unistd.h>
18 13
19 static constexpr std::string_view PROC_LOCATION = "/proc"; 14 static constexpr std::string_view PROC_LOCATION = "/proc";
20 15
21 namespace animia::internal::proc { 16 namespace animia::internal::proc {
22 17
23 /* this uses dirent instead of std::filesystem; it would make a bit 18 template<typename T = int>
24 more sense to use the latter, but this is platform dependent already :) */ 19 static T StringToInt(const std::string& str, T def = 0) {
25 static std::vector<std::string> GetAllFilesInDir(const std::string& _dir) { 20 std::istringstream s(str);
26 std::vector<std::string> ret; 21 s >> def;
27 22 return def;
28 DIR* dir = opendir(_dir.c_str());
29 if (!dir)
30 return ret;
31
32 struct dirent* dp;
33 while ((dp = readdir(dir)) != NULL) {
34 if (!(!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")))
35 ret.push_back(_dir + "/" + dp->d_name);
36 }
37
38 closedir(dir);
39 return ret;
40 }
41
42 static std::string Basename(const std::string& path) {
43 return path.substr(path.find_last_of("/") + 1, path.length());
44 } 23 }
45 24
46 static bool IsRegularFile(std::string link) { 25 static bool IsRegularFile(std::string link) {
47 struct stat sb; 26 struct stat sb;
48 if (stat(link.c_str(), &sb) == -1) 27 if (stat(link.c_str(), &sb) == -1)
49 return false; 28 return false;
29
50 return S_ISREG(sb.st_mode); 30 return S_ISREG(sb.st_mode);
51 } 31 }
52 32
53 static bool AreFlagsOk(pid_t pid, int fd) { 33 static bool AreFlagsOk(pid_t pid, int fd) {
54 const std::string path = std::string(PROC_LOCATION) + "/" + std::to_string(pid) + "/fdinfo/" + std::to_string(fd); 34 const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fdinfo" / std::to_string(fd);
55 35
56 std::ifstream file(path.c_str()); 36 std::ifstream file(path);
57 if (!file) 37 if (!file)
58 return false; 38 return false;
59 39
60 int flags = 0; 40 int flags = 0;
61 for (std::string line; std::getline(file, line); ) 41 for (std::string line; std::getline(file, line); )
62 if (line.find("flags:", 0) == 0) 42 if (line.find("flags:", 0) == 0)
63 flags = std::stoi(line.substr(line.find_last_not_of("0123456789") + 1)); 43 flags = StringToInt(line.substr(line.find_last_not_of("0123456789") + 1));
64 44
65 if (flags & O_WRONLY || flags & O_RDWR) 45 if (flags & O_WRONLY || flags & O_RDWR)
66 return false; 46 return false;
47
67 return true; 48 return true;
68 } 49 }
69 50
70 static bool GetFilenameFromFd(std::string link, std::string& out) { 51 static bool GetFilenameFromFd(std::string link, std::string& out) {
71 /* gets around stupid linux limitation where /proc doesn't 52 /* gets around stupid linux limitation where /proc doesn't
72 * give actual size readings of the string 53 * give actual size readings of the string
73 */ 54 */
74 constexpr size_t OUT_MAX = (1 << 15); // 32KiB 55 constexpr size_t OUT_MAX = (1ul << 15); // 32KiB
75 out.resize(1024); 56 out.resize(32);
76 57
77 for (ssize_t exe_used = 0; 58 ssize_t exe_used = 0;
78 out.length() < OUT_MAX && exe_used >= (ssize_t)(out.length() - 1); 59 while (out.length() < OUT_MAX && exe_used >= static_cast<ssize_t>(out.length())) {
79 out.resize(out.length() * 2)) { 60 out.resize(out.length() * 2);
61
80 exe_used = readlink(link.c_str(), &out.front(), out.length()); 62 exe_used = readlink(link.c_str(), &out.front(), out.length());
81 if (exe_used == (ssize_t)-1 || exe_used < (ssize_t)1) 63 if (exe_used == (ssize_t)-1 || exe_used < (ssize_t)1)
82 return false; // we got a bad result, i think 64 return false; // we got a bad result. SAD!
83 } 65 }
84 66
85 out.resize(out.find('\0')); 67 out.resize(out.find('\0'));
86 68
87 return true; 69 return true;
88 } 70 }
89 71
90 static std::string GetProcessName(pid_t pid) { 72 static bool GetProcessName(pid_t pid, std::string& result) {
91 std::string result; 73 const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "comm";
92
93 const std::string path = std::string(PROC_LOCATION) + "/" + std::to_string(pid) + "/comm";
94 74
95 if (!util::ReadFile(path, result)) 75 if (!util::ReadFile(path, result))
96 return ""; 76 return false;
97 77
98 result.erase(std::remove(result.begin(), result.end(), '\n'), result.end()); 78 result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
99 79 return true;
100 return result;
101 } 80 }
102 81
103 bool EnumerateOpenProcesses(process_proc_t process_proc) { 82 bool EnumerateOpenProcesses(process_proc_t process_proc) {
104 bool success = false; 83 bool success = false;
105 for (const auto& dir : GetAllFilesInDir(std::string(PROC_LOCATION))) { 84
106 pid_t pid; 85 for (const auto& dir : std::filesystem::directory_iterator{PROC_LOCATION}) {
86 Process proc;
87
107 try { 88 try {
108 pid = std::stoul(Basename(dir)); 89 proc.pid = StringToInt(dir.path().stem());
109 success = true; 90 success = true;
110 } catch (std::invalid_argument) { 91 } catch (std::invalid_argument const& ex) {
111 continue; 92 continue;
112 } 93 }
113 if (!process_proc({pid, GetProcessName(pid)})) 94
95 if (!GetProcessName(proc.pid, proc.name))
96 continue;
97
98 if (!process_proc(proc))
114 return false; 99 return false;
115 } 100 }
101
116 return success; 102 return success;
117 } 103 }
118 104
119 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) { 105 bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
120 if (!open_file_proc) 106 if (!open_file_proc)
121 return false; 107 return false;
122 108
123 for (const auto& pid : pids) { 109 for (const auto& pid : pids) {
124 const std::string path = std::string(PROC_LOCATION) + "/" + std::to_string(pid) + "/fd"; 110 const std::filesystem::path path = std::filesystem::path(PROC_LOCATION) / std::to_string(pid) / "fd";
125 111
126 for (const auto& dir : GetAllFilesInDir(path)) { 112 for (const auto& dir : std::filesystem::directory_iterator{path}) {
127 if (!AreFlagsOk(pid, std::stoi(Basename(dir)))) 113 if (!AreFlagsOk(pid, StringToInt(dir.path().stem())))
128 continue; 114 continue;
129 115
130 std::string name; 116 std::string name;
131 if (!GetFilenameFromFd(dir, name)) 117 if (!GetFilenameFromFd(dir.path(), name))
132 continue; 118 continue;
133 119
134 if (!IsRegularFile(name)) 120 if (!IsRegularFile(name))
135 continue; 121 continue;
136 122