neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

signal.c (5422B)


      1 #include <assert.h>
      2 #include <stdbool.h>
      3 #include <stdio.h>
      4 
      5 #ifndef MSWIN
      6 # include <signal.h>
      7 #endif
      8 
      9 #include "nvim/autocmd.h"
     10 #include "nvim/autocmd_defs.h"
     11 #include "nvim/buffer_defs.h"
     12 #include "nvim/eval/vars.h"
     13 #include "nvim/event/defs.h"
     14 #include "nvim/event/signal.h"
     15 #include "nvim/ex_cmds2.h"
     16 #include "nvim/globals.h"
     17 #include "nvim/log.h"
     18 #include "nvim/main.h"
     19 #include "nvim/option_vars.h"
     20 #include "nvim/os/signal.h"
     21 
     22 #ifdef SIGPWR
     23 # include "nvim/memline.h"
     24 #endif
     25 
     26 #ifdef MSWIN
     27 # include "nvim/os/os_win_console.h"
     28 #endif
     29 
     30 static SignalWatcher spipe, shup, sint, squit, sterm, susr1, swinch, ststp;
     31 #ifdef SIGPWR
     32 static SignalWatcher spwr;
     33 #endif
     34 
     35 static bool rejecting_deadly;
     36 
     37 #include "os/signal.c.generated.h"
     38 
     39 void signal_init(void)
     40 {
     41 #ifndef MSWIN
     42  // Ensure a clean slate by unblocking all signals. For example, if SIGCHLD is
     43  // blocked, libuv may hang after spawning a subprocess on Linux. #5230
     44  sigset_t mask;
     45  sigemptyset(&mask);
     46  if (pthread_sigmask(SIG_SETMASK, &mask, NULL) != 0) {
     47    ELOG("Could not unblock signals, nvim might behave strangely.");
     48  }
     49 #endif
     50 
     51  signal_watcher_init(&main_loop, &spipe, NULL);
     52  signal_watcher_init(&main_loop, &shup, NULL);
     53  signal_watcher_init(&main_loop, &sint, NULL);
     54  signal_watcher_init(&main_loop, &squit, NULL);
     55  signal_watcher_init(&main_loop, &sterm, NULL);
     56  signal_watcher_init(&main_loop, &ststp, NULL);
     57 #ifdef SIGPWR
     58  signal_watcher_init(&main_loop, &spwr, NULL);
     59 #endif
     60 #ifdef SIGUSR1
     61  signal_watcher_init(&main_loop, &susr1, NULL);
     62 #endif
     63 #ifdef SIGWINCH
     64  signal_watcher_init(&main_loop, &swinch, NULL);
     65 #endif
     66  signal_start();
     67 }
     68 
     69 void signal_teardown(void)
     70 {
     71  signal_stop();
     72  signal_watcher_close(&spipe, NULL);
     73  signal_watcher_close(&shup, NULL);
     74  signal_watcher_close(&sint, NULL);
     75  signal_watcher_close(&squit, NULL);
     76  signal_watcher_close(&sterm, NULL);
     77  signal_watcher_close(&ststp, NULL);
     78 #ifdef SIGPWR
     79  signal_watcher_close(&spwr, NULL);
     80 #endif
     81 #ifdef SIGUSR1
     82  signal_watcher_close(&susr1, NULL);
     83 #endif
     84 #ifdef SIGWINCH
     85  signal_watcher_close(&swinch, NULL);
     86 #endif
     87 }
     88 
     89 void signal_start(void)
     90 {
     91 #ifdef SIGPIPE
     92  signal_watcher_start(&spipe, on_signal, SIGPIPE);
     93 #endif
     94  signal_watcher_start(&shup, on_signal, SIGHUP);
     95  signal_watcher_start(&sint, on_signal, SIGINT);
     96 #ifdef SIGQUIT
     97  signal_watcher_start(&squit, on_signal, SIGQUIT);
     98 #endif
     99  signal_watcher_start(&sterm, on_signal, SIGTERM);
    100 #ifdef SIGTSTP
    101  signal_watcher_start(&ststp, on_signal, SIGTSTP);
    102 #endif
    103 #ifdef SIGPWR
    104  signal_watcher_start(&spwr, on_signal, SIGPWR);
    105 #endif
    106 #ifdef SIGUSR1
    107  signal_watcher_start(&susr1, on_signal, SIGUSR1);
    108 #endif
    109 #ifdef SIGWINCH
    110  signal_watcher_start(&swinch, on_signal, SIGWINCH);
    111 #endif
    112 }
    113 
    114 void signal_stop(void)
    115 {
    116 #ifdef SIGPIPE
    117  signal_watcher_stop(&spipe);
    118 #endif
    119  signal_watcher_stop(&shup);
    120  signal_watcher_stop(&sint);
    121 #ifdef SIGQUIT
    122  signal_watcher_stop(&squit);
    123 #endif
    124  signal_watcher_stop(&sterm);
    125  signal_watcher_stop(&ststp);
    126 #ifdef SIGPWR
    127  signal_watcher_stop(&spwr);
    128 #endif
    129 #ifdef SIGUSR1
    130  signal_watcher_stop(&susr1);
    131 #endif
    132 #ifdef SIGWINCH
    133  signal_watcher_stop(&swinch);
    134 #endif
    135 }
    136 
    137 void signal_reject_deadly(void)
    138 {
    139  rejecting_deadly = true;
    140 }
    141 
    142 void signal_accept_deadly(void)
    143 {
    144  rejecting_deadly = false;
    145 }
    146 
    147 static char *signal_name(int signum)
    148 {
    149  switch (signum) {
    150 #ifdef SIGPWR
    151  case SIGPWR:
    152    return "SIGPWR";
    153 #endif
    154 #ifdef SIGPIPE
    155  case SIGPIPE:
    156    return "SIGPIPE";
    157 #endif
    158  case SIGTERM:
    159    return "SIGTERM";
    160 #ifdef SIGTSTP
    161  case SIGTSTP:
    162    return "SIGTSTP";
    163 #endif
    164 #ifdef SIGQUIT
    165  case SIGQUIT:
    166    return "SIGQUIT";
    167 #endif
    168  case SIGHUP:
    169    return "SIGHUP";
    170  case SIGINT:
    171    return "SIGINT";
    172 #ifdef SIGUSR1
    173  case SIGUSR1:
    174    return "SIGUSR1";
    175 #endif
    176 #ifdef SIGWINCH
    177  case SIGWINCH:
    178    return "SIGWINCH";
    179 #endif
    180  default:
    181    return "Unknown";
    182  }
    183 }
    184 
    185 // This function handles deadly signals.
    186 // It tries to preserve any swap files and exit properly.
    187 // (partly from Elvis).
    188 // NOTE: this is scheduled on the event loop, not called directly from a signal handler.
    189 static void deadly_signal(int signum)
    190  FUNC_ATTR_NORETURN
    191 {
    192  // Set the v:dying variable.
    193  set_vim_var_nr(VV_DYING, 1);
    194  v_dying = 1;
    195 
    196  ILOG("got signal %d (%s)", signum, signal_name(signum));
    197 
    198  snprintf(IObuff, IOSIZE, "Nvim: Caught deadly signal '%s'\n", signal_name(signum));
    199 
    200  if (p_awa && signum != SIGTERM && signum != SIGINT) {
    201    autowrite_all();
    202  }
    203 
    204  // Preserve files and exit.
    205  preserve_exit(IObuff);
    206 }
    207 
    208 static void on_signal(SignalWatcher *handle, int signum, void *data)
    209 {
    210  assert(signum >= 0);
    211  switch (signum) {
    212 #ifdef SIGPWR
    213  case SIGPWR:
    214    // Signal of a power failure (eg batteries low), flush the swap files to be safe
    215    ml_sync_all(false, false, true);
    216    break;
    217 #endif
    218 #ifdef SIGPIPE
    219  case SIGPIPE:
    220    // Ignore
    221    break;
    222 #endif
    223 #ifdef SIGTSTP
    224  case SIGTSTP:
    225    if (p_awa) {
    226      autowrite_all();
    227    }
    228    break;
    229 #endif
    230  case SIGHUP:
    231 #ifdef MSWIN
    232    os_clear_hwnd();
    233 #endif
    234  case SIGINT:
    235  case SIGTERM:
    236 #ifdef SIGQUIT
    237  case SIGQUIT:
    238 #endif
    239    if (!rejecting_deadly) {
    240      deadly_signal(signum);
    241    }
    242    break;
    243 #ifdef SIGUSR1
    244  case SIGUSR1:
    245    apply_autocmds(EVENT_SIGNAL, "SIGUSR1", curbuf->b_fname, true, curbuf);
    246    break;
    247 #endif
    248 #ifdef SIGWINCH
    249  case SIGWINCH:
    250    apply_autocmds(EVENT_SIGNAL, "SIGWINCH", curbuf->b_fname, true, curbuf);
    251    break;
    252 #endif
    253  default:
    254    ELOG("invalid signal: %d", signum);
    255    break;
    256  }
    257 }