summaryrefslogtreecommitdiff
path: root/sftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp.c')
-rw-r--r--sftp.c188
1 files changed, 136 insertions, 52 deletions
diff --git a/sftp.c b/sftp.c
index a47ccf5a2..f01c9194c 100644
--- a/sftp.c
+++ b/sftp.c
@@ -16,7 +16,7 @@
16 16
17#include "includes.h" 17#include "includes.h"
18 18
19RCSID("$OpenBSD: sftp.c,v 1.45 2004/03/03 09:31:20 djm Exp $"); 19RCSID("$OpenBSD: sftp.c,v 1.56 2004/07/11 17:48:47 deraadt Exp $");
20 20
21#include "buffer.h" 21#include "buffer.h"
22#include "xmalloc.h" 22#include "xmalloc.h"
@@ -46,21 +46,32 @@ static pid_t sshpid = -1;
46/* This is set to 0 if the progressmeter is not desired. */ 46/* This is set to 0 if the progressmeter is not desired. */
47int showprogress = 1; 47int showprogress = 1;
48 48
49/* SIGINT received during command processing */
50volatile sig_atomic_t interrupted = 0;
51
52/* I wish qsort() took a separate ctx for the comparison function...*/
53int sort_flag;
54
49int remote_glob(struct sftp_conn *, const char *, int, 55int remote_glob(struct sftp_conn *, const char *, int,
50 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ 56 int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
51 57
52#ifdef HAVE___PROGNAME
53extern char *__progname; 58extern char *__progname;
54#else
55char *__progname;
56#endif
57 59
58/* Separators for interactive commands */ 60/* Separators for interactive commands */
59#define WHITESPACE " \t\r\n" 61#define WHITESPACE " \t\r\n"
60 62
61/* Define what type of ls view (0 - multi-column) */ 63/* ls flags */
62#define LONG_VIEW 1 /* Full view ala ls -l */ 64#define LS_LONG_VIEW 0x01 /* Full view ala ls -l */
63#define SHORT_VIEW 2 /* Single row view ala ls -1 */ 65#define LS_SHORT_VIEW 0x02 /* Single row view ala ls -1 */
66#define LS_NUMERIC_VIEW 0x04 /* Long view with numeric uid/gid */
67#define LS_NAME_SORT 0x08 /* Sort by name (default) */
68#define LS_TIME_SORT 0x10 /* Sort by mtime */
69#define LS_SIZE_SORT 0x20 /* Sort by file size */
70#define LS_REVERSE_SORT 0x40 /* Reverse sort order */
71#define LS_SHOW_ALL 0x80 /* Don't skip filenames starting with '.' */
72
73#define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW)
74#define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
64 75
65/* Commands for interactive mode */ 76/* Commands for interactive mode */
66#define I_CHDIR 1 77#define I_CHDIR 1
@@ -131,6 +142,24 @@ static const struct CMD cmds[] = {
131int interactive_loop(int fd_in, int fd_out, char *file1, char *file2); 142int interactive_loop(int fd_in, int fd_out, char *file1, char *file2);
132 143
133static void 144static void
145killchild(int signo)
146{
147 if (sshpid > 1)
148 kill(sshpid, SIGTERM);
149
150 _exit(1);
151}
152
153static void
154cmd_interrupt(int signo)
155{
156 const char msg[] = "\rInterrupt \n";
157
158 write(STDERR_FILENO, msg, sizeof(msg) - 1);
159 interrupted = 1;
160}
161
162static void
134help(void) 163help(void)
135{ 164{
136 printf("Available commands:\n"); 165 printf("Available commands:\n");
@@ -254,13 +283,13 @@ path_append(char *p1, char *p2)
254static char * 283static char *
255make_absolute(char *p, char *pwd) 284make_absolute(char *p, char *pwd)
256{ 285{
257 char *abs; 286 char *abs_str;
258 287
259 /* Derelativise */ 288 /* Derelativise */
260 if (p && p[0] != '/') { 289 if (p && p[0] != '/') {
261 abs = path_append(pwd, p); 290 abs_str = path_append(pwd, p);
262 xfree(p); 291 xfree(p);
263 return(abs); 292 return(abs_str);
264 } else 293 } else
265 return(p); 294 return(p);
266} 295}
@@ -313,15 +342,41 @@ parse_ls_flags(const char **cpp, int *lflag)
313{ 342{
314 const char *cp = *cpp; 343 const char *cp = *cpp;
315 344
345 /* Defaults */
346 *lflag = LS_NAME_SORT;
347
316 /* Check for flags */ 348 /* Check for flags */
317 if (cp++[0] == '-') { 349 if (cp++[0] == '-') {
318 for(; strchr(WHITESPACE, *cp) == NULL; cp++) { 350 for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
319 switch (*cp) { 351 switch (*cp) {
320 case 'l': 352 case 'l':
321 *lflag = LONG_VIEW; 353 *lflag &= ~VIEW_FLAGS;
354 *lflag |= LS_LONG_VIEW;
322 break; 355 break;
323 case '1': 356 case '1':
324 *lflag = SHORT_VIEW; 357 *lflag &= ~VIEW_FLAGS;
358 *lflag |= LS_SHORT_VIEW;
359 break;
360 case 'n':
361 *lflag &= ~VIEW_FLAGS;
362 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
363 break;
364 case 'S':
365 *lflag &= ~SORT_FLAGS;
366 *lflag |= LS_SIZE_SORT;
367 break;
368 case 't':
369 *lflag &= ~SORT_FLAGS;
370 *lflag |= LS_TIME_SORT;
371 break;
372 case 'r':
373 *lflag |= LS_REVERSE_SORT;
374 break;
375 case 'f':
376 *lflag &= ~SORT_FLAGS;
377 break;
378 case 'a':
379 *lflag |= LS_SHOW_ALL;
325 break; 380 break;
326 default: 381 default:
327 error("Invalid flag -%c", *cp); 382 error("Invalid flag -%c", *cp);
@@ -369,7 +424,7 @@ get_pathname(const char **cpp, char **path)
369 i++; 424 i++;
370 if (cp[i] != '\'' && cp[i] != '\"' && 425 if (cp[i] != '\'' && cp[i] != '\"' &&
371 cp[i] != '\\') { 426 cp[i] != '\\') {
372 error("Bad escaped character '\%c'", 427 error("Bad escaped character '\\%c'",
373 cp[i]); 428 cp[i]);
374 goto fail; 429 goto fail;
375 } 430 }
@@ -465,7 +520,7 @@ process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
465 goto out; 520 goto out;
466 } 521 }
467 522
468 for (i = 0; g.gl_pathv[i]; i++) { 523 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
469 if (infer_path(g.gl_pathv[i], &tmp)) { 524 if (infer_path(g.gl_pathv[i], &tmp)) {
470 err = -1; 525 err = -1;
471 goto out; 526 goto out;
@@ -534,7 +589,7 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
534 goto out; 589 goto out;
535 } 590 }
536 591
537 for (i = 0; g.gl_pathv[i]; i++) { 592 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
538 if (!is_reg(g.gl_pathv[i])) { 593 if (!is_reg(g.gl_pathv[i])) {
539 error("skipping non-regular file %s", 594 error("skipping non-regular file %s",
540 g.gl_pathv[i]); 595 g.gl_pathv[i]);
@@ -582,8 +637,17 @@ sdirent_comp(const void *aa, const void *bb)
582{ 637{
583 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; 638 SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
584 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; 639 SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
640 int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
641
642#define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
643 if (sort_flag & LS_NAME_SORT)
644 return (rmul * strcmp(a->filename, b->filename));
645 else if (sort_flag & LS_TIME_SORT)
646 return (rmul * NCMP(a->a.mtime, b->a.mtime));
647 else if (sort_flag & LS_SIZE_SORT)
648 return (rmul * NCMP(a->a.size, b->a.size));
585 649
586 return (strcmp(a->filename, b->filename)); 650 fatal("Unknown ls sort type");
587} 651}
588 652
589/* sftp ls.1 replacement for directories */ 653/* sftp ls.1 replacement for directories */
@@ -596,14 +660,16 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
596 if ((n = do_readdir(conn, path, &d)) != 0) 660 if ((n = do_readdir(conn, path, &d)) != 0)
597 return (n); 661 return (n);
598 662
599 if (!(lflag & SHORT_VIEW)) { 663 if (!(lflag & LS_SHORT_VIEW)) {
600 int m = 0, width = 80; 664 int m = 0, width = 80;
601 struct winsize ws; 665 struct winsize ws;
602 char *tmp; 666 char *tmp;
603 667
604 /* Count entries for sort and find longest filename */ 668 /* Count entries for sort and find longest filename */
605 for (n = 0; d[n] != NULL; n++) 669 for (n = 0; d[n] != NULL; n++) {
606 m = MAX(m, strlen(d[n]->filename)); 670 if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
671 m = MAX(m, strlen(d[n]->filename));
672 }
607 673
608 /* Add any subpath that also needs to be counted */ 674 /* Add any subpath that also needs to be counted */
609 tmp = path_strip(path, strip_path); 675 tmp = path_strip(path, strip_path);
@@ -619,24 +685,33 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
619 colspace = MIN(colspace, width); 685 colspace = MIN(colspace, width);
620 } 686 }
621 687
622 qsort(d, n, sizeof(*d), sdirent_comp); 688 if (lflag & SORT_FLAGS) {
689 sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
690 qsort(d, n, sizeof(*d), sdirent_comp);
691 }
623 692
624 for (n = 0; d[n] != NULL; n++) { 693 for (n = 0; d[n] != NULL && !interrupted; n++) {
625 char *tmp, *fname; 694 char *tmp, *fname;
626 695
696 if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
697 continue;
698
627 tmp = path_append(path, d[n]->filename); 699 tmp = path_append(path, d[n]->filename);
628 fname = path_strip(tmp, strip_path); 700 fname = path_strip(tmp, strip_path);
629 xfree(tmp); 701 xfree(tmp);
630 702
631 if (lflag & LONG_VIEW) { 703 if (lflag & LS_LONG_VIEW) {
632 char *lname; 704 if (lflag & LS_NUMERIC_VIEW) {
633 struct stat sb; 705 char *lname;
706 struct stat sb;
634 707
635 memset(&sb, 0, sizeof(sb)); 708 memset(&sb, 0, sizeof(sb));
636 attrib_to_stat(&d[n]->a, &sb); 709 attrib_to_stat(&d[n]->a, &sb);
637 lname = ls_file(fname, &sb, 1); 710 lname = ls_file(fname, &sb, 1);
638 printf("%s\n", lname); 711 printf("%s\n", lname);
639 xfree(lname); 712 xfree(lname);
713 } else
714 printf("%s\n", d[n]->longname);
640 } else { 715 } else {
641 printf("%-*s", colspace, fname); 716 printf("%-*s", colspace, fname);
642 if (c >= columns) { 717 if (c >= columns) {
@@ -649,7 +724,7 @@ do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
649 xfree(fname); 724 xfree(fname);
650 } 725 }
651 726
652 if (!(lflag & LONG_VIEW) && (c != 1)) 727 if (!(lflag & LS_LONG_VIEW) && (c != 1))
653 printf("\n"); 728 printf("\n");
654 729
655 free_sftp_dirents(d); 730 free_sftp_dirents(d);
@@ -673,6 +748,9 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
673 return (-1); 748 return (-1);
674 } 749 }
675 750
751 if (interrupted)
752 goto out;
753
676 /* 754 /*
677 * If the glob returns a single match, which is the same as the 755 * If the glob returns a single match, which is the same as the
678 * input glob, and it is a directory, then just list its contents 756 * input glob, and it is a directory, then just list its contents
@@ -690,7 +768,7 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
690 } 768 }
691 } 769 }
692 770
693 if (!(lflag & SHORT_VIEW)) { 771 if (!(lflag & LS_SHORT_VIEW)) {
694 int m = 0, width = 80; 772 int m = 0, width = 80;
695 struct winsize ws; 773 struct winsize ws;
696 774
@@ -706,12 +784,12 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
706 colspace = width / columns; 784 colspace = width / columns;
707 } 785 }
708 786
709 for (i = 0; g.gl_pathv[i]; i++) { 787 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
710 char *fname; 788 char *fname;
711 789
712 fname = path_strip(g.gl_pathv[i], strip_path); 790 fname = path_strip(g.gl_pathv[i], strip_path);
713 791
714 if (lflag & LONG_VIEW) { 792 if (lflag & LS_LONG_VIEW) {
715 char *lname; 793 char *lname;
716 struct stat sb; 794 struct stat sb;
717 795
@@ -740,9 +818,10 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
740 xfree(fname); 818 xfree(fname);
741 } 819 }
742 820
743 if (!(lflag & LONG_VIEW) && (c != 1)) 821 if (!(lflag & LS_LONG_VIEW) && (c != 1))
744 printf("\n"); 822 printf("\n");
745 823
824 out:
746 if (g.gl_pathc) 825 if (g.gl_pathc)
747 globfree(&g); 826 globfree(&g);
748 827
@@ -952,7 +1031,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
952 case I_RM: 1031 case I_RM:
953 path1 = make_absolute(path1, *pwd); 1032 path1 = make_absolute(path1, *pwd);
954 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1033 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
955 for (i = 0; g.gl_pathv[i]; i++) { 1034 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
956 printf("Removing %s\n", g.gl_pathv[i]); 1035 printf("Removing %s\n", g.gl_pathv[i]);
957 err = do_rm(conn, g.gl_pathv[i]); 1036 err = do_rm(conn, g.gl_pathv[i]);
958 if (err != 0 && err_abort) 1037 if (err != 0 && err_abort)
@@ -1041,7 +1120,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1041 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; 1120 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1042 a.perm = n_arg; 1121 a.perm = n_arg;
1043 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1122 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1044 for (i = 0; g.gl_pathv[i]; i++) { 1123 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1045 printf("Changing mode on %s\n", g.gl_pathv[i]); 1124 printf("Changing mode on %s\n", g.gl_pathv[i]);
1046 err = do_setstat(conn, g.gl_pathv[i], &a); 1125 err = do_setstat(conn, g.gl_pathv[i], &a);
1047 if (err != 0 && err_abort) 1126 if (err != 0 && err_abort)
@@ -1052,7 +1131,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1052 case I_CHGRP: 1131 case I_CHGRP:
1053 path1 = make_absolute(path1, *pwd); 1132 path1 = make_absolute(path1, *pwd);
1054 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1133 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1055 for (i = 0; g.gl_pathv[i]; i++) { 1134 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1056 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1135 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1057 if (err != 0 && err_abort) 1136 if (err != 0 && err_abort)
1058 break; 1137 break;
@@ -1180,6 +1259,8 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1180 for (;;) { 1259 for (;;) {
1181 char *cp; 1260 char *cp;
1182 1261
1262 signal(SIGINT, SIG_IGN);
1263
1183 printf("sftp> "); 1264 printf("sftp> ");
1184 1265
1185 /* XXX: use libedit */ 1266 /* XXX: use libedit */
@@ -1195,6 +1276,10 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1195 if (cp) 1276 if (cp)
1196 *cp = '\0'; 1277 *cp = '\0';
1197 1278
1279 /* Handle user interrupts gracefully during commands */
1280 interrupted = 0;
1281 signal(SIGINT, cmd_interrupt);
1282
1198 err = parse_dispatch_command(conn, cmd, &pwd, batchmode); 1283 err = parse_dispatch_command(conn, cmd, &pwd, batchmode);
1199 if (err != 0) 1284 if (err != 0)
1200 break; 1285 break;
@@ -1206,15 +1291,6 @@ interactive_loop(int fd_in, int fd_out, char *file1, char *file2)
1206} 1291}
1207 1292
1208static void 1293static void
1209killchild(int signo)
1210{
1211 if (sshpid > 1)
1212 kill(sshpid, signo);
1213
1214 _exit(1);
1215}
1216
1217static void
1218connect_to_server(char *path, char **args, int *in, int *out) 1294connect_to_server(char *path, char **args, int *in, int *out)
1219{ 1295{
1220 int c_in, c_out; 1296 int c_in, c_out;
@@ -1243,15 +1319,23 @@ connect_to_server(char *path, char **args, int *in, int *out)
1243 if ((dup2(c_in, STDIN_FILENO) == -1) || 1319 if ((dup2(c_in, STDIN_FILENO) == -1) ||
1244 (dup2(c_out, STDOUT_FILENO) == -1)) { 1320 (dup2(c_out, STDOUT_FILENO) == -1)) {
1245 fprintf(stderr, "dup2: %s\n", strerror(errno)); 1321 fprintf(stderr, "dup2: %s\n", strerror(errno));
1246 exit(1); 1322 _exit(1);
1247 } 1323 }
1248 close(*in); 1324 close(*in);
1249 close(*out); 1325 close(*out);
1250 close(c_in); 1326 close(c_in);
1251 close(c_out); 1327 close(c_out);
1252 execv(path, args); 1328
1329 /*
1330 * The underlying ssh is in the same process group, so we must
1331 * ignore SIGINT if we want to gracefully abort commands,
1332 * otherwise the signal will make it to the ssh process and
1333 * kill it too
1334 */
1335 signal(SIGINT, SIG_IGN);
1336 execvp(path, args);
1253 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 1337 fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
1254 exit(1); 1338 _exit(1);
1255 } 1339 }
1256 1340
1257 signal(SIGTERM, killchild); 1341 signal(SIGTERM, killchild);
@@ -1280,7 +1364,7 @@ int
1280main(int argc, char **argv) 1364main(int argc, char **argv)
1281{ 1365{
1282 int in, out, ch, err; 1366 int in, out, ch, err;
1283 char *host, *userhost, *cp, *file2; 1367 char *host, *userhost, *cp, *file2 = NULL;
1284 int debug_level = 0, sshver = 2; 1368 int debug_level = 0, sshver = 2;
1285 char *file1 = NULL, *sftp_server = NULL; 1369 char *file1 = NULL, *sftp_server = NULL;
1286 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 1370 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
@@ -1331,7 +1415,7 @@ main(int argc, char **argv)
1331 fatal("Batch file already specified."); 1415 fatal("Batch file already specified.");
1332 1416
1333 /* Allow "-" as stdin */ 1417 /* Allow "-" as stdin */
1334 if (strcmp(optarg, "-") != 0 && 1418 if (strcmp(optarg, "-") != 0 &&
1335 (infile = fopen(optarg, "r")) == NULL) 1419 (infile = fopen(optarg, "r")) == NULL)
1336 fatal("%s (%s).", strerror(errno), optarg); 1420 fatal("%s (%s).", strerror(errno), optarg);
1337 showprogress = 0; 1421 showprogress = 0;