Skip to content
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

bitcoind: Add -daemonwait option to wait for initialization #21007

Merged
merged 3 commits into from
Mar 11, 2021

Conversation

laanwj
Copy link
Member

@laanwj laanwj commented Jan 25, 2021

This adds a -daemonwait flag that does the same as -daemon except that it, from a user perspective, backgrounds the process only after initialization is complete. This is similar to the behaviour of some other software such as c-lightning.

This can be useful when the process launching bitcoind wants to guarantee that either the RPC server is running, or that initialization failed, before continuing. The exit code indicates the initialization result.

The use of the libc function daemon() is replaced by a custom implementation which is inspired by the glibc implementation, but which also creates a pipe from the child to the parent process for communication.

An additional advantage of having our own daemon() implementation is that no MACOS-specific pragmas are needed anymore to silence a deprecation warning.

TODO:

  • Factor out token_read and token_write to an utility, and use them in shutdown.cpp as well—this is exactly the same kind of communication mechanism.

    • RAII-ify pipe endpoints.
  • Improve granularity of the configure.ac checks. This currently still checks for the function daemon() which makes no sense as it's not used. It should check for individual functions such as
    fork() and setsid() etc—the former being required, the second optional.

  • [-] Signal propagation during initialization: if say, pressing Ctrl-C during -daemonwait it would be good to pass this SIGINT on to the child process instead of detaching the parent process and letting the child run free. This is not necessary, see bitcoind: Add -daemonwait option to wait for initialization #21007 (comment).

Future:

  • Consider if it makes sense to use this in the RPC tests (there would be no more need for "is RPC ready" polling loops). I think this is out of scope for this PR.

@hebasto
Copy link
Member

hebasto commented Jan 25, 2021

This can be useful when the process launching bitcoind wants to guarantee that either the RPC server is running, or that initialization failed, before continuing. The exit code indicates the initialization result.

Why such behavior couldn't be the default?

src/init.cpp Outdated Show resolved Hide resolved
@laanwj
Copy link
Member Author

laanwj commented Jan 25, 2021

Why such behavior couldn't be the default?

I didn't do that to avoid that discussion for now 🙂 . It could always be made default at some point in the future if that's what people want.

@hebasto
Copy link
Member

hebasto commented Jan 25, 2021

Concept ACK.

@DrahtBot
Copy link
Contributor

DrahtBot commented Jan 26, 2021

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, 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.

@practicalswift
Copy link
Contributor

Concept ACK

@theStack
Copy link
Contributor

Concept ACK

Copy link
Member

@promag promag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested ACK 7482634 on macos.

With the following diff

