From patchwork Sat Oct 15 12:39:42 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eli Zaretskii X-Patchwork-Id: 16530 Received: (qmail 72650 invoked by alias); 15 Oct 2016 12:40:11 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 72624 invoked by uid 89); 15 Oct 2016 12:40:09 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.4 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_PASS autolearn=ham version=3.3.2 spammy=logs X-HELO: eggs.gnu.org Received: from eggs.gnu.org (HELO eggs.gnu.org) (208.118.235.92) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 15 Oct 2016 12:39:59 +0000 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bvOFn-0004NM-Cf for gdb-patches@sourceware.org; Sat, 15 Oct 2016 08:39:57 -0400 Received: from fencepost.gnu.org ([2001:4830:134:3::e]:45275) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bvOFh-0004K6-Cs; Sat, 15 Oct 2016 08:39:49 -0400 Received: from 84.94.185.246.cable.012.net.il ([84.94.185.246]:4378 helo=home-c4e4a596f7) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA1:128) (Exim 4.82) (envelope-from ) id 1bvOFe-00025o-SK; Sat, 15 Oct 2016 08:39:47 -0400 Date: Sat, 15 Oct 2016 15:39:42 +0300 Message-Id: <83zim523ch.fsf@gnu.org> From: Eli Zaretskii To: gdb-patches@sourceware.org CC: samsurfer117@gmail.com In-reply-to: <8360ot3kzq.fsf@gnu.org> (message from Eli Zaretskii on Sat, 15 Oct 2016 14:33:13 +0300) Subject: Re: CYGWIN file input redirection Reply-to: Eli Zaretskii References: <83twcxn3ne.fsf@gnu.org> <8360ot3kzq.fsf@gnu.org> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2001:4830:134:3::e X-IsSubscribed: yes > Date: Sat, 15 Oct 2016 14:33:13 +0300 > From: Eli Zaretskii > CC: samsurfer117@gmail.com > > > Date: Fri, 30 Sep 2016 12:22:13 +0300 > > From: Eli Zaretskii > > CC: gdb-patches@sourceware.org > > > > > How can we fix this bug? > > > > Someone needs to write and submit the code to (a) parse the command > > line, like "run < foo >> bar 2> baz" and glean the redirections from > > it; and (b) actually implement the redirection in the CreateProcess > > call. > > Guess what? someone just did. > > The patches below implement redirection support for native debugging > of MinGW programs. (I reused some of the code I wrote years ago for > the DJGPP project.) > > OK to push to master? Sorry, forgot to attach the ChangeLog entries. Here's the patch again with the logs: 2016-10-15 Eli Zaretskii Support command-line redirection for Windows native debugging * windows-nat.c (redir_open, redir_set_redirection) (redirect_inferior_handles) [!__CYGWIN__]: New functions for redirecting standard handles of the inferior. (windows_create_inferior) [!__CYGWIN__]: Use them to redirect standard handles of the inferior based on redirection symbols on the command line. --- gdb/windows-nat.c~1 2016-10-09 12:37:04.538125000 +0300 +++ gdb/windows-nat.c 2016-10-15 14:27:51.966125000 +0300 @@ -2054,6 +2054,166 @@ clear_win32_environment (char **env) } #endif +#ifndef __CYGWIN__ + +/* Support routines for redirecting standard handles of the inferior. */ + +static int +redir_open (const char *redir_string, HANDLE *inp, HANDLE *out, HANDLE *err) +{ + HANDLE *hdl; + int mode, fd; + const char *fname = redir_string + 1;; + + switch (*redir_string) + { + case '<': + hdl = inp; + mode = O_RDONLY; + break; + case '1': case '2': + fname++; + /* FALLTHROUGH */ + case '>': + hdl = (*redir_string == '2') ? err : out; + mode = O_WRONLY | O_CREAT; + if (*fname == '>') + { + fname++; + mode |= O_APPEND; + } + else + mode |= O_TRUNC; + break; + default: + return -1; + } + fname++; /* skip the separator space */ + fd = _open (fname, mode, _S_IREAD | _S_IWRITE); + if (fd < 0) + return -1; + /* _open just sets a flag for O_APPEND, which won't be passed to the + inferior, so we need to actually move the file pointer. */ + if ((mode & O_APPEND) != 0) + _lseek (fd, 0L, SEEK_END); + *hdl = (HANDLE)_get_osfhandle (fd); + return 0; +} + +static int +redir_set_redirection (const char *s, HANDLE *inp, HANDLE *out, HANDLE *err) +{ + char buf[__PMAX + 5]; /* extra space for redirection string */ + char *d = buf; + const char *start = s; + int quote = 0; + + *d++ = *s++; /* copy the 1st character, < or > or a digit */ + if ((*start == '>' || *start == '1' || *start == '2') + && *s == '>') + { + *d++ = *s++; + if (*s == '>' && *start != '>') + *d++ = *s++; + } + while (isspace (*s)) /* skip whitespace before file name */ + s++; + *d++ = ' '; /* separate file name with a single space */ + + /* Copy the file name. */ + while (*s) + { + /* Remove quoting characters from the file name in buf[]. */ + if (*s == '"') /* could support '..' quoting here */ + { + if (!quote) + quote = *s++; + else if (*s == quote) + { + quote = 0; + s++; + } + else + *d++ = *s++; + } + else if (*s == '\\') + { + if (s[1] == '"') /* could support '..' here */ + s++; + *d++ = *s++; + } + else if (isspace (*s) && !quote) + break; + else + *d++ = *s++; + if (d - buf >= sizeof (buf) - 1) + { + errno = ENAMETOOLONG; + return 0; + } + } + *d = '\0'; + + /* Windows doesn't allow redirection characters in file names, so we + can bail out early if they use them, or if there's no target file + name after the redirection symbol. */ + if (d[-1] == '>' || d[-1] == '<') + { + errno = ENOENT; + return 0; + } + if (redir_open (buf, inp, out, err) == 0) + return s - start; + return 0; +} + +static bool +redirect_inferior_handles (const char *cmd_orig, char *cmd, + HANDLE *inp, HANDLE *out, HANDLE *err) +{ + const char *s = cmd_orig; + char *d = cmd; + int quote = 0; + bool retval = false; + + while (isspace (*s)) + *d++ = *s++; + + while (*s) + { + if (*s == '"') /* could also support '..' quoting here */ + { + if (!quote) + quote = *s; + else if (*s == quote) + quote = 0; + } + else if (*s == '\\') + { + if (s[1] == '"') /* escaped quote char */ + s++; + } + else if (!quote) + { + if (*s == '<' || *s == '>' + || ((*s == '1' || *s == '2') && s[1] == '>')) + { + int skip = redir_set_redirection (s, inp, out, err); + + if (skip <= 0) + return false; + retval = true; + s += skip; + } + } + if (*s) + *d++ = *s++; + } + *d = '\0'; + return retval; +} +#endif /* !__CYGWIN__ */ + /* Start an inferior windows child process and sets inferior_ptid to its pid. EXEC_FILE is the file to run. ALLARGS is a string containing the arguments to the program. @@ -2076,20 +2236,24 @@ windows_create_inferior (struct target_o size_t len; int tty; int ostdin, ostdout, ostderr; -#else +#else /* !__CYGWIN__ */ char real_path[__PMAX]; char shell[__PMAX]; /* Path to shell */ char *toexec; - char *args; - size_t args_len; - HANDLE tty; + char *args, *allargs_copy; + size_t args_len, allargs_len; + HANDLE tty = INVALID_HANDLE_VALUE; + HANDLE inf_stdin = INVALID_HANDLE_VALUE; + HANDLE inf_stdout = INVALID_HANDLE_VALUE; + HANDLE inf_stderr = INVALID_HANDLE_VALUE; + bool redirected = false; char *w32env; char *temp; size_t envlen; int i; size_t envsize; char **env; -#endif +#endif /* !__CYGWIN__ */ PROCESS_INFORMATION pi; BOOL ret; DWORD flags = 0; @@ -2121,7 +2285,7 @@ windows_create_inferior (struct target_o error (_("Error starting executable: %d"), errno); cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t)); mbstowcs (cygallargs, allargs, len); -#else +#else /* !__USEWIDE */ cygallargs = allargs; #endif } @@ -2137,12 +2301,12 @@ windows_create_inferior (struct target_o + mbstowcs (NULL, allargs, 0) + 2; cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t)); swprintf (cygallargs, len, L" -c 'exec %s %s'", exec_file, allargs); -#else +#else /* !__USEWIDE */ len = (sizeof (" -c 'exec '") + strlen (exec_file) + strlen (allargs) + 2); cygallargs = (char *) alloca (len); xsnprintf (cygallargs, len, " -c 'exec %s %s'", exec_file, allargs); -#endif +#endif /* __USEWIDE */ toexec = shell; flags |= DEBUG_PROCESS; } @@ -2153,12 +2317,12 @@ windows_create_inferior (struct target_o wcscpy (args, toexec); wcscat (args, L" "); wcscat (args, cygallargs); -#else +#else /* !__USEWIDE */ args = (cygwin_buf_t *) alloca (strlen (toexec) + strlen (cygallargs) + 2); strcpy (args, toexec); strcat (args, " "); strcat (args, cygallargs); -#endif +#endif /* !__USEWIDE */ #ifdef CW_CVT_ENV_TO_WINENV /* First try to create a direct Win32 copy of the POSIX environment. */ @@ -2167,7 +2331,7 @@ windows_create_inferior (struct target_o flags |= CREATE_UNICODE_ENVIRONMENT; else /* If that fails, fall back to old method tweaking GDB's environment. */ -#endif +#endif /* CW_CVT_ENV_TO_WINENV */ { /* Reset all Win32 environment variables to avoid leftover on next run. */ clear_win32_environment (environ); @@ -2232,21 +2396,26 @@ windows_create_inferior (struct target_o close (ostdout); close (ostderr); } -#else - toexec = exec_file; - /* Build the command line, a space-separated list of tokens where - the first token is the name of the module to be executed. - To avoid ambiguities introduced by spaces in the module name, - we quote it. */ - args_len = strlen (toexec) + 2 /* quotes */ + strlen (allargs) + 2; - args = (char *) alloca (args_len); - xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs); - - flags |= DEBUG_ONLY_THIS_PROCESS; - - if (!inferior_io_terminal) - tty = INVALID_HANDLE_VALUE; - else +#else /* !__CYGWIN__ */ + allargs_len = strlen (allargs); + allargs_copy = strcpy ((char *)alloca (allargs_len + 1), allargs); + if (strpbrk (allargs_copy, "<>")) + { + int e = errno; + errno = 0; + redirected = + redirect_inferior_handles (allargs, allargs_copy, + &inf_stdin, &inf_stdout, &inf_stderr); + if (errno) + warning (_("Error in redirection: %s."), strerror (errno)); + else + errno = e; + allargs_len = strlen (allargs_copy); + } + if (inferior_io_terminal + && !(inf_stdin != INVALID_HANDLE_VALUE + && inf_stdout != INVALID_HANDLE_VALUE + && inf_stderr != INVALID_HANDLE_VALUE)) { SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); @@ -2257,15 +2426,32 @@ windows_create_inferior (struct target_o if (tty == INVALID_HANDLE_VALUE) warning (_("Warning: Failed to open TTY %s, error %#x."), inferior_io_terminal, (unsigned) GetLastError ()); - else - { - si.hStdInput = tty; - si.hStdOutput = tty; - si.hStdError = tty; - si.dwFlags |= STARTF_USESTDHANDLES; - } + } + if (redirected || tty != INVALID_HANDLE_VALUE) + { + si.hStdInput = inf_stdin == INVALID_HANDLE_VALUE ? tty : inf_stdin; + if (si.hStdInput == INVALID_HANDLE_VALUE) + si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); + si.hStdOutput = inf_stdout == INVALID_HANDLE_VALUE ? tty : inf_stdout; + if (si.hStdOutput == INVALID_HANDLE_VALUE) + si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); + si.hStdError = inf_stderr == INVALID_HANDLE_VALUE ? tty : inf_stderr; + if (si.hStdError == INVALID_HANDLE_VALUE) + si.hStdError = GetStdHandle (STD_ERROR_HANDLE); + si.dwFlags |= STARTF_USESTDHANDLES; } + toexec = exec_file; + /* Build the command line, a space-separated list of tokens where + the first token is the name of the module to be executed. + To avoid ambiguities introduced by spaces in the module name, + we quote it. */ + args_len = strlen (toexec) + 2 /* quotes */ + allargs_len + 2; + args = (char *) alloca (args_len); + xsnprintf (args, args_len, "\"%s\" %s", toexec, allargs_copy); + + flags |= DEBUG_ONLY_THIS_PROCESS; + /* CreateProcess takes the environment list as a null terminated set of strings (i.e. two nulls terminate the list). */ @@ -2304,7 +2490,13 @@ windows_create_inferior (struct target_o &pi); if (tty != INVALID_HANDLE_VALUE) CloseHandle (tty); -#endif + if (inf_stdin != INVALID_HANDLE_VALUE) + CloseHandle (inf_stdin); + if (inf_stdout != INVALID_HANDLE_VALUE) + CloseHandle (inf_stdout); + if (inf_stderr != INVALID_HANDLE_VALUE) + CloseHandle (inf_stderr); +#endif /* !__CYGWIN__ */ if (!ret) error (_("Error creating process %s, (error %u)."),