1/*
2 * signal.c
3 *
4 * Copyright (C) 2010-2018 Aerospike, Inc.
5 *
6 * Portions may be licensed to Aerospike, Inc. under one or more contributor
7 * license agreements.
8 *
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU Affero General Public License as published by the Free
11 * Software Foundation, either version 3 of the License, or (at your option) any
12 * later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see http://www.gnu.org/licenses/
21 */
22
23#include <pthread.h>
24#include <signal.h>
25#include <stdbool.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29
30#include "cf_thread.h"
31#include "fault.h"
32
33
34//==========================================================
35// Typedefs & constants.
36//
37
38typedef void (*action_t)(int sig, siginfo_t *info, void *ctx);
39
40// String constants in version.c, generated by make.
41extern const char aerospike_build_type[];
42extern const char aerospike_build_id[];
43extern const char aerospike_build_os[];
44
45
46//==========================================================
47// Globals.
48//
49
50// The mutex that the main function deadlocks on after starting the service.
51extern pthread_mutex_t g_main_deadlock;
52extern bool g_startup_complete;
53
54
55//==========================================================
56// Local helpers.
57//
58
59static inline void
60set_action(int sig_num, action_t act)
61{
62 struct sigaction sa;
63 memset(&sa, 0, sizeof(sa));
64
65 sa.sa_sigaction = act;
66 sigemptyset(&sa.sa_mask);
67 // SA_SIGINFO prefers sa_sigaction over sa_handler.
68 sa.sa_flags = SA_RESTART | SA_SIGINFO;
69
70 if (sigaction(sig_num, &sa, NULL) < 0) {
71 cf_crash(AS_AS, "could not register signal handler for %d", sig_num);
72 }
73}
74
75static inline void
76set_handler(int sig_num, sighandler_t hand)
77{
78 struct sigaction sa;
79 memset(&sa, 0, sizeof(sa));
80
81 sa.sa_handler = hand;
82 sigemptyset(&sa.sa_mask);
83 // No SA_SIGINFO; use sa_handler.
84 sa.sa_flags = SA_RESTART;
85
86 if (sigaction(sig_num, &sa, NULL) < 0) {
87 cf_crash(AS_AS, "could not register signal handler for %d", sig_num);
88 }
89}
90
91static inline void
92reraise_signal(int sig_num)
93{
94 set_handler(sig_num, SIG_DFL);
95 raise(sig_num);
96}
97
98
99//==========================================================
100// Signal handlers.
101//
102
103// We get here on some crashes.
104void
105as_sig_handle_abort(int sig_num, siginfo_t *info, void *ctx)
106{
107 cf_warning(AS_AS, "SIGABRT received, aborting %s build %s os %s",
108 aerospike_build_type, aerospike_build_id, aerospike_build_os);
109
110 cf_fault_print_signal_context(ctx);
111 reraise_signal(sig_num);
112}
113
114void
115as_sig_handle_bus(int sig_num, siginfo_t *info, void *ctx)
116{
117 cf_warning(AS_AS, "SIGBUS received, aborting %s build %s os %s",
118 aerospike_build_type, aerospike_build_id, aerospike_build_os);
119
120 cf_fault_print_signal_context(ctx);
121 reraise_signal(sig_num);
122}
123
124// Floating point exception.
125void
126as_sig_handle_fpe(int sig_num, siginfo_t *info, void *ctx)
127{
128 cf_warning(AS_AS, "SIGFPE received, aborting %s build %s os %s",
129 aerospike_build_type, aerospike_build_id, aerospike_build_os);
130
131 cf_fault_print_signal_context(ctx);
132 reraise_signal(sig_num);
133}
134
135// This signal is our cue to roll the log.
136void
137as_sig_handle_hup(int sig_num, siginfo_t *info, void *ctx)
138{
139 cf_info(AS_AS, "SIGHUP received, rolling log");
140
141 cf_fault_sink_logroll();
142}
143
144// We get here on some crashes.
145void
146as_sig_handle_ill(int sig_num, siginfo_t *info, void *ctx)
147{
148 cf_warning(AS_AS, "SIGILL received, aborting %s build %s os %s",
149 aerospike_build_type, aerospike_build_id, aerospike_build_os);
150
151 cf_fault_print_signal_context(ctx);
152 reraise_signal(sig_num);
153}
154
155// We get here on cf_crash_nostack(), cf_assert_nostack(), or Ctrl-C when
156// running in foreground.
157void
158as_sig_handle_int(int sig_num, siginfo_t *info, void *ctx)
159{
160 cf_warning(AS_AS, "SIGINT received, shutting down %s build %s os %s",
161 aerospike_build_type, aerospike_build_id, aerospike_build_os);
162
163 if (! g_startup_complete) {
164 cf_warning(AS_AS, "startup was not complete, exiting immediately");
165 _exit(1);
166 }
167
168 pthread_mutex_unlock(&g_main_deadlock);
169}
170
171// We get here if we intentionally trigger the signal.
172void
173as_sig_handle_quit(int sig_num, siginfo_t *info, void *ctx)
174{
175 cf_warning(AS_AS, "SIGQUIT received, aborting %s build %s os %s",
176 aerospike_build_type, aerospike_build_id, aerospike_build_os);
177
178 cf_fault_print_signal_context(ctx);
179 reraise_signal(sig_num);
180}
181
182// We get here on some crashes.
183void
184as_sig_handle_segv(int sig_num, siginfo_t *info, void *ctx)
185{
186 cf_warning(AS_AS, "SIGSEGV received, aborting %s build %s os %s",
187 aerospike_build_type, aerospike_build_id, aerospike_build_os);
188
189 cf_fault_print_signal_context(ctx);
190 reraise_signal(sig_num);
191}
192
193// We get here on normal shutdown.
194void
195as_sig_handle_term(int sig_num, siginfo_t *info, void *ctx)
196{
197 cf_info(AS_AS, "SIGTERM received, shutting down %s build %s os %s",
198 aerospike_build_type, aerospike_build_id, aerospike_build_os);
199
200 if (! g_startup_complete) {
201 cf_warning(AS_AS, "startup was not complete, exiting immediately");
202 _exit(0);
203 }
204
205 pthread_mutex_unlock(&g_main_deadlock);
206}
207
208// We get here on cf_crash() and cf_assert().
209void
210as_sig_handle_usr1(int sig_num, siginfo_t *info, void *ctx)
211{
212 cf_warning(AS_AS, "SIGUSR1 received, aborting %s build %s os %s",
213 aerospike_build_type, aerospike_build_id, aerospike_build_os);
214
215 cf_fault_print_signal_context(ctx);
216 reraise_signal(SIGABRT);
217}
218
219
220//==========================================================
221// Public API.
222//
223
224void
225as_signal_setup()
226{
227 set_action(SIGABRT, as_sig_handle_abort);
228 set_action(SIGBUS, as_sig_handle_bus);
229 set_action(SIGFPE, as_sig_handle_fpe);
230 set_action(SIGHUP, as_sig_handle_hup);
231 set_action(SIGILL, as_sig_handle_ill);
232 set_action(SIGINT, as_sig_handle_int);
233 set_action(SIGQUIT, as_sig_handle_quit);
234 set_action(SIGSEGV, as_sig_handle_segv);
235 set_action(SIGTERM, as_sig_handle_term);
236 set_action(SIGUSR1, as_sig_handle_usr1);
237 set_action(SIGUSR2, cf_thread_traces_action);
238
239 // Block SIGPIPE signal when there is some error while writing to pipe. The
240 // write() call will return with a normal error which we can handle.
241 set_handler(SIGPIPE, SIG_IGN);
242}
243