1 // The library for various signal related issues.
2 #include "config.h" // IWYU pragma: keep
3
4 #include <errno.h>
5 #include <signal.h>
6 #include <stdio.h>
7 #ifdef HAVE_SIGINFO_H
8 #include <siginfo.h>
9 #endif
10 #include <pthread.h>
11
12 #include "common.h"
13 #include "event.h"
14 #include "fallback.h" // IWYU pragma: keep
15 #include "parser.h"
16 #include "proc.h"
17 #include "reader.h"
18 #include "signal.h"
19 #include "termsize.h"
20 #include "topic_monitor.h"
21 #include "wutil.h" // IWYU pragma: keep
22
23 /// Struct describing an entry for the lookup table used to convert between signal names and signal
24 /// ids, etc.
25 struct lookup_entry {
26 /// Signal id.
27 int signal;
28 /// Signal name.
29 const wchar_t *name;
30 /// Signal description.
31 const wchar_t *desc;
32 };
33
34 /// Lookup table used to convert between signal names and signal ids, etc.
35 static const struct lookup_entry signal_table[] = {
36 #ifdef SIGHUP
37 {SIGHUP, L"SIGHUP", N_(L"Terminal hung up")},
38 #endif
39 #ifdef SIGINT
40 {SIGINT, L"SIGINT", N_(L"Quit request from job control (^C)")},
41 #endif
42 #ifdef SIGQUIT
43 {SIGQUIT, L"SIGQUIT", N_(L"Quit request from job control with core dump (^\\)")},
44 #endif
45 #ifdef SIGILL
46 {SIGILL, L"SIGILL", N_(L"Illegal instruction")},
47 #endif
48 #ifdef SIGTRAP
49 {SIGTRAP, L"SIGTRAP", N_(L"Trace or breakpoint trap")},
50 #endif
51 #ifdef SIGABRT
52 {SIGABRT, L"SIGABRT", N_(L"Abort")},
53 #endif
54 #ifdef SIGBUS
55 {SIGBUS, L"SIGBUS", N_(L"Misaligned address error")},
56 #endif
57 #ifdef SIGFPE
58 {SIGFPE, L"SIGFPE", N_(L"Floating point exception")},
59 #endif
60 #ifdef SIGKILL
61 {SIGKILL, L"SIGKILL", N_(L"Forced quit")},
62 #endif
63 #ifdef SIGUSR1
64 {SIGUSR1, L"SIGUSR1", N_(L"User defined signal 1")},
65 #endif
66 #ifdef SIGUSR2
67 {SIGUSR2, L"SIGUSR2", N_(L"User defined signal 2")},
68 #endif
69 #ifdef SIGSEGV
70 {SIGSEGV, L"SIGSEGV", N_(L"Address boundary error")},
71 #endif
72 #ifdef SIGPIPE
73 {SIGPIPE, L"SIGPIPE", N_(L"Broken pipe")},
74 #endif
75 #ifdef SIGALRM
76 {SIGALRM, L"SIGALRM", N_(L"Timer expired")},
77 #endif
78 #ifdef SIGTERM
79 {SIGTERM, L"SIGTERM", N_(L"Polite quit request")},
80 #endif
81 #ifdef SIGCHLD
82 {SIGCHLD, L"SIGCHLD", N_(L"Child process status changed")},
83 #endif
84 #ifdef SIGCONT
85 {SIGCONT, L"SIGCONT", N_(L"Continue previously stopped process")},
86 #endif
87 #ifdef SIGSTOP
88 {SIGSTOP, L"SIGSTOP", N_(L"Forced stop")},
89 #endif
90 #ifdef SIGTSTP
91 {SIGTSTP, L"SIGTSTP", N_(L"Stop request from job control (^Z)")},
92 #endif
93 #ifdef SIGTTIN
94 {SIGTTIN, L"SIGTTIN", N_(L"Stop from terminal input")},
95 #endif
96 #ifdef SIGTTOU
97 {SIGTTOU, L"SIGTTOU", N_(L"Stop from terminal output")},
98 #endif
99 #ifdef SIGURG
100 {SIGURG, L"SIGURG", N_(L"Urgent socket condition")},
101 #endif
102 #ifdef SIGXCPU
103 {SIGXCPU, L"SIGXCPU", N_(L"CPU time limit exceeded")},
104 #endif
105 #ifdef SIGXFSZ
106 {SIGXFSZ, L"SIGXFSZ", N_(L"File size limit exceeded")},
107 #endif
108 #ifdef SIGVTALRM
109 {SIGVTALRM, L"SIGVTALRM", N_(L"Virtual timer expired")},
110 #endif
111 #ifdef SIGPROF
112 {SIGPROF, L"SIGPROF", N_(L"Profiling timer expired")},
113 #endif
114 #ifdef SIGWINCH
115 {SIGWINCH, L"SIGWINCH", N_(L"Window size change")},
116 #endif
117 #ifdef SIGWIND
118 {SIGWIND, L"SIGWIND", N_(L"Window size change")},
119 #endif
120 #ifdef SIGIO
121 {SIGIO, L"SIGIO", N_(L"I/O on asynchronous file descriptor is possible")},
122 #endif
123 #ifdef SIGPWR
124 {SIGPWR, L"SIGPWR", N_(L"Power failure")},
125 #endif
126 #ifdef SIGSYS
127 {SIGSYS, L"SIGSYS", N_(L"Bad system call")},
128 #endif
129 #ifdef SIGINFO
130 {SIGINFO, L"SIGINFO", N_(L"Information request")},
131 #endif
132 #ifdef SIGSTKFLT
133 {SIGSTKFLT, L"SISTKFLT", N_(L"Stack fault")},
134 #endif
135 #ifdef SIGEMT
136 {SIGEMT, L"SIGEMT", N_(L"Emulator trap")},
137 #endif
138 #ifdef SIGIOT
139 {SIGIOT, L"SIGIOT", N_(L"Abort (Alias for SIGABRT)")},
140 #endif
141 #ifdef SIGUNUSED
142 {SIGUNUSED, L"SIGUNUSED", N_(L"Unused signal")},
143 #endif
144 };
145
146 /// Test if \c name is a string describing the signal named \c canonical.
match_signal_name(const wchar_t * canonical,const wchar_t * name)147 static int match_signal_name(const wchar_t *canonical, const wchar_t *name) {
148 if (wcsncasecmp(name, L"sig", const_strlen("sig")) == 0) name += 3;
149
150 return wcscasecmp(canonical + const_strlen("sig"), name) == 0;
151 }
152
wcs2sig(const wchar_t * str)153 int wcs2sig(const wchar_t *str) {
154 for (const auto &data : signal_table) {
155 if (match_signal_name(data.name, str)) {
156 return data.signal;
157 }
158 }
159
160 int res = fish_wcstoi(str);
161 if (errno || res < 0) return -1;
162 return res;
163 }
164
sig2wcs(int sig)165 const wchar_t *sig2wcs(int sig) {
166 for (const auto &data : signal_table) {
167 if (data.signal == sig) {
168 return data.name;
169 }
170 }
171
172 return _(L"Unknown");
173 }
174
signal_get_desc(int sig)175 const wchar_t *signal_get_desc(int sig) {
176 for (const auto &data : signal_table) {
177 if (data.signal == sig) {
178 return _(data.desc);
179 }
180 }
181
182 return _(L"Unknown");
183 }
184
185 /// Store the "main" pid. This allows us to reliably determine if we are in a forked child.
186 static const pid_t s_main_pid = getpid();
187
188 /// It's possible that we receive a signal after we have forked, but before we have reset the signal
189 /// handlers (or even run the pthread_atfork calls). In that event we will do something dumb like
190 /// swallow SIGINT. Ensure that doesn't happen. Check if we are the main fish process; if not, reset
191 /// and re-raise the signal. \return whether we re-raised the signal.
reraise_if_forked_child(int sig)192 static bool reraise_if_forked_child(int sig) {
193 // Don't use is_forked_child: it relies on atfork handlers which may have not yet run.
194 if (getpid() == s_main_pid) {
195 return false;
196 }
197 signal(sig, SIG_DFL);
198 raise(sig);
199 return true;
200 }
201
202 /// The cancellation signal we have received.
203 /// Of course this is modified from a signal handler.
204 static volatile relaxed_atomic_t<sig_atomic_t> s_cancellation_signal{0};
205
signal_clear_cancel()206 void signal_clear_cancel() { s_cancellation_signal = 0; }
207
signal_check_cancel()208 int signal_check_cancel() { return s_cancellation_signal; }
209
210 /// The single signal handler. By centralizing signal handling we ensure that we can never install
211 /// the "wrong" signal handler (see #5969).
fish_signal_handler(int sig,siginfo_t * info,void * context)212 static void fish_signal_handler(int sig, siginfo_t *info, void *context) {
213 UNUSED(info);
214 UNUSED(context);
215
216 // Ensure we preserve errno.
217 const int saved_errno = errno;
218
219 // Check if we are a forked child.
220 if (reraise_if_forked_child(sig)) {
221 errno = saved_errno;
222 return;
223 }
224
225 // Check if fish script cares about this.
226 const bool observed = event_is_signal_observed(sig);
227 if (observed) {
228 event_enqueue_signal(sig);
229 }
230
231 // Do some signal-specific stuff.
232 switch (sig) {
233 #ifdef SIGWINCH
234 case SIGWINCH:
235 /// Respond to a winch signal by telling the termsize container.
236 termsize_container_t::handle_winch();
237 break;
238 #endif
239
240 case SIGHUP:
241 /// Respond to a hup signal by exiting, unless it is caught by a shellscript function,
242 /// in which case we do nothing.
243 if (!observed) {
244 reader_sighup();
245 }
246 topic_monitor_t::principal().post(topic_t::sighupint);
247 break;
248
249 case SIGTERM:
250 /// Handle sigterm. The only thing we do is restore the front process ID, then die.
251 restore_term_foreground_process_group_for_exit();
252 signal(SIGTERM, SIG_DFL);
253 raise(SIGTERM);
254 break;
255
256 case SIGINT:
257 /// Interactive mode ^C handler. Respond to int signal by setting interrupted-flag and
258 /// stopping all loops and conditionals.
259 s_cancellation_signal = SIGINT;
260 reader_handle_sigint();
261 topic_monitor_t::principal().post(topic_t::sighupint);
262 break;
263
264 case SIGCHLD:
265 // A child process stopped or exited.
266 topic_monitor_t::principal().post(topic_t::sigchld);
267 break;
268
269 case SIGALRM:
270 // We have a sigalarm handler that does nothing. This is used in the signal torture
271 // test, to verify that we behave correctly when receiving lots of irrelevant signals.
272 break;
273 }
274 errno = saved_errno;
275 }
276
signal_reset_handlers()277 void signal_reset_handlers() {
278 struct sigaction act;
279 sigemptyset(&act.sa_mask);
280 act.sa_flags = 0;
281 act.sa_handler = SIG_DFL;
282
283 for (const auto &data : signal_table) {
284 if (data.signal == SIGHUP) {
285 struct sigaction oact;
286 sigaction(SIGHUP, nullptr, &oact);
287 if (oact.sa_handler == SIG_IGN) continue;
288 }
289 sigaction(data.signal, &act, nullptr);
290 }
291 }
292
set_interactive_handlers()293 static void set_interactive_handlers() {
294 struct sigaction act, oact;
295 act.sa_flags = 0;
296 oact.sa_flags = 0;
297 sigemptyset(&act.sa_mask);
298
299 // Interactive mode. Ignore interactive signals. We are a shell, we know what is best for
300 // the user.
301 act.sa_handler = SIG_IGN;
302 sigaction(SIGTSTP, &act, nullptr);
303 sigaction(SIGTTOU, &act, nullptr);
304
305 // We don't ignore SIGTTIN because we might send it to ourself.
306 act.sa_sigaction = &fish_signal_handler;
307 act.sa_flags = SA_SIGINFO;
308 sigaction(SIGTTIN, &act, nullptr);
309
310 // SIGTERM restores the terminal controlling process before dying.
311 act.sa_sigaction = &fish_signal_handler;
312 act.sa_flags = SA_SIGINFO;
313 sigaction(SIGTERM, &act, nullptr);
314
315 sigaction(SIGHUP, nullptr, &oact);
316 if (oact.sa_handler == SIG_DFL) {
317 act.sa_sigaction = &fish_signal_handler;
318 act.sa_flags = SA_SIGINFO;
319 sigaction(SIGHUP, &act, nullptr);
320 }
321
322 // SIGALARM as part of our signal torture test
323 act.sa_sigaction = &fish_signal_handler;
324 act.sa_flags = SA_SIGINFO;
325 sigaction(SIGALRM, &act, nullptr);
326
327 #ifdef SIGWINCH
328 act.sa_sigaction = &fish_signal_handler;
329 act.sa_flags = SA_SIGINFO;
330 sigaction(SIGWINCH, &act, nullptr);
331 #endif
332 }
333
334 /// Sets up appropriate signal handlers.
signal_set_handlers(bool interactive)335 void signal_set_handlers(bool interactive) {
336 struct sigaction act;
337 act.sa_flags = 0;
338 sigemptyset(&act.sa_mask);
339
340 // Ignore SIGPIPE. We'll detect failed writes and deal with them appropriately. We don't want
341 // this signal interrupting other syscalls or terminating us.
342 act.sa_sigaction = nullptr;
343 act.sa_handler = SIG_IGN;
344 sigaction(SIGPIPE, &act, nullptr);
345
346 // Ignore SIGQUIT.
347 act.sa_handler = SIG_IGN;
348 sigaction(SIGQUIT, &act, nullptr);
349
350 // Apply our SIGINT handler.
351 act.sa_sigaction = &fish_signal_handler;
352 act.sa_flags = SA_SIGINFO;
353 sigaction(SIGINT, &act, nullptr);
354
355 // Whether or not we're interactive we want SIGCHLD to not interrupt restartable syscalls.
356 act.sa_sigaction = &fish_signal_handler;
357 act.sa_flags = SA_SIGINFO | SA_RESTART;
358 if (sigaction(SIGCHLD, &act, nullptr)) {
359 wperror(L"sigaction");
360 FATAL_EXIT();
361 }
362
363 if (interactive) {
364 set_interactive_handlers();
365 }
366 }
367
signal_set_handlers_once(bool interactive)368 void signal_set_handlers_once(bool interactive) {
369 static std::once_flag s_noninter_once;
370 std::call_once(s_noninter_once, signal_set_handlers, false);
371
372 static std::once_flag s_inter_once;
373 if (interactive) std::call_once(s_inter_once, set_interactive_handlers);
374 }
375
signal_handle(int sig)376 void signal_handle(int sig) {
377 struct sigaction act;
378
379 // These should always be handled.
380 if ((sig == SIGINT) || (sig == SIGQUIT) || (sig == SIGTSTP) || (sig == SIGTTIN) ||
381 (sig == SIGTTOU) || (sig == SIGCHLD))
382 return;
383
384 act.sa_flags = 0;
385 sigemptyset(&act.sa_mask);
386 act.sa_flags = SA_SIGINFO;
387 act.sa_sigaction = &fish_signal_handler;
388 sigaction(sig, &act, nullptr);
389 }
390
get_signals_with_handlers(sigset_t * set)391 void get_signals_with_handlers(sigset_t *set) {
392 sigemptyset(set);
393 for (const auto &data : signal_table) {
394 struct sigaction act = {};
395 sigaction(data.signal, nullptr, &act);
396 // If SIGHUP is being ignored (e.g., because were were run via `nohup`) don't reset it.
397 // We don't special case other signals because if they're being ignored that shouldn't
398 // affect processes we spawn. They should get the default behavior for those signals.
399 if (data.signal == SIGHUP && act.sa_handler == SIG_IGN) continue;
400 if (act.sa_handler != SIG_DFL) sigaddset(set, data.signal);
401 }
402 }
403
404 /// Ensure we did not inherit any blocked signals. See issue #3964.
signal_unblock_all()405 void signal_unblock_all() {
406 sigset_t iset;
407 sigemptyset(&iset);
408 sigprocmask(SIG_SETMASK, &iset, nullptr);
409 }
410
sigchecker_t(topic_t signal)411 sigchecker_t::sigchecker_t(topic_t signal) : topic_(signal) {
412 // Call check() to update our generation.
413 check();
414 }
415
check()416 bool sigchecker_t::check() {
417 auto &tm = topic_monitor_t::principal();
418 generation_t gen = tm.generation_for_topic(topic_);
419 bool changed = this->gen_ != gen;
420 this->gen_ = gen;
421 return changed;
422 }
423
wait() const424 void sigchecker_t::wait() const {
425 auto &tm = topic_monitor_t::principal();
426 generation_list_t gens = generation_list_t::invalids();
427 gens.at(topic_) = this->gen_;
428 tm.check(&gens, true /* wait */);
429 }
430