Commit | Line | Data |
---|---|---|
22e155fc | 1 | /* |
cd5a06d8 KB |
2 | * Copyright (c) 1983, 1990, 1992, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
ebb479fc | 4 | * |
27c71911 | 5 | * %sccs.include.redist.c% |
22e155fc DF |
6 | */ |
7 | ||
8 | #ifndef lint | |
cd5a06d8 KB |
9 | static char copyright[] = |
10 | "@(#) Copyright (c) 1983, 1990, 1992, 1993\n\ | |
11 | The Regents of the University of California. All rights reserved.\n"; | |
ebb479fc | 12 | #endif /* not lint */ |
22e155fc | 13 | |
b7ff3bb3 | 14 | #ifndef lint |
cd5a06d8 | 15 | static char sccsid[] = "@(#)rcp.c 8.1 (Berkeley) %G%"; |
ebb479fc | 16 | #endif /* not lint */ |
b7ff3bb3 | 17 | |
ed0072ae | 18 | #include <sys/param.h> |
b7ff3bb3 | 19 | #include <sys/stat.h> |
af86e024 | 20 | #include <sys/time.h> |
a045d3a2 | 21 | #include <sys/socket.h> |
94a2d2a7 | 22 | #include <netinet/in.h> |
f8b77578 MK |
23 | #include <netinet/in_systm.h> |
24 | #include <netinet/ip.h> | |
0b35f0fe | 25 | |
bd7da30a | 26 | #include <ctype.h> |
a045d3a2 | 27 | #include <dirent.h> |
8809a3e2 | 28 | #include <err.h> |
bd7da30a | 29 | #include <errno.h> |
a045d3a2 | 30 | #include <fcntl.h> |
bd7da30a | 31 | #include <netdb.h> |
b7ff3bb3 | 32 | #include <pwd.h> |
bd7da30a | 33 | #include <signal.h> |
35673a6d | 34 | #include <stdio.h> |
42029053 | 35 | #include <stdlib.h> |
a045d3a2 | 36 | #include <string.h> |
bd7da30a KB |
37 | #include <string.h> |
38 | #include <unistd.h> | |
39 | ||
e4ba000a | 40 | #include "pathnames.h" |
0b35f0fe | 41 | #include "extern.h" |
4ca10280 | 42 | |
35673a6d | 43 | #ifdef KERBEROS |
1b8d273e | 44 | #include <kerberosIV/des.h> |
579f51c2 | 45 | #include <kerberosIV/krb.h> |
0b35f0fe | 46 | |
5d178979 KF |
47 | char dst_realm_buf[REALM_SZ]; |
48 | char *dest_realm = NULL; | |
898015bc | 49 | int use_kerberos = 1; |
5d178979 KF |
50 | CREDENTIALS cred; |
51 | Key_schedule schedule; | |
52 | extern char *krb_realmofhost(); | |
0b35f0fe | 53 | #define OPTIONS "dfKk:prt" |
898015bc | 54 | #else |
5d178979 | 55 | #define OPTIONS "dfprt" |
2b6b7619 KF |
56 | #endif |
57 | ||
35673a6d | 58 | struct passwd *pwd; |
5d178979 KF |
59 | u_short port; |
60 | uid_t userid; | |
61 | int errs, rem; | |
62 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; | |
371655cc | 63 | |
5d178979 | 64 | #define CMDNEEDS 64 |
d4c44137 | 65 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ |
b7ff3bb3 | 66 | |
0b35f0fe KB |
67 | #ifdef KERBEROS |
68 | int kerberos __P((char **, char *, char *, char *)); | |
69 | void oldw __P((const char *, ...)); | |
70 | #endif | |
71 | int response __P((void)); | |
72 | void rsource __P((char *, struct stat *)); | |
73 | void sink __P((int, char *[])); | |
74 | void source __P((int, char *[])); | |
75 | void tolocal __P((int, char *[])); | |
76 | void toremote __P((char *, int, char *[])); | |
77 | void usage __P((void)); | |
a045d3a2 | 78 | |
0b35f0fe | 79 | int |
b7ff3bb3 BJ |
80 | main(argc, argv) |
81 | int argc; | |
0b35f0fe | 82 | char *argv[]; |
b7ff3bb3 | 83 | { |
11bfe9c9 | 84 | struct servent *sp; |
f95b553a | 85 | int ch, fflag, tflag; |
0b35f0fe | 86 | char *targ, *shell; |
92e58d95 | 87 | |
f95b553a | 88 | fflag = tflag = 0; |
5d178979 | 89 | while ((ch = getopt(argc, argv, OPTIONS)) != EOF) |
0b35f0fe KB |
90 | switch(ch) { /* User-visible flags. */ |
91 | case 'K': | |
92 | #ifdef KERBEROS | |
93 | use_kerberos = 0; | |
94 | #endif | |
af86e024 | 95 | break; |
5d178979 KF |
96 | #ifdef KERBEROS |
97 | case 'k': | |
5d178979 | 98 | dest_realm = dst_realm_buf; |
0b35f0fe | 99 | (void)strncpy(dst_realm_buf, optarg, REALM_SZ); |
f95b553a | 100 | break; |
35673a6d | 101 | #endif |
0b35f0fe KB |
102 | case 'p': |
103 | pflag = 1; | |
104 | break; | |
105 | case 'r': | |
106 | iamrecursive = 1; | |
107 | break; | |
108 | /* Server options. */ | |
5d178979 KF |
109 | case 'd': |
110 | targetshouldbedirectory = 1; | |
111 | break; | |
112 | case 'f': /* "from" */ | |
113 | iamremote = 1; | |
114 | fflag = 1; | |
115 | break; | |
116 | case 't': /* "to" */ | |
117 | iamremote = 1; | |
118 | tflag = 1; | |
119 | break; | |
35673a6d KB |
120 | case '?': |
121 | default: | |
5f286db0 | 122 | usage(); |
af86e024 | 123 | } |
35673a6d KB |
124 | argc -= optind; |
125 | argv += optind; | |
126 | ||
5d178979 | 127 | #ifdef KERBEROS |
0b35f0fe KB |
128 | if (use_kerberos) { |
129 | shell = "kshell"; | |
130 | if ((sp = getservbyname(shell, "tcp")) == NULL) { | |
131 | use_kerberos = 0; | |
132 | oldw("can't get entry for %s/tcp service", shell); | |
133 | sp = getservbyname(shell = "shell", "tcp"); | |
134 | } | |
135 | } else | |
42029053 | 136 | sp = getservbyname(shell = "shell", "tcp"); |
5d178979 | 137 | #else |
42029053 | 138 | sp = getservbyname(shell = "shell", "tcp"); |
5d178979 | 139 | #endif |
8809a3e2 KB |
140 | if (sp == NULL) |
141 | errx(1, "%s/tcp: unknown service", shell); | |
5d178979 KF |
142 | port = sp->s_port; |
143 | ||
8809a3e2 KB |
144 | if ((pwd = getpwuid(userid = getuid())) == NULL) |
145 | errx(1, "unknown user %d", (int)userid); | |
5d178979 | 146 | |
0b35f0fe KB |
147 | rem = STDIN_FILENO; /* XXX */ |
148 | ||
149 | if (fflag) { /* Follow "protocol", send data. */ | |
f95b553a KB |
150 | (void)response(); |
151 | (void)setuid(userid); | |
152 | source(argc, argv); | |
153 | exit(errs); | |
154 | } | |
155 | ||
0b35f0fe | 156 | if (tflag) { /* Receive data. */ |
f95b553a KB |
157 | (void)setuid(userid); |
158 | sink(argc, argv); | |
159 | exit(errs); | |
160 | } | |
161 | ||
5f286db0 KB |
162 | if (argc < 2) |
163 | usage(); | |
b7ff3bb3 BJ |
164 | if (argc > 2) |
165 | targetshouldbedirectory = 1; | |
35673a6d | 166 | |
5f286db0 | 167 | rem = -1; |
0b35f0fe | 168 | /* Command to be executed on remote system using "rsh". */ |
5d178979 | 169 | #ifdef KERBEROS |
a045d3a2 KB |
170 | (void)snprintf(cmd, sizeof(cmd), |
171 | "rcp%s%s%s%s", iamrecursive ? " -r" : "", | |
898015bc | 172 | "", |
5d178979 KF |
173 | pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); |
174 | #else | |
a045d3a2 KB |
175 | (void)snprintf(cmd, sizeof(cmd), "rcp%s%s%s", |
176 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
177 | targetshouldbedirectory ? " -d" : ""); | |
5d178979 | 178 | #endif |
35673a6d KB |
179 | |
180 | (void)signal(SIGPIPE, lostconn); | |
181 | ||
0b35f0fe KB |
182 | if (targ = colon(argv[argc - 1])) /* Dest is remote host. */ |
183 | toremote(targ, argc, argv); | |
35673a6d | 184 | else { |
0b35f0fe | 185 | tolocal(argc, argv); /* Dest is local host. */ |
35673a6d KB |
186 | if (targetshouldbedirectory) |
187 | verifydir(argv[argc - 1]); | |
188 | } | |
189 | exit(errs); | |
190 | } | |
191 | ||
0b35f0fe | 192 | void |
35673a6d | 193 | toremote(targ, argc, argv) |
0b35f0fe | 194 | char *targ, *argv[]; |
35673a6d | 195 | int argc; |
35673a6d | 196 | { |
a045d3a2 | 197 | int i, len, tos; |
d4c44137 | 198 | char *bp, *host, *src, *suser, *thost, *tuser; |
35673a6d KB |
199 | |
200 | *targ++ = 0; | |
201 | if (*targ == 0) | |
202 | targ = "."; | |
203 | ||
8809a3e2 | 204 | if (thost = strchr(argv[argc - 1], '@')) { |
5d178979 | 205 | /* user@host */ |
35673a6d KB |
206 | *thost++ = 0; |
207 | tuser = argv[argc - 1]; | |
208 | if (*tuser == '\0') | |
9e9252a8 | 209 | tuser = NULL; |
35673a6d KB |
210 | else if (!okname(tuser)) |
211 | exit(1); | |
212 | } else { | |
213 | thost = argv[argc - 1]; | |
214 | tuser = NULL; | |
215 | } | |
2b6b7619 | 216 | |
35673a6d KB |
217 | for (i = 0; i < argc - 1; i++) { |
218 | src = colon(argv[i]); | |
219 | if (src) { /* remote to remote */ | |
220 | *src++ = 0; | |
221 | if (*src == 0) | |
222 | src = "."; | |
8809a3e2 | 223 | host = strchr(argv[i], '@'); |
a045d3a2 KB |
224 | len = strlen(_PATH_RSH) + strlen(argv[i]) + |
225 | strlen(src) + (tuser ? strlen(tuser) : 0) + | |
226 | strlen(thost) + strlen(targ) + CMDNEEDS + 20; | |
227 | if (!(bp = malloc(len))) | |
8809a3e2 | 228 | err(1, NULL); |
35673a6d KB |
229 | if (host) { |
230 | *host++ = 0; | |
231 | suser = argv[i]; | |
232 | if (*suser == '\0') | |
233 | suser = pwd->pw_name; | |
234 | else if (!okname(suser)) | |
235 | continue; | |
a045d3a2 | 236 | (void)snprintf(bp, len, |
35673a6d KB |
237 | "%s %s -l %s -n %s %s '%s%s%s:%s'", |
238 | _PATH_RSH, host, suser, cmd, src, | |
239 | tuser ? tuser : "", tuser ? "@" : "", | |
240 | thost, targ); | |
241 | } else | |
a045d3a2 KB |
242 | (void)snprintf(bp, len, |
243 | "%s %s -n %s %s '%s%s%s:%s'", | |
35673a6d KB |
244 | _PATH_RSH, argv[i], cmd, src, |
245 | tuser ? tuser : "", tuser ? "@" : "", | |
246 | thost, targ); | |
0b35f0fe | 247 | (void)susystem(bp, userid); |
d4c44137 | 248 | (void)free(bp); |
35673a6d KB |
249 | } else { /* local to remote */ |
250 | if (rem == -1) { | |
a045d3a2 KB |
251 | len = strlen(targ) + CMDNEEDS + 20; |
252 | if (!(bp = malloc(len))) | |
8809a3e2 | 253 | err(1, NULL); |
a045d3a2 | 254 | (void)snprintf(bp, len, "%s -t %s", cmd, targ); |
35673a6d KB |
255 | host = thost; |
256 | #ifdef KERBEROS | |
257 | if (use_kerberos) | |
5d178979 KF |
258 | rem = kerberos(&host, bp, |
259 | pwd->pw_name, | |
d4c44137 | 260 | tuser ? tuser : pwd->pw_name); |
35673a6d KB |
261 | else |
262 | #endif | |
9e9252a8 JB |
263 | rem = rcmd(&host, port, pwd->pw_name, |
264 | tuser ? tuser : pwd->pw_name, | |
d4c44137 | 265 | bp, 0); |
35673a6d KB |
266 | if (rem < 0) |
267 | exit(1); | |
f8b77578 MK |
268 | tos = IPTOS_THROUGHPUT; |
269 | if (setsockopt(rem, IPPROTO_IP, IP_TOS, | |
0b35f0fe | 270 | &tos, sizeof(int)) < 0) |
8809a3e2 | 271 | warn("TOS (ignored)"); |
35673a6d KB |
272 | if (response() < 0) |
273 | exit(1); | |
d4c44137 | 274 | (void)free(bp); |
35673a6d | 275 | (void)setuid(userid); |
b7ff3bb3 | 276 | } |
35673a6d | 277 | source(1, argv+i); |
b7ff3bb3 | 278 | } |
35673a6d KB |
279 | } |
280 | } | |
281 | ||
0b35f0fe | 282 | void |
35673a6d KB |
283 | tolocal(argc, argv) |
284 | int argc; | |
0b35f0fe | 285 | char *argv[]; |
35673a6d | 286 | { |
a045d3a2 | 287 | int i, len, tos; |
d4c44137 | 288 | char *bp, *host, *src, *suser; |
35673a6d KB |
289 | |
290 | for (i = 0; i < argc - 1; i++) { | |
0b35f0fe | 291 | if (!(src = colon(argv[i]))) { /* Local to local. */ |
a045d3a2 KB |
292 | len = strlen(_PATH_CP) + strlen(argv[i]) + |
293 | strlen(argv[argc - 1]) + 20; | |
294 | if (!(bp = malloc(len))) | |
8809a3e2 | 295 | err(1, NULL); |
a045d3a2 | 296 | (void)snprintf(bp, len, "%s%s%s %s %s", _PATH_CP, |
d4c44137 KB |
297 | iamrecursive ? " -r" : "", pflag ? " -p" : "", |
298 | argv[i], argv[argc - 1]); | |
83cb1dc5 AC |
299 | if (susystem(bp, userid)) |
300 | ++errs; | |
d4c44137 KB |
301 | (void)free(bp); |
302 | continue; | |
303 | } | |
304 | *src++ = 0; | |
305 | if (*src == 0) | |
306 | src = "."; | |
8809a3e2 | 307 | if ((host = strchr(argv[i], '@')) == NULL) { |
0b35f0fe KB |
308 | host = argv[i]; |
309 | suser = pwd->pw_name; | |
310 | } else { | |
d4c44137 KB |
311 | *host++ = 0; |
312 | suser = argv[i]; | |
313 | if (*suser == '\0') | |
35673a6d | 314 | suser = pwd->pw_name; |
d4c44137 | 315 | else if (!okname(suser)) |
35673a6d | 316 | continue; |
b7ff3bb3 | 317 | } |
a045d3a2 | 318 | len = strlen(src) + CMDNEEDS + 20; |
0b35f0fe | 319 | if ((bp = malloc(len)) == NULL) |
8809a3e2 | 320 | err(1, NULL); |
a045d3a2 | 321 | (void)snprintf(bp, len, "%s -f %s", cmd, src); |
0b35f0fe | 322 | rem = |
d4c44137 | 323 | #ifdef KERBEROS |
0b35f0fe KB |
324 | use_kerberos ? |
325 | kerberos(&host, bp, pwd->pw_name, suser) : | |
d4c44137 | 326 | #endif |
0b35f0fe | 327 | rcmd(&host, port, pwd->pw_name, suser, bp, 0); |
d4c44137 | 328 | (void)free(bp); |
83cb1dc5 AC |
329 | if (rem < 0) { |
330 | ++errs; | |
d4c44137 | 331 | continue; |
83cb1dc5 | 332 | } |
f8b77578 MK |
333 | (void)seteuid(userid); |
334 | tos = IPTOS_THROUGHPUT; | |
0b35f0fe | 335 | if (setsockopt(rem, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) |
8809a3e2 | 336 | warn("TOS (ignored)"); |
d4c44137 | 337 | sink(1, argv + argc - 1); |
f8b77578 | 338 | (void)seteuid(0); |
d4c44137 KB |
339 | (void)close(rem); |
340 | rem = -1; | |
b7ff3bb3 | 341 | } |
b7ff3bb3 BJ |
342 | } |
343 | ||
0b35f0fe | 344 | void |
b7ff3bb3 BJ |
345 | source(argc, argv) |
346 | int argc; | |
0b35f0fe | 347 | char *argv[]; |
b7ff3bb3 | 348 | { |
b7ff3bb3 | 349 | struct stat stb; |
35673a6d KB |
350 | static BUF buffer; |
351 | BUF *bp; | |
5f10dd79 | 352 | off_t i; |
83cb1dc5 | 353 | int amt, fd, haderr, indx, result; |
35673a6d | 354 | char *last, *name, buf[BUFSIZ]; |
b7ff3bb3 | 355 | |
83cb1dc5 AC |
356 | for (indx = 0; indx < argc; ++indx) { |
357 | name = argv[indx]; | |
0b35f0fe KB |
358 | if ((fd = open(name, O_RDONLY, 0)) < 0) |
359 | goto syserr; | |
360 | if (fstat(fd, &stb)) { | |
8809a3e2 | 361 | syserr: run_err("%s: %s", name, strerror(errno)); |
0b35f0fe | 362 | goto next; |
b7ff3bb3 | 363 | } |
0b35f0fe | 364 | switch (stb.st_mode & S_IFMT) { |
b7ff3bb3 BJ |
365 | case S_IFREG: |
366 | break; | |
b7ff3bb3 BJ |
367 | case S_IFDIR: |
368 | if (iamrecursive) { | |
af86e024 | 369 | rsource(name, &stb); |
0b35f0fe | 370 | goto next; |
b7ff3bb3 | 371 | } |
35673a6d | 372 | /* FALLTHROUGH */ |
b7ff3bb3 | 373 | default: |
8809a3e2 | 374 | run_err("%s: not a regular file", name); |
0b35f0fe | 375 | goto next; |
b7ff3bb3 | 376 | } |
8809a3e2 | 377 | if ((last = strrchr(name, '/')) == NULL) |
b7ff3bb3 BJ |
378 | last = name; |
379 | else | |
0b35f0fe | 380 | ++last; |
af86e024 JL |
381 | if (pflag) { |
382 | /* | |
383 | * Make it compatible with possible future | |
384 | * versions expecting microseconds. | |
385 | */ | |
bd7da30a | 386 | (void)snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", |
0b35f0fe | 387 | stb.st_mtimespec.ts_sec, stb.st_atimespec.ts_sec); |
bd7da30a | 388 | (void)write(rem, buf, strlen(buf)); |
0b35f0fe KB |
389 | if (response() < 0) |
390 | goto next; | |
371655cc | 391 | } |
0b35f0fe | 392 | #define MODEMASK (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) |
bd7da30a | 393 | (void)snprintf(buf, sizeof(buf), "C%04o %qd %s\n", |
0b35f0fe | 394 | stb.st_mode & MODEMASK, stb.st_size, last); |
bd7da30a | 395 | (void)write(rem, buf, strlen(buf)); |
0b35f0fe KB |
396 | if (response() < 0) |
397 | goto next; | |
83cb1dc5 AC |
398 | if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { |
399 | next: (void)close(fd); | |
400 | continue; | |
401 | } | |
402 | ||
403 | /* Keep writing after an error so that we stay sync'd up. */ | |
404 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | |
371655cc | 405 | amt = bp->cnt; |
b7ff3bb3 BJ |
406 | if (i + amt > stb.st_size) |
407 | amt = stb.st_size - i; | |
83cb1dc5 AC |
408 | if (!haderr) { |
409 | result = read(fd, bp->buf, amt); | |
410 | if (result != amt) | |
411 | haderr = result >= 0 ? EIO : errno; | |
412 | } | |
413 | if (haderr) | |
414 | (void)write(rem, bp->buf, amt); | |
415 | else { | |
416 | result = write(rem, bp->buf, amt); | |
417 | if (result != amt) | |
418 | haderr = result >= 0 ? EIO : errno; | |
0b35f0fe | 419 | } |
b7ff3bb3 | 420 | } |
83cb1dc5 AC |
421 | if (close(fd) && !haderr) |
422 | haderr = errno; | |
423 | if (!haderr) | |
35673a6d | 424 | (void)write(rem, "", 1); |
83cb1dc5 | 425 | else |
8809a3e2 | 426 | run_err("%s: %s", name, strerror(haderr)); |
35673a6d | 427 | (void)response(); |
b7ff3bb3 BJ |
428 | } |
429 | } | |
430 | ||
0b35f0fe | 431 | void |
af86e024 | 432 | rsource(name, statp) |
b7ff3bb3 | 433 | char *name; |
af86e024 | 434 | struct stat *statp; |
b7ff3bb3 | 435 | { |
a045d3a2 KB |
436 | DIR *dirp; |
437 | struct dirent *dp; | |
d4c44137 | 438 | char *last, *vect[1], path[MAXPATHLEN]; |
b7ff3bb3 | 439 | |
a045d3a2 | 440 | if (!(dirp = opendir(name))) { |
8809a3e2 | 441 | run_err("%s: %s", name, strerror(errno)); |
b7ff3bb3 BJ |
442 | return; |
443 | } | |
8809a3e2 | 444 | last = strrchr(name, '/'); |
b7ff3bb3 BJ |
445 | if (last == 0) |
446 | last = name; | |
447 | else | |
448 | last++; | |
af86e024 | 449 | if (pflag) { |
bd7da30a | 450 | (void)snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", |
0b35f0fe | 451 | statp->st_mtimespec.ts_sec, statp->st_atimespec.ts_sec); |
bd7da30a | 452 | (void)write(rem, path, strlen(path)); |
af86e024 | 453 | if (response() < 0) { |
a045d3a2 | 454 | closedir(dirp); |
af86e024 JL |
455 | return; |
456 | } | |
457 | } | |
bd7da30a | 458 | (void)snprintf(path, sizeof(path), |
0b35f0fe | 459 | "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); |
bd7da30a | 460 | (void)write(rem, path, strlen(path)); |
b7ff3bb3 | 461 | if (response() < 0) { |
a045d3a2 | 462 | closedir(dirp); |
b7ff3bb3 BJ |
463 | return; |
464 | } | |
a045d3a2 | 465 | while (dp = readdir(dirp)) { |
b7ff3bb3 BJ |
466 | if (dp->d_ino == 0) |
467 | continue; | |
468 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
469 | continue; | |
d4c44137 | 470 | if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { |
8809a3e2 | 471 | run_err("%s/%s: name too long", name, dp->d_name); |
b7ff3bb3 BJ |
472 | continue; |
473 | } | |
a045d3a2 | 474 | (void)snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); |
d4c44137 KB |
475 | vect[0] = path; |
476 | source(1, vect); | |
b7ff3bb3 | 477 | } |
0b35f0fe | 478 | (void)closedir(dirp); |
35673a6d KB |
479 | (void)write(rem, "E\n", 2); |
480 | (void)response(); | |
b7ff3bb3 BJ |
481 | } |
482 | ||
a045d3a2 | 483 | void |
b7ff3bb3 BJ |
484 | sink(argc, argv) |
485 | int argc; | |
0b35f0fe | 486 | char *argv[]; |
b7ff3bb3 | 487 | { |
35673a6d | 488 | static BUF buffer; |
0b35f0fe | 489 | register char *cp; |
371655cc | 490 | struct stat stb; |
af86e024 | 491 | struct timeval tv[2]; |
ef49bf4f | 492 | enum { YES, NO, DISPLAYED } wrerr; |
0b35f0fe | 493 | BUF *bp; |
d4c44137 | 494 | off_t i, j; |
83cb1dc5 AC |
495 | int amt, count, exists, first, mask, mode, ofd, omode; |
496 | int setimes, size, targisdir, wrerrno; | |
497 | char ch, *np, *targ, *why, *vect[1], buf[BUFSIZ]; | |
b7ff3bb3 | 498 | |
d4c44137 KB |
499 | #define atime tv[0] |
500 | #define mtime tv[1] | |
501 | #define SCREWUP(str) { why = str; goto screwup; } | |
502 | ||
503 | setimes = targisdir = 0; | |
504 | mask = umask(0); | |
a4372a41 | 505 | if (!pflag) |
35673a6d | 506 | (void)umask(mask); |
11bfe9c9 | 507 | if (argc != 1) { |
8809a3e2 | 508 | run_err("ambiguous target"); |
b7ff3bb3 BJ |
509 | exit(1); |
510 | } | |
511 | targ = *argv; | |
512 | if (targetshouldbedirectory) | |
513 | verifydir(targ); | |
35673a6d | 514 | (void)write(rem, "", 1); |
0b35f0fe | 515 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) |
b7ff3bb3 | 516 | targisdir = 1; |
d4c44137 KB |
517 | for (first = 1;; first = 0) { |
518 | cp = buf; | |
b7ff3bb3 BJ |
519 | if (read(rem, cp, 1) <= 0) |
520 | return; | |
521 | if (*cp++ == '\n') | |
d4c44137 | 522 | SCREWUP("unexpected <newline>"); |
b7ff3bb3 | 523 | do { |
d4c44137 | 524 | if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) |
b7ff3bb3 | 525 | SCREWUP("lost connection"); |
d4c44137 KB |
526 | *cp++ = ch; |
527 | } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); | |
b7ff3bb3 | 528 | *cp = 0; |
d4c44137 KB |
529 | |
530 | if (buf[0] == '\01' || buf[0] == '\02') { | |
b7ff3bb3 | 531 | if (iamremote == 0) |
0b35f0fe KB |
532 | (void)write(STDERR_FILENO, |
533 | buf + 1, strlen(buf + 1)); | |
d4c44137 | 534 | if (buf[0] == '\02') |
b7ff3bb3 | 535 | exit(1); |
0b35f0fe | 536 | ++errs; |
b7ff3bb3 BJ |
537 | continue; |
538 | } | |
d4c44137 | 539 | if (buf[0] == 'E') { |
35673a6d | 540 | (void)write(rem, "", 1); |
b7ff3bb3 BJ |
541 | return; |
542 | } | |
af86e024 | 543 | |
3eafec57 | 544 | if (ch == '\n') |
6f6da275 | 545 | *--cp = 0; |
3eafec57 | 546 | |
af86e024 | 547 | #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); |
d4c44137 | 548 | cp = buf; |
af86e024 JL |
549 | if (*cp == 'T') { |
550 | setimes++; | |
551 | cp++; | |
552 | getnum(mtime.tv_sec); | |
553 | if (*cp++ != ' ') | |
554 | SCREWUP("mtime.sec not delimited"); | |
555 | getnum(mtime.tv_usec); | |
556 | if (*cp++ != ' ') | |
557 | SCREWUP("mtime.usec not delimited"); | |
558 | getnum(atime.tv_sec); | |
559 | if (*cp++ != ' ') | |
560 | SCREWUP("atime.sec not delimited"); | |
561 | getnum(atime.tv_usec); | |
562 | if (*cp++ != '\0') | |
563 | SCREWUP("atime.usec not delimited"); | |
35673a6d | 564 | (void)write(rem, "", 1); |
af86e024 JL |
565 | continue; |
566 | } | |
4550ab9e RC |
567 | if (*cp != 'C' && *cp != 'D') { |
568 | /* | |
569 | * Check for the case "rcp remote:foo\* local:bar". | |
570 | * In this case, the line "No match." can be returned | |
571 | * by the shell before the rcp command on the remote is | |
572 | * executed so the ^Aerror_message convention isn't | |
573 | * followed. | |
574 | */ | |
575 | if (first) { | |
8809a3e2 | 576 | run_err("%s", cp); |
4550ab9e RC |
577 | exit(1); |
578 | } | |
b7ff3bb3 | 579 | SCREWUP("expected control record"); |
4550ab9e | 580 | } |
b7ff3bb3 | 581 | mode = 0; |
d4c44137 | 582 | for (++cp; cp < buf + 5; cp++) { |
b7ff3bb3 BJ |
583 | if (*cp < '0' || *cp > '7') |
584 | SCREWUP("bad mode"); | |
585 | mode = (mode << 3) | (*cp - '0'); | |
586 | } | |
587 | if (*cp++ != ' ') | |
588 | SCREWUP("mode not delimited"); | |
0b35f0fe KB |
589 | |
590 | for (size = 0; isdigit(*cp);) | |
b7ff3bb3 BJ |
591 | size = size * 10 + (*cp++ - '0'); |
592 | if (*cp++ != ' ') | |
593 | SCREWUP("size not delimited"); | |
d4c44137 KB |
594 | if (targisdir) { |
595 | static char *namebuf; | |
596 | static int cursize; | |
42029053 | 597 | size_t need; |
d4c44137 KB |
598 | |
599 | need = strlen(targ) + strlen(cp) + 250; | |
600 | if (need > cursize) { | |
42029053 | 601 | if (!(namebuf = malloc(need))) |
8809a3e2 | 602 | run_err("%s", strerror(errno)); |
d4c44137 | 603 | } |
a045d3a2 | 604 | (void)snprintf(namebuf, need, "%s%s%s", targ, |
b7ff3bb3 | 605 | *targ ? "/" : "", cp); |
d4c44137 | 606 | np = namebuf; |
83cb1dc5 | 607 | } else |
d4c44137 KB |
608 | np = targ; |
609 | exists = stat(np, &stb) == 0; | |
610 | if (buf[0] == 'D') { | |
83cb1dc5 | 611 | int mod_flag = pflag; |
11bfe9c9 | 612 | if (exists) { |
0b35f0fe | 613 | if (!S_ISDIR(stb.st_mode)) { |
b7ff3bb3 BJ |
614 | errno = ENOTDIR; |
615 | goto bad; | |
616 | } | |
a4372a41 | 617 | if (pflag) |
d4c44137 | 618 | (void)chmod(np, mode); |
83cb1dc5 AC |
619 | } else { |
620 | /* Handle copying from a read-only directory */ | |
621 | mod_flag = 1; | |
622 | if (mkdir(np, mode | S_IRWXU) < 0) | |
623 | goto bad; | |
624 | } | |
d4c44137 KB |
625 | vect[0] = np; |
626 | sink(1, vect); | |
af86e024 JL |
627 | if (setimes) { |
628 | setimes = 0; | |
d4c44137 | 629 | if (utimes(np, tv) < 0) |
8809a3e2 | 630 | run_err("%s: set times: %s", |
ef49bf4f | 631 | np, strerror(errno)); |
af86e024 | 632 | } |
83cb1dc5 AC |
633 | if (mod_flag) |
634 | (void)chmod(np, mode); | |
b7ff3bb3 | 635 | continue; |
11bfe9c9 | 636 | } |
83cb1dc5 AC |
637 | omode = mode; |
638 | mode |= S_IWRITE; | |
d4c44137 | 639 | if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { |
8809a3e2 | 640 | bad: run_err("%s: %s", np, strerror(errno)); |
b7ff3bb3 | 641 | continue; |
b7ff3bb3 | 642 | } |
35673a6d | 643 | (void)write(rem, "", 1); |
0b35f0fe | 644 | if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { |
d4c44137 | 645 | (void)close(ofd); |
371655cc KM |
646 | continue; |
647 | } | |
648 | cp = bp->buf; | |
ef49bf4f | 649 | wrerr = NO; |
83cb1dc5 | 650 | for (count = i = 0; i < size; i += BUFSIZ) { |
371655cc | 651 | amt = BUFSIZ; |
b7ff3bb3 BJ |
652 | if (i + amt > size) |
653 | amt = size - i; | |
371655cc | 654 | count += amt; |
b7ff3bb3 | 655 | do { |
371655cc | 656 | j = read(rem, cp, amt); |
9e9252a8 | 657 | if (j <= 0) { |
8809a3e2 | 658 | run_err("%s", j ? strerror(errno) : |
d4c44137 | 659 | "dropped connection"); |
b7ff3bb3 | 660 | exit(1); |
9e9252a8 | 661 | } |
b7ff3bb3 BJ |
662 | amt -= j; |
663 | cp += j; | |
664 | } while (amt > 0); | |
371655cc | 665 | if (count == bp->cnt) { |
83cb1dc5 AC |
666 | /* Keep reading so we stay sync'd up. */ |
667 | if (wrerr == NO) { | |
668 | j = write(ofd, bp->buf, count); | |
669 | if (j != count) { | |
670 | wrerr = YES; | |
671 | wrerrno = j >= 0 ? EIO : errno; | |
672 | } | |
673 | } | |
371655cc KM |
674 | count = 0; |
675 | cp = bp->buf; | |
676 | } | |
b7ff3bb3 | 677 | } |
ef49bf4f | 678 | if (count != 0 && wrerr == NO && |
bd7da30a | 679 | (j = write(ofd, bp->buf, count)) != count) { |
ef49bf4f | 680 | wrerr = YES; |
bd7da30a KB |
681 | wrerrno = j >= 0 ? EIO : errno; |
682 | } | |
ef49bf4f | 683 | if (ftruncate(ofd, size)) { |
8809a3e2 | 684 | run_err("%s: truncate: %s", np, strerror(errno)); |
ef49bf4f KB |
685 | wrerr = DISPLAYED; |
686 | } | |
83cb1dc5 AC |
687 | if (pflag) { |
688 | if (exists || omode != mode) | |
8809a3e2 KB |
689 | if (fchmod(ofd, omode)) |
690 | run_err("%s: set mode: %s", | |
691 | np, strerror(errno)); | |
83cb1dc5 AC |
692 | } else { |
693 | if (!exists && omode != mode) | |
8809a3e2 KB |
694 | if (fchmod(ofd, omode & ~mask)) |
695 | run_err("%s: set mode: %s", | |
696 | np, strerror(errno)); | |
83cb1dc5 | 697 | } |
d4c44137 | 698 | (void)close(ofd); |
35673a6d | 699 | (void)response(); |
ef49bf4f | 700 | if (setimes && wrerr == NO) { |
af86e024 | 701 | setimes = 0; |
ef49bf4f | 702 | if (utimes(np, tv) < 0) { |
8809a3e2 | 703 | run_err("%s: set times: %s", |
ef49bf4f KB |
704 | np, strerror(errno)); |
705 | wrerr = DISPLAYED; | |
706 | } | |
707 | } | |
708 | switch(wrerr) { | |
709 | case YES: | |
8809a3e2 | 710 | run_err("%s: %s", np, strerror(wrerrno)); |
ef49bf4f KB |
711 | break; |
712 | case NO: | |
35673a6d | 713 | (void)write(rem, "", 1); |
ef49bf4f KB |
714 | break; |
715 | case DISPLAYED: | |
716 | break; | |
717 | } | |
b7ff3bb3 BJ |
718 | } |
719 | screwup: | |
8809a3e2 | 720 | run_err("protocol error: %s", why); |
5f286db0 KB |
721 | exit(1); |
722 | } | |
5d178979 KF |
723 | |
724 | #ifdef KERBEROS | |
5d178979 KF |
725 | int |
726 | kerberos(host, bp, locuser, user) | |
5d178979 KF |
727 | char **host, *bp, *locuser, *user; |
728 | { | |
729 | struct servent *sp; | |
730 | ||
731 | again: | |
732 | if (use_kerberos) { | |
733 | rem = KSUCCESS; | |
734 | errno = 0; | |
735 | if (dest_realm == NULL) | |
736 | dest_realm = krb_realmofhost(*host); | |
0b35f0fe KB |
737 | rem = |
738 | krcmd(host, port, user, bp, 0, dest_realm); | |
5d178979 KF |
739 | |
740 | if (rem < 0) { | |
741 | use_kerberos = 0; | |
8809a3e2 KB |
742 | if ((sp = getservbyname("shell", "tcp")) == NULL) |
743 | errx(1, "unknown service shell/tcp"); | |
5d178979 | 744 | if (errno == ECONNREFUSED) |
0b35f0fe KB |
745 | oldw("remote host doesn't support Kerberos"); |
746 | else if (errno == ENOENT) | |
747 | oldw("can't provide Kerberos authentication data"); | |
5d178979 KF |
748 | port = sp->s_port; |
749 | goto again; | |
750 | } | |
751 | } else { | |
bd7da30a | 752 | rem = rcmd(host, port, locuser, user, bp, 0); |
5d178979 | 753 | } |
0b35f0fe | 754 | return (rem); |
5d178979 KF |
755 | } |
756 | #endif /* KERBEROS */ | |
0b35f0fe KB |
757 | |
758 | int | |
759 | response() | |
760 | { | |
761 | register char *cp; | |
762 | char ch, resp, rbuf[BUFSIZ]; | |
763 | ||
764 | if (read(rem, &resp, sizeof(resp)) != sizeof(resp)) | |
765 | lostconn(0); | |
766 | ||
767 | cp = rbuf; | |
768 | switch(resp) { | |
769 | case 0: /* ok */ | |
770 | return (0); | |
771 | default: | |
772 | *cp++ = resp; | |
773 | /* FALLTHROUGH */ | |
774 | case 1: /* error, followed by error msg */ | |
775 | case 2: /* fatal error, "" */ | |
776 | do { | |
777 | if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) | |
778 | lostconn(0); | |
779 | *cp++ = ch; | |
780 | } while (cp < &rbuf[BUFSIZ] && ch != '\n'); | |
781 | ||
782 | if (!iamremote) | |
783 | (void)write(STDERR_FILENO, rbuf, cp - rbuf); | |
784 | ++errs; | |
785 | if (resp == 1) | |
786 | return (-1); | |
787 | exit(1); | |
788 | } | |
789 | /* NOTREACHED */ | |
790 | } | |
791 | ||
792 | void | |
793 | usage() | |
794 | { | |
795 | #ifdef KERBEROS | |
0b35f0fe KB |
796 | (void)fprintf(stderr, "%s\n\t%s\n", |
797 | "usage: rcp [-Kp] [-k realm] f1 f2", | |
798 | "or: rcp [-Kpr] [-k realm] f1 ... fn directory"); | |
0b35f0fe KB |
799 | #else |
800 | (void)fprintf(stderr, | |
801 | "usage: rcp [-p] f1 f2; or: rcp [-pr] f1 ... fn directory\n"); | |
802 | #endif | |
803 | exit(1); | |
804 | } | |
805 | ||
806 | #if __STDC__ | |
807 | #include <stdarg.h> | |
808 | #else | |
809 | #include <varargs.h> | |
810 | #endif | |
811 | ||
812 | #ifdef KERBEROS | |
813 | void | |
814 | #if __STDC__ | |
815 | oldw(const char *fmt, ...) | |
816 | #else | |
817 | oldw(fmt, va_alist) | |
818 | char *fmt; | |
819 | va_dcl | |
820 | #endif | |
821 | { | |
822 | va_list ap; | |
823 | #if __STDC__ | |
824 | va_start(ap, fmt); | |
825 | #else | |
826 | va_start(ap); | |
827 | #endif | |
828 | (void)fprintf(stderr, "rcp: "); | |
829 | (void)vfprintf(stderr, fmt, ap); | |
830 | (void)fprintf(stderr, ", using standard rcp\n"); | |
831 | va_end(ap); | |
832 | } | |
833 | #endif | |
834 | ||
835 | void | |
836 | #if __STDC__ | |
8809a3e2 | 837 | run_err(const char *fmt, ...) |
0b35f0fe | 838 | #else |
8809a3e2 | 839 | run_err(fmt, va_alist) |
0b35f0fe KB |
840 | char *fmt; |
841 | va_dcl | |
842 | #endif | |
843 | { | |
844 | static FILE *fp; | |
845 | va_list ap; | |
846 | #if __STDC__ | |
847 | va_start(ap, fmt); | |
848 | #else | |
849 | va_start(ap); | |
850 | #endif | |
851 | ||
852 | ++errs; | |
8809a3e2 | 853 | if (fp == NULL && !(fp = fdopen(rem, "w"))) |
0b35f0fe KB |
854 | return; |
855 | (void)fprintf(fp, "%c", 0x01); | |
856 | (void)fprintf(fp, "rcp: "); | |
857 | (void)vfprintf(fp, fmt, ap); | |
858 | (void)fprintf(fp, "\n"); | |
0b35f0fe | 859 | (void)fflush(fp); |
8809a3e2 KB |
860 | |
861 | if (!iamremote) | |
862 | vwarnx(fmt, ap); | |
863 | ||
864 | va_end(ap); | |
0b35f0fe | 865 | } |