sftp.c revision 1.9 1 1.9 christos /* $NetBSD: sftp.c,v 1.9 2012/05/02 02:41:08 christos Exp $ */
2 1.9 christos /* $OpenBSD: sftp.c,v 1.134 2011/11/16 12:24:28 oga Exp $ */
3 1.1 christos /*
4 1.1 christos * Copyright (c) 2001-2004 Damien Miller <djm (at) openbsd.org>
5 1.1 christos *
6 1.1 christos * Permission to use, copy, modify, and distribute this software for any
7 1.1 christos * purpose with or without fee is hereby granted, provided that the above
8 1.1 christos * copyright notice and this permission notice appear in all copies.
9 1.1 christos *
10 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 christos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 christos */
18 1.1 christos
19 1.2 christos #include "includes.h"
20 1.9 christos __RCSID("$NetBSD: sftp.c,v 1.9 2012/05/02 02:41:08 christos Exp $");
21 1.1 christos #include <sys/types.h>
22 1.1 christos #include <sys/ioctl.h>
23 1.1 christos #include <sys/wait.h>
24 1.1 christos #include <sys/stat.h>
25 1.1 christos #include <sys/socket.h>
26 1.1 christos #include <sys/param.h>
27 1.1 christos #include <sys/statvfs.h>
28 1.1 christos
29 1.1 christos #include <ctype.h>
30 1.1 christos #include <errno.h>
31 1.1 christos #include <glob.h>
32 1.1 christos #include <histedit.h>
33 1.1 christos #include <paths.h>
34 1.4 adam #include <libgen.h>
35 1.1 christos #include <signal.h>
36 1.1 christos #include <stdlib.h>
37 1.1 christos #include <stdio.h>
38 1.1 christos #include <string.h>
39 1.1 christos #include <unistd.h>
40 1.1 christos #include <util.h>
41 1.1 christos #include <stdarg.h>
42 1.1 christos
43 1.1 christos #include "xmalloc.h"
44 1.1 christos #include "log.h"
45 1.1 christos #include "pathnames.h"
46 1.1 christos #include "misc.h"
47 1.1 christos
48 1.1 christos #include "sftp.h"
49 1.1 christos #include "buffer.h"
50 1.1 christos #include "sftp-common.h"
51 1.1 christos #include "sftp-client.h"
52 1.2 christos #include "fmt_scaled.h"
53 1.1 christos
54 1.4 adam #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
55 1.4 adam #define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */
56 1.4 adam
57 1.1 christos /* File to read commands from */
58 1.1 christos FILE* infile;
59 1.1 christos
60 1.1 christos /* Are we in batchfile mode? */
61 1.1 christos int batchmode = 0;
62 1.1 christos
63 1.1 christos /* PID of ssh transport process */
64 1.1 christos static pid_t sshpid = -1;
65 1.1 christos
66 1.1 christos /* This is set to 0 if the progressmeter is not desired. */
67 1.1 christos int showprogress = 1;
68 1.1 christos
69 1.4 adam /* When this option is set, we always recursively download/upload directories */
70 1.4 adam int global_rflag = 0;
71 1.4 adam
72 1.4 adam /* When this option is set, the file transfers will always preserve times */
73 1.4 adam int global_pflag = 0;
74 1.4 adam
75 1.1 christos /* SIGINT received during command processing */
76 1.1 christos volatile sig_atomic_t interrupted = 0;
77 1.1 christos
78 1.1 christos /* I wish qsort() took a separate ctx for the comparison function...*/
79 1.1 christos int sort_flag;
80 1.1 christos
81 1.4 adam /* Context used for commandline completion */
82 1.4 adam struct complete_ctx {
83 1.4 adam struct sftp_conn *conn;
84 1.4 adam char **remote_pathp;
85 1.4 adam };
86 1.4 adam
87 1.1 christos int remote_glob(struct sftp_conn *, const char *, int,
88 1.1 christos int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */
89 1.1 christos
90 1.1 christos /* Separators for interactive commands */
91 1.1 christos #define WHITESPACE " \t\r\n"
92 1.1 christos
93 1.1 christos /* ls flags */
94 1.4 adam #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */
95 1.4 adam #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */
96 1.4 adam #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */
97 1.4 adam #define LS_NAME_SORT 0x0008 /* Sort by name (default) */
98 1.4 adam #define LS_TIME_SORT 0x0010 /* Sort by mtime */
99 1.4 adam #define LS_SIZE_SORT 0x0020 /* Sort by file size */
100 1.4 adam #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */
101 1.4 adam #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */
102 1.4 adam #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */
103 1.1 christos
104 1.4 adam #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS)
105 1.1 christos #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT)
106 1.1 christos
107 1.1 christos /* Commands for interactive mode */
108 1.1 christos #define I_CHDIR 1
109 1.1 christos #define I_CHGRP 2
110 1.1 christos #define I_CHMOD 3
111 1.1 christos #define I_CHOWN 4
112 1.1 christos #define I_DF 24
113 1.1 christos #define I_GET 5
114 1.1 christos #define I_HELP 6
115 1.1 christos #define I_LCHDIR 7
116 1.7 christos #define I_LINK 25
117 1.1 christos #define I_LLS 8
118 1.1 christos #define I_LMKDIR 9
119 1.1 christos #define I_LPWD 10
120 1.1 christos #define I_LS 11
121 1.1 christos #define I_LUMASK 12
122 1.1 christos #define I_MKDIR 13
123 1.1 christos #define I_PUT 14
124 1.1 christos #define I_PWD 15
125 1.1 christos #define I_QUIT 16
126 1.1 christos #define I_RENAME 17
127 1.1 christos #define I_RM 18
128 1.1 christos #define I_RMDIR 19
129 1.1 christos #define I_SHELL 20
130 1.1 christos #define I_SYMLINK 21
131 1.1 christos #define I_VERSION 22
132 1.1 christos #define I_PROGRESS 23
133 1.1 christos
134 1.1 christos struct CMD {
135 1.1 christos const char *c;
136 1.1 christos const int n;
137 1.4 adam const int t;
138 1.1 christos };
139 1.1 christos
140 1.4 adam /* Type of completion */
141 1.4 adam #define NOARGS 0
142 1.4 adam #define REMOTE 1
143 1.4 adam #define LOCAL 2
144 1.4 adam
145 1.1 christos static const struct CMD cmds[] = {
146 1.4 adam { "bye", I_QUIT, NOARGS },
147 1.4 adam { "cd", I_CHDIR, REMOTE },
148 1.4 adam { "chdir", I_CHDIR, REMOTE },
149 1.4 adam { "chgrp", I_CHGRP, REMOTE },
150 1.4 adam { "chmod", I_CHMOD, REMOTE },
151 1.4 adam { "chown", I_CHOWN, REMOTE },
152 1.4 adam { "df", I_DF, REMOTE },
153 1.4 adam { "dir", I_LS, REMOTE },
154 1.4 adam { "exit", I_QUIT, NOARGS },
155 1.4 adam { "get", I_GET, REMOTE },
156 1.4 adam { "help", I_HELP, NOARGS },
157 1.4 adam { "lcd", I_LCHDIR, LOCAL },
158 1.4 adam { "lchdir", I_LCHDIR, LOCAL },
159 1.4 adam { "lls", I_LLS, LOCAL },
160 1.4 adam { "lmkdir", I_LMKDIR, LOCAL },
161 1.7 christos { "ln", I_LINK, REMOTE },
162 1.4 adam { "lpwd", I_LPWD, LOCAL },
163 1.4 adam { "ls", I_LS, REMOTE },
164 1.4 adam { "lumask", I_LUMASK, NOARGS },
165 1.4 adam { "mkdir", I_MKDIR, REMOTE },
166 1.4 adam { "mget", I_GET, REMOTE },
167 1.4 adam { "mput", I_PUT, LOCAL },
168 1.4 adam { "progress", I_PROGRESS, NOARGS },
169 1.4 adam { "put", I_PUT, LOCAL },
170 1.4 adam { "pwd", I_PWD, REMOTE },
171 1.4 adam { "quit", I_QUIT, NOARGS },
172 1.4 adam { "rename", I_RENAME, REMOTE },
173 1.4 adam { "rm", I_RM, REMOTE },
174 1.4 adam { "rmdir", I_RMDIR, REMOTE },
175 1.4 adam { "symlink", I_SYMLINK, REMOTE },
176 1.4 adam { "version", I_VERSION, NOARGS },
177 1.4 adam { "!", I_SHELL, NOARGS },
178 1.4 adam { "?", I_HELP, NOARGS },
179 1.4 adam { NULL, -1, -1 }
180 1.1 christos };
181 1.1 christos
182 1.7 christos int interactive_loop(struct sftp_conn *, const char *file1, const char *file2);
183 1.1 christos
184 1.1 christos /* ARGSUSED */
185 1.8 joerg __dead static void
186 1.1 christos killchild(int signo)
187 1.1 christos {
188 1.1 christos if (sshpid > 1) {
189 1.1 christos kill(sshpid, SIGTERM);
190 1.1 christos waitpid(sshpid, NULL, 0);
191 1.1 christos }
192 1.1 christos
193 1.1 christos _exit(1);
194 1.1 christos }
195 1.1 christos
196 1.1 christos /* ARGSUSED */
197 1.1 christos static void
198 1.1 christos cmd_interrupt(int signo)
199 1.1 christos {
200 1.1 christos const char msg[] = "\rInterrupt \n";
201 1.1 christos int olderrno = errno;
202 1.1 christos
203 1.1 christos write(STDERR_FILENO, msg, sizeof(msg) - 1);
204 1.1 christos interrupted = 1;
205 1.1 christos errno = olderrno;
206 1.1 christos }
207 1.1 christos
208 1.1 christos static void
209 1.1 christos help(void)
210 1.1 christos {
211 1.1 christos printf("Available commands:\n"
212 1.1 christos "bye Quit sftp\n"
213 1.1 christos "cd path Change remote directory to 'path'\n"
214 1.1 christos "chgrp grp path Change group of file 'path' to 'grp'\n"
215 1.1 christos "chmod mode path Change permissions of file 'path' to 'mode'\n"
216 1.1 christos "chown own path Change owner of file 'path' to 'own'\n"
217 1.1 christos "df [-hi] [path] Display statistics for current directory or\n"
218 1.1 christos " filesystem containing 'path'\n"
219 1.1 christos "exit Quit sftp\n"
220 1.4 adam "get [-Ppr] remote [local] Download file\n"
221 1.1 christos "help Display this help text\n"
222 1.1 christos "lcd path Change local directory to 'path'\n"
223 1.1 christos "lls [ls-options [path]] Display local directory listing\n"
224 1.1 christos "lmkdir path Create local directory\n"
225 1.7 christos "ln [-s] oldpath newpath Link remote file (-s for symlink)\n"
226 1.1 christos "lpwd Print local working directory\n"
227 1.4 adam "ls [-1afhlnrSt] [path] Display remote directory listing\n"
228 1.1 christos "lumask umask Set local umask to 'umask'\n"
229 1.1 christos "mkdir path Create remote directory\n"
230 1.1 christos "progress Toggle display of progress meter\n"
231 1.4 adam "put [-Ppr] local [remote] Upload file\n"
232 1.1 christos "pwd Display remote working directory\n"
233 1.1 christos "quit Quit sftp\n"
234 1.1 christos "rename oldpath newpath Rename remote file\n"
235 1.1 christos "rm path Delete remote file\n"
236 1.1 christos "rmdir path Remove remote directory\n"
237 1.1 christos "symlink oldpath newpath Symlink remote file\n"
238 1.1 christos "version Show SFTP version\n"
239 1.1 christos "!command Execute 'command' in local shell\n"
240 1.1 christos "! Escape to local shell\n"
241 1.1 christos "? Synonym for help\n");
242 1.1 christos }
243 1.1 christos
244 1.1 christos static void
245 1.1 christos local_do_shell(const char *args)
246 1.1 christos {
247 1.1 christos int status;
248 1.7 christos const char *shell;
249 1.1 christos pid_t pid;
250 1.1 christos
251 1.1 christos if (!*args)
252 1.1 christos args = NULL;
253 1.1 christos
254 1.7 christos if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
255 1.1 christos shell = _PATH_BSHELL;
256 1.1 christos
257 1.1 christos if ((pid = fork()) == -1)
258 1.1 christos fatal("Couldn't fork: %s", strerror(errno));
259 1.1 christos
260 1.1 christos if (pid == 0) {
261 1.1 christos /* XXX: child has pipe fds to ssh subproc open - issue? */
262 1.1 christos if (args) {
263 1.1 christos debug3("Executing %s -c \"%s\"", shell, args);
264 1.1 christos execl(shell, shell, "-c", args, (char *)NULL);
265 1.1 christos } else {
266 1.1 christos debug3("Executing %s", shell);
267 1.1 christos execl(shell, shell, (char *)NULL);
268 1.1 christos }
269 1.1 christos fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell,
270 1.1 christos strerror(errno));
271 1.1 christos _exit(1);
272 1.1 christos }
273 1.1 christos while (waitpid(pid, &status, 0) == -1)
274 1.1 christos if (errno != EINTR)
275 1.1 christos fatal("Couldn't wait for child: %s", strerror(errno));
276 1.1 christos if (!WIFEXITED(status))
277 1.1 christos error("Shell exited abnormally");
278 1.1 christos else if (WEXITSTATUS(status))
279 1.1 christos error("Shell exited with status %d", WEXITSTATUS(status));
280 1.1 christos }
281 1.1 christos
282 1.1 christos static void
283 1.1 christos local_do_ls(const char *args)
284 1.1 christos {
285 1.1 christos if (!args || !*args)
286 1.1 christos local_do_shell(_PATH_LS);
287 1.1 christos else {
288 1.1 christos int len = strlen(_PATH_LS " ") + strlen(args) + 1;
289 1.1 christos char *buf = xmalloc(len);
290 1.1 christos
291 1.1 christos /* XXX: quoting - rip quoting code from ftp? */
292 1.1 christos snprintf(buf, len, _PATH_LS " %s", args);
293 1.1 christos local_do_shell(buf);
294 1.1 christos xfree(buf);
295 1.1 christos }
296 1.1 christos }
297 1.1 christos
298 1.1 christos /* Strip one path (usually the pwd) from the start of another */
299 1.1 christos static char *
300 1.1 christos path_strip(char *path, char *strip)
301 1.1 christos {
302 1.1 christos size_t len;
303 1.1 christos
304 1.1 christos if (strip == NULL)
305 1.1 christos return (xstrdup(path));
306 1.1 christos
307 1.1 christos len = strlen(strip);
308 1.1 christos if (strncmp(path, strip, len) == 0) {
309 1.1 christos if (strip[len - 1] != '/' && path[len] == '/')
310 1.1 christos len++;
311 1.1 christos return (xstrdup(path + len));
312 1.1 christos }
313 1.1 christos
314 1.1 christos return (xstrdup(path));
315 1.1 christos }
316 1.1 christos
317 1.1 christos static char *
318 1.1 christos make_absolute(char *p, char *pwd)
319 1.1 christos {
320 1.1 christos char *abs_str;
321 1.1 christos
322 1.1 christos /* Derelativise */
323 1.1 christos if (p && p[0] != '/') {
324 1.1 christos abs_str = path_append(pwd, p);
325 1.1 christos xfree(p);
326 1.1 christos return(abs_str);
327 1.1 christos } else
328 1.1 christos return(p);
329 1.1 christos }
330 1.1 christos
331 1.1 christos static int
332 1.4 adam parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag,
333 1.4 adam int *rflag)
334 1.1 christos {
335 1.1 christos extern int opterr, optind, optopt, optreset;
336 1.1 christos int ch;
337 1.1 christos
338 1.1 christos optind = optreset = 1;
339 1.1 christos opterr = 0;
340 1.1 christos
341 1.4 adam *rflag = *pflag = 0;
342 1.4 adam while ((ch = getopt(argc, argv, "PpRr")) != -1) {
343 1.1 christos switch (ch) {
344 1.1 christos case 'p':
345 1.1 christos case 'P':
346 1.1 christos *pflag = 1;
347 1.1 christos break;
348 1.4 adam case 'r':
349 1.4 adam case 'R':
350 1.4 adam *rflag = 1;
351 1.4 adam break;
352 1.1 christos default:
353 1.1 christos error("%s: Invalid flag -%c", cmd, optopt);
354 1.1 christos return -1;
355 1.1 christos }
356 1.1 christos }
357 1.1 christos
358 1.1 christos return optind;
359 1.1 christos }
360 1.1 christos
361 1.1 christos static int
362 1.7 christos parse_link_flags(const char *cmd, char **argv, int argc, int *sflag)
363 1.7 christos {
364 1.7 christos extern int opterr, optind, optopt, optreset;
365 1.7 christos int ch;
366 1.7 christos
367 1.7 christos optind = optreset = 1;
368 1.7 christos opterr = 0;
369 1.7 christos
370 1.7 christos *sflag = 0;
371 1.7 christos while ((ch = getopt(argc, argv, "s")) != -1) {
372 1.7 christos switch (ch) {
373 1.7 christos case 's':
374 1.7 christos *sflag = 1;
375 1.7 christos break;
376 1.7 christos default:
377 1.7 christos error("%s: Invalid flag -%c", cmd, optopt);
378 1.7 christos return -1;
379 1.7 christos }
380 1.7 christos }
381 1.7 christos
382 1.7 christos return optind;
383 1.7 christos }
384 1.7 christos
385 1.7 christos static int
386 1.1 christos parse_ls_flags(char **argv, int argc, int *lflag)
387 1.1 christos {
388 1.1 christos extern int opterr, optind, optopt, optreset;
389 1.1 christos int ch;
390 1.1 christos
391 1.1 christos optind = optreset = 1;
392 1.1 christos opterr = 0;
393 1.1 christos
394 1.1 christos *lflag = LS_NAME_SORT;
395 1.4 adam while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) {
396 1.1 christos switch (ch) {
397 1.1 christos case '1':
398 1.1 christos *lflag &= ~VIEW_FLAGS;
399 1.1 christos *lflag |= LS_SHORT_VIEW;
400 1.1 christos break;
401 1.1 christos case 'S':
402 1.1 christos *lflag &= ~SORT_FLAGS;
403 1.1 christos *lflag |= LS_SIZE_SORT;
404 1.1 christos break;
405 1.1 christos case 'a':
406 1.1 christos *lflag |= LS_SHOW_ALL;
407 1.1 christos break;
408 1.1 christos case 'f':
409 1.1 christos *lflag &= ~SORT_FLAGS;
410 1.1 christos break;
411 1.4 adam case 'h':
412 1.4 adam *lflag |= LS_SI_UNITS;
413 1.4 adam break;
414 1.1 christos case 'l':
415 1.4 adam *lflag &= ~LS_SHORT_VIEW;
416 1.1 christos *lflag |= LS_LONG_VIEW;
417 1.1 christos break;
418 1.1 christos case 'n':
419 1.4 adam *lflag &= ~LS_SHORT_VIEW;
420 1.1 christos *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
421 1.1 christos break;
422 1.1 christos case 'r':
423 1.1 christos *lflag |= LS_REVERSE_SORT;
424 1.1 christos break;
425 1.1 christos case 't':
426 1.1 christos *lflag &= ~SORT_FLAGS;
427 1.1 christos *lflag |= LS_TIME_SORT;
428 1.1 christos break;
429 1.1 christos default:
430 1.1 christos error("ls: Invalid flag -%c", optopt);
431 1.1 christos return -1;
432 1.1 christos }
433 1.1 christos }
434 1.1 christos
435 1.1 christos return optind;
436 1.1 christos }
437 1.1 christos
438 1.1 christos static int
439 1.1 christos parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
440 1.1 christos {
441 1.1 christos extern int opterr, optind, optopt, optreset;
442 1.1 christos int ch;
443 1.1 christos
444 1.1 christos optind = optreset = 1;
445 1.1 christos opterr = 0;
446 1.1 christos
447 1.1 christos *hflag = *iflag = 0;
448 1.1 christos while ((ch = getopt(argc, argv, "hi")) != -1) {
449 1.1 christos switch (ch) {
450 1.1 christos case 'h':
451 1.1 christos *hflag = 1;
452 1.1 christos break;
453 1.1 christos case 'i':
454 1.1 christos *iflag = 1;
455 1.1 christos break;
456 1.1 christos default:
457 1.1 christos error("%s: Invalid flag -%c", cmd, optopt);
458 1.1 christos return -1;
459 1.1 christos }
460 1.1 christos }
461 1.1 christos
462 1.1 christos return optind;
463 1.1 christos }
464 1.1 christos
465 1.1 christos static int
466 1.1 christos is_dir(char *path)
467 1.1 christos {
468 1.1 christos struct stat sb;
469 1.1 christos
470 1.1 christos /* XXX: report errors? */
471 1.1 christos if (stat(path, &sb) == -1)
472 1.1 christos return(0);
473 1.1 christos
474 1.1 christos return(S_ISDIR(sb.st_mode));
475 1.1 christos }
476 1.1 christos
477 1.1 christos static int
478 1.1 christos remote_is_dir(struct sftp_conn *conn, char *path)
479 1.1 christos {
480 1.1 christos Attrib *a;
481 1.1 christos
482 1.1 christos /* XXX: report errors? */
483 1.1 christos if ((a = do_stat(conn, path, 1)) == NULL)
484 1.1 christos return(0);
485 1.1 christos if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
486 1.1 christos return(0);
487 1.1 christos return(S_ISDIR(a->perm));
488 1.1 christos }
489 1.1 christos
490 1.4 adam /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */
491 1.4 adam static int
492 1.4 adam pathname_is_dir(char *pathname)
493 1.4 adam {
494 1.4 adam size_t l = strlen(pathname);
495 1.4 adam
496 1.4 adam return l > 0 && pathname[l - 1] == '/';
497 1.4 adam }
498 1.4 adam
499 1.1 christos static int
500 1.4 adam process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd,
501 1.4 adam int pflag, int rflag)
502 1.1 christos {
503 1.1 christos char *abs_src = NULL;
504 1.1 christos char *abs_dst = NULL;
505 1.1 christos glob_t g;
506 1.4 adam char *filename, *tmp=NULL;
507 1.4 adam int i, err = 0;
508 1.1 christos
509 1.1 christos abs_src = xstrdup(src);
510 1.1 christos abs_src = make_absolute(abs_src, pwd);
511 1.4 adam memset(&g, 0, sizeof(g));
512 1.1 christos
513 1.1 christos debug3("Looking up %s", abs_src);
514 1.4 adam if (remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) {
515 1.1 christos error("File \"%s\" not found.", abs_src);
516 1.1 christos err = -1;
517 1.1 christos goto out;
518 1.1 christos }
519 1.1 christos
520 1.4 adam /*
521 1.4 adam * If multiple matches then dst must be a directory or
522 1.4 adam * unspecified.
523 1.4 adam */
524 1.4 adam if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) {
525 1.4 adam error("Multiple source paths, but destination "
526 1.4 adam "\"%s\" is not a directory", dst);
527 1.1 christos err = -1;
528 1.1 christos goto out;
529 1.1 christos }
530 1.1 christos
531 1.1 christos for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
532 1.4 adam tmp = xstrdup(g.gl_pathv[i]);
533 1.4 adam if ((filename = basename(tmp)) == NULL) {
534 1.4 adam error("basename %s: %s", tmp, strerror(errno));
535 1.4 adam xfree(tmp);
536 1.1 christos err = -1;
537 1.1 christos goto out;
538 1.1 christos }
539 1.1 christos
540 1.1 christos if (g.gl_matchc == 1 && dst) {
541 1.1 christos if (is_dir(dst)) {
542 1.4 adam abs_dst = path_append(dst, filename);
543 1.4 adam } else {
544 1.1 christos abs_dst = xstrdup(dst);
545 1.4 adam }
546 1.1 christos } else if (dst) {
547 1.4 adam abs_dst = path_append(dst, filename);
548 1.4 adam } else {
549 1.4 adam abs_dst = xstrdup(filename);
550 1.4 adam }
551 1.4 adam xfree(tmp);
552 1.1 christos
553 1.1 christos printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
554 1.4 adam if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
555 1.4 adam if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL,
556 1.4 adam pflag || global_pflag, 1) == -1)
557 1.4 adam err = -1;
558 1.4 adam } else {
559 1.4 adam if (do_download(conn, g.gl_pathv[i], abs_dst, NULL,
560 1.4 adam pflag || global_pflag) == -1)
561 1.4 adam err = -1;
562 1.4 adam }
563 1.1 christos xfree(abs_dst);
564 1.1 christos abs_dst = NULL;
565 1.1 christos }
566 1.1 christos
567 1.1 christos out:
568 1.1 christos xfree(abs_src);
569 1.1 christos globfree(&g);
570 1.1 christos return(err);
571 1.1 christos }
572 1.1 christos
573 1.1 christos static int
574 1.4 adam process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd,
575 1.4 adam int pflag, int rflag)
576 1.1 christos {
577 1.1 christos char *tmp_dst = NULL;
578 1.1 christos char *abs_dst = NULL;
579 1.4 adam char *tmp = NULL, *filename = NULL;
580 1.1 christos glob_t g;
581 1.1 christos int err = 0;
582 1.4 adam int i, dst_is_dir = 1;
583 1.1 christos struct stat sb;
584 1.1 christos
585 1.1 christos if (dst) {
586 1.1 christos tmp_dst = xstrdup(dst);
587 1.1 christos tmp_dst = make_absolute(tmp_dst, pwd);
588 1.1 christos }
589 1.1 christos
590 1.1 christos memset(&g, 0, sizeof(g));
591 1.1 christos debug3("Looking up %s", src);
592 1.4 adam if (glob(src, GLOB_NOCHECK | GLOB_LIMIT | GLOB_MARK, NULL, &g)) {
593 1.1 christos error("File \"%s\" not found.", src);
594 1.1 christos err = -1;
595 1.1 christos goto out;
596 1.1 christos }
597 1.1 christos
598 1.4 adam /* If we aren't fetching to pwd then stash this status for later */
599 1.4 adam if (tmp_dst != NULL)
600 1.4 adam dst_is_dir = remote_is_dir(conn, tmp_dst);
601 1.4 adam
602 1.1 christos /* If multiple matches, dst may be directory or unspecified */
603 1.4 adam if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) {
604 1.4 adam error("Multiple paths match, but destination "
605 1.4 adam "\"%s\" is not a directory", tmp_dst);
606 1.1 christos err = -1;
607 1.1 christos goto out;
608 1.1 christos }
609 1.1 christos
610 1.1 christos for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
611 1.1 christos if (stat(g.gl_pathv[i], &sb) == -1) {
612 1.1 christos err = -1;
613 1.1 christos error("stat %s: %s", g.gl_pathv[i], strerror(errno));
614 1.1 christos continue;
615 1.1 christos }
616 1.4 adam
617 1.4 adam tmp = xstrdup(g.gl_pathv[i]);
618 1.4 adam if ((filename = basename(tmp)) == NULL) {
619 1.4 adam error("basename %s: %s", tmp, strerror(errno));
620 1.4 adam xfree(tmp);
621 1.1 christos err = -1;
622 1.1 christos goto out;
623 1.1 christos }
624 1.1 christos
625 1.1 christos if (g.gl_matchc == 1 && tmp_dst) {
626 1.1 christos /* If directory specified, append filename */
627 1.4 adam if (dst_is_dir)
628 1.4 adam abs_dst = path_append(tmp_dst, filename);
629 1.4 adam else
630 1.1 christos abs_dst = xstrdup(tmp_dst);
631 1.1 christos } else if (tmp_dst) {
632 1.4 adam abs_dst = path_append(tmp_dst, filename);
633 1.4 adam } else {
634 1.4 adam abs_dst = make_absolute(xstrdup(filename), pwd);
635 1.4 adam }
636 1.4 adam xfree(tmp);
637 1.1 christos
638 1.1 christos printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
639 1.4 adam if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
640 1.4 adam if (upload_dir(conn, g.gl_pathv[i], abs_dst,
641 1.4 adam pflag || global_pflag, 1) == -1)
642 1.4 adam err = -1;
643 1.4 adam } else {
644 1.4 adam if (do_upload(conn, g.gl_pathv[i], abs_dst,
645 1.4 adam pflag || global_pflag) == -1)
646 1.4 adam err = -1;
647 1.4 adam }
648 1.1 christos }
649 1.1 christos
650 1.1 christos out:
651 1.1 christos if (abs_dst)
652 1.1 christos xfree(abs_dst);
653 1.1 christos if (tmp_dst)
654 1.1 christos xfree(tmp_dst);
655 1.1 christos globfree(&g);
656 1.1 christos return(err);
657 1.1 christos }
658 1.1 christos
659 1.1 christos static int
660 1.1 christos sdirent_comp(const void *aa, const void *bb)
661 1.1 christos {
662 1.7 christos const SFTP_DIRENT *a = *(const SFTP_DIRENT * const *)aa;
663 1.7 christos const SFTP_DIRENT *b = *(const SFTP_DIRENT * const *)bb;
664 1.1 christos int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1;
665 1.1 christos
666 1.1 christos #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1))
667 1.1 christos if (sort_flag & LS_NAME_SORT)
668 1.1 christos return (rmul * strcmp(a->filename, b->filename));
669 1.1 christos else if (sort_flag & LS_TIME_SORT)
670 1.1 christos return (rmul * NCMP(a->a.mtime, b->a.mtime));
671 1.1 christos else if (sort_flag & LS_SIZE_SORT)
672 1.1 christos return (rmul * NCMP(a->a.size, b->a.size));
673 1.1 christos
674 1.1 christos fatal("Unknown ls sort type");
675 1.2 christos /*NOTREACHED*/
676 1.2 christos return 0;
677 1.1 christos }
678 1.1 christos
679 1.1 christos /* sftp ls.1 replacement for directories */
680 1.1 christos static int
681 1.1 christos do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
682 1.1 christos {
683 1.1 christos int n;
684 1.1 christos u_int c = 1, colspace = 0, columns = 1;
685 1.1 christos SFTP_DIRENT **d;
686 1.1 christos
687 1.1 christos if ((n = do_readdir(conn, path, &d)) != 0)
688 1.1 christos return (n);
689 1.1 christos
690 1.1 christos if (!(lflag & LS_SHORT_VIEW)) {
691 1.1 christos u_int m = 0, width = 80;
692 1.1 christos struct winsize ws;
693 1.1 christos char *tmp;
694 1.1 christos
695 1.1 christos /* Count entries for sort and find longest filename */
696 1.1 christos for (n = 0; d[n] != NULL; n++) {
697 1.1 christos if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL))
698 1.1 christos m = MAX(m, strlen(d[n]->filename));
699 1.1 christos }
700 1.1 christos
701 1.1 christos /* Add any subpath that also needs to be counted */
702 1.1 christos tmp = path_strip(path, strip_path);
703 1.1 christos m += strlen(tmp);
704 1.1 christos xfree(tmp);
705 1.1 christos
706 1.1 christos if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
707 1.1 christos width = ws.ws_col;
708 1.1 christos
709 1.1 christos columns = width / (m + 2);
710 1.1 christos columns = MAX(columns, 1);
711 1.1 christos colspace = width / columns;
712 1.1 christos colspace = MIN(colspace, width);
713 1.1 christos }
714 1.1 christos
715 1.1 christos if (lflag & SORT_FLAGS) {
716 1.1 christos for (n = 0; d[n] != NULL; n++)
717 1.1 christos ; /* count entries */
718 1.1 christos sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT);
719 1.1 christos qsort(d, n, sizeof(*d), sdirent_comp);
720 1.1 christos }
721 1.1 christos
722 1.1 christos for (n = 0; d[n] != NULL && !interrupted; n++) {
723 1.1 christos char *tmp, *fname;
724 1.1 christos
725 1.1 christos if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL))
726 1.1 christos continue;
727 1.1 christos
728 1.1 christos tmp = path_append(path, d[n]->filename);
729 1.1 christos fname = path_strip(tmp, strip_path);
730 1.1 christos xfree(tmp);
731 1.1 christos
732 1.1 christos if (lflag & LS_LONG_VIEW) {
733 1.4 adam if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) {
734 1.1 christos char *lname;
735 1.1 christos struct stat sb;
736 1.1 christos
737 1.1 christos memset(&sb, 0, sizeof(sb));
738 1.1 christos attrib_to_stat(&d[n]->a, &sb);
739 1.4 adam lname = ls_file(fname, &sb, 1,
740 1.4 adam (lflag & LS_SI_UNITS));
741 1.1 christos printf("%s\n", lname);
742 1.1 christos xfree(lname);
743 1.1 christos } else
744 1.1 christos printf("%s\n", d[n]->longname);
745 1.1 christos } else {
746 1.1 christos printf("%-*s", colspace, fname);
747 1.1 christos if (c >= columns) {
748 1.1 christos printf("\n");
749 1.1 christos c = 1;
750 1.1 christos } else
751 1.1 christos c++;
752 1.1 christos }
753 1.1 christos
754 1.1 christos xfree(fname);
755 1.1 christos }
756 1.1 christos
757 1.1 christos if (!(lflag & LS_LONG_VIEW) && (c != 1))
758 1.1 christos printf("\n");
759 1.1 christos
760 1.1 christos free_sftp_dirents(d);
761 1.1 christos return (0);
762 1.1 christos }
763 1.1 christos
764 1.1 christos /* sftp ls.1 replacement which handles path globs */
765 1.1 christos static int
766 1.1 christos do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
767 1.1 christos int lflag)
768 1.1 christos {
769 1.7 christos Attrib *a = NULL;
770 1.7 christos char *fname, *lname;
771 1.1 christos glob_t g;
772 1.7 christos int err;
773 1.7 christos struct winsize ws;
774 1.7 christos u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80;
775 1.7 christos struct stat *stp;
776 1.7 christos #ifndef GLOB_KEEPSTAT
777 1.7 christos struct stat st;
778 1.7 christos #define GLOB_KEEPSTAT 0
779 1.7 christos #endif
780 1.1 christos
781 1.1 christos memset(&g, 0, sizeof(g));
782 1.1 christos
783 1.7 christos if (remote_glob(conn, path,
784 1.9 christos GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT,
785 1.9 christos NULL, &g) ||
786 1.7 christos (g.gl_pathc && !g.gl_matchc)) {
787 1.1 christos if (g.gl_pathc)
788 1.1 christos globfree(&g);
789 1.1 christos error("Can't ls: \"%s\" not found", path);
790 1.7 christos return -1;
791 1.1 christos }
792 1.1 christos
793 1.1 christos if (interrupted)
794 1.1 christos goto out;
795 1.1 christos
796 1.1 christos /*
797 1.1 christos * If the glob returns a single match and it is a directory,
798 1.1 christos * then just list its contents.
799 1.1 christos */
800 1.7 christos if (g.gl_matchc == 1 &&
801 1.7 christos #if GLOB_KEEPSTAT != 0
802 1.7 christos (stp = g.gl_statv[0]) != NULL &&
803 1.7 christos #else
804 1.7 christos lstat(g.gl_pathv[0], stp = &st) != -1 &&
805 1.7 christos #endif
806 1.7 christos S_ISDIR(stp->st_mode)) {
807 1.7 christos err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag);
808 1.7 christos globfree(&g);
809 1.7 christos return err;
810 1.7 christos }
811 1.1 christos
812 1.7 christos if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
813 1.7 christos width = ws.ws_col;
814 1.1 christos
815 1.1 christos if (!(lflag & LS_SHORT_VIEW)) {
816 1.1 christos /* Count entries for sort and find longest filename */
817 1.1 christos for (i = 0; g.gl_pathv[i]; i++)
818 1.1 christos m = MAX(m, strlen(g.gl_pathv[i]));
819 1.1 christos
820 1.1 christos columns = width / (m + 2);
821 1.1 christos columns = MAX(columns, 1);
822 1.1 christos colspace = width / columns;
823 1.1 christos }
824 1.1 christos
825 1.1 christos for (i = 0; g.gl_pathv[i] && !interrupted; i++, a = NULL) {
826 1.1 christos fname = path_strip(g.gl_pathv[i], strip_path);
827 1.1 christos if (lflag & LS_LONG_VIEW) {
828 1.7 christos #if GLOB_KEEPSTAT != 0
829 1.7 christos stp = g.gl_statv[i];
830 1.7 christos #else
831 1.7 christos if (lstat(g.gl_pathv[i], stp = &st) == -1)
832 1.7 christos stp = NULL;
833 1.7 christos #endif
834 1.7 christos if (stp == NULL) {
835 1.7 christos error("no stat information for %s", fname);
836 1.7 christos continue;
837 1.7 christos }
838 1.7 christos lname = ls_file(fname, stp, 1, (lflag & LS_SI_UNITS));
839 1.1 christos printf("%s\n", lname);
840 1.1 christos xfree(lname);
841 1.1 christos } else {
842 1.1 christos printf("%-*s", colspace, fname);
843 1.1 christos if (c >= columns) {
844 1.1 christos printf("\n");
845 1.1 christos c = 1;
846 1.1 christos } else
847 1.1 christos c++;
848 1.1 christos }
849 1.1 christos xfree(fname);
850 1.1 christos }
851 1.1 christos
852 1.1 christos if (!(lflag & LS_LONG_VIEW) && (c != 1))
853 1.1 christos printf("\n");
854 1.1 christos
855 1.1 christos out:
856 1.1 christos if (g.gl_pathc)
857 1.1 christos globfree(&g);
858 1.1 christos
859 1.7 christos return 0;
860 1.1 christos }
861 1.1 christos
862 1.1 christos static int
863 1.1 christos do_df(struct sftp_conn *conn, char *path, int hflag, int iflag)
864 1.1 christos {
865 1.1 christos struct sftp_statvfs st;
866 1.1 christos char s_used[FMT_SCALED_STRSIZE];
867 1.1 christos char s_avail[FMT_SCALED_STRSIZE];
868 1.1 christos char s_root[FMT_SCALED_STRSIZE];
869 1.1 christos char s_total[FMT_SCALED_STRSIZE];
870 1.4 adam unsigned long long ffree;
871 1.1 christos
872 1.1 christos if (do_statvfs(conn, path, &st, 1) == -1)
873 1.1 christos return -1;
874 1.1 christos if (iflag) {
875 1.4 adam ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0;
876 1.1 christos printf(" Inodes Used Avail "
877 1.1 christos "(root) %%Capacity\n");
878 1.1 christos printf("%11llu %11llu %11llu %11llu %3llu%%\n",
879 1.1 christos (unsigned long long)st.f_files,
880 1.1 christos (unsigned long long)(st.f_files - st.f_ffree),
881 1.1 christos (unsigned long long)st.f_favail,
882 1.4 adam (unsigned long long)st.f_ffree, ffree);
883 1.1 christos } else if (hflag) {
884 1.1 christos strlcpy(s_used, "error", sizeof(s_used));
885 1.1 christos strlcpy(s_avail, "error", sizeof(s_avail));
886 1.1 christos strlcpy(s_root, "error", sizeof(s_root));
887 1.1 christos strlcpy(s_total, "error", sizeof(s_total));
888 1.1 christos fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used);
889 1.1 christos fmt_scaled(st.f_bavail * st.f_frsize, s_avail);
890 1.1 christos fmt_scaled(st.f_bfree * st.f_frsize, s_root);
891 1.1 christos fmt_scaled(st.f_blocks * st.f_frsize, s_total);
892 1.1 christos printf(" Size Used Avail (root) %%Capacity\n");
893 1.1 christos printf("%7sB %7sB %7sB %7sB %3llu%%\n",
894 1.1 christos s_total, s_used, s_avail, s_root,
895 1.1 christos (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
896 1.1 christos st.f_blocks));
897 1.1 christos } else {
898 1.1 christos printf(" Size Used Avail "
899 1.1 christos "(root) %%Capacity\n");
900 1.1 christos printf("%12llu %12llu %12llu %12llu %3llu%%\n",
901 1.1 christos (unsigned long long)(st.f_frsize * st.f_blocks / 1024),
902 1.1 christos (unsigned long long)(st.f_frsize *
903 1.1 christos (st.f_blocks - st.f_bfree) / 1024),
904 1.1 christos (unsigned long long)(st.f_frsize * st.f_bavail / 1024),
905 1.1 christos (unsigned long long)(st.f_frsize * st.f_bfree / 1024),
906 1.1 christos (unsigned long long)(100 * (st.f_blocks - st.f_bfree) /
907 1.1 christos st.f_blocks));
908 1.1 christos }
909 1.1 christos return 0;
910 1.1 christos }
911 1.1 christos
912 1.1 christos /*
913 1.1 christos * Undo escaping of glob sequences in place. Used to undo extra escaping
914 1.1 christos * applied in makeargv() when the string is destined for a function that
915 1.1 christos * does not glob it.
916 1.1 christos */
917 1.1 christos static void
918 1.1 christos undo_glob_escape(char *s)
919 1.1 christos {
920 1.1 christos size_t i, j;
921 1.1 christos
922 1.1 christos for (i = j = 0;;) {
923 1.1 christos if (s[i] == '\0') {
924 1.1 christos s[j] = '\0';
925 1.1 christos return;
926 1.1 christos }
927 1.1 christos if (s[i] != '\\') {
928 1.1 christos s[j++] = s[i++];
929 1.1 christos continue;
930 1.1 christos }
931 1.1 christos /* s[i] == '\\' */
932 1.1 christos ++i;
933 1.1 christos switch (s[i]) {
934 1.1 christos case '?':
935 1.1 christos case '[':
936 1.1 christos case '*':
937 1.1 christos case '\\':
938 1.1 christos s[j++] = s[i++];
939 1.1 christos break;
940 1.1 christos case '\0':
941 1.1 christos s[j++] = '\\';
942 1.1 christos s[j] = '\0';
943 1.1 christos return;
944 1.1 christos default:
945 1.1 christos s[j++] = '\\';
946 1.1 christos s[j++] = s[i++];
947 1.1 christos break;
948 1.1 christos }
949 1.1 christos }
950 1.1 christos }
951 1.1 christos
952 1.1 christos /*
953 1.1 christos * Split a string into an argument vector using sh(1)-style quoting,
954 1.1 christos * comment and escaping rules, but with some tweaks to handle glob(3)
955 1.1 christos * wildcards.
956 1.4 adam * The "sloppy" flag allows for recovery from missing terminating quote, for
957 1.4 adam * use in parsing incomplete commandlines during tab autocompletion.
958 1.4 adam *
959 1.1 christos * Returns NULL on error or a NULL-terminated array of arguments.
960 1.4 adam *
961 1.4 adam * If "lastquote" is not NULL, the quoting character used for the last
962 1.4 adam * argument is placed in *lastquote ("\0", "'" or "\"").
963 1.4 adam *
964 1.4 adam * If "terminated" is not NULL, *terminated will be set to 1 when the
965 1.4 adam * last argument's quote has been properly terminated or 0 otherwise.
966 1.4 adam * This parameter is only of use if "sloppy" is set.
967 1.1 christos */
968 1.1 christos #define MAXARGS 128
969 1.1 christos #define MAXARGLEN 8192
970 1.1 christos static char **
971 1.4 adam makeargv(const char *arg, int *argcp, int sloppy, char *lastquote,
972 1.4 adam u_int *terminated)
973 1.1 christos {
974 1.1 christos int argc, quot;
975 1.1 christos size_t i, j;
976 1.1 christos static char argvs[MAXARGLEN];
977 1.1 christos static char *argv[MAXARGS + 1];
978 1.1 christos enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
979 1.1 christos
980 1.1 christos *argcp = argc = 0;
981 1.1 christos if (strlen(arg) > sizeof(argvs) - 1) {
982 1.1 christos args_too_longs:
983 1.1 christos error("string too long");
984 1.1 christos return NULL;
985 1.1 christos }
986 1.4 adam if (terminated != NULL)
987 1.4 adam *terminated = 1;
988 1.4 adam if (lastquote != NULL)
989 1.4 adam *lastquote = '\0';
990 1.1 christos state = MA_START;
991 1.1 christos i = j = 0;
992 1.1 christos for (;;) {
993 1.2 christos if (isspace((unsigned char)arg[i])) {
994 1.1 christos if (state == MA_UNQUOTED) {
995 1.1 christos /* Terminate current argument */
996 1.1 christos argvs[j++] = '\0';
997 1.1 christos argc++;
998 1.1 christos state = MA_START;
999 1.1 christos } else if (state != MA_START)
1000 1.1 christos argvs[j++] = arg[i];
1001 1.1 christos } else if (arg[i] == '"' || arg[i] == '\'') {
1002 1.1 christos q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
1003 1.1 christos if (state == MA_START) {
1004 1.1 christos argv[argc] = argvs + j;
1005 1.1 christos state = q;
1006 1.4 adam if (lastquote != NULL)
1007 1.4 adam *lastquote = arg[i];
1008 1.1 christos } else if (state == MA_UNQUOTED)
1009 1.1 christos state = q;
1010 1.1 christos else if (state == q)
1011 1.1 christos state = MA_UNQUOTED;
1012 1.1 christos else
1013 1.1 christos argvs[j++] = arg[i];
1014 1.1 christos } else if (arg[i] == '\\') {
1015 1.1 christos if (state == MA_SQUOTE || state == MA_DQUOTE) {
1016 1.1 christos quot = state == MA_SQUOTE ? '\'' : '"';
1017 1.1 christos /* Unescape quote we are in */
1018 1.1 christos /* XXX support \n and friends? */
1019 1.1 christos if (arg[i + 1] == quot) {
1020 1.1 christos i++;
1021 1.1 christos argvs[j++] = arg[i];
1022 1.1 christos } else if (arg[i + 1] == '?' ||
1023 1.1 christos arg[i + 1] == '[' || arg[i + 1] == '*') {
1024 1.1 christos /*
1025 1.1 christos * Special case for sftp: append
1026 1.1 christos * double-escaped glob sequence -
1027 1.1 christos * glob will undo one level of
1028 1.1 christos * escaping. NB. string can grow here.
1029 1.1 christos */
1030 1.1 christos if (j >= sizeof(argvs) - 5)
1031 1.1 christos goto args_too_longs;
1032 1.1 christos argvs[j++] = '\\';
1033 1.1 christos argvs[j++] = arg[i++];
1034 1.1 christos argvs[j++] = '\\';
1035 1.1 christos argvs[j++] = arg[i];
1036 1.1 christos } else {
1037 1.1 christos argvs[j++] = arg[i++];
1038 1.1 christos argvs[j++] = arg[i];
1039 1.1 christos }
1040 1.1 christos } else {
1041 1.1 christos if (state == MA_START) {
1042 1.1 christos argv[argc] = argvs + j;
1043 1.1 christos state = MA_UNQUOTED;
1044 1.4 adam if (lastquote != NULL)
1045 1.4 adam *lastquote = '\0';
1046 1.1 christos }
1047 1.1 christos if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
1048 1.1 christos arg[i + 1] == '*' || arg[i + 1] == '\\') {
1049 1.1 christos /*
1050 1.1 christos * Special case for sftp: append
1051 1.1 christos * escaped glob sequence -
1052 1.1 christos * glob will undo one level of
1053 1.1 christos * escaping.
1054 1.1 christos */
1055 1.1 christos argvs[j++] = arg[i++];
1056 1.1 christos argvs[j++] = arg[i];
1057 1.1 christos } else {
1058 1.1 christos /* Unescape everything */
1059 1.1 christos /* XXX support \n and friends? */
1060 1.1 christos i++;
1061 1.1 christos argvs[j++] = arg[i];
1062 1.1 christos }
1063 1.1 christos }
1064 1.1 christos } else if (arg[i] == '#') {
1065 1.1 christos if (state == MA_SQUOTE || state == MA_DQUOTE)
1066 1.1 christos argvs[j++] = arg[i];
1067 1.1 christos else
1068 1.1 christos goto string_done;
1069 1.1 christos } else if (arg[i] == '\0') {
1070 1.1 christos if (state == MA_SQUOTE || state == MA_DQUOTE) {
1071 1.4 adam if (sloppy) {
1072 1.4 adam state = MA_UNQUOTED;
1073 1.4 adam if (terminated != NULL)
1074 1.4 adam *terminated = 0;
1075 1.4 adam goto string_done;
1076 1.4 adam }
1077 1.1 christos error("Unterminated quoted argument");
1078 1.1 christos return NULL;
1079 1.1 christos }
1080 1.1 christos string_done:
1081 1.1 christos if (state == MA_UNQUOTED) {
1082 1.1 christos argvs[j++] = '\0';
1083 1.1 christos argc++;
1084 1.1 christos }
1085 1.1 christos break;
1086 1.1 christos } else {
1087 1.1 christos if (state == MA_START) {
1088 1.1 christos argv[argc] = argvs + j;
1089 1.1 christos state = MA_UNQUOTED;
1090 1.4 adam if (lastquote != NULL)
1091 1.4 adam *lastquote = '\0';
1092 1.1 christos }
1093 1.1 christos if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
1094 1.1 christos (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
1095 1.1 christos /*
1096 1.1 christos * Special case for sftp: escape quoted
1097 1.1 christos * glob(3) wildcards. NB. string can grow
1098 1.1 christos * here.
1099 1.1 christos */
1100 1.1 christos if (j >= sizeof(argvs) - 3)
1101 1.1 christos goto args_too_longs;
1102 1.1 christos argvs[j++] = '\\';
1103 1.1 christos argvs[j++] = arg[i];
1104 1.1 christos } else
1105 1.1 christos argvs[j++] = arg[i];
1106 1.1 christos }
1107 1.1 christos i++;
1108 1.1 christos }
1109 1.1 christos *argcp = argc;
1110 1.1 christos return argv;
1111 1.1 christos }
1112 1.1 christos
1113 1.1 christos static int
1114 1.4 adam parse_args(const char **cpp, int *pflag, int *rflag, int *lflag, int *iflag,
1115 1.7 christos int *hflag, int *sflag, unsigned long *n_arg, char **path1, char **path2)
1116 1.1 christos {
1117 1.1 christos const char *cmd, *cp = *cpp;
1118 1.1 christos char *cp2, **argv;
1119 1.1 christos int base = 0;
1120 1.1 christos long l;
1121 1.1 christos int i, cmdnum, optidx, argc;
1122 1.1 christos
1123 1.1 christos /* Skip leading whitespace */
1124 1.1 christos cp = cp + strspn(cp, WHITESPACE);
1125 1.1 christos
1126 1.1 christos /* Check for leading '-' (disable error processing) */
1127 1.1 christos *iflag = 0;
1128 1.1 christos if (*cp == '-') {
1129 1.1 christos *iflag = 1;
1130 1.1 christos cp++;
1131 1.4 adam cp = cp + strspn(cp, WHITESPACE);
1132 1.1 christos }
1133 1.1 christos
1134 1.4 adam /* Ignore blank lines and lines which begin with comment '#' char */
1135 1.4 adam if (*cp == '\0' || *cp == '#')
1136 1.4 adam return (0);
1137 1.4 adam
1138 1.4 adam if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL)
1139 1.1 christos return -1;
1140 1.1 christos
1141 1.1 christos /* Figure out which command we have */
1142 1.1 christos for (i = 0; cmds[i].c != NULL; i++) {
1143 1.1 christos if (strcasecmp(cmds[i].c, argv[0]) == 0)
1144 1.1 christos break;
1145 1.1 christos }
1146 1.1 christos cmdnum = cmds[i].n;
1147 1.1 christos cmd = cmds[i].c;
1148 1.1 christos
1149 1.1 christos /* Special case */
1150 1.1 christos if (*cp == '!') {
1151 1.1 christos cp++;
1152 1.1 christos cmdnum = I_SHELL;
1153 1.1 christos } else if (cmdnum == -1) {
1154 1.1 christos error("Invalid command.");
1155 1.1 christos return -1;
1156 1.1 christos }
1157 1.1 christos
1158 1.1 christos /* Get arguments and parse flags */
1159 1.4 adam *lflag = *pflag = *rflag = *hflag = *n_arg = 0;
1160 1.1 christos *path1 = *path2 = NULL;
1161 1.1 christos optidx = 1;
1162 1.1 christos switch (cmdnum) {
1163 1.1 christos case I_GET:
1164 1.1 christos case I_PUT:
1165 1.7 christos if ((optidx = parse_getput_flags(cmd, argv, argc,
1166 1.7 christos pflag, rflag)) == -1)
1167 1.1 christos return -1;
1168 1.1 christos /* Get first pathname (mandatory) */
1169 1.1 christos if (argc - optidx < 1) {
1170 1.1 christos error("You must specify at least one path after a "
1171 1.1 christos "%s command.", cmd);
1172 1.1 christos return -1;
1173 1.1 christos }
1174 1.1 christos *path1 = xstrdup(argv[optidx]);
1175 1.1 christos /* Get second pathname (optional) */
1176 1.1 christos if (argc - optidx > 1) {
1177 1.1 christos *path2 = xstrdup(argv[optidx + 1]);
1178 1.1 christos /* Destination is not globbed */
1179 1.1 christos undo_glob_escape(*path2);
1180 1.1 christos }
1181 1.1 christos break;
1182 1.7 christos case I_LINK:
1183 1.7 christos if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1)
1184 1.7 christos return -1;
1185 1.7 christos case I_SYMLINK:
1186 1.1 christos case I_RENAME:
1187 1.1 christos if (argc - optidx < 2) {
1188 1.1 christos error("You must specify two paths after a %s "
1189 1.1 christos "command.", cmd);
1190 1.1 christos return -1;
1191 1.1 christos }
1192 1.1 christos *path1 = xstrdup(argv[optidx]);
1193 1.1 christos *path2 = xstrdup(argv[optidx + 1]);
1194 1.1 christos /* Paths are not globbed */
1195 1.1 christos undo_glob_escape(*path1);
1196 1.1 christos undo_glob_escape(*path2);
1197 1.1 christos break;
1198 1.1 christos case I_RM:
1199 1.1 christos case I_MKDIR:
1200 1.1 christos case I_RMDIR:
1201 1.1 christos case I_CHDIR:
1202 1.1 christos case I_LCHDIR:
1203 1.1 christos case I_LMKDIR:
1204 1.1 christos /* Get pathname (mandatory) */
1205 1.1 christos if (argc - optidx < 1) {
1206 1.1 christos error("You must specify a path after a %s command.",
1207 1.1 christos cmd);
1208 1.1 christos return -1;
1209 1.1 christos }
1210 1.1 christos *path1 = xstrdup(argv[optidx]);
1211 1.1 christos /* Only "rm" globs */
1212 1.1 christos if (cmdnum != I_RM)
1213 1.1 christos undo_glob_escape(*path1);
1214 1.1 christos break;
1215 1.1 christos case I_DF:
1216 1.1 christos if ((optidx = parse_df_flags(cmd, argv, argc, hflag,
1217 1.1 christos iflag)) == -1)
1218 1.1 christos return -1;
1219 1.1 christos /* Default to current directory if no path specified */
1220 1.1 christos if (argc - optidx < 1)
1221 1.1 christos *path1 = NULL;
1222 1.1 christos else {
1223 1.1 christos *path1 = xstrdup(argv[optidx]);
1224 1.1 christos undo_glob_escape(*path1);
1225 1.1 christos }
1226 1.1 christos break;
1227 1.1 christos case I_LS:
1228 1.1 christos if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
1229 1.1 christos return(-1);
1230 1.1 christos /* Path is optional */
1231 1.1 christos if (argc - optidx > 0)
1232 1.1 christos *path1 = xstrdup(argv[optidx]);
1233 1.1 christos break;
1234 1.1 christos case I_LLS:
1235 1.1 christos /* Skip ls command and following whitespace */
1236 1.1 christos cp = cp + strlen(cmd) + strspn(cp, WHITESPACE);
1237 1.1 christos case I_SHELL:
1238 1.1 christos /* Uses the rest of the line */
1239 1.1 christos break;
1240 1.1 christos case I_LUMASK:
1241 1.1 christos case I_CHMOD:
1242 1.1 christos base = 8;
1243 1.1 christos case I_CHOWN:
1244 1.1 christos case I_CHGRP:
1245 1.1 christos /* Get numeric arg (mandatory) */
1246 1.1 christos if (argc - optidx < 1)
1247 1.1 christos goto need_num_arg;
1248 1.1 christos errno = 0;
1249 1.1 christos l = strtol(argv[optidx], &cp2, base);
1250 1.1 christos if (cp2 == argv[optidx] || *cp2 != '\0' ||
1251 1.1 christos ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1252 1.1 christos l < 0) {
1253 1.1 christos need_num_arg:
1254 1.1 christos error("You must supply a numeric argument "
1255 1.1 christos "to the %s command.", cmd);
1256 1.1 christos return -1;
1257 1.1 christos }
1258 1.1 christos *n_arg = l;
1259 1.1 christos if (cmdnum == I_LUMASK)
1260 1.1 christos break;
1261 1.1 christos /* Get pathname (mandatory) */
1262 1.1 christos if (argc - optidx < 2) {
1263 1.1 christos error("You must specify a path after a %s command.",
1264 1.1 christos cmd);
1265 1.1 christos return -1;
1266 1.1 christos }
1267 1.1 christos *path1 = xstrdup(argv[optidx + 1]);
1268 1.1 christos break;
1269 1.1 christos case I_QUIT:
1270 1.1 christos case I_PWD:
1271 1.1 christos case I_LPWD:
1272 1.1 christos case I_HELP:
1273 1.1 christos case I_VERSION:
1274 1.1 christos case I_PROGRESS:
1275 1.1 christos break;
1276 1.1 christos default:
1277 1.1 christos fatal("Command not implemented");
1278 1.1 christos }
1279 1.1 christos
1280 1.1 christos *cpp = cp;
1281 1.1 christos return(cmdnum);
1282 1.1 christos }
1283 1.1 christos
1284 1.1 christos static int
1285 1.1 christos parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1286 1.1 christos int err_abort)
1287 1.1 christos {
1288 1.1 christos char *path1, *path2, *tmp;
1289 1.7 christos int pflag = 0, rflag = 0, lflag = 0, iflag = 0, hflag = 0, sflag = 0;
1290 1.7 christos int cmdnum, i;
1291 1.1 christos unsigned long n_arg = 0;
1292 1.1 christos Attrib a, *aa;
1293 1.1 christos char path_buf[MAXPATHLEN];
1294 1.1 christos int err = 0;
1295 1.1 christos glob_t g;
1296 1.1 christos
1297 1.2 christos pflag = 0; /* XXX gcc */
1298 1.2 christos lflag = 0; /* XXX gcc */
1299 1.2 christos iflag = 0; /* XXX gcc */
1300 1.2 christos hflag = 0; /* XXX gcc */
1301 1.2 christos n_arg = 0; /* XXX gcc */
1302 1.2 christos
1303 1.1 christos path1 = path2 = NULL;
1304 1.7 christos cmdnum = parse_args(&cmd, &pflag, &rflag, &lflag, &iflag, &hflag,
1305 1.7 christos &sflag, &n_arg, &path1, &path2);
1306 1.1 christos
1307 1.1 christos if (iflag != 0)
1308 1.1 christos err_abort = 0;
1309 1.1 christos
1310 1.1 christos memset(&g, 0, sizeof(g));
1311 1.1 christos
1312 1.1 christos /* Perform command */
1313 1.1 christos switch (cmdnum) {
1314 1.1 christos case 0:
1315 1.1 christos /* Blank line */
1316 1.1 christos break;
1317 1.1 christos case -1:
1318 1.1 christos /* Unrecognized command */
1319 1.1 christos err = -1;
1320 1.1 christos break;
1321 1.1 christos case I_GET:
1322 1.4 adam err = process_get(conn, path1, path2, *pwd, pflag, rflag);
1323 1.1 christos break;
1324 1.1 christos case I_PUT:
1325 1.4 adam err = process_put(conn, path1, path2, *pwd, pflag, rflag);
1326 1.1 christos break;
1327 1.1 christos case I_RENAME:
1328 1.1 christos path1 = make_absolute(path1, *pwd);
1329 1.1 christos path2 = make_absolute(path2, *pwd);
1330 1.1 christos err = do_rename(conn, path1, path2);
1331 1.1 christos break;
1332 1.1 christos case I_SYMLINK:
1333 1.7 christos sflag = 1;
1334 1.7 christos case I_LINK:
1335 1.7 christos path1 = make_absolute(path1, *pwd);
1336 1.1 christos path2 = make_absolute(path2, *pwd);
1337 1.7 christos err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
1338 1.1 christos break;
1339 1.1 christos case I_RM:
1340 1.1 christos path1 = make_absolute(path1, *pwd);
1341 1.1 christos remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1342 1.1 christos for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1343 1.1 christos printf("Removing %s\n", g.gl_pathv[i]);
1344 1.1 christos err = do_rm(conn, g.gl_pathv[i]);
1345 1.1 christos if (err != 0 && err_abort)
1346 1.1 christos break;
1347 1.1 christos }
1348 1.1 christos break;
1349 1.1 christos case I_MKDIR:
1350 1.1 christos path1 = make_absolute(path1, *pwd);
1351 1.1 christos attrib_clear(&a);
1352 1.1 christos a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1353 1.1 christos a.perm = 0777;
1354 1.4 adam err = do_mkdir(conn, path1, &a, 1);
1355 1.1 christos break;
1356 1.1 christos case I_RMDIR:
1357 1.1 christos path1 = make_absolute(path1, *pwd);
1358 1.1 christos err = do_rmdir(conn, path1);
1359 1.1 christos break;
1360 1.1 christos case I_CHDIR:
1361 1.1 christos path1 = make_absolute(path1, *pwd);
1362 1.1 christos if ((tmp = do_realpath(conn, path1)) == NULL) {
1363 1.1 christos err = 1;
1364 1.1 christos break;
1365 1.1 christos }
1366 1.1 christos if ((aa = do_stat(conn, tmp, 0)) == NULL) {
1367 1.1 christos xfree(tmp);
1368 1.1 christos err = 1;
1369 1.1 christos break;
1370 1.1 christos }
1371 1.1 christos if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
1372 1.1 christos error("Can't change directory: Can't check target");
1373 1.1 christos xfree(tmp);
1374 1.1 christos err = 1;
1375 1.1 christos break;
1376 1.1 christos }
1377 1.1 christos if (!S_ISDIR(aa->perm)) {
1378 1.1 christos error("Can't change directory: \"%s\" is not "
1379 1.1 christos "a directory", tmp);
1380 1.1 christos xfree(tmp);
1381 1.1 christos err = 1;
1382 1.1 christos break;
1383 1.1 christos }
1384 1.1 christos xfree(*pwd);
1385 1.1 christos *pwd = tmp;
1386 1.1 christos break;
1387 1.1 christos case I_LS:
1388 1.1 christos if (!path1) {
1389 1.4 adam do_ls_dir(conn, *pwd, *pwd, lflag);
1390 1.1 christos break;
1391 1.1 christos }
1392 1.1 christos
1393 1.1 christos /* Strip pwd off beginning of non-absolute paths */
1394 1.1 christos tmp = NULL;
1395 1.1 christos if (*path1 != '/')
1396 1.1 christos tmp = *pwd;
1397 1.1 christos
1398 1.1 christos path1 = make_absolute(path1, *pwd);
1399 1.1 christos err = do_globbed_ls(conn, path1, tmp, lflag);
1400 1.1 christos break;
1401 1.1 christos case I_DF:
1402 1.1 christos /* Default to current directory if no path specified */
1403 1.1 christos if (path1 == NULL)
1404 1.1 christos path1 = xstrdup(*pwd);
1405 1.1 christos path1 = make_absolute(path1, *pwd);
1406 1.1 christos err = do_df(conn, path1, hflag, iflag);
1407 1.1 christos break;
1408 1.1 christos case I_LCHDIR:
1409 1.1 christos if (chdir(path1) == -1) {
1410 1.1 christos error("Couldn't change local directory to "
1411 1.1 christos "\"%s\": %s", path1, strerror(errno));
1412 1.1 christos err = 1;
1413 1.1 christos }
1414 1.1 christos break;
1415 1.1 christos case I_LMKDIR:
1416 1.1 christos if (mkdir(path1, 0777) == -1) {
1417 1.1 christos error("Couldn't create local directory "
1418 1.1 christos "\"%s\": %s", path1, strerror(errno));
1419 1.1 christos err = 1;
1420 1.1 christos }
1421 1.1 christos break;
1422 1.1 christos case I_LLS:
1423 1.1 christos local_do_ls(cmd);
1424 1.1 christos break;
1425 1.1 christos case I_SHELL:
1426 1.1 christos local_do_shell(cmd);
1427 1.1 christos break;
1428 1.1 christos case I_LUMASK:
1429 1.1 christos umask(n_arg);
1430 1.1 christos printf("Local umask: %03lo\n", n_arg);
1431 1.1 christos break;
1432 1.1 christos case I_CHMOD:
1433 1.1 christos path1 = make_absolute(path1, *pwd);
1434 1.1 christos attrib_clear(&a);
1435 1.1 christos a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
1436 1.1 christos a.perm = n_arg;
1437 1.1 christos remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1438 1.1 christos for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1439 1.1 christos printf("Changing mode on %s\n", g.gl_pathv[i]);
1440 1.1 christos err = do_setstat(conn, g.gl_pathv[i], &a);
1441 1.1 christos if (err != 0 && err_abort)
1442 1.1 christos break;
1443 1.1 christos }
1444 1.1 christos break;
1445 1.1 christos case I_CHOWN:
1446 1.1 christos case I_CHGRP:
1447 1.1 christos path1 = make_absolute(path1, *pwd);
1448 1.1 christos remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1449 1.1 christos for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1450 1.1 christos if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
1451 1.1 christos if (err_abort) {
1452 1.1 christos err = -1;
1453 1.1 christos break;
1454 1.1 christos } else
1455 1.1 christos continue;
1456 1.1 christos }
1457 1.1 christos if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
1458 1.1 christos error("Can't get current ownership of "
1459 1.1 christos "remote file \"%s\"", g.gl_pathv[i]);
1460 1.1 christos if (err_abort) {
1461 1.1 christos err = -1;
1462 1.1 christos break;
1463 1.1 christos } else
1464 1.1 christos continue;
1465 1.1 christos }
1466 1.1 christos aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
1467 1.1 christos if (cmdnum == I_CHOWN) {
1468 1.1 christos printf("Changing owner on %s\n", g.gl_pathv[i]);
1469 1.1 christos aa->uid = n_arg;
1470 1.1 christos } else {
1471 1.1 christos printf("Changing group on %s\n", g.gl_pathv[i]);
1472 1.1 christos aa->gid = n_arg;
1473 1.1 christos }
1474 1.1 christos err = do_setstat(conn, g.gl_pathv[i], aa);
1475 1.1 christos if (err != 0 && err_abort)
1476 1.1 christos break;
1477 1.1 christos }
1478 1.1 christos break;
1479 1.1 christos case I_PWD:
1480 1.1 christos printf("Remote working directory: %s\n", *pwd);
1481 1.1 christos break;
1482 1.1 christos case I_LPWD:
1483 1.1 christos if (!getcwd(path_buf, sizeof(path_buf))) {
1484 1.1 christos error("Couldn't get local cwd: %s", strerror(errno));
1485 1.1 christos err = -1;
1486 1.1 christos break;
1487 1.1 christos }
1488 1.1 christos printf("Local working directory: %s\n", path_buf);
1489 1.1 christos break;
1490 1.1 christos case I_QUIT:
1491 1.1 christos /* Processed below */
1492 1.1 christos break;
1493 1.1 christos case I_HELP:
1494 1.1 christos help();
1495 1.1 christos break;
1496 1.1 christos case I_VERSION:
1497 1.1 christos printf("SFTP protocol version %u\n", sftp_proto_version(conn));
1498 1.1 christos break;
1499 1.1 christos case I_PROGRESS:
1500 1.1 christos showprogress = !showprogress;
1501 1.1 christos if (showprogress)
1502 1.1 christos printf("Progress meter enabled\n");
1503 1.1 christos else
1504 1.1 christos printf("Progress meter disabled\n");
1505 1.1 christos break;
1506 1.1 christos default:
1507 1.1 christos fatal("%d is not implemented", cmdnum);
1508 1.1 christos }
1509 1.1 christos
1510 1.1 christos if (g.gl_pathc)
1511 1.1 christos globfree(&g);
1512 1.1 christos if (path1)
1513 1.1 christos xfree(path1);
1514 1.1 christos if (path2)
1515 1.1 christos xfree(path2);
1516 1.1 christos
1517 1.1 christos /* If an unignored error occurs in batch mode we should abort. */
1518 1.1 christos if (err_abort && err != 0)
1519 1.1 christos return (-1);
1520 1.1 christos else if (cmdnum == I_QUIT)
1521 1.1 christos return (1);
1522 1.1 christos
1523 1.1 christos return (0);
1524 1.1 christos }
1525 1.1 christos
1526 1.7 christos static const char *
1527 1.1 christos prompt(EditLine *el)
1528 1.1 christos {
1529 1.1 christos return ("sftp> ");
1530 1.1 christos }
1531 1.1 christos
1532 1.4 adam /* Display entries in 'list' after skipping the first 'len' chars */
1533 1.4 adam static void
1534 1.4 adam complete_display(char **list, u_int len)
1535 1.4 adam {
1536 1.4 adam u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen;
1537 1.4 adam struct winsize ws;
1538 1.7 christos const char *tmp;
1539 1.4 adam
1540 1.4 adam /* Count entries for sort and find longest */
1541 1.4 adam for (y = 0; list[y]; y++)
1542 1.4 adam m = MAX(m, strlen(list[y]));
1543 1.4 adam
1544 1.4 adam if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1)
1545 1.4 adam width = ws.ws_col;
1546 1.4 adam
1547 1.4 adam m = m > len ? m - len : 0;
1548 1.4 adam columns = width / (m + 2);
1549 1.4 adam columns = MAX(columns, 1);
1550 1.4 adam colspace = width / columns;
1551 1.4 adam colspace = MIN(colspace, width);
1552 1.4 adam
1553 1.4 adam printf("\n");
1554 1.4 adam m = 1;
1555 1.4 adam for (y = 0; list[y]; y++) {
1556 1.4 adam llen = strlen(list[y]);
1557 1.4 adam tmp = llen > len ? list[y] + len : "";
1558 1.4 adam printf("%-*s", colspace, tmp);
1559 1.4 adam if (m >= columns) {
1560 1.4 adam printf("\n");
1561 1.4 adam m = 1;
1562 1.4 adam } else
1563 1.4 adam m++;
1564 1.4 adam }
1565 1.4 adam printf("\n");
1566 1.4 adam }
1567 1.4 adam
1568 1.4 adam /*
1569 1.4 adam * Given a "list" of words that begin with a common prefix of "word",
1570 1.4 adam * attempt to find an autocompletion to extends "word" by the next
1571 1.4 adam * characters common to all entries in "list".
1572 1.4 adam */
1573 1.4 adam static char *
1574 1.4 adam complete_ambiguous(const char *word, char **list, size_t count)
1575 1.4 adam {
1576 1.4 adam if (word == NULL)
1577 1.4 adam return NULL;
1578 1.4 adam
1579 1.4 adam if (count > 0) {
1580 1.4 adam u_int y, matchlen = strlen(list[0]);
1581 1.4 adam
1582 1.4 adam /* Find length of common stem */
1583 1.4 adam for (y = 1; list[y]; y++) {
1584 1.4 adam u_int x;
1585 1.4 adam
1586 1.4 adam for (x = 0; x < matchlen; x++)
1587 1.4 adam if (list[0][x] != list[y][x])
1588 1.4 adam break;
1589 1.4 adam
1590 1.4 adam matchlen = x;
1591 1.4 adam }
1592 1.4 adam
1593 1.4 adam if (matchlen > strlen(word)) {
1594 1.4 adam char *tmp = xstrdup(list[0]);
1595 1.4 adam
1596 1.4 adam tmp[matchlen] = '\0';
1597 1.4 adam return tmp;
1598 1.4 adam }
1599 1.4 adam }
1600 1.4 adam
1601 1.4 adam return xstrdup(word);
1602 1.4 adam }
1603 1.4 adam
1604 1.4 adam /* Autocomplete a sftp command */
1605 1.4 adam static int
1606 1.4 adam complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote,
1607 1.4 adam int terminated)
1608 1.4 adam {
1609 1.4 adam u_int y, count = 0, cmdlen, tmplen;
1610 1.4 adam char *tmp, **list, argterm[3];
1611 1.4 adam const LineInfo *lf;
1612 1.4 adam
1613 1.4 adam list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *));
1614 1.4 adam
1615 1.4 adam /* No command specified: display all available commands */
1616 1.4 adam if (cmd == NULL) {
1617 1.4 adam for (y = 0; cmds[y].c; y++)
1618 1.4 adam list[count++] = xstrdup(cmds[y].c);
1619 1.4 adam
1620 1.4 adam list[count] = NULL;
1621 1.4 adam complete_display(list, 0);
1622 1.4 adam
1623 1.4 adam for (y = 0; list[y] != NULL; y++)
1624 1.4 adam xfree(list[y]);
1625 1.4 adam xfree(list);
1626 1.4 adam return count;
1627 1.4 adam }
1628 1.4 adam
1629 1.4 adam /* Prepare subset of commands that start with "cmd" */
1630 1.4 adam cmdlen = strlen(cmd);
1631 1.4 adam for (y = 0; cmds[y].c; y++) {
1632 1.4 adam if (!strncasecmp(cmd, cmds[y].c, cmdlen))
1633 1.4 adam list[count++] = xstrdup(cmds[y].c);
1634 1.4 adam }
1635 1.4 adam list[count] = NULL;
1636 1.4 adam
1637 1.9 christos if (count == 0) {
1638 1.9 christos xfree(list);
1639 1.4 adam return 0;
1640 1.9 christos }
1641 1.4 adam
1642 1.4 adam /* Complete ambigious command */
1643 1.4 adam tmp = complete_ambiguous(cmd, list, count);
1644 1.4 adam if (count > 1)
1645 1.4 adam complete_display(list, 0);
1646 1.4 adam
1647 1.4 adam for (y = 0; list[y]; y++)
1648 1.4 adam xfree(list[y]);
1649 1.4 adam xfree(list);
1650 1.4 adam
1651 1.4 adam if (tmp != NULL) {
1652 1.4 adam tmplen = strlen(tmp);
1653 1.4 adam cmdlen = strlen(cmd);
1654 1.4 adam /* If cmd may be extended then do so */
1655 1.4 adam if (tmplen > cmdlen)
1656 1.4 adam if (el_insertstr(el, tmp + cmdlen) == -1)
1657 1.4 adam fatal("el_insertstr failed.");
1658 1.4 adam lf = el_line(el);
1659 1.4 adam /* Terminate argument cleanly */
1660 1.4 adam if (count == 1) {
1661 1.4 adam y = 0;
1662 1.4 adam if (!terminated)
1663 1.4 adam argterm[y++] = quote;
1664 1.4 adam if (lastarg || *(lf->cursor) != ' ')
1665 1.4 adam argterm[y++] = ' ';
1666 1.4 adam argterm[y] = '\0';
1667 1.4 adam if (y > 0 && el_insertstr(el, argterm) == -1)
1668 1.4 adam fatal("el_insertstr failed.");
1669 1.4 adam }
1670 1.4 adam xfree(tmp);
1671 1.4 adam }
1672 1.4 adam
1673 1.4 adam return count;
1674 1.4 adam }
1675 1.4 adam
1676 1.4 adam /*
1677 1.4 adam * Determine whether a particular sftp command's arguments (if any)
1678 1.4 adam * represent local or remote files.
1679 1.4 adam */
1680 1.4 adam static int
1681 1.4 adam complete_is_remote(char *cmd) {
1682 1.4 adam int i;
1683 1.4 adam
1684 1.4 adam if (cmd == NULL)
1685 1.4 adam return -1;
1686 1.4 adam
1687 1.4 adam for (i = 0; cmds[i].c; i++) {
1688 1.4 adam if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c)))
1689 1.4 adam return cmds[i].t;
1690 1.4 adam }
1691 1.4 adam
1692 1.4 adam return -1;
1693 1.4 adam }
1694 1.4 adam
1695 1.4 adam /* Autocomplete a filename "file" */
1696 1.4 adam static int
1697 1.4 adam complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path,
1698 1.4 adam char *file, int remote, int lastarg, char quote, int terminated)
1699 1.4 adam {
1700 1.4 adam glob_t g;
1701 1.4 adam char *tmp, *tmp2, ins[3];
1702 1.4 adam u_int i, hadglob, pwdlen, len, tmplen, filelen;
1703 1.4 adam const LineInfo *lf;
1704 1.4 adam
1705 1.4 adam /* Glob from "file" location */
1706 1.4 adam if (file == NULL)
1707 1.4 adam tmp = xstrdup("*");
1708 1.4 adam else
1709 1.4 adam xasprintf(&tmp, "%s*", file);
1710 1.4 adam
1711 1.4 adam memset(&g, 0, sizeof(g));
1712 1.4 adam if (remote != LOCAL) {
1713 1.4 adam tmp = make_absolute(tmp, remote_path);
1714 1.4 adam remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1715 1.4 adam } else
1716 1.6 christos glob(tmp, GLOB_LIMIT|GLOB_DOOFFS|GLOB_MARK, NULL, &g);
1717 1.4 adam
1718 1.4 adam /* Determine length of pwd so we can trim completion display */
1719 1.4 adam for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) {
1720 1.4 adam /* Terminate counting on first unescaped glob metacharacter */
1721 1.4 adam if (tmp[tmplen] == '*' || tmp[tmplen] == '?') {
1722 1.4 adam if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0')
1723 1.4 adam hadglob = 1;
1724 1.4 adam break;
1725 1.4 adam }
1726 1.4 adam if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0')
1727 1.4 adam tmplen++;
1728 1.4 adam if (tmp[tmplen] == '/')
1729 1.4 adam pwdlen = tmplen + 1; /* track last seen '/' */
1730 1.4 adam }
1731 1.4 adam xfree(tmp);
1732 1.4 adam
1733 1.4 adam if (g.gl_matchc == 0)
1734 1.4 adam goto out;
1735 1.4 adam
1736 1.4 adam if (g.gl_matchc > 1)
1737 1.4 adam complete_display(g.gl_pathv, pwdlen);
1738 1.4 adam
1739 1.4 adam tmp = NULL;
1740 1.4 adam /* Don't try to extend globs */
1741 1.4 adam if (file == NULL || hadglob)
1742 1.4 adam goto out;
1743 1.4 adam
1744 1.4 adam tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc);
1745 1.4 adam tmp = path_strip(tmp2, remote_path);
1746 1.4 adam xfree(tmp2);
1747 1.4 adam
1748 1.4 adam if (tmp == NULL)
1749 1.4 adam goto out;
1750 1.4 adam
1751 1.4 adam tmplen = strlen(tmp);
1752 1.4 adam filelen = strlen(file);
1753 1.4 adam
1754 1.4 adam if (tmplen > filelen) {
1755 1.4 adam tmp2 = tmp + filelen;
1756 1.4 adam len = strlen(tmp2);
1757 1.4 adam /* quote argument on way out */
1758 1.4 adam for (i = 0; i < len; i++) {
1759 1.4 adam ins[0] = '\\';
1760 1.4 adam ins[1] = tmp2[i];
1761 1.4 adam ins[2] = '\0';
1762 1.4 adam switch (tmp2[i]) {
1763 1.4 adam case '\'':
1764 1.4 adam case '"':
1765 1.4 adam case '\\':
1766 1.4 adam case '\t':
1767 1.7 christos case '[':
1768 1.4 adam case ' ':
1769 1.4 adam if (quote == '\0' || tmp2[i] == quote) {
1770 1.4 adam if (el_insertstr(el, ins) == -1)
1771 1.4 adam fatal("el_insertstr "
1772 1.4 adam "failed.");
1773 1.4 adam break;
1774 1.4 adam }
1775 1.4 adam /* FALLTHROUGH */
1776 1.4 adam default:
1777 1.4 adam if (el_insertstr(el, ins + 1) == -1)
1778 1.4 adam fatal("el_insertstr failed.");
1779 1.4 adam break;
1780 1.4 adam }
1781 1.4 adam }
1782 1.4 adam }
1783 1.4 adam
1784 1.4 adam lf = el_line(el);
1785 1.4 adam if (g.gl_matchc == 1) {
1786 1.4 adam i = 0;
1787 1.4 adam if (!terminated)
1788 1.4 adam ins[i++] = quote;
1789 1.4 adam if (*(lf->cursor - 1) != '/' &&
1790 1.4 adam (lastarg || *(lf->cursor) != ' '))
1791 1.4 adam ins[i++] = ' ';
1792 1.4 adam ins[i] = '\0';
1793 1.4 adam if (i > 0 && el_insertstr(el, ins) == -1)
1794 1.4 adam fatal("el_insertstr failed.");
1795 1.4 adam }
1796 1.4 adam xfree(tmp);
1797 1.4 adam
1798 1.4 adam out:
1799 1.4 adam globfree(&g);
1800 1.4 adam return g.gl_matchc;
1801 1.4 adam }
1802 1.4 adam
1803 1.4 adam /* tab-completion hook function, called via libedit */
1804 1.4 adam static unsigned char
1805 1.4 adam complete(EditLine *el, int ch)
1806 1.4 adam {
1807 1.4 adam char **argv, *line, quote;
1808 1.4 adam u_int argc, carg, cursor, len, terminated, ret = CC_ERROR;
1809 1.4 adam const LineInfo *lf;
1810 1.4 adam struct complete_ctx *complete_ctx;
1811 1.4 adam
1812 1.4 adam lf = el_line(el);
1813 1.5 adam if (el_get(el, EL_CLIENTDATA, &complete_ctx) != 0)
1814 1.4 adam fatal("%s: el_get failed", __func__);
1815 1.4 adam
1816 1.4 adam /* Figure out which argument the cursor points to */
1817 1.4 adam cursor = lf->cursor - lf->buffer;
1818 1.4 adam line = (char *)xmalloc(cursor + 1);
1819 1.4 adam memcpy(line, lf->buffer, cursor);
1820 1.4 adam line[cursor] = '\0';
1821 1.4 adam argv = makeargv(line, &carg, 1, "e, &terminated);
1822 1.4 adam xfree(line);
1823 1.4 adam
1824 1.4 adam /* Get all the arguments on the line */
1825 1.4 adam len = lf->lastchar - lf->buffer;
1826 1.4 adam line = (char *)xmalloc(len + 1);
1827 1.4 adam memcpy(line, lf->buffer, len);
1828 1.4 adam line[len] = '\0';
1829 1.4 adam argv = makeargv(line, &argc, 1, NULL, NULL);
1830 1.4 adam
1831 1.4 adam /* Ensure cursor is at EOL or a argument boundary */
1832 1.4 adam if (line[cursor] != ' ' && line[cursor] != '\0' &&
1833 1.4 adam line[cursor] != '\n') {
1834 1.4 adam xfree(line);
1835 1.4 adam return ret;
1836 1.4 adam }
1837 1.4 adam
1838 1.4 adam if (carg == 0) {
1839 1.4 adam /* Show all available commands */
1840 1.4 adam complete_cmd_parse(el, NULL, argc == carg, '\0', 1);
1841 1.4 adam ret = CC_REDISPLAY;
1842 1.4 adam } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') {
1843 1.4 adam /* Handle the command parsing */
1844 1.4 adam if (complete_cmd_parse(el, argv[0], argc == carg,
1845 1.4 adam quote, terminated) != 0)
1846 1.4 adam ret = CC_REDISPLAY;
1847 1.4 adam } else if (carg >= 1) {
1848 1.4 adam /* Handle file parsing */
1849 1.4 adam int remote = complete_is_remote(argv[0]);
1850 1.4 adam char *filematch = NULL;
1851 1.4 adam
1852 1.4 adam if (carg > 1 && line[cursor-1] != ' ')
1853 1.4 adam filematch = argv[carg - 1];
1854 1.4 adam
1855 1.4 adam if (remote != 0 &&
1856 1.4 adam complete_match(el, complete_ctx->conn,
1857 1.4 adam *complete_ctx->remote_pathp, filematch,
1858 1.4 adam remote, carg == argc, quote, terminated) != 0)
1859 1.4 adam ret = CC_REDISPLAY;
1860 1.4 adam }
1861 1.4 adam
1862 1.4 adam xfree(line);
1863 1.4 adam return ret;
1864 1.4 adam }
1865 1.4 adam
1866 1.1 christos int
1867 1.7 christos interactive_loop(struct sftp_conn *conn, const char *file1, const char *file2)
1868 1.1 christos {
1869 1.4 adam char *remote_path;
1870 1.1 christos char *dir = NULL;
1871 1.1 christos char cmd[2048];
1872 1.1 christos int err, interactive;
1873 1.1 christos EditLine *el = NULL;
1874 1.1 christos History *hl = NULL;
1875 1.1 christos HistEvent hev;
1876 1.1 christos extern char *__progname;
1877 1.4 adam struct complete_ctx complete_ctx;
1878 1.1 christos
1879 1.1 christos if (!batchmode && isatty(STDIN_FILENO)) {
1880 1.1 christos if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL)
1881 1.1 christos fatal("Couldn't initialise editline");
1882 1.1 christos if ((hl = history_init()) == NULL)
1883 1.1 christos fatal("Couldn't initialise editline history");
1884 1.1 christos history(hl, &hev, H_SETSIZE, 100);
1885 1.1 christos el_set(el, EL_HIST, history, hl);
1886 1.1 christos
1887 1.1 christos el_set(el, EL_PROMPT, prompt);
1888 1.1 christos el_set(el, EL_EDITOR, "emacs");
1889 1.1 christos el_set(el, EL_TERMINAL, NULL);
1890 1.1 christos el_set(el, EL_SIGNAL, 1);
1891 1.1 christos el_source(el, NULL);
1892 1.4 adam
1893 1.4 adam /* Tab Completion */
1894 1.4 adam el_set(el, EL_ADDFN, "ftp-complete",
1895 1.7 christos "Context sensitive argument completion", complete);
1896 1.4 adam complete_ctx.conn = conn;
1897 1.4 adam complete_ctx.remote_pathp = &remote_path;
1898 1.4 adam el_set(el, EL_CLIENTDATA, (void*)&complete_ctx);
1899 1.4 adam el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1900 1.1 christos }
1901 1.1 christos
1902 1.4 adam remote_path = do_realpath(conn, ".");
1903 1.4 adam if (remote_path == NULL)
1904 1.1 christos fatal("Need cwd");
1905 1.1 christos
1906 1.1 christos if (file1 != NULL) {
1907 1.1 christos dir = xstrdup(file1);
1908 1.4 adam dir = make_absolute(dir, remote_path);
1909 1.1 christos
1910 1.1 christos if (remote_is_dir(conn, dir) && file2 == NULL) {
1911 1.1 christos printf("Changing to: %s\n", dir);
1912 1.1 christos snprintf(cmd, sizeof cmd, "cd \"%s\"", dir);
1913 1.4 adam if (parse_dispatch_command(conn, cmd,
1914 1.4 adam &remote_path, 1) != 0) {
1915 1.1 christos xfree(dir);
1916 1.4 adam xfree(remote_path);
1917 1.1 christos xfree(conn);
1918 1.1 christos return (-1);
1919 1.1 christos }
1920 1.1 christos } else {
1921 1.1 christos if (file2 == NULL)
1922 1.1 christos snprintf(cmd, sizeof cmd, "get %s", dir);
1923 1.1 christos else
1924 1.1 christos snprintf(cmd, sizeof cmd, "get %s %s", dir,
1925 1.1 christos file2);
1926 1.1 christos
1927 1.4 adam err = parse_dispatch_command(conn, cmd,
1928 1.4 adam &remote_path, 1);
1929 1.1 christos xfree(dir);
1930 1.4 adam xfree(remote_path);
1931 1.1 christos xfree(conn);
1932 1.1 christos return (err);
1933 1.1 christos }
1934 1.1 christos xfree(dir);
1935 1.1 christos }
1936 1.1 christos
1937 1.1 christos setvbuf(stdout, NULL, _IOLBF, 0);
1938 1.1 christos setvbuf(infile, NULL, _IOLBF, 0);
1939 1.1 christos
1940 1.1 christos interactive = !batchmode && isatty(STDIN_FILENO);
1941 1.1 christos err = 0;
1942 1.1 christos for (;;) {
1943 1.1 christos char *cp;
1944 1.1 christos const char *line;
1945 1.1 christos int count = 0;
1946 1.1 christos
1947 1.1 christos signal(SIGINT, SIG_IGN);
1948 1.1 christos
1949 1.1 christos if (el == NULL) {
1950 1.1 christos if (interactive)
1951 1.1 christos printf("sftp> ");
1952 1.1 christos if (fgets(cmd, sizeof(cmd), infile) == NULL) {
1953 1.1 christos if (interactive)
1954 1.1 christos printf("\n");
1955 1.1 christos break;
1956 1.1 christos }
1957 1.1 christos if (!interactive) { /* Echo command */
1958 1.1 christos printf("sftp> %s", cmd);
1959 1.1 christos if (strlen(cmd) > 0 &&
1960 1.1 christos cmd[strlen(cmd) - 1] != '\n')
1961 1.1 christos printf("\n");
1962 1.1 christos }
1963 1.1 christos } else {
1964 1.4 adam if ((line = el_gets(el, &count)) == NULL ||
1965 1.4 adam count <= 0) {
1966 1.1 christos printf("\n");
1967 1.1 christos break;
1968 1.1 christos }
1969 1.1 christos history(hl, &hev, H_ENTER, line);
1970 1.1 christos if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) {
1971 1.1 christos fprintf(stderr, "Error: input line too long\n");
1972 1.1 christos continue;
1973 1.1 christos }
1974 1.1 christos }
1975 1.1 christos
1976 1.1 christos cp = strrchr(cmd, '\n');
1977 1.1 christos if (cp)
1978 1.1 christos *cp = '\0';
1979 1.1 christos
1980 1.1 christos /* Handle user interrupts gracefully during commands */
1981 1.1 christos interrupted = 0;
1982 1.1 christos signal(SIGINT, cmd_interrupt);
1983 1.1 christos
1984 1.4 adam err = parse_dispatch_command(conn, cmd, &remote_path,
1985 1.4 adam batchmode);
1986 1.1 christos if (err != 0)
1987 1.1 christos break;
1988 1.1 christos }
1989 1.4 adam xfree(remote_path);
1990 1.1 christos xfree(conn);
1991 1.1 christos
1992 1.1 christos if (el != NULL)
1993 1.1 christos el_end(el);
1994 1.1 christos
1995 1.1 christos /* err == 1 signifies normal "quit" exit */
1996 1.1 christos return (err >= 0 ? 0 : -1);
1997 1.1 christos }
1998 1.1 christos
1999 1.1 christos static void
2000 1.7 christos connect_to_server(const char *path, char **args, int *in, int *out)
2001 1.1 christos {
2002 1.1 christos int c_in, c_out;
2003 1.1 christos
2004 1.1 christos int inout[2];
2005 1.1 christos
2006 1.1 christos if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
2007 1.1 christos fatal("socketpair: %s", strerror(errno));
2008 1.1 christos *in = *out = inout[0];
2009 1.1 christos c_in = c_out = inout[1];
2010 1.1 christos
2011 1.1 christos if ((sshpid = fork()) == -1)
2012 1.1 christos fatal("fork: %s", strerror(errno));
2013 1.1 christos else if (sshpid == 0) {
2014 1.1 christos if ((dup2(c_in, STDIN_FILENO) == -1) ||
2015 1.1 christos (dup2(c_out, STDOUT_FILENO) == -1)) {
2016 1.1 christos fprintf(stderr, "dup2: %s\n", strerror(errno));
2017 1.1 christos _exit(1);
2018 1.1 christos }
2019 1.1 christos close(*in);
2020 1.1 christos close(*out);
2021 1.1 christos close(c_in);
2022 1.1 christos close(c_out);
2023 1.1 christos
2024 1.1 christos /*
2025 1.1 christos * The underlying ssh is in the same process group, so we must
2026 1.1 christos * ignore SIGINT if we want to gracefully abort commands,
2027 1.1 christos * otherwise the signal will make it to the ssh process and
2028 1.4 adam * kill it too. Contrawise, since sftp sends SIGTERMs to the
2029 1.4 adam * underlying ssh, it must *not* ignore that signal.
2030 1.1 christos */
2031 1.1 christos signal(SIGINT, SIG_IGN);
2032 1.4 adam signal(SIGTERM, SIG_DFL);
2033 1.1 christos execvp(path, args);
2034 1.1 christos fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
2035 1.1 christos _exit(1);
2036 1.1 christos }
2037 1.1 christos
2038 1.1 christos signal(SIGTERM, killchild);
2039 1.1 christos signal(SIGINT, killchild);
2040 1.1 christos signal(SIGHUP, killchild);
2041 1.1 christos close(c_in);
2042 1.1 christos close(c_out);
2043 1.1 christos }
2044 1.1 christos
2045 1.8 joerg __dead static void
2046 1.1 christos usage(void)
2047 1.1 christos {
2048 1.1 christos extern char *__progname;
2049 1.1 christos
2050 1.1 christos fprintf(stderr,
2051 1.4 adam "usage: %s [-1246Cpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n"
2052 1.4 adam " [-D sftp_server_path] [-F ssh_config] "
2053 1.7 christos "[-i identity_file] [-l limit]\n"
2054 1.4 adam " [-o ssh_option] [-P port] [-R num_requests] "
2055 1.4 adam "[-S program]\n"
2056 1.4 adam " [-s subsystem | sftp_server] host\n"
2057 1.1 christos " %s [user@]host[:file ...]\n"
2058 1.1 christos " %s [user@]host[:dir[/]]\n"
2059 1.4 adam " %s -b batchfile [user@]host\n",
2060 1.4 adam __progname, __progname, __progname, __progname);
2061 1.1 christos exit(1);
2062 1.1 christos }
2063 1.1 christos
2064 1.1 christos int
2065 1.1 christos main(int argc, char **argv)
2066 1.1 christos {
2067 1.1 christos int in, out, ch, err;
2068 1.4 adam char *host = NULL, *userhost, *cp, *file2 = NULL;
2069 1.1 christos int debug_level = 0, sshver = 2;
2070 1.7 christos const char *file1 = NULL, *sftp_server = NULL;
2071 1.7 christos const char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2072 1.7 christos const char *errstr;
2073 1.1 christos LogLevel ll = SYSLOG_LEVEL_INFO;
2074 1.1 christos arglist args;
2075 1.1 christos extern int optind;
2076 1.1 christos extern char *optarg;
2077 1.4 adam struct sftp_conn *conn;
2078 1.4 adam size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2079 1.4 adam size_t num_requests = DEFAULT_NUM_REQUESTS;
2080 1.7 christos long long limit_kbps = 0;
2081 1.1 christos
2082 1.1 christos /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2083 1.1 christos sanitise_stdfd();
2084 1.1 christos
2085 1.1 christos memset(&args, '\0', sizeof(args));
2086 1.1 christos args.list = NULL;
2087 1.1 christos addargs(&args, "%s", ssh_program);
2088 1.1 christos addargs(&args, "-oForwardX11 no");
2089 1.1 christos addargs(&args, "-oForwardAgent no");
2090 1.1 christos addargs(&args, "-oPermitLocalCommand no");
2091 1.1 christos addargs(&args, "-oClearAllForwardings yes");
2092 1.1 christos
2093 1.1 christos ll = SYSLOG_LEVEL_INFO;
2094 1.1 christos infile = stdin;
2095 1.1 christos
2096 1.4 adam while ((ch = getopt(argc, argv,
2097 1.7 christos "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2098 1.1 christos switch (ch) {
2099 1.4 adam /* Passed through to ssh(1) */
2100 1.4 adam case '4':
2101 1.4 adam case '6':
2102 1.1 christos case 'C':
2103 1.4 adam addargs(&args, "-%c", ch);
2104 1.4 adam break;
2105 1.4 adam /* Passed through to ssh(1) with argument */
2106 1.4 adam case 'F':
2107 1.4 adam case 'c':
2108 1.4 adam case 'i':
2109 1.4 adam case 'o':
2110 1.4 adam addargs(&args, "-%c", ch);
2111 1.4 adam addargs(&args, "%s", optarg);
2112 1.4 adam break;
2113 1.4 adam case 'q':
2114 1.4 adam showprogress = 0;
2115 1.4 adam addargs(&args, "-%c", ch);
2116 1.4 adam break;
2117 1.4 adam case 'P':
2118 1.4 adam addargs(&args, "-oPort %s", optarg);
2119 1.1 christos break;
2120 1.1 christos case 'v':
2121 1.1 christos if (debug_level < 3) {
2122 1.1 christos addargs(&args, "-v");
2123 1.1 christos ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
2124 1.1 christos }
2125 1.1 christos debug_level++;
2126 1.1 christos break;
2127 1.1 christos case '1':
2128 1.1 christos sshver = 1;
2129 1.1 christos if (sftp_server == NULL)
2130 1.1 christos sftp_server = _PATH_SFTP_SERVER;
2131 1.1 christos break;
2132 1.4 adam case '2':
2133 1.4 adam sshver = 2;
2134 1.1 christos break;
2135 1.4 adam case 'B':
2136 1.4 adam copy_buffer_len = strtol(optarg, &cp, 10);
2137 1.4 adam if (copy_buffer_len == 0 || *cp != '\0')
2138 1.4 adam fatal("Invalid buffer size \"%s\"", optarg);
2139 1.1 christos break;
2140 1.1 christos case 'b':
2141 1.1 christos if (batchmode)
2142 1.1 christos fatal("Batch file already specified.");
2143 1.1 christos
2144 1.1 christos /* Allow "-" as stdin */
2145 1.1 christos if (strcmp(optarg, "-") != 0 &&
2146 1.1 christos (infile = fopen(optarg, "r")) == NULL)
2147 1.1 christos fatal("%s (%s).", strerror(errno), optarg);
2148 1.1 christos showprogress = 0;
2149 1.1 christos batchmode = 1;
2150 1.1 christos addargs(&args, "-obatchmode yes");
2151 1.1 christos break;
2152 1.4 adam case 'p':
2153 1.4 adam global_pflag = 1;
2154 1.4 adam break;
2155 1.4 adam case 'D':
2156 1.1 christos sftp_direct = optarg;
2157 1.1 christos break;
2158 1.7 christos case 'l':
2159 1.7 christos limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2160 1.7 christos &errstr);
2161 1.7 christos if (errstr != NULL)
2162 1.7 christos usage();
2163 1.7 christos limit_kbps *= 1024; /* kbps */
2164 1.7 christos break;
2165 1.4 adam case 'r':
2166 1.4 adam global_rflag = 1;
2167 1.1 christos break;
2168 1.1 christos case 'R':
2169 1.1 christos num_requests = strtol(optarg, &cp, 10);
2170 1.1 christos if (num_requests == 0 || *cp != '\0')
2171 1.1 christos fatal("Invalid number of requests \"%s\"",
2172 1.1 christos optarg);
2173 1.1 christos break;
2174 1.4 adam case 's':
2175 1.4 adam sftp_server = optarg;
2176 1.4 adam break;
2177 1.4 adam case 'S':
2178 1.4 adam ssh_program = optarg;
2179 1.4 adam replacearg(&args, 0, "%s", ssh_program);
2180 1.4 adam break;
2181 1.1 christos case 'h':
2182 1.1 christos default:
2183 1.1 christos usage();
2184 1.1 christos }
2185 1.1 christos }
2186 1.1 christos
2187 1.1 christos if (!isatty(STDERR_FILENO))
2188 1.1 christos showprogress = 0;
2189 1.1 christos
2190 1.1 christos log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
2191 1.1 christos
2192 1.1 christos if (sftp_direct == NULL) {
2193 1.1 christos if (optind == argc || argc > (optind + 2))
2194 1.1 christos usage();
2195 1.1 christos
2196 1.1 christos userhost = xstrdup(argv[optind]);
2197 1.1 christos file2 = argv[optind+1];
2198 1.1 christos
2199 1.1 christos if ((host = strrchr(userhost, '@')) == NULL)
2200 1.1 christos host = userhost;
2201 1.1 christos else {
2202 1.1 christos *host++ = '\0';
2203 1.1 christos if (!userhost[0]) {
2204 1.1 christos fprintf(stderr, "Missing username\n");
2205 1.1 christos usage();
2206 1.1 christos }
2207 1.4 adam addargs(&args, "-l");
2208 1.4 adam addargs(&args, "%s", userhost);
2209 1.1 christos }
2210 1.1 christos
2211 1.1 christos if ((cp = colon(host)) != NULL) {
2212 1.1 christos *cp++ = '\0';
2213 1.1 christos file1 = cp;
2214 1.1 christos }
2215 1.1 christos
2216 1.1 christos host = cleanhostname(host);
2217 1.1 christos if (!*host) {
2218 1.1 christos fprintf(stderr, "Missing hostname\n");
2219 1.1 christos usage();
2220 1.1 christos }
2221 1.1 christos
2222 1.1 christos addargs(&args, "-oProtocol %d", sshver);
2223 1.1 christos
2224 1.1 christos /* no subsystem if the server-spec contains a '/' */
2225 1.1 christos if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
2226 1.1 christos addargs(&args, "-s");
2227 1.1 christos
2228 1.4 adam addargs(&args, "--");
2229 1.1 christos addargs(&args, "%s", host);
2230 1.1 christos addargs(&args, "%s", (sftp_server != NULL ?
2231 1.1 christos sftp_server : "sftp"));
2232 1.1 christos
2233 1.1 christos connect_to_server(ssh_program, args.list, &in, &out);
2234 1.1 christos } else {
2235 1.1 christos args.list = NULL;
2236 1.1 christos addargs(&args, "sftp-server");
2237 1.1 christos
2238 1.1 christos connect_to_server(sftp_direct, args.list, &in, &out);
2239 1.1 christos }
2240 1.1 christos freeargs(&args);
2241 1.1 christos
2242 1.7 christos conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2243 1.4 adam if (conn == NULL)
2244 1.4 adam fatal("Couldn't initialise connection to server");
2245 1.4 adam
2246 1.4 adam if (!batchmode) {
2247 1.4 adam if (sftp_direct == NULL)
2248 1.4 adam fprintf(stderr, "Connected to %s.\n", host);
2249 1.4 adam else
2250 1.4 adam fprintf(stderr, "Attached to %s.\n", sftp_direct);
2251 1.4 adam }
2252 1.4 adam
2253 1.4 adam err = interactive_loop(conn, file1, file2);
2254 1.1 christos
2255 1.1 christos close(in);
2256 1.1 christos close(out);
2257 1.1 christos if (batchmode)
2258 1.1 christos fclose(infile);
2259 1.1 christos
2260 1.1 christos while (waitpid(sshpid, NULL, 0) == -1)
2261 1.1 christos if (errno != EINTR)
2262 1.1 christos fatal("Couldn't wait for ssh process: %s",
2263 1.1 christos strerror(errno));
2264 1.1 christos
2265 1.1 christos exit(err == 0 ? 0 : 1);
2266 1.1 christos }
2267