root/trunk/whisperlib/common/sync/process.cc

Revision 7, 9.8 kB (checked in by whispercastorg, 2 years ago)

version 0.2.0

Line 
1 // Copyright (c) 2009, Whispersoft s.r.l.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Whispersoft s.r.l. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Cosmin Tudorache
31
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include "common/base/errno.h"
37 #include "common/base/timer.h"
38 #include "common/base/scoped_ptr.h"
39 #include "common/sync/process.h"
40
41 // #declared in unistd.h
42 // Points to an array of strings called the 'environment'.
43 // By convention these strings have the form 'name=value'.
44
45 // """
46 // is initialized as a pointer to an array of character pointers to
47 // the environment strings. The argv and environ arrays are each
48 // terminated by a null pointer. The null pointer terminating  the
49 // argv array is not counted in argc.
50 // """
51
52 extern char** environ;
53
54 namespace process {
55
56 void MakeStringVector(const char* const argv[], vector<string>* out) {
57   if ( argv ) {
58     for ( int i = 0; argv[i] != NULL; i++ ) {
59       out->push_back(string(argv[i]));
60     }
61   }
62 }
63
64 Process::Process(const char* path, const char* arg, ...)
65   : path_(path),
66     bind_pid_(kInvalidPid),
67     pid_(kInvalidPid),
68     exit_status_(kInvalidExitValue),
69     exit_callback_(NULL),
70     executor_thread_(NewCallback(this, &Process::ExecutorRun)),
71     executor_thread_start_(false, true),
72     executor_thread_stop_(false, true),
73     executor_end_(false, true) {
74   va_list ap;
75   ::va_start(ap, arg);
76   const char* crt = arg;
77   while ( crt ) {
78     argv_.push_back(crt);
79     crt = va_arg(ap, const char*);
80   }
81   ::va_end(ap);
82   MakeStringVector(environ, &envp_);
83 }
84
85 Process::Process(const char* path, char* const argv[], char* const envp[])
86   : path_(path),
87     bind_pid_(kInvalidPid),
88     pid_(kInvalidPid),
89     exit_status_(kInvalidExitValue),
90     exit_callback_(NULL),
91     executor_thread_(NewCallback(this, &Process::ExecutorRun)),
92     executor_thread_start_(false, true),
93     executor_thread_stop_(false, true),
94     executor_end_(false, true) {
95   MakeStringVector(argv, &argv_);
96   MakeStringVector(envp, &envp_);
97 }
98
99 Process::Process(const string& path, const vector<string>& argv)
100   : path_(path),
101     argv_(argv),
102     bind_pid_(kInvalidPid),
103     pid_(kInvalidPid),
104     exit_status_(kInvalidExitValue),
105     exit_callback_(NULL),
106     executor_thread_(NewCallback(this, &Process::ExecutorRun)),
107     executor_thread_start_(false, true),
108     executor_thread_stop_(false, true),
109     executor_end_(false, true) {
110   MakeStringVector(environ, &envp_);
111 }
112
113 Process::Process(const string& path,
114                  const vector<string>& argv,
115                  const vector<string>& envp)
116   : path_(path),
117     argv_(argv),
118     envp_(envp),
119     bind_pid_(kInvalidPid),
120     pid_(kInvalidPid),
121     exit_status_(kInvalidExitValue),
122     exit_callback_(NULL),
123     executor_thread_(NewCallback(this, &Process::ExecutorRun)),
124     executor_thread_start_(false, true),
125     executor_thread_stop_(false, true),
126     executor_end_(false, true) {
127 }
128
129 Process::Process(pid_t pid)
130   : path_(),
131     argv_(),
132     envp_(),
133     bind_pid_(pid),
134     pid_(kInvalidPid),
135     exit_status_(kInvalidExitValue),
136     exit_callback_(NULL),
137     executor_thread_(NewCallback(this, &Process::ExecutorRun)),
138     executor_thread_start_(false, true),
139     executor_thread_stop_(false, true),
140     executor_end_(false, true) {
141 }
142
143 Process::~Process() {
144   int result;
145   if ( !Wait(5000, &result) ) {
146     Kill();
147   }
148   CHECK(!IsRunning());
149 }
150
151 bool Process::Start() {
152   if ( IsStarted() ) {
153     return false;
154   }
155   if ( !executor_thread_.Start() ) {
156     LOG_ERROR << "Failed to start executor thread: "
157               << GetLastSystemErrorDescription();
158     return false;
159   }
160   // 5 seconds should be enough
161   if ( !executor_thread_start_.Wait(5000) ) {
162     LOG_ERROR << "Timeout waiting for executor thread startup";
163     executor_thread_.Kill();
164     executor_thread_start_.Reset();
165     executor_thread_stop_.Reset();
166     pid_ = kInvalidPid;
167     return false;
168   }
169   return pid_ != kInvalidPid;
170 }
171
172 // Returns the running process pid.
173 // If the process is not started, returns kInvalidPid.
174 pid_t Process::Pid() const {
175   return pid_;
176 }
177
178 // Sends a signal to the running process.
179 // Returns success status. On failure, call GetLastSystemError() for errno code.
180 bool Process::Signal(int signum) {
181   if ( !IsStarted() ) {
182     return false;
183   }
184   if ( ::kill(Pid(), signum) ) {
185     LOG_ERROR << "::kill failed for pid_=" << Pid()
186               << ", signal=" << signum
187               << ", error=" << GetLastSystemErrorDescription();
188     return false;
189   }
190   return true;
191 }
192
193 // Kills the process.
194 void Process::Kill() {
195   if ( !IsRunning() ) {
196     return;
197   }
198
199   // detach
200   executor_end_.Signal();
201   executor_thread_stop_.Wait(5000);
202
203   // kill
204   if ( ::kill(Pid(), SIGKILL) ) {
205     LOG_ERROR << "::kill failed for pid_=" << Pid()
206               << ", signal=" << SIGKILL
207               << ", error=" << GetLastSystemErrorDescription();
208   } else {
209     LOG_WARNING << "Process [pid=" << Pid() << "] killed.";
210   }
211 }
212
213 void Process::Detach() {
214   if ( !IsRunning() ) {
215     return;
216   }
217   // detach
218   executor_end_.Signal();
219   executor_thread_stop_.Wait(5000);
220 }
221
222 bool Process::Wait(uint32 timeout_ms, int* exit_status) {
223   bool success = executor_thread_stop_.Wait(timeout_ms);
224   if ( !success ) {
225     // Timeout, the process is still running.
226     return false;
227   }
228   *exit_status = exit_status_;
229   return true;
230 }
231
232 void Process::SetExitCallback(ExitCallback* exit_callback) {
233   exit_callback_ = exit_callback;
234 }
235
236 bool Process::IsStarted() const {
237   return pid_ != kInvalidPid;
238 }
239
240 bool Process::IsRunning() const {
241   return IsStarted() &&                      // already started
242          exit_status_ == kInvalidExitValue;  // not yet terminated
243 }
244
245 //////////////////////////////////////////////////////////////////
246 //
247 //                  execute using execve
248 //
249 void Process::ExecutorRun() {
250   if ( bind_pid_ == kInvalidPid ) {
251     LOG_WARNING << "Starting process: path_=" << path_
252                 << " argv_=" << strutil::ToString(argv_)
253                 << " envp_=" << strutil::ToString(envp_);
254
255     const char* path = path_.c_str();
256
257     vector<string>::const_iterator it;
258     uint32 i;
259
260     scoped_array<char*> argv(new char*[argv_.size() + 2]);
261     argv[0] = const_cast<char*>(path);
262     for ( it = argv_.begin(), i = 1; it != argv_.end(); ++it, ++i ) {
263       argv[i] = const_cast<char*>(it->c_str());
264     }
265     argv[argv_.size() + 1] = NULL;
266
267     scoped_array<char*> envp(new char*[envp_.size() + 1]);
268     for ( it = envp_.begin(), i = 0; it != envp_.end(); ++it, ++i ) {
269       envp[i] = const_cast<char*>(it->c_str());
270     }
271     envp[envp_.size()] = NULL;
272
273     pid_t pid = ::fork();
274     if ( pid == 0 ) {
275       // child process here
276       int result = ::execve(path, argv.get(), envp.get());
277       ::_exit(result);
278     }
279     pid_ = pid;
280     LOG_WARNING << "Process [pid=" << pid_ << "] running.";
281   } else {
282     // test if bind_pid_ exists
283     int result = ::kill(bind_pid_, 0);
284     if ( result == -1 ) {
285       switch ( GetLastSystemError() ) {
286         case ESRCH:
287           LOG_ERROR << "Process [pid=" << pid_ << "] does not exist.";
288           break;
289         case EPERM:
290           LOG_ERROR << "Process [pid=" << pid_ << "]. You don't have "
291                     << " permission to send signals to this process.";
292           break;
293         default:
294           LOG_ERROR << "::kill failed for pid=" << pid_ << " with error="
295                     << GetLastSystemErrorDescription();
296       }
297       exit_status_ = kErrorExitValue;
298       executor_thread_start_.Signal();
299       executor_thread_stop_.Signal();
300       return;
301     }
302     pid_ = bind_pid_;
303   }
304
305   // parent thread [the child is running in background]
306
307   executor_thread_start_.Signal();
308   while ( true ) {
309     int status;
310     const pid_t result = ::waitpid(pid_, &status, WNOHANG);
311     if ( result == -1 ) {
312       LOG_ERROR << "::waitpid [pid=" << pid_ << "] failed: "
313                 << GetLastSystemErrorDescription();
314       exit_status_ = kErrorExitValue;
315       break;
316     }
317     if ( result == 0 ) {
318       // timeout, sleep for 1 sec then try again ::waitpid
319       bool success = executor_end_.Wait(1000);
320       if ( success ) {
321         exit_status_ = kDetachedExitValue;
322         break;
323       } else {
324         continue;
325       }
326     }
327     CHECK_EQ(result, pid_);
328     exit_status_ = WEXITSTATUS(status);
329     LOG_WARNING << "Process [pid=" << pid_ << "] terminated: path_=\""
330                 << path_ << "\", exit_status_=" << exit_status_;
331     break;
332   };
333   CHECK_NE(exit_status_, kInvalidExitValue);
334
335   if ( exit_callback_ ) {
336     exit_callback_->Run(exit_status_);
337   }
338
339   executor_thread_stop_.Signal();
340 }
341 }
Note: See TracBrowser for help on using the browser.