initial changes
This commit is contained in:
67
monitarr/libmonitarr/include/data_db.hpp
Normal file
67
monitarr/libmonitarr/include/data_db.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef LIBMONITARR_INCLUDE_DATA_DB_HPP_
|
||||
#define LIBMONITARR_INCLUDE_DATA_DB_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
struct data_entry final {
|
||||
std::string download_id;
|
||||
std::uint64_t last_check{};
|
||||
std::uint64_t size_left{};
|
||||
};
|
||||
|
||||
class data_db final {
|
||||
public:
|
||||
data_db() noexcept = default;
|
||||
data_db(const data_db &) noexcept = default;
|
||||
data_db(data_db &&) noexcept = default;
|
||||
|
||||
auto operator=(const data_db &) noexcept -> data_db & = default;
|
||||
auto operator=(data_db &&) noexcept -> data_db & = default;
|
||||
|
||||
~data_db() noexcept;
|
||||
|
||||
private:
|
||||
std::shared_ptr<rocksdb::TransactionDB> db_;
|
||||
|
||||
private:
|
||||
[[nodiscard]] auto
|
||||
perform_action(std::string_view function_name,
|
||||
std::function<rocksdb::Status(rocksdb::TransactionDB *tx_db)>
|
||||
action) const -> bool;
|
||||
|
||||
[[nodiscard]] auto perform_action(
|
||||
std::string_view function_name,
|
||||
std::function<rocksdb::Status(rocksdb::Transaction *txn)> action) -> bool;
|
||||
|
||||
public:
|
||||
void close();
|
||||
|
||||
[[nodiscard]] auto get(std::string_view download_id) const
|
||||
-> std::optional<data_entry>;
|
||||
|
||||
void open(std::string_view data_dir);
|
||||
|
||||
void remove(std::string_view download_id);
|
||||
|
||||
void set(const data_entry &entry);
|
||||
};
|
||||
} // namespace monitarr
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN template <>
|
||||
struct adl_serializer<monitarr::data_entry> {
|
||||
static void to_json(json &data, const monitarr::data_entry &value) {
|
||||
data["download_id"] = value.download_id;
|
||||
data["last_check"] = value.last_check;
|
||||
data["size_left"] = value.size_left;
|
||||
}
|
||||
|
||||
static void from_json(const json &data, monitarr::data_entry &value) {
|
||||
data.at("download_id").get_to(value.download_id);
|
||||
data.at("last_check").get_to(value.last_check);
|
||||
data.at("size_left").get_to(value.size_left);
|
||||
}
|
||||
};
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // LIBMONITARR_INCLUDE_DATA_DB_HPP_
|
||||
10
monitarr/libmonitarr/include/initialize.hpp
Normal file
10
monitarr/libmonitarr/include/initialize.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef LIBMONITARR_INCLUDE_INITIALIZE_HPP_
|
||||
#define LIBMONITARR_INCLUDE_INITIALIZE_HPP_
|
||||
|
||||
namespace monitarr {
|
||||
void project_cleanup();
|
||||
|
||||
[[nodiscard]] auto project_initialize() -> bool;
|
||||
} // namespace monitarr
|
||||
|
||||
#endif // LIBMONITARR_INCLUDE_INITIALIZE_HPP_
|
||||
89
monitarr/libmonitarr/include/settings.hpp
Normal file
89
monitarr/libmonitarr/include/settings.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef LIBMONITARR_INCLUDE_SETTINGS_HPP_
|
||||
#define LIBMONITARR_INCLUDE_SETTINGS_HPP_
|
||||
|
||||
#include "utils/config.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
inline constexpr const auto default_interval{
|
||||
std::chrono::minutes{5U},
|
||||
};
|
||||
|
||||
inline constexpr const auto default_timeout{
|
||||
std::chrono::minutes{24U * 60U},
|
||||
};
|
||||
|
||||
struct server_cfg final {
|
||||
std::string id;
|
||||
std::string api_key;
|
||||
std::string api_version;
|
||||
std::chrono::minutes timeout{default_timeout};
|
||||
std::string url;
|
||||
};
|
||||
|
||||
struct app_config final {
|
||||
std::chrono::minutes check_interval{default_interval};
|
||||
std::vector<server_cfg> server_list{
|
||||
server_cfg{
|
||||
"lidarr",
|
||||
"7228e4739091469db81acfe2b97aa973",
|
||||
"v1",
|
||||
std::chrono::minutes(1440),
|
||||
"http://192.168.1.60:8686",
|
||||
},
|
||||
server_cfg{
|
||||
"radarr",
|
||||
"affa8c547cee48e0b8c49994c0aaa931",
|
||||
"v3",
|
||||
std::chrono::minutes(1440),
|
||||
"http://192.168.1.60:7878",
|
||||
},
|
||||
server_cfg{
|
||||
"sonarr",
|
||||
"ead2cdf34f774ad1b8185271c733c5ad",
|
||||
"v3",
|
||||
std::chrono::minutes(1440),
|
||||
"http://192.168.1.60:8989",
|
||||
},
|
||||
};
|
||||
|
||||
void load(std::string_view file_path);
|
||||
|
||||
void save(std::string_view file_path) const;
|
||||
};
|
||||
} // namespace monitarr
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
template <> struct adl_serializer<monitarr::server_cfg> {
|
||||
static void to_json(json &data, const monitarr::server_cfg &value) {
|
||||
data["id"] = value.id;
|
||||
data["api_key"] = value.api_key;
|
||||
data["api_version"] = value.api_version;
|
||||
data["timeout_minutes"] = value.timeout.count();
|
||||
data["url"] = value.url;
|
||||
}
|
||||
|
||||
static void from_json(const json &data, monitarr::server_cfg &value) {
|
||||
data.at("id").get_to(value.id);
|
||||
data.at("api_key").get_to(value.api_key);
|
||||
data.at("api_version").get_to(value.api_version);
|
||||
value.timeout =
|
||||
std::chrono::minutes(data.at("timeout_minutes").get<std::int32_t>());
|
||||
data.at("url").get_to(value.url);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct adl_serializer<monitarr::app_config> {
|
||||
static void to_json(json &data, const monitarr::app_config &value) {
|
||||
data["check_interval_minutes"] = value.check_interval.count();
|
||||
data["server_list"] = value.server_list;
|
||||
}
|
||||
|
||||
static void from_json(const json &data, monitarr::app_config &value) {
|
||||
value.check_interval = std::chrono::minutes(
|
||||
data.at("check_interval_minutes").get<std::int32_t>());
|
||||
data.at("server_list").get_to(value.server_list);
|
||||
}
|
||||
};
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // LIBMONITARR_INCLUDE_SETTINGS_HPP_
|
||||
12
monitarr/libmonitarr/include/version.hpp
Normal file
12
monitarr/libmonitarr/include/version.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef LIBMONITARR_INCLUDE_VERSION_HPP_
|
||||
#define LIBMONITARR_INCLUDE_VERSION_HPP_
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace monitarr {
|
||||
[[nodiscard]] auto project_get_git_rev() -> std::string_view;
|
||||
|
||||
[[nodiscard]] auto project_get_version() -> std::string_view;
|
||||
} // namespace monitarr
|
||||
|
||||
#endif // LIBMONITARR_INCLUDE_VERSION_HPP_
|
||||
131
monitarr/libmonitarr/src/data_db.cpp
Normal file
131
monitarr/libmonitarr/src/data_db.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "data_db.hpp"
|
||||
|
||||
#include "utils/error.hpp"
|
||||
#include "utils/path.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
data_db::~data_db() noexcept { close(); }
|
||||
|
||||
void data_db::close() {
|
||||
if (db_) {
|
||||
db_->Close();
|
||||
db_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto data_db::get(std::string_view download_id) const
|
||||
-> std::optional<data_entry> {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
std::optional<data_entry> ret;
|
||||
if (not perform_action(
|
||||
function_name,
|
||||
[&download_id, &ret](rocksdb::TransactionDB *txn_db) -> auto {
|
||||
std::string value;
|
||||
auto res = txn_db->Get(rocksdb::ReadOptions{}, download_id, &value);
|
||||
if (res.ok()) {
|
||||
ret = nlohmann::json::parse(value).get<data_entry>();
|
||||
}
|
||||
|
||||
return res.IsNotFound() ? rocksdb::Status{} : res;
|
||||
})) {
|
||||
fmt::println("failed to get|{}", download_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void data_db::open(std::string_view data_dir) {
|
||||
auto db_path = utils::path::combine(data_dir, {"state_db"});
|
||||
fmt::println("opening database|{}", db_path);
|
||||
|
||||
rocksdb::Options options{};
|
||||
options.create_if_missing = true;
|
||||
options.create_missing_column_families = true;
|
||||
options.db_log_dir = data_dir;
|
||||
options.keep_log_file_num = 10;
|
||||
|
||||
rocksdb::TransactionDB *ptr{};
|
||||
auto status = rocksdb::TransactionDB::Open(
|
||||
options, rocksdb::TransactionDBOptions{}, db_path, &ptr);
|
||||
if (not status.ok()) {
|
||||
throw std::runtime_error(fmt::format("failed to open database|{}|{}",
|
||||
db_path, status.ToString()));
|
||||
}
|
||||
|
||||
db_ = std::shared_ptr<rocksdb::TransactionDB>(ptr);
|
||||
}
|
||||
|
||||
auto data_db::perform_action(
|
||||
std::string_view function_name,
|
||||
std::function<rocksdb::Status(rocksdb::TransactionDB *tx_db)> action) const
|
||||
-> bool {
|
||||
try {
|
||||
auto res = action(db_.get());
|
||||
if (not res.ok()) {
|
||||
utils::error::handle_error(function_name, res.ToString());
|
||||
}
|
||||
|
||||
return res.ok();
|
||||
} catch (const std::exception &ex) {
|
||||
utils::error::handle_exception(function_name, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto data_db::perform_action(
|
||||
std::string_view function_name,
|
||||
std::function<rocksdb::Status(rocksdb::Transaction *txn)> action) -> bool {
|
||||
std::unique_ptr<rocksdb::Transaction> txn{
|
||||
db_->BeginTransaction(rocksdb::WriteOptions{},
|
||||
rocksdb::TransactionOptions{}),
|
||||
};
|
||||
|
||||
try {
|
||||
auto res = action(txn.get());
|
||||
if (res.ok()) {
|
||||
auto commit_res = txn->Commit();
|
||||
if (commit_res.ok()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
utils::error::handle_error(function_name,
|
||||
"rocksdb commit failed|" + res.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
utils::error::handle_error(function_name,
|
||||
"rocksdb action failed|" + res.ToString());
|
||||
} catch (const std::exception &ex) {
|
||||
utils::error::handle_exception(function_name, ex);
|
||||
}
|
||||
|
||||
auto rollback_res = txn->Rollback();
|
||||
utils::error::handle_error(function_name, "rocksdb rollback failed|" +
|
||||
rollback_res.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
void data_db::remove(std::string_view download_id) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not perform_action(function_name,
|
||||
[&download_id](rocksdb::Transaction *txn) -> auto {
|
||||
return txn->Delete(download_id);
|
||||
})) {
|
||||
fmt::println("failed to remove|{}", download_id);
|
||||
}
|
||||
}
|
||||
|
||||
void data_db::set(const data_entry &entry) {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
if (not perform_action(
|
||||
function_name, [&entry](rocksdb::Transaction *txn) -> auto {
|
||||
return txn->Put(entry.download_id, nlohmann::json(entry).dump());
|
||||
})) {
|
||||
fmt::println("failed to set|{}", nlohmann::json(entry).dump(2));
|
||||
}
|
||||
}
|
||||
} // namespace monitarr
|
||||
90
monitarr/libmonitarr/src/initialize.cpp
Normal file
90
monitarr/libmonitarr/src/initialize.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#if defined(PROJECT_ENABLE_CURL)
|
||||
#include "curl/curl.h"
|
||||
#endif // defined(PROJECT_ENABLE_CURL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_OPENSSL)
|
||||
#include "openssl/ssl.h"
|
||||
#endif // defined(PROJECT_ENABLE_OPENSSL)
|
||||
|
||||
#if defined(PROJECT_REQUIRE_ALPINE) && !defined(PROJECT_IS_MINGW)
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#endif // defined(PROJECT_REQUIRE_ALPINE) && !defined(PROJECT_IS_MINGW)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
#include "sodium.h"
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
#include "sqlite3.h"
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
#include "initialize.hpp"
|
||||
|
||||
#if defined(PROJECT_REQUIRE_ALPINE) && !defined(PROJECT_IS_MINGW)
|
||||
#include "utils/path.hpp"
|
||||
#endif // defined(PROJECT_REQUIRE_ALPINE) && !defined (PROJECT_IS_MINGW)
|
||||
|
||||
namespace monitarr {
|
||||
auto project_initialize() -> bool {
|
||||
#if defined(PROJECT_REQUIRE_ALPINE) && !defined(PROJECT_IS_MINGW)
|
||||
{
|
||||
static constexpr const auto guard_size{4096U};
|
||||
static constexpr const auto stack_size{8U * 1024U * 1024U};
|
||||
|
||||
pthread_attr_t attr{};
|
||||
pthread_attr_setstacksize(&attr, stack_size);
|
||||
pthread_attr_setguardsize(&attr, guard_size);
|
||||
pthread_setattr_default_np(&attr);
|
||||
|
||||
setenv("ICU_DATA", utils::path::combine(".", {"/icu"}).c_str(), 1);
|
||||
}
|
||||
#endif // defined(PROJECT_REQUIRE_ALPINE) && !defined(PROJECT_IS_MINGW)
|
||||
|
||||
#if defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
{
|
||||
if (sodium_init() == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_LIBSODIUM)
|
||||
|
||||
#if defined(PROJECT_ENABLE_OPENSSL)
|
||||
{ SSL_library_init(); }
|
||||
#endif // defined(PROJECT_ENABLE_OPENSSL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_CURL)
|
||||
{
|
||||
auto res = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_CURL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
{
|
||||
auto res = sqlite3_initialize();
|
||||
if (res != SQLITE_OK) {
|
||||
#if defined(PROJECT_ENABLE_CURL)
|
||||
curl_global_cleanup();
|
||||
#endif // defined(PROJECT_ENABLE_CURL)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void project_cleanup() {
|
||||
#if defined(PROJECT_ENABLE_CURL)
|
||||
curl_global_cleanup();
|
||||
#endif // defined(PROJECT_ENABLE_CURL)
|
||||
|
||||
#if defined(PROJECT_ENABLE_SQLITE)
|
||||
sqlite3_shutdown();
|
||||
#endif // defined(PROJECT_ENABLE_SQLITE)
|
||||
}
|
||||
} // namespace monitarr
|
||||
18
monitarr/libmonitarr/src/settings.cpp
Normal file
18
monitarr/libmonitarr/src/settings.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "settings.hpp"
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
void app_config::load(std::string_view file_path) {
|
||||
nlohmann::json data;
|
||||
if (utils::file::read_json_file(file_path, data)) {
|
||||
*this = data.get<app_config>();
|
||||
}
|
||||
}
|
||||
|
||||
void app_config::save(std::string_view file_path) const {
|
||||
if (utils::file::write_json_file(file_path, nlohmann::json(*this))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // namespace monitarr
|
||||
260
monitarr/monitarr/main.cpp
Normal file
260
monitarr/monitarr/main.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#if defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
#include "backward.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
#include <execution>
|
||||
|
||||
#include "initialize.hpp"
|
||||
|
||||
#include "data_db.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/config.hpp"
|
||||
#include "utils/file.hpp"
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/unix.hpp"
|
||||
#include "utils/windows.hpp"
|
||||
|
||||
namespace monitarr {
|
||||
static void remove_stalled(std::string_view download_id, std::string_view title,
|
||||
std::uint64_t episode_id, std::uint64_t movie_id,
|
||||
const server_cfg &server, data_db &state_db) {
|
||||
fmt::println("remove and block {}|{}", download_id, title);
|
||||
state_db.remove(download_id);
|
||||
|
||||
httplib::Client cli{server.url};
|
||||
cli.set_default_headers({
|
||||
{"X-Api-Key", server.api_key},
|
||||
});
|
||||
|
||||
auto response = cli.Delete(
|
||||
fmt::format("/api/{}/queue/{}?blocklist=true&skipRedownload=false",
|
||||
server.api_version,
|
||||
utils::string::split(download_id, '/', false).at(1U)));
|
||||
if (response->status != httplib::StatusCode::OK_200) {
|
||||
fmt::println("remove and block result|{}|{}", server.url, response->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils::string::contains("radarr", server.id)) {
|
||||
nlohmann::json data({
|
||||
{"name", "MoviesSearch"},
|
||||
{"movieIds", {movie_id}},
|
||||
});
|
||||
|
||||
response = cli.Post("/api/{}/command", data.dump(), "application/json");
|
||||
if (response->status != httplib::StatusCode::OK_200) {
|
||||
fmt::println("failed to search radarr|{}|{}", server.url,
|
||||
response->status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils::string::contains("sonarr", server.id)) {
|
||||
nlohmann::json data({
|
||||
{"name", "EpisodeSearch"},
|
||||
{"episodeIds", {episode_id}},
|
||||
});
|
||||
|
||||
response = cli.Post("/api/{}/command", data.dump(), "application/json");
|
||||
if (response->status != httplib::StatusCode::OK_200) {
|
||||
fmt::println("failed to search sonarr|{}|{}", server.url,
|
||||
response->status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void check_server(const server_cfg &server, data_db &state_db) {
|
||||
httplib::Client cli{server.url};
|
||||
cli.set_default_headers({
|
||||
{"X-Api-Key", server.api_key},
|
||||
});
|
||||
|
||||
std::uint16_t page{0U};
|
||||
while (++page != 0U) {
|
||||
httplib::Params params;
|
||||
params.emplace("page", std::to_string(page));
|
||||
params.emplace("pageSize", "50");
|
||||
|
||||
auto response =
|
||||
cli.Get(fmt::format("/api/{}/queue", server.api_version), params, {});
|
||||
if (response->status != httplib::StatusCode::OK_200) {
|
||||
fmt::println("check server request failed|{}|{}", server.url,
|
||||
response->status);
|
||||
break;
|
||||
}
|
||||
|
||||
auto json_data = nlohmann::json::parse(response->body);
|
||||
if (json_data.at("page").get<std::uint32_t>() != page) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto now = utils::time::get_time_now();
|
||||
|
||||
for (const auto &record : json_data.at("records")) {
|
||||
auto download_id = fmt::format(
|
||||
"{}/{}", server.id, record.at("downloadId").get<std::string>());
|
||||
auto episode_id = record.contains("episodeId")
|
||||
? record["episodeId"].get<std::uint64_t>()
|
||||
: std::uint64_t{0U};
|
||||
auto movie_id = record.contains("movieId")
|
||||
? record["movieId"].get<std::uint64_t>()
|
||||
: std::uint64_t{0U};
|
||||
auto size_left = record.at("sizeleft").get<std::uint64_t>();
|
||||
auto title = record.at("title").get<std::string>();
|
||||
|
||||
auto data = state_db.get(download_id);
|
||||
const auto update_entry = [&download_id, &now, &size_left, &state_db,
|
||||
&title, url = server.url]() {
|
||||
if (size_left == 0U) {
|
||||
state_db.remove(download_id);
|
||||
return;
|
||||
}
|
||||
|
||||
fmt::println("updating {}|{}|{}|{}", download_id, title, now,
|
||||
size_left);
|
||||
state_db.set(data_entry{
|
||||
download_id,
|
||||
now,
|
||||
size_left,
|
||||
});
|
||||
};
|
||||
|
||||
if (data.has_value()) {
|
||||
if (std::chrono::nanoseconds(now - data->last_check) >=
|
||||
server.timeout) {
|
||||
if (size_left == data->size_left) {
|
||||
remove_stalled(download_id, title, episode_id, movie_id, server,
|
||||
state_db);
|
||||
} else {
|
||||
update_entry();
|
||||
}
|
||||
} else if (size_left == 0U) {
|
||||
state_db.remove(download_id);
|
||||
}
|
||||
} else {
|
||||
update_entry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto load_config(std::string &cfg_file) -> app_config {
|
||||
auto cfg_dir = utils::get_environment_variable("MONITARR_CFG_DIR");
|
||||
if (cfg_dir.empty()) {
|
||||
cfg_dir = utils::path::combine(".", {"config"});
|
||||
}
|
||||
|
||||
if (not utils::file::directory{cfg_dir}.create_directory()) {
|
||||
throw std::runtime_error(fmt::format("failed to create config dir|{}",
|
||||
cfg_dir,
|
||||
utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
cfg_file = utils::path::combine(cfg_dir, {"monitarr.json"});
|
||||
|
||||
fmt::println("loading config|{}", cfg_file);
|
||||
app_config cfg{};
|
||||
cfg.load(cfg_file);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
[[nodiscard]] static auto load_db() -> data_db {
|
||||
auto data_dir = utils::get_environment_variable("MONITARR_DATA_DIR");
|
||||
if (data_dir.empty()) {
|
||||
data_dir = utils::path::combine(".", {"data"});
|
||||
}
|
||||
|
||||
if (not utils::file::directory{data_dir}.create_directory()) {
|
||||
throw std::runtime_error(fmt::format("failed to create data dir|{}",
|
||||
data_dir,
|
||||
utils::get_last_error_code()));
|
||||
}
|
||||
|
||||
data_db state_db{};
|
||||
state_db.open(data_dir);
|
||||
return state_db;
|
||||
}
|
||||
} // namespace monitarr
|
||||
|
||||
using namespace monitarr;
|
||||
|
||||
auto main(int /* argc */, char ** /* argv */) -> int {
|
||||
MONITARR_USES_FUNCTION_NAME();
|
||||
|
||||
#if defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
static backward::SignalHandling sh;
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
if (not monitarr::project_initialize()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static std::mutex mtx;
|
||||
static std::condition_variable notify;
|
||||
static stop_type stop_requested{false};
|
||||
|
||||
static const auto quit_handler = [](int sig) {
|
||||
fmt::println("stop requested|{}", sig);
|
||||
stop_requested = true;
|
||||
|
||||
mutex_lock lock(mtx);
|
||||
notify.notify_all();
|
||||
};
|
||||
|
||||
std::signal(SIGINT, quit_handler);
|
||||
std::signal(SIGQUIT, quit_handler);
|
||||
std::signal(SIGTERM, quit_handler);
|
||||
|
||||
auto ret{0};
|
||||
|
||||
try {
|
||||
std::string cfg_file;
|
||||
auto cfg{load_config(cfg_file)};
|
||||
auto state_db{load_db()};
|
||||
|
||||
if (cfg.server_list.empty()) {
|
||||
fmt::println("no servers have been configured");
|
||||
ret = 3;
|
||||
} else {
|
||||
while (not stop_requested) {
|
||||
std::for_each(std::execution::par, cfg.server_list.begin(),
|
||||
cfg.server_list.end(), [&state_db](auto &&server) {
|
||||
if (stop_requested) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
check_server(server, state_db);
|
||||
} catch (const std::exception &ex) {
|
||||
utils::error::handle_exception(function_name, ex);
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
}
|
||||
});
|
||||
unique_mutex_lock lock(mtx);
|
||||
if (stop_requested) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fmt::println("waiting for next check|{}", cfg.check_interval);
|
||||
notify.wait_for(lock, cfg.check_interval);
|
||||
}
|
||||
}
|
||||
|
||||
cfg.save(cfg_file);
|
||||
state_db.close();
|
||||
} catch (const std::exception &ex) {
|
||||
utils::error::handle_exception(function_name, ex);
|
||||
ret = 2;
|
||||
} catch (...) {
|
||||
utils::error::handle_exception(function_name);
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
fmt::println("terminating application|{}", ret);
|
||||
monitarr::project_cleanup();
|
||||
return ret;
|
||||
}
|
||||
29
monitarr/monitarr_test/main.cpp
Normal file
29
monitarr/monitarr_test/main.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#if defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
#include "backward.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "initialize.hpp"
|
||||
#include "utils/config.hpp"
|
||||
|
||||
using namespace monitarr;
|
||||
|
||||
int PROJECT_TEST_RESULT{0};
|
||||
|
||||
auto main(int argc, char **argv) -> int {
|
||||
#if defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
static backward::SignalHandling sh;
|
||||
#endif
|
||||
|
||||
if (not monitarr::project_initialize()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
PROJECT_TEST_RESULT = RUN_ALL_TESTS();
|
||||
|
||||
monitarr::project_cleanup();
|
||||
|
||||
return PROJECT_TEST_RESULT;
|
||||
}
|
||||
14
monitarr/version.cpp.in
Normal file
14
monitarr/version.cpp.in
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "version.hpp"
|
||||
|
||||
namespace {
|
||||
static constexpr const std::string_view git_rev = "@PROJECT_GIT_REV@";
|
||||
static constexpr const std::string_view version =
|
||||
"@PROJECT_MAJOR_VERSION@.@PROJECT_MINOR_VERSION@.@PROJECT_REVISION_VERSION@"
|
||||
"-@PROJECT_RELEASE_ITER@";
|
||||
} // namespace
|
||||
|
||||
namespace monitarr {
|
||||
auto project_get_git_rev() -> std::string_view { return git_rev; }
|
||||
|
||||
auto project_get_version() -> std::string_view { return version; }
|
||||
} // namespace monitarr
|
||||
64
monitarr/version.rc.in
Normal file
64
monitarr/version.rc.in
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <windows.h>
|
||||
#define VER_FILEVERSION @PROJECT_MAJOR_VERSION@,@PROJECT_MINOR_VERSION@,@PROJECT_REVISION_VERSION@,@PROJECT_RELEASE_NUM@
|
||||
#define VER_FILEVERSION_STR "@PROJECT_MAJOR_VERSION@.@PROJECT_MINOR_VERSION@.@PROJECT_REVISION_VERSION@-@PROJECT_RELEASE_ITER@_@PROJECT_GIT_REV@\0"
|
||||
|
||||
#define VER_PRODUCTVERSION @PROJECT_MAJOR_VERSION@,@PROJECT_MINOR_VERSION@,@PROJECT_REVISION_VERSION@,@PROJECT_RELEASE_NUM@
|
||||
#define VER_PRODUCTVERSION_STR "@PROJECT_MAJOR_VERSION@.@PROJECT_MINOR_VERSION@.@PROJECT_REVISION_VERSION@-@PROJECT_RELEASE_ITER@_@PROJECT_GIT_REV@\0"
|
||||
|
||||
#define VER_COMPANYNAME_STR "@PROJECT_COMPANY_NAME@\0"
|
||||
#define VER_INTERNALNAME_STR "@PROJECT_NAME@ @PROJECT_MAJOR_VERSION@.@PROJECT_MINOR_VERSION@.@PROJECT_REVISION_VERSION@-@PROJECT_RELEASE_ITER@_@PROJECT_GIT_REV@\0"
|
||||
#define VER_LEGALCOPYRIGHT_STR "@PROJECT_COPYRIGHT@\0"
|
||||
#define VER_ORIGINALFILENAME_STR "@PROJECT_NAME@.exe\0"
|
||||
#define VER_LEGALTRADEMARKS1_STR "\0"
|
||||
#define VER_LEGALTRADEMARKS2_STR "\0"
|
||||
#define VER_FILEDESCRIPTION_STR "@PROJECT_DESC@\0"
|
||||
#define VER_PRODUCTNAME_STR "@PROJECT_NAME@ @PROJECT_MAJOR_VERSION@.@PROJECT_MINOR_VERSION@.@PROJECT_REVISION_VERSION@-@PROJECT_RELEASE_ITER@_@PROJECT_GIT_REV@\0"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define VER_DEBUG VS_FF_DEBUG
|
||||
#else
|
||||
#define VER_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define VER_PRERELEASE @PROJECT_PRERELEASE@
|
||||
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VER_FILEVERSION
|
||||
PRODUCTVERSION VER_PRODUCTVERSION
|
||||
FILEFLAGSMASK (VS_FF_DEBUG|VS_FF_PRERELEASE)
|
||||
FILEFLAGS (VER_DEBUG|VER_PRERELEASE)
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", VER_COMPANYNAME_STR
|
||||
VALUE "FileDescription", VER_FILEDESCRIPTION_STR
|
||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||
VALUE "InternalName", VER_INTERNALNAME_STR
|
||||
VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
|
||||
VALUE "LegalTrademarks1", VER_LEGALTRADEMARKS1_STR
|
||||
VALUE "LegalTrademarks2", VER_LEGALTRADEMARKS2_STR
|
||||
VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR
|
||||
VALUE "ProductName", VER_PRODUCTNAME_STR
|
||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||
END
|
||||
END
|
||||
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
/* The following line should only be modified for localized versions. */
|
||||
/* It consists of any number of WORD,WORD pairs, with each pair */
|
||||
/* describing a language,codepage combination supported by the file. */
|
||||
/* */
|
||||
/* For example, a file might have values "0x409,1252" indicating that it */
|
||||
/* supports English language (0x409) in the Windows ANSI codepage (1252). */
|
||||
|
||||
VALUE "Translation", 0x409, 1252
|
||||
|
||||
END
|
||||
END
|
||||
Reference in New Issue
Block a user