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
26static SignalWatcher spipe, shup, squit, sterm, susr1;
27#ifdef SIGPWR
28static SignalWatcher spwr;
29#endif
30
31static bool rejecting_deadly;
32
33#ifdef INCLUDE_GENERATED_DECLARATIONS
34# include "os/signal.c.generated.h"
35#endif
36
37void 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
71void 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
86void 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
100void signal_reject_deadly(void)
101{
102 rejecting_deadly = true;
103}
104
105void signal_accept_deadly(void)
106{
107 rejecting_deadly = false;
108}
109
110static 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.
143static 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
157static 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