process: Port child process spawning to the Windows platform
This commit is contained in:
@@ -220,13 +220,295 @@ process_t* process_start(char *const argv[], char *const envp[],
|
||||
|
||||
#else /* WIN32 */
|
||||
|
||||
/**
|
||||
* Private data of an process_t object.
|
||||
*/
|
||||
struct private_process_t {
|
||||
|
||||
/**
|
||||
* Public process_t interface.
|
||||
*/
|
||||
process_t public;
|
||||
|
||||
/**
|
||||
* child stdin pipe
|
||||
*/
|
||||
HANDLE in[PIPE_ENDS];
|
||||
|
||||
/**
|
||||
* child stdout pipe
|
||||
*/
|
||||
HANDLE out[PIPE_ENDS];
|
||||
|
||||
/**
|
||||
* child stderr pipe
|
||||
*/
|
||||
HANDLE err[PIPE_ENDS];
|
||||
|
||||
/**
|
||||
* child process information
|
||||
*/
|
||||
PROCESS_INFORMATION pi;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clean up state associated to child process
|
||||
*/
|
||||
static void process_destroy(private_process_t *this)
|
||||
{
|
||||
if (this->in[PIPE_READ])
|
||||
{
|
||||
CloseHandle(this->in[PIPE_READ]);
|
||||
}
|
||||
if (this->in[PIPE_WRITE])
|
||||
{
|
||||
CloseHandle(this->in[PIPE_WRITE]);
|
||||
}
|
||||
if (this->out[PIPE_READ])
|
||||
{
|
||||
CloseHandle(this->out[PIPE_READ]);
|
||||
}
|
||||
if (this->out[PIPE_WRITE])
|
||||
{
|
||||
CloseHandle(this->out[PIPE_WRITE]);
|
||||
}
|
||||
if (this->err[PIPE_READ])
|
||||
{
|
||||
CloseHandle(this->err[PIPE_READ]);
|
||||
}
|
||||
if (this->err[PIPE_WRITE])
|
||||
{
|
||||
CloseHandle(this->err[PIPE_WRITE]);
|
||||
}
|
||||
if (this->pi.hProcess)
|
||||
{
|
||||
CloseHandle(this->pi.hProcess);
|
||||
CloseHandle(this->pi.hThread);
|
||||
}
|
||||
free(this);
|
||||
}
|
||||
|
||||
METHOD(process_t, wait_, bool,
|
||||
private_process_t *this, int *code)
|
||||
{
|
||||
DWORD ec;
|
||||
|
||||
if (WaitForSingleObject(this->pi.hProcess, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
DBG1(DBG_LIB, "waiting for child process failed: 0x%08x",
|
||||
GetLastError());
|
||||
process_destroy(this);
|
||||
return FALSE;
|
||||
}
|
||||
if (code)
|
||||
{
|
||||
if (!GetExitCodeProcess(this->pi.hProcess, &ec))
|
||||
{
|
||||
DBG1(DBG_LIB, "getting child process exit code failed: 0x%08x",
|
||||
GetLastError());
|
||||
process_destroy(this);
|
||||
return FALSE;
|
||||
}
|
||||
*code = ec;
|
||||
}
|
||||
process_destroy(this);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a command line argument to buf, optionally quoted
|
||||
*/
|
||||
static void append_arg(char *buf, u_int len, char *arg, char *quote)
|
||||
{
|
||||
char *space = "";
|
||||
int current;
|
||||
|
||||
current = strlen(buf);
|
||||
if (current)
|
||||
{
|
||||
space = " ";
|
||||
}
|
||||
snprintf(buf + current, len - current, "%s%s%s%s", space, quote, arg, quote);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a null-terminate env string to buf
|
||||
*/
|
||||
static void append_env(char *buf, u_int len, char *env)
|
||||
{
|
||||
char *pos = buf;
|
||||
int current;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
pos += strlen(pos);
|
||||
if (!pos[1])
|
||||
{
|
||||
if (pos == buf)
|
||||
{
|
||||
current = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = pos - buf + 1;
|
||||
}
|
||||
snprintf(buf + current, len - current, "%s", env);
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
process_t* process_start(char *const argv[], char *const envp[],
|
||||
int *in, int *out, int *err, bool close_all)
|
||||
{
|
||||
return NULL;
|
||||
private_process_t *this;
|
||||
char arg[32768], env[32768];
|
||||
SECURITY_ATTRIBUTES sa = {
|
||||
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||
.bInheritHandle = TRUE,
|
||||
};
|
||||
STARTUPINFO sui = {
|
||||
.cb = sizeof(STARTUPINFO),
|
||||
};
|
||||
int i;
|
||||
|
||||
memset(arg, 0, sizeof(arg));
|
||||
memset(env, 0, sizeof(env));
|
||||
|
||||
for (i = 0; argv[i]; i++)
|
||||
{
|
||||
if (!strchr(argv[i], ' '))
|
||||
{ /* no spaces, fine for appending */
|
||||
append_arg(arg, sizeof(arg) - 1, argv[i], "");
|
||||
}
|
||||
else if (argv[i][0] == '"' &&
|
||||
argv[i][strlen(argv[i]) - 1] == '"' &&
|
||||
strchr(argv[i] + 1, '"') == argv[i] + strlen(argv[i]) - 1)
|
||||
{ /* already properly quoted */
|
||||
append_arg(arg, sizeof(arg) - 1, argv[i], "");
|
||||
}
|
||||
else if (strchr(argv[i], ' ') && !strchr(argv[i], '"'))
|
||||
{ /* spaces, but no quotes; append quoted */
|
||||
append_arg(arg, sizeof(arg) - 1, argv[i], "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG1(DBG_LIB, "invalid command line argument: %s", argv[i]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (envp)
|
||||
{
|
||||
for (i = 0; envp[i]; i++)
|
||||
{
|
||||
append_env(env, sizeof(env) - 1, envp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.wait = _wait_,
|
||||
},
|
||||
);
|
||||
|
||||
if (in)
|
||||
{
|
||||
sui.dwFlags = STARTF_USESTDHANDLES;
|
||||
if (!CreatePipe(&this->in[PIPE_READ], &this->in[PIPE_WRITE], &sa, 0))
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!SetHandleInformation(this->in[PIPE_WRITE], HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
sui.hStdInput = this->in[PIPE_READ];
|
||||
*in = _open_osfhandle((uintptr_t)this->in[PIPE_WRITE], 0);
|
||||
if (*in == -1)
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (out)
|
||||
{
|
||||
sui.dwFlags = STARTF_USESTDHANDLES;
|
||||
if (!CreatePipe(&this->out[PIPE_READ], &this->out[PIPE_WRITE], &sa, 0))
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!SetHandleInformation(this->out[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
sui.hStdOutput = this->out[PIPE_WRITE];
|
||||
*out = _open_osfhandle((uintptr_t)this->out[PIPE_READ], 0);
|
||||
if (*out == -1)
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
sui.dwFlags = STARTF_USESTDHANDLES;
|
||||
if (!CreatePipe(&this->err[PIPE_READ], &this->err[PIPE_WRITE], &sa, 0))
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
if (!SetHandleInformation(this->err[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
sui.hStdError = this->err[PIPE_WRITE];
|
||||
*err = _open_osfhandle((uintptr_t)this->err[PIPE_READ], 0);
|
||||
if (*err == -1)
|
||||
{
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CreateProcess(argv[0], arg, NULL, NULL, TRUE,
|
||||
NORMAL_PRIORITY_CLASS, env, NULL, &sui, &this->pi))
|
||||
{
|
||||
DBG1(DBG_LIB, "creating process '%s' failed: 0x%08x",
|
||||
argv[0], GetLastError());
|
||||
process_destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* close child process end of pipes */
|
||||
if (this->in[PIPE_READ])
|
||||
{
|
||||
CloseHandle(this->in[PIPE_READ]);
|
||||
this->in[PIPE_READ] = NULL;
|
||||
}
|
||||
if (this->out[PIPE_WRITE])
|
||||
{
|
||||
CloseHandle(this->out[PIPE_WRITE]);
|
||||
this->out[PIPE_WRITE] = NULL;
|
||||
}
|
||||
if (this->err[PIPE_WRITE])
|
||||
{
|
||||
CloseHandle(this->err[PIPE_WRITE]);
|
||||
this->err[PIPE_WRITE] = NULL;
|
||||
}
|
||||
/* our side gets closed over the osf_handle closed by caller */
|
||||
this->in[PIPE_WRITE] = NULL;
|
||||
this->out[PIPE_READ] = NULL;
|
||||
this->err[PIPE_READ] = NULL;
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
#endif /* WIN32 */
|
||||
|
||||
Reference in New Issue
Block a user