diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 08:06:26 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 08:06:26 +0000 |
commit | 1660d4b7a65d9ad2ce0deaa19d35579ca4084ac5 (patch) | |
tree | 6cf8220b628ebd2ccfc1375dd6516c6996e9abcc /src/utils_progress.c | |
parent | Initial commit. (diff) | |
download | cryptsetup-1660d4b7a65d9ad2ce0deaa19d35579ca4084ac5.tar.xz cryptsetup-1660d4b7a65d9ad2ce0deaa19d35579ca4084ac5.zip |
Adding upstream version 2:2.6.1.upstream/2%2.6.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/utils_progress.c')
-rw-r--r-- | src/utils_progress.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/utils_progress.c b/src/utils_progress.c new file mode 100644 index 0000000..76b1818 --- /dev/null +++ b/src/utils_progress.c @@ -0,0 +1,301 @@ +/* + * cryptsetup - progress output utilities + * + * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2023 Milan Broz + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <assert.h> +#include "cryptsetup.h" + +#define MINUTES_90 UINT64_C(5400000000) /* 90 minutes in microseconds */ +#define HOURS_36 UINT64_C(129600000000) /* 36 hours in microseconds */ + +#define MINUTES(A) (A) / UINT64_C(60000000) /* microseconds to minutes */ +#define SECONDS(A) (A) / UINT64_C(1000000) /* microseconds to seconds */ +#define HOURS(A) (A) / UINT64_C(3600000000) /* microseconds to hours */ +#define DAYS(A) (A) / UINT64_C(86400000000) /* microseconds to days */ + +#define REMAIN_SECONDS(A) (SECONDS((A))) % 60 +#define REMAIN_MINUTES(A) (MINUTES((A))) % 60 + +/* The difference in microseconds between two times in "timeval" format. */ +static uint64_t time_diff(struct timeval *start, struct timeval *end) +{ + return (end->tv_sec - start->tv_sec) * UINT64_C(1000000) + + (end->tv_usec - start->tv_usec); +} + +static void tools_clear_line(void) +{ + /* vt100 code clear line */ + log_std("\33[2K\r"); +} + +static void bytes_to_units(uint64_t *bytes, const char **units) +{ + if (*bytes < (UINT64_C(1) << 32)) { /* less than 4 GiBs */ + *units = "MiB"; + *bytes >>= 20; + } else if (*bytes < (UINT64_C(1) << 42)) { /* less than 4 TiBs */ + *units = "GiB"; + *bytes >>= 30; + } else if (*bytes < (UINT64_C(1) << 52)) { /* less than 4 PiBs */ + *units = "TiB"; + *bytes >>= 40; + } else if (*bytes < (UINT64_C(1) << 62)) { /* less than 4 EiBs */ + *units = "PiB"; + *bytes >>= 50; + } else { + *units = "EiB"; + *bytes >>= 60; + } +} + +static bool time_to_human_string(uint64_t usecs, char *buf, size_t buf_len) +{ + ssize_t r; + + if (usecs < MINUTES_90) + r = snprintf(buf, buf_len, _("%02" PRIu64 "m%02" PRIu64 "s"), MINUTES(usecs), REMAIN_SECONDS(usecs)); + else if (usecs < HOURS_36) + r = snprintf(buf, buf_len, _("%02" PRIu64 "h%02" PRIu64 "m%02" PRIu64 "s"), HOURS(usecs), REMAIN_MINUTES(usecs), REMAIN_SECONDS(usecs)); + else + r = snprintf(buf, buf_len, _("%02" PRIu64 " days"), DAYS(usecs)); + + if (r < 0 || (size_t)r >= buf_len) + return false; + + return true; +} + +static void log_progress(uint64_t bytes, uint64_t device_size, uint64_t eta, double uib, const char *ustr, const char *eol) +{ + double progress; + int r; + const char *units; + char time[128], written[128], speed[128]; + + /* + * TRANSLATORS: 'time' string with examples: + * "12m44s" : meaning 12 minutes 44 seconds + * "26h12m44s" : meaning 26 hours 12 minutes 44 seconds + * "3 days" + */ + if (!time_to_human_string(eta, time, sizeof(time))) + return; + + progress = (double)bytes / device_size * 100.0; + + bytes_to_units(&bytes, &units); + r = snprintf(written, sizeof(written), _("%4" PRIu64 " %s written"), bytes, units); + if (r < 0 || (size_t)r >= sizeof(written)) + return; + + r = snprintf(speed, sizeof(speed), _("speed %5.1f %s/s"), uib, ustr); + if (r < 0 || (size_t)r >= sizeof(speed)) + return; + + /* + * TRANSLATORS: 'time', 'written' and 'speed' string are supposed + * to get translated as well. 'eol' is always new-line or empty. + * See above. + */ + log_std(_("Progress: %5.1f%%, ETA %s, %s, %s%s"), + progress, time, written, speed, eol); +} + +static void log_progress_final(uint64_t time_spent, uint64_t bytes, double uib, const char *ustr) +{ + int r; + const char *units; + char time[128], written[128], speed[128]; + + /* + * TRANSLATORS: 'time' string with examples: + * "12m44s" : meaning 12 minutes 44 seconds + * "26h12m44s" : meaning 26 hours 12 minutes 44 seconds + * "3 days" + */ + if (!time_to_human_string(time_spent, time, sizeof(time))) + return; + + bytes_to_units(&bytes, &units); + r = snprintf(written, sizeof(written) - 1, _("%4" PRIu64 " %s written"), bytes, units); + if (r < 0 || (size_t)r >= sizeof(written)) + return; + + r = snprintf(speed, sizeof(speed) - 1, _("speed %5.1f %s/s"), uib, ustr); + if (r < 0 || (size_t)r >= sizeof(speed)) + return; + + /* + * TRANSLATORS: 'time', 'written' and 'speed' string are supposed + * to get translated as well. See above + */ + log_std(_("Finished, time %s, %s, %s\n"), time, written, speed); +} + +static bool calculate_tdiff(bool final, uint64_t bytes, struct tools_progress_params *parms, double *r_tdiff) +{ + uint64_t frequency; + struct timeval now_time; + + assert(r_tdiff); + + gettimeofday(&now_time, NULL); + if (parms->start_time.tv_sec == 0 && parms->start_time.tv_usec == 0) { + parms->start_time = now_time; + parms->end_time = now_time; + parms->start_offset = bytes; + return false; + } + + if (parms->frequency) + frequency = parms->frequency * UINT64_C(1000000); + else + frequency = 500000; + + if (!final && time_diff(&parms->end_time, &now_time) < frequency) + return false; + + parms->end_time = now_time; + + *r_tdiff = time_diff(&parms->start_time, &parms->end_time) / 1E6; + if (!*r_tdiff) + return false; + + return true; +} + +static void tools_time_progress(uint64_t device_size, uint64_t bytes, struct tools_progress_params *parms) +{ + uint64_t eta; + double tdiff, uib; + const char *eol, *ustr; + bool final = (bytes == device_size); + + if (!calculate_tdiff(final, bytes, parms, &tdiff)) + return; + + if (parms->frequency) + eol = "\n"; + else + eol = ""; + + uib = (double)(bytes - parms->start_offset) / tdiff; + + eta = (uint64_t)((device_size / uib - tdiff) * 1E6); + + if (uib > 1073741824.0f) { + uib /= 1073741824.0f; + ustr = "GiB"; + } else if (uib > 1048576.0f) { + uib /= 1048576.0f; + ustr = "MiB"; + } else if (uib > 1024.0f) { + uib /= 1024.0f; + ustr = "KiB"; + } else + ustr = "B"; + + if (!parms->frequency) + tools_clear_line(); + + if (final) + log_progress_final((uint64_t)(tdiff * 1E6), bytes, uib, ustr); + else + log_progress(bytes, device_size, eta, uib, ustr, eol); + + fflush(stdout); +} + +static void log_progress_json(const char *device, uint64_t bytes, uint64_t device_size, uint64_t eta, uint64_t uib, uint64_t time_spent) +{ + int r; + char json[PATH_MAX+256]; + + r = snprintf(json, sizeof(json) - 1, + "{\"device\":\"%s\"," + "\"device_bytes\":\"%" PRIu64 "\"," /* in bytes */ + "\"device_size\":\"%" PRIu64 "\"," /* in bytes */ + "\"speed\":\"%" PRIu64 "\"," /* in bytes per second */ + "\"eta_ms\":\"%" PRIu64 "\"," /* in milliseconds */ + "\"time_ms\":\"%" PRIu64 "\"}\n", /* in milliseconds */ + device, bytes, device_size, uib, eta, time_spent); + + if (r < 0 || (size_t)r >= sizeof(json) - 1) + return; + + log_std("%s", json); +} + +static void tools_time_progress_json(uint64_t device_size, uint64_t bytes, struct tools_progress_params *parms) +{ + double tdiff, uib; + bool final = (bytes == device_size); + + if (!calculate_tdiff(final, bytes, parms, &tdiff)) + return; + + uib = (double)(bytes - parms->start_offset) / tdiff; + + log_progress_json(parms->device, + bytes, + device_size, + final ? UINT64_C(0) : (uint64_t)((device_size / uib - tdiff) * 1E3), + (uint64_t)uib, + (uint64_t)(tdiff * 1E3)); + + fflush(stdout); +} + +int tools_progress(uint64_t size, uint64_t offset, void *usrptr) +{ + int r = 0; + struct tools_progress_params *parms = (struct tools_progress_params *)usrptr; + + if (parms && parms->json_output) + tools_time_progress_json(size, offset, parms); + else if (parms && !parms->batch_mode) + tools_time_progress(size, offset, parms); + + check_signal(&r); + if (r) { + if (!parms || (!parms->frequency && !parms->json_output)) + tools_clear_line(); + if (parms && parms->interrupt_message) + log_err("%s", parms->interrupt_message); + } + + return r; +} + +const char *tools_get_device_name(const char *device, char **r_backing_file) +{ + char *bfile; + + assert(r_backing_file); + + bfile = crypt_loop_backing_file(device); + if (bfile) { + *r_backing_file = bfile; + return bfile; + } + + return device; +} |