/** ** Simple entropy harvester based upon the havege RNG ** ** Copyright 2018-2024 Jirka Hladky hladky DOT jiri AT gmail DOT com ** Copyright 2009-2014 Gary Wuertz gary@issiweb.com ** Copyright 2011-2012 BenEleventh Consulting manolson@beneleventh.com ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ #include "config.h" #if defined(HAVE_SYS_AUXV_H) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_DAEMON #include #include #include #include #endif #ifndef NO_COMMAND_MODE #include "havegecmd.h" #include #endif #include #include "haveged.h" #include "havegecollect.h" /** * stringize operators for maintainable text */ #define STRZ(a) #a #define SETTINGL(msg,val) STRZ(val) msg #define SETTINGR(msg,val) msg STRZ(val) // {{{ VERSION_TEXT static const char* VERSION_TEXT = "haveged %s\n\n" "Copyright (C) 2018-2024 Jirka Hladky \n" "Copyright (C) 2009-2014 Gary Wuertz \n" "Copyright (C) 2011-2012 BenEleventh Consulting \n\n" "License GPLv3+: GNU GPL version 3 or later .\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"; // }}} VERSION_TEXT /** * Parameters */ static struct pparams defaults = { .daemon = PACKAGE, .exit_code = 1, .setup = 0, .ncores = 0, .buffersz = 0, .detached = 0, .foreground = 0, .once = 0, .d_cache = 0, .i_cache = 0, .run_level = 0, .low_water = 0, .tests_config = 0, .os_rel = "/proc/sys/kernel/osrelease", .pid_file = PID_DEFAULT, .poolsize = "/proc/sys/kernel/random/poolsize", .random_device = "/dev/random", .sample_in = INPUT_DEFAULT, .sample_out = OUTPUT_DEFAULT, .verbose = 0, .watermark = "/proc/sys/kernel/random/write_wakeup_threshold", .command = 0, .time_interval = TIME_INTERVAL }; struct pparams *params = &defaults; #ifdef RAW_IN_ENABLE FILE *fd_in; /** * The injection diagnostic */ static int injectFile(volatile H_UINT *pData, H_UINT szData); #endif /** * havege instance used by application */ static H_PTR handle = NULL; /** * Local prototypes */ #ifndef NO_DAEMON static int poolSize = 0; static void daemonize(void); static int get_poolsize(void); static void run_daemon(H_PTR handle, const volatile char *path, char *const argv[]); static void set_watermark(int level); #endif static void anchor_info(H_PTR h); static int get_runsize(unsigned int *bufct, unsigned int *bufrem, char *bp); static char *ppSize(char *buffer, double sz); static void run_app(H_PTR handle, H_UINT bufct, H_UINT bufres); static void show_meterInfo(H_UINT id, H_UINT event); static void tidy_exit(int signum); static void usage(int db, int nopts, struct option *long_options, const char **cmds); static sigset_t mask, omask; sem_t *sem = NULL; #define ATOU(a) (unsigned int)atoi(a) /** * Entry point */ int main(int argc, char **argv) { volatile char *path = strdup(argv[0]); volatile char *arg0 = argv[0]; #if defined(HAVE_SYS_AUXV_H) if (path[0] != '/') path = (char*)getauxval(AT_EXECFN); #endif static const char* cmds[] = { "b", "buffer", "1", SETTINGR("Buffer size [KW], default: ",COLLECT_BUFSIZE), "d", "data", "1", SETTINGR("Data cache size [KB], with fallback to: ", GENERIC_DCACHE ), #ifndef NO_COMMAND_MODE "c", "command", "1", "Send a command mode to an already running haveged", #endif "i", "inst", "1", SETTINGR("Instruction cache size [KB], with fallback to: ", GENERIC_ICACHE), "f", "file", "1", "Sample output file, default: '" OUTPUT_DEFAULT "', '-' for stdout", "F", "Foreground", "0", "Run daemon in foreground", "e", "once", "0", "Provide entropy to the kernel once and quit immediatelly", "r", "run", "1", "0=daemon, 1=config info, >1=KB sample", "n", "number", "1", "Output size in [k|m|g|t] bytes, 0 = unlimited to stdout", "o", "onlinetest", "1", "[t][c] x=[a[n][w]][b[w]] 't'ot, 'c'ontinuous, default: ta8b", "p", "pidfile", "1", "daemon pidfile, default: " PID_DEFAULT , "s", "source", "1", "Injection source file, default: '" INPUT_DEFAULT "', '-' for stdin", #if NUMBER_CORES>1 "t", "threads", "1", "Number of threads", #endif "T", "time_interval", "1", "Time interval in seconds to add entropy unconditionally. Max rate/timestep is " TOSTRING(PSELECT_TIMEOUT) " seconds. Default: " TOSTRING(TIME_INTERVAL) " seconds.", "v", "verbose", "1", "Verbose mask 0=none,1=summary,2=retries,4=timing,8=loop,16=code,32=test,64=RNDADDENTROPY", "w", "write", "1", "Set write_wakeup_threshold [bits]", "V", "version", "0", "Print version information and exit", "h", "help", "0", "This help" }; static int nopts = sizeof(cmds)/(4*sizeof(char *)); struct option long_options[nopts+1]; char short_options[1+nopts*2]; int c,i,j; H_UINT bufct, bufrem, ierr; H_PARAMS cmd; if (havege_version(HAVEGE_PREP_VERSION)==NULL) error_exit("version conflict %s!=%s", HAVEGE_PREP_VERSION, havege_version(NULL)); #if NO_DAEMON==1 params->setup |= RUN_AS_APP; #endif #ifdef RAW_IN_ENABLE #define DIAG_USAGE2 SETTINGL("=inject ticks,", DIAG_RUN_INJECT)\ SETTINGL("=inject data", DIAG_RUN_TEST) params->setup |= INJECT | RUN_AS_APP; #else #define DIAG_USAGE2 "" #endif #ifdef RAW_OUT_ENABLE #define DIAG_USAGE1 SETTINGL("=capture,", DIAG_RUN_CAPTURE) params->setup |= CAPTURE | RUN_AS_APP; #else #define DIAG_USAGE1 "" #endif #if NUMBER_CORES>1 params->setup |= MULTI_CORE; #endif #ifndef NO_COMMAND_MODE first_byte = arg0[0]; #endif if (access("/etc/initrd-release", F_OK) >= 0) { arg0[0] = '@'; path[0] = '/'; } #ifdef SIGHUP signal(SIGHUP, tidy_exit); #endif signal(SIGINT, tidy_exit); signal(SIGTERM, tidy_exit); #ifndef NO_COMMAND_MODE signal(SIGPIPE, SIG_IGN); #endif sigemptyset(&mask); #ifdef SIGHUP sigaddset(&mask, SIGHUP); #endif sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); #ifndef NO_COMMAND_MODE sigaddset(&mask, SIGPIPE); #endif #if NUMBER_CORES>1 /* Hmmm ... currently the code does not use pthread_create(3) but fork(2) */ pthread_sigmask(SIG_UNBLOCK, &mask, NULL); #else sigprocmask(SIG_UNBLOCK, &mask, NULL); #endif strcpy(short_options,""); bufct = bufrem = 0; /** * Build options */ for(i=j=0;j<(nopts*4);j+=4) { switch(cmds[j][0]) { case 'o': #ifdef ONLINE_TESTS_ENABLE break; #else continue; #endif case 'r': #if defined(RAW_IN_ENABLE) || defined (RAW_OUT_ENABLE) if (0!=(params->setup & (INJECT|CAPTURE))) { params->daemon = "havege_diagnostic"; cmds[j+3] = "run level, 0=diagnostic off,1=config info," DIAG_USAGE1 DIAG_USAGE2 ; } else #endif if (0!=(params->setup & RUN_AS_APP)) continue; break; case 's': if (0 == (params->setup & INJECT)) continue; break; case 't': if (0 == (params->setup & MULTI_CORE)) continue; break; case 'p': case 'w': case 'F': case 'T': if (0 !=(params->setup & RUN_AS_APP)) continue; break; } long_options[i].name = cmds[j+1]; long_options[i].has_arg = atoi(cmds[j+2]); long_options[i].flag = NULL; long_options[i].val = cmds[j][0]; strcat(short_options,cmds[j]); if (long_options[i].has_arg!=0) strcat(short_options,":"); // printf("Long option number %u\n", i); // printf("name\t%s\n", long_options[i].name); // printf("has_arg\t%d\n", long_options[i].has_arg); i += 1; } memset(&long_options[i], 0, sizeof(struct option)); // printf("Short %s\n", short_options); do { c = getopt_long (argc, argv, short_options, long_options, NULL); // printf("Char %c\n", c); switch(c) { case 'F': params->setup |= RUN_IN_FG; params->foreground = 1; break; case 'e': params->setup |= RUN_ONCE; params->once = 1; break; case 'b': params->buffersz = ATOU(optarg) * 1024; if (params->buffersz<4) error_exit("invalid size %s", optarg); break; #ifndef NO_COMMAND_MODE case 'c': params->command = optarg; params->setup |= CMD_MODE; break; #endif case 'd': params->d_cache = ATOU(optarg); break; case 'i': params->i_cache = ATOU(optarg); break; case 'f': params->sample_out = optarg; if (strcmp(optarg,"-") == 0 ) params->setup |= USE_STDOUT; break; case 'n': if (get_runsize(&bufct, &bufrem, optarg)) error_exit("invalid count: %s", optarg); params->setup |= RUN_AS_APP|RANGE_SPEC; if (bufct==0 && bufrem==0) params->setup |= USE_STDOUT; /* ugly but documented behavior! */ break; case 'o': params->tests_config = optarg; break; case 'p': params->pid_file = optarg; break; case 'r': params->run_level = ATOU(optarg); if (params->run_level != 0) params->setup |= RUN_AS_APP; break; case 's': params->sample_in = optarg; break; case 't': params->ncores = ATOU(optarg); if (params->ncores > NUMBER_CORES) error_exit("invalid thread count: %s", optarg); break; case 'T': params->time_interval = ATOU(optarg); break; case 'v': params->verbose = ATOU(optarg); break; case 'w': params->setup |= SET_LWM; params->low_water = ATOU(optarg); break; case '?': case 'h': usage(0, nopts, long_options, cmds); /* fallthrough */ case 'V': printf(VERSION_TEXT, HAVEGE_PREP_VERSION); exit(EXIT_SUCCESS); case -1: break; } } while (c!=-1); #ifndef NO_COMMAND_MODE if (params->setup & CMD_MODE) { int ret = 0, len; uint32_t size; char *ptr, answer[6], cmd[2]; fd_set read_fd; sigset_t block; /* init semaphore */ sem = sem_open(SEM_NAME, 0); if (sem == NULL) { print_msg("sem_open() failed \n"); print_msg("Error : %s \n", strerror(errno)); ret = -1; goto err; } socket_fd = cmd_connect(params); if (socket_fd < 0) { ret = -1; goto err; } cmd[0] = getcmd(params->command); if (cmd[0] < 0) { ret = -1; goto err; } cmd[1] = '\0'; switch (cmd[0]) { char *root; case MAGIC_CHROOT: root = optarg; size = (uint32_t)strlen(root)+1; cmd[1] = '\002'; /* * Synchronise haveged -c instance and daemon instance * prevent daemon instance from readin messages * from the socket until the -c instance finish writting */ sem_wait(sem); safeout(socket_fd, &cmd[0], 2); send_uinteger(socket_fd, size); safeout(socket_fd, root, size); /* * unblock the daemon instance as we finished writting */ sem_post(sem); break; case MAGIC_CLOSE: ptr = &cmd[0]; len = (int)strlen(cmd)+1; safeout(socket_fd, ptr, len); break; case '?': default: ret = -1; break; } answer[0] = '\0'; ptr = &answer[0]; sigemptyset(&block); sigaddset(&block, SIGPIPE); FD_ZERO(&read_fd); FD_SET(socket_fd, &read_fd); do { struct timeval timeout = {6, 0}; ret = select(socket_fd+1, &read_fd, NULL, NULL, &timeout); if (ret >= 0) break; if (errno != EINTR) error_exit("Select error: %s", strerror(errno)); } while (1); ret = safein(socket_fd, ptr, 1); if (ret < 0) goto err; switch (answer[0]) { case '\002': { char *msg; ret = receive_uinteger(socket_fd, &size); if (ret < 0) goto err; msg = calloc(size, sizeof(char)); if (!msg) error_exit("can not allocate memory for message from UNIX socket: %s", strerror(errno)); ret = safein(socket_fd, msg, size); if (ret < 0) goto err; fprintf(stderr, "%s: %s", params->daemon, msg); free(msg); ret = -1; } break; case '\x6': ret = 0; break; case '\x15': default: ret = -1; break; } err: close(socket_fd); sem_close(sem); return ret; } else if (!(params->setup & RUN_AS_APP)){ socket_fd = cmd_listen(params); if (socket_fd >= 0) fprintf(stderr, "%s: command socket is listening at fd %d\n", params->daemon, socket_fd); else { if (socket_fd == -2) { fprintf(stderr, "%s: command socket already in use\n", params->daemon); error_exit("%s: please check if there is another instance of haveged running\n", params->daemon); } else { fprintf(stderr, "%s: can not initialize command socket: %s\n", params->daemon, strerror(errno)); fprintf(stderr, "%s: disabling command mode for this instance\n", params->daemon); } } /* Initilize named semaphore to synchronize command isntances */ sem = sem_open(SEM_NAME, O_CREAT, 0644, 1); if (sem == NULL) { error_exit("Couldn't create nammed semaphore " SEM_NAME" error: %s", strerror(errno)); } } #endif if (params->tests_config == 0) params->tests_config = (0 != (params->setup & RUN_AS_APP))? TESTS_DEFAULT_APP : TESTS_DEFAULT_RUN; memset(&cmd, 0, sizeof(H_PARAMS)); cmd.collectSize = params->buffersz; cmd.icacheSize = params->i_cache; cmd.dcacheSize = params->d_cache; cmd.options = params->verbose & 0xff; cmd.nCores = params->ncores; cmd.testSpec = params->tests_config; cmd.msg_out = print_msg; if (0 != (params->setup & RUN_AS_APP)) { cmd.ioSz = APP_BUFF_SIZE * sizeof(H_UINT); if (params->verbose!=0 && 0==(params->setup & RANGE_SPEC)) params->run_level = 1; } #ifndef NO_DAEMON else { poolSize = get_poolsize(); i = (poolSize + 7)/8 * sizeof(H_UINT); cmd.ioSz = sizeof(struct rand_pool_info) + i *sizeof(H_UINT); } #endif if (0 != (params->verbose & H_DEBUG_TIME)) cmd.metering = show_meterInfo; if (0 !=(params->setup & CAPTURE) && 0 != (params->run_level == DIAG_RUN_CAPTURE)) cmd.options |= H_DEBUG_RAW_OUT; #ifdef RAW_IN_ENABLE if (0 !=(params->setup & INJECT) && 0 != (params->run_level & (DIAG_RUN_INJECT|DIAG_RUN_TEST))) { if (strcmp(params->sample_in,"-") == 0 ) fd_in = stdin; else fd_in = fopen(params->sample_in, "rb"); if (NULL == fd_in) error_exit("Unable to open: %s", params->sample_in); cmd.injection = injectFile; if (params->run_level==DIAG_RUN_INJECT) cmd.options |= H_DEBUG_RAW_IN; else if (params->run_level==DIAG_RUN_TEST) cmd.options |= H_DEBUG_TEST_IN; else usage(1, nopts, long_options, cmds); } #endif handle = havege_create(&cmd); ierr = handle==NULL? H_NOHANDLE : handle->error; switch(ierr) { case H_NOERR: break; case H_NOTESTSPEC: error_exit("unrecognized test setup: %s", cmd.testSpec); break; default: error_exit("Couldn't initialize haveged (%d)", ierr); } if (0 != (params->setup & RUN_AS_APP)) { if (params->run_level==1) anchor_info(handle); else if (0==(params->setup&(INJECT|CAPTURE))) { /* must specify range with --number or --run > 1 but not both */ if (params->run_level>1) { if (0==(params->setup&RANGE_SPEC)) { /* --run specified */ bufct = params->run_level/sizeof(H_UINT); bufrem = (params->run_level%sizeof(H_UINT))*1024; } else usage(2, nopts, long_options, cmds); /* both specified */ } else if (0==(params->setup&RANGE_SPEC)) usage(3,nopts, long_options, cmds); /* neither specified */ else if (0==(params->setup&USE_STDOUT)&&(bufct+bufrem)==0) usage(4, nopts, long_options, cmds); /* only with stdout */ run_app(handle, bufct, bufrem); } else if (0==(params->setup&USE_STDOUT)&&(bufct+bufrem)==0) usage(5, nopts, long_options, cmds); /* only with stdout */ else run_app(handle, bufct, bufrem); } #ifndef NO_DAEMON else run_daemon(handle, path, argv); #endif havege_destroy(handle); exit(0); } #ifndef NO_DAEMON /** * The usual daemon setup */ static void daemonize( /* RETURN: nothing */ void) /* IN: nothing */ { FILE *fh; openlog(params->daemon, LOG_CONS, LOG_DAEMON); syslog(LOG_NOTICE, "%s starting up", params->daemon); if (daemon(0, 0) == -1) error_exit("Cannot fork into the background"); fh = fopen(params->pid_file, "w"); if (!fh) error_exit("Couldn't open PID file \"%s\" for writing: %s.", params->pid_file, strerror(errno)); fprintf(fh, "%i", getpid()); fclose(fh); params->detached = 1; } /** * Get configured poolsize */ static int get_poolsize( /* RETURN: number of bits */ void) /* IN: nothing */ { FILE *poolsize_fh,*osrel_fh; unsigned int major,minor; int max_bits; poolsize_fh = fopen(params->poolsize, "rb"); if (poolsize_fh) { if (fscanf(poolsize_fh, "%d", &max_bits)!=1) max_bits = -1; fclose(poolsize_fh); osrel_fh = fopen(params->os_rel, "rb"); if (osrel_fh) { if (fscanf(osrel_fh,"%u.%u", &major, &minor)<2) major = minor = 0; fclose(osrel_fh); if (major==2 && minor==4) max_bits *= 8; } } else max_bits = -1; if (max_bits < 1) error_exit("Couldn't get poolsize"); return max_bits; } /** * Run as a daemon writing to random device entropy pool */ static void run_daemon( /* RETURN: nothing */ H_PTR h, /* IN: app instance */ const volatile char *path, char *const argv[]) { int random_fd = -1; #ifndef NO_COMMAND_MODE int conn_fd = -1; #endif struct rand_pool_info *output; struct stat stat_buf; time_t t[2]; if (0 != params->run_level) { anchor_info(h); return; } if (params->foreground==0) { daemonize(); havege_reparent(handle); } else printf ("%s starting up\n", params->daemon); if (0 != havege_run(h)) error_exit("Couldn't initialize HAVEGE rng %d", h->error); if (0 != (params->verbose & H_DEBUG_INFO)) anchor_info(h); if (params->low_water>0) set_watermark(params->low_water); if ( lstat(params->random_device, &stat_buf) != 0 ) error_exit("lstat has failed for the random device \"%s\": %s", params->random_device, strerror(errno)); if ( S_ISLNK(stat_buf.st_mode) ) error_exit("random device \"%s\" is a link. This is not supported for the security reasons.", params->random_device); random_fd = open(params->random_device, O_RDWR); if (random_fd == -1) error_exit("Couldn't open random device: %s", strerror(errno)); output = (struct rand_pool_info *) h->io_buf; #if NUMBER_CORES>1 pthread_sigmask(SIG_BLOCK, &mask, &omask); #else sigprocmask(SIG_BLOCK, &mask, &omask); #endif t[0] = 0; for(;;) { int current,nbytes,r,max=0; H_UINT fills; char buf[120]; fd_set write_fd; #ifndef NO_COMMAND_MODE fd_set read_fd; #endif if (params->exit_code > 128) error_exit("Stopping due to signal %d\n", params->exit_code - 128); t[1] = time(NULL); if (t[1] - t[0] > params->time_interval) { /* add entropy on daemon start and then every TIME_INTERVAL seconds unconditionally */ nbytes = poolSize; r = (nbytes+sizeof(H_UINT)-1)/sizeof(H_UINT); fills = h->n_fills; if (havege_rng(h, (H_UINT *)output->buf, r)<1) error_exit("RNG failed! %d", h->error); output->buf_size = nbytes; /* entropy is 8 bits per byte */ output->entropy_count = nbytes * 8; if (ioctl(random_fd, RNDADDENTROPY, output) == -1) error_exit("RNDADDENTROPY failed!"); h->n_entropy_bytes += nbytes; if (params->once == 1) { params->exit_code = 0; error_exit("Entropy refilled once (%d bytes), exiting.", nbytes); } if (0 != (params->verbose & H_RNDADDENTROPY_INFO) && h->n_fills > fills) { if (havege_status_dump(h, H_SD_TOPIC_SUM, buf, sizeof(buf))>0) print_msg("%s\n", buf); } t[0] = t[1]; continue; } FD_ZERO(&write_fd); #ifndef NO_COMMAND_MODE if (socket_fd >= 0) { FD_ZERO(&read_fd); } #endif FD_SET(random_fd, &write_fd); if (random_fd > max) max = random_fd; #ifndef NO_COMMAND_MODE if (socket_fd >= 0) { FD_SET(socket_fd, &read_fd); if (socket_fd > max) max = socket_fd; if (conn_fd >= 0) { FD_SET(conn_fd, &read_fd); if (conn_fd > max) max = conn_fd; } } #endif for(;;) { struct timespec timeout = {PSELECT_TIMEOUT, 0}; int rc; #ifndef NO_COMMAND_MODE if (socket_fd >= 0) { rc = pselect(max+1, &read_fd, &write_fd, NULL, &timeout, &omask); } else { rc = pselect(max+1, NULL, &write_fd, NULL, &timeout, &omask); } #else rc = pselect(max+1, NULL, &write_fd, NULL, &timeout, &omask); #endif t[1] = time(NULL); if (t[1] - t[0] > params->time_interval) break; if (rc >= 0) break; if (params->exit_code > 128) break; if (errno != EINTR) error_exit("Select error: %s", strerror(errno)); } if (params->exit_code > 128) continue; #ifndef NO_COMMAND_MODE if ( socket_fd >= 0 && FD_ISSET(socket_fd, &read_fd) && conn_fd < 0) { # ifdef HAVE_ACCEPT4 conn_fd = accept4(socket_fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); if (conn_fd < 0 && (errno == ENOSYS || errno == ENOTSUP)) { # endif conn_fd = accept(socket_fd, NULL, NULL); if (conn_fd >= 0) { fcntl(conn_fd, F_SETFL, O_NONBLOCK); fcntl(conn_fd, F_SETFD, FD_CLOEXEC); } # ifdef HAVE_ACCEPT4 } # endif if (conn_fd >= 0) continue; } if (conn_fd >= 0 && FD_ISSET(conn_fd, &read_fd)) conn_fd = socket_handler(conn_fd, path, argv, params); #endif if (!FD_ISSET(random_fd, &write_fd)) continue; if (ioctl(random_fd, RNDGETENTCNT, ¤t) == -1) error_exit("Couldn't query entropy-level from kernel"); /* get number of bytes needed to fill pool */ nbytes = (poolSize - current + 7)/8; if(nbytes<1) continue; /* get that many random bytes */ r = (nbytes+sizeof(H_UINT)-1)/sizeof(H_UINT); fills = h->n_fills; if (havege_rng(h, (H_UINT *)output->buf, r)<1) error_exit("RNG failed! %d", h->error); output->buf_size = nbytes; /* entropy is 8 bits per byte */ output->entropy_count = nbytes * 8; t[0] = t[1]; if (ioctl(random_fd, RNDADDENTROPY, output) == -1) error_exit("RNDADDENTROPY failed!"); h->n_entropy_bytes += nbytes; if (0 != (params->verbose & H_RNDADDENTROPY_INFO) && h->n_fills > fills) { if (havege_status_dump(h, H_SD_TOPIC_SUM, buf, sizeof(buf))>0) print_msg("%s\n", buf); } } } /** * Set random write threshold */ static void set_watermark( /* RETURN: nothing */ int level) /* IN: threshold */ { FILE *wm_fh; if ( level > (poolSize - 32)) level = poolSize - 32; wm_fh = fopen(params->watermark, "w"); if (wm_fh) { fprintf(wm_fh, "%d\n", level); fclose(wm_fh); } else if (errno == EACCES) fprintf(stderr, "No access to %s, can't set watermark (maybe running in a container?)\n", params->watermark); else error_exit("Fail:set_watermark()!"); } #endif /** * Display handle information */ static void anchor_info(H_PTR h) { char buf[120]; H_SD_TOPIC topics[4] = {H_SD_TOPIC_BUILD, H_SD_TOPIC_TUNE, H_SD_TOPIC_TEST, H_SD_TOPIC_SUM}; int i; for(i=0;i<4;i++) if (havege_status_dump(h, topics[i], buf, sizeof(buf))>0) print_msg("%s\n", buf); } /** * Bail.... */ void error_exit( /* RETURN: nothing */ const char *format, /* IN: msg format */ ...) /* IN: varadic args */ { char buffer[4096]; va_list ap; va_start(ap, format); vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); #ifndef NO_DAEMON if (params->detached!=0) { unlink(params->pid_file); syslog(LOG_INFO, "%s: %s", params->daemon, buffer); } else #endif { fprintf(stderr, "%s: %s\n", params->daemon, buffer); if (0 !=(params->setup & (RUN_AS_APP | RUN_IN_FG) ) && 0 != handle) { if (havege_status_dump(handle, H_SD_TOPIC_TEST, buffer, sizeof(buffer))>0) fprintf(stderr, "%s\n", buffer); if (havege_status_dump(handle, H_SD_TOPIC_SUM, buffer, sizeof(buffer))>0) fprintf(stderr, "%s\n", buffer); } } havege_destroy(handle); exit(params->exit_code); } /** * Implement fixed point shorthand for run sizes */ static int get_runsize( /* RETURN: the size */ H_UINT *bufct, /* OUT: nbr app buffers */ H_UINT *bufrem, /* OUT: residue */ char *bp) /* IN: the specification */ { char *suffix; double f; int p2 = 0; int p10 = APP_BUFF_SIZE * sizeof(H_UINT); long long ct; f = strtod(bp, &suffix); if (f < 0 || strlen(suffix)>1) return 1; switch(*suffix) { case 't': case 'T': p2 += 1; /* fallthrough */ case 'g': case 'G': p2 += 1; /* fallthrough */ case 'm': case 'M': p2 += 1; /* fallthrough */ case 'k': case 'K': p2 += 1; /* fallthrough */ case 0: break; default: return 2; } while(p2-- > 0) f *= 1024; ct = f; if (f != 0 && ct==0) return 3; if ((double) (ct+1) < f) return 3; *bufrem = (H_UINT)(ct%p10); *bufct = (H_UINT)(ct/p10); if (*bufct == (ct/p10)) return 0; /* hack to allow 16t */ ct -= 1; *bufrem = (H_UINT)(ct%p10); *bufct = (H_UINT)(ct/p10); return (*bufct == (ct/p10))? 0 : 4; } #ifdef RAW_IN_ENABLE /** * The injection diagnostic */ static int injectFile( /* RETURN: not used */ volatile H_UINT *pData, /* OUT: data buffer */ H_UINT szData) /* IN: H_UINT needed */ { int r; if ((r=fread((void *)pData, sizeof(H_UINT), szData, fd_in)) != szData) error_exit("Cannot read data in file: %d!=%d", r, szData); return 0; } #endif /** * Pretty print the collection size */ static char *ppSize( /* RETURN: the formatted size */ char *buffer, /* IN: work space */ double sz) /* IN: the size */ { char units[] = {'T', 'G', 'M', 'K', 0}; double factor = 1024.0 * 1024.0 * 1024.0 * 1024.0; int i; for (i=0;0 != units[i];i++) { if (sz >= factor) break; factor /= 1024.0; } snprintf(buffer, 32, "%.4g %c byte", sz / factor, units[i]); return buffer; } /** * Execution notices - to stderr or syslog */ void print_msg( /* RETURN: nothing */ const char *format, /* IN: format string */ ...) /* IN: args */ { char buffer[128]; va_list ap; va_start(ap, format); snprintf(buffer, sizeof(buffer), "%s: %s", params->daemon, format); #ifndef NO_DAEMON if (params->detached != 0) vsyslog(LOG_INFO, buffer, ap); else #endif vfprintf(stderr, buffer, ap); va_end(ap); } /** * Run as application writing to a file */ static void run_app( /* RETURN: nothing */ H_PTR h, /* IN: app instance */ H_UINT bufct, /* IN: # buffers to fill */ H_UINT bufres) /* IN: # bytes extra */ { H_UINT *buffer; FILE *fout = NULL; H_UINT ct=0; int limits = bufct; if (0 != havege_run(h)) error_exit("Couldn't initialize HAVEGE rng %d", h->error); if (0 != (params->setup & USE_STDOUT)) { params->sample_out = "stdout"; fout = stdout; } else if (!(fout = fopen (params->sample_out, "wb"))) error_exit("Cannot open file <%s> for writing.\n", params->sample_out); limits = bufct!=0? 1 : bufres != 0; buffer = (H_UINT *)h->io_buf; #ifdef RAW_IN_ENABLE { char *format, *in="",*out,*sz,*src=""; if (params->run_level==DIAG_RUN_INJECT) in = "tics"; else if (params->run_level==DIAG_RUN_TEST) in = "data"; if (*in!=0) { src =(fd_in==stdin)? "stdin" : params->sample_in; format = "Inject %s from %s, writing %s bytes to %s\n"; } else format = "Writing %s%s%s bytes to %s\n"; if (limits) sz = ppSize((char *)buffer, (1.0 * bufct) * APP_BUFF_SIZE * sizeof(H_UINT) + bufres); else sz = "unlimited"; out = (fout==stdout)? "stdout" : params->sample_out; fprintf(stderr, format, in, src, sz, out); } #else if (limits) fprintf(stderr, "Writing %s output to %s\n", ppSize((char *)buffer, (1.0 * bufct) * APP_BUFF_SIZE * sizeof(H_UINT) + bufres), params->sample_out); else fprintf(stderr, "Writing unlimited bytes to stdout\n"); #endif while(!limits || ct++ < bufct) { if (havege_rng(h, buffer, APP_BUFF_SIZE)<1) error_exit("RNG failed %d!", h->error); if (fwrite (buffer, 1, APP_BUFF_SIZE * sizeof(H_UINT), fout) == 0) error_exit("Cannot write data in file: %s", strerror(errno)); } ct = (bufres + sizeof(H_UINT) - 1)/sizeof(H_UINT); if (ct) { if (havege_rng(h, buffer, ct)<1) error_exit("RNG failed %d!", h->error); if (fwrite (buffer, 1, bufres, fout) == 0) error_exit("Cannot write data in file: %s", strerror(errno)); } fclose(fout); if (0 != (params->verbose & H_DEBUG_INFO)) anchor_info(h); } /** * Show collection info. */ static void show_meterInfo( /* RETURN: nothing */ H_UINT id, /* IN: identifier */ H_UINT event) /* IN: start/stop */ { struct timeval tm; /* N.B. if multiple thread, each child gets its own copy of this */ static H_METER status; gettimeofday(&tm, NULL); if (event == 0) status.estart = ((double)tm.tv_sec*1000.0 + (double)tm.tv_usec/1000.0); else { status.etime = ((double)tm.tv_sec*1000.0 + (double)tm.tv_usec/1000.0); if ((status.etime -= status.estart)<0.0) status.etime=0.0; status.n_fill += 1; print_msg("%d fill %g ms\n", id, status.etime); } } /** * Signal handler */ static void tidy_exit( /* OUT: nothing */ int signum) /* IN: signal number */ { params->exit_code = 128 + signum; } /** * send usage display to stderr */ static void usage( /* OUT: nothing */ int loc, /* IN: debugging aid */ int nopts, /* IN: number of options */ struct option *long_options, /* IN: long options */ const char **cmds) /* IN: associated text */ { int i, j; (void)loc; fprintf(stderr, "\nUsage: %s [options]\n\n", params->daemon); #ifndef NO_DAEMON fprintf(stderr, "Collect entropy and feed into random pool or write to file.\n"); #else fprintf(stderr, "Collect entropy and write to file.\n"); #endif fprintf(stderr, " Options:\n"); for(i=j=0;long_options[i].val != 0;i++,j+=4) { while(cmds[j][0] != long_options[i].val && (j+4) < (nopts * 4)) j += 4; fprintf(stderr," --%-13s, -%c %s %s\n", long_options[i].name, long_options[i].val, long_options[i].has_arg? "[]":" ",cmds[j+3]); } fprintf(stderr, "\n"); exit(1); }