[BACK]Return to sftp.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/sftp.c, Revision 1.34.2.2

1.1       djm         1: /*
1.34.2.2! brad        2:  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
1.1       djm         3:  *
1.34.2.2! brad        4:  * Permission to use, copy, modify, and distribute this software for any
        !             5:  * purpose with or without fee is hereby granted, provided that the above
        !             6:  * copyright notice and this permission notice appear in all copies.
1.1       djm         7:  *
1.34.2.2! brad        8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !             9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       djm        15:  */
                     16:
                     17: #include "includes.h"
                     18:
1.34.2.2! brad       19: RCSID("$OpenBSD: sftp.c,v 1.44 2004/02/17 11:03:08 djm Exp $");
        !            20:
        !            21: #include <glob.h>
1.1       djm        22:
                     23: #include "buffer.h"
                     24: #include "xmalloc.h"
                     25: #include "log.h"
                     26: #include "pathnames.h"
1.16      mouring    27: #include "misc.h"
1.1       djm        28:
                     29: #include "sftp.h"
                     30: #include "sftp-common.h"
                     31: #include "sftp-client.h"
1.15      mouring    32:
1.34.2.2! brad       33: /* File to read commands from */
1.10      deraadt    34: FILE* infile;
1.34.2.2! brad       35:
        !            36: /* Are we in batchfile mode? */
        !            37: int batchmode = 0;
        !            38:
        !            39: /* Size of buffer used when copying files */
1.24      djm        40: size_t copy_buffer_len = 32768;
1.34.2.2! brad       41:
        !            42: /* Number of concurrent outstanding requests */
1.26      djm        43: size_t num_requests = 16;
1.34.2.2! brad       44:
        !            45: /* PID of ssh transport process */
