[1/4] GDB: inferior standard I/O redirection

Message ID 1444045617-14526-2-git-send-email-crosa@redhat.com
State New, archived
Headers

Commit Message

Cleber Rosa Oct. 5, 2015, 11:46 a.m. UTC
  This introduces a set of commands:

 * set inferior-stdin (aliased as stdin)
 * set inferior-stdout (aliased as stdout)
 * set inferior-stderr (aliased as stderr)

Those commands complement the "set inferior-tty" command, also
allowing the inferior standard I/O to be defined before the inferior
itself is started.

This is useful in a number of situations, including when shell based
redirection (the only current way to do it) is not desirable.

gdb/doc/ChangeLog:
2015-10-05  Cleber Rosa  <crosa@redhat.com>

    * gdb.texinfo (info): Added section on commands stdin, stdout and
    stderr

gdb/ChangeLog:
2015-10-05  Cleber Rosa  <crosa@redhat.com>

    * NEWS: New commands in gdb: set inferior-stdin (aliased as
    stdin), set inferior-stdout (aliased as stdout) and set
    inferior-stderr (aliased as stderr).
    * fork-child.c (set_std_io_helper): New utility function.
    * infcmd.c: Added inferior_io_std_scratch array.
    (set_inferior_io_helper): New utility function.
    (set_inferior_io_stdin): New function.
    (get_inferior_io_stdin): Likewise.
    (set_inferior_io_stdin_command): Likewise.
    (show_inferior_io_stdin_command): Likewise.
    (set_inferior_io_stdout): Likewise.
    (get_inferior_io_stdout): Likewise.
    (set_inferior_io_stdout_command): Likewise.
    (show_inferior_io_stdout_command): Likewise.
    (set_inferior_io_stderr): Likewise.
    (get_inferior_io_stderr): Likewise.
    (set_inferior_io_stderr_command): Likewise.
    (show_inferior_io_stderr_command): Likewise.
    (get_inferior_args): Added inferior-stdin/stdin,
    inferior-stdout/stdout and inferior-stderr/stderr commands.
    * inferior.c (free_inferior): Free inferior standard_io.
    * inferior.h (set_inferior_io_stdin): New declaration.
    (get_inferior_io_stdin): Likewise.
    (set_inferior_io_stdout): Likewise.
    (get_inferior_io_stdout): Likewise.
    (set_inferior_io_stderr): Likewise.
    (get_inferior_io_stderr): Likewise.
    (struct inferior): <inferior_io>: New field.

gdb/testsuite/ChangeLog:
2015-10-05  Cleber Rosa  <crosa@redhat.com>

    * gdb.base/default.exp: New tests for stdin, stdout and stderr
    commands.
---
 gdb/NEWS                           |  15 ++++
 gdb/doc/gdb.texinfo                |  97 +++++++++++++++++++-----
 gdb/fork-child.c                   |  81 ++++++++++++++++++++
 gdb/infcmd.c                       | 146 +++++++++++++++++++++++++++++++++++++
 gdb/inferior.c                     |   3 +
 gdb/inferior.h                     |  11 +++
 gdb/testsuite/gdb.base/default.exp |   6 ++
 7 files changed, 341 insertions(+), 18 deletions(-)
  

Comments

Eli Zaretskii Oct. 5, 2015, 12:23 p.m. UTC | #1
> From: Cleber Rosa <crosa@redhat.com>
> Cc: areis@redhat.com, palves@redhat.com, Cleber Rosa <crosa@redhat.com>
> Date: Mon,  5 Oct 2015 08:46:54 -0300
> 
> This introduces a set of commands:
> 
>  * set inferior-stdin (aliased as stdin)
>  * set inferior-stdout (aliased as stdout)
>  * set inferior-stderr (aliased as stderr)

Thanks.

> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -42,6 +42,21 @@ set remote multiprocess-extensions-packet
>  show remote multiprocess-extensions-packet
>    Set/show the use of the remote protocol multiprocess extensions.
>  
> +set inferior-stdin
> +stdin
> +show inferior-stdin
> +  Set/show the standard input for the program being debugged.
> +
> +set inferior-stdout
> +stdout
> +show inferior-stdout
> +  Set/show the standard output for the program being debugged.
> +
> +set inferior-stderr
> +stderr
> +show inferior-stderr
> +  Set/show the standard error for the program being debugged.
> +

