transmission/libtransmission/watchdir.cc

160 lines
3.8 KiB
C++
Raw Normal View History

// This file Copyright © 2015-2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#define LIBTRANSMISSION_WATCHDIR_MODULE
2022-09-21 23:34:18 +00:00
#include <chrono>
2022-08-10 13:34:51 +00:00
#include <set>
#include "transmission.h"
#include "error-types.h"
2022-08-10 13:34:51 +00:00
#include "error.h"
#include "file.h"
#include "log.h"
#include "tr-strbuf.h"
2022-08-10 13:34:51 +00:00
#include "utils.h" // for _()
#include "watchdir-base.h"
using namespace std::literals;
2022-08-10 13:34:51 +00:00
namespace libtransmission
{
namespace
{
2022-08-10 13:34:51 +00:00
[[nodiscard]] constexpr std::string_view actionToString(Watchdir::Action action)
{
switch (action)
{
2022-08-10 13:34:51 +00:00
case Watchdir::Action::Retry:
return "retry";
2022-08-10 13:34:51 +00:00
case Watchdir::Action::Done:
return "done";
}
2022-08-10 13:34:51 +00:00
return "???";
}
2022-08-10 13:34:51 +00:00
[[nodiscard]] bool isRegularFile(std::string_view dir, std::string_view name)
{
2022-08-10 13:34:51 +00:00
auto const path = tr_pathbuf{ dir, '/', name };
2022-08-10 13:34:51 +00:00
tr_error* error = nullptr;
auto const info = tr_sys_path_get_info(path, 0, &error);
if (error != nullptr)
{
2022-08-10 13:34:51 +00:00
if (!TR_ERROR_IS_ENOENT(error->code))
{
2022-08-10 13:34:51 +00:00
tr_logAddWarn(fmt::format(
_("Skipping '{path}': {error} ({error_code})"),
fmt::arg("path", path),
fmt::arg("error", error->message),
fmt::arg("error_code", error->code)));
}
2022-08-10 13:34:51 +00:00
tr_error_free(error);
}
2022-08-10 13:34:51 +00:00
return info && info->isFile();
}
2022-08-10 13:34:51 +00:00
} // namespace
2022-08-10 13:34:51 +00:00
std::chrono::milliseconds Watchdir::generic_rescan_interval_ = Watchdir::DefaultGenericRescanInterval;
2022-08-10 13:34:51 +00:00
namespace impl
{
2022-08-10 13:34:51 +00:00
void BaseWatchdir::processFile(std::string_view basename)
{
if (!isRegularFile(dirname_, basename) || handled_.count(basename) != 0)
{
2022-08-10 13:34:51 +00:00
return;
}
2022-08-10 13:34:51 +00:00
auto const action = callback_(dirname_, basename);
tr_logAddDebug(fmt::format("Callback decided to {:s} file '{:s}'", actionToString(action), basename));
if (action == Action::Retry)
{
auto const [iter, added] = pending_.try_emplace(std::string{ basename });
2022-08-10 13:34:51 +00:00
auto const now = std::chrono::steady_clock::now();
auto& info = iter->second;
++info.strikes;
info.last_kick_at = now;
2022-08-10 13:34:51 +00:00
if (info.first_kick_at == Timestamp{})
{
2022-08-10 13:34:51 +00:00
info.first_kick_at = now;
}
2022-08-10 13:34:51 +00:00
if (now - info.first_kick_at > timeoutDuration())
{
2022-08-10 13:34:51 +00:00
tr_logAddWarn(fmt::format(_("Couldn't add torrent file '{path}'"), fmt::arg("path", basename)));
pending_.erase(iter);
}
2022-08-10 13:34:51 +00:00
else
{
2022-08-10 13:34:51 +00:00
setNextKickTime(info);
restartTimerIfPending();
}
}
2022-08-10 13:34:51 +00:00
else if (action == Action::Done)
{
handled_.emplace(basename);
}
2022-08-10 13:34:51 +00:00
}
2022-08-10 13:34:51 +00:00
void BaseWatchdir::scan()
{
auto new_dir_entries = std::set<std::string>{};
2022-08-10 13:34:51 +00:00
tr_error* error = nullptr;
auto const dir = tr_sys_dir_open(dirname_.c_str(), &error);
if (dir == TR_BAD_SYS_DIR)
{
tr_logAddWarn(fmt::format(
_("Couldn't read '{path}': {error} ({error_code})"),
fmt::arg("path", dirname()),
fmt::arg("error", error->message),
fmt::arg("error_code", error->code)));
tr_error_free(error);
return;
}
2022-08-10 13:34:51 +00:00
for (;;)
{
2022-08-10 13:34:51 +00:00
char const* const name = tr_sys_dir_read_name(dir, &error);
if (name == nullptr)
{
2022-08-10 13:34:51 +00:00
break;
}
2022-08-10 13:34:51 +00:00
if ("."sv == name || ".."sv == name)
{
2022-08-10 13:34:51 +00:00
continue;
}
2022-08-10 13:34:51 +00:00
processFile(name);
}
2022-08-10 13:34:51 +00:00
if (error != nullptr)
{
tr_logAddWarn(fmt::format(
_("Couldn't read '{path}': {error} ({error_code})"),
fmt::arg("path", dirname()),
fmt::arg("error", error->message),
fmt::arg("error_code", error->code)));
tr_error_free(error);
}
2022-08-10 13:34:51 +00:00
tr_sys_dir_close(dir);
}
2022-08-10 13:34:51 +00:00
} // namespace impl
} // namespace libtransmission