diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index cc226e72ea7..0360fa9d72a 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -460,7 +460,7 @@ bool shall_tint_background(void) { return cache != 0; } -void draw_progress_bar_unbuffered(const char *prefix, double percentage) { +void draw_progress_bar_impl(const char *prefix, double percentage) { fputc('\r', stderr); if (prefix) { fputs(prefix, stderr); @@ -516,7 +516,7 @@ void draw_progress_bar_unbuffered(const char *prefix, double percentage) { } -void clear_progress_bar_unbuffered(const char *prefix) { +void clear_progress_bar_impl(const char *prefix) { fputc('\r', stderr); if (terminal_is_dumb()) @@ -531,28 +531,14 @@ void clear_progress_bar_unbuffered(const char *prefix) { } void draw_progress_bar(const char *prefix, double percentage) { - /* We are going output a bunch of small strings that shall appear as a single line to STDERR which is * unbuffered by default. Let's temporarily turn on full buffering, so that this is passed to the tty * as a single buffer, to make things more efficient. */ - char buffer[LONG_LINE_MAX]; - setvbuf(stderr, buffer, _IOFBF, sizeof(buffer)); - - draw_progress_bar_unbuffered(prefix, percentage); - - fflush(stderr); - - /* Disable buffering again */ - setvbuf(stderr, NULL, _IONBF, 0); + WITH_BUFFERED_STDERR; + draw_progress_bar_impl(prefix, percentage); } void clear_progress_bar(const char *prefix) { - char buffer[LONG_LINE_MAX]; - setvbuf(stderr, buffer, _IOFBF, sizeof(buffer)); - - clear_progress_bar_unbuffered(prefix); - - fflush(stderr); - - setvbuf(stderr, NULL, _IONBF, 0); + WITH_BUFFERED_STDERR; + clear_progress_bar_impl(prefix); } diff --git a/src/shared/pretty-print.h b/src/shared/pretty-print.h index d3af149a2ef..446f305b733 100644 --- a/src/shared/pretty-print.h +++ b/src/shared/pretty-print.h @@ -55,5 +55,34 @@ bool shall_tint_background(void); void draw_progress_bar(const char *prefix, double percentage); void clear_progress_bar(const char *prefix); -void draw_progress_bar_unbuffered(const char *prefix, double percentage); -void clear_progress_bar_unbuffered(const char *prefix); +void draw_progress_bar_impl(const char *prefix, double percentage); +void clear_progress_bar_impl(const char *prefix); + +static inline FILE* enable_buffering(FILE *f, char *buffer, size_t size) { + assert(f); + assert(buffer); + assert(size > 0); + + if (setvbuf(f, buffer, _IOFBF, size) != 0) + return NULL; + + return f; +} + +static inline void fflush_and_disable_bufferingp(FILE **p) { + assert(p); + + if (*p) { + fflush(*p); + setvbuf(*p, NULL, _IONBF, 0); /* Disable buffering again. */ + } +} + +/* Even though the macro below is slightly generic, but it may not work most streams except for stderr, + * as stdout is buffered and fopen() enables buffering by default. */ +#define _WITH_BUFFERED_STREAM(f, size, p) \ + _unused_ _cleanup_(fflush_and_disable_bufferingp) FILE *p = \ + enable_buffering(f, (char[size]) {}, size) + +#define WITH_BUFFERED_STDERR \ + _WITH_BUFFERED_STREAM(stderr, LONG_LINE_MAX, UNIQ_T(p, UNIQ)) diff --git a/src/sysupdate/updatectl.c b/src/sysupdate/updatectl.c index c298b244641..fad68ebd881 100644 --- a/src/sysupdate/updatectl.c +++ b/src/sysupdate/updatectl.c @@ -798,8 +798,7 @@ static int update_render_progress(sd_event_source *source, void *userdata) { /* We're outputting lots of small strings to STDERR, which is unbuffered by default. So let's turn * on full buffering, so we pass this all to the TTY in one go, to make things more efficient */ - char buffer[LONG_LINE_MAX]; - setvbuf(stderr, buffer, _IOFBF, sizeof(buffer)); + WITH_BUFFERED_STDERR; if (!terminal_is_dumb()) { for (size_t i = 0; i <= n; i++) @@ -815,23 +814,23 @@ static int update_render_progress(sd_event_source *source, void *userdata) { int progress = PTR_TO_INT(p); if (progress == UPDATE_PROGRESS_FAILED) { - clear_progress_bar_unbuffered(target); + clear_progress_bar_impl(target); fprintf(stderr, "%s: %s Unknown failure\n", target, RED_CROSS_MARK()); total += 100; } else if (progress == -EALREADY) { - clear_progress_bar_unbuffered(target); + clear_progress_bar_impl(target); fprintf(stderr, "%s: %s Already up-to-date\n", target, GREEN_CHECK_MARK()); n--; /* Don't consider this target in the total */ } else if (progress < 0) { - clear_progress_bar_unbuffered(target); + clear_progress_bar_impl(target); fprintf(stderr, "%s: %s %s\n", target, RED_CROSS_MARK(), STRERROR(progress)); total += 100; } else if (progress == UPDATE_PROGRESS_DONE) { - clear_progress_bar_unbuffered(target); + clear_progress_bar_impl(target); fprintf(stderr, "%s: %s Done\n", target, GREEN_CHECK_MARK()); total += 100; } else { - draw_progress_bar_unbuffered(target, progress); + draw_progress_bar_impl(target, progress); fputs("\n", stderr); total += progress; } @@ -839,9 +838,9 @@ static int update_render_progress(sd_event_source *source, void *userdata) { if (n > 1) { if (exiting) - clear_progress_bar_unbuffered(target); + clear_progress_bar_impl(target); else { - draw_progress_bar_unbuffered("Total", (double) total / n); + draw_progress_bar_impl("Total", (double) total / n); if (terminal_is_dumb()) fputs("\n", stderr); } @@ -855,8 +854,6 @@ static int update_render_progress(sd_event_source *source, void *userdata) { } else if (!exiting) fputs("------\n", stderr); - fflush(stderr); - setvbuf(stderr, NULL, _IONBF, 0); /* Disable buffering again */ return 0; }