@matheusbsbs / matheus-bagliano / commits / 7e5d776854
Fork 0

Windows: Implement a custom spawnve().

The problem with Windows's own implementation is that it tries to be clever when a console program is invoked from a GUI application: In this case it sometimes automatically allocates a new console window. As a consequence, the IO channels of the spawned program are directed to the console, but the invoking application listens on channels that are now directed to nowhere. In this implementation we use the lowlevel facilities of CreateProcess(), which offers a flag to tell the system not to open a console. As a side effect, only stdin, stdout, and stderr channels will be accessible from C programs that are spawned. Other channels (file handles, pipe handles, etc.) are still inherited by the spawned program, but it doesn't get enough information to access them. Johannes Schindelin integrated path quoting and unified the various *execv* and *spawnv* helpers. Eric Raible suggested to also quote '{'. Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>

Johannes Sixt committed Nov 24, 2007 at 22:49 UTC 7e5d776854e858ae69a4cde9db8de6675ffb5de6
3 files changed +199 -7
compat/mingw.c
+197 -6
@@ -1,4 +1,5 @@
1 #include "../git-compat-util.h"
2 +#include "../strbuf.h"
3
4 unsigned int _CRT_fmode = _O_BINARY;
5
@@ -186,6 +187,65 @@ char *mingw_getcwd(char *pointer, int len)
187 return ret;
188 }
189
190 +/*
191 + * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
192 + * (Parsing C++ Command-Line Arguments)
193 + */
194 +static const char *quote_arg(const char *arg)
195 +{
196 + /* count chars to quote */
197 + int len = 0, n = 0;
198 + int force_quotes = 0;
199 + char *q, *d;
200 + const char *p = arg;
201 + if (!*p) force_quotes = 1;
202 + while (*p) {
203 + if (isspace(*p) || *p == '*' || *p == '?' || *p == '{')
204 + force_quotes = 1;
205 + else if (*p == '"')
206 + n++;
207 + else if (*p == '\\') {
208 + int count = 0;
209 + while (*p == '\\') {
210 + count++;
211 + p++;
212 + len++;
213 + }
214 + if (*p == '"')
215 + n += count*2 + 1;
216 + continue;
217 + }
218 + len++;
219 + p++;
220 + }
221 + if (!force_quotes && n == 0)
222 + return arg;
223 +
224 + /* insert \ where necessary */
225 + d = q = xmalloc(len+n+3);
226 + *d++ = '"';
227 + while (*arg) {
228 + if (*arg == '"')
229 + *d++ = '\\';
230 + else if (*arg == '\\') {
231 + int count = 0;
232 + while (*arg == '\\') {
233 + count++;
234 + *d++ = *arg++;
235 + }
236 + if (*arg == '"') {
237 + while (count-- > 0)
238 + *d++ = '\\';
239 + *d++ = '\\';
240 + }
241 + }
242 + *d++ = *arg++;
243 + }
244 + *d++ = '"';
245 + *d++ = 0;
246 + return q;
247 +}
248 +
249 static const char *parse_interpreter(const char *cmd)
250 {
251 static char buf[100];
@@ -307,6 +367,138 @@ static char *path_lookup(const char *cmd, char **path, int exe_only)
367 return prog;
368 }
369
370 +static int env_compare(const void *a, const void *b)
371 +{
372 + char *const *ea = a;
373 + char *const *eb = b;
374 + return strcasecmp(*ea, *eb);
375 +}
376 +
377 +static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
378 + int prepend_cmd)
379 +{
380 + STARTUPINFO si;
381 + PROCESS_INFORMATION pi;
382 + struct strbuf envblk, args;
383 + unsigned flags;
384 + BOOL ret;
385 +
386 + /* Determine whether or not we are associated to a console */
387 + HANDLE cons = CreateFile("CONOUT$", GENERIC_WRITE,
388 + FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
389 + FILE_ATTRIBUTE_NORMAL, NULL);
390 + if (cons == INVALID_HANDLE_VALUE) {
391 + /* There is no console associated with this process.
392 + * Since the child is a console process, Windows
393 + * would normally create a console window. But
394 + * since we'll be redirecting std streams, we do
395 + * not need the console.
396 + */
397 + flags = CREATE_NO_WINDOW;
398 + } else {
399 + /* There is already a console. If we specified
400 + * CREATE_NO_WINDOW here, too, Windows would
401 + * disassociate the child from the console.
402 + * Go figure!
403 + */
404 + flags = 0;
405 + CloseHandle(cons);
406 + }
407 + memset(&si, 0, sizeof(si));
408 + si.cb = sizeof(si);
409 + si.dwFlags = STARTF_USESTDHANDLES;
410 + si.hStdInput = (HANDLE) _get_osfhandle(0);
411 + si.hStdOutput = (HANDLE) _get_osfhandle(1);
412 + si.hStdError = (HANDLE) _get_osfhandle(2);
413 +
414 + /* concatenate argv, quoting args as we go */
415 + strbuf_init(&args, 0);
416 + if (prepend_cmd) {
417 + char *quoted = (char *)quote_arg(cmd);
418 + strbuf_addstr(&args, quoted);
419 + if (quoted != cmd)
420 + free(quoted);
421 + }
422 + for (; *argv; argv++) {
423 + char *quoted = (char *)quote_arg(*argv);
424 + if (*args.buf)
425 + strbuf_addch(&args, ' ');
426 + strbuf_addstr(&args, quoted);
427 + if (quoted != *argv)
428 + free(quoted);
429 + }
430 +
431 + if (env) {
432 + int count = 0;
433 + char **e, **sorted_env;
434 +
435 + for (e = env; *e; e++)
436 + count++;
437 +
438 + /* environment must be sorted */
439 + sorted_env = xmalloc(sizeof(*sorted_env) * (count + 1));
440 + memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1));
441 + qsort(sorted_env, count, sizeof(*sorted_env), env_compare);
442 +
443 + strbuf_init(&envblk, 0);
444 + for (e = sorted_env; *e; e++) {
445 + strbuf_addstr(&envblk, *e);
446 + strbuf_addch(&envblk, '\0');
447 + }
448 + free(sorted_env);
449 + }
450 +
451 + memset(&pi, 0, sizeof(pi));
452 + ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
453 + env ? envblk.buf : NULL, NULL, &si, &pi);
454 +
455 + if (env)
456 + strbuf_release(&envblk);
457 + strbuf_release(&args);
458 +
459 + if (!ret) {
460 + errno = ENOENT;
461 + return -1;
462 + }
463 + CloseHandle(pi.hThread);
464 + return (pid_t)pi.hProcess;
465 +}
466 +
467 +pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env)
468 +{
469 + pid_t pid;
470 + char **path = get_path_split();
471 + char *prog = path_lookup(cmd, path, 0);
472 +
473 + if (!prog) {
474 + errno = ENOENT;
475 + pid = -1;
476 + }
477 + else {
478 + const char *interpr = parse_interpreter(prog);
479 +
480 + if (interpr) {
481 + const char *argv0 = argv[0];
482 + char *iprog = path_lookup(interpr, path, 1);
483 + argv[0] = prog;
484 + if (!iprog) {
485 + errno = ENOENT;
486 + pid = -1;
487 + }
488 + else {
489 + pid = mingw_spawnve(iprog, argv, env, 1);
490 + free(iprog);
491 + }
492 + argv[0] = argv0;
493 + }
494 + else
495 + pid = mingw_spawnve(prog, argv, env, 0);
496 + free(prog);
497 + }
498 + free_path_split(path);
499 + return pid;
500 +}
501 +
502 static int try_shell_exec(const char *cmd, char *const *argv, char **env)
503 {
504 const char *interpr = parse_interpreter(cmd);
@@ -322,11 +514,10 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env)
514 int argc = 0;
515 const char **argv2;
516 while (argv[argc]) argc++;
325 - argv2 = xmalloc(sizeof(*argv) * (argc+2));
326 - argv2[0] = (char *)interpr;
327 - argv2[1] = (char *)cmd; /* full path to the script file */
328 - memcpy(&argv2[2], &argv[1], sizeof(*argv) * argc);
329 - pid = spawnve(_P_NOWAIT, prog, argv2, (const char **)env);
517 + argv2 = xmalloc(sizeof(*argv) * (argc+1));
518 + argv2[0] = (char *)cmd; /* full path to the script file */
519 + memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
520 + pid = mingw_spawnve(prog, argv2, env, 1);
521 if (pid >= 0) {
522 int status;
523 if (waitpid(pid, &status, 0) < 0)
@@ -347,7 +538,7 @@ static void mingw_execve(const char *cmd, char *const *argv, char *const *env)
538 if (!try_shell_exec(cmd, argv, (char **)env)) {
539 int pid, status;
540
350 - pid = spawnve(_P_NOWAIT, cmd, (const char **)argv, (const char **)env);
541 + pid = mingw_spawnve(cmd, (const char **)argv, (char **)env, 0);
542 if (pid < 0)
543 return;
544 if (waitpid(pid, &status, 0) < 0)
compat/mingw.h
+1
@@ -160,6 +160,7 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
160 int mingw_rename(const char*, const char*);
161 #define rename mingw_rename
162
163 +pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env);
164 void mingw_execvp(const char *cmd, char *const *argv);
165 #define execvp mingw_execvp
166
run-command.c
+1 -1
@@ -168,7 +168,7 @@ int start_command(struct child_process *cmd)
168 cmd->argv[0] = git_cmd.buf;
169 }
170
171 - cmd->pid = spawnvpe(_P_NOWAIT, cmd->argv[0], cmd->argv, (const char **)env);
171 + cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
172
173 if (cmd->env)
174 free_environ(env);