-
Notifications
You must be signed in to change notification settings - Fork 37.7k
init: don't delete PID file if it was not generated #28946
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
You can test similarly to #28784: will@ubuntu in ~ :
₿ bitcoind -regtest -daemon=1
Bitcoin Core starting
will@ubuntu in ~ :
₿ file ~/.bitcoin/regtest/bitcoind.pid
/home/will/.bitcoin/regtest/bitcoind.pid: ASCII text
will@ubuntu in ~ :
₿ bitcoind -regtest -daemon=1
Error: Cannot obtain a lock on data directory /home/will/.bitcoin/regtest. Bitcoin Core is probably already running.
will@ubuntu in ~ :
₿ file ~/.bitcoin/regtest/bitcoind.pid
/home/will/.bitcoin/regtest/bitcoind.pid: cannot open `/home/will/.bitcoin/regtest/bitcoind.pid' (No such file or directory) |
src/init.cpp
Outdated
if (!g_generated_pid) return; | ||
try { | ||
if (!fs::remove(GetPidFile(*node.args))) { | ||
LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__); | |
LogPrintf("Unable to remove PID file: File does not exist\n"); |
nit: No need to take this over, if you change the function name, the warning is already unique, and user can enable to print the function name, if they really want to.
src/init.cpp
Outdated
{ | ||
if (!g_generated_pid) return; | ||
try { | ||
if (!fs::remove(GetPidFile(*node.args))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: No need to pass node
, when you only need args?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, that's nicer. Will fix.
src/init.cpp
Outdated
LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__); | ||
} | ||
} catch (const fs::filesystem_error& e) { | ||
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
5eefe8d
to
98ba5ba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
src/init.cpp
Outdated
try { | ||
if (!fs::remove(GetPidFile(args))) { | ||
LogPrintf("Unable to remove PID file: File does not exist\n"); | ||
} | ||
} catch (const fs::filesystem_error& e) { | ||
LogPrintf("Unable to remove PID file: %s\n", fsbridge::get_filesystem_error_message(e)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can be simplified
try { | |
if (!fs::remove(GetPidFile(args))) { | |
LogPrintf("Unable to remove PID file: File does not exist\n"); | |
} | |
} catch (const fs::filesystem_error& e) { | |
LogPrintf("Unable to remove PID file: %s\n", fsbridge::get_filesystem_error_message(e)); | |
} | |
if (std::error_code error; !fs::remove(GetPidFile(args), error)) { | |
std::string msg{error ? error.message() : "File does not exist"}; | |
LogPrintf("Unable to remove PID file: %s\n", msg); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is passing error codes our preferred error handling technique, or just slightly shorter?
I'd naturally opt for the former, and I think errors from it will be slightly more detailed, including an error msg instead of only error code (although it doesn't make much difference in this case), but can very easily be persuaded to switch to the latter if it's preferred...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just like the brevity of it. I'm using error.message()
and not error.value()
so the user will see a user friendly error message. Could include the pid path in the log line if preferred, probably helpful to the user to know which file to change permissions on indeed.
For example, with this patch and chmod a-w
on the datadir this will print:
Unable to remove PID file: Permission denied
For a missing file, this will print
Unable to remove PID file: File does not exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've taken this in e56e8cd and added the pid path to the log message.
test/functional/feature_filelock.py
Outdated
@@ -30,6 +30,10 @@ def run_test(self): | |||
expected_msg = f"Error: Cannot obtain a lock on data directory {datadir}. {self.config['environment']['PACKAGE_NAME']} is probably already running." | |||
self.nodes[1].assert_start_raises_init_error(extra_args=[f'-datadir={self.nodes[0].datadir_path}', '-noserver'], expected_msg=expected_msg) | |||
|
|||
self.log.info("Check that cookie and PID files are not deleted when attempting to start a second bitcoind using the same datadir") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cookie file is not yet checked here, would leave that for the other pull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, thanks. I wrote this commit on top as I was reviewing the other one, then cherry-picked and made it standalone. I guess this slipped through.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixed in e56e8cd
return true; | ||
} else { | ||
return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), SysErrorString(errno))); | ||
} | ||
} | ||
|
||
static void RemovePidFile(const ArgsManager& args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#28784 stores the path in . I don't have a particularly strong conviction in either direction, but I'd use the same approach for both. Storing the location of the file to be deleted seems a bit more robust, so I think I'd lean towards this approach:static std::optional<std::string> g_generated_cookie
git diff on 98ba5ba
diff --git a/src/init.cpp b/src/init.cpp
index afa0ab1ee6..f2d74db13f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -96,6 +96,7 @@
#include <cstdio>
#include <fstream>
#include <functional>
+#include <optional>
#include <set>
#include <string>
#include <thread>
@@ -155,7 +156,7 @@ static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
* The PID file facilities.
*/
static const char* BITCOIN_PID_FILENAME = "bitcoind.pid";
-static bool g_generated_pid{false};
+static std::optional<fs::path> g_generated_pid;
static fs::path GetPidFile(const ArgsManager& args)
{
@@ -164,25 +165,26 @@ static fs::path GetPidFile(const ArgsManager& args)
[[nodiscard]] static bool CreatePidFile(const ArgsManager& args)
{
- std::ofstream file{GetPidFile(args)};
+ const fs::path pid_path{GetPidFile(args)};
+ std::ofstream file{pid_path};
if (file) {
#ifdef WIN32
tfm::format(file, "%d\n", GetCurrentProcessId());
#else
tfm::format(file, "%d\n", getpid());
#endif
- g_generated_pid = true;
+ g_generated_pid = pid_path;
return true;
} else {
- return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), SysErrorString(errno)));
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(pid_path), SysErrorString(errno)));
}
}
-static void RemovePidFile(const ArgsManager& args)
+static void RemovePidFile()
{
if (!g_generated_pid) return;
try {
- if (!fs::remove(GetPidFile(args))) {
+ if (!fs::remove(g_generated_pid.value())) {
LogPrintf("Unable to remove PID file: File does not exist\n");
}
} catch (const fs::filesystem_error& e) {
@@ -364,7 +366,7 @@ void Shutdown(NodeContext& node)
node.scheduler.reset();
node.kernel.reset();
- RemovePidFile(*node.args);
+ RemovePidFile();
LogPrintf("%s: done\n", __func__);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, looking at 28784 more closely it seems g_generated_cookie
stores the cookie, not the path. My bad.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the idea of storing a bool with whether it has been generated or not, and use the {Get|Create|Remove}PidFile()
functions as necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay with the boolean approach if you prefer it. I just like the "we created this file, so let's remove this file" explicitness over "we created a file, so let's remove a file". But in practice, no big difference.
98ba5ba
to
e56e8cd
Compare
ACK e56e8cd |
src/init.cpp
Outdated
if (!g_generated_pid) return; | ||
if (std::error_code error; !fs::remove(GetPidFile(args), error)) { | ||
std::string msg{error ? error.message() : "File does not exist"}; | ||
auto pid_path = args.GetPathArg("-pid", BITCOIN_PID_FILENAME); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to just use GetPidFile()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that's cleaner. Fixed in 82f18e9 along with a typo in the commit message.
e56e8cd
to
82f18e9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 82f18e9
return true; | ||
} else { | ||
return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), SysErrorString(errno))); | ||
} | ||
} | ||
|
||
static void RemovePidFile(const ArgsManager& args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: quick docstring would be nice
/**
* Remove PID file, but only if it was also generated by this process to avoid problems when
* multiple bitcoind processes are started on the same datadir.
*/
git diff on 82f18e9
diff --git a/src/init.cpp b/src/init.cpp
index fced24a6ff..7a87467252 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -177,7 +177,10 @@ static fs::path GetPidFile(const ArgsManager& args)
return InitError(strprintf(_("Unable to create the PID file '%s': %s"), fs::PathToString(GetPidFile(args)), SysErrorString(errno)));
}
}
-
+/**
+ * Remove PID file, but only if it was also generated by this process to avoid problems when
+ * multiple bitcoind processes are started on the same datadir.
+ */
static void RemovePidFile(const ArgsManager& args)
{
if (!g_generated_pid) return;
src/init.cpp
Outdated
static void RemovePidFile(const ArgsManager& args) | ||
{ | ||
if (!g_generated_pid) return; | ||
auto pid_path = GetPidFile(args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit
auto pid_path = GetPidFile(args); | |
const auto pid_path{GetPidFile(args)}; |
src/init.cpp
Outdated
auto pid_path = GetPidFile(args); | ||
if (std::error_code error; !fs::remove(pid_path, error)) { | ||
std::string msg{error ? error.message() : "File does not exist"}; | ||
LogPrintf("Unable to remove PID file %s: %s\n", fs::quoted(fs::PathToString(pid_path)), msg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'm not sure how important the quoting is, could just use parentheses to make it a bit more concise?
LogPrintf("Unable to remove PID file %s: %s\n", fs::quoted(fs::PathToString(pid_path)), msg); | |
LogPrintf("Unable to remove PID file (%s): %s\n", fs::PathToString(pid_path), msg); |
@stickies-v took most of your suggestions in 2b28f7d |
82f18e9
to
2b28f7d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 2b28f7d
src/init.cpp
Outdated
/** | ||
* Whether this process has created a PID file. | ||
* Used when determining whether we should remove the PID file on shutdown. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think this more clearly explains the rationale without being too explicit about implementation
/** | |
* Whether this process has created a PID file. | |
* Used when determining whether we should remove the PID file on shutdown. | |
*/ | |
//! True if this process created the pid file, so we don't delete it too early if multiple processes were started |
Previously, starting a second bitcoind using the same datadir would correctly fail to init and shutdown. However during shutdown the PID file belonging to the first instance would be erroneously removed by the second process shutting down. Fix this to only delete the PID file if we created it.
2b28f7d
to
8f6ab31
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 8f6ab31
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 8f6ab31
ACK 8f6ab31 |
Previously, starting a second bitcoind using the same datadir would correctly fail to init and shutdown. However during shutdown the PID file belonging to the first instance would be erroneously removed by the second process shutting down. Fix this to only delete the PID file if we created it. Github-Pull: bitcoin#28946 Rebased-From: 8f6ab31
Previously, starting a second bitcoind using the same datadir would correctly fail to init and shutdown. However during shutdown the PID file belonging to the first instance would be erroneously removed by the second process shutting down. Fix this to only delete the PID file if we created it. Github-Pull: bitcoin#28946 Rebased-From: 8f6ab31
In a similar vein to #28784, if a second
bitcoind
is started using the same datadir it will fail to start up, but during shutdown remove the PID file from the firstbitcoind
instance.