ProcessManagerImpl::ProcessManagerImpl(Application& app) : mApp(app) , mSigChild(app.getClock().getIOService(), SIGCHLD) , mImplsSize(app.getMetrics().NewCounter({"process", "memory", "handles"})) { std::lock_guard<std::recursive_mutex> guard(gImplsMutex); startSignalWait(); }
void processRequest() { std::cout << "Incoming request message: " << message_ << std::endl; signal_.reset(); std::cout << "Setting the signal set" << std::endl; signal_ = boost::shared_ptr<boost::asio::signal_set>(new boost::asio::signal_set(requestService_, SIGCHLD)); std::cout << "Informing the io_services of the pending fork" << std::endl; requestService_.notify_fork(boost::asio::io_service::fork_prepare); responseService_.notify_fork(boost::asio::io_service::fork_prepare); pid_t pid = fork(); if (pid == 0) { // this is the child process std::cout << "[child] Informing the child io_services of the completed fork" << std::endl; requestService_.notify_fork(boost::asio::io_service::fork_child); responseService_.notify_fork(boost::asio::io_service::fork_child); std::cout << "[child] Canceling the signal on the child" << std::endl; signal_->cancel(); std::cout << "[child] Exec-ing the new process" << std::endl; const char* cmd = "/bin/sleep"; const char* arg1 = "5"; execl(cmd, cmd, arg1, NULL); // the execl call only returns if an error has occurred, // so exit immediately exit(-1); } else { // we are the parent (or there was an error in fork) std::cout << "[parent] Informing the parent io_services of the completed fork" << std::endl; requestService_.notify_fork(boost::asio::io_service::fork_parent); responseService_.notify_fork(boost::asio::io_service::fork_parent); if (pid > 0) { std::cout << "[parent] Capturing the child pid (" << pid << ")" << std::endl; childPid_ = pid; startSignalWait(); } } }
void handleSignal(const boost::system::error_code& ec) { std::cout << "handling the SIGCHLD signal" << std::endl; int status; if (waitpid(childPid_, &status, 0) > 0) { std::cout << "got a signal on the child" << std::endl; int exitCode = -1; if (WIFEXITED(status)) { exitCode = WEXITSTATUS(status); } std::cout << "posting the response back" << std::endl; boost::shared_ptr<Response> response(boost::shared_ptr<Response>(new Response(exitCode))); responseService_.post(boost::bind(&Response::processResponse, response)); } else { startSignalWait(); } }
void ProcessManagerImpl::handleSignalWait() { std::lock_guard<std::recursive_mutex> guard(gImplsMutex); for (;;) { int status = 0; int pid = waitpid(-1, &status, WNOHANG); if (pid > 0) { auto pair = gImpls.find(pid); assert(pair != gImpls.end()); auto impl = pair->second; asio::error_code ec; if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) { CLOG(DEBUG, "Process") << "process " << pid << " exited " << WEXITSTATUS(status) << ": " << impl->mCmdLine; } else { CLOG(WARNING, "Process") << "process " << pid << " exited " << WEXITSTATUS(status) << ": " << impl->mCmdLine; } #ifdef __linux__ // Linux posix_spawnp does not fault on file-not-found in the // parent process at the point of invocation, as BSD does; so // rather than a fatal error / throw we get an ambiguous and // easily-overlooked shell-like 'exit 127' on waitpid. if (WEXITSTATUS(status) == 127) { CLOG(WARNING, "Process") << ""; CLOG(WARNING, "Process") << "************"; CLOG(WARNING, "Process") << ""; CLOG(WARNING, "Process") << " likely 'missing command':"; CLOG(WARNING, "Process") << ""; CLOG(WARNING, "Process") << " " << impl->mCmdLine; CLOG(WARNING, "Process") << ""; CLOG(WARNING, "Process") << "************"; CLOG(WARNING, "Process") << ""; } #endif // FIXME: this doesn't _quite_ do the right thing; it conveys // the exit status back to the caller but it puts it in "system // category" which on POSIX means if you call .message() on it // you'll get perror(value()), which is not correct. Errno has // nothing to do with process exit values. We could make a new // error_category to tighten this up, but it's a bunch of work // just to convey the meaningless string "exited" to the user. ec = asio::error_code(WEXITSTATUS(status), asio::system_category()); } else { // FIXME: for now we also collapse all non-WIFEXITED exits on // posix into a single "exit 1" error_code. This is enough // for most callers; we can enrich it if anyone really wants // to differentiate various signals that might have killed // the child. ec = asio::error_code(1, asio::system_category()); } --gNumProcessesActive; gImpls.erase(pair); *(impl->mOuterEc) = ec; impl->mOuterTimer->cancel(); } else { break; } } mImplsSize.set_count(gImpls.size()); maybeRunPendingProcesses(); startSignalWait(); }