Skip to content
Snippets Groups Projects
Commit 439b7d96 authored by Adrien Béraud's avatar Adrien Béraud
Browse files

dhtnode: daemonize with the new -d argument

Running dhtnode with -d will make it detach and fork.
Add an argument to -v to log to a file instead of stdio.
parent 1a888dbe
Branches
Tags
No related merge requests found
......@@ -24,6 +24,7 @@ extern "C" {
}
#include <set>
#include <thread> // std::this_thread::sleep_for
using namespace dht;
......@@ -68,9 +69,177 @@ void print_help() {
<< std::endl;
}
void cmd_loop(DhtRunner& dht, dht_params& params)
{
print_node_info(dht, params);
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
// using the GNU History API
using_history();
while (true)
{
// using the GNU Readline API
std::string line = readLine();
if (!line.empty() && line[0] == '\0')
break;
std::istringstream iss(line);
std::string op, idstr, value;
iss >> op >> idstr;
if (op == "x" || op == "q" || op == "exit" || op == "quit") {
break;
} else if (op == "h" || op == "help") {
print_help();
continue;
} else if (op == "ll") {
print_node_info(dht, params);
unsigned good4, dubious4, cached4, incoming4;
unsigned good6, dubious6, cached6, incoming6;
dht.getNodesStats(AF_INET, &good4, &dubious4, &cached4, &incoming4);
dht.getNodesStats(AF_INET6, &good6, &dubious6, &cached6, &incoming6);
std::cout << "IPv4 nodes : " << good4 << " good, " << dubious4 << " dubious, " << incoming4 << " incoming." << std::endl;
std::cout << "IPv6 nodes : " << good6 << " good, " << dubious6 << " dubious, " << incoming6 << " incoming." << std::endl;
continue;
} else if (op == "lr") {
std::cout << "IPv4 routing table:" << std::endl;
std::cout << dht.getRoutingTablesLog(AF_INET) << std::endl;
std::cout << "IPv6 routing table:" << std::endl;
std::cout << dht.getRoutingTablesLog(AF_INET6) << std::endl;
continue;
} else if (op == "ld") {
std::cout << dht.getStorageLog() << std::endl;
continue;
} else if (op == "ls") {
std::cout << "Searches:" << std::endl;
std::cout << dht.getSearchesLog() << std::endl;
continue;
} else if (op == "la") {
std::cout << "Reported public addresses:" << std::endl;
auto addrs = dht.getPublicAddressStr();
for (const auto& addr : addrs)
std::cout << addr << std::endl;
continue;
} else if (op == "b") {
try {
auto addr = splitPort(idstr);
if (not addr.first.empty() and addr.second.empty()){
std::stringstream ss;
ss << DHT_DEFAULT_PORT;
addr.second = ss.str();
}
dht.bootstrap(addr.first.c_str(), addr.second.c_str());
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
continue;
} else if (op == "log") {
params.log = !params.log;
if (params.log)
enableLogging(dht);
else
disableLogging(dht);
continue;
}
if (op.empty())
continue;
dht::InfoHash id {idstr};
static const std::set<std::string> VALID_OPS {"g", "l", "p", "s", "e", "a"};
if (VALID_OPS.find(op) == VALID_OPS.cend()) {
std::cout << "Unknown command: " << op << std::endl;
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl;
continue;
}
static constexpr dht::InfoHash INVALID_ID {};
if (id == INVALID_ID) {
std::cout << "Syntax error: invalid InfoHash." << std::endl;
continue;
}
auto start = std::chrono::high_resolution_clock::now();
if (op == "g") {
dht.get(id, [start](std::shared_ptr<Value> value) {
auto now = std::chrono::high_resolution_clock::now();
std::cout << "Get: found value (after " << print_dt(now-start) << "s)" << std::endl;
std::cout << "\t" << *value << std::endl;
return true;
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Get: " << (ok ? "completed" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "l") {
std::cout << id << std::endl;
dht.listen(id, [](std::shared_ptr<Value> value) {
std::cout << "Listen: found value:" << std::endl;
std::cout << "\t" << *value << std::endl;
return true;
});
}
else if (op == "p") {
std::string v;
iss >> v;
dht.put(id, dht::Value {
dht::ValueType::USER_DATA.id,
std::vector<uint8_t> {v.begin(), v.end()}
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "s") {
if (not params.generate_identity) {
print_id_req();
continue;
}
std::string v;
iss >> v;
dht.putSigned(id, dht::Value {
dht::ValueType::USER_DATA.id,
std::vector<uint8_t> {v.begin(), v.end()}
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Put signed: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "e") {
if (not params.generate_identity) {
print_id_req();
continue;
}
std::string tostr;
std::string v;
iss >> tostr >> v;
dht.putEncrypted(id, InfoHash(tostr), dht::Value {
dht::ValueType::USER_DATA.id,
std::vector<uint8_t> {v.begin(), v.end()}
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Put encrypted: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "a") {
in_port_t port;
iss >> port;
dht.put(id, dht::Value {dht::IpServiceAnnouncement::TYPE.id, dht::IpServiceAnnouncement(port)}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
}
std::cout << std::endl << "Stopping node..." << std::endl;
}
int
main(int argc, char **argv)
{
if (int rc = gnutls_global_init()) // TODO: remove with GnuTLS >= 3.3
throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
DhtRunner dht;
try {
auto params = parseArgs(argc, argv);
......@@ -78,191 +247,39 @@ main(int argc, char **argv)
print_usage();
return 0;
}
// TODO: remove with GnuTLS >= 3.3
int rc = gnutls_global_init();
if (rc != GNUTLS_E_SUCCESS)
throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
if (params.daemonize) {
daemonize();
}
dht::crypto::Identity crt {};
if (params.generate_identity) {
auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
}
dht.run(params.port, crt, true, params.is_bootstrap_node);
if (params.log)
enableLogging(dht);
if (params.log) {
if (not params.logfile.empty())
enableFileLogging(dht, params.logfile);
else
enableLogging(dht);
}
if (not params.bootstrap.first.empty()) {
std::cout << "Bootstrap: " << params.bootstrap.first << ":" << params.bootstrap.second << std::endl;
//std::cout << "Bootstrap: " << params.bootstrap.first << ":" << params.bootstrap.second << std::endl;
dht.bootstrap(params.bootstrap.first.c_str(), params.bootstrap.second.c_str());
}
print_node_info(dht, params);
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
// using the GNU History API
using_history();
while (true)
{
// using the GNU Readline API
std::string line = readLine();
if (!line.empty() && line[0] == '\0')
break;
std::istringstream iss(line);
std::string op, idstr, value;
iss >> op >> idstr;
if (op == "x" || op == "q" || op == "exit" || op == "quit") {
break;
} else if (op == "h" || op == "help") {
print_help();
continue;
} else if (op == "ll") {
print_node_info(dht, params);
unsigned good4, dubious4, cached4, incoming4;
unsigned good6, dubious6, cached6, incoming6;
dht.getNodesStats(AF_INET, &good4, &dubious4, &cached4, &incoming4);
dht.getNodesStats(AF_INET6, &good6, &dubious6, &cached6, &incoming6);
std::cout << "IPv4 nodes : " << good4 << " good, " << dubious4 << " dubious, " << incoming4 << " incoming." << std::endl;
std::cout << "IPv6 nodes : " << good6 << " good, " << dubious6 << " dubious, " << incoming6 << " incoming." << std::endl;
continue;
} else if (op == "lr") {
std::cout << "IPv4 routing table:" << std::endl;
std::cout << dht.getRoutingTablesLog(AF_INET) << std::endl;
std::cout << "IPv6 routing table:" << std::endl;
std::cout << dht.getRoutingTablesLog(AF_INET6) << std::endl;
continue;
} else if (op == "ld") {
std::cout << dht.getStorageLog() << std::endl;
continue;
} else if (op == "ls") {
std::cout << "Searches:" << std::endl;
std::cout << dht.getSearchesLog() << std::endl;
continue;
} else if (op == "la") {
std::cout << "Reported public addresses:" << std::endl;
auto addrs = dht.getPublicAddressStr();
for (const auto& addr : addrs)
std::cout << addr << std::endl;
continue;
} else if (op == "b") {
try {
auto addr = splitPort(idstr);
if (not addr.first.empty() and addr.second.empty()){
std::stringstream ss;
ss << DHT_DEFAULT_PORT;
addr.second = ss.str();
}
dht.bootstrap(addr.first.c_str(), addr.second.c_str());
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
continue;
} else if (op == "log") {
params.log = !params.log;
if (params.log)
enableLogging(dht);
else
disableLogging(dht);
continue;
}
if (op.empty())
continue;
dht::InfoHash id {idstr};
static const std::set<std::string> VALID_OPS {"g", "l", "p", "s", "e", "a"};
if (VALID_OPS.find(op) == VALID_OPS.cend()) {
std::cout << "Unknown command: " << op << std::endl;
std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl;
continue;
}
static constexpr dht::InfoHash INVALID_ID {};
if (id == INVALID_ID) {
std::cout << "Syntax error: invalid InfoHash." << std::endl;
continue;
}
auto start = std::chrono::high_resolution_clock::now();
if (op == "g") {
dht.get(id, [start](std::shared_ptr<Value> value) {
auto now = std::chrono::high_resolution_clock::now();
std::cout << "Get: found value (after " << print_dt(now-start) << "s)" << std::endl;
std::cout << "\t" << *value << std::endl;
return true;
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Get: " << (ok ? "completed" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "l") {
std::cout << id << std::endl;
dht.listen(id, [](std::shared_ptr<Value> value) {
std::cout << "Listen: found value:" << std::endl;
std::cout << "\t" << *value << std::endl;
return true;
});
}
else if (op == "p") {
std::string v;
iss >> v;
dht.put(id, dht::Value {
dht::ValueType::USER_DATA.id,
std::vector<uint8_t> {v.begin(), v.end()}
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Put: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "s") {
if (not params.generate_identity) {
print_id_req();
continue;
}
std::string v;
iss >> v;
dht.putSigned(id, dht::Value {
dht::ValueType::USER_DATA.id,
std::vector<uint8_t> {v.begin(), v.end()}
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Put signed: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "e") {
if (not params.generate_identity) {
print_id_req();
continue;
}
std::string tostr;
std::string v;
iss >> tostr >> v;
dht.putEncrypted(id, InfoHash(tostr), dht::Value {
dht::ValueType::USER_DATA.id,
std::vector<uint8_t> {v.begin(), v.end()}
}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Put encrypted: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
else if (op == "a") {
in_port_t port;
iss >> port;
dht.put(id, dht::Value {dht::IpServiceAnnouncement::TYPE.id, dht::IpServiceAnnouncement(port)}, [start](bool ok) {
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Announce: " << (ok ? "success" : "failure") << " (took " << print_dt(end-start) << "s)" << std::endl;
});
}
if (params.daemonize) {
while (true)
std::this_thread::sleep_for(std::chrono::seconds(30));
} else {
cmd_loop(dht, params);
}
std::cout << std::endl << "Stopping node..." << std::endl;
} catch(const std::exception&e) {
std::cout << std::endl << e.what() << std::endl;
std::cerr << std::endl << e.what() << std::endl;
}
std::condition_variable cv;
......@@ -276,7 +293,7 @@ main(int argc, char **argv)
cv.notify_all();
});
// wait for shutdown
// wait for shutdown
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [&](){ return done.load(); });
......
......@@ -20,16 +20,22 @@
// Common utility methods used by C++ OpenDHT tools.
#include <opendht.h>
#include <getopt.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <string>
#include <vector>
#include <chrono>
#include <iostream>
#include <sstream>
#include <opendht.h>
#include <getopt.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <fstream>
/**
* Terminal colors for logging
......@@ -87,6 +93,19 @@ enableLogging(dht::DhtRunner& dht)
);
}
void
enableFileLogging(dht::DhtRunner& dht, const std::string& path)
{
auto logfile = std::make_shared<std::fstream>();
logfile->open(path, std::ios::out);
dht.setLoggers(
[=](char const* m, va_list args){ printLog(*logfile, m, args); },
[=](char const* m, va_list args){ printLog(*logfile, m, args); },
[=](char const* m, va_list args){ printLog(*logfile, m, args); }
);
}
void
disableLogging(dht::DhtRunner& dht)
{
......@@ -130,9 +149,11 @@ static const constexpr in_port_t DHT_DEFAULT_PORT = 4222;
struct dht_params {
bool help {false}; // print help and exit
bool log {false};
std::string logfile {};
in_port_t port {0};
bool is_bootstrap_node {false};
bool generate_identity {false};
bool daemonize {false};
std::pair<std::string, std::string> bootstrap {};
};
......@@ -142,6 +163,7 @@ static const constexpr struct option long_options[] = {
{"bootstrap", optional_argument, nullptr, 'b'},
{"identity", no_argument , nullptr, 'i'},
{"verbose", no_argument , nullptr, 'v'},
{"daemonize", no_argument , nullptr, 'd'},
{nullptr, 0, nullptr, 0}
};
......@@ -149,7 +171,7 @@ dht_params
parseArgs(int argc, char **argv) {
dht_params params;
int opt;
while ((opt = getopt_long(argc, argv, ":hivp:b:", long_options, nullptr)) != -1) {
while ((opt = getopt_long(argc, argv, ":hidv:p:b:", long_options, nullptr)) != -1) {
switch (opt) {
case 'p': {
int port_arg = atoi(optarg);
......@@ -162,7 +184,7 @@ parseArgs(int argc, char **argv) {
case 'b':
if (optarg) {
params.bootstrap = splitPort((optarg[0] == '=') ? optarg+1 : optarg);
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty()){
if (not params.bootstrap.first.empty() and params.bootstrap.second.empty()) {
std::stringstream ss;
ss << DHT_DEFAULT_PORT;
params.bootstrap.second = ss.str();
......@@ -175,16 +197,24 @@ parseArgs(int argc, char **argv) {
params.help = true;
break;
case 'v':
if (optarg)
params.logfile = {optarg};
params.log = true;
break;
case 'i':
params.generate_identity = true;
break;
case 'd':
params.daemonize = true;
break;
case ':':
switch (optopt) {
case 'b':
params.is_bootstrap_node = true;
break;
case 'v':
params.log = true;
break;
default:
std::cout << "option requires an argument -- '" << optopt << '\'' << std::endl;
break;
......@@ -208,3 +238,43 @@ readLine(const char* prefix = PROMPT)
return line_read ? std::string(line_read) : std::string("\0", 1);
}
void signal_handler(int sig)
{
switch(sig) {
case SIGHUP:
break;
case SIGTERM:
exit(EXIT_SUCCESS);
break;
}
}
void daemonize()
{
pid_t pid = fork();
if (pid < 0) exit(EXIT_FAILURE);
if (pid > 0) exit(EXIT_SUCCESS);
umask(0);
pid_t sid = setsid();
if (sid < 0) {
exit(EXIT_FAILURE);
}
if ((chdir("/")) < 0) {
exit(EXIT_FAILURE);
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
signal(SIGCHLD,SIG_IGN); /* ignore child */
signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGHUP,signal_handler); /* catch hangup signal */
signal(SIGTERM,signal_handler); /* catch kill signal */
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment