=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ftp/util.c,v retrieving revision 1.86 retrieving revision 1.87 diff -c -r1.86 -r1.87 *** src/usr.bin/ftp/util.c 2017/12/23 20:04:23 1.86 --- src/usr.bin/ftp/util.c 2019/05/12 20:44:39 1.87 *************** *** 1,1099 **** - /* $OpenBSD: util.c,v 1.86 2017/12/23 20:04:23 cheloha Exp $ */ - /* $NetBSD: util.c,v 1.12 1997/08/18 10:20:27 lukem Exp $ */ - - /*- - * Copyright (c) 1997-1999 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Luke Mewburn. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, - * NASA Ames Research Center. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - /* ! * Copyright (c) 1985, 1989, 1993, 1994 ! * The Regents of the University of California. All rights reserved. * ! * Redistribution and use in source and binary forms, with or without ! * modification, are permitted provided that the following conditions ! * are met: ! * 1. Redistributions of source code must retain the above copyright ! * notice, this list of conditions and the following disclaimer. ! * 2. Redistributions in binary form must reproduce the above copyright ! * notice, this list of conditions and the following disclaimer in the ! * documentation and/or other materials provided with the distribution. ! * 3. Neither the name of the University nor the names of its contributors ! * may be used to endorse or promote products derived from this software ! * without specific prior written permission. * ! * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! * SUCH DAMAGE. */ ! /* ! * FTP User Program -- Misc support routines ! */ ! #include #include - #include - #include - #include #include #include ! #include ! #include ! #include #include - #include #include #include - #include #include ! #include #include ! #include "ftp_var.h" ! #include "pathnames.h" ! #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) ! #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) - static void updateprogressmeter(int); - /* ! * Connect to peer server and ! * auto-login, if possible. */ ! void ! setpeer(int argc, char *argv[]) { ! char *host, *port; ! if (connected) { ! fprintf(ttyout, "Already connected to %s, use close first.\n", ! hostname); ! code = -1; ! return; ! } ! #ifndef SMALL ! if (argc < 2) ! (void)another(&argc, &argv, "to"); ! if (argc < 2 || argc > 3) { ! fprintf(ttyout, "usage: %s host [port]\n", argv[0]); ! code = -1; ! return; ! } ! #endif /* !SMALL */ ! if (gatemode) ! port = gateport; ! else ! port = ftpport; ! if (argc > 2) ! port = argv[2]; ! if (gatemode) { ! if (gateserver == NULL || *gateserver == '\0') ! errx(1, "gateserver not defined (shouldn't happen)"); ! host = hookup(gateserver, port); ! } else ! host = hookup(argv[1], port); ! ! if (host) { ! int overbose; ! ! if (gatemode) { ! if (command("PASSERVE %s", argv[1]) != COMPLETE) ! return; ! if (verbose) ! fprintf(ttyout, ! "Connected via pass-through server %s\n", ! gateserver); ! } ! ! connected = 1; ! /* ! * Set up defaults for FTP. ! */ ! (void)strlcpy(formname, "non-print", sizeof formname); ! form = FORM_N; ! (void)strlcpy(modename, "stream", sizeof modename); ! mode = MODE_S; ! (void)strlcpy(structname, "file", sizeof structname); ! stru = STRU_F; ! (void)strlcpy(bytename, "8", sizeof bytename); ! bytesize = 8; ! ! /* ! * Set type to 0 (not specified by user), ! * meaning binary by default, but don't bother ! * telling server. We can use binary ! * for text files unless changed by the user. ! */ ! (void)strlcpy(typename, "binary", sizeof typename); ! curtype = TYPE_A; ! type = 0; ! if (autologin) ! (void)ftp_login(argv[1], NULL, NULL); ! ! overbose = verbose; ! #ifndef SMALL ! if (!debug) ! #endif /* !SMALL */ ! verbose = -1; ! if (command("SYST") == COMPLETE && overbose) { ! char *cp, c; ! c = 0; ! cp = strchr(reply_string + 4, ' '); ! if (cp == NULL) ! cp = strchr(reply_string + 4, '\r'); ! if (cp) { ! if (cp[-1] == '.') ! cp--; ! c = *cp; ! *cp = '\0'; ! } ! ! fprintf(ttyout, "Remote system type is %s.\n", reply_string + 4); ! if (cp) ! *cp = c; ! } ! if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { ! if (proxy) ! unix_proxy = 1; ! else ! unix_server = 1; ! if (overbose) ! fprintf(ttyout, "Using %s mode to transfer files.\n", ! typename); ! } else { ! if (proxy) ! unix_proxy = 0; ! else ! unix_server = 0; ! } ! verbose = overbose; } } ! /* ! * login to remote host, using given username & password if supplied ! */ int ! ftp_login(const char *host, char *user, char *pass) { ! char tmp[80], *acctname = NULL, host_name[HOST_NAME_MAX+1]; ! char anonpass[LOGIN_NAME_MAX + 1 + HOST_NAME_MAX+1]; /* "user@hostname" */ ! int n, aflag = 0, retry = 0; ! struct passwd *pw; ! #ifndef SMALL ! if (user == NULL && !anonftp) { ! if (ruserpass(host, &user, &pass, &acctname) < 0) { ! code = -1; ! return (0); ! } } - #endif /* !SMALL */ ! /* ! * Set up arguments for an anonymous FTP session, if necessary. ! */ ! if ((user == NULL || pass == NULL) && anonftp) { ! memset(anonpass, 0, sizeof(anonpass)); ! memset(host_name, 0, sizeof(host_name)); ! /* ! * Set up anonymous login password. ! */ ! if ((user = getlogin()) == NULL) { ! if ((pw = getpwuid(getuid())) == NULL) ! user = "anonymous"; ! else ! user = pw->pw_name; ! } ! gethostname(host_name, sizeof(host_name)); ! #ifndef DONT_CHEAT_ANONPASS ! /* ! * Every anonymous FTP server I've encountered ! * will accept the string "username@", and will ! * append the hostname itself. We do this by default ! * since many servers are picky about not having ! * a FQDN in the anonymous password. - thorpej@netbsd.org ! */ ! snprintf(anonpass, sizeof(anonpass) - 1, "%s@", ! user); ! #else ! snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s", ! user, hp->h_name); ! #endif ! pass = anonpass; ! user = "anonymous"; /* as per RFC 1635 */ } ! tryagain: ! if (retry) ! user = "ftp"; /* some servers only allow "ftp" */ ! while (user == NULL) { ! char *myname = getlogin(); ! ! if (myname == NULL && (pw = getpwuid(getuid())) != NULL) ! myname = pw->pw_name; ! if (myname) ! fprintf(ttyout, "Name (%s:%s): ", host, myname); ! else ! fprintf(ttyout, "Name (%s): ", host); ! user = myname; ! if (fgets(tmp, sizeof(tmp), stdin) != NULL) { ! tmp[strcspn(tmp, "\n")] = '\0'; ! if (tmp[0] != '\0') ! user = tmp; } - else - exit(0); - } - n = command("USER %s", user); - if (n == CONTINUE) { - if (pass == NULL) - pass = getpass("Password:"); - n = command("PASS %s", pass); - } - if (n == CONTINUE) { - aflag++; - if (acctname == NULL) - acctname = getpass("Account:"); - n = command("ACCT %s", acctname); - } - if ((n != COMPLETE) || - (!aflag && acctname != NULL && command("ACCT %s", acctname) != COMPLETE)) { - warnx("Login %s failed.", user); - if (retry || !anonftp) - return (0); - else - retry = 1; - goto tryagain; - } - if (proxy) - return (1); - connected = -1; - #ifndef SMALL - for (n = 0; n < macnum; ++n) { - if (!strcmp("init", macros[n].mac_name)) { - (void)strlcpy(line, "$init", sizeof line); - makeargv(); - domacro(margc, margv); - break; - } - } - #endif /* SMALL */ - return (1); - } ! /* ! * `another' gets another argument, and stores the new argc and argv. ! * It reverts to the top level (via main.c's intr()) on EOF/error. ! * ! * Returns false if no new arguments have been added. ! */ ! #ifndef SMALL ! int ! another(int *pargc, char ***pargv, const char *prompt) ! { ! int len = strlen(line), ret; ! if (len >= sizeof(line) - 3) { ! fputs("sorry, arguments too long.\n", ttyout); ! intr(); ! } ! fprintf(ttyout, "(%s) ", prompt); ! line[len++] = ' '; ! if (fgets(&line[len], (int)(sizeof(line) - len), stdin) == NULL) { ! clearerr(stdin); ! intr(); ! } ! len += strlen(&line[len]); ! if (len > 0 && line[len - 1] == '\n') ! line[len - 1] = '\0'; ! makeargv(); ! ret = margc > *pargc; ! *pargc = margc; ! *pargv = margv; ! return (ret); ! } ! #endif /* !SMALL */ ! ! /* ! * glob files given in argv[] from the remote server. ! * if errbuf isn't NULL, store error messages there instead ! * of writing to the screen. ! * if type isn't NULL, use LIST instead of NLST, and store filetype. ! * 'd' means directory, 's' means symbolic link, '-' means plain ! * file. ! */ ! char * ! remglob2(char *argv[], int doswitch, char **errbuf, FILE **ftemp, char *type) ! { ! char temp[PATH_MAX], *bufp, *cp, *lmode; ! static char buf[PATH_MAX], **args; ! int oldverbose, oldhash, fd; ! ! if (!mflag) { ! if (!doglob) ! args = NULL; ! else { ! if (*ftemp) { ! (void)fclose(*ftemp); ! *ftemp = NULL; ! } } ! return (NULL); } - if (!doglob) { - if (args == NULL) - args = argv; - if ((cp = *++args) == NULL) - args = NULL; - return (cp); - } - if (*ftemp == NULL) { - int len; ! cp = _PATH_TMP; ! len = strlen(cp); ! if (len + sizeof(TMPFILE) + (cp[len-1] != '/') > sizeof(temp)) { ! warnx("unable to create temporary file: %s", ! strerror(ENAMETOOLONG)); ! return (NULL); ! } ! ! (void)strlcpy(temp, cp, sizeof temp); ! if (temp[len-1] != '/') ! temp[len++] = '/'; ! (void)strlcpy(&temp[len], TMPFILE, sizeof temp - len); ! if ((fd = mkstemp(temp)) < 0) { ! warn("unable to create temporary file: %s", temp); ! return (NULL); ! } ! close(fd); ! oldverbose = verbose; ! verbose = (errbuf != NULL) ? -1 : 0; ! oldhash = hash; ! hash = 0; ! if (doswitch) ! pswitch(!proxy); ! for (lmode = "w"; *++argv != NULL; lmode = "a") ! recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode, ! 0, 0); ! if ((code / 100) != COMPLETE) { ! if (errbuf != NULL) ! *errbuf = reply_string; ! } ! if (doswitch) ! pswitch(!proxy); ! verbose = oldverbose; ! hash = oldhash; ! *ftemp = fopen(temp, "r"); ! (void)unlink(temp); ! if (*ftemp == NULL) { ! if (errbuf == NULL) ! fputs("can't find list of remote files, oops.\n", ! ttyout); ! else ! *errbuf = ! "can't find list of remote files, oops."; ! return (NULL); ! } } - #ifndef SMALL - again: - #endif - if (fgets(buf, sizeof(buf), *ftemp) == NULL) { - (void)fclose(*ftemp); - *ftemp = NULL; - return (NULL); - } ! buf[strcspn(buf, "\n")] = '\0'; ! bufp = buf; ! ! #ifndef SMALL ! if (type) { ! parse_list(&bufp, type); ! if (!bufp || ! (bufp[0] == '.' && /* LIST defaults to -a on some ftp */ ! (bufp[1] == '\0' || /* servers. Ignore '.' and '..'. */ ! (bufp[1] == '.' && bufp[2] == '\0')))) ! goto again; } - #endif /* !SMALL */ ! return (bufp); } - /* - * wrapper for remglob2 - */ - char * - remglob(char *argv[], int doswitch, char **errbuf) - { - static FILE *ftemp = NULL; - - return remglob2(argv, doswitch, errbuf, &ftemp, NULL); - } - - #ifndef SMALL int ! confirm(const char *cmd, const char *file) { ! char str[BUFSIZ]; ! if (file && (confirmrest || !interactive)) ! return (1); ! top: ! if (file) ! fprintf(ttyout, "%s %s? ", cmd, file); ! else ! fprintf(ttyout, "Continue with %s? ", cmd); ! (void)fflush(ttyout); ! if (fgets(str, sizeof(str), stdin) == NULL) ! goto quit; ! switch (tolower((unsigned char)*str)) { ! case '?': ! fprintf(ttyout, ! "? help\n" ! "a answer yes to all\n" ! "n answer no\n" ! "p turn off prompt mode\n" ! "q answer no to all\n" ! "y answer yes\n"); ! goto top; ! case 'a': ! confirmrest = 1; ! fprintf(ttyout, "Prompting off for duration of %s.\n", ! cmd); ! break; ! case 'n': ! return (0); ! case 'p': ! interactive = 0; ! fputs("Interactive mode: off.\n", ttyout); ! break; ! case 'q': ! quit: ! mflag = 0; ! clearerr(stdin); ! return (0); ! case 'y': ! return(1); ! break; ! default: ! fprintf(ttyout, "?, a, n, p, q, y " ! "are the only acceptable commands!\n"); ! goto top; ! break; ! } ! return (1); ! } ! #endif /* !SMALL */ ! /* ! * Glob a local file name specification with ! * the expectation of a single return value. ! * Can't control multiple values being expanded ! * from the expression, we return only the first. ! */ ! int ! globulize(char **cpp) ! { ! glob_t gl; ! int flags; ! if (!doglob) ! return (1); ! ! flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; ! memset(&gl, 0, sizeof(gl)); ! if (glob(*cpp, flags, NULL, &gl) || ! gl.gl_pathc == 0) { ! warnx("%s: not found", *cpp); ! globfree(&gl); ! return (0); } - /* XXX: caller should check if *cpp changed, and - * free(*cpp) if that is the case - */ - *cpp = strdup(gl.gl_pathv[0]); - if (*cpp == NULL) - err(1, NULL); - globfree(&gl); - return (1); - } ! /* ! * determine size of remote file ! */ ! off_t ! remotesize(const char *file, int noisy) ! { ! int overbose; ! off_t size; ! ! overbose = verbose; ! size = -1; ! #ifndef SMALL ! if (!debug) ! #endif /* !SMALL */ ! verbose = -1; ! if (command("SIZE %s", file) == COMPLETE) { ! char *cp, *ep; ! ! cp = strchr(reply_string, ' '); ! if (cp != NULL) { ! cp++; ! size = strtoll(cp, &ep, 10); ! if (*ep != '\0' && !isspace((unsigned char)*ep)) ! size = -1; ! } ! } else if (noisy ! #ifndef SMALL ! && !debug ! #endif /* !SMALL */ ! ) { ! fputs(reply_string, ttyout); ! fputc('\n', ttyout); ! } ! verbose = overbose; ! return (size); } ! /* ! * determine last modification time (in GMT) of remote file ! */ ! time_t ! remotemodtime(const char *file, int noisy) { ! int overbose; ! time_t rtime; ! int ocode; ! overbose = verbose; ! ocode = code; ! rtime = -1; ! #ifndef SMALL ! if (!debug) ! #endif /* !SMALL */ ! verbose = -1; ! if (command("MDTM %s", file) == COMPLETE) { ! struct tm timebuf; ! int yy, mo, day, hour, min, sec; ! /* ! * time-val = 14DIGIT [ "." 1*DIGIT ] ! * YYYYMMDDHHMMSS[.sss] ! * mdtm-response = "213" SP time-val CRLF / error-response ! */ ! /* TODO: parse .sss as well, use timespecs. */ ! char *timestr = reply_string; ! ! /* Repair `19%02d' bug on server side */ ! while (!isspace((unsigned char)*timestr)) ! timestr++; ! while (isspace((unsigned char)*timestr)) ! timestr++; ! if (strncmp(timestr, "191", 3) == 0) { ! fprintf(ttyout, ! "Y2K warning! Fixed incorrect time-val received from server.\n"); ! timestr[0] = ' '; ! timestr[1] = '2'; ! timestr[2] = '0'; ! } ! sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, ! &day, &hour, &min, &sec); ! memset(&timebuf, 0, sizeof(timebuf)); ! timebuf.tm_sec = sec; ! timebuf.tm_min = min; ! timebuf.tm_hour = hour; ! timebuf.tm_mday = day; ! timebuf.tm_mon = mo - 1; ! timebuf.tm_year = yy - 1900; ! timebuf.tm_isdst = -1; ! rtime = mktime(&timebuf); ! if (rtime == -1 && (noisy ! #ifndef SMALL ! || debug ! #endif /* !SMALL */ ! )) ! fprintf(ttyout, "Can't convert %s to a time.\n", reply_string); ! else ! rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */ ! } else if (noisy ! #ifndef SMALL ! && !debug ! #endif /* !SMALL */ ! ) { ! fputs(reply_string, ttyout); ! fputc('\n', ttyout); ! } ! verbose = overbose; ! if (rtime == -1) ! code = ocode; ! return (rtime); } - /* - * Ensure file is in or under dir. - * Returns 1 if so, 0 if not (or an error occurred). - */ int ! fileindir(const char *file, const char *dir) { ! char parentdirbuf[PATH_MAX], *parentdir; ! char realdir[PATH_MAX]; ! size_t dirlen; ! /* determine parent directory of file */ ! (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf)); ! parentdir = dirname(parentdirbuf); ! if (strcmp(parentdir, ".") == 0) ! return 1; /* current directory is ok */ ! /* find the directory */ ! if (realpath(parentdir, realdir) == NULL) { ! warn("Unable to determine real path of `%s'", parentdir); return 0; - } - if (realdir[0] != '/') /* relative result is ok */ - return 1; ! dirlen = strlen(dir); ! if (strncmp(realdir, dir, dirlen) == 0 && ! (realdir[dirlen] == '/' || realdir[dirlen] == '\0')) ! return 1; ! return 0; } - - /* - * Returns true if this is the controlling/foreground process, else false. - */ - int - foregroundproc(void) - { - static pid_t pgrp = -1; - int ctty_pgrp; - - if (pgrp == -1) - pgrp = getpgrp(); - - return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && - ctty_pgrp == pgrp)); - } - - /* ARGSUSED */ - static void - updateprogressmeter(int signo) - { - int save_errno = errno; - - /* update progressmeter if foreground process or in -m mode */ - if (foregroundproc() || progress == -1) - progressmeter(0, NULL); - errno = save_errno; - } - - /* - * Display a transfer progress bar if progress is non-zero. - * SIGALRM is hijacked for use by this function. - * - Before the transfer, set filesize to size of file (or -1 if unknown), - * and call with flag = -1. This starts the once per second timer, - * and a call to updateprogressmeter() upon SIGALRM. - * - During the transfer, updateprogressmeter will call progressmeter - * with flag = 0 - * - After the transfer, call with flag = 1 - */ - static struct timespec start; - - char *action; - void ! progressmeter(int flag, const char *filename) { ! /* ! * List of order of magnitude prefixes. ! * The last is `P', as 2^64 = 16384 Petabytes ! */ ! static const char prefixes[] = " KMGTP"; ! static struct timespec lastupdate; ! static off_t lastsize; ! static char *title = NULL; ! struct timespec now, td, wait; ! off_t cursize, abbrevsize; ! double elapsed; ! int ratio, barlength, i, remaining, overhead = 30; ! char buf[512]; ! ! if (flag == -1) { ! clock_gettime(CLOCK_MONOTONIC, &start); ! lastupdate = start; ! lastsize = restart_point; ! } ! clock_gettime(CLOCK_MONOTONIC, &now); ! if (!progress || filesize < 0) return; - cursize = bytes + restart_point; ! if (filesize) ! ratio = cursize * 100 / filesize; ! else ! ratio = 100; ! ratio = MAXIMUM(ratio, 0); ! ratio = MINIMUM(ratio, 100); ! if (!verbose && flag == -1) { ! filename = basename(filename); ! if (filename != NULL) { ! free(title); ! title = strdup(filename); ! } ! } ! ! buf[0] = 0; ! if (!verbose && action != NULL) { ! int l = strlen(action); ! char *dotdot = ""; ! ! if (l < 7) ! l = 7; ! else if (l > 12) { ! l = 12; ! dotdot = "..."; ! overhead += 3; ! } ! snprintf(buf, sizeof(buf), "\r%-*.*s%s ", l, l, action, ! dotdot); ! overhead += l + 1; ! } else ! snprintf(buf, sizeof(buf), "\r"); ! ! if (!verbose && title != NULL) { ! int l = strlen(title); ! char *dotdot = ""; ! ! if (l < 12) ! l = 12; ! else if (l > 25) { ! l = 22; ! dotdot = "..."; ! overhead += 3; ! } ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! "%-*.*s%s %3d%% ", l, l, title, ! dotdot, ratio); ! overhead += l + 1; ! } else ! snprintf(buf, sizeof(buf), "\r%3d%% ", ratio); ! ! barlength = ttywidth - overhead; ! if (barlength > 0) { ! i = barlength * ratio / 100; ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! "|%.*s%*s|", i, ! "*******************************************************" ! "*******************************************************" ! "*******************************************************" ! "*******************************************************" ! "*******************************************************" ! "*******************************************************" ! "*******************************************************", ! barlength - i, ""); ! } ! ! i = 0; ! abbrevsize = cursize; ! while (abbrevsize >= 100000 && i < sizeof(prefixes)-1) { ! i++; ! abbrevsize >>= 10; ! } ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! " %5lld %c%c ", (long long)abbrevsize, prefixes[i], ! prefixes[i] == ' ' ? ' ' : 'B'); ! ! timespecsub(&now, &lastupdate, &wait); ! if (cursize > lastsize) { ! lastupdate = now; ! lastsize = cursize; ! if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */ ! start.tv_sec += wait.tv_sec; ! start.tv_nsec += wait.tv_nsec; ! } ! wait.tv_sec = 0; ! } ! ! timespecsub(&now, &start, &td); ! elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0); ! ! if (flag == 1) { ! i = (int)elapsed / 3600; ! if (i) ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! "%2d:", i); ! else ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! " "); ! i = (int)elapsed % 3600; ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! "%02d:%02d ", i / 60, i % 60); ! } else if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! " --:-- ETA"); ! } else if (wait.tv_sec >= STALLTIME) { ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! " - stalled -"); ! } else { ! remaining = (int)((filesize - restart_point) / ! (bytes / elapsed) - elapsed); ! i = remaining / 3600; ! if (i) ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! "%2d:", i); ! else ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! " "); ! i = remaining % 3600; ! snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ! "%02d:%02d ETA", i / 60, i % 60); ! } ! (void)write(fileno(ttyout), buf, strlen(buf)); ! ! if (flag == -1) { ! (void)signal(SIGALRM, updateprogressmeter); ! alarmtimer(1); /* set alarm timer for 1 Hz */ ! } else if (flag == 1) { ! alarmtimer(0); ! (void)putc('\n', ttyout); ! free(title); ! title = NULL; ! } ! fflush(ttyout); } - /* - * Display transfer statistics. - * Requires start to be initialised by progressmeter(-1), - * direction to be defined by xfer routines, and filesize and bytes - * to be updated by xfer routines - * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR - * instead of TTYOUT. - */ void ! ptransfer(int siginfo) { ! struct timespec now, td; ! double elapsed; ! off_t bs; ! int meg, remaining, hh; ! char buf[100]; ! if (!verbose && !siginfo) ! return; ! ! clock_gettime(CLOCK_MONOTONIC, &now); ! timespecsub(&now, &start, &td); ! elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0); ! bs = bytes / (elapsed == 0.0 ? 1 : elapsed); ! meg = 0; ! if (bs > (1024 * 1024)) ! meg = 1; ! ! /* XXX floating point printf in signal handler */ ! (void)snprintf(buf, sizeof(buf), ! "%lld byte%s %s in %.2f seconds (%.2f %sB/s)\n", ! (long long)bytes, bytes == 1 ? "" : "s", direction, elapsed, ! bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K"); ! ! if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 && ! bytes + restart_point <= filesize) { ! remaining = (int)((filesize - restart_point) / ! (bytes / elapsed) - elapsed); ! hh = remaining / 3600; ! remaining %= 3600; ! ! /* "buf+len(buf) -1" to overwrite \n */ ! snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf), ! " ETA: %02d:%02d:%02d\n", hh, remaining / 60, ! remaining % 60); } - (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, strlen(buf)); - } ! /* ! * List words in stringlist, vertically arranged ! */ ! #ifndef SMALL ! void ! list_vertical(StringList *sl) ! { ! int i, j, w; ! int columns, width, lines; ! char *p; ! ! width = 0; ! ! for (i = 0 ; i < sl->sl_cur ; i++) { ! w = strlen(sl->sl_str[i]); ! if (w > width) ! width = w; } - width = (width + 8) &~ 7; ! columns = ttywidth / width; ! if (columns == 0) ! columns = 1; ! lines = (sl->sl_cur + columns - 1) / columns; ! for (i = 0; i < lines; i++) { ! for (j = 0; j < columns; j++) { ! p = sl->sl_str[j * lines + i]; ! if (p) ! fputs(p, ttyout); ! if (j * lines + i + lines >= sl->sl_cur) { ! putc('\n', ttyout); ! break; ! } ! w = strlen(p); ! while (w < width) { ! w = (w + 8) &~ 7; ! (void)putc('\t', ttyout); ! } ! } ! } ! } ! #endif /* !SMALL */ ! /* ! * Update the global ttywidth value, using TIOCGWINSZ. ! */ ! /* ARGSUSED */ ! void ! setttywidth(int signo) ! { ! int save_errno = errno; ! struct winsize winsize; ! ! if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1) ! ttywidth = winsize.ws_col ? winsize.ws_col : 80; ! else ! ttywidth = 80; ! errno = save_errno; ! } ! ! /* ! * Set the SIGALRM interval timer for wait seconds, 0 to disable. ! */ ! void ! alarmtimer(int wait) ! { ! int save_errno = errno; ! struct itimerval itv; ! ! itv.it_value.tv_sec = wait; ! itv.it_value.tv_usec = 0; ! itv.it_interval = itv.it_value; ! setitimer(ITIMER_REAL, &itv, NULL); ! errno = save_errno; ! } ! ! /* ! * Setup or cleanup EditLine structures ! */ ! #ifndef SMALL ! void ! controlediting(void) ! { ! HistEvent hev; ! ! if (editing && el == NULL && hist == NULL) { ! el = el_init(__progname, stdin, ttyout, stderr); /* init editline */ ! hist = history_init(); /* init the builtin history */ ! history(hist, &hev, H_SETSIZE, 100); /* remember 100 events */ ! el_set(el, EL_HIST, history, hist); /* use history */ ! ! el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */ ! el_set(el, EL_PROMPT, prompt); /* set the prompt function */ ! ! /* add local file completion, bind to TAB */ ! el_set(el, EL_ADDFN, "ftp-complete", ! "Context sensitive argument completion", ! complete); ! el_set(el, EL_BIND, "^I", "ftp-complete", NULL); ! ! el_source(el, NULL); /* read ~/.editrc */ ! el_set(el, EL_SIGNAL, 1); ! } else if (!editing) { ! if (hist) { ! history_end(hist); ! hist = NULL; ! } ! if (el) { ! el_end(el); ! el = NULL; ! } ! } ! } ! #endif /* !SMALL */ ! ! /* ! * Wait for an asynchronous connect(2) attempt to finish. ! */ ! int ! connect_wait(int s) ! { ! struct pollfd pfd[1]; ! int error = 0; ! socklen_t len = sizeof(error); ! ! pfd[0].fd = s; ! pfd[0].events = POLLOUT; ! ! if (poll(pfd, 1, -1) == -1) ! return -1; ! if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) ! return -1; ! if (error != 0) { ! errno = error; ! return -1; ! } ! return 0; } --- 1,227 ---- /* ! * Copyright (c) 2015 Sunil Nimmagadda * ! * Permission to use, copy, modify, and distribute this software for any ! * purpose with or without fee is hereby granted, provided that the above ! * copyright notice and this permission notice appear in all copies. * ! * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ! * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ! * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ! * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ! * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ! * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ! * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ ! #include ! #include #include #include #include ! #include ! #include #include #include + #include #include #include ! #include #include ! #include "ftp.h" ! #include "xmalloc.h" ! static void tooslow(int); /* ! * Wait for an asynchronous connect(2) attempt to finish. */ ! int ! connect_wait(int s) { ! struct pollfd pfd[1]; ! int error = 0; ! socklen_t len = sizeof(error); ! pfd[0].fd = s; ! pfd[0].events = POLLOUT; ! if (poll(pfd, 1, -1) == -1) ! return -1; ! if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) ! return -1; ! if (error != 0) { ! errno = error; ! return -1; } + return 0; } ! static void ! tooslow(int signo) ! { ! dprintf(STDERR_FILENO, "%s: connect taking too long\n", getprogname()); ! _exit(2); ! } ! int ! tcp_connect(const char *host, const char *port, int timeout) { ! struct addrinfo hints, *res, *res0; ! char hbuf[NI_MAXHOST]; ! const char *cause = NULL; ! int error, s = -1, save_errno; ! if (host == NULL) { ! warnx("hostname missing"); ! return -1; } ! memset(&hints, 0, sizeof hints); ! hints.ai_family = family; ! hints.ai_socktype = SOCK_STREAM; ! if ((error = getaddrinfo(host, port, &hints, &res0))) { ! warnx("%s: %s", host, gai_strerror(error)); ! return -1; ! } ! if (timeout) { ! (void)signal(SIGALRM, tooslow); ! alarm(timeout); } ! for (res = res0; res; res = res->ai_next) { ! if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, ! sizeof hbuf, NULL, 0, NI_NUMERICHOST) != 0) ! (void)strlcpy(hbuf, "(unknown)", sizeof hbuf); ! log_info("Trying %s...\n", hbuf); ! s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); ! if (s == -1) { ! cause = "socket"; ! continue; } ! for (error = connect(s, res->ai_addr, res->ai_addrlen); ! error != 0 && errno == EINTR; error = connect_wait(s)) ! continue; ! if (error != 0) { ! cause = "connect"; ! save_errno = errno; ! close(s); ! errno = save_errno; ! s = -1; ! continue; } ! ! break; } ! freeaddrinfo(res0); ! if (s == -1) { ! warn("%s", cause); ! return -1; } ! if (timeout) { ! signal(SIGALRM, SIG_DFL); ! alarm(0); } ! return s; } int ! fd_request(char *path, int flags, off_t *offset) { ! struct imsg imsg; ! off_t *poffset; ! int fd, save_errno; ! send_message(&child_ibuf, IMSG_OPEN, flags, path, strlen(path) + 1, -1); ! if (read_message(&child_ibuf, &imsg) == 0) ! return -1; ! if (imsg.hdr.type != IMSG_OPEN) ! errx(1, "%s: IMSG_OPEN expected", __func__); ! fd = imsg.fd; ! if (offset) { ! poffset = imsg.data; ! *offset = *poffset; } ! save_errno = imsg.hdr.peerid; ! imsg_free(&imsg); ! errno = save_errno; ! return fd; } ! void ! send_message(struct imsgbuf *ibuf, int type, uint32_t peerid, ! void *msg, size_t msglen, int fd) { ! if (imsg_compose(ibuf, type, peerid, 0, fd, msg, msglen) != 1) ! err(1, "imsg_compose"); ! if (imsg_flush(ibuf) != 0) ! err(1, "imsg_flush"); } int ! read_message(struct imsgbuf *ibuf, struct imsg *imsg) { ! int n; ! if ((n = imsg_read(ibuf)) == -1) ! err(1, "%s: imsg_read", __func__); ! if (n == 0) ! return 0; ! if ((n = imsg_get(ibuf, imsg)) == -1) ! err(1, "%s: imsg_get", __func__); ! if (n == 0) return 0; ! return n; } void ! log_info(const char *fmt, ...) { ! va_list ap; ! if (verbose == 0) return; ! va_start(ap, fmt); ! vfprintf(msgout, fmt, ap); ! va_end(ap); } void ! copy_file(FILE *dst, FILE *src, off_t *offset) { ! char *tmp_buf; ! size_t r; ! tmp_buf = xmalloc(TMPBUF_LEN); ! while ((r = fread(tmp_buf, 1, TMPBUF_LEN, src)) != 0 && !interrupted) { ! *offset += r; ! if (fwrite(tmp_buf, 1, r, dst) != r) ! err(1, "%s: fwrite", __func__); } ! if (interrupted) { ! free(tmp_buf); ! return; } ! if (!feof(src)) ! errx(1, "%s: fread", __func__); ! free(tmp_buf); }