progressmeter.c revision 113908
1113908Sdes/* 2113908Sdes * Copyright (c) 1999 Theo de Raadt. All rights reserved. 3113908Sdes * Copyright (c) 1999 Aaron Campbell. All rights reserved. 4113908Sdes * 5113908Sdes * Redistribution and use in source and binary forms, with or without 6113908Sdes * modification, are permitted provided that the following conditions 7113908Sdes * are met: 8113908Sdes * 1. Redistributions of source code must retain the above copyright 9113908Sdes * notice, this list of conditions and the following disclaimer. 10113908Sdes * 2. Redistributions in binary form must reproduce the above copyright 11113908Sdes * notice, this list of conditions and the following disclaimer in the 12113908Sdes * documentation and/or other materials provided with the distribution. 13113908Sdes * 14113908Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15113908Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16113908Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17113908Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18113908Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19113908Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20113908Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21113908Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22113908Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23113908Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24113908Sdes */ 25113908Sdes 26113908Sdes/* 27113908Sdes * Parts from: 28113908Sdes * 29113908Sdes * Copyright (c) 1983, 1990, 1992, 1993, 1995 30113908Sdes * The Regents of the University of California. All rights reserved. 31113908Sdes * 32113908Sdes * Redistribution and use in source and binary forms, with or without 33113908Sdes * modification, are permitted provided that the following conditions 34113908Sdes * are met: 35113908Sdes * 1. Redistributions of source code must retain the above copyright 36113908Sdes * notice, this list of conditions and the following disclaimer. 37113908Sdes * 2. Redistributions in binary form must reproduce the above copyright 38113908Sdes * notice, this list of conditions and the following disclaimer in the 39113908Sdes * documentation and/or other materials provided with the distribution. 40113908Sdes * 3. All advertising materials mentioning features or use of this software 41113908Sdes * must display the following acknowledgement: 42113908Sdes * This product includes software developed by the University of 43113908Sdes * California, Berkeley and its contributors. 44113908Sdes * 4. Neither the name of the University nor the names of its contributors 45113908Sdes * may be used to endorse or promote products derived from this software 46113908Sdes * without specific prior written permission. 47113908Sdes * 48113908Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49113908Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50113908Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51113908Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52113908Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53113908Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54113908Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55113908Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56113908Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57113908Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58113908Sdes * SUCH DAMAGE. 59113908Sdes * 60113908Sdes */ 61113908Sdes 62113908Sdes#include "includes.h" 63113908SdesRCSID("$OpenBSD: progressmeter.c,v 1.3 2003/03/17 10:38:38 markus Exp $"); 64113908Sdes 65113908Sdes#ifdef HAVE_LIBGEN_H 66113908Sdes#include <libgen.h> 67113908Sdes#endif 68113908Sdes 69113908Sdes#include "atomicio.h" 70113908Sdes#include "progressmeter.h" 71113908Sdes 72113908Sdes/* Number of seconds before xfer considered "stalled". */ 73113908Sdes#define STALLTIME 5 74113908Sdes/* alarm() interval for updating progress meter. */ 75113908Sdes#define PROGRESSTIME 1 76113908Sdes 77113908Sdes/* Signal handler used for updating the progress meter. */ 78113908Sdesstatic void update_progress_meter(int); 79113908Sdes 80113908Sdes/* Returns non-zero if we are the foreground process. */ 81113908Sdesstatic int foregroundproc(void); 82113908Sdes 83113908Sdes/* Returns width of the terminal (for progress meter calculations). */ 84113908Sdesstatic int get_tty_width(void); 85113908Sdes 86113908Sdes/* Visual statistics about files as they are transferred. */ 87113908Sdesstatic void draw_progress_meter(void); 88113908Sdes 89113908Sdes/* Time a transfer started. */ 90113908Sdesstatic struct timeval start; 91113908Sdes 92113908Sdes/* Number of bytes of current file transferred so far. */ 93113908Sdesstatic volatile off_t *statbytes; 94113908Sdes 95113908Sdes/* Total size of current file. */ 96113908Sdesstatic off_t totalbytes; 97113908Sdes 98113908Sdes/* Name of current file being transferred. */ 99113908Sdesstatic char *curfile; 100113908Sdes 101113908Sdes/* Time of last update. */ 102113908Sdesstatic struct timeval lastupdate; 103113908Sdes 104113908Sdes/* Size at the time of the last update. */ 105113908Sdesstatic off_t lastsize; 106113908Sdes 107113908Sdesvoid 108113908Sdesstart_progress_meter(char *file, off_t filesize, off_t *counter) 109113908Sdes{ 110113908Sdes if ((curfile = basename(file)) == NULL) 111113908Sdes curfile = file; 112113908Sdes 113113908Sdes totalbytes = filesize; 114113908Sdes statbytes = counter; 115113908Sdes (void) gettimeofday(&start, (struct timezone *) 0); 116113908Sdes lastupdate = start; 117113908Sdes lastsize = 0; 118113908Sdes 119113908Sdes draw_progress_meter(); 120113908Sdes signal(SIGALRM, update_progress_meter); 121113908Sdes alarm(PROGRESSTIME); 122113908Sdes} 123113908Sdes 124113908Sdesvoid 125113908Sdesstop_progress_meter() 126113908Sdes{ 127113908Sdes alarm(0); 128113908Sdes draw_progress_meter(); 129113908Sdes if (foregroundproc() != 0) 130113908Sdes atomicio(write, fileno(stdout), "\n", 1); 131113908Sdes} 132113908Sdes 133113908Sdesstatic void 134113908Sdesupdate_progress_meter(int ignore) 135113908Sdes{ 136113908Sdes int save_errno = errno; 137113908Sdes 138113908Sdes draw_progress_meter(); 139113908Sdes signal(SIGALRM, update_progress_meter); 140113908Sdes alarm(PROGRESSTIME); 141113908Sdes errno = save_errno; 142113908Sdes} 143113908Sdes 144113908Sdesstatic int 145113908Sdesforegroundproc(void) 146113908Sdes{ 147113908Sdes static pid_t pgrp = -1; 148113908Sdes int ctty_pgrp; 149113908Sdes 150113908Sdes if (pgrp == -1) 151113908Sdes pgrp = getpgrp(); 152113908Sdes 153113908Sdes#ifdef HAVE_TCGETPGRP 154113908Sdes return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 && 155113908Sdes ctty_pgrp == pgrp); 156113908Sdes#else 157113908Sdes return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && 158113908Sdes ctty_pgrp == pgrp)); 159113908Sdes#endif 160113908Sdes} 161113908Sdes 162113908Sdesstatic void 163113908Sdesdraw_progress_meter() 164113908Sdes{ 165113908Sdes static const char spaces[] = " " 166113908Sdes " " 167113908Sdes " " 168113908Sdes " " 169113908Sdes " " 170113908Sdes " "; 171113908Sdes static const char prefixes[] = " KMGTP"; 172113908Sdes struct timeval now, td, wait; 173113908Sdes off_t cursize, abbrevsize, bytespersec; 174113908Sdes double elapsed; 175113908Sdes int ratio, remaining, i, ai, bi, nspaces; 176113908Sdes char buf[512]; 177113908Sdes 178113908Sdes if (foregroundproc() == 0) 179113908Sdes return; 180113908Sdes 181113908Sdes (void) gettimeofday(&now, (struct timezone *) 0); 182113908Sdes cursize = *statbytes; 183113908Sdes if (totalbytes != 0) { 184113908Sdes ratio = 100.0 * cursize / totalbytes; 185113908Sdes ratio = MAX(ratio, 0); 186113908Sdes ratio = MIN(ratio, 100); 187113908Sdes } else 188113908Sdes ratio = 100; 189113908Sdes 190113908Sdes abbrevsize = cursize; 191113908Sdes for (ai = 0; abbrevsize >= 10000 && ai < sizeof(prefixes); ai++) 192113908Sdes abbrevsize >>= 10; 193113908Sdes 194113908Sdes timersub(&now, &lastupdate, &wait); 195113908Sdes if (cursize > lastsize) { 196113908Sdes lastupdate = now; 197113908Sdes lastsize = cursize; 198113908Sdes wait.tv_sec = 0; 199113908Sdes } 200113908Sdes timersub(&now, &start, &td); 201113908Sdes elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 202113908Sdes 203113908Sdes bytespersec = 0; 204113908Sdes if (cursize > 0) { 205113908Sdes bytespersec = cursize; 206113908Sdes if (elapsed > 0.0) 207113908Sdes bytespersec /= elapsed; 208113908Sdes } 209113908Sdes for (bi = 1; bytespersec >= 1024000 && bi < sizeof(prefixes); bi++) 210113908Sdes bytespersec >>= 10; 211113908Sdes 212113908Sdes nspaces = MIN(get_tty_width() - 79, sizeof(spaces) - 1); 213113908Sdes 214113908Sdes#ifdef HAVE_LONG_LONG_INT 215113908Sdes snprintf(buf, sizeof(buf), 216113908Sdes "\r%-45.45s%.*s%3d%% %4lld%c%c %3lld.%01d%cB/s", 217113908Sdes curfile, 218113908Sdes nspaces, 219113908Sdes spaces, 220113908Sdes ratio, 221113908Sdes (long long)abbrevsize, 222113908Sdes prefixes[ai], 223113908Sdes ai == 0 ? ' ' : 'B', 224113908Sdes (long long)(bytespersec / 1024), 225113908Sdes (int)((bytespersec % 1024) * 10 / 1024), 226113908Sdes prefixes[bi] 227113908Sdes ); 228113908Sdes#else 229113908Sdes /* XXX: Handle integer overflow? */ 230113908Sdes snprintf(buf, sizeof(buf), 231113908Sdes "\r%-45.45s%.*s%3d%% %4lu%c%c %3lu.%01d%cB/s", 232113908Sdes curfile, 233113908Sdes nspaces, 234113908Sdes spaces, 235113908Sdes ratio, 236113908Sdes (u_long)abbrevsize, 237113908Sdes prefixes[ai], 238113908Sdes ai == 0 ? ' ' : 'B', 239113908Sdes (u_long)(bytespersec / 1024), 240113908Sdes (int)((bytespersec % 1024) * 10 / 1024), 241113908Sdes prefixes[bi] 242113908Sdes ); 243113908Sdes#endif 244113908Sdes 245113908Sdes if (cursize <= 0 || elapsed <= 0.0 || cursize > totalbytes) { 246113908Sdes snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 247113908Sdes " --:-- ETA"); 248113908Sdes } else if (wait.tv_sec >= STALLTIME) { 249113908Sdes snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 250113908Sdes " - stalled -"); 251113908Sdes } else { 252113908Sdes if (cursize != totalbytes) 253113908Sdes remaining = (int)(totalbytes / (cursize / elapsed) - 254113908Sdes elapsed); 255113908Sdes else 256113908Sdes remaining = elapsed; 257113908Sdes 258113908Sdes i = remaining / 3600; 259113908Sdes if (i) 260113908Sdes snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 261113908Sdes "%2d:", i); 262113908Sdes else 263113908Sdes snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 264113908Sdes " "); 265113908Sdes i = remaining % 3600; 266113908Sdes snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), 267113908Sdes "%02d:%02d%s", i / 60, i % 60, 268113908Sdes (cursize != totalbytes) ? " ETA" : " "); 269113908Sdes } 270113908Sdes atomicio(write, fileno(stdout), buf, strlen(buf)); 271113908Sdes} 272113908Sdes 273113908Sdesstatic int 274113908Sdesget_tty_width(void) 275113908Sdes{ 276113908Sdes struct winsize winsize; 277113908Sdes 278113908Sdes if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) 279113908Sdes return (winsize.ws_col ? winsize.ws_col : 80); 280113908Sdes else 281113908Sdes return (80); 282113908Sdes} 283