master
c 310 lines 11.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "common.h"
4 #include "daemon/status-file.h"
5 #include "protected-access.h"
6
7 #ifdef ENABLE_SENTRY
8 #include "sentry-native/sentry-native.h"
9 #endif
10
11 typedef enum signal_action {
12 NETDATA_SIGNAL_IGNORE,
13 NETDATA_SIGNAL_EXIT_CLEANLY,
14 #if defined(FSANITIZE_ADDRESS)
15 NETDATA_SIGNAL_EXIT_NOW,
16 #endif
17 NETDATA_SIGNAL_REOPEN_LOGS,
18 NETDATA_SIGNAL_RELOAD_HEALTH,
19 NETDATA_SIGNAL_DEADLY,
20 } SIGNAL_ACTION;
21
22 static struct {
23 int signo; // the signal
24 const char *name; // the name of the signal
25 size_t count; // the number of signals received
26 SIGNAL_ACTION action; // the action to take
27 EXIT_REASON reason;
28 } signals_waiting[] = {
29 { SIGPIPE, "SIGPIPE", 0, NETDATA_SIGNAL_IGNORE, EXIT_REASON_NONE },
30 { SIGINT , "SIGINT", 0, NETDATA_SIGNAL_EXIT_CLEANLY, EXIT_REASON_SIGINT },
31 { SIGQUIT, "SIGQUIT", 0, NETDATA_SIGNAL_EXIT_CLEANLY, EXIT_REASON_SIGQUIT },
32 { SIGTERM, "SIGTERM", 0, NETDATA_SIGNAL_EXIT_CLEANLY, EXIT_REASON_SIGTERM },
33 { SIGHUP, "SIGHUP", 0, NETDATA_SIGNAL_REOPEN_LOGS, EXIT_REASON_NONE },
34 #if defined(FSANITIZE_ADDRESS)
35 { SIGUSR1, "SIGUSR1", 0, NETDATA_SIGNAL_EXIT_NOW, EXIT_REASON_NONE },
36 #endif
37 { SIGUSR2, "SIGUSR2", 0, NETDATA_SIGNAL_RELOAD_HEALTH, EXIT_REASON_NONE },
38 { SIGBUS, "SIGBUS", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGBUS },
39 { SIGSEGV, "SIGSEGV", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGSEGV },
40 { SIGFPE, "SIGFPE", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGFPE },
41 { SIGILL, "SIGILL", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGILL },
42 { SIGABRT, "SIGABRT", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGABRT },
43 { SIGSYS, "SIGSYS", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGSYS },
44 { SIGXCPU, "SIGXCPU", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGXCPU },
45 { SIGXFSZ, "SIGXFSZ", 0, NETDATA_SIGNAL_DEADLY, EXIT_REASON_SIGXFSZ },
46 };
47
48 static void (*original_handlers[NSIG])(int) = {0};
49 static void (*original_sigactions[NSIG])(int, siginfo_t *, void *) = {0};
50
51 NEVER_INLINE
52 void nd_signal_handler(int signo, siginfo_t *info, void *context __maybe_unused) {
53 signal_protected_access_check(signo, info, context);
54
55 for(size_t i = 0; i < _countof(signals_waiting) ; i++) {
56 if(signals_waiting[i].signo != signo)
57 continue;
58
59 signals_waiting[i].count++;
60
61 #if defined(FSANITIZE_ADDRESS)
62 if(signals_waiting[i].action == NETDATA_SIGNAL_EXIT_NOW)
63 exit(1);
64 #endif
65
66 if(signals_waiting[i].action == NETDATA_SIGNAL_DEADLY) {
67 bool chained_handler = original_sigactions[signo] || (original_handlers[signo] && original_handlers[signo] != SIG_IGN && original_handlers[signo] != SIG_DFL);
68
69 // Update the status file
70 SIGNAL_CODE sc = info ? signal_code(signo, info->si_code) : 0;
71
72 // Get fault address based on signal type
73 void *fault_address = NULL;
74 if (info && (signo == SIGSEGV || signo == SIGBUS || signo == SIGILL || signo == SIGFPE))
75 fault_address = info->si_addr;
76
77 if(daemon_status_file_deadly_signal_received(signals_waiting[i].reason, sc, fault_address, chained_handler)) {
78 // this is a duplicate event, do not send it to sentry
79 #ifdef ENABLE_SENTRY
80 nd_sentry_crash_report(false);
81 #else
82 chained_handler = false;
83 #endif
84 }
85
86 // log it
87 char b[1024];
88 size_t len = 0;
89 len = strcatz(b, len, "SIGNAL HANDLER: received deadly signal: ", sizeof(b));
90 len = strcatz(b, len, signals_waiting[i].name, sizeof(b));
91 if(sc) {
92 char buf[128];
93 SIGNAL_CODE_2str_h(sc, buf, sizeof(buf));
94 len = strcatz(b, len, " (", sizeof(b));
95 len = strcatz(b, len, buf, sizeof(b));
96 len = strcatz(b, len, ")", sizeof(b));
97 }
98 len = strcatz(b, len, " in thread ", sizeof(b));
99 len += print_uint64(&b[len], gettid_cached());
100 len = strcatz(b, len, " ", sizeof(b));
101 len = strcatz(b, len, nd_thread_tag_async_safe(), sizeof(b));
102 len = strcatz(b, len, "!\n", sizeof(b));
103
104 if(write(STDERR_FILENO, b, strlen(b)) == -1) {
105 // nothing to do - we cannot write but there is no way to complain about it
106 ;
107 }
108
109 // Chain to the original handler if it exists
110 if(chained_handler) {
111 if (original_sigactions[signo]) {
112 original_sigactions[signo](signo, info, context);
113 return; // Original handler should handle the signal
114 }
115
116 if (original_handlers[signo]) {
117 original_handlers[signo](signo);
118 return; // Original handler should handle the signal
119 }
120 }
121
122 // If there's no original handler or we can't chain, reset to default and re-raise
123 struct sigaction sa;
124 sa.sa_handler = SIG_DFL;
125 sigemptyset(&sa.sa_mask);
126 sa.sa_flags = 0;
127 if(sigaction(signo, &sa, NULL) < 0) { ; }
128
129 // Re-raise the signal, which now uses the default action.
130 raise(signo);
131 }
132
133 break;
134 }
135 }
136
137 // Unmask all signals the netdata main signal handler uses.
138 // All other signals remain masked.
139 static void posix_unmask_my_signals(void) {
140 sigset_t sigset;
141 sigemptyset(&sigset);
142
143 for (size_t i = 0; i < _countof(signals_waiting) ; i++)
144 sigaddset(&sigset, signals_waiting[i].signo);
145
146 if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) != 0)
147 netdata_log_error("SIGNAL: cannot unmask netdata signals");
148 }
149
150 void nd_cleanup_deadly_signals(void) {
151 struct sigaction act;
152 memset(&act, 0, sizeof(struct sigaction));
153
154 // ignore all signals while we run in a signal handler
155 sigfillset(&act.sa_mask);
156
157 for (size_t i = 0; i < _countof(signals_waiting); i++) {
158 if(signals_waiting[i].action != NETDATA_SIGNAL_DEADLY)
159 continue;
160
161 act.sa_flags = 0;
162 act.sa_handler = SIG_DFL;
163
164 if (sigaction(signals_waiting[i].signo, &act, NULL) == -1)
165 netdata_log_error("SIGNAL: Failed to cleanup signal handler for: %s", signals_waiting[i].name);
166 }
167
168 memset(original_handlers, 0, sizeof(original_handlers));
169 memset(original_sigactions, 0, sizeof(original_sigactions));
170 }
171
172 void nd_initialize_signals(bool chain_existing) {
173 signals_block_all_except_deadly();
174
175 // Set the signal handler name for stack trace filtering
176 #ifdef HAVE_LIBBACKTRACE
177 stacktrace_set_signal_handler_function("nd_signal_handler");
178 #endif
179
180 struct sigaction act;
181 memset(&act, 0, sizeof(struct sigaction));
182
183 // ignore all signals while we run in a signal handler
184 sigfillset(&act.sa_mask);
185
186 for (size_t i = 0; i < _countof(signals_waiting); i++) {
187 int signo = signals_waiting[i].signo;
188
189 // If chaining is requested, get the current handler first
190 struct sigaction old_act;
191 if (chain_existing &&
192 sigaction(signo, NULL, &old_act) == 0 &&
193 (uintptr_t)old_act.sa_handler != (uintptr_t)nd_signal_handler) {
194 // Save the original handlers for chaining
195 if (old_act.sa_flags & SA_SIGINFO)
196 original_sigactions[signo] = old_act.sa_sigaction;
197 else
198 original_handlers[signo] = old_act.sa_handler;
199 }
200
201 switch (signals_waiting[i].action) {
202 case NETDATA_SIGNAL_IGNORE:
203 act.sa_flags = 0;
204 act.sa_handler = SIG_IGN;
205 break;
206 default:
207 act.sa_flags = SA_SIGINFO;
208 act.sa_sigaction = nd_signal_handler;
209 break;
210 }
211
212 if (sigaction(signals_waiting[i].signo, &act, NULL) == -1)
213 netdata_log_error("SIGNAL: Failed to change signal handler for: %s", signals_waiting[i].name);
214 }
215 }
216
217 NEVER_INLINE
218 static void process_triggered_signals(void) {
219 size_t found;
220 do {
221 found = 0;
222 for (size_t i = 0; i < _countof(signals_waiting) ; i++) {
223 if (!signals_waiting[i].count)
224 continue;
225
226 found++;
227 signals_waiting[i].count = 0;
228 const char *name = signals_waiting[i].name;
229
230 switch (signals_waiting[i].action) {
231 case NETDATA_SIGNAL_RELOAD_HEALTH:
232 if(exit_initiated_get())
233 netdata_log_info("SIGNAL: Received %s. Ignoring it, as we are exiting...", name);
234 else {
235 nd_log_limits_unlimited();
236 netdata_log_info("SIGNAL: Received %s. Reloading HEALTH configuration...", name);
237 nd_log_limits_reset();
238 execute_command(CMD_RELOAD_HEALTH, NULL, NULL);
239 }
240 break;
241
242 case NETDATA_SIGNAL_REOPEN_LOGS:
243 if(exit_initiated_get())
244 netdata_log_info("SIGNAL: Received %s. Ignoring it, as we are exiting...", name);
245 else {
246 nd_log_limits_unlimited();
247 netdata_log_info("SIGNAL: Received %s. Reopening all log files...", name);
248 nd_log_limits_reset();
249 execute_command(CMD_REOPEN_LOGS, NULL, NULL);
250 }
251 break;
252
253 case NETDATA_SIGNAL_EXIT_CLEANLY:
254 nd_log_limits_unlimited();
255 netdata_log_info("SIGNAL: Received %s. Cleaning up to exit...", name);
256 commands_exit();
257 netdata_exit_gracefully(signals_waiting[i].reason, true);
258 break;
259
260 case NETDATA_SIGNAL_DEADLY:
261 _exit(1);
262 break;
263
264 default:
265 netdata_log_info("SIGNAL: Received %s. No signal handler configured. Ignoring it.", name);
266 break;
267 }
268 }
269 } while(found);
270 }
271
272 static inline bool threshold_trigger_smaller(bool *last, double threshold, double hysteresis, double free_mem) {
273 bool triggered = *last;
274
275 if (free_mem < threshold)
276 *last = true;
277
278 if (free_mem >= (threshold + hysteresis))
279 *last = false;
280
281 return !triggered && *last;
282 }
283
284 NEVER_INLINE
285 void nd_process_signals(void) {
286 posix_unmask_my_signals();
287 const usec_t save_every_ut = 15 * 60 * USEC_PER_SEC;
288 usec_t last_update_mt = now_monotonic_usec();
289 bool triggered1 = false, triggered5 = false, triggered10 = false;
290
291 while (true) {
292 bool save_again = false;
293 double free_mem = os_system_memory_available_percent(os_system_memory(false));
294
295 save_again =
296 threshold_trigger_smaller(&triggered1, 1.0, 1.0, free_mem) ||
297 threshold_trigger_smaller(&triggered5, 5.0, 1.0, free_mem) ||
298 threshold_trigger_smaller(&triggered10, 10.0, 1.0, free_mem);
299
300 usec_t mt = now_monotonic_usec();
301 if ((mt - last_update_mt) >= save_every_ut || save_again) {
302 daemon_status_file_update_status(DAEMON_STATUS_NONE);
303 last_update_mt += save_every_ut;
304 }
305
306 if(poll(NULL, 0, 13 * MSEC_PER_SEC + 379) < 0) { ; }
307
308 process_triggered_signals();
309 }
310 }