git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Johannes Sixt <johannes.sixt@telecom.at>
To: git@vger.kernel.org
Cc: Johannes Sixt <johannes.sixt@telecom.at>
Subject: [PATCH 27/40] Windows: Implement a custom spawnve().
Date: Wed, 27 Feb 2008 19:54:50 +0100	[thread overview]
Message-ID: <1204138503-6126-28-git-send-email-johannes.sixt@telecom.at> (raw)
In-Reply-To: <1204138503-6126-1-git-send-email-johannes.sixt@telecom.at>

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.

Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
---
 compat/mingw.c    |  203 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 git-compat-util.h |    1 +
 run-command.c     |    2 +-
 3 files changed, 199 insertions(+), 7 deletions(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index 1260be8..146c170 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1,4 +1,5 @@
 #include "../git-compat-util.h"
+#include "../strbuf.h"
 
 unsigned int _CRT_fmode = _O_BINARY;
 
@@ -190,6 +191,65 @@ char *mingw_getcwd(char *pointer, int len)
 	return ret;
 }
 
+/*
+ * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
+ * (Parsing C++ Command-Line Arguments)
+ */
+static const char *quote_arg(const char *arg)
+{
+	/* count chars to quote */
+	int len = 0, n = 0;
+	int force_quotes = 0;
+	char *q, *d;
+	const char *p = arg;
+	if (!*p) force_quotes = 1;
+	while (*p) {
+		if (isspace(*p) || *p == '*' || *p == '?')
+			force_quotes = 1;
+		else if (*p == '"')
+			n++;
+		else if (*p == '\\') {
+			int count = 0;
+			while (*p == '\\') {
+				count++;
+				p++;
+				len++;
+			}
+			if (*p == '"')
+				n += count*2 + 1;
+			continue;
+		}
+		len++;
+		p++;
+	}
+	if (!force_quotes && n == 0)
+		return arg;
+
+	/* insert \ where necessary */
+	d = q = xmalloc(len+n+3);
+	*d++ = '"';
+	while (*arg) {
+		if (*arg == '"')
+			*d++ = '\\';
+		else if (*arg == '\\') {
+			int count = 0;
+			while (*arg == '\\') {
+				count++;
+				*d++ = *arg++;
+			}
+			if (*arg == '"') {
+				while (count-- > 0)
+					*d++ = '\\';
+				*d++ = '\\';
+			}
+		}
+		*d++ = *arg++;
+	}
+	*d++ = '"';
+	*d++ = 0;
+	return q;
+}
+
 static const char *parse_interpreter(const char *cmd)
 {
 	static char buf[100];
@@ -311,6 +371,138 @@ static char *path_lookup(const char *cmd, char **path, int exe_only)
 	return prog;
 }
 
