ProcessUtil.h
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package ArmarXCore::core
17 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
18 * GNU General Public License
19 */
20
21#pragma once
22
23#include <chrono>
24#include <optional>
25#include <string>
26#include <vector>
27
29{
30 /**
31 * @return whether the process with the given pid has terminated but has not been reaped yet.
32 *
33 * A zombie still occupies its pid and still has a /proc entry, but it is dead: it cannot be
34 * signalled, it will never run again, and it disappears only once somebody waits for it.
35 * `pid <= 0` is never a zombie.
36 */
37 bool IsZombie(int pid);
38
39 /**
40 * @return whether a process with the given pid currently exists *and has not terminated*.
41 * `pid <= 0` is never alive.
42 *
43 * Zombies are deliberately not alive. The whole point of asking is to decide whether there is
44 * still a process there to wait for, to signal, or to refuse a start over, and for a zombie the
45 * answer to all three is no - waiting for one to disappear runs into the full timeout, and
46 * signalling one is silently discarded (kill() even reports success).
47 */
48 bool IsAlive(int pid);
49
50 /**
51 * Reads the name of the executable behind `pid` (the basename of argv[0]).
52 *
53 * Returns an empty string if the name could not be determined. That is *not* the same as
54 * "the process is gone": a process that has been forked but has not reached execve() yet, a
55 * zombie and a process we are not allowed to inspect all report an empty name while being
56 * very much alive. Callers must use IsAlive() to tell the two cases apart before drawing
57 * conclusions about the process' status.
58 */
59 std::string GetProcessName(int pid);
60
61 /**
62 * Reads the full argument vector of `pid` from /proc/<pid>/cmdline.
63 *
64 * Returns an empty vector when it cannot be read - see GetProcessName() for why that does not
65 * imply the process is gone.
66 */
67 std::vector<std::string> GetProcessArguments(int pid);
68
69 /**
70 * A live process as seen by ScanProcesses().
71 */
73 {
74 int pid = -1;
75 /// Basename of argv[0].
76 std::string name;
77 /// The full argument vector, argv[0] included.
78 std::vector<std::string> arguments;
79 };
80
81 /**
82 * Walks /proc and returns every process whose argument vector could be read.
83 *
84 * This is the only way to find an application the ScenarioManager did not launch itself (or
85 * whose pid it lost), since nothing else ties a running process back to an application.
86 * Processes that cannot be read - other users' processes, or ones exiting while we walk - are
87 * silently skipped.
88 *
89 * The walk costs one open() per process on the machine, so callers are expected to do it for
90 * all applications at once rather than per application, and not on every status poll.
91 */
92 std::vector<ProcessInfo> ScanProcesses();
93
94 /**
95 * @return the value of the `--Ice.Config=` argument in `arguments`, if there is one.
96 */
97 std::optional<std::string> FindIceConfigArgument(const std::vector<std::string>& arguments);
98
99 /**
100 * Whether the process behind a pid still is the one we think it is.
101 */
102 enum class Identity
103 {
104 /// argv says this is the expected executable, and it does not claim a foreign config.
106 /// argv says this is a different program, or the same program serving a different config.
108 /// argv could not be read, so nothing can be concluded either way. A zombie, a process
109 /// forked but not yet exec'd, and a process we may not inspect all land here.
111 };
112
113 /**
114 * Checks whether the process with the given pid is still the application it is supposed to be.
115 *
116 * Pids are recycled. Once the process an application was launched as has exited, the kernel is
117 * free to hand its pid to something completely unrelated, and from that moment on every
118 * conclusion drawn from `/proc/<pid>` is about a stranger: the application looks alive when it
119 * is not, a start is refused because "it is already running", and a stop signals - possibly
120 * kills - an innocent process.
121 *
122 * `configPath` is the stronger of the two criteria and catches a pid recycled by *another*
123 * instance of the same binary; it is only applied when the process carries an `--Ice.Config`
124 * at all, since an application started by hand from a shell legitimately has none.
125 *
126 * @param executableName basename of the application's executable.
127 * @param configPath the application's config file; may be empty, in which case only the name
128 * is compared.
129 */
131 const std::string& executableName,
132 const std::string& configPath);
133
134 /**
135 * Blocks until the process with the given pid has disappeared, at most for `timeout`.
136 * @return true if the process is gone, false if it was still alive when `timeout` elapsed.
137 */
138 bool WaitForExit(int pid,
139 std::chrono::milliseconds timeout,
140 std::chrono::milliseconds pollInterval = std::chrono::milliseconds(20));
141} // namespace ScenarioManager::Exec::ProcessUtil
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().
Definition ProcessUtil.h:73
std::vector< std::string > arguments
The full argument vector, argv[0] included.
Definition ProcessUtil.h:78
std::string name
Basename of argv[0].
Definition ProcessUtil.h:76