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