+static int env_compare(const void *a, const void *b)
+{
+	char *const *ea = a;
+	char *const *eb = b;
+	return strcasecmp(*ea, *eb);
+}
+
+static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
+			   int prepend_cmd)
+{
+	STARTUPINFO si;
+	PROCESS_INFORMATION pi;
+	struct strbuf envblk, args;
+	unsigned flags;
+	BOOL ret;
+
+	/* Determine whether or not we are associated to a console */
+	HANDLE cons = CreateFile("CONOUT$", GENERIC_WRITE,
+			FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+			FILE_ATTRIBUTE_NORMAL, NULL);
+	if (cons == INVALID_HANDLE_VALUE) {
+		/* There is no console associated with this process.
+		 * Since the child is a console process, Windows
+		 * would normally create a console window. But
+		 * since we'll be redirecting std streams, we do
+		 * not need the console.
+		 */
+		flags = CREATE_NO_WINDOW;
+	} else {
+		/* There is already a console. If we specified
+		 * CREATE_NO_WINDOW here, too, Windows would
+		 * disassociate the child from the console.
+		 * Go figure!
+		 */
+		flags = 0;
+		CloseHandle(cons);
+	}
+	memset(&si, 0, sizeof(si));
+	si.cb = sizeof(si);
+	si.dwFlags = STARTF_USESTDHANDLES;
+	si.hStdInput = (HANDLE) _get_osfhandle(0);
+	si.hStdOutput = (HANDLE) _get_osfhandle(1);
+	si.hStdError = (HANDLE) _get_osfhandle(2);
+
+	/* concatenate argv, quoting args as we go */
+	strbuf_init(&args, 0);
+	if (prepend_cmd) {
+		char *quoted = (char *)quote_arg(cmd);
+		strbuf_addstr(&args, quoted);
+		if (quoted != cmd)
+			free(quoted);
+	}
+	for (; *argv; argv++) {
+		char *quoted = (char *)quote_arg(*argv);
+		if (*args.buf)
+			strbuf_addch(&args, ' ');
+		strbuf_addstr(&args, quoted);
+		if (quoted != *argv)
+			free(quoted);
+	}
+
+	if (env) {
+		int count = 0;
+		char **e, **sorted_env;
+
+		for (e = env; *e; e++)
+			count++;
+
+		/* environment must be sorted */
+		sorted_env = xmalloc(sizeof(*sorted_env) * (count + 1));
+		memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1));
+		qsort(sorted_env, count, sizeof(*sorted_env), env_compare);
+
+		strbuf_init(&envblk, 0);
+		for (e = sorted_env; *e; e++) {
+			strbuf_addstr(&envblk, *e);
+			strbuf_addch(&envblk, '\0');
+		}
+		free(sorted_env);
+	}
+
+	memset(&pi, 0, sizeof(pi));
+	ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
+		env ? envblk.buf : NULL, NULL, &si, &pi);
+
+	if (env)
+		strbuf_release(&envblk);
+	strbuf_release(&args);
+
+	if (!ret) {
+		errno = ENOENT;
+		return -1;
+	}
+	CloseHandle(pi.hThread);
+	return (pid_t)pi.hProcess;
+}
+
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env)
+{
+	pid_t pid;
+	char **path = get_path_split();
+	char *prog = path_lookup(cmd, path, 0);
+
+	if (!prog) {
+		errno = ENOENT;
+		pid = -1;
+	}
+	else {
+		const char *interpr = parse_interpreter(prog);
+
+		if (interpr) {
+			const char *argv0 = argv[0];
+			char *iprog = path_lookup(interpr, path, 1);
+			argv[0] = prog;
+			if (!iprog) {
+				errno = ENOENT;
+				pid = -1;
+			}
+			else {
+				pid = mingw_spawnve(iprog, argv, env, 1);
+				free(iprog);
+			}
+			argv[0] = argv0;
+		}
+		else
+			pid = mingw_spawnve(prog, argv, env, 0);
+		free(prog);
+	}
+	free_path_split(path);
+	return pid;
+}
+
 static int try_shell_exec(const char *cmd, char *const *argv, char **env)
 {
 	const char *interpr = parse_interpreter(cmd);
@@ -326,11 +518,10 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env)
 		int argc = 0;
 		const char **argv2;
 		while (argv[argc]) argc++;
-		argv2 = xmalloc(sizeof(*argv) * (argc+2));
-		argv2[0] = (char *)interpr;
-		argv2[1] = (char *)cmd;	/* full path to the script file */
-		memcpy(&argv2[2], &argv[1], sizeof(*argv) * argc);
-		pid = spawnve(_P_NOWAIT, prog, argv2, (const char **)env);
+		argv2 = xmalloc(sizeof(*argv) * (argc+1));
+		argv2[0] = (char *)cmd;	/* full path to the script file */
+		memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
+		pid = mingw_spawnve(prog, argv2, env, 1);
 		if (pid >= 0) {
 			int status;
 			if (waitpid(pid, &status, 0) < 0)
@@ -351,7 +542,7 @@ static void mingw_execve(const char *cmd, char *const *argv, char *const *env)
 	if (!try_shell_exec(cmd, argv, (char **)env)) {
 		int pid, status;
 
-		pid = spawnve(_P_NOWAIT, cmd, (const char **)argv, (const char **)env);
+		pid = mingw_spawnve(cmd, (const char **)argv, (char **)env, 0);
 		if (pid < 0)
 			return;
 		if (waitpid(pid, &status, 0) < 0)
diff --git a/git-compat-util.h b/git-compat-util.h
index 0324789..570eb10 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -611,6 +611,7 @@ int mingw_rename(const char*, const char*);
 int mingw_vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
 #define vsnprintf mingw_vsnprintf
 
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env);
 void mingw_execvp(const char *cmd, char *const *argv);
 #define execvp mingw_execvp
 
diff --git a/run-command.c b/run-command.c
index 3834f86..5ed338c 100644
--- a/run-command.c
+++ b/run-command.c
@@ -156,7 +156,7 @@ int start_command(struct child_process *cmd)
 		cmd->argv[0] = git_cmd.buf;
 	}
 
