1 /* $NetBSD: util.c,v 1.40 1999/01/05 22:54:49 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * Copyright (c) 1985, 1989, 1993, 1994 42 * The Regents of the University of California. All rights reserved. 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * 3. All advertising materials mentioning features or use of this software 53 * must display the following acknowledgement: 54 * This product includes software developed by the University of 55 * California, Berkeley and its contributors. 56 * 4. Neither the name of the University nor the names of its contributors 57 * may be used to endorse or promote products derived from this software 58 * without specific prior written permission. 59 * 60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70 * SUCH DAMAGE. 71 */ 72 73 #include <sys/cdefs.h> 74 #ifndef lint 75 __RCSID("$NetBSD: util.c,v 1.40 1999/01/05 22:54:49 lukem Exp $"); 76 #endif /* not lint */ 77 78 /* 79 * FTP User Program -- Misc support routines 80 */ 81 #include <sys/types.h> 82 #include <sys/socket.h> 83 #include <sys/ioctl.h> 84 #include <sys/time.h> 85 #include <arpa/ftp.h> 86 87 #include <ctype.h> 88 #include <err.h> 89 #include <fcntl.h> 90 #include <glob.h> 91 #include <termios.h> 92 #include <signal.h> 93 #include <limits.h> 94 #include <pwd.h> 95 #include <stdio.h> 96 #include <stdlib.h> 97 #include <string.h> 98 #include <time.h> 99 #include <tzfile.h> 100 #include <unistd.h> 101 102 #include "ftp_var.h" 103 #include "pathnames.h" 104 105 #ifndef HAVE_TIMEGM 106 static time_t sub_mkgmt __P((struct tm *tm)); 107 #endif 108 109 /* 110 * Connect to peer server and 111 * auto-login, if possible. 112 */ 113 void 114 setpeer(argc, argv) 115 int argc; 116 char *argv[]; 117 { 118 char *host; 119 in_port_t port; 120 121 if (connected) { 122 fprintf(ttyout, "Already connected to %s, use close first.\n", 123 hostname); 124 code = -1; 125 return; 126 } 127 if (argc < 2) 128 (void)another(&argc, &argv, "to"); 129 if (argc < 2 || argc > 3) { 130 fprintf(ttyout, "usage: %s host-name [port]\n", argv[0]); 131 code = -1; 132 return; 133 } 134 if (gatemode) 135 port = gateport; 136 else 137 port = ftpport; 138 if (argc > 2) { 139 char *ep; 140 long nport; 141 142 nport = strtol(argv[2], &ep, 10); 143 if (nport < 1 || nport > MAX_IN_PORT_T || *ep != '\0') { 144 fprintf(ttyout, "%s: bad port number '%s'.\n", 145 argv[1], argv[2]); 146 fprintf(ttyout, "usage: %s host-name [port]\n", 147 argv[0]); 148 code = -1; 149 return; 150 } 151 port = htons((in_port_t)nport); 152 } 153 154 if (gatemode) { 155 if (gateserver == NULL || *gateserver == '\0') 156 errx(1, "gateserver not defined (shouldn't happen)"); 157 host = hookup(gateserver, port); 158 } else 159 host = hookup(argv[1], port); 160 161 if (host) { 162 int overbose; 163 164 if (gatemode) { 165 if (command("PASSERVE %s", argv[1]) != COMPLETE) 166 return; 167 if (verbose) 168 fprintf(ttyout, 169 "Connected via pass-through server %s\n", 170 gateserver); 171 } 172 173 connected = 1; 174 /* 175 * Set up defaults for FTP. 176 */ 177 (void)strcpy(typename, "ascii"), type = TYPE_A; 178 curtype = TYPE_A; 179 (void)strcpy(formname, "non-print"), form = FORM_N; 180 (void)strcpy(modename, "stream"), mode = MODE_S; 181 (void)strcpy(structname, "file"), stru = STRU_F; 182 (void)strcpy(bytename, "8"), bytesize = 8; 183 if (autologin) 184 (void)ftp_login(argv[1], NULL, NULL); 185 186 overbose = verbose; 187 if (debug == 0) 188 verbose = -1; 189 if (command("SYST") == COMPLETE && overbose) { 190 char *cp, c; 191 c = 0; 192 cp = strchr(reply_string + 4, ' '); 193 if (cp == NULL) 194 cp = strchr(reply_string + 4, '\r'); 195 if (cp) { 196 if (cp[-1] == '.') 197 cp--; 198 c = *cp; 199 *cp = '\0'; 200 } 201 202 fprintf(ttyout, "Remote system type is %s.\n", 203 reply_string + 4); 204 if (cp) 205 *cp = c; 206 } 207 if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { 208 if (proxy) 209 unix_proxy = 1; 210 else 211 unix_server = 1; 212 /* 213 * Set type to 0 (not specified by user), 214 * meaning binary by default, but don't bother 215 * telling server. We can use binary 216 * for text files unless changed by the user. 217 */ 218 type = 0; 219 (void)strcpy(typename, "binary"); 220 if (overbose) 221 fprintf(ttyout, 222 "Using %s mode to transfer files.\n", 223 typename); 224 } else { 225 if (proxy) 226 unix_proxy = 0; 227 else 228 unix_server = 0; 229 if (overbose && 230 !strncmp(reply_string, "215 TOPS20", 10)) 231 fputs( 232 "Remember to set tenex mode when transferring binary files from this machine.\n", 233 ttyout); 234 } 235 verbose = overbose; 236 } 237 } 238 239 /* 240 * login to remote host, using given username & password if supplied 241 */ 242 int 243 ftp_login(host, user, pass) 244 const char *host; 245 const char *user, *pass; 246 { 247 char tmp[80]; 248 const char *acct; 249 char anonpass[MAXLOGNAME + 2]; /* "user@" */ 250 struct passwd *pw; 251 int n, aflag = 0; 252 253 acct = NULL; 254 if (user == NULL) { 255 if (ruserpass(host, &user, &pass, &acct) < 0) { 256 code = -1; 257 return (0); 258 } 259 } 260 261 /* 262 * Set up arguments for an anonymous FTP session, if necessary. 263 */ 264 if ((user == NULL || pass == NULL) && anonftp) { 265 memset(anonpass, 0, sizeof(anonpass)); 266 267 /* 268 * Set up anonymous login password. 269 */ 270 if ((pass = getenv("FTPANONPASS")) == NULL) { 271 if ((pass = getlogin()) == NULL) { 272 if ((pw = getpwuid(getuid())) == NULL) 273 pass = "anonymous"; 274 else 275 pass = pw->pw_name; 276 } 277 /* 278 * Every anonymous FTP server I've encountered 279 * will accept the string "username@", and will 280 * append the hostname itself. We do this by default 281 * since many servers are picky about not having 282 * a FQDN in the anonymous password. 283 * - thorpej@netbsd.org 284 */ 285 snprintf(anonpass, sizeof(anonpass) - 1, "%s@", pass); 286 pass = anonpass; 287 } 288 user = "anonymous"; /* as per RFC 1635 */ 289 } 290 291 while (user == NULL) { 292 const char *myname = getlogin(); 293 294 if (myname == NULL && (pw = getpwuid(getuid())) != NULL) 295 myname = pw->pw_name; 296 if (myname) 297 fprintf(ttyout, "Name (%s:%s): ", host, myname); 298 else 299 fprintf(ttyout, "Name (%s): ", host); 300 *tmp = '\0'; 301 (void)fgets(tmp, sizeof(tmp) - 1, stdin); 302 tmp[strlen(tmp) - 1] = '\0'; 303 if (*tmp == '\0') 304 user = myname; 305 else 306 user = tmp; 307 } 308 n = command("USER %s", user); 309 if (n == CONTINUE) { 310 if (pass == NULL) 311 pass = getpass("Password:"); 312 n = command("PASS %s", pass); 313 } 314 if (n == CONTINUE) { 315 aflag++; 316 if (acct == NULL) 317 acct = getpass("Account:"); 318 n = command("ACCT %s", acct); 319 } 320 if ((n != COMPLETE) || 321 (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) { 322 warnx("Login failed."); 323 return (0); 324 } 325 if (proxy) 326 return (1); 327 connected = -1; 328 for (n = 0; n < macnum; ++n) { 329 if (!strcmp("init", macros[n].mac_name)) { 330 (void)strcpy(line, "$init"); 331 makeargv(); 332 domacro(margc, margv); 333 break; 334 } 335 } 336 return (1); 337 } 338 339 /* 340 * `another' gets another argument, and stores the new argc and argv. 341 * It reverts to the top level (via main.c's intr()) on EOF/error. 342 * 343 * Returns false if no new arguments have been added. 344 */ 345 int 346 another(pargc, pargv, prompt) 347 int *pargc; 348 char ***pargv; 349 const char *prompt; 350 { 351 int len = strlen(line), ret; 352 353 if (len >= sizeof(line) - 3) { 354 fputs("sorry, arguments too long.\n", ttyout); 355 intr(); 356 } 357 fprintf(ttyout, "(%s) ", prompt); 358 line[len++] = ' '; 359 if (fgets(&line[len], sizeof(line) - len, stdin) == NULL) 360 intr(); 361 len += strlen(&line[len]); 362 if (len > 0 && line[len - 1] == '\n') 363 line[len - 1] = '\0'; 364 makeargv(); 365 ret = margc > *pargc; 366 *pargc = margc; 367 *pargv = margv; 368 return (ret); 369 } 370 371 /* 372 * glob files given in argv[] from the remote server. 373 * if errbuf isn't NULL, store error messages there instead 374 * of writing to the screen. 375 */ 376 char * 377 remglob(argv, doswitch, errbuf) 378 char *argv[]; 379 int doswitch; 380 char **errbuf; 381 { 382 char temp[MAXPATHLEN]; 383 static char buf[MAXPATHLEN]; 384 static FILE *ftemp = NULL; 385 static char **args; 386 int oldverbose, oldhash, fd; 387 char *cp, *mode; 388 389 if (!mflag) { 390 if (!doglob) 391 args = NULL; 392 else { 393 if (ftemp) { 394 (void)fclose(ftemp); 395 ftemp = NULL; 396 } 397 } 398 return (NULL); 399 } 400 if (!doglob) { 401 if (args == NULL) 402 args = argv; 403 if ((cp = *++args) == NULL) 404 args = NULL; 405 return (cp); 406 } 407 if (ftemp == NULL) { 408 (void)snprintf(temp, sizeof(temp), "%s/%s", tmpdir, TMPFILE); 409 if ((fd = mkstemp(temp)) < 0) { 410 warn("unable to create temporary file %s", temp); 411 return (NULL); 412 } 413 close(fd); 414 oldverbose = verbose; 415 verbose = (errbuf != NULL) ? -1 : 0; 416 oldhash = hash; 417 hash = 0; 418 if (doswitch) 419 pswitch(!proxy); 420 for (mode = "w"; *++argv != NULL; mode = "a") 421 recvrequest("NLST", temp, *argv, mode, 0, 0); 422 if ((code / 100) != COMPLETE) { 423 if (errbuf != NULL) 424 *errbuf = reply_string; 425 } 426 if (doswitch) 427 pswitch(!proxy); 428 verbose = oldverbose; 429 hash = oldhash; 430 ftemp = fopen(temp, "r"); 431 (void)unlink(temp); 432 if (ftemp == NULL) { 433 if (errbuf == NULL) 434 fputs( 435 "can't find list of remote files, oops.\n", 436 ttyout); 437 else 438 *errbuf = 439 "can't find list of remote files, oops."; 440 return (NULL); 441 } 442 } 443 if (fgets(buf, sizeof(buf), ftemp) == NULL) { 444 (void)fclose(ftemp); 445 ftemp = NULL; 446 return (NULL); 447 } 448 if ((cp = strchr(buf, '\n')) != NULL) 449 *cp = '\0'; 450 return (buf); 451 } 452 453 int 454 confirm(cmd, file) 455 const char *cmd, *file; 456 { 457 char line[BUFSIZ]; 458 459 if (!interactive || confirmrest) 460 return (1); 461 fprintf(ttyout, "%s %s? ", cmd, file); 462 (void)fflush(ttyout); 463 if (fgets(line, sizeof(line), stdin) == NULL) 464 return (0); 465 switch (tolower(*line)) { 466 case 'n': 467 return (0); 468 case 'p': 469 interactive = 0; 470 fputs("Interactive mode: off.\n", ttyout); 471 break; 472 case 'a': 473 confirmrest = 1; 474 fprintf(ttyout, "Prompting off for duration of %s.\n", 475 cmd); 476 break; 477 } 478 return (1); 479 } 480 481 /* 482 * Glob a local file name specification with 483 * the expectation of a single return value. 484 * Can't control multiple values being expanded 485 * from the expression, we return only the first. 486 */ 487 int 488 globulize(cpp) 489 char **cpp; 490 { 491 glob_t gl; 492 int flags; 493 494 if (!doglob) 495 return (1); 496 497 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 498 memset(&gl, 0, sizeof(gl)); 499 if (glob(*cpp, flags, NULL, &gl) || 500 gl.gl_pathc == 0) { 501 warnx("%s: not found", *cpp); 502 globfree(&gl); 503 return (0); 504 } 505 /* XXX: caller should check if *cpp changed, and 506 * free(*cpp) if that is the case 507 */ 508 *cpp = xstrdup(gl.gl_pathv[0]); 509 globfree(&gl); 510 return (1); 511 } 512 513 /* 514 * determine size of remote file 515 */ 516 off_t 517 remotesize(file, noisy) 518 const char *file; 519 int noisy; 520 { 521 int overbose; 522 off_t size; 523 524 overbose = verbose; 525 size = -1; 526 if (debug == 0) 527 verbose = -1; 528 if (command("SIZE %s", file) == COMPLETE) { 529 char *cp, *ep; 530 531 cp = strchr(reply_string, ' '); 532 if (cp != NULL) { 533 cp++; 534 #ifndef NO_QUAD 535 size = strtoq(cp, &ep, 10); 536 #else 537 size = strtol(cp, &ep, 10); 538 #endif 539 if (*ep != '\0' && !isspace((unsigned char)*ep)) 540 size = -1; 541 } 542 } else if (noisy && debug == 0) { 543 fputs(reply_string, ttyout); 544 putc('\n', ttyout); 545 } 546 verbose = overbose; 547 return (size); 548 } 549 550 /* 551 * determine last modification time (in GMT) of remote file 552 */ 553 time_t 554 remotemodtime(file, noisy) 555 const char *file; 556 int noisy; 557 { 558 int overbose; 559 time_t rtime; 560 int ocode; 561 562 overbose = verbose; 563 ocode = code; 564 rtime = -1; 565 if (debug == 0) 566 verbose = -1; 567 if (command("MDTM %s", file) == COMPLETE) { 568 struct tm timebuf; 569 int yy, mo, day, hour, min, sec; 570 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, 571 &day, &hour, &min, &sec); 572 memset(&timebuf, 0, sizeof(timebuf)); 573 timebuf.tm_sec = sec; 574 timebuf.tm_min = min; 575 timebuf.tm_hour = hour; 576 timebuf.tm_mday = day; 577 timebuf.tm_mon = mo - 1; 578 timebuf.tm_year = yy - TM_YEAR_BASE; 579 timebuf.tm_isdst = -1; 580 rtime = mkgmtime(&timebuf); 581 if (rtime == -1 && (noisy || debug != 0)) 582 fprintf(ttyout, "Can't convert %s to a time.\n", 583 reply_string); 584 } else if (noisy && debug == 0) { 585 fputs(reply_string, ttyout); 586 putc('\n', ttyout); 587 } 588 verbose = overbose; 589 if (rtime == -1) 590 code = ocode; 591 return (rtime); 592 } 593 594 /* 595 * UTC version of mktime(3) 596 */ 597 #ifdef HAVE_TIMEGM 598 time_t 599 mkgmtime(tm) 600 struct tm *tm; 601 { 602 603 /* This is very clean, but not portable at all. */ 604 return (timegm(tm)); 605 } 606 607 #else /* not HAVE_TIMEGM */ 608 609 /* 610 * This code is not portable, but works on most Unix-like systems. 611 * If the local timezone has no summer time, using mktime(3) function 612 * and adjusting offset would be usable (adjusting leap seconds 613 * is still required, though), but the assumption is not always true. 614 * 615 * Anyway, no portable and correct implementation of UTC to time_t 616 * conversion exists.... 617 */ 618 619 static time_t 620 sub_mkgmt(tm) 621 struct tm *tm; 622 { 623 int y, nleapdays; 624 time_t t; 625 /* days before the month */ 626 static const unsigned short moff[12] = { 627 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 628 }; 629 630 /* 631 * XXX This code assumes the given time to be normalized. 632 * Normalizing here is impossible in case the given time is a leap 633 * second but the local time library is ignorant of leap seconds. 634 */ 635 636 /* minimal sanity checking not to access outside of the array */ 637 if ((unsigned) tm->tm_mon >= 12) 638 return (time_t) -1; 639 if (tm->tm_year < EPOCH_YEAR - TM_YEAR_BASE) 640 return (time_t) -1; 641 642 y = tm->tm_year + TM_YEAR_BASE - (tm->tm_mon < 2); 643 nleapdays = y / 4 - y / 100 + y / 400 - 644 ((EPOCH_YEAR-1) / 4 - (EPOCH_YEAR-1) / 100 + (EPOCH_YEAR-1) / 400); 645 t = ((((time_t) (tm->tm_year - (EPOCH_YEAR - TM_YEAR_BASE)) * 365 + 646 moff[tm->tm_mon] + tm->tm_mday - 1 + nleapdays) * 24 + 647 tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec; 648 649 return (t < 0 ? (time_t) -1 : t); 650 } 651 652 time_t 653 mkgmtime(tm) 654 struct tm *tm; 655 { 656 time_t t, t2; 657 struct tm *tm2; 658 int sec; 659 660 /* Do the first guess. */ 661 if ((t = sub_mkgmt(tm)) == (time_t) -1) 662 return (time_t) -1; 663 664 /* save value in case *tm is overwritten by gmtime() */ 665 sec = tm->tm_sec; 666 667 tm2 = gmtime(&t); 668 if ((t2 = sub_mkgmt(tm2)) == (time_t) -1) 669 return (time_t) -1; 670 671 if (t2 < t || tm2->tm_sec != sec) { 672 /* 673 * Adjust for leap seconds. 674 * 675 * real time_t time 676 * | 677 * tm 678 * / ... (a) first sub_mkgmt() conversion 679 * t 680 * | 681 * tm2 682 * / ... (b) second sub_mkgmt() conversion 683 * t2 684 * --->time 685 */ 686 /* 687 * Do the second guess, assuming (a) and (b) are almost equal. 688 */ 689 t += t - t2; 690 tm2 = gmtime(&t); 691 692 /* 693 * Either (a) or (b), may include one or two extra 694 * leap seconds. Try t, t + 2, t - 2, t + 1, and t - 1. 695 */ 696 if (tm2->tm_sec == sec 697 || (t += 2, tm2 = gmtime(&t), tm2->tm_sec == sec) 698 || (t -= 4, tm2 = gmtime(&t), tm2->tm_sec == sec) 699 || (t += 3, tm2 = gmtime(&t), tm2->tm_sec == sec) 700 || (t -= 2, tm2 = gmtime(&t), tm2->tm_sec == sec)) 701 ; /* found */ 702 else { 703 /* 704 * Not found. 705 */ 706 if (sec >= 60) 707 /* 708 * The given time is a leap second 709 * (sec 60 or 61), but the time library 710 * is ignorant of the leap second. 711 */ 712 ; /* treat sec 60 as 59, 713 sec 61 as 0 of the next minute */ 714 else 715 /* The given time may not be normalized. */ 716 t++; /* restore t */ 717 } 718 } 719 720 return (t < 0 ? (time_t) -1 : t); 721 } 722 #endif /* not HAVE_TIMEGM */ 723 724 #ifndef SMALL 725 726 /* 727 * return non-zero if we're the current foreground process 728 */ 729 int 730 foregroundproc() 731 { 732 static pid_t pgrp = -1; 733 int ctty_pgrp; 734 735 if (pgrp == -1) 736 pgrp = getpgrp(); 737 738 return ((ioctl(fileno(ttyout), TIOCGPGRP, &ctty_pgrp) != -1 && 739 ctty_pgrp == (int)pgrp)); 740 } 741 742 743 static void updateprogressmeter __P((int)); 744 745 static void 746 updateprogressmeter(dummy) 747 int dummy; 748 { 749 750 /* 751 * print progress bar only if we are foreground process. 752 */ 753 if (foregroundproc()) 754 progressmeter(0); 755 } 756 #endif /* SMALL */ 757 758 759 /* 760 * List of order of magnitude prefixes. 761 * The last is `P', as 2^64 = 16384 Petabytes 762 */ 763 static const char prefixes[] = " KMGTP"; 764 765 /* 766 * Display a transfer progress bar if progress is non-zero. 767 * SIGALRM is hijacked for use by this function. 768 * - Before the transfer, set filesize to size of file (or -1 if unknown), 769 * and call with flag = -1. This starts the once per second timer, 770 * and a call to updateprogressmeter() upon SIGALRM. 771 * - During the transfer, updateprogressmeter will call progressmeter 772 * with flag = 0 773 * - After the transfer, call with flag = 1 774 */ 775 static struct timeval start; 776 static struct timeval lastupdate; 777 778 void 779 progressmeter(flag) 780 int flag; 781 { 782 #ifndef SMALL 783 static off_t lastsize; 784 struct timeval now, td, wait; 785 off_t cursize, abbrevsize, bytespersec; 786 double elapsed; 787 int ratio, barlength, i, len, remaining; 788 char buf[256]; 789 790 len = 0; 791 792 if (flag == -1) { 793 (void)gettimeofday(&start, NULL); 794 lastupdate = start; 795 lastsize = restart_point; 796 } 797 (void)gettimeofday(&now, NULL); 798 if (!progress || filesize <= 0) 799 return; 800 cursize = bytes + restart_point; 801 802 ratio = (int)((double)cursize * 100.0 / (double)filesize); 803 ratio = MAX(ratio, 0); 804 ratio = MIN(ratio, 100); 805 len += snprintf(buf + len, sizeof(buf) - len, "\r%3d%% ", ratio); 806 807 barlength = ttywidth - 43; 808 if (barlength > 0) { 809 i = barlength * ratio / 100; 810 len += snprintf(buf + len, sizeof(buf) - len, 811 "|%.*s%*s|", i, 812 "*****************************************************************************" 813 "*****************************************************************************", 814 barlength - i, ""); 815 } 816 817 abbrevsize = cursize; 818 for (i = 0; abbrevsize >= 100000 && i < sizeof(prefixes); i++) 819 abbrevsize >>= 10; 820 len += snprintf(buf + len, sizeof(buf) - len, 821 #ifndef NO_QUAD 822 " %5qd %c%c ", (long long)abbrevsize, 823 #else 824 " %5ld %c%c ", (long)abbrevsize, 825 #endif 826 prefixes[i], 827 i == 0 ? ' ' : 'B'); 828 829 timersub(&now, &lastupdate, &wait); 830 if (cursize > lastsize) { 831 lastupdate = now; 832 lastsize = cursize; 833 if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */ 834 start.tv_sec += wait.tv_sec; 835 start.tv_usec += wait.tv_usec; 836 } 837 wait.tv_sec = 0; 838 } 839 840 timersub(&now, &start, &td); 841 elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 842 843 bytespersec = 0; 844 if (bytes > 0) { 845 bytespersec = bytes; 846 if (elapsed > 0.0) 847 bytespersec /= elapsed; 848 } 849 for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++) 850 bytespersec >>= 10; 851 len += snprintf(buf + len, sizeof(buf) - len, 852 #ifndef NO_QUAD 853 " %3qd.%02d %cB/s ", (long long)bytespersec / 1024, 854 #else 855 " %3ld.%02d %cB/s ", (long)bytespersec / 1024, 856 #endif 857 (int)((bytespersec % 1024) * 100 / 1024), 858 prefixes[i]); 859 860 if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { 861 len += snprintf(buf + len, sizeof(buf) - len, 862 " --:-- ETA"); 863 } else if (wait.tv_sec >= STALLTIME) { 864 len += snprintf(buf + len, sizeof(buf) - len, 865 " - stalled -"); 866 } else { 867 remaining = (int) 868 ((filesize - restart_point) / (bytes / elapsed) - elapsed); 869 if (remaining >= 100 * SECSPERHOUR) 870 len += snprintf(buf + len, sizeof(buf) - len, 871 " --:-- ETA"); 872 else { 873 i = remaining / SECSPERHOUR; 874 if (i) 875 len += snprintf(buf + len, sizeof(buf) - len, 876 "%2d:", i); 877 else 878 len += snprintf(buf + len, sizeof(buf) - len, 879 " "); 880 i = remaining % SECSPERHOUR; 881 len += snprintf(buf + len, sizeof(buf) - len, 882 "%02d:%02d ETA", i / 60, i % 60); 883 } 884 } 885 (void)write(fileno(ttyout), buf, len); 886 887 if (flag == -1) { 888 (void)xsignal(SIGALRM, updateprogressmeter); 889 alarmtimer(1); /* set alarm timer for 1 Hz */ 890 } else if (flag == 1) { 891 (void)xsignal(SIGALRM, SIG_DFL); 892 alarmtimer(0); 893 (void)putc('\n', ttyout); 894 } 895 fflush(ttyout); 896 #endif /* SMALL */ 897 } 898 899 /* 900 * Display transfer statistics. 901 * Requires start to be initialised by progressmeter(-1), 902 * direction to be defined by xfer routines, and filesize and bytes 903 * to be updated by xfer routines 904 * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr 905 * instead of ttyout. 906 */ 907 void 908 ptransfer(siginfo) 909 int siginfo; 910 { 911 #ifndef SMALL 912 struct timeval now, td, wait; 913 double elapsed; 914 off_t bytespersec; 915 int remaining, hh, i, len; 916 char buf[100]; 917 918 if (!verbose && !siginfo) 919 return; 920 921 (void)gettimeofday(&now, NULL); 922 timersub(&now, &start, &td); 923 elapsed = td.tv_sec + (td.tv_usec / 1000000.0); 924 bytespersec = 0; 925 if (bytes > 0) { 926 bytespersec = bytes; 927 if (elapsed > 0.0) 928 bytespersec /= elapsed; 929 } 930 len = 0; 931 len += snprintf(buf + len, sizeof(buf) - len, 932 #ifndef NO_QUAD 933 "%qd byte%s %s in ", (long long)bytes, 934 #else 935 "%ld byte%s %s in ", (long)bytes, 936 #endif 937 bytes == 1 ? "" : "s", direction); 938 remaining = (int)elapsed; 939 if (remaining > SECSPERDAY) { 940 int days; 941 942 days = remaining / SECSPERDAY; 943 remaining %= SECSPERDAY; 944 len += snprintf(buf + len, sizeof(buf) - len, 945 "%d day%s ", days, days == 1 ? "" : "s"); 946 } 947 hh = remaining / SECSPERHOUR; 948 remaining %= SECSPERHOUR; 949 if (hh) 950 len += snprintf(buf + len, sizeof(buf) - len, "%2d:", hh); 951 len += snprintf(buf + len, sizeof(buf) - len, 952 "%02d:%02d ", remaining / 60, remaining % 60); 953 954 for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++) 955 bytespersec >>= 10; 956 len += snprintf(buf + len, sizeof(buf) - len, 957 #ifndef NO_QUAD 958 "(%qd.%02d %cB/s)", (long long)bytespersec / 1024, 959 #else 960 "(%ld.%02d %cB/s)", (long)bytespersec / 1024, 961 #endif 962 (int)((bytespersec % 1024) * 100 / 1024), 963 prefixes[i]); 964 965 if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 966 && bytes + restart_point <= filesize) { 967 remaining = (int)((filesize - restart_point) / 968 (bytes / elapsed) - elapsed); 969 hh = remaining / SECSPERHOUR; 970 remaining %= SECSPERHOUR; 971 len += snprintf(buf + len, sizeof(buf) - len, " ETA: "); 972 if (hh) 973 len += snprintf(buf + len, sizeof(buf) - len, "%2d:", 974 hh); 975 len += snprintf(buf + len, sizeof(buf) - len, 976 "%02d:%02d", remaining / 60, remaining % 60); 977 timersub(&now, &lastupdate, &wait); 978 if (wait.tv_sec >= STALLTIME) 979 len += snprintf(buf + len, sizeof(buf) - len, 980 " (stalled)"); 981 } 982 len += snprintf(buf + len, sizeof(buf) - len, "\n"); 983 (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len); 984 #endif /* SMALL */ 985 } 986 987 /* 988 * List words in stringlist, vertically arranged 989 */ 990 void 991 list_vertical(sl) 992 StringList *sl; 993 { 994 int i, j, w; 995 int columns, width, lines, items; 996 char *p; 997 998 width = items = 0; 999 1000 for (i = 0 ; i < sl->sl_cur ; i++) { 1001 w = strlen(sl->sl_str[i]); 1002 if (w > width) 1003 width = w; 1004 } 1005 width = (width + 8) &~ 7; 1006 1007 columns = ttywidth / width; 1008 if (columns == 0) 1009 columns = 1; 1010 lines = (sl->sl_cur + columns - 1) / columns; 1011 for (i = 0; i < lines; i++) { 1012 for (j = 0; j < columns; j++) { 1013 p = sl->sl_str[j * lines + i]; 1014 if (p) 1015 fputs(p, ttyout); 1016 if (j * lines + i + lines >= sl->sl_cur) { 1017 putc('\n', ttyout); 1018 break; 1019 } 1020 w = strlen(p); 1021 while (w < width) { 1022 w = (w + 8) &~ 7; 1023 (void)putc('\t', ttyout); 1024 } 1025 } 1026 } 1027 } 1028 1029 /* 1030 * Update the global ttywidth value, using TIOCGWINSZ. 1031 */ 1032 void 1033 setttywidth(a) 1034 int a; 1035 { 1036 struct winsize winsize; 1037 1038 if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 && 1039 winsize.ws_col != 0) 1040 ttywidth = winsize.ws_col; 1041 else 1042 ttywidth = 80; 1043 } 1044 1045 /* 1046 * Set the SIGALRM interval timer for wait seconds, 0 to disable. 1047 */ 1048 void 1049 alarmtimer(wait) 1050 int wait; 1051 { 1052 struct itimerval itv; 1053 1054 itv.it_value.tv_sec = wait; 1055 itv.it_value.tv_usec = 0; 1056 itv.it_interval = itv.it_value; 1057 setitimer(ITIMER_REAL, &itv, NULL); 1058 } 1059 1060 /* 1061 * Setup or cleanup EditLine structures 1062 */ 1063 #ifndef SMALL 1064 void 1065 controlediting() 1066 { 1067 if (editing && el == NULL && hist == NULL) { 1068 HistEvent ev; 1069 int editmode; 1070 1071 el = el_init(__progname, stdin, ttyout, stderr); 1072 /* init editline */ 1073 hist = history_init(); /* init the builtin history */ 1074 history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */ 1075 el_set(el, EL_HIST, history, hist); /* use history */ 1076 1077 el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */ 1078 el_set(el, EL_PROMPT, prompt); /* set the prompt function */ 1079 1080 /* add local file completion, bind to TAB */ 1081 el_set(el, EL_ADDFN, "ftp-complete", 1082 "Context sensitive argument completion", 1083 complete); 1084 el_set(el, EL_BIND, "^I", "ftp-complete", NULL); 1085 el_source(el, NULL); /* read ~/.editrc */ 1086 if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0) 1087 editing = 0; /* the user doesn't want editing, 1088 * so disable, and let statement 1089 * below cleanup */ 1090 else 1091 el_set(el, EL_SIGNAL, 1); 1092 } 1093 if (!editing) { 1094 if (hist) { 1095 history_end(hist); 1096 hist = NULL; 1097 } 1098 if (el) { 1099 el_end(el); 1100 el = NULL; 1101 } 1102 } 1103 } 1104 #endif /* !SMALL */ 1105 1106 /* 1107 * Parse the specified socket buffer size. 1108 */ 1109 int 1110 getsockbufsize(arg) 1111 const char *arg; 1112 { 1113 char *cp; 1114 int val; 1115 1116 if (!isdigit((unsigned char)arg[0])) 1117 return (-1); 1118 1119 val = strtol(arg, &cp, 10); 1120 if (cp != NULL) { 1121 if (cp[1] != '\0') 1122 return (-1); 1123 if (cp[0] == 'k') 1124 val *= 1024; 1125 if (cp[0] == 'm') 1126 val *= 1024 * 1024; 1127 } 1128 1129 if (val < 0) 1130 return (-1); 1131 1132 return (val); 1133 } 1134 1135 /* 1136 * Set up socket buffer sizes before a connection is made. 1137 */ 1138 void 1139 setupsockbufsize(sock) 1140 int sock; 1141 { 1142 static int sndbuf_default, rcvbuf_default; 1143 int len, size; 1144 1145 /* 1146 * Get the default socket buffer sizes if we don't already 1147 * have them. It doesn't matter which socket we do this 1148 * to, because on the first call no socket buffer sizes 1149 * will have been modified, so we are guaranteed to get 1150 * the system defaults. 1151 */ 1152 if (sndbuf_default == 0) { 1153 len = sizeof(sndbuf_default); 1154 if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf_default, 1155 &len) < 0) 1156 err(1, "unable to get default sndbuf size"); 1157 len = sizeof(rcvbuf_default); 1158 if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf_default, 1159 &len) < 0) 1160 err(1, "unable to get default rcvbuf size"); 1161 1162 } 1163 1164 size = sndbuf_size ? sndbuf_size : sndbuf_default; 1165 if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) 1166 warn("unable to set sndbuf size %d", size); 1167 1168 size = rcvbuf_size ? rcvbuf_size : rcvbuf_default; 1169 if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) 1170 warn("unable to set rcvbuf size %d", size); 1171 } 1172 1173 /* 1174 * If the socket buffer sizes were not set manually (i.e. came from a 1175 * configuration file), reset them so the right thing will happen on 1176 * subsequent connections. 1177 */ 1178 void 1179 resetsockbufsize() 1180 { 1181 1182 if (sndbuf_manual == 0) 1183 sndbuf_size = 0; 1184 if (rcvbuf_manual == 0) 1185 rcvbuf_size = 0; 1186 } 1187 1188 /* 1189 * Internal version of connect(2); sets socket buffer sizes first. 1190 */ 1191 int 1192 xconnect(sock, name, namelen) 1193 int sock; 1194 const struct sockaddr *name; 1195 int namelen; 1196 { 1197 1198 setupsockbufsize(sock); 1199 return (connect(sock, name, namelen)); 1200 } 1201 1202 /* 1203 * Internal version of listen(2); sets socket buffer sizes first. 1204 */ 1205 int 1206 xlisten(sock, backlog) 1207 int sock, backlog; 1208 { 1209 1210 setupsockbufsize(sock); 1211 return (listen(sock, backlog)); 1212 } 1213 1214 void * 1215 xmalloc(size) 1216 size_t size; 1217 { 1218 void *p; 1219 1220 p = malloc(size); 1221 if (p == NULL) 1222 err(1, "Unable to allocate %ld bytes of memory", (long)size); 1223 return (p); 1224 } 1225 1226 char * 1227 xstrdup(str) 1228 const char *str; 1229 { 1230 char *s; 1231 1232 if (str == NULL) 1233 errx(1, "xstrdup() called with NULL argument"); 1234 s = strdup(str); 1235 if (s == NULL) 1236 err(1, "Unable to allocate memory for string copy"); 1237 return (s); 1238 } 1239 1240 sig_t 1241 xsignal(sig, func) 1242 int sig; 1243 void (*func) __P((int)); 1244 { 1245 struct sigaction act, oact; 1246 1247 act.sa_handler = func; 1248 sigemptyset(&act.sa_mask); 1249 act.sa_flags = SA_RESTART; 1250 if (sigaction(sig, &act, &oact) < 0) 1251 return (SIG_ERR); 1252 return (oact.sa_handler); 1253 } 1254