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