1
0

main.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. #define _POSIX_C_SOURCE 200112L
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <stdint.h>
  6. #include <stdbool.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include <pthread.h>
  10. #include <signal.h>
  11. #include <sodium/core.h>
  12. #include <sodium/randombytes.h>
  13. #ifdef PASSPHRASE
  14. #include <sodium/crypto_pwhash.h>
  15. #endif
  16. #include <sodium/utils.h>
  17. #include "types.h"
  18. #include "vec.h"
  19. #include "base32.h"
  20. #include "cpucount.h"
  21. #include "keccak.h"
  22. #include "ioutil.h"
  23. #include "common.h"
  24. #include "yaml.h"
  25. #include "filters.h"
  26. #include "worker.h"
  27. #include "likely.h"
  28. #ifndef _WIN32
  29. #define FSZ "%zu"
  30. #else
  31. #define FSZ "%Iu"
  32. #endif
  33. // Argon2 hashed passphrase stretching settings
  34. // NOTE: changing these will break compatibility
  35. #define PWHASH_OPSLIMIT 48
  36. #define PWHASH_MEMLIMIT 64 * 1024 * 1024
  37. #define PWHASH_ALG crypto_pwhash_ALG_ARGON2ID13
  38. static int quietflag = 0;
  39. static int verboseflag = 0;
  40. #ifndef PCRE2FILTER
  41. static int wantdedup = 0;
  42. #endif
  43. // 0, direndpos, onionendpos
  44. // printstartpos = either 0 or direndpos
  45. // printlen = either onionendpos + 1 or ONION_LEN + 1 (additional 1 is for newline)
  46. size_t onionendpos; // end of .onion within string
  47. size_t direndpos; // end of dir before .onion within string
  48. size_t printstartpos; // where to start printing from
  49. size_t printlen; // precalculated, related to printstartpos
  50. pthread_mutex_t fout_mutex;
  51. FILE *fout;
  52. #ifdef PASSPHRASE
  53. u8 orig_determseed[SEED_LEN];
  54. const char *checkpointfile = 0;
  55. #endif
  56. static void termhandler(int sig)
  57. {
  58. switch (sig) {
  59. case SIGTERM:
  60. case SIGINT:
  61. endwork = 1;
  62. break;
  63. }
  64. }
  65. #ifdef STATISTICS
  66. struct tstatstruct {
  67. u64 numcalc;
  68. u64 numsuccess;
  69. u64 numrestart;
  70. u32 oldnumcalc;
  71. u32 oldnumsuccess;
  72. u32 oldnumrestart;
  73. } ;
  74. VEC_STRUCT(tstatsvec,struct tstatstruct);
  75. #endif
  76. static void printhelp(FILE *out,const char *progname)
  77. {
  78. fprintf(out,
  79. // 1 2 3 4 5 6 7
  80. //1234567890123456789012345678901234567890123456789012345678901234567890123456789
  81. "Usage: %s FILTER [FILTER...] [OPTION]\n"
  82. " %s -f FILTERFILE [OPTION]\n"
  83. "Options:\n"
  84. " -f FILTERFILE specify filter file which contains filters separated\n"
  85. " by newlines.\n"
  86. " -D deduplicate filters.\n"
  87. " -q do not print diagnostic output to stderr.\n"
  88. " -x do not print onion names.\n"
  89. " -v print more diagnostic data.\n"
  90. " -o FILENAME output onion names to specified file (append).\n"
  91. " -O FILENAME output onion names to specified file (overwrite).\n"
  92. " -F include directory names in onion names output.\n"
  93. " -d DIRNAME output directory.\n"
  94. " -t NUMTHREADS specify number of threads to utilise\n"
  95. " (default - try detecting CPU core count).\n"
  96. " -j NUMTHREADS same as -t.\n"
  97. " -n NUMKEYS specify number of keys (default - 0 - unlimited).\n"
  98. " -N NUMWORDS specify number of words per key (default - 1).\n"
  99. " -Z deprecated, does nothing.\n"
  100. " -z deprecated, does nothing.\n"
  101. " -B use batching key generation method (current default).\n"
  102. " -s print statistics each 10 seconds.\n"
  103. " -S SECONDS print statistics every specified amount of seconds.\n"
  104. " -T do not reset statistics counters when printing.\n"
  105. " -y output generated keys in YAML format instead of\n"
  106. " dumping them to filesystem.\n"
  107. " -Y [FILENAME [host.onion]]\n"
  108. " parse YAML encoded input and extract key(s) to\n"
  109. " filesystem.\n"
  110. #ifdef PASSPHRASE
  111. " -p PASSPHRASE use passphrase to initialize the random seed with.\n"
  112. " -P same as -p, but takes passphrase from PASSPHRASE\n"
  113. " environment variable.\n"
  114. " --checkpoint filename\n"
  115. " load/save checkpoint of progress to specified file\n"
  116. " (requires passphrase).\n"
  117. " --skipnear skip near passphrase keys; you probably want this\n"
  118. " because of improved safety unless you're trying to\n"
  119. " regenerate an old key; possible future default.\n"
  120. " --warnnear print warning about passphrase key being near another\n"
  121. " (safety hazard); prefer --skipnear to this unless\n"
  122. " you're regenerating an old key.\n"
  123. #endif
  124. " --rawyaml raw (unprefixed) public/secret keys for -y/-Y\n"
  125. " (may be useful for tor controller API).\n"
  126. " -h, --help, --usage print help to stdout and quit.\n"
  127. " -V, --version print version information to stdout and exit.\n"
  128. ,progname,progname);
  129. fflush(out);
  130. }
  131. static void printversion(void)
  132. {
  133. fprintf(stdout,"mkp224o " VERSION "\n");
  134. fflush(stdout);
  135. }
  136. static void e_additional(void)
  137. {
  138. fprintf(stderr,"additional argument required\n");
  139. exit(1);
  140. }
  141. #ifndef STATISTICS
  142. static void e_nostatistics(void)
  143. {
  144. fprintf(stderr,"statistics support not compiled in\n");
  145. exit(1);
  146. }
  147. #endif
  148. static void setworkdir(const char *wd)
  149. {
  150. free(workdir);
  151. size_t l = strlen(wd);
  152. if (!l) {
  153. workdir = 0;
  154. workdirlen = 0;
  155. if (!quietflag)
  156. fprintf(stderr,"unset workdir\n");
  157. return;
  158. }
  159. unsigned needslash = 0;
  160. if (wd[l-1] != '/')
  161. needslash = 1;
  162. char *s = (char *) malloc(l + needslash + 1);
  163. if (!s)
  164. abort();
  165. memcpy(s,wd,l);
  166. if (needslash)
  167. s[l++] = '/';
  168. s[l] = 0;
  169. workdir = s;
  170. workdirlen = l;
  171. if (!quietflag)
  172. fprintf(stderr,"set workdir: %s\n",workdir);
  173. }
  174. #ifdef PASSPHRASE
  175. static void setpassphrase(const char *pass)
  176. {
  177. static u8 salt[crypto_pwhash_SALTBYTES] = {0};
  178. fprintf(stderr,"expanding passphrase (may take a while)...");
  179. if (crypto_pwhash(determseed,sizeof(determseed),
  180. pass,strlen(pass),salt,
  181. PWHASH_OPSLIMIT,PWHASH_MEMLIMIT,PWHASH_ALG) != 0)
  182. {
  183. fprintf(stderr," out of memory!\n");
  184. exit(1);
  185. }
  186. fprintf(stderr," done.\n");
  187. }
  188. static void savecheckpoint(void)
  189. {
  190. u8 checkpoint[SEED_LEN];
  191. bool carry = 0;
  192. pthread_mutex_lock(&determseed_mutex);
  193. for (int i = 0; i < SEED_LEN; i++) {
  194. checkpoint[i] = determseed[i] - orig_determseed[i] - carry;
  195. carry = checkpoint[i] > determseed[i];
  196. }
  197. pthread_mutex_unlock(&determseed_mutex);
  198. if (syncwrite(checkpointfile,1,checkpoint,SEED_LEN) < 0) {
  199. pthread_mutex_lock(&fout_mutex);
  200. fprintf(stderr,"ERROR: could not save checkpoint to \"%s\"\n",checkpointfile);
  201. pthread_mutex_unlock(&fout_mutex);
  202. }
  203. }
  204. static volatile int checkpointer_endwork = 0;
  205. static void *checkpointworker(void *arg)
  206. {
  207. (void) arg;
  208. struct timespec ts;
  209. memset(&ts,0,sizeof(ts));
  210. ts.tv_nsec = 100000000;
  211. struct timespec nowtime;
  212. u64 ilasttime,inowtime;
  213. clock_gettime(CLOCK_MONOTONIC,&nowtime);
  214. ilasttime = (1000000 * (u64)nowtime.tv_sec) + ((u64)nowtime.tv_nsec / 1000);
  215. while (!unlikely(checkpointer_endwork)) {
  216. clock_gettime(CLOCK_MONOTONIC,&nowtime);
  217. inowtime = (1000000 * (u64)nowtime.tv_sec) + ((u64)nowtime.tv_nsec / 1000);
  218. if ((i64)(inowtime - ilasttime) >= 300 * 1000000 /* 5 minutes */) {
  219. savecheckpoint();
  220. ilasttime = inowtime;
  221. }
  222. }
  223. savecheckpoint();
  224. return 0;
  225. }
  226. #endif
  227. VEC_STRUCT(threadvec,pthread_t);
  228. #include "filters_inc.inc.h"
  229. #include "filters_main.inc.h"
  230. enum worker_type {
  231. WT_BATCH,
  232. };
  233. int main(int argc,char **argv)
  234. {
  235. const char *outfile = 0;
  236. const char *infile = 0;
  237. const char *onehostname = 0;
  238. const char *arg;
  239. int ignoreargs = 0;
  240. int dirnameflag = 0;
  241. int numthreads = 0;
  242. enum worker_type wt = WT_BATCH;
  243. int yamlinput = 0;
  244. #ifdef PASSPHRASE
  245. int deterministic = 0;
  246. #endif
  247. int outfileoverwrite = 0;
  248. struct threadvec threads;
  249. #ifdef STATISTICS
  250. struct statsvec stats;
  251. struct tstatsvec tstats;
  252. u64 reportdelay = 0;
  253. int realtimestats = 1;
  254. #endif
  255. int tret;
  256. if (sodium_init() < 0) {
  257. fprintf(stderr,"sodium_init() failed\n");
  258. return 1;
  259. }
  260. worker_init();
  261. filters_init();
  262. setvbuf(stderr,0,_IONBF,0);
  263. fout = stdout;
  264. const char *progname = argv[0];
  265. if (argc <= 1) {
  266. printhelp(stderr,progname);
  267. exit(1);
  268. }
  269. argc--; argv++;
  270. while (argc--) {
  271. arg = *argv++;
  272. if (!ignoreargs && *arg == '-') {
  273. int numargit = 0;
  274. nextarg:
  275. ++arg;
  276. ++numargit;
  277. if (*arg == '-') {
  278. if (numargit > 1) {
  279. fprintf(stderr,"unrecognised argument: -\n");
  280. exit(1);
  281. }
  282. ++arg;
  283. if (!*arg)
  284. ignoreargs = 1;
  285. else if (!strcmp(arg,"help") || !strcmp(arg,"usage")) {
  286. printhelp(stdout,progname);
  287. exit(0);
  288. }
  289. else if (!strcmp(arg,"version")) {
  290. printversion();
  291. exit(0);
  292. }
  293. else if (!strcmp(arg,"rawyaml"))
  294. yamlraw = 1;
  295. #ifdef PASSPHRASE
  296. else if (!strcmp(arg,"checkpoint")) {
  297. if (argc--)
  298. checkpointfile = *argv++;
  299. else
  300. e_additional();
  301. }
  302. else if (!strcmp(arg,"skipnear")) {
  303. pw_skipnear = 1;
  304. pw_warnnear = 0;
  305. }
  306. else if (!strcmp(arg,"warnnear")) {
  307. pw_warnnear = 1;
  308. pw_skipnear = 0;
  309. }
  310. #endif // PASSPHRASE
  311. else {
  312. fprintf(stderr,"unrecognised argument: --%s\n",arg);
  313. exit(1);
  314. }
  315. numargit = 0;
  316. }
  317. else if (*arg == 0) {
  318. if (numargit == 1)
  319. ignoreargs = 1;
  320. continue;
  321. }
  322. else if (*arg == 'h') {
  323. printhelp(stdout,progname);
  324. exit(0);
  325. }
  326. else if (*arg == 'V') {
  327. printversion();
  328. exit(0);
  329. }
  330. else if (*arg == 'f') {
  331. if (argc--) {
  332. if (!loadfilterfile(*argv++))
  333. exit(1);
  334. }
  335. else
  336. e_additional();
  337. }
  338. else if (*arg == 'D') {
  339. #ifndef PCRE2FILTER
  340. wantdedup = 1;
  341. #else
  342. fprintf(stderr,"WARNING: deduplication isn't supported with regex filters\n");
  343. #endif
  344. }
  345. else if (*arg == 'q')
  346. ++quietflag;
  347. else if (*arg == 'x')
  348. fout = 0;
  349. else if (*arg == 'v')
  350. verboseflag = 1;
  351. else if (*arg == 'o') {
  352. outfileoverwrite = 0;
  353. if (argc--)
  354. outfile = *argv++;
  355. else
  356. e_additional();
  357. }
  358. else if (*arg == 'O') {
  359. outfileoverwrite = 1;
  360. if (argc--)
  361. outfile = *argv++;
  362. else
  363. e_additional();
  364. }
  365. else if (*arg == 'F')
  366. dirnameflag = 1;
  367. else if (*arg == 'd') {
  368. if (argc--)
  369. setworkdir(*argv++);
  370. else
  371. e_additional();
  372. }
  373. else if (*arg == 't' || *arg == 'j') {
  374. if (argc--)
  375. numthreads = atoi(*argv++);
  376. else
  377. e_additional();
  378. }
  379. else if (*arg == 'n') {
  380. if (argc--)
  381. numneedgenerate = (size_t)atoll(*argv++);
  382. else
  383. e_additional();
  384. }
  385. else if (*arg == 'N') {
  386. if (argc--)
  387. numwords = atoi(*argv++);
  388. else
  389. e_additional();
  390. }
  391. else if (*arg == 'Z')
  392. /* ignored */ ;
  393. else if (*arg == 'z')
  394. /* ignored */ ;
  395. else if (*arg == 'B')
  396. wt = WT_BATCH;
  397. else if (*arg == 's') {
  398. #ifdef STATISTICS
  399. reportdelay = 10000000;
  400. #else
  401. e_nostatistics();
  402. #endif
  403. }
  404. else if (*arg == 'S') {
  405. #ifdef STATISTICS
  406. if (argc--)
  407. reportdelay = (u64)atoll(*argv++) * 1000000;
  408. else
  409. e_additional();
  410. #else
  411. e_nostatistics();
  412. #endif
  413. }
  414. else if (*arg == 'T') {
  415. #ifdef STATISTICS
  416. realtimestats = 0;
  417. #else
  418. e_nostatistics();
  419. #endif
  420. }
  421. else if (*arg == 'y')
  422. yamloutput = 1;
  423. else if (*arg == 'Y') {
  424. yamlinput = 1;
  425. if (argc) {
  426. --argc;
  427. infile = *argv++;
  428. if (!*infile)
  429. infile = 0;
  430. if (argc) {
  431. --argc;
  432. onehostname = *argv++;
  433. if (!*onehostname)
  434. onehostname = 0;
  435. if (onehostname && strlen(onehostname) != ONION_LEN) {
  436. fprintf(stderr,"bad onion argument length\n");
  437. exit(1);
  438. }
  439. }
  440. }
  441. }
  442. #ifdef PASSPHRASE
  443. else if (*arg == 'p') {
  444. if (argc--) {
  445. setpassphrase(*argv++);
  446. deterministic = 1;
  447. }
  448. else
  449. e_additional();
  450. }
  451. else if (*arg == 'P') {
  452. const char *pass = getenv("PASSPHRASE");
  453. if (!pass) {
  454. fprintf(stderr,"store passphrase in PASSPHRASE environment variable\n");
  455. exit(1);
  456. }
  457. setpassphrase(pass);
  458. deterministic = 1;
  459. }
  460. #endif // PASSPHRASE
  461. else {
  462. fprintf(stderr,"unrecognised argument: -%c\n",*arg);
  463. exit(1);
  464. }
  465. if (numargit)
  466. goto nextarg;
  467. }
  468. else
  469. filters_add(arg);
  470. }
  471. if (yamlinput && yamloutput) {
  472. fprintf(stderr,"both -y and -Y does not make sense\n");
  473. exit(1);
  474. }
  475. if (yamlraw && !yamlinput && !yamloutput) {
  476. fprintf(stderr,"--rawyaml requires either -y or -Y to do anything\n");
  477. exit(1);
  478. }
  479. #ifdef PASSPHRASE
  480. if (checkpointfile && !deterministic) {
  481. fprintf(stderr,"--checkpoint requires passphrase\n");
  482. exit(1);
  483. }
  484. #endif
  485. if (outfile) {
  486. fout = fopen(outfile,!outfileoverwrite ? "a" : "w");
  487. if (!fout) {
  488. perror("failed to open output file");
  489. exit(1);
  490. }
  491. }
  492. if (!fout && yamloutput) {
  493. fprintf(stderr,"nil output with yaml mode does not make sense\n");
  494. exit(1);
  495. }
  496. if (workdir)
  497. createdir(workdir,1);
  498. direndpos = workdirlen;
  499. onionendpos = workdirlen + ONION_LEN;
  500. if (!dirnameflag) {
  501. printstartpos = direndpos;
  502. printlen = ONION_LEN + 1; // + '\n'
  503. } else {
  504. printstartpos = 0;
  505. printlen = onionendpos + 1; // + '\n'
  506. }
  507. if (yamlinput) {
  508. char *sname = makesname();
  509. FILE *fin = stdin;
  510. if (infile) {
  511. fin = fopen(infile,"r");
  512. if (!fin) {
  513. fprintf(stderr,"failed to open input file\n");
  514. return 1;
  515. }
  516. }
  517. tret = yamlin_parseandcreate(fin,sname,onehostname,yamlraw);
  518. if (infile) {
  519. fclose(fin);
  520. fin = 0;
  521. }
  522. free(sname);
  523. if (tret)
  524. return tret;
  525. goto done;
  526. }
  527. filters_prepare();
  528. filters_print();
  529. #ifdef STATISTICS
  530. if (!filters_count() && !reportdelay)
  531. #else
  532. if (!filters_count())
  533. #endif
  534. return 0;
  535. #ifdef EXPANDMASK
  536. if (numwords > 1 && flattened)
  537. fprintf(stderr,"WARNING: -N switch will produce bogus results because we can't know filter width. reconfigure with --enable-besort and recompile.\n");
  538. #endif
  539. if (yamloutput)
  540. yamlout_init();
  541. pthread_mutex_init(&keysgenerated_mutex,0);
  542. pthread_mutex_init(&fout_mutex,0);
  543. #ifdef PASSPHRASE
  544. pthread_mutex_init(&determseed_mutex,0);
  545. #endif
  546. if (numthreads <= 0) {
  547. numthreads = cpucount();
  548. if (numthreads <= 0)
  549. numthreads = 1;
  550. }
  551. if (!quietflag)
  552. fprintf(stderr,"using %d %s\n",
  553. numthreads,numthreads == 1 ? "thread" : "threads");
  554. #ifdef PASSPHRASE
  555. if (deterministic) {
  556. if (!quietflag && numneedgenerate != 1 && !pw_skipnear && !pw_warnnear)
  557. fprintf(stderr,
  558. // 1 2 3 4 5 6 7
  559. //1234567890123456789012345678901234567890123456789012345678901234567890123456789
  560. "CAUTION: avoid using keys generated with the same password for unrelated\n"
  561. " services, as single leaked key may help an attacker to regenerate\n"
  562. " related keys; to silence this warning, pass --skipnear or --warnnear.\n");
  563. if (checkpointfile) {
  564. memcpy(orig_determseed,determseed,sizeof(determseed));
  565. // Read current checkpoint position if file exists
  566. FILE *checkout = fopen(checkpointfile,"r");
  567. if (checkout) {
  568. u8 checkpoint[SEED_LEN];
  569. if(fread(checkpoint,1,SEED_LEN,checkout) != SEED_LEN) {
  570. fprintf(stderr,"failed to read checkpoint file\n");
  571. exit(1);
  572. }
  573. fclose(checkout);
  574. // Apply checkpoint to determseed
  575. bool carry = 0;
  576. for (int i = 0; i < SEED_LEN; i++) {
  577. determseed[i] += checkpoint[i] + carry;
  578. carry = determseed[i] < checkpoint[i];
  579. }
  580. }
  581. }
  582. }
  583. #endif
  584. signal(SIGTERM,termhandler);
  585. signal(SIGINT,termhandler);
  586. VEC_INIT(threads);
  587. VEC_ADDN(threads,numthreads);
  588. #ifdef STATISTICS
  589. VEC_INIT(stats);
  590. VEC_ADDN(stats,numthreads);
  591. VEC_ZERO(stats);
  592. VEC_INIT(tstats);
  593. VEC_ADDN(tstats,numthreads);
  594. VEC_ZERO(tstats);
  595. #endif
  596. pthread_attr_t tattr,*tattrp = &tattr;
  597. tret = pthread_attr_init(tattrp);
  598. if (tret) {
  599. perror("pthread_attr_init");
  600. tattrp = 0;
  601. }
  602. else {
  603. // 256KiB plus whatever batch stuff uses if in batch mode
  604. size_t ss = 256 << 10;
  605. if (wt == WT_BATCH)
  606. ss += worker_batch_memuse();
  607. // align to 64KiB
  608. ss = (ss + (64 << 10) - 1) & ~((64 << 10) - 1);
  609. //printf("stack size: " FSZ "\n",ss);
  610. tret = pthread_attr_setstacksize(tattrp,ss);
  611. if (tret)
  612. perror("pthread_attr_setstacksize");
  613. }
  614. for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
  615. void *tp = 0;
  616. #ifdef STATISTICS
  617. tp = &VEC_BUF(stats,i);
  618. #endif
  619. tret = pthread_create(
  620. &VEC_BUF(threads,i),
  621. tattrp,
  622. #ifdef PASSPHRASE
  623. deterministic
  624. ? CRYPTO_NAMESPACE(worker_batch_pass)
  625. :
  626. #endif
  627. CRYPTO_NAMESPACE(worker_batch),
  628. tp
  629. );
  630. if (tret) {
  631. fprintf(stderr,"error while making " FSZ "th thread: %s\n",i,strerror(tret));
  632. exit(1);
  633. }
  634. }
  635. if (tattrp) {
  636. tret = pthread_attr_destroy(tattrp);
  637. if (tret)
  638. perror("pthread_attr_destroy");
  639. }
  640. #ifdef PASSPHRASE
  641. pthread_t checkpoint_thread;
  642. if (checkpointfile) {
  643. tret = pthread_create(&checkpoint_thread,NULL,checkpointworker,NULL);
  644. if (tret) {
  645. fprintf(stderr,"error while making checkpoint thread: %s\n",strerror(tret));
  646. exit(1);
  647. }
  648. }
  649. #endif
  650. #ifdef STATISTICS
  651. struct timespec nowtime;
  652. u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
  653. if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
  654. perror("failed to get time");
  655. exit(1);
  656. }
  657. istarttime = (1000000 * (u64)nowtime.tv_sec) + ((u64)nowtime.tv_nsec / 1000);
  658. #endif
  659. struct timespec ts;
  660. memset(&ts,0,sizeof(ts));
  661. ts.tv_nsec = 100000000;
  662. while (!endwork) {
  663. if (numneedgenerate && keysgenerated >= numneedgenerate) {
  664. endwork = 1;
  665. break;
  666. }
  667. nanosleep(&ts,0);
  668. #ifdef STATISTICS
  669. clock_gettime(CLOCK_MONOTONIC,&nowtime);
  670. inowtime = (1000000 * (u64)nowtime.tv_sec) + ((u64)nowtime.tv_nsec / 1000);
  671. u64 sumcalc = 0,sumsuccess = 0,sumrestart = 0;
  672. for (int i = 0;i < numthreads;++i) {
  673. u32 newt,tdiff;
  674. // numcalc
  675. newt = VEC_BUF(stats,i).numcalc.v;
  676. tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
  677. VEC_BUF(tstats,i).oldnumcalc = newt;
  678. VEC_BUF(tstats,i).numcalc += (u64)tdiff;
  679. sumcalc += VEC_BUF(tstats,i).numcalc;
  680. // numsuccess
  681. newt = VEC_BUF(stats,i).numsuccess.v;
  682. tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
  683. VEC_BUF(tstats,i).oldnumsuccess = newt;
  684. VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
  685. sumsuccess += VEC_BUF(tstats,i).numsuccess;
  686. // numrestart
  687. newt = VEC_BUF(stats,i).numrestart.v;
  688. tdiff = newt - VEC_BUF(tstats,i).oldnumrestart;
  689. VEC_BUF(tstats,i).oldnumrestart = newt;
  690. VEC_BUF(tstats,i).numrestart += (u64)tdiff;
  691. sumrestart += VEC_BUF(tstats,i).numrestart;
  692. }
  693. if (reportdelay && (!ireporttime || (i64)(inowtime - ireporttime) >= (i64)reportdelay)) {
  694. if (ireporttime)
  695. ireporttime += reportdelay;
  696. else
  697. ireporttime = inowtime;
  698. if (!ireporttime)
  699. ireporttime = 1;
  700. double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
  701. double succpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
  702. double restpersec = (1000000.0 * sumrestart) / (inowtime - istarttime);
  703. fprintf(stderr,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
  704. calcpersec,succpersec,restpersec,
  705. (inowtime - istarttime + elapsedoffset) / 1000000.0);
  706. if (realtimestats) {
  707. for (int i = 0;i < numthreads;++i) {
  708. VEC_BUF(tstats,i).numcalc = 0;
  709. VEC_BUF(tstats,i).numsuccess = 0;
  710. VEC_BUF(tstats,i).numrestart = 0;
  711. }
  712. elapsedoffset += inowtime - istarttime;
  713. istarttime = inowtime;
  714. }
  715. }
  716. if (sumcalc > U64_MAX / 2) {
  717. for (int i = 0;i < numthreads;++i) {
  718. VEC_BUF(tstats,i).numcalc /= 2;
  719. VEC_BUF(tstats,i).numsuccess /= 2;
  720. VEC_BUF(tstats,i).numrestart /= 2;
  721. }
  722. u64 timediff = (inowtime - istarttime + 1) / 2;
  723. elapsedoffset += timediff;
  724. istarttime += timediff;
  725. }
  726. #endif
  727. }
  728. if (!quietflag)
  729. fprintf(stderr,"waiting for threads to finish...");
  730. for (size_t i = 0;i < VEC_LENGTH(threads);++i)
  731. pthread_join(VEC_BUF(threads,i),0);
  732. #ifdef PASSPHRASE
  733. if (checkpointfile) {
  734. checkpointer_endwork = 1;
  735. pthread_join(checkpoint_thread,0);
  736. }
  737. #endif
  738. if (!quietflag)
  739. fprintf(stderr," done.\n");
  740. if (yamloutput)
  741. yamlout_clean();
  742. #ifdef PASSPHRASE
  743. pthread_mutex_destroy(&determseed_mutex);
  744. #endif
  745. pthread_mutex_destroy(&fout_mutex);
  746. pthread_mutex_destroy(&keysgenerated_mutex);
  747. done:
  748. filters_clean();
  749. if (outfile)
  750. fclose(fout);
  751. return 0;
  752. }