1.34.2.1  brad       46: static pid_t sshpid = -1;
1.7       markus     47:
1.34.2.2! brad       48: /* This is set to 0 if the progressmeter is not desired. */
        !            49: int showprogress;
        !            50:
        !            51: int remote_glob(struct sftp_conn *, const char *, int,
        !            52:     int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
        !            53:
        !            54: /* Separators for interactive commands */
        !            55: #define WHITESPACE " \t\r\n"
        !            56:
        !            57: /* Define what type of ls view (0 - multi-column) */
        !            58: #define LONG_VIEW 1            /* Full view ala ls -l */
        !            59: #define SHORT_VIEW 2           /* Single row view ala ls -1 */
        !            60:
        !            61: /* Commands for interactive mode */
        !            62: #define I_CHDIR                1
        !            63: #define I_CHGRP                2
        !            64: #define I_CHMOD                3
        !            65: #define I_CHOWN                4
        !            66: #define I_GET          5
        !            67: #define I_HELP         6
        !            68: #define I_LCHDIR       7
        !            69: #define I_LLS          8
        !            70: #define I_LMKDIR       9
        !            71: #define I_LPWD         10
        !            72: #define I_LS           11
        !            73: #define I_LUMASK       12
        !            74: #define I_MKDIR                13
        !            75: #define I_PUT          14
        !            76: #define I_PWD          15
        !            77: #define I_QUIT         16
        !            78: #define I_RENAME       17
        !            79: #define I_RM           18
        !            80: #define I_RMDIR                19
        !            81: #define I_SHELL                20
        !            82: #define I_SYMLINK      21
        !            83: #define I_VERSION      22
        !            84: #define I_PROGRESS     23
        !            85:
        !            86: struct CMD {
        !            87:        const char *c;
        !            88:        const int n;
        !            89: };
        !            90:
        !            91: static const struct CMD cmds[] = {
        !            92:        { "bye",        I_QUIT },
        !            93:        { "cd",         I_CHDIR },
        !            94:        { "chdir",      I_CHDIR },
        !            95:        { "chgrp",      I_CHGRP },
        !            96:        { "chmod",      I_CHMOD },
        !            97:        { "chown",      I_CHOWN },
        !            98:        { "dir",        I_LS },
        !            99:        { "exit",       I_QUIT },
        !           100:        { "get",        I_GET },
        !           101:        { "mget",       I_GET },
        !           102:        { "help",       I_HELP },
        !           103:        { "lcd",        I_LCHDIR },
        !           104:        { "lchdir",     I_LCHDIR },
        !           105:        { "lls",        I_LLS },
        !           106:        { "lmkdir",     I_LMKDIR },
        !           107:        { "ln",         I_SYMLINK },
        !           108:        { "lpwd",       I_LPWD },
        !           109:        { "ls",         I_LS },
        !           110:        { "lumask",     I_LUMASK },
        !           111:        { "mkdir",      I_MKDIR },
        !           112:        { "progress",   I_PROGRESS },
        !           113:        { "put",        I_PUT },
        !           114:        { "mput",       I_PUT },
        !           115:        { "pwd",        I_PWD },
        !           116:        { "quit",       I_QUIT },
        !           117:        { "rename",     I_RENAME },
        !           118:        { "rm",         I_RM },
        !           119:        { "rmdir",      I_RMDIR },
        !           120:        { "symlink",    I_SYMLINK },
        !           121:        { "version",    I_VERSION },
        !           122:        { "!",          I_SHELL },
        !           123:        { "?",          I_HELP },
        !           124:        { NULL,                 -1}
        !           125: };
        !           126:
        !           127: int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
        !           128:
        !           129: static void
        !           130: help(void)
        !           131: {
        !           132:        printf("Available commands:\n");
        !           133:        printf("cd path                       Change remote directory to 'path'\n");
        !           134:        printf("lcd path                      Change local directory to 'path'\n");
        !           135:        printf("chgrp grp path                Change group of file 'path' to 'grp'\n");
        !           136:        printf("chmod mode path               Change permissions of file 'path' to 'mode'\n");
        !           137:        printf("chown own path                Change owner of file 'path' to 'own'\n");
        !           138:        printf("help                          Display this help text\n");
        !           139:        printf("get remote-path [local-path]  Download file\n");
        !           140:        printf("lls [ls-options [path]]       Display local directory listing\n");
        !           141:        printf("ln oldpath newpath            Symlink remote file\n");
        !           142:        printf("lmkdir path                   Create local directory\n");
        !           143:        printf("lpwd                          Print local working directory\n");
        !           144:        printf("ls [path]                     Display remote directory listing\n");
        !           145:        printf("lumask umask                  Set local umask to 'umask'\n");
        !           146:        printf("mkdir path                    Create remote directory\n");
        !           147:        printf("progress                      Toggle display of progress meter\n");
        !           148:        printf("put local-path [remote-path]  Upload file\n");
        !           149:        printf("pwd                           Display remote working directory\n");
        !           150:        printf("exit                          Quit sftp\n");
        !           151:        printf("quit                          Quit sftp\n");
        !           152:        printf("rename oldpath newpath        Rename remote file\n");
        !           153:        printf("rmdir path                    Remove remote directory\n");
        !           154:        printf("rm path                       Delete remote file\n");
        !           155:        printf("symlink oldpath newpath       Symlink remote file\n");
        !           156:        printf("version                       Show SFTP version\n");
        !           157:        printf("!command                      Execute 'command' in local shell\n");
        !           158:        printf("!                             Escape to local shell\n");
        !           159:        printf("?                             Synonym for help\n");
        !           160: }
        !           161:
        !           162: static void
        !           163: local_do_shell(const char *args)
        !           164: {
        !           165:        int status;
        !           166:        char *shell;
        !           167:        pid_t pid;
        !           168:
        !           169:        if (!*args)
        !           170:                args = NULL;
        !           171:
        !           172:        if ((shell = getenv("SHELL")) == NULL)
        !           173:                shell = _PATH_BSHELL;
        !           174:
        !           175:        if ((pid = fork()) == -1)
        !           176:                fatal("Couldn't fork: %s", strerror(errno));
        !           177:
        !           178:        if (pid == 0) {
        !           179:                /* XXX: child has pipe fds to ssh subproc open - issue? */
        !           180:                if (args) {
        !           181:                        debug3("Executing %s -c \"%s\"", shell, args);
        !           182:                        execl(shell, shell, "-c", args, (char *)NULL);
        !           183:                } else {
        !           184:                        debug3("Executing %s", shell);
        !           185:                        execl(shell, shell, (char *)NULL);
        !           186:                }
        !           187:                fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
        !           188:                    strerror(errno));
        !           189:                _exit(1);
        !           190:        }
        !           191:        while (waitpid(pid, &status, 0) == -1)
        !           192:                if (errno != EINTR)
        !           193:                        fatal("Couldn't wait for child: %s", strerror(errno));
        !           194:        if (!WIFEXITED(status))
        !           195:                error("Shell exited abormally");
        !           196:        else if (WEXITSTATUS(status))
        !           197:                error("Shell exited with status %d", WEXITSTATUS(status));
        !           198: }
        !           199:
        !           200: static void
        !           201: local_do_ls(const char *args)
        !           202: {
        !           203:        if (!args || !*args)
        !           204:                local_do_shell(_PATH_LS);
        !           205:        else {
        !           206:                int len = strlen(_PATH_LS " ") + strlen(args) + 1;
        !           207:                char *buf = xmalloc(len);
        !           208:
        !           209:                /* XXX: quoting - rip quoting code from ftp? */
        !           210:                snprintf(buf, len, _PATH_LS " %s", args);
        !           211:                local_do_shell(buf);
        !           212:                xfree(buf);
        !           213:        }
        !           214: }
        !           215:
        !           216: /* Strip one path (usually the pwd) from the start of another */
        !           217: static char *
        !           218: path_strip(char *path, char *strip)
        !           219: {
        !           220:        size_t len;
        !           221:
        !           222:        if (strip == NULL)
        !           223:                return (xstrdup(path));
        !           224:
        !           225:        len = strlen(strip);
        !           226:        if (strip != NULL && strncmp(path, strip, len) == 0) {
        !           227:                if (strip[len - 1] != '/' && path[len] == '/')
        !           228:                        len++;
        !           229:                return (xstrdup(path + len));
        !           230:        }
        !           231:
        !           232:        return (xstrdup(path));
        !           233: }
        !           234:
        !           235: static char *
        !           236: path_append(char *p1, char *p2)
        !           237: {
        !           238:        char *ret;
        !           239:        int len = strlen(p1) + strlen(p2) + 2;
        !           240:
        !           241:        ret = xmalloc(len);
        !           242:        strlcpy(ret, p1, len);
        !           243:        if (p1[strlen(p1) - 1] != '/')
        !           244:                strlcat(ret, "/", len);
        !           245:        strlcat(ret, p2, len);
        !           246:
        !           247:        return(ret);
        !           248: }
        !           249:
        !           250: static char *
        !           251: make_absolute(char *p, char *pwd)
        !           252: {
        !           253:        char *abs;
        !           254:
        !           255:        /* Derelativise */
        !           256:        if (p && p[0] != '/') {
        !           257:                abs = path_append(pwd, p);
        !           258:                xfree(p);
        !           259:                return(abs);
        !           260:        } else
        !           261:                return(p);
        !           262: }
        !           263:
        !           264: static int
        !           265: infer_path(const char *p, char **ifp)
        !           266: {
        !           267:        char *cp;
        !           268:
        !           269:        cp = strrchr(p, '/');
        !           270:        if (cp == NULL) {
        !           271:                *ifp = xstrdup(p);
        !           272:                return(0);
        !           273:        }
        !           274:
        !           275:        if (!cp[1]) {
        !           276:                error("Invalid path");
        !           277:                return(-1);
        !           278:        }
        !           279:
        !           280:        *ifp = xstrdup(cp + 1);
        !           281:        return(0);
        !           282: }
        !           283:
        !           284: static int
        !           285: parse_getput_flags(const char **cpp, int *pflag)
        !           286: {
        !           287:        const char *cp = *cpp;
        !           288:
        !           289:        /* Check for flags */
        !           290:        if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
        !           291:                switch (cp[1]) {
        !           292:                case 'p':
        !           293:                case 'P':
        !           294:                        *pflag = 1;
        !           295:                        break;
        !           296:                default:
        !           297:                        error("Invalid flag -%c", cp[1]);
        !           298:                        return(-1);
        !           299:                }
        !           300:                cp += 2;
        !           301:                *cpp = cp + strspn(cp, WHITESPACE);
        !           302:        }
        !           303:
        !           304:        return(0);
        !           305: }
        !           306:
        !           307: static int
        !           308: parse_ls_flags(const char **cpp, int *lflag)
        !           309: {
        !           310:        const char *cp = *cpp;
        !           311:
        !           312:        /* Check for flags */
        !           313:        if (cp++[0] == '-') {
        !           314:                for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
        !           315:                        switch (*cp) {
        !           316:                        case 'l':
        !           317:                                *lflag = LONG_VIEW;
        !           318:                                break;
        !           319:                        case '1':
        !           320:                                *lflag = SHORT_VIEW;
        !           321:                                break;
        !           322:                        default:
        !           323:                                error("Invalid flag -%c", *cp);
        !           324:                                return(-1);
        !           325:                        }
        !           326:                }
        !           327:                *cpp = cp + strspn(cp, WHITESPACE);
        !           328:        }
        !           329:
        !           330:        return(0);
        !           331: }
        !           332:
        !           333: static int
        !           334: get_pathname(const char **cpp, char **path)
        !           335: {
        !           336:        const char *cp = *cpp, *end;
        !           337:        char quot;
        !           338:        int i, j;
        !           339:
        !           340:        cp += strspn(cp, WHITESPACE);
        !           341:        if (!*cp) {
        !           342:                *cpp = cp;
        !           343:                *path = NULL;
        !           344:                return (0);
        !           345:        }
        !           346:
        !           347:        *path = xmalloc(strlen(cp) + 1);
        !           348:
        !           349:        /* Check for quoted filenames */
        !           350:        if (*cp == '\"' || *cp == '\'') {
        !           351:                quot = *cp++;
        !           352:
        !           353:                /* Search for terminating quote, unescape some chars */
        !           354:                for (i = j = 0; i <= strlen(cp); i++) {
        !           355:                        if (cp[i] == quot) {    /* Found quote */
        !           356:                                i++;
        !           357:                                (*path)[j] = '\0';
        !           358:                                break;
        !           359:                        }
        !           360:                        if (cp[i] == '\0') {    /* End of string */
        !           361:                                error("Unterminated quote");
        !           362:                                goto fail;
        !           363:                        }
        !           364:                        if (cp[i] == '\\') {    /* Escaped characters */
        !           365:                                i++;
        !           366:                                if (cp[i] != '\'' && cp[i] != '\"' &&
        !           367:                                    cp[i] != '\\') {
        !           368:                                        error("Bad escaped character '\%c'",
        !           369:                                            cp[i]);
        !           370:                                        goto fail;
        !           371:                                }
        !           372:                        }
        !           373:                        (*path)[j++] = cp[i];
        !           374:                }
        !           375:
        !           376:                if (j == 0) {
        !           377:                        error("Empty quotes");
        !           378:                        goto fail;
        !           379:                }
        !           380:                *cpp = cp + i + strspn(cp + i, WHITESPACE);
        !           381:        } else {
        !           382:                /* Read to end of filename */
        !           383:                end = strpbrk(cp, WHITESPACE);
        !           384:                if (end == NULL)
        !           385:                        end = strchr(cp, '\0');
        !           386:                *cpp = end + strspn(end, WHITESPACE);
        !           387:
        !           388:                memcpy(*path, cp, end - cp);
        !           389:                (*path)[end - cp] = '\0';
        !           390:        }
        !           391:        return (0);
        !           392:
        !           393:  fail:
        !           394:        xfree(*path);
        !           395:        *path = NULL;
        !           396:        return (-1);
        !           397: }
        !           398:
        !           399: static int
        !           400: is_dir(char *path)
        !           401: {
        !           402:        struct stat sb;
        !           403:
        !           404:        /* XXX: report errors? */
        !           405:        if (stat(path, &sb) == -1)
        !           406:                return(0);
        !           407:
        !           408:        return(sb.st_mode & S_IFDIR);
        !           409: }
        !           410:
        !           411: static int
        !           412: is_reg(char *path)
        !           413: {
        !           414:        struct stat sb;
        !           415:
        !           416:        if (stat(path, &sb) == -1)
        !           417:                fatal("stat %s: %s", path, strerror(errno));
        !           418:
        !           419:        return(S_ISREG(sb.st_mode));
        !           420: }
        !           421:
        !           422: static int
        !           423: remote_is_dir(struct sftp_conn *conn, char *path)
        !           424: {
        !           425:        Attrib *a;
        !           426:
        !           427:        /* XXX: report errors? */
        !           428:        if ((a = do_stat(conn, path, 1)) == NULL)
        !           429:                return(0);
        !           430:        if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
        !           431:                return(0);
        !           432:        return(a->perm & S_IFDIR);
        !           433: }
        !           434:
        !           435: static int
        !           436: process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
        !           437: {
        !           438:        char *abs_src = NULL;
        !           439:        char *abs_dst = NULL;
        !           440:        char *tmp;
        !           441:        glob_t g;
        !           442:        int err = 0;
        !           443:        int i;
        !           444:
        !           445:        abs_src = xstrdup(src);
        !           446:        abs_src = make_absolute(abs_src, pwd);
        !           447:
        !           448:        memset(&g, 0, sizeof(g));
        !           449:        debug3("Looking up %s", abs_src);
        !           450:        if (remote_glob(conn, abs_src, 0, NULL, &g)) {
        !           451:                error("File \"%s\" not found.", abs_src);
        !           452:                err = -1;
        !           453:                goto out;
        !           454:        }
        !           455:
        !           456:        /* If multiple matches, dst must be a directory or unspecified */
        !           457:        if (g.gl_matchc > 1 && dst && !is_dir(dst)) {
        !           458:                error("Multiple files match, but \"%s\" is not a directory",
        !           459:                    dst);
        !           460:                err = -1;
        !           461:                goto out;
        !           462:        }
        !           463:
        !           464:        for (i = 0; g.gl_pathv[i]; i++) {
        !           465:                if (infer_path(g.gl_pathv[i], &tmp)) {
        !           466:                        err = -1;
        !           467:                        goto out;
        !           468:                }
        !           469:
        !           470:                if (g.gl_matchc == 1 && dst) {
        !           471:                        /* If directory specified, append filename */
        !           472:                        if (is_dir(dst)) {
        !           473:                                if (infer_path(g.gl_pathv[0], &tmp)) {
        !           474:                                        err = 1;
        !           475:                                        goto out;
        !           476:                                }
        !           477:                                abs_dst = path_append(dst, tmp);
        !           478:                                xfree(tmp);
        !           479:                        } else
        !           480:                                abs_dst = xstrdup(dst);
        !           481:                } else if (dst) {
        !           482:                        abs_dst = path_append(dst, tmp);
        !           483:                        xfree(tmp);
        !           484:                } else
        !           485:                        abs_dst = tmp;
        !           486:
        !           487:                printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
        !           488:                if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
        !           489:                        err = -1;
        !           490:                xfree(abs_dst);
        !           491:                abs_dst = NULL;
        !           492:        }
        !           493:
        !           494: out:
        !           495:        xfree(abs_src);
        !           496:        if (abs_dst)
        !           497:                xfree(abs_dst);
        !           498:        globfree(&g);
        !           499:        return(err);
        !           500: }
        !           501:
        !           502: static int
        !           503: process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
        !           504: {
        !           505:        char *tmp_dst = NULL;
        !           506:        char *abs_dst = NULL;
        !           507:        char *tmp;
        !           508:        glob_t g;
        !           509:        int err = 0;
        !           510:        int i;
        !           511:
        !           512:        if (dst) {
        !           513:                tmp_dst = xstrdup(dst);
        !           514:                tmp_dst = make_absolute(tmp_dst, pwd);
        !           515:        }
        !           516:
        !           517:        memset(&g, 0, sizeof(g));
        !           518:        debug3("Looking up %s", src);
        !           519:        if (glob(src, 0, NULL, &g)) {
        !           520:                error("File \"%s\" not found.", src);
        !           521:                err = -1;
        !           522:                goto out;
        !           523:        }
        !           524:
        !           525:        /* If multiple matches, dst may be directory or unspecified */
        !           526:        if (g.gl_matchc > 1 && tmp_dst && !remote_is_dir(conn, tmp_dst)) {
        !           527:                error("Multiple files match, but \"%s\" is not a directory",
        !           528:                    tmp_dst);
        !           529:                err = -1;
        !           530:                goto out;
        !           531:        }
        !           532:
        !           533:        for (i = 0; g.gl_pathv[i]; i++) {
        !           534:                if (!is_reg(g.gl_pathv[i])) {
        !           535:                        error("skipping non-regular file %s",
        !           536:                            g.gl_pathv[i]);
        !           537:                        continue;
        !           538:                }
        !           539:                if (infer_path(g.gl_pathv[i], &tmp)) {
        !           540:                        err = -1;
        !           541:                        goto out;
        !           542:                }
        !           543:
        !           544:                if (g.gl_matchc == 1 && tmp_dst) {
        !           545:                        /* If directory specified, append filename */
        !           546:                        if (remote_is_dir(conn, tmp_dst)) {
        !           547:                                if (infer_path(g.gl_pathv[0], &tmp)) {
        !           548:                                        err = 1;
        !           549:                                        goto out;
        !           550:                                }
        !           551:                                abs_dst = path_append(tmp_dst, tmp);
        !           552:                                xfree(tmp);
        !           553:                        } else
        !           554:                                abs_dst = xstrdup(tmp_dst);
        !           555:
        !           556:                } else if (tmp_dst) {
        !           557:                        abs_dst = path_append(tmp_dst, tmp);
        !           558:                        xfree(tmp);
        !           559:                } else
        !           560:                        abs_dst = make_absolute(tmp, pwd);
        !           561:
        !           562:                printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
        !           563:                if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
        !           564:                        err = -1;
        !           565:        }
        !           566:
        !           567: out:
        !           568:        if (abs_dst)
        !           569:                xfree(abs_dst);
        !           570:        if (tmp_dst)
        !           571:                xfree(tmp_dst);
        !           572:        globfree(&g);
        !           573:        return(err);
        !           574: }
        !           575:
        !           576: static int
        !           577: sdirent_comp(const void *aa, const void *bb)
        !           578: {
        !           579:        SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
        !           580:        SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
        !           581:
        !           582:        return (strcmp(a->filename, b->filename));
        !           583: }
        !           584:
        !           585: /* sftp ls.1 replacement for directories */
        !           586: static int
        !           587: do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
        !           588: {
        !           589:        int n, c = 1, colspace = 0, columns = 1;
        !           590:        SFTP_DIRENT **d;
        !           591:
        !           592:        if ((n = do_readdir(conn, path, &d)) != 0)
        !           593:                return (n);
        !           594:
        !           595:        if (!(lflag & SHORT_VIEW)) {
        !           596:                int m = 0, width = 80;
        !           597:                struct winsize ws;
        !           598:                char *tmp;
        !           599:
        !           600:                /* Count entries for sort and find longest filename */
        !           601:                for (n = 0; d[n] != NULL; n++)
        !           602:                        m = MAX(m, strlen(d[n]->filename));
        !           603:
        !           604:                /* Add any subpath that also needs to be counted */
        !           605:                tmp = path_strip(path, strip_path);
        !           606:                m += strlen(tmp);
        !           607:                xfree(tmp);
        !           608:
        !           609:                if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
        !           610:                        width = ws.ws_col;
        !           611:
        !           612:                columns = width / (m + 2);
        !           613:                columns = MAX(columns, 1);
        !           614:                colspace = width / columns;
        !           615:                colspace = MIN(colspace, width);
        !           616:        }
        !           617:
        !           618:        qsort(d, n, sizeof(*d), sdirent_comp);
        !           619:
        !           620:        for (n = 0; d[n] != NULL; n++) {
        !           621:                char *tmp, *fname;
        !           622:
        !           623:                tmp = path_append(path, d[n]->filename);
        !           624:                fname = path_strip(tmp, strip_path);
        !           625:                xfree(tmp);
        !           626:
        !           627:                if (lflag & LONG_VIEW) {
        !           628:                        char *lname;
        !           629:                        struct stat sb;
        !           630:
        !           631:                        memset(&sb, 0, sizeof(sb));
        !           632:                        attrib_to_stat(&d[n]->a, &sb);
        !           633:                        lname = ls_file(fname, &sb, 1);
        !           634:                        printf("%s\n", lname);
        !           635:                        xfree(lname);
        !           636:                } else {
        !           637:                        printf("%-*s", colspace, fname);
        !           638:                        if (c >= columns) {
        !           639:                                printf("\n");
        !           640:                                c = 1;
        !           641:                        } else
        !           642:                                c++;
        !           643:                }
        !           644:
        !           645:                xfree(fname);
        !           646:        }
        !           647:
        !           648:        if (!(lflag & LONG_VIEW) && (c != 1))
        !           649:                printf("\n");
        !           650:
        !           651:        free_sftp_dirents(d);
        !           652:        return (0);
        !           653: }
        !           654:
        !           655: /* sftp ls.1 replacement which handles path globs */
        !           656: static int
        !           657: do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
        !           658:     int lflag)
        !           659: {
        !           660:        glob_t g;
        !           661:        int i, c = 1, colspace = 0, columns = 1;
        !           662:        Attrib *a;
        !           663:
        !           664:        memset(&g, 0, sizeof(g));
        !           665:
        !           666:        if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
        !           667:            NULL, &g)) {
        !           668:                error("Can't ls: \"%s\" not found", path);
        !           669:                return (-1);
        !           670:        }
        !           671:
        !           672:        /*
        !           673:         * If the glob returns a single match, which is the same as the
        !           674:         * input glob, and it is a directory, then just list its contents
        !           675:         */
        !           676:        if (g.gl_pathc == 1 &&
        !           677:            strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) {
        !           678:                if ((a = do_lstat(conn, path, 1)) == NULL) {
        !           679:                        globfree(&g);
        !           680:                        return (-1);
        !           681:                }
        !           682:                if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
        !           683:                    S_ISDIR(a->perm)) {
        !           684:                        globfree(&g);
        !           685:                        return (do_ls_dir(conn, path, strip_path, lflag));
        !           686:                }
        !           687:        }
        !           688:
        !           689:        if (!(lflag & SHORT_VIEW)) {
        !           690:                int m = 0, width = 80;
        !           691:                struct winsize ws;
        !           692:
        !           693:                /* Count entries for sort and find longest filename */
        !           694:                for (i = 0; g.gl_pathv[i]; i++)
        !           695:                        m = MAX(m, strlen(g.gl_pathv[i]));
        !           696:
        !           697:                if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
        !           698:                        width = ws.ws_col;
        !           699:
        !           700:                columns = width / (m + 2);
        !           701:                columns = MAX(columns, 1);
        !           702:                colspace = width / columns;
        !           703:        }
        !           704:
        !           705:        for (i = 0; g.gl_pathv[i]; i++) {
        !           706:                char *fname;
        !           707:
        !           708:                fname = path_strip(g.gl_pathv[i], strip_path);
        !           709:
        !           710:                if (lflag & LONG_VIEW) {
        !           711:                        char *lname;
        !           712:                        struct stat sb;
        !           713:
        !           714:                        /*
        !           715:                         * XXX: this is slow - 1 roundtrip per path
        !           716:                         * A solution to this is to fork glob() and
        !           717:                         * build a sftp specific version which keeps the
        !           718:                         * attribs (which currently get thrown away)
        !           719:                         * that the server returns as well as the filenames.
        !           720:                         */
        !           721:                        memset(&sb, 0, sizeof(sb));
        !           722:                        a = do_lstat(conn, g.gl_pathv[i], 1);
        !           723:                        if (a != NULL)
        !           724:                                attrib_to_stat(a, &sb);
        !           725:                        lname = ls_file(fname, &sb, 1);
        !           726:                        printf("%s\n", lname);
        !           727:                        xfree(lname);
        !           728:                } else {
        !           729:                        printf("%-*s", colspace, fname);
        !           730:                        if (c >= columns) {
        !           731:                                printf("\n");
        !           732:                                c = 1;
        !           733:                        } else
        !           734:                                c++;
        !           735:                }
        !           736:                xfree(fname);
        !           737:        }
        !           738:
        !           739:        if (!(lflag & LONG_VIEW) && (c != 1))
        !           740:                printf("\n");
        !           741:
        !           742:        if (g.gl_pathc)
        !           743:                globfree(&g);
        !           744:
        !           745:        return (0);
        !           746: }
        !           747:
        !           748: static int
        !           749: parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
        !           750:     unsigned long *n_arg, char **path1, char **path2)
        !           751: {
        !           752:        const char *cmd, *cp = *cpp;
        !           753:        char *cp2;
        !           754:        int base = 0;
        !           755:        long l;
        !           756:        int i, cmdnum;
        !           757:
        !           758:        /* Skip leading whitespace */
        !           759:        cp = cp + strspn(cp, WHITESPACE);
        !           760:
        !           761:        /* Ignore blank lines and lines which begin with comment '#' char */
        !           762:        if (*cp == '\0' || *cp == '#')
        !           763:                return (0);
        !           764:
        !           765:        /* Check for leading '-' (disable error processing) */
        !           766:        *iflag = 0;
        !           767:        if (*cp == '-') {
        !           768:                *iflag = 1;
        !           769:                cp++;
        !           770:        }
        !           771:
        !           772:        /* Figure out which command we have */
        !           773:        for (i = 0; cmds[i].c; i++) {
        !           774:                int cmdlen = strlen(cmds[i].c);
        !           775:
        !           776:                /* Check for command followed by whitespace */
        !           777:                if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
        !           778:                    strchr(WHITESPACE, cp[cmdlen])) {
        !           779:                        cp += cmdlen;
        !           780:                        cp = cp + strspn(cp, WHITESPACE);
        !           781:                        break;
        !           782:                }
        !           783:        }
        !           784:        cmdnum = cmds[i].n;
        !           785:        cmd = cmds[i].c;
        !           786:
        !           787:        /* Special case */
        !           788:        if (*cp == '!') {
        !           789:                cp++;
        !           790:                cmdnum = I_SHELL;
        !           791:        } else if (cmdnum == -1) {
        !           792:                error("Invalid command.");
        !           793:                return (-1);
        !           794:        }
        !           795:
        !           796:        /* Get arguments and parse flags */
        !           797:        *lflag = *pflag = *n_arg = 0;
        !           798:        *path1 = *path2 = NULL;
        !           799:        switch (cmdnum) {
        !           800:        case I_GET:
        !           801:        case I_PUT:
        !           802:                if (parse_getput_flags(&cp, pflag))
        !           803:                        return(-1);
        !           804:                /* Get first pathname (mandatory) */
        !           805:                if (get_pathname(&cp, path1))
        !           806:                        return(-1);
        !           807:                if (*path1 == NULL) {
        !           808:                        error("You must specify at least one path after a "
        !           809:                            "%s command.", cmd);
        !           810:                        return(-1);
        !           811:                }
        !           812:                /* Try to get second pathname (optional) */
        !           813:                if (get_pathname(&cp, path2))
        !           814:                        return(-1);
        !           815:                break;
        !           816:        case I_RENAME:
        !           817:        case I_SYMLINK:
        !           818:                if (get_pathname(&cp, path1))
        !           819:                        return(-1);
        !           820:                if (get_pathname(&cp, path2))
        !           821:                        return(-1);
        !           822:                if (!*path1 || !*path2) {
        !           823:                        error("You must specify two paths after a %s "
        !           824:                            "command.", cmd);
        !           825:                        return(-1);
        !           826:                }
        !           827:                break;
        !           828:        case I_RM:
        !           829:        case I_MKDIR:
        !           830:        case I_RMDIR:
        !           831:        case I_CHDIR:
        !           832:        case I_LCHDIR:
        !           833:        case I_LMKDIR:
        !           834:                /* Get pathname (mandatory) */
        !           835:                if (get_pathname(&cp, path1))
        !           836:                        return(-1);
        !           837:                if (*path1 == NULL) {
        !           838:                        error("You must specify a path after a %s command.",
        !           839:                            cmd);
        !           840:                        return(-1);
        !           841:                }
        !           842:                break;
        !           843:        case I_LS:
        !           844:                if (parse_ls_flags(&cp, lflag))
        !           845:                        return(-1);
        !           846:                /* Path is optional */
        !           847:                if (get_pathname(&cp, path1))
        !           848:                        return(-1);
        !           849:                break;
        !           850:        case I_LLS:
        !           851:        case I_SHELL:
        !           852:                /* Uses the rest of the line */
        !           853:                break;
        !           854:        case I_LUMASK:
        !           855:                base = 8;
        !           856:        case I_CHMOD:
        !           857:                base = 8;
        !           858:        case I_CHOWN:
        !           859:        case I_CHGRP:
        !           860:                /* Get numeric arg (mandatory) */
        !           861:                l = strtol(cp, &cp2, base);
        !           862:                if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
        !           863:                    errno == ERANGE) || l < 0) {
        !           864:                        error("You must supply a numeric argument "
        !           865:                            "to the %s command.", cmd);
        !           866:                        return(-1);
        !           867:                }
        !           868:                cp = cp2;
        !           869:                *n_arg = l;
        !           870:                if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
        !           871:                        break;
        !           872:                if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
        !           873:                        error("You must supply a numeric argument "
        !           874:                            "to the %s command.", cmd);
        !           875:                        return(-1);
        !           876:                }
        !           877:                cp += strspn(cp, WHITESPACE);
        !           878:
        !           879:                /* Get pathname (mandatory) */
        !           880:                if (get_pathname(&cp, path1))
        !           881:                        return(-1);
        !           882:                if (*path1 == NULL) {
        !           883:                        error("You must specify a path after a %s command.",
        !           884:                            cmd);
        !           885:                        return(-1);
        !           886:                }
        !           887:                break;
        !           888:        case I_QUIT:
        !           889:        case I_PWD:
        !           890:        case I_LPWD:
        !           891:        case I_HELP:
        !           892:        case I_VERSION:
        !           893:        case I_PROGRESS:
        !           894:                break;
        !           895:        default:
        !           896:                fatal("Command not implemented");
        !           897:        }
        !           898:
        !           899:        *cpp = cp;
        !           900:        return(cmdnum);
        !           901: }
        !           902:
        !           903: static int
        !           904: parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
        !           905:     int err_abort)
        !           906: {
        !           907:        char *path1, *path2, *tmp;
        !           908:        int pflag, lflag, iflag, cmdnum, i;
        !           909:        unsigned long n_arg;
        !           910:        Attrib a, *aa;
        !           911:        char path_buf[MAXPATHLEN];
        !           912:        int err = 0;
        !           913:        glob_t g;
        !           914:
        !           915:        path1 = path2 = NULL;
        !           916:        cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg,
        !           917:            &path1, &path2);
        !           918:
        !           919:        if (iflag != 0)
        !           920:                err_abort = 0;
        !           921:
        !           922:        memset(&g, 0, sizeof(g));
        !           923:
        !           924:        /* Perform command */
        !           925:        switch (cmdnum) {
        !           926:        case 0:
        !           927:                /* Blank line */
        !           928:                break;
        !           929:        case -1:
        !           930:                /* Unrecognized command */
        !           931:                err = -1;
        !           932:                break;
        !           933:        case I_GET:
        !           934:                err = process_get(conn, path1, path2, *pwd, pflag);
        !           935:                break;
        !           936:        case I_PUT:
        !           937:                err = process_put(conn, path1, path2, *pwd, pflag);
        !           938:                break;
        !           939:        case I_RENAME:
        !           940:                path1 = make_absolute(path1, *pwd);
        !           941:                path2 = make_absolute(path2, *pwd);
        !           942:                err = do_rename(conn, path1, path2);
        !           943:                break;
        !           944:        case I_SYMLINK:
        !           945:                path2 = make_absolute(path2, *pwd);
        !           946:                err = do_symlink(conn, path1, path2);
        !           947:                break;
        !           948:        case I_RM:
        !           949:                path1 = make_absolute(path1, *pwd);
        !           950:                remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
        !           951:                for (i = 0; g.gl_pathv[i]; i++) {
        !           952:                        printf("Removing %s\n", g.gl_pathv[i]);
        !           953:                        err = do_rm(conn, g.gl_pathv[i]);
        !           954:                        if (err != 0 && err_abort)
        !           955:                                break;
        !           956:                }
        !           957:                break;
        !           958:        case I_MKDIR:
        !           959:                path1 = make_absolute(path1, *pwd);
        !           960:                attrib_clear(&a);
        !           961:                a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
        !           962:                a.perm = 0777;
        !           963:                err = do_mkdir(conn, path1, &a);
        !           964:                break;
        !           965:        case I_RMDIR:
        !           966:                path1 = make_absolute(path1, *pwd);
        !           967:                err = do_rmdir(conn, path1);
        !           968:                break;
        !           969:        case I_CHDIR:
        !           970:                path1 = make_absolute(path1, *pwd);
        !           971:                if ((tmp = do_realpath(conn, path1)) == NULL) {
        !           972:                        err = 1;
        !           973:                        break;
        !           974:                }
        !           975:                if ((aa = do_stat(conn, tmp, 0)) == NULL) {
        !           976:                        xfree(tmp);
        !           977:                        err = 1;
        !           978:                        break;
        !           979:                }
        !           980:                if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
        !           981:                        error("Can't change directory: Can't check target");
        !           982:                        xfree(tmp);
        !           983:                        err = 1;
        !           984:                        break;
        !           985:                }
        !           986:                if (!S_ISDIR(aa->perm)) {
        !           987:                        error("Can't change directory: \"%s\" is not "
        !           988:                            "a directory", tmp);
        !           989:                        xfree(tmp);
        !           990:                        err = 1;
        !           991:                        break;
        !           992:                }
        !           993:                xfree(*pwd);
        !           994:                *pwd = tmp;
        !           995:                break;
        !           996:        case I_LS:
        !           997:                if (!path1) {
        !           998:                        do_globbed_ls(conn, *pwd, *pwd, lflag);
        !           999:                        break;
        !          1000:                }
        !          1001:
        !          1002:                /* Strip pwd off beginning of non-absolute paths */
        !          1003:                tmp = NULL;
        !          1004:                if (*path1 != '/')
        !          1005:                        tmp = *pwd;
        !          1006:
        !          1007:                path1 = make_absolute(path1, *pwd);
        !          1008:                err = do_globbed_ls(conn, path1, tmp, lflag);
        !          1009:                break;
        !          1010:        case I_LCHDIR:
        !          1011:                if (chdir(path1) == -1) {
        !          1012:                        error("Couldn't change local directory to "
        !          1013:                            "\"%s\": %s", path1, strerror(errno));
        !          1014:                        err = 1;
        !          1015:                }
        !          1016:                break;
        !          1017:        case I_LMKDIR:
        !          1018:                if (mkdir(path1, 0777) == -1) {
        !          1019:                        error("Couldn't create local directory "
        !          1020:                            "\"%s\": %s", path1, strerror(errno));
        !          1021:                        err = 1;
        !          1022:                }
        !          1023:                break;
        !          1024:        case I_LLS:
        !          1025:                local_do_ls(cmd);
        !          1026:                break;
        !          1027:        case I_SHELL:
        !          1028:                local_do_shell(cmd);
        !          1029:                break;
        !          1030:        case I_LUMASK:
        !          1031:                umask(n_arg);
        !          1032:                printf("Local umask: %03lo\n", n_arg);
        !          1033:                break;
        !          1034:        case I_CHMOD:
        !          1035:                path1 = make_absolute(path1, *pwd);
        !          1036:                attrib_clear(&a);
        !          1037:                a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
        !          1038:                a.perm = n_arg;
        !          1039:                remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
        !          1040:                for (i = 0; g.gl_pathv[i]; i++) {
        !          1041:                        printf("Changing mode on %s\n", g.gl_pathv[i]);
        !          1042:                        err = do_setstat(conn, g.gl_pathv[i], &a);
        !          1043:                        if (err != 0 && err_abort)
        !          1044:                                break;
        !          1045:                }
        !          1046:                break;
        !          1047:        case I_CHOWN:
        !          1048:        case I_CHGRP:
        !          1049:                path1 = make_absolute(path1, *pwd);
        !          1050:                remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
        !          1051:                for (i = 0; g.gl_pathv[i]; i++) {
        !          1052:                        if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
        !          1053:                                if (err != 0 && err_abort)
        !          1054:                                        break;
        !          1055:                                else
        !          1056:                                        continue;
        !          1057:                        }
        !          1058:                        if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
        !          1059:                                error("Can't get current ownership of "
        !          1060:                                    "remote file \"%s\"", g.gl_pathv[i]);
        !          1061:                                if (err != 0 && err_abort)
        !          1062:                                        break;
        !          1063:                                else
        !          1064:                                        continue;
        !          1065:                        }
        !          1066:                        aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
        !          1067:                        if (cmdnum == I_CHOWN) {
        !          1068:                                printf("Changing owner on %s\n", g.gl_pathv[i]);
        !          1069:                                aa->uid = n_arg;
        !          1070:                        } else {
        !          1071:                                printf("Changing group on %s\n", g.gl_pathv[i]);
        !          1072:                                aa->gid = n_arg;
        !          1073:                        }
        !          1074:                        err = do_setstat(conn, g.gl_pathv[i], aa);
        !          1075:                        if (err != 0 && err_abort)
        !          1076:                                break;
        !          1077:                }
        !          1078:                break;
        !          1079:        case I_PWD:
        !          1080:                printf("Remote working directory: %s\n", *pwd);
        !          1081:                break;
        !          1082:        case I_LPWD:
        !          1083:                if (!getcwd(path_buf, sizeof(path_buf))) {
        !          1084:                        error("Couldn't get local cwd: %s", strerror(errno));
        !          1085:                        err = -1;
        !          1086:                        break;
        !          1087:                }
        !          1088:                printf("Local working directory: %s\n", path_buf);
        !          1089:                break;
        !          1090:        case I_QUIT:
        !          1091:                /* Processed below */
        !          1092:                break;
        !          1093:        case I_HELP:
        !          1094:                help();
        !          1095:                break;
        !          1096:        case I_VERSION:
        !          1097:                printf("SFTP protocol version %u\n", sftp_proto_version(conn));
        !          1098:                break;
        !          1099:        case I_PROGRESS:
        !          1100:                showprogress = !showprogress;
        !          1101:                if (showprogress)
        !          1102:                        printf("Progress meter enabled\n");
        !          1103:                else
        !          1104:                        printf("Progress meter disabled\n");
        !          1105:                break;
        !          1106:        default:
        !          1107:                fatal("%d is not implemented", cmdnum);
        !          1108:        }
        !          1109:
        !          1110:        if (g.gl_pathc)
        !          1111:                globfree(&g);
        !          1112:        if (path1)
        !          1113:                xfree(path1);
        !          1114:        if (path2)
        !          1115:                xfree(path2);
        !          1116:
        !          1117:        /* If an unignored error occurs in batch mode we should abort. */
        !          1118:        if (err_abort && err != 0)
        !          1119:                return (-1);
        !          1120:        else if (cmdnum == I_QUIT)
        !          1121:                return (1);
        !          1122:
        !          1123:        return (0);
        !          1124: }
        !          1125:
        !          1126: int
        !          1127: interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
        !          1128: {
        !          1129:        char *pwd;
        !          1130:        char *dir = NULL;
        !          1131:        char cmd[2048];
        !          1132:        struct sftp_conn *conn;
        !          1133:        int err;
        !          1134:
        !          1135:        conn = do_init(fd_in, fd_out, copy_buffer_len, num_requests);
        !          1136:        if (conn == NULL)
        !          1137:                fatal("Couldn't initialise connection to server");
        !          1138:
        !          1139:        pwd = do_realpath(conn, ".");
        !          1140:        if (pwd == NULL)
        !          1141:                fatal("Need cwd");
        !          1142:
        !          1143:        if (file1 != NULL) {
        !          1144:                dir = xstrdup(file1);
        !          1145:                dir = make_absolute(dir, pwd);
        !          1146:
        !          1147:                if (remote_is_dir(conn, dir) && file2 == NULL) {
        !          1148:                        printf("Changing to: %s\n", dir);
        !          1149:                        snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
        !          1150:                        if (parse_dispatch_command(conn, cmd, &pwd, 1) != 0)
        !          1151:                                return (-1);
        !          1152:                } else {
        !          1153:                        if (file2 == NULL)
        !          1154:                                snprintf(cmd, sizeof cmd, "get %s", dir);
        !          1155:                        else
        !          1156:                                snprintf(cmd, sizeof cmd, "get %s %s", dir,
        !          1157:                                    file2);
        !          1158:
        !          1159:                        err = parse_dispatch_command(conn, cmd, &pwd, 1);
        !          1160:                        xfree(dir);
        !          1161:                        xfree(pwd);
        !          1162:                        return (err);
        !          1163:                }
        !          1164:                xfree(dir);
        !          1165:        }
        !          1166:
        !          1167:        setvbuf(stdout, NULL, _IOLBF, 0);
        !          1168:        setvbuf(infile, NULL, _IOLBF, 0);
        !          1169:
        !          1170:        err = 0;
        !          1171:        for (;;) {
        !          1172:                char *cp;
        !          1173:
        !          1174:                printf("sftp> ");
        !          1175:
        !          1176:                /* XXX: use libedit */
        !          1177:                if (fgets(cmd, sizeof(cmd), infile) == NULL) {
        !          1178:                        printf("\n");
        !          1179:                        break;
        !          1180:                }
        !          1181:
        !          1182:                if (batchmode) /* Echo command */
        !          1183:                        printf("%s", cmd);
        !          1184:
        !          1185:                cp = strrchr(cmd, '\n');
        !          1186:                if (cp)
        !          1187:                        *cp = '\0';
        !          1188:
        !          1189:                err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
        !          1190:                if (err != 0)
        !          1191:                        break;
        !          1192:        }
        !          1193:        xfree(pwd);
        !          1194:
        !          1195:        /* err == 1 signifies normal "quit" exit */
        !          1196:        return (err >= 0 ? 0 : -1);
        !          1197: }
