Line data Source code
1 : // DUNE DAQ modification notice:
2 : // This file has been modified from the original ATLAS system source for the DUNE DAQ project.
3 : // Fork baseline commit: system-00-00-20 (2020-09-25).
4 : // Renamed since fork: yes (from src/Process.cxx to src/Process.cpp).
5 :
6 : /*
7 : * Process.cxx
8 : * OksSystem
9 : *
10 : * Created by Matthias Wiesmann on 18.01.05.
11 : * Copyright 2005 CERN. All rights reserved.
12 : *
13 : */
14 :
15 : #include <sys/types.h>
16 : #include <sys/wait.h>
17 : #include <sysexits.h>
18 : #include <unistd.h>
19 :
20 : #include <iostream>
21 : #include <sstream>
22 :
23 : #include "ers/ers.hpp"
24 :
25 : #include "okssystem/Process.hpp"
26 : #include "okssystem/exceptions.hpp"
27 :
28 : const int OksSystem::Process::TEST_BASE_VALUE = 182; /**< Lowest value for test manager result code */
29 : const int OksSystem::Process::TEST_MAX_VALUE = 186; /**< Highest value for test manager result code */
30 :
31 : const int OksSystem::Process::TERMINATION_WAIT =
32 : 100000; /**< Wait time before deciding a termination signal did not work (in nanoseconds) */
33 :
34 : const char* const OksSystem::Process::SYS_EXITS_NAMES[] = {
35 : "command line usage error", "data format error",
36 : "cannot open input", "addressee unknown",
37 : "host name unknown", "service unavailable",
38 : "internal software error", "okssystem error",
39 : "critical OS file missing", "can't create (user) output file",
40 : "input/output error", "temp failure; user is invited to retry",
41 : "remote error in protocol", "permission denied ",
42 : "configuration error"
43 : };
44 : const char* const OksSystem::Process::TEST_EXITS_NAMES[] = { "undefined test",
45 : "test failed",
46 : "test unresolved",
47 : "test untested",
48 : "unsupported test" };
49 : const char* const OksSystem::Process::OK_EXIT_NAME = "ok";
50 :
51 : OksSystem::Process* OksSystem::Process::s_instance = 0;
52 :
53 : /** Gives textual representation of exit values.
54 : * This method recognises return codes from \c sysexits and the values returned by the text-manager
55 : * \param return_value the integer returned by a program
56 : * \return a pointer to the string desribing the code, or a null pointer if the code is not recognised
57 : */
58 :
59 : const char*
60 0 : OksSystem::Process::exit_text(int return_value)
61 : {
62 0 : if (0 == return_value)
63 : return OK_EXIT_NAME;
64 0 : if ((return_value >= EX__BASE) && (return_value <= EX__MAX))
65 0 : return SYS_EXITS_NAMES[return_value - EX__BASE];
66 0 : if ((return_value >= TEST_BASE_VALUE) && (return_value <= TEST_MAX_VALUE))
67 0 : return TEST_EXITS_NAMES[return_value - TEST_BASE_VALUE];
68 : return 0;
69 : } // return_text
70 :
71 : /** Builds a pretty string for an exit value
72 : * \param return_value the integer returned by a program
73 : * \return if the code is recognised by \c exit_text, this text and the number between parenthesis
74 : * if not, the \c return_value converter into a string
75 : */
76 :
77 : std::string
78 0 : OksSystem::Process::exit_pretty(int return_value)
79 : {
80 0 : std::ostringstream stream;
81 0 : const char* text = OksSystem::Process::exit_text(return_value);
82 0 : if (text) {
83 0 : stream << text << '(' << return_value << ')';
84 : } else {
85 0 : stream << return_value;
86 : }
87 0 : return stream.str();
88 0 : } // exit_string
89 :
90 : /** Default current process instance
91 : * If needed the instance is created
92 : * \return singleton instance for current process
93 : */
94 :
95 : const OksSystem::Process*
96 0 : OksSystem::Process::instance() throw()
97 : {
98 0 : if (0 == s_instance) {
99 0 : s_instance = new OksSystem::Process();
100 : } // if
101 0 : return s_instance;
102 : } // instance
103 :
104 : /** Sets the process name of the current process
105 : */
106 :
107 : void
108 0 : OksSystem::Process::set_name(const std::string& name) throw()
109 : {
110 0 : (void)instance();
111 0 : s_instance->m_process_name = name;
112 0 : } // set_name
113 :
114 : /** Constructor from current process
115 : * For the current process, the singleton factory method \c instance should be used
116 : */
117 :
118 0 : OksSystem::Process::Process()
119 : {
120 0 : m_process_id = ::getpid();
121 0 : } // Process
122 :
123 : /** Copy constructor
124 : * \param other original instance
125 : */
126 :
127 0 : OksSystem::Process::Process(const Process& other)
128 : {
129 0 : m_process_id = other.m_process_id;
130 0 : m_process_name = other.m_process_name;
131 0 : } // Process
132 :
133 : /** Constructor for pid
134 : * \param id pid of the process
135 : */
136 :
137 0 : OksSystem::Process::Process(pid_t id)
138 : {
139 0 : m_process_id = id;
140 0 : } // Process
141 :
142 : /** Constructor from pid and process name
143 : * \param id pid of the process
144 : * \param name display name of the process
145 : */
146 :
147 0 : OksSystem::Process::Process(pid_t id, const std::string& name)
148 : {
149 0 : m_process_id = id;
150 0 : m_process_name = name;
151 0 : }
152 :
153 : /** Destructor
154 : */
155 :
156 0 : OksSystem::Process::~Process() throw()
157 : {
158 0 : m_process_id = 0;
159 0 : } // ~Process
160 :
161 : /** Cast operator - converts to process-id
162 : */
163 :
164 0 : OksSystem::Process::operator pid_t() const throw()
165 : {
166 0 : return m_process_id;
167 : } // operator pid_t
168 :
169 : /** Waits for a process to terminate.
170 : * If the process terminates normally, the return value is the termination status of the process.
171 : * If the process stops or is signaled, an SignalIssue is thrown.
172 : * \return the termination status of the process
173 : */
174 :
175 : int
176 0 : OksSystem::Process::join(bool throw_non_zero) const
177 : {
178 0 : ERS_PRECONDITION(!equals(*instance()));
179 0 : int status;
180 0 : errno = 0;
181 0 : pid_t pid = ::waitpid(m_process_id, &status, 0);
182 0 : if (pid != m_process_id) {
183 0 : std::string message = "on process " + this->to_string();
184 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "waitpid", message.c_str());
185 0 : } // if
186 0 : if (WIFEXITED(status)) {
187 0 : int exit_status = WEXITSTATUS(status);
188 0 : if ((throw_non_zero == false) || (exit_status == 0))
189 : return exit_status;
190 0 : throw OksSystem::TerminationIssue(ERS_HERE, errno, exit_status);
191 : } // exit status
192 0 : if (WIFSIGNALED(status))
193 0 : throw OksSystem::SignalIssue(ERS_HERE, errno, WTERMSIG(status));
194 0 : if (WIFSTOPPED(status))
195 0 : throw OksSystem::SignalIssue(ERS_HERE, errno, WSTOPSIG(status));
196 0 : return WEXITSTATUS(status);
197 : } // join
198 :
199 : /** Sends a signal to the process
200 : * \param signal_number number of the signal
201 : */
202 :
203 : void
204 0 : OksSystem::Process::signal(int signal_number) const
205 : {
206 0 : const int status = ::kill(m_process_id, signal_number);
207 0 : if (status < 0) {
208 0 : std::string message = "on process " + this->to_string();
209 0 : throw OksSystem::OksSystemCallIssue(ERS_HERE, errno, "kill", message.c_str());
210 0 : }
211 0 : } // signal
212 :
213 : /** Checks if the process actually exists on the host
214 : * \return true if the process exists
215 : * \note currently the check is done by sending signal 0 to the process
216 : * in case of failure, we know process does not exist
217 : */
218 :
219 : bool
220 0 : OksSystem::Process::exists() const
221 : {
222 0 : const int status = ::kill(m_process_id, 0);
223 0 : return (status >= 0);
224 : } // exists
225 :
226 : /** Checks if two process are equal
227 : * \param other the process to compare this process to
228 : * \return true if both proceses are equal (same pid).
229 : */
230 :
231 : bool
232 0 : OksSystem::Process::equals(const Process& other) const throw()
233 : {
234 0 : return m_process_id == other.m_process_id;
235 : } // equals
236 :
237 : /** Terminates a process
238 : * Termination is implemented by
239 : * \li sending the TERM signal
240 : */
241 :
242 : void
243 0 : OksSystem::Process::terminate() const
244 : {
245 : /*
246 : struct timespec wait_time;
247 : struct timespec remain_time;
248 : wait_time.tv_sec = 0;
249 : wait_time.tv_nsec = TERMINATION_WAIT;
250 : */
251 0 : if (exists()) {
252 0 : signal(SIGTERM);
253 : }
254 0 : return;
255 : }
256 :
257 : /* This is a piece of crap
258 : nanosleep(&wait_time,&remain_time);
259 : if (exists()) {
260 : signal(SIGQUIT);
261 : } else {
262 : return;
263 : } //
264 : nanosleep(&wait_time,&remain_time);
265 : if (exists()) {
266 : signal(SIGKILL);
267 : } // exists
268 : } // terminate
269 : */
270 : /** Builds a string description of the process
271 : * \return textual description
272 : */
273 :
274 : std::string
275 0 : OksSystem::Process::to_string() const throw()
276 : {
277 0 : std::ostringstream stream;
278 0 : if (!m_process_name.empty()) {
279 0 : stream << m_process_name << ' ';
280 : }
281 0 : stream << "pid: " << m_process_id;
282 0 : return stream.str();
283 0 : } // to_string
284 :
285 : /** \return the process id for the process
286 : */
287 :
288 : pid_t
289 0 : OksSystem::Process::process_id() const throw()
290 : {
291 0 : return m_process_id;
292 : }
293 :
294 : /** Streaming operator
295 : * \param out destination stream
296 : * \param proc the process to stream
297 : * \see Process::to_string()
298 : */
299 :
300 : std::ostream&
301 0 : operator<<(std::ostream& out, const OksSystem::Process& proc)
302 : {
303 0 : out << proc.to_string();
304 0 : return out;
305 : } // operator<<
306 :
307 : /** Comparison operator
308 : * \param a first process to compare
309 : * \param b second process to compare
310 : * \return true if they are equal
311 : * \see OksSystem::Process::equals
312 : */
313 :
314 : bool
315 0 : operator==(const OksSystem::Process& a, const OksSystem::Process& b) throw()
316 : {
317 0 : return a.equals(b);
318 : } // operator ==
319 :
320 : /** Comparison operator
321 : * \param a first process to compare
322 : * \param b second process to compare
323 : * \return false if they are equal
324 : * \see OksSystem::Process::equals
325 : */
326 :
327 : bool
328 0 : operator!=(const OksSystem::Process& a, const OksSystem::Process& b) throw()
329 : {
330 0 : return !a.equals(b);
331 : } // operator !=
|