2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-09-21 23:34:18 +00:00
|
|
|
#include <chrono>
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <string>
|
|
|
|
#include <string_view>
|
2022-08-10 13:34:51 +00:00
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <fmt/core.h>
|
2022-10-01 14:12:49 +00:00
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#define LIBTRANSMISSION_WATCHDIR_MODULE
|
2022-04-09 14:58:02 +00:00
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/error-types.h"
|
|
|
|
#include "libtransmission/error.h"
|
|
|
|
#include "libtransmission/file.h"
|
|
|
|
#include "libtransmission/log.h"
|
|
|
|
#include "libtransmission/tr-strbuf.h"
|
|
|
|
#include "libtransmission/utils.h" // for _()
|
|
|
|
#include "libtransmission/watchdir-base.h"
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-04-09 14:58:02 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
namespace libtransmission
|
|
|
|
{
|
|
|
|
namespace
|
2016-01-02 14:28:59 +00:00
|
|
|
{
|
2022-04-09 14:58:02 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
[[nodiscard]] constexpr std::string_view actionToString(Watchdir::Action action)
|
|
|
|
{
|
|
|
|
switch (action)
|
2016-01-02 14:28:59 +00:00
|
|
|
{
|
2022-08-10 13:34:51 +00:00
|
|
|
case Watchdir::Action::Retry:
|
|
|
|
return "retry";
|
2022-04-09 14:58:02 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
case Watchdir::Action::Done:
|
|
|
|
return "done";
|
2022-04-09 14:58:02 +00:00
|
|
|
}
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
return "???";
|
|
|
|
}
|
2022-04-09 14:58:02 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
[[nodiscard]] bool isRegularFile(std::string_view dir, std::string_view name)
|
2016-01-02 14:28:59 +00:00
|
|
|
{
|
2022-08-10 13:34:51 +00:00
|
|
|
auto const path = tr_pathbuf{ dir, '/', name };
|
2017-04-19 12:04:45 +00:00
|
|
|
|
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-04-09 14:58:02 +00:00
|
|
|
{
|
2022-08-10 13:34:51 +00:00
|
|
|
if (!TR_ERROR_IS_ENOENT(error->code))
|
2022-04-09 14:58:02 +00:00
|
|
|
{
|
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-04-09 14:58:02 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
tr_error_free(error);
|
2016-01-02 14:28:59 +00:00
|
|
|
}
|
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
return info && info->isFile();
|
|
|
|
}
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
} // namespace
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
namespace impl
|
|
|
|
{
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
void BaseWatchdir::processFile(std::string_view basename)
|
|
|
|
{
|
|
|
|
if (!isRegularFile(dirname_, basename) || handled_.count(basename) != 0)
|
2022-04-09 14:58:02 +00:00
|
|
|
{
|
2022-08-10 13:34:51 +00:00
|
|
|
return;
|
2022-04-09 14:58:02 +00:00
|
|
|
}
|
2016-01-02 14:28:59 +00:00
|
|
|
|
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)
|
2022-04-09 14:58:02 +00:00
|
|
|
{
|
2022-08-20 20:57:32 +00:00
|
|
|
auto const [iter, added] = pending_.try_emplace(std::string{ basename });
|
2016-01-02 14:28:59 +00:00
|
|
|
|
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;
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
if (info.first_kick_at == Timestamp{})
|
2022-04-09 14:58:02 +00:00
|
|
|
{
|
2022-08-10 13:34:51 +00:00
|
|
|
info.first_kick_at = now;
|
2022-04-09 14:58:02 +00:00
|
|
|
}
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
if (now - info.first_kick_at > timeoutDuration())
|
2022-04-09 14:58:02 +00:00
|
|
|
{
|
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-04-09 14:58:02 +00:00
|
|
|
}
|
2022-08-10 13:34:51 +00:00
|
|
|
else
|
2016-01-02 14:28:59 +00:00
|
|
|
{
|
2022-08-10 13:34:51 +00:00
|
|
|
setNextKickTime(info);
|
|
|
|
restartTimerIfPending();
|
2022-04-09 14:58:02 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-10 13:34:51 +00:00
|
|
|
else if (action == Action::Done)
|
2022-04-09 14:58:02 +00:00
|
|
|
{
|
2022-08-20 20:57:32 +00:00
|
|
|
handled_.emplace(basename);
|
2016-01-02 14:28:59 +00:00
|
|
|
}
|
2022-08-10 13:34:51 +00:00
|
|
|
}
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
void BaseWatchdir::scan()
|
|
|
|
{
|
|
|
|
tr_error* error = nullptr;
|
2016-01-02 14:28:59 +00:00
|
|
|
|
2023-04-18 21:23:20 +00:00
|
|
|
for (auto const& file : tr_sys_dir_get_files(dirname_, tr_basename_is_not_dotfile, &error))
|
2016-01-02 14:28:59 +00:00
|
|
|
{
|
2023-04-18 21:23:20 +00:00
|
|
|
processFile(file);
|
2022-04-09 14:58:02 +00:00
|
|
|
}
|
2016-01-02 14:28:59 +00:00
|
|
|
|
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);
|
|
|
|
}
|
2016-01-02 14:28:59 +00:00
|
|
|
}
|
|
|
|
|
2022-08-10 13:34:51 +00:00
|
|
|
} // namespace impl
|
|
|
|
} // namespace libtransmission
|