OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / swarmd / src / main.cpp

97 lines · cpp ref:
// swarmd — a generic swarm daemon.
//
//   swarmd --socket <path> [--bootstrap host:port] [--storage <dir>]
//
// Omit --bootstrap to use the public DHT. Omit --storage and the content plane
// stays off — content commands are then refused, which is a better answer than
// accepting a fetch that has nowhere to put a file.

#include <csignal>
#include <cstdio>
#include <cstring>

#include <sodium.h>

#include <string>
#include <unistd.h>

#include "daemon.h"

// ⛔ A DAEMON MUST NAME ITS OWN PROCESS. Without this the only way to stop one
// is to match its command line, and a pattern that matches a daemon's argv also
// matches the shell that typed it — `pkill -f` killed an ssh session's own
// remote shell twice in this project. The pidfile sits beside the socket, so
// whoever can reach the socket can stop exactly the process behind it.
static void write_pidfile(const std::string &socket_path) {
	FILE *f = fopen((socket_path + ".pid").c_str(), "w");
	if (!f) return;   // diagnostics, never a blocker
	fprintf(f, "%ld\n", (long)getpid());
	fclose(f);
}

static void remove_pidfile(const std::string &socket_path) {
	::unlink((socket_path + ".pid").c_str());
}

static volatile sig_atomic_t g_stop = 0;
static void on_signal(int) { g_stop = 1; }

int main(int argc, char **argv) {
	if (sodium_init() < 0) {
		fprintf(stderr, "sodium_init failed\n");
		return 1;
	}

	const char *sock = nullptr;
	const char *bootstrap = nullptr;
	const char *storage = nullptr;
	for (int i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "--socket") && i + 1 < argc) sock = argv[++i];
		else if (!strcmp(argv[i], "--bootstrap") && i + 1 < argc) bootstrap = argv[++i];
		else if (!strcmp(argv[i], "--storage") && i + 1 < argc) storage = argv[++i];
	}
	if (!sock) {
		fprintf(stderr, "usage: swarmd --socket <path> [--bootstrap host:port] "
						"[--storage <dir>]\n");
		return 1;
	}

	signal(SIGINT, on_signal);
	signal(SIGTERM, on_signal);

	swarmd::Daemon d;
	if (!d.start(sock, bootstrap, storage)) {
		fprintf(stderr, "failed to start (socket %s)\n", sock);
		return 1;
	}
	write_pidfile(sock);
	printf("[swarmd] listening on %s (pid %ld)\n", sock, (long)getpid());
	fflush(stdout);

	// ⛔ SAY WHETHER THE SWARM IS REACHABLE, not just whether we started.
	// "listening" is a LOCAL fact. Two peers once sat on one topic for ten
	// minutes both reporting success while neither had ever bootstrapped.
	int ticks = 0;
	int last_peers = -1;
	bool last_boot = false;
	while (!g_stop) {
		d.poll(200);
		const bool boot = d.dht_bootstrapped();
		const int peers = d.peers();
		if (boot != last_boot || peers != last_peers || (ticks % 25) == 0) {
			printf("[swarmd] dht=%s peers=%d local=%d\n",
				   boot ? "up" : (d.dht_active() ? "connecting" : "starting"),
				   peers, d.local_clients());
			fflush(stdout);
			last_boot = boot;
			last_peers = peers;
		}
		ticks++;
	}

	printf("[swarmd] stopping\n");
	d.stop();
	remove_pidfile(sock);
	return 0;
}