blocxx

Process.cpp

Go to the documentation of this file.
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