-	cmd->pid = spawnvpe(_P_NOWAIT, cmd->argv[0], cmd->argv, (const char **)env);
+	cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
 
 	if (cmd->env)
 		free_environ(env);
-- 
1.5.4.1.126.ge5a7d

  parent reply	other threads:[~2008-02-27 18:59 UTC|newest]

Thread overview: 138+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-02-27 18:54 [PATCH 00/40] MinGW port Johannes Sixt
2008-02-27 18:54 ` [PATCH 01/40] Add compat/regex.[ch] and compat/fnmatch.[ch] Johannes Sixt
2008-02-27 23:43   ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 02/40] Compile some programs only conditionally Johannes Sixt
2008-02-28 11:57   ` Johannes Schindelin
2008-02-28 20:30     ` Johannes Sixt
2008-02-29  0:47       ` Johannes Schindelin
2008-02-29 20:58         ` Johannes Sixt
2008-02-29 21:53           ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 03/40] Add target architecture MinGW Johannes Sixt
2008-02-28 12:05   ` Johannes Schindelin
2008-02-28 12:57     ` Paolo Bonzini
2008-02-28 14:56       ` Johannes Schindelin
2008-02-28 20:40     ` Johannes Sixt
2008-02-29  1:07       ` Johannes Schindelin
2008-02-29 21:03         ` Johannes Sixt
2008-02-29 21:54           ` Johannes Schindelin
2008-03-05 21:21     ` Johannes Sixt
2008-03-05 22:18       ` Johannes Schindelin
2008-03-05 22:22         ` Junio C Hamano
2008-03-05 22:28           ` Johannes Schindelin
2008-03-05 22:51             ` Junio C Hamano
2008-03-06  0:11               ` Johannes Schindelin
2008-03-06  1:14             ` [PATCH 1/2] Add strbuf_initf() Johannes Schindelin
2008-03-06  6:33               ` Mike Hommey
2008-03-06  9:03                 ` Reece Dunn
2008-03-06 10:55                   ` Johannes Schindelin
2008-03-06 11:53                     ` Reece Dunn
2008-03-06 12:52                       ` Johannes Schindelin
2008-03-06 16:29                         ` [PATCH 1/2 v2] Add strbuf_vaddf(), use it in strbuf_addf(), and add strbuf_initf() Johannes Schindelin
2008-03-06 16:38                           ` Johannes Sixt
2008-03-06 16:47                             ` Johannes Sixt
2008-03-06 16:59                               ` Johannes Schindelin
2008-03-06 18:18                     ` [PATCH 1/2] Add strbuf_initf() Kristian Høgsberg
2008-03-06 18:26                       ` Johannes Schindelin
2008-03-06 18:35                         ` Kristian Høgsberg
2008-03-06 19:10                         ` Mike Hommey
2008-03-06 10:53                 ` Johannes Schindelin
2008-03-06 12:09                   ` Jeff King
2008-03-06  1:15             ` [PATCH 2/2] format-patch: add --reviewed-by=<ident> Johannes Schindelin
2008-03-06  2:40               ` Junio C Hamano
2008-03-06 10:40                 ` Johannes Schindelin
2008-03-06 20:38         ` [PATCH 03/40] Add target architecture MinGW Johannes Sixt
2008-03-11 21:30       ` Johannes Sixt
2008-03-11 23:28         ` Johannes Schindelin
2008-03-12 22:59           ` Johannes Sixt
2008-03-12 23:06             ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 04/40] Windows: Use the Windows style PATH separator ';' Johannes Sixt
2008-02-28  9:25   ` Paolo Bonzini
2008-02-28 20:43     ` Johannes Sixt
2008-02-29  1:09       ` Johannes Schindelin
2008-02-29  7:57       ` Paolo Bonzini
2008-02-29 12:19         ` Johannes Schindelin
2008-02-29 12:45           ` Paolo Bonzini
2008-02-29 12:59             ` Johannes Schindelin
2008-02-28 17:57   ` Junio C Hamano
2008-02-27 18:54 ` [PATCH 05/40] Windows: Strip ".exe" from the program name Johannes Sixt
2008-02-27 18:54 ` [PATCH 06/40] Windows: Implement a wrapper of the open() function Johannes Sixt
2008-02-27 18:54 ` [PATCH 07/40] Windows: A minimal implemention of getpwuid() Johannes Sixt
2008-02-27 18:54 ` [PATCH 08/40] Windows: always chmod(, 0666) before unlink() Johannes Sixt
2008-02-28 12:09   ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 09/40] Windows: Work around misbehaved rename() Johannes Sixt
2008-02-27 18:54 ` [PATCH 10/40] Windows: Treat Windows style path names Johannes Sixt
2008-02-28 12:18   ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 11/40] Windows: Handle absolute paths in safe_create_leading_directories() Johannes Sixt
2008-02-27 18:54 ` [PATCH 12/40] Windows: Implement gettimeofday() Johannes Sixt
2008-02-27 18:54 ` [PATCH 13/40] Windows: Fix PRIuMAX definition Johannes Sixt
2008-02-28 12:21   ` Johannes Schindelin
2008-02-28 20:45     ` Johannes Sixt
2008-02-27 18:54 ` [PATCH 14/40] Windows: Implement setitimer() and sigaction() Johannes Sixt
2008-02-27 18:54 ` [PATCH 15/40] Windows: A work-around for a misbehaved vsnprintf Johannes Sixt
2008-02-27 18:54 ` [PATCH 16/40] Windows: Wrap execve so that shell scripts can be invoked Johannes Sixt
2008-02-27 18:54 ` [PATCH 17/40] Windows: A pipe() replacement whose ends are not inherited to children Johannes Sixt
2008-02-27 18:54 ` [PATCH 18/40] Windows: Implement start_command() Johannes Sixt
2008-02-27 18:54 ` [PATCH 19/40] Windows: Change the name of hook scripts to make them not executable Johannes Sixt
2008-02-28 15:20   ` Johannes Schindelin
2008-02-28 20:48     ` Johannes Sixt
2008-02-29  1:11       ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 20/40] Windows: A rudimentary poll() emulation Johannes Sixt
2008-02-28  9:36   ` Paolo Bonzini
2008-02-28 20:49     ` Johannes Sixt
     [not found]       ` <5d46db230802282019o21f9ed9fo75fed8744625289e@mail.gmail.com>
     [not found]         ` <200802292216.25014.johannes.sixt@telecom.at>
