diff options
author | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 1999-10-27 13:42:43 +1000 |
commit | d4a8b7e34dd619a4debf9a206c81db26d1402ea6 (patch) | |
tree | a47d770a2f790f40d18b0982d4e55fa7cfb1fa3b /scp.c |
Initial revision
Diffstat (limited to 'scp.c')
-rw-r--r-- | scp.c | 1220 |
1 files changed, 1220 insertions, 0 deletions
@@ -0,0 +1,1220 @@ | |||
1 | /* | ||
2 | |||
3 | scp - secure remote copy. This is basically patched BSD rcp which uses ssh | ||
4 | to do the data transfer (instead of using rcmd). | ||
5 | |||
6 | NOTE: This version should NOT be suid root. (This uses ssh to do the transfer | ||
7 | and ssh has the necessary privileges.) | ||
8 | |||
9 | 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> | ||
10 | |||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * Copyright (c) 1983, 1990, 1992, 1993, 1995 | ||
15 | * The Regents of the University of California. All rights reserved. | ||
16 | * | ||
17 | * Redistribution and use in source and binary forms, with or without | ||
18 | * modification, are permitted provided that the following conditions | ||
19 | * are met: | ||
20 | * 1. Redistributions of source code must retain the above copyright | ||
21 | * notice, this list of conditions and the following disclaimer. | ||
22 | * 2. Redistributions in binary form must reproduce the above copyright | ||
23 | * notice, this list of conditions and the following disclaimer in the | ||
24 | * documentation and/or other materials provided with the distribution. | ||
25 | * 3. All advertising materials mentioning features or use of this software | ||
26 | * must display the following acknowledgement: | ||
27 | * This product includes software developed by the University of | ||
28 | * California, Berkeley and its contributors. | ||
29 | * 4. Neither the name of the University nor the names of its contributors | ||
30 | * may be used to endorse or promote products derived from this software | ||
31 | * without specific prior written permission. | ||
32 | * | ||
33 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
34 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
35 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
36 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
37 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
38 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
39 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
40 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
41 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
42 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
43 | * SUCH DAMAGE. | ||
44 | * | ||
45 | * $Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
46 | */ | ||
47 | |||
48 | #include "includes.h" | ||
49 | RCSID("$Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
50 | |||
51 | #include "ssh.h" | ||
52 | #include "xmalloc.h" | ||
53 | #include <utime.h> | ||
54 | |||
55 | #define _PATH_CP "cp" | ||
56 | |||
57 | /* For progressmeter() -- number of seconds before xfer considered "stalled" */ | ||
58 | #define STALLTIME 5 | ||
59 | |||
60 | /* Visual statistics about files as they are transferred. */ | ||
61 | void progressmeter(int); | ||
62 | |||
63 | /* Returns width of the terminal (for progress meter calculations). */ | ||
64 | int getttywidth(void); | ||
65 | |||
66 | /* Time a transfer started. */ | ||
67 | static struct timeval start; | ||
68 | |||
69 | /* Number of bytes of current file transferred so far. */ | ||
70 | volatile unsigned long statbytes; | ||
71 | |||
72 | /* Total size of current file. */ | ||
73 | unsigned long totalbytes = 0; | ||
74 | |||
75 | /* Name of current file being transferred. */ | ||
76 | char *curfile; | ||
77 | |||
78 | /* This is set to non-zero to enable verbose mode. */ | ||
79 | int verbose = 0; | ||
80 | |||
81 | /* This is set to non-zero if compression is desired. */ | ||
82 | int compress = 0; | ||
83 | |||
84 | /* This is set to zero if the progressmeter is not desired. */ | ||
85 | int showprogress = 1; | ||
86 | |||
87 | /* This is set to non-zero if running in batch mode (that is, password | ||
88 | and passphrase queries are not allowed). */ | ||
89 | int batchmode = 0; | ||
90 | |||
91 | /* This is set to the cipher type string if given on the command line. */ | ||
92 | char *cipher = NULL; | ||
93 | |||
94 | /* This is set to the RSA authentication identity file name if given on | ||
95 | the command line. */ | ||
96 | char *identity = NULL; | ||
97 | |||
98 | /* This is the port to use in contacting the remote site (is non-NULL). */ | ||
99 | char *port = NULL; | ||
100 | |||
101 | /* This function executes the given command as the specified user on the given | ||
102 | host. This returns < 0 if execution fails, and >= 0 otherwise. | ||
103 | This assigns the input and output file descriptors on success. */ | ||
104 | |||
105 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | ||
106 | { | ||
107 | int pin[2], pout[2], reserved[2]; | ||
108 | |||
109 | if (verbose) | ||
110 | fprintf(stderr, "Executing: host %s, user %s, command %s\n", | ||
111 | host, remuser ? remuser : "(unspecified)", cmd); | ||
112 | |||
113 | /* Reserve two descriptors so that the real pipes won't get descriptors | ||
114 | 0 and 1 because that will screw up dup2 below. */ | ||
115 | pipe(reserved); | ||
116 | |||
117 | /* Create a socket pair for communicating with ssh. */ | ||
118 | if (pipe(pin) < 0) | ||
119 | fatal("pipe: %s", strerror(errno)); | ||
120 | if (pipe(pout) < 0) | ||
121 | fatal("pipe: %s", strerror(errno)); | ||
122 | |||
123 | /* Free the reserved descriptors. */ | ||
124 | close(reserved[0]); | ||
125 | close(reserved[1]); | ||
126 | |||
127 | /* For a child to execute the command on the remote host using ssh. */ | ||
128 | if (fork() == 0) | ||
129 | { | ||
130 | char *args[100]; | ||
131 | unsigned int i; | ||
132 | |||
133 | /* Child. */ | ||
134 | close(pin[1]); | ||
135 | close(pout[0]); | ||
136 | dup2(pin[0], 0); | ||
137 | dup2(pout[1], 1); | ||
138 | close(pin[0]); | ||
139 | close(pout[1]); | ||
140 | |||
141 | i = 0; | ||
142 | args[i++] = SSH_PROGRAM; | ||
143 | args[i++] = "-x"; | ||
144 | args[i++] = "-oFallBackToRsh no"; | ||
145 | if (verbose) | ||
146 | args[i++] = "-v"; | ||
147 | if (compress) | ||
148 | args[i++] = "-C"; | ||
149 | if (batchmode) | ||
150 | args[i++] = "-oBatchMode yes"; | ||
151 | if (cipher != NULL) | ||
152 | { | ||
153 | args[i++] = "-c"; | ||
154 | args[i++] = cipher; | ||
155 | } | ||
156 | if (identity != NULL) | ||
157 | { | ||
158 | args[i++] = "-i"; | ||
159 | args[i++] = identity; | ||
160 | } | ||
161 | if (port != NULL) | ||
162 | { | ||
163 | args[i++] = "-p"; | ||
164 | args[i++] = port; | ||
165 | } | ||
166 | if (remuser != NULL) | ||
167 | { | ||
168 | args[i++] = "-l"; | ||
169 | args[i++] = remuser; | ||
170 | } | ||
171 | args[i++] = host; | ||
172 | args[i++] = cmd; | ||
173 | args[i++] = NULL; | ||
174 | |||
175 | execvp(SSH_PROGRAM, args); | ||
176 | perror(SSH_PROGRAM); | ||
177 | exit(1); | ||
178 | } | ||
179 | /* Parent. Close the other side, and return the local side. */ | ||
180 | close(pin[0]); | ||
181 | *fdout = pin[1]; | ||
182 | close(pout[1]); | ||
183 | *fdin = pout[0]; | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | void fatal(const char *fmt, ...) | ||
188 | { | ||
189 | va_list ap; | ||
190 | char buf[1024]; | ||
191 | |||
192 | va_start(ap, fmt); | ||
193 | vsnprintf(buf, sizeof(buf), fmt, ap); | ||
194 | va_end(ap); | ||
195 | fprintf(stderr, "%s\n", buf); | ||
196 | exit(255); | ||
197 | } | ||
198 | |||
199 | /* This stuff used to be in BSD rcp extern.h. */ | ||
200 | |||
201 | typedef struct { | ||
202 | int cnt; | ||
203 | char *buf; | ||
204 | } BUF; | ||
205 | |||
206 | extern int iamremote; | ||
207 | |||
208 | BUF *allocbuf(BUF *, int, int); | ||
209 | char *colon(char *); | ||
210 | void lostconn(int); | ||
211 | void nospace(void); | ||
212 | int okname(char *); | ||
213 | void run_err(const char *, ...); | ||
214 | void verifydir(char *); | ||
215 | |||
216 | /* Stuff from BSD rcp.c continues. */ | ||
217 | |||
218 | struct passwd *pwd; | ||
219 | uid_t userid; | ||
220 | int errs, remin, remout; | ||
221 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; | ||
222 | |||
223 | #define CMDNEEDS 64 | ||
224 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | ||
225 | |||
226 | int response(void); | ||
227 | void rsource(char *, struct stat *); | ||
228 | void sink(int, char *[]); | ||
229 | void source(int, char *[]); | ||
230 | void tolocal(int, char *[]); | ||
231 | void toremote(char *, int, char *[]); | ||
232 | void usage(void); | ||
233 | |||
234 | int | ||
235 | main(argc, argv) | ||
236 | int argc; | ||
237 | char *argv[]; | ||
238 | { | ||
239 | int ch, fflag, tflag; | ||
240 | char *targ; | ||
241 | extern char *optarg; | ||
242 | extern int optind; | ||
243 | |||
244 | fflag = tflag = 0; | ||
245 | while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q")) != EOF) | ||
246 | switch(ch) { /* User-visible flags. */ | ||
247 | case 'p': | ||
248 | pflag = 1; | ||
249 | break; | ||
250 | case 'P': | ||
251 | port = optarg; | ||
252 | break; | ||
253 | case 'r': | ||
254 | iamrecursive = 1; | ||
255 | break; | ||
256 | /* Server options. */ | ||
257 | case 'd': | ||
258 | targetshouldbedirectory = 1; | ||
259 | break; | ||
260 | case 'f': /* "from" */ | ||
261 | iamremote = 1; | ||
262 | fflag = 1; | ||
263 | break; | ||
264 | case 't': /* "to" */ | ||
265 | iamremote = 1; | ||
266 | tflag = 1; | ||
267 | break; | ||
268 | case 'c': | ||
269 | cipher = optarg; | ||
270 | break; | ||
271 | case 'i': | ||
272 | identity = optarg; | ||
273 | break; | ||
274 | case 'v': | ||
275 | verbose = 1; | ||
276 | break; | ||
277 | case 'B': | ||
278 | batchmode = 1; | ||
279 | break; | ||
280 | case 'C': | ||
281 | compress = 1; | ||
282 | break; | ||
283 | case 'q': | ||
284 | showprogress = 0; | ||
285 | break; | ||
286 | case '?': | ||
287 | default: | ||
288 | usage(); | ||
289 | } | ||
290 | argc -= optind; | ||
291 | argv += optind; | ||
292 | |||
293 | if ((pwd = getpwuid(userid = getuid())) == NULL) | ||
294 | fatal("unknown user %d", (int)userid); | ||
295 | |||
296 | if (! isatty(STDERR_FILENO)) | ||
297 | showprogress = 0; | ||
298 | |||
299 | remin = STDIN_FILENO; | ||
300 | remout = STDOUT_FILENO; | ||
301 | |||
302 | if (fflag) { /* Follow "protocol", send data. */ | ||
303 | (void)response(); | ||
304 | source(argc, argv); | ||
305 | exit(errs != 0); | ||
306 | } | ||
307 | |||
308 | if (tflag) { /* Receive data. */ | ||
309 | sink(argc, argv); | ||
310 | exit(errs != 0); | ||
311 | } | ||
312 | |||
313 | if (argc < 2) | ||
314 | usage(); | ||
315 | if (argc > 2) | ||
316 | targetshouldbedirectory = 1; | ||
317 | |||
318 | remin = remout = -1; | ||
319 | /* Command to be executed on remote system using "ssh". */ | ||
320 | (void)sprintf(cmd, "scp%s%s%s%s", verbose ? " -v" : "", | ||
321 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | ||
322 | targetshouldbedirectory ? " -d" : ""); | ||
323 | |||
324 | (void)signal(SIGPIPE, lostconn); | ||
325 | |||
326 | if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ | ||
327 | toremote(targ, argc, argv); | ||
328 | else { | ||
329 | tolocal(argc, argv); /* Dest is local host. */ | ||
330 | if (targetshouldbedirectory) | ||
331 | verifydir(argv[argc - 1]); | ||
332 | } | ||
333 | exit(errs != 0); | ||
334 | } | ||
335 | |||
336 | void | ||
337 | toremote(targ, argc, argv) | ||
338 | char *targ, *argv[]; | ||
339 | int argc; | ||
340 | { | ||
341 | int i, len; | ||
342 | char *bp, *host, *src, *suser, *thost, *tuser; | ||
343 | |||
344 | *targ++ = 0; | ||
345 | if (*targ == 0) | ||
346 | targ = "."; | ||
347 | |||
348 | if ((thost = strchr(argv[argc - 1], '@'))) { | ||
349 | /* user@host */ | ||
350 | *thost++ = 0; | ||
351 | tuser = argv[argc - 1]; | ||
352 | if (*tuser == '\0') | ||
353 | tuser = NULL; | ||
354 | else if (!okname(tuser)) | ||
355 | exit(1); | ||
356 | } else { | ||
357 | thost = argv[argc - 1]; | ||
358 | tuser = NULL; | ||
359 | } | ||
360 | |||
361 | for (i = 0; i < argc - 1; i++) { | ||
362 | src = colon(argv[i]); | ||
363 | if (src) { /* remote to remote */ | ||
364 | *src++ = 0; | ||
365 | if (*src == 0) | ||
366 | src = "."; | ||
367 | host = strchr(argv[i], '@'); | ||
368 | len = strlen(SSH_PROGRAM) + strlen(argv[i]) + | ||
369 | strlen(src) + (tuser ? strlen(tuser) : 0) + | ||
370 | strlen(thost) + strlen(targ) + CMDNEEDS + 32; | ||
371 | bp = xmalloc(len); | ||
372 | if (host) { | ||
373 | *host++ = 0; | ||
374 | suser = argv[i]; | ||
375 | if (*suser == '\0') | ||
376 | suser = pwd->pw_name; | ||
377 | else if (!okname(suser)) | ||
378 | continue; | ||
379 | (void)sprintf(bp, | ||
380 | "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", | ||
381 | SSH_PROGRAM, verbose ? " -v" : "", | ||
382 | suser, host, cmd, src, | ||
383 | tuser ? tuser : "", tuser ? "@" : "", | ||
384 | thost, targ); | ||
385 | } else | ||
386 | (void)sprintf(bp, | ||
387 | "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", | ||
388 | SSH_PROGRAM, verbose ? " -v" : "", | ||
389 | argv[i], cmd, src, | ||
390 | tuser ? tuser : "", tuser ? "@" : "", | ||
391 | thost, targ); | ||
392 | if (verbose) | ||
393 | fprintf(stderr, "Executing: %s\n", bp); | ||
394 | (void)system(bp); | ||
395 | (void)xfree(bp); | ||
396 | } else { /* local to remote */ | ||
397 | if (remin == -1) { | ||
398 | len = strlen(targ) + CMDNEEDS + 20; | ||
399 | bp = xmalloc(len); | ||
400 | (void)sprintf(bp, "%s -t %s", cmd, targ); | ||
401 | host = thost; | ||
402 | if (do_cmd(host, tuser, | ||
403 | bp, &remin, &remout) < 0) | ||
404 | exit(1); | ||
405 | if (response() < 0) | ||
406 | exit(1); | ||
407 | (void)xfree(bp); | ||
408 | } | ||
409 | source(1, argv+i); | ||
410 | } | ||
411 | } | ||
412 | } | ||
413 | |||
414 | void | ||
415 | tolocal(argc, argv) | ||
416 | int argc; | ||
417 | char *argv[]; | ||
418 | { | ||
419 | int i, len; | ||
420 | char *bp, *host, *src, *suser; | ||
421 | |||
422 | for (i = 0; i < argc - 1; i++) { | ||
423 | if (!(src = colon(argv[i]))) { /* Local to local. */ | ||
424 | len = strlen(_PATH_CP) + strlen(argv[i]) + | ||
425 | strlen(argv[argc - 1]) + 20; | ||
426 | bp = xmalloc(len); | ||
427 | (void)sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, | ||
428 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | ||
429 | argv[i], argv[argc - 1]); | ||
430 | if (verbose) | ||
431 | fprintf(stderr, "Executing: %s\n", bp); | ||
432 | if (system(bp)) | ||
433 | ++errs; | ||
434 | (void)xfree(bp); | ||
435 | continue; | ||
436 | } | ||
437 | *src++ = 0; | ||
438 | if (*src == 0) | ||
439 | src = "."; | ||
440 | if ((host = strchr(argv[i], '@')) == NULL) { | ||
441 | host = argv[i]; | ||
442 | suser = NULL; | ||
443 | } else { | ||
444 | *host++ = 0; | ||
445 | suser = argv[i]; | ||
446 | if (*suser == '\0') | ||
447 | suser = pwd->pw_name; | ||
448 | else if (!okname(suser)) | ||
449 | continue; | ||
450 | } | ||
451 | len = strlen(src) + CMDNEEDS + 20; | ||
452 | bp = xmalloc(len); | ||
453 | (void)sprintf(bp, "%s -f %s", cmd, src); | ||
454 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) { | ||
455 | (void)xfree(bp); | ||
456 | ++errs; | ||
457 | continue; | ||
458 | } | ||
459 | xfree(bp); | ||
460 | sink(1, argv + argc - 1); | ||
461 | (void)close(remin); | ||
462 | remin = remout = -1; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | void | ||
467 | source(argc, argv) | ||
468 | int argc; | ||
469 | char *argv[]; | ||
470 | { | ||
471 | struct stat stb; | ||
472 | static BUF buffer; | ||
473 | BUF *bp; | ||
474 | off_t i; | ||
475 | int amt, fd, haderr, indx, result; | ||
476 | char *last, *name, buf[2048]; | ||
477 | |||
478 | for (indx = 0; indx < argc; ++indx) { | ||
479 | name = argv[indx]; | ||
480 | statbytes = 0; | ||
481 | if ((fd = open(name, O_RDONLY, 0)) < 0) | ||
482 | goto syserr; | ||
483 | if (fstat(fd, &stb) < 0) { | ||
484 | syserr: run_err("%s: %s", name, strerror(errno)); | ||
485 | goto next; | ||
486 | } | ||
487 | switch (stb.st_mode & S_IFMT) { | ||
488 | case S_IFREG: | ||
489 | break; | ||
490 | case S_IFDIR: | ||
491 | if (iamrecursive) { | ||
492 | rsource(name, &stb); | ||
493 | goto next; | ||
494 | } | ||
495 | /* FALLTHROUGH */ | ||
496 | default: | ||
497 | run_err("%s: not a regular file", name); | ||
498 | goto next; | ||
499 | } | ||
500 | if ((last = strrchr(name, '/')) == NULL) | ||
501 | last = name; | ||
502 | else | ||
503 | ++last; | ||
504 | curfile = last; | ||
505 | if (pflag) { | ||
506 | /* | ||
507 | * Make it compatible with possible future | ||
508 | * versions expecting microseconds. | ||
509 | */ | ||
510 | (void)sprintf(buf, "T%lu 0 %lu 0\n", | ||
511 | (unsigned long)stb.st_mtime, | ||
512 | (unsigned long)stb.st_atime); | ||
513 | (void)write(remout, buf, strlen(buf)); | ||
514 | if (response() < 0) | ||
515 | goto next; | ||
516 | } | ||
517 | #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) | ||
518 | (void)sprintf(buf, "C%04o %lu %s\n", | ||
519 | (unsigned int)(stb.st_mode & FILEMODEMASK), | ||
520 | (unsigned long)stb.st_size, | ||
521 | last); | ||
522 | if (verbose) | ||
523 | { | ||
524 | fprintf(stderr, "Sending file modes: %s", buf); | ||
525 | fflush(stderr); | ||
526 | } | ||
527 | (void)write(remout, buf, strlen(buf)); | ||
528 | if (response() < 0) | ||
529 | goto next; | ||
530 | if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { | ||
531 | next: (void)close(fd); | ||
532 | continue; | ||
533 | } | ||
534 | |||
535 | if (showprogress) { | ||
536 | totalbytes = stb.st_size; | ||
537 | progressmeter(-1); | ||
538 | } | ||
539 | |||
540 | /* Keep writing after an error so that we stay sync'd up. */ | ||
541 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | ||
542 | amt = bp->cnt; | ||
543 | if (i + amt > stb.st_size) | ||
544 | amt = stb.st_size - i; | ||
545 | if (!haderr) { | ||
546 | result = read(fd, bp->buf, amt); | ||
547 | if (result != amt) | ||
548 | haderr = result >= 0 ? EIO : errno; | ||
549 | } | ||
550 | if (haderr) | ||
551 | (void)write(remout, bp->buf, amt); | ||
552 | else { | ||
553 | result = write(remout, bp->buf, amt); | ||
554 | if (result != amt) | ||
555 | haderr = result >= 0 ? EIO : errno; | ||
556 | statbytes += result; | ||
557 | } | ||
558 | } | ||
559 | if(showprogress) | ||
560 | progressmeter(1); | ||
561 | |||
562 | if (close(fd) < 0 && !haderr) | ||
563 | haderr = errno; | ||
564 | if (!haderr) | ||
565 | (void)write(remout, "", 1); | ||
566 | else | ||
567 | run_err("%s: %s", name, strerror(haderr)); | ||
568 | (void)response(); | ||
569 | } | ||
570 | } | ||
571 | |||
572 | void | ||
573 | rsource(name, statp) | ||
574 | char *name; | ||
575 | struct stat *statp; | ||
576 | { | ||
577 | DIR *dirp; | ||
578 | struct dirent *dp; | ||
579 | char *last, *vect[1], path[1100]; | ||
580 | |||
581 | if (!(dirp = opendir(name))) { | ||
582 | run_err("%s: %s", name, strerror(errno)); | ||
583 | return; | ||
584 | } | ||
585 | last = strrchr(name, '/'); | ||
586 | if (last == 0) | ||
587 | last = name; | ||
588 | else | ||
589 | last++; | ||
590 | if (pflag) { | ||
591 | (void)sprintf(path, "T%lu 0 %lu 0\n", | ||
592 | (unsigned long)statp->st_mtime, | ||
593 | (unsigned long)statp->st_atime); | ||
594 | (void)write(remout, path, strlen(path)); | ||
595 | if (response() < 0) { | ||
596 | closedir(dirp); | ||
597 | return; | ||
598 | } | ||
599 | } | ||
600 | (void)sprintf(path, | ||
601 | "D%04o %d %.1024s\n", (unsigned int)(statp->st_mode & FILEMODEMASK), | ||
602 | 0, last); | ||
603 | if (verbose) | ||
604 | fprintf(stderr, "Entering directory: %s", path); | ||
605 | (void)write(remout, path, strlen(path)); | ||
606 | if (response() < 0) { | ||
607 | closedir(dirp); | ||
608 | return; | ||
609 | } | ||
610 | while ((dp = readdir(dirp))) { | ||
611 | if (dp->d_ino == 0) | ||
612 | continue; | ||
613 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | ||
614 | continue; | ||
615 | if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { | ||
616 | run_err("%s/%s: name too long", name, dp->d_name); | ||
617 | continue; | ||
618 | } | ||
619 | (void)sprintf(path, "%s/%s", name, dp->d_name); | ||
620 | vect[0] = path; | ||
621 | source(1, vect); | ||
622 | } | ||
623 | (void)closedir(dirp); | ||
624 | (void)write(remout, "E\n", 2); | ||
625 | (void)response(); | ||
626 | } | ||
627 | |||
628 | void | ||
629 | sink(argc, argv) | ||
630 | int argc; | ||
631 | char *argv[]; | ||
632 | { | ||
633 | static BUF buffer; | ||
634 | struct stat stb; | ||
635 | enum { YES, NO, DISPLAYED } wrerr; | ||
636 | BUF *bp; | ||
637 | off_t i, j; | ||
638 | int amt, count, exists, first, mask, mode, ofd, omode; | ||
639 | int setimes, size, targisdir, wrerrno = 0; | ||
640 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; | ||
641 | struct utimbuf ut; | ||
642 | int dummy_usec; | ||
643 | |||
644 | #define SCREWUP(str) { why = str; goto screwup; } | ||
645 | |||
646 | setimes = targisdir = 0; | ||
647 | mask = umask(0); | ||
648 | if (!pflag) | ||
649 | (void)umask(mask); | ||
650 | if (argc != 1) { | ||
651 | run_err("ambiguous target"); | ||
652 | exit(1); | ||
653 | } | ||
654 | targ = *argv; | ||
655 | if (targetshouldbedirectory) | ||
656 | verifydir(targ); | ||
657 | |||
658 | (void)write(remout, "", 1); | ||
659 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | ||
660 | targisdir = 1; | ||
661 | for (first = 1;; first = 0) { | ||
662 | cp = buf; | ||
663 | if (read(remin, cp, 1) <= 0) | ||
664 | return; | ||
665 | if (*cp++ == '\n') | ||
666 | SCREWUP("unexpected <newline>"); | ||
667 | do { | ||
668 | if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) | ||
669 | SCREWUP("lost connection"); | ||
670 | *cp++ = ch; | ||
671 | } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); | ||
672 | *cp = 0; | ||
673 | |||
674 | if (buf[0] == '\01' || buf[0] == '\02') { | ||
675 | if (iamremote == 0) | ||
676 | (void)write(STDERR_FILENO, | ||
677 | buf + 1, strlen(buf + 1)); | ||
678 | if (buf[0] == '\02') | ||
679 | exit(1); | ||
680 | ++errs; | ||
681 | continue; | ||
682 | } | ||
683 | if (buf[0] == 'E') { | ||
684 | (void)write(remout, "", 1); | ||
685 | return; | ||
686 | } | ||
687 | |||
688 | if (ch == '\n') | ||
689 | *--cp = 0; | ||
690 | |||
691 | #define getnum(t) (t) = 0; \ | ||
692 | while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); | ||
693 | cp = buf; | ||
694 | if (*cp == 'T') { | ||
695 | setimes++; | ||
696 | cp++; | ||
697 | getnum(ut.modtime); | ||
698 | if (*cp++ != ' ') | ||
699 | SCREWUP("mtime.sec not delimited"); | ||
700 | getnum(dummy_usec); | ||
701 | if (*cp++ != ' ') | ||
702 | SCREWUP("mtime.usec not delimited"); | ||
703 | getnum(ut.actime); | ||
704 | if (*cp++ != ' ') | ||
705 | SCREWUP("atime.sec not delimited"); | ||
706 | getnum(dummy_usec); | ||
707 | if (*cp++ != '\0') | ||
708 | SCREWUP("atime.usec not delimited"); | ||
709 | (void)write(remout, "", 1); | ||
710 | continue; | ||
711 | } | ||
712 | if (*cp != 'C' && *cp != 'D') { | ||
713 | /* | ||
714 | * Check for the case "rcp remote:foo\* local:bar". | ||
715 | * In this case, the line "No match." can be returned | ||
716 | * by the shell before the rcp command on the remote is | ||
717 | * executed so the ^Aerror_message convention isn't | ||
718 | * followed. | ||
719 | */ | ||
720 | if (first) { | ||
721 | run_err("%s", cp); | ||
722 | exit(1); | ||
723 | } | ||
724 | SCREWUP("expected control record"); | ||
725 | } | ||
726 | mode = 0; | ||
727 | for (++cp; cp < buf + 5; cp++) { | ||
728 | if (*cp < '0' || *cp > '7') | ||
729 | SCREWUP("bad mode"); | ||
730 | mode = (mode << 3) | (*cp - '0'); | ||
731 | } | ||
732 | if (*cp++ != ' ') | ||
733 | SCREWUP("mode not delimited"); | ||
734 | |||
735 | for (size = 0; *cp >= '0' && *cp <= '9';) | ||
736 | size = size * 10 + (*cp++ - '0'); | ||
737 | if (*cp++ != ' ') | ||
738 | SCREWUP("size not delimited"); | ||
739 | if (targisdir) { | ||
740 | static char *namebuf; | ||
741 | static int cursize; | ||
742 | size_t need; | ||
743 | |||
744 | need = strlen(targ) + strlen(cp) + 250; | ||
745 | if (need > cursize) | ||
746 | namebuf = xmalloc(need); | ||
747 | (void)sprintf(namebuf, "%s%s%s", targ, | ||
748 | *targ ? "/" : "", cp); | ||
749 | np = namebuf; | ||
750 | } else | ||
751 | np = targ; | ||
752 | curfile = cp; | ||
753 | exists = stat(np, &stb) == 0; | ||
754 | if (buf[0] == 'D') { | ||
755 | int mod_flag = pflag; | ||
756 | if (exists) { | ||
757 | if (!S_ISDIR(stb.st_mode)) { | ||
758 | errno = ENOTDIR; | ||
759 | goto bad; | ||
760 | } | ||
761 | if (pflag) | ||
762 | (void)chmod(np, mode); | ||
763 | } else { | ||
764 | /* Handle copying from a read-only directory */ | ||
765 | mod_flag = 1; | ||
766 | if (mkdir(np, mode | S_IRWXU) < 0) | ||
767 | goto bad; | ||
768 | } | ||
769 | vect[0] = np; | ||
770 | sink(1, vect); | ||
771 | if (setimes) { | ||
772 | setimes = 0; | ||
773 | if (utime(np, &ut) < 0) | ||
774 | run_err("%s: set times: %s", | ||
775 | np, strerror(errno)); | ||
776 | } | ||
777 | if (mod_flag) | ||
778 | (void)chmod(np, mode); | ||
779 | continue; | ||
780 | } | ||
781 | omode = mode; | ||
782 | mode |= S_IWRITE; | ||
783 | if ((ofd = open(np, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) { | ||
784 | bad: run_err("%s: %s", np, strerror(errno)); | ||
785 | continue; | ||
786 | } | ||
787 | (void)write(remout, "", 1); | ||
788 | if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { | ||
789 | (void)close(ofd); | ||
790 | continue; | ||
791 | } | ||
792 | cp = bp->buf; | ||
793 | wrerr = NO; | ||
794 | |||
795 | if (showprogress) { | ||
796 | totalbytes = size; | ||
797 | progressmeter(-1); | ||
798 | } | ||
799 | statbytes = 0; | ||
800 | for (count = i = 0; i < size; i += 4096) { | ||
801 | amt = 4096; | ||
802 | if (i + amt > size) | ||
803 | amt = size - i; | ||
804 | count += amt; | ||
805 | do { | ||
806 | j = read(remin, cp, amt); | ||
807 | if (j <= 0) { | ||
808 | run_err("%s", j ? strerror(errno) : | ||
809 | "dropped connection"); | ||
810 | exit(1); | ||
811 | } | ||
812 | amt -= j; | ||
813 | cp += j; | ||
814 | statbytes += j; | ||
815 | } while (amt > 0); | ||
816 | if (count == bp->cnt) { | ||
817 | /* Keep reading so we stay sync'd up. */ | ||
818 | if (wrerr == NO) { | ||
819 | j = write(ofd, bp->buf, count); | ||
820 | if (j != count) { | ||
821 | wrerr = YES; | ||
822 | wrerrno = j >= 0 ? EIO : errno; | ||
823 | } | ||
824 | } | ||
825 | count = 0; | ||
826 | cp = bp->buf; | ||
827 | } | ||
828 | } | ||
829 | if (showprogress) | ||
830 | progressmeter(1); | ||
831 | if (count != 0 && wrerr == NO && | ||
832 | (j = write(ofd, bp->buf, count)) != count) { | ||
833 | wrerr = YES; | ||
834 | wrerrno = j >= 0 ? EIO : errno; | ||
835 | } | ||
836 | #if 0 | ||
837 | if (ftruncate(ofd, size)) { | ||
838 | run_err("%s: truncate: %s", np, strerror(errno)); | ||
839 | wrerr = DISPLAYED; | ||
840 | } | ||
841 | #endif | ||
842 | if (pflag) { | ||
843 | if (exists || omode != mode) | ||
844 | if (fchmod(ofd, omode)) | ||
845 | run_err("%s: set mode: %s", | ||
846 | np, strerror(errno)); | ||
847 | } else { | ||
848 | if (!exists && omode != mode) | ||
849 | if (fchmod(ofd, omode & ~mask)) | ||
850 | run_err("%s: set mode: %s", | ||
851 | np, strerror(errno)); | ||
852 | } | ||
853 | (void)close(ofd); | ||
854 | (void)response(); | ||
855 | if (setimes && wrerr == NO) { | ||
856 | setimes = 0; | ||
857 | if (utime(np, &ut) < 0) { | ||
858 | run_err("%s: set times: %s", | ||
859 | np, strerror(errno)); | ||
860 | wrerr = DISPLAYED; | ||
861 | } | ||
862 | } | ||
863 | switch(wrerr) { | ||
864 | case YES: | ||
865 | run_err("%s: %s", np, strerror(wrerrno)); | ||
866 | break; | ||
867 | case NO: | ||
868 | (void)write(remout, "", 1); | ||
869 | break; | ||
870 | case DISPLAYED: | ||
871 | break; | ||
872 | } | ||
873 | } | ||
874 | screwup: | ||
875 | run_err("protocol error: %s", why); | ||
876 | exit(1); | ||
877 | } | ||
878 | |||
879 | int | ||
880 | response() | ||
881 | { | ||
882 | char ch, *cp, resp, rbuf[2048]; | ||
883 | |||
884 | if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) | ||
885 | lostconn(0); | ||
886 | |||
887 | cp = rbuf; | ||
888 | switch(resp) { | ||
889 | case 0: /* ok */ | ||
890 | return (0); | ||
891 | default: | ||
892 | *cp++ = resp; | ||
893 | /* FALLTHROUGH */ | ||
894 | case 1: /* error, followed by error msg */ | ||
895 | case 2: /* fatal error, "" */ | ||
896 | do { | ||
897 | if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) | ||
898 | lostconn(0); | ||
899 | *cp++ = ch; | ||
900 | } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); | ||
901 | |||
902 | if (!iamremote) | ||
903 | (void)write(STDERR_FILENO, rbuf, cp - rbuf); | ||
904 | ++errs; | ||
905 | if (resp == 1) | ||
906 | return (-1); | ||
907 | exit(1); | ||
908 | } | ||
909 | /* NOTREACHED */ | ||
910 | } | ||
911 | |||
912 | void | ||
913 | usage() | ||
914 | { | ||
915 | (void)fprintf(stderr, | ||
916 | "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); | ||
917 | exit(1); | ||
918 | } | ||
919 | |||
920 | void | ||
921 | run_err(const char *fmt, ...) | ||
922 | { | ||
923 | static FILE *fp; | ||
924 | va_list ap; | ||
925 | va_start(ap, fmt); | ||
926 | |||
927 | ++errs; | ||
928 | if (fp == NULL && !(fp = fdopen(remout, "w"))) | ||
929 | return; | ||
930 | (void)fprintf(fp, "%c", 0x01); | ||
931 | (void)fprintf(fp, "scp: "); | ||
932 | (void)vfprintf(fp, fmt, ap); | ||
933 | (void)fprintf(fp, "\n"); | ||
934 | (void)fflush(fp); | ||
935 | |||
936 | if (!iamremote) | ||
937 | { | ||
938 | vfprintf(stderr, fmt, ap); | ||
939 | fprintf(stderr, "\n"); | ||
940 | } | ||
941 | |||
942 | va_end(ap); | ||
943 | } | ||
944 | |||
945 | /* Stuff below is from BSD rcp util.c. */ | ||
946 | |||
947 | /*- | ||
948 | * Copyright (c) 1992, 1993 | ||
949 | * The Regents of the University of California. All rights reserved. | ||
950 | * | ||
951 | * Redistribution and use in source and binary forms, with or without | ||
952 | * modification, are permitted provided that the following conditions | ||
953 | * are met: | ||
954 | * 1. Redistributions of source code must retain the above copyright | ||
955 | * notice, this list of conditions and the following disclaimer. | ||
956 | * 2. Redistributions in binary form must reproduce the above copyright | ||
957 | * notice, this list of conditions and the following disclaimer in the | ||
958 | * documentation and/or other materials provided with the distribution. | ||
959 | * 3. All advertising materials mentioning features or use of this software | ||
960 | * must display the following acknowledgement: | ||
961 | * This product includes software developed by the University of | ||
962 | * California, Berkeley and its contributors. | ||
963 | * 4. Neither the name of the University nor the names of its contributors | ||
964 | * may be used to endorse or promote products derived from this software | ||
965 | * without specific prior written permission. | ||
966 | * | ||
967 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
968 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
969 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
970 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
971 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
972 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
973 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
974 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
975 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
976 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
977 | * SUCH DAMAGE. | ||
978 | * | ||
979 | * $Id: scp.c,v 1.1 1999/10/27 03:42:45 damien Exp $ | ||
980 | */ | ||
981 | |||
982 | char * | ||
983 | colon(cp) | ||
984 | char *cp; | ||
985 | { | ||
986 | if (*cp == ':') /* Leading colon is part of file name. */ | ||
987 | return (0); | ||
988 | |||
989 | for (; *cp; ++cp) { | ||
990 | if (*cp == ':') | ||
991 | return (cp); | ||
992 | if (*cp == '/') | ||
993 | return (0); | ||
994 | } | ||
995 | return (0); | ||
996 | } | ||
997 | |||
998 | void | ||
999 | verifydir(cp) | ||
1000 | char *cp; | ||
1001 | { | ||
1002 | struct stat stb; | ||
1003 | |||
1004 | if (!stat(cp, &stb)) { | ||
1005 | if (S_ISDIR(stb.st_mode)) | ||
1006 | return; | ||
1007 | errno = ENOTDIR; | ||
1008 | } | ||
1009 | run_err("%s: %s", cp, strerror(errno)); | ||
1010 | exit(1); | ||
1011 | } | ||
1012 | |||
1013 | int | ||
1014 | okname(cp0) | ||
1015 | char *cp0; | ||
1016 | { | ||
1017 | int c; | ||
1018 | char *cp; | ||
1019 | |||
1020 | cp = cp0; | ||
1021 | do { | ||
1022 | c = *cp; | ||
1023 | if (c & 0200) | ||
1024 | goto bad; | ||
1025 | if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') | ||
1026 | goto bad; | ||
1027 | } while (*++cp); | ||
1028 | return (1); | ||
1029 | |||
1030 | bad: fprintf(stderr, "%s: invalid user name", cp0); | ||
1031 | return (0); | ||
1032 | } | ||
1033 | |||
1034 | BUF * | ||
1035 | allocbuf(bp, fd, blksize) | ||
1036 | BUF *bp; | ||
1037 | int fd, blksize; | ||
1038 | { | ||
1039 | size_t size; | ||
1040 | struct stat stb; | ||
1041 | |||
1042 | if (fstat(fd, &stb) < 0) { | ||
1043 | run_err("fstat: %s", strerror(errno)); | ||
1044 | return (0); | ||
1045 | } | ||
1046 | if (stb.st_blksize == 0) | ||
1047 | size = blksize; | ||
1048 | else | ||
1049 | size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % | ||
1050 | stb.st_blksize; | ||
1051 | if (bp->cnt >= size) | ||
1052 | return (bp); | ||
1053 | if (bp->buf == NULL) | ||
1054 | bp->buf = xmalloc(size); | ||
1055 | else | ||
1056 | bp->buf = xrealloc(bp->buf, size); | ||
1057 | bp->cnt = size; | ||
1058 | return (bp); | ||
1059 | } | ||
1060 | |||
1061 | void | ||
1062 | lostconn(signo) | ||
1063 | int signo; | ||
1064 | { | ||
1065 | if (!iamremote) | ||
1066 | fprintf(stderr, "lost connection\n"); | ||
1067 | exit(1); | ||
1068 | } | ||
1069 | |||
1070 | /* | ||
1071 | * ensure all of data on socket comes through. f==read || f==write | ||
1072 | */ | ||
1073 | int | ||
1074 | atomicio(f, fd, s, n) | ||
1075 | int (*f)(); | ||
1076 | char *s; | ||
1077 | { | ||
1078 | int res, pos = 0; | ||
1079 | |||
1080 | while (n>pos) { | ||
1081 | res = (f)(fd, s+pos, n-pos); | ||
1082 | switch (res) { | ||
1083 | case -1: | ||
1084 | if (errno==EINTR || errno==EAGAIN) | ||
1085 | continue; | ||
1086 | case 0: | ||
1087 | return (res); | ||
1088 | default: | ||
1089 | pos += res; | ||
1090 | } | ||
1091 | } | ||
1092 | return (pos); | ||
1093 | } | ||
1094 | |||
1095 | void | ||
1096 | alarmtimer(int wait) | ||
1097 | { | ||
1098 | struct itimerval itv; | ||
1099 | |||
1100 | itv.it_value.tv_sec = wait; | ||
1101 | itv.it_value.tv_usec = 0; | ||
1102 | itv.it_interval = itv.it_value; | ||
1103 | setitimer(ITIMER_REAL, &itv, NULL); | ||
1104 | } | ||
1105 | |||
1106 | void | ||
1107 | updateprogressmeter(void) | ||
1108 | { | ||
1109 | int save_errno = errno; | ||
1110 | |||
1111 | progressmeter(0); | ||
1112 | errno = save_errno; | ||
1113 | } | ||
1114 | |||
1115 | void | ||
1116 | progressmeter(int flag) | ||
1117 | { | ||
1118 | static const char prefixes[] = " KMGTP"; | ||
1119 | static struct timeval lastupdate; | ||
1120 | static off_t lastsize; | ||
1121 | struct timeval now, td, wait; | ||
1122 | off_t cursize, abbrevsize; | ||
1123 | double elapsed; | ||
1124 | int ratio, barlength, i, remaining; | ||
1125 | char buf[256]; | ||
1126 | |||
1127 | if (flag == -1) { | ||
1128 | (void)gettimeofday(&start, (struct timezone *)0); | ||
1129 | lastupdate = start; | ||
1130 | lastsize = 0; | ||
1131 | } | ||
1132 | (void)gettimeofday(&now, (struct timezone *)0); | ||
1133 | cursize = statbytes; | ||
1134 | if (totalbytes != 0) { | ||
1135 | ratio = cursize * 100 / totalbytes; | ||
1136 | ratio = MAX(ratio, 0); | ||
1137 | ratio = MIN(ratio, 100); | ||
1138 | } | ||
1139 | else | ||
1140 | ratio = 100; | ||
1141 | |||
1142 | snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); | ||
1143 | |||
1144 | barlength = getttywidth() - 51; | ||
1145 | if (barlength > 0) { | ||
1146 | i = barlength * ratio / 100; | ||
1147 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1148 | "|%.*s%*s|", i, | ||
1149 | "*****************************************************************************" | ||
1150 | "*****************************************************************************", | ||
1151 | barlength - i, ""); | ||
1152 | } | ||
1153 | |||
1154 | i = 0; | ||
1155 | abbrevsize = cursize; | ||
1156 | while (abbrevsize >= 100000 && i < sizeof(prefixes)) { | ||
1157 | i++; | ||
1158 | abbrevsize >>= 10; | ||
1159 | } | ||
1160 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", | ||
1161 | (quad_t)abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : | ||
1162 | 'B'); | ||
1163 | |||
1164 | timersub(&now, &lastupdate, &wait); | ||
1165 | if (cursize > lastsize) { | ||
1166 | lastupdate = now; | ||
1167 | lastsize = cursize; | ||
1168 | if (wait.tv_sec >= STALLTIME) { | ||
1169 | start.tv_sec += wait.tv_sec; | ||
1170 | start.tv_usec += wait.tv_usec; | ||
1171 | } | ||
1172 | wait.tv_sec = 0; | ||
1173 | } | ||
1174 | |||
1175 | timersub(&now, &start, &td); | ||
1176 | elapsed = td.tv_sec + (td.tv_usec / 1000000.0); | ||
1177 | |||
1178 | if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { | ||
1179 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1180 | " --:-- ETA"); | ||
1181 | } else if (wait.tv_sec >= STALLTIME) { | ||
1182 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1183 | " - stalled -"); | ||
1184 | } else { | ||
1185 | remaining = (int)(totalbytes / (statbytes / elapsed) - elapsed); | ||
1186 | i = elapsed / 3600; | ||
1187 | if (i) | ||
1188 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1189 | "%2d:", i); | ||
1190 | else | ||
1191 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1192 | " "); | ||
1193 | i = remaining % 3600; | ||
1194 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | ||
1195 | "%02d:%02d ETA", i / 60, i % 60); | ||
1196 | } | ||
1197 | atomicio(write, fileno(stdout), buf, strlen(buf)); | ||
1198 | |||
1199 | if (flag == -1) { | ||
1200 | signal(SIGALRM, (void *)updateprogressmeter); | ||
1201 | alarmtimer(1); | ||
1202 | } else if (flag == 1) { | ||
1203 | alarmtimer(0); | ||
1204 | write(fileno(stdout), "\n", 1); | ||
1205 | statbytes = 0; | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | int | ||
1210 | getttywidth(void) | ||
1211 | { | ||
1212 | struct winsize winsize; | ||
1213 | |||
1214 | if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) | ||
1215 | return(winsize.ws_col ? winsize.ws_col : 80); | ||
1216 | else | ||
1217 | return(80); | ||
1218 | } | ||
1219 | |||
1220 | |||