1 | #include <daemon/BaseDaemon.h> |
2 | |
3 | #include <sys/stat.h> |
4 | #include <sys/types.h> |
5 | #include <sys/time.h> |
6 | #include <fcntl.h> |
7 | #include <errno.h> |
8 | #include <string.h> |
9 | #include <signal.h> |
10 | #include <cxxabi.h> |
11 | #include <execinfo.h> |
12 | #include <unistd.h> |
13 | |
14 | #include <typeinfo> |
15 | #include <sys/time.h> |
16 | #include <sys/resource.h> |
17 | #include <iostream> |
18 | #include <fstream> |
19 | #include <sstream> |
20 | #include <memory> |
21 | |
22 | #include <Poco/Observer.h> |
23 | #include <Poco/AutoPtr.h> |
24 | #include <Poco/PatternFormatter.h> |
25 | #include <Poco/TaskManager.h> |
26 | #include <Poco/File.h> |
27 | #include <Poco/Path.h> |
28 | #include <Poco/Message.h> |
29 | #include <Poco/Util/Application.h> |
30 | #include <Poco/Exception.h> |
31 | #include <Poco/ErrorHandler.h> |
32 | #include <Poco/Condition.h> |
33 | #include <Poco/SyslogChannel.h> |
34 | #include <Poco/DirectoryIterator.h> |
35 | |
36 | #include <common/logger_useful.h> |
37 | #include <common/ErrorHandlers.h> |
38 | #include <common/argsToConfig.h> |
39 | #include <common/getThreadNumber.h> |
40 | #include <common/coverage.h> |
41 | |
42 | #include <IO/WriteBufferFromFile.h> |
43 | #include <IO/WriteBufferFromFileDescriptorDiscardOnFailure.h> |
44 | #include <IO/ReadBufferFromFileDescriptor.h> |
45 | #include <IO/ReadHelpers.h> |
46 | #include <IO/WriteHelpers.h> |
47 | #include <Common/Exception.h> |
48 | #include <Common/PipeFDs.h> |
49 | #include <Common/StackTrace.h> |
50 | #include <Common/getMultipleKeysFromConfig.h> |
51 | #include <Common/ClickHouseRevision.h> |
52 | #include <Common/Config/ConfigProcessor.h> |
53 | #include <Common/config_version.h> |
54 | |
55 | #ifdef __APPLE__ |
56 | // ucontext is not available without _XOPEN_SOURCE |
57 | #define _XOPEN_SOURCE 700 |
58 | #endif |
59 | #include <ucontext.h> |
60 | |
61 | |
62 | DB::PipeFDs signal_pipe; |
63 | |
64 | |
65 | /** Reset signal handler to the default and send signal to itself. |
66 | * It's called from user signal handler to write core dump. |
67 | */ |
68 | static void call_default_signal_handler(int sig) |
69 | { |
70 | signal(sig, SIG_DFL); |
71 | raise(sig); |
72 | } |
73 | |
74 | |
75 | static constexpr size_t max_query_id_size = 127; |
76 | |
77 | static const size_t buf_size = |
78 | sizeof(int) |
79 | + sizeof(siginfo_t) |
80 | + sizeof(ucontext_t) |
81 | + sizeof(StackTrace) |
82 | + sizeof(UInt32) |
83 | + max_query_id_size + 1; /// query_id + varint encoded length |
84 | |
85 | |
86 | using signal_function = void(int, siginfo_t*, void*); |
87 | |
88 | static void writeSignalIDtoSignalPipe(int sig) |
89 | { |
90 | char buf[buf_size]; |
91 | DB::WriteBufferFromFileDescriptor out(signal_pipe.fds_rw[1], buf_size, buf); |
92 | DB::writeBinary(sig, out); |
93 | out.next(); |
94 | } |
95 | |
96 | /** Signal handler for HUP / USR1 */ |
97 | static void closeLogsSignalHandler(int sig, siginfo_t * info, void * context) |
98 | { |
99 | writeSignalIDtoSignalPipe(sig); |
100 | } |
101 | |
102 | static void terminateRequestedSignalHandler(int sig, siginfo_t * info, void * context) |
103 | { |
104 | writeSignalIDtoSignalPipe(sig); |
105 | } |
106 | |
107 | |
108 | /** Handler for "fault" or diagnostic signals. Send data about fault to separate thread to write into log. |
109 | */ |
110 | static void signalHandler(int sig, siginfo_t * info, void * context) |
111 | { |
112 | char buf[buf_size]; |
113 | DB::WriteBufferFromFileDescriptorDiscardOnFailure out(signal_pipe.fds_rw[1], buf_size, buf); |
114 | |
115 | const ucontext_t signal_context = *reinterpret_cast<ucontext_t *>(context); |
116 | const StackTrace stack_trace(signal_context); |
117 | |
118 | StringRef query_id = CurrentThread::getQueryId(); /// This is signal safe. |
119 | query_id.size = std::min(query_id.size, max_query_id_size); |
120 | |
121 | DB::writeBinary(sig, out); |
122 | DB::writePODBinary(*info, out); |
123 | DB::writePODBinary(signal_context, out); |
124 | DB::writePODBinary(stack_trace, out); |
125 | DB::writeBinary(UInt32(getThreadNumber()), out); |
126 | DB::writeStringBinary(query_id, out); |
127 | |
128 | out.next(); |
129 | |
130 | if (sig != SIGTSTP) /// This signal is used for debugging. |
131 | { |
132 | /// The time that is usually enough for separate thread to print info into log. |
133 | ::sleep(10); |
134 | call_default_signal_handler(sig); |
135 | } |
136 | } |
137 | |
138 | |
139 | /** The thread that read info about signal or std::terminate from pipe. |
140 | * On HUP / USR1, close log files (for new files to be opened later). |
141 | * On information about std::terminate, write it to log. |
142 | * On other signals, write info to log. |
143 | */ |
144 | class SignalListener : public Poco::Runnable |
145 | { |
146 | public: |
147 | enum Signals : int |
148 | { |
149 | StdTerminate = -1, |
150 | StopThread = -2 |
151 | }; |
152 | |
153 | explicit SignalListener(BaseDaemon & daemon_) |
154 | : log(&Logger::get("BaseDaemon" )) |
155 | , daemon(daemon_) |
156 | { |
157 | } |
158 | |
159 | void run() |
160 | { |
161 | char buf[buf_size]; |
162 | DB::ReadBufferFromFileDescriptor in(signal_pipe.fds_rw[0], buf_size, buf); |
163 | |
164 | while (!in.eof()) |
165 | { |
166 | int sig = 0; |
167 | DB::readBinary(sig, in); |
168 | |
169 | if (sig == Signals::StopThread) |
170 | { |
171 | LOG_INFO(log, "Stop SignalListener thread" ); |
172 | break; |
173 | } |
174 | else if (sig == SIGHUP || sig == SIGUSR1) |
175 | { |
176 | LOG_DEBUG(log, "Received signal to close logs." ); |
177 | BaseDaemon::instance().closeLogs(BaseDaemon::instance().logger()); |
178 | LOG_INFO(log, "Opened new log file after received signal." ); |
179 | } |
180 | else if (sig == Signals::StdTerminate) |
181 | { |
182 | UInt32 thread_num; |
183 | std::string message; |
184 | |
185 | DB::readBinary(thread_num, in); |
186 | DB::readBinary(message, in); |
187 | |
188 | onTerminate(message, thread_num); |
189 | } |
190 | else if (sig == SIGINT || |
191 | sig == SIGQUIT || |
192 | sig == SIGTERM) |
193 | { |
194 | daemon.handleSignal(sig); |
195 | } |
196 | else |
197 | { |
198 | siginfo_t info; |
199 | ucontext_t context; |
200 | StackTrace stack_trace(NoCapture{}); |
201 | UInt32 thread_num; |
202 | std::string query_id; |
203 | |
204 | DB::readPODBinary(info, in); |
205 | DB::readPODBinary(context, in); |
206 | DB::readPODBinary(stack_trace, in); |
207 | DB::readBinary(thread_num, in); |
208 | DB::readBinary(query_id, in); |
209 | |
210 | /// This allows to receive more signals if failure happens inside onFault function. |
211 | /// Example: segfault while symbolizing stack trace. |
212 | std::thread([=, this] { onFault(sig, info, context, stack_trace, thread_num, query_id); }).detach(); |
213 | } |
214 | } |
215 | } |
216 | |
217 | private: |
218 | Logger * log; |
219 | BaseDaemon & daemon; |
220 | |
221 | private: |
222 | void onTerminate(const std::string & message, UInt32 thread_num) const |
223 | { |
224 | LOG_FATAL(log, "(version " << VERSION_STRING << VERSION_OFFICIAL << ") (from thread " << thread_num << ") " << message); |
225 | } |
226 | |
227 | void onFault( |
228 | int sig, |
229 | const siginfo_t & info, |
230 | const ucontext_t & context, |
231 | const StackTrace & stack_trace, |
232 | UInt32 thread_num, |
233 | const std::string & query_id) const |
234 | { |
235 | LOG_FATAL(log, "########################################" ); |
236 | |
237 | { |
238 | std::stringstream message; |
239 | message << "(version " << VERSION_STRING << VERSION_OFFICIAL << ")" ; |
240 | message << " (from thread " << thread_num << ")" ; |
241 | if (query_id.empty()) |
242 | message << " (no query)" ; |
243 | else |
244 | message << " (query_id: " << query_id << ")" ; |
245 | message << " Received signal " << strsignal(sig) << " (" << sig << ")" << "." ; |
246 | |
247 | LOG_FATAL(log, message.rdbuf()); |
248 | } |
249 | |
250 | LOG_FATAL(log, signalToErrorMessage(sig, info, context)); |
251 | |
252 | if (stack_trace.getSize()) |
253 | { |
254 | /// Write bare stack trace (addresses) just in case if we will fail to print symbolized stack trace. |
255 | /// NOTE This still require memory allocations and mutex lock inside logger. BTW we can also print it to stderr using write syscalls. |
256 | |
257 | std::stringstream bare_stacktrace; |
258 | bare_stacktrace << "Stack trace:" ; |
259 | for (size_t i = stack_trace.getOffset(); i < stack_trace.getSize(); ++i) |
260 | bare_stacktrace << ' ' << stack_trace.getFrames()[i]; |
261 | |
262 | LOG_FATAL(log, bare_stacktrace.rdbuf()); |
263 | } |
264 | |
265 | /// Write symbolized stack trace line by line for better grep-ability. |
266 | stack_trace.toStringEveryLine([&](const std::string & s) { LOG_FATAL(log, s); }); |
267 | } |
268 | }; |
269 | |
270 | |
271 | /** To use with std::set_terminate. |
272 | * Collects slightly more info than __gnu_cxx::__verbose_terminate_handler, |
273 | * and send it to pipe. Other thread will read this info from pipe and asynchronously write it to log. |
274 | * Look at libstdc++-v3/libsupc++/vterminate.cc for example. |
275 | */ |
276 | static void terminate_handler() |
277 | { |
278 | static thread_local bool terminating = false; |
279 | if (terminating) |
280 | { |
281 | abort(); |
282 | return; /// Just for convenience. |
283 | } |
284 | |
285 | terminating = true; |
286 | |
287 | std::string log_message; |
288 | |
289 | if (std::current_exception()) |
290 | log_message = "Terminate called for uncaught exception:\n" + DB::getCurrentExceptionMessage(true); |
291 | else |
292 | log_message = "Terminate called without an active exception" ; |
293 | |
294 | static const size_t buf_size = 1024; |
295 | |
296 | if (log_message.size() > buf_size - 16) |
297 | log_message.resize(buf_size - 16); |
298 | |
299 | char buf[buf_size]; |
300 | DB::WriteBufferFromFileDescriptor out(signal_pipe.fds_rw[1], buf_size, buf); |
301 | |
302 | DB::writeBinary(static_cast<int>(SignalListener::StdTerminate), out); |
303 | DB::writeBinary(UInt32(getThreadNumber()), out); |
304 | DB::writeBinary(log_message, out); |
305 | out.next(); |
306 | |
307 | abort(); |
308 | } |
309 | |
310 | |
311 | static std::string createDirectory(const std::string & file) |
312 | { |
313 | auto path = Poco::Path(file).makeParent(); |
314 | if (path.toString().empty()) |
315 | return "" ; |
316 | Poco::File(path).createDirectories(); |
317 | return path.toString(); |
318 | }; |
319 | |
320 | |
321 | static bool tryCreateDirectories(Poco::Logger * logger, const std::string & path) |
322 | { |
323 | try |
324 | { |
325 | Poco::File(path).createDirectories(); |
326 | return true; |
327 | } |
328 | catch (...) |
329 | { |
330 | LOG_WARNING(logger, __PRETTY_FUNCTION__ << ": when creating " << path << ", " << DB::getCurrentExceptionMessage(true)); |
331 | } |
332 | return false; |
333 | } |
334 | |
335 | |
336 | void BaseDaemon::reloadConfiguration() |
337 | { |
338 | /** If the program is not run in daemon mode and 'config-file' is not specified, |
339 | * then we use config from 'config.xml' file in current directory, |
340 | * but will log to console (or use parameters --log-file, --errorlog-file from command line) |
341 | * instead of using files specified in config.xml. |
342 | * (It's convenient to log in console when you start server without any command line parameters.) |
343 | */ |
344 | config_path = config().getString("config-file" , "config.xml" ); |
345 | DB::ConfigProcessor config_processor(config_path, false, true); |
346 | config_processor.setConfigPath(Poco::Path(config_path).makeParent().toString()); |
347 | loaded_config = config_processor.loadConfig(/* allow_zk_includes = */ true); |
348 | |
349 | if (last_configuration != nullptr) |
350 | config().removeConfiguration(last_configuration); |
351 | last_configuration = loaded_config.configuration.duplicate(); |
352 | config().add(last_configuration, PRIO_DEFAULT, false); |
353 | } |
354 | |
355 | |
356 | BaseDaemon::BaseDaemon() |
357 | { |
358 | checkRequiredInstructions(); |
359 | } |
360 | |
361 | |
362 | BaseDaemon::~BaseDaemon() |
363 | { |
364 | writeSignalIDtoSignalPipe(SignalListener::StopThread); |
365 | signal_listener_thread.join(); |
366 | signal_pipe.close(); |
367 | } |
368 | |
369 | |
370 | enum class InstructionFail |
371 | { |
372 | NONE = 0, |
373 | SSE3 = 1, |
374 | SSSE3 = 2, |
375 | SSE4_1 = 3, |
376 | SSE4_2 = 4, |
377 | AVX = 5, |
378 | AVX2 = 6, |
379 | AVX512 = 7 |
380 | }; |
381 | |
382 | static std::string instructionFailToString(InstructionFail fail) |
383 | { |
384 | switch(fail) |
385 | { |
386 | case InstructionFail::NONE: |
387 | return "NONE" ; |
388 | case InstructionFail::SSE3: |
389 | return "SSE3" ; |
390 | case InstructionFail::SSSE3: |
391 | return "SSSE3" ; |
392 | case InstructionFail::SSE4_1: |
393 | return "SSE4.1" ; |
394 | case InstructionFail::SSE4_2: |
395 | return "SSE4.2" ; |
396 | case InstructionFail::AVX: |
397 | return "AVX" ; |
398 | case InstructionFail::AVX2: |
399 | return "AVX2" ; |
400 | case InstructionFail::AVX512: |
401 | return "AVX512" ; |
402 | } |
403 | __builtin_unreachable(); |
404 | } |
405 | |
406 | |
407 | static sigjmp_buf jmpbuf; |
408 | |
409 | static void sigIllCheckHandler(int sig, siginfo_t * info, void * context) |
410 | { |
411 | siglongjmp(jmpbuf, 1); |
412 | } |
413 | |
414 | /// Check if necessary sse extensions are available by trying to execute some sse instructions. |
415 | /// If instruction is unavailable, SIGILL will be sent by kernel. |
416 | static void checkRequiredInstructions(volatile InstructionFail & fail) |
417 | { |
418 | #if __SSE3__ |
419 | fail = InstructionFail::SSE3; |
420 | __asm__ volatile ("addsubpd %%xmm0, %%xmm0" : : : "xmm0" ); |
421 | #endif |
422 | |
423 | #if __SSSE3__ |
424 | fail = InstructionFail::SSSE3; |
425 | __asm__ volatile ("pabsw %%xmm0, %%xmm0" : : : "xmm0" ); |
426 | |
427 | #endif |
428 | |
429 | #if __SSE4_1__ |
430 | fail = InstructionFail::SSE4_1; |
431 | __asm__ volatile ("pmaxud %%xmm0, %%xmm0" : : : "xmm0" ); |
432 | #endif |
433 | |
434 | #if __SSE4_2__ |
435 | fail = InstructionFail::SSE4_2; |
436 | __asm__ volatile ("pcmpgtq %%xmm0, %%xmm0" : : : "xmm0" ); |
437 | #endif |
438 | |
439 | #if __AVX__ |
440 | fail = InstructionFail::AVX; |
441 | __asm__ volatile ("vaddpd %%ymm0, %%ymm0, %%ymm0" : : : "ymm0" ); |
442 | #endif |
443 | |
444 | #if __AVX2__ |
445 | fail = InstructionFail::AVX2; |
446 | __asm__ volatile ("vpabsw %%ymm0, %%ymm0" : : : "ymm0" ); |
447 | #endif |
448 | |
449 | #if __AVX512__ |
450 | fail = InstructionFail::AVX512; |
451 | __asm__ volatile ("vpabsw %%zmm0, %%zmm0" : : : "zmm0" ); |
452 | #endif |
453 | |
454 | fail = InstructionFail::NONE; |
455 | } |
456 | |
457 | |
458 | void BaseDaemon::checkRequiredInstructions() |
459 | { |
460 | struct sigaction sa{}; |
461 | struct sigaction sa_old{}; |
462 | sa.sa_sigaction = sigIllCheckHandler; |
463 | sa.sa_flags = SA_SIGINFO; |
464 | auto signal = SIGILL; |
465 | if (sigemptyset(&sa.sa_mask) != 0 |
466 | || sigaddset(&sa.sa_mask, signal) != 0 |
467 | || sigaction(signal, &sa, &sa_old) != 0) |
468 | { |
469 | std::cerr << "Can not set signal handler\n" ; |
470 | exit(1); |
471 | } |
472 | |
473 | volatile InstructionFail fail = InstructionFail::NONE; |
474 | |
475 | if (sigsetjmp(jmpbuf, 1)) |
476 | { |
477 | std::cerr << "Instruction check fail. There is no " << instructionFailToString(fail) << " instruction set\n" ; |
478 | exit(1); |
479 | } |
480 | |
481 | ::checkRequiredInstructions(fail); |
482 | |
483 | if (sigaction(signal, &sa_old, nullptr)) |
484 | { |
485 | std::cerr << "Can not set signal handler\n" ; |
486 | exit(1); |
487 | } |
488 | } |
489 | |
490 | |
491 | void BaseDaemon::terminate() |
492 | { |
493 | getTaskManager().cancelAll(); |
494 | if (::raise(SIGTERM) != 0) |
495 | throw Poco::SystemException("cannot terminate process" ); |
496 | } |
497 | |
498 | void BaseDaemon::kill() |
499 | { |
500 | dumpCoverageReportIfPossible(); |
501 | pid.clear(); |
502 | if (::raise(SIGKILL) != 0) |
503 | throw Poco::SystemException("cannot kill process" ); |
504 | } |
505 | |
506 | void BaseDaemon::sleep(double seconds) |
507 | { |
508 | wakeup_event.reset(); |
509 | wakeup_event.tryWait(seconds * 1000); |
510 | } |
511 | |
512 | void BaseDaemon::wakeup() |
513 | { |
514 | wakeup_event.set(); |
515 | } |
516 | |
517 | std::string BaseDaemon::getDefaultCorePath() const |
518 | { |
519 | return "/opt/cores/" ; |
520 | } |
521 | |
522 | void BaseDaemon::closeFDs() |
523 | { |
524 | #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)) |
525 | Poco::File proc_path{"/dev/fd" }; |
526 | #else |
527 | Poco::File proc_path{"/proc/self/fd" }; |
528 | #endif |
529 | if (proc_path.isDirectory()) /// Hooray, proc exists |
530 | { |
531 | std::vector<std::string> fds; |
532 | /// in /proc/self/fd directory filenames are numeric file descriptors |
533 | proc_path.list(fds); |
534 | for (const auto & fd_str : fds) |
535 | { |
536 | int fd = DB::parse<int>(fd_str); |
537 | if (fd > 2 && fd != signal_pipe.fds_rw[0] && fd != signal_pipe.fds_rw[1]) |
538 | ::close(fd); |
539 | } |
540 | } |
541 | else |
542 | { |
543 | int max_fd = -1; |
544 | #ifdef _SC_OPEN_MAX |
545 | max_fd = sysconf(_SC_OPEN_MAX); |
546 | if (max_fd == -1) |
547 | #endif |
548 | max_fd = 256; /// bad fallback |
549 | for (int fd = 3; fd < max_fd; ++fd) |
550 | if (fd != signal_pipe.fds_rw[0] && fd != signal_pipe.fds_rw[1]) |
551 | ::close(fd); |
552 | } |
553 | } |
554 | |
555 | namespace |
556 | { |
557 | /// In debug version on Linux, increase oom score so that clickhouse is killed |
558 | /// first, instead of some service. Use a carefully chosen random score of 555: |
559 | /// the maximum is 1000, and chromium uses 300 for its tab processes. Ignore |
560 | /// whatever errors that occur, because it's just a debugging aid and we don't |
561 | /// care if it breaks. |
562 | #if defined(__linux__) && !defined(NDEBUG) |
563 | void debugIncreaseOOMScore() |
564 | { |
565 | const std::string new_score = "555" ; |
566 | try |
567 | { |
568 | DB::WriteBufferFromFile buf("/proc/self/oom_score_adj" ); |
569 | buf.write(new_score.c_str(), new_score.size()); |
570 | } |
571 | catch (const Poco::Exception & e) |
572 | { |
573 | LOG_WARNING(&Logger::root(), "Failed to adjust OOM score: '" + |
574 | e.displayText() + "'." ); |
575 | return; |
576 | } |
577 | LOG_INFO(&Logger::root(), "Set OOM score adjustment to " + new_score); |
578 | } |
579 | #else |
580 | void debugIncreaseOOMScore() {} |
581 | #endif |
582 | } |
583 | |
584 | void BaseDaemon::initialize(Application & self) |
585 | { |
586 | closeFDs(); |
587 | task_manager.reset(new Poco::TaskManager); |
588 | ServerApplication::initialize(self); |
589 | |
590 | /// now highest priority (lowest value) is PRIO_APPLICATION = -100, we want higher! |
591 | argsToConfig(argv(), config(), PRIO_APPLICATION - 100); |
592 | |
593 | bool is_daemon = config().getBool("application.runAsDaemon" , false); |
594 | |
595 | if (is_daemon) |
596 | { |
597 | /** When creating pid file and looking for config, will search for paths relative to the working path of the program when started. |
598 | */ |
599 | std::string path = Poco::Path(config().getString("application.path" )).setFileName("" ).toString(); |
600 | if (0 != chdir(path.c_str())) |
601 | throw Poco::Exception("Cannot change directory to " + path); |
602 | } |
603 | |
604 | reloadConfiguration(); |
605 | |
606 | /// This must be done before creation of any files (including logs). |
607 | mode_t umask_num = 0027; |
608 | if (config().has("umask" )) |
609 | { |
610 | std::string umask_str = config().getString("umask" ); |
611 | std::stringstream stream; |
612 | stream << umask_str; |
613 | stream >> std::oct >> umask_num; |
614 | } |
615 | umask(umask_num); |
616 | |
617 | DB::ConfigProcessor(config_path).savePreprocessedConfig(loaded_config, "" ); |
618 | |
619 | /// Write core dump on crash. |
620 | { |
621 | struct rlimit rlim; |
622 | if (getrlimit(RLIMIT_CORE, &rlim)) |
623 | throw Poco::Exception("Cannot getrlimit" ); |
624 | /// 1 GiB by default. If more - it writes to disk too long. |
625 | rlim.rlim_cur = config().getUInt64("core_dump.size_limit" , 1024 * 1024 * 1024); |
626 | |
627 | if (rlim.rlim_cur && setrlimit(RLIMIT_CORE, &rlim)) |
628 | { |
629 | /// It doesn't work under address/thread sanitizer. http://lists.llvm.org/pipermail/llvm-bugs/2013-April/027880.html |
630 | std::cerr << "Cannot set max size of core file to " + std::to_string(rlim.rlim_cur) << std::endl; |
631 | } |
632 | } |
633 | |
634 | /// This must be done before any usage of DateLUT. In particular, before any logging. |
635 | if (config().has("timezone" )) |
636 | { |
637 | const std::string timezone = config().getString("timezone" ); |
638 | if (0 != setenv("TZ" , timezone.data(), 1)) |
639 | throw Poco::Exception("Cannot setenv TZ variable" ); |
640 | |
641 | tzset(); |
642 | DateLUT::setDefaultTimezone(timezone); |
643 | } |
644 | |
645 | std::string log_path = config().getString("logger.log" , "" ); |
646 | if (!log_path.empty()) |
647 | log_path = Poco::Path(log_path).setFileName("" ).toString(); |
648 | |
649 | /** Redirect stdout, stderr to separate files in the log directory (or in the specified file). |
650 | * Some libraries write to stderr in case of errors in debug mode, |
651 | * and this output makes sense even if the program is run in daemon mode. |
652 | * We have to do it before buildLoggers, for errors on logger initialization will be written to these files. |
653 | * If logger.stderr is specified then stderr will be forcibly redirected to that file. |
654 | */ |
655 | if ((!log_path.empty() && is_daemon) || config().has("logger.stderr" )) |
656 | { |
657 | std::string stderr_path = config().getString("logger.stderr" , log_path + "/stderr.log" ); |
658 | if (!freopen(stderr_path.c_str(), "a+" , stderr)) |
659 | throw Poco::OpenFileException("Cannot attach stderr to " + stderr_path); |
660 | } |
661 | |
662 | if ((!log_path.empty() && is_daemon) || config().has("logger.stdout" )) |
663 | { |
664 | std::string stdout_path = config().getString("logger.stdout" , log_path + "/stdout.log" ); |
665 | if (!freopen(stdout_path.c_str(), "a+" , stdout)) |
666 | throw Poco::OpenFileException("Cannot attach stdout to " + stdout_path); |
667 | } |
668 | |
669 | /// Create pid file. |
670 | if (config().has("pid" )) |
671 | pid.seed(config().getString("pid" )); |
672 | |
673 | /// Change path for logging. |
674 | if (!log_path.empty()) |
675 | { |
676 | std::string path = createDirectory(log_path); |
677 | if (is_daemon |
678 | && chdir(path.c_str()) != 0) |
679 | throw Poco::Exception("Cannot change directory to " + path); |
680 | } |
681 | else |
682 | { |
683 | if (is_daemon |
684 | && chdir("/tmp" ) != 0) |
685 | throw Poco::Exception("Cannot change directory to /tmp" ); |
686 | } |
687 | |
688 | // sensitive data masking rules are not used here |
689 | buildLoggers(config(), logger(), self.commandName()); |
690 | |
691 | if (is_daemon) |
692 | { |
693 | /** Change working directory to the directory to write core dumps. |
694 | * We have to do it after buildLoggers, because there is the case when config files was in current directory. |
695 | */ |
696 | |
697 | std::string core_path = config().getString("core_path" , "" ); |
698 | if (core_path.empty()) |
699 | core_path = getDefaultCorePath(); |
700 | |
701 | tryCreateDirectories(&logger(), core_path); |
702 | |
703 | Poco::File cores = core_path; |
704 | if (!(cores.exists() && cores.isDirectory())) |
705 | { |
706 | core_path = !log_path.empty() ? log_path : "/opt/" ; |
707 | tryCreateDirectories(&logger(), core_path); |
708 | } |
709 | |
710 | if (0 != chdir(core_path.c_str())) |
711 | throw Poco::Exception("Cannot change directory to " + core_path); |
712 | } |
713 | |
714 | initializeTerminationAndSignalProcessing(); |
715 | logRevision(); |
716 | debugIncreaseOOMScore(); |
717 | |
718 | for (const auto & key : DB::getMultipleKeysFromConfig(config(), "" , "graphite" )) |
719 | { |
720 | graphite_writers.emplace(key, std::make_unique<GraphiteWriter>(key)); |
721 | } |
722 | } |
723 | |
724 | |
725 | void BaseDaemon::initializeTerminationAndSignalProcessing() |
726 | { |
727 | std::set_terminate(terminate_handler); |
728 | |
729 | /// We want to avoid SIGPIPE when working with sockets and pipes, and just handle return value/errno instead. |
730 | { |
731 | sigset_t sig_set; |
732 | if (sigemptyset(&sig_set) || sigaddset(&sig_set, SIGPIPE) || pthread_sigmask(SIG_BLOCK, &sig_set, nullptr)) |
733 | throw Poco::Exception("Cannot block signal." ); |
734 | } |
735 | |
736 | /// Setup signal handlers. |
737 | auto add_signal_handler = |
738 | [](const std::vector<int> & signals, signal_function handler) |
739 | { |
740 | struct sigaction sa; |
741 | memset(&sa, 0, sizeof(sa)); |
742 | sa.sa_sigaction = handler; |
743 | sa.sa_flags = SA_SIGINFO; |
744 | |
745 | { |
746 | if (sigemptyset(&sa.sa_mask)) |
747 | throw Poco::Exception("Cannot set signal handler." ); |
748 | |
749 | for (auto signal : signals) |
750 | if (sigaddset(&sa.sa_mask, signal)) |
751 | throw Poco::Exception("Cannot set signal handler." ); |
752 | |
753 | for (auto signal : signals) |
754 | if (sigaction(signal, &sa, nullptr)) |
755 | throw Poco::Exception("Cannot set signal handler." ); |
756 | } |
757 | }; |
758 | |
759 | /// SIGTSTP is added for debugging purposes. To output a stack trace of any running thread at anytime. |
760 | |
761 | add_signal_handler({SIGABRT, SIGSEGV, SIGILL, SIGBUS, SIGSYS, SIGFPE, SIGPIPE, SIGTSTP}, signalHandler); |
762 | add_signal_handler({SIGHUP, SIGUSR1}, closeLogsSignalHandler); |
763 | add_signal_handler({SIGINT, SIGQUIT, SIGTERM}, terminateRequestedSignalHandler); |
764 | |
765 | /// Set up Poco ErrorHandler for Poco Threads. |
766 | static KillingErrorHandler killing_error_handler; |
767 | Poco::ErrorHandler::set(&killing_error_handler); |
768 | |
769 | signal_pipe.setNonBlocking(); |
770 | signal_pipe.tryIncreaseSize(1 << 20); |
771 | |
772 | signal_listener.reset(new SignalListener(*this)); |
773 | signal_listener_thread.start(*signal_listener); |
774 | } |
775 | |
776 | void BaseDaemon::logRevision() const |
777 | { |
778 | Logger::root().information("Starting " + std::string{VERSION_FULL} + " with revision " + std::to_string(ClickHouseRevision::get())); |
779 | } |
780 | |
781 | /// Makes server shutdown if at least one Poco::Task have failed. |
782 | void BaseDaemon::exitOnTaskError() |
783 | { |
784 | Poco::Observer<BaseDaemon, Poco::TaskFailedNotification> obs(*this, &BaseDaemon::handleNotification); |
785 | getTaskManager().addObserver(obs); |
786 | } |
787 | |
788 | /// Used for exitOnTaskError() |
789 | void BaseDaemon::handleNotification(Poco::TaskFailedNotification *_tfn) |
790 | { |
791 | task_failed = true; |
792 | Poco::AutoPtr<Poco::TaskFailedNotification> fn(_tfn); |
793 | Logger *lg = &(logger()); |
794 | LOG_ERROR(lg, "Task '" << fn->task()->name() << "' failed. Daemon is shutting down. Reason - " << fn->reason().displayText()); |
795 | ServerApplication::terminate(); |
796 | } |
797 | |
798 | void BaseDaemon::defineOptions(Poco::Util::OptionSet& _options) |
799 | { |
800 | Poco::Util::ServerApplication::defineOptions (_options); |
801 | |
802 | _options.addOption( |
803 | Poco::Util::Option("config-file" , "C" , "load configuration from a given file" ) |
804 | .required(false) |
805 | .repeatable(false) |
806 | .argument("<file>" ) |
807 | .binding("config-file" )); |
808 | |
809 | _options.addOption( |
810 | Poco::Util::Option("log-file" , "L" , "use given log file" ) |
811 | .required(false) |
812 | .repeatable(false) |
813 | .argument("<file>" ) |
814 | .binding("logger.log" )); |
815 | |
816 | _options.addOption( |
817 | Poco::Util::Option("errorlog-file" , "E" , "use given log file for errors only" ) |
818 | .required(false) |
819 | .repeatable(false) |
820 | .argument("<file>" ) |
821 | .binding("logger.errorlog" )); |
822 | |
823 | _options.addOption( |
824 | Poco::Util::Option("pid-file" , "P" , "use given pidfile" ) |
825 | .required(false) |
826 | .repeatable(false) |
827 | .argument("<file>" ) |
828 | .binding("pid" )); |
829 | } |
830 | |
831 | bool isPidRunning(pid_t pid) |
832 | { |
833 | if (getpgid(pid) >= 0) |
834 | return 1; |
835 | return 0; |
836 | } |
837 | |
838 | void BaseDaemon::PID::seed(const std::string & file_) |
839 | { |
840 | file = Poco::Path(file_).absolute().toString(); |
841 | Poco::File poco_file(file); |
842 | |
843 | if (poco_file.exists()) |
844 | { |
845 | pid_t pid_read = 0; |
846 | { |
847 | std::ifstream in(file); |
848 | if (in.good()) |
849 | { |
850 | in >> pid_read; |
851 | if (pid_read && isPidRunning(pid_read)) |
852 | throw Poco::Exception("Pid file exists and program running with pid = " + std::to_string(pid_read) + ", should not start daemon." ); |
853 | } |
854 | } |
855 | std::cerr << "Old pid file exists (with pid = " << pid_read << "), removing." << std::endl; |
856 | poco_file.remove(); |
857 | } |
858 | |
859 | int fd = open(file.c_str(), |
860 | O_CREAT | O_EXCL | O_WRONLY, |
861 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); |
862 | |
863 | if (-1 == fd) |
864 | { |
865 | file.clear(); |
866 | if (EEXIST == errno) |
867 | throw Poco::Exception("Pid file exists, should not start daemon." ); |
868 | throw Poco::CreateFileException("Cannot create pid file." ); |
869 | } |
870 | |
871 | try |
872 | { |
873 | std::stringstream s; |
874 | s << getpid(); |
875 | if (static_cast<ssize_t>(s.str().size()) != write(fd, s.str().c_str(), s.str().size())) |
876 | throw Poco::Exception("Cannot write to pid file." ); |
877 | } |
878 | catch (...) |
879 | { |
880 | close(fd); |
881 | throw; |
882 | } |
883 | |
884 | close(fd); |
885 | } |
886 | |
887 | void BaseDaemon::PID::clear() |
888 | { |
889 | if (!file.empty()) |
890 | { |
891 | Poco::File(file).remove(); |
892 | file.clear(); |
893 | } |
894 | } |
895 | |
896 | void BaseDaemon::handleSignal(int signal_id) |
897 | { |
898 | if (signal_id == SIGINT || |
899 | signal_id == SIGQUIT || |
900 | signal_id == SIGTERM) |
901 | { |
902 | std::unique_lock<std::mutex> lock(signal_handler_mutex); |
903 | { |
904 | ++terminate_signals_counter; |
905 | sigint_signals_counter += signal_id == SIGINT; |
906 | signal_event.notify_all(); |
907 | } |
908 | |
909 | onInterruptSignals(signal_id); |
910 | } |
911 | else |
912 | throw DB::Exception(std::string("Unsupported signal: " ) + strsignal(signal_id), 0); |
913 | } |
914 | |
915 | void BaseDaemon::onInterruptSignals(int signal_id) |
916 | { |
917 | is_cancelled = true; |
918 | LOG_INFO(&logger(), "Received termination signal (" << strsignal(signal_id) << ")" ); |
919 | |
920 | if (sigint_signals_counter >= 2) |
921 | { |
922 | LOG_INFO(&logger(), "Received second signal Interrupt. Immediately terminate." ); |
923 | kill(); |
924 | } |
925 | } |
926 | |
927 | |
928 | void BaseDaemon::waitForTerminationRequest() |
929 | { |
930 | std::unique_lock<std::mutex> lock(signal_handler_mutex); |
931 | signal_event.wait(lock, [this](){ return terminate_signals_counter > 0; }); |
932 | } |
933 | |