(root)/
gettext-0.22.4/
libtextstyle/
lib/
term-style-control.c
       1  /* Terminal control for outputting styled text to a terminal.
       2     Copyright (C) 2006-2008, 2017, 2019-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2019.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include "term-style-control.h"
      22  
      23  /* Set to 1 to get debugging output regarding signals.  */
      24  #define DEBUG_SIGNALS 0
      25  
      26  #include <errno.h>
      27  #include <signal.h>
      28  #include <stdlib.h>
      29  #include <string.h>
      30  #include <unistd.h>
      31  #if DEBUG_SIGNALS
      32  # include <stdio.h>
      33  #endif
      34  #if HAVE_TCGETATTR
      35  # include <termios.h>
      36  # if !defined NOFLSH            /* QNX */
      37  #  define NOFLSH 0
      38  # endif
      39  #endif
      40  #if HAVE_TCGETATTR
      41  # include <sys/stat.h>
      42  #endif
      43  
      44  #include "fatal-signal.h"
      45  #include "sig-handler.h"
      46  #include "full-write.h"
      47  #include "same-inode.h"
      48  #include "xalloc.h"
      49  
      50  #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
      51  
      52  
      53  /* ============================ EINTR handling ============================ */
      54  
      55  /* EINTR handling for tcgetattr(), tcsetattr().
      56     These functions can return -1/EINTR even when we don't have any
      57     signal handlers set up, namely when we get interrupted via SIGSTOP.  */
      58  
      59  #if HAVE_TCGETATTR
      60  
      61  static inline int
      62  nonintr_tcgetattr (int fd, struct termios *tcp)
      63  {
      64    int retval;
      65  
      66    do
      67      retval = tcgetattr (fd, tcp);
      68    while (retval < 0 && errno == EINTR);
      69  
      70    return retval;
      71  }
      72  
      73  static inline int
      74  nonintr_tcsetattr (int fd, int flush_mode, const struct termios *tcp)
      75  {
      76    int retval;
      77  
      78    do
      79      retval = tcsetattr (fd, flush_mode, tcp);
      80    while (retval < 0 && errno == EINTR);
      81  
      82    return retval;
      83  }
      84  
      85  #endif
      86  
      87  
      88  /* ========================== Logging primitives ========================== */
      89  
      90  /* We need logging, especially for the signal handling, because
      91       - Debugging through gdb is hardly possible, because gdb produces output
      92         by itself and interferes with the process states.
      93       - strace is buggy when it comes to SIGTSTP handling:  By default, it
      94         sends the process a SIGSTOP signal instead of SIGTSTP.  It supports
      95         an option '-D -I4' to mitigate this, though.  Also, race conditions
      96         appear with different probability with and without strace.
      97     fprintf(stderr) is not possible within async-safe code, because fprintf()
      98     may invoke malloc().  */
      99  
     100  #if DEBUG_SIGNALS
     101  
     102  /* Log a simple message.  */
     103  static _GL_ASYNC_SAFE void
     104  log_message (const char *message)
     105  {
     106    full_write (STDERR_FILENO, message, strlen (message));
     107  }
     108  
     109  #else
     110  
     111  # define log_message(message)
     112  
     113  #endif
     114  
     115  #if HAVE_TCGETATTR || DEBUG_SIGNALS
     116  
     117  /* Async-safe implementation of sprintf (str, "%d", n).  */
     118  static _GL_ASYNC_SAFE void
     119  sprintf_integer (char *str, int x)
     120  {
     121    unsigned int y;
     122    char buf[20];
     123    char *p;
     124    size_t n;
     125  
     126    if (x < 0)
     127      {
     128        *str++ = '-';
     129        y = (unsigned int) (-1 - x) + 1;
     130      }
     131    else
     132      y = x;
     133  
     134    p = buf + sizeof (buf);
     135    do
     136      {
     137        *--p = '0' + (y % 10);
     138        y = y / 10;
     139      }
     140    while (y > 0);
     141    n = buf + sizeof (buf) - p;
     142    memcpy (str, p, n);
     143    str[n] = '\0';
     144  }
     145  
     146  #endif
     147  
     148  #if HAVE_TCGETATTR
     149  
     150  /* Async-safe conversion of errno value to string.  */
     151  static _GL_ASYNC_SAFE void
     152  simple_errno_string (char *str, int errnum)
     153  {
     154    switch (errnum)
     155      {
     156      case EBADF:  strcpy (str, "EBADF"); break;
     157      case EINTR:  strcpy (str, "EINTR"); break;
     158      case EINVAL: strcpy (str, "EINVAL"); break;
     159      case EIO:    strcpy (str, "EIO"); break;
     160      case ENOTTY: strcpy (str, "ENOTTY"); break;
     161      default: sprintf_integer (str, errnum); break;
     162      }
     163  }
     164  
     165  #endif
     166  
     167  #if DEBUG_SIGNALS
     168  
     169  /* Async-safe conversion of signal number to name.  */
     170  static _GL_ASYNC_SAFE void
     171  simple_signal_string (char *str, int sig)
     172  {
     173    switch (sig)
     174      {
     175      /* Fatal signals (see fatal-signal.c).  */
     176      #ifdef SIGINT
     177      case SIGINT:   strcpy (str, "SIGINT"); break;
     178      #endif
     179      #ifdef SIGTERM
     180      case SIGTERM:  strcpy (str, "SIGTERM"); break;
     181      #endif
     182      #ifdef SIGHUP
     183      case SIGHUP:   strcpy (str, "SIGHUP"); break;
     184      #endif
     185      #ifdef SIGPIPE
     186      case SIGPIPE:  strcpy (str, "SIGPIPE"); break;
     187      #endif
     188      #ifdef SIGXCPU
     189      case SIGXCPU:  strcpy (str, "SIGXCPU"); break;
     190      #endif
     191      #ifdef SIGXFSZ
     192      case SIGXFSZ:  strcpy (str, "SIGXFSZ"); break;
     193      #endif
     194      #ifdef SIGBREAK
     195      case SIGBREAK: strcpy (str, "SIGBREAK"); break;
     196      #endif
     197      /* Stopping signals.  */
     198      #ifdef SIGTSTP
     199      case SIGTSTP:  strcpy (str, "SIGTSTP"); break;
     200      #endif
     201      #ifdef SIGTTIN
     202      case SIGTTIN:  strcpy (str, "SIGTTIN"); break;
     203      #endif
     204      #ifdef SIGTTOU
     205      case SIGTTOU:  strcpy (str, "SIGTTOU"); break;
     206      #endif
     207      /* Continuing signals.  */
     208      #ifdef SIGCONT
     209      case SIGCONT:  strcpy (str, "SIGCONT"); break;
     210      #endif
     211      default: sprintf_integer (str, sig); break;
     212      }
     213  }
     214  
     215  /* Emit a message that a given signal handler is being run.  */
     216  static _GL_ASYNC_SAFE void
     217  log_signal_handler_called (int sig)
     218  {
     219    char message[100];
     220    strcpy (message, "Signal handler for signal ");
     221    simple_signal_string (message + strlen (message), sig);
     222    strcat (message, " called.\n");
     223    log_message (message);
     224  }
     225  
     226  #else
     227  
     228  static void
     229  log_signal_handler_called (_GL_UNUSED int sig)
     230  {
     231  }
     232  
     233  #endif
     234  
     235  
     236  /* ============================ Signal handling ============================ */
     237  
     238  /* There are several situations which can cause garbled output on the terminal's
     239     screen:
     240     (1) When the program calls exit() after calling flush_to_current_style,
     241         the program would terminate and leave the terminal in a non-default
     242         state.
     243     (2) When the program is interrupted through a fatal signal, the terminal
     244         would be left in a non-default state.
     245     (3) When the program is stopped through a stopping signal, the terminal
     246         would be left (for temporary use by other programs) in a non-default
     247         state.
     248     (4) When a foreground process receives a SIGINT, the kernel(!) prints '^C'.
     249         On Linux, the place where this happens is
     250           linux-5.0/drivers/tty/n_tty.c:713..730
     251         within a call sequence
     252           n_tty_receive_signal_char (n_tty.c:1245..1246)
     253           -> commit_echoes (n_tty.c:792)
     254           -> __process_echoes (n_tty.c:713..730).
     255     (5) When a signal is sent, the output buffer is cleared.
     256         On Linux, this output buffer consists of the "echo buffer" in the tty
     257         and the "output buffer" in the driver.  The place where this happens is
     258           linux-5.0/drivers/tty/n_tty.c:1133..1140
     259         within a call
     260           isig (n_tty.c:1133..1140).
     261  
     262     How do we mitigate these problems?
     263     (1) We install an exit handler that restores the terminal to the default
     264         state.
     265     (2) If tty_control is TTYCTL_PARTIAL or TTYCTL_FULL:
     266         For some of the fatal signals (see gnulib's 'fatal-signal' module for
     267         the precise list), we install a handler that attempts to restore the
     268         terminal to the default state.  Since the terminal may be in the middle
     269         of outputting an escape sequence at this point, the first escape
     270         sequence emitted from this handler may have no effect and produce
     271         garbled characters instead.  Therefore the handler outputs the cleanup
     272         sequence twice.
     273         For the other fatal signals, we don't do anything.
     274     (3) If tty_control is TTYCTL_PARTIAL or TTYCTL_FULL:
     275         For some of the stopping signals (SIGTSTP, SIGTTIN, SIGTTOU), we install
     276         a handler that attempts to restore the terminal to the default state.
     277         For SIGCONT, we install a handler that does the opposite: it puts the
     278         terminal into the desired state again.
     279         For SIGSTOP, we cannot do anything.
     280     (4) If tty_control is TTYCTL_FULL:
     281         The kernel's action depends on L_ECHO(tty) and L_ISIG(tty), that is, on
     282         the local modes of the tty (see
     283         <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
     284         section 11.2.5).  We don't want to change L_ISIG; hence we change L_ECHO.
     285         So, we disable the ECHO local flag of the tty; the equivalent command is
     286         'stty -echo'.
     287     (5) If tty_control is TTYCTL_FULL:
     288         The kernel's action depends on !L_NOFLSH(tty), that is, again on the
     289         local modes of the tty (see
     290         <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
     291         section 11.2.5).  So, we enable the NOFLSH local flag of the tty; the
     292         equivalent command is 'stty noflsh'.
     293         For terminals with a baud rate < 9600 this is suboptimal.  For this case
     294         - where the traditional flushing behaviour makes sense - we would use a
     295         technique that involves tcdrain(), TIOCOUTQ, and usleep() when it is OK
     296         to disable NOFLSH.
     297  
     298     Regarding (4) and (5), there is a complication: Changing the local modes is
     299     done through tcsetattr().  However, when the process is put into the
     300     background, tcsetattr() does not operate the same way as when the process is
     301     running in the foreground.
     302     To test this kind of behaviour, use the 'color-filter' example like this:
     303       $ yes | ./filter '.*'
     304       <Ctrl-Z>
     305       $ bg 1
     306     We have three possible implementation options:
     307       * If we don't ignore the signal SIGTTOU:
     308         If the TOSTOP bit in the terminal's local mode is clear (command
     309         equivalent: 'stty -tostop') and the process is put into the background,
     310         normal output would continue (per POSIX
     311         <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
     312         section 11.2.5) but tcsetattr() calls would cause it to stop due to
     313         a SIGTTOU signal (per POSIX
     314         <https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html>).
     315         Thus, the program would behave differently with term-style-control than
     316         without.
     317       * If we ignore the signal SIGTTOU when the TOSTOP bit in the terminal's
     318         local mode is clear (i.e. when (tc.c_lflag & TOSTOP) == 0):
     319         The tcsetattr() calls do not stop the process, but they don't have the
     320         desired effect.
     321         On Linux, when I put the process into the background and then kill it with
     322         signal SIGINT, I can see that the last operation on the terminal settings
     323         (as shown by 'strace') is
     324           ioctl(1, TCSETSW, {B38400 opost isig icanon echo ...}) = 0
     325         and yet, once the process is terminated, the terminal settings contain
     326         '-echo', not 'echo'.
     327       * Don't call tcsetattr() if the process is not in the foreground.
     328         This approach produces reliable results.
     329  
     330     Blocking some signals while a non-default style is active is *not* useful:
     331       - It does not help against (1), since exit() is not a signal.
     332       - Signal handlers are the better approach against (2) and (3).
     333       - It does not help against (4) and (5), because the kernel's actions happen
     334         outside the process.  */
     335  #define BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT 0
     336  
     337  /* File descriptor of the currently active 'struct term_style_controller' and
     338     'struct term_style_user_data'.  */
     339  static int volatile term_fd = -1;
     340  
     341  #if HAVE_TCGETATTR
     342  
     343  /* Status of the process group of term_fd.  */
     344  typedef enum
     345  {
     346    PGRP_UNKNOWN = 0,     /* term_fd < 0.  Unknown status.  */
     347    PGRP_NO_TTY,          /* term_fd >= 0 but is not connected to a tty.  */
     348    PGRP_IN_FOREGROUND,   /* term_fd >= 0 is a tty.  This process is running in
     349                             the foreground.  */
     350    PGRP_IN_BACKGROUND    /* term_fd >= 0 is a tty.  This process is running in
     351                             the background.  */
     352  } pgrp_status_t;
     353  static pgrp_status_t volatile pgrp_status = PGRP_UNKNOWN;
     354  
     355  /* Update pgrp_status, depending on term_fd.  */
     356  static _GL_ASYNC_SAFE void
     357  update_pgrp_status (void)
     358  {
     359    int fd = term_fd;
     360    if (fd < 0)
     361      {
     362        pgrp_status = PGRP_UNKNOWN;
     363        log_message ("pgrp_status = PGRP_UNKNOWN\n");
     364      }
     365    else
     366      {
     367        pid_t p = tcgetpgrp (fd);
     368        if (p < 0)
     369          {
     370            pgrp_status = PGRP_NO_TTY;
     371            log_message ("pgrp_status = PGRP_NO_TTY\n");
     372          }
     373        else
     374          {
     375            /* getpgrp () changes when the process gets put into the background
     376               by a shell that implements job control.  */
     377            if (p == getpgrp ())
     378              {
     379                pgrp_status = PGRP_IN_FOREGROUND;
     380                log_message ("pgrp_status = PGRP_IN_FOREGROUND\n");
     381              }
     382            else
     383              {
     384                pgrp_status = PGRP_IN_BACKGROUND;
     385                log_message ("pgrp_status = PGRP_IN_BACKGROUND\n");
     386              }
     387          }
     388      }
     389  }
     390  
     391  #else
     392  
     393  # define update_pgrp_status()
     394  
     395  #endif
     396  
     397  /* Controller and its user_data that contain information about how to do
     398     output.  */
     399  static const struct term_style_controller * volatile active_controller;
     400  static struct term_style_user_data * volatile active_user_data;
     401  
     402  /* The 'struct term_style_control_data' embedded in active_user_data.
     403     Same as
     404       (active_controller != NULL
     405        ? active_controller->get_control_data (active_user_data)
     406        : NULL).  */
     407  static struct term_style_control_data * volatile active_control_data;
     408  
     409  /* The fd contained in active_control_data.
     410     Same as
     411       (active_controller != NULL
     412        ? active_control_data->fd
     413        : -1).  */
     414  static int volatile active_fd = -1;
     415  
     416  /* The exit handler.  */
     417  static void
     418  atexit_handler (void)
     419  {
     420    /* Only do something while some output was started but not completed.  */
     421    if (active_controller != NULL)
     422      {
     423        active_controller->restore (active_user_data);
     424        deactivate_term_non_default_mode (active_controller, active_user_data);
     425        #if 0 /* not needed */
     426        deactivate_term_style_controller (active_controller, active_user_data);
     427        #endif
     428      }
     429  }
     430  
     431  #if HAVE_TCGETATTR
     432  
     433  /* Return a failure message after tcsetattr() failed.  */
     434  static _GL_ASYNC_SAFE void
     435  tcsetattr_failed (char message[100], const char *caller)
     436  {
     437    int errnum = errno;
     438    strcpy (message, caller);
     439    strcat (message, ": tcsetattr(fd=");
     440    sprintf_integer (message + strlen (message), active_fd);
     441    strcat (message, ") failed, errno=");
     442    simple_errno_string (message + strlen (message), errnum);
     443    strcat (message, "\n");
     444  }
     445  
     446  /* True when orig_lflag represents the original tc.c_lflag.  */
     447  static bool volatile orig_lflag_set;
     448  static tcflag_t volatile orig_lflag;
     449  
     450  /* Modifies the tty's local mode, preparing for non-default terminal state.
     451     Used only when the active_control_data's tty_control is TTYCTL_FULL.  */
     452  static _GL_ASYNC_SAFE void
     453  clobber_local_mode (void)
     454  {
     455    /* Here, active_fd == term_fd.  */
     456    if (pgrp_status == PGRP_IN_FOREGROUND)
     457      {
     458        struct termios tc;
     459        if (nonintr_tcgetattr (active_fd, &tc) >= 0)
     460          {
     461            if (!orig_lflag_set)
     462              orig_lflag = tc.c_lflag;
     463            /* Set orig_lflag_set to true before actually modifying the tty's
     464               local mode, because restore_local_mode does nothing if
     465               orig_lflag_set is false.  */
     466            orig_lflag_set = true;
     467            tc.c_lflag &= ~ECHO;
     468            tc.c_lflag |= NOFLSH;
     469            if (nonintr_tcsetattr (active_fd, TCSANOW, &tc) < 0)
     470              {
     471                /* Since tcsetattr failed, restore_local_mode does not need to
     472                   restore anything.  Set orig_lflag_set to false to indicate
     473                   this.  */
     474                orig_lflag_set = false;
     475                {
     476                  char message[100];
     477                  tcsetattr_failed (message,
     478                                    "term-style-control:clobber_local_mode");
     479                  full_write (STDERR_FILENO, message, strlen (message));
     480                }
     481              }
     482          }
     483      }
     484  }
     485  
     486  /* Modifies the tty's local mode, once the terminal is back to the default state.
     487     Returns true if ECHO was turned off.
     488     Used only when the active_control_data's tty_control is TTYCTL_FULL.  */
     489  static _GL_ASYNC_SAFE bool
     490  restore_local_mode (void)
     491  {
     492    /* Here, active_fd == term_fd.  */
     493    bool echo_was_off = false;
     494    /* Nothing to do if !orig_lflag_set.  */
     495    if (orig_lflag_set)
     496      {
     497        struct termios tc;
     498        if (nonintr_tcgetattr (active_fd, &tc) >= 0)
     499          {
     500            echo_was_off = (tc.c_lflag & ECHO) == 0;
     501            tc.c_lflag = orig_lflag;
     502            if (nonintr_tcsetattr (active_fd, TCSADRAIN, &tc) < 0)
     503              {
     504                char message[100];
     505                tcsetattr_failed (message,
     506                                  "term-style-control:restore_local_mode");
     507                full_write (STDERR_FILENO, message, strlen (message));
     508              }
     509          }
     510        orig_lflag_set = false;
     511      }
     512    return echo_was_off;
     513  }
     514  
     515  #endif
     516  
     517  #if defined SIGCONT
     518  
     519  /* The list of signals whose default behaviour is to stop or continue the
     520     program.  */
     521  static int const job_control_signals[] =
     522    {
     523      #ifdef SIGTSTP
     524      SIGTSTP,
     525      #endif
     526      #ifdef SIGTTIN
     527      SIGTTIN,
     528      #endif
     529      #ifdef SIGTTOU
     530      SIGTTOU,
     531      #endif
     532      #ifdef SIGCONT
     533      SIGCONT,
     534      #endif
     535      0
     536    };
     537  
     538  # define num_job_control_signals (SIZEOF (job_control_signals) - 1)
     539  
     540  #endif
     541  
     542  /* The following signals are relevant because they output escape sequences to
     543     the terminal:
     544       - fatal signals,
     545       - stopping signals,
     546       - continuing signals (SIGCONT).  */
     547  
     548  static sigset_t relevant_signal_set;
     549  static bool relevant_signal_set_initialized = false;
     550  
     551  static void
     552  init_relevant_signal_set ()
     553  {
     554    if (!relevant_signal_set_initialized)
     555      {
     556        int fatal_signals[64];
     557        size_t num_fatal_signals;
     558        size_t i;
     559  
     560        num_fatal_signals = get_fatal_signals (fatal_signals);
     561  
     562        sigemptyset (&relevant_signal_set);
     563        for (i = 0; i < num_fatal_signals; i++)
     564          sigaddset (&relevant_signal_set, fatal_signals[i]);
     565        #if defined SIGCONT
     566        for (i = 0; i < num_job_control_signals; i++)
     567          sigaddset (&relevant_signal_set, job_control_signals[i]);
     568        #endif
     569  
     570        relevant_signal_set_initialized = true;
     571      }
     572  }
     573  
     574  /* Temporarily delay the relevant signals.  */
     575  static _GL_ASYNC_SAFE inline void
     576  block_relevant_signals ()
     577  {
     578    /* The caller must ensure that init_relevant_signal_set () was already
     579       called.  */
     580    if (!relevant_signal_set_initialized)
     581      abort ();
     582  
     583    sigprocmask (SIG_BLOCK, &relevant_signal_set, NULL);
     584  }
     585  
     586  /* Stop delaying the relevant signals.  */
     587  static _GL_ASYNC_SAFE inline void
     588  unblock_relevant_signals ()
     589  {
     590    sigprocmask (SIG_UNBLOCK, &relevant_signal_set, NULL);
     591  }
     592  
     593  #if defined SIGCONT
     594  
     595  /* Determines whether a signal is ignored.  */
     596  static _GL_ASYNC_SAFE bool
     597  is_ignored (int sig)
     598  {
     599    struct sigaction action;
     600  
     601    return (sigaction (sig, NULL, &action) >= 0
     602            && get_handler (&action) == SIG_IGN);
     603  }
     604  
     605  #endif
     606  
     607  #if HAVE_TCGETATTR
     608  
     609  /* Write the same signal marker that the kernel would have printed if ECHO had
     610     been turned on.  See (4) above.
     611     This is a makeshift and is not perfect:
     612       - When stderr refers to a different target than active_control_data->fd,
     613         it is too hairy to write the signal marker.
     614       - In some cases, when the signal was generated right before and delivered
     615         right after a clobber_local_mode invocation, the result is that the
     616         marker appears twice, e.g. ^C^C.  This occurs only with a small
     617         probability.
     618       - In some cases, when the signal was generated right before and delivered
     619         right after a restore_local_mode invocation, the result is that the
     620         marker does not appear at all.  This occurs only with a small
     621         probability.
     622     To test this kind of behaviour, use the 'test-term-style-control-yes' example
     623     like this:
     624       $ ./test-term-style-control-yes
     625   */
     626  static _GL_ASYNC_SAFE void
     627  show_signal_marker (int sig)
     628  {
     629    /* Write to stderr, not to active_control_data->fd, because
     630       active_control_data->fd is often logged or used with 'less -R'.  */
     631    if (active_controller != NULL && active_control_data->same_as_stderr)
     632      switch (sig)
     633        {
     634        /* The kernel's action when the user presses the INTR key.  */
     635        case SIGINT:
     636          full_write (STDERR_FILENO, "^C", 2); break;
     637        /* The kernel's action when the user presses the SUSP key.  */
     638        case SIGTSTP:
     639          full_write (STDERR_FILENO, "^Z", 2); break;
     640        /* The kernel's action when the user presses the QUIT key.  */
     641        case SIGQUIT:
     642          full_write (STDERR_FILENO, "^\\", 2); break;
     643        default: break;
     644        }
     645  }
     646  
     647  #endif
     648  
     649  /* The main code of the signal handler for fatal signals and stopping signals.
     650     It is reentrant.  */
     651  static _GL_ASYNC_SAFE void
     652  fatal_or_stopping_signal_handler (int sig)
     653  {
     654    #if HAVE_TCGETATTR
     655    bool echo_was_off = false;
     656    #endif
     657    /* Only do something while some output was interrupted.  */
     658    if (active_controller != NULL
     659        && active_control_data->tty_control != TTYCTL_NONE)
     660      {
     661        unsigned int i;
     662  
     663        /* Block the relevant signals.  This is needed, because the output
     664           of escape sequences below (usually through tputs invocations) is
     665           not reentrant.  */
     666        block_relevant_signals ();
     667  
     668        /* Restore the terminal to the default state.  */
     669        for (i = 0; i < 2; i++)
     670          active_controller->async_restore (active_user_data);
     671        #if HAVE_TCGETATTR
     672        if (active_control_data->tty_control == TTYCTL_FULL)
     673          {
     674            /* Restore the local mode, once the escape sequences output above
     675               have reached their destination.  */
     676            echo_was_off = restore_local_mode ();
     677          }
     678        #endif
     679  
     680        /* Unblock the relevant signals.  */
     681        unblock_relevant_signals ();
     682      }
     683  
     684    #if HAVE_TCGETATTR
     685    if (echo_was_off)
     686      show_signal_marker (sig);
     687    #endif
     688  }
     689  
     690  /* The signal handler for fatal signals.
     691     It is reentrant.  */
     692  static _GL_ASYNC_SAFE void
     693  fatal_signal_handler (int sig)
     694  {
     695    log_signal_handler_called (sig);
     696    fatal_or_stopping_signal_handler (sig);
     697  }
     698  
     699  #if defined SIGCONT
     700  
     701  /* The signal handler for stopping signals.
     702     It is reentrant.  */
     703  static _GL_ASYNC_SAFE void
     704  stopping_signal_handler (int sig)
     705  {
     706    int saved_errno = errno;
     707  
     708    log_signal_handler_called (sig);
     709    fatal_or_stopping_signal_handler (sig);
     710  
     711    /* Now execute the signal's default action.
     712       We reinstall the handler later, during the SIGCONT handler.  */
     713    {
     714      struct sigaction action;
     715      action.sa_handler = SIG_DFL;
     716      action.sa_flags = SA_NODEFER;
     717      sigemptyset (&action.sa_mask);
     718      sigaction (sig, &action, NULL);
     719    }
     720    errno = saved_errno;
     721    raise (sig);
     722  }
     723  
     724  /* The signal handler for SIGCONT.
     725     It is reentrant.  */
     726  static _GL_ASYNC_SAFE void
     727  continuing_signal_handler (int sigcont)
     728  {
     729    int saved_errno = errno;
     730  
     731    log_signal_handler_called (sigcont);
     732    update_pgrp_status ();
     733    /* Only do something while some output was interrupted.  */
     734    if (active_controller != NULL
     735        && active_control_data->tty_control != TTYCTL_NONE)
     736      {
     737        /* Reinstall the signals handlers removed in stopping_signal_handler.  */
     738        {
     739          unsigned int i;
     740  
     741          for (i = 0; i < num_job_control_signals; i++)
     742            {
     743              int sig = job_control_signals[i];
     744  
     745              if (sig != SIGCONT && !is_ignored (sig))
     746                {
     747                  struct sigaction action;
     748                  action.sa_handler = &stopping_signal_handler;
     749                  /* If we get a stopping or continuing signal while executing
     750                     stopping_signal_handler or continuing_signal_handler, enter
     751                     it recursively, since it is reentrant.
     752                     Hence no SA_RESETHAND.  */
     753                  action.sa_flags = SA_NODEFER;
     754                  sigemptyset (&action.sa_mask);
     755                  sigaction (sig, &action, NULL);
     756                }
     757            }
     758        }
     759  
     760        /* Block the relevant signals.  This is needed, because the output of
     761           escape sequences done inside the async_set_attributes_from_default
     762           call below is not reentrant.  */
     763        block_relevant_signals ();
     764  
     765        #if HAVE_TCGETATTR
     766        if (active_control_data->tty_control == TTYCTL_FULL)
     767          {
     768            /* Modify the local mode.  */
     769            clobber_local_mode ();
     770          }
     771        #endif
     772        /* Set the terminal attributes.  */
     773        active_controller->async_set_attributes_from_default (active_user_data);
     774  
     775        /* Unblock the relevant signals.  */
     776        unblock_relevant_signals ();
     777      }
     778  
     779    errno = saved_errno;
     780  }
     781  
     782  /* Ensure the signal handlers are installed.
     783     Once they are installed, we leave them installed.  It's not worth
     784     installing and uninstalling them each time we switch the terminal to a
     785     non-default state and back; instead we set active_controller to tell the
     786     signal handler whether it has something to do or not.  */
     787  
     788  static void
     789  ensure_continuing_signal_handler (void)
     790  {
     791    static bool signal_handler_installed = false;
     792  
     793    if (!signal_handler_installed)
     794      {
     795        int sig = SIGCONT;
     796        struct sigaction action;
     797        action.sa_handler = &continuing_signal_handler;
     798        /* If we get a stopping or continuing signal while executing
     799           continuing_signal_handler, enter it recursively, since it is
     800           reentrant.  Hence no SA_RESETHAND.  */
     801        action.sa_flags = SA_NODEFER;
     802        sigemptyset (&action.sa_mask);
     803        sigaction (sig, &action, NULL);
     804  
     805        signal_handler_installed = true;
     806      }
     807  }
     808  
     809  #endif
     810  
     811  static void
     812  ensure_other_signal_handlers (void)
     813  {
     814    static bool signal_handlers_installed = false;
     815  
     816    if (!signal_handlers_installed)
     817      {
     818        /* Install the handlers for the fatal signals.  */
     819        if (at_fatal_signal (fatal_signal_handler) < 0)
     820          xalloc_die ();
     821  
     822        #if defined SIGCONT
     823  
     824        /* Install the handlers for the stopping and continuing signals.  */
     825        {
     826          unsigned int i;
     827  
     828          for (i = 0; i < num_job_control_signals; i++)
     829            {
     830              int sig = job_control_signals[i];
     831  
     832              if (sig == SIGCONT)
     833                /* Already handled in ensure_continuing_signal_handler.  */
     834                ;
     835              else if (!is_ignored (sig))
     836                {
     837                  struct sigaction action;
     838                  action.sa_handler = &stopping_signal_handler;
     839                  /* If we get a stopping or continuing signal while executing
     840                     stopping_signal_handler, enter it recursively, since it is
     841                     reentrant.  Hence no SA_RESETHAND.  */
     842                  action.sa_flags = SA_NODEFER;
     843                  sigemptyset (&action.sa_mask);
     844                  sigaction (sig, &action, NULL);
     845                }
     846              #if DEBUG_SIGNALS
     847              else
     848                {
     849                  fprintf (stderr, "Signal %d is ignored. Not installing a handler!\n",
     850                           sig);
     851                  fflush (stderr);
     852                }
     853              #endif
     854            }
     855        }
     856  
     857        #endif
     858  
     859        signal_handlers_installed = true;
     860      }
     861  }
     862  
     863  
     864  /* ============================== Public API ============================== */
     865  
     866  void
     867  activate_term_non_default_mode (const struct term_style_controller *controller,
     868                                  struct term_style_user_data *user_data)
     869  {
     870    struct term_style_control_data *control_data =
     871      controller->get_control_data (user_data);
     872  
     873    if (!control_data->non_default_active)
     874      {
     875        if (control_data->tty_control != TTYCTL_NONE)
     876          ensure_other_signal_handlers ();
     877  
     878        #if BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT
     879        /* Block fatal signals, so that a SIGINT or similar doesn't interrupt
     880           us without the possibility of restoring the terminal's state.
     881           Likewise for SIGTSTP etc.  */
     882        block_relevant_signals ();
     883        #endif
     884  
     885        /* Enable the exit handler for restoring the terminal's state,
     886           and make the signal handlers effective.  */
     887        if (active_controller != NULL)
     888          {
     889            /* We can't support two active controllers with non-default
     890               attributes at the same time.  */
     891            abort ();
     892          }
     893        /* The uses of 'volatile' (and ISO C 99 section 5.1.2.3.(5)) ensure that
     894           we set active_controller to a non-NULL value only after the memory
     895           locations active_user_data, active_control_data, active_fd have been
     896           filled.  */
     897        active_fd = control_data->fd;
     898        active_control_data = control_data;
     899        active_user_data = user_data;
     900        active_controller = controller;
     901  
     902        #if HAVE_TCGETATTR
     903        /* Now that the signal handlers are effective, modify the tty.  */
     904        if (active_control_data->tty_control == TTYCTL_FULL)
     905          {
     906            /* Modify the local mode.  */
     907            clobber_local_mode ();
     908          }
     909        #endif
     910  
     911        control_data->non_default_active = true;
     912      }
     913  }
     914  
     915  void
     916  deactivate_term_non_default_mode (const struct term_style_controller *controller,
     917                                    struct term_style_user_data *user_data)
     918  {
     919    struct term_style_control_data *control_data =
     920      controller->get_control_data (user_data);
     921  
     922    if (control_data->non_default_active)
     923      {
     924        #if HAVE_TCGETATTR
     925        /* Before we make the signal handlers ineffective, modify the tty.  */
     926        if (active_control_data->tty_control == TTYCTL_FULL)
     927          {
     928            /* Restore the local mode, once the tputs calls from out_attr_change
     929               have reached their destination.  */
     930            restore_local_mode ();
     931          }
     932        #endif
     933  
     934        /* Disable the exit handler, and make the signal handlers ineffective.  */
     935        /* The uses of 'volatile' (and ISO C 99 section 5.1.2.3.(5)) ensure that
     936           we reset active_user_data, active_control_data, active_fd only after
     937           the memory location active_controller has been cleared.  */
     938        active_controller = NULL;
     939        active_user_data = NULL;
     940        active_control_data = NULL;
     941        active_fd = -1;
     942  
     943        #if BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT
     944        /* Unblock the relevant signals.  */
     945        unblock_relevant_signals ();
     946        #endif
     947  
     948        control_data->non_default_active = false;
     949      }
     950  }
     951  
     952  void
     953  activate_term_style_controller (const struct term_style_controller *controller,
     954                                  struct term_style_user_data *user_data,
     955                                  int fd, ttyctl_t tty_control)
     956  {
     957    struct term_style_control_data *control_data =
     958      controller->get_control_data (user_data);
     959  
     960    control_data->fd = fd;
     961  
     962    /* Prepare tty control.  */
     963    if (tty_control == TTYCTL_AUTO)
     964      tty_control = TTYCTL_FULL;
     965    control_data->tty_control = tty_control;
     966    if (control_data->tty_control != TTYCTL_NONE)
     967      init_relevant_signal_set ();
     968    #if HAVE_TCGETATTR
     969    if (control_data->tty_control == TTYCTL_FULL)
     970      {
     971        struct stat statbuf1;
     972        struct stat statbuf2;
     973        if (fd == STDERR_FILENO
     974            || (fstat (fd, &statbuf1) >= 0
     975                && fstat (STDERR_FILENO, &statbuf2) >= 0
     976                && psame_inode (&statbuf1, &statbuf2)))
     977          control_data->same_as_stderr = true;
     978        else
     979          control_data->same_as_stderr = false;
     980      }
     981    else
     982      /* This value is actually not used.  */
     983      control_data->same_as_stderr = false;
     984    #endif
     985  
     986    control_data->non_default_active = false;
     987  
     988    /* Start keeping track of the process group status.  */
     989    term_fd = fd;
     990    #if defined SIGCONT
     991    ensure_continuing_signal_handler ();
     992    #endif
     993    update_pgrp_status ();
     994  
     995    /* Register an exit handler.  */
     996    {
     997      static bool registered = false;
     998      if (!registered)
     999        {
    1000          atexit (atexit_handler);
    1001          registered = true;
    1002        }
    1003    }
    1004  }
    1005  
    1006  void
    1007  deactivate_term_style_controller (const struct term_style_controller *controller,
    1008                                    struct term_style_user_data *user_data)
    1009  {
    1010    struct term_style_control_data *control_data =
    1011      controller->get_control_data (user_data);
    1012  
    1013    /* Verify that the non-default attributes mode is turned off.  */
    1014    if (control_data->non_default_active)
    1015      abort ();
    1016  
    1017    term_fd = -1;
    1018    update_pgrp_status ();
    1019  }