From e854bea3b6837b22e9b6a69acbb28b4c8b470c81 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 19 Sep 2024 19:22:54 +0200 Subject: misc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- Makefile | 3 +- avg.c | 55 +++++++------- common.h | 42 +++++++++-- deadshred.c | 235 +++++++++++++++++++++++++++--------------------------------- sig.c | 50 +++++++++++++ 5 files changed, 219 insertions(+), 166 deletions(-) create mode 100644 sig.c diff --git a/Makefile b/Makefile index bfe5c26..0e2dff4 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ OBJ =\ fmt.o\ text.o\ avg.o\ - rnd.o + rnd.o\ + sig.o HDR =\ common.h diff --git a/avg.c b/avg.c index 4078c50..f24b174 100644 --- a/avg.c +++ b/avg.c @@ -5,16 +5,11 @@ /* 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) +was_write_average_overrun(int i, int seconds, const struct status *s) { struct timespec diff; - libsimple_difftimespec(&diff, now, &write_average_begin_times[i]); + libsimple_difftimespec(&diff, &s->now, &s->write_average_begin_times[i]); if (diff.tv_sec >= seconds) return 1; if (diff.tv_sec == seconds - 1 && diff.tv_nsec >= DECISECONDS(9)) @@ -24,48 +19,48 @@ was_write_average_overrun(int i, const struct timespec *now, int seconds) static void -shift_write_average(void) +shift_write_average(struct status *s) { - 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)); + s->write_average_i -= 1; + memmove(&s->write_average_begin_times[0], &s->write_average_begin_times[1], + (size_t)s->write_average_i * sizeof(*s->write_average_begin_times)); + memmove(&s->write_average_amounts[0], &s->write_average_amounts[1], + (size_t)s->write_average_i * sizeof(*s->write_average_amounts)); } void -wravg_init(const struct timespec *start_time) +wravg_init(const struct timespec *start_time, struct status *s) { - write_average_begin_times[0] = *start_time; + s->write_average_begin_times[0] = *start_time; } void -wravg_update_write(ssize_t amount) +wravg_update_write(ssize_t amount, struct status *s) { if (amount > 0) - write_average_amounts[write_average_i] += (off_t)amount; + s->write_average_amounts[s->write_average_i] += (off_t)amount; } void -wravg_update_time(const struct timespec *now) +wravg_update_time(struct status *s) { - 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; + if (was_write_average_overrun(s->write_average_i, 1, s)) { + s->write_average_i += 1; + if (s->write_average_i == WRAVG_STEPS) + shift_write_average(s); + s->write_average_begin_times[s->write_average_i] = s->now; + s->write_average_amounts[s->write_average_i] = 0; } - while (write_average_i && was_write_average_overrun(0, now, (int)ELEMSOF(write_average_amounts))) - shift_write_average(); + while (s->write_average_i && was_write_average_overrun(0, WRAVG_STEPS, s)) + shift_write_average(s); } const char * -wravg_get(const struct timespec *now) +wravg_get(struct status *s) { static char buf[512]; @@ -74,10 +69,10 @@ wravg_get(const struct timespec *now) double write_average; int i; - for (i = 0; i <= write_average_i; i++) - write_average_sum += write_average_amounts[i]; + for (i = 0; i <= s->write_average_i; i++) + write_average_sum += s->write_average_amounts[i]; - libsimple_difftimespec(&write_average_time, now, &write_average_begin_times[0]); + libsimple_difftimespec(&write_average_time, &s->now, &s->write_average_begin_times[0]); if (write_average_time.tv_sec < 0) return "-"; diff --git a/common.h b/common.h index 875375c..c1dbcfc 100644 --- a/common.h +++ b/common.h @@ -12,6 +12,9 @@ #define WHOLE_SECOND DECISECONDS(10) +#define WRAVG_STEPS 5 + + enum direction { FORWARDS = 0, BACKWARDS = 1 @@ -24,10 +27,32 @@ struct span { size_t blocksize; }; +struct status { + struct timespec now; + off_t shredded; + off_t bad_bytes; + uintmax_t bad_writes; + uintmax_t bad_sections; + uintmax_t pass_nr; + struct timespec last_success; + enum direction direction; + + int write_average_i; + struct timespec write_average_begin_times[WRAVG_STEPS]; + off_t write_average_amounts[WRAVG_STEPS]; +}; +#define STATUS_INIT {.pass_nr = 1, .last_success = {-1, 0}, .direction = FORWARDS} + + +/* deadshred.c */ +extern struct status status; +extern _Atomic volatile sig_atomic_t exiting; + /* 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); @@ -38,14 +63,17 @@ 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); +void wravg_init(const struct timespec *start_time, struct status *s); +void wravg_update_write(ssize_t amount, struct status *s); +void wravg_update_time(struct status *s); +const char *wravg_get(struct status *s); + /* rnd.c */ #if defined(__GNUC__) @@ -55,3 +83,9 @@ 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); + + +/* sig.c */ +void setup_sighandler(void); +void block_sigs(void); +void unblock_sigs(void); diff --git a/deadshred.c b/deadshred.c index 5956115..28a64fe 100644 --- a/deadshred.c +++ b/deadshred.c @@ -2,49 +2,30 @@ #include "common.h" #include -/* TODO why has the program started to freeze (since multithreading?)*/ - USAGE("[-o offset] [-l length | -e postend] device [< random-source]"); +/* TODO add option (-b) to switch start direction */ -static _Atomic volatile sig_atomic_t exiting = 0; +struct status status = STATUS_INIT; +_Atomic volatile sig_atomic_t exiting = 0; static struct span *spans = NULL; static size_t nspans = 0; static size_t spans_size = 0; -static off_t shredded = 0; static off_t total_size = 0; - static char total_size_1000[256]; static char total_size_1024[256]; -static uintmax_t bad_writes = 0; -static uintmax_t bad_sections = 0; -static off_t bad_bytes = 0; - static clockid_t clck = CLOCK_MONOTONIC_COARSE; static const char *clkcstr = "CLOCK_MONOTONIC_COARSE"; -static struct timespec last_success = {-1, 0}; static struct timespec max_success = {-1, 0}; static struct timespec start_time; -static const struct timespec progress_print_interval = {0, MILLISECONDS(500)}; -static const struct timespec poll_timeout = {0, MILLISECONDS(500)}; -static int progress_print_sig_pipe[2]; -static pthread_mutex_t progress_mutex; - -static enum direction direction = FORWARDS; /* TODO add option (-b) to switch start direction */ -static uintmax_t pass_nr = 1; - - -static void -signal_handler(int signo) -{ - (void) signo; - exiting = 1; -} +static const struct timespec status_print_interval = {0, MILLISECONDS(500)}; +static const struct timespec poll_timeout = {1, 0}; +static int status_print_pipe[2]; static void @@ -79,7 +60,7 @@ add_span(off_t off, off_t amount, size_t blocksize, int try_join) static void -print_progress(int done, const struct timespec *now) +print_status(int done, struct status *s) { static char buf1[2048] = {0}; static char buf2[2048] = {0}; @@ -95,14 +76,14 @@ print_progress(int done, const struct timespec *now) struct timespec since_success = {-1, 0}; struct timespec time_spent; - if (last_success.tv_sec >= 0) { - libsimple_difftimespec(&since_success, now, &last_success); + if (s->last_success.tv_sec >= 0) { + libsimple_difftimespec(&since_success, &s->now, &s->last_success); if (libsimple_cmptimespec(&since_success, &max_success) > 0) max_success = since_success; } /* TODO deal with machine and process suspension */ - libsimple_difftimespec(&time_spent, now, &start_time); + libsimple_difftimespec(&time_spent, &s->now, &start_time); sprintf(bufi == 0 ? buf1 : buf2, "%ji bytes (%s, %s, %.2lf %%) of %s (%s) shredded\033[K\n" @@ -113,28 +94,28 @@ print_progress(int done, const struct timespec *now) "pass: %ju; pass direction: %s\033[K\n" "%s", /* line 1 { */ - (intmax_t)shredded, - humansize1000(shredded, subbuf1), - humansize1024(shredded, subbuf2), - 100 * (double)shredded / (double)total_size, + (intmax_t)s->shredded, + humansize1000(s->shredded, subbuf1), + humansize1024(s->shredded, subbuf2), + 100 * (double)s->shredded / (double)total_size, total_size_1000, total_size_1024, /* } line 2 { */ - bad_writes, - bad_sections, - (intmax_t)bad_bytes, - humansize1000(bad_bytes, subbuf3), - humansize1024(bad_bytes, subbuf4), + s->bad_writes, + s->bad_sections, + (intmax_t)s->bad_bytes, + humansize1000(s->bad_bytes, subbuf3), + humansize1024(s->bad_bytes, subbuf4), /* } line 3 { */ durationstr(&time_spent, subbuf5, 1), - wravg_get(now), + wravg_get(s), /* } line 4 { */ durationstr(&since_success, subbuf6, 2), /* } line 5 { */ durationstr(&max_success, subbuf7, 2), /* } line 6 { */ - pass_nr, - direction == FORWARDS ? "forwards" : "backwards", + s->pass_nr, + s->direction == FORWARDS ? "forwards" : "backwards", /* } */ done ? "" : "\033[6A"); @@ -148,22 +129,19 @@ print_progress(int done, const struct timespec *now) static void * -progress_print_loop(void *user) +status_print_loop(void *user) { - struct pollfd pfd = {.fd = progress_print_sig_pipe[0], .events = POLLOUT}; - struct timespec now; + struct pollfd pfd = {.fd = status_print_pipe[0], .events = POLLIN}; ssize_t r; int terminate = 0; - sigset_t sigset; + struct status s = STATUS_INIT; + size_t status_off; (void) user; - sigemptyset(&sigset); - sigaddset(&sigset, SIGTERM); - sigaddset(&sigset, SIGINT); - errno = pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); - if (errno) - eprintf("pthread_sigmask SIG_UNBLOCK {SIGTERM, SIGINT} NULL:"); + s.last_success = start_time; + + unblock_sigs(); do { switch (ppoll(&pfd, 1U, &poll_timeout, NULL)) { @@ -176,29 +154,32 @@ progress_print_loop(void *user) case 0: break; default: - r = read(progress_print_sig_pipe[0], &now, sizeof(now)); - if (r == (ssize_t)sizeof(now)) { + status_off = 0; + read_status_again: + r = read(status_print_pipe[0], &((char *)&s)[status_off], sizeof(s) - status_off); + if (r == (ssize_t)(sizeof(s) - status_off)) { goto have_time; } else if (!r) { terminate = 1; + break; } else if (r < 0) { if (errno == EINTR) - continue; + goto read_status_again; eprintf("read :"); + } else { + status_off += (size_t)r; + goto read_status_again; } - break; } - if (clock_gettime(clck, &now)) + if (clock_gettime(clck, &s.now)) eprintf("clock_gettime %s:", clkcstr); have_time: - pthread_mutex_lock(&progress_mutex); if (exiting) { fprintf(stderr, "\033[K\nTermination initialised by user...\033[K\n\033[K\n"); terminate = 1; } - print_progress(0, &now); - wravg_update_time(&now); - pthread_mutex_unlock(&progress_mutex); + print_status(0, &s); + wravg_update_time(&s); } while (!terminate); return NULL; @@ -210,33 +191,46 @@ shredspan(int fd, struct span *span, const char *fname) { off_t off, n; ssize_t r; - struct timespec now, when = {0, 0}; + size_t status_off; + struct timespec when = {0, 0}; int bad = span->bad > 0; int first_fail = 1; const char *random_data; - pthread_mutex_lock(&progress_mutex); - - off = (direction == FORWARDS ? span->start : span->end); - while (direction == FORWARDS ? off < span->end : off > span->start) { + off = (status.direction == FORWARDS ? span->start : span->end); + while (status.direction == FORWARDS ? off < span->end : off > span->start) { if (exiting) { userexit: - if (direction == FORWARDS) + if (status.direction == FORWARDS) span->start = off; else span->end = off; - close(progress_print_sig_pipe[1]); - progress_print_sig_pipe[1] = -1; - goto out; + close(status_print_pipe[1]); + status_print_pipe[1] = -1; + return; } - if (clock_gettime(clck, &now)) + if (clock_gettime(clck, &status.now)) eprintf("clock_gettime %s:", clkcstr); - if (libsimple_cmptimespec(&now, &when) >= 0) { - libsimple_sumtimespec(&when, &now, &progress_print_interval); - write(progress_print_sig_pipe[1], &now, sizeof(now)); + if (libsimple_cmptimespec(&status.now, &when) >= 0) { + libsimple_sumtimespec(&when, &status.now, &status_print_interval); + status_off = 0; + write_status_again: + r = write(status_print_pipe[1], &((const char *)&status)[status_off], sizeof(status) - status_off); + if (r != (ssize_t)(sizeof(status) - status_off)) { + if (r >= 0) { + status_off += (size_t)r; + goto write_status_again; + } else if (r == EINTR) { + if (exiting) + goto userexit; + goto write_status_again; + } else { + eprintf("write :"); + } + } } - if (direction == FORWARDS) { + if (status.direction == FORWARDS) { n = MIN((off_t)span->blocksize, span->end - off); } else { n = off--; @@ -248,69 +242,61 @@ shredspan(int fd, struct span *span, const char *fname) break; } random_data = get_random(span->blocksize); - pthread_mutex_unlock(&progress_mutex); pwrite_again: r = pwrite(fd, random_data, (size_t)n, off); if (r < 0) { if (errno == EINTR) { - if (exiting) { - pthread_mutex_lock(&progress_mutex); + if (exiting) goto userexit; - } goto pwrite_again; } - pthread_mutex_lock(&progress_mutex); if (errno != EIO) weprintf("pwrite %s %zu %ji:", fname, (size_t)n, (intmax_t)off); add_span(off, n, span->blocksize == 1U ? 1U : span->blocksize >> 1, !first_fail); first_fail = 0; - if (direction == FORWARDS) + if (status.direction == FORWARDS) off += n; if (!span->bad) - bad_bytes += n; + status.bad_bytes += n; if (bad) bad = 0; else - bad_sections += 1U; + status.bad_sections += 1U; when.tv_sec = 0; when.tv_nsec = 0; - bad_writes += 1U; + status.bad_writes += 1U; continue; } - pthread_mutex_lock(&progress_mutex); - if (direction == FORWARDS) { + if (status.direction == FORWARDS) { off += (off_t)r; } else if ((off_t)r < n) { n -= (off_t)r; add_span(off + (off_t)r, n, span->blocksize == 1U ? 1U : span->blocksize >> 1, !first_fail); first_fail = 0; - if (direction == FORWARDS) + if (status.direction == FORWARDS) off += n; if (!span->bad) - bad_bytes += n; + status.bad_bytes += n; if (bad) bad = 0; else - bad_sections += 1U; + status.bad_sections += 1U; when.tv_sec = 0; when.tv_nsec = 0; - bad_writes += 1U; + status.bad_writes += 1U; } - shredded += (off_t)r; + status.shredded += (off_t)r; used_random(r); - wravg_update_write(r); - last_success = now; + wravg_update_write(r, &status); + status.last_success = status.now; if (span->bad) { - bad_bytes -= (off_t)r; + status.bad_bytes -= (off_t)r; span->bad -= (off_t)r; } } if (bad && !span->bad) - bad_sections -= 1U; - -out: - pthread_mutex_unlock(&progress_mutex); + status.bad_sections -= 1U; } @@ -349,10 +335,7 @@ main(int argc, char *argv[]) off_t off = -1, len = -1, end = -1; size_t i, j; int fd; - struct timespec now; - struct sigaction sa; - pthread_t progress_print_thread; - sigset_t sigset; + pthread_t status_print_thread; ARGBEGIN { case 'o': @@ -377,21 +360,6 @@ main(int argc, char *argv[]) if (argc != 1) usage(); - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = &signal_handler; - sigemptyset(&sa.sa_mask); - if (sigaction(SIGTERM, &sa, NULL)) - eprintf("sigaction SIGTERM {.sa_handler=, .sa_mask={}, .sa_flags=0} NULL:"); - if (sigaction(SIGINT, &sa, NULL)) - eprintf("sigaction SIGINT {.sa_handler=, .sa_mask={}, .sa_flags=0} NULL:"); - - sigemptyset(&sigset); - sigaddset(&sigset, SIGTERM); - sigaddset(&sigset, SIGINT); - errno = pthread_sigmask(SIG_BLOCK, &sigset, NULL); - if (errno) - eprintf("pthread_sigmask SIG_BLOCK {SIGTERM, SIGINT} NULL:"); - fd = open(argv[0], O_WRONLY | O_DSYNC); if (fd < 0) eprintf("open %s O_WRONLY|O_DSYNC:", argv[0]); @@ -425,16 +393,22 @@ main(int argc, char *argv[]) } total_size = spans[0].end - spans[0].start; + if (!total_size) { + weprintf("attempting to shred 0 bytes"); + close(fd); + return 0; + } + humansize1000(total_size, total_size_1000); humansize1024(total_size, total_size_1024); - if (pipe(progress_print_sig_pipe)) + if (pipe(status_print_pipe)) eprintf("pipe:"); - errno = pthread_mutex_init(&progress_mutex, NULL); - if (errno) - eprintf("pthread_mutex_init NULL:"); - errno = pthread_create(&progress_print_thread, NULL, &progress_print_loop, NULL); + setup_sighandler(); + block_sigs(); + + errno = pthread_create(&status_print_thread, NULL, &status_print_loop, NULL); if (errno) eprintf("pthread_create NULL:"); @@ -444,8 +418,8 @@ main(int argc, char *argv[]) if (clock_gettime(clck, &start_time)) eprintf("clock_gettime %s:", clkcstr); } - wravg_init(&start_time); - last_success = start_time; + wravg_init(&start_time, &status); + status.last_success = start_time; while (nspans) { size_t old_nspans = nspans; @@ -458,22 +432,21 @@ main(int argc, char *argv[]) /* 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; - pass_nr++; + status.direction ^= 1; + status.pass_nr += 1U; } close(fd); - if (progress_print_sig_pipe[1] >= 0) - close(progress_print_sig_pipe[1]); + if (status_print_pipe[1] >= 0) + close(status_print_pipe[1]); - errno = pthread_join(progress_print_thread, NULL); + errno = pthread_join(status_print_thread, NULL); if (errno) weprintf("pthread_join:"); - pthread_mutex_destroy(&progress_mutex); - if (clock_gettime(clck, &now)) + if (clock_gettime(clck, &status.now)) eprintf("clock_gettime %s:", clkcstr); - print_progress(1, &now); + print_status(1, &status); if (nspans) { /* TODO document in man page */ diff --git a/sig.c b/sig.c new file mode 100644 index 0000000..c7900fd --- /dev/null +++ b/sig.c @@ -0,0 +1,50 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static void +sighandler(int signo) +{ + (void) signo; + exiting = 1; +} + + +void +setup_sighandler(void) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &sighandler; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGTERM, &sa, NULL)) + eprintf("sigaction SIGTERM {.sa_handler=, .sa_mask={}, .sa_flags=0} NULL:"); + if (sigaction(SIGINT, &sa, NULL)) + eprintf("sigaction SIGINT {.sa_handler=, .sa_mask={}, .sa_flags=0} NULL:"); +} + + +void +block_sigs(void) +{ + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGTERM); + sigaddset(&sigset, SIGINT); + errno = pthread_sigmask(SIG_BLOCK, &sigset, NULL); + if (errno) + eprintf("pthread_sigmask SIG_BLOCK {SIGTERM, SIGINT} NULL:"); +} + + +void +unblock_sigs(void) +{ + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGTERM); + sigaddset(&sigset, SIGINT); + errno = pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + if (errno) + eprintf("pthread_sigmask SIG_UNBLOCK {SIGTERM, SIGINT} NULL:"); +} -- cgit v1.2.3-70-g09d2