Esempio n. 1
0
// 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);
    });
}
Esempio n. 2
0
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>());
}
Esempio n. 3
0
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;
}
Esempio n. 4
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());
}
Esempio n. 5
0
/** 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();
	}
}
Esempio n. 6
0
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();
}
Esempio n. 7
0
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();
}
Esempio n. 9
0
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());
    }
  }
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
// 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));
}
Esempio n. 12
0
void commandDiscarded(const Subprocess& s, const string& cmd)
{
  VLOG(1) << "'" << cmd << "' is being discarded";
  os::killtree(s.pid(), SIGKILL);
}
Esempio n. 13
0
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
}
Esempio n. 14
0
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();
        }
    }
}
Esempio n. 15
0
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;
}