|
12 | 12 |
|
13 | 13 | #include <ctime> |
14 | 14 | #include <future> |
| 15 | +#include <set> |
15 | 16 |
|
16 | 17 | #if defined(__APPLE__) |
17 | 18 | extern char** environ; |
@@ -249,9 +250,9 @@ namespace vcpkg |
249 | 250 | #endif |
250 | 251 | } |
251 | 252 |
|
252 | | - Optional<ProcessStat> try_parse_process_stat_file(StringView text, StringView origin) |
| 253 | + Optional<ProcessStat> try_parse_process_stat_file(const FileContents& contents) |
253 | 254 | { |
254 | | - ParserBase p(text, origin); |
| 255 | + ParserBase p(contents.content, contents.origin); |
255 | 256 |
|
256 | 257 | p.match_while(ParserBase::is_ascii_digit); // pid %d (ignored) |
257 | 258 |
|
@@ -293,61 +294,115 @@ namespace vcpkg |
293 | 294 | } |
294 | 295 | return nullopt; |
295 | 296 | } |
| 297 | +} // namespace vcpkg |
296 | 298 |
|
| 299 | +namespace |
| 300 | +{ |
| 301 | +#if defined(_WIN32) |
| 302 | + struct ToolHelpProcessSnapshot |
| 303 | + { |
| 304 | + ToolHelpProcessSnapshot() noexcept : snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) { } |
| 305 | + ToolHelpProcessSnapshot(const ToolHelpProcessSnapshot&) = delete; |
| 306 | + ToolHelpProcessSnapshot& operator=(const ToolHelpProcessSnapshot&) = delete; |
| 307 | + ~ToolHelpProcessSnapshot() |
| 308 | + { |
| 309 | + if (snapshot != INVALID_HANDLE_VALUE) |
| 310 | + { |
| 311 | + CloseHandle(snapshot); |
| 312 | + } |
| 313 | + } |
| 314 | + explicit operator bool() const noexcept { return snapshot != INVALID_HANDLE_VALUE; } |
| 315 | + |
| 316 | + BOOL Process32First(PPROCESSENTRY32W entry) const noexcept { return Process32FirstW(snapshot, entry); } |
| 317 | + BOOL Process32Next(PPROCESSENTRY32W entry) const noexcept { return Process32NextW(snapshot, entry); } |
| 318 | + |
| 319 | + private: |
| 320 | + HANDLE snapshot; |
| 321 | + }; |
| 322 | +#elif defined(__linux__) |
| 323 | + Optional<ProcessStat> try_get_process_stat_by_pid(int pid) |
| 324 | + { |
| 325 | + auto filepath = fmt::format("/proc/{}/stat", pid); |
| 326 | + auto maybe_contents = real_filesystem.try_read_contents(filepath); |
| 327 | + if (auto contents = maybe_contents.get()) |
| 328 | + { |
| 329 | + return try_parse_process_stat_file(*contents); |
| 330 | + } |
| 331 | + |
| 332 | + return nullopt; |
| 333 | + } |
| 334 | +#endif // ^^^ __linux__ |
| 335 | +} // unnamed namespace |
| 336 | + |
| 337 | +namespace vcpkg |
| 338 | +{ |
297 | 339 | void get_parent_process_list(std::vector<std::string>& ret) |
298 | 340 | { |
299 | 341 | ret.clear(); |
300 | 342 | #if defined(_WIN32) |
301 | 343 | // Enumerate all processes in the system snapshot. |
302 | | - auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
303 | | - if (snapshot == INVALID_HANDLE_VALUE) return; |
304 | | - |
305 | 344 | std::map<DWORD, DWORD> pid_ppid_map; |
306 | 345 | std::map<DWORD, std::string> pid_exe_path_map; |
| 346 | + std::set<DWORD> seen_pids; |
307 | 347 |
|
308 | | - PROCESSENTRY32W entry; |
309 | | - memset(&entry, 0, sizeof(entry)); |
| 348 | + PROCESSENTRY32W entry{}; |
310 | 349 | entry.dwSize = sizeof(entry); |
311 | | - if (Process32FirstW(snapshot, &entry)) |
312 | 350 | { |
313 | | - do |
| 351 | + ToolHelpProcessSnapshot snapshot; |
| 352 | + if (!snapshot) |
314 | 353 | { |
315 | | - pid_ppid_map.emplace(entry.th32ProcessID, entry.th32ParentProcessID); |
316 | | - pid_exe_path_map.emplace(entry.th32ProcessID, Strings::to_utf8(entry.szExeFile)); |
317 | | - } while (Process32NextW(snapshot, &entry)); |
318 | | - } |
319 | | - CloseHandle(snapshot); |
| 354 | + return; |
| 355 | + } |
| 356 | + |
| 357 | + if (snapshot.Process32First(&entry)) |
| 358 | + { |
| 359 | + do |
| 360 | + { |
| 361 | + pid_ppid_map.emplace(entry.th32ProcessID, entry.th32ParentProcessID); |
| 362 | + pid_exe_path_map.emplace(entry.th32ProcessID, Strings::to_utf8(entry.szExeFile)); |
| 363 | + } while (snapshot.Process32Next(&entry)); |
| 364 | + } |
| 365 | + } // destroy snapshot |
320 | 366 |
|
321 | 367 | // Find hierarchy of current process |
322 | | - auto it = pid_ppid_map.find(GetCurrentProcessId()); |
323 | | - if (it == pid_ppid_map.end()) return; |
324 | | - while (true) |
| 368 | + |
| 369 | + for (DWORD next_parent = GetCurrentProcessId();;) |
325 | 370 | { |
326 | | - it = pid_ppid_map.find(it->second); |
327 | | - if (it == pid_ppid_map.end()) break; |
| 371 | + if (Util::Sets::contains(seen_pids, next_parent)) |
| 372 | + { |
| 373 | + // parent graph loops, for example if a parent terminates and the PID is reused by a child launch |
| 374 | + break; |
| 375 | + } |
| 376 | + |
| 377 | + seen_pids.insert(next_parent); |
| 378 | + auto it = pid_ppid_map.find(next_parent); |
| 379 | + if (it == pid_ppid_map.end()) |
| 380 | + { |
| 381 | + break; |
| 382 | + } |
| 383 | + |
328 | 384 | ret.push_back(pid_exe_path_map[it->first]); |
| 385 | + next_parent = it->second; |
329 | 386 | } |
330 | 387 | #elif defined(__linux__) |
331 | | - std::error_code ec; |
332 | | - auto vcpkg_stat_filepath = fmt::format("/proc/{}/stat", getpid()); |
333 | | - auto vcpkg_stat_contents = real_filesystem.read_contents(vcpkg_stat_filepath, ec); |
334 | | - if (ec) return; |
335 | | - |
336 | | - auto maybe_vcpkg_stat = try_parse_process_stat_file(vcpkg_stat_contents, vcpkg_stat_filepath); |
| 388 | + std::set<int> seen_pids; |
| 389 | + auto maybe_vcpkg_stat = try_get_process_stat_by_pid(getpid()); |
337 | 390 | if (auto vcpkg_stat = maybe_vcpkg_stat.get()) |
338 | 391 | { |
339 | | - auto pid = vcpkg_stat->ppid; |
340 | | - while (pid != 0) |
| 392 | + for (auto next_parent = vcpkg_stat->ppid; next_parent != 0;) |
341 | 393 | { |
342 | | - auto stat_filepath = fmt::format("/proc/{}/stat", pid); |
343 | | - auto contents = real_filesystem.read_contents(stat_filepath, ec); |
344 | | - if (ec) break; |
| 394 | + if (Util::Sets::contains(seen_pids, next_parent)) |
| 395 | + { |
| 396 | + // parent graph loops, for example if a parent terminates and the PID is reused by a child launch |
| 397 | + break; |
| 398 | + } |
345 | 399 |
|
346 | | - auto maybe_stat = try_parse_process_stat_file(contents, stat_filepath); |
347 | | - if (auto stat = maybe_stat.get()) |
| 400 | + seen_pids.insert(next_parent); |
| 401 | + auto maybe_next_parent_stat = try_get_process_stat_by_pid(next_parent); |
| 402 | + if (auto next_parent_stat = maybe_next_parent_stat.get()) |
348 | 403 | { |
349 | | - ret.push_back(stat->executable_name); |
350 | | - pid = stat->ppid; |
| 404 | + ret.push_back(next_parent_stat->executable_name); |
| 405 | + next_parent = next_parent_stat->ppid; |
351 | 406 | } |
352 | 407 | else |
353 | 408 | { |
|
0 commit comments