Changeset View
Changeset View
Standalone View
Standalone View
src/seeder/main.cpp
#include <algorithm> | #include "bitcoin.h" | ||||
#include "db.h" | |||||
#define __STDC_FORMAT_MACROS | #include <algorithm> | ||||
#include <atomic> | #include <atomic> | ||||
#include <cstdio> | |||||
#include <cstdlib> | |||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <inttypes.h> | |||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include "bitcoin.h" | #define __STDC_FORMAT_MACROS | ||||
#include "db.h" | #include <inttypes.h> | ||||
using namespace std; | |||||
bool fTestNet = false; | bool fTestNet = false; | ||||
class CDnsSeedOpts { | class CDnsSeedOpts { | ||||
public: | public: | ||||
int nThreads; | int nThreads; | ||||
int nPort; | int nPort; | ||||
int nDnsThreads; | int nDnsThreads; | ||||
int fUseTestNet; | int fUseTestNet; | ||||
int fWipeBan; | int fWipeBan; | ||||
int fWipeIgnore; | int fWipeIgnore; | ||||
const char *mbox; | const char *mbox; | ||||
const char *ns; | const char *ns; | ||||
const char *host; | const char *host; | ||||
const char *tor; | const char *tor; | ||||
const char *ipv4_proxy; | const char *ipv4_proxy; | ||||
const char *ipv6_proxy; | const char *ipv6_proxy; | ||||
std::set<uint64_t> filter_whitelist; | std::set<uint64_t> filter_whitelist; | ||||
CDnsSeedOpts() | CDnsSeedOpts() | ||||
: nThreads(96), nDnsThreads(4), nPort(53), mbox(NULL), ns(NULL), | : nThreads(96), nDnsThreads(4), nPort(53), mbox(nullptr), ns(nullptr), | ||||
host(NULL), tor(NULL), fUseTestNet(false), fWipeBan(false), | host(nullptr), tor(nullptr), fUseTestNet(false), fWipeBan(false), | ||||
fWipeIgnore(false), ipv4_proxy(NULL), ipv6_proxy(NULL) {} | fWipeIgnore(false), ipv4_proxy(nullptr), ipv6_proxy(nullptr) {} | ||||
void ParseCommandLine(int argc, char **argv) { | void ParseCommandLine(int argc, char **argv) { | ||||
static const char *help = | static const char *help = | ||||
"Bitcoin-seeder\n" | "Bitcoin-seeder\n" | ||||
"Usage: %s -h <host> -n <ns> [-m <mbox>] [-t <threads>] [-p " | "Usage: %s -h <host> -n <ns> [-m <mbox>] [-t <threads>] [-p " | ||||
"<port>]\n" | "<port>]\n" | ||||
"\n" | "\n" | ||||
"Options:\n" | "Options:\n" | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | void ParseCommandLine(int argc, char **argv) { | ||||
} | } | ||||
case 'n': { | case 'n': { | ||||
ns = optarg; | ns = optarg; | ||||
break; | break; | ||||
} | } | ||||
case 't': { | case 't': { | ||||
int n = strtol(optarg, NULL, 10); | int n = strtol(optarg, nullptr, 10); | ||||
if (n > 0 && n < 1000) nThreads = n; | if (n > 0 && n < 1000) nThreads = n; | ||||
break; | break; | ||||
} | } | ||||
case 'd': { | case 'd': { | ||||
int n = strtol(optarg, NULL, 10); | int n = strtol(optarg, nullptr, 10); | ||||
if (n > 0 && n < 1000) nDnsThreads = n; | if (n > 0 && n < 1000) nDnsThreads = n; | ||||
break; | break; | ||||
} | } | ||||
case 'p': { | case 'p': { | ||||
int p = strtol(optarg, NULL, 10); | int p = strtol(optarg, nullptr, 10); | ||||
if (p > 0 && p < 65536) nPort = p; | if (p > 0 && p < 65536) nPort = p; | ||||
break; | break; | ||||
} | } | ||||
case 'o': { | case 'o': { | ||||
tor = optarg; | tor = optarg; | ||||
break; | break; | ||||
} | } | ||||
Show All 32 Lines | void ParseCommandLine(int argc, char **argv) { | ||||
filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH); | filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH); | ||||
filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | | filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | | ||||
NODE_BLOOM); | NODE_BLOOM); | ||||
filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | | filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | | ||||
NODE_XTHIN); | NODE_XTHIN); | ||||
filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | | filter_whitelist.insert(NODE_NETWORK | NODE_BITCOIN_CASH | | ||||
NODE_BLOOM | NODE_XTHIN); | NODE_BLOOM | NODE_XTHIN); | ||||
} | } | ||||
if (host != NULL && ns == NULL) showHelp = true; | if (host != nullptr && ns == nullptr) showHelp = true; | ||||
if (showHelp) fprintf(stderr, help, argv[0]); | if (showHelp) fprintf(stderr, help, argv[0]); | ||||
} | } | ||||
}; | }; | ||||
extern "C" { | extern "C" { | ||||
#include "dns.h" | #include "dns.h" | ||||
} | } | ||||
CAddrDb db; | CAddrDb db; | ||||
extern "C" void *ThreadCrawler(void *data) { | extern "C" void *ThreadCrawler(void *data) { | ||||
int *nThreads = (int *)data; | int *nThreads = (int *)data; | ||||
do { | do { | ||||
std::vector<CServiceResult> ips; | std::vector<CServiceResult> ips; | ||||
int wait = 5; | int wait = 5; | ||||
db.GetMany(ips, 16, wait); | db.GetMany(ips, 16, wait); | ||||
int64 now = time(NULL); | int64 now = time(nullptr); | ||||
if (ips.empty()) { | if (ips.empty()) { | ||||
wait *= 1000; | wait *= 1000; | ||||
wait += rand() % (500 * *nThreads); | wait += rand() % (500 * *nThreads); | ||||
Sleep(wait); | Sleep(wait); | ||||
continue; | continue; | ||||
} | } | ||||
vector<CAddress> addr; | std::vector<CAddress> addr; | ||||
for (int i = 0; i < ips.size(); i++) { | for (int i = 0; i < ips.size(); i++) { | ||||
CServiceResult &res = ips[i]; | CServiceResult &res = ips[i]; | ||||
res.nBanTime = 0; | res.nBanTime = 0; | ||||
res.nClientV = 0; | res.nClientV = 0; | ||||
res.nHeight = 0; | res.nHeight = 0; | ||||
res.strClientV = ""; | res.strClientV = ""; | ||||
bool getaddr = res.ourLastSuccess + 86400 < now; | bool getaddr = res.ourLastSuccess + 86400 < now; | ||||
res.fGood = | res.fGood = TestNode(res.service, res.nBanTime, res.nClientV, | ||||
TestNode(res.service, res.nBanTime, res.nClientV, | res.strClientV, res.nHeight, | ||||
res.strClientV, res.nHeight, getaddr ? &addr : NULL); | getaddr ? &addr : nullptr); | ||||
} | } | ||||
db.ResultMany(ips); | db.ResultMany(ips); | ||||
db.Add(addr); | db.Add(addr); | ||||
} while (1); | } while (1); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
extern "C" int GetIPList(void *thread, char *requestedHostname, addr_t *addr, | extern "C" int GetIPList(void *thread, char *requestedHostname, addr_t *addr, | ||||
Show All 16 Lines | public: | ||||
std::set<uint64_t> filterWhitelist; | std::set<uint64_t> filterWhitelist; | ||||
void cacheHit(uint64_t requestedFlags, bool force = false) { | void cacheHit(uint64_t requestedFlags, bool force = false) { | ||||
static bool nets[NET_MAX] = {}; | static bool nets[NET_MAX] = {}; | ||||
if (!nets[NET_IPV4]) { | if (!nets[NET_IPV4]) { | ||||
nets[NET_IPV4] = true; | nets[NET_IPV4] = true; | ||||
nets[NET_IPV6] = true; | nets[NET_IPV6] = true; | ||||
} | } | ||||
time_t now = time(NULL); | time_t now = time(nullptr); | ||||
FlagSpecificData &thisflag = perflag[requestedFlags]; | FlagSpecificData &thisflag = perflag[requestedFlags]; | ||||
thisflag.cacheHits++; | thisflag.cacheHits++; | ||||
if (force || | if (force || | ||||
thisflag.cacheHits * 400 > | thisflag.cacheHits * 400 > | ||||
(thisflag.cache.size() * thisflag.cache.size()) || | (thisflag.cache.size() * thisflag.cache.size()) || | ||||
(thisflag.cacheHits * thisflag.cacheHits * 20 > | (thisflag.cacheHits * thisflag.cacheHits * 20 > | ||||
thisflag.cache.size() && | thisflag.cache.size() && | ||||
(now - thisflag.cacheTime > 5))) { | (now - thisflag.cacheTime > 5))) { | ||||
set<CNetAddr> ips; | std::set<CNetAddr> ips; | ||||
db.GetIPs(ips, requestedFlags, 1000, nets); | db.GetIPs(ips, requestedFlags, 1000, nets); | ||||
dbQueries++; | dbQueries++; | ||||
thisflag.cache.clear(); | thisflag.cache.clear(); | ||||
thisflag.nIPv4 = 0; | thisflag.nIPv4 = 0; | ||||
thisflag.nIPv6 = 0; | thisflag.nIPv6 = 0; | ||||
thisflag.cache.reserve(ips.size()); | thisflag.cache.reserve(ips.size()); | ||||
for (set<CNetAddr>::iterator it = ips.begin(); it != ips.end(); | for (auto &ip : ips) { | ||||
it++) { | |||||
struct in_addr addr; | struct in_addr addr; | ||||
struct in6_addr addr6; | struct in6_addr addr6; | ||||
if ((*it).GetInAddr(&addr)) { | if (ip.GetInAddr(&addr)) { | ||||
addr_t a; | addr_t a; | ||||
a.v = 4; | a.v = 4; | ||||
memcpy(&a.data.v4, &addr, 4); | memcpy(&a.data.v4, &addr, 4); | ||||
thisflag.cache.push_back(a); | thisflag.cache.push_back(a); | ||||
thisflag.nIPv4++; | thisflag.nIPv4++; | ||||
} else if ((*it).GetIn6Addr(&addr6)) { | } else if (ip.GetIn6Addr(&addr6)) { | ||||
addr_t a; | addr_t a; | ||||
a.v = 6; | a.v = 6; | ||||
memcpy(&a.data.v6, &addr6, 16); | memcpy(&a.data.v6, &addr6, 16); | ||||
thisflag.cache.push_back(a); | thisflag.cache.push_back(a); | ||||
thisflag.nIPv6++; | thisflag.nIPv6++; | ||||
} | } | ||||
} | } | ||||
thisflag.cacheHits = 0; | thisflag.cacheHits = 0; | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | while (i < max) { | ||||
addr[i] = thisflag.cache[j]; | addr[i] = thisflag.cache[j]; | ||||
thisflag.cache[j] = thisflag.cache[i]; | thisflag.cache[j] = thisflag.cache[i]; | ||||
thisflag.cache[i] = addr[i]; | thisflag.cache[i] = addr[i]; | ||||
i++; | i++; | ||||
} | } | ||||
return max; | return max; | ||||
} | } | ||||
vector<CDnsThread *> dnsThread; | std::vector<CDnsThread *> dnsThread; | ||||
extern "C" void *ThreadDNS(void *arg) { | extern "C" void *ThreadDNS(void *arg) { | ||||
CDnsThread *thread = (CDnsThread *)arg; | CDnsThread *thread = (CDnsThread *)arg; | ||||
thread->run(); | thread->run(); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
int StatCompare(const CAddrReport &a, const CAddrReport &b) { | int StatCompare(const CAddrReport &a, const CAddrReport &b) { | ||||
if (a.uptime[4] == b.uptime[4]) { | if (a.uptime[4] == b.uptime[4]) { | ||||
if (a.uptime[3] == b.uptime[3]) { | if (a.uptime[3] == b.uptime[3]) { | ||||
return a.clientVersion > b.clientVersion; | return a.clientVersion > b.clientVersion; | ||||
} else { | } else { | ||||
return a.uptime[3] > b.uptime[3]; | return a.uptime[3] > b.uptime[3]; | ||||
} | } | ||||
} else { | } else { | ||||
return a.uptime[4] > b.uptime[4]; | return a.uptime[4] > b.uptime[4]; | ||||
} | } | ||||
} | } | ||||
extern "C" void *ThreadDumper(void *) { | extern "C" void *ThreadDumper(void *) { | ||||
int count = 0; | int count = 0; | ||||
do { | do { | ||||
Sleep(100000 << count); // First 100s, than 200s, 400s, 800s, 1600s, and | // First 100s, than 200s, 400s, 800s, 1600s, and then 3200s forever | ||||
// then 3200s forever | Sleep(100000 << count); | ||||
if (count < 5) count++; | if (count < 5) { | ||||
count++; | |||||
} | |||||
{ | { | ||||
vector<CAddrReport> v = db.GetAll(); | std::vector<CAddrReport> v = db.GetAll(); | ||||
sort(v.begin(), v.end(), StatCompare); | sort(v.begin(), v.end(), StatCompare); | ||||
FILE *f = fopen("dnsseed.dat.new", "w+"); | FILE *f = fopen("dnsseed.dat.new", "w+"); | ||||
if (f) { | if (f) { | ||||
{ | { | ||||
CAutoFile cf(f); | CAutoFile cf(f); | ||||
cf << db; | cf << db; | ||||
} | } | ||||
rename("dnsseed.dat.new", "dnsseed.dat"); | rename("dnsseed.dat.new", "dnsseed.dat"); | ||||
} | } | ||||
FILE *d = fopen("dnsseed.dump", "w"); | FILE *d = fopen("dnsseed.dump", "w"); | ||||
fprintf(d, "# address good " | fprintf(d, "# address good " | ||||
"lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " | "lastSuccess %%(2h) %%(8h) %%(1d) %%(7d) " | ||||
"%%(30d) blocks svcs version\n"); | "%%(30d) blocks svcs version\n"); | ||||
double stat[5] = {0, 0, 0, 0, 0}; | double stat[5] = {0, 0, 0, 0, 0}; | ||||
for (vector<CAddrReport>::const_iterator it = v.begin(); | for (CAddrReport rep : v) { | ||||
it < v.end(); it++) { | |||||
CAddrReport rep = *it; | |||||
fprintf( | fprintf( | ||||
d, | d, | ||||
"%-47s %4d %11" PRId64 | "%-47s %4d %11" PRId64 | ||||
" %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 | " %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6.2f%% %6i %08" PRIx64 | ||||
" %5i \"%s\"\n", | " %5i \"%s\"\n", | ||||
rep.ip.ToString().c_str(), (int)rep.fGood, rep.lastSuccess, | rep.ip.ToString().c_str(), (int)rep.fGood, rep.lastSuccess, | ||||
100.0 * rep.uptime[0], 100.0 * rep.uptime[1], | 100.0 * rep.uptime[0], 100.0 * rep.uptime[1], | ||||
100.0 * rep.uptime[2], 100.0 * rep.uptime[3], | 100.0 * rep.uptime[2], 100.0 * rep.uptime[3], | ||||
100.0 * rep.uptime[4], rep.blocks, rep.services, | 100.0 * rep.uptime[4], rep.blocks, rep.services, | ||||
rep.clientVersion, rep.clientSubVersion.c_str()); | rep.clientVersion, rep.clientSubVersion.c_str()); | ||||
stat[0] += rep.uptime[0]; | stat[0] += rep.uptime[0]; | ||||
stat[1] += rep.uptime[1]; | stat[1] += rep.uptime[1]; | ||||
stat[2] += rep.uptime[2]; | stat[2] += rep.uptime[2]; | ||||
stat[3] += rep.uptime[3]; | stat[3] += rep.uptime[3]; | ||||
stat[4] += rep.uptime[4]; | stat[4] += rep.uptime[4]; | ||||
} | } | ||||
fclose(d); | fclose(d); | ||||
FILE *ff = fopen("dnsstats.log", "a"); | FILE *ff = fopen("dnsstats.log", "a"); | ||||
fprintf(ff, "%llu %g %g %g %g %g\n", | fprintf(ff, "%llu %g %g %g %g %g\n", | ||||
(unsigned long long)(time(NULL)), stat[0], stat[1], stat[2], | (unsigned long long)(time(nullptr)), stat[0], stat[1], | ||||
stat[3], stat[4]); | stat[2], stat[3], stat[4]); | ||||
fclose(ff); | fclose(ff); | ||||
} | } | ||||
} while (1); | } while (1); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
extern "C" void *ThreadStats(void *) { | extern "C" void *ThreadStats(void *) { | ||||
bool first = true; | bool first = true; | ||||
do { | do { | ||||
char c[256]; | char c[256]; | ||||
time_t tim = time(NULL); | time_t tim = time(nullptr); | ||||
struct tm *tmp = localtime(&tim); | struct tm *tmp = localtime(&tim); | ||||
strftime(c, 256, "[%y-%m-%d %H:%M:%S]", tmp); | strftime(c, 256, "[%y-%m-%d %H:%M:%S]", tmp); | ||||
CAddrDbStats stats; | CAddrDbStats stats; | ||||
db.GetStats(stats); | db.GetStats(stats); | ||||
if (first) { | if (first) { | ||||
first = false; | first = false; | ||||
printf("\n\n\n\x1b[3A"); | printf("\n\n\n\x1b[3A"); | ||||
} else | } else | ||||
Show All 11 Lines | do { | ||||
stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, | stats.nNew, stats.nAvail - stats.nTracked - stats.nNew, | ||||
stats.nBanned, (unsigned long long)requests, | stats.nBanned, (unsigned long long)requests, | ||||
(unsigned long long)queries); | (unsigned long long)queries); | ||||
Sleep(1000); | Sleep(1000); | ||||
} while (1); | } while (1); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
static const string mainnet_seeds[] = { | static const std::string mainnet_seeds[] = { | ||||
"seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", "seed.bitprim.org", | "seed.bitcoinabc.org", "seed-abc.bitcoinforks.org", "seed.bitprim.org", | ||||
"seed.deadalnix.me", "seeder.criptolayer.net", ""}; | "seed.deadalnix.me", "seeder.criptolayer.net", ""}; | ||||
static const string testnet_seeds[] = {"testnet-seed.bitcoinabc.org", | static const std::string testnet_seeds[] = { | ||||
"testnet-seed.bitcoinabc.org", | |||||
"testnet-seed-abc.bitcoinforks.org", | "testnet-seed-abc.bitcoinforks.org", | ||||
"testnet-seed.bitcoinunlimited.info", | "testnet-seed.bitcoinunlimited.info", | ||||
"testnet-seed.bitprim.org", | "testnet-seed.bitprim.org", | ||||
"testnet-seed.deadalnix.me", | "testnet-seed.deadalnix.me", | ||||
"testnet-seeder.criptolayer.net", | "testnet-seeder.criptolayer.net", | ||||
""}; | ""}; | ||||
static const string *seeds = mainnet_seeds; | static const std::string *seeds = mainnet_seeds; | ||||
extern "C" void *ThreadSeeder(void *) { | extern "C" void *ThreadSeeder(void *) { | ||||
if (!fTestNet) { | if (!fTestNet) { | ||||
db.Add(CService("kjy2eqzk4zwi5zd3.onion", 8333), true); | db.Add(CService("kjy2eqzk4zwi5zd3.onion", 8333), true); | ||||
} | } | ||||
do { | do { | ||||
for (int i = 0; seeds[i] != ""; i++) { | for (int i = 0; seeds[i] != ""; i++) { | ||||
vector<CNetAddr> ips; | std::vector<CNetAddr> ips; | ||||
LookupHost(seeds[i].c_str(), ips); | LookupHost(seeds[i].c_str(), ips); | ||||
for (vector<CNetAddr>::iterator it = ips.begin(); it != ips.end(); | for (auto &ip : ips) { | ||||
it++) { | db.Add(CService(ip, GetDefaultPort()), true); | ||||
db.Add(CService(*it, GetDefaultPort()), true); | |||||
} | } | ||||
} | } | ||||
Sleep(1800000); | Sleep(1800000); | ||||
} while (1); | } while (1); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
signal(SIGPIPE, SIG_IGN); | signal(SIGPIPE, SIG_IGN); | ||||
setbuf(stdout, NULL); | setbuf(stdout, nullptr); | ||||
CDnsSeedOpts opts; | CDnsSeedOpts opts; | ||||
opts.ParseCommandLine(argc, argv); | opts.ParseCommandLine(argc, argv); | ||||
printf("Supporting whitelisted filters: "); | printf("Supporting whitelisted filters: "); | ||||
for (std::set<uint64_t>::const_iterator it = opts.filter_whitelist.begin(); | for (std::set<uint64_t>::const_iterator it = opts.filter_whitelist.begin(); | ||||
it != opts.filter_whitelist.end(); it++) { | it != opts.filter_whitelist.end(); it++) { | ||||
if (it != opts.filter_whitelist.begin()) { | if (it != opts.filter_whitelist.begin()) { | ||||
printf(","); | printf(","); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | int main(int argc, char **argv) { | ||||
} | } | ||||
pthread_t threadDns, threadSeed, threadDump, threadStats; | pthread_t threadDns, threadSeed, threadDump, threadStats; | ||||
if (fDNS) { | if (fDNS) { | ||||
printf("Starting %i DNS threads for %s on %s (port %i)...", | printf("Starting %i DNS threads for %s on %s (port %i)...", | ||||
opts.nDnsThreads, opts.host, opts.ns, opts.nPort); | opts.nDnsThreads, opts.host, opts.ns, opts.nPort); | ||||
dnsThread.clear(); | dnsThread.clear(); | ||||
for (int i = 0; i < opts.nDnsThreads; i++) { | for (int i = 0; i < opts.nDnsThreads; i++) { | ||||
dnsThread.push_back(new CDnsThread(&opts, i)); | dnsThread.push_back(new CDnsThread(&opts, i)); | ||||
pthread_create(&threadDns, NULL, ThreadDNS, dnsThread[i]); | pthread_create(&threadDns, nullptr, ThreadDNS, dnsThread[i]); | ||||
printf("."); | printf("."); | ||||
Sleep(20); | Sleep(20); | ||||
} | } | ||||
printf("done\n"); | printf("done\n"); | ||||
} | } | ||||
printf("Starting seeder..."); | printf("Starting seeder..."); | ||||
pthread_create(&threadSeed, NULL, ThreadSeeder, NULL); | pthread_create(&threadSeed, nullptr, ThreadSeeder, nullptr); | ||||
printf("done\n"); | printf("done\n"); | ||||
printf("Starting %i crawler threads...", opts.nThreads); | printf("Starting %i crawler threads...", opts.nThreads); | ||||
pthread_attr_t attr_crawler; | pthread_attr_t attr_crawler; | ||||
pthread_attr_init(&attr_crawler); | pthread_attr_init(&attr_crawler); | ||||
pthread_attr_setstacksize(&attr_crawler, 0x20000); | pthread_attr_setstacksize(&attr_crawler, 0x20000); | ||||
for (int i = 0; i < opts.nThreads; i++) { | for (int i = 0; i < opts.nThreads; i++) { | ||||
pthread_t thread; | pthread_t thread; | ||||
pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); | pthread_create(&thread, &attr_crawler, ThreadCrawler, &opts.nThreads); | ||||
} | } | ||||
pthread_attr_destroy(&attr_crawler); | pthread_attr_destroy(&attr_crawler); | ||||
printf("done\n"); | printf("done\n"); | ||||
pthread_create(&threadStats, NULL, ThreadStats, NULL); | pthread_create(&threadStats, nullptr, ThreadStats, nullptr); | ||||
pthread_create(&threadDump, NULL, ThreadDumper, NULL); | pthread_create(&threadDump, nullptr, ThreadDumper, nullptr); | ||||
void *res; | void *res; | ||||
pthread_join(threadDump, &res); | pthread_join(threadDump, &res); | ||||
return 0; | return 0; | ||||
} | } |