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