static int posix_spawn_common()

in watchman/winbuild/posix_spawn.cpp [260:467]


static int posix_spawn_common(
    bool search_path,
    pid_t* pid,
    const char* path,
    const posix_spawn_file_actions_t* file_actions,
    const posix_spawnattr_t* attrp,
    char* const argv[],
    char* const envp[]) {
  auto sinfo = STARTUPINFOEX();
  auto sec = SECURITY_ATTRIBUTES();
  auto pinfo = PROCESS_INFORMATION();
  char* cmdbuf;
  char* env_block;
  DWORD create_flags = CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT;
  int ret;
  int i;
  HANDLE inherited_handles[3] = {0, 0, 0};

  cmdbuf = build_command_line(argv);
  if (!cmdbuf) {
    return ENOMEM;
  }

  env_block = make_env_block(envp);
  if (!env_block) {
    free(cmdbuf);
    return ENOMEM;
  }

  sinfo.StartupInfo.cb = sizeof(sinfo);
  sinfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  sinfo.StartupInfo.wShowWindow = SW_HIDE;

  sec.nLength = sizeof(sec);
  sec.bInheritHandle = TRUE;

  if (attrp->flags & POSIX_SPAWN_SETPGROUP) {
    create_flags |= CREATE_NEW_PROCESS_GROUP;
  }

  for (i = 0; i < file_actions->nacts; i++) {
    struct _posix_spawn_file_action* act = &file_actions->acts[i];
    HANDLE* target = NULL;

    switch (act->target_fd) {
      case 0:
        target = &sinfo.StartupInfo.hStdInput;
        break;
      case 1:
        target = &sinfo.StartupInfo.hStdOutput;
        break;
      case 2:
        target = &sinfo.StartupInfo.hStdError;
        break;
    }

    if (!target) {
      logf(ERR, "posix_spawn: can't target fd outside range [0-2]\n");
      ret = ENOSYS;
      goto done;
    }

    if (act->action != _posix_spawn_file_action::open_file) {
      // Process a dup(2) action
      DWORD err;

      if (*target) {
        CloseHandle(*target);
        *target = INVALID_HANDLE_VALUE;
      }

      if (act->action == _posix_spawn_file_action::dup_fd) {
        HANDLE src = NULL;
        switch (act->u.source_fd) {
          case 0:
            src = sinfo.StartupInfo.hStdInput;
            break;
          case 1:
            src = sinfo.StartupInfo.hStdOutput;
            break;
          case 2:
            src = sinfo.StartupInfo.hStdError;
            break;
        }
        if (!src) {
          src = (HANDLE)_get_osfhandle(act->u.source_fd);
        }
        act->u.dup_local_handle = intptr_t(src);
      }

      if (!DuplicateHandle(
              GetCurrentProcess(),
              (HANDLE)act->u.dup_local_handle,
              GetCurrentProcess(),
              target,
              0,
              TRUE,
              DUPLICATE_SAME_ACCESS)) {
        err = GetLastError();
        logf(
            ERR,
            "posix_spawn: failed to duplicate handle: {}\n",
            win32_strerror(err));
        ret = map_win32_err(err);
        goto done;
      }
    } else {
      // Process an open(2) action

      auto h = w_handle_open(
          act->u.open_info.name, act->u.open_info.flags & ~O_CLOEXEC);
      if (!h) {
        ret = errno;
        logf(
            ERR,
            "posix_spawn: failed to open {} into target fd {}: {}\n",
            act->u.open_info.name,
            act->target_fd,
            folly::errnoStr(ret));
        goto done;
      }

      if (*target) {
        CloseHandle(*target);
      }
      *target = (HANDLE)h.release();
    }
  }

  if (!sinfo.StartupInfo.hStdInput) {
    sinfo.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  }
  if (!sinfo.StartupInfo.hStdOutput) {
    sinfo.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  }
  if (!sinfo.StartupInfo.hStdError) {
    sinfo.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  }

  // Ensure that we only pass the stdio handles to the child.
  {
    SIZE_T size = 0;

    inherited_handles[0] = sinfo.StartupInfo.hStdInput;
    inherited_handles[1] = sinfo.StartupInfo.hStdOutput;
    inherited_handles[2] = sinfo.StartupInfo.hStdError;
    sinfo.lpAttributeList = NULL;

    InitializeProcThreadAttributeList(NULL, 1, 0, &size);
    sinfo.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)malloc(size);
    InitializeProcThreadAttributeList(sinfo.lpAttributeList, 1, 0, &size);
    UpdateProcThreadAttribute(
        sinfo.lpAttributeList,
        0,
        PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
        inherited_handles,
        3 * sizeof(HANDLE),
        NULL,
        NULL);
  }

  if (!CreateProcess(
          search_path ? NULL : path,
          cmdbuf,
          &sec,
          &sec,
          TRUE,
          create_flags,
          env_block,
          attrp->working_dir,
          &sinfo.StartupInfo,
          &pinfo)) {
    logf(
        ERR,
        "CreateProcess: `{}`: (cwd={}) {}\n",
        cmdbuf,
        attrp->working_dir ? attrp->working_dir : "<process cwd>",
        win32_strerror(GetLastError()));
    ret = EACCES;
  } else {
    *pid = (pid_t)pinfo.dwProcessId;

    // Record the pid -> handle mapping for later wait/reap
    child_procs.wlock()->emplace(pinfo.dwProcessId, pinfo.hProcess);

    CloseHandle(pinfo.hThread);
    ret = 0;
  }

  free(sinfo.lpAttributeList);

done:
  free(cmdbuf);
  free(env_block);

  // If we manufactured any handles, close them out now
  if (sinfo.StartupInfo.hStdInput != GetStdHandle(STD_INPUT_HANDLE)) {
    CloseHandle(sinfo.StartupInfo.hStdInput);
  }
  if (sinfo.StartupInfo.hStdOutput != GetStdHandle(STD_OUTPUT_HANDLE)) {
    CloseHandle(sinfo.StartupInfo.hStdOutput);
  }
  if (sinfo.StartupInfo.hStdError != GetStdHandle(STD_ERROR_HANDLE)) {
    CloseHandle(sinfo.StartupInfo.hStdError);
  }

  return ret;
}