/* Low level interface to ptrace, for GDB when running under Unix.
- Copyright (C) 1986-2016 Free Software Foundation, Inc.
+ Copyright (C) 1986-2019 Free Software Foundation, Inc.
This file is part of GDB.
#include "terminal.h"
#include "target.h"
#include "gdbthread.h"
-#include "observer.h"
+#include "observable.h"
#include <signal.h>
#include <fcntl.h>
#include "gdb_select.h"
#include "inflow.h"
#include "gdbcmd.h"
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#include "common/job-control.h"
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#define O_NOCTTY 0
#endif
-extern void _initialize_inflow (void);
-
static void pass_signal (int);
-static void child_terminal_ours_1 (int);
+static void child_terminal_ours_1 (target_terminal_state);
\f
/* Record terminal status separately for debugger and inferior. */
/* Terminal related info we need to keep track of. Each inferior
holds an instance of this structure --- we save it whenever the
- corresponding inferior stops, and restore it to the foreground
- inferior when it resumes. */
+ corresponding inferior stops, and restore it to the terminal when
+ the inferior is resumed in the foreground. */
struct terminal_info
{
/* The name of the tty (from the `tty' command) that we gave to the
char *run_terminal;
/* TTY state. We save it whenever the inferior stops, and restore
- it when it resumes. */
+ it when it resumes in the foreground. */
serial_ttystate ttystate;
-#ifdef PROCESS_GROUP_TYPE
- /* Process group. Saved and restored just like ttystate. */
- PROCESS_GROUP_TYPE process_group;
+#ifdef HAVE_TERMIOS_H
+ /* The terminal's foreground process group. Saved whenever the
+ inferior stops. This is the pgrp displayed by "info terminal".
+ Note that this may be not the inferior's actual process group,
+ since each inferior that we spawn has its own process group, and
+ only one can be in the foreground at a time. When the inferior
+ resumes, if we can determine the inferior's actual pgrp, then we
+ make that the foreground pgrp instead of what was saved here.
+ While it's a bit arbitrary which inferior's pgrp ends up in the
+ foreground when we resume several inferiors, this at least makes
+ 'resume inf1+inf2' + 'stop all' + 'resume inf2' end up with
+ inf2's pgrp in the foreground instead of inf1's (which would be
+ problematic since it would be left stopped: Ctrl-C wouldn't work,
+ for example). */
+ pid_t process_group;
#endif
/* fcntl flags. Saved and restored just like ttystate. */
};
/* 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 our own tty state taken during initialization of GDB.
- This is used as the initial tty state given to each new inferior. */
+/* 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
+/* RAII class used to ignore SIGTTOU in a scope. */
-/* Return the process group of the current inferior. */
-
-PROCESS_GROUP_TYPE
-inferior_process_group (void)
+class scoped_ignore_sigttou
{
- return get_inflow_inferior_data (current_inferior ())->process_group;
-}
+public:
+ scoped_ignore_sigttou ()
+ {
+#ifdef SIGTTOU
+ if (job_control)
+ m_osigttou = signal (SIGTTOU, SIG_IGN);
#endif
+ }
+
+ ~scoped_ignore_sigttou ()
+ {
+#ifdef SIGTTOU
+ if (job_control)
+ signal (SIGTTOU, m_osigttou);
+#endif
+ }
+
+ DISABLE_COPY_AND_ASSIGN (scoped_ignore_sigttou);
+
+private:
+#ifdef SIGTTOU
+ sighandler_t m_osigttou = NULL;
+#endif
+};
/* While the inferior is running, we want SIGINT and SIGQUIT to go to the
inferior only. If we have job control, that takes care of it. If not,
to SIG_IGN. */
static sighandler_t sigint_ours;
+#ifdef SIGQUIT
static sighandler_t sigquit_ours;
+#endif
/* 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
fork_inferior, while forking a new child. */
static const char *inferior_thisrun_terminal;
-/* Nonzero if our terminal settings are in effect. Zero if the
- inferior's settings are in effect. Ignored if !gdb_has_a_terminal
- (). */
-
-int terminal_is_ours;
-
-#ifdef PROCESS_GROUP_TYPE
-static PROCESS_GROUP_TYPE
-gdb_getpgrp (void)
-{
- int process_group = -1;
-
-#ifdef HAVE_TERMIOS
- process_group = tcgetpgrp (0);
-#endif
-#ifdef HAVE_TERMIO
- process_group = getpgrp ();
-#endif
-#ifdef HAVE_SGTTY
- ioctl (0, TIOCGPGRP, &process_group);
-#endif
- return process_group;
-}
-#endif
-
-enum gdb_has_a_terminal_flag_enum
- {
- yes, no, have_not_checked
- }
-gdb_has_a_terminal_flag = have_not_checked;
+/* Track who owns GDB's terminal (is it GDB or some inferior?). While
+ target_terminal::is_ours() etc. tracks the core's intention and is
+ independent of the target backend, this tracks the actual state of
+ GDB's own tty. So for example,
-/* The value of the "interactive-mode" setting. */
-static enum auto_boolean interactive_mode = AUTO_BOOLEAN_AUTO;
+ (target_terminal::is_inferior () && gdb_tty_state == terminal_is_ours)
-/* Implement the "show interactive-mode" option. */
+ is true when the (native) inferior is not sharing a terminal with
+ GDB (e.g., because we attached to an inferior that is running on a
+ different terminal). */
+static target_terminal_state gdb_tty_state = target_terminal_state::is_ours;
-static void
-show_interactive_mode (struct ui_file *file, int from_tty,
- struct cmd_list_element *c,
- const char *value)
-{
- if (interactive_mode == AUTO_BOOLEAN_AUTO)
- fprintf_filtered (file, "Debugger's interactive mode "
- "is %s (currently %s).\n",
- value, gdb_has_a_terminal () ? "on" : "off");
- else
- fprintf_filtered (file, "Debugger's interactive mode is %s.\n", value);
-}
-
-/* Set the initial tty state that is to be inherited by new inferiors. */
+/* See terminal.h. */
void
set_initial_gdb_ttystate (void)
{
- initial_gdb_ttystate = serial_get_tty_state (stdin_serial);
-}
+ /* Note we can't do any of this in _initialize_inflow because at
+ that point stdin_serial has not been created yet. */
-/* Does GDB have a terminal (on stdin)? */
-int
-gdb_has_a_terminal (void)
-{
- if (interactive_mode != AUTO_BOOLEAN_AUTO)
- return interactive_mode == AUTO_BOOLEAN_TRUE;
+ initial_gdb_ttystate = serial_get_tty_state (stdin_serial);
- switch (gdb_has_a_terminal_flag)
+ if (initial_gdb_ttystate != NULL)
{
- 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. */
-
+ 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 ();
+#ifdef HAVE_TERMIOS_H
+ our_terminal_info.process_group = tcgetpgrp (0);
#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) \
before we actually run the inferior. */
void
-child_terminal_init_with_pgrp (int pgrp)
+child_terminal_init (struct target_ops *self)
{
- struct inferior *inf = current_inferior ();
- struct terminal_info *tinfo = get_inflow_inferior_data (inf);
+ if (!gdb_has_a_terminal ())
+ return;
-#ifdef PROCESS_GROUP_TYPE
- /* 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;
+ inferior *inf = current_inferior ();
+ terminal_info *tinfo = get_inflow_inferior_data (inf);
+
+#ifdef HAVE_TERMIOS_H
+ /* A child we spawn should be a process group leader (PGID==PID) at
+ this point, though that may not be true if we're attaching to an
+ existing process. */
+ tinfo->process_group = inf->pid;
#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. */
- terminal_is_ours = 1;
- }
+ xfree (tinfo->ttystate);
+ tinfo->ttystate = serial_copy_tty_state (stdin_serial, initial_gdb_ttystate);
}
/* Save the terminal settings again. This is necessary for the TUI
}
}
-void
-child_terminal_init (struct target_ops *self)
+/* Try to determine whether TTY is GDB's input terminal. Returns
+ TRIBOOL_UNKNOWN if we can't tell. */
+
+static tribool
+is_gdb_terminal (const char *tty)
{
-#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
- 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. */
- child_terminal_init_with_pgrp (ptid_get_pid (inferior_ptid));
-#endif /* PROCESS_GROUP_TYPE */
+ struct stat gdb_tty;
+ struct stat other_tty;
+ int res;
+
+ res = stat (tty, &other_tty);
+ if (res == -1)
+ return TRIBOOL_UNKNOWN;
+
+ res = fstat (STDIN_FILENO, &gdb_tty);
+ if (res == -1)
+ return TRIBOOL_UNKNOWN;
+
+ return ((gdb_tty.st_dev == other_tty.st_dev
+ && gdb_tty.st_ino == other_tty.st_ino)
+ ? TRIBOOL_TRUE
+ : TRIBOOL_FALSE);
}
-/* Put the inferior's terminal settings into effect.
- This is preparation for starting or resuming the inferior.
+/* Helper for sharing_input_terminal. Try to determine whether
+ inferior INF is using the same TTY for input as GDB is. Returns
+ TRIBOOL_UNKNOWN if we can't tell. */
- 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. */
+static tribool
+sharing_input_terminal_1 (inferior *inf)
+{
+ /* Using host-dependent code here is fine, because the
+ child_terminal_foo functions are meant to be used by child/native
+ targets. */
+#if defined (__linux__) || defined (__sun__)
+ char buf[100];
+
+ xsnprintf (buf, sizeof (buf), "/proc/%d/fd/0", inf->pid);
+ return is_gdb_terminal (buf);
+#else
+ return TRIBOOL_UNKNOWN;
+#endif
+}
+
+/* Return true if the inferior is using the same TTY for input as GDB
+ is. If this is true, then we save/restore terminal flags/state.
+
+ This is necessary because if inf->attach_flag is set, we don't
+ offhand know whether we are sharing a terminal with the inferior or
+ not. Attaching a process without a terminal is one case where we
+ do not; attaching a process which we ran from the same shell as GDB
+ via `&' is one case where we do.
+
+ If we can't determine, we assume the TTY is being shared. This
+ works OK if you're only debugging one inferior. However, if you're
+ debugging more than one inferior, and e.g., one is spawned by GDB
+ with "run" (sharing terminal with GDB), and another is attached to
+ (and running on a different terminal, as is most common), then it
+ matters, because we can only restore the terminal settings of one
+ of the inferiors, and in that scenario, we want to restore the
+ settings of the "run"'ed inferior.
+
+ Note, this is not the same as determining whether GDB and the
+ inferior are in the same session / connected to the same
+ controlling tty. An inferior (fork child) may call setsid,
+ disconnecting itself from the ctty, while still leaving
+ stdin/stdout/stderr associated with the original terminal. If
+ we're debugging that process, we should also save/restore terminal
+ settings. */
+
+static bool
+sharing_input_terminal (inferior *inf)
+{
+ terminal_info *tinfo = get_inflow_inferior_data (inf);
+
+ tribool res = sharing_input_terminal_1 (inf);
+
+ if (res == TRIBOOL_UNKNOWN)
+ {
+ /* As fallback, if we can't determine by stat'ing the inferior's
+ tty directly (because it's not supported on this host) and
+ the child was spawned, check whether run_terminal is our tty.
+ This isn't ideal, since this is checking the child's
+ controlling terminal, not the input terminal (which may have
+ been redirected), but is still better than nothing. A false
+ positive ("set inferior-tty" points to our terminal, but I/O
+ was redirected) is much more likely than a false negative
+ ("set inferior-tty" points to some other terminal, and then
+ output was redirected to our terminal), and with a false
+ positive we just end up trying to save/restore terminal
+ settings when we didn't need to or we actually can't. */
+ if (tinfo->run_terminal != NULL)
+ res = is_gdb_terminal (tinfo->run_terminal);
+
+ /* If we still can't determine, assume yes. */
+ if (res == TRIBOOL_UNKNOWN)
+ return true;
+ }
+
+ return res == TRIBOOL_TRUE;
+}
+
+/* Put the inferior's terminal settings into effect. This is
+ preparation for starting or resuming the inferior. */
void
child_terminal_inferior (struct target_ops *self)
{
- struct inferior *inf;
- struct terminal_info *tinfo;
-
- if (!terminal_is_ours)
+ /* If we resume more than one inferior in the foreground on GDB's
+ terminal, then the first inferior's terminal settings "win".
+ Note that every child process is put in its own process group, so
+ the first process that ends up resumed ends up determining which
+ process group the kernel forwards Ctrl-C/Ctrl-Z (SIGINT/SIGTTOU)
+ to. */
+ if (gdb_tty_state == target_terminal_state::is_inferior)
return;
- inf = current_inferior ();
- tinfo = get_inflow_inferior_data (inf);
+ inferior *inf = current_inferior ();
+ terminal_info *tinfo = get_inflow_inferior_data (inf);
if (gdb_has_a_terminal ()
&& tinfo->ttystate != NULL
- && tinfo->run_terminal == NULL)
+ && sharing_input_terminal (inf))
{
int result;
+ /* Ignore SIGTTOU since it will happen when we try to set the
+ terminal's state (if gdb_tty_state is currently
+ ours_for_output). */
+ scoped_ignore_sigttou ignore_sigttou;
+
#ifdef F_GETFL
- /* 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, tinfo->tflags);
result = fcntl (0, F_SETFL, tinfo->tflags);
OOPSY ("fcntl F_SETFL");
#endif
- /* Because we were careful to not change in or out of raw mode in
- 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,
- tinfo->ttystate);
+ result = serial_set_tty_state (stdin_serial, tinfo->ttystate);
OOPSY ("setting tty state");
if (!job_control)
#endif
}
- /* If attach_flag is set, we don't know whether we are sharing a
- terminal with the inferior or not. (attaching a process
- without a terminal is one case where we do not; attaching a
- process which we ran from the same shell as GDB via `&' is
- one case where we do, I think (but perhaps this is not
- `sharing' in the sense that we need to save and restore tty
- state)). I don't know if there is any way to tell whether we
- are sharing a terminal. So what we do is to go through all
- the saving and restoring of the tty state, but ignore errors
- setting the process group, which will happen if we are not
- sharing a terminal). */
-
if (job_control)
{
-#ifdef HAVE_TERMIOS
+#ifdef HAVE_TERMIOS_H
+ /* If we can't tell the inferior's actual process group,
+ then restore whatever was the foreground pgrp the last
+ time the inferior was running. See also comments
+ describing terminal_state::process_group. */
+#ifdef HAVE_GETPGID
+ result = tcsetpgrp (0, getpgid (inf->pid));
+#else
result = tcsetpgrp (0, tinfo->process_group);
- if (!inf->attach_flag)
- OOPSY ("tcsetpgrp");
#endif
-
-#ifdef HAVE_SGTTY
- result = ioctl (0, TIOCSPGRP, &tinfo->process_group);
- if (!inf->attach_flag)
- OOPSY ("TIOCSPGRP");
+ if (result == -1)
+ {
+#if 0
+ /* This fails if either GDB has no controlling terminal,
+ e.g., running under 'setsid(1)', or if the inferior
+ is not attached to GDB's controlling terminal. E.g.,
+ if it called setsid to create a new session or used
+ the TIOCNOTTY ioctl, or simply if we've attached to a
+ process running on another terminal and we couldn't
+ tell whether it was sharing GDB's terminal (and so
+ assumed yes). */
+ fprintf_unfiltered
+ (gdb_stderr,
+ "[tcsetpgrp failed in child_terminal_inferior: %s]\n",
+ safe_strerror (errno));
+#endif
+ }
#endif
}
+ gdb_tty_state = target_terminal_state::is_inferior;
}
- terminal_is_ours = 0;
}
/* Put some of our terminal settings into effect,
void
child_terminal_ours_for_output (struct target_ops *self)
{
- child_terminal_ours_1 (1);
+ child_terminal_ours_1 (target_terminal_state::is_ours_for_output);
}
/* Put our terminal settings into effect.
void
child_terminal_ours (struct target_ops *self)
{
- child_terminal_ours_1 (0);
+ child_terminal_ours_1 (target_terminal_state::is_ours);
}
-/* output_only is not used, and should not be used unless we introduce
- separate terminal_is_ours and terminal_is_ours_for_output
- flags. */
+/* Save the current terminal settings in the inferior's terminal_info
+ cache. */
-static void
-child_terminal_ours_1 (int output_only)
+void
+child_terminal_save_inferior (struct target_ops *self)
{
- struct inferior *inf;
- struct terminal_info *tinfo;
-
- if (terminal_is_ours)
+ /* Avoid attempting all the ioctl's when running in batch. */
+ if (!gdb_has_a_terminal ())
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);
+ inferior *inf = current_inferior ();
+ terminal_info *tinfo = get_inflow_inferior_data (inf);
- if (tinfo->run_terminal != NULL || gdb_has_a_terminal () == 0)
+ /* No need to save/restore if the inferior is not sharing GDB's
+ tty. */
+ if (!sharing_input_terminal (inf))
return;
- {
-#ifdef SIGTTOU
- /* Ignore this signal since it will happen when we try to set the
- pgrp. */
- sighandler_t osigttou = NULL;
+ xfree (tinfo->ttystate);
+ tinfo->ttystate = serial_get_tty_state (stdin_serial);
+
+#ifdef HAVE_TERMIOS_H
+ tinfo->process_group = tcgetpgrp (0);
#endif
- int result;
-#ifdef SIGTTOU
- if (job_control)
- osigttou = signal (SIGTTOU, SIG_IGN);
+#ifdef F_GETFL
+ tinfo->tflags = fcntl (0, F_GETFL, 0);
#endif
+}
- xfree (tinfo->ttystate);
- tinfo->ttystate = serial_get_tty_state (stdin_serial);
+/* Switch terminal state to DESIRED_STATE, either is_ours, or
+ is_ours_for_output. */
-#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. */
- tinfo->process_group = gdb_getpgrp ();
-#endif
+static void
+child_terminal_ours_1 (target_terminal_state desired_state)
+{
+ gdb_assert (desired_state != target_terminal_state::is_inferior);
+
+ /* Avoid attempting all the ioctl's when running in batch. */
+ if (!gdb_has_a_terminal ())
+ return;
- /* Here we used to set ICANON in our ttystate, but I believe this
- was an artifact from before when we used readline. Readline sets
- the tty state when it needs to.
- FIXME-maybe: However, query() expects non-raw mode and doesn't
- use readline. Maybe query should use readline (on the other hand,
- this only matters for HAVE_SGTTY, not termio or termios, I think). */
+ if (gdb_tty_state != desired_state)
+ {
+ int result ATTRIBUTE_UNUSED;
- /* Set tty state to our_ttystate. We don't change in our out of raw
- 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. */
+ /* Ignore SIGTTOU since it will happen when we try to set the
+ terminal's pgrp. */
+ scoped_ignore_sigttou ignore_sigttou;
- serial_noflush_set_tty_state (stdin_serial, our_terminal_info.ttystate,
- tinfo->ttystate);
+ /* Set tty state to our_ttystate. */
+ serial_set_tty_state (stdin_serial, our_terminal_info.ttystate);
- if (job_control)
+ /* If we only want output, then leave the inferior's pgrp in the
+ foreground, so that Ctrl-C/Ctrl-Z reach the inferior
+ directly. */
+ if (job_control && desired_state == target_terminal_state::is_ours)
{
-#ifdef HAVE_TERMIOS
+#ifdef HAVE_TERMIOS_H
result = tcsetpgrp (0, our_terminal_info.process_group);
#if 0
/* This fails on Ultrix with EINVAL if you run the testsuite
safe_strerror (errno));
#endif
#endif /* termios */
-
-#ifdef HAVE_SGTTY
- result = ioctl (0, TIOCSPGRP, &our_terminal_info.process_group);
-#endif
}
-#ifdef SIGTTOU
- if (job_control)
- signal (SIGTTOU, osigttou);
-#endif
-
- if (!job_control)
+ if (!job_control && desired_state == target_terminal_state::is_ours)
{
signal (SIGINT, sigint_ours);
#ifdef SIGQUIT
}
#ifdef F_GETFL
- 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
- is some reason, however perverse. Perhaps not though... */
- result = fcntl (0, F_SETFL, our_terminal_info.tflags);
result = fcntl (0, F_SETFL, our_terminal_info.tflags);
#endif
+
+ gdb_tty_state = desired_state;
+ }
+}
+
+/* Interrupt the inferior. Implementation of target_interrupt for
+ child/native targets. */
+
+void
+child_interrupt (struct target_ops *self)
+{
+ /* Interrupt the first inferior that has a resumed thread. */
+ thread_info *resumed = NULL;
+ for (thread_info *thr : all_non_exited_threads ())
+ {
+ if (thr->executing)
+ {
+ resumed = thr;
+ break;
+ }
+ if (thr->suspend.waitstatus_pending_p)
+ resumed = thr;
+ }
+
+ if (resumed != NULL)
+ {
+ /* Note that unlike pressing Ctrl-C on the controlling terminal,
+ here we only interrupt one process, not the whole process
+ group. This is because interrupting a process group (with
+ either Ctrl-C or with kill(3) with negative PID) sends a
+ SIGINT to each process in the process group, and we may not
+ be debugging all processes in the process group. */
+#ifndef _WIN32
+ kill (resumed->inf->pid, SIGINT);
+#endif
+ }
+}
+
+/* Pass a Ctrl-C to the inferior as-if a Ctrl-C was pressed while the
+ inferior was in the foreground. Implementation of
+ target_pass_ctrlc for child/native targets. */
+
+void
+child_pass_ctrlc (struct target_ops *self)
+{
+ gdb_assert (!target_terminal::is_ours ());
+
+#ifdef HAVE_TERMIOS_H
+ if (job_control)
+ {
+ pid_t term_pgrp = tcgetpgrp (0);
+
+ /* If there's any inferior sharing our terminal, pass the SIGINT
+ to the terminal's foreground process group. This acts just
+ like the user typed a ^C on the terminal while the inferior
+ was in the foreground. Note that using a negative process
+ number in kill() is a System V-ism. The proper BSD interface
+ is killpg(). However, all modern BSDs support the System V
+ interface too. */
+
+ if (term_pgrp != -1 && term_pgrp != our_terminal_info.process_group)
+ {
+ kill (-term_pgrp, SIGINT);
+ return;
+ }
+ }
+#endif
+
+ /* Otherwise, pass the Ctrl-C to the first inferior that was resumed
+ in the foreground. */
+ for (inferior *inf : all_inferiors ())
+ {
+ if (inf->terminal_state != target_terminal_state::is_ours)
+ {
+ gdb_assert (inf->pid != 0);
+
+#ifndef _WIN32
+ kill (inf->pid, SIGINT);
+#endif
+ return;
+ }
}
+
+ /* If no inferior was resumed in the foreground, then how did the
+ !is_ours assert above pass? */
+ gdb_assert_not_reached ("no inferior resumed in the fg found");
}
/* Per-inferior data key. */
{
struct terminal_info *info;
+ inf->terminal_state = target_terminal_state::is_ours;
+
info = (struct terminal_info *) inferior_data (inf, inflow_inferior_data);
if (info != NULL)
{
if (tinfo_from->ttystate)
tinfo_to->ttystate
= serial_copy_tty_state (stdin_serial, tinfo_from->ttystate);
+
+ to->terminal_state = from->terminal_state;
}
+/* See terminal.h. */
+
void
-term_info (char *arg, int from_tty)
+swap_terminal_info (inferior *a, inferior *b)
{
- target_terminal_info (arg, from_tty);
+ terminal_info *info_a
+ = (terminal_info *) inferior_data (a, inflow_inferior_data);
+ terminal_info *info_b
+ = (terminal_info *) inferior_data (a, inflow_inferior_data);
+
+ set_inferior_data (a, inflow_inferior_data, info_b);
+ set_inferior_data (b, inflow_inferior_data, info_a);
+
+ std::swap (a->terminal_state, b->terminal_state);
+}
+
+void
+info_terminal_command (const char *arg, int from_tty)
+{
+ target_terminal::info (arg, from_tty);
}
void
return;
}
- if (ptid_equal (inferior_ptid, null_ptid))
+ if (inferior_ptid == null_ptid)
return;
inf = current_inferior ();
printf_filtered ("\n");
}
-#ifdef PROCESS_GROUP_TYPE
+#ifdef HAVE_TERMIOS_H
printf_filtered ("Process group = %d\n", (int) tinfo->process_group);
#endif
void
new_tty (void)
{
- int tty;
-
if (inferior_thisrun_terminal == 0)
return;
#if !defined(__GO32__) && !defined(_WIN32)
+ int tty;
+
#ifdef TIOCNOTTY
/* Disconnect the child process from our controlling terminal. On some
systems (SVR4 for example), this may cause a SIGTTOU, so temporarily
tty = open ("/dev/tty", O_RDWR);
if (tty > 0)
{
- sighandler_t osigttou;
+ scoped_ignore_sigttou ignore_sigttou;
- osigttou = signal (SIGTTOU, SIG_IGN);
ioctl (tty, TIOCNOTTY, 0);
close (tty);
- signal (SIGTTOU, osigttou);
}
#endif
pass_signal (int signo)
{
#ifndef _WIN32
- kill (ptid_get_pid (inferior_ptid), SIGINT);
+ kill (inferior_ptid.pid (), SIGINT);
#endif
}
#endif /* HAVE_SETSID */
}
-/* This is here because this is where we figure out whether we (probably)
- have job control. Just using job_control only does part of it because
- setpgid or setpgrp might not exist on a system without job control.
- It might be considered misplaced (on the other hand, process groups and
- job control are closely related to ttys).
-
- For a more clean implementation, in libiberty, put a setpgid which merely
- calls setpgrp and a setpgrp which does nothing (any system with job control
- will have one or the other). */
-int
-gdb_setpgid (void)
-{
- int retval = 0;
-
- if (job_control)
- {
-#if defined (HAVE_TERMIOS) || defined (TIOCGPGRP)
-#ifdef HAVE_SETPGID
- /* The call setpgid (0, 0) is supposed to work and mean the same
- thing as this, but on Ultrix 4.2A it fails with EPERM (and
- setpgid (getpid (), getpid ()) succeeds). */
- retval = setpgid (getpid (), getpid ());
-#else
-#ifdef HAVE_SETPGRP
-#ifdef SETPGRP_VOID
- retval = setpgrp ();
-#else
- retval = setpgrp (getpid (), getpid ());
-#endif
-#endif /* HAVE_SETPGRP */
-#endif /* HAVE_SETPGID */
-#endif /* defined (HAVE_TERMIOS) || defined (TIOCGPGRP) */
- }
-
- return retval;
-}
-
/* Get all the current tty settings (including whether we have a
tty at all!). We can't do this in _initialize_inflow because
serial_fdopen() won't work until the serial_ops_list is
void
_initialize_inflow (void)
{
- add_info ("terminal", term_info,
+ add_info ("terminal", info_terminal_command,
_("Print inferior's saved terminal status."));
- add_setshow_auto_boolean_cmd ("interactive-mode", class_support,
- &interactive_mode, _("\
-Set whether GDB's standard input is a terminal."), _("\
-Show whether GDB's standard input is a terminal."), _("\
-If on, GDB assumes that standard input is a terminal. In practice, it\n\
-means that GDB should wait for the user to answer queries associated to\n\
-commands entered at the command prompt. If off, GDB assumes that standard\n\
-input is not a terminal, and uses the default answer to all queries.\n\
-If auto (the default), determine which mode to use based on the standard\n\
-input settings."),
- NULL,
- show_interactive_mode,
- &setlist, &showlist);
-
- terminal_is_ours = 1;
-
- /* OK, figure out whether we have job control. If neither termios nor
- sgtty (i.e. termio or go32), leave job_control 0. */
-
-#if defined (HAVE_TERMIOS)
- /* Do all systems with termios have the POSIX way of identifying job
- control? I hope so. */
-#ifdef _POSIX_JOB_CONTROL
- job_control = 1;
-#else
-#ifdef _SC_JOB_CONTROL
- job_control = sysconf (_SC_JOB_CONTROL);
-#else
- job_control = 0; /* Have to assume the worst. */
-#endif /* _SC_JOB_CONTROL */
-#endif /* _POSIX_JOB_CONTROL */
-#endif /* HAVE_TERMIOS */
-
-#ifdef HAVE_SGTTY
-#ifdef TIOCGPGRP
- job_control = 1;
-#else
- job_control = 0;
-#endif /* TIOCGPGRP */
-#endif /* sgtty */
+ /* OK, figure out whether we have job control. */
+ have_job_control ();
- observer_attach_inferior_exit (inflow_inferior_exit);
+ gdb::observers::inferior_exit.attach (inflow_inferior_exit);
inflow_inferior_data
= register_inferior_data_with_cleanup (NULL, inflow_inferior_data_cleanup);