00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00040 #include "blocxx/BLOCXX_config.h"
00041
00042 #include "blocxx/DateTime.hpp"
00043 #include "blocxx/Exec.hpp"
00044
00045 #include "blocxx/Format.hpp"
00046 #include "blocxx/Process.hpp"
00047 #include "blocxx/SafeCString.hpp"
00048 #include "blocxx/String.hpp"
00049 #include "blocxx/Thread.hpp"
00050 #include "blocxx/UnnamedPipe.hpp"
00051 #include "blocxx/Paths.hpp"
00052 #include "blocxx/TimeoutTimer.hpp"
00053 #include "blocxx/SignalUtils.hpp"
00054 #include "blocxx/ThreadPool.hpp"
00055 #include "blocxx/Runnable.hpp"
00056 #include "blocxx/LazyGlobal.hpp"
00057 #include "blocxx/Logger.hpp"
00058 #include "blocxx/GlobalString.hpp"
00059 #include "blocxx/WaitpidThreadFix.hpp"
00060 #include "blocxx/System.hpp"
00061
00062 #ifdef BLOCXX_WIN32
00063 #include "blocxx/WinProcessUtils.hpp"
00064 #else
00065 #include <sys/wait.h>
00066 #endif
00067
00068 #include <fcntl.h>
00069 #include <signal.h>
00070 #include <cerrno>
00071 #include <cmath>
00072 #include <algorithm>
00073 #include <limits>
00074 #ifdef BLOCXX_HAVE_UNISTD_H
00075 #include <unistd.h>
00076 #endif
00077
00078 #if defined(sigemptyset)
00079
00080 #undef sigemptyset
00081 #endif // sigemptyset
00082
00083 namespace BLOCXX_NAMESPACE
00084 {
00085
00086 static const char* TERM_MESSAGE = "Terminate Process";
00087
00088 namespace
00089 {
00090 GlobalString COMPONENT_NAME = BLOCXX_GLOBAL_STRING_INIT("blocxx.Process");
00091 }
00092
00093
00094
00095 Process::Status pollStatusImpl(ProcId pid);
00096
00097 BLOCXX_DEFINE_EXCEPTION(ProcessError);
00098
00099
00100
00101
00102 Process::Status::Status(ProcId pid, int status)
00103 : m_status_available(pid > 0),
00104 m_status(status)
00105 {
00106 }
00107
00108 Process::Status::Status(int rep1, int rep2, Repr)
00109 : m_status_available(static_cast<bool>(rep1)),
00110 m_status(rep2)
00111 {
00112 }
00113
00114 #ifdef BLOCXX_WIN32
00115
00116 Process::Status::Status() : m_status_available(false), m_status(STILL_ACTIVE)
00117 {
00118 }
00119
00120 bool Process::Status::running() const
00121 {
00122 return m_status == STILL_ACTIVE;
00123 }
00124
00125 bool Process::Status::terminated() const
00126 {
00127 return m_status != STILL_ACTIVE;
00128 }
00129
00130 bool Process::Status::exitTerminated() const
00131 {
00132 return m_status != STILL_ACTIVE;
00133 }
00134
00135 int Process::Status::exitStatus() const
00136 {
00137 return m_status;
00138 }
00139
00140 int Process::Status::getPOSIXwaitpidStatus() const
00141 {
00142 return m_status;
00143 }
00144
00145 bool Process::Status::signalTerminated() const
00146 {
00147 return false;
00148 }
00149
00150 int Process::Status::termSignal() const
00151 {
00152 return -1;
00153 }
00154
00155 bool Process::Status::stopped() const
00156 {
00157 return false;
00158 }
00159
00160 int Process::Status::stopSignal() const
00161 {
00162 return -1;
00163 }
00164
00165 #else
00166
00167 Process::Status::Status() : m_status_available(false), m_status(0)
00168 {
00169 }
00170
00171 bool Process::Status::running() const
00172 {
00173 return !m_status_available;
00174 }
00175
00176 bool Process::Status::terminated() const
00177 {
00178 return m_status_available && (WIFEXITED(m_status) || WIFSIGNALED(m_status));
00179 }
00180
00181 bool Process::Status::exitTerminated() const
00182 {
00183 return m_status_available && WIFEXITED(m_status);
00184 }
00185
00186 int Process::Status::exitStatus() const
00187 {
00188 return WEXITSTATUS(m_status);
00189 }
00190
00191 int Process::Status::getPOSIXwaitpidStatus() const
00192 {
00193 return m_status;
00194 }
00195
00196 bool Process::Status::signalTerminated() const
00197 {
00198 return m_status_available && WIFSIGNALED(m_status);
00199 }
00200
00201 int Process::Status::termSignal() const
00202 {
00203 return WTERMSIG(m_status);
00204 }
00205
00206 bool Process::Status::stopped() const
00207 {
00208 return m_status_available && WIFSTOPPED(m_status);
00209 }
00210
00211 int Process::Status::stopSignal() const
00212 {
00213 return WSTOPSIG(m_status);
00214 }
00215
00216 #endif
00217
00218 void Process::Status::repr(int & rep1, int & rep2) const
00219 {
00220 rep1 = static_cast<int>(m_status_available);
00221 rep2 = m_status;
00222 }
00223
00224 bool Process::Status::terminatedSuccessfully() const
00225 {
00226 return exitTerminated() && exitStatus() == 0;
00227 }
00228
00229 String Process::Status::toString() const
00230 {
00231 if (running())
00232 {
00233 return "running";
00234 }
00235 else if (stopped())
00236 {
00237 return Format("stopped by %1", SignalUtils::signalName(stopSignal()));
00238 }
00239 else if (terminated())
00240 {
00241 if (exitTerminated())
00242 {
00243 return Format("exited with status %1", String(exitStatus()));
00244 }
00245 else if (signalTerminated())
00246 {
00247 return Format("terminated by signal %1", SignalUtils::signalName(termSignal()));
00248 }
00249 }
00250 return "Unknown";
00251 }
00252
00253
00254
00255 ProcessImpl::~ProcessImpl()
00256 {
00257 }
00258
00259 namespace
00260 {
00261
00262 class ChildProcessImpl : public ProcessImpl
00263 {
00264 public:
00265 virtual int kill(ProcId pid, int sig)
00266 {
00267 #ifdef BLOCXX_WIN32
00268 return -1;
00269 #else
00270 return ::kill(pid, sig) == 0 ? 0 : errno;
00271 #endif
00272 }
00273
00274 virtual Process::Status pollStatus(ProcId pid)
00275 {
00276 if (WaitpidThreadFix::shouldUseWaitpidThreadFix())
00277 {
00278 return WaitpidThreadFix::waitPid(pid);
00279 }
00280 return pollStatusImpl(pid);
00281 }
00282 };
00283
00284
00285 struct ZombieReaperPoolCreator
00286 {
00287 static ThreadPool* create(int dummy)
00288 {
00289 return new ThreadPool(ThreadPool::DYNAMIC_SIZE, (std::numeric_limits<UInt32>::max)(), 0);
00290 }
00291 };
00292 LazyGlobal<ThreadPool, int, ZombieReaperPoolCreator> g_zombieReaperPool = BLOCXX_LAZY_GLOBAL_INIT(0);
00293
00294 class ZombieReaper : public Runnable
00295 {
00296 public:
00297 ZombieReaper(ProcId pid, const ProcessImplRef& impl)
00298 : m_pid(pid)
00299 , m_impl(impl)
00300 {
00301 }
00302 virtual void run()
00303 {
00304 Logger lgr(COMPONENT_NAME);
00305 BLOCXX_LOG_DEBUG(lgr, Format("ZombieReaper getting status for %1.", m_pid));
00306 Process::Status status = m_impl->pollStatus(m_pid);
00307 while (!status.terminated())
00308 {
00309 Thread::sleep(Timeout::relative(10));
00310 BLOCXX_LOG_DEBUG(lgr, Format("ZombieReaper getting status for %1.", m_pid));
00311 status = m_impl->pollStatus(m_pid);
00312 }
00313 BLOCXX_LOG_DEBUG(lgr, Format("ZombieReaper got status for %1: %2.", m_pid, status.toString()));
00314 }
00315 private:
00316 ProcId m_pid;
00317 ProcessImplRef m_impl;
00318 };
00319
00320 }
00321
00322
00323
00324
00325 Process::Process(
00326 UnnamedPipeRef const & in, UnnamedPipeRef const & out,
00327 UnnamedPipeRef const & err, ProcId pid
00328 )
00329 : m_impl(new ChildProcessImpl())
00330 , m_in(in)
00331 , m_out(out)
00332 , m_err(err)
00333 , m_pid(pid)
00334 , m_status()
00335 {
00336 }
00337
00338 Process::Process(
00339 const ProcessImplRef& impl, UnnamedPipeRef const & in, UnnamedPipeRef const & out,
00340 UnnamedPipeRef const & err, ProcId pid
00341 )
00342 : m_impl(impl)
00343 , m_in(in)
00344 , m_out(out)
00345 , m_err(err)
00346 , m_pid(pid)
00347 , m_status()
00348 {
00349 }
00350
00351
00352 Process::Process(ProcId pid)
00353 : m_impl(new ChildProcessImpl())
00354 , m_in()
00355 , m_out()
00356 , m_err()
00357 , m_pid(pid)
00358 , m_status()
00359 {
00360 }
00361
00362 Process::~Process()
00363 {
00364 if (m_pid < 0)
00365 {
00366 return;
00367 }
00368 try
00369 {
00370 this->waitCloseTerm(Timeout::relative(0.0), Timeout::relative(1.0), Timeout::relative(2.0));
00371 }
00372 catch (Exception& e)
00373 {
00374 Logger lgr(COMPONENT_NAME);
00375 BLOCXX_LOG_DEBUG(lgr, Format("Process::~Process caught %1 from waitCloseTerm()", e));
00376
00377 if (!m_status.terminated())
00378 {
00379 BLOCXX_LOG_DEBUG(lgr, Format("Process %1 didn't exit cleanly. Creating a ZombieReaper for it.", m_pid));
00380 static_cast<ThreadPool>(g_zombieReaperPool).addWork(new ZombieReaper(m_pid, m_impl));
00381 }
00382 }
00383 catch (...)
00384 {
00385
00386 if (!m_status.terminated())
00387 {
00388 Logger lgr(COMPONENT_NAME);
00389 BLOCXX_LOG_DEBUG(lgr, Format("Process %1 didn't exit cleanly. Creating a ZombieReaper for it.", m_pid));
00390 static_cast<ThreadPool>(g_zombieReaperPool).addWork(new ZombieReaper(m_pid, m_impl));
00391 }
00392 }
00393 }
00394
00395 void Process::release()
00396 {
00397 m_in = 0;
00398 m_out = 0;
00399 m_err = 0;
00400 m_pid = BLOCXX_INVALID_HANDLE;
00401 }
00402
00403 UnnamedPipeRef Process::in() const
00404 {
00405 return m_in;
00406 }
00407
00408 UnnamedPipeRef Process::out() const
00409 {
00410 return m_out;
00411 }
00412
00413 UnnamedPipeRef Process::err() const
00414 {
00415 return m_err;
00416 }
00417
00418 ProcId Process::pid() const
00419 {
00420 return m_pid;
00421 }
00422
00423 Process::Status Process::processStatus()
00424 {
00425
00426 if (m_pid >= 0 && !m_status.terminated())
00427 {
00428 m_status = m_impl->pollStatus(m_pid);
00429 }
00430 return m_status;
00431 }
00432
00433 namespace
00434 {
00435 inline void upr_close(UnnamedPipeRef & x)
00436 {
00437 if (x)
00438 {
00439 x->close();
00440 }
00441 }
00442 }
00443
00444 void Process::waitCloseTerm(float wait_initial, float wait_close, float wait_term)
00445 {
00446 waitCloseTerm(Timeout::relative(wait_initial), Timeout::relative(wait_close), Timeout::relative(wait_term));
00447 }
00448
00449 void Process::waitCloseTerm(const Timeout& wait_initial, const Timeout& wait_close, const Timeout& wait_term,
00450 ETerminationSelectionFlag terminationSelectionFlag)
00451 {
00452 if (m_pid < 0)
00453 {
00454 return;
00455 }
00456
00457 processStatus();
00458
00459 if (m_status.terminated())
00460 {
00461 return;
00462 }
00463
00464 if (m_pid == getCurProcessId())
00465 {
00466 BLOCXX_THROW(ProcessErrorException, "Process::m_pid == the current process id");
00467 }
00468
00469 TimeoutTimer initialTimer(wait_initial);
00470 TimeoutTimer closeTimer(wait_close);
00471 TimeoutTimer termTimer(wait_term);
00472
00473 if (wait_initial.getType() == Timeout::E_RELATIVE && wait_initial.getRelative() > 0 && this->terminatesWithin(initialTimer.asAbsoluteTimeout()))
00474 {
00475 return;
00476 }
00477
00478 if (wait_close.getType() == Timeout::E_RELATIVE && wait_close.getRelative() > 0)
00479 {
00480
00481
00482
00483
00484 upr_close(m_in);
00485 upr_close(m_out);
00486 upr_close(m_err);
00487
00488 if (this->terminatesWithin(closeTimer.asAbsoluteTimeout()))
00489 {
00490 return;
00491 }
00492 }
00493
00494 #ifdef BLOCXX_WIN32
00495
00496 if (wait_term.getType() == Timeout::E_RELATIVE && wait_term.getRelative() > 0 && this->terminateByMessage(termTimer.asAbsoluteTimeout()))
00497 {
00498 return;
00499 }
00500
00501
00502
00503 Timeout const killTimeout = Timeout::relative(60.0);
00504 if (!killProcess(killTimeout, terminationSelectionFlag))
00505 {
00506 BLOCXX_THROW(ProcessErrorException, "Child process has not terminated after killProcess().");
00507 }
00508
00509 #else
00510
00511 if (wait_term.getType() == Timeout::E_RELATIVE && wait_term.getRelative() > 0 && this->killWait(termTimer.asAbsoluteTimeout(), SIGTERM, "SIGTERM", terminationSelectionFlag))
00512 {
00513 return;
00514 }
00515
00516
00517 Timeout const sigkillTimeout = Timeout::relative(60.0);
00518 if (!killWait(sigkillTimeout, SIGKILL, "SIGKILL", terminationSelectionFlag))
00519 {
00520 BLOCXX_THROW(ProcessErrorException, "Child process has not terminated after sending it a SIGKILL.");
00521 }
00522
00523 #endif
00524 }
00525
00526
00527
00528
00529
00530 bool Process::terminatesWithin(const Timeout& wait_time)
00531 {
00532 float const mult = 1.20;
00533 float const max_period = 5000.0;
00534 float period = 100.0;
00535 TimeoutTimer timer(wait_time);
00536 while (!timer.expired() && !m_status.terminated())
00537 {
00538 Thread::sleep(static_cast<UInt32>(period));
00539 period = (std::min)(max_period, period * mult);
00540 m_status = m_impl->pollStatus(m_pid);
00541 timer.loop();
00542 }
00543 return m_status.terminated();
00544 }
00545
00546
00547
00548 #ifdef BLOCXX_WIN32
00549
00550 Process::Status pollStatusImpl(ProcId pid)
00551 {
00552 DWORD exitCode;
00553
00554 DWORD rc1 = WaitForSingleObject(pid, 0);
00555 if(rc1 == WAIT_FAILED)
00556 {
00557 String msg;
00558 System::lastErrorMsg("pollStatusImpl() 1: ", msg);
00559 BLOCXX_THROW_ERRNO_MSG(ProcessErrorException, msg);
00560 }
00561
00562 BOOL rc = GetExitCodeProcess(pid, &exitCode);
00563
00564 if (!rc)
00565 {
00566 String msg;
00567 System::lastErrorMsg("pollStatusImpl() 2: ", msg);
00568 BLOCXX_THROW_ERRNO_MSG(ProcessErrorException, msg);
00569 }
00570
00571 return Process::Status(pid, exitCode);
00572 }
00573
00574
00575
00576 bool Process::terminateByMessage(const Timeout& waitTime)
00577 {
00578 DWORD bsmApp = BSM_APPLICATIONS;
00579 UINT termMsg = RegisterWindowMessage(TERM_MESSAGE);
00580 BOOL bSucceed = BroadcastSystemMessage(BSF_IGNORECURRENTTASK, &bsmApp, termMsg, NULL, NULL);
00581
00582 if (bSucceed == -1)
00583 {
00584 if (this->processStatus().terminated())
00585 {
00586 return true;
00587 }
00588 else
00589 {
00590 String msg;
00591 System::lastErrorMsg("Process::terminateByMessage()", msg);
00592 BLOCXX_THROW_ERRNO_MSG(ProcessErrorException, msg);
00593 }
00594 }
00595
00596 return this->terminatesWithin(waitTime);
00597 }
00598
00599 bool Process::killProcess(const Timeout& waitTime, ETerminationSelectionFlag terminationSelectionFlag)
00600 {
00601 DWORD result = ERROR_SUCCESS;
00602
00603 DWORD pId = WinUtils::getProcessIdNT(m_pid);
00604 if (terminationSelectionFlag == E_TERMINATE_PROCESS_GROUP)
00605 {
00606 result = WinUtils::killProcessGroup(pId);
00607 }
00608 else
00609 {
00610 result = WinUtils::killProcess(pId);
00611 }
00612
00613 if (result != ERROR_SUCCESS)
00614 {
00615 if (this->processStatus().terminated())
00616 {
00617 return true;
00618 }
00619 else
00620 {
00621 String msg;
00622 System::lastErrorMsg("Process::killProcess()", msg);
00623 BLOCXX_THROW_ERRNO_MSG(ProcessErrorException, msg);
00624 }
00625 }
00626
00627 return this->terminatesWithin(waitTime);
00628 }
00629
00630 ProcId Process::getCurProcessId()
00631 {
00632 return GetCurrentProcess();
00633 }
00634
00635 #else
00636
00637 Process::Status pollStatusImpl(ProcId pid)
00638 {
00639 ProcId wpid;
00640 int status;
00641
00642 do
00643 {
00644
00645 wpid = ::waitpid(pid, &status, WNOHANG | WUNTRACED);
00646
00647 } while (wpid < 0 && errno == EINTR);
00648
00649 if (wpid < 0)
00650 {
00651 BLOCXX_THROW_ERRNO_MSG(ProcessErrorException, "waitpid() failed");
00652 }
00653 return Process::Status(wpid, status);
00654 }
00655
00656
00657
00658
00659
00660 bool Process::killWait(const Timeout& wait_time, int sig, char const * signame, ETerminationSelectionFlag terminationSelectionFlag)
00661 {
00662 ProcId killArg = terminationSelectionFlag == E_TERMINATE_PROCESS_GROUP ? -m_pid : m_pid;
00663 int errnum = m_impl->kill(killArg, sig);
00664 if (errnum != 0)
00665 {
00666
00667 if (this->processStatus().terminated())
00668 {
00669 return true;
00670 }
00671 else
00672 {
00673 Format fmt("Failed sending %1 to process %2.", signame, m_pid);
00674 char const * msg = fmt.c_str();
00675 errno = errnum;
00676 BLOCXX_THROW_ERRNO_MSG(ProcessErrorException, msg);
00677 }
00678 }
00679 return this->terminatesWithin(wait_time);
00680 }
00681
00682 ProcId Process::getCurProcessId()
00683 {
00684 return ::getpid();
00685 }
00686
00687 #endif
00688
00689 }