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
62DB::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 */
68static void call_default_signal_handler(int sig)
69{
70 signal(sig, SIG_DFL);
71 raise(sig);
72}
73
74
75static constexpr size_t max_query_id_size = 127;
76
77static 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
86using signal_function = void(int, siginfo_t*, void*);
87
88static 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 */
97static void closeLogsSignalHandler(int sig, siginfo_t * info, void * context)
98{
99 writeSignalIDtoSignalPipe(sig);
100}
101
102static 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 */
110static 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 */
144class SignalListener : public Poco::Runnable
145{
146public:
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
217private:
218 Logger * log;
219 BaseDaemon & daemon;
220
221private:
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 */
276static 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
311static 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
321static 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
336void 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
356BaseDaemon::BaseDaemon()
357{
358 checkRequiredInstructions();
359}
360
361
362BaseDaemon::~BaseDaemon()
363{
364 writeSignalIDtoSignalPipe(SignalListener::StopThread);
365 signal_listener_thread.join();
366 signal_pipe.close();
367}
368
369
370enum 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
382static 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
407static sigjmp_buf jmpbuf;
408
409static 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.
416static 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
458void 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
491void BaseDaemon::terminate()
492{
493 getTaskManager().cancelAll();
494 if (::raise(SIGTERM) != 0)
495 throw Poco::SystemException("cannot terminate process");
496}
497
498void BaseDaemon::kill()
499{
500 dumpCoverageReportIfPossible();
501 pid.clear();
502 if (::raise(SIGKILL) != 0)
503 throw Poco::SystemException("cannot kill process");
504}
505
506void BaseDaemon::sleep(double seconds)
507{
508 wakeup_event.reset();
509 wakeup_event.tryWait(seconds * 1000);
510}
511
512void BaseDaemon::wakeup()
513{
514 wakeup_event.set();
515}
516
517std::string BaseDaemon::getDefaultCorePath() const
518{
519 return "/opt/cores/";
520}
521
522void 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
555namespace
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)
563void 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
580void debugIncreaseOOMScore() {}
581#endif
582}
583
584void 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
725void 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
776void 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.
782void BaseDaemon::exitOnTaskError()
783{
784 Poco::Observer<BaseDaemon, Poco::TaskFailedNotification> obs(*this, &BaseDaemon::handleNotification);
785 getTaskManager().addObserver(obs);
786}
787
788/// Used for exitOnTaskError()
789void 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
798void 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
831bool isPidRunning(pid_t pid)
832{
833 if (getpgid(pid) >= 0)
834 return 1;
835 return 0;
836}
837
838void 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
887void BaseDaemon::PID::clear()
888{
889 if (!file.empty())
890 {
891 Poco::File(file).remove();
892 file.clear();
893 }
894}
895
896void 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
915void 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
928void 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