Commit | Line | Data |
---|---|---|
ee116499 | 1 | /* $OpenBSD: scp.c,v 1.248 2022/05/13 06:31:50 djm Exp $ */ |
16c343f1 PA |
2 | /* |
3 | * scp - secure remote copy. This is basically patched BSD rcp which | |
4 | * uses ssh to do the data transfer (instead of using rcmd). | |
5 | * | |
6 | * NOTE: This version should NOT be suid root. (This uses ssh to | |
7 | * do the transfer and ssh has the necessary privileges.) | |
8 | * | |
9 | * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> | |
10 | * | |
11 | * As far as I am concerned, the code I have written for this software | |
12 | * can be used freely for any purpose. Any derived versions of this | |
13 | * software must be clearly marked as such, and if the derived work is | |
14 | * incompatible with the protocol description in the RFC file, it must be | |
15 | * called by a name other than "ssh" or "Secure Shell". | |
16 | */ | |
17 | /* | |
18 | * Copyright (c) 1999 Theo de Raadt. All rights reserved. | |
19 | * Copyright (c) 1999 Aaron Campbell. All rights reserved. | |
20 | * | |
21 | * Redistribution and use in source and binary forms, with or without | |
22 | * modification, are permitted provided that the following conditions | |
23 | * are met: | |
24 | * 1. Redistributions of source code must retain the above copyright | |
25 | * notice, this list of conditions and the following disclaimer. | |
26 | * 2. Redistributions in binary form must reproduce the above copyright | |
27 | * notice, this list of conditions and the following disclaimer in the | |
28 | * documentation and/or other materials provided with the distribution. | |
29 | * | |
30 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
31 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
32 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
33 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
34 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
35 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
36 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
37 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
38 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
39 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
40 | */ | |
41 | ||
42 | /* | |
43 | * Parts from: | |
44 | * | |
45 | * Copyright (c) 1983, 1990, 1992, 1993, 1995 | |
46 | * The Regents of the University of California. All rights reserved. | |
47 | * | |
48 | * Redistribution and use in source and binary forms, with or without | |
49 | * modification, are permitted provided that the following conditions | |
50 | * are met: | |
51 | * 1. Redistributions of source code must retain the above copyright | |
52 | * notice, this list of conditions and the following disclaimer. | |
53 | * 2. Redistributions in binary form must reproduce the above copyright | |
54 | * notice, this list of conditions and the following disclaimer in the | |
55 | * documentation and/or other materials provided with the distribution. | |
56 | * 3. Neither the name of the University nor the names of its contributors | |
57 | * may be used to endorse or promote products derived from this software | |
58 | * without specific prior written permission. | |
59 | * | |
60 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
61 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
62 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
63 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
64 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
65 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
66 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
67 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
68 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
69 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
70 | * SUCH DAMAGE. | |
71 | * | |
72 | */ | |
73 | ||
74 | #include "includes.h" | |
75 | ||
76 | #include <sys/types.h> | |
16c343f1 PA |
77 | #ifdef HAVE_SYS_STAT_H |
78 | # include <sys/stat.h> | |
79 | #endif | |
80 | #ifdef HAVE_POLL_H | |
81 | #include <poll.h> | |
82 | #else | |
83 | # ifdef HAVE_SYS_POLL_H | |
84 | # include <sys/poll.h> | |
85 | # endif | |
86 | #endif | |
87 | #ifdef HAVE_SYS_TIME_H | |
88 | # include <sys/time.h> | |
89 | #endif | |
90 | #include <sys/wait.h> | |
91 | #include <sys/uio.h> | |
92 | ||
93 | #include <ctype.h> | |
94 | #include <dirent.h> | |
95 | #include <errno.h> | |
96 | #include <fcntl.h> | |
0cbfa66c | 97 | #ifdef HAVE_FNMATCH_H |
664f4763 | 98 | #include <fnmatch.h> |
0cbfa66c | 99 | #endif |
50a69bb5 SW |
100 | #ifdef USE_SYSTEM_GLOB |
101 | # include <glob.h> | |
102 | #else | |
103 | # include "openbsd-compat/glob.h" | |
104 | #endif | |
105 | #ifdef HAVE_LIBGEN_H | |
106 | #include <libgen.h> | |
107 | #endif | |
e9778795 PA |
108 | #include <limits.h> |
109 | #include <locale.h> | |
16c343f1 PA |
110 | #include <pwd.h> |
111 | #include <signal.h> | |
112 | #include <stdarg.h> | |
ce74baca | 113 | #ifdef HAVE_STDINT_H |
0cbfa66c | 114 | # include <stdint.h> |
ce74baca | 115 | #endif |
16c343f1 PA |
116 | #include <stdio.h> |
117 | #include <stdlib.h> | |
118 | #include <string.h> | |
119 | #include <time.h> | |
120 | #include <unistd.h> | |
36e94dc5 | 121 | #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) |
16c343f1 PA |
122 | #include <vis.h> |
123 | #endif | |
124 | ||
125 | #include "xmalloc.h" | |
664f4763 | 126 | #include "ssh.h" |
16c343f1 PA |
127 | #include "atomicio.h" |
128 | #include "pathnames.h" | |
129 | #include "log.h" | |
130 | #include "misc.h" | |
131 | #include "progressmeter.h" | |
e9778795 | 132 | #include "utf8.h" |
ee116499 | 133 | #include "sftp.h" |
16c343f1 | 134 | |
50a69bb5 SW |
135 | #include "sftp-common.h" |
136 | #include "sftp-client.h" | |
137 | ||
16c343f1 PA |
138 | extern char *__progname; |
139 | ||
140 | #define COPY_BUFLEN 16384 | |
141 | ||
50a69bb5 SW |
142 | int do_cmd(char *, char *, char *, int, int, char *, int *, int *, pid_t *); |
143 | int do_cmd2(char *, char *, int, char *, int, int); | |
16c343f1 PA |
144 | |
145 | /* Struct for addargs */ | |
146 | arglist args; | |
9f304aaf | 147 | arglist remote_remote_args; |
16c343f1 PA |
148 | |
149 | /* Bandwidth limit */ | |
9f304aaf PA |
150 | long long limit_kbps = 0; |
151 | struct bwlimit bwlimit; | |
16c343f1 PA |
152 | |
153 | /* Name of current file being transferred. */ | |
154 | char *curfile; | |
155 | ||
156 | /* This is set to non-zero to enable verbose mode. */ | |
157 | int verbose_mode = 0; | |
50a69bb5 | 158 | LogLevel log_level = SYSLOG_LEVEL_INFO; |
16c343f1 PA |
159 | |
160 | /* This is set to zero if the progressmeter is not desired. */ | |
161 | int showprogress = 1; | |
162 | ||
9f304aaf PA |
163 | /* |
164 | * This is set to non-zero if remote-remote copy should be piped | |
165 | * through this process. | |
166 | */ | |
50a69bb5 | 167 | int throughlocal = 1; |
9f304aaf | 168 | |
664f4763 | 169 | /* Non-standard port to use for the ssh connection or -1. */ |
170 | int sshport = -1; | |
171 | ||
16c343f1 PA |
172 | /* This is the program to execute for the secured connection. ("ssh" or -S) */ |
173 | char *ssh_program = _PATH_SSH_PROGRAM; | |
174 | ||
175 | /* This is used to store the pid of ssh_program */ | |
176 | pid_t do_cmd_pid = -1; | |
50a69bb5 SW |
177 | pid_t do_cmd_pid2 = -1; |
178 | ||
179 | /* Needed for sftp */ | |
180 | volatile sig_atomic_t interrupted = 0; | |
181 | ||
182 | int remote_glob(struct sftp_conn *, const char *, int, | |
183 | int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ | |
16c343f1 PA |
184 | |
185 | static void | |
186 | killchild(int signo) | |
187 | { | |
188 | if (do_cmd_pid > 1) { | |
189 | kill(do_cmd_pid, signo ? signo : SIGTERM); | |
190 | waitpid(do_cmd_pid, NULL, 0); | |
191 | } | |
50a69bb5 SW |
192 | if (do_cmd_pid2 > 1) { |
193 | kill(do_cmd_pid2, signo ? signo : SIGTERM); | |
194 | waitpid(do_cmd_pid2, NULL, 0); | |
195 | } | |
16c343f1 PA |
196 | |
197 | if (signo) | |
198 | _exit(1); | |
199 | exit(1); | |
200 | } | |
201 | ||
856ea928 | 202 | static void |
50a69bb5 | 203 | suspone(int pid, int signo) |
856ea928 PA |
204 | { |
205 | int status; | |
206 | ||
50a69bb5 SW |
207 | if (pid > 1) { |
208 | kill(pid, signo); | |
209 | while (waitpid(pid, &status, WUNTRACED) == -1 && | |
856ea928 PA |
210 | errno == EINTR) |
211 | ; | |
856ea928 PA |
212 | } |
213 | } | |
214 | ||
50a69bb5 SW |
215 | static void |
216 | suspchild(int signo) | |
217 | { | |
218 | suspone(do_cmd_pid, signo); | |
219 | suspone(do_cmd_pid2, signo); | |
220 | kill(getpid(), SIGSTOP); | |
221 | } | |
222 | ||
16c343f1 PA |
223 | static int |
224 | do_local_cmd(arglist *a) | |
225 | { | |
226 | u_int i; | |
227 | int status; | |
228 | pid_t pid; | |
229 | ||
230 | if (a->num == 0) | |
231 | fatal("do_local_cmd: no arguments"); | |
232 | ||
233 | if (verbose_mode) { | |
234 | fprintf(stderr, "Executing:"); | |
235 | for (i = 0; i < a->num; i++) | |
e9778795 | 236 | fmprintf(stderr, " %s", a->list[i]); |
16c343f1 PA |
237 | fprintf(stderr, "\n"); |
238 | } | |
239 | if ((pid = fork()) == -1) | |
240 | fatal("do_local_cmd: fork: %s", strerror(errno)); | |
241 | ||
242 | if (pid == 0) { | |
243 | execvp(a->list[0], a->list); | |
244 | perror(a->list[0]); | |
245 | exit(1); | |
246 | } | |
247 | ||
248 | do_cmd_pid = pid; | |
0cbfa66c DF |
249 | ssh_signal(SIGTERM, killchild); |
250 | ssh_signal(SIGINT, killchild); | |
251 | ssh_signal(SIGHUP, killchild); | |
16c343f1 PA |
252 | |
253 | while (waitpid(pid, &status, 0) == -1) | |
254 | if (errno != EINTR) | |
255 | fatal("do_local_cmd: waitpid: %s", strerror(errno)); | |
256 | ||
257 | do_cmd_pid = -1; | |
258 | ||
259 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | |
260 | return (-1); | |
261 | ||
262 | return (0); | |
263 | } | |
264 | ||
265 | /* | |
266 | * This function executes the given command as the specified user on the | |
267 | * given host. This returns < 0 if execution fails, and >= 0 otherwise. This | |
268 | * assigns the input and output file descriptors on success. | |
269 | */ | |
270 | ||
271 | int | |
50a69bb5 SW |
272 | do_cmd(char *program, char *host, char *remuser, int port, int subsystem, |
273 | char *cmd, int *fdin, int *fdout, pid_t *pid) | |
16c343f1 PA |
274 | { |
275 | int pin[2], pout[2], reserved[2]; | |
276 | ||
277 | if (verbose_mode) | |
e9778795 | 278 | fmprintf(stderr, |
16c343f1 | 279 | "Executing: program %s host %s, user %s, command %s\n", |
50a69bb5 | 280 | program, host, |
16c343f1 PA |
281 | remuser ? remuser : "(unspecified)", cmd); |
282 | ||
664f4763 | 283 | if (port == -1) |
284 | port = sshport; | |
285 | ||
16c343f1 PA |
286 | /* |
287 | * Reserve two descriptors so that the real pipes won't get | |
288 | * descriptors 0 and 1 because that will screw up dup2 below. | |
289 | */ | |
0cbfa66c | 290 | if (pipe(reserved) == -1) |
16c343f1 PA |
291 | fatal("pipe: %s", strerror(errno)); |
292 | ||
293 | /* Create a socket pair for communicating with ssh. */ | |
0cbfa66c | 294 | if (pipe(pin) == -1) |
16c343f1 | 295 | fatal("pipe: %s", strerror(errno)); |
0cbfa66c | 296 | if (pipe(pout) == -1) |
16c343f1 PA |
297 | fatal("pipe: %s", strerror(errno)); |
298 | ||
299 | /* Free the reserved descriptors. */ | |
300 | close(reserved[0]); | |
301 | close(reserved[1]); | |
302 | ||
0cbfa66c DF |
303 | ssh_signal(SIGTSTP, suspchild); |
304 | ssh_signal(SIGTTIN, suspchild); | |
305 | ssh_signal(SIGTTOU, suspchild); | |
856ea928 | 306 | |
16c343f1 | 307 | /* Fork a child to execute the command on the remote host using ssh. */ |
50a69bb5 SW |
308 | *pid = fork(); |
309 | if (*pid == 0) { | |
16c343f1 PA |
310 | /* Child. */ |
311 | close(pin[1]); | |
312 | close(pout[0]); | |
313 | dup2(pin[0], 0); | |
314 | dup2(pout[1], 1); | |
315 | close(pin[0]); | |
316 | close(pout[1]); | |
317 | ||
50a69bb5 | 318 | replacearg(&args, 0, "%s", program); |
664f4763 | 319 | if (port != -1) { |
320 | addargs(&args, "-p"); | |
321 | addargs(&args, "%d", port); | |
322 | } | |
856ea928 PA |
323 | if (remuser != NULL) { |
324 | addargs(&args, "-l"); | |
325 | addargs(&args, "%s", remuser); | |
326 | } | |
50a69bb5 SW |
327 | if (subsystem) |
328 | addargs(&args, "-s"); | |
856ea928 | 329 | addargs(&args, "--"); |
16c343f1 PA |
330 | addargs(&args, "%s", host); |
331 | addargs(&args, "%s", cmd); | |
332 | ||
50a69bb5 SW |
333 | execvp(program, args.list); |
334 | perror(program); | |
16c343f1 | 335 | exit(1); |
50a69bb5 | 336 | } else if (*pid == -1) { |
16c343f1 PA |
337 | fatal("fork: %s", strerror(errno)); |
338 | } | |
339 | /* Parent. Close the other side, and return the local side. */ | |
340 | close(pin[0]); | |
341 | *fdout = pin[1]; | |
342 | close(pout[1]); | |
343 | *fdin = pout[0]; | |
0cbfa66c DF |
344 | ssh_signal(SIGTERM, killchild); |
345 | ssh_signal(SIGINT, killchild); | |
346 | ssh_signal(SIGHUP, killchild); | |
16c343f1 PA |
347 | return 0; |
348 | } | |
349 | ||
9f304aaf | 350 | /* |
664f4763 | 351 | * This function executes a command similar to do_cmd(), but expects the |
9f304aaf PA |
352 | * input and output descriptors to be setup by a previous call to do_cmd(). |
353 | * This way the input and output of two commands can be connected. | |
354 | */ | |
355 | int | |
50a69bb5 SW |
356 | do_cmd2(char *host, char *remuser, int port, char *cmd, |
357 | int fdin, int fdout) | |
9f304aaf | 358 | { |
9f304aaf | 359 | int status; |
50a69bb5 | 360 | pid_t pid; |
9f304aaf PA |
361 | |
362 | if (verbose_mode) | |
e9778795 | 363 | fmprintf(stderr, |
9f304aaf PA |
364 | "Executing: 2nd program %s host %s, user %s, command %s\n", |
365 | ssh_program, host, | |
366 | remuser ? remuser : "(unspecified)", cmd); | |
367 | ||
664f4763 | 368 | if (port == -1) |
369 | port = sshport; | |
370 | ||
9f304aaf PA |
371 | /* Fork a child to execute the command on the remote host using ssh. */ |
372 | pid = fork(); | |
373 | if (pid == 0) { | |
374 | dup2(fdin, 0); | |
375 | dup2(fdout, 1); | |
376 | ||
377 | replacearg(&args, 0, "%s", ssh_program); | |
664f4763 | 378 | if (port != -1) { |
379 | addargs(&args, "-p"); | |
380 | addargs(&args, "%d", port); | |
381 | } | |
9f304aaf PA |
382 | if (remuser != NULL) { |
383 | addargs(&args, "-l"); | |
384 | addargs(&args, "%s", remuser); | |
385 | } | |
0cbfa66c | 386 | addargs(&args, "-oBatchMode=yes"); |
9f304aaf PA |
387 | addargs(&args, "--"); |
388 | addargs(&args, "%s", host); | |
389 | addargs(&args, "%s", cmd); | |
390 | ||
391 | execvp(ssh_program, args.list); | |
392 | perror(ssh_program); | |
393 | exit(1); | |
394 | } else if (pid == -1) { | |
395 | fatal("fork: %s", strerror(errno)); | |
396 | } | |
397 | while (waitpid(pid, &status, 0) == -1) | |
398 | if (errno != EINTR) | |
399 | fatal("do_cmd2: waitpid: %s", strerror(errno)); | |
400 | return 0; | |
401 | } | |
402 | ||
16c343f1 PA |
403 | typedef struct { |
404 | size_t cnt; | |
405 | char *buf; | |
406 | } BUF; | |
407 | ||
408 | BUF *allocbuf(BUF *, int, int); | |
409 | void lostconn(int); | |
410 | int okname(char *); | |
50a69bb5 SW |
411 | void run_err(const char *,...) |
412 | __attribute__((__format__ (printf, 1, 2))) | |
413 | __attribute__((__nonnull__ (1))); | |
414 | int note_err(const char *,...) | |
415 | __attribute__((__format__ (printf, 1, 2))); | |
16c343f1 PA |
416 | void verifydir(char *); |
417 | ||
418 | struct passwd *pwd; | |
419 | uid_t userid; | |
50a69bb5 | 420 | int errs, remin, remout, remin2, remout2; |
664f4763 | 421 | int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; |
16c343f1 PA |
422 | |
423 | #define CMDNEEDS 64 | |
424 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | |
425 | ||
50a69bb5 SW |
426 | enum scp_mode_e { |
427 | MODE_SCP, | |
428 | MODE_SFTP | |
429 | }; | |
430 | ||
16c343f1 PA |
431 | int response(void); |
432 | void rsource(char *, struct stat *); | |
664f4763 | 433 | void sink(int, char *[], const char *); |
16c343f1 | 434 | void source(int, char *[]); |
50a69bb5 SW |
435 | void tolocal(int, char *[], enum scp_mode_e, char *sftp_direct); |
436 | void toremote(int, char *[], enum scp_mode_e, char *sftp_direct); | |
16c343f1 PA |
437 | void usage(void); |
438 | ||
50a69bb5 SW |
439 | void source_sftp(int, char *, char *, struct sftp_conn *); |
440 | void sink_sftp(int, char *, const char *, struct sftp_conn *); | |
441 | void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *, | |
442 | char *, char *); | |
443 | ||
16c343f1 PA |
444 | int |
445 | main(int argc, char **argv) | |
446 | { | |
447 | int ch, fflag, tflag, status, n; | |
50a69bb5 | 448 | char **newargv, *argv0; |
9f304aaf | 449 | const char *errstr; |
16c343f1 PA |
450 | extern char *optarg; |
451 | extern int optind; | |
ee116499 | 452 | enum scp_mode_e mode = MODE_SFTP; |
50a69bb5 | 453 | char *sftp_direct = NULL; |
16c343f1 PA |
454 | |
455 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | |
456 | sanitise_stdfd(); | |
457 | ||
ce74baca | 458 | msetlocale(); |
e9778795 | 459 | |
16c343f1 | 460 | /* Copy argv, because we modify it */ |
50a69bb5 | 461 | argv0 = argv[0]; |
ce74baca | 462 | newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv)); |
16c343f1 PA |
463 | for (n = 0; n < argc; n++) |
464 | newargv[n] = xstrdup(argv[n]); | |
465 | argv = newargv; | |
466 | ||
467 | __progname = ssh_get_progname(argv[0]); | |
468 | ||
50a69bb5 SW |
469 | log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); |
470 | ||
16c343f1 | 471 | memset(&args, '\0', sizeof(args)); |
9f304aaf PA |
472 | memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); |
473 | args.list = remote_remote_args.list = NULL; | |
16c343f1 PA |
474 | addargs(&args, "%s", ssh_program); |
475 | addargs(&args, "-x"); | |
9f304aaf PA |
476 | addargs(&args, "-oPermitLocalCommand=no"); |
477 | addargs(&args, "-oClearAllForwardings=yes"); | |
664f4763 | 478 | addargs(&args, "-oRemoteCommand=none"); |
479 | addargs(&args, "-oRequestTTY=no"); | |
16c343f1 | 480 | |
664f4763 | 481 | fflag = Tflag = tflag = 0; |
482 | while ((ch = getopt(argc, argv, | |
50a69bb5 | 483 | "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:")) != -1) { |
16c343f1 PA |
484 | switch (ch) { |
485 | /* User-visible flags. */ | |
486 | case '1': | |
ce74baca MD |
487 | fatal("SSH protocol v.1 is no longer supported"); |
488 | break; | |
16c343f1 | 489 | case '2': |
ce74baca MD |
490 | /* Ignored */ |
491 | break; | |
50a69bb5 | 492 | case 'A': |
16c343f1 PA |
493 | case '4': |
494 | case '6': | |
495 | case 'C': | |
496 | addargs(&args, "-%c", ch); | |
9f304aaf PA |
497 | addargs(&remote_remote_args, "-%c", ch); |
498 | break; | |
50a69bb5 SW |
499 | case 'D': |
500 | sftp_direct = optarg; | |
501 | break; | |
9f304aaf PA |
502 | case '3': |
503 | throughlocal = 1; | |
16c343f1 | 504 | break; |
50a69bb5 SW |
505 | case 'R': |
506 | throughlocal = 0; | |
507 | break; | |
16c343f1 PA |
508 | case 'o': |
509 | case 'c': | |
510 | case 'i': | |
511 | case 'F': | |
664f4763 | 512 | case 'J': |
9f304aaf PA |
513 | addargs(&remote_remote_args, "-%c", ch); |
514 | addargs(&remote_remote_args, "%s", optarg); | |
856ea928 PA |
515 | addargs(&args, "-%c", ch); |
516 | addargs(&args, "%s", optarg); | |
16c343f1 | 517 | break; |
50a69bb5 SW |
518 | case 'O': |
519 | mode = MODE_SCP; | |
520 | break; | |
521 | case 's': | |
522 | mode = MODE_SFTP; | |
523 | break; | |
16c343f1 | 524 | case 'P': |
664f4763 | 525 | sshport = a2port(optarg); |
526 | if (sshport <= 0) | |
527 | fatal("bad port \"%s\"\n", optarg); | |
16c343f1 PA |
528 | break; |
529 | case 'B': | |
9f304aaf PA |
530 | addargs(&remote_remote_args, "-oBatchmode=yes"); |
531 | addargs(&args, "-oBatchmode=yes"); | |
16c343f1 PA |
532 | break; |
533 | case 'l': | |
9f304aaf PA |
534 | limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, |
535 | &errstr); | |
536 | if (errstr != NULL) | |
16c343f1 | 537 | usage(); |
9f304aaf PA |
538 | limit_kbps *= 1024; /* kbps */ |
539 | bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); | |
16c343f1 PA |
540 | break; |
541 | case 'p': | |
542 | pflag = 1; | |
543 | break; | |
544 | case 'r': | |
545 | iamrecursive = 1; | |
546 | break; | |
547 | case 'S': | |
548 | ssh_program = xstrdup(optarg); | |
549 | break; | |
550 | case 'v': | |
551 | addargs(&args, "-v"); | |
9f304aaf | 552 | addargs(&remote_remote_args, "-v"); |
50a69bb5 SW |
553 | if (verbose_mode == 0) |
554 | log_level = SYSLOG_LEVEL_DEBUG1; | |
555 | else if (log_level < SYSLOG_LEVEL_DEBUG3) | |
556 | log_level++; | |
16c343f1 PA |
557 | verbose_mode = 1; |
558 | break; | |
559 | case 'q': | |
560 | addargs(&args, "-q"); | |
9f304aaf | 561 | addargs(&remote_remote_args, "-q"); |
16c343f1 PA |
562 | showprogress = 0; |
563 | break; | |
564 | ||
565 | /* Server options. */ | |
566 | case 'd': | |
567 | targetshouldbedirectory = 1; | |
568 | break; | |
569 | case 'f': /* "from" */ | |
570 | iamremote = 1; | |
571 | fflag = 1; | |
572 | break; | |
573 | case 't': /* "to" */ | |
574 | iamremote = 1; | |
575 | tflag = 1; | |
576 | #ifdef HAVE_CYGWIN | |
577 | setmode(0, O_BINARY); | |
578 | #endif | |
579 | break; | |
664f4763 | 580 | case 'T': |
581 | Tflag = 1; | |
582 | break; | |
16c343f1 PA |
583 | default: |
584 | usage(); | |
585 | } | |
664f4763 | 586 | } |
16c343f1 PA |
587 | argc -= optind; |
588 | argv += optind; | |
589 | ||
50a69bb5 SW |
590 | log_init(argv0, log_level, SYSLOG_FACILITY_USER, 2); |
591 | ||
592 | /* Do this last because we want the user to be able to override it */ | |
593 | addargs(&args, "-oForwardAgent=no"); | |
594 | ||
595 | if (iamremote) | |
596 | mode = MODE_SCP; | |
597 | ||
16c343f1 PA |
598 | if ((pwd = getpwuid(userid = getuid())) == NULL) |
599 | fatal("unknown user %u", (u_int) userid); | |
600 | ||
601 | if (!isatty(STDOUT_FILENO)) | |
602 | showprogress = 0; | |
603 | ||
e9778795 PA |
604 | if (pflag) { |
605 | /* Cannot pledge: -p allows setuid/setgid files... */ | |
606 | } else { | |
607 | if (pledge("stdio rpath wpath cpath fattr tty proc exec", | |
608 | NULL) == -1) { | |
609 | perror("pledge"); | |
610 | exit(1); | |
611 | } | |
612 | } | |
613 | ||
16c343f1 PA |
614 | remin = STDIN_FILENO; |
615 | remout = STDOUT_FILENO; | |
616 | ||
617 | if (fflag) { | |
618 | /* Follow "protocol", send data. */ | |
619 | (void) response(); | |
620 | source(argc, argv); | |
621 | exit(errs != 0); | |
622 | } | |
623 | if (tflag) { | |
624 | /* Receive data. */ | |
664f4763 | 625 | sink(argc, argv, NULL); |
16c343f1 PA |
626 | exit(errs != 0); |
627 | } | |
628 | if (argc < 2) | |
629 | usage(); | |
630 | if (argc > 2) | |
631 | targetshouldbedirectory = 1; | |
632 | ||
633 | remin = remout = -1; | |
634 | do_cmd_pid = -1; | |
635 | /* Command to be executed on remote system using "ssh". */ | |
636 | (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", | |
637 | verbose_mode ? " -v" : "", | |
638 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
639 | targetshouldbedirectory ? " -d" : ""); | |
640 | ||
0cbfa66c | 641 | (void) ssh_signal(SIGPIPE, lostconn); |
16c343f1 | 642 | |
664f4763 | 643 | if (colon(argv[argc - 1])) /* Dest is remote host. */ |
50a69bb5 | 644 | toremote(argc, argv, mode, sftp_direct); |
16c343f1 PA |
645 | else { |
646 | if (targetshouldbedirectory) | |
647 | verifydir(argv[argc - 1]); | |
50a69bb5 | 648 | tolocal(argc, argv, mode, sftp_direct); /* Dest is local host. */ |
16c343f1 PA |
649 | } |
650 | /* | |
651 | * Finally check the exit status of the ssh process, if one was forked | |
cb5eb4f1 | 652 | * and no error has occurred yet |
16c343f1 | 653 | */ |
50a69bb5 | 654 | if (do_cmd_pid != -1 && (mode == MODE_SFTP || errs == 0)) { |
16c343f1 PA |
655 | if (remin != -1) |
656 | (void) close(remin); | |
657 | if (remout != -1) | |
658 | (void) close(remout); | |
659 | if (waitpid(do_cmd_pid, &status, 0) == -1) | |
660 | errs = 1; | |
661 | else { | |
662 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) | |
663 | errs = 1; | |
664 | } | |
665 | } | |
666 | exit(errs != 0); | |
667 | } | |
668 | ||
9f304aaf PA |
669 | /* Callback from atomicio6 to update progress meter and limit bandwidth */ |
670 | static int | |
671 | scpio(void *_cnt, size_t s) | |
16c343f1 | 672 | { |
9f304aaf PA |
673 | off_t *cnt = (off_t *)_cnt; |
674 | ||
675 | *cnt += s; | |
664f4763 | 676 | refresh_progress_meter(0); |
9f304aaf PA |
677 | if (limit_kbps > 0) |
678 | bandwidth_limit(&bwlimit, s); | |
679 | return 0; | |
16c343f1 PA |
680 | } |
681 | ||
36e94dc5 PA |
682 | static int |
683 | do_times(int fd, int verb, const struct stat *sb) | |
684 | { | |
685 | /* strlen(2^64) == 20; strlen(10^6) == 7 */ | |
686 | char buf[(20 + 7 + 2) * 2 + 2]; | |
687 | ||
688 | (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", | |
689 | (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), | |
690 | (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); | |
691 | if (verb) { | |
692 | fprintf(stderr, "File mtime %lld atime %lld\n", | |
693 | (long long)sb->st_mtime, (long long)sb->st_atime); | |
694 | fprintf(stderr, "Sending file timestamps: %s", buf); | |
695 | } | |
696 | (void) atomicio(vwrite, fd, buf, strlen(buf)); | |
697 | return (response()); | |
698 | } | |
699 | ||
664f4763 | 700 | static int |
701 | parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp, | |
50a69bb5 | 702 | char **pathp) |
664f4763 | 703 | { |
704 | int r; | |
705 | ||
706 | r = parse_uri("scp", uri, userp, hostp, portp, pathp); | |
707 | if (r == 0 && *pathp == NULL) | |
708 | *pathp = xstrdup("."); | |
709 | return r; | |
710 | } | |
711 | ||
712 | /* Appends a string to an array; returns 0 on success, -1 on alloc failure */ | |
713 | static int | |
714 | append(char *cp, char ***ap, size_t *np) | |
715 | { | |
716 | char **tmp; | |
717 | ||
718 | if ((tmp = reallocarray(*ap, *np + 1, sizeof(*tmp))) == NULL) | |
719 | return -1; | |
720 | tmp[(*np)] = cp; | |
721 | (*np)++; | |
722 | *ap = tmp; | |
723 | return 0; | |
724 | } | |
725 | ||
726 | /* | |
727 | * Finds the start and end of the first brace pair in the pattern. | |
728 | * returns 0 on success or -1 for invalid patterns. | |
729 | */ | |
730 | static int | |
731 | find_brace(const char *pattern, int *startp, int *endp) | |
732 | { | |
733 | int i; | |
734 | int in_bracket, brace_level; | |
735 | ||
736 | *startp = *endp = -1; | |
737 | in_bracket = brace_level = 0; | |
738 | for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) { | |
739 | switch (pattern[i]) { | |
740 | case '\\': | |
741 | /* skip next character */ | |
742 | if (pattern[i + 1] != '\0') | |
743 | i++; | |
744 | break; | |
745 | case '[': | |
746 | in_bracket = 1; | |
747 | break; | |
748 | case ']': | |
749 | in_bracket = 0; | |
750 | break; | |
751 | case '{': | |
752 | if (in_bracket) | |
753 | break; | |
754 | if (pattern[i + 1] == '}') { | |
755 | /* Protect a single {}, for find(1), like csh */ | |
756 | i++; /* skip */ | |
757 | break; | |
758 | } | |
759 | if (*startp == -1) | |
760 | *startp = i; | |
761 | brace_level++; | |
762 | break; | |
763 | case '}': | |
764 | if (in_bracket) | |
765 | break; | |
766 | if (*startp < 0) { | |
767 | /* Unbalanced brace */ | |
768 | return -1; | |
769 | } | |
770 | if (--brace_level <= 0) | |
771 | *endp = i; | |
772 | break; | |
773 | } | |
774 | } | |
775 | /* unbalanced brackets/braces */ | |
776 | if (*endp < 0 && (*startp >= 0 || in_bracket)) | |
777 | return -1; | |
778 | return 0; | |
779 | } | |
780 | ||
781 | /* | |
782 | * Assembles and records a successfully-expanded pattern, returns -1 on | |
783 | * alloc failure. | |
784 | */ | |
785 | static int | |
786 | emit_expansion(const char *pattern, int brace_start, int brace_end, | |
787 | int sel_start, int sel_end, char ***patternsp, size_t *npatternsp) | |
788 | { | |
789 | char *cp; | |
790 | int o = 0, tail_len = strlen(pattern + brace_end + 1); | |
791 | ||
792 | if ((cp = malloc(brace_start + (sel_end - sel_start) + | |
793 | tail_len + 1)) == NULL) | |
794 | return -1; | |
795 | ||
796 | /* Pattern before initial brace */ | |
797 | if (brace_start > 0) { | |
798 | memcpy(cp, pattern, brace_start); | |
799 | o = brace_start; | |
800 | } | |
801 | /* Current braced selection */ | |
802 | if (sel_end - sel_start > 0) { | |
803 | memcpy(cp + o, pattern + sel_start, | |
804 | sel_end - sel_start); | |
805 | o += sel_end - sel_start; | |
806 | } | |
807 | /* Remainder of pattern after closing brace */ | |
808 | if (tail_len > 0) { | |
809 | memcpy(cp + o, pattern + brace_end + 1, tail_len); | |
810 | o += tail_len; | |
811 | } | |
812 | cp[o] = '\0'; | |
813 | if (append(cp, patternsp, npatternsp) != 0) { | |
814 | free(cp); | |
815 | return -1; | |
816 | } | |
817 | return 0; | |
818 | } | |
819 | ||
820 | /* | |
821 | * Expand the first encountered brace in pattern, appending the expanded | |
822 | * patterns it yielded to the *patternsp array. | |
823 | * | |
824 | * Returns 0 on success or -1 on allocation failure. | |
825 | * | |
826 | * Signals whether expansion was performed via *expanded and whether | |
827 | * pattern was invalid via *invalid. | |
828 | */ | |
829 | static int | |
830 | brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp, | |
831 | int *expanded, int *invalid) | |
832 | { | |
833 | int i; | |
834 | int in_bracket, brace_start, brace_end, brace_level; | |
835 | int sel_start, sel_end; | |
836 | ||
837 | *invalid = *expanded = 0; | |
838 | ||
839 | if (find_brace(pattern, &brace_start, &brace_end) != 0) { | |
840 | *invalid = 1; | |
841 | return 0; | |
842 | } else if (brace_start == -1) | |
843 | return 0; | |
844 | ||
845 | in_bracket = brace_level = 0; | |
846 | for (i = sel_start = brace_start + 1; i < brace_end; i++) { | |
847 | switch (pattern[i]) { | |
848 | case '{': | |
849 | if (in_bracket) | |
850 | break; | |
851 | brace_level++; | |
852 | break; | |
853 | case '}': | |
854 | if (in_bracket) | |
855 | break; | |
856 | brace_level--; | |
857 | break; | |
858 | case '[': | |
859 | in_bracket = 1; | |
860 | break; | |
861 | case ']': | |
862 | in_bracket = 0; | |
863 | break; | |
864 | case '\\': | |
865 | if (i < brace_end - 1) | |
866 | i++; /* skip */ | |
867 | break; | |
868 | } | |
869 | if (pattern[i] == ',' || i == brace_end - 1) { | |
870 | if (in_bracket || brace_level > 0) | |
871 | continue; | |
872 | /* End of a selection, emit an expanded pattern */ | |
873 | ||
874 | /* Adjust end index for last selection */ | |
875 | sel_end = (i == brace_end - 1) ? brace_end : i; | |
876 | if (emit_expansion(pattern, brace_start, brace_end, | |
877 | sel_start, sel_end, patternsp, npatternsp) != 0) | |
878 | return -1; | |
879 | /* move on to the next selection */ | |
880 | sel_start = i + 1; | |
881 | continue; | |
882 | } | |
883 | } | |
884 | if (in_bracket || brace_level > 0) { | |
885 | *invalid = 1; | |
886 | return 0; | |
887 | } | |
888 | /* success */ | |
889 | *expanded = 1; | |
890 | return 0; | |
891 | } | |
892 | ||
893 | /* Expand braces from pattern. Returns 0 on success, -1 on failure */ | |
894 | static int | |
895 | brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp) | |
896 | { | |
897 | char *cp, *cp2, **active = NULL, **done = NULL; | |
898 | size_t i, nactive = 0, ndone = 0; | |
899 | int ret = -1, invalid = 0, expanded = 0; | |
900 | ||
901 | *patternsp = NULL; | |
902 | *npatternsp = 0; | |
903 | ||
904 | /* Start the worklist with the original pattern */ | |
905 | if ((cp = strdup(pattern)) == NULL) | |
906 | return -1; | |
907 | if (append(cp, &active, &nactive) != 0) { | |
908 | free(cp); | |
909 | return -1; | |
910 | } | |
911 | while (nactive > 0) { | |
912 | cp = active[nactive - 1]; | |
913 | nactive--; | |
914 | if (brace_expand_one(cp, &active, &nactive, | |
915 | &expanded, &invalid) == -1) { | |
916 | free(cp); | |
917 | goto fail; | |
918 | } | |
919 | if (invalid) | |
50a69bb5 | 920 | fatal_f("invalid brace pattern \"%s\"", cp); |
664f4763 | 921 | if (expanded) { |
922 | /* | |
923 | * Current entry expanded to new entries on the | |
924 | * active list; discard the progenitor pattern. | |
925 | */ | |
926 | free(cp); | |
927 | continue; | |
928 | } | |
929 | /* | |
930 | * Pattern did not expand; append the finename component to | |
931 | * the completed list | |
932 | */ | |
933 | if ((cp2 = strrchr(cp, '/')) != NULL) | |
934 | *cp2++ = '\0'; | |
935 | else | |
936 | cp2 = cp; | |
937 | if (append(xstrdup(cp2), &done, &ndone) != 0) { | |
938 | free(cp); | |
939 | goto fail; | |
940 | } | |
941 | free(cp); | |
942 | } | |
943 | /* success */ | |
944 | *patternsp = done; | |
945 | *npatternsp = ndone; | |
946 | done = NULL; | |
947 | ndone = 0; | |
948 | ret = 0; | |
949 | fail: | |
950 | for (i = 0; i < nactive; i++) | |
951 | free(active[i]); | |
952 | free(active); | |
953 | for (i = 0; i < ndone; i++) | |
954 | free(done[i]); | |
955 | free(done); | |
956 | return ret; | |
957 | } | |
958 | ||
50a69bb5 SW |
959 | static struct sftp_conn * |
960 | do_sftp_connect(char *host, char *user, int port, char *sftp_direct, | |
961 | int *reminp, int *remoutp, int *pidp) | |
962 | { | |
963 | if (sftp_direct == NULL) { | |
964 | if (do_cmd(ssh_program, host, user, port, 1, "sftp", | |
965 | reminp, remoutp, pidp) < 0) | |
966 | return NULL; | |
967 | ||
968 | } else { | |
ee116499 | 969 | freeargs(&args); |
50a69bb5 SW |
970 | addargs(&args, "sftp-server"); |
971 | if (do_cmd(sftp_direct, host, NULL, -1, 0, "sftp", | |
972 | reminp, remoutp, pidp) < 0) | |
973 | return NULL; | |
974 | } | |
975 | return do_init(*reminp, *remoutp, 32768, 64, limit_kbps); | |
976 | } | |
977 | ||
16c343f1 | 978 | void |
50a69bb5 | 979 | toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) |
16c343f1 | 980 | { |
664f4763 | 981 | char *suser = NULL, *host = NULL, *src = NULL; |
982 | char *bp, *tuser, *thost, *targ; | |
983 | int sport = -1, tport = -1; | |
50a69bb5 | 984 | struct sftp_conn *conn = NULL, *conn2 = NULL; |
16c343f1 | 985 | arglist alist; |
50a69bb5 | 986 | int i, r, status; |
9f304aaf | 987 | u_int j; |
16c343f1 PA |
988 | |
989 | memset(&alist, '\0', sizeof(alist)); | |
990 | alist.list = NULL; | |
991 | ||
664f4763 | 992 | /* Parse target */ |
993 | r = parse_scp_uri(argv[argc - 1], &tuser, &thost, &tport, &targ); | |
994 | if (r == -1) { | |
995 | fmprintf(stderr, "%s: invalid uri\n", argv[argc - 1]); | |
996 | ++errs; | |
997 | goto out; | |
998 | } | |
999 | if (r != 0) { | |
1000 | if (parse_user_host_path(argv[argc - 1], &tuser, &thost, | |
1001 | &targ) == -1) { | |
1002 | fmprintf(stderr, "%s: invalid target\n", argv[argc - 1]); | |
1003 | ++errs; | |
1004 | goto out; | |
1005 | } | |
16c343f1 | 1006 | } |
16c343f1 | 1007 | |
664f4763 | 1008 | /* Parse source files */ |
16c343f1 | 1009 | for (i = 0; i < argc - 1; i++) { |
664f4763 | 1010 | free(suser); |
1011 | free(host); | |
1012 | free(src); | |
1013 | r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); | |
1014 | if (r == -1) { | |
1015 | fmprintf(stderr, "%s: invalid uri\n", argv[i]); | |
1016 | ++errs; | |
1017 | continue; | |
1018 | } | |
1019 | if (r != 0) { | |
1020 | parse_user_host_path(argv[i], &suser, &host, &src); | |
1021 | } | |
1022 | if (suser != NULL && !okname(suser)) { | |
1023 | ++errs; | |
1024 | continue; | |
1025 | } | |
1026 | if (host && throughlocal) { /* extended remote to remote */ | |
50a69bb5 SW |
1027 | if (mode == MODE_SFTP) { |
1028 | if (remin == -1) { | |
1029 | /* Connect to dest now */ | |
1030 | conn = do_sftp_connect(thost, tuser, | |
1031 | tport, sftp_direct, | |
1032 | &remin, &remout, &do_cmd_pid); | |
1033 | if (conn == NULL) { | |
1034 | fatal("Unable to open " | |
1035 | "destination connection"); | |
1036 | } | |
1037 | debug3_f("origin in %d out %d pid %ld", | |
1038 | remin, remout, (long)do_cmd_pid); | |
1039 | } | |
1040 | /* | |
1041 | * XXX remember suser/host/sport and only | |
1042 | * reconnect if they change between arguments. | |
1043 | * would save reconnections for cases like | |
1044 | * scp -3 hosta:/foo hosta:/bar hostb: | |
1045 | */ | |
1046 | /* Connect to origin now */ | |
1047 | conn2 = do_sftp_connect(host, suser, | |
1048 | sport, sftp_direct, | |
1049 | &remin2, &remout2, &do_cmd_pid2); | |
1050 | if (conn2 == NULL) { | |
1051 | fatal("Unable to open " | |
1052 | "source connection"); | |
1053 | } | |
1054 | debug3_f("destination in %d out %d pid %ld", | |
1055 | remin2, remout2, (long)do_cmd_pid2); | |
1056 | throughlocal_sftp(conn2, conn, src, targ); | |
1057 | (void) close(remin2); | |
1058 | (void) close(remout2); | |
1059 | remin2 = remout2 = -1; | |
1060 | if (waitpid(do_cmd_pid2, &status, 0) == -1) | |
1061 | ++errs; | |
1062 | else if (!WIFEXITED(status) || | |
1063 | WEXITSTATUS(status) != 0) | |
1064 | ++errs; | |
1065 | do_cmd_pid2 = -1; | |
1066 | continue; | |
1067 | } else { | |
1068 | xasprintf(&bp, "%s -f %s%s", cmd, | |
1069 | *src == '-' ? "-- " : "", src); | |
1070 | if (do_cmd(ssh_program, host, suser, sport, 0, | |
1071 | bp, &remin, &remout, &do_cmd_pid) < 0) | |
1072 | exit(1); | |
1073 | free(bp); | |
1074 | xasprintf(&bp, "%s -t %s%s", cmd, | |
1075 | *targ == '-' ? "-- " : "", targ); | |
1076 | if (do_cmd2(thost, tuser, tport, bp, | |
1077 | remin, remout) < 0) | |
1078 | exit(1); | |
1079 | free(bp); | |
1080 | (void) close(remin); | |
1081 | (void) close(remout); | |
1082 | remin = remout = -1; | |
1083 | } | |
664f4763 | 1084 | } else if (host) { /* standard remote to remote */ |
50a69bb5 SW |
1085 | /* |
1086 | * Second remote user is passed to first remote side | |
1087 | * via scp command-line. Ensure it contains no obvious | |
1088 | * shell characters. | |
1089 | */ | |
1090 | if (tuser != NULL && !okname(tuser)) { | |
1091 | ++errs; | |
1092 | continue; | |
1093 | } | |
664f4763 | 1094 | if (tport != -1 && tport != SSH_DEFAULT_PORT) { |
1095 | /* This would require the remote support URIs */ | |
1096 | fatal("target port not supported with two " | |
50a69bb5 | 1097 | "remote hosts and the -R option"); |
664f4763 | 1098 | } |
1099 | ||
16c343f1 PA |
1100 | freeargs(&alist); |
1101 | addargs(&alist, "%s", ssh_program); | |
16c343f1 | 1102 | addargs(&alist, "-x"); |
9f304aaf | 1103 | addargs(&alist, "-oClearAllForwardings=yes"); |
16c343f1 | 1104 | addargs(&alist, "-n"); |
9f304aaf PA |
1105 | for (j = 0; j < remote_remote_args.num; j++) { |
1106 | addargs(&alist, "%s", | |
1107 | remote_remote_args.list[j]); | |
1108 | } | |
664f4763 | 1109 | |
1110 | if (sport != -1) { | |
1111 | addargs(&alist, "-p"); | |
1112 | addargs(&alist, "%d", sport); | |
1113 | } | |
1114 | if (suser) { | |
16c343f1 PA |
1115 | addargs(&alist, "-l"); |
1116 | addargs(&alist, "%s", suser); | |
16c343f1 | 1117 | } |
856ea928 | 1118 | addargs(&alist, "--"); |
16c343f1 PA |
1119 | addargs(&alist, "%s", host); |
1120 | addargs(&alist, "%s", cmd); | |
1121 | addargs(&alist, "%s", src); | |
1122 | addargs(&alist, "%s%s%s:%s", | |
1123 | tuser ? tuser : "", tuser ? "@" : "", | |
1124 | thost, targ); | |
1125 | if (do_local_cmd(&alist) != 0) | |
1126 | errs = 1; | |
1127 | } else { /* local to remote */ | |
50a69bb5 SW |
1128 | if (mode == MODE_SFTP) { |
1129 | if (remin == -1) { | |
1130 | /* Connect to remote now */ | |
1131 | conn = do_sftp_connect(thost, tuser, | |
1132 | tport, sftp_direct, | |
1133 | &remin, &remout, &do_cmd_pid); | |
1134 | if (conn == NULL) { | |
1135 | fatal("Unable to open sftp " | |
1136 | "connection"); | |
1137 | } | |
1138 | } | |
1139 | ||
1140 | /* The protocol */ | |
1141 | source_sftp(1, argv[i], targ, conn); | |
1142 | continue; | |
1143 | } | |
1144 | /* SCP */ | |
16c343f1 | 1145 | if (remin == -1) { |
99e85e0d PA |
1146 | xasprintf(&bp, "%s -t %s%s", cmd, |
1147 | *targ == '-' ? "-- " : "", targ); | |
50a69bb5 SW |
1148 | if (do_cmd(ssh_program, thost, tuser, tport, 0, |
1149 | bp, &remin, &remout, &do_cmd_pid) < 0) | |
16c343f1 PA |
1150 | exit(1); |
1151 | if (response() < 0) | |
1152 | exit(1); | |
36e94dc5 | 1153 | free(bp); |
16c343f1 PA |
1154 | } |
1155 | source(1, argv + i); | |
1156 | } | |
1157 | } | |
664f4763 | 1158 | out: |
50a69bb5 SW |
1159 | if (mode == MODE_SFTP) |
1160 | free(conn); | |
664f4763 | 1161 | free(tuser); |
1162 | free(thost); | |
1163 | free(targ); | |
1164 | free(suser); | |
1165 | free(host); | |
1166 | free(src); | |
16c343f1 PA |
1167 | } |
1168 | ||
1169 | void | |
50a69bb5 | 1170 | tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct) |
16c343f1 | 1171 | { |
664f4763 | 1172 | char *bp, *host = NULL, *src = NULL, *suser = NULL; |
16c343f1 | 1173 | arglist alist; |
50a69bb5 | 1174 | struct sftp_conn *conn = NULL; |
664f4763 | 1175 | int i, r, sport = -1; |
16c343f1 PA |
1176 | |
1177 | memset(&alist, '\0', sizeof(alist)); | |
1178 | alist.list = NULL; | |
1179 | ||
1180 | for (i = 0; i < argc - 1; i++) { | |
664f4763 | 1181 | free(suser); |
1182 | free(host); | |
1183 | free(src); | |
1184 | r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); | |
1185 | if (r == -1) { | |
1186 | fmprintf(stderr, "%s: invalid uri\n", argv[i]); | |
1187 | ++errs; | |
1188 | continue; | |
1189 | } | |
1190 | if (r != 0) | |
1191 | parse_user_host_path(argv[i], &suser, &host, &src); | |
1192 | if (suser != NULL && !okname(suser)) { | |
1193 | ++errs; | |
1194 | continue; | |
1195 | } | |
1196 | if (!host) { /* Local to local. */ | |
16c343f1 PA |
1197 | freeargs(&alist); |
1198 | addargs(&alist, "%s", _PATH_CP); | |
1199 | if (iamrecursive) | |
1200 | addargs(&alist, "-r"); | |
1201 | if (pflag) | |
1202 | addargs(&alist, "-p"); | |
856ea928 | 1203 | addargs(&alist, "--"); |
16c343f1 PA |
1204 | addargs(&alist, "%s", argv[i]); |
1205 | addargs(&alist, "%s", argv[argc-1]); | |
1206 | if (do_local_cmd(&alist)) | |
1207 | ++errs; | |
1208 | continue; | |
1209 | } | |
664f4763 | 1210 | /* Remote to local. */ |
50a69bb5 SW |
1211 | if (mode == MODE_SFTP) { |
1212 | conn = do_sftp_connect(host, suser, sport, | |
1213 | sftp_direct, &remin, &remout, &do_cmd_pid); | |
1214 | if (conn == NULL) { | |
1215 | error("sftp connection failed"); | |
1216 | ++errs; | |
1217 | continue; | |
1218 | } | |
1219 | ||
1220 | /* The protocol */ | |
1221 | sink_sftp(1, argv[argc - 1], src, conn); | |
1222 | ||
1223 | free(conn); | |
1224 | (void) close(remin); | |
1225 | (void) close(remout); | |
1226 | remin = remout = -1; | |
1227 | continue; | |
1228 | } | |
1229 | /* SCP */ | |
99e85e0d PA |
1230 | xasprintf(&bp, "%s -f %s%s", |
1231 | cmd, *src == '-' ? "-- " : "", src); | |
50a69bb5 SW |
1232 | if (do_cmd(ssh_program, host, suser, sport, 0, bp, |
1233 | &remin, &remout, &do_cmd_pid) < 0) { | |
36e94dc5 | 1234 | free(bp); |
16c343f1 PA |
1235 | ++errs; |
1236 | continue; | |
1237 | } | |
36e94dc5 | 1238 | free(bp); |
664f4763 | 1239 | sink(1, argv + argc - 1, src); |
16c343f1 PA |
1240 | (void) close(remin); |
1241 | remin = remout = -1; | |
1242 | } | |
664f4763 | 1243 | free(suser); |
1244 | free(host); | |
1245 | free(src); | |
16c343f1 PA |
1246 | } |
1247 | ||
50a69bb5 SW |
1248 | /* Prepare remote path, handling ~ by assuming cwd is the homedir */ |
1249 | static char * | |
1250 | prepare_remote_path(struct sftp_conn *conn, const char *path) | |
1251 | { | |
ee116499 AHJ |
1252 | size_t nslash; |
1253 | ||
50a69bb5 | 1254 | /* Handle ~ prefixed paths */ |
50a69bb5 SW |
1255 | if (*path == '\0' || strcmp(path, "~") == 0) |
1256 | return xstrdup("."); | |
ee116499 AHJ |
1257 | if (*path != '~') |
1258 | return xstrdup(path); | |
1259 | if (strncmp(path, "~/", 2) == 0) { | |
1260 | if ((nslash = strspn(path + 2, "/")) == strlen(path + 2)) | |
1261 | return xstrdup("."); | |
1262 | return xstrdup(path + 2 + nslash); | |
1263 | } | |
50a69bb5 SW |
1264 | if (can_expand_path(conn)) |
1265 | return do_expand_path(conn, path); | |
1266 | /* No protocol extension */ | |
1267 | error("server expand-path extension is required " | |
1268 | "for ~user paths in SFTP mode"); | |
1269 | return NULL; | |
1270 | } | |
1271 | ||
1272 | void | |
1273 | source_sftp(int argc, char *src, char *targ, struct sftp_conn *conn) | |
1274 | { | |
1275 | char *target = NULL, *filename = NULL, *abs_dst = NULL; | |
ee116499 AHJ |
1276 | int src_is_dir, target_is_dir; |
1277 | Attrib a; | |
1278 | struct stat st; | |
1279 | ||
1280 | memset(&a, '\0', sizeof(a)); | |
1281 | if (stat(src, &st) != 0) | |
1282 | fatal("stat local \"%s\": %s", src, strerror(errno)); | |
1283 | src_is_dir = S_ISDIR(st.st_mode); | |
50a69bb5 | 1284 | if ((filename = basename(src)) == NULL) |
ee116499 | 1285 | fatal("basename \"%s\": %s", src, strerror(errno)); |
50a69bb5 SW |
1286 | |
1287 | /* | |
1288 | * No need to glob here - the local shell already took care of | |
1289 | * the expansions | |
1290 | */ | |
1291 | if ((target = prepare_remote_path(conn, targ)) == NULL) | |
1292 | cleanup_exit(255); | |
1293 | target_is_dir = remote_is_dir(conn, target); | |
1294 | if (targetshouldbedirectory && !target_is_dir) { | |
ee116499 AHJ |
1295 | debug("target directory \"%s\" does not exist", target); |
1296 | a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS; | |
1297 | a.perm = st.st_mode | 0700; /* ensure writable */ | |
1298 | if (do_mkdir(conn, target, &a, 1) != 0) | |
1299 | cleanup_exit(255); /* error already logged */ | |
1300 | target_is_dir = 1; | |
50a69bb5 SW |
1301 | } |
1302 | if (target_is_dir) | |
1303 | abs_dst = path_append(target, filename); | |
1304 | else { | |
1305 | abs_dst = target; | |
1306 | target = NULL; | |
1307 | } | |
1308 | debug3_f("copying local %s to remote %s", src, abs_dst); | |
1309 | ||
ee116499 | 1310 | if (src_is_dir && iamrecursive) { |
50a69bb5 | 1311 | if (upload_dir(conn, src, abs_dst, pflag, |
ee116499 AHJ |
1312 | SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) { |
1313 | error("failed to upload directory %s to %s", src, targ); | |
50a69bb5 SW |
1314 | errs = 1; |
1315 | } | |
ee116499 AHJ |
1316 | } else if (do_upload(conn, src, abs_dst, pflag, 0, 0, 1) != 0) { |
1317 | error("failed to upload file %s to %s", src, targ); | |
50a69bb5 SW |
1318 | errs = 1; |
1319 | } | |
1320 | ||
1321 | free(abs_dst); | |
1322 | free(target); | |
1323 | } | |
1324 | ||
16c343f1 PA |
1325 | void |
1326 | source(int argc, char **argv) | |
1327 | { | |
1328 | struct stat stb; | |
1329 | static BUF buffer; | |
1330 | BUF *bp; | |
3b69e377 | 1331 | off_t i, statbytes; |
36e94dc5 | 1332 | size_t amt, nr; |
16c343f1 | 1333 | int fd = -1, haderr, indx; |
0cbfa66c | 1334 | char *last, *name, buf[PATH_MAX + 128], encname[PATH_MAX]; |
16c343f1 PA |
1335 | int len; |
1336 | ||
1337 | for (indx = 0; indx < argc; ++indx) { | |
1338 | name = argv[indx]; | |
1339 | statbytes = 0; | |
1340 | len = strlen(name); | |
1341 | while (len > 1 && name[len-1] == '/') | |
1342 | name[--len] = '\0'; | |
ee116499 | 1343 | if ((fd = open(name, O_RDONLY|O_NONBLOCK)) == -1) |
16c343f1 PA |
1344 | goto syserr; |
1345 | if (strchr(name, '\n') != NULL) { | |
1346 | strnvis(encname, name, sizeof(encname), VIS_NL); | |
1347 | name = encname; | |
1348 | } | |
0cbfa66c | 1349 | if (fstat(fd, &stb) == -1) { |
16c343f1 PA |
1350 | syserr: run_err("%s: %s", name, strerror(errno)); |
1351 | goto next; | |
1352 | } | |
3b69e377 PA |
1353 | if (stb.st_size < 0) { |
1354 | run_err("%s: %s", name, "Negative file size"); | |
1355 | goto next; | |
1356 | } | |
16c343f1 PA |
1357 | unset_nonblock(fd); |
1358 | switch (stb.st_mode & S_IFMT) { | |
1359 | case S_IFREG: | |
1360 | break; | |
1361 | case S_IFDIR: | |
1362 | if (iamrecursive) { | |
1363 | rsource(name, &stb); | |
1364 | goto next; | |
1365 | } | |
1366 | /* FALLTHROUGH */ | |
1367 | default: | |
1368 | run_err("%s: not a regular file", name); | |
1369 | goto next; | |
1370 | } | |
1371 | if ((last = strrchr(name, '/')) == NULL) | |
1372 | last = name; | |
1373 | else | |
1374 | ++last; | |
1375 | curfile = last; | |
1376 | if (pflag) { | |
36e94dc5 | 1377 | if (do_times(remout, verbose_mode, &stb) < 0) |
16c343f1 PA |
1378 | goto next; |
1379 | } | |
1380 | #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) | |
1381 | snprintf(buf, sizeof buf, "C%04o %lld %s\n", | |
1382 | (u_int) (stb.st_mode & FILEMODEMASK), | |
1383 | (long long)stb.st_size, last); | |
e9778795 PA |
1384 | if (verbose_mode) |
1385 | fmprintf(stderr, "Sending file modes: %s", buf); | |
16c343f1 PA |
1386 | (void) atomicio(vwrite, remout, buf, strlen(buf)); |
1387 | if (response() < 0) | |
1388 | goto next; | |
1389 | if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { | |
1390 | next: if (fd != -1) { | |
1391 | (void) close(fd); | |
1392 | fd = -1; | |
1393 | } | |
1394 | continue; | |
1395 | } | |
1396 | if (showprogress) | |
1397 | start_progress_meter(curfile, stb.st_size, &statbytes); | |
1398 | set_nonblock(remout); | |
1399 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | |
1400 | amt = bp->cnt; | |
3b69e377 | 1401 | if (i + (off_t)amt > stb.st_size) |
16c343f1 PA |
1402 | amt = stb.st_size - i; |
1403 | if (!haderr) { | |
36e94dc5 PA |
1404 | if ((nr = atomicio(read, fd, |
1405 | bp->buf, amt)) != amt) { | |
16c343f1 | 1406 | haderr = errno; |
36e94dc5 PA |
1407 | memset(bp->buf + nr, 0, amt - nr); |
1408 | } | |
16c343f1 PA |
1409 | } |
1410 | /* Keep writing after error to retain sync */ | |
1411 | if (haderr) { | |
1412 | (void)atomicio(vwrite, remout, bp->buf, amt); | |
36e94dc5 | 1413 | memset(bp->buf, 0, amt); |
16c343f1 PA |
1414 | continue; |
1415 | } | |
9f304aaf | 1416 | if (atomicio6(vwrite, remout, bp->buf, amt, scpio, |
16c343f1 PA |
1417 | &statbytes) != amt) |
1418 | haderr = errno; | |
1419 | } | |
1420 | unset_nonblock(remout); | |
16c343f1 PA |
1421 | |
1422 | if (fd != -1) { | |
0cbfa66c | 1423 | if (close(fd) == -1 && !haderr) |
16c343f1 PA |
1424 | haderr = errno; |
1425 | fd = -1; | |
1426 | } | |
1427 | if (!haderr) | |
1428 | (void) atomicio(vwrite, remout, "", 1); | |
1429 | else | |
1430 | run_err("%s: %s", name, strerror(haderr)); | |
1431 | (void) response(); | |
e9778795 PA |
1432 | if (showprogress) |
1433 | stop_progress_meter(); | |
16c343f1 PA |
1434 | } |
1435 | } | |
1436 | ||
1437 | void | |
1438 | rsource(char *name, struct stat *statp) | |
1439 | { | |
1440 | DIR *dirp; | |
1441 | struct dirent *dp; | |
e9778795 | 1442 | char *last, *vect[1], path[PATH_MAX]; |
16c343f1 PA |
1443 | |
1444 | if (!(dirp = opendir(name))) { | |
1445 | run_err("%s: %s", name, strerror(errno)); | |
1446 | return; | |
1447 | } | |
1448 | last = strrchr(name, '/'); | |
e9778795 | 1449 | if (last == NULL) |
16c343f1 PA |
1450 | last = name; |
1451 | else | |
1452 | last++; | |
1453 | if (pflag) { | |
36e94dc5 | 1454 | if (do_times(remout, verbose_mode, statp) < 0) { |
16c343f1 PA |
1455 | closedir(dirp); |
1456 | return; | |
1457 | } | |
1458 | } | |
1459 | (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", | |
1460 | (u_int) (statp->st_mode & FILEMODEMASK), 0, last); | |
1461 | if (verbose_mode) | |
e9778795 | 1462 | fmprintf(stderr, "Entering directory: %s", path); |
16c343f1 PA |
1463 | (void) atomicio(vwrite, remout, path, strlen(path)); |
1464 | if (response() < 0) { | |
1465 | closedir(dirp); | |
1466 | return; | |
1467 | } | |
1468 | while ((dp = readdir(dirp)) != NULL) { | |
1469 | if (dp->d_ino == 0) | |
1470 | continue; | |
1471 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
1472 | continue; | |
1473 | if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { | |
1474 | run_err("%s/%s: name too long", name, dp->d_name); | |
1475 | continue; | |
1476 | } | |
1477 | (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); | |
1478 | vect[0] = path; | |
1479 | source(1, vect); | |
1480 | } | |
1481 | (void) closedir(dirp); | |
1482 | (void) atomicio(vwrite, remout, "E\n", 2); | |
1483 | (void) response(); | |
1484 | } | |
1485 | ||
50a69bb5 SW |
1486 | void |
1487 | sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn) | |
1488 | { | |
1489 | char *abs_src = NULL; | |
1490 | char *abs_dst = NULL; | |
1491 | glob_t g; | |
1492 | char *filename, *tmp = NULL; | |
ee116499 AHJ |
1493 | int i, r, err = 0, dst_is_dir; |
1494 | struct stat st; | |
50a69bb5 SW |
1495 | |
1496 | memset(&g, 0, sizeof(g)); | |
ee116499 | 1497 | |
50a69bb5 SW |
1498 | /* |
1499 | * Here, we need remote glob as SFTP can not depend on remote shell | |
1500 | * expansions | |
1501 | */ | |
50a69bb5 SW |
1502 | if ((abs_src = prepare_remote_path(conn, src)) == NULL) { |
1503 | err = -1; | |
1504 | goto out; | |
1505 | } | |
1506 | ||
1507 | debug3_f("copying remote %s to local %s", abs_src, dst); | |
1508 | if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { | |
1509 | if (r == GLOB_NOSPACE) | |
ee116499 | 1510 | error("%s: too many glob matches", src); |
50a69bb5 | 1511 | else |
ee116499 | 1512 | error("%s: %s", src, strerror(ENOENT)); |
50a69bb5 SW |
1513 | err = -1; |
1514 | goto out; | |
1515 | } | |
1516 | ||
ee116499 AHJ |
1517 | if ((r = stat(dst, &st)) != 0) |
1518 | debug2_f("stat local \"%s\": %s", dst, strerror(errno)); | |
1519 | dst_is_dir = r == 0 && S_ISDIR(st.st_mode); | |
1520 | ||
1521 | if (g.gl_matchc > 1 && !dst_is_dir) { | |
1522 | if (r == 0) { | |
1523 | error("Multiple files match pattern, but destination " | |
1524 | "\"%s\" is not a directory", dst); | |
1525 | err = -1; | |
1526 | goto out; | |
1527 | } | |
1528 | debug2_f("creating destination \"%s\"", dst); | |
1529 | if (mkdir(dst, 0777) != 0) { | |
1530 | error("local mkdir \"%s\": %s", dst, strerror(errno)); | |
1531 | err = -1; | |
1532 | goto out; | |
1533 | } | |
1534 | dst_is_dir = 1; | |
50a69bb5 SW |
1535 | } |
1536 | ||
1537 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { | |
1538 | tmp = xstrdup(g.gl_pathv[i]); | |
1539 | if ((filename = basename(tmp)) == NULL) { | |
1540 | error("basename %s: %s", tmp, strerror(errno)); | |
1541 | err = -1; | |
1542 | goto out; | |
1543 | } | |
1544 | ||
ee116499 | 1545 | if (dst_is_dir) |
50a69bb5 SW |
1546 | abs_dst = path_append(dst, filename); |
1547 | else | |
1548 | abs_dst = xstrdup(dst); | |
1549 | ||
1550 | debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); | |
1551 | if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { | |
1552 | if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, | |
ee116499 | 1553 | pflag, SFTP_PROGRESS_ONLY, 0, 0, 1, 1) == -1) |
50a69bb5 SW |
1554 | err = -1; |
1555 | } else { | |
1556 | if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, | |
ee116499 | 1557 | pflag, 0, 0, 1) == -1) |
50a69bb5 SW |
1558 | err = -1; |
1559 | } | |
1560 | free(abs_dst); | |
1561 | abs_dst = NULL; | |
1562 | free(tmp); | |
1563 | tmp = NULL; | |
1564 | } | |
1565 | ||
1566 | out: | |
1567 | free(abs_src); | |
1568 | free(tmp); | |
1569 | globfree(&g); | |
1570 | if (err == -1) | |
1571 | errs = 1; | |
1572 | } | |
1573 | ||
1574 | ||
ce74baca MD |
1575 | #define TYPE_OVERFLOW(type, val) \ |
1576 | ((sizeof(type) == 4 && (val) > INT32_MAX) || \ | |
1577 | (sizeof(type) == 8 && (val) > INT64_MAX) || \ | |
1578 | (sizeof(type) != 4 && sizeof(type) != 8)) | |
1579 | ||
16c343f1 | 1580 | void |
664f4763 | 1581 | sink(int argc, char **argv, const char *src) |
16c343f1 PA |
1582 | { |
1583 | static BUF buffer; | |
1584 | struct stat stb; | |
16c343f1 PA |
1585 | BUF *bp; |
1586 | off_t i; | |
1587 | size_t j, count; | |
1588 | int amt, exists, first, ofd; | |
1589 | mode_t mode, omode, mask; | |
1590 | off_t size, statbytes; | |
36e94dc5 | 1591 | unsigned long long ull; |
0cbfa66c | 1592 | int setimes, targisdir, wrerr; |
e9778795 | 1593 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; |
664f4763 | 1594 | char **patterns = NULL; |
1595 | size_t n, npatterns = 0; | |
16c343f1 PA |
1596 | struct timeval tv[2]; |
1597 | ||
1598 | #define atime tv[0] | |
1599 | #define mtime tv[1] | |
1600 | #define SCREWUP(str) { why = str; goto screwup; } | |
1601 | ||
ce74baca MD |
1602 | if (TYPE_OVERFLOW(time_t, 0) || TYPE_OVERFLOW(off_t, 0)) |
1603 | SCREWUP("Unexpected off_t/time_t size"); | |
1604 | ||
16c343f1 PA |
1605 | setimes = targisdir = 0; |
1606 | mask = umask(0); | |
1607 | if (!pflag) | |
1608 | (void) umask(mask); | |
1609 | if (argc != 1) { | |
1610 | run_err("ambiguous target"); | |
1611 | exit(1); | |
1612 | } | |
1613 | targ = *argv; | |
1614 | if (targetshouldbedirectory) | |
1615 | verifydir(targ); | |
1616 | ||
1617 | (void) atomicio(vwrite, remout, "", 1); | |
1618 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | |
1619 | targisdir = 1; | |
664f4763 | 1620 | if (src != NULL && !iamrecursive && !Tflag) { |
1621 | /* | |
1622 | * Prepare to try to restrict incoming filenames to match | |
1623 | * the requested destination file glob. | |
1624 | */ | |
1625 | if (brace_expand(src, &patterns, &npatterns) != 0) | |
50a69bb5 | 1626 | fatal_f("could not expand pattern"); |
664f4763 | 1627 | } |
16c343f1 PA |
1628 | for (first = 1;; first = 0) { |
1629 | cp = buf; | |
1630 | if (atomicio(read, remin, cp, 1) != 1) | |
664f4763 | 1631 | goto done; |
16c343f1 PA |
1632 | if (*cp++ == '\n') |
1633 | SCREWUP("unexpected <newline>"); | |
1634 | do { | |
1635 | if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) | |
1636 | SCREWUP("lost connection"); | |
1637 | *cp++ = ch; | |
1638 | } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); | |
1639 | *cp = 0; | |
1640 | if (verbose_mode) | |
e9778795 | 1641 | fmprintf(stderr, "Sink: %s", buf); |
16c343f1 PA |
1642 | |
1643 | if (buf[0] == '\01' || buf[0] == '\02') { | |
e9778795 PA |
1644 | if (iamremote == 0) { |
1645 | (void) snmprintf(visbuf, sizeof(visbuf), | |
1646 | NULL, "%s", buf + 1); | |
16c343f1 | 1647 | (void) atomicio(vwrite, STDERR_FILENO, |
e9778795 PA |
1648 | visbuf, strlen(visbuf)); |
1649 | } | |
16c343f1 PA |
1650 | if (buf[0] == '\02') |
1651 | exit(1); | |
1652 | ++errs; | |
1653 | continue; | |
1654 | } | |
1655 | if (buf[0] == 'E') { | |
1656 | (void) atomicio(vwrite, remout, "", 1); | |
664f4763 | 1657 | goto done; |
16c343f1 PA |
1658 | } |
1659 | if (ch == '\n') | |
1660 | *--cp = 0; | |
1661 | ||
1662 | cp = buf; | |
1663 | if (*cp == 'T') { | |
1664 | setimes++; | |
1665 | cp++; | |
36e94dc5 PA |
1666 | if (!isdigit((unsigned char)*cp)) |
1667 | SCREWUP("mtime.sec not present"); | |
1668 | ull = strtoull(cp, &cp, 10); | |
16c343f1 PA |
1669 | if (!cp || *cp++ != ' ') |
1670 | SCREWUP("mtime.sec not delimited"); | |
ce74baca | 1671 | if (TYPE_OVERFLOW(time_t, ull)) |
36e94dc5 PA |
1672 | setimes = 0; /* out of range */ |
1673 | mtime.tv_sec = ull; | |
16c343f1 | 1674 | mtime.tv_usec = strtol(cp, &cp, 10); |
36e94dc5 PA |
1675 | if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || |
1676 | mtime.tv_usec > 999999) | |
16c343f1 | 1677 | SCREWUP("mtime.usec not delimited"); |
36e94dc5 PA |
1678 | if (!isdigit((unsigned char)*cp)) |
1679 | SCREWUP("atime.sec not present"); | |
1680 | ull = strtoull(cp, &cp, 10); | |
16c343f1 PA |
1681 | if (!cp || *cp++ != ' ') |
1682 | SCREWUP("atime.sec not delimited"); | |
ce74baca | 1683 | if (TYPE_OVERFLOW(time_t, ull)) |
36e94dc5 PA |
1684 | setimes = 0; /* out of range */ |
1685 | atime.tv_sec = ull; | |
16c343f1 | 1686 | atime.tv_usec = strtol(cp, &cp, 10); |
36e94dc5 PA |
1687 | if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || |
1688 | atime.tv_usec > 999999) | |
16c343f1 PA |
1689 | SCREWUP("atime.usec not delimited"); |
1690 | (void) atomicio(vwrite, remout, "", 1); | |
1691 | continue; | |
1692 | } | |
1693 | if (*cp != 'C' && *cp != 'D') { | |
1694 | /* | |
1695 | * Check for the case "rcp remote:foo\* local:bar". | |
1696 | * In this case, the line "No match." can be returned | |
1697 | * by the shell before the rcp command on the remote is | |
1698 | * executed so the ^Aerror_message convention isn't | |
1699 | * followed. | |
1700 | */ | |
1701 | if (first) { | |
1702 | run_err("%s", cp); | |
1703 | exit(1); | |
1704 | } | |
1705 | SCREWUP("expected control record"); | |
1706 | } | |
1707 | mode = 0; | |
1708 | for (++cp; cp < buf + 5; cp++) { | |
1709 | if (*cp < '0' || *cp > '7') | |
1710 | SCREWUP("bad mode"); | |
1711 | mode = (mode << 3) | (*cp - '0'); | |
1712 | } | |
664f4763 | 1713 | if (!pflag) |
1714 | mode &= ~mask; | |
16c343f1 PA |
1715 | if (*cp++ != ' ') |
1716 | SCREWUP("mode not delimited"); | |
1717 | ||
ce74baca MD |
1718 | if (!isdigit((unsigned char)*cp)) |
1719 | SCREWUP("size not present"); | |
1720 | ull = strtoull(cp, &cp, 10); | |
1721 | if (!cp || *cp++ != ' ') | |
16c343f1 | 1722 | SCREWUP("size not delimited"); |
ce74baca MD |
1723 | if (TYPE_OVERFLOW(off_t, ull)) |
1724 | SCREWUP("size out of range"); | |
1725 | size = (off_t)ull; | |
1726 | ||
664f4763 | 1727 | if (*cp == '\0' || strchr(cp, '/') != NULL || |
1728 | strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { | |
16c343f1 PA |
1729 | run_err("error: unexpected filename: %s", cp); |
1730 | exit(1); | |
1731 | } | |
664f4763 | 1732 | if (npatterns > 0) { |
1733 | for (n = 0; n < npatterns; n++) { | |
1734 | if (fnmatch(patterns[n], cp, 0) == 0) | |
1735 | break; | |
1736 | } | |
1737 | if (n >= npatterns) | |
1738 | SCREWUP("filename does not match request"); | |
1739 | } | |
16c343f1 PA |
1740 | if (targisdir) { |
1741 | static char *namebuf; | |
1742 | static size_t cursize; | |
1743 | size_t need; | |
1744 | ||
1745 | need = strlen(targ) + strlen(cp) + 250; | |
1746 | if (need > cursize) { | |
36e94dc5 | 1747 | free(namebuf); |
16c343f1 PA |
1748 | namebuf = xmalloc(need); |
1749 | cursize = need; | |
1750 | } | |
1751 | (void) snprintf(namebuf, need, "%s%s%s", targ, | |
1752 | strcmp(targ, "/") ? "/" : "", cp); | |
1753 | np = namebuf; | |
1754 | } else | |
1755 | np = targ; | |
1756 | curfile = cp; | |
1757 | exists = stat(np, &stb) == 0; | |
1758 | if (buf[0] == 'D') { | |
1759 | int mod_flag = pflag; | |
1760 | if (!iamrecursive) | |
1761 | SCREWUP("received directory without -r"); | |
1762 | if (exists) { | |
1763 | if (!S_ISDIR(stb.st_mode)) { | |
1764 | errno = ENOTDIR; | |
1765 | goto bad; | |
1766 | } | |
1767 | if (pflag) | |
1768 | (void) chmod(np, mode); | |
1769 | } else { | |
50a69bb5 | 1770 | /* Handle copying from a read-only directory */ |
16c343f1 | 1771 | mod_flag = 1; |
0cbfa66c | 1772 | if (mkdir(np, mode | S_IRWXU) == -1) |
16c343f1 PA |
1773 | goto bad; |
1774 | } | |
1775 | vect[0] = xstrdup(np); | |
664f4763 | 1776 | sink(1, vect, src); |
16c343f1 PA |
1777 | if (setimes) { |
1778 | setimes = 0; | |
0cbfa66c | 1779 | (void) utimes(vect[0], tv); |
16c343f1 PA |
1780 | } |
1781 | if (mod_flag) | |
1782 | (void) chmod(vect[0], mode); | |
36e94dc5 | 1783 | free(vect[0]); |
16c343f1 PA |
1784 | continue; |
1785 | } | |
1786 | omode = mode; | |
36e94dc5 | 1787 | mode |= S_IWUSR; |
0cbfa66c | 1788 | if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) == -1) { |
16c343f1 PA |
1789 | bad: run_err("%s: %s", np, strerror(errno)); |
1790 | continue; | |
1791 | } | |
1792 | (void) atomicio(vwrite, remout, "", 1); | |
1793 | if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { | |
1794 | (void) close(ofd); | |
1795 | continue; | |
1796 | } | |
1797 | cp = bp->buf; | |
0cbfa66c | 1798 | wrerr = 0; |
16c343f1 | 1799 | |
0cbfa66c DF |
1800 | /* |
1801 | * NB. do not use run_err() unless immediately followed by | |
1802 | * exit() below as it may send a spurious reply that might | |
1803 | * desyncronise us from the peer. Use note_err() instead. | |
1804 | */ | |
16c343f1 PA |
1805 | statbytes = 0; |
1806 | if (showprogress) | |
1807 | start_progress_meter(curfile, size, &statbytes); | |
1808 | set_nonblock(remin); | |
1809 | for (count = i = 0; i < size; i += bp->cnt) { | |
1810 | amt = bp->cnt; | |
1811 | if (i + amt > size) | |
1812 | amt = size - i; | |
1813 | count += amt; | |
1814 | do { | |
9f304aaf PA |
1815 | j = atomicio6(read, remin, cp, amt, |
1816 | scpio, &statbytes); | |
16c343f1 PA |
1817 | if (j == 0) { |
1818 | run_err("%s", j != EPIPE ? | |
1819 | strerror(errno) : | |
1820 | "dropped connection"); | |
1821 | exit(1); | |
1822 | } | |
1823 | amt -= j; | |
1824 | cp += j; | |
1825 | } while (amt > 0); | |
1826 | ||
1827 | if (count == bp->cnt) { | |
1828 | /* Keep reading so we stay sync'd up. */ | |
0cbfa66c | 1829 | if (!wrerr) { |
16c343f1 PA |
1830 | if (atomicio(vwrite, ofd, bp->buf, |
1831 | count) != count) { | |
0cbfa66c DF |
1832 | note_err("%s: %s", np, |
1833 | strerror(errno)); | |
1834 | wrerr = 1; | |
16c343f1 PA |
1835 | } |
1836 | } | |
1837 | count = 0; | |
1838 | cp = bp->buf; | |
1839 | } | |
1840 | } | |
1841 | unset_nonblock(remin); | |
0cbfa66c | 1842 | if (count != 0 && !wrerr && |
16c343f1 | 1843 | atomicio(vwrite, ofd, bp->buf, count) != count) { |
0cbfa66c DF |
1844 | note_err("%s: %s", np, strerror(errno)); |
1845 | wrerr = 1; | |
16c343f1 | 1846 | } |
0cbfa66c DF |
1847 | if (!wrerr && (!exists || S_ISREG(stb.st_mode)) && |
1848 | ftruncate(ofd, size) != 0) | |
1849 | note_err("%s: truncate: %s", np, strerror(errno)); | |
16c343f1 PA |
1850 | if (pflag) { |
1851 | if (exists || omode != mode) | |
1852 | #ifdef HAVE_FCHMOD | |
1853 | if (fchmod(ofd, omode)) { | |
1854 | #else /* HAVE_FCHMOD */ | |
1855 | if (chmod(np, omode)) { | |
1856 | #endif /* HAVE_FCHMOD */ | |
0cbfa66c | 1857 | note_err("%s: set mode: %s", |
16c343f1 | 1858 | np, strerror(errno)); |
16c343f1 PA |
1859 | } |
1860 | } else { | |
1861 | if (!exists && omode != mode) | |
1862 | #ifdef HAVE_FCHMOD | |
1863 | if (fchmod(ofd, omode & ~mask)) { | |
1864 | #else /* HAVE_FCHMOD */ | |
1865 | if (chmod(np, omode & ~mask)) { | |
1866 | #endif /* HAVE_FCHMOD */ | |
0cbfa66c | 1867 | note_err("%s: set mode: %s", |
16c343f1 | 1868 | np, strerror(errno)); |
16c343f1 PA |
1869 | } |
1870 | } | |
0cbfa66c | 1871 | if (close(ofd) == -1) |
50a69bb5 | 1872 | note_err("%s: close: %s", np, strerror(errno)); |
16c343f1 | 1873 | (void) response(); |
e9778795 PA |
1874 | if (showprogress) |
1875 | stop_progress_meter(); | |
0cbfa66c | 1876 | if (setimes && !wrerr) { |
16c343f1 | 1877 | setimes = 0; |
0cbfa66c DF |
1878 | if (utimes(np, tv) == -1) { |
1879 | note_err("%s: set times: %s", | |
16c343f1 | 1880 | np, strerror(errno)); |
16c343f1 PA |
1881 | } |
1882 | } | |
0cbfa66c DF |
1883 | /* If no error was noted then signal success for this file */ |
1884 | if (note_err(NULL) == 0) | |
16c343f1 | 1885 | (void) atomicio(vwrite, remout, "", 1); |
16c343f1 | 1886 | } |
664f4763 | 1887 | done: |
1888 | for (n = 0; n < npatterns; n++) | |
1889 | free(patterns[n]); | |
1890 | free(patterns); | |
1891 | return; | |
16c343f1 | 1892 | screwup: |
664f4763 | 1893 | for (n = 0; n < npatterns; n++) |
1894 | free(patterns[n]); | |
1895 | free(patterns); | |
16c343f1 PA |
1896 | run_err("protocol error: %s", why); |
1897 | exit(1); | |
1898 | } | |
1899 | ||
50a69bb5 SW |
1900 | void |
1901 | throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to, | |
1902 | char *src, char *targ) | |
1903 | { | |
1904 | char *target = NULL, *filename = NULL, *abs_dst = NULL; | |
1905 | char *abs_src = NULL, *tmp = NULL; | |
1906 | glob_t g; | |
1907 | int i, r, targetisdir, err = 0; | |
1908 | ||
1909 | if ((filename = basename(src)) == NULL) | |
1910 | fatal("basename %s: %s", src, strerror(errno)); | |
1911 | ||
1912 | if ((abs_src = prepare_remote_path(from, src)) == NULL || | |
1913 | (target = prepare_remote_path(to, targ)) == NULL) | |
1914 | cleanup_exit(255); | |
1915 | memset(&g, 0, sizeof(g)); | |
1916 | ||
1917 | targetisdir = remote_is_dir(to, target); | |
1918 | if (!targetisdir && targetshouldbedirectory) { | |
ee116499 | 1919 | error("%s: destination is not a directory", targ); |
50a69bb5 SW |
1920 | err = -1; |
1921 | goto out; | |
1922 | } | |
1923 | ||
1924 | debug3_f("copying remote %s to remote %s", abs_src, target); | |
1925 | if ((r = remote_glob(from, abs_src, GLOB_MARK, NULL, &g)) != 0) { | |
1926 | if (r == GLOB_NOSPACE) | |
ee116499 | 1927 | error("%s: too many glob matches", src); |
50a69bb5 | 1928 | else |
ee116499 | 1929 | error("%s: %s", src, strerror(ENOENT)); |
50a69bb5 SW |
1930 | err = -1; |
1931 | goto out; | |
1932 | } | |
1933 | ||
1934 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { | |
1935 | tmp = xstrdup(g.gl_pathv[i]); | |
1936 | if ((filename = basename(tmp)) == NULL) { | |
1937 | error("basename %s: %s", tmp, strerror(errno)); | |
1938 | err = -1; | |
1939 | goto out; | |
1940 | } | |
1941 | ||
1942 | if (targetisdir) | |
1943 | abs_dst = path_append(target, filename); | |
1944 | else | |
1945 | abs_dst = xstrdup(target); | |
1946 | ||
1947 | debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); | |
1948 | if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) { | |
1949 | if (crossload_dir(from, to, g.gl_pathv[i], abs_dst, | |
1950 | NULL, pflag, SFTP_PROGRESS_ONLY, 1) == -1) | |
1951 | err = -1; | |
1952 | } else { | |
1953 | if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL, | |
1954 | pflag) == -1) | |
1955 | err = -1; | |
1956 | } | |
1957 | free(abs_dst); | |
1958 | abs_dst = NULL; | |
1959 | free(tmp); | |
1960 | tmp = NULL; | |
1961 | } | |
1962 | ||
1963 | out: | |
1964 | free(abs_src); | |
1965 | free(abs_dst); | |
1966 | free(target); | |
1967 | free(tmp); | |
1968 | globfree(&g); | |
1969 | if (err == -1) | |
1970 | errs = 1; | |
1971 | } | |
1972 | ||
16c343f1 PA |
1973 | int |
1974 | response(void) | |
1975 | { | |
e9778795 | 1976 | char ch, *cp, resp, rbuf[2048], visbuf[2048]; |
16c343f1 PA |
1977 | |
1978 | if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) | |
1979 | lostconn(0); | |
1980 | ||
1981 | cp = rbuf; | |
1982 | switch (resp) { | |
1983 | case 0: /* ok */ | |
1984 | return (0); | |
1985 | default: | |
1986 | *cp++ = resp; | |
1987 | /* FALLTHROUGH */ | |
1988 | case 1: /* error, followed by error msg */ | |
1989 | case 2: /* fatal error, "" */ | |
1990 | do { | |
1991 | if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) | |
1992 | lostconn(0); | |
1993 | *cp++ = ch; | |
1994 | } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); | |
1995 | ||
e9778795 PA |
1996 | if (!iamremote) { |
1997 | cp[-1] = '\0'; | |
1998 | (void) snmprintf(visbuf, sizeof(visbuf), | |
1999 | NULL, "%s\n", rbuf); | |
2000 | (void) atomicio(vwrite, STDERR_FILENO, | |
2001 | visbuf, strlen(visbuf)); | |
2002 | } | |
16c343f1 PA |
2003 | ++errs; |
2004 | if (resp == 1) | |
2005 | return (-1); | |
2006 | exit(1); | |
2007 | } | |
2008 | /* NOTREACHED */ | |
2009 | } | |
2010 | ||
2011 | void | |
2012 | usage(void) | |
2013 | { | |
2014 | (void) fprintf(stderr, | |
50a69bb5 SW |
2015 | "usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]\n" |
2016 | " [-i identity_file] [-J destination] [-l limit]\n" | |
2017 | " [-o ssh_option] [-P port] [-S program] source ... target\n"); | |
16c343f1 PA |
2018 | exit(1); |
2019 | } | |
2020 | ||
2021 | void | |
2022 | run_err(const char *fmt,...) | |
2023 | { | |
2024 | static FILE *fp; | |
2025 | va_list ap; | |
2026 | ||
2027 | ++errs; | |
2028 | if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { | |
2029 | (void) fprintf(fp, "%c", 0x01); | |
2030 | (void) fprintf(fp, "scp: "); | |
2031 | va_start(ap, fmt); | |
2032 | (void) vfprintf(fp, fmt, ap); | |
2033 | va_end(ap); | |
2034 | (void) fprintf(fp, "\n"); | |
2035 | (void) fflush(fp); | |
2036 | } | |
2037 | ||
2038 | if (!iamremote) { | |
2039 | va_start(ap, fmt); | |
e9778795 | 2040 | vfmprintf(stderr, fmt, ap); |
16c343f1 PA |
2041 | va_end(ap); |
2042 | fprintf(stderr, "\n"); | |
2043 | } | |
2044 | } | |
2045 | ||
0cbfa66c DF |
2046 | /* |
2047 | * Notes a sink error for sending at the end of a file transfer. Returns 0 if | |
2048 | * no error has been noted or -1 otherwise. Use note_err(NULL) to flush | |
2049 | * any active error at the end of the transfer. | |
2050 | */ | |
2051 | int | |
2052 | note_err(const char *fmt, ...) | |
2053 | { | |
2054 | static char *emsg; | |
2055 | va_list ap; | |
2056 | ||
2057 | /* Replay any previously-noted error */ | |
2058 | if (fmt == NULL) { | |
2059 | if (emsg == NULL) | |
2060 | return 0; | |
2061 | run_err("%s", emsg); | |
2062 | free(emsg); | |
2063 | emsg = NULL; | |
2064 | return -1; | |
2065 | } | |
2066 | ||
2067 | errs++; | |
2068 | /* Prefer first-noted error */ | |
2069 | if (emsg != NULL) | |
2070 | return -1; | |
2071 | ||
2072 | va_start(ap, fmt); | |
2073 | vasnmprintf(&emsg, INT_MAX, NULL, fmt, ap); | |
2074 | va_end(ap); | |
2075 | return -1; | |
2076 | } | |
2077 | ||
16c343f1 PA |
2078 | void |
2079 | verifydir(char *cp) | |
2080 | { | |
2081 | struct stat stb; | |
2082 | ||
2083 | if (!stat(cp, &stb)) { | |
2084 | if (S_ISDIR(stb.st_mode)) | |
2085 | return; | |
2086 | errno = ENOTDIR; | |
2087 | } | |
2088 | run_err("%s: %s", cp, strerror(errno)); | |
2089 | killchild(0); | |
2090 | } | |
2091 | ||
2092 | int | |
2093 | okname(char *cp0) | |
2094 | { | |
2095 | int c; | |
2096 | char *cp; | |
2097 | ||
2098 | cp = cp0; | |
2099 | do { | |
2100 | c = (int)*cp; | |
2101 | if (c & 0200) | |
2102 | goto bad; | |
36e94dc5 | 2103 | if (!isalpha(c) && !isdigit((unsigned char)c)) { |
16c343f1 PA |
2104 | switch (c) { |
2105 | case '\'': | |
2106 | case '"': | |
2107 | case '`': | |
2108 | case ' ': | |
2109 | case '#': | |
2110 | goto bad; | |
2111 | default: | |
2112 | break; | |
2113 | } | |
2114 | } | |
2115 | } while (*++cp); | |
2116 | return (1); | |
2117 | ||
e9778795 | 2118 | bad: fmprintf(stderr, "%s: invalid user name\n", cp0); |
16c343f1 PA |
2119 | return (0); |
2120 | } | |
2121 | ||
2122 | BUF * | |
2123 | allocbuf(BUF *bp, int fd, int blksize) | |
2124 | { | |
2125 | size_t size; | |
2126 | #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE | |
2127 | struct stat stb; | |
2128 | ||
0cbfa66c | 2129 | if (fstat(fd, &stb) == -1) { |
16c343f1 PA |
2130 | run_err("fstat: %s", strerror(errno)); |
2131 | return (0); | |
2132 | } | |
ce74baca | 2133 | size = ROUNDUP(stb.st_blksize, blksize); |
16c343f1 PA |
2134 | if (size == 0) |
2135 | size = blksize; | |
2136 | #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ | |
2137 | size = blksize; | |
2138 | #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ | |
2139 | if (bp->cnt >= size) | |
2140 | return (bp); | |
ce74baca | 2141 | bp->buf = xrecallocarray(bp->buf, bp->cnt, size, 1); |
16c343f1 PA |
2142 | bp->cnt = size; |
2143 | return (bp); | |
2144 | } | |
2145 | ||
2146 | void | |
2147 | lostconn(int signo) | |
2148 | { | |
2149 | if (!iamremote) | |
36e94dc5 | 2150 | (void)write(STDERR_FILENO, "lost connection\n", 16); |
16c343f1 PA |
2151 | if (signo) |
2152 | _exit(1); | |
2153 | else | |
2154 | exit(1); | |
2155 | } | |
50a69bb5 SW |
2156 | |
2157 | void | |
2158 | cleanup_exit(int i) | |
2159 | { | |
2160 | if (remin > 0) | |
2161 | close(remin); | |
2162 | if (remout > 0) | |
2163 | close(remout); | |
2164 | if (remin2 > 0) | |
2165 | close(remin2); | |
2166 | if (remout2 > 0) | |
2167 | close(remout2); | |
2168 | if (do_cmd_pid > 0) | |
2169 | waitpid(do_cmd_pid, NULL, 0); | |
2170 | if (do_cmd_pid2 > 0) | |
2171 | waitpid(do_cmd_pid2, NULL, 0); | |
2172 | exit(i); | |
2173 | } |