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