1.34      fgsch    1198:
1.18      itojun   1199: static void
1.34.2.1  brad     1200: killchild(int signo)
                   1201: {
                   1202:        if (sshpid > 1)
                   1203:                kill(sshpid, signo);
                   1204:
                   1205:        _exit(1);
                   1206: }
                   1207:
                   1208: static void
                   1209: connect_to_server(char *path, char **args, int *in, int *out)
1.1       djm      1210: {
                   1211:        int c_in, c_out;
1.30      deraadt  1212:
1.1       djm      1213: #ifdef USE_PIPES
                   1214:        int pin[2], pout[2];
1.30      deraadt  1215:
1.1       djm      1216:        if ((pipe(pin) == -1) || (pipe(pout) == -1))
                   1217:                fatal("pipe: %s", strerror(errno));
                   1218:        *in = pin[0];
                   1219:        *out = pout[1];
                   1220:        c_in = pout[0];
                   1221:        c_out = pin[1];
                   1222: #else /* USE_PIPES */
                   1223:        int inout[2];
1.30      deraadt  1224:
1.1       djm      1225:        if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
                   1226:                fatal("socketpair: %s", strerror(errno));
                   1227:        *in = *out = inout[0];
                   1228:        c_in = c_out = inout[1];
                   1229: #endif /* USE_PIPES */
                   1230:
1.34.2.1  brad     1231:        if ((sshpid = fork()) == -1)
1.1       djm      1232:                fatal("fork: %s", strerror(errno));
1.34.2.1  brad     1233:        else if (sshpid == 0) {
1.1       djm      1234:                if ((dup2(c_in, STDIN_FILENO) == -1) ||
                   1235:                    (dup2(c_out, STDOUT_FILENO) == -1)) {
                   1236:                        fprintf(stderr, "dup2: %s\n", strerror(errno));
                   1237:                        exit(1);
                   1238:                }
                   1239:                close(*in);
                   1240:                close(*out);
                   1241:                close(c_in);
                   1242:                close(c_out);
1.23      djm      1243:                execv(path, args);
                   1244:                fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1.1       djm      1245:                exit(1);
                   1246:        }
                   1247:
1.34.2.1  brad     1248:        signal(SIGTERM, killchild);
                   1249:        signal(SIGINT, killchild);
                   1250:        signal(SIGHUP, killchild);
1.1       djm      1251:        close(c_in);
                   1252:        close(c_out);
                   1253: }
                   1254:
