2 * cryptsetup - progress output utilities
4 * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
5 * Copyright (C) 2009-2023 Milan Broz
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "cryptsetup.h"
25 #define MINUTES_90 UINT64_C(5400000000) /* 90 minutes in microseconds */
26 #define HOURS_36 UINT64_C(129600000000) /* 36 hours in microseconds */
28 #define MINUTES(A) (A) / UINT64_C(60000000) /* microseconds to minutes */
29 #define SECONDS(A) (A) / UINT64_C(1000000) /* microseconds to seconds */
30 #define HOURS(A) (A) / UINT64_C(3600000000) /* microseconds to hours */
31 #define DAYS(A) (A) / UINT64_C(86400000000) /* microseconds to days */
33 #define REMAIN_SECONDS(A) (SECONDS((A))) % 60
34 #define REMAIN_MINUTES(A) (MINUTES((A))) % 60
36 /* The difference in microseconds between two times in "timeval" format. */
37 static uint64_t time_diff(struct timeval *start, struct timeval *end)
39 return (end->tv_sec - start->tv_sec) * UINT64_C(1000000)
40 + (end->tv_usec - start->tv_usec);
43 static void tools_clear_line(void)
45 /* vt100 code clear line */
49 static void bytes_to_units(uint64_t *bytes, const char **units)
51 if (*bytes < (UINT64_C(1) << 32)) { /* less than 4 GiBs */
54 } else if (*bytes < (UINT64_C(1) << 42)) { /* less than 4 TiBs */
57 } else if (*bytes < (UINT64_C(1) << 52)) { /* less than 4 PiBs */
60 } else if (*bytes < (UINT64_C(1) << 62)) { /* less than 4 EiBs */
69 static bool time_to_human_string(uint64_t usecs, char *buf, size_t buf_len)
73 if (usecs < MINUTES_90)
74 r = snprintf(buf, buf_len, _("%02" PRIu64 "m%02" PRIu64 "s"), MINUTES(usecs), REMAIN_SECONDS(usecs));
75 else if (usecs < HOURS_36)
76 r = snprintf(buf, buf_len, _("%02" PRIu64 "h%02" PRIu64 "m%02" PRIu64 "s"), HOURS(usecs), REMAIN_MINUTES(usecs), REMAIN_SECONDS(usecs));
78 r = snprintf(buf, buf_len, _("%02" PRIu64 " days"), DAYS(usecs));
80 if (r < 0 || (size_t)r >= buf_len)
86 static void log_progress(uint64_t bytes, uint64_t device_size, uint64_t eta, double uib, const char *ustr, const char *eol)
91 char time[128], written[128], speed[128];
94 * TRANSLATORS: 'time' string with examples:
95 * "12m44s" : meaning 12 minutes 44 seconds
96 * "26h12m44s" : meaning 26 hours 12 minutes 44 seconds
99 if (!time_to_human_string(eta, time, sizeof(time)))
102 progress = (double)bytes / device_size * 100.0;
104 bytes_to_units(&bytes, &units);
105 r = snprintf(written, sizeof(written), _("%4" PRIu64 " %s written"), bytes, units);
106 if (r < 0 || (size_t)r >= sizeof(written))
109 r = snprintf(speed, sizeof(speed), _("speed %5.1f %s/s"), uib, ustr);
110 if (r < 0 || (size_t)r >= sizeof(speed))
114 * TRANSLATORS: 'time', 'written' and 'speed' string are supposed
115 * to get translated as well. 'eol' is always new-line or empty.
118 log_std(_("Progress: %5.1f%%, ETA %s, %s, %s%s"),
119 progress, time, written, speed, eol);
122 static void log_progress_final(uint64_t time_spent, uint64_t bytes, double uib, const char *ustr)
126 char time[128], written[128], speed[128];
129 * TRANSLATORS: 'time' string with examples:
130 * "12m44s" : meaning 12 minutes 44 seconds
131 * "26h12m44s" : meaning 26 hours 12 minutes 44 seconds
134 if (!time_to_human_string(time_spent, time, sizeof(time)))
137 bytes_to_units(&bytes, &units);
138 r = snprintf(written, sizeof(written) - 1, _("%4" PRIu64 " %s written"), bytes, units);
139 if (r < 0 || (size_t)r >= sizeof(written))
142 r = snprintf(speed, sizeof(speed) - 1, _("speed %5.1f %s/s"), uib, ustr);
143 if (r < 0 || (size_t)r >= sizeof(speed))
147 * TRANSLATORS: 'time', 'written' and 'speed' string are supposed
148 * to get translated as well. See above
150 log_std(_("Finished, time %s, %s, %s\n"), time, written, speed);
153 static bool calculate_tdiff(bool final, uint64_t bytes, struct tools_progress_params *parms, double *r_tdiff)
156 struct timeval now_time;
160 gettimeofday(&now_time, NULL);
161 if (parms->start_time.tv_sec == 0 && parms->start_time.tv_usec == 0) {
162 parms->start_time = now_time;
163 parms->end_time = now_time;
164 parms->start_offset = bytes;
168 if (parms->frequency)
169 frequency = parms->frequency * UINT64_C(1000000);
173 if (!final && time_diff(&parms->end_time, &now_time) < frequency)
176 parms->end_time = now_time;
178 *r_tdiff = time_diff(&parms->start_time, &parms->end_time) / 1E6;
185 static void tools_time_progress(uint64_t device_size, uint64_t bytes, struct tools_progress_params *parms)
189 const char *eol, *ustr;
190 bool final = (bytes == device_size);
192 if (!calculate_tdiff(final, bytes, parms, &tdiff))
195 if (parms->frequency)
200 uib = (double)(bytes - parms->start_offset) / tdiff;
202 eta = (uint64_t)((device_size / uib - tdiff) * 1E6);
204 if (uib > 1073741824.0f) {
205 uib /= 1073741824.0f;
207 } else if (uib > 1048576.0f) {
210 } else if (uib > 1024.0f) {
216 if (!parms->frequency)
220 log_progress_final((uint64_t)(tdiff * 1E6), bytes, uib, ustr);
222 log_progress(bytes, device_size, eta, uib, ustr, eol);
227 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)
230 char json[PATH_MAX+256];
232 r = snprintf(json, sizeof(json) - 1,
233 "{\"device\":\"%s\","
234 "\"device_bytes\":\"%" PRIu64 "\"," /* in bytes */
235 "\"device_size\":\"%" PRIu64 "\"," /* in bytes */
236 "\"speed\":\"%" PRIu64 "\"," /* in bytes per second */
237 "\"eta_ms\":\"%" PRIu64 "\"," /* in milliseconds */
238 "\"time_ms\":\"%" PRIu64 "\"}\n", /* in milliseconds */
239 device, bytes, device_size, uib, eta, time_spent);
241 if (r < 0 || (size_t)r >= sizeof(json) - 1)
247 static void tools_time_progress_json(uint64_t device_size, uint64_t bytes, struct tools_progress_params *parms)
250 bool final = (bytes == device_size);
252 if (!calculate_tdiff(final, bytes, parms, &tdiff))
255 uib = (double)(bytes - parms->start_offset) / tdiff;
257 log_progress_json(parms->device,
260 final ? UINT64_C(0) : (uint64_t)((device_size / uib - tdiff) * 1E3),
262 (uint64_t)(tdiff * 1E3));
267 int tools_progress(uint64_t size, uint64_t offset, void *usrptr)
270 struct tools_progress_params *parms = (struct tools_progress_params *)usrptr;
272 if (parms && parms->json_output)
273 tools_time_progress_json(size, offset, parms);
274 else if (parms && !parms->batch_mode)
275 tools_time_progress(size, offset, parms);
279 if (!parms || (!parms->frequency && !parms->json_output))
281 if (parms && parms->interrupt_message)
282 log_err("%s", parms->interrupt_message);
288 const char *tools_get_device_name(const char *device, char **r_backing_file)
292 assert(r_backing_file);
294 bfile = crypt_loop_backing_file(device);
296 *r_backing_file = bfile;