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
foregroundproc(void)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
updateprogressmeter(int dummy)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
progressmeter(int flag)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