int PW_MAIN()

in util/process_wrapper/process_wrapper.cc [29:225]


int PW_MAIN(int argc, const CharType* argv[], const CharType* envp[]) {
  using namespace process_wrapper;

  System::EnvironmentBlock environment_block;
  // Taking all environment variables from the current process
  // and sending them down to the child process
  for (int i = 0; envp[i] != nullptr; ++i) {
    environment_block.push_back(envp[i]);
  }

  System::EnvironmentBlock environment_file_block;

  using Subst = std::pair<System::StrType, System::StrType>;

  System::StrType exec_path;
  std::vector<Subst> subst_mappings;
  std::vector<Subst> stamp_mappings;
  System::StrType volatile_status_file;
  System::StrType stdout_file;
  System::StrType stderr_file;
  System::StrType touch_file;
  System::StrType copy_source;
  System::StrType copy_dest;
  System::Arguments arguments;
  System::Arguments file_arguments;

  // Processing current process argument list until -- is encountered
  // everthing after gets sent down to the child process
  for (int i = 1; i < argc; ++i) {
    System::StrType arg = argv[i];
    if (++i == argc) {
      std::cerr << "process wrapper error: argument \"" << ToUtf8(arg)
                << "\" missing parameter.\n";
      return -1;
    }
    if (arg == PW_SYS_STR("--subst")) {
      System::StrType subst = argv[i];
      size_t equal_pos = subst.find('=');
      if (equal_pos == std::string::npos) {
        std::cerr << "process wrapper error: wrong substituion format for \""
                  << ToUtf8(subst) << "\".\n";
        return -1;
      }
      System::StrType key = subst.substr(0, equal_pos);
      if (key.empty()) {
        std::cerr << "process wrapper error: empty key for substituion \""
                  << ToUtf8(subst) << "\".\n";
        return -1;
      }
      System::StrType value = subst.substr(equal_pos + 1, subst.size());
      if (value == PW_SYS_STR("${pwd}")) {
        value = System::GetWorkingDirectory();
      }
      subst_mappings.push_back({std::move(key), std::move(value)});
    } else if (arg == PW_SYS_STR("--volatile-status-file")) {
      if (!volatile_status_file.empty()) {
        std::cerr << "process wrapper error: \"--volatile-status-file\" can "
                     "only appear once.\n";
        return -1;
      }
      if (!ReadStampStatusToArray(argv[i], stamp_mappings)) {
        return -1;
      }
    } else if (arg == PW_SYS_STR("--env-file")) {
      if (!ReadFileToArray(argv[i], environment_file_block)) {
        return -1;
      }
    } else if (arg == PW_SYS_STR("--arg-file")) {
      if (!ReadFileToArray(argv[i], file_arguments)) {
        return -1;
      }
    } else if (arg == PW_SYS_STR("--touch-file")) {
      if (!touch_file.empty()) {
        std::cerr << "process wrapper error: \"--touch-file\" can only appear "
                     "once.\n";
        return -1;
      }
      touch_file = argv[i];
    } else if (arg == PW_SYS_STR("--copy-output")) {
      // i is already at the first arg position, accountint we need another arg
      // and then -- executable_name.
      if (i + 1 > argc) {
        std::cerr
            << "process wrapper error: \"--copy-output\" needs 2 parameters.\n";
        return -1;
      }
      if (!copy_source.empty() || !copy_dest.empty()) {
        std::cerr << "process wrapper error: \"--copy-output\" can only appear "
                     "once.\n";
        return -1;
      }
      copy_source = argv[i];
      copy_dest = argv[++i];
      if (copy_source == copy_dest) {
        std::cerr << "process wrapper error: \"--copy-output\" source and dest "
                     "need to be different.\n";
        return -1;
      }
    } else if (arg == PW_SYS_STR("--stdout-file")) {
      if (!stdout_file.empty()) {
        std::cerr << "process wrapper error: \"--stdout-file\" can only appear "
                     "once.\n";
        return -1;
      }
      stdout_file = argv[i];
    } else if (arg == PW_SYS_STR("--stderr-file")) {
      if (!stderr_file.empty()) {
        std::cerr << "process wrapper error: \"--stderr-file\" can only appear "
                     "once.\n";
        return -1;
      }
      stderr_file = argv[i];
    } else if (arg == PW_SYS_STR("--")) {
      exec_path = argv[i];
      for (++i; i < argc; ++i) {
        arguments.push_back(argv[i]);
      }
      // after we consume all arguments we append the files arguments
      for (const System::StrType& file_arg : file_arguments) {
        arguments.push_back(file_arg);
      }
    } else {
      std::cerr << "process wrapper error: unknow argument \"" << ToUtf8(arg)
                << "\"." << '\n';
      return -1;
    }
  }

  // Stamp any format string in an environment variable block
  for (const Subst& stamp : stamp_mappings) {
    System::StrType token = PW_SYS_STR("{");
    token += stamp.first;
    token.push_back('}');
    for (System::StrType& env : environment_file_block) {
      ReplaceToken(env, token, stamp.second);
    }
  }

  // Join environment variables arrays
  environment_block.reserve(environment_block.size() +
                            environment_file_block.size());
  environment_block.insert(environment_block.end(),
                           environment_file_block.begin(),
                           environment_file_block.end());

  if (subst_mappings.size()) {
    for (const Subst& subst : subst_mappings) {
      System::StrType token = PW_SYS_STR("${");
      token += subst.first;
      token.push_back('}');
      for (System::StrType& arg : arguments) {
        ReplaceToken(arg, token, subst.second);
      }

      for (System::StrType& env : environment_block) {
        ReplaceToken(env, token, subst.second);
      }
    }
  }

  // Have the last values added take precedence over the first.
  // This is simpler than needing to track duplicates and explicitly override
  // them.
  std::reverse(environment_block.begin(), environment_block.end());

  int exit_code = System::Exec(exec_path, arguments, environment_block,
                               stdout_file, stderr_file);
  if (exit_code == 0) {
    if (!touch_file.empty()) {
      std::ofstream file(touch_file);
      if (file.fail()) {
        std::cerr << "process wrapper error: failed to create touch file: \""
                  << ToUtf8(touch_file) << "\"\n";
        return -1;
      }
      file.close();
    }

    // we perform a copy of the output if necessary
    if (!copy_source.empty() && !copy_dest.empty()) {
      std::ifstream source(copy_source, std::ios::binary);
      if (source.fail()) {
        std::cerr << "process wrapper error: failed to open copy source: \""
                  << ToUtf8(copy_source) << "\"\n";
        return -1;
      }
      std::ofstream dest(copy_dest, std::ios::binary);
      if (dest.fail()) {
        std::cerr << "process wrapper error: failed to open copy dest: \""
                  << ToUtf8(copy_dest) << "\"\n";
        return -1;
      }
      dest << source.rdbuf();
    }
  }
  return exit_code;
}