aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/StatusFile.cpp
blob: a9ffce7ddf88644193eb0d027633d23da9cbbd4f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include "StatusFile.h"

#include <sys/file.h>
#include <fcntl.h>
#include <cerrno>

#include <Common/logger_useful.h>
#include <Common/ClickHouseRevision.h>
#include <Common/LocalDateTime.h>
#include <base/errnoToString.h>
#include <base/defines.h>

#include <IO/ReadBufferFromFile.h>
#include <IO/LimitReadBuffer.h>
#include <IO/WriteBufferFromFileDescriptor.h>
#include <IO/Operators.h>
#include <filesystem>

namespace fs = std::filesystem;

namespace DB
{

namespace ErrorCodes
{
    extern const int CANNOT_OPEN_FILE;
    extern const int CANNOT_TRUNCATE_FILE;
    extern const int CANNOT_SEEK_THROUGH_FILE;
}


StatusFile::FillFunction StatusFile::write_pid = [](WriteBuffer & out)
{
    out << getpid();
};

StatusFile::FillFunction StatusFile::write_full_info = [](WriteBuffer & out)
{
    out << "PID: " << getpid() << "\n"
        << "Started at: " << LocalDateTime(time(nullptr)) << "\n"
        << "Revision: " << ClickHouseRevision::getVersionRevision() << "\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 (fs::exists(path))
    {
        std::string contents;
        {
            ReadBufferFromFile in(path, 1024);
            LimitReadBuffer limit_in(in, 1024, /* trow_exception */ false, /* exact_limit */ {});
            readStringUntilEOF(contents, limit_in);
        }

        if (!contents.empty())
            LOG_INFO(&Poco::Logger::get("StatusFile"), "Status file {} already exists - unclean restart. Contents:\n{}", path, contents);
        else
            LOG_INFO(&Poco::Logger::get("StatusFile"), "Status file {} already exists and is empty - probably unclean hardware restart.", path);
    }

    fd = ::open(path.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0666);

    if (-1 == fd)
        throwFromErrnoWithPath("Cannot open file " + path, path, ErrorCodes::CANNOT_OPEN_FILE);

    try
    {
        int flock_ret = flock(fd, LOCK_EX | LOCK_NB);
        if (-1 == flock_ret)
        {
            if (errno == EWOULDBLOCK)
                throw Exception(ErrorCodes::CANNOT_OPEN_FILE, "Cannot lock file {}. Another server instance in same directory is already running.", path);
            else
                throwFromErrnoWithPath("Cannot lock file " + path, path, ErrorCodes::CANNOT_OPEN_FILE);
        }

        if (0 != ftruncate(fd, 0))
            throwFromErrnoWithPath("Cannot ftruncate " + path, path, ErrorCodes::CANNOT_TRUNCATE_FILE);

        if (0 != lseek(fd, 0, SEEK_SET))
            throwFromErrnoWithPath("Cannot lseek " + path, path, ErrorCodes::CANNOT_SEEK_THROUGH_FILE);

        /// Write information about current server instance to the file.
        WriteBufferFromFileDescriptor out(fd, 1024);
        fill(out);
    }
    catch (...)
    {
        int err = close(fd);
        chassert(!err || errno == EINTR);
        throw;
    }
}


StatusFile::~StatusFile()
{
    if (0 != close(fd))
        LOG_ERROR(&Poco::Logger::get("StatusFile"), "Cannot close file {}, {}", path, errnoToString());

    if (0 != unlink(path.c_str()))
        LOG_ERROR(&Poco::Logger::get("StatusFile"), "Cannot unlink file {}, {}", path, errnoToString());
}

}