Skip to content

Commit

Permalink
lightningd: simplify --daemon.
Browse files Browse the repository at this point in the history
Dumb programs which have a --daemon option call fork() early.  This is
terrible UX since startup errors get lost: the program exits with
"success" immediately then you discover via the logs that it didn't
start at all.

However, forking late introduced a heap of problems with changing
pids.  Instead, fork early but keep stderr and the parent around: if
we fail early on, the parent fails with us.  We release our parent
with an explicit action just before the main loop.

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell authored and cdecker committed Aug 4, 2019
1 parent fc024f8 commit 979fbeb
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 270 deletions.
4 changes: 0 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ CCAN_OBJS := \
ccan-crypto-sha256.o \
ccan-crypto-shachain.o \
ccan-crypto-siphash24.o \
ccan-daemonize.o \
ccan-err.o \
ccan-fdpass.o \
ccan-htable.o \
Expand Down Expand Up @@ -132,7 +131,6 @@ CCAN_HEADERS := \
$(CCANDIR)/ccan/crypto/sha256/sha256.h \
$(CCANDIR)/ccan/crypto/shachain/shachain.h \
$(CCANDIR)/ccan/crypto/siphash24/siphash24.h \
$(CCANDIR)/ccan/daemonize/daemonize.h \
$(CCANDIR)/ccan/endian/endian.h \
$(CCANDIR)/ccan/err/err.h \
$(CCANDIR)/ccan/fdpass/fdpass.h \
Expand Down Expand Up @@ -617,8 +615,6 @@ ccan-opt-parse.o: $(CCANDIR)/ccan/opt/parse.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-opt-usage.o: $(CCANDIR)/ccan/opt/usage.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-daemonize.o: $(CCANDIR)/ccan/daemonize/daemonize.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-err.o: $(CCANDIR)/ccan/err/err.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-noerr.o: $(CCANDIR)/ccan/noerr/noerr.c
Expand Down
1 change: 0 additions & 1 deletion ccan/ccan/daemonize/LICENSE

This file was deleted.

54 changes: 0 additions & 54 deletions ccan/ccan/daemonize/_info

This file was deleted.

45 changes: 0 additions & 45 deletions ccan/ccan/daemonize/daemonize.c

This file was deleted.

21 changes: 0 additions & 21 deletions ccan/ccan/daemonize/daemonize.h

This file was deleted.

76 changes: 0 additions & 76 deletions ccan/ccan/daemonize/test/run.c

This file was deleted.

72 changes: 41 additions & 31 deletions lightningd/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
#include <ccan/array_size/array_size.h>
#include <ccan/cast/cast.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <ccan/daemonize/daemonize.h>
#include <ccan/err/err.h>
#include <ccan/io/fdpass/fdpass.h>
#include <ccan/io/io.h>
Expand Down Expand Up @@ -77,6 +76,7 @@
#include <lightningd/options.h>
#include <onchaind/onchain_wire.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

Expand Down Expand Up @@ -198,7 +198,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)

/*~ This is detailed in chaintopology.c */
ld->topology = new_topology(ld, ld->log);
ld->daemon = false;
ld->daemon_parent_fd = -1;
ld->config_filename = NULL;
ld->pidfile = NULL;
ld->proxyaddr = NULL;
Expand Down Expand Up @@ -501,30 +501,37 @@ static void init_txfilter(struct wallet *w, struct txfilter *filter)
* don't prevent unmounting whatever filesystem you happen to start in.
*
* But we define every path relative to our (~/.lightning) data dir, so we
* make sure we stay there.
* make sure we stay there. The rest of this is taken from ccan/daemonize,
* which was based on W. Richard Steven's advice in Programming in The Unix
* Environment.
*/
static void daemonize_but_keep_dir(struct lightningd *ld)
static void complete_daemonize(struct lightningd *ld)
{
/* daemonize moves us into /, but we want to be here */
const char *cwd = path_cwd(NULL);

/*~ SQLite3 does NOT like being open across fork(), a.k.a. daemonize() */
db_close_for_fork(ld->wallet->db);
if (!cwd)
fatal("Could not get current directory: %s", strerror(errno));
if (!daemonize())
fatal("Could not become a daemon: %s", strerror(errno));

/*~ Move back: important, since lightning dir may be relative! */
if (chdir(cwd) != 0)
fatal("Could not return to directory %s: %s",
cwd, strerror(errno));

db_reopen_after_fork(ld->wallet->db);

/*~ Why not allocate cwd off tmpctx? Probably because this code predates
* tmpctx. So we free manually here. */
tal_free(cwd);
int ok_status = 0;

/* Don't hold files open. */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

/* Many routines write to stderr; that can cause chaos if used
* for something else, so set it here. */
if (open("/dev/null", O_WRONLY) != 0)
fatal("Could not open /dev/null: %s", strerror(errno));
if (dup2(0, STDERR_FILENO) != STDERR_FILENO)
fatal("Could not dup /dev/null for stderr: %s", strerror(errno));
close(0);

/* Session leader so ^C doesn't whack us. */
if (setsid() == (pid_t)-1)
fatal("Could not setsid: %s", strerror(errno));

/* Discard our parent's old-fashioned umask prejudices. */
umask(0);

/* OK, parent, you can exit(0) now. */
write_all(ld->daemon_parent_fd, &ok_status, sizeof(ok_status));
close(ld->daemon_parent_fd);
}

/*~ It's pretty standard behaviour (especially for daemons) to create and
Expand Down Expand Up @@ -785,13 +792,6 @@ int main(int argc, char *argv[])
* in case that runs into trouble. */
crashlog = ld->log;

/*~ We defer --daemon until we've completed most initialization: that
* way we'll exit with an error rather than silently exiting 0, then
* realizing we can't start and forcing the confused user to read the
* logs. */
if (ld->daemon)
daemonize_but_keep_dir(ld);

/*~ We have to do this after daemonize, since that changes our pid! */
pidfile_write(ld, pid_fd);

Expand Down Expand Up @@ -830,6 +830,16 @@ int main(int argc, char *argv[])
* can start the poll loop which queries bitcoind for new blocks. */
begin_topology(ld->topology);

/*~ To handle --daemon, we fork the daemon early (otherwise we hit
* issues with our pid changing), but keep the parent around until
* we've completed most initialization: that way we'll exit with an
* error rather than silently exiting 0, then realizing we can't start
* and forcing the confused user to read the logs.
*
* But we're all initialized, so detach and have parent exit now. */
if (ld->daemon_parent_fd != -1)
complete_daemonize(ld);

/*~ The root of every backtrace (almost). This is our main event
* loop. */
void *io_loop_ret = io_loop_with_timers(ld);
Expand Down
5 changes: 3 additions & 2 deletions lightningd/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ struct lightningd {
/* The directory to find all the subdaemons. */
const char *daemon_dir;

/* Are we told to run in the background. */
bool daemon;
/* If we told to run in the background, this is our parent fd, otherwise
* -1. */
int daemon_parent_fd;

int pid_fd;

Expand Down
Loading

0 comments on commit 979fbeb

Please sign in to comment.