2016-01-15 03:55:07 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
2020-02-17 14:27:09 +00:00
|
|
|
|
#include <unistd.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <memory>
|
2016-12-12 03:33:34 +00:00
|
|
|
|
#include <functional>
|
2017-11-20 04:15:43 +00:00
|
|
|
|
#include <optional>
|
2017-03-17 00:44:00 +00:00
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <condition_variable>
|
2017-03-20 19:14:00 +00:00
|
|
|
|
#include <atomic>
|
2017-11-23 09:08:52 +00:00
|
|
|
|
#include <chrono>
|
2016-01-15 03:55:07 +00:00
|
|
|
|
#include <Poco/Process.h>
|
|
|
|
|
#include <Poco/ThreadPool.h>
|
|
|
|
|
#include <Poco/Util/Application.h>
|
|
|
|
|
#include <Poco/Util/ServerApplication.h>
|
|
|
|
|
#include <Poco/Net/SocketAddress.h>
|
2017-01-19 18:53:29 +00:00
|
|
|
|
#include <Poco/Version.h>
|
2021-10-02 07:13:14 +00:00
|
|
|
|
#include <base/types.h>
|
|
|
|
|
#include <base/logger_useful.h>
|
|
|
|
|
#include <base/getThreadId.h>
|
2016-01-15 04:13:00 +00:00
|
|
|
|
#include <daemon/GraphiteWriter.h>
|
2018-02-28 20:34:25 +00:00
|
|
|
|
#include <Common/Config/ConfigProcessor.h>
|
2020-07-04 13:54:24 +00:00
|
|
|
|
#include <Common/StatusFile.h>
|
2019-06-14 14:00:37 +00:00
|
|
|
|
#include <loggers/Loggers.h>
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2019-12-22 17:20:33 +00:00
|
|
|
|
|
2020-08-08 01:21:04 +00:00
|
|
|
|
/// \brief Base class for applications that can run as daemons.
|
2016-01-15 03:55:07 +00:00
|
|
|
|
///
|
|
|
|
|
/// \code
|
2018-11-21 17:27:13 +00:00
|
|
|
|
/// # Some possible command line options:
|
|
|
|
|
/// # --config-file, -C or --config - path to configuration file. By default - config.xml in the current directory.
|
|
|
|
|
/// # --log-file
|
|
|
|
|
/// # --errorlog-file
|
|
|
|
|
/// # --daemon - run as daemon; without this option, the program will be attached to the terminal and also output logs to stderr.
|
|
|
|
|
/// <daemon_name> --daemon --config-file=localfile.xml --log-file=log.log --errorlog-file=error.log
|
2016-01-15 03:55:07 +00:00
|
|
|
|
/// \endcode
|
|
|
|
|
///
|
2018-11-21 17:27:13 +00:00
|
|
|
|
/// You can configure different log options for different loggers used inside program
|
|
|
|
|
/// by providing subsections to "logger" in configuration file.
|
2019-06-14 14:00:37 +00:00
|
|
|
|
class BaseDaemon : public Poco::Util::ServerApplication, public Loggers
|
2016-01-15 03:55:07 +00:00
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
|
friend class SignalListener;
|
2016-06-08 14:39:19 +00:00
|
|
|
|
|
2016-01-15 03:55:07 +00:00
|
|
|
|
public:
|
2018-11-21 20:56:37 +00:00
|
|
|
|
static inline constexpr char DEFAULT_GRAPHITE_CONFIG_NAME[] = "graphite";
|
2017-08-23 12:05:15 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
BaseDaemon();
|
2018-08-26 01:30:13 +00:00
|
|
|
|
~BaseDaemon() override;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2020-12-22 17:10:10 +00:00
|
|
|
|
/// Load configuration, prepare loggers, etc.
|
2017-04-01 07:20:54 +00:00
|
|
|
|
void initialize(Poco::Util::Application &) override;
|
|
|
|
|
|
|
|
|
|
void reloadConfiguration();
|
|
|
|
|
|
2020-12-22 17:10:10 +00:00
|
|
|
|
/// Process command line parameters
|
2020-05-09 23:22:51 +00:00
|
|
|
|
void defineOptions(Poco::Util::OptionSet & new_options) override;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
2020-12-22 17:10:10 +00:00
|
|
|
|
/// Graceful shutdown
|
|
|
|
|
static void terminate();
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
2020-12-22 17:10:10 +00:00
|
|
|
|
/// Forceful shutdown
|
2021-01-07 18:36:38 +00:00
|
|
|
|
[[noreturn]] void kill();
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
2020-12-22 17:10:10 +00:00
|
|
|
|
/// Cancellation request has been received.
|
2017-08-09 14:33:07 +00:00
|
|
|
|
bool isCancelled() const
|
2017-04-01 07:20:54 +00:00
|
|
|
|
{
|
|
|
|
|
return is_cancelled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BaseDaemon & instance()
|
|
|
|
|
{
|
|
|
|
|
return dynamic_cast<BaseDaemon &>(Poco::Util::Application::instance());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// return none if daemon doesn't exist, reference to the daemon otherwise
|
2017-11-20 04:15:43 +00:00
|
|
|
|
static std::optional<std::reference_wrapper<BaseDaemon>> tryGetInstance() { return tryGetInstance<BaseDaemon>(); }
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
|
|
/// В Graphite компоненты пути(папки) разделяются точкой.
|
|
|
|
|
/// У нас принят путь формата root_path.hostname_yandex_ru.key
|
|
|
|
|
/// root_path по умолчанию one_min
|
|
|
|
|
/// key - лучше группировать по смыслу. Например "meminfo.cached" или "meminfo.free", "meminfo.total"
|
|
|
|
|
template <class T>
|
2017-08-23 12:05:15 +00:00
|
|
|
|
void writeToGraphite(const std::string & key, const T & value, const std::string & config_name = DEFAULT_GRAPHITE_CONFIG_NAME, time_t timestamp = 0, const std::string & custom_root_path = "")
|
2017-04-01 07:20:54 +00:00
|
|
|
|
{
|
2021-02-19 12:51:26 +00:00
|
|
|
|
auto *writer = getGraphiteWriter(config_name);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
if (writer)
|
|
|
|
|
writer->write(key, value, timestamp, custom_root_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T>
|
2017-08-23 12:05:15 +00:00
|
|
|
|
void writeToGraphite(const GraphiteWriter::KeyValueVector<T> & key_vals, const std::string & config_name = DEFAULT_GRAPHITE_CONFIG_NAME, time_t timestamp = 0, const std::string & custom_root_path = "")
|
2017-04-01 07:20:54 +00:00
|
|
|
|
{
|
2021-02-19 12:51:26 +00:00
|
|
|
|
auto *writer = getGraphiteWriter(config_name);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
if (writer)
|
|
|
|
|
writer->write(key_vals, timestamp, custom_root_path);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 09:08:52 +00:00
|
|
|
|
template <class T>
|
|
|
|
|
void writeToGraphite(const GraphiteWriter::KeyValueVector<T> & key_vals, const std::chrono::system_clock::time_point & current_time, const std::string & custom_root_path)
|
|
|
|
|
{
|
2021-02-19 12:51:26 +00:00
|
|
|
|
auto *writer = getGraphiteWriter();
|
2017-11-23 09:08:52 +00:00
|
|
|
|
if (writer)
|
|
|
|
|
writer->write(key_vals, std::chrono::system_clock::to_time_t(current_time), custom_root_path);
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-23 12:05:15 +00:00
|
|
|
|
GraphiteWriter * getGraphiteWriter(const std::string & config_name = DEFAULT_GRAPHITE_CONFIG_NAME)
|
2017-04-01 07:20:54 +00:00
|
|
|
|
{
|
|
|
|
|
if (graphite_writers.count(config_name))
|
|
|
|
|
return graphite_writers[config_name].get();
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-12 12:23:22 +00:00
|
|
|
|
/// close all process FDs except
|
|
|
|
|
/// 0-2 -- stdin, stdout, stderr
|
|
|
|
|
/// also doesn't close global internal pipes for signal handling
|
2020-03-18 00:57:00 +00:00
|
|
|
|
static void closeFDs();
|
2018-08-12 12:23:22 +00:00
|
|
|
|
|
2020-12-22 02:57:22 +00:00
|
|
|
|
/// If this method is called after initialization and before run,
|
|
|
|
|
/// will fork child process and setup watchdog that will print diagnostic info, if the child terminates.
|
|
|
|
|
/// argv0 is needed to change process name (consequently, it is needed for scripts involving "pgrep", "pidof" to work correctly).
|
|
|
|
|
void shouldSetupWatchdog(char * argv0_);
|
|
|
|
|
|
2021-01-07 02:56:57 +00:00
|
|
|
|
/// Hash of the binary for integrity checks.
|
|
|
|
|
String getStoredBinaryHash() const;
|
|
|
|
|
|
2016-01-15 03:55:07 +00:00
|
|
|
|
protected:
|
2017-04-01 07:20:54 +00:00
|
|
|
|
virtual void logRevision() const;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
/// thread safe
|
|
|
|
|
virtual void handleSignal(int signal_id);
|
2016-06-08 14:39:19 +00:00
|
|
|
|
|
2018-08-07 17:57:44 +00:00
|
|
|
|
/// initialize termination process and signal handlers
|
|
|
|
|
virtual void initializeTerminationAndSignalProcessing();
|
|
|
|
|
|
2020-12-22 02:57:22 +00:00
|
|
|
|
/// fork the main process and watch if it was killed
|
|
|
|
|
void setupWatchdog();
|
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
void waitForTerminationRequest()
|
2019-02-15 11:46:07 +00:00
|
|
|
|
#if defined(POCO_CLICKHOUSE_PATCH) || POCO_VERSION >= 0x02000000 // in old upstream poco not vitrual
|
2017-04-01 07:20:54 +00:00
|
|
|
|
override
|
2017-01-13 11:25:44 +00:00
|
|
|
|
#endif
|
2017-04-01 07:20:54 +00:00
|
|
|
|
;
|
|
|
|
|
/// thread safe
|
|
|
|
|
virtual void onInterruptSignals(int signal_id);
|
2016-06-08 14:39:19 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
template <class Daemon>
|
2017-11-20 04:15:43 +00:00
|
|
|
|
static std::optional<std::reference_wrapper<Daemon>> tryGetInstance();
|
2016-09-05 16:29:21 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
virtual std::string getDefaultCorePath() const;
|
2017-01-09 13:42:29 +00:00
|
|
|
|
|
2021-05-12 13:04:34 +00:00
|
|
|
|
virtual std::string getDefaultConfigFileName() const;
|
|
|
|
|
|
2020-12-22 02:57:22 +00:00
|
|
|
|
std::optional<DB::StatusFile> pid_file;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
std::atomic_bool is_cancelled{false};
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
bool log_to_console = false;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2020-12-22 17:10:10 +00:00
|
|
|
|
/// A thread that acts on HUP and USR1 signal (close logs).
|
2017-04-01 07:20:54 +00:00
|
|
|
|
Poco::Thread signal_listener_thread;
|
|
|
|
|
std::unique_ptr<Poco::Runnable> signal_listener;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
std::map<std::string, std::unique_ptr<GraphiteWriter>> graphite_writers;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
std::mutex signal_handler_mutex;
|
|
|
|
|
std::condition_variable signal_event;
|
|
|
|
|
std::atomic_size_t terminate_signals_counter{0};
|
|
|
|
|
std::atomic_size_t sigint_signals_counter{0};
|
2017-03-17 00:44:00 +00:00
|
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
std::string config_path;
|
2018-11-27 16:11:46 +00:00
|
|
|
|
DB::ConfigProcessor::LoadedConfig loaded_config;
|
2017-06-19 13:31:55 +00:00
|
|
|
|
Poco::Util::AbstractConfiguration * last_configuration = nullptr;
|
2020-06-20 09:07:05 +00:00
|
|
|
|
|
|
|
|
|
String build_id_info;
|
2021-01-07 02:56:57 +00:00
|
|
|
|
String stored_binary_hash;
|
Reset signal handlers just before closing signal pipe to avoid EBADF after
stack trace:
(gdb) bt
0 0x00007ffff7dda355 in raise () from /usr/lib/libc.so.6
1 0x00007ffff7dc3853 in abort () from /usr/lib/libc.so.6
2 0x0000000013a78180 in abort_message (format=format@entry=0x5781e10 "terminate_handler unexpectedly threw an exception") at abort_message.cpp:76
3 0x0000000013a899ff in std::__terminate (func=<optimized out>) at cxa_handlers.cpp:67
4 0x0000000013a771a1 in __cxxabiv1::call_terminate (native_exception=native_exception@entry=true, unwind_exception=unwind_exception@entry=0x7ffff70e9060) at cxa_personality.cpp:323
5 0x0000000013a77669 in __cxxabiv1::scan_eh_tab (results=..., actions=<optimized out>, native_exception=native_exception@entry=true, unwind_exception=0x7ffff70e9060, context=context@entry=0x7fffffffc3a0) at cxa_personality.cpp:887
6 0x0000000013a77bde in __cxxabiv1::__gxx_personality_v0 (version=<optimized out>, actions=<optimized out>, exceptionClass=<optimized out>, unwind_exception=0x7ffff70e9060, context=0x7fffffffc3a0) at cxa_personality.cpp:969
7 0x0000000013a962d7 in unwind_phase1 (exception_object=0x7ffff70e9060, cursor=0x7fffffffc3a0, uc=0x7fffffffc2f0) at UnwindLevel1.c:98
8 _Unwind_RaiseException (exception_object=exception_object@entry=0x7ffff70e9060) at UnwindLevel1.c:363
9 0x0000000013a8929b in __cxxabiv1::__cxa_rethrow () at cxa_exception.cpp:607
10 0x000000000f170ad2 in DB::WriteBuffer::next (this=0x7fffffffc510) at WriteBuffer.h:52
11 writeSignalIDtoSignalPipe (sig=<optimized out>) at BaseDaemon.cpp:111
12 <signal handler called>
13 0x00007ffff7e9895b in munmap () from /usr/lib/libc.so.6
14 0x00007ffff7f7e255 in free_stacks () from /usr/lib/libpthread.so.0
15 0x00007ffff7f7e6aa in __deallocate_stack () from /usr/lib/libpthread.so.0
16 0x00007ffff7f80898 in __pthread_clockjoin_ex () from /usr/lib/libpthread.so.0
17 0x0000000013a759f7 in std::__1::__libcpp_thread_join (__t=0x7fff0aa2b090) at __threading_support:409
18 std::__1::thread::join (this=this@entry=0x7fff0aa2b090) at thread.cpp:56
19 0x000000000a4c9399 in ThreadPoolImpl<std::__1::thread>::finalize (this=this@entry=0x7ffff7053900) at list:355
20 0x000000000a4c9426 in ThreadPoolImpl<std::__1::thread>::~ThreadPoolImpl (this=0x7ffff7053900, __in_chrg=<optimized out>) at ThreadPool.cpp:172
21 0x000000000a4c8a9c in GlobalThreadPool::~GlobalThreadPool (this=0x7ffff7053900, __in_chrg=<optimized out>) at ThreadPool.h:132
22 std::__1::default_delete<GlobalThreadPool>::operator() (__ptr=0x7ffff7053900, this=<optimized out>) at memory:2363
23 std::__1::unique_ptr<GlobalThreadPool, std::__1::default_delete<GlobalThreadPool> >::reset (__p=0x0, this=<optimized out>) at memory:2618
24 std::__1::unique_ptr<GlobalThreadPool, std::__1::default_delete<GlobalThreadPool> >::~unique_ptr (this=<optimized out>, __in_chrg=<optimized out>) at memory:2572
25 0x00007ffff7ddcc57 in __run_exit_handlers () from /usr/lib/libc.so.6
26 0x00007ffff7ddcdfe in exit () from /usr/lib/libc.so.6
27 0x00007ffff7dc5009 in __libc_start_main () from /usr/lib/libc.so.6
28 0x000000000a48702e in _start () at new:340
2020-08-13 19:51:41 +00:00
|
|
|
|
|
|
|
|
|
std::vector<int> handled_signals;
|
2020-12-22 02:57:22 +00:00
|
|
|
|
|
|
|
|
|
bool should_setup_watchdog = false;
|
|
|
|
|
char * argv0 = nullptr;
|
2016-01-15 03:55:07 +00:00
|
|
|
|
};
|
2016-09-05 16:29:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class Daemon>
|
2017-11-20 04:15:43 +00:00
|
|
|
|
std::optional<std::reference_wrapper<Daemon>> BaseDaemon::tryGetInstance()
|
2016-09-05 16:29:21 +00:00
|
|
|
|
{
|
2017-04-01 07:20:54 +00:00
|
|
|
|
Daemon * ptr = nullptr;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
ptr = dynamic_cast<Daemon *>(&Poco::Util::Application::instance());
|
|
|
|
|
}
|
|
|
|
|
catch (const Poco::NullPointerException &)
|
|
|
|
|
{
|
|
|
|
|
/// if daemon doesn't exist than instance() throw NullPointerException
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ptr)
|
2017-11-20 04:15:43 +00:00
|
|
|
|
return std::optional<std::reference_wrapper<Daemon>>(*ptr);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
else
|
|
|
|
|
return {};
|
2016-09-05 16:29:21 +00:00
|
|
|
|
}
|