2016-02-09 17:06:50 +00:00
|
|
|
#include <daemon/BaseDaemon.h>
|
2018-06-15 17:32:35 +00:00
|
|
|
#include <daemon/OwnFormattingChannel.h>
|
|
|
|
#include <daemon/OwnPatternFormatter.h>
|
2018-02-28 20:34:25 +00:00
|
|
|
#include <Common/Config/ConfigProcessor.h>
|
2018-06-15 17:32:35 +00:00
|
|
|
#include <daemon/OwnSplitChannel.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
2019-01-19 21:05:20 +00:00
|
|
|
#include <fcntl.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <cxxabi.h>
|
|
|
|
#include <execinfo.h>
|
2018-08-12 12:23:22 +00:00
|
|
|
#include <unistd.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
#include <typeinfo>
|
|
|
|
#include <common/logger_useful.h>
|
|
|
|
#include <common/ErrorHandlers.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <iostream>
|
2017-11-16 19:09:08 +00:00
|
|
|
#include <fstream>
|
2017-08-18 01:00:13 +00:00
|
|
|
#include <sstream>
|
2017-03-21 19:08:09 +00:00
|
|
|
#include <memory>
|
2016-01-15 03:55:07 +00:00
|
|
|
#include <Poco/Observer.h>
|
|
|
|
#include <Poco/Logger.h>
|
|
|
|
#include <Poco/AutoPtr.h>
|
2019-04-17 14:53:54 +00:00
|
|
|
#include <common/getThreadNumber.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
#include <Poco/PatternFormatter.h>
|
|
|
|
#include <Poco/ConsoleChannel.h>
|
|
|
|
#include <Poco/TaskManager.h>
|
|
|
|
#include <Poco/File.h>
|
|
|
|
#include <Poco/Path.h>
|
|
|
|
#include <Poco/Message.h>
|
|
|
|
#include <Poco/Util/AbstractConfiguration.h>
|
|
|
|
#include <Poco/Util/XMLConfiguration.h>
|
2018-02-08 19:12:37 +00:00
|
|
|
#include <Poco/Util/MapConfiguration.h>
|
|
|
|
#include <Poco/Util/Application.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
#include <Poco/Exception.h>
|
|
|
|
#include <Poco/ErrorHandler.h>
|
|
|
|
#include <Poco/Condition.h>
|
|
|
|
#include <Poco/SyslogChannel.h>
|
2018-08-12 12:23:22 +00:00
|
|
|
#include <Poco/DirectoryIterator.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <Common/Exception.h>
|
|
|
|
#include <IO/WriteBufferFromFileDescriptor.h>
|
|
|
|
#include <IO/ReadBufferFromFileDescriptor.h>
|
|
|
|
#include <IO/ReadHelpers.h>
|
|
|
|
#include <IO/WriteHelpers.h>
|
|
|
|
#include <Common/getMultipleKeysFromConfig.h>
|
|
|
|
#include <Common/ClickHouseRevision.h>
|
2018-07-17 18:22:32 +00:00
|
|
|
#include <Common/config_version.h>
|
2016-10-04 09:58:38 +00:00
|
|
|
#include <daemon/OwnPatternFormatter.h>
|
2018-06-06 20:57:07 +00:00
|
|
|
#include <Common/CurrentThread.h>
|
2018-06-01 15:06:45 +00:00
|
|
|
#include <Poco/Net/RemoteSyslogChannel.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2019-03-27 15:42:24 +00:00
|
|
|
#if USE_UNWIND
|
|
|
|
#define UNW_LOCAL_ONLY
|
|
|
|
#include <libunwind.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef __APPLE__
|
|
|
|
// ucontext is not available without _XOPEN_SOURCE
|
|
|
|
#define _XOPEN_SOURCE
|
|
|
|
#endif
|
|
|
|
#include <ucontext.h>
|
|
|
|
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/** For transferring information from signal handler to a separate thread.
|
|
|
|
* If you need to do something serious in case of a signal (example: write a message to the log),
|
|
|
|
* then sending information to a separate thread through pipe and doing all the stuff asynchronously
|
|
|
|
* - is probably the only safe method for doing it.
|
|
|
|
* (Because it's only safe to use reentrant functions in signal handlers.)
|
2016-01-15 03:55:07 +00:00
|
|
|
*/
|
|
|
|
struct Pipe
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
union
|
|
|
|
{
|
|
|
|
int fds[2];
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
int read_fd;
|
|
|
|
int write_fd;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
Pipe()
|
|
|
|
{
|
|
|
|
read_fd = -1;
|
|
|
|
write_fd = -1;
|
|
|
|
|
|
|
|
if (0 != pipe(fds))
|
2018-11-21 20:56:37 +00:00
|
|
|
DB::throwFromErrno("Cannot create pipe", 0);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void close()
|
|
|
|
{
|
|
|
|
if (-1 != read_fd)
|
|
|
|
{
|
|
|
|
::close(read_fd);
|
|
|
|
read_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (-1 != write_fd)
|
|
|
|
{
|
|
|
|
::close(write_fd);
|
|
|
|
write_fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~Pipe()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
2016-01-15 03:55:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Pipe signal_pipe;
|
|
|
|
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/** Reset signal handler to the default and send signal to itself.
|
|
|
|
* It's called from user signal handler to write core dump.
|
2016-01-15 03:55:07 +00:00
|
|
|
*/
|
|
|
|
static void call_default_signal_handler(int sig)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
signal(sig, SIG_DFL);
|
2018-08-20 23:16:50 +00:00
|
|
|
raise(sig);
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-17 14:53:54 +00:00
|
|
|
using ThreadNumber = decltype(getThreadNumber());
|
2016-01-15 03:55:07 +00:00
|
|
|
static const size_t buf_size = sizeof(int) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(ThreadNumber);
|
|
|
|
|
2016-06-08 14:39:19 +00:00
|
|
|
using signal_function = void(int, siginfo_t*, void*);
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2016-06-08 14:39:19 +00:00
|
|
|
static void writeSignalIDtoSignalPipe(int sig)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
char buf[buf_size];
|
|
|
|
DB::WriteBufferFromFileDescriptor out(signal_pipe.write_fd, buf_size, buf);
|
|
|
|
DB::writeBinary(sig, out);
|
|
|
|
out.next();
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/** Signal handler for HUP / USR1 */
|
2016-06-08 14:39:19 +00:00
|
|
|
static void closeLogsSignalHandler(int sig, siginfo_t * info, void * context)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
writeSignalIDtoSignalPipe(sig);
|
2016-06-08 14:39:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void terminateRequestedSignalHandler(int sig, siginfo_t * info, void * context)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
writeSignalIDtoSignalPipe(sig);
|
2016-06-08 14:39:19 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2017-09-30 11:04:56 +00:00
|
|
|
thread_local bool already_signal_handled = false;
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/** Handler for "fault" signals. Send data about fault to separate thread to write into log.
|
2016-01-15 03:55:07 +00:00
|
|
|
*/
|
2016-06-08 14:39:19 +00:00
|
|
|
static void faultSignalHandler(int sig, siginfo_t * info, void * context)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-09-30 11:04:56 +00:00
|
|
|
if (already_signal_handled)
|
|
|
|
return;
|
|
|
|
already_signal_handled = true;
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
char buf[buf_size];
|
|
|
|
DB::WriteBufferFromFileDescriptor out(signal_pipe.write_fd, buf_size, buf);
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
DB::writeBinary(sig, out);
|
|
|
|
DB::writePODBinary(*info, out);
|
|
|
|
DB::writePODBinary(*reinterpret_cast<const ucontext_t *>(context), out);
|
2019-04-17 14:53:54 +00:00
|
|
|
DB::writeBinary(getThreadNumber(), out);
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
out.next();
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/// The time that is usually enough for separate thread to print info into log.
|
2017-04-01 07:20:54 +00:00
|
|
|
::sleep(10);
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
call_default_signal_handler(sig);
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-06-23 14:41:07 +00:00
|
|
|
#if USE_UNWIND
|
2019-02-13 13:42:01 +00:00
|
|
|
/** We suppress the following ASan report. Also shown by Valgrind.
|
|
|
|
==124==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7f054be57000 at pc 0x0000068b0649 bp 0x7f060eeac590 sp 0x7f060eeabd40
|
|
|
|
READ of size 1 at 0x7f054be57000 thread T3
|
|
|
|
#0 0x68b0648 in write (/usr/bin/clickhouse+0x68b0648)
|
|
|
|
#1 0x717da02 in write_validate /build/obj-x86_64-linux-gnu/../contrib/libunwind/src/x86_64/Ginit.c:110:13
|
|
|
|
#2 0x717da02 in mincore_validate /build/obj-x86_64-linux-gnu/../contrib/libunwind/src/x86_64/Ginit.c:146
|
|
|
|
#3 0x717dec1 in validate_mem /build/obj-x86_64-linux-gnu/../contrib/libunwind/src/x86_64/Ginit.c:206:7
|
|
|
|
#4 0x717dec1 in access_mem /build/obj-x86_64-linux-gnu/../contrib/libunwind/src/x86_64/Ginit.c:240
|
|
|
|
#5 0x71881a9 in dwarf_get /build/obj-x86_64-linux-gnu/../contrib/libunwind/include/tdep-x86_64/libunwind_i.h:168:12
|
|
|
|
#6 0x71881a9 in apply_reg_state /build/obj-x86_64-linux-gnu/../contrib/libunwind/src/dwarf/Gparser.c:872
|
|
|
|
#7 0x718705c in _ULx86_64_dwarf_step /build/obj-x86_64-linux-gnu/../contrib/libunwind/src/dwarf/Gparser.c:953:10
|
|
|
|
#8 0x718f155 in _ULx86_64_step /build/obj-x86_64-linux-gnu/../contrib/libunwind/src/x86_64/Gstep.c:71:9
|
|
|
|
#9 0x7162671 in backtraceLibUnwind(void**, unsigned long, ucontext_t&) /build/obj-x86_64-linux-gnu/../libs/libdaemon/src/BaseDaemon.cpp:202:14
|
|
|
|
*/
|
|
|
|
size_t NO_SANITIZE_ADDRESS backtraceLibUnwind(void ** out_frames, size_t max_frames, ucontext_t & context)
|
2017-06-23 05:16:34 +00:00
|
|
|
{
|
|
|
|
unw_cursor_t cursor;
|
|
|
|
|
2017-09-28 19:42:01 +00:00
|
|
|
if (unw_init_local2(&cursor, &context, UNW_INIT_SIGNAL_FRAME) < 0)
|
2017-06-23 05:16:34 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
for (; i < max_frames; ++i)
|
|
|
|
{
|
|
|
|
unw_word_t ip;
|
|
|
|
unw_get_reg(&cursor, UNW_REG_IP, &ip);
|
|
|
|
out_frames[i] = reinterpret_cast<void*>(ip);
|
2018-01-13 00:40:41 +00:00
|
|
|
|
|
|
|
/// NOTE This triggers "AddressSanitizer: stack-buffer-overflow". Looks like false positive.
|
|
|
|
/// It's Ok, because we use this method if the program is crashed nevertheless.
|
2017-06-23 05:16:34 +00:00
|
|
|
if (!unw_step(&cursor))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
2017-06-23 14:41:07 +00:00
|
|
|
#endif
|
2017-06-23 05:16:34 +00:00
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
|
|
|
|
/** The thread that read info about signal or std::terminate from pipe.
|
|
|
|
* On HUP / USR1, close log files (for new files to be opened later).
|
|
|
|
* On information about std::terminate, write it to log.
|
|
|
|
* On other signals, write info to log.
|
2016-01-15 03:55:07 +00:00
|
|
|
*/
|
|
|
|
class SignalListener : public Poco::Runnable
|
|
|
|
{
|
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
enum Signals : int
|
|
|
|
{
|
|
|
|
StdTerminate = -1,
|
|
|
|
StopThread = -2
|
|
|
|
};
|
|
|
|
|
2017-09-07 21:04:48 +00:00
|
|
|
explicit SignalListener(BaseDaemon & daemon_)
|
|
|
|
: log(&Logger::get("BaseDaemon"))
|
|
|
|
, daemon(daemon_)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void run()
|
|
|
|
{
|
|
|
|
char buf[buf_size];
|
|
|
|
DB::ReadBufferFromFileDescriptor in(signal_pipe.read_fd, buf_size, buf);
|
|
|
|
|
|
|
|
while (!in.eof())
|
|
|
|
{
|
|
|
|
int sig = 0;
|
|
|
|
DB::readBinary(sig, in);
|
|
|
|
|
|
|
|
if (sig == Signals::StopThread)
|
|
|
|
{
|
|
|
|
LOG_INFO(log, "Stop SignalListener thread");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (sig == SIGHUP || sig == SIGUSR1)
|
|
|
|
{
|
|
|
|
LOG_DEBUG(log, "Received signal to close logs.");
|
|
|
|
BaseDaemon::instance().closeLogs();
|
|
|
|
LOG_INFO(log, "Opened new log file after received signal.");
|
|
|
|
}
|
|
|
|
else if (sig == Signals::StdTerminate)
|
|
|
|
{
|
|
|
|
ThreadNumber thread_num;
|
|
|
|
std::string message;
|
|
|
|
|
|
|
|
DB::readBinary(thread_num, in);
|
|
|
|
DB::readBinary(message, in);
|
|
|
|
|
|
|
|
onTerminate(message, thread_num);
|
|
|
|
}
|
|
|
|
else if (sig == SIGINT ||
|
|
|
|
sig == SIGQUIT ||
|
|
|
|
sig == SIGTERM)
|
|
|
|
{
|
|
|
|
daemon.handleSignal(sig);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
siginfo_t info;
|
|
|
|
ucontext_t context;
|
|
|
|
ThreadNumber thread_num;
|
|
|
|
|
|
|
|
DB::readPODBinary(info, in);
|
|
|
|
DB::readPODBinary(context, in);
|
|
|
|
DB::readBinary(thread_num, in);
|
|
|
|
|
|
|
|
onFault(sig, info, context, thread_num);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
|
|
private:
|
2017-04-01 07:20:54 +00:00
|
|
|
Logger * log;
|
|
|
|
BaseDaemon & daemon;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2016-06-08 14:39:19 +00:00
|
|
|
private:
|
2017-04-01 07:20:54 +00:00
|
|
|
void onTerminate(const std::string & message, ThreadNumber thread_num) const
|
|
|
|
{
|
2019-04-03 14:06:59 +00:00
|
|
|
LOG_ERROR(log, "(version " << VERSION_STRING << VERSION_OFFICIAL << ") (from thread " << thread_num << ") " << message);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void onFault(int sig, siginfo_t & info, ucontext_t & context, ThreadNumber thread_num) const
|
|
|
|
{
|
|
|
|
LOG_ERROR(log, "########################################");
|
2019-04-03 14:06:59 +00:00
|
|
|
LOG_ERROR(log, "(version " << VERSION_STRING << VERSION_OFFICIAL << ") (from thread " << thread_num << ") "
|
2017-04-01 07:20:54 +00:00
|
|
|
<< "Received signal " << strsignal(sig) << " (" << sig << ")" << ".");
|
|
|
|
|
|
|
|
void * caller_address = nullptr;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2016-01-16 01:38:07 +00:00
|
|
|
#if defined(__x86_64__)
|
2017-04-01 07:20:54 +00:00
|
|
|
/// Get the address at the time the signal was raised from the RIP (x86-64)
|
|
|
|
#if defined(__FreeBSD__)
|
|
|
|
caller_address = reinterpret_cast<void *>(context.uc_mcontext.mc_rip);
|
|
|
|
#elif defined(__APPLE__)
|
|
|
|
caller_address = reinterpret_cast<void *>(context.uc_mcontext->__ss.__rip);
|
|
|
|
#else
|
|
|
|
caller_address = reinterpret_cast<void *>(context.uc_mcontext.gregs[REG_RIP]);
|
2018-03-16 05:18:13 +00:00
|
|
|
auto err_mask = context.uc_mcontext.gregs[REG_ERR];
|
2017-04-01 07:20:54 +00:00
|
|
|
#endif
|
2016-01-16 01:38:07 +00:00
|
|
|
#elif defined(__aarch64__)
|
2017-04-01 07:20:54 +00:00
|
|
|
caller_address = reinterpret_cast<void *>(context.uc_mcontext.pc);
|
2016-01-16 01:38:07 +00:00
|
|
|
#endif
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2018-03-16 05:18:13 +00:00
|
|
|
switch (sig)
|
|
|
|
{
|
|
|
|
case SIGSEGV:
|
|
|
|
{
|
|
|
|
/// Print info about address and reason.
|
|
|
|
if (nullptr == info.si_addr)
|
|
|
|
LOG_ERROR(log, "Address: NULL pointer.");
|
|
|
|
else
|
|
|
|
LOG_ERROR(log, "Address: " << info.si_addr);
|
|
|
|
|
|
|
|
#if defined(__x86_64__) && !defined(__FreeBSD__) && !defined(__APPLE__)
|
|
|
|
if ((err_mask & 0x02))
|
|
|
|
LOG_ERROR(log, "Access: write.");
|
|
|
|
else
|
|
|
|
LOG_ERROR(log, "Access: read.");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (info.si_code)
|
|
|
|
{
|
|
|
|
case SEGV_ACCERR:
|
|
|
|
LOG_ERROR(log, "Attempted access has violated the permissions assigned to the memory area.");
|
|
|
|
break;
|
|
|
|
case SEGV_MAPERR:
|
|
|
|
LOG_ERROR(log, "Address not mapped to object.");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_ERROR(log, "Unknown si_code.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SIGBUS:
|
|
|
|
{
|
|
|
|
switch (info.si_code)
|
|
|
|
{
|
|
|
|
case BUS_ADRALN:
|
|
|
|
LOG_ERROR(log, "Invalid address alignment.");
|
|
|
|
break;
|
|
|
|
case BUS_ADRERR:
|
|
|
|
LOG_ERROR(log, "Non-existant physical address.");
|
|
|
|
break;
|
|
|
|
case BUS_OBJERR:
|
|
|
|
LOG_ERROR(log, "Object specific hardware error.");
|
|
|
|
break;
|
2018-03-18 09:02:29 +00:00
|
|
|
|
|
|
|
// Linux specific
|
|
|
|
#if defined(BUS_MCEERR_AR)
|
2018-03-16 05:18:13 +00:00
|
|
|
case BUS_MCEERR_AR:
|
|
|
|
LOG_ERROR(log, "Hardware memory error: action required.");
|
|
|
|
break;
|
2018-03-18 09:02:29 +00:00
|
|
|
#endif
|
|
|
|
#if defined(BUS_MCEERR_AO)
|
2018-03-16 05:18:13 +00:00
|
|
|
case BUS_MCEERR_AO:
|
|
|
|
LOG_ERROR(log, "Hardware memory error: action optional.");
|
|
|
|
break;
|
2018-03-18 09:02:29 +00:00
|
|
|
#endif
|
|
|
|
|
2018-03-16 05:18:13 +00:00
|
|
|
default:
|
|
|
|
LOG_ERROR(log, "Unknown si_code.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SIGILL:
|
|
|
|
{
|
|
|
|
switch (info.si_code)
|
|
|
|
{
|
|
|
|
case ILL_ILLOPC:
|
|
|
|
LOG_ERROR(log, "Illegal opcode.");
|
|
|
|
break;
|
|
|
|
case ILL_ILLOPN:
|
|
|
|
LOG_ERROR(log, "Illegal operand.");
|
|
|
|
break;
|
|
|
|
case ILL_ILLADR:
|
|
|
|
LOG_ERROR(log, "Illegal addressing mode.");
|
|
|
|
break;
|
|
|
|
case ILL_ILLTRP:
|
|
|
|
LOG_ERROR(log, "Illegal trap.");
|
|
|
|
break;
|
|
|
|
case ILL_PRVOPC:
|
|
|
|
LOG_ERROR(log, "Privileged opcode.");
|
|
|
|
break;
|
|
|
|
case ILL_PRVREG:
|
|
|
|
LOG_ERROR(log, "Privileged register.");
|
|
|
|
break;
|
|
|
|
case ILL_COPROC:
|
|
|
|
LOG_ERROR(log, "Coprocessor error.");
|
|
|
|
break;
|
|
|
|
case ILL_BADSTK:
|
|
|
|
LOG_ERROR(log, "Internal stack error.");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_ERROR(log, "Unknown si_code.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SIGFPE:
|
|
|
|
{
|
|
|
|
switch (info.si_code)
|
|
|
|
{
|
|
|
|
case FPE_INTDIV:
|
|
|
|
LOG_ERROR(log, "Integer divide by zero.");
|
|
|
|
break;
|
|
|
|
case FPE_INTOVF:
|
|
|
|
LOG_ERROR(log, "Integer overflow.");
|
|
|
|
break;
|
|
|
|
case FPE_FLTDIV:
|
|
|
|
LOG_ERROR(log, "Floating point divide by zero.");
|
|
|
|
break;
|
|
|
|
case FPE_FLTOVF:
|
|
|
|
LOG_ERROR(log, "Floating point overflow.");
|
|
|
|
break;
|
|
|
|
case FPE_FLTUND:
|
|
|
|
LOG_ERROR(log, "Floating point underflow.");
|
|
|
|
break;
|
|
|
|
case FPE_FLTRES:
|
|
|
|
LOG_ERROR(log, "Floating point inexact result.");
|
|
|
|
break;
|
|
|
|
case FPE_FLTINV:
|
|
|
|
LOG_ERROR(log, "Floating point invalid operation.");
|
|
|
|
break;
|
|
|
|
case FPE_FLTSUB:
|
|
|
|
LOG_ERROR(log, "Subscript out of range.");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_ERROR(log, "Unknown si_code.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
static const int max_frames = 50;
|
|
|
|
void * frames[max_frames];
|
2017-06-23 14:41:07 +00:00
|
|
|
|
|
|
|
#if USE_UNWIND
|
2017-06-23 05:16:34 +00:00
|
|
|
int frames_size = backtraceLibUnwind(frames, max_frames, context);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-06-23 05:16:34 +00:00
|
|
|
if (frames_size)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-06-23 14:41:07 +00:00
|
|
|
#else
|
2017-07-20 15:18:45 +00:00
|
|
|
/// No libunwind means no backtrace, because we are in a different thread from the one where the signal happened.
|
|
|
|
/// So at least print the function where the signal happened.
|
|
|
|
if (caller_address)
|
2017-06-23 14:41:07 +00:00
|
|
|
{
|
2017-07-20 15:18:45 +00:00
|
|
|
frames[0] = caller_address;
|
|
|
|
int frames_size = 1;
|
2017-06-23 14:41:07 +00:00
|
|
|
#endif
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
char ** symbols = backtrace_symbols(frames, frames_size);
|
|
|
|
|
|
|
|
if (!symbols)
|
|
|
|
{
|
|
|
|
if (caller_address)
|
|
|
|
LOG_ERROR(log, "Caller address: " << caller_address);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-20 15:18:45 +00:00
|
|
|
for (int i = 0; i < frames_size; ++i)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-01-17 18:06:39 +00:00
|
|
|
/// Perform demangling of names. Name is in parentheses, before '+' character.
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
char * name_start = nullptr;
|
|
|
|
char * name_end = nullptr;
|
|
|
|
char * demangled_name = nullptr;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if (nullptr != (name_start = strchr(symbols[i], '('))
|
|
|
|
&& nullptr != (name_end = strchr(name_start, '+')))
|
|
|
|
{
|
|
|
|
++name_start;
|
|
|
|
*name_end = '\0';
|
|
|
|
demangled_name = abi::__cxa_demangle(name_start, 0, 0, &status);
|
|
|
|
*name_end = '+';
|
|
|
|
}
|
|
|
|
|
|
|
|
std::stringstream res;
|
|
|
|
|
|
|
|
res << i << ". ";
|
|
|
|
|
|
|
|
if (nullptr != demangled_name && 0 == status)
|
|
|
|
{
|
|
|
|
res.write(symbols[i], name_start - symbols[i]);
|
|
|
|
res << demangled_name << name_end;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
res << symbols[i];
|
|
|
|
|
|
|
|
LOG_ERROR(log, res.rdbuf());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-15 03:55:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/** To use with std::set_terminate.
|
|
|
|
* Collects slightly more info than __gnu_cxx::__verbose_terminate_handler,
|
|
|
|
* and send it to pipe. Other thread will read this info from pipe and asynchronously write it to log.
|
|
|
|
* Look at libstdc++-v3/libsupc++/vterminate.cc for example.
|
2016-01-15 03:55:07 +00:00
|
|
|
*/
|
|
|
|
static void terminate_handler()
|
|
|
|
{
|
2017-10-19 19:36:37 +00:00
|
|
|
static thread_local bool terminating = false;
|
2017-04-01 07:20:54 +00:00
|
|
|
if (terminating)
|
|
|
|
{
|
|
|
|
abort();
|
2017-09-07 21:04:48 +00:00
|
|
|
return; /// Just for convenience.
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
terminating = true;
|
|
|
|
|
2018-08-29 18:35:46 +00:00
|
|
|
std::string log_message;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-08-29 18:35:46 +00:00
|
|
|
if (std::current_exception())
|
|
|
|
log_message = "Terminate called for uncaught exception:\n" + DB::getCurrentExceptionMessage(true);
|
2017-04-01 07:20:54 +00:00
|
|
|
else
|
2018-08-29 18:35:46 +00:00
|
|
|
log_message = "Terminate called without an active exception";
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
static const size_t buf_size = 1024;
|
|
|
|
|
|
|
|
if (log_message.size() > buf_size - 16)
|
|
|
|
log_message.resize(buf_size - 16);
|
|
|
|
|
|
|
|
char buf[buf_size];
|
|
|
|
DB::WriteBufferFromFileDescriptor out(signal_pipe.write_fd, buf_size, buf);
|
|
|
|
|
|
|
|
DB::writeBinary(static_cast<int>(SignalListener::StdTerminate), out);
|
2019-04-17 14:53:54 +00:00
|
|
|
DB::writeBinary(getThreadNumber(), out);
|
2017-04-01 07:20:54 +00:00
|
|
|
DB::writeBinary(log_message, out);
|
|
|
|
out.next();
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-14 20:41:46 +00:00
|
|
|
static std::string createDirectory(const std::string & file)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2018-02-14 20:41:46 +00:00
|
|
|
auto path = Poco::Path(file).makeParent();
|
|
|
|
if (path.toString().empty())
|
|
|
|
return "";
|
2018-02-08 19:12:37 +00:00
|
|
|
Poco::File(path).createDirectories();
|
|
|
|
return path.toString();
|
2016-01-15 03:55:07 +00:00
|
|
|
};
|
|
|
|
|
2017-02-14 12:11:46 +00:00
|
|
|
static bool tryCreateDirectories(Poco::Logger * logger, const std::string & path)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
Poco::File(path).createDirectories();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
LOG_WARNING(logger, __PRETTY_FUNCTION__ << ": when creating " << path << ", " << DB::getCurrentExceptionMessage(true));
|
|
|
|
}
|
|
|
|
return false;
|
2017-02-14 12:11:46 +00:00
|
|
|
}
|
|
|
|
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::reloadConfiguration()
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2018-01-17 18:06:39 +00:00
|
|
|
/** If the program is not run in daemon mode and 'config-file' is not specified,
|
|
|
|
* then we use config from 'config.xml' file in current directory,
|
|
|
|
* but will log to console (or use parameters --log-file, --errorlog-file from command line)
|
|
|
|
* instead of using files specified in config.xml.
|
|
|
|
* (It's convenient to log in console when you start server without any command line parameters.)
|
2017-04-01 07:20:54 +00:00
|
|
|
*/
|
|
|
|
config_path = config().getString("config-file", "config.xml");
|
2018-11-27 16:11:46 +00:00
|
|
|
DB::ConfigProcessor config_processor(config_path, false, true);
|
|
|
|
config_processor.setConfigPath(Poco::Path(config_path).makeParent().toString());
|
|
|
|
loaded_config = config_processor.loadConfig(/* allow_zk_includes = */ true);
|
|
|
|
|
2017-06-19 13:31:55 +00:00
|
|
|
if (last_configuration != nullptr)
|
|
|
|
config().removeConfiguration(last_configuration);
|
|
|
|
last_configuration = loaded_config.configuration.duplicate();
|
|
|
|
config().add(last_configuration, PRIO_DEFAULT, false);
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-02 12:26:07 +00:00
|
|
|
BaseDaemon::BaseDaemon()
|
|
|
|
{
|
2019-02-02 14:23:48 +00:00
|
|
|
checkRequiredInstructions();
|
2019-02-02 12:26:07 +00:00
|
|
|
}
|
2016-03-04 02:25:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
BaseDaemon::~BaseDaemon()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
writeSignalIDtoSignalPipe(SignalListener::StopThread);
|
|
|
|
signal_listener_thread.join();
|
|
|
|
signal_pipe.close();
|
2016-03-04 02:25:04 +00:00
|
|
|
}
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
|
|
|
2019-02-02 17:07:20 +00:00
|
|
|
enum class InstructionFail
|
|
|
|
{
|
2019-02-02 12:26:07 +00:00
|
|
|
NONE = 0,
|
2019-02-02 13:15:13 +00:00
|
|
|
SSE3 = 1,
|
|
|
|
SSSE3 = 2,
|
|
|
|
SSE4_1 = 3,
|
|
|
|
SSE4_2 = 4,
|
|
|
|
AVX = 5,
|
|
|
|
AVX2 = 6,
|
|
|
|
AVX512 = 7
|
2019-02-02 12:26:07 +00:00
|
|
|
};
|
|
|
|
|
2019-02-02 17:07:20 +00:00
|
|
|
static std::string instructionFailToString(InstructionFail fail)
|
2019-02-02 13:15:13 +00:00
|
|
|
{
|
|
|
|
switch(fail)
|
|
|
|
{
|
|
|
|
case InstructionFail::NONE:
|
|
|
|
return "NONE";
|
|
|
|
case InstructionFail::SSE3:
|
|
|
|
return "SSE3";
|
|
|
|
case InstructionFail::SSSE3:
|
|
|
|
return "SSSE3";
|
|
|
|
case InstructionFail::SSE4_1:
|
|
|
|
return "SSE4.1";
|
|
|
|
case InstructionFail::SSE4_2:
|
|
|
|
return "SSE4.2";
|
|
|
|
case InstructionFail::AVX:
|
|
|
|
return "AVX";
|
|
|
|
case InstructionFail::AVX2:
|
|
|
|
return "AVX2";
|
|
|
|
case InstructionFail::AVX512:
|
|
|
|
return "AVX512";
|
|
|
|
}
|
2019-02-02 14:23:48 +00:00
|
|
|
__builtin_unreachable();
|
2019-02-02 13:15:13 +00:00
|
|
|
}
|
|
|
|
|
2019-02-02 12:26:07 +00:00
|
|
|
|
|
|
|
static sigjmp_buf jmpbuf;
|
|
|
|
|
2019-02-02 14:23:48 +00:00
|
|
|
static void sigIllCheckHandler(int sig, siginfo_t * info, void * context)
|
2019-02-02 12:26:07 +00:00
|
|
|
{
|
|
|
|
siglongjmp(jmpbuf, 1);
|
|
|
|
}
|
|
|
|
|
2019-02-02 13:15:13 +00:00
|
|
|
/// Check if necessary sse extensions are available by trying to execute some sse instructions.
|
|
|
|
/// If instruction is unavailable, SIGILL will be sent by kernel.
|
2019-02-02 14:23:48 +00:00
|
|
|
static void checkRequiredInstructions(volatile InstructionFail & fail)
|
2019-02-02 13:15:13 +00:00
|
|
|
{
|
|
|
|
#if __SSE3__
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::SSE3;
|
2019-02-02 13:15:13 +00:00
|
|
|
__asm__ volatile ("addsubpd %%xmm0, %%xmm0" : : : "xmm0");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __SSSE3__
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::SSSE3;
|
2019-02-02 13:15:13 +00:00
|
|
|
__asm__ volatile ("pabsw %%xmm0, %%xmm0" : : : "xmm0");
|
|
|
|
|
|
|
|
#endif
|
2019-02-02 12:26:07 +00:00
|
|
|
|
2019-02-02 13:15:13 +00:00
|
|
|
#if __SSE4_1__
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::SSE4_1;
|
2019-02-02 13:15:13 +00:00
|
|
|
__asm__ volatile ("pmaxud %%xmm0, %%xmm0" : : : "xmm0");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __SSE4_2__
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::SSE4_2;
|
2019-02-02 13:15:13 +00:00
|
|
|
__asm__ volatile ("pcmpgtq %%xmm0, %%xmm0" : : : "xmm0");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __AVX__
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::AVX;
|
2019-02-27 20:04:35 +00:00
|
|
|
__asm__ volatile ("vaddpd %%ymm0, %%ymm0, %%ymm0" : : : "ymm0");
|
2019-02-02 13:15:13 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __AVX2__
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::AVX2;
|
2019-03-07 16:38:39 +00:00
|
|
|
__asm__ volatile ("vpabsw %%ymm0, %%ymm0" : : : "ymm0");
|
2019-02-02 13:15:13 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if __AVX512__
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::AVX512;
|
2019-03-07 16:38:39 +00:00
|
|
|
__asm__ volatile ("vpabsw %%zmm0, %%zmm0" : : : "zmm0");
|
2019-02-02 13:15:13 +00:00
|
|
|
#endif
|
|
|
|
|
2019-02-02 13:54:30 +00:00
|
|
|
fail = InstructionFail::NONE;
|
2019-02-02 12:26:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-02 14:23:48 +00:00
|
|
|
void BaseDaemon::checkRequiredInstructions()
|
2019-02-02 12:26:07 +00:00
|
|
|
{
|
2019-02-02 13:54:30 +00:00
|
|
|
struct sigaction sa{};
|
2019-02-02 14:23:48 +00:00
|
|
|
struct sigaction sa_old{};
|
|
|
|
sa.sa_sigaction = sigIllCheckHandler;
|
2019-02-02 12:26:07 +00:00
|
|
|
sa.sa_flags = SA_SIGINFO;
|
|
|
|
auto signal = SIGILL;
|
2019-02-02 17:07:20 +00:00
|
|
|
if (sigemptyset(&sa.sa_mask) != 0
|
|
|
|
|| sigaddset(&sa.sa_mask, signal) != 0
|
|
|
|
|| sigaction(signal, &sa, &sa_old) != 0)
|
|
|
|
{
|
2019-02-02 13:07:02 +00:00
|
|
|
std::cerr << "Can not set signal handler\n";
|
2019-02-02 12:26:07 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
volatile InstructionFail fail = InstructionFail::NONE;
|
|
|
|
|
2019-02-02 17:07:20 +00:00
|
|
|
if (sigsetjmp(jmpbuf, 1))
|
|
|
|
{
|
|
|
|
std::cerr << "Instruction check fail. There is no " << instructionFailToString(fail) << " instruction set\n";
|
2019-02-02 12:26:07 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-02-02 14:23:48 +00:00
|
|
|
::checkRequiredInstructions(fail);
|
2019-02-02 12:26:07 +00:00
|
|
|
|
2019-02-02 17:07:20 +00:00
|
|
|
if (sigaction(signal, &sa_old, nullptr))
|
|
|
|
{
|
2019-02-02 13:07:02 +00:00
|
|
|
std::cerr << "Can not set signal handler\n";
|
2019-02-02 12:26:07 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::terminate()
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
getTaskManager().cancelAll();
|
2018-08-20 23:16:50 +00:00
|
|
|
if (::raise(SIGTERM) != 0)
|
2017-04-01 07:20:54 +00:00
|
|
|
throw Poco::SystemException("cannot terminate process");
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::kill()
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
pid.clear();
|
2018-08-20 23:16:50 +00:00
|
|
|
if (::raise(SIGKILL) != 0)
|
|
|
|
throw Poco::SystemException("cannot kill process");
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::sleep(double seconds)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
wakeup_event.reset();
|
|
|
|
wakeup_event.tryWait(seconds * 1000);
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::wakeup()
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
wakeup_event.set();
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-02-28 20:34:25 +00:00
|
|
|
void BaseDaemon::buildLoggers(Poco::Util::AbstractConfiguration & config)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2018-02-28 20:34:25 +00:00
|
|
|
auto current_logger = config.getString("logger");
|
|
|
|
if (config_logger == current_logger)
|
|
|
|
return;
|
|
|
|
config_logger = current_logger;
|
2018-01-19 18:54:40 +00:00
|
|
|
|
2018-05-15 16:22:00 +00:00
|
|
|
bool is_daemon = config.getBool("application.runAsDaemon", false);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-06-15 17:32:35 +00:00
|
|
|
/// Split logs to ordinary log, error log, syslog and console.
|
|
|
|
/// Use extended interface of Channel for more comprehensive logging.
|
|
|
|
Poco::AutoPtr<DB::OwnSplitChannel> split = new DB::OwnSplitChannel;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-02-28 20:34:25 +00:00
|
|
|
auto log_level = config.getString("logger.level", "trace");
|
2018-04-19 18:19:12 +00:00
|
|
|
const auto log_path = config.getString("logger.log", "");
|
|
|
|
if (!log_path.empty())
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-04-19 18:19:12 +00:00
|
|
|
createDirectory(log_path);
|
|
|
|
std::cerr << "Logging " << log_level << " to " << log_path << std::endl;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
// Set up two channel chains.
|
2018-11-21 20:56:37 +00:00
|
|
|
log_file = new Poco::FileChannel;
|
2018-04-19 18:19:12 +00:00
|
|
|
log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(log_path).absolute().toString());
|
2018-02-28 20:34:25 +00:00
|
|
|
log_file->setProperty(Poco::FileChannel::PROP_ROTATION, config.getRawString("logger.size", "100M"));
|
2017-11-21 17:46:28 +00:00
|
|
|
log_file->setProperty(Poco::FileChannel::PROP_ARCHIVE, "number");
|
2018-02-28 20:34:25 +00:00
|
|
|
log_file->setProperty(Poco::FileChannel::PROP_COMPRESS, config.getRawString("logger.compress", "true"));
|
|
|
|
log_file->setProperty(Poco::FileChannel::PROP_PURGECOUNT, config.getRawString("logger.count", "1"));
|
|
|
|
log_file->setProperty(Poco::FileChannel::PROP_FLUSH, config.getRawString("logger.flush", "true"));
|
|
|
|
log_file->setProperty(Poco::FileChannel::PROP_ROTATEONOPEN, config.getRawString("logger.rotateOnOpen", "false"));
|
2017-04-01 07:20:54 +00:00
|
|
|
log_file->open();
|
2018-06-15 17:32:35 +00:00
|
|
|
|
|
|
|
Poco::AutoPtr<OwnPatternFormatter> pf = new OwnPatternFormatter(this);
|
|
|
|
|
|
|
|
Poco::AutoPtr<DB::OwnFormattingChannel> log = new DB::OwnFormattingChannel(pf, log_file);
|
|
|
|
split->addChannel(log);
|
2018-01-19 18:54:40 +00:00
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-04-19 18:19:12 +00:00
|
|
|
const auto errorlog_path = config.getString("logger.errorlog", "");
|
|
|
|
if (!errorlog_path.empty())
|
2018-01-19 18:54:40 +00:00
|
|
|
{
|
2018-04-19 18:19:12 +00:00
|
|
|
createDirectory(errorlog_path);
|
|
|
|
std::cerr << "Logging errors to " << errorlog_path << std::endl;
|
2018-06-15 17:32:35 +00:00
|
|
|
|
2018-11-21 20:56:37 +00:00
|
|
|
error_log_file = new Poco::FileChannel;
|
2018-04-19 18:19:12 +00:00
|
|
|
error_log_file->setProperty(Poco::FileChannel::PROP_PATH, Poco::Path(errorlog_path).absolute().toString());
|
2018-02-28 20:34:25 +00:00
|
|
|
error_log_file->setProperty(Poco::FileChannel::PROP_ROTATION, config.getRawString("logger.size", "100M"));
|
2018-01-19 18:54:40 +00:00
|
|
|
error_log_file->setProperty(Poco::FileChannel::PROP_ARCHIVE, "number");
|
2018-02-28 20:34:25 +00:00
|
|
|
error_log_file->setProperty(Poco::FileChannel::PROP_COMPRESS, config.getRawString("logger.compress", "true"));
|
|
|
|
error_log_file->setProperty(Poco::FileChannel::PROP_PURGECOUNT, config.getRawString("logger.count", "1"));
|
|
|
|
error_log_file->setProperty(Poco::FileChannel::PROP_FLUSH, config.getRawString("logger.flush", "true"));
|
|
|
|
error_log_file->setProperty(Poco::FileChannel::PROP_ROTATEONOPEN, config.getRawString("logger.rotateOnOpen", "false"));
|
2018-06-15 17:32:35 +00:00
|
|
|
|
|
|
|
Poco::AutoPtr<OwnPatternFormatter> pf = new OwnPatternFormatter(this);
|
|
|
|
|
|
|
|
Poco::AutoPtr<DB::OwnFormattingChannel> errorlog = new DB::OwnFormattingChannel(pf, error_log_file);
|
2018-11-21 20:56:37 +00:00
|
|
|
errorlog->setLevel(Poco::Message::PRIO_NOTICE);
|
2018-01-19 18:54:40 +00:00
|
|
|
errorlog->open();
|
2018-06-15 17:32:35 +00:00
|
|
|
split->addChannel(errorlog);
|
2018-01-19 18:54:40 +00:00
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-01-19 18:54:40 +00:00
|
|
|
/// "dynamic_layer_selection" is needed only for Yandex.Metrika, that share part of ClickHouse code.
|
|
|
|
/// We don't need this configuration parameter.
|
2018-01-17 18:10:38 +00:00
|
|
|
|
2018-02-28 20:34:25 +00:00
|
|
|
if (config.getBool("logger.use_syslog", false) || config.getBool("dynamic_layer_selection", false))
|
2018-01-19 18:54:40 +00:00
|
|
|
{
|
2018-06-01 16:27:21 +00:00
|
|
|
const std::string & cmd_name = commandName();
|
2018-06-01 15:06:45 +00:00
|
|
|
|
|
|
|
if (config.has("logger.syslog.address"))
|
|
|
|
{
|
|
|
|
syslog_channel = new Poco::Net::RemoteSyslogChannel();
|
|
|
|
// syslog address
|
|
|
|
syslog_channel->setProperty(Poco::Net::RemoteSyslogChannel::PROP_LOGHOST, config.getString("logger.syslog.address"));
|
|
|
|
if (config.has("logger.syslog.hostname"))
|
|
|
|
{
|
|
|
|
syslog_channel->setProperty(Poco::Net::RemoteSyslogChannel::PROP_HOST, config.getString("logger.syslog.hostname"));
|
|
|
|
}
|
|
|
|
syslog_channel->setProperty(Poco::Net::RemoteSyslogChannel::PROP_FORMAT, config.getString("logger.syslog.format", "syslog"));
|
|
|
|
syslog_channel->setProperty(Poco::Net::RemoteSyslogChannel::PROP_FACILITY, config.getString("logger.syslog.facility", "LOG_USER"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
syslog_channel = new Poco::SyslogChannel();
|
2018-06-01 16:27:21 +00:00
|
|
|
syslog_channel->setProperty(Poco::SyslogChannel::PROP_NAME, cmd_name);
|
2018-06-01 15:06:45 +00:00
|
|
|
syslog_channel->setProperty(Poco::SyslogChannel::PROP_OPTIONS, config.getString("logger.syslog.options", "LOG_CONS|LOG_PID"));
|
|
|
|
syslog_channel->setProperty(Poco::SyslogChannel::PROP_FACILITY, config.getString("logger.syslog.facility", "LOG_DAEMON"));
|
|
|
|
}
|
2018-06-15 17:32:35 +00:00
|
|
|
syslog_channel->open();
|
|
|
|
|
|
|
|
Poco::AutoPtr<OwnPatternFormatter> pf = new OwnPatternFormatter(this, OwnPatternFormatter::ADD_LAYER_TAG);
|
2018-06-01 15:06:45 +00:00
|
|
|
|
2018-06-15 17:32:35 +00:00
|
|
|
Poco::AutoPtr<DB::OwnFormattingChannel> log = new DB::OwnFormattingChannel(pf, syslog_channel);
|
2018-01-19 18:54:40 +00:00
|
|
|
split->addChannel(log);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-01-19 18:54:40 +00:00
|
|
|
|
2018-02-28 20:34:25 +00:00
|
|
|
if (config.getBool("logger.console", false) || (!config.hasProperty("logger.console") && !is_daemon && (isatty(STDIN_FILENO) || isatty(STDERR_FILENO))))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-06-15 17:32:35 +00:00
|
|
|
Poco::AutoPtr<DB::OwnFormattingChannel> log = new DB::OwnFormattingChannel(new OwnPatternFormatter(this), new Poco::ConsoleChannel);
|
2018-02-28 20:34:25 +00:00
|
|
|
logger().warning("Logging " + log_level + " to console");
|
2018-01-19 18:54:40 +00:00
|
|
|
split->addChannel(log);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
2018-01-19 18:54:40 +00:00
|
|
|
split->open();
|
|
|
|
logger().close();
|
|
|
|
logger().setChannel(split);
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
// Global logging level (it can be overridden for specific loggers).
|
2018-02-28 20:34:25 +00:00
|
|
|
logger().setLevel(log_level);
|
|
|
|
|
|
|
|
// Set level to all already created loggers
|
|
|
|
std::vector <std::string> names;
|
|
|
|
Logger::root().names(names);
|
|
|
|
for (const auto & name : names)
|
|
|
|
Logger::root().get(name).setLevel(log_level);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
// Attach to the root logger.
|
2018-02-28 20:34:25 +00:00
|
|
|
Logger::root().setLevel(log_level);
|
2017-04-01 07:20:54 +00:00
|
|
|
Logger::root().setChannel(logger().getChannel());
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
// Explicitly specified log levels for specific loggers.
|
2018-11-21 20:56:37 +00:00
|
|
|
Poco::Util::AbstractConfiguration::Keys levels;
|
2018-02-28 20:34:25 +00:00
|
|
|
config.keys("logger.levels", levels);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-11-21 20:56:37 +00:00
|
|
|
if (!levels.empty())
|
|
|
|
for (const auto & level : levels)
|
|
|
|
Logger::get(level).setLevel(config.getString("logger.levels." + level, "trace"));
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::closeLogs()
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (log_file)
|
|
|
|
log_file->close();
|
|
|
|
if (error_log_file)
|
|
|
|
error_log_file->close();
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
if (!log_file)
|
|
|
|
logger().warning("Logging to console but received signal to close log file (ignoring).");
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2017-02-14 12:12:23 +00:00
|
|
|
std::string BaseDaemon::getDefaultCorePath() const
|
2017-01-09 13:42:29 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
return "/opt/cores/";
|
2017-01-09 13:42:29 +00:00
|
|
|
}
|
|
|
|
|
2018-08-12 12:23:22 +00:00
|
|
|
void BaseDaemon::closeFDs()
|
|
|
|
{
|
|
|
|
#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__))
|
|
|
|
Poco::File proc_path{"/dev/fd"};
|
|
|
|
#else
|
|
|
|
Poco::File proc_path{"/proc/self/fd"};
|
|
|
|
#endif
|
|
|
|
if (proc_path.isDirectory()) /// Hooray, proc exists
|
|
|
|
{
|
2019-01-22 14:37:28 +00:00
|
|
|
std::vector<std::string> fds;
|
|
|
|
/// in /proc/self/fd directory filenames are numeric file descriptors
|
|
|
|
proc_path.list(fds);
|
|
|
|
for (const auto & fd_str : fds)
|
2018-08-12 12:23:22 +00:00
|
|
|
{
|
2019-01-22 16:28:05 +00:00
|
|
|
int fd = DB::parse<int>(fd_str);
|
2018-08-12 12:23:22 +00:00
|
|
|
if (fd > 2 && fd != signal_pipe.read_fd && fd != signal_pipe.write_fd)
|
|
|
|
::close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-01-22 16:28:05 +00:00
|
|
|
int max_fd = -1;
|
2018-08-12 12:23:22 +00:00
|
|
|
#ifdef _SC_OPEN_MAX
|
|
|
|
max_fd = sysconf(_SC_OPEN_MAX);
|
|
|
|
if (max_fd == -1)
|
|
|
|
#endif
|
|
|
|
max_fd = 256; /// bad fallback
|
2019-01-22 16:28:05 +00:00
|
|
|
for (int fd = 3; fd < max_fd; ++fd)
|
2018-08-12 12:23:22 +00:00
|
|
|
if (fd != signal_pipe.read_fd && fd != signal_pipe.write_fd)
|
|
|
|
::close(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-18 01:00:13 +00:00
|
|
|
void BaseDaemon::initialize(Application & self)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2018-08-12 12:23:22 +00:00
|
|
|
closeFDs();
|
2017-04-01 07:20:54 +00:00
|
|
|
task_manager.reset(new Poco::TaskManager);
|
|
|
|
ServerApplication::initialize(self);
|
|
|
|
|
2018-02-08 19:12:37 +00:00
|
|
|
{
|
|
|
|
/// Parsing all args and converting to config layer
|
|
|
|
/// Test: -- --1=1 --1=2 --3 5 7 8 -9 10 -11=12 14= 15== --16==17 --=18 --19= --20 21 22 --23 --24 25 --26 -27 28 ---29=30 -- ----31 32 --33 3-4
|
|
|
|
Poco::AutoPtr<Poco::Util::MapConfiguration> map_config = new Poco::Util::MapConfiguration;
|
|
|
|
std::string key;
|
|
|
|
for(auto & arg : argv())
|
|
|
|
{
|
|
|
|
auto key_start = arg.find_first_not_of('-');
|
|
|
|
auto pos_minus = arg.find('-');
|
|
|
|
auto pos_eq = arg.find('=');
|
|
|
|
|
|
|
|
// old saved '--key', will set to some true value "1"
|
|
|
|
if (!key.empty() && pos_minus != std::string::npos && pos_minus < key_start)
|
|
|
|
{
|
|
|
|
map_config->setString(key, "1");
|
|
|
|
key = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pos_eq == std::string::npos)
|
|
|
|
{
|
|
|
|
if (!key.empty())
|
|
|
|
{
|
|
|
|
if (pos_minus == std::string::npos || pos_minus > key_start)
|
|
|
|
{
|
|
|
|
map_config->setString(key, arg);
|
|
|
|
}
|
|
|
|
key = "";
|
|
|
|
}
|
|
|
|
if (pos_minus != std::string::npos && key_start != std::string::npos && pos_minus < key_start)
|
|
|
|
key = arg.substr(key_start);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
key = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key_start == std::string::npos)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pos_minus > key_start)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
key = arg.substr(key_start, pos_eq - key_start);
|
|
|
|
if (key.empty())
|
|
|
|
continue;
|
|
|
|
std::string value;
|
|
|
|
if (arg.size() > pos_eq)
|
|
|
|
value = arg.substr(pos_eq+1);
|
|
|
|
|
|
|
|
map_config->setString(key, value);
|
|
|
|
key = "";
|
|
|
|
}
|
|
|
|
/// now highest priority (lowest value) is PRIO_APPLICATION = -100, we want higher!
|
|
|
|
config().add(map_config, PRIO_APPLICATION - 100);
|
|
|
|
}
|
|
|
|
|
2018-05-15 16:22:00 +00:00
|
|
|
bool is_daemon = config().getBool("application.runAsDaemon", false);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
if (is_daemon)
|
|
|
|
{
|
2018-01-17 18:06:39 +00:00
|
|
|
/** When creating pid file and looking for config, will search for paths relative to the working path of the program when started.
|
2017-04-01 07:20:54 +00:00
|
|
|
*/
|
|
|
|
std::string path = Poco::Path(config().getString("application.path")).setFileName("").toString();
|
|
|
|
if (0 != chdir(path.c_str()))
|
|
|
|
throw Poco::Exception("Cannot change directory to " + path);
|
|
|
|
}
|
|
|
|
|
|
|
|
reloadConfiguration();
|
|
|
|
|
2017-11-21 16:54:25 +00:00
|
|
|
/// This must be done before creation of any files (including logs).
|
2019-01-30 21:29:50 +00:00
|
|
|
mode_t umask_num = 0027;
|
2017-11-21 16:54:25 +00:00
|
|
|
if (config().has("umask"))
|
|
|
|
{
|
|
|
|
std::string umask_str = config().getString("umask");
|
|
|
|
std::stringstream stream;
|
|
|
|
stream << umask_str;
|
|
|
|
stream >> std::oct >> umask_num;
|
|
|
|
}
|
2019-01-30 21:29:50 +00:00
|
|
|
umask(umask_num);
|
2017-11-21 16:54:25 +00:00
|
|
|
|
2018-11-27 16:11:46 +00:00
|
|
|
DB::ConfigProcessor(config_path).savePreprocessedConfig(loaded_config, "");
|
2017-11-21 16:54:25 +00:00
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/// Write core dump on crash.
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
struct rlimit rlim;
|
|
|
|
if (getrlimit(RLIMIT_CORE, &rlim))
|
|
|
|
throw Poco::Exception("Cannot getrlimit");
|
2018-01-17 18:06:39 +00:00
|
|
|
/// 1 GiB by default. If more - it writes to disk too long.
|
2017-04-01 07:20:54 +00:00
|
|
|
rlim.rlim_cur = config().getUInt64("core_dump.size_limit", 1024 * 1024 * 1024);
|
|
|
|
|
2019-02-21 18:51:43 +00:00
|
|
|
if (rlim.rlim_cur && setrlimit(RLIMIT_CORE, &rlim))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-01-17 18:06:39 +00:00
|
|
|
/// It doesn't work under address/thread sanitizer. http://lists.llvm.org/pipermail/llvm-bugs/2013-April/027880.html
|
2019-02-21 18:51:43 +00:00
|
|
|
std::cerr << "Cannot set max size of core file to " + std::to_string(rlim.rlim_cur) << std::endl;
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This must be done before any usage of DateLUT. In particular, before any logging.
|
|
|
|
if (config().has("timezone"))
|
|
|
|
{
|
|
|
|
if (0 != setenv("TZ", config().getString("timezone").data(), 1))
|
|
|
|
throw Poco::Exception("Cannot setenv TZ variable");
|
|
|
|
|
|
|
|
tzset();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string log_path = config().getString("logger.log", "");
|
|
|
|
if (!log_path.empty())
|
|
|
|
log_path = Poco::Path(log_path).setFileName("").toString();
|
|
|
|
|
2018-05-15 17:05:13 +00:00
|
|
|
/** Redirect stdout, stderr to separate files in the log directory (or in the specified file).
|
|
|
|
* Some libraries write to stderr in case of errors in debug mode,
|
|
|
|
* and this output makes sense even if the program is run in daemon mode.
|
|
|
|
* We have to do it before buildLoggers, for errors on logger initialization will be written to these files.
|
|
|
|
* If logger.stderr is specified then stderr will be forcibly redirected to that file.
|
|
|
|
*/
|
2018-05-15 18:10:50 +00:00
|
|
|
if ((!log_path.empty() && is_daemon) || config().has("logger.stderr"))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-08-29 18:48:14 +00:00
|
|
|
std::string stderr_path = config().getString("logger.stderr", log_path + "/stderr.log");
|
2018-05-15 17:05:13 +00:00
|
|
|
if (!freopen(stderr_path.c_str(), "a+", stderr))
|
|
|
|
throw Poco::OpenFileException("Cannot attach stderr to " + stderr_path);
|
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-05-15 18:10:50 +00:00
|
|
|
if ((!log_path.empty() && is_daemon) || config().has("logger.stdout"))
|
2018-05-15 17:05:13 +00:00
|
|
|
{
|
2018-08-29 18:48:14 +00:00
|
|
|
std::string stdout_path = config().getString("logger.stdout", log_path + "/stdout.log");
|
2018-05-15 17:05:13 +00:00
|
|
|
if (!freopen(stdout_path.c_str(), "a+", stdout))
|
|
|
|
throw Poco::OpenFileException("Cannot attach stdout to " + stdout_path);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 17:05:13 +00:00
|
|
|
/// Create pid file.
|
2019-05-06 12:12:18 +00:00
|
|
|
if (config().has("pid"))
|
2018-05-15 17:05:13 +00:00
|
|
|
pid.seed(config().getString("pid"));
|
|
|
|
|
2018-02-28 20:34:25 +00:00
|
|
|
/// Change path for logging.
|
2018-04-19 18:19:12 +00:00
|
|
|
if (!log_path.empty())
|
2018-02-28 20:34:25 +00:00
|
|
|
{
|
2018-04-19 18:19:12 +00:00
|
|
|
std::string path = createDirectory(log_path);
|
2018-02-28 20:34:25 +00:00
|
|
|
if (is_daemon
|
|
|
|
&& chdir(path.c_str()) != 0)
|
|
|
|
throw Poco::Exception("Cannot change directory to " + path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (is_daemon
|
|
|
|
&& chdir("/tmp") != 0)
|
|
|
|
throw Poco::Exception("Cannot change directory to /tmp");
|
|
|
|
}
|
|
|
|
|
|
|
|
buildLoggers(config());
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
if (is_daemon)
|
|
|
|
{
|
2018-01-17 18:06:39 +00:00
|
|
|
/** Change working directory to the directory to write core dumps.
|
|
|
|
* We have to do it after buildLoggers, because there is the case when config files was in current directory.
|
2017-04-01 07:20:54 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
std::string core_path = config().getString("core_path", "");
|
|
|
|
if (core_path.empty())
|
|
|
|
core_path = getDefaultCorePath();
|
|
|
|
|
|
|
|
tryCreateDirectories(&logger(), core_path);
|
|
|
|
|
|
|
|
Poco::File cores = core_path;
|
|
|
|
if (!(cores.exists() && cores.isDirectory()))
|
|
|
|
{
|
|
|
|
core_path = !log_path.empty() ? log_path : "/opt/";
|
|
|
|
tryCreateDirectories(&logger(), core_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (0 != chdir(core_path.c_str()))
|
|
|
|
throw Poco::Exception("Cannot change directory to " + core_path);
|
|
|
|
}
|
|
|
|
|
2018-08-07 17:57:44 +00:00
|
|
|
initializeTerminationAndSignalProcessing();
|
|
|
|
logRevision();
|
|
|
|
|
|
|
|
for (const auto & key : DB::getMultipleKeysFromConfig(config(), "", "graphite"))
|
|
|
|
{
|
|
|
|
graphite_writers.emplace(key, std::make_unique<GraphiteWriter>(key));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BaseDaemon::initializeTerminationAndSignalProcessing()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
std::set_terminate(terminate_handler);
|
|
|
|
|
2017-10-13 18:58:19 +00:00
|
|
|
/// We want to avoid SIGPIPE when working with sockets and pipes, and just handle return value/errno instead.
|
|
|
|
{
|
|
|
|
sigset_t sig_set;
|
|
|
|
if (sigemptyset(&sig_set) || sigaddset(&sig_set, SIGPIPE) || pthread_sigmask(SIG_BLOCK, &sig_set, nullptr))
|
|
|
|
throw Poco::Exception("Cannot block signal.");
|
|
|
|
}
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/// Setup signal handlers.
|
2017-04-01 07:20:54 +00:00
|
|
|
auto add_signal_handler =
|
|
|
|
[](const std::vector<int> & signals, signal_function handler)
|
|
|
|
{
|
|
|
|
struct sigaction sa;
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sa_sigaction = handler;
|
|
|
|
sa.sa_flags = SA_SIGINFO;
|
|
|
|
|
|
|
|
{
|
|
|
|
if (sigemptyset(&sa.sa_mask))
|
|
|
|
throw Poco::Exception("Cannot set signal handler.");
|
|
|
|
|
|
|
|
for (auto signal : signals)
|
|
|
|
if (sigaddset(&sa.sa_mask, signal))
|
|
|
|
throw Poco::Exception("Cannot set signal handler.");
|
|
|
|
|
|
|
|
for (auto signal : signals)
|
2018-08-20 23:16:50 +00:00
|
|
|
if (sigaction(signal, &sa, nullptr))
|
2017-04-01 07:20:54 +00:00
|
|
|
throw Poco::Exception("Cannot set signal handler.");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
add_signal_handler({SIGABRT, SIGSEGV, SIGILL, SIGBUS, SIGSYS, SIGFPE, SIGPIPE}, faultSignalHandler);
|
|
|
|
add_signal_handler({SIGHUP, SIGUSR1}, closeLogsSignalHandler);
|
|
|
|
add_signal_handler({SIGINT, SIGQUIT, SIGTERM}, terminateRequestedSignalHandler);
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/// Set up Poco ErrorHandler for Poco Threads.
|
2017-04-01 07:20:54 +00:00
|
|
|
static KillingErrorHandler killing_error_handler;
|
|
|
|
Poco::ErrorHandler::set(&killing_error_handler);
|
|
|
|
|
|
|
|
signal_listener.reset(new SignalListener(*this));
|
|
|
|
signal_listener_thread.start(*signal_listener);
|
|
|
|
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-10 07:54:22 +00:00
|
|
|
void BaseDaemon::logRevision() const
|
2016-02-09 17:06:50 +00:00
|
|
|
{
|
2018-08-20 19:35:04 +00:00
|
|
|
Logger::root().information("Starting " + std::string{VERSION_FULL} + " with revision " + std::to_string(ClickHouseRevision::get()));
|
2016-02-09 17:06:50 +00:00
|
|
|
}
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/// Makes server shutdown if at least one Poco::Task have failed.
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::exitOnTaskError()
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2018-11-21 20:56:37 +00:00
|
|
|
Poco::Observer<BaseDaemon, Poco::TaskFailedNotification> obs(*this, &BaseDaemon::handleNotification);
|
2017-04-01 07:20:54 +00:00
|
|
|
getTaskManager().addObserver(obs);
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2018-01-17 18:06:39 +00:00
|
|
|
/// Used for exitOnTaskError()
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::handleNotification(Poco::TaskFailedNotification *_tfn)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
task_failed = true;
|
2018-11-21 20:56:37 +00:00
|
|
|
Poco::AutoPtr<Poco::TaskFailedNotification> fn(_tfn);
|
2017-04-01 07:20:54 +00:00
|
|
|
Logger *lg = &(logger());
|
|
|
|
LOG_ERROR(lg, "Task '" << fn->task()->name() << "' failed. Daemon is shutting down. Reason - " << fn->reason().displayText());
|
|
|
|
ServerApplication::terminate();
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::defineOptions(Poco::Util::OptionSet& _options)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
Poco::Util::ServerApplication::defineOptions (_options);
|
|
|
|
|
|
|
|
_options.addOption(
|
2018-01-17 16:39:56 +00:00
|
|
|
Poco::Util::Option("config-file", "C", "load configuration from a given file")
|
|
|
|
.required(false)
|
|
|
|
.repeatable(false)
|
|
|
|
.argument("<file>")
|
|
|
|
.binding("config-file"));
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
_options.addOption(
|
2018-01-17 16:39:56 +00:00
|
|
|
Poco::Util::Option("log-file", "L", "use given log file")
|
|
|
|
.required(false)
|
|
|
|
.repeatable(false)
|
|
|
|
.argument("<file>")
|
|
|
|
.binding("logger.log"));
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
_options.addOption(
|
2018-01-17 16:39:56 +00:00
|
|
|
Poco::Util::Option("errorlog-file", "E", "use given log file for errors only")
|
|
|
|
.required(false)
|
|
|
|
.repeatable(false)
|
|
|
|
.argument("<file>")
|
|
|
|
.binding("logger.errorlog"));
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
_options.addOption(
|
2018-01-17 16:39:56 +00:00
|
|
|
Poco::Util::Option("pid-file", "P", "use given pidfile")
|
|
|
|
.required(false)
|
|
|
|
.repeatable(false)
|
|
|
|
.argument("<file>")
|
|
|
|
.binding("pid"));
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2017-11-16 19:09:08 +00:00
|
|
|
bool isPidRunning(pid_t pid)
|
|
|
|
{
|
|
|
|
if (getpgid(pid) >= 0)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
2016-01-15 03:55:07 +00:00
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::PID::seed(const std::string & file_)
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2018-01-17 18:06:39 +00:00
|
|
|
file = Poco::Path(file_).absolute().toString();
|
2017-11-16 19:09:08 +00:00
|
|
|
Poco::File poco_file(file);
|
|
|
|
|
|
|
|
if (poco_file.exists())
|
|
|
|
{
|
|
|
|
pid_t pid_read = 0;
|
|
|
|
{
|
|
|
|
std::ifstream in(file);
|
|
|
|
if (in.good())
|
|
|
|
{
|
|
|
|
in >> pid_read;
|
|
|
|
if (pid_read && isPidRunning(pid_read))
|
|
|
|
throw Poco::Exception("Pid file exists and program running with pid = " + std::to_string(pid_read) + ", should not start daemon.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::cerr << "Old pid file exists (with pid = " << pid_read << "), removing." << std::endl;
|
|
|
|
poco_file.remove();
|
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
int fd = open(file.c_str(),
|
|
|
|
O_CREAT | O_EXCL | O_WRONLY,
|
|
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
|
|
|
|
|
|
if (-1 == fd)
|
|
|
|
{
|
|
|
|
file.clear();
|
|
|
|
if (EEXIST == errno)
|
|
|
|
throw Poco::Exception("Pid file exists, should not start daemon.");
|
|
|
|
throw Poco::CreateFileException("Cannot create pid file.");
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::stringstream s;
|
|
|
|
s << getpid();
|
|
|
|
if (static_cast<ssize_t>(s.str().size()) != write(fd, s.str().c_str(), s.str().size()))
|
|
|
|
throw Poco::Exception("Cannot write to pid file.");
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
close(fd);
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 17:06:50 +00:00
|
|
|
void BaseDaemon::PID::clear()
|
2016-01-15 03:55:07 +00:00
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (!file.empty())
|
|
|
|
{
|
|
|
|
Poco::File(file).remove();
|
|
|
|
file.clear();
|
|
|
|
}
|
2016-01-15 03:55:07 +00:00
|
|
|
}
|
2016-06-08 14:39:19 +00:00
|
|
|
|
|
|
|
void BaseDaemon::handleSignal(int signal_id)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
if (signal_id == SIGINT ||
|
|
|
|
signal_id == SIGQUIT ||
|
|
|
|
signal_id == SIGTERM)
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(signal_handler_mutex);
|
|
|
|
{
|
|
|
|
++terminate_signals_counter;
|
|
|
|
sigint_signals_counter += signal_id == SIGINT;
|
|
|
|
signal_event.notify_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
onInterruptSignals(signal_id);
|
|
|
|
}
|
|
|
|
else
|
2018-11-22 21:19:58 +00:00
|
|
|
throw DB::Exception(std::string("Unsupported signal: ") + strsignal(signal_id), 0);
|
2016-06-08 14:39:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BaseDaemon::onInterruptSignals(int signal_id)
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
is_cancelled = true;
|
|
|
|
LOG_INFO(&logger(), "Received termination signal (" << strsignal(signal_id) << ")");
|
|
|
|
|
|
|
|
if (sigint_signals_counter >= 2)
|
|
|
|
{
|
|
|
|
LOG_INFO(&logger(), "Received second signal Interrupt. Immediately terminate.");
|
|
|
|
kill();
|
|
|
|
}
|
2016-06-08 14:39:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BaseDaemon::waitForTerminationRequest()
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
std::unique_lock<std::mutex> lock(signal_handler_mutex);
|
|
|
|
signal_event.wait(lock, [this](){ return terminate_signals_counter > 0; });
|
2016-06-08 14:39:19 +00:00
|
|
|
}
|
|
|
|
|