#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 */