1*f982db4aSGavin Atkinson /* $NetBSD: progressbar.c,v 1.7 2005/04/11 01:49:31 lukem Exp $ */ 2*f982db4aSGavin Atkinson 3*f982db4aSGavin Atkinson /*- 4*f982db4aSGavin Atkinson * Copyright (c) 1997-2003 The NetBSD Foundation, Inc. 5*f982db4aSGavin Atkinson * All rights reserved. 6*f982db4aSGavin Atkinson * 7*f982db4aSGavin Atkinson * This code is derived from software contributed to The NetBSD Foundation 8*f982db4aSGavin Atkinson * by Luke Mewburn. 9*f982db4aSGavin Atkinson * 10*f982db4aSGavin Atkinson * Redistribution and use in source and binary forms, with or without 11*f982db4aSGavin Atkinson * modification, are permitted provided that the following conditions 12*f982db4aSGavin Atkinson * are met: 13*f982db4aSGavin Atkinson * 1. Redistributions of source code must retain the above copyright 14*f982db4aSGavin Atkinson * notice, this list of conditions and the following disclaimer. 15*f982db4aSGavin Atkinson * 2. Redistributions in binary form must reproduce the above copyright 16*f982db4aSGavin Atkinson * notice, this list of conditions and the following disclaimer in the 17*f982db4aSGavin Atkinson * documentation and/or other materials provided with the distribution. 18*f982db4aSGavin Atkinson * 3. All advertising materials mentioning features or use of this software 19*f982db4aSGavin Atkinson * must display the following acknowledgement: 20*f982db4aSGavin Atkinson * This product includes software developed by the NetBSD 21*f982db4aSGavin Atkinson * Foundation, Inc. and its contributors. 22*f982db4aSGavin Atkinson * 4. Neither the name of The NetBSD Foundation nor the names of its 23*f982db4aSGavin Atkinson * contributors may be used to endorse or promote products derived 24*f982db4aSGavin Atkinson * from this software without specific prior written permission. 25*f982db4aSGavin Atkinson * 26*f982db4aSGavin Atkinson * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27*f982db4aSGavin Atkinson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28*f982db4aSGavin Atkinson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29*f982db4aSGavin Atkinson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30*f982db4aSGavin Atkinson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31*f982db4aSGavin Atkinson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32*f982db4aSGavin Atkinson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33*f982db4aSGavin Atkinson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34*f982db4aSGavin Atkinson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35*f982db4aSGavin Atkinson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36*f982db4aSGavin Atkinson * POSSIBILITY OF SUCH DAMAGE. 37*f982db4aSGavin Atkinson */ 38*f982db4aSGavin Atkinson 39*f982db4aSGavin Atkinson #include <sys/cdefs.h> 40*f982db4aSGavin Atkinson #ifndef lint 41*f982db4aSGavin Atkinson __RCSID("$NetBSD: progressbar.c,v 1.7 2005/04/11 01:49:31 lukem Exp $"); 42*f982db4aSGavin Atkinson #endif /* not lint */ 43*f982db4aSGavin Atkinson 44*f982db4aSGavin Atkinson /* 45*f982db4aSGavin Atkinson * FTP User Program -- Misc support routines 46*f982db4aSGavin Atkinson */ 47*f982db4aSGavin Atkinson #include <sys/types.h> 48*f982db4aSGavin Atkinson #include <sys/param.h> 49*f982db4aSGavin Atkinson #include <sys/time.h> 50*f982db4aSGavin Atkinson 51*f982db4aSGavin Atkinson #include <err.h> 52*f982db4aSGavin Atkinson #include <errno.h> 53*f982db4aSGavin Atkinson #include <signal.h> 54*f982db4aSGavin Atkinson #include <stdio.h> 55*f982db4aSGavin Atkinson #include <stdlib.h> 56*f982db4aSGavin Atkinson #include <string.h> 57*f982db4aSGavin Atkinson #include <time.h> 58*f982db4aSGavin Atkinson #include <unistd.h> 59*f982db4aSGavin Atkinson 60*f982db4aSGavin Atkinson #include "progressbar.h" 61*f982db4aSGavin Atkinson 62*f982db4aSGavin Atkinson #define SECSPERHOUR (60 * 60) 63*f982db4aSGavin Atkinson #define SECSPERDAY ((long)60 * 60 * 24) 64*f982db4aSGavin Atkinson 65*f982db4aSGavin Atkinson #if !defined(NO_PROGRESS) 66*f982db4aSGavin Atkinson /* 67*f982db4aSGavin Atkinson * return non-zero if we're the current foreground process 68*f982db4aSGavin Atkinson */ 69*f982db4aSGavin Atkinson int 70*f982db4aSGavin Atkinson foregroundproc(void) 71*f982db4aSGavin Atkinson { 72*f982db4aSGavin Atkinson static pid_t pgrp = -1; 73*f982db4aSGavin Atkinson 74*f982db4aSGavin Atkinson if (pgrp == -1) 75*f982db4aSGavin Atkinson pgrp = getpgrp(); 76*f982db4aSGavin Atkinson 77*f982db4aSGavin Atkinson return (tcgetpgrp(fileno(ttyout)) == pgrp); 78*f982db4aSGavin Atkinson } 79*f982db4aSGavin Atkinson #endif /* !defined(NO_PROGRESS) */ 80*f982db4aSGavin Atkinson 81*f982db4aSGavin Atkinson 82*f982db4aSGavin Atkinson static void updateprogressmeter(int); 83*f982db4aSGavin Atkinson 84*f982db4aSGavin Atkinson /* 85*f982db4aSGavin Atkinson * SIGALRM handler to update the progress meter 86*f982db4aSGavin Atkinson */ 87*f982db4aSGavin Atkinson static void 88*f982db4aSGavin Atkinson updateprogressmeter(int dummy) 89*f982db4aSGavin Atkinson { 90*f982db4aSGavin Atkinson int oerrno = errno; 91*f982db4aSGavin Atkinson 92*f982db4aSGavin Atkinson progressmeter(0); 93*f982db4aSGavin Atkinson errno = oerrno; 94*f982db4aSGavin Atkinson } 95*f982db4aSGavin Atkinson 96*f982db4aSGavin Atkinson /* 97*f982db4aSGavin Atkinson * List of order of magnitude prefixes. 98*f982db4aSGavin Atkinson * The last is `P', as 2^64 = 16384 Petabytes 99*f982db4aSGavin Atkinson */ 100*f982db4aSGavin Atkinson static const char prefixes[] = " KMGTP"; 101*f982db4aSGavin Atkinson 102*f982db4aSGavin Atkinson /* 103*f982db4aSGavin Atkinson * Display a transfer progress bar if progress is non-zero. 104*f982db4aSGavin Atkinson * SIGALRM is hijacked for use by this function. 105*f982db4aSGavin Atkinson * - Before the transfer, set filesize to size of file (or -1 if unknown), 106*f982db4aSGavin Atkinson * and call with flag = -1. This starts the once per second timer, 107*f982db4aSGavin Atkinson * and a call to updateprogressmeter() upon SIGALRM. 108*f982db4aSGavin Atkinson * - During the transfer, updateprogressmeter will call progressmeter 109*f982db4aSGavin Atkinson * with flag = 0 110*f982db4aSGavin Atkinson * - After the transfer, call with flag = 1 111*f982db4aSGavin Atkinson */ 112*f982db4aSGavin Atkinson static struct timeval start; 113*f982db4aSGavin Atkinson static struct timeval lastupdate; 114*f982db4aSGavin Atkinson 115*f982db4aSGavin Atkinson #define BUFLEFT (sizeof(buf) - len) 116*f982db4aSGavin Atkinson 117*f982db4aSGavin Atkinson void 118*f982db4aSGavin Atkinson progressmeter(int flag) 119*f982db4aSGavin Atkinson { 120*f982db4aSGavin Atkinson static off_t lastsize; 121*f982db4aSGavin Atkinson off_t cursize; 122*f982db4aSGavin Atkinson struct timeval now, wait; 123*f982db4aSGavin Atkinson #ifndef NO_PROGRESS 124*f982db4aSGavin Atkinson struct timeval td; 125*f982db4aSGavin Atkinson off_t abbrevsize, bytespersec; 126*f982db4aSGavin Atkinson double elapsed; 127*f982db4aSGavin Atkinson int ratio, barlength, i, remaining; 128*f982db4aSGavin Atkinson 129*f982db4aSGavin Atkinson /* 130*f982db4aSGavin Atkinson * Work variables for progress bar. 131*f982db4aSGavin Atkinson * 132*f982db4aSGavin Atkinson * XXX: if the format of the progress bar changes 133*f982db4aSGavin Atkinson * (especially the number of characters in the 134*f982db4aSGavin Atkinson * `static' portion of it), be sure to update 135*f982db4aSGavin Atkinson * these appropriately. 136*f982db4aSGavin Atkinson */ 137*f982db4aSGavin Atkinson #endif 138*f982db4aSGavin Atkinson int len; 139*f982db4aSGavin Atkinson char buf[256]; /* workspace for progress bar */ 140*f982db4aSGavin Atkinson #ifndef NO_PROGRESS 141*f982db4aSGavin Atkinson #define BAROVERHEAD 43 /* non `*' portion of progress bar */ 142*f982db4aSGavin Atkinson /* 143*f982db4aSGavin Atkinson * stars should contain at least 144*f982db4aSGavin Atkinson * sizeof(buf) - BAROVERHEAD entries 145*f982db4aSGavin Atkinson */ 146*f982db4aSGavin Atkinson static const char stars[] = 147*f982db4aSGavin Atkinson "*****************************************************************************" 148*f982db4aSGavin Atkinson "*****************************************************************************" 149*f982db4aSGavin Atkinson "*****************************************************************************"; 150*f982db4aSGavin Atkinson 151*f982db4aSGavin Atkinson #endif 152*f982db4aSGavin Atkinson 153*f982db4aSGavin Atkinson if (flag == -1) { 154*f982db4aSGavin Atkinson (void)gettimeofday(&start, NULL); 155*f982db4aSGavin Atkinson lastupdate = start; 156*f982db4aSGavin Atkinson lastsize = restart_point; 157*f982db4aSGavin Atkinson } 158*f982db4aSGavin Atkinson 159*f982db4aSGavin Atkinson (void)gettimeofday(&now, NULL); 160*f982db4aSGavin Atkinson cursize = bytes + restart_point; 161*f982db4aSGavin Atkinson timersub(&now, &lastupdate, &wait); 162*f982db4aSGavin Atkinson if (cursize > lastsize) { 163*f982db4aSGavin Atkinson lastupdate = now; 164*f982db4aSGavin Atkinson lastsize = cursize; 165*f982db4aSGavin Atkinson wait.tv_sec = 0; 166*f982db4aSGavin Atkinson } else { 167*f982db4aSGavin Atkinson #ifndef STANDALONE_PROGRESS 168*f982db4aSGavin Atkinson if (quit_time > 0 && wait.tv_sec > quit_time) { 169*f982db4aSGavin Atkinson len = snprintf(buf, sizeof(buf), "\r\n%s: " 170*f982db4aSGavin Atkinson "transfer aborted because stalled for %lu sec.\r\n", 171*f982db4aSGavin Atkinson getprogname(), (unsigned long)wait.tv_sec); 172*f982db4aSGavin Atkinson (void)write(fileno(ttyout), buf, len); 173*f982db4aSGavin Atkinson (void)xsignal(SIGALRM, SIG_DFL); 174*f982db4aSGavin Atkinson alarmtimer(0); 175*f982db4aSGavin Atkinson siglongjmp(toplevel, 1); 176*f982db4aSGavin Atkinson } 177*f982db4aSGavin Atkinson #endif /* !STANDALONE_PROGRESS */ 178*f982db4aSGavin Atkinson } 179*f982db4aSGavin Atkinson /* 180*f982db4aSGavin Atkinson * Always set the handler even if we are not the foreground process. 181*f982db4aSGavin Atkinson */ 182*f982db4aSGavin Atkinson #ifdef STANDALONE_PROGRESS 183*f982db4aSGavin Atkinson if (progress) { 184*f982db4aSGavin Atkinson #else 185*f982db4aSGavin Atkinson if (quit_time > 0 || progress) { 186*f982db4aSGavin Atkinson #endif /* !STANDALONE_PROGRESS */ 187*f982db4aSGavin Atkinson if (flag == -1) { 188*f982db4aSGavin Atkinson (void)xsignal_restart(SIGALRM, updateprogressmeter, 1); 189*f982db4aSGavin Atkinson alarmtimer(1); /* set alarm timer for 1 Hz */ 190*f982db4aSGavin Atkinson } else if (flag == 1) { 191*f982db4aSGavin Atkinson (void)xsignal(SIGALRM, SIG_DFL); 192*f982db4aSGavin Atkinson alarmtimer(0); 193*f982db4aSGavin Atkinson } 194*f982db4aSGavin Atkinson } 195*f982db4aSGavin Atkinson #ifndef NO_PROGRESS 196*f982db4aSGavin Atkinson if (!progress) 197*f982db4aSGavin Atkinson return; 198*f982db4aSGavin Atkinson len = 0; 199*f982db4aSGavin Atkinson 200*f982db4aSGavin Atkinson /* 201*f982db4aSGavin Atkinson * print progress bar only if we are foreground process. 202*f982db4aSGavin Atkinson */ 203*f982db4aSGavin Atkinson if (! foregroundproc()) 204*f982db4aSGavin Atkinson return; 205*f982db4aSGavin Atkinson 206*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "\r"); 207*f982db4aSGavin Atkinson if (prefix) 208*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "%s", prefix); 209*f982db4aSGavin Atkinson if (filesize > 0) { 210*f982db4aSGavin Atkinson ratio = (int)((double)cursize * 100.0 / (double)filesize); 211*f982db4aSGavin Atkinson ratio = MAX(ratio, 0); 212*f982db4aSGavin Atkinson ratio = MIN(ratio, 100); 213*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "%3d%% ", ratio); 214*f982db4aSGavin Atkinson 215*f982db4aSGavin Atkinson /* 216*f982db4aSGavin Atkinson * calculate the length of the `*' bar, ensuring that 217*f982db4aSGavin Atkinson * the number of stars won't exceed the buffer size 218*f982db4aSGavin Atkinson */ 219*f982db4aSGavin Atkinson barlength = MIN(sizeof(buf) - 1, ttywidth) - BAROVERHEAD; 220*f982db4aSGavin Atkinson if (prefix) 221*f982db4aSGavin Atkinson barlength -= strlen(prefix); 222*f982db4aSGavin Atkinson if (barlength > 0) { 223*f982db4aSGavin Atkinson i = barlength * ratio / 100; 224*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 225*f982db4aSGavin Atkinson "|%.*s%*s|", i, stars, barlength - i, ""); 226*f982db4aSGavin Atkinson } 227*f982db4aSGavin Atkinson } 228*f982db4aSGavin Atkinson 229*f982db4aSGavin Atkinson abbrevsize = cursize; 230*f982db4aSGavin Atkinson for (i = 0; abbrevsize >= 100000 && i < sizeof(prefixes); i++) 231*f982db4aSGavin Atkinson abbrevsize >>= 10; 232*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, " " LLFP("5") " %c%c ", 233*f982db4aSGavin Atkinson (LLT)abbrevsize, 234*f982db4aSGavin Atkinson prefixes[i], 235*f982db4aSGavin Atkinson i == 0 ? ' ' : 'B'); 236*f982db4aSGavin Atkinson 237*f982db4aSGavin Atkinson timersub(&now, &start, &td); 238*f982db4aSGavin Atkinson elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 239*f982db4aSGavin Atkinson 240*f982db4aSGavin Atkinson bytespersec = 0; 241*f982db4aSGavin Atkinson if (bytes > 0) { 242*f982db4aSGavin Atkinson bytespersec = bytes; 243*f982db4aSGavin Atkinson if (elapsed > 0.0) 244*f982db4aSGavin Atkinson bytespersec /= elapsed; 245*f982db4aSGavin Atkinson } 246*f982db4aSGavin Atkinson for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++) 247*f982db4aSGavin Atkinson bytespersec >>= 10; 248*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 249*f982db4aSGavin Atkinson " " LLFP("3") ".%02d %cB/s ", 250*f982db4aSGavin Atkinson (LLT)(bytespersec / 1024), 251*f982db4aSGavin Atkinson (int)((bytespersec % 1024) * 100 / 1024), 252*f982db4aSGavin Atkinson prefixes[i]); 253*f982db4aSGavin Atkinson 254*f982db4aSGavin Atkinson if (filesize > 0) { 255*f982db4aSGavin Atkinson if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { 256*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, " --:-- ETA"); 257*f982db4aSGavin Atkinson } else if (wait.tv_sec >= STALLTIME) { 258*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, " - stalled -"); 259*f982db4aSGavin Atkinson } else { 260*f982db4aSGavin Atkinson remaining = (int) 261*f982db4aSGavin Atkinson ((filesize - restart_point) / (bytes / elapsed) - 262*f982db4aSGavin Atkinson elapsed); 263*f982db4aSGavin Atkinson if (remaining >= 100 * SECSPERHOUR) 264*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 265*f982db4aSGavin Atkinson " --:-- ETA"); 266*f982db4aSGavin Atkinson else { 267*f982db4aSGavin Atkinson i = remaining / SECSPERHOUR; 268*f982db4aSGavin Atkinson if (i) 269*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 270*f982db4aSGavin Atkinson "%2d:", i); 271*f982db4aSGavin Atkinson else 272*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 273*f982db4aSGavin Atkinson " "); 274*f982db4aSGavin Atkinson i = remaining % SECSPERHOUR; 275*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 276*f982db4aSGavin Atkinson "%02d:%02d ETA", i / 60, i % 60); 277*f982db4aSGavin Atkinson } 278*f982db4aSGavin Atkinson } 279*f982db4aSGavin Atkinson } 280*f982db4aSGavin Atkinson if (flag == 1) 281*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "\n"); 282*f982db4aSGavin Atkinson (void)write(fileno(ttyout), buf, len); 283*f982db4aSGavin Atkinson 284*f982db4aSGavin Atkinson #endif /* !NO_PROGRESS */ 285*f982db4aSGavin Atkinson } 286*f982db4aSGavin Atkinson 287*f982db4aSGavin Atkinson #ifndef STANDALONE_PROGRESS 288*f982db4aSGavin Atkinson /* 289*f982db4aSGavin Atkinson * Display transfer statistics. 290*f982db4aSGavin Atkinson * Requires start to be initialised by progressmeter(-1), 291*f982db4aSGavin Atkinson * direction to be defined by xfer routines, and filesize and bytes 292*f982db4aSGavin Atkinson * to be updated by xfer routines 293*f982db4aSGavin Atkinson * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr 294*f982db4aSGavin Atkinson * instead of ttyout. 295*f982db4aSGavin Atkinson */ 296*f982db4aSGavin Atkinson void 297*f982db4aSGavin Atkinson ptransfer(int siginfo) 298*f982db4aSGavin Atkinson { 299*f982db4aSGavin Atkinson struct timeval now, td, wait; 300*f982db4aSGavin Atkinson double elapsed; 301*f982db4aSGavin Atkinson off_t bytespersec; 302*f982db4aSGavin Atkinson int remaining, hh, i, len; 303*f982db4aSGavin Atkinson 304*f982db4aSGavin Atkinson char buf[256]; /* Work variable for transfer status. */ 305*f982db4aSGavin Atkinson 306*f982db4aSGavin Atkinson if (!verbose && !progress && !siginfo) 307*f982db4aSGavin Atkinson return; 308*f982db4aSGavin Atkinson 309*f982db4aSGavin Atkinson (void)gettimeofday(&now, NULL); 310*f982db4aSGavin Atkinson timersub(&now, &start, &td); 311*f982db4aSGavin Atkinson elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 312*f982db4aSGavin Atkinson bytespersec = 0; 313*f982db4aSGavin Atkinson if (bytes > 0) { 314*f982db4aSGavin Atkinson bytespersec = bytes; 315*f982db4aSGavin Atkinson if (elapsed > 0.0) 316*f982db4aSGavin Atkinson bytespersec /= elapsed; 317*f982db4aSGavin Atkinson } 318*f982db4aSGavin Atkinson len = 0; 319*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, LLF " byte%s %s in ", 320*f982db4aSGavin Atkinson (LLT)bytes, bytes == 1 ? "" : "s", direction); 321*f982db4aSGavin Atkinson remaining = (int)elapsed; 322*f982db4aSGavin Atkinson if (remaining > SECSPERDAY) { 323*f982db4aSGavin Atkinson int days; 324*f982db4aSGavin Atkinson 325*f982db4aSGavin Atkinson days = remaining / SECSPERDAY; 326*f982db4aSGavin Atkinson remaining %= SECSPERDAY; 327*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 328*f982db4aSGavin Atkinson "%d day%s ", days, days == 1 ? "" : "s"); 329*f982db4aSGavin Atkinson } 330*f982db4aSGavin Atkinson hh = remaining / SECSPERHOUR; 331*f982db4aSGavin Atkinson remaining %= SECSPERHOUR; 332*f982db4aSGavin Atkinson if (hh) 333*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "%2d:", hh); 334*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, 335*f982db4aSGavin Atkinson "%02d:%02d ", remaining / 60, remaining % 60); 336*f982db4aSGavin Atkinson 337*f982db4aSGavin Atkinson for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++) 338*f982db4aSGavin Atkinson bytespersec >>= 10; 339*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "(" LLF ".%02d %cB/s)", 340*f982db4aSGavin Atkinson (LLT)(bytespersec / 1024), 341*f982db4aSGavin Atkinson (int)((bytespersec % 1024) * 100 / 1024), 342*f982db4aSGavin Atkinson prefixes[i]); 343*f982db4aSGavin Atkinson 344*f982db4aSGavin Atkinson if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 345*f982db4aSGavin Atkinson && bytes + restart_point <= filesize) { 346*f982db4aSGavin Atkinson remaining = (int)((filesize - restart_point) / 347*f982db4aSGavin Atkinson (bytes / elapsed) - elapsed); 348*f982db4aSGavin Atkinson hh = remaining / SECSPERHOUR; 349*f982db4aSGavin Atkinson remaining %= SECSPERHOUR; 350*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, " ETA: "); 351*f982db4aSGavin Atkinson if (hh) 352*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "%2d:", hh); 353*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "%02d:%02d", 354*f982db4aSGavin Atkinson remaining / 60, remaining % 60); 355*f982db4aSGavin Atkinson timersub(&now, &lastupdate, &wait); 356*f982db4aSGavin Atkinson if (wait.tv_sec >= STALLTIME) 357*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, " (stalled)"); 358*f982db4aSGavin Atkinson } 359*f982db4aSGavin Atkinson len += snprintf(buf + len, BUFLEFT, "\n"); 360*f982db4aSGavin Atkinson (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len); 361*f982db4aSGavin Atkinson } 362*f982db4aSGavin Atkinson 363*f982db4aSGavin Atkinson /* 364*f982db4aSGavin Atkinson * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress 365*f982db4aSGavin Atkinson */ 366*f982db4aSGavin Atkinson void 367*f982db4aSGavin Atkinson psummary(int notused) 368*f982db4aSGavin Atkinson { 369*f982db4aSGavin Atkinson int oerrno = errno; 370*f982db4aSGavin Atkinson 371*f982db4aSGavin Atkinson if (bytes > 0) { 372*f982db4aSGavin Atkinson if (fromatty) 373*f982db4aSGavin Atkinson write(fileno(ttyout), "\n", 1); 374*f982db4aSGavin Atkinson ptransfer(1); 375*f982db4aSGavin Atkinson } 376*f982db4aSGavin Atkinson errno = oerrno; 377*f982db4aSGavin Atkinson } 378*f982db4aSGavin Atkinson #endif /* !STANDALONE_PROGRESS */ 379*f982db4aSGavin Atkinson 380*f982db4aSGavin Atkinson 381*f982db4aSGavin Atkinson /* 382*f982db4aSGavin Atkinson * Set the SIGALRM interval timer for wait seconds, 0 to disable. 383*f982db4aSGavin Atkinson */ 384*f982db4aSGavin Atkinson void 385*f982db4aSGavin Atkinson alarmtimer(int wait) 386*f982db4aSGavin Atkinson { 387*f982db4aSGavin Atkinson struct itimerval itv; 388*f982db4aSGavin Atkinson 389*f982db4aSGavin Atkinson itv.it_value.tv_sec = wait; 390*f982db4aSGavin Atkinson itv.it_value.tv_usec = 0; 391*f982db4aSGavin Atkinson itv.it_interval = itv.it_value; 392*f982db4aSGavin Atkinson setitimer(ITIMER_REAL, &itv, NULL); 393*f982db4aSGavin Atkinson } 394*f982db4aSGavin Atkinson 395*f982db4aSGavin Atkinson 396*f982db4aSGavin Atkinson /* 397*f982db4aSGavin Atkinson * Install a POSIX signal handler, allowing the invoker to set whether 398*f982db4aSGavin Atkinson * the signal should be restartable or not 399*f982db4aSGavin Atkinson */ 400*f982db4aSGavin Atkinson sigfunc 401*f982db4aSGavin Atkinson xsignal_restart(int sig, sigfunc func, int restartable) 402*f982db4aSGavin Atkinson { 403*f982db4aSGavin Atkinson struct sigaction act, oact; 404*f982db4aSGavin Atkinson act.sa_handler = func; 405*f982db4aSGavin Atkinson 406*f982db4aSGavin Atkinson sigemptyset(&act.sa_mask); 407*f982db4aSGavin Atkinson #if defined(SA_RESTART) /* 4.4BSD, Posix(?), SVR4 */ 408*f982db4aSGavin Atkinson act.sa_flags = restartable ? SA_RESTART : 0; 409*f982db4aSGavin Atkinson #elif defined(SA_INTERRUPT) /* SunOS 4.x */ 410*f982db4aSGavin Atkinson act.sa_flags = restartable ? 0 : SA_INTERRUPT; 411*f982db4aSGavin Atkinson #else 412*f982db4aSGavin Atkinson #error "system must have SA_RESTART or SA_INTERRUPT" 413*f982db4aSGavin Atkinson #endif 414*f982db4aSGavin Atkinson if (sigaction(sig, &act, &oact) < 0) 415*f982db4aSGavin Atkinson return (SIG_ERR); 416*f982db4aSGavin Atkinson return (oact.sa_handler); 417*f982db4aSGavin Atkinson } 418*f982db4aSGavin Atkinson 419*f982db4aSGavin Atkinson /* 420*f982db4aSGavin Atkinson * Install a signal handler with the `restartable' flag set dependent upon 421*f982db4aSGavin Atkinson * which signal is being set. (This is a wrapper to xsignal_restart()) 422*f982db4aSGavin Atkinson */ 423*f982db4aSGavin Atkinson sigfunc 424*f982db4aSGavin Atkinson xsignal(int sig, sigfunc func) 425*f982db4aSGavin Atkinson { 426*f982db4aSGavin Atkinson int restartable; 427*f982db4aSGavin Atkinson 428*f982db4aSGavin Atkinson /* 429*f982db4aSGavin Atkinson * Some signals print output or change the state of the process. 430*f982db4aSGavin Atkinson * There should be restartable, so that reads and writes are 431*f982db4aSGavin Atkinson * not affected. Some signals should cause program flow to change; 432*f982db4aSGavin Atkinson * these signals should not be restartable, so that the system call 433*f982db4aSGavin Atkinson * will return with EINTR, and the program will go do something 434*f982db4aSGavin Atkinson * different. If the signal handler calls longjmp() or siglongjmp(), 435*f982db4aSGavin Atkinson * it doesn't matter if it's restartable. 436*f982db4aSGavin Atkinson */ 437*f982db4aSGavin Atkinson 438*f982db4aSGavin Atkinson switch(sig) { 439*f982db4aSGavin Atkinson #ifdef SIGINFO 440*f982db4aSGavin Atkinson case SIGINFO: 441*f982db4aSGavin Atkinson #endif 442*f982db4aSGavin Atkinson case SIGQUIT: 443*f982db4aSGavin Atkinson case SIGUSR1: 444*f982db4aSGavin Atkinson case SIGUSR2: 445*f982db4aSGavin Atkinson case SIGWINCH: 446*f982db4aSGavin Atkinson restartable = 1; 447*f982db4aSGavin Atkinson break; 448*f982db4aSGavin Atkinson 449*f982db4aSGavin Atkinson case SIGALRM: 450*f982db4aSGavin Atkinson case SIGINT: 451*f982db4aSGavin Atkinson case SIGPIPE: 452*f982db4aSGavin Atkinson restartable = 0; 453*f982db4aSGavin Atkinson break; 454*f982db4aSGavin Atkinson 455*f982db4aSGavin Atkinson default: 456*f982db4aSGavin Atkinson /* 457*f982db4aSGavin Atkinson * This is unpleasant, but I don't know what would be better. 458*f982db4aSGavin Atkinson * Right now, this "can't happen" 459*f982db4aSGavin Atkinson */ 460*f982db4aSGavin Atkinson errx(1, "xsignal_restart called with signal %d", sig); 461*f982db4aSGavin Atkinson } 462*f982db4aSGavin Atkinson 463*f982db4aSGavin Atkinson return(xsignal_restart(sig, func, restartable)); 464*f982db4aSGavin Atkinson } 465