// Wait for a subprocess and test the status code for the following // conditions of 'expected_status': // 1. 'None' = Anything but '0'. // 2. 'Some' = the value of 'expected_status'. // Returns Nothing if the resulting status code matches the // expectation otherwise a Failure with the output of the subprocess. // TODO(jmlvanre): Turn this into a generally useful abstraction for // gtest where we can have a more straigtforward 'expected_status'. Future<Nothing> await_subprocess( const Subprocess& subprocess, const Option<int>& expected_status = None()) { // Dup the pipe fd of the subprocess so we can read the output if // needed. int out = dup(subprocess.out().get()); // Once we get the status of the process. return subprocess.status() .then([=](const Option<int>& status) -> Future<Nothing> { // If the status is not set, fail out. if (status.isNone()) { return Failure("Subprocess status is none"); } // If the status is not what we expect then fail out with the // output of the subprocess. The failure message will include // the assertion failures of the subprocess. if ((expected_status.isSome() && status.get() != expected_status.get()) || (expected_status.isNone() && status.get() == 0)) { return io::read(out) .then([](const string& output) -> Future<Nothing> { return Failure("\n[++++++++++] Subprocess output.\n" + output + "[++++++++++]\n"); }); } // If the subprocess ran successfully then return nothing. return Nothing(); }).onAny([=]() { os::close(out); }); }
std::pair<bool, std::vector<pbxbuild::Tool::Invocation>> SimpleExecutor:: performInvocations( Filesystem *filesystem, pbxproj::PBX::Target::shared_ptr const &target, pbxbuild::Target::Environment const &targetEnvironment, std::vector<pbxbuild::Tool::Invocation> const &orderedInvocations, bool createProductStructure) { for (pbxbuild::Tool::Invocation const &invocation : orderedInvocations) { // TODO(grp): This should perhaps be a separate flag for a 'phony' invocation. if (invocation.executable().path().empty()) { continue; } if (invocation.createsProductStructure() != createProductStructure) { continue; } std::map<std::string, std::string> sortedEnvironment = std::map<std::string, std::string>(invocation.environment().begin(), invocation.environment().end()); xcformatter::Formatter::Print(_formatter->beginInvocation(invocation, invocation.executable().displayName(), createProductStructure)); if (!_dryRun) { for (std::string const &output : invocation.outputs()) { std::string directory = FSUtil::GetDirectoryName(output); if (!filesystem->createDirectory(directory)) { return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation })); } } if (!invocation.executable().builtin().empty()) { /* For built-in tools, run them in-process. */ std::shared_ptr<builtin::Driver> driver = _builtins.driver(invocation.executable().builtin()); if (driver == nullptr) { xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure)); return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation })); } if (driver->run(invocation.arguments(), invocation.environment(), filesystem, invocation.workingDirectory()) != 0) { xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure)); return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation })); } } else { /* External tool, run the tool externally. */ Subprocess process; if (!process.execute(invocation.executable().path(), invocation.arguments(), invocation.environment(), invocation.workingDirectory()) || process.exitcode() != 0) { xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure)); return std::make_pair(false, std::vector<pbxbuild::Tool::Invocation>({ invocation })); } } } xcformatter::Formatter::Print(_formatter->finishInvocation(invocation, invocation.executable().displayName(), createProductStructure)); } return std::make_pair(true, std::vector<pbxbuild::Tool::Invocation>()); }
int main(int argc, char *argv[]) { std::vector<std::string> args; Subprocess proc = Subprocess(args); proc.run(); for (int i = 0; i < 20; ++i) proc.poll(); return 0; }
TEST(SubprocessSet, Single) { SubprocessSet subprocs_; Subprocess* ls = new Subprocess(&subprocs_); EXPECT_TRUE(ls->Start("cmd.exe /c DIR C:\\")); subprocs_.Add(ls); while (!ls->done()) { subprocs_.DoWork(); } ASSERT_TRUE(ls->Finish()); ASSERT_NE("", ls->stdout_.buf_); ASSERT_EQ(1, subprocs_.finished_.size()); }
/** Runs the process and waits until it's finished. * @param[out] out takes the output written to stdout by the executed subprocess * @return true if process terminated properly * @throw SignalException if CTRL-C was pressed during execution */ bool Process::run (string *out) { Subprocess subprocess; if (!subprocess.run(_cmd, _paramstr)) return false; for (;;) { if (out) { out->clear(); subprocess.readFromPipe(*out); } Subprocess::State state = subprocess.state(); if (state != Subprocess::State::RUNNING) return state == Subprocess::State::FINISHED; SignalHandler::instance().check(); } }
static Future<Nothing> _checkError(const string& cmd, const Subprocess& s) { Option<int> status = s.status().get(); if (status.isNone()) { return Failure("No status found for '" + cmd + "'"); } if (status.get() != 0) { // TODO(tnachen): Consider returning stdout as well. CHECK_SOME(s.err()); return io::read(s.err().get()) .then(lambda::bind(failure<Nothing>, cmd, status.get(), lambda::_1)); } return Nothing(); }
Future<Version> Docker::_version(const string& cmd, const Subprocess& s) { const Option<int>& status = s.status().get(); if (status.isNone() || status.get() != 0) { string msg = "Failed to execute '" + cmd + "': "; if (status.isSome()) { msg += WSTRINGIFY(status.get()); } else { msg += "unknown exit status"; } return Failure(msg); } CHECK_SOME(s.out()); return io::read(s.out().get()) .then(lambda::bind(&Docker::__version, lambda::_1)); }
int StartSubprocess(const char *name, SubprocessType type, SubprocessProtocol protocol) { D("starting %s subprocess (protocol=%s): '%s'", type == SubprocessType::kRaw ? "raw" : "PTY", protocol == SubprocessProtocol::kNone ? "none" : "shell", name); Subprocess* subprocess = new Subprocess(name, type, protocol); if (!subprocess) { LOG(ERROR) << "failed to allocate new subprocess"; return -1; } if (!subprocess->ForkAndExec()) { LOG(ERROR) << "failed to start subprocess"; delete subprocess; return -1; } D("subprocess creation successful: local_socket_fd=%d, pid=%d", subprocess->local_socket_fd(), subprocess->pid()); return subprocess->local_socket_fd(); }
void SubprocessSet::DoWork() { DWORD numbytesread; Subprocess* subproc; LPOVERLAPPED overlapped; BOOL ok = GetQueuedCompletionStatus(g_ioport, &numbytesread, (PULONG_PTR) &subproc, &overlapped, INFINITE); assert(GetLastError() == ERROR_BROKEN_PIPE || ok); assert(&subproc->stdout_.overlapped_ == overlapped || &subproc->stderr_.overlapped_ == overlapped ); Subprocess::Stream* stream; if (overlapped == &subproc->stdout_.overlapped_) stream = &subproc->stdout_; else stream = &subproc->stderr_; stream->ProcessInput(ok ? numbytesread : 0); if (subproc->done()) { vector<Subprocess*>::iterator end = std::remove(running_.begin(), running_.end(), subproc); if (running_.end() != end) { finished_.push(subproc); running_.resize(end - running_.begin()); } } }
static bool CopyPath(std::string const &inputPath, std::string const &outputPath) { if (!FSUtil::CreateDirectory(FSUtil::GetDirectoryName(outputPath))) { return false; } Subprocess cp; if (!cp.execute("/bin/cp", { "-R", inputPath, outputPath }) || cp.exitcode() != 0) { return false; } /* Should preserve permissions but make writable. */ Subprocess chmod; if (!chmod.execute("/bin/chmod", { "-R", "+w", outputPath }) || chmod.exitcode() != 0) { return false; } return true; }
// Returns a failure if no status or non-zero status returned from // subprocess. static Future<Nothing> checkError(const string& cmd, const Subprocess& s) { return s.status() .then(lambda::bind(_checkError, cmd, s)); }
void commandDiscarded(const Subprocess& s, const string& cmd) { VLOG(1) << "'" << cmd << "' is being discarded"; os::killtree(s.pid(), SIGKILL); }
void Subprocess::cb_child_watch(GPid pid, gint status, gpointer data) { Subprocess *context = static_cast<Subprocess*>(data); context->setRunning(false); // g_spawn_close_pid(pid); // useless on POSIX }
int main(int argc, char **argv) { std::vector<std::string> args = std::vector<std::string>(argv + 1, argv + argc); /* * Parse out the options, or print help & exit. */ Options options; std::pair<bool, std::string> result = libutil::Options::Parse<Options>(&options, args); if (!result.first) { return Help(result.second); } /* * Handle the basic options that don't need SDKs. */ if (options.tool().empty()) { if (options.help()) { return Help(); } else if (options.version()) { return Version(); } } /* * Parse fallback options from the environment. */ std::string toolchainsInput = options.toolchain(); if (toolchainsInput.empty()) { if (char const *toolchains = getenv("TOOLCHAINS")) { toolchainsInput = std::string(toolchains); } } std::string SDK = options.SDK(); if (SDK.empty()) { if (char const *sdkroot = getenv("SDKROOT")) { SDK = std::string(sdkroot); } else { /* Default SDK. */ SDK = "macosx"; } } bool verbose = options.verbose() || getenv("xcrun_verbose") != NULL; bool log = options.log() || getenv("xcrun_log") != NULL; bool nocache = options.noCache() || getenv("xcrun_nocache") != NULL; /* * Warn about unhandled arguments. */ if (nocache || options.killCache()) { fprintf(stderr, "warning: cache options not implemented\n"); } /* * Create filesystem. */ auto filesystem = std::unique_ptr<Filesystem>(new DefaultFilesystem()); /* * Load the SDK manager from the developer root. */ ext::optional<std::string> developerRoot = xcsdk::Environment::DeveloperRoot(filesystem.get()); if (!developerRoot) { fprintf(stderr, "error: unable to find developer root\n"); return -1; } auto configuration = xcsdk::Configuration::Load(filesystem.get(), xcsdk::Configuration::DefaultPaths()); auto manager = xcsdk::SDK::Manager::Open(filesystem.get(), *developerRoot, configuration); if (manager == nullptr) { fprintf(stderr, "error: unable to load manager from '%s'\n", developerRoot->c_str()); return -1; } if (verbose) { fprintf(stderr, "verbose: using developer root '%s'\n", manager->path().c_str()); } /* * Determine the SDK to use. */ xcsdk::SDK::Target::shared_ptr target = manager->findTarget(SDK); if (target == nullptr) { fprintf(stderr, "error: unable to find sdk '%s'\n", SDK.c_str()); return -1; } if (verbose) { fprintf(stderr, "verbose: using sdk '%s': %s\n", target->canonicalName().c_str(), target->path().c_str()); } /* * Determine the toolchains to use. Default to the SDK's toolchains. */ xcsdk::SDK::Toolchain::vector toolchains; if (!toolchainsInput.empty()) { /* If the custom toolchain exists, use it instead. */ std::vector<std::string> toolchainTokens = pbxsetting::Type::ParseList(toolchainsInput); for (std::string const &toolchainToken : toolchainTokens) { if (auto TC = manager->findToolchain(toolchainToken)) { toolchains.push_back(TC); } } if (toolchains.empty()) { fprintf(stderr, "error: unable to find toolchains in '%s'\n", toolchainsInput.c_str()); return -1; } } else { toolchains = target->toolchains(); } if (toolchains.empty()) { fprintf(stderr, "error: unable to find any toolchains\n"); return -1; } if (verbose) { fprintf(stderr, "verbose: using toolchain(s):"); for (xcsdk::SDK::Toolchain::shared_ptr const &toolchain : toolchains) { fprintf(stderr, " '%s'", toolchain->identifier().c_str()); } fprintf(stderr, "\n"); } /* * Perform actions. */ if (options.showSDKPath()) { printf("%s\n", target->path().c_str()); return 0; } else if (options.showSDKVersion()) { printf("%s\n", target->path().c_str()); return 0; } else if (options.showSDKBuildVersion()) { if (auto product = target->product()) { printf("%s\n", product->buildVersion().c_str()); return 0; } else { fprintf(stderr, "error: sdk has no build version\n"); return -1; } } else if (options.showSDKPlatformPath()) { if (auto platform = target->platform()) { printf("%s\n", platform->path().c_str()); } else { fprintf(stderr, "error: sdk has no platform\n"); return -1; } } else if (options.showSDKPlatformVersion()) { if (auto platform = target->platform()) { printf("%s\n", platform->version().c_str()); } else { fprintf(stderr, "error: sdk has no platform\n"); return -1; } } else { if (options.tool().empty()) { return Help("no tool provided"); } /* * Collect search paths for the tool. Can be in toolchains, target, developer root, or default paths. */ std::vector<std::string> executablePaths = target->executablePaths(toolchains); std::vector<std::string> defaultExecutablePaths = FSUtil::GetExecutablePaths(); executablePaths.insert(executablePaths.end(), defaultExecutablePaths.begin(), defaultExecutablePaths.end()); /* * Find the tool to execute. */ ext::optional<std::string> executable = filesystem->findExecutable(options.tool(), executablePaths); if (!executable) { fprintf(stderr, "error: tool '%s' not found\n", options.tool().c_str()); return 1; } if (verbose) { fprintf(stderr, "verbose: resolved tool '%s' to: %s\n", options.tool().c_str(), executable->c_str()); } if (options.find()) { /* * Just find the tool; i.e. print its path. */ printf("%s\n", executable->c_str()); return 0; } else { /* Run is the default. */ /* * Update effective environment to include the target path. */ std::unordered_map<std::string, std::string> environment = SysUtil::EnvironmentVariables(); environment["SDKROOT"] = target->path(); if (log) { printf("env SDKROOT=%s %s\n", target->path().c_str(), executable->c_str()); } /* * Execute the process! */ if (verbose) { printf("verbose: executing tool: %s\n", executable->c_str()); } Subprocess process; if (!process.execute(*executable, options.args(), environment)) { fprintf(stderr, "error: unable to execute tool '%s'\n", options.tool().c_str()); return -1; } return process.exitcode(); } } }
int ServerApplication::init(int argc, char *argv[]) { // Hide child's output if (hasFeature(FEATURE_SERVER) && options["child"].toBoolean()) options["log-to-screen"].setDefault(false); int ret = Application::init(argc, argv); if (ret == -1) return -1; if (!hasFeature(FEATURE_SERVER)) return ret; if (!options["child"].toBoolean()) { #ifndef _WIN32 if (options["fork"].toBoolean()) { // Note, all threads must be stopped before daemonize() forks and // SignalManager runs in a thread. SignalManager::instance().setEnabled(false); SystemUtilities::daemonize(); SignalManager::instance().setEnabled(true); } #endif if (options["pid"].toBoolean()) { // Try to acquire an exclusive lock new ProcessLock(options["pid-file"], 10); // OK to leak LOG_INFO(1, "Acquired exclusive lock on " << options["pid-file"]); } } #ifndef _WIN32 try { if (options["run-as"].hasValue()) { LOG_INFO(1, "Switching to user " << options["run-as"]); SystemUtilities::setUser(options["run-as"]); } } CBANG_CATCH_ERROR; #endif if (!options["child"].toBoolean() && options["respawn"].toBoolean()) { #ifndef _WIN32 // Child restart handler SignalManager::instance().addHandler(SIGUSR1, this); #endif // Setup child arguments vector<string> args; args.push_back(argv[0]); args.push_back((char *)"--child"); args.push_back((char *)"--lifeline"); args.push_back(String(SystemUtilities::getPID())); args.insert(args.end(), argv + 1, argv + argc); unsigned lastRespawn = 0; unsigned respawnCount = 0; while (!shouldQuit()) { // Check respawn rate unsigned now = Time::now(); if (now - lastRespawn < 60) { if (++respawnCount == 5) { LOG_ERROR("Respawning too fast. Exiting."); break; } } else respawnCount = 0; lastRespawn = now; // Spawn child Subprocess child; child.exec(args, Subprocess::NULL_STDOUT | Subprocess::NULL_STDERR); uint64_t killTime = 0; while (child.isRunning()) { if (!killTime && (restartChild || shouldQuit())) { LOG_INFO(1, "Shutting down child at PID=" << child.getPID()); child.interrupt(); restartChild = false; killTime = Time::now() + 300; // Give it 5 mins to shutdown } if (killTime && killTime < Time::now()) { LOG_INFO(1, "Child failed to shutdown cleanly, killing"); killTime = 0; child.kill(); } Timer::sleep(0.1); } LOG_INFO(1, "Child exited with return code " << child.getReturnCode()); } exit(0); } return ret; }