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;
}