blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2005, Quest Software, Inc. All rights reserved. 00003 * Copyright (C) 2006, Novell, Inc. All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * * Redistributions of source code must retain the above copyright notice, 00009 * this list of conditions and the following disclaimer. 00010 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Quest Software, Inc., 00015 * nor Novell, Inc., 00016 * nor Network Associates, 00017 * nor the names of its contributors or employees may be used to 00018 * endorse or promote products derived from this software without 00019 * specific prior written permission. 00020 * 00021 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00022 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00023 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00024 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00025 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00026 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00027 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00028 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00029 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00030 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00031 * POSSIBILITY OF SUCH DAMAGE. 00032 *******************************************************************************/ 00033 00034 00040 #include "blocxx/BLOCXX_config.h" 00041 00042 #include "blocxx/DateTime.hpp" 00043 #include "blocxx/Exec.hpp" 00044 // To get ExecErrorException declaration 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 // We want to use the function instead of the macro (for scoping reasons). 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 // This function is called by both 00094 // ProcessChildImpl::pollStatus and WaitpidThreadFix::waitPid 00095 Process::Status pollStatusImpl(ProcId pid); 00096 00097 BLOCXX_DEFINE_EXCEPTION(ProcessError); 00098 00099 // -------------------- Process::Status --------------------------- 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 } // end unnamed namespace 00321 00322 00323 // --- Process --- 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 // Make a last ditch attempt to prevent zombies. 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 // Make a last ditch attempt to prevent zombies. 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 // m_pid tested in case this method is called inappropriately 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) // safety check in case called inappropriately 00453 { 00454 return; 00455 } 00456 00457 processStatus(); // update m_status 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 // Close the streams. If the child process is blocked waiting to output, 00481 // then this will cause it to get a SIGPIPE (or ERROR_BROKEN_PIPE on Windows), 00482 // and it may be able to clean up after itself. Likewise, if the child process 00483 // is blocked waiting for input, it will now detect EOF. 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 // Give it a full minute to make sure we don't leave zombies hanging around 00502 // if the system is heavily loaded 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 // Give it a full minute to make sure we don't leave zombies hanging around 00516 // if the system is heavily loaded 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 // Waits wait_time at most wait_time seconds for process to terminate, setting 00527 // m_status. 00528 // RETURNS: whether or not process terminated. 00529 // 00530 bool Process::terminatesWithin(const Timeout& wait_time) 00531 { 00532 float const mult = 1.20; 00533 float const max_period = 5000.0; // milliseconds 00534 float period = 100.0; // milliseconds 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 //------------------ Platform-dependent methods -------------------------- 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 // Sends a defined message to a process hoping that the process knows it and 00575 // will be able to terminate itself 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 // Use WUNTRACED so that we can detect if process stopped 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 // Sends signal sig to child process and waits wait_time seconds for it 00657 // to terminate. If an error occurs, signame is used in constructing the 00658 // error message. 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 // maybe kill() failed because child terminated first 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 } // namespace BLOCXX_NAMESPACE