michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: set ts=8 et sw=2 tw=80: michael@0: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "base/eintr_wrapper.h" michael@0: #include "base/message_loop.h" michael@0: #include "base/process_util.h" michael@0: michael@0: #include "chrome/common/process_watcher.h" michael@0: michael@0: // Maximum amount of time (in milliseconds) to wait for the process to exit. michael@0: // XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc michael@0: static const int kMaxWaitMs = 2000; michael@0: michael@0: namespace { michael@0: michael@0: bool michael@0: IsProcessDead(pid_t process) michael@0: { michael@0: bool exited = false; michael@0: // don't care if the process crashed, just if it exited michael@0: base::DidProcessCrash(&exited, process); michael@0: return exited; michael@0: } michael@0: michael@0: michael@0: class ChildReaper : public base::MessagePumpLibevent::SignalEvent, michael@0: public base::MessagePumpLibevent::SignalWatcher michael@0: { michael@0: public: michael@0: explicit ChildReaper(pid_t process) : process_(process) michael@0: { michael@0: } michael@0: michael@0: virtual ~ChildReaper() michael@0: { michael@0: // subclasses should have cleaned up |process_| already michael@0: DCHECK(!process_); michael@0: michael@0: // StopCatching() is implicit michael@0: } michael@0: michael@0: // @override michael@0: virtual void OnSignal(int sig) michael@0: { michael@0: DCHECK(SIGCHLD == sig); michael@0: DCHECK(process_); michael@0: michael@0: // this may be the SIGCHLD for a process other than |process_| michael@0: if (IsProcessDead(process_)) { michael@0: process_ = 0; michael@0: StopCatching(); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: void WaitForChildExit() michael@0: { michael@0: DCHECK(process_); michael@0: HANDLE_EINTR(waitpid(process_, NULL, 0)); michael@0: } michael@0: michael@0: pid_t process_; michael@0: michael@0: private: michael@0: DISALLOW_EVIL_CONSTRUCTORS(ChildReaper); michael@0: }; michael@0: michael@0: michael@0: // Fear the reaper michael@0: class ChildGrimReaper : public ChildReaper, michael@0: public Task michael@0: { michael@0: public: michael@0: explicit ChildGrimReaper(pid_t process) : ChildReaper(process) michael@0: { michael@0: } michael@0: michael@0: virtual ~ChildGrimReaper() michael@0: { michael@0: if (process_) michael@0: KillProcess(); michael@0: } michael@0: michael@0: // @override michael@0: virtual void Run() michael@0: { michael@0: // we may have already been signaled by the time this runs michael@0: if (process_) michael@0: KillProcess(); michael@0: } michael@0: michael@0: private: michael@0: void KillProcess() michael@0: { michael@0: DCHECK(process_); michael@0: michael@0: if (IsProcessDead(process_)) { michael@0: process_ = 0; michael@0: return; michael@0: } michael@0: michael@0: if (0 == kill(process_, SIGKILL)) { michael@0: // XXX this will block for whatever amount of time it takes the michael@0: // XXX OS to tear down the process's resources. might need to michael@0: // XXX rethink this if it proves expensive michael@0: WaitForChildExit(); michael@0: } michael@0: else { michael@0: CHROMIUM_LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!" michael@0: << "("<< errno << ")."; michael@0: } michael@0: process_ = 0; michael@0: } michael@0: michael@0: DISALLOW_EVIL_CONSTRUCTORS(ChildGrimReaper); michael@0: }; michael@0: michael@0: michael@0: class ChildLaxReaper : public ChildReaper, michael@0: public MessageLoop::DestructionObserver michael@0: { michael@0: public: michael@0: explicit ChildLaxReaper(pid_t process) : ChildReaper(process) michael@0: { michael@0: } michael@0: michael@0: virtual ~ChildLaxReaper() michael@0: { michael@0: // WillDestroyCurrentMessageLoop() should have reaped process_ already michael@0: DCHECK(!process_); michael@0: } michael@0: michael@0: // @override michael@0: virtual void OnSignal(int sig) michael@0: { michael@0: ChildReaper::OnSignal(sig); michael@0: michael@0: if (!process_) { michael@0: MessageLoop::current()->RemoveDestructionObserver(this); michael@0: delete this; michael@0: } michael@0: } michael@0: michael@0: // @override michael@0: virtual void WillDestroyCurrentMessageLoop() michael@0: { michael@0: DCHECK(process_); michael@0: michael@0: WaitForChildExit(); michael@0: process_ = 0; michael@0: michael@0: // XXX don't think this is necessary, since destruction can only michael@0: // be observed once, but can't hurt michael@0: MessageLoop::current()->RemoveDestructionObserver(this); michael@0: delete this; michael@0: } michael@0: michael@0: private: michael@0: DISALLOW_EVIL_CONSTRUCTORS(ChildLaxReaper); michael@0: }; michael@0: michael@0: } // namespace michael@0: michael@0: michael@0: /** michael@0: * Do everything possible to ensure that |process| has been reaped michael@0: * before this process exits. michael@0: * michael@0: * |grim| decides how strict to be with the child's shutdown. michael@0: * michael@0: * | child exit timeout | upon parent shutdown: michael@0: * +--------------------+---------------------------------- michael@0: * force=true | 2 seconds | kill(child, SIGKILL) michael@0: * force=false | infinite | waitpid(child) michael@0: * michael@0: * If a child process doesn't shut down properly, and |grim=false| michael@0: * used, then the parent will wait on the child forever. So, michael@0: * |force=false| is expected to be used when an external entity can be michael@0: * responsible for terminating hung processes, e.g. automated test michael@0: * harnesses. michael@0: */ michael@0: void michael@0: ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process, michael@0: bool force) michael@0: { michael@0: DCHECK(process != base::GetCurrentProcId()); michael@0: DCHECK(process > 0); michael@0: michael@0: if (IsProcessDead(process)) michael@0: return; michael@0: michael@0: MessageLoopForIO* loop = MessageLoopForIO::current(); michael@0: if (force) { michael@0: ChildGrimReaper* reaper = new ChildGrimReaper(process); michael@0: michael@0: loop->CatchSignal(SIGCHLD, reaper, reaper); michael@0: // |loop| takes ownership of |reaper| michael@0: loop->PostDelayedTask(FROM_HERE, reaper, kMaxWaitMs); michael@0: } else { michael@0: ChildLaxReaper* reaper = new ChildLaxReaper(process); michael@0: michael@0: loop->CatchSignal(SIGCHLD, reaper, reaper); michael@0: // |reaper| destroys itself after destruction notification michael@0: loop->AddDestructionObserver(reaper); michael@0: } michael@0: }