1 /* 2 * Copyright (c) 1999 Theo de Raadt. All rights reserved. 3 * Copyright (c) 1999 Aaron Campbell. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 /* 27 * Parts from: 28 * 29 * Copyright (c) 1983, 1990, 1992, 1993, 1995 30 * The Regents of the University of California. All rights reserved. 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 3. All advertising materials mentioning features or use of this software 41 * must display the following acknowledgement: 42 * This product includes software developed by the University of 43 * California, Berkeley and its contributors. 44 * 4. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 * 60 */ 61 62 #include "includes.h" 63 RCSID("$OpenBSD: progressmeter.c,v 1.3 2003/03/17 10:38:38 markus Exp $"); 64 65 #ifdef HAVE_LIBGEN_H 66 #include <libgen.h> 67 #endif 68 69 #include "atomicio.h" 70 #include "progressmeter.h" 71 72 /* Number of seconds before xfer considered "stalled". */ 73 #define STALLTIME 5 74 /* alarm() interval for updating progress meter. */ 75 #define PROGRESSTIME 1 76 77 /* Signal handler used for updating the progress meter. */ 78 static void update_progress_meter(int); 79 80 /* Returns non-zero if we are the foreground process. */ 81 static int foregroundproc(void); 82 83 /* Returns width of the terminal (for progress meter calculations). */ 84 static int get_tty_width(void); 85 86 /* Visual statistics about files as they are transferred. */ 87 static void draw_progress_meter(void); 88 89 /* Time a transfer started. */ 90 static struct timeval start; 91 92 /* Number of bytes of current file transferred so far. */ 93 static volatile off_t *statbytes; 94 95 /* Total size of current file. */ 96 static off_t totalbytes; 97 98 /* Name of current file being transferred. */ 99 static char *curfile; 100 101 /* Time of last update. */ 102 static struct timeval lastupdate; 103 104 /* Size at the time of the last update. */ 105 static off_t lastsize; 106 107 void 108 start_progress_meter(char *file, off_t filesize, off_t *counter) 109 { 110 if ((curfile = basename(file)) == NULL) 111 curfile = file; 112 113 totalbytes = filesize; 114 statbytes = counter; 115 (void) gettimeofday(&start, (struct timezone *) 0); 116 lastupdate = start; 117 lastsize = 0; 118 119 draw_progress_meter(); 120 signal(SIGALRM, update_progress_meter); 121 alarm(PROGRESSTIME); 122 } 123 124 void 125 stop_progress_meter() 126 { 127 alarm(0); 128 draw_progress_meter(); 129 if (foregroundproc() != 0) 130 atomicio(write, fileno(stdout), "\n", 1); 131 } 132 133 static void 134 update_progress_meter(int ignore) 135 { 136 int save_errno = errno; 137 138 draw_progress_meter(); 139 signal(SIGALRM, update_progress_meter); 140 alarm(PROGRESSTIME); 141 errno = save_errno; 142 } 143 144 static int 145 foregroundproc(void) 146 { 147 static pid_t pgrp = -1; 148 int ctty_pgrp; 149 150 if (pgrp == -1) 151 pgrp = getpgrp(); 152 153 #ifdef HAVE_TCGETPGRP 154 return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 && 155 ctty_pgrp == pgrp); 156 #else 157 return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && 158 ctty_pgrp == pgrp)); 159 #endif 160 } 161 162 static void 163 draw_progress_meter() 164 { 165 static const char spaces[] = " " 166 " " 167 " " 168 " " 169 " " 170 " "; 171 static const char prefixes[] = " KMGTP"; 172 struct timeval now, td, wait; 173 off_t cursize, abbrevsize, bytespersec; 174 double elapsed; 175 int ratio, remaining, i, ai, bi, nspaces; 176 char buf[512]; 177 178 if (foregroundproc() == 0) 179 return; 180 181 (void) gettimeofday(&now, (struct timezone *) 0); 182 cursize = *statbytes; 183 if (totalbytes != 0) { 184 ratio = 100.0 * cursize / totalbytes; 185 ratio = MAX(ratio, 0); 186 ratio = MIN(ratio, 100); 187 } else 188 ratio = 100; 189 190 abbrevsize = cursize; 191 for (ai = 0; abbrevsize >= 10000 && ai < sizeof(prefixes); ai++) 192 abbrevsize >>= 10; 193 194 timersub(&now, &lastupdate, &wait); 195 if (cursize > lastsize) { 196 lastupdate = now; 197 lastsize = cursize; 198 wait.tv_sec = 0; 199 } 200 timersub(&now, &start, &td); 201 elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 202 203 bytespersec = 0; 204 if (cursize > 0) { 205 bytespersec = cursize; 206 if (elapsed > 0.0) 207 bytespersec /= elapsed; 208 } 209 for (bi = 1; bytespersec >= 1024000 && bi < sizeof(prefixes); bi++) 210 bytespersec >>= 10; 211 212 nspaces = MIN(get_tty_width() - 79, sizeof(spaces) - 1); 213 214 #ifdef HAVE_LONG_LONG_INT 215 snprintf(buf, sizeof(buf), 216 "\r%-45.45s%.*s%3d%% %4lld%c%c %3lld.%01d%cB/s", 217 curfile, 218 nspaces, 219 spaces, 220 ratio, 221 (long long)abbrevsize, 222 prefixes[ai], 223 ai == 0 ? ' ' : 'B', 224 (long long)(bytespersec / 1024), 225 (int)((bytespersec % 1024) * 10 / 1024), 226 prefixes[bi] 227 ); 228 #else 229 /* XXX: Handle integer overflow? */ 230 snprintf(buf, sizeof(buf), 231 "\r%-45.45s%.*s%3d%% %4lu%c%c %3lu.%01d%cB/s", 232 curfile, 233 nspaces, 234 spaces, 235 ratio, 236 (u_long)abbrevsize, 237 prefixes[ai], 238 ai == 0 ? ' ' : 'B', 239 (u_long)(bytespersec / 1024), 240 (int)((bytespersec % 1024) * 10 / 1024), 241 prefixes[bi] 242 ); 243 #endif 244 245 if (cursize <= 0 || elapsed <= 0.0 || cursize > totalbytes) { 246 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 247 " --:-- ETA"); 248 } else if (wait.tv_sec >= STALLTIME) { 249 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 250 " - stalled -"); 251 } else { 252 if (cursize != totalbytes) 253 remaining = (int)(totalbytes / (cursize / elapsed) - 254 elapsed); 255 else 256 remaining = elapsed; 257 258 i = remaining / 3600; 259 if (i) 260 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 261 "%2d:", i); 262 else 263 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 264 " "); 265 i = remaining % 3600; 266 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 267 "%02d:%02d%s", i / 60, i % 60, 268 (cursize != totalbytes) ? " ETA" : " "); 269 } 270 atomicio(write, fileno(stdout), buf, strlen(buf)); 271 } 272 273 static int 274 get_tty_width(void) 275 { 276 struct winsize winsize; 277 278 if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) 279 return (winsize.ws_col ? winsize.ws_col : 80); 280 else 281 return (80); 282 } 283