This part is OK.

> +When you use the @code{tty} command or redirect input using either
> +@code{stdin}/@code{stdout}/@code{stderr} or the @code{run} command,
> +only the input @emph{for your program} is affected.  The input for
> +@value{GDBN} still comes from your terminal and from the original
> +standard I/O channels.  @code{tty} is an alias for @code{set
> +inferior-tty}.

It is strange to talk about redirecting input, when the description
also includes stdout and stderr.  Did you really mean only input here?

> +  add_setshow_filename_cmd ("inferior-stdin", class_run,
> +			    &inferior_io_std_scratch[0], _("\
> +Set standard input for future runs of program being debugged."), _("\

This correctly says that the effect of these commands is only for
_future_ runs of the inferior, but the text in the manual completely
omits this important detail, so the reader could think we have some
magical way of redirecting I/O of an already running program.

The documentation is OK with those gotchas fixed.

Thanks.
  
Pedro Alves Oct. 21, 2015, 10:38 a.m. UTC | #2
On 10/05/2015 12:46 PM, Cleber Rosa wrote:

> +/* Helper function that attempts to open a file and if successful
> +   redirects another file descriptor to the one just opened.  If
> +   file_name is not given, this returns -1 as a simple way to flag
> +   a skip (user has not asked for a redirection).  Seems OK since
> +   this is a helper function.  */
> +
> +static int
> +set_std_io_helper (const char *file_name, int std_io_fd, int flags, mode_t mode)
> +{
> +  int fd;
> +
> +  if (file_name == NULL)
> +    return -1;
> +
> +  fd = open (file_name, flags, mode);
> +  if (fd < 0)
> +    return 0;
> +
> +  if (dup2 (fd, std_io_fd) == -1) {

{ goes on the next line, and the reindent block.

> +    close (fd);
> +    return 0;
> +  }
> +
> +  close (fd);
> +  return 1;
> +}
> +
> +/* Attempts to redirect stdin, stdout and stderr. If redirection was

Double-space after period.

> +   not requested by the user, it will be skipped in the helper function.
> +   In case of errors, each requested and failed redirection error will
> +   be passed on for individual error reporting (no details such as
> +   errno/perror though).  */
> +
> +#define SET_STDIO_ERROR_STDIN  0x01
> +#define SET_STDIO_ERROR_STDOUT 0x02
> +#define SET_STDIO_ERROR_STDERR 0x04
> +
> +static int
> +set_std_io (void)
> +{
> +  int result = 0;
> +  char *file_name = NULL;
> +
> +  if (set_std_io_helper (get_inferior_io_stdin(), 0,
> +                         O_RDONLY, S_IRUSR | S_IWUSR) == 0)
> +    result |= SET_STDIO_ERROR_STDIN;
> +
> +  if (set_std_io_helper (get_inferior_io_stdout(), 1,
> +                         O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR) == 0)
> +    result |= SET_STDIO_ERROR_STDOUT;
> +
> +  if (set_std_io_helper (get_inferior_io_stderr(), 2,
> +                         O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR) == 0)
> +    result |= SET_STDIO_ERROR_STDERR;

Shouldn't we specify O_APPEND?  I'm thinking of the case of starting
multiple inferiors simultaneously under gdb.  Not one after the other,
but really in parallel.  E.g., run&; add-inferior ...; inferior 2; run&; etc.

> +
> +  return result;
> +}
> +
>  /* Start an inferior Unix 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.  ENV is the environment vector to
> @@ -141,6 +199,7 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env,
>    struct inferior *inf;
>    int i;
>    int save_errno;
> +  int io_redir_errors = 0;
>  
>    /* If no exec file handed to us, get it from the exec-file command
>       -- with a good, common error message if none is specified.  */
> @@ -358,6 +417,28 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env,
>           path to find $SHELL.  Rich Pixley says so, and I agree.  */
>        environ = env;
>  
> +      /* Sets the inferior process standard I/O (input, output, error)
> +	 redirection if set with the "set inferior std{in,out,err}"
> +	 commands.  */
> +      gdb_flush (gdb_stdout);
> +      gdb_flush (gdb_stderr);
> +      io_redir_errors = set_std_io();

Space before parens.  More instances below.

> +      if (io_redir_errors & SET_STDIO_ERROR_STDIN)
> +        fprintf_unfiltered (gdb_stderr,
> +                            "Could not set %s as stdin for %s\n",

Wrap user-visible strings in _() for i18n.  More instances below.

> +                            get_inferior_io_stdin(),
> +                            exec_file);
> +      if (io_redir_errors & SET_STDIO_ERROR_STDOUT)
> +        fprintf_unfiltered (gdb_stderr,
> +                            "Could not set %s as stdout for %s\n",
> +                            get_inferior_io_stdout(),
> +                            exec_file);
> +      if (io_redir_errors & SET_STDIO_ERROR_STDERR)
> +        fprintf_unfiltered (gdb_stderr,
> +                            "Could not set %s as stderr for %s\n",
> +                            get_inferior_io_stderr(),
> +                            exec_file);
> +
>        if (exec_fun != NULL)
>          (*exec_fun) (argv[0], argv, env);
>        else
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index 4713490..9941a98 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -115,6 +115,11 @@ static char *inferior_args_scratch;
>  
>  static char *inferior_io_terminal_scratch;
>  
> +/* Scratch area where 'set inferior-{stdin,stdout,stderr}' will store
> +   user provided value.  */
> +
> +static char *inferior_io_std_scratch[3] = { NULL, NULL, NULL };
> +
>  /* Pid of our debugged inferior, or 0 if no inferior now.
>     Since various parts of infrun.c test this to see whether there is a program
>     being debugged it should be nonzero (currently 3 is used) for remote
> @@ -182,6 +187,117 @@ show_inferior_tty_command (struct ui_file *file, int from_tty,
>  		      "is \"%s\".\n"), inferior_io_terminal);
>  }
>  

New functions below need intro comments.  For extern functions,
use "/* See whatever.h.  */".

> +static void
> +set_inferior_io_helper (const char *file_name, int stdfd)
> +{
> +  gdb_assert (0 <= stdfd && stdfd <= 2);
> +  xfree (current_inferior ()->standard_io[stdfd]);
> +  current_inferior ()->standard_io[stdfd] =
> +    file_name != NULL ? xstrdup (file_name) : NULL;
> +}
> +
> +void
> +set_inferior_io_stdin (const char *file_name)
> +{
> +  set_inferior_io_helper(file_name, 0);

Space before parens.


> diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
> index 4395c98..2ae4f90 100644
> --- a/gdb/testsuite/gdb.base/default.exp
> +++ b/gdb/testsuite/gdb.base/default.exp
> @@ -798,6 +798,12 @@ gdb_test "thread find" "Command requires an argument." "thread find"
>  gdb_test "thread name" "No thread selected" "thread name"
>  #test tty
>  gdb_test "tty" "Argument required .filename to set it to\..*" "tty"
> +#test stdin
> +gdb_test "stdin" "Argument required .filename to set it to\..*" "stdin"
> +#test stdout
> +gdb_test "stdout" "Argument required .filename to set it to\..*" "stdout"
> +#test stderr
> +gdb_test "stderr" "Argument required .filename to set it to\..*" "stderr"
>  #test until "u" abbreviation
>  gdb_test "u" "The program is not being run." "until \"u\" abbreviation"
>  #test until
> 

It'd be great if we also tested that it actually works.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 2e38d9a..07bd4db 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -42,6 +42,21 @@  set remote multiprocess-extensions-packet
 show remote multiprocess-extensions-packet
   Set/show the use of the remote protocol multiprocess extensions.
 
+set inferior-stdin
+stdin
+show inferior-stdin
+  Set/show the standard input for the program being debugged.
+
+set inferior-stdout
+stdout
+show inferior-stdout
+  Set/show the standard output for the program being debugged.
+
+set inferior-stderr
+stderr
+show inferior-stderr
+  Set/show the standard error for the program being debugged.
+
 * The "disassemble" command accepts a new modifier: /s.
   It prints mixed source+disassembly like /m with two differences:
   - disassembled instructions are now printed in program order, and
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 57e47b8..39d5a1c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2443,6 +2443,12 @@  current working directory of the debuggee.
 @cindex redirection
 @cindex i/o
 @cindex terminal
+@value{GDBN} supports different ways of redirecting your program's
+input and output, including setting a specific terminal (a
+@code{tty}), individually setting standard input, output and error
+(@code{stdin}, @code{stdout} and @code{stderr}) or using shell based
+redirection (@code{run > output}).
+
 By default, the program you run under @value{GDBN} does input and output to
 the same terminal that @value{GDBN} uses.  @value{GDBN} switches the terminal
 to its own terminal modes to interact with you, but it records the terminal
@@ -2456,19 +2462,9 @@  Displays information recorded by @value{GDBN} about the terminal modes your
 program is using.
 @end table
 
-You can redirect your program's input and/or output using shell
-redirection with the @code{run} command.  For example,
-
-@smallexample
-run > outfile
-@end smallexample
-
-@noindent
-starts your program, diverting its output to the file @file{outfile}.
-
 @kindex tty
 @cindex controlling terminal
-Another way to specify where your program should do input and output is
+One way to specify where your program should do input and output is
 with the @code{tty} command.  This command accepts a file name as
 argument, and causes this file to be the default for future @code{run}
 commands.  It also resets the controlling terminal for the child
@@ -2483,14 +2479,17 @@  directs that processes started with subsequent @code{run} commands
 default to do input and output on the terminal @file{/dev/ttyb} and have
 that as their controlling terminal.
 
-An explicit redirection in @code{run} overrides the @code{tty} command's
-effect on the input/output device, but not its effect on the controlling
-terminal.
+An explicit redirection using either
+@code{stdin}/@code{stdout}/@code{stderr} or @code{run} overrides the
+@code{tty} command's effect on the input/output device, but not its
+effect on the controlling terminal.
 
-When you use the @code{tty} command or redirect input in the @code{run}
-command, only the input @emph{for your program} is affected.  The input
-for @value{GDBN} still comes from your terminal.  @code{tty} is an alias
-for @code{set inferior-tty}.
+When you use the @code{tty} command or redirect input using either
+@code{stdin}/@code{stdout}/@code{stderr} or the @code{run} command,
+only the input @emph{for your program} is affected.  The input for
+@value{GDBN} still comes from your terminal and from the original
+standard I/O channels.  @code{tty} is an alias for @code{set
+inferior-tty}.
 
 @cindex inferior tty
 @cindex set inferior controlling terminal
@@ -2508,6 +2507,68 @@  Set the tty for the program being debugged to /dev/ttyb.
 Show the current tty for the program being debugged.
 @end table
 
+@cindex inferior standard i/o
+@cindex set inferior standard i/o
+As mentioned before, you can also redirect your program's standard
+input, output and error individually with the @code{stdin},
+@code{stdout} and @code{stderr} commands.  For example,
+
+@smallexample
+stdin /tmp/answers
+stdout /tmp/output
+stderr /dev/null
+@end smallexample
+
+@noindent
+will set your program's standard input (file descriptor number 0) to
+the @file{/tmp/answers}, the standard output (file description number
+1) to the @file{/tmp/output} and will silence the standard error (file
+descriptor number 2) by redirecting it to @file{/dev/null}.
+
+The @code{stdin}, @code{stdout} and @code{stderr} are actually aliases
+to the commands @code{set inferior-stdin}, @code{set inferior-stdout}
+and @code{set inferior-stderr}.
+
+@table @code
+@item set inferior-stdin /tmp/answers
+@kindex set inferior-stdin
+@kindex stdin
+Set the standard input for the program being debugged to /tmp/answers.
+
+@item show inferior-stdin
+@kindex show inferior-stdin
+Show the current standard input redirection for the program being debugged.
+
+@item set inferior-stdout /tmp/output
+@kindex set inferior-stdout
+@kindex stdout
+Set the standard output for the program being debugged to /tmp/output.
+
+@item show inferior-stdout
+@kindex show inferior-stdout
+Show the current standard output redirection for the program being debugged.
+
+@item set inferior-stderr /dev/null
+@kindex set inferior-stderr
+@kindex stderr
+Set the standard error for the program being debugged to /dev/null
+
+@item show inferior-error
+@kindex show inferior-error
+Show the current standard error redirection for the program being debugged.
+@end table
+
+@cindex inferior i/o shell based redirection
+Yet another way of doing redirection is by using the shell
+capabilities with the @code{run} command.  For example,
+
+@smallexample
+run > outfile
+@end smallexample
+
+@noindent
+starts your program, diverting its output to the file @file{outfile}.
+
 @node Attach
 @section Debugging an Already-running Process
 @kindex attach
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index 959f578..1b1bd47 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -108,6 +108,64 @@  escape_bang_in_quoted_argument (const char *shell_file)
   return 0;
 }
 
+/* Helper function that attempts to open a file and if successful
+   redirects another file descriptor to the one just opened.  If
+   file_name is not given, this returns -1 as a simple way to flag
+   a skip (user has not asked for a redirection).  Seems OK since
+   this is a helper function.  */
+
+static int
+set_std_io_helper (const char *file_name, int std_io_fd, int flags, mode_t mode)
+{
+  int fd;
+
+  if (file_name == NULL)
+    return -1;
+
+  fd = open (file_name, flags, mode);
+  if (fd < 0)
+    return 0;
+
+  if (dup2 (fd, std_io_fd) == -1) {
+    close (fd);
+    return 0;
+  }
+
+  close (fd);
+  return 1;
+}
+
+/* Attempts to redirect stdin, stdout and stderr. If redirection was
+   not requested by the user, it will be skipped in the helper function.
+   In case of errors, each requested and failed redirection error will
+   be passed on for individual error reporting (no details such as
+   errno/perror though).  */
+
+#define SET_STDIO_ERROR_STDIN  0x01
+#define SET_STDIO_ERROR_STDOUT 0x02
+#define SET_STDIO_ERROR_STDERR 0x04
+
+static int
+set_std_io (void)
+{
+  int result = 0;
+  char *file_name = NULL;
+
+  if (set_std_io_helper (get_inferior_io_stdin(), 0,
+                         O_RDONLY, S_IRUSR | S_IWUSR) == 0)
+    result |= SET_STDIO_ERROR_STDIN;
+
+  if (set_std_io_helper (get_inferior_io_stdout(), 1,
+                         O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR) == 0)
+    result |= SET_STDIO_ERROR_STDOUT;
+
+  if (set_std_io_helper (get_inferior_io_stderr(), 2,
+                         O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR) == 0)
+    result |= SET_STDIO_ERROR_STDERR;
+
+  return result;
+}
+
 /* Start an inferior Unix 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.  ENV is the environment vector to
@@ -141,6 +199,7 @@  fork_inferior (char *exec_file_arg, char *allargs, char **env,
   struct inferior *inf;
   int i;
   int save_errno;
+  int io_redir_errors = 0;
 
   /* If no exec file handed to us, get it from the exec-file command
      -- with a good, common error message if none is specified.  */
@@ -358,6 +417,28 @@  fork_inferior (char *exec_file_arg, char *allargs, char **env,
          path to find $SHELL.  Rich Pixley says so, and I agree.  */
       environ = env;
 
+      /* Sets the inferior process standard I/O (input, output, error)
+	 redirection if set with the "set inferior std{in,out,err}"
+	 commands.  */
+      gdb_flush (gdb_stdout);
+      gdb_flush (gdb_stderr);
+      io_redir_errors = set_std_io();
+      if (io_redir_errors & SET_STDIO_ERROR_STDIN)
+        fprintf_unfiltered (gdb_stderr,
+                            "Could not set %s as stdin for %s\n",
+                            get_inferior_io_stdin(),
+                            exec_file);
+      if (io_redir_errors & SET_STDIO_ERROR_STDOUT)
+        fprintf_unfiltered (gdb_stderr,
+                            "Could not set %s as stdout for %s\n",
+                            get_inferior_io_stdout(),
+                            exec_file);
+      if (io_redir_errors & SET_STDIO_ERROR_STDERR)
+        fprintf_unfiltered (gdb_stderr,
+                            "Could not set %s as stderr for %s\n",
+                            get_inferior_io_stderr(),
+                            exec_file);
+
       if (exec_fun != NULL)
         (*exec_fun) (argv[0], argv, env);
       else
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 4713490..9941a98 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -115,6 +115,11 @@  static char *inferior_args_scratch;
 
 static char *inferior_io_terminal_scratch;
 
+/* Scratch area where 'set inferior-{stdin,stdout,stderr}' will store
+   user provided value.  */
+
+static char *inferior_io_std_scratch[3] = { NULL, NULL, NULL };
+
 /* Pid of our debugged inferior, or 0 if no inferior now.
    Since various parts of infrun.c test this to see whether there is a program
    being debugged it should be nonzero (currently 3 is used) for remote
@@ -182,6 +187,117 @@  show_inferior_tty_command (struct ui_file *file, int from_tty,
 		      "is \"%s\".\n"), inferior_io_terminal);
 }
 
+static void
+set_inferior_io_helper (const char *file_name, int stdfd)
+{
+  gdb_assert (0 <= stdfd && stdfd <= 2);
+  xfree (current_inferior ()->standard_io[stdfd]);
+  current_inferior ()->standard_io[stdfd] =
+    file_name != NULL ? xstrdup (file_name) : NULL;
+}
+
+void
+set_inferior_io_stdin (const char *file_name)
+{
+  set_inferior_io_helper(file_name, 0);
+}
+
+const char *
+get_inferior_io_stdin (void)
+{
+  return current_inferior ()->standard_io[0];
+}
+
+static void
+set_inferior_stdin_command (char *args, int from_tty,
+			    struct cmd_list_element *c)
+{
+  set_inferior_io_stdin (inferior_io_std_scratch[0]);
+}
+
+static void
+show_inferior_stdin_command (struct ui_file *file, int from_tty,
+			     struct cmd_list_element *c, const char *value)
+{
+  /* Note that we ignore the passed-in value in favor of computing it
+     directly.  */
+  const char *inferior_io_stdin = get_inferior_io_stdin ();
+
+  if (inferior_io_stdin == NULL)
+    inferior_io_stdin = "";
+  fprintf_filtered (gdb_stdout,
+		    _("Standard input for future runs of program being "
+		      "debugged is \"%s\".\n"), inferior_io_stdin);
+}
+
+void
+set_inferior_io_stdout (const char *file_name)
+{
+  set_inferior_io_helper(file_name, 1);
+}
+
+const char *
+get_inferior_io_stdout (void)
+{
+  return current_inferior ()->standard_io[1];
+}
+
+static void
+set_inferior_stdout_command (char *args, int from_tty,
+			     struct cmd_list_element *c)
+{
+  set_inferior_io_stdout (inferior_io_std_scratch[1]);
+}
+
+static void
+show_inferior_stdout_command (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *c, const char *value)
+{
+  /* Note that we ignore the passed-in value in favor of computing it
+     directly.  */
+  const char *inferior_io_stdout = get_inferior_io_stdout ();
+
+  if (inferior_io_stdout == NULL)
+    inferior_io_stdout = "";
+  fprintf_filtered (gdb_stdout,
+		    _("Standard output for future runs of program being "
+		      "debugged is \"%s\".\n"), inferior_io_stdout);
+}
+
+void
+set_inferior_io_stderr (const char *file_name)
+{
+  set_inferior_io_helper(file_name, 2);
+}
+
+const char *
+get_inferior_io_stderr (void)
+{
+  return current_inferior ()->standard_io[2];
+}
+
+static void
+set_inferior_stderr_command (char *args, int from_tty,
+			     struct cmd_list_element *c)
+{
+  set_inferior_io_stderr (inferior_io_std_scratch[2]);
+}
+
+static void
+show_inferior_stderr_command (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *c, const char *value)
+{
+  /* Note that we ignore the passed-in value in favor of computing it
+     directly.  */
+  const char *inferior_io_stderr = get_inferior_io_stderr ();
+
+  if (inferior_io_stderr == NULL)
+    inferior_io_stderr = "";
+  fprintf_filtered (gdb_stderr,
+		    _("Standard error for future runs of program being "
+		      "debugged is \"%s\".\n"), inferior_io_stderr);
+}
+
 char *
 get_inferior_args (void)
 {
@@ -3132,6 +3248,36 @@  Usage: set inferior-tty /dev/pts/1"),
 			    &setlist, &showlist);
   add_com_alias ("tty", "set inferior-tty", class_alias, 0);
 
+  add_setshow_filename_cmd ("inferior-stdin", class_run,
+			    &inferior_io_std_scratch[0], _("\
+Set standard input for future runs of program being debugged."), _("\
+Show standard input for future runs of program being debugged."), _("\
+Usage: set inferior-input /tmp/redirected.stdin"),
+			    set_inferior_stdin_command,
+			    show_inferior_stdin_command,
+			    &setlist, &showlist);
+  add_com_alias ("stdin", "set inferior-stdin", class_alias, 0);
+
+  add_setshow_filename_cmd ("inferior-stdout", class_run,
+			    &inferior_io_std_scratch[1], _("\
+Set standard output for future runs of program being debugged."), _("\
+Show standard output for future runs of program being debugged."), _("\
+Usage: set inferior-stdout /tmp/redirected.stdout"),
+			    set_inferior_stdout_command,
+			    show_inferior_stdout_command,
+			    &setlist, &showlist);
+  add_com_alias ("stdout", "set inferior-stdout", class_alias, 0);
+
+  add_setshow_filename_cmd ("inferior-stderr", class_run,
+			    &inferior_io_std_scratch[2], _("\
+Set standard error for future runs of program being debugged."), _("\
+Show standard error for future runs of program being debugged."), _("\
+Usage: set inferior-stderr /tmp/redirected.stderr"),
+			    set_inferior_stderr_command,
+			    show_inferior_stderr_command,
+			    &setlist, &showlist);
+  add_com_alias ("stderr", "set inferior-stderr", class_alias, 0);
+
   cmd_name = "args";
   add_setshow_string_noescape_cmd (cmd_name, class_run,
 				   &inferior_args_scratch, _("\
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 04e9a28..6c6b5aa 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -99,6 +99,9 @@  free_inferior (struct inferior *inf)
   inferior_free_data (inf);
   xfree (inf->args);
   xfree (inf->terminal);
+  xfree (inf->standard_io[0]);
+  xfree (inf->standard_io[1]);
+  xfree (inf->standard_io[2]);
   free_environ (inf->environment);
   target_desc_info_free (inf->tdesc_info);
   xfree (inf->priv);
diff --git a/gdb/inferior.h b/gdb/inferior.h
index e09cb00..ed42bbd 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -77,6 +77,14 @@  extern void clear_sigint_trap (void);
 extern void set_inferior_io_terminal (const char *terminal_name);
 extern const char *get_inferior_io_terminal (void);
 
+/* Set/get file name for standard input/output/error.  */
+extern void set_inferior_io_stdin (const char *file_name);
+extern const char *get_inferior_io_stdin (void);
+extern void set_inferior_io_stdout (const char *file_name);
+extern const char *get_inferior_io_stdout (void);
+extern void set_inferior_io_stderr (const char *file_name);
+extern const char *get_inferior_io_stderr (void);
+
 /* Collected pid, tid, etc. of the debugged inferior.  When there's
    no inferior, ptid_get_pid (inferior_ptid) will be 0.  */
 
@@ -329,6 +337,9 @@  struct inferior
   /* The name of terminal device to use for I/O.  */
   char *terminal;
 
+  /* The names of files to use as standard input/output/error */
+  char *standard_io[3];
+
   /* Environment to use for running inferior,
      in format described in environ.h.  */
   struct gdb_environ *environment;
diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 4395c98..2ae4f90 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -798,6 +798,12 @@  gdb_test "thread find" "Command requires an argument." "thread find"
 gdb_test "thread name" "No thread selected" "thread name"
 #test tty
 gdb_test "tty" "Argument required .filename to set it to\..*" "tty"
+#test stdin
+gdb_test "stdin" "Argument required .filename to set it to\..*" "stdin"
+#test stdout
+gdb_test "stdout" "Argument required .filename to set it to\..*" "stdout"
+#test stderr
+gdb_test "stderr" "Argument required .filename to set it to\..*" "stderr"
 #test until "u" abbreviation
 gdb_test "u" "The program is not being run." "until \"u\" abbreviation"
 #test until