--- a/src/init.cpp
+++ b/src/init.cpp
@@ -780,6 +780,9 @@ static bool InitSanityCheck()

 static bool AppInitServers(const util::Ref& context, NodeContext& node)
 {
+    sleep(5);
+    return false;
+
     const ArgsManager& args = *Assert(node.args);
     RPCServer::OnStarted(&OnRPCStarted);
     RPCServer::OnStopped(&OnRPCStopped);

On master

./src/bitcoind -regtest -daemon  && echo running
Bitcoin Core starting
running

But it's not running :trollface:

And with this change:

./src/bitcoind -regtest -daemonwait && echo running
Bitcoin Core starting
Error during initializaton - check debug.log for details

Good stuff, still have to review code change.

@laanwj
Copy link
Member Author

laanwj commented Jan 27, 2021

Thanks for testing! FWIW what I use to force a failure in AppInitMain is

src/bitcoind -regtest -daemonwait -pid=/invalid/path

(pid is, by definition, only written in the daemon process)
But that works too 😄

@hebasto
Copy link
Member

hebasto commented Jan 27, 2021

(pid is, by definition, only written in the daemon process)

Not sure if this is true in the master branch.

@laanwj
Copy link
Member Author

laanwj commented Jan 27, 2021

Not sure if this is true in the master branch.

I am sure of this. I did not change this at all, and besides, it would be a bug otherwise. After all, what use is the temporary parent process' PID.

@hebasto
Copy link
Member

hebasto commented Jan 27, 2021

... it would be a bug otherwise.

src/bitcoind -daemon=0

creates bitcoind.pid

@laanwj
Copy link
Member Author

laanwj commented Jan 27, 2021

creates bitcoind.pid

That's not my point. My point is that it creates the PID file after daemonizing (when daemon is enabled). Sure, it also creates the PID file otherwise, that's okay. The only thing I was trying to say is that the option can be used for testing -daemonwait because it fails after daemonizing, nothing more.

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested at 7482634, debug-builds cleanly for me and seems to behave as expected.

invalid path

$ ./src/bitcoind -signet -daemonwait -pid=/invalid/path
Bitcoin Core starting
Error during initializaton - check debug.log for details

(this particular case does not log anything to the debug log)

happy path

$ ./src/bitcoind -signet -daemonwait ; ./src/bitcoin-cli -signet stop
Bitcoin Core starting
Bitcoin Core stopping

debug log is as expected

2021-01-27T17:49:33Z Command-line arg: daemonwait=""
2021-01-27T17:49:33Z Command-line arg: signet=""
2021-01-27T17:49:33Z Using at most 125 automatic connections (1024 file descriptors available)
...
2021-01-27T17:49:35Z init message: Done loading
2021-01-27T17:49:35Z dnsseed thread start
2021-01-27T17:49:35Z Waiting 11 seconds before querying DNS seeds.
2021-01-27T17:49:35Z tor: Thread interrupt
2021-01-27T17:49:35Z addcon thread exit
...

2021-01-27T17:49:36Z Shutdown: done

passing -daemonwait=1 behaves (and logs) as expected as well

Ctrl-C works with a second or two of delay; it seems to not take effect until the wait is released but I'm not sure.

@laanwj
Copy link
Member Author

laanwj commented Jan 27, 2021

Thanks for testing @jonatack ,

(this particular case does not log anything to the debug log)

That it doesn't log anything is peculiar ! (but unrelated to this PR, I guess, the same problem would happen with -daemon, it would just mysteriously exit in the background).

Ctrl-C works with a second or two of delay; it seems to not take effect until the wait is released but I'm not sure.

It does respond to it? That's interesting. I thought the child process wouldn't get the SIGINT signal at all (hence my remaining TODO). That it doesn't react immediately is normal, It can't interrupt during verification of a block, for example. it's the same when interrupting a non-daemon startup.

@jonatack
Copy link
Member

Tested with testnet, which is much slower than signet for me (95 sec vs 3)

$ time ./src/bitcoind -testnet -daemonwait=1 && ./src/bitcoin-cli -testnet stop
Bitcoin Core starting

real	1m35.265s
user	0m0.137s
sys	0m0.015s
Bitcoin Core stopping
$

Ctrl-C right after launching interrupts successfully after the same time period elapses

$ time ./src/bitcoind -testnet -daemonwait=1
Bitcoin Core starting
^C
real	1m39.239s
user	0m0.130s
sys	0m0.022s
$

@laanwj
Copy link
Member Author

laanwj commented Jan 28, 2021

I've looked into this a bit.

It looks like that until the parent process exits, the child process is still attached to the terminal (or at least, shell session), so it still gets the SIGINT from Ctrl-C directly. Even though it did dup2 stdin/stdout/stderr to /dev/null already.

So there appears to be no need to do explicit signal propagation. That's neat. I'll remove that TODO and open this for review.

@laanwj laanwj marked this pull request as ready for review January 28, 2021 12:03
Copy link
Contributor

@ajtowns ajtowns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 8e6d339

src/bitcoind.cpp Outdated Show resolved Hide resolved
src/bitcoind.cpp Outdated Show resolved Hide resolved
src/util/tokenpipe.cpp Outdated Show resolved Hide resolved
src/util/tokenpipe.cpp Outdated Show resolved Hide resolved
Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK 8e6d339

Some nit suggestions below, feel free to ignore.

src/Makefile.am Outdated Show resolved Hide resolved
src/util/tokenpipe.cpp Outdated Show resolved Hide resolved
src/init.h Outdated Show resolved Hide resolved
// from terminal.
int fd = open("/dev/null", O_RDWR);
if (fd >= 0) {
bool err = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fd and err can be const

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure what the common thing is to do for internal variables, do we have a const-unless-required-otherwise recommendation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't think it's explicitly stated, other than maybe via the C++ Code Guidelines linked to in the developer notes, but it's what I've been doing/reviewing, and seeing others say and do.

assert(result == 1);
break;
}
int res = g_shutdown_r.TokenRead();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

res here and line 54 can be const

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach ACK 8e6d339

src/util/tokenpipe.cpp Outdated Show resolved Hide resolved
src/util/tokenpipe.h Outdated Show resolved Hide resolved
src/util/tokenpipe.h Outdated Show resolved Hide resolved
src/util/tokenpipe.h Outdated Show resolved Hide resolved
src/util/tokenpipe.h Outdated Show resolved Hide resolved
src/util/tokenpipe.h Outdated Show resolved Hide resolved
src/bitcoind.cpp Outdated Show resolved Hide resolved
src/bitcoind.cpp Outdated Show resolved Hide resolved
src/util/tokenpipe.h Outdated Show resolved Hide resolved
src/util/tokenpipe.h Outdated Show resolved Hide resolved
@laanwj
Copy link
Member Author

laanwj commented Mar 3, 2021

@hebasto @jonatack @ajtowns
Thanks for the reviews. I have addressed your comments.

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK b83b386 reviewed diff/code, built/tested

Some nits if you retouch.

src/bitcoind.cpp Outdated Show resolved Hide resolved
src/util/tokenpipe.cpp Show resolved Hide resolved
src/bitcoind.cpp Outdated Show resolved Hide resolved
@laanwj
Copy link
Member Author

laanwj commented Mar 4, 2021

Thanks @jonatack, have addressed the comments and re-pushed
b83b386c6e03a57f93110dfefdbbf515e62b5753..9a09a494e59b61d7a185da44f9c6736cb536c0c1

@jonatack
Copy link
Member

jonatack commented Mar 4, 2021

re-ACK 9a09a49

This adds a `-daemonwait` flag that does the same as `-daemon` except
it, from a user perspective, backgrounds the process only after
initialization is complete.

This can be useful when the process launching bitcoind wants to
guarantee that either the RPC server is running, or that initialization
failed, before continuing. The exit code indicates the initialization
result.

This replaces the use of the libc function `daemon()` by a custom
implementation which is inspired by the glibc implementation, but also
creates a pipe from the child to the parent process for communication.

An additional advantage of having our own `daemon()` implementation is
that no MACOS-specific pragmas are needed anymore to silence a
deprecation warning.
@laanwj
Copy link
Member Author

laanwj commented Mar 4, 2021

Repushed for an ordering issue in tokenpipe.cpp reported out-of-band, the implementation file now has same class order as the header
9a09a494e59b61d7a185da44f9c6736cb536c0c1..e017a913d0d78ef0766cf73586fe7a38488e1a26

@jonatack
Copy link
Member

jonatack commented Mar 4, 2021

Tested ACK e017a91 checked change since previous review is move-only

$ time ./src/bitcoind -testnet -daemonwait=1 && ./src/bitcoin-cli -testnet stop
Bitcoin Core starting

real	1m4.095s
user	0m0.132s
sys	0m0.027s
Bitcoin Core stopping

$ time ./src/bitcoind -testnet -daemonwait=1
Bitcoin Core starting
^C 
real	1m5.311s
user	0m0.047s
sys	0m0.023s

@laanwj laanwj merged commit 92cf3a2 into bitcoin:master Mar 11, 2021
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Mar 11, 2021
laanwj added a commit that referenced this pull request Mar 16, 2021
4d008f9 Always add -daemonwait to known command line arguments (Hennadii Stepanov)

Pull request description:

  This is a follow up of #21007.

  When `AC_CHECK_DECLS([fork])` fails:

  - on master (8e65320):
  ```
  $ src/bitcoind -daemonwait
  Error: Error parsing command line arguments: Invalid parameter -daemonwait

  ```

  - with this PR:
  ```
  $ src/bitcoind -daemonwait
  Error: -daemon is not supported on this operating system

  ```

ACKs for top commit:
  laanwj:
    Code review ACK 4d008f9

Tree-SHA512: 7fcb5e9d76958adcf57e04fa74bd2a98d62459d81a3c57a97bd74c346cbf47c53e560a15455fb024e912c3b44e8487a83499e993b282871ba069953e665d88a9
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Mar 16, 2021
…uments

4d008f9 Always add -daemonwait to known command line arguments (Hennadii Stepanov)

Pull request description:

  This is a follow up of bitcoin#21007.

  When `AC_CHECK_DECLS([fork])` fails:

  - on master (8e65320):
  ```
  $ src/bitcoind -daemonwait
  Error: Error parsing command line arguments: Invalid parameter -daemonwait

  ```

  - with this PR:
  ```
  $ src/bitcoind -daemonwait
  Error: -daemon is not supported on this operating system

  ```

ACKs for top commit:
  laanwj:
    Code review ACK 4d008f9

Tree-SHA512: 7fcb5e9d76958adcf57e04fa74bd2a98d62459d81a3c57a97bd74c346cbf47c53e560a15455fb024e912c3b44e8487a83499e993b282871ba069953e665d88a9
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Sep 17, 2021
…uments

4d008f9 Always add -daemonwait to known command line arguments (Hennadii Stepanov)

Pull request description:

  This is a follow up of bitcoin#21007.

  When `AC_CHECK_DECLS([fork])` fails:

  - on master (8e65320):
  ```
  $ src/bitcoind -daemonwait
  Error: Error parsing command line arguments: Invalid parameter -daemonwait

  ```

  - with this PR:
  ```
  $ src/bitcoind -daemonwait
  Error: -daemon is not supported on this operating system

  ```

ACKs for top commit:
  laanwj:
    Code review ACK 4d008f9

Tree-SHA512: 7fcb5e9d76958adcf57e04fa74bd2a98d62459d81a3c57a97bd74c346cbf47c53e560a15455fb024e912c3b44e8487a83499e993b282871ba069953e665d88a9
whitslack added a commit to whitslack/bitcoin that referenced this pull request Jan 14, 2022
When other initscripts depend on bitcoind, it's because their daemons
want to be able to invoke bitcoin-cli or to communicate with bitcoind
via RPC. That can't happen until some time after bitcoind has forked
into the background when it is started with the -daemon option. The
-daemonwait option was added in 92cf3a2 (bitcoin#21007) to address exactly
this issue, so let's use it.

To avoid blocking startup for arbitrarily long, as users would be
annoyed if agetty/display-manager doesn't start quickly, we now mark
the bitcoind service initially as inactive at startup and then call
back to OpenRC to mark the service as started (and proceed to start
any dependent services) after bitcoind forks into the background.
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Aug 16, 2022
@fanquake
Copy link
Member

This change was part of 22.0, removing "Needs release note".

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants