/* Low level interface to ptrace, for GDB when running under Unix.
- Copyright (C) 1986-2018 Free Software Foundation, Inc.
+ Copyright (C) 1986-2024 Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "command.h"
#include "observable.h"
#include <signal.h>
#include <fcntl.h>
-#include "gdb_select.h"
+#include "gdbsupport/gdb_select.h"
-#include "inflow.h"
-#include "gdbcmd.h"
+#include "cli/cli-cmds.h"
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
-#include "job-control.h"
+#include "gdbsupport/job-control.h"
+#include "gdbsupport/scoped_ignore_sigttou.h"
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
+#ifdef __CYGWIN__
+#include <sys/cygwin.h>
+#endif
+
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
the inferior is resumed in the foreground. */
struct terminal_info
{
+ terminal_info () = default;
+ ~terminal_info ();
+
+ terminal_info &operator= (const terminal_info &) = default;
+
/* The name of the tty (from the `tty' command) that we gave to the
inferior when it was started. */
- char *run_terminal;
+ std::string run_terminal;
/* TTY state. We save it whenever the inferior stops, and restore
it when it resumes in the foreground. */
- serial_ttystate ttystate;
+ serial_ttystate ttystate {};
#ifdef HAVE_TERMIOS_H
/* The terminal's foreground process group. Saved whenever the
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;
+ pid_t process_group = 0;
#endif
/* fcntl flags. Saved and restored just like ttystate. */
- int tflags;
+ int tflags = 0;
};
/* Our own tty state, which we restore every time we need to deal with
static struct terminal_info *get_inflow_inferior_data (struct inferior *);
-/* RAII class used to ignore SIGTTOU in a scope. */
-
-class scoped_ignore_sigttou
-{
-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,
we save our handlers in these two variables and set SIGINT and SIGQUIT
to SIG_IGN. */
-static sighandler_t sigint_ours;
+static std::optional<sighandler_t> sigint_ours;
#ifdef SIGQUIT
-static sighandler_t sigquit_ours;
+static std::optional<sighandler_t> sigquit_ours;
#endif
/* The name of the tty (from the `tty' command) that we're giving to
be) used as a transient global by new_tty_prefork,
create_tty_session, new_tty and new_tty_postfork, all called from
fork_inferior, while forking a new child. */
-static const char *inferior_thisrun_terminal;
+static std::string inferior_thisrun_terminal;
/* 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
#define OOPSY(what) \
if (result == -1) \
- fprintf_unfiltered(gdb_stderr, "[%s failed in terminal_inferior: %s]\n", \
- what, safe_strerror (errno))
+ gdb_printf(gdb_stderr, "[%s failed in terminal_inferior: %s]\n", \
+ what, safe_strerror (errno))
/* Initialize the terminal settings we record for the inferior,
before we actually run the inferior. */
}
}
-/* Try to determine whether TTY is GDB's input terminal. Returns
- TRIBOOL_UNKNOWN if we can't tell. */
+/* See inferior.h. */
-static tribool
+tribool
is_gdb_terminal (const char *tty)
{
struct stat gdb_tty;
: TRIBOOL_FALSE);
}
-/* 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. */
-
-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.
{
terminal_info *tinfo = get_inflow_inferior_data (inf);
- tribool res = sharing_input_terminal_1 (inf);
+ tribool res = sharing_input_terminal (inf->pid);
if (res == TRIBOOL_UNKNOWN)
{
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 (!tinfo->run_terminal.empty ())
+ res = is_gdb_terminal (tinfo->run_terminal.c_str ());
/* If we still can't determine, assume yes. */
if (res == TRIBOOL_UNKNOWN)
if (!job_control)
{
- sigint_ours = signal (SIGINT, SIG_IGN);
+ sigint_ours = install_sigint_handler (SIG_IGN);
#ifdef SIGQUIT
sigquit_ours = signal (SIGQUIT, SIG_IGN);
#endif
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);
-#endif
+ pid_t pgrp = tinfo->process_group;
+#ifdef __CYGWIN__
+ /* The Windows native target uses Win32 routines to run or
+ attach to processes (CreateProcess / DebugActiveProcess),
+ so a Cygwin inferior has a Windows PID, rather than a
+ Cygwin PID. We want to pass the Cygwin PID to Cygwin
+ tcsetpgrp if we have a Cygwin inferior, so try to convert
+ first. If we have a non-Cygwin inferior, we'll end up
+ passing down the WINPID to tcsetpgrp, stored in
+ terminal_state::process_group. tcsetpgrp still succeeds
+ in that case, and it seems preferable to switch the
+ foreground pgrp away from GDB, for consistency. */
+ pid_t cygpid = cygwin_internal (CW_WINPID_TO_CYGWIN_PID, inf->pid);
+ if (cygpid <= cygwin_internal (CW_MAX_CYGWIN_PID))
+ pgrp = getpgid (cygpid);
+#elif defined (HAVE_GETPGID)
+ pgrp = getpgid (inf->pid);
+#endif
+ result = tcsetpgrp (0, pgrp);
if (result == -1)
{
#if 0
process running on another terminal and we couldn't
tell whether it was sharing GDB's terminal (and so
assumed yes). */
- fprintf_unfiltered
+ gdb_printf
(gdb_stderr,
"[tcsetpgrp failed in child_terminal_inferior: %s]\n",
safe_strerror (errno));
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 child_terminal_ours: %s]\n",
- safe_strerror (errno));
+ gdb_printf (gdb_stderr,
+ "[tcsetpgrp failed in child_terminal_ours: %s]\n",
+ safe_strerror (errno));
#endif
#endif /* termios */
}
if (!job_control && desired_state == target_terminal_state::is_ours)
{
- signal (SIGINT, sigint_ours);
+ if (sigint_ours.has_value ())
+ install_sigint_handler (*sigint_ours);
+ sigint_ours.reset ();
#ifdef SIGQUIT
- signal (SIGQUIT, sigquit_ours);
+ if (sigquit_ours.has_value ())
+ signal (SIGQUIT, *sigquit_ours);
+ sigquit_ours.reset ();
#endif
}
child_interrupt (struct target_ops *self)
{
/* Interrupt the first inferior that has a resumed thread. */
- thread_info *thr;
thread_info *resumed = NULL;
- ALL_NON_EXITED_THREADS (thr)
+ for (thread_info *thr : all_non_exited_threads ())
{
- if (thr->executing)
+ if (thr->executing ())
{
resumed = thr;
break;
}
- if (thr->suspend.waitstatus_pending_p)
+ if (thr->has_pending_waitstatus ())
resumed = thr;
}
/* Otherwise, pass the Ctrl-C to the first inferior that was resumed
in the foreground. */
- inferior *inf;
- ALL_INFERIORS (inf)
+ for (inferior *inf : all_inferiors ())
{
if (inf->terminal_state != target_terminal_state::is_ours)
{
}
/* Per-inferior data key. */
-static const struct inferior_data *inflow_inferior_data;
+static const registry<inferior>::key<terminal_info> inflow_inferior_data;
-static void
-inflow_inferior_data_cleanup (struct inferior *inf, void *arg)
+terminal_info::~terminal_info ()
{
- struct terminal_info *info = (struct terminal_info *) arg;
-
- xfree (info->run_terminal);
- xfree (info->ttystate);
- xfree (info);
+ xfree (ttystate);
}
/* Get the current svr4 data. If none is found yet, add it now. This
{
struct terminal_info *info;
- info = (struct terminal_info *) inferior_data (inf, inflow_inferior_data);
+ info = inflow_inferior_data.get (inf);
if (info == NULL)
- {
- info = XCNEW (struct terminal_info);
- set_inferior_data (inf, inflow_inferior_data, info);
- }
+ info = inflow_inferior_data.emplace (inf);
return info;
}
static void
inflow_inferior_exit (struct inferior *inf)
{
- 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)
- {
- xfree (info->run_terminal);
- xfree (info->ttystate);
- xfree (info);
- set_inferior_data (inf, inflow_inferior_data, NULL);
- }
+ inflow_inferior_data.clear (inf);
}
void
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);
to->terminal_state = from->terminal_state;
}
+/* See terminal.h. */
+
void
+swap_terminal_info (inferior *a, inferior *b)
+{
+ terminal_info *info_a = inflow_inferior_data.get (a);
+ terminal_info *info_b = inflow_inferior_data.get (b);
+
+ inflow_inferior_data.set (a, info_b);
+ inflow_inferior_data.set (b, info_a);
+
+ std::swap (a->terminal_state, b->terminal_state);
+}
+
+static void
info_terminal_command (const char *arg, int from_tty)
{
target_terminal::info (arg, from_tty);
if (!gdb_has_a_terminal ())
{
- printf_filtered (_("This GDB does not control a terminal.\n"));
+ gdb_printf (_("This GDB does not control a terminal.\n"));
return;
}
inf = current_inferior ();
tinfo = get_inflow_inferior_data (inf);
- printf_filtered (_("Inferior's terminal status "
- "(currently saved by GDB):\n"));
+ gdb_printf (_("Inferior's terminal status "
+ "(currently saved by GDB):\n"));
/* First the fcntl flags. */
{
flags = tinfo->tflags;
- printf_filtered ("File descriptor flags = ");
+ gdb_printf ("File descriptor flags = ");
#ifndef O_ACCMODE
#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
switch (flags & (O_ACCMODE))
{
case O_RDONLY:
- printf_filtered ("O_RDONLY");
+ gdb_printf ("O_RDONLY");
break;
case O_WRONLY:
- printf_filtered ("O_WRONLY");
+ gdb_printf ("O_WRONLY");
break;
case O_RDWR:
- printf_filtered ("O_RDWR");
+ gdb_printf ("O_RDWR");
break;
}
flags &= ~(O_ACCMODE);
#ifdef O_NONBLOCK
if (flags & O_NONBLOCK)
- printf_filtered (" | O_NONBLOCK");
+ gdb_printf (" | O_NONBLOCK");
flags &= ~O_NONBLOCK;
#endif
print it as O_NONBLOCK, which is good cause that is what POSIX
has, and the flag will already be cleared by the time we get here. */
if (flags & O_NDELAY)
- printf_filtered (" | O_NDELAY");
+ gdb_printf (" | O_NDELAY");
flags &= ~O_NDELAY;
#endif
if (flags & O_APPEND)
- printf_filtered (" | O_APPEND");
+ gdb_printf (" | O_APPEND");
flags &= ~O_APPEND;
#if defined (O_BINARY)
if (flags & O_BINARY)
- printf_filtered (" | O_BINARY");
+ gdb_printf (" | O_BINARY");
flags &= ~O_BINARY;
#endif
if (flags)
- printf_filtered (" | 0x%x", flags);
- printf_filtered ("\n");
+ gdb_printf (" | 0x%x", flags);
+ gdb_printf ("\n");
}
#ifdef HAVE_TERMIOS_H
- printf_filtered ("Process group = %d\n", (int) tinfo->process_group);
+ gdb_printf ("Process group = %d\n", (int) tinfo->process_group);
#endif
serial_print_tty_state (stdin_serial, tinfo->ttystate, gdb_stdout);
\f
/* NEW_TTY_PREFORK is called before forking a new child process,
so we can record the state of ttys in the child to be formed.
- TTYNAME is null if we are to share the terminal with gdb;
- or points to a string containing the name of the desired tty.
+ TTYNAME is empty if we are to share the terminal with gdb;
+ otherwise it contains the name of the desired tty.
NEW_TTY is called in new child processes under Unix, which will
become debugger target processes. This actually switches to
the terminal specified in the NEW_TTY_PREFORK call. */
void
-new_tty_prefork (const char *ttyname)
+new_tty_prefork (std::string ttyname)
{
/* Save the name for later, for determining whether we and the child
are sharing a tty. */
- inferior_thisrun_terminal = ttyname;
+ inferior_thisrun_terminal = std::move (ttyname);
}
#if !defined(__GO32__) && !defined(_WIN32)
{
if (result < 0)
{
- print_sys_errmsg (msg, errno);
+ gdb_printf (gdb_stderr, "%s:%s.\n", msg,
+ safe_strerror (errno));
_exit (1);
}
}
void
new_tty (void)
{
- if (inferior_thisrun_terminal == 0)
+ if (inferior_thisrun_terminal.empty ())
return;
#if !defined(__GO32__) && !defined(_WIN32)
int tty;
systems (SVR4 for example), this may cause a SIGTTOU, so temporarily
ignore SIGTTOU. */
tty = open ("/dev/tty", O_RDWR);
- if (tty > 0)
+ if (tty >= 0)
{
scoped_ignore_sigttou ignore_sigttou;
#endif
/* Now open the specified new terminal. */
- tty = open (inferior_thisrun_terminal, O_RDWR | O_NOCTTY);
- check_syscall (inferior_thisrun_terminal, tty);
+ tty = open (inferior_thisrun_terminal.c_str (), O_RDWR | O_NOCTTY);
+ check_syscall (inferior_thisrun_terminal.c_str (), tty);
/* Avoid use of dup2; doesn't exist on all systems. */
if (tty != 0)
/* NEW_TTY_POSTFORK is called after forking a new child process, and
adding it to the inferior table, to store the TTYNAME being used by
- the child, or null if it sharing the terminal with gdb. */
+ the child, or empty if it sharing the terminal with gdb. */
void
new_tty_postfork (void)
/* Save the name for later, for determining whether we and the child
are sharing a tty. */
- if (inferior_thisrun_terminal)
- {
- struct inferior *inf = current_inferior ();
- struct terminal_info *tinfo = get_inflow_inferior_data (inf);
-
- tinfo->run_terminal = xstrdup (inferior_thisrun_terminal);
- }
+ struct inferior *inf = current_inferior ();
+ struct terminal_info *tinfo = get_inflow_inferior_data (inf);
- inferior_thisrun_terminal = NULL;
+ tinfo->run_terminal = std::move (inferior_thisrun_terminal);
+ inferior_thisrun_terminal.clear ();
}
\f
struct inferior *inf = current_inferior ();
struct terminal_info *tinfo = get_inflow_inferior_data (inf);
- if (inf->attach_flag || tinfo->run_terminal)
+ if (inf->attach_flag || !tinfo->run_terminal.empty ())
{
- osig = signal (SIGINT, pass_signal);
+ osig = install_sigint_handler (pass_signal);
osig_set = 1;
}
else
{
if (osig_set)
{
- signal (SIGINT, osig);
+ install_sigint_handler (osig);
osig_set = 0;
}
}
#ifdef HAVE_SETSID
pid_t ret;
- if (!job_control || inferior_thisrun_terminal == 0)
+ if (!job_control || inferior_thisrun_terminal.empty ())
return 0;
ret = setsid ();
stdin_serial = serial_fdopen (0);
}
+void _initialize_inflow ();
void
-_initialize_inflow (void)
+_initialize_inflow ()
{
add_info ("terminal", info_terminal_command,
_("Print inferior's saved terminal status."));
/* OK, figure out whether we have job control. */
have_job_control ();
- gdb::observers::inferior_exit.attach (inflow_inferior_exit);
-
- inflow_inferior_data
- = register_inferior_data_with_cleanup (NULL, inflow_inferior_data_cleanup);
+ gdb::observers::inferior_exit.attach (inflow_inferior_exit, "inflow");
}