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