-
Notifications
You must be signed in to change notification settings - Fork 1
/
idle.c
302 lines (231 loc) · 6.82 KB
/
idle.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/* idle.c
* 13 June 2005
* Scott Bronson
*
* Prints the in-progress display while a zmodem transfer is happening.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include "log.h"
#include "fifo.h"
#include "io/io.h"
#include "pipe.h"
#include "task.h"
#include "idle.h"
#include "util.h"
typedef struct {
// this data structure contains the stats for the transfer
// in human-readable units.
char rnum[64]; // number of bytes received
char snum[64]; // number of bytes sent
char rbps[64]; // receive rate
char sbps[64]; // send rate
char xfertime[64]; // elapsed time of transfer
} idle_numbers;
#ifdef __APPLE__
// clock_gettime is not implemented on OSX
#include <mach/clock.h>
#include <mach/mach_init.h>
#include <mach/mach_host.h>
#include <mach/mach_port.h>
typedef int clockid_t;
#define CLOCK_MONOTONIC 0
int clock_gettime(clockid_t clock_id, struct timespec* ts)
{
clock_serv_t cclock;
mach_timespec_t now;
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &now);
mach_port_deallocate(mach_task_self(), cclock);
ts->tv_sec = now.tv_sec;
ts->tv_nsec = now.tv_nsec;
return 0;
}
#endif
static void human_bytes(size_t size, char *buf, int bufsiz)
{
static const char *suffixes[] = { "B", "kB", "MB", "GB", 0 };
enum { step = 1024 };
const char **suffix = &suffixes[0];
size_t base = 1;
size_t num;
int rem;
if(size > 0) {
while(*suffix) {
if(size >= base && size < base*step) {
num = size / base;
rem = (size * 100 / base) % 100;
if(base == 1) {
snprintf(buf, bufsiz, "%ld %s", (long)num, *suffix);
} else {
snprintf(buf, bufsiz, "%ld.%02d %s", (long)num, rem, *suffix);
}
return;
}
base *= 1024;
suffix += 1;
}
}
snprintf(buf, bufsiz, "%ld B", (long)size);
}
static int human_time(double dsecs, char *buf, int bufsiz)
{
char *cp = buf;
int cnt;
int seconds = (int)dsecs;
int minutes = seconds / 60;
int hours = seconds / 3600;
if(hours > 0) {
cnt = snprintf(cp, bufsiz, "%d:", hours);
cp += cnt; bufsiz -= cnt;
}
cnt = snprintf(buf, bufsiz, "%d:%02d.%d", minutes % 60,
seconds % 60, (int)(10*dsecs)%10);
cp += cnt; bufsiz -= cnt;
return cp - buf;
}
/** Returns the difference between the two timepsecs in seconds. */
static double timespec_diff(struct timespec *end, struct timespec *start)
{
double ds, de;
ds = start->tv_sec + ((double)start->tv_nsec)/1000000000;
de = end->tv_sec + ((double)end->tv_nsec)/1000000000;
return de - ds;
}
idle_state* idle_create(master_pipe *mp, const char *command)
{
idle_state *idle = malloc(sizeof(idle_state));
if(idle == NULL) {
fprintf(stderr, "Could not allocate the idle structure.\n");
bail(51);
}
memset(idle, 0, sizeof(idle_state));
idle->command = command;
idle->send_start_count = mp->input_master.bytes_written;
idle->recv_start_count = mp->master_output.bytes_written;
idle->call_cnt = 0;
clock_gettime(CLOCK_MONOTONIC, &idle->start_time);
return idle;
}
void idle_destroy(idle_state* idle)
{
free(idle);
}
static void idle_get_numbers(task_spec *spec, idle_numbers *out)
{
struct timespec end_time;
double xfertime;
idle_state *idle = (idle_state*)spec->idle_refcon;
clock_gettime(CLOCK_MONOTONIC, &end_time);
xfertime = timespec_diff(&end_time, &idle->start_time);
if(xfertime <= 0.0) {
// assume a nanosecond if no time elapsed.
// this ensures we never divide by 0.
xfertime = 0.000000001;
}
int sendcnt = spec->master->input_master.bytes_written - idle->send_start_count;
human_bytes(sendcnt, out->snum, sizeof(out->snum));
human_bytes((size_t)((double)sendcnt/xfertime), out->sbps, sizeof(out->sbps));
int recvcnt = spec->master->master_output.bytes_written - idle->recv_start_count;
human_bytes(recvcnt, out->rnum, sizeof(out->rnum));
human_bytes((size_t)((double)recvcnt/xfertime), out->rbps, sizeof(out->rbps));
human_time(xfertime, out->xfertime, sizeof(out->xfertime));
}
/** Pads the string out to the given number of characters with spaces
* @param buf The string to pad
* @param width How long the string should be, including blanks.
*/
static void pad_with_blanks(char *buf, int width)
{
int i;
for(i=strlen(buf); i<width; i++) {
buf[i] = ' ';
}
}
/** Prints a continually updated status string during the transfer.
*/
int idle_proc(task_spec *spec)
{
enum {
sleeptime = 300, // time between invocations in ms.
fuzztime = 50, // if there are less than fuzztime ms until
// the idle proc comes due, then we just run the idle proc
// now. It's early, but the user won't know the difference.
// More efficient to do it this way rather than requiring our
// own timeout at precisely the right time.
// In short:
// minimum time to next idleproc: sleeptime - fuzztime.
// maximum time to next idleproc: sleeptime.
};
char buf[256];
int len;
idle_numbers numbers, *n = &numbers;
idle_state *idle = (idle_state*)spec->idle_refcon;
struct timespec now_time;
double diff;
int bah;
if(opt_quiet) {
return sleeptime;
}
// We will get called much more often than our sleeptime when i/o
// is heavy. Ensure that we don't update the display too fast.
// The first time we're called, tv_sec will be 0. After that,
// we just need to ensure that we wait for the appropriate amount
// of time before updating the display again.
if(idle->last_time.tv_sec != 0) {
clock_gettime(CLOCK_MONOTONIC, &now_time);
diff = timespec_diff(&now_time, &idle->last_time);
bah = sleeptime - (int)(diff*1000);
if(bah > fuzztime) {
return bah;
}
}
log_dbg("updating display");
idle->call_cnt += 1;
idle_get_numbers(spec, &numbers);
snprintf(buf, sizeof(buf),
"%s %s: received %s at %s/s, sent %s at %s/s",
n->xfertime, idle->command, n->rnum, n->rbps, n->snum, n->sbps);
len = get_window_width();
if(len > sizeof(buf) - 1) {
len = sizeof(buf) - 1;
}
pad_with_blanks(buf, len);
buf[len-1] = '\r';
// ok, this list of pointers is a little silly.
write(spec->master->task_head->next->spec->outfd, buf, len);
clock_gettime(CLOCK_MONOTONIC, &idle->last_time);
return sleeptime;
}
/** Called at the end of the transfer to print a final status string.
* It also frees the idle state.
*/
void idle_end(task_spec *spec)
{
int len;
// We know that the new task is established before the
// old task's destructor is called.
char buf[256];
idle_numbers numbers, *n = &numbers;
idle_state *idle = (idle_state*)spec->idle_refcon;
if(opt_quiet) {
return;
}
idle_get_numbers(spec, &numbers);
snprintf(buf, sizeof(buf),
"Received %s at %s/s Sent %s at %s/s.",
n->rnum, n->rbps, n->snum, n->sbps);
len = get_window_width();
if(len > sizeof(buf) - 1) {
len = sizeof(buf) - 1;
}
pad_with_blanks(buf, len);
buf[len-2] = '\r';
buf[len-1] = '\n';
pipe_write(&spec->master->master_output, buf, len);
idle_destroy(idle);
}