123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957 |
- /* Low level interface to ptrace, for GDB when running under Unix.
- Copyright (C) 1986-2022 Free Software Foundation, Inc.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- 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 "serial.h"
- #include "terminal.h"
- #include "target.h"
- #include "gdbthread.h"
- #include "observable.h"
- #include <signal.h>
- #include <fcntl.h>
- #include "gdbsupport/gdb_select.h"
- #include "gdbcmd.h"
- #ifdef HAVE_TERMIOS_H
- #include <termios.h>
- #endif
- #include "gdbsupport/job-control.h"
- #include "gdbsupport/scoped_ignore_sigttou.h"
- #ifdef HAVE_SYS_IOCTL_H
- #include <sys/ioctl.h>
- #endif
- #ifndef O_NOCTTY
- #define O_NOCTTY 0
- #endif
- static void pass_signal (int);
- static void child_terminal_ours_1 (target_terminal_state);
- /* Record terminal status separately for debugger and inferior. */
- static struct serial *stdin_serial;
- /* 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 terminal when
- 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. */
- 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 {};
- #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 = 0;
- #endif
- /* fcntl flags. Saved and restored just like ttystate. */
- int tflags = 0;
- };
- /* Our own tty state, which we restore every time we need to deal with
- 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 *);
- /* 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;
- #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
- 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 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
- independent of the target backend, this tracks the actual state of
- GDB's own tty. So for example,
- (target_terminal::is_inferior () && gdb_tty_state == terminal_is_ours)
- 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;
- /* See terminal.h. */
- void
- set_initial_gdb_ttystate (void)
- {
- /* 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
- #ifdef HAVE_TERMIOS_H
- our_terminal_info.process_group = tcgetpgrp (0);
- #endif
- }
- }
- /* 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) \
- if (result == -1) \
- 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. */
- void
- child_terminal_init (struct target_ops *self)
- {
- if (!gdb_has_a_terminal ())
- return;
- 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
- 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
- when it switches to TUI or non-TUI mode; curses changes the terminal
- and gdb must be able to restore it correctly. */
- void
- gdb_save_tty_state (void)
- {
- if (gdb_has_a_terminal ())
- {
- xfree (our_terminal_info.ttystate);
- our_terminal_info.ttystate = serial_get_tty_state (stdin_serial);
- }
- }
- /* 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)
- {
- 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);
- }
- /* 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.
- 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.empty ())
- res = is_gdb_terminal (tinfo->run_terminal.c_str ());
- /* 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)
- {
- /* 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;
- inferior *inf = current_inferior ();
- terminal_info *tinfo = get_inflow_inferior_data (inf);
- if (gdb_has_a_terminal ()
- && tinfo->ttystate != 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
- result = fcntl (0, F_SETFL, tinfo->tflags);
- OOPSY ("fcntl F_SETFL");
- #endif
- result = serial_set_tty_state (stdin_serial, tinfo->ttystate);
- OOPSY ("setting tty state");
- if (!job_control)
- {
- sigint_ours = signal (SIGINT, SIG_IGN);
- #ifdef SIGQUIT
- sigquit_ours = signal (SIGQUIT, SIG_IGN);
- #endif
- }
- if (job_control)
- {
- #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);
- #endif
- 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). */
- gdb_printf
- (gdb_stderr,
- "[tcsetpgrp failed in child_terminal_inferior: %s]\n",
- safe_strerror (errno));
- #endif
- }
- #endif
- }
- gdb_tty_state = target_terminal_state::is_inferior;
- }
- }
- /* Put some of our terminal settings into effect,
- enough to get proper results from our output,
- but do not change into or out of RAW mode
- 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.
- N.B. The implementation is (currently) no different than
- child_terminal_ours. See child_terminal_ours_1. */
- void
- child_terminal_ours_for_output (struct target_ops *self)
- {
- child_terminal_ours_1 (target_terminal_state::is_ours_for_output);
- }
- /* Put our terminal settings into effect.
- First record the inferior's terminal settings
- 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
- child_terminal_ours (struct target_ops *self)
- {
- child_terminal_ours_1 (target_terminal_state::is_ours);
- }
- /* Save the current terminal settings in the inferior's terminal_info
- cache. */
- void
- child_terminal_save_inferior (struct target_ops *self)
- {
- /* Avoid attempting all the ioctl's when running in batch. */
- if (!gdb_has_a_terminal ())
- return;
- inferior *inf = current_inferior ();
- terminal_info *tinfo = get_inflow_inferior_data (inf);
- /* No need to save/restore if the inferior is not sharing GDB's
- tty. */
- if (!sharing_input_terminal (inf))
- return;
- xfree (tinfo->ttystate);
- tinfo->ttystate = serial_get_tty_state (stdin_serial);
- #ifdef HAVE_TERMIOS_H
- tinfo->process_group = tcgetpgrp (0);
- #endif
- #ifdef F_GETFL
- tinfo->tflags = fcntl (0, F_GETFL, 0);
- #endif
- }
- /* Switch terminal state to DESIRED_STATE, either is_ours, or
- is_ours_for_output. */
- 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;
- if (gdb_tty_state != desired_state)
- {
- int result ATTRIBUTE_UNUSED;
- /* Ignore SIGTTOU since it will happen when we try to set the
- terminal's pgrp. */
- scoped_ignore_sigttou ignore_sigttou;
- /* Set tty state to our_ttystate. */
- serial_set_tty_state (stdin_serial, our_terminal_info.ttystate);
- /* 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_H
- result = tcsetpgrp (0, our_terminal_info.process_group);
- #if 0
- /* This fails on Ultrix with EINVAL if you run the testsuite
- in the background with nohup, and then log out. GDB never
- used to check for an error here, so perhaps there are other
- such situations as well. */
- if (result == -1)
- 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);
- #ifdef SIGQUIT
- signal (SIGQUIT, sigquit_ours);
- #endif
- }
- #ifdef F_GETFL
- 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->has_pending_waitstatus ())
- 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. */
- static const struct inferior_key<terminal_info> inflow_inferior_data;
- terminal_info::~terminal_info ()
- {
- xfree (ttystate);
- }
- /* 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 = inflow_inferior_data.get (inf);
- if (info == NULL)
- info = inflow_inferior_data.emplace (inf);
- return info;
- }
- /* This is a "inferior_exit" observer. Releases 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 is about to be removed from the inferior
- list. */
- static void
- inflow_inferior_exit (struct inferior *inf)
- {
- inf->terminal_state = target_terminal_state::is_ours;
- inflow_inferior_data.clear (inf);
- }
- void
- copy_terminal_info (struct inferior *to, struct inferior *from)
- {
- struct terminal_info *tinfo_to, *tinfo_from;
- tinfo_to = get_inflow_inferior_data (to);
- tinfo_from = get_inflow_inferior_data (from);
- xfree (tinfo_to->ttystate);
- *tinfo_to = *tinfo_from;
- 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);
- }
- void
- 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 ())
- {
- gdb_printf (_("This GDB does not control a terminal.\n"));
- return;
- }
- if (inferior_ptid == null_ptid)
- return;
- inf = current_inferior ();
- tinfo = get_inflow_inferior_data (inf);
- gdb_printf (_("Inferior's terminal status "
- "(currently saved by GDB):\n"));
- /* First the fcntl flags. */
- {
- int flags;
- flags = tinfo->tflags;
- gdb_printf ("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. */
- switch (flags & (O_ACCMODE))
- {
- case O_RDONLY:
- gdb_printf ("O_RDONLY");
- break;
- case O_WRONLY:
- gdb_printf ("O_WRONLY");
- break;
- case O_RDWR:
- gdb_printf ("O_RDWR");
- break;
- }
- flags &= ~(O_ACCMODE);
- #ifdef O_NONBLOCK
- if (flags & O_NONBLOCK)
- gdb_printf (" | O_NONBLOCK");
- flags &= ~O_NONBLOCK;
- #endif
- #if defined (O_NDELAY)
- /* If O_NDELAY and O_NONBLOCK are defined to the same thing, we will
- 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)
- gdb_printf (" | O_NDELAY");
- flags &= ~O_NDELAY;
- #endif
- if (flags & O_APPEND)
- gdb_printf (" | O_APPEND");
- flags &= ~O_APPEND;
- #if defined (O_BINARY)
- if (flags & O_BINARY)
- gdb_printf (" | O_BINARY");
- flags &= ~O_BINARY;
- #endif
- if (flags)
- gdb_printf (" | 0x%x", flags);
- gdb_printf ("\n");
- }
- #ifdef HAVE_TERMIOS_H
- gdb_printf ("Process group = %d\n", (int) tinfo->process_group);
- #endif
- serial_print_tty_state (stdin_serial, tinfo->ttystate, gdb_stdout);
- }
- /* 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 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 (std::string ttyname)
- {
- /* Save the name for later, for determining whether we and the child
- are sharing a tty. */
- inferior_thisrun_terminal = std::move (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. */
- static void
- check_syscall (const char *msg, int result)
- {
- if (result < 0)
- {
- print_sys_errmsg (msg, errno);
- _exit (1);
- }
- }
- #endif
- void
- new_tty (void)
- {
- if (inferior_thisrun_terminal.empty ())
- 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
- ignore SIGTTOU. */
- tty = open ("/dev/tty", O_RDWR);
- if (tty >= 0)
- {
- scoped_ignore_sigttou ignore_sigttou;
- ioctl (tty, TIOCNOTTY, 0);
- close (tty);
- }
- #endif
- /* Now open the specified new terminal. */
- 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)
- {
- close (0);
- check_syscall ("dup'ing tty into fd 0", dup (tty));
- }
- if (tty != 1)
- {
- close (1);
- check_syscall ("dup'ing tty into fd 1", dup (tty));
- }
- if (tty != 2)
- {
- close (2);
- check_syscall ("dup'ing tty into fd 2", dup (tty));
- }
- #ifdef TIOCSCTTY
- /* Make tty our new controlling terminal. */
- 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"),
- safe_strerror (errno));
- #endif
- if (tty > 2)
- close (tty);
- #endif /* !go32 && !win32 */
- }
- /* 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 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. */
- struct inferior *inf = current_inferior ();
- struct terminal_info *tinfo = get_inflow_inferior_data (inf);
- tinfo->run_terminal = std::move (inferior_thisrun_terminal);
- inferior_thisrun_terminal.clear ();
- }
- /* Call set_sigint_trap when you need to pass a signal on to an attached
- process when handling SIGINT. */
- static void
- pass_signal (int signo)
- {
- #ifndef _WIN32
- kill (inferior_ptid.pid (), SIGINT);
- #endif
- }
- static sighandler_t osig;
- static int osig_set;
- void
- set_sigint_trap (void)
- {
- struct inferior *inf = current_inferior ();
- struct terminal_info *tinfo = get_inflow_inferior_data (inf);
- if (inf->attach_flag || !tinfo->run_terminal.empty ())
- {
- osig = signal (SIGINT, pass_signal);
- osig_set = 1;
- }
- else
- osig_set = 0;
- }
- void
- clear_sigint_trap (void)
- {
- if (osig_set)
- {
- signal (SIGINT, osig);
- osig_set = 0;
- }
- }
- /* Create a new session if the inferior will run in a different tty.
- A session is UNIX's way of grouping processes that share a controlling
- terminal, so a new one is needed if the inferior terminal will be
- different from GDB's.
- Returns the session id of the new session, 0 if no session was created
- or -1 if an error occurred. */
- pid_t
- create_tty_session (void)
- {
- #ifdef HAVE_SETSID
- pid_t ret;
- if (!job_control || inferior_thisrun_terminal.empty ())
- return 0;
- ret = setsid ();
- if (ret == -1)
- warning (_("Failed to create new terminal session: setsid: %s"),
- safe_strerror (errno));
- return ret;
- #else
- return 0;
- #endif /* HAVE_SETSID */
- }
- /* 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
- initialized, but we don't want to do it lazily either, so
- that we can guarantee stdin_serial is opened if there is
- a terminal. */
- void
- initialize_stdin_serial (void)
- {
- stdin_serial = serial_fdopen (0);
- }
- 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");
- }
|