diff options
Diffstat (limited to '')
| -rw-r--r-- | Makefile | 10 | ||||
| -rw-r--r-- | avg.c | 94 | ||||
| -rw-r--r-- | common.h | 57 | ||||
| -rw-r--r-- | deadshred.c | 400 | ||||
| -rw-r--r-- | fmt.c | 189 | ||||
| -rw-r--r-- | io.c | 25 | ||||
| -rw-r--r-- | rnd.c | 80 | ||||
| -rw-r--r-- | text.c | 46 | 
8 files changed, 515 insertions, 386 deletions
@@ -4,9 +4,15 @@ CONFIGFILE = config.mk  include $(CONFIGFILE)  OBJ =\ -	deadshred.o +	deadshred.o\ +	io.o\ +	fmt.o\ +	text.o\ +	avg.o\ +	rnd.o -HDR = +HDR =\ +	common.h  all: deadshred  $(OBJ): $(HDR) @@ -0,0 +1,94 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/* TODO deal with machine and process suspension */ + + +static struct timespec write_average_begin_times[5]; +static off_t write_average_amounts[ELEMSOF(write_average_begin_times)] = {0}; +static int write_average_i = 0; + + +static int +was_write_average_overrun(int i, const struct timespec *now, int seconds) +{ +	struct timespec diff; +	libsimple_difftimespec(&diff, now, &write_average_begin_times[i]); +	if (diff.tv_sec >= seconds) +		return 1; +	if (diff.tv_sec == seconds - 1 && diff.tv_nsec >= DECISECONDS(9)) +		return 1; +	return 0; +} + + +static void +shift_write_average(void) +{ +	write_average_i--; +	memmove(&write_average_begin_times[0], &write_average_begin_times[1], +	        (size_t)write_average_i * sizeof(*write_average_begin_times)); +	memmove(&write_average_amounts[0], &write_average_amounts[1], +	        (size_t)write_average_i * sizeof(*write_average_amounts)); +} + + +void +wravg_init(const struct timespec *start_time) +{ +	write_average_begin_times[0] = *start_time; +} + + +void +wravg_update_write(ssize_t amount) +{ +	if (amount > 0) +		write_average_amounts[write_average_i] += (off_t)amount; +} + + +void +wravg_update_time(const struct timespec *now) +{ +	if (was_write_average_overrun(write_average_i, now, 1)) { +		write_average_i++; +		if (write_average_i == ELEMSOF(write_average_amounts)) +			shift_write_average(); +		write_average_begin_times[write_average_i] = *now; +		write_average_amounts[write_average_i] = 0; +	} +	while (write_average_i && was_write_average_overrun(0, now, (int)ELEMSOF(write_average_amounts))) +		shift_write_average(); +} + + +const char * +wravg_get(const struct timespec *now) +{ +	static char buf[512]; + +	struct timespec write_average_time; +	off_t write_average_sum = 0; +	double write_average; +	int i; + +	for (i = 0; i <= write_average_i; i++) +		write_average_sum += write_average_amounts[i]; + +	libsimple_difftimespec(&write_average_time, now, &write_average_begin_times[0]); + +	if (write_average_time.tv_sec < 0) +		return "-"; +	if (write_average_time.tv_sec == 0 && write_average_time.tv_nsec < CENTISECONDS(10)) +		return "-"; +	if (write_average_sum == 0) +		return "0"; + +	write_average = (double)write_average_time.tv_nsec; +	write_average /= WHOLE_SECOND; +	write_average += (double)write_average_time.tv_sec; +	write_average = (double)write_average_sum / write_average; +	return humanbytespersecond(write_average, buf); +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..875375c --- /dev/null +++ b/common.h @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/mount.h> +#include <pthread.h> +#include <libsimple.h> + + +#define NANOSECONDS(X) X##L +#define MICROSECONDS(X) NANOSECONDS(X##000) +#define MILLISECONDS(X) MICROSECONDS(X##000) +#define CENTISECONDS(X) MILLISECONDS(X##0) +#define DECISECONDS(X) CENTISECONDS(X##0) +#define WHOLE_SECOND DECISECONDS(10) + + +enum direction { +	FORWARDS = 0, +	BACKWARDS = 1 +}; + +struct span { +	off_t start; +	off_t end; +	off_t bad; +	size_t blocksize; +}; + + +/* io.c */ +off_t filesize(int fd, const char *fname); + +/* fmt.c */ +const char *humansize1000(off_t s, char *buf); +const char *humansize1024(off_t s, char *buf); +#if defined(__GNUC__) +__attribute__((__pure__)) +#endif +off_t unhumansize(const char *s, char flag); +const char *durationstr(const struct timespec *dur, char *buf, int second_decimals); +const char *humanbytespersecond(double bytes_per_second, char *buf); + +/* text.c */ +int have_micro_symbol(void); + +/* avg.c */ +void wravg_init(const struct timespec *start_time); +void wravg_update_write(ssize_t amount); +void wravg_update_time(const struct timespec *now); +const char *wravg_get(const struct timespec *now); + +/* rnd.c */ +#if defined(__GNUC__) +__attribute__((__const__)) +#endif +size_t max_blksize(void); +void init_random(int fd, const char *fname); +const char *get_random(size_t needed); +void used_random(ssize_t amount); diff --git a/deadshred.c b/deadshred.c index 50528c4..5956115 100644 --- a/deadshred.c +++ b/deadshred.c @@ -1,30 +1,11 @@  /* See LICENSE file for copyright and license details. */ -#include <sys/mount.h> -#include <pthread.h> -#include <libsimple.h> +#include "common.h"  #include <libsimple-arg.h> +/* TODO why has the program started to freeze (since multithreading?)*/ -USAGE("[-o offset] [-l length | -e postend] device [< random-source]"); - -#define NANOSECONDS(X) X##L -#define MICROSECONDS(X) NANOSECONDS(X##000) -#define MILLISECONDS(X) MICROSECONDS(X##000) -#define CENTISECONDS(X) MILLISECONDS(X##0) -#define DECISECONDS(X) CENTISECONDS(X##0) -#define WHOLE_SECOND DECISECONDS(10) -enum direction { -	FORWARDS = 0, -	BACKWARDS = 1 -}; - -struct span { -	off_t start; -	off_t end; -	off_t bad; -	size_t blocksize; -}; +USAGE("[-o offset] [-l length | -e postend] device [< random-source]");  static _Atomic volatile sig_atomic_t exiting = 0; @@ -36,10 +17,6 @@ static size_t spans_size = 0;  static off_t shredded = 0;  static off_t total_size = 0; -static char reservoir[128U << 10]; -static size_t reservoir_off = sizeof(reservoir); -static int use_stdin; -  static char total_size_1000[256];  static char total_size_1024[256]; @@ -58,12 +35,7 @@ static const struct timespec poll_timeout            = {0, MILLISECONDS(500)};  static int progress_print_sig_pipe[2];  static pthread_mutex_t progress_mutex; -/* TODO deal with machine and process suspension */ -static struct timespec write_average_begin_times[5]; -static off_t write_average_amounts[ELEMSOF(write_average_begin_times)] = {0}; -static int write_average_i = 0; - -static enum direction direction = FORWARDS; +static enum direction direction = FORWARDS; /* TODO add option (-b) to switch start direction */  static uintmax_t pass_nr = 1; @@ -75,60 +47,6 @@ signal_handler(int signo)  } -static off_t -filesize(int fd, const char *fname) -{ -	struct stat st; - -	if (fstat(fd, &st)) -		eprintf("fstat %s:", fname); - -	switch (st.st_mode & S_IFMT) { -	case S_IFREG: -		break; -	case S_IFBLK: -		if (ioctl(fd, BLKGETSIZE64, &st.st_size) < 0) -			eprintf("ioctl %s BLKGETSIZE64:", fname); -		break; -	default: -		eprintf("%s: not a regular file or block device", fname); -	} - -	return st.st_size; -} - - -static void -ensure_random(size_t needed) -{ -	size_t off; -	ssize_t r; - -	if (sizeof(reservoir) - reservoir_off >= needed) -		return; - -	if (!use_stdin) { -		libsimple_random_bytes(&libsimple_random_bits, NULL, reservoir, reservoir_off); -		reservoir_off = 0; -		return; -	} - -	for (off = 0; off < reservoir_off;) { -		r = read(STDIN_FILENO, &reservoir[off], reservoir_off - off); -		if (r <= 0) { -			if (!r) -				eprintf("random source depleted"); -			if (errno == EINTR) -				continue; -			eprintf("read <stdin>:"); -		} -		off += (size_t)r; -	} - -	reservoir_off = 0; -} - -  static void  add_span(off_t off, off_t amount, size_t blocksize, int try_join)  { @@ -160,222 +78,6 @@ add_span(off_t off, off_t amount, size_t blocksize, int try_join)  } -static char * -humansize1000(off_t s, char *buf) -{ -	const char *units = "kMGTPEZYRQ"; -	size_t unit = 0; -	if (s < 1000) { -		sprintf(buf, "%u B", (unsigned)s); -		return buf; -	} -	s /= 100; -	while (units[unit + 1U] && s >= 10000) { -		s /= 1000; -		unit++; -	} -	sprintf(buf, "%u.%u %cB", (unsigned)s / 10U, (unsigned)s % 10U, units[unit]); -	return buf; -} - - -static char * -humansize1024(off_t s, char *buf) -{ -	const char *units = "KMGTPEZYRQ"; -	size_t unit = 0; -	if (s < 1024) { -		sprintf(buf, "%u B", (unsigned)s); -		return buf; -	} -	while (units[unit + 1U] && s >= 1024 * 1024) { -		s /= 1024; -		unit++; -	} -	sprintf(buf, "%lu.%lu %ciB", (unsigned long int)s / 1024UL, (unsigned long int)(s * 10 % 10240) / 1024UL, units[unit]); -	return buf; -} - - -#if defined(__GNUC__) -__attribute__((__pure__)) -#endif -static off_t -unhumansize(const char *s, char flag) -{ -	off_t sum = 0, term, digit, divisor, power, base; - -	if (!isdigit(s[0]) && !(s[0] == '-' && isdigit(s[1]))) -		usage(); - -	do { -		divisor = 1; -		term = 0; -		while (isdigit(*s)) { -			digit = (*s++ & 15); -			if (term > (OFF_MAX - digit) / 10) -				eprintf("value of -%c flag is too large", flag); -			term = term * 10 + digit; -		} -		if (*s == '.') { -			s++; -			while (isdigit(*s)) { -				digit = (*s++ & 15); -				if (term > (OFF_MAX - digit) / 10) -					eprintf("value of -%c flag is too large", flag); -				term = term * 10 + digit; -				divisor *= 10; -			} -		} - -		power = 0; -		switch (*s) { -		case 'Q': power++; /* fall through */ -		case 'R': power++; /* fall through */ -		case 'Y': power++; /* fall through */ -		case 'Z': power++; /* fall through */ -		case 'E': power++; /* fall through */ -		case 'P': power++; /* fall through */ -		case 'T': power++; /* fall through */ -		case 'G': power++; /* fall through */ -		case 'M': power++; /* fall through */ -		case 'k': case 'K': power++; -			if (s[1] == 'i' || s[2] == 'B') { -				base = 1024; -				s = &s[3]; -			} else if (s[1] == 'B') { -				base = 1000; -				s = &s[2]; -			} else { -				base = 1024; -				s = &s[1]; -			} -			while (power) { -				term *= base; -				power--; -			} -			break; -		case 'B': -			if (!power && divisor > 1) -				usage(); -			s++; -			break; -		default: -			break; -		} -		sum += term /= divisor; - -		while (*s == ' ' || *s == ',' || *s == '+') -			s++; - -	} while (isdigit(s[0]) || (s[0] == '.' && isdigit(s[1]))); - -	return sum; -} - - -static const char * -durationstr(const struct timespec *dur, char *buf, int second_decimals) -{ -	uintmax_t ss, s, m, h, d, ss_div = (uintmax_t)WHOLE_SECOND; -	char *p; -	const char *unit; -	int i; - -	if (dur->tv_sec < 0 || dur->tv_nsec < 0) -		return "-"; - -	if (second_decimals < 0) -		second_decimals = 0; -	else if (second_decimals > 9) -		second_decimals = 9; - -	for (i = 0; i < second_decimals; i++) -		ss_div /= 10U; -	ss = (uintmax_t)dur->tv_nsec / ss_div; -	s = (uintmax_t)dur->tv_sec % 60U; -	m = (uintmax_t)dur->tv_sec / 60U % 60U; -	h = (uintmax_t)dur->tv_sec / 60U / 60U % 24U; -	d = (uintmax_t)dur->tv_sec / 60U / 60U / 24U; - -	p = buf; -	if (d) -		p += sprintf(p, "%ju days, ", d); -	if (h) { -		p += sprintf(p, "%ju:%02ju:%02ju", h, m, s); -		unit = "hours"; -	} else if (m) { -		p += sprintf(p, "%ju:%02ju", m, s); -		unit = "minutes"; -	} else { -		p += sprintf(p, "%ju", s); -		unit = "seconds"; -	} -	if (second_decimals) -		p += sprintf(p, ".%0*ju", second_decimals, ss); -	p += sprintf(p, " %s", unit); - -	return buf; -} - - -#if defined(__linux__) -#include <linux/kd.h> -static int -have_micro_symbol(void) -{ -	static int ret = -1; -	if (ret < 0) { -		struct unimapdesc desc; -		struct unipair *pairs = NULL; -		size_t i; -		ret = 1; -		desc.entry_ct = 0; -		desc.entries = NULL; -		if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) -			if (!desc.entry_ct) -				goto out; -		desc.entries = pairs = ecalloc(desc.entry_ct, sizeof(*pairs)); -		if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) -			goto out; -		for (i = 0; i < desc.entry_ct; i++) -			if (desc.entries[i++].unicode == 0xB5U) -				goto out; -		ret = 0; -	out: -		free(pairs); -	} -	return ret; -} -#else -# define have_micro_symbol() 1 -#endif - - -static int -was_write_average_overrun(int i, const struct timespec *now, int seconds) -{ -	struct timespec diff; -	libsimple_difftimespec(&diff, now, &write_average_begin_times[i]); -	if (diff.tv_sec >= seconds) -		return 1; -	if (diff.tv_sec == seconds - 1 && diff.tv_nsec >= DECISECONDS(9)) -		return 1; -	return 0; -} - - -static void -shift_write_average(void) -{ -	write_average_i--; -	memmove(&write_average_begin_times[0], &write_average_begin_times[1], -	        (size_t)write_average_i * sizeof(*write_average_begin_times)); -	memmove(&write_average_amounts[0], &write_average_amounts[1], -	        (size_t)write_average_i * sizeof(*write_average_amounts)); -} - -  static void  print_progress(int done, const struct timespec *now)  { @@ -390,48 +92,8 @@ print_progress(int done, const struct timespec *now)  	char subbuf5[256];  	char subbuf6[256];  	char subbuf7[256]; -	char write_average_buf[512];  	struct timespec since_success = {-1, 0}; -	struct timespec time_spent, write_average_time; -	int i; -	off_t write_average_sum = 0; -	double write_average; - -	for (i = 0; i <= write_average_i; i++) -		write_average_sum += write_average_amounts[i]; -	libsimple_difftimespec(&write_average_time, now, &write_average_begin_times[0]); -	if (write_average_time.tv_sec < 0 || (write_average_time.tv_sec == 0 && write_average_time.tv_nsec < CENTISECONDS(10))) { -		stpcpy(write_average_buf, "-"); -	} else if (write_average_sum == 0) { -		stpcpy(write_average_buf, "0"); -	} else { -		const char *units_big = "kMGTPEZYRQ"; -		const char *units_small = "munpfazyrq"; -		int unit = -1; -		write_average = (double)write_average_time.tv_nsec; -		write_average /= WHOLE_SECOND; -		write_average += (double)write_average_time.tv_sec; -		write_average = (double)write_average_sum / write_average; -		if (write_average < (double)0.01f) { -			do { -				write_average *= 1000; -				unit++; -			} while (units_small[unit + 1] && write_average < (double)0.01f); -			if (units_small[unit] == 'u' && have_micro_symbol()) -				sprintf(write_average_buf, "%.02lf µB/s", write_average); -			else -				sprintf(write_average_buf, "%.02lf %cB/s", write_average, units_small[unit]); -		} else { -			while (units_big[unit + 1] && write_average >= 1000) { -				write_average /= 1000; -				unit++; -			} -			if (unit < 0) -				sprintf(write_average_buf, "%.02lf B/s", write_average); -			else -				sprintf(write_average_buf, "%.02lf %cB/s", write_average, units_big[unit]); -		} -	} +	struct timespec time_spent;  	if (last_success.tv_sec >= 0) {  		libsimple_difftimespec(&since_success, now, &last_success); @@ -465,7 +127,7 @@ print_progress(int done, const struct timespec *now)  	        humansize1024(bad_bytes, subbuf4),  	        /* } line 3 { */  	        durationstr(&time_spent, subbuf5, 1), -		write_average_buf, +	        wravg_get(now),  	        /* } line 4 { */  	        durationstr(&since_success, subbuf6, 2),  	        /* } line 5 { */ @@ -485,21 +147,6 @@ print_progress(int done, const struct timespec *now)  } -static void -update_progress(const struct timespec *now) -{ -	if (was_write_average_overrun(write_average_i, now, 1)) { -		write_average_i++; -		if (write_average_i == ELEMSOF(write_average_amounts)) -			shift_write_average(); -		write_average_begin_times[write_average_i] = *now; -		write_average_amounts[write_average_i] = 0; -	} -	while (write_average_i && was_write_average_overrun(0, now, (int)ELEMSOF(write_average_amounts))) -		shift_write_average(); -} - -  static void *  progress_print_loop(void *user)  { @@ -550,7 +197,7 @@ progress_print_loop(void *user)  			terminate = 1;  		}  		print_progress(0, &now); -		update_progress(&now); +		wravg_update_time(&now);  		pthread_mutex_unlock(&progress_mutex);  	} while (!terminate); @@ -566,6 +213,7 @@ shredspan(int fd, struct span *span, const char *fname)  	struct timespec now, when = {0, 0};  	int bad = span->bad > 0;  	int first_fail = 1; +	const char *random_data;  	pthread_mutex_lock(&progress_mutex); @@ -588,7 +236,6 @@ shredspan(int fd, struct span *span, const char *fname)  			libsimple_sumtimespec(&when, &now, &progress_print_interval);  			write(progress_print_sig_pipe[1], &now, sizeof(now));  		} -		ensure_random(span->blocksize);  		if (direction == FORWARDS) {  			n = MIN((off_t)span->blocksize, span->end - off);  		} else { @@ -600,9 +247,10 @@ shredspan(int fd, struct span *span, const char *fname)  			if (!n)  				break;  		} +		random_data = get_random(span->blocksize);  		pthread_mutex_unlock(&progress_mutex);  	pwrite_again: -		r = pwrite(fd, &reservoir[reservoir_off], (size_t)n, off); +		r = pwrite(fd, random_data, (size_t)n, off);  		if (r < 0) {  			if (errno == EINTR) {  				if (exiting) { @@ -649,8 +297,8 @@ shredspan(int fd, struct span *span, const char *fname)  			bad_writes += 1U;  		}  		shredded += (off_t)r; -		reservoir_off += (size_t)r; -		write_average_amounts[write_average_i] += (off_t)r; +		used_random(r); +		wravg_update_write(r);  		last_success = now;  		if (span->bad) {  			bad_bytes -= (off_t)r; @@ -748,30 +396,13 @@ main(int argc, char *argv[])  	if (fd < 0)  		eprintf("open %s O_WRONLY|O_DSYNC:", argv[0]); -	use_stdin = !isatty(STDIN_FILENO); -	if (use_stdin) { -		struct stat st; -		if (fstat(STDIN_FILENO, &st)) { -			if (errno == EBADF) -				use_stdin = 0; -			else -				eprintf("fstat <stdin>:"); -		} else { -			if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) -				weprintf("stdin is open but is not a TTY, character device, FIFO, or socket"); -		} -	} -	if (!use_stdin) { -		libsimple_srand(); -	} +	init_random(STDIN_FILENO, "<stdin>");  	spans = emalloc(sizeof(*spans));  	spans[0].start = 0;  	spans[0].end = filesize(fd, argv[0]);  	spans[0].bad = 0; -	spans[0].blocksize = sizeof(reservoir); -	while (spans[0].blocksize & (spans[0].blocksize - 1U)) -		spans[0].blocksize &= (spans[0].blocksize - 1U); +	spans[0].blocksize = max_blksize();  	nspans = 1U;  	spans_size = 1U; @@ -813,7 +444,7 @@ main(int argc, char *argv[])  		if (clock_gettime(clck, &start_time))  			eprintf("clock_gettime %s:", clkcstr);  	} -	write_average_begin_times[0] = start_time; +	wravg_init(&start_time);  	last_success = start_time;  	while (nspans) { @@ -824,6 +455,7 @@ main(int argc, char *argv[])  			memmove(&spans[0], &spans[i], (nspans -= i) * sizeof(*spans));  			break;  		} +		/* TODO bug: this is sometimes reached immediately on write failure */  		for (i = 0, j = nspans, nspans -= old_nspans; i < nspans;)  			spans[i++] = spans[--j];  		direction ^= 1; @@ -0,0 +1,189 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static const char *units_big = "KMGTPEZYRQ"; +static const char *units_small = "munpfazyrq"; /* 'u' should be "µ" */ + + +const char * +humansize1000(off_t s, char *buf) +{ +	size_t unit = 0; +	if (s < 1000) { +		sprintf(buf, "%u B", (unsigned)s); +		return buf; +	} +	s /= 100; +	while (units_big[unit + 1U] && s >= 10000) { +		s /= 1000; +		unit++; +	} +	sprintf(buf, "%u.%u %cB", (unsigned)s / 10U, (unsigned)s % 10U, units_big[unit]); +	return buf; +} + + +const char * +humansize1024(off_t s, char *buf) +{ +	size_t unit = 0; +	if (s < 1024) { +		sprintf(buf, "%u B", (unsigned)s); +		return buf; +	} +	while (units_big[unit + 1U] && s >= 1024 * 1024) { +		s /= 1024; +		unit++; +	} +	sprintf(buf, "%lu.%lu %ciB", (unsigned long int)s / 1024UL, (unsigned long int)(s * 10 % 10240) / 1024UL, units_big[unit]); +	return buf; +} + + +off_t +unhumansize(const char *s, char flag) +{ +	off_t sum = 0, term, digit, divisor, power, base; + +	if (!isdigit(s[0]) && !(s[0] == '-' && isdigit(s[1]))) +		eprintf("value of -%c is not a file size", flag); + +	do { +		divisor = 1; +		term = 0; +		while (isdigit(*s)) { +			digit = (*s++ & 15); +			if (term > (OFF_MAX - digit) / 10) +				eprintf("value of -%c option is too large", flag); +			term = term * 10 + digit; +		} +		if (*s == '.') { +			s++; +			while (isdigit(*s)) { +				digit = (*s++ & 15); +				if (term > (OFF_MAX - digit) / 10) +					eprintf("value of -%c option is too large", flag); +				term = term * 10 + digit; +				divisor *= 10; +			} +		} + +		power = 0; +		switch (*s) { +		case 'Q': power++; /* fall through */ +		case 'R': power++; /* fall through */ +		case 'Y': power++; /* fall through */ +		case 'Z': power++; /* fall through */ +		case 'E': power++; /* fall through */ +		case 'P': power++; /* fall through */ +		case 'T': power++; /* fall through */ +		case 'G': power++; /* fall through */ +		case 'M': power++; /* fall through */ +		case 'k': case 'K': power++; +			if (s[1] == 'i' || s[2] == 'B') { +				base = 1024; +				s = &s[3]; +			} else if (s[1] == 'B') { +				base = 1000; +				s = &s[2]; +			} else { +				base = 1024; +				s = &s[1]; +			} +			while (power) { +				term *= base; +				power--; +			} +			break; +		case 'B': +			s++; +			/* fall through */ +		default: +			if (divisor > 1) +				eprintf("value of -%c contains non-integer exact byte count", flag); +			break; +		} +		sum += term /= divisor; + +		while (*s == ' ' || *s == ',' || *s == '+') +			s++; + +	} while (isdigit(s[0]) || (s[0] == '.' && isdigit(s[1]))); + +	return sum; +} + + +const char * +durationstr(const struct timespec *dur, char *buf, int second_decimals) +{ +	uintmax_t ss, s, m, h, d, ss_div = (uintmax_t)WHOLE_SECOND; +	char *p; +	const char *unit; +	int i; + +	if (dur->tv_sec < 0 || dur->tv_nsec < 0) +		return "-"; + +	if (second_decimals < 0) +		second_decimals = 0; +	else if (second_decimals > 9) +		second_decimals = 9; + +	for (i = 0; i < second_decimals; i++) +		ss_div /= 10U; +	ss = (uintmax_t)dur->tv_nsec / ss_div; +	s = (uintmax_t)dur->tv_sec % 60U; +	m = (uintmax_t)dur->tv_sec / 60U % 60U; +	h = (uintmax_t)dur->tv_sec / 60U / 60U % 24U; +	d = (uintmax_t)dur->tv_sec / 60U / 60U / 24U; + +	p = buf; +	if (d) +		p += sprintf(p, "%ju days, ", d); +	if (h) { +		p += sprintf(p, "%ju:%02ju:%02ju", h, m, s); +		unit = "hours"; +	} else if (m) { +		p += sprintf(p, "%ju:%02ju", m, s); +		unit = "minutes"; +	} else { +		p += sprintf(p, "%ju", s); +		unit = "seconds"; +	} +	if (second_decimals) +		p += sprintf(p, ".%0*ju", second_decimals, ss); +	p += sprintf(p, " %s", unit); + +	return buf; +} + + +const char * +humanbytespersecond(double bytes_per_second, char *buf) +{ +	int unit = -1; + +	if (bytes_per_second < (double)0.01f) { +		do { +			bytes_per_second *= 1000; +			unit++; +		} while (units_small[unit + 1] && bytes_per_second < (double)0.01f); +		if (units_small[unit] == 'u' && have_micro_symbol()) +			sprintf(buf, "%.02lf µB/s", bytes_per_second); +		else +			sprintf(buf, "%.02lf %cB/s", bytes_per_second, units_small[unit]); +	} else { +		while (units_big[unit + 1] && bytes_per_second >= 1000) { +			bytes_per_second /= 1000; +			unit++; +		} +		if (unit < 0) +			sprintf(buf, "%.02lf B/s", bytes_per_second); +		else +			sprintf(buf, "%.02lf %cB/s", bytes_per_second, units_big[unit]); +	} + +	return buf; +} @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +off_t +filesize(int fd, const char *fname) +{ +	struct stat st; + +	if (fstat(fd, &st)) +		eprintf("fstat %s:", fname); + +	switch (st.st_mode & S_IFMT) { +	case S_IFREG: +		break; +	case S_IFBLK: +		if (ioctl(fd, BLKGETSIZE64, &st.st_size) < 0) +			eprintf("ioctl %s BLKGETSIZE64:", fname); +		break; +	default: +		eprintf("%s: not a regular file or block device", fname); +	} + +	return st.st_size; +} @@ -0,0 +1,80 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static char reservoir[128U << 10]; +static size_t reservoir_off = sizeof(reservoir); +static int random_fd; +static const char *random_fname; + + +size_t +max_blksize(void) +{ +	size_t ret = sizeof(reservoir); +	while (ret & (ret - 1U)) +		ret &= ret - 1U; +	return ret; +} + + +void +init_random(int fd, const char *fname) +{ +	struct stat st; + +	random_fname = fname; +	random_fd = fd; + +	errno = 0; +	if (isatty(random_fd) || errno == EBADF) { +	use_builtin_generator: +		random_fd = -1; +		libsimple_srand(); +	} else if (fstat(random_fd, &st)) { +		if (errno == EBADF) +			goto use_builtin_generator; +		eprintf("fstat %s:", random_fname); +	} else if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) { +		eprintf("%s is of a unsupported inode type (must be TTY, character device, FIFO, or socket)", fname); +	} +} + + +const char * +get_random(size_t needed) +{ +	size_t off; +	ssize_t r; + +	if (sizeof(reservoir) - reservoir_off >= needed) +		return &reservoir[reservoir_off]; + +	if (random_fd < 0) { +		libsimple_random_bytes(&libsimple_random_bits, NULL, reservoir, reservoir_off); +		reservoir_off = 0; +		return reservoir; +	} + +	for (off = 0; off < reservoir_off;) { +		r = read(random_fd, &reservoir[off], reservoir_off - off); +		if (r <= 0) { +			if (!r) +				eprintf("random source depleted"); +			if (errno == EINTR) +				continue; +			eprintf("read %s:", random_fname); +		} +		off += (size_t)r; +	} + +	reservoir_off = 0; +	return reservoir; +} + + +void +used_random(ssize_t amount) +{ +	reservoir_off += (size_t)amount; +} @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +#if defined(__linux__) +# include <linux/kd.h> + +int +have_micro_symbol(void) +{ +	static int ret = -1; +	if (ret < 0) { +		struct unimapdesc desc; +		struct unipair *pairs = NULL; +		size_t i; +		ret = 1; +		desc.entry_ct = 0; +		desc.entries = NULL; +		if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) +			if (!desc.entry_ct) +				goto out; +		desc.entries = pairs = ecalloc(desc.entry_ct, sizeof(*pairs)); +		if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) +			goto out; +		for (i = 0; i < desc.entry_ct; i++) +			if (desc.entries[i++].unicode == 0xB5U) +				goto out; +		ret = 0; +	out: +		free(pairs); +	} +	return ret; +} + +#else + +# if defined(__GNUC__) +__attribute__((__const__)) +# endif +int +have_micro_symbol(void) +{ +	return 1; +} + +#endif  | 