2008-02-29 21:47           ` Govind Salinas
2008-02-29 22:16             ` Johannes Sixt
2008-02-29 23:17             ` Brian Dessent
2008-03-01 15:48       ` Robin Rosenberg
2008-03-01 19:24         ` Johannes Sixt
2008-02-27 18:54 ` [PATCH 21/40] Windows: Disambiguate DOS style paths from SSH URLs Johannes Sixt
2008-02-28 15:22   ` Johannes Schindelin
2008-02-28 20:51     ` Johannes Sixt
2008-02-27 18:54 ` [PATCH 22/40] Windows: Implement asynchronous functions as threads Johannes Sixt
2008-02-28 15:28   ` Johannes Schindelin
2008-02-28 17:48     ` Paul Franz
2008-02-29  1:27       ` Johannes Schindelin
2008-02-29  1:46         ` Paul Franz
2008-02-29  1:54           ` Johannes Schindelin
2008-02-29  3:08             ` Paul Franz
2008-02-29  7:51               ` Junio C Hamano
2008-02-29 11:45                 ` Paul Franz
2008-02-29 10:26               ` Johannes Schindelin
2008-02-28 21:01     ` Johannes Sixt
2008-02-29  1:17       ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 23/40] Windows: Local clone must use the drive letter in absolute paths Johannes Sixt
2008-02-28 15:31   ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 24/40] Windows: Work around incompatible sort and find Johannes Sixt
2008-02-27 18:54 ` [PATCH 25/40] Windows: Implement a cpio emulation in git-clone.sh Johannes Sixt
2008-02-27 18:54 ` [PATCH 26/40] Windows: Implement wrappers for gethostbyname(), socket(), and connect() Johannes Sixt
2008-02-27 18:54 ` Johannes Sixt [this message]
2008-02-28 15:36   ` [PATCH 27/40] Windows: Implement a custom spawnve() Johannes Schindelin
2008-02-28 21:04     ` Johannes Sixt
2008-02-29  1:18       ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 28/40] Windows: Add a new lstat and fstat implementation based on Win32 API Johannes Sixt
2008-02-27 18:54 ` [PATCH 29/40] Windows: Use a customized struct stat that also has the st_blocks member Johannes Sixt
2008-02-27 18:54 ` [PATCH 30/40] Turn builtin_exec_path into a function Johannes Sixt
2008-02-27 18:54 ` [PATCH 31/40] Compute the ultimate fallback for exec_path from the program invocation Johannes Sixt
2008-02-27 18:54 ` [PATCH 32/40] Windows: Use a relative default template_dir and ETC_GITCONFIG Johannes Sixt
2008-02-27 18:54 ` [PATCH 33/40] When installing, be prepared that template_dir may be relative Johannes Sixt
2008-02-28  9:49   ` Paolo Bonzini
2008-02-28 15:45   ` Johannes Schindelin
2008-02-28 15:57     ` Paolo Bonzini
2008-02-28 21:12     ` Johannes Sixt
2008-02-29  1:21       ` Johannes Schindelin
2008-02-27 18:54 ` [PATCH 34/40] Windows: Make the pager work Johannes Sixt
2008-02-27 18:54 ` [PATCH 35/40] Windows: Work around an oddity when a pipe with no reader is written to Johannes Sixt
2008-02-27 18:54 ` [PATCH 36/40] Avoid the "dup dance" in wt_status_print_verbose() when possible Johannes Sixt
2008-02-28 15:48   ` Johannes Schindelin
2008-02-27 18:55 ` [PATCH 37/40] Windows: Make 'git help -a' work Johannes Sixt
2008-02-28  9:52   ` Paolo Bonzini
2008-02-27 18:55 ` [PATCH 38/40] Windows: TMP and TEMP environment variables specify a temporary directory Johannes Sixt
2008-02-27 18:55 ` [PATCH 39/40] Windows: Fix ntohl() related warnings about printf formatting Johannes Sixt
2008-02-27 18:55 ` [PATCH 40/40] compat/pread.c: Add foward decl to fix warning Johannes Sixt
2008-02-28 15:51   ` Johannes Schindelin
2008-02-27 22:01 ` [PATCH 00/40] MinGW port Marius Storm-Olsen
2008-02-27 23:34   ` Martin Langhoff
2008-02-28  3:38     ` Nguyen Thai Ngoc Duy
2008-02-27 23:58 ` Johannes Schindelin
2008-03-02 21:20 ` Johannes Sixt
2008-03-02 22:07   ` Johannes Schindelin
2008-03-03 18:34     ` Johannes Sixt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1204138503-6126-28-git-send-email-johannes.sixt@telecom.at \
    --to=johannes.sixt@telecom.at \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).