Normalize "pid" file handling #3501

This commit is contained in:
Alexey Milovidov 2020-07-04 16:54:24 +03:00
parent 89034ed565
commit 176a7f2f72
8 changed files with 36 additions and 82 deletions

View File

@ -628,7 +628,7 @@ void BaseDaemon::initialize(Application & self)
/// Create pid file. /// Create pid file.
if (config().has("pid")) if (config().has("pid"))
pid.emplace(config().getString("pid")); pid.emplace(config().getString("pid"), DB::StatusFile::write_pid);
/// Change path for logging. /// Change path for logging.
if (!log_path.empty()) if (!log_path.empty())
@ -812,63 +812,6 @@ void BaseDaemon::defineOptions(Poco::Util::OptionSet & new_options)
Poco::Util::ServerApplication::defineOptions(new_options); Poco::Util::ServerApplication::defineOptions(new_options);
} }
bool isPidRunning(pid_t pid)
{
return getpgid(pid) >= 0;
}
BaseDaemon::PID::PID(const std::string & file_)
{
file = Poco::Path(file_).absolute().toString();
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();
}
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)
{
if (EEXIST == errno)
throw Poco::Exception("Pid file exists, should not start daemon.");
throw Poco::CreateFileException("Cannot create pid file.");
}
SCOPE_EXIT({ close(fd); });
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.");
}
BaseDaemon::PID::~PID()
{
try
{
Poco::File(file).remove();
}
catch (...)
{
DB::tryLogCurrentException(__PRETTY_FUNCTION__);
}
}
void BaseDaemon::handleSignal(int signal_id) void BaseDaemon::handleSignal(int signal_id)
{ {
if (signal_id == SIGINT || if (signal_id == SIGINT ||

View File

@ -22,6 +22,7 @@
#include <common/getThreadId.h> #include <common/getThreadId.h>
#include <daemon/GraphiteWriter.h> #include <daemon/GraphiteWriter.h>
#include <Common/Config/ConfigProcessor.h> #include <Common/Config/ConfigProcessor.h>
#include <Common/StatusFile.h>
#include <loggers/Loggers.h> #include <loggers/Loggers.h>
@ -163,16 +164,7 @@ protected:
std::unique_ptr<Poco::TaskManager> task_manager; std::unique_ptr<Poco::TaskManager> task_manager;
/// RAII wrapper for pid file. std::optional<DB::StatusFile> pid;
struct PID
{
std::string file;
PID(const std::string & file_);
~PID();
};
std::optional<PID> pid;
std::atomic_bool is_cancelled{false}; std::atomic_bool is_cancelled{false};

View File

@ -1,4 +1,6 @@
#include "ClusterCopierApp.h" #include "ClusterCopierApp.h"
#include <Common/StatusFile.h>
namespace DB namespace DB
{ {
@ -91,7 +93,7 @@ void ClusterCopierApp::defineOptions(Poco::Util::OptionSet & options)
void ClusterCopierApp::mainImpl() void ClusterCopierApp::mainImpl()
{ {
StatusFile status_file(process_path + "/status"); StatusFile status_file(process_path + "/status", StatusFile::write_full_info);
ThreadStatus thread_status; ThreadStatus thread_status;
auto * log = &logger(); auto * log = &logger();

View File

@ -66,7 +66,6 @@
#include <Dictionaries/registerDictionaries.h> #include <Dictionaries/registerDictionaries.h>
#include <Disks/registerDisks.h> #include <Disks/registerDisks.h>
#include <Databases/DatabaseMemory.h> #include <Databases/DatabaseMemory.h>
#include <Common/StatusFile.h>
#include "Aliases.h" #include "Aliases.h"

View File

@ -248,7 +248,7 @@ try
if (!context->getPath().empty()) if (!context->getPath().empty())
{ {
/// Lock path directory before read /// Lock path directory before read
status.emplace(context->getPath() + "status"); status.emplace(context->getPath() + "status", StatusFile::write_full_info);
LOG_DEBUG(log, "Loading metadata from {}", context->getPath()); LOG_DEBUG(log, "Loading metadata from {}", context->getPath());
loadMetadataSystem(*context); loadMetadataSystem(*context);

View File

@ -378,7 +378,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
global_context->setPath(path); global_context->setPath(path);
StatusFile status{path + "status"}; StatusFile status{path + "status", StatusFile::write_full_info};
SCOPE_EXIT({ SCOPE_EXIT({
/** Ask to cancel background jobs all table engines, /** Ask to cancel background jobs all table engines,

View File

@ -30,8 +30,21 @@ namespace ErrorCodes
} }
StatusFile::StatusFile(const std::string & path_) StatusFile::FillFunction StatusFile::write_pid = [](WriteBuffer & out)
: path(path_) {
out << getpid() << "\n";
};
StatusFile::FillFunction StatusFile::write_full_info = [](WriteBuffer & out)
{
out << "PID: " << getpid() << "\n"
<< "Started at: " << LocalDateTime(time(nullptr)) << "\n"
<< "Revision: " << ClickHouseRevision::get() << "\n";
};
StatusFile::StatusFile(std::string path_, FillFunction fill_)
: path(std::move(path_)), fill(std::move(fill_))
{ {
/// If file already exists. NOTE Minor race condition. /// If file already exists. NOTE Minor race condition.
if (Poco::File(path).exists()) if (Poco::File(path).exists())
@ -72,13 +85,8 @@ StatusFile::StatusFile(const std::string & path_)
throwFromErrnoWithPath("Cannot lseek " + path, path, ErrorCodes::CANNOT_SEEK_THROUGH_FILE); throwFromErrnoWithPath("Cannot lseek " + path, path, ErrorCodes::CANNOT_SEEK_THROUGH_FILE);
/// Write information about current server instance to the file. /// Write information about current server instance to the file.
{ WriteBufferFromFileDescriptor out(fd, 1024);
WriteBufferFromFileDescriptor out(fd, 1024); fill(out);
out
<< "PID: " << getpid() << "\n"
<< "Started at: " << LocalDateTime(time(nullptr)) << "\n"
<< "Revision: " << ClickHouseRevision::get() << "\n";
}
} }
catch (...) catch (...)
{ {

View File

@ -1,23 +1,33 @@
#pragma once #pragma once
#include <string> #include <string>
#include <functional>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
namespace DB namespace DB
{ {
class WriteBuffer;
/** Provides that no more than one server works with one data directory. /** Provides that no more than one server works with one data directory.
*/ */
class StatusFile : private boost::noncopyable class StatusFile : private boost::noncopyable
{ {
public: public:
explicit StatusFile(const std::string & path_); using FillFunction = std::function<void(WriteBuffer&)>;
StatusFile(std::string path_, FillFunction fill_);
~StatusFile(); ~StatusFile();
/// You can use one of these functions to fill the file or provide your own.
static FillFunction write_pid;
static FillFunction write_full_info;
private: private:
const std::string path; const std::string path;
FillFunction fill;
int fd = -1; int fd = -1;
}; };