1.18      itojun   1255: static void
1.1       djm      1256: usage(void)
                   1257: {
1.25      mpech    1258:        extern char *__progname;
1.27      markus   1259:
1.19      stevesk  1260:        fprintf(stderr,
1.34.2.2! brad     1261:            "usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
        !          1262:            "            [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
        !          1263:            "            [-S program] [-s subsystem | sftp_server] host\n"
        !          1264:            "       %s [[user@]host[:file [file]]]\n"
        !          1265:            "       %s [[user@]host[:dir[/]]]\n"
        !          1266:            "       %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
1.1       djm      1267:        exit(1);
                   1268: }
                   1269:
1.2       stevesk  1270: int
1.1       djm      1271: main(int argc, char **argv)
                   1272: {
1.33      djm      1273:        int in, out, ch, err;
1.14      mouring  1274:        char *host, *userhost, *cp, *file2;
1.17      mouring  1275:        int debug_level = 0, sshver = 2;
                   1276:        char *file1 = NULL, *sftp_server = NULL;
1.23      djm      1277:        char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
1.17      mouring  1278:        LogLevel ll = SYSLOG_LEVEL_INFO;
                   1279:        arglist args;
1.3       djm      1280:        extern int optind;
                   1281:        extern char *optarg;
1.1       djm      1282:
1.17      mouring  1283:        args.list = NULL;
1.22      deraadt  1284:        addargs(&args, "ssh");          /* overwritten with ssh_program */
1.17      mouring  1285:        addargs(&args, "-oForwardX11 no");
                   1286:        addargs(&args, "-oForwardAgent no");
1.21      stevesk  1287:        addargs(&args, "-oClearAllForwardings yes");
1.34.2.2! brad     1288:
1.17      mouring  1289:        ll = SYSLOG_LEVEL_INFO;
1.34.2.2! brad     1290:        infile = stdin;
1.3       djm      1291:
1.26      djm      1292:        while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
1.3       djm      1293:                switch (ch) {
                   1294:                case 'C':
1.17      mouring  1295:                        addargs(&args, "-C");
1.3       djm      1296:                        break;
                   1297:                case 'v':
1.17      mouring  1298:                        if (debug_level < 3) {
                   1299:                                addargs(&args, "-v");
                   1300:                                ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
                   1301:                        }
                   1302:                        debug_level++;
1.3       djm      1303:                        break;
1.19      stevesk  1304:                case 'F':
1.3       djm      1305:                case 'o':
1.19      stevesk  1306:                        addargs(&args, "-%c%s", ch, optarg);
1.7       markus   1307:                        break;
                   1308:                case '1':
1.17      mouring  1309:                        sshver = 1;
1.7       markus   1310:                        if (sftp_server == NULL)
                   1311:                                sftp_server = _PATH_SFTP_SERVER;
                   1312:                        break;
                   1313:                case 's':
                   1314:                        sftp_server = optarg;
                   1315:                        break;
                   1316:                case 'S':
                   1317:                        ssh_program = optarg;
1.3       djm      1318:                        break;
1.10      deraadt  1319:                case 'b':
1.34.2.2! brad     1320:                        if (batchmode)
        !          1321:                                fatal("Batch file already specified.");
        !          1322:
        !          1323:                        /* Allow "-" as stdin */
        !          1324:                        if (strcmp(optarg, "-") != 0 &&
        !          1325:                           (infile = fopen(optarg, "r")) == NULL)
        !          1326:                                fatal("%s (%s).", strerror(errno), optarg);
1.34      fgsch    1327:                        showprogress = 0;
1.34.2.2! brad     1328:                        batchmode = 1;
1.10      deraadt  1329:                        break;
1.23      djm      1330:                case 'P':
                   1331:                        sftp_direct = optarg;
1.24      djm      1332:                        break;
                   1333:                case 'B':
                   1334:                        copy_buffer_len = strtol(optarg, &cp, 10);
                   1335:                        if (copy_buffer_len == 0 || *cp != '\0')
                   1336:                                fatal("Invalid buffer size \"%s\"", optarg);
1.26      djm      1337:                        break;
                   1338:                case 'R':
                   1339:                        num_requests = strtol(optarg, &cp, 10);
                   1340:                        if (num_requests == 0 || *cp != '\0')
1.27      markus   1341:                                fatal("Invalid number of requests \"%s\"",
1.26      djm      1342:                                    optarg);
1.23      djm      1343:                        break;
1.3       djm      1344:                case 'h':
                   1345:                default:
1.1       djm      1346:                        usage();
                   1347:                }
                   1348:        }
                   1349:
1.29      markus   1350:        log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
                   1351:
1.23      djm      1352:        if (sftp_direct == NULL) {
                   1353:                if (optind == argc || argc > (optind + 2))
                   1354:                        usage();
                   1355:
                   1356:                userhost = xstrdup(argv[optind]);
                   1357:                file2 = argv[optind+1];
1.1       djm      1358:
1.32      markus   1359:                if ((host = strrchr(userhost, '@')) == NULL)
1.23      djm      1360:                        host = userhost;
                   1361:                else {
                   1362:                        *host++ = '\0';
                   1363:                        if (!userhost[0]) {
                   1364:                                fprintf(stderr, "Missing username\n");
                   1365:                                usage();
                   1366:                        }
                   1367:                        addargs(&args, "-l%s",userhost);
                   1368:                }
1.3       djm      1369:
1.34.2.2! brad     1370:                if ((cp = colon(host)) != NULL) {
        !          1371:                        *cp++ = '\0';
        !          1372:                        file1 = cp;
        !          1373:                }
        !          1374:
1.23      djm      1375:                host = cleanhostname(host);
                   1376:                if (!*host) {
                   1377:                        fprintf(stderr, "Missing hostname\n");
1.1       djm      1378:                        usage();
                   1379:                }
                   1380:
1.23      djm      1381:                addargs(&args, "-oProtocol %d", sshver);
                   1382:
                   1383:                /* no subsystem if the server-spec contains a '/' */
                   1384:                if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
                   1385:                        addargs(&args, "-s");
                   1386:
                   1387:                addargs(&args, "%s", host);
1.27      markus   1388:                addargs(&args, "%s", (sftp_server != NULL ?
1.23      djm      1389:                    sftp_server : "sftp"));
                   1390:                args.list[0] = ssh_program;
                   1391:
1.34.2.2! brad     1392:                if (!batchmode)
        !          1393:                        fprintf(stderr, "Connecting to %s...\n", host);
1.34.2.1  brad     1394:                connect_to_server(ssh_program, args.list, &in, &out);
1.23      djm      1395:        } else {
                   1396:                args.list = NULL;
                   1397:                addargs(&args, "sftp-server");
                   1398:
1.34.2.2! brad     1399:                if (!batchmode)
        !          1400:                        fprintf(stderr, "Attaching to %s...\n", sftp_direct);
1.34.2.1  brad     1401:                connect_to_server(sftp_direct, args.list, &in, &out);
1.1       djm      1402:        }
                   1403:
1.33      djm      1404:        err = interactive_loop(in, out, file1, file2);
1.1       djm      1405:
                   1406:        close(in);
                   1407:        close(out);
1.34.2.2! brad     1408:        if (batchmode)
1.10      deraadt  1409:                fclose(infile);
1.1       djm      1410:
1.28      markus   1411:        while (waitpid(sshpid, NULL, 0) == -1)
                   1412:                if (errno != EINTR)
                   1413:                        fatal("Couldn't wait for ssh process: %s",
                   1414:                            strerror(errno));
1.1       djm      1415:
1.33      djm      1416:        exit(err == 0 ? 0 : 1);
1.1       djm      1417: }