1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
3 | |
4 | #include <assert.h> |
5 | #include <stdbool.h> |
6 | |
7 | #include <uv.h> |
8 | #ifndef WIN32 |
9 | # include <signal.h> // for sigset_t |
10 | #endif |
11 | |
12 | #include "nvim/ascii.h" |
13 | #include "nvim/log.h" |
14 | #include "nvim/vim.h" |
15 | #include "nvim/globals.h" |
16 | #include "nvim/memline.h" |
17 | #include "nvim/eval.h" |
18 | #include "nvim/fileio.h" |
19 | #include "nvim/main.h" |
20 | #include "nvim/memory.h" |
21 | #include "nvim/misc1.h" |
22 | #include "nvim/event/signal.h" |
23 | #include "nvim/os/signal.h" |
24 | #include "nvim/event/loop.h" |
25 | |
26 | static SignalWatcher spipe, shup, squit, sterm, susr1; |
27 | #ifdef SIGPWR |
28 | static SignalWatcher spwr; |
29 | #endif |
30 | |
31 | static bool rejecting_deadly; |
32 | |
33 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
34 | # include "os/signal.c.generated.h" |
35 | #endif |
36 | |
37 | void signal_init(void) |
38 | { |
39 | #ifndef WIN32 |
40 | // Ensure a clean slate by unblocking all signals. For example, if SIGCHLD is |
41 | // blocked, libuv may hang after spawning a subprocess on Linux. #5230 |
42 | sigset_t mask; |
43 | sigemptyset(&mask); |
44 | if (pthread_sigmask(SIG_SETMASK, &mask, NULL) != 0) { |
45 | ELOG("Could not unblock signals, nvim might behave strangely." ); |
46 | } |
47 | #endif |
48 | |
49 | signal_watcher_init(&main_loop, &spipe, NULL); |
50 | signal_watcher_init(&main_loop, &shup, NULL); |
51 | signal_watcher_init(&main_loop, &squit, NULL); |
52 | signal_watcher_init(&main_loop, &sterm, NULL); |
53 | #ifdef SIGPIPE |
54 | signal_watcher_start(&spipe, on_signal, SIGPIPE); |
55 | #endif |
56 | signal_watcher_start(&shup, on_signal, SIGHUP); |
57 | #ifdef SIGQUIT |
58 | signal_watcher_start(&squit, on_signal, SIGQUIT); |
59 | #endif |
60 | signal_watcher_start(&sterm, on_signal, SIGTERM); |
61 | #ifdef SIGPWR |
62 | signal_watcher_init(&main_loop, &spwr, NULL); |
63 | signal_watcher_start(&spwr, on_signal, SIGPWR); |
64 | #endif |
65 | #ifdef SIGUSR1 |
66 | signal_watcher_init(&main_loop, &susr1, NULL); |
67 | signal_watcher_start(&susr1, on_signal, SIGUSR1); |
68 | #endif |
69 | } |
70 | |
71 | void signal_teardown(void) |
72 | { |
73 | signal_stop(); |
74 | signal_watcher_close(&spipe, NULL); |
75 | signal_watcher_close(&shup, NULL); |
76 | signal_watcher_close(&squit, NULL); |
77 | signal_watcher_close(&sterm, NULL); |
78 | #ifdef SIGPWR |
79 | signal_watcher_close(&spwr, NULL); |
80 | #endif |
81 | #ifdef SIGUSR1 |
82 | signal_watcher_close(&susr1, NULL); |
83 | #endif |
84 | } |
85 | |
86 | void signal_stop(void) |
87 | { |
88 | signal_watcher_stop(&spipe); |
89 | signal_watcher_stop(&shup); |
90 | signal_watcher_stop(&squit); |
91 | signal_watcher_stop(&sterm); |
92 | #ifdef SIGPWR |
93 | signal_watcher_stop(&spwr); |
94 | #endif |
95 | #ifdef SIGUSR1 |
96 | signal_watcher_stop(&susr1); |
97 | #endif |
98 | } |
99 | |
100 | void signal_reject_deadly(void) |
101 | { |
102 | rejecting_deadly = true; |
103 | } |
104 | |
105 | void signal_accept_deadly(void) |
106 | { |
107 | rejecting_deadly = false; |
108 | } |
109 | |
110 | static char * signal_name(int signum) |
111 | { |
112 | switch (signum) { |
113 | #ifdef SIGPWR |
114 | case SIGPWR: |
115 | return "SIGPWR" ; |
116 | #endif |
117 | #ifdef SIGPIPE |
118 | case SIGPIPE: |
119 | return "SIGPIPE" ; |
120 | #endif |
121 | case SIGTERM: |
122 | return "SIGTERM" ; |
123 | #ifdef SIGQUIT |
124 | case SIGQUIT: |
125 | return "SIGQUIT" ; |
126 | #endif |
127 | case SIGHUP: |
128 | return "SIGHUP" ; |
129 | #ifdef SIGUSR1 |
130 | case SIGUSR1: |
131 | return "SIGUSR1" ; |
132 | #endif |
133 | default: |
134 | return "Unknown" ; |
135 | } |
136 | } |
137 | |
138 | // This function handles deadly signals. |
139 | // It tries to preserve any swap files and exit properly. |
140 | // (partly from Elvis). |
141 | // NOTE: Avoid unsafe functions, such as allocating memory, they can result in |
142 | // a deadlock. |
143 | static void deadly_signal(int signum) |
144 | { |
145 | // Set the v:dying variable. |
146 | set_vim_var_nr(VV_DYING, 1); |
147 | |
148 | WLOG("got signal %d (%s)" , signum, signal_name(signum)); |
149 | |
150 | snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\n" , |
151 | signal_name(signum)); |
152 | |
153 | // Preserve files and exit. |
154 | preserve_exit(); |
155 | } |
156 | |
157 | static void on_signal(SignalWatcher *handle, int signum, void *data) |
158 | { |
159 | assert(signum >= 0); |
160 | switch (signum) { |
161 | #ifdef SIGPWR |
162 | case SIGPWR: |
163 | // Signal of a power failure(eg batteries low), flush the swap files to |
164 | // be safe |
165 | ml_sync_all(false, false, true); |
166 | break; |
167 | #endif |
168 | #ifdef SIGPIPE |
169 | case SIGPIPE: |
170 | // Ignore |
171 | break; |
172 | #endif |
173 | case SIGTERM: |
174 | #ifdef SIGQUIT |
175 | case SIGQUIT: |
176 | #endif |
177 | case SIGHUP: |
178 | if (!rejecting_deadly) { |
179 | deadly_signal(signum); |
180 | } |
181 | break; |
182 | #ifdef SIGUSR1 |
183 | case SIGUSR1: |
184 | apply_autocmds(EVENT_SIGNAL, (char_u *)"SIGUSR1" , curbuf->b_fname, true, |
185 | curbuf); |
186 | break; |
187 | #endif |
188 | default: |
189 | ELOG("invalid signal: %d" , signum); |
190 | break; |
191 | } |
192 | } |
193 | |