fix docs
[mkp224o.git] / main.c
blob041ccd237ba0d34bb6e77d18bc2f8097465de498
1 #ifdef __linux__
2 #define _POSIX_C_SOURCE 200112L
3 #endif
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <pthread.h>
10 #include <signal.h>
11 #include <sodium/randombytes.h>
12 #include <sodium/utils.h>
14 #include "types.h"
15 #include "likely.h"
16 #include "vec.h"
17 #include "base32.h"
18 #include "cpucount.h"
19 #include "keccak.h"
20 #include "ed25519/ed25519.h"
21 #include "ioutil.h"
22 #include "common.h"
23 #include "yaml.h"
25 #ifndef _WIN32
26 #define FSZ "%zu"
27 #else
28 #define FSZ "%Iu"
29 #endif
31 // additional 0 terminator is added by C
32 static const char * const pkprefix = "== ed25519v1-public: type0 ==\0\0";
33 static const char * const skprefix = "== ed25519v1-secret: type0 ==\0\0";
35 static const char checksumstr[] = ".onion checksum";
36 #define checksumstrlen (sizeof(checksumstr) - 1) // 15
38 // output directory
39 static char *workdir = 0;
40 static size_t workdirlen = 0;
42 static int quietflag = 0;
43 //static int wantdedup = 0;
44 #define wantdedup 0
46 // 0, direndpos, onionendpos
47 // printstartpos = either 0 or direndpos
48 // printlen = either onionendpos + 1 or ONION_LEN + 1 (additional 1 is for newline)
49 size_t onionendpos; // end of .onion within string
50 size_t direndpos; // end of dir before .onion within string
51 size_t printstartpos; // where to start printing from
52 size_t printlen; // precalculated, related to printstartpos
54 static int yamloutput = 0;
55 static int numwords = 1;
56 static size_t numneedgenerate = 0;
58 static pthread_mutex_t keysgenerated_mutex;
59 static volatile size_t keysgenerated = 0;
60 static volatile int endwork = 0;
62 pthread_mutex_t fout_mutex;
63 FILE *fout;
65 static void termhandler(int sig)
67 switch (sig) {
68 case SIGTERM:
69 case SIGINT:
70 endwork = 1;
71 break;
75 #include "filters.h"
77 #ifdef STATISTICS
78 #define ADDNUMSUCCESS ++st->numsuccess.v
79 #else
80 #define ADDNUMSUCCESS do ; while (0)
81 #endif
83 // statistics, if enabled
84 #ifdef STATISTICS
85 struct statstruct {
86 union {
87 u32 v;
88 size_t align;
89 } numcalc;
90 union {
91 u32 v;
92 size_t align;
93 } numsuccess;
94 union {
95 u32 v;
96 size_t align;
97 } numrestart;
98 } ;
99 VEC_STRUCT(statsvec,struct statstruct);
101 struct tstatstruct {
102 u64 numcalc;
103 u64 numsuccess;
104 u64 numrestart;
105 u32 oldnumcalc;
106 u32 oldnumsuccess;
107 u32 oldnumrestart;
109 VEC_STRUCT(tstatsvec,struct tstatstruct);
110 #endif
112 static void onionready(char *sname,const u8 *secret,const u8 *pubonion)
114 if (endwork)
115 return;
117 if (numneedgenerate) {
118 pthread_mutex_lock(&keysgenerated_mutex);
119 if (keysgenerated >= numneedgenerate) {
120 pthread_mutex_unlock(&keysgenerated_mutex);
121 return;
123 ++keysgenerated;
124 if (keysgenerated == numneedgenerate)
125 endwork = 1;
126 pthread_mutex_unlock(&keysgenerated_mutex);
129 if (!yamloutput) {
130 if (createdir(sname,1) != 0) {
131 pthread_mutex_lock(&fout_mutex);
132 fprintf(stderr,"ERROR: could not create directory for key output\n");
133 pthread_mutex_unlock(&fout_mutex);
134 return;
137 strcpy(&sname[onionendpos],"/hs_ed25519_secret_key");
138 writetofile(sname,secret,FORMATTED_SECRET_LEN,1);
140 strcpy(&sname[onionendpos],"/hs_ed25519_public_key");
141 writetofile(sname,pubonion,FORMATTED_PUBLIC_LEN,0);
143 strcpy(&sname[onionendpos],"/hostname");
144 FILE *hfile = fopen(sname,"w");
145 sname[onionendpos] = '\n';
146 if (hfile) {
147 fwrite(&sname[direndpos],ONION_LEN + 1,1,hfile);
148 fclose(hfile);
150 if (fout) {
151 pthread_mutex_lock(&fout_mutex);
152 fwrite(&sname[printstartpos],printlen,1,fout);
153 fflush(fout);
154 pthread_mutex_unlock(&fout_mutex);
156 } else
157 yamlout_writekeys(&sname[direndpos],pubonion,secret);
160 union pubonionunion {
161 u8 raw[PKPREFIX_SIZE + PUBLIC_LEN + 32];
162 struct {
163 u64 prefix[4];
164 u64 key[4];
165 u64 hash[4];
166 } i;
169 static char *makesname()
171 char *sname = (char *) malloc(workdirlen + ONION_LEN + 63 + 1);
172 if (!sname)
173 abort();
174 if (workdir)
175 memcpy(sname,workdir,workdirlen);
176 return sname;
179 // little endian inc
180 static void addsk32(u8 *sk)
182 register unsigned int c = 8;
183 for (size_t i = 0;i < 32;++i) {
184 c = (unsigned int)sk[i] + c; sk[i] = c & 0xFF; c >>= 8;
185 // unsure if needed
186 if (!c) break;
190 // 0123 4567 xxxx --3--> 3456 7xxx
191 // 0123 4567 xxxx --1--> 1234 567x
192 static inline void shiftpk(u8 *dst,const u8 *src,size_t sbits)
194 size_t i,sbytes = sbits / 8;
195 sbits %= 8;
196 for (i = 0;i + sbytes < PUBLIC_LEN;++i) {
197 dst[i] = (u8) ((src[i+sbytes] << sbits) |
198 (src[i+sbytes+1] >> (8 - sbits)));
200 for(;i < PUBLIC_LEN;++i)
201 dst[i] = 0;
204 static void *dowork(void *task)
206 union pubonionunion pubonion;
207 u8 * const pk = &pubonion.raw[PKPREFIX_SIZE];
208 u8 secret[SKPREFIX_SIZE + SECRET_LEN];
209 u8 * const sk = &secret[SKPREFIX_SIZE];
210 u8 seed[SEED_LEN];
211 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
212 u8 wpk[PUBLIC_LEN + 1];
213 size_t i;
214 char *sname;
215 #ifdef STATISTICS
216 struct statstruct *st = (struct statstruct *)task;
217 #endif
218 PREFILTER
220 memcpy(secret,skprefix,SKPREFIX_SIZE);
221 wpk[PUBLIC_LEN] = 0;
222 memset(&pubonion,0,sizeof(pubonion));
223 memcpy(pubonion.raw,pkprefix,PKPREFIX_SIZE);
224 // write version later as it will be overwritten by hash
225 memcpy(hashsrc,checksumstr,checksumstrlen);
226 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
228 sname = makesname();
230 initseed:
231 randombytes(seed,sizeof(seed));
232 ed25519_seckey_expand(sk,seed);
233 #ifdef STATISTICS
234 ++st->numrestart.v;
235 #endif
237 again:
238 if (unlikely(endwork))
239 goto end;
241 ed25519_pubkey(pk,sk);
243 #ifdef STATISTICS
244 ++st->numcalc.v;
245 #endif
247 DOFILTER(i,pk,{
248 if (numwords > 1) {
249 shiftpk(wpk,pk,filter_len(i));
250 size_t j;
251 for (int w = 1;;) {
252 DOFILTER(j,wpk,goto secondfind);
253 goto next;
254 secondfind:
255 if (++w >= numwords)
256 break;
257 shiftpk(wpk,wpk,filter_len(j));
260 // sanity check
261 if ((sk[0] & 248) != sk[0] || ((sk[31] & 63) | 64) != sk[31])
262 goto initseed;
264 ADDNUMSUCCESS;
266 // calc checksum
267 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
268 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
269 // version byte
270 pk[PUBLIC_LEN + 2] = 0x03;
271 // base32
272 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
273 onionready(sname,secret,pubonion.raw);
274 pk[PUBLIC_LEN] = 0;
275 goto initseed;
277 next:
278 addsk32(sk);
279 goto again;
281 end:
282 free(sname);
283 POSTFILTER
284 sodium_memzero(secret,sizeof(secret));
285 sodium_memzero(seed,sizeof(seed));
286 return 0;
289 static void addsztoscalar32(u8 *dst,size_t v)
291 int i;
292 u32 c = 0;
293 for (i = 0;i < 32;++i) {
294 c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
295 v >>= 8;
296 ++dst;
300 static void *dofastwork(void *task)
302 union pubonionunion pubonion;
303 u8 * const pk = &pubonion.raw[PKPREFIX_SIZE];
304 u8 secret[SKPREFIX_SIZE + SECRET_LEN];
305 u8 * const sk = &secret[SKPREFIX_SIZE];
306 u8 seed[SEED_LEN];
307 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
308 u8 wpk[PUBLIC_LEN + 1];
309 ge_p3 ge_public;
310 size_t counter;
311 size_t i;
312 char *sname;
313 #ifdef STATISTICS
314 struct statstruct *st = (struct statstruct *)task;
315 #endif
316 PREFILTER
318 memcpy(secret,skprefix,SKPREFIX_SIZE);
319 wpk[PUBLIC_LEN] = 0;
320 memset(&pubonion,0,sizeof(pubonion));
321 memcpy(pubonion.raw,pkprefix,PKPREFIX_SIZE);
322 // write version later as it will be overwritten by hash
323 memcpy(hashsrc,checksumstr,checksumstrlen);
324 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
326 sname = makesname();
328 initseed:
329 #ifdef STATISTICS
330 ++st->numrestart.v;
331 #endif
332 randombytes(seed,sizeof(seed));
333 ed25519_seckey_expand(sk,seed);
335 ge_scalarmult_base(&ge_public,sk);
336 ge_p3_tobytes(pk,&ge_public);
338 for (counter = 0;counter < SIZE_MAX-8;counter += 8) {
339 ge_p1p1 sum;
341 if (unlikely(endwork))
342 goto end;
344 DOFILTER(i,pk,{
345 if (numwords > 1) {
346 shiftpk(wpk,pk,filter_len(i));
347 size_t j;
348 for (int w = 1;;) {
349 DOFILTER(j,wpk,goto secondfind);
350 goto next;
351 secondfind:
352 if (++w >= numwords)
353 break;
354 shiftpk(wpk,wpk,filter_len(j));
357 // found!
358 // update secret key with counter
359 addsztoscalar32(sk,counter);
360 // sanity check
361 if ((sk[0] & 248) != sk[0] || ((sk[31] & 63) | 64) != sk[31])
362 goto initseed;
364 ADDNUMSUCCESS;
366 // calc checksum
367 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
368 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
369 // version byte
370 pk[PUBLIC_LEN + 2] = 0x03;
371 // full name
372 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
373 onionready(sname,secret,pubonion.raw);
374 pk[PUBLIC_LEN] = 0;
375 // don't reuse same seed
376 goto initseed;
378 next:
379 ge_add(&sum, &ge_public,&ge_eightpoint);
380 ge_p1p1_to_p3(&ge_public,&sum);
381 ge_p3_tobytes(pk,&ge_public);
382 #ifdef STATISTICS
383 ++st->numcalc.v;
384 #endif
386 goto initseed;
388 end:
389 free(sname);
390 POSTFILTER
391 sodium_memzero(secret,sizeof(secret));
392 sodium_memzero(seed,sizeof(seed));
393 return 0;
396 static void printhelp(FILE *out,const char *progname)
398 fprintf(out,
399 "Usage: %s filter [filter...] [options]\n"
400 " %s -f filterfile [options]\n"
401 "Options:\n"
402 "\t-h - print help to stdout and quit\n"
403 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
404 "\t-q - do not print diagnostic output to stderr\n"
405 "\t-x - do not print onion names\n"
406 "\t-o filename - output onion names to specified file (append)\n"
407 "\t-O filename - output onion names to specified file (overwrite)\n"
408 "\t-F - include directory names in onion names output\n"
409 "\t-d dirname - output directory\n"
410 "\t-t numthreads - specify number of threads (default - auto)\n"
411 "\t-j numthreads - same as -t\n"
412 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
413 "\t-N numwords - specify number of words per key (default - 1)\n"
414 "\t-z - use faster key generation method. this is now default\n"
415 "\t-Z - use slower key generation method\n"
416 "\t-s - print statistics each 10 seconds\n"
417 "\t-S t - print statistics every specified ammount of seconds\n"
418 "\t-T - do not reset statistics counters when printing\n"
419 "\t-y - output generated keys in yaml format instead of dumping them to filesystem\n"
420 "\t-Y [filename [host.onion]] - parse yaml encoded input and extract key(s) to filesystem\n"
421 ,progname,progname);
422 fflush(out);
425 enum {
426 Q_ADDITIONAL = 101,
427 Q_UNRECOGNISED,
428 Q_NOSTATISTICS,
429 Q_FAILOPENOUTPUT,
430 Q_FAILTHREAD,
431 Q_FAILTIME,
432 Q_FAILOPENINPUT,
435 static void e_additional()
437 fprintf(stderr,"additional argument required\n");
438 exit(Q_ADDITIONAL);
441 #ifndef STATISTICS
442 static void e_nostatistics()
444 fprintf(stderr,"statistics support not compiled in\n");
445 exit(Q_NOSTATISTICS);
447 #endif
449 static void setworkdir(const char *wd)
451 free(workdir);
452 size_t l = strlen(wd);
453 if (!l) {
454 workdir = 0;
455 workdirlen = 0;
456 if (!quietflag)
457 fprintf(stderr,"unset workdir\n");
458 return;
460 unsigned needslash = 0;
461 if (wd[l-1] != '/')
462 needslash = 1;
463 char *s = (char *) malloc(l + needslash + 1);
464 if (!s)
465 abort();
466 memcpy(s,wd,l);
467 if (needslash)
468 s[l++] = '/';
469 s[l] = 0;
471 workdir = s;
472 workdirlen = l;
473 if (!quietflag)
474 fprintf(stderr,"set workdir: %s\n",workdir);
477 VEC_STRUCT(threadvec, pthread_t);
479 int main(int argc,char **argv)
481 const char *outfile = 0;
482 const char *infile = 0;
483 const char *hostname = 0;
484 const char *arg;
485 int ignoreargs = 0;
486 int dirnameflag = 0;
487 int numthreads = 0;
488 int fastkeygen = 1;
489 int yamlinput = 0;
490 int outfileoverwrite = 0;
491 struct threadvec threads;
492 #ifdef STATISTICS
493 struct statsvec stats;
494 struct tstatsvec tstats;
495 u64 reportdelay = 0;
496 int realtimestats = 1;
497 #endif
498 int tret;
500 ge_initeightpoint();
501 filters_init();
503 setvbuf(stderr,0,_IONBF,0);
504 fout = stdout;
506 const char *progname = argv[0];
507 if (argc <= 1) {
508 printhelp(stderr,progname);
509 exit(1);
511 argc--; argv++;
513 while (argc--) {
514 arg = *argv++;
515 if (!ignoreargs && *arg == '-') {
516 int numargit = 0;
517 nextarg:
518 ++arg;
519 ++numargit;
520 if (*arg == '-') {
521 if (numargit > 1) {
522 fprintf(stderr,"unrecognised argument: -\n");
523 exit(Q_UNRECOGNISED);
525 ++arg;
526 if (!*arg)
527 ignoreargs = 1;
528 else if (!strcmp(arg,"help") || !strcmp(arg,"usage")) {
529 printhelp(stdout,progname);
530 exit(0);
532 else {
533 fprintf(stderr,"unrecognised argument: --%s\n",arg);
534 exit(Q_UNRECOGNISED);
536 numargit = 0;
538 else if (*arg == 0) {
539 if (numargit == 1)
540 ignoreargs = 1;
541 continue;
543 else if (*arg == 'h') {
544 printhelp(stdout,progname);
545 exit(0);
547 else if (*arg == 'f') {
548 if (argc--)
549 loadfilterfile(*argv++);
550 else
551 e_additional();
553 else if (*arg == 'q')
554 ++quietflag;
555 else if (*arg == 'x')
556 fout = 0;
557 else if (*arg == 'o') {
558 outfileoverwrite = 0;
559 if (argc--)
560 outfile = *argv++;
561 else
562 e_additional();
564 else if (*arg == 'O') {
565 outfileoverwrite = 1;
566 if (argc--)
567 outfile = *argv++;
568 else
569 e_additional();
571 else if (*arg == 'F')
572 dirnameflag = 1;
573 else if (*arg == 'd') {
574 if (argc--)
575 setworkdir(*argv++);
576 else
577 e_additional();
579 else if (*arg == 't' || *arg == 'j') {
580 if (argc--)
581 numthreads = atoi(*argv++);
582 else
583 e_additional();
585 else if (*arg == 'n') {
586 if (argc--)
587 numneedgenerate = (size_t)atoll(*argv++);
588 else
589 e_additional();
591 else if (*arg == 'N') {
592 if (argc--)
593 numwords = atoi(*argv++);
594 else
595 e_additional();
597 else if (*arg == 'Z')
598 fastkeygen = 0;
599 else if (*arg == 'z')
600 fastkeygen = 1;
601 else if (*arg == 's') {
602 #ifdef STATISTICS
603 reportdelay = 10000000;
604 #else
605 e_nostatistics();
606 #endif
608 else if (*arg == 'S') {
609 #ifdef STATISTICS
610 if (argc--)
611 reportdelay = (u64)atoll(*argv++) * 1000000;
612 else
613 e_additional();
614 #else
615 e_nostatistics();
616 #endif
618 else if (*arg == 'T') {
619 #ifdef STATISTICS
620 realtimestats = 0;
621 #else
622 e_nostatistics();
623 #endif
625 else if (*arg == 'y')
626 yamloutput = 1;
627 else if (*arg == 'Y') {
628 yamlinput = 1;
629 if (argc) {
630 --argc;
631 infile = *argv++;
632 if (!*infile)
633 infile = 0;
634 if (argc) {
635 --argc;
636 hostname = *argv++;
637 if (!*hostname)
638 hostname = 0;
639 if (hostname && strlen(hostname) != ONION_LEN) {
640 fprintf(stderr,"bad onion argument length\n");
641 exit(Q_UNRECOGNISED);
646 else {
647 fprintf(stderr,"unrecognised argument: -%c\n",*arg);
648 exit(Q_UNRECOGNISED);
650 if (numargit)
651 goto nextarg;
653 else
654 filters_add(arg);
657 if (outfile) {
658 fout = fopen(outfile,!outfileoverwrite ? "a" : "w");
659 if (!fout) {
660 perror("failed to open output file");
661 exit(Q_FAILOPENOUTPUT);
665 if (!fout && yamloutput) {
666 fprintf(stderr,"nil output with yaml mode does not make sense\n");
667 exit(Q_FAILOPENOUTPUT); // define new err code?
670 if (workdir)
671 createdir(workdir,1);
673 direndpos = workdirlen;
674 onionendpos = workdirlen + ONION_LEN;
676 if (!dirnameflag) {
677 printstartpos = direndpos;
678 printlen = ONION_LEN + 1; // + '\n'
679 } else {
680 printstartpos = 0;
681 printlen = onionendpos + 1; // + '\n'
684 if (yamlinput) {
685 char *sname = makesname();
686 FILE *fin = stdin;
687 if (infile) {
688 fin = fopen(infile,"r");
689 if (!fin) {
690 fprintf(stderr,"failed to open input file\n");
691 return Q_FAILOPENINPUT;
694 tret = yamlin_parseandcreate(fin,sname,hostname);
695 if (infile) {
696 fclose(fin);
697 fin = 0;
699 free(sname);
701 if (tret)
702 return tret;
704 goto done;
707 filters_prepare();
709 filters_print();
711 #ifdef STATISTICS
712 if (!filters_count() && !reportdelay)
713 #else
714 if (!filters_count())
715 #endif
716 return 0;
718 #ifdef EXPANDMASK
719 if (numwords > 1 && flattened)
720 fprintf(stderr,"WARNING: -N switch will produce bogus results because we can't know filter width. reconfigure with --enable-besort and recompile.\n");
721 #endif
723 if (yamloutput)
724 yamlout_init();
726 pthread_mutex_init(&keysgenerated_mutex,0);
727 pthread_mutex_init(&fout_mutex,0);
729 if (numthreads <= 0) {
730 numthreads = cpucount();
731 if (numthreads <= 0)
732 numthreads = 1;
733 if (!quietflag)
734 fprintf(stderr,"using %d %s\n",
735 numthreads,numthreads == 1 ? "thread" : "threads");
738 signal(SIGTERM,termhandler);
739 signal(SIGINT,termhandler);
741 VEC_INIT(threads);
742 VEC_ADDN(threads,numthreads);
743 #ifdef STATISTICS
744 VEC_INIT(stats);
745 VEC_ADDN(stats,numthreads);
746 VEC_ZERO(stats);
747 VEC_INIT(tstats);
748 VEC_ADDN(tstats,numthreads);
749 VEC_ZERO(tstats);
750 #endif
752 pthread_attr_t tattr,*tattrp = &tattr;
753 tret = pthread_attr_init(tattrp);
754 if (tret) {
755 perror("pthread_attr_init");
756 tattrp = 0;
758 else {
759 tret = pthread_attr_setstacksize(tattrp,80<<10);
760 if (tret)
761 perror("pthread_attr_setstacksize");
764 for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
765 void *tp = 0;
766 #ifdef STATISTICS
767 tp = &VEC_BUF(stats,i);
768 #endif
769 tret = pthread_create(&VEC_BUF(threads,i),tattrp,fastkeygen ? dofastwork : dowork,tp);
770 if (tret) {
771 fprintf(stderr,"error while making " FSZ "th thread: %s\n",i,strerror(tret));
772 exit(Q_FAILTHREAD);
776 if (tattrp) {
777 tret = pthread_attr_destroy(tattrp);
778 if (tret)
779 perror("pthread_attr_destroy");
782 #ifdef STATISTICS
783 struct timespec nowtime;
784 u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
785 if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
786 perror("failed to get time");
787 exit(Q_FAILTIME);
789 istarttime = (1000000 * (u64)nowtime.tv_sec) + ((u64)nowtime.tv_nsec / 1000);
790 #endif
791 struct timespec ts;
792 memset(&ts,0,sizeof(ts));
793 ts.tv_nsec = 100000000;
794 while (!endwork) {
795 if (numneedgenerate && keysgenerated >= numneedgenerate) {
796 endwork = 1;
797 break;
799 nanosleep(&ts,0);
801 #ifdef STATISTICS
802 clock_gettime(CLOCK_MONOTONIC,&nowtime);
803 inowtime = (1000000 * (u64)nowtime.tv_sec) + ((u64)nowtime.tv_nsec / 1000);
804 u64 sumcalc = 0,sumsuccess = 0,sumrestart = 0;
805 for (int i = 0;i < numthreads;++i) {
806 u32 newt,tdiff;
807 // numcalc
808 newt = VEC_BUF(stats,i).numcalc.v;
809 tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
810 VEC_BUF(tstats,i).oldnumcalc = newt;
811 VEC_BUF(tstats,i).numcalc += (u64)tdiff;
812 sumcalc += VEC_BUF(tstats,i).numcalc;
813 // numsuccess
814 newt = VEC_BUF(stats,i).numsuccess.v;
815 tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
816 VEC_BUF(tstats,i).oldnumsuccess = newt;
817 VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
818 sumsuccess += VEC_BUF(tstats,i).numsuccess;
819 // numrestart
820 newt = VEC_BUF(stats,i).numrestart.v;
821 tdiff = newt - VEC_BUF(tstats,i).oldnumrestart;
822 VEC_BUF(tstats,i).oldnumrestart = newt;
823 VEC_BUF(tstats,i).numrestart += (u64)tdiff;
824 sumrestart += VEC_BUF(tstats,i).numrestart;
826 if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) {
827 if (ireporttime)
828 ireporttime += reportdelay;
829 else
830 ireporttime = inowtime;
831 if (!ireporttime)
832 ireporttime = 1;
834 double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
835 double succpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
836 double restpersec = (1000000.0 * sumrestart) / (inowtime - istarttime);
837 fprintf(stderr,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
838 calcpersec,succpersec,restpersec,
839 (inowtime - istarttime + elapsedoffset) / 1000000.0);
841 if (realtimestats) {
842 for (int i = 0;i < numthreads;++i) {
843 VEC_BUF(tstats,i).numcalc = 0;
844 VEC_BUF(tstats,i).numsuccess = 0;
845 VEC_BUF(tstats,i).numrestart = 0;
847 elapsedoffset += inowtime - istarttime;
848 istarttime = inowtime;
851 if (sumcalc > U64_MAX / 2) {
852 for (int i = 0;i < numthreads;++i) {
853 VEC_BUF(tstats,i).numcalc /= 2;
854 VEC_BUF(tstats,i).numsuccess /= 2;
855 VEC_BUF(tstats,i).numrestart /= 2;
857 u64 timediff = (inowtime - istarttime + 1) / 2;
858 elapsedoffset += timediff;
859 istarttime += timediff;
861 #endif
864 if (!quietflag)
865 fprintf(stderr,"waiting for threads to finish...");
866 for (size_t i = 0;i < VEC_LENGTH(threads);++i)
867 pthread_join(VEC_BUF(threads,i),0);
868 if (!quietflag)
869 fprintf(stderr," done.\n");
871 if (yamloutput)
872 yamlout_clean();
874 pthread_mutex_destroy(&keysgenerated_mutex);
875 pthread_mutex_destroy(&fout_mutex);
877 done:
878 filters_clean();
880 if (outfile)
881 fclose(fout);
883 return 0;