/* Low level interface to ptrace, for GDB when running under Unix.
- Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
- 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009 Free Software Foundation, Inc.
+ Copyright (C) 1986-2017 Free Software Foundation, Inc.
This file is part of GDB.
#include "target.h"
#include "gdbthread.h"
#include "observer.h"
-
-#include "gdb_string.h"
#include <signal.h>
#include <fcntl.h>
#include "gdb_select.h"
#include "inflow.h"
+#include "gdbcmd.h"
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#define O_NOCTTY 0
#endif
-#if defined (SIGIO) && defined (FASYNC) && defined (FD_SET) && defined (F_SETOWN)
-static void handle_sigio (int);
-#endif
-
extern void _initialize_inflow (void);
static void pass_signal (int);
-static void kill_command (char *, int);
-
-static void terminal_ours_1 (int);
+static void child_terminal_ours_1 (int);
\f
/* Record terminal status separately for debugger and inferior. */
};
/* Our own tty state, which we restore every time we need to deal with
- the terminal. This is only set once, when GDB first starts. The
- settings of flags which readline saves and restores and
+ the terminal. This is set once, when GDB first starts, and then
+ whenever we enter/leave TUI mode (gdb_save_tty_state). The
+ settings of flags which readline saves and restores are
unimportant. */
static struct terminal_info our_terminal_info;
+/* Snapshot of the initial tty state taken during initialization of
+ GDB, before readline/ncurses have had a chance to change it. This
+ is used as the initial tty state given to each new spawned
+ inferior. Unlike our_terminal_info, this is only ever set
+ once. */
+static serial_ttystate initial_gdb_ttystate;
+
+static struct terminal_info *get_inflow_inferior_data (struct inferior *);
+
#ifdef PROCESS_GROUP_TYPE
/* Return the process group of the current inferior. */
PROCESS_GROUP_TYPE
inferior_process_group (void)
{
- return current_inferior ()->terminal_info->process_group;
+ return get_inflow_inferior_data (current_inferior ())->process_group;
}
#endif
we save our handlers in these two variables and set SIGINT and SIGQUIT
to SIG_IGN. */
-static void (*sigint_ours) ();
-static void (*sigquit_ours) ();
+static sighandler_t sigint_ours;
+static sighandler_t sigquit_ours;
/* The name of the tty (from the `tty' command) that we're giving to
the inferior when starting it up. This is only (and should only
gdb_getpgrp (void)
{
int process_group = -1;
+
#ifdef HAVE_TERMIOS
process_group = tcgetpgrp (0);
#endif
}
#endif
-enum
- {
- yes, no, have_not_checked
- }
-gdb_has_a_terminal_flag = have_not_checked;
+/* See terminal.h. */
-/* Does GDB have a terminal (on stdin)? */
-int
-gdb_has_a_terminal (void)
+void
+set_initial_gdb_ttystate (void)
{
- switch (gdb_has_a_terminal_flag)
- {
- case yes:
- return 1;
- case no:
- return 0;
- case have_not_checked:
- /* Get all the current tty settings (including whether we have a
- tty at all!). Can't do this in _initialize_inflow because
- serial_fdopen() won't work until the serial_ops_list is
- initialized. */
+ /* Note we can't do any of this in _initialize_inflow because at
+ that point stdin_serial has not been created yet. */
+ initial_gdb_ttystate = serial_get_tty_state (stdin_serial);
+
+ if (initial_gdb_ttystate != NULL)
+ {
+ our_terminal_info.ttystate
+ = serial_copy_tty_state (stdin_serial, initial_gdb_ttystate);
#ifdef F_GETFL
our_terminal_info.tflags = fcntl (0, F_GETFL, 0);
#endif
-
- gdb_has_a_terminal_flag = no;
- if (stdin_serial != NULL)
- {
- our_terminal_info.ttystate = serial_get_tty_state (stdin_serial);
-
- if (our_terminal_info.ttystate != NULL)
- {
- gdb_has_a_terminal_flag = yes;
#ifdef PROCESS_GROUP_TYPE
- our_terminal_info.process_group = gdb_getpgrp ();
+ our_terminal_info.process_group = gdb_getpgrp ();
#endif
- }
- }
-
- return gdb_has_a_terminal_flag == yes;
- default:
- /* "Can't happen". */
- return 0;
}
}
+/* Does GDB have a terminal (on stdin)? */
+
+static int
+gdb_has_a_terminal (void)
+{
+ return initial_gdb_ttystate != NULL;
+}
+
/* Macro for printing errors from ioctl operations */
#define OOPSY(what) \
fprintf_unfiltered(gdb_stderr, "[%s failed in terminal_inferior: %s]\n", \
what, safe_strerror (errno))
-static void terminal_ours_1 (int);
-
/* Initialize the terminal settings we record for the inferior,
before we actually run the inferior. */
void
-terminal_init_inferior_with_pgrp (int pgrp)
+child_terminal_init_with_pgrp (int pgrp)
{
- if (gdb_has_a_terminal ())
- {
- struct inferior *inf = current_inferior ();
-
- /* We could just as well copy our_ttystate (if we felt like
- adding a new function serial_copy_tty_state()). */
- xfree (inf->terminal_info->ttystate);
- inf->terminal_info->ttystate
- = serial_get_tty_state (stdin_serial);
+ struct inferior *inf = current_inferior ();
+ struct terminal_info *tinfo = get_inflow_inferior_data (inf);
#ifdef PROCESS_GROUP_TYPE
- inf->terminal_info->process_group = pgrp;
+ /* Store the process group even without a terminal as it is used not
+ only to reset the tty foreground process group, but also to
+ interrupt the inferior. */
+ tinfo->process_group = pgrp;
#endif
+ if (gdb_has_a_terminal ())
+ {
+ xfree (tinfo->ttystate);
+ tinfo->ttystate = serial_copy_tty_state (stdin_serial,
+ initial_gdb_ttystate);
+
/* Make sure that next time we call terminal_inferior (which will be
before the program runs, as it needs to be), we install the new
process group. */
and gdb must be able to restore it correctly. */
void
-terminal_save_ours (void)
+gdb_save_tty_state (void)
{
if (gdb_has_a_terminal ())
{
- /* We could just as well copy our_ttystate (if we felt like adding
- a new function serial_copy_tty_state). */
xfree (our_terminal_info.ttystate);
our_terminal_info.ttystate = serial_get_tty_state (stdin_serial);
}
}
void
-terminal_init_inferior (void)
+child_terminal_init (struct target_ops *self)
{
#ifdef PROCESS_GROUP_TYPE
- /* This is for Lynx, and should be cleaned up by having Lynx be a separate
- debugging target with a version of target_terminal_init_inferior which
- passes in the process group to a generic routine which does all the work
- (and the non-threaded child_terminal_init_inferior can just pass in
- inferior_ptid to the same routine). */
+ /* This is for Lynx, and should be cleaned up by having Lynx be a
+ separate debugging target with a version of target_terminal_init
+ which passes in the process group to a generic routine which does
+ all the work (and the non-threaded child_terminal_init can just
+ pass in inferior_ptid to the same routine). */
/* We assume INFERIOR_PID is also the child's process group. */
- terminal_init_inferior_with_pgrp (PIDGET (inferior_ptid));
+ child_terminal_init_with_pgrp (ptid_get_pid (inferior_ptid));
#endif /* PROCESS_GROUP_TYPE */
}
/* Put the inferior's terminal settings into effect.
- This is preparation for starting or resuming the inferior. */
+ This is preparation for starting or resuming the inferior.
+
+ N.B. Targets that want to use this with async support must build that
+ support on top of this (e.g., the caller still needs to remove stdin
+ from the event loop). E.g., see linux_nat_terminal_inferior. */
void
-terminal_inferior (void)
+child_terminal_inferior (struct target_ops *self)
{
struct inferior *inf;
+ struct terminal_info *tinfo;
if (!terminal_is_ours)
return;
inf = current_inferior ();
+ tinfo = get_inflow_inferior_data (inf);
if (gdb_has_a_terminal ()
- && inf->terminal_info->ttystate != NULL
- && inf->terminal_info->run_terminal == NULL)
+ && tinfo->ttystate != NULL
+ && tinfo->run_terminal == NULL)
{
int result;
/* Is there a reason this is being done twice? It happens both
places we use F_SETFL, so I'm inclined to think perhaps there
is some reason, however perverse. Perhaps not though... */
- result = fcntl (0, F_SETFL, inf->terminal_info->tflags);
- result = fcntl (0, F_SETFL, inf->terminal_info->tflags);
+ result = fcntl (0, F_SETFL, tinfo->tflags);
+ result = fcntl (0, F_SETFL, tinfo->tflags);
OOPSY ("fcntl F_SETFL");
#endif
terminal_ours, we will not change in our out of raw mode with
this call, so we don't flush any input. */
result = serial_set_tty_state (stdin_serial,
- inf->terminal_info->ttystate);
+ tinfo->ttystate);
OOPSY ("setting tty state");
if (!job_control)
{
- sigint_ours = (void (*)()) signal (SIGINT, SIG_IGN);
+ sigint_ours = signal (SIGINT, SIG_IGN);
#ifdef SIGQUIT
- sigquit_ours = (void (*)()) signal (SIGQUIT, SIG_IGN);
+ sigquit_ours = signal (SIGQUIT, SIG_IGN);
#endif
}
if (job_control)
{
#ifdef HAVE_TERMIOS
- result = tcsetpgrp (0, inf->terminal_info->process_group);
+ result = tcsetpgrp (0, tinfo->process_group);
if (!inf->attach_flag)
OOPSY ("tcsetpgrp");
#endif
#ifdef HAVE_SGTTY
- result = ioctl (0, TIOCSPGRP, &inf->terminal_info->process_group);
+ result = ioctl (0, TIOCSPGRP, &tinfo->process_group);
if (!inf->attach_flag)
OOPSY ("TIOCSPGRP");
#endif
so that no input is discarded.
After doing this, either terminal_ours or terminal_inferior
- should be called to get back to a normal state of affairs. */
+ should be called to get back to a normal state of affairs.
+
+ N.B. The implementation is (currently) no different than
+ child_terminal_ours. See child_terminal_ours_1. */
void
-terminal_ours_for_output (void)
+child_terminal_ours_for_output (struct target_ops *self)
{
- terminal_ours_1 (1);
+ child_terminal_ours_1 (1);
}
/* Put our terminal settings into effect.
First record the inferior's terminal settings
- so they can be restored properly later. */
+ so they can be restored properly later.
+
+ N.B. Targets that want to use this with async support must build that
+ support on top of this (e.g., the caller still needs to add stdin to the
+ event loop). E.g., see linux_nat_terminal_ours. */
void
-terminal_ours (void)
+child_terminal_ours (struct target_ops *self)
{
- terminal_ours_1 (0);
+ child_terminal_ours_1 (0);
}
/* output_only is not used, and should not be used unless we introduce
flags. */
static void
-terminal_ours_1 (int output_only)
+child_terminal_ours_1 (int output_only)
{
struct inferior *inf;
+ struct terminal_info *tinfo;
if (terminal_is_ours)
return;
+ terminal_is_ours = 1;
+
/* Checking inferior->run_terminal is necessary so that
if GDB is running in the background, it won't block trying
to do the ioctl()'s below. Checking gdb_has_a_terminal
avoids attempting all the ioctl's when running in batch. */
inf = current_inferior ();
+ tinfo = get_inflow_inferior_data (inf);
- if (inf->terminal_info->run_terminal != NULL || gdb_has_a_terminal () == 0)
+ if (tinfo->run_terminal != NULL || gdb_has_a_terminal () == 0)
return;
-
- if (!terminal_is_ours)
+ else
{
#ifdef SIGTTOU
/* Ignore this signal since it will happen when we try to set the
pgrp. */
- void (*osigttou) () = NULL;
+ sighandler_t osigttou = NULL;
#endif
- int result;
-
- terminal_is_ours = 1;
+ int result ATTRIBUTE_UNUSED;
#ifdef SIGTTOU
if (job_control)
- osigttou = (void (*)()) signal (SIGTTOU, SIG_IGN);
+ osigttou = signal (SIGTTOU, SIG_IGN);
#endif
- xfree (inf->terminal_info->ttystate);
- inf->terminal_info->ttystate = serial_get_tty_state (stdin_serial);
+ xfree (tinfo->ttystate);
+ tinfo->ttystate = serial_get_tty_state (stdin_serial);
#ifdef PROCESS_GROUP_TYPE
if (!inf->attach_flag)
/* If setpgrp failed in terminal_inferior, this would give us
our process group instead of the inferior's. See
terminal_inferior for details. */
- inf->terminal_info->process_group = gdb_getpgrp ();
+ tinfo->process_group = gdb_getpgrp ();
#endif
/* Here we used to set ICANON in our ttystate, but I believe this
mode, to avoid flushing input. We need to do the same thing
regardless of output_only, because we don't have separate
terminal_is_ours and terminal_is_ours_for_output flags. It's OK,
- though, since readline will deal with raw mode when/if it needs to.
- */
+ though, since readline will deal with raw mode when/if it needs
+ to. */
serial_noflush_set_tty_state (stdin_serial, our_terminal_info.ttystate,
- inf->terminal_info->ttystate);
+ tinfo->ttystate);
if (job_control)
{
used to check for an error here, so perhaps there are other
such situations as well. */
if (result == -1)
- fprintf_unfiltered (gdb_stderr, "[tcsetpgrp failed in terminal_ours: %s]\n",
+ fprintf_unfiltered (gdb_stderr,
+ "[tcsetpgrp failed in child_terminal_ours: %s]\n",
safe_strerror (errno));
#endif
#endif /* termios */
}
#ifdef F_GETFL
- inf->terminal_info->tflags = fcntl (0, F_GETFL, 0);
+ tinfo->tflags = fcntl (0, F_GETFL, 0);
/* Is there a reason this is being done twice? It happens both
places we use F_SETFL, so I'm inclined to think perhaps there
}
}
-/* This is a "new_inferior" observer. It's business is to allocate
- the TERMINAL_INFO member of the inferior structure. This field is
- private to inflow.c, and its type is opaque to the rest of GDB.
- PID is the target pid of the inferior that has just been added to
- the inferior list. */
+/* Per-inferior data key. */
+static const struct inferior_data *inflow_inferior_data;
static void
-inflow_new_inferior (int pid)
+inflow_inferior_data_cleanup (struct inferior *inf, void *arg)
{
- struct inferior *inf = find_inferior_pid (pid);
+ struct terminal_info *info = (struct terminal_info *) arg;
- inf->terminal_info = XZALLOC (struct terminal_info);
+ xfree (info->run_terminal);
+ xfree (info->ttystate);
+ xfree (info);
+}
+
+/* Get the current svr4 data. If none is found yet, add it now. This
+ function always returns a valid object. */
+
+static struct terminal_info *
+get_inflow_inferior_data (struct inferior *inf)
+{
+ struct terminal_info *info;
+
+ info = (struct terminal_info *) inferior_data (inf, inflow_inferior_data);
+ if (info == NULL)
+ {
+ info = XCNEW (struct terminal_info);
+ set_inferior_data (inf, inflow_inferior_data, info);
+ }
+
+ return info;
}
/* This is a "inferior_exit" observer. Releases the TERMINAL_INFO member
list. */
static void
-inflow_inferior_exit (int pid)
+inflow_inferior_exit (struct inferior *inf)
{
- struct inferior *inf = find_inferior_pid (pid);
+ struct terminal_info *info;
- xfree (inf->terminal_info->run_terminal);
- xfree (inf->terminal_info);
- inf->terminal_info = NULL;
+ info = (struct terminal_info *) inferior_data (inf, inflow_inferior_data);
+ if (info != NULL)
+ {
+ xfree (info->run_terminal);
+ xfree (info->ttystate);
+ xfree (info);
+ set_inferior_data (inf, inflow_inferior_data, NULL);
+ }
}
void
copy_terminal_info (struct inferior *to, struct inferior *from)
{
- *to->terminal_info = *from->terminal_info;
- if (from->terminal_info->run_terminal)
- to->terminal_info->run_terminal = from->terminal_info->run_terminal;
+ struct terminal_info *tinfo_to, *tinfo_from;
+
+ tinfo_to = get_inflow_inferior_data (to);
+ tinfo_from = get_inflow_inferior_data (from);
+
+ xfree (tinfo_to->run_terminal);
+ xfree (tinfo_to->ttystate);
+
+ *tinfo_to = *tinfo_from;
+
+ if (tinfo_from->run_terminal)
+ tinfo_to->run_terminal
+ = xstrdup (tinfo_from->run_terminal);
+
+ if (tinfo_from->ttystate)
+ tinfo_to->ttystate
+ = serial_copy_tty_state (stdin_serial, tinfo_from->ttystate);
}
void
}
void
-child_terminal_info (char *args, int from_tty)
+child_terminal_info (struct target_ops *self, const char *args, int from_tty)
{
struct inferior *inf;
+ struct terminal_info *tinfo;
if (!gdb_has_a_terminal ())
{
return;
inf = current_inferior ();
+ tinfo = get_inflow_inferior_data (inf);
- printf_filtered (_("Inferior's terminal status (currently saved by GDB):\n"));
+ printf_filtered (_("Inferior's terminal status "
+ "(currently saved by GDB):\n"));
/* First the fcntl flags. */
{
int flags;
- flags = inf->terminal_info->tflags;
+ flags = tinfo->tflags;
printf_filtered ("File descriptor flags = ");
#ifndef O_ACCMODE
#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
#endif
- /* (O_ACCMODE) parens are to avoid Ultrix header file bug */
+ /* (O_ACCMODE) parens are to avoid Ultrix header file bug. */
switch (flags & (O_ACCMODE))
{
case O_RDONLY:
}
#ifdef PROCESS_GROUP_TYPE
- printf_filtered ("Process group = %d\n",
- (int) inf->terminal_info->process_group);
+ printf_filtered ("Process group = %d\n", (int) tinfo->process_group);
#endif
- serial_print_tty_state (stdin_serial,
- inf->terminal_info->ttystate,
- gdb_stdout);
+ serial_print_tty_state (stdin_serial, tinfo->ttystate, gdb_stdout);
}
\f
/* NEW_TTY_PREFORK is called before forking a new child process,
inferior_thisrun_terminal = ttyname;
}
-
+#if !defined(__GO32__) && !defined(_WIN32)
/* If RESULT, assumed to be the return value from a system call, is
negative, print the error message indicated by errno and exit.
MSG should identify the operation that failed. */
_exit (1);
}
}
+#endif
void
new_tty (void)
#ifdef TIOCNOTTY
/* Disconnect the child process from our controlling terminal. On some
systems (SVR4 for example), this may cause a SIGTTOU, so temporarily
- ignore SIGTTOU. */
+ ignore SIGTTOU. */
tty = open ("/dev/tty", O_RDWR);
if (tty > 0)
{
- void (*osigttou) ();
+ sighandler_t osigttou;
- osigttou = (void (*)()) signal (SIGTTOU, SIG_IGN);
+ osigttou = signal (SIGTTOU, SIG_IGN);
ioctl (tty, TIOCNOTTY, 0);
close (tty);
signal (SIGTTOU, osigttou);
if (ioctl (tty, TIOCSCTTY, 0) == -1)
/* Mention GDB in warning because it will appear in the inferior's
terminal instead of GDB's. */
- warning ("GDB: Failed to set controlling terminal: %s",
+ warning (_("GDB: Failed to set controlling terminal: %s"),
safe_strerror (errno));
#endif
are sharing a tty. */
if (inferior_thisrun_terminal)
- current_inferior ()->terminal_info->run_terminal
- = xstrdup (inferior_thisrun_terminal);
-
- inferior_thisrun_terminal = NULL;
-}
-
-\f
-/* Kill the inferior process. Make us have no inferior. */
-
-static void
-kill_command (char *arg, int from_tty)
-{
- /* FIXME: This should not really be inferior_ptid (or target_has_execution).
- It should be a distinct flag that indicates that a target is active, cuz
- some targets don't have processes! */
-
- if (ptid_equal (inferior_ptid, null_ptid))
- error (_("The program is not being run."));
- if (!query (_("Kill the program being debugged? ")))
- error (_("Not confirmed."));
- target_kill ();
-
- /* If the current target interface claims there's still execution,
- then don't mess with threads of other processes. */
- if (!target_has_execution)
{
- init_thread_list (); /* Destroy thread info */
+ struct inferior *inf = current_inferior ();
+ struct terminal_info *tinfo = get_inflow_inferior_data (inf);
- /* Killing off the inferior can leave us with a core file. If
- so, print the state we are left in. */
- if (target_has_stack)
- {
- printf_filtered (_("In %s,\n"), target_longname);
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
- }
+ tinfo->run_terminal = xstrdup (inferior_thisrun_terminal);
}
- bfd_cache_close_all ();
+
+ inferior_thisrun_terminal = NULL;
}
+
\f
/* Call set_sigint_trap when you need to pass a signal on to an attached
- process when handling SIGINT */
+ process when handling SIGINT. */
static void
pass_signal (int signo)
{
#ifndef _WIN32
- kill (PIDGET (inferior_ptid), SIGINT);
+ kill (ptid_get_pid (inferior_ptid), SIGINT);
#endif
}
-static void (*osig) ();
+static sighandler_t osig;
static int osig_set;
void
set_sigint_trap (void)
{
struct inferior *inf = current_inferior ();
- if (inf->attach_flag || inf->terminal_info->run_terminal)
+ struct terminal_info *tinfo = get_inflow_inferior_data (inf);
+
+ if (inf->attach_flag || tinfo->run_terminal)
{
- osig = (void (*)()) signal (SIGINT, pass_signal);
+ osig = signal (SIGINT, pass_signal);
osig_set = 1;
}
else
ret = setsid ();
if (ret == -1)
- warning ("Failed to create new terminal session: setsid: %s",
+ warning (_("Failed to create new terminal session: setsid: %s"),
safe_strerror (errno));
return ret;
add_info ("terminal", term_info,
_("Print inferior's saved terminal status."));
- add_com ("kill", class_run, kill_command,
- _("Kill execution of program being debugged."));
-
- inferior_ptid = null_ptid;
-
terminal_is_ours = 1;
/* OK, figure out whether we have job control. If neither termios nor
#ifdef _SC_JOB_CONTROL
job_control = sysconf (_SC_JOB_CONTROL);
#else
- job_control = 0; /* have to assume the worst */
+ job_control = 0; /* Have to assume the worst. */
#endif /* _SC_JOB_CONTROL */
#endif /* _POSIX_JOB_CONTROL */
#endif /* HAVE_TERMIOS */
#endif /* TIOCGPGRP */
#endif /* sgtty */
- observer_attach_new_inferior (inflow_new_inferior);
observer_attach_inferior_exit (inflow_inferior_exit);
+
+ inflow_inferior_data
+ = register_inferior_data_with_cleanup (NULL, inflow_inferior_data_cleanup);
}