diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -21,3 +21,9 @@
- The `getmininginfo` RPC now omits `currentblocksize` and `currentblocktx`
when a block was never assembled via RPC on this node.
+Seeder
+------
+ - Added the `seederdir` option to the seeder allowing a directory to be
+ specified for the files the seeder writes to and reads from (default is the
+ `~/.bitcoin` directory). See help for more details.
+
diff --git a/src/seeder/main.cpp b/src/seeder/main.cpp
--- a/src/seeder/main.cpp
+++ b/src/seeder/main.cpp
@@ -94,6 +94,26 @@
ipv6_proxy = gArgs.GetArg("-proxyipv6", DEFAULT_IPV6_PROXY);
SelectParams(gArgs.GetChainName());
+ if (!CheckSeederDirOption()) {
+ tfm::format(std::cout,
+ "Specified data directory \"%s\" does not exist.\n",
+ gArgs.GetArg("-seederdir", ""));
+ return EXIT_FAILURE;
+ }
+
+ // Warn about relative -seederdir path
+ if (gArgs.IsArgSet("-seederdir") &&
+ !fs::path(gArgs.GetArg("-seederdir", "")).is_absolute()) {
+ tfm::format(
+ std::cout,
+ "Warning: relative seederdir option '%s' specified, which will "
+ "be interpreted relative to the current working directory "
+ "'%s'. This is fragile, because if the seeder is started in "
+ "the future from a different location, it will be unable to "
+ "locate the current data files.\n",
+ gArgs.GetArg("-seederdir", ""), fs::current_path().string());
+ }
+
if (gArgs.IsArgSet("-filter")) {
// Parse whitelist additions
std::string flagString = gArgs.GetArg("-filter", "");
@@ -154,6 +174,10 @@
gArgs.AddArg("-help-debug",
"Show all debugging options (usage: --help -help-debug)",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-seederdir=
",
+ "Specify directory to hold the subdirectory for files the "
+ "seeder writes to and reads from (default: ~/.bitcoin/",
+ ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();
gArgs.AddArg("-help", "", ArgsManager::ALLOW_ANY,
@@ -382,15 +406,16 @@
{
std::vector v = db.GetAll();
sort(v.begin(), v.end(), StatCompare);
- FILE *f = fsbridge::fopen("dnsseed.dat.new", "w+");
+ FILE *f = fsbridge::fopen(GetSeederDir() / "dnsseed.dat.new", "w+");
if (f) {
{
CAutoFile cf(f, SER_DISK, CLIENT_VERSION);
cf << db;
}
- rename("dnsseed.dat.new", "dnsseed.dat");
+ rename(GetSeederDir() / "dnsseed.dat.new",
+ GetSeederDir() / "dnsseed.dat");
}
- fsbridge::ofstream d{"dnsseed.dump"};
+ fsbridge::ofstream d{GetSeederDir() / "dnsseed.dump"};
tfm::format(
d, "# address good "
"lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) "
@@ -413,7 +438,8 @@
stat[3] += rep.uptime[3];
stat[4] += rep.uptime[4];
}
- fsbridge::ofstream ff{"dnsstats.log", std::ios_base::app};
+ fsbridge::ofstream ff{GetSeederDir() / "dnsstats.log",
+ std::ios_base::app};
tfm::format(ff, "%llu %g %g %g %g %g\n",
(unsigned long long)(time(nullptr)), stat[0], stat[1],
stat[2], stat[3], stat[4]);
@@ -534,7 +560,8 @@
tfm::format(std::cerr, "No e-mail address set. Please use -m.\n");
return EXIT_FAILURE;
}
- FILE *f = fsbridge::fopen("dnsseed.dat", "r");
+
+ FILE *f = fsbridge::fopen(GetSeederDir() / "dnsseed.dat", "r");
if (f) {
tfm::format(std::cout, "Loading dnsseed.dat...");
CAutoFile cf(f, SER_DISK, CLIENT_VERSION);
diff --git a/src/util/system.h b/src/util/system.h
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -83,10 +83,13 @@
fs::path GetDefaultDataDir();
// The blocks directory is always net specific.
const fs::path &GetBlocksDir();
+// The seeder directory is always net specific.
+const fs::path &GetSeederDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
// Return true if -datadir option points to a valid directory or is not
// specified.
bool CheckDataDirOption();
+bool CheckSeederDirOption();
/** Tests only */
void ClearDatadirCache();
fs::path GetConfigFile(const std::string &confPath);
diff --git a/src/util/system.cpp b/src/util/system.cpp
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -645,6 +645,7 @@
}
static fs::path g_blocks_path_cache_net_specific;
+static fs::path seederPathCachedNetSpecific;
static fs::path pathCached;
static fs::path pathCachedNetSpecific;
static RecursiveMutex csPathCached;
@@ -675,6 +676,31 @@
return path;
}
+const fs::path &GetSeederDir() {
+ LOCK(csPathCached);
+ fs::path &path = seederPathCachedNetSpecific;
+
+ // Cache the path to avoid calling fs::create_directories on every call of
+ // this function
+ if (!path.empty()) {
+ return path;
+ }
+ if (gArgs.IsArgSet("-seederdir")) {
+ path = fs::system_complete(gArgs.GetArg("-seederdir", ""));
+ if (!fs::is_directory(path)) {
+ path = "";
+ return path;
+ }
+ } else {
+ path = GetDataDir(false);
+ }
+
+ path /= BaseParams().DataDir();
+ path /= "seeder";
+ fs::create_directories(path);
+ return path;
+}
+
const fs::path &GetDataDir(bool fNetSpecific) {
LOCK(csPathCached);
fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
@@ -719,12 +745,19 @@
return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
}
+bool CheckSeederDirOption() {
+ std::string seederdir = gArgs.GetArg("-seederdir", "");
+ return seederdir.empty() ||
+ fs::is_directory(fs::system_complete(seederdir));
+}
+
void ClearDatadirCache() {
LOCK(csPathCached);
pathCached = fs::path();
pathCachedNetSpecific = fs::path();
g_blocks_path_cache_net_specific = fs::path();
+ seederPathCachedNetSpecific = fs::path();
}
fs::path GetConfigFile(const std::string &confPath) {