From 439b7d966cfc819ae76f65d60b5850a226f45933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com> Date: Sat, 19 Dec 2015 19:40:20 -0500 Subject: [PATCH] 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. --- tools/dhtnode.cpp | 357 ++++++++++++++++++++++--------------------- tools/tools_common.h | 84 +++++++++- 2 files changed, 264 insertions(+), 177 deletions(-) diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp index 66808cb9..097a8f05 100644 --- a/tools/dhtnode.cpp +++ b/tools/dhtnode.cpp @@ -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(); }); diff --git a/tools/tools_common.h b/tools/tools_common.h index b2dbf89c..c8fcab41 100644 --- a/tools/tools_common.h +++ b/tools/tools_common.h @@ -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 */ +} -- GitLab