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 }