Imported Upstream version 2.6.1
[platform/upstream/cryptsetup.git] / src / utils_progress.c
1 /*
2  * cryptsetup - progress output utilities
3  *
4  * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2009-2023 Milan Broz
6  *
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.
11  *
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.
16  *
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.
20  */
21
22 #include <assert.h>
23 #include "cryptsetup.h"
24
25 #define MINUTES_90 UINT64_C(5400000000)   /* 90 minutes in microseconds */
26 #define HOURS_36   UINT64_C(129600000000) /* 36 hours in microseconds */
27
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 */
32
33 #define REMAIN_SECONDS(A) (SECONDS((A))) % 60
34 #define REMAIN_MINUTES(A) (MINUTES((A))) % 60
35
36 /* The difference in microseconds between two times in "timeval" format. */
37 static uint64_t time_diff(struct timeval *start, struct timeval *end)
38 {
39         return (end->tv_sec - start->tv_sec) * UINT64_C(1000000)
40                 + (end->tv_usec - start->tv_usec);
41 }
42
43 static void tools_clear_line(void)
44 {
45         /* vt100 code clear line */
46         log_std("\33[2K\r");
47 }
48
49 static void bytes_to_units(uint64_t *bytes, const char **units)
50 {
51         if (*bytes < (UINT64_C(1) << 32)) { /* less than 4 GiBs */
52                 *units = "MiB";
53                 *bytes >>= 20;
54         } else if (*bytes < (UINT64_C(1) << 42)) { /* less than 4 TiBs */
55                 *units = "GiB";
56                 *bytes >>= 30;
57         } else if (*bytes < (UINT64_C(1) << 52)) { /* less than 4 PiBs */
58                 *units = "TiB";
59                 *bytes >>= 40;
60         } else if (*bytes < (UINT64_C(1) << 62)) { /* less than 4 EiBs */
61                 *units = "PiB";
62                 *bytes >>= 50;
63         } else {
64                 *units = "EiB";
65                 *bytes >>= 60;
66         }
67 }
68
69 static bool time_to_human_string(uint64_t usecs, char *buf, size_t buf_len)
70 {
71         ssize_t r;
72
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));
77         else
78                 r = snprintf(buf, buf_len, _("%02" PRIu64 " days"), DAYS(usecs));
79
80         if (r < 0 || (size_t)r >= buf_len)
81                 return false;
82
83         return true;
84 }
85
86 static void log_progress(uint64_t bytes, uint64_t device_size, uint64_t eta, double uib, const char *ustr, const char *eol)
87 {
88         double progress;
89         int r;
90         const char *units;
91         char time[128], written[128], speed[128];
92
93         /*
94          * TRANSLATORS: 'time' string with examples:
95          * "12m44s"    : meaning 12 minutes 44 seconds
96          * "26h12m44s" : meaning 26 hours 12 minutes 44 seconds
97          * "3 days"
98          */
99         if (!time_to_human_string(eta, time, sizeof(time)))
100                 return;
101
102         progress = (double)bytes / device_size * 100.0;
103
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))
107                 return;
108
109         r = snprintf(speed, sizeof(speed), _("speed %5.1f %s/s"), uib, ustr);
110         if (r < 0 || (size_t)r >= sizeof(speed))
111                 return;
112
113         /*
114          * TRANSLATORS: 'time', 'written' and 'speed' string are supposed
115          * to get translated as well. 'eol' is always new-line or empty.
116          * See above.
117          */
118         log_std(_("Progress: %5.1f%%, ETA %s, %s, %s%s"),
119                 progress, time, written, speed, eol);
120 }
121
122 static void log_progress_final(uint64_t time_spent, uint64_t bytes, double uib, const char *ustr)
123 {
124         int r;
125         const char *units;
126         char time[128], written[128], speed[128];
127
128         /*
129          * TRANSLATORS: 'time' string with examples:
130          * "12m44s"    : meaning 12 minutes 44 seconds
131          * "26h12m44s" : meaning 26 hours 12 minutes 44 seconds
132          * "3 days"
133          */
134         if (!time_to_human_string(time_spent, time, sizeof(time)))
135                 return;
136
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))
140                 return;
141
142         r = snprintf(speed, sizeof(speed) - 1, _("speed %5.1f %s/s"), uib, ustr);
143         if (r < 0 || (size_t)r >= sizeof(speed))
144                 return;
145
146         /*
147          * TRANSLATORS: 'time', 'written' and 'speed' string are supposed
148          * to get translated as well. See above
149          */
150         log_std(_("Finished, time %s, %s, %s\n"), time, written, speed);
151 }
152
153 static bool calculate_tdiff(bool final, uint64_t bytes, struct tools_progress_params *parms, double *r_tdiff)
154 {
155         uint64_t frequency;
156         struct timeval now_time;
157
158         assert(r_tdiff);
159
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;
165                 return false;
166         }
167
168         if (parms->frequency)
169                 frequency = parms->frequency * UINT64_C(1000000);
170         else
171                 frequency = 500000;
172
173         if (!final && time_diff(&parms->end_time, &now_time) < frequency)
174                 return false;
175
176         parms->end_time = now_time;
177
178         *r_tdiff = time_diff(&parms->start_time, &parms->end_time) / 1E6;
179         if (!*r_tdiff)
180                 return false;
181
182         return true;
183 }
184
185 static void tools_time_progress(uint64_t device_size, uint64_t bytes, struct tools_progress_params *parms)
186 {
187         uint64_t eta;
188         double tdiff, uib;
189         const char *eol, *ustr;
190         bool final = (bytes == device_size);
191
192         if (!calculate_tdiff(final, bytes, parms, &tdiff))
193                 return;
194
195         if (parms->frequency)
196                 eol = "\n";
197         else
198                 eol = "";
199
200         uib = (double)(bytes - parms->start_offset) / tdiff;
201
202         eta = (uint64_t)((device_size / uib - tdiff) * 1E6);
203
204         if (uib > 1073741824.0f) {
205                 uib /= 1073741824.0f;
206                 ustr = "GiB";
207         } else if (uib > 1048576.0f) {
208                 uib /= 1048576.0f;
209                 ustr = "MiB";
210         } else if (uib > 1024.0f) {
211                 uib /= 1024.0f;
212                 ustr = "KiB";
213         } else
214                 ustr = "B";
215
216         if (!parms->frequency)
217                 tools_clear_line();
218
219         if (final)
220                 log_progress_final((uint64_t)(tdiff * 1E6), bytes, uib, ustr);
221         else
222                 log_progress(bytes, device_size, eta, uib, ustr, eol);
223
224         fflush(stdout);
225 }
226
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)
228 {
229         int r;
230         char json[PATH_MAX+256];
231
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);
240
241         if (r < 0 || (size_t)r >= sizeof(json) - 1)
242                 return;
243
244         log_std("%s", json);
245 }
246
247 static void tools_time_progress_json(uint64_t device_size, uint64_t bytes, struct tools_progress_params *parms)
248 {
249         double tdiff, uib;
250         bool final = (bytes == device_size);
251
252         if (!calculate_tdiff(final, bytes, parms, &tdiff))
253                 return;
254
255         uib = (double)(bytes - parms->start_offset) / tdiff;
256
257         log_progress_json(parms->device,
258                           bytes,
259                           device_size,
260                           final ? UINT64_C(0) : (uint64_t)((device_size / uib - tdiff) * 1E3),
261                           (uint64_t)uib,
262                           (uint64_t)(tdiff * 1E3));
263
264         fflush(stdout);
265 }
266
267 int tools_progress(uint64_t size, uint64_t offset, void *usrptr)
268 {
269         int r = 0;
270         struct tools_progress_params *parms = (struct tools_progress_params *)usrptr;
271
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);
276
277         check_signal(&r);
278         if (r) {
279                 if (!parms || (!parms->frequency && !parms->json_output))
280                         tools_clear_line();
281                 if (parms && parms->interrupt_message)
282                         log_err("%s", parms->interrupt_message);
283         }
284
285         return r;
286 }
287
288 const char *tools_get_device_name(const char *device, char **r_backing_file)
289 {
290         char *bfile;
291
292         assert(r_backing_file);
293
294         bfile = crypt_loop_backing_file(device);
295         if (bfile) {
296                 *r_backing_file = bfile;
297                 return bfile;
298         }
299
300         return device;
301 }