]> andersk Git - openssh.git/blob - progressmeter.c
- deraadt@cvs.openbsd.org 2006/03/19 18:56:41
[openssh.git] / progressmeter.c
1 /*
2  * Copyright (c) 2003 Nils Nordman.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "includes.h"
26
27 #include <sys/ioctl.h>
28
29 #include <signal.h>
30
31 #include "progressmeter.h"
32 #include "atomicio.h"
33 #include "misc.h"
34
35 #define DEFAULT_WINSIZE 80
36 #define MAX_WINSIZE 512
37 #define PADDING 1               /* padding between the progress indicators */
38 #define UPDATE_INTERVAL 1       /* update the progress meter every second */
39 #define STALL_TIME 5            /* we're stalled after this many seconds */
40
41 /* determines whether we can output to the terminal */
42 static int can_output(void);
43
44 /* formats and inserts the specified size into the given buffer */
45 static void format_size(char *, int, off_t);
46 static void format_rate(char *, int, off_t);
47
48 /* window resizing */
49 static void sig_winch(int);
50 static void setscreensize(void);
51
52 /* updates the progressmeter to reflect the current state of the transfer */
53 void refresh_progress_meter(void);
54
55 /* signal handler for updating the progress meter */
56 static void update_progress_meter(int);
57
58 static time_t start;            /* start progress */
59 static time_t last_update;      /* last progress update */
60 static char *file;              /* name of the file being transferred */
61 static off_t end_pos;           /* ending position of transfer */
62 static off_t cur_pos;           /* transfer position as of last refresh */
63 static volatile off_t *counter; /* progress counter */
64 static long stalled;            /* how long we have been stalled */
65 static int bytes_per_second;    /* current speed in bytes per second */
66 static int win_size;            /* terminal window size */
67 static volatile sig_atomic_t win_resized; /* for window resizing */
68
69 /* units for format_size */
70 static const char unit[] = " KMGT";
71
72 static int
73 can_output(void)
74 {
75         return (getpgrp() == tcgetpgrp(STDOUT_FILENO));
76 }
77
78 static void
79 format_rate(char *buf, int size, off_t bytes)
80 {
81         int i;
82
83         bytes *= 100;
84         for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++)
85                 bytes = (bytes + 512) / 1024;
86         if (i == 0) {
87                 i++;
88                 bytes = (bytes + 512) / 1024;
89         }
90         snprintf(buf, size, "%3lld.%1lld%c%s",
91             (long long) (bytes + 5) / 100,
92             (long long) (bytes + 5) / 10 % 10,
93             unit[i],
94             i ? "B" : " ");
95 }
96
97 static void
98 format_size(char *buf, int size, off_t bytes)
99 {
100         int i;
101
102         for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++)
103                 bytes = (bytes + 512) / 1024;
104         snprintf(buf, size, "%4lld%c%s",
105             (long long) bytes,
106             unit[i],
107             i ? "B" : " ");
108 }
109
110 void
111 refresh_progress_meter(void)
112 {
113         char buf[MAX_WINSIZE + 1];
114         time_t now;
115         off_t transferred;
116         double elapsed;
117         int percent;
118         off_t bytes_left;
119         int cur_speed;
120         int hours, minutes, seconds;
121         int i, len;
122         int file_len;
123
124         transferred = *counter - cur_pos;
125         cur_pos = *counter;
126         now = time(NULL);
127         bytes_left = end_pos - cur_pos;
128
129         if (bytes_left > 0)
130                 elapsed = now - last_update;
131         else {
132                 elapsed = now - start;
133                 /* Calculate true total speed when done */
134                 transferred = end_pos;
135                 bytes_per_second = 0;
136         }
137
138         /* calculate speed */
139         if (elapsed != 0)
140                 cur_speed = (transferred / elapsed);
141         else
142                 cur_speed = transferred;
143
144 #define AGE_FACTOR 0.9
145         if (bytes_per_second != 0) {
146                 bytes_per_second = (bytes_per_second * AGE_FACTOR) +
147                     (cur_speed * (1.0 - AGE_FACTOR));
148         } else
149                 bytes_per_second = cur_speed;
150
151         /* filename */
152         buf[0] = '\0';
153         file_len = win_size - 35;
154         if (file_len > 0) {
155                 len = snprintf(buf, file_len + 1, "\r%s", file);
156                 if (len < 0)
157                         len = 0;
158                 if (len >= file_len + 1)
159                         len = file_len;
160                 for (i = len;  i < file_len; i++ )
161                         buf[i] = ' ';
162                 buf[file_len] = '\0';
163         }
164
165         /* percent of transfer done */
166         if (end_pos != 0)
167                 percent = ((float)cur_pos / end_pos) * 100;
168         else
169                 percent = 100;
170         snprintf(buf + strlen(buf), win_size - strlen(buf),
171             " %3d%% ", percent);
172
173         /* amount transferred */
174         format_size(buf + strlen(buf), win_size - strlen(buf),
175             cur_pos);
176         strlcat(buf, " ", win_size);
177
178         /* bandwidth usage */
179         format_rate(buf + strlen(buf), win_size - strlen(buf),
180             (off_t)bytes_per_second);
181         strlcat(buf, "/s ", win_size);
182
183         /* ETA */
184         if (!transferred)
185                 stalled += elapsed;
186         else
187                 stalled = 0;
188
189         if (stalled >= STALL_TIME)
190                 strlcat(buf, "- stalled -", win_size);
191         else if (bytes_per_second == 0 && bytes_left)
192                 strlcat(buf, "  --:-- ETA", win_size);
193         else {
194                 if (bytes_left > 0)
195                         seconds = bytes_left / bytes_per_second;
196                 else
197                         seconds = elapsed;
198
199                 hours = seconds / 3600;
200                 seconds -= hours * 3600;
201                 minutes = seconds / 60;
202                 seconds -= minutes * 60;
203
204                 if (hours != 0)
205                         snprintf(buf + strlen(buf), win_size - strlen(buf),
206                             "%d:%02d:%02d", hours, minutes, seconds);
207                 else
208                         snprintf(buf + strlen(buf), win_size - strlen(buf),
209                             "  %02d:%02d", minutes, seconds);
210
211                 if (bytes_left > 0)
212                         strlcat(buf, " ETA", win_size);
213                 else
214                         strlcat(buf, "    ", win_size);
215         }
216
217         atomicio(vwrite, STDOUT_FILENO, buf, win_size - 1);
218         last_update = now;
219 }
220
221 static void
222 update_progress_meter(int ignore)
223 {
224         int save_errno;
225
226         save_errno = errno;
227
228         if (win_resized) {
229                 setscreensize();
230                 win_resized = 0;
231         }
232         if (can_output())
233                 refresh_progress_meter();
234
235         signal(SIGALRM, update_progress_meter);
236         alarm(UPDATE_INTERVAL);
237         errno = save_errno;
238 }
239
240 void
241 start_progress_meter(char *f, off_t filesize, off_t *ctr)
242 {
243         start = last_update = time(NULL);
244         file = f;
245         end_pos = filesize;
246         cur_pos = 0;
247         counter = ctr;
248         stalled = 0;
249         bytes_per_second = 0;
250
251         setscreensize();
252         if (can_output())
253                 refresh_progress_meter();
254
255         signal(SIGALRM, update_progress_meter);
256         signal(SIGWINCH, sig_winch);
257         alarm(UPDATE_INTERVAL);
258 }
259
260 void
261 stop_progress_meter(void)
262 {
263         alarm(0);
264
265         if (!can_output())
266                 return;
267
268         /* Ensure we complete the progress */
269         if (cur_pos != end_pos)
270                 refresh_progress_meter();
271
272         atomicio(vwrite, STDOUT_FILENO, "\n", 1);
273 }
274
275 /*ARGSUSED*/
276 static void
277 sig_winch(int sig)
278 {
279         win_resized = 1;
280 }
281
282 static void
283 setscreensize(void)
284 {
285         struct winsize winsize;
286
287         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 &&
288             winsize.ws_col != 0) {
289                 if (winsize.ws_col > MAX_WINSIZE)
290                         win_size = MAX_WINSIZE;
291                 else
292                         win_size = winsize.ws_col;
293         } else
294                 win_size = DEFAULT_WINSIZE;
295         win_size += 1;                                  /* trailing \0 */
296 }
This page took 0.064595 seconds and 5 git commands to generate.