Commit | Line | Data |
---|---|---|
22e155fc | 1 | /* |
ebb479fc KB |
2 | * Copyright (c) 1983 The Regents of the University of California. |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms are permitted | |
6 | * provided that the above copyright notice and this paragraph are | |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
22e155fc DF |
16 | */ |
17 | ||
18 | #ifndef lint | |
19 | char copyright[] = | |
ebb479fc | 20 | "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ |
22e155fc | 21 | All rights reserved.\n"; |
ebb479fc | 22 | #endif /* not lint */ |
22e155fc | 23 | |
b7ff3bb3 | 24 | #ifndef lint |
2b6b7619 | 25 | static char sccsid[] = "@(#)rcp.c 5.11 (Berkeley) 9/22/88"; |
ebb479fc | 26 | #endif /* not lint */ |
b7ff3bb3 | 27 | |
4ca10280 SL |
28 | /* |
29 | * rcp | |
30 | */ | |
ed0072ae | 31 | #include <sys/param.h> |
5f286db0 | 32 | #include <sys/file.h> |
b7ff3bb3 | 33 | #include <sys/stat.h> |
af86e024 | 34 | #include <sys/time.h> |
b7ff3bb3 | 35 | #include <sys/ioctl.h> |
94a2d2a7 SL |
36 | |
37 | #include <netinet/in.h> | |
38 | ||
39 | #include <stdio.h> | |
40 | #include <signal.h> | |
b7ff3bb3 BJ |
41 | #include <pwd.h> |
42 | #include <ctype.h> | |
92e58d95 | 43 | #include <netdb.h> |
b7ff3bb3 | 44 | #include <errno.h> |
4ca10280 | 45 | |
2b6b7619 KF |
46 | #ifdef KERBEROS |
47 | #include <kerberos/krb.h> | |
48 | char krb_realm[REALM_SZ]; | |
49 | int use_kerberos = 1, encrypt = 0; | |
50 | CREDENTIALS cred; | |
51 | Key_schedule schedule; | |
52 | #endif | |
53 | ||
b7ff3bb3 | 54 | int rem; |
6521d648 | 55 | char *colon(), *index(), *rindex(), *malloc(), *strcpy(); |
b7ff3bb3 BJ |
56 | int errs; |
57 | int lostconn(); | |
b7ff3bb3 BJ |
58 | int errno; |
59 | char *sys_errlist[]; | |
60 | int iamremote, targetshouldbedirectory; | |
61 | int iamrecursive; | |
af86e024 | 62 | int pflag; |
b7ff3bb3 BJ |
63 | struct passwd *pwd; |
64 | struct passwd *getpwuid(); | |
11bfe9c9 RC |
65 | int userid; |
66 | int port; | |
b7ff3bb3 | 67 | |
371655cc KM |
68 | struct buffer { |
69 | int cnt; | |
70 | char *buf; | |
71 | } *allocbuf(); | |
72 | ||
b7ff3bb3 BJ |
73 | /*VARARGS*/ |
74 | int error(); | |
75 | ||
76 | #define ga() (void) write(rem, "", 1) | |
77 | ||
78 | main(argc, argv) | |
79 | int argc; | |
80 | char **argv; | |
81 | { | |
82 | char *targ, *host, *src; | |
9f526ab5 | 83 | char *suser, *tuser, *thost; |
b7ff3bb3 BJ |
84 | int i; |
85 | char buf[BUFSIZ], cmd[16]; | |
11bfe9c9 | 86 | struct servent *sp; |
92e58d95 RC |
87 | |
88 | sp = getservbyname("shell", "tcp"); | |
89 | if (sp == NULL) { | |
90 | fprintf(stderr, "rcp: shell/tcp: unknown service\n"); | |
91 | exit(1); | |
92 | } | |
11bfe9c9 RC |
93 | port = sp->s_port; |
94 | pwd = getpwuid(userid = getuid()); | |
b7ff3bb3 BJ |
95 | if (pwd == 0) { |
96 | fprintf(stderr, "who are you?\n"); | |
97 | exit(1); | |
98 | } | |
af86e024 JL |
99 | |
100 | for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) { | |
101 | (*argv)++; | |
102 | while (**argv) switch (*(*argv)++) { | |
103 | ||
104 | case 'r': | |
105 | iamrecursive++; | |
106 | break; | |
107 | ||
2b6b7619 KF |
108 | #ifdef KERBEROS |
109 | case 'x': | |
110 | encrypt = 1; | |
111 | des_set_key(cred.session, schedule); | |
112 | break; | |
113 | ||
114 | case 'k': | |
115 | strncpy(krb_realm, ++argv, REALM_SZ); | |
116 | break; | |
117 | #endif | |
118 | ||
af86e024 JL |
119 | case 'p': /* preserve mtimes and atimes */ |
120 | pflag++; | |
121 | break; | |
122 | ||
123 | /* The rest of these are not for users. */ | |
124 | case 'd': | |
125 | targetshouldbedirectory = 1; | |
126 | break; | |
127 | ||
128 | case 'f': /* "from" */ | |
129 | iamremote = 1; | |
130 | (void) response(); | |
131 | (void) setuid(userid); | |
132 | source(--argc, ++argv); | |
133 | exit(errs); | |
134 | ||
135 | case 't': /* "to" */ | |
136 | iamremote = 1; | |
137 | (void) setuid(userid); | |
138 | sink(--argc, ++argv); | |
139 | exit(errs); | |
140 | ||
141 | default: | |
5f286db0 | 142 | usage(); |
af86e024 | 143 | } |
b7ff3bb3 | 144 | } |
5f286db0 KB |
145 | if (argc < 2) |
146 | usage(); | |
b7ff3bb3 BJ |
147 | if (argc > 2) |
148 | targetshouldbedirectory = 1; | |
5f286db0 | 149 | rem = -1; |
af86e024 JL |
150 | (void) sprintf(cmd, "rcp%s%s%s", |
151 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | |
152 | targetshouldbedirectory ? " -d" : ""); | |
153 | (void) signal(SIGPIPE, lostconn); | |
b7ff3bb3 | 154 | targ = colon(argv[argc - 1]); |
af86e024 | 155 | if (targ) { /* ... to remote */ |
b7ff3bb3 | 156 | *targ++ = 0; |
06fb72f5 SL |
157 | if (*targ == 0) |
158 | targ = "."; | |
9f526ab5 RC |
159 | thost = index(argv[argc - 1], '@'); |
160 | if (thost) { | |
161 | *thost++ = 0; | |
162 | tuser = argv[argc - 1]; | |
163 | if (*tuser == '\0') | |
9e9252a8 | 164 | tuser = NULL; |
9f526ab5 | 165 | else if (!okname(tuser)) |
b7ff3bb3 | 166 | exit(1); |
9f526ab5 RC |
167 | } else { |
168 | thost = argv[argc - 1]; | |
9e9252a8 | 169 | tuser = NULL; |
9f526ab5 | 170 | } |
b7ff3bb3 BJ |
171 | for (i = 0; i < argc - 1; i++) { |
172 | src = colon(argv[i]); | |
af86e024 | 173 | if (src) { /* remote to remote */ |
b7ff3bb3 | 174 | *src++ = 0; |
06fb72f5 | 175 | if (*src == 0) |
195c1de2 | 176 | src = "."; |
9f526ab5 RC |
177 | host = index(argv[i], '@'); |
178 | if (host) { | |
179 | *host++ = 0; | |
180 | suser = argv[i]; | |
181 | if (*suser == '\0') | |
182 | suser = pwd->pw_name; | |
183 | else if (!okname(suser)) | |
b7ff3bb3 | 184 | continue; |
2b1c563f | 185 | (void) sprintf(buf, "/usr/ucb/rsh %s -l %s -n %s %s '%s%s%s:%s'", |
48431025 KB |
186 | host, suser, cmd, src, |
187 | tuser ? tuser : "", | |
9e9252a8 JB |
188 | tuser ? "@" : "", |
189 | thost, targ); | |
b7ff3bb3 | 190 | } else |
2b1c563f | 191 | (void) sprintf(buf, "/usr/ucb/rsh %s -n %s %s '%s%s%s:%s'", |
48431025 KB |
192 | argv[i], cmd, src, |
193 | tuser ? tuser : "", | |
9e9252a8 JB |
194 | tuser ? "@" : "", |
195 | thost, targ); | |
b7ff3bb3 | 196 | (void) susystem(buf); |
af86e024 | 197 | } else { /* local to remote */ |
b7ff3bb3 BJ |
198 | if (rem == -1) { |
199 | (void) sprintf(buf, "%s -t %s", | |
200 | cmd, targ); | |
9f526ab5 | 201 | host = thost; |
2b6b7619 KF |
202 | #ifdef KERBEROS |
203 | try_again: | |
204 | if(use_kerberos) { | |
205 | rem = KSUCCESS; | |
206 | if(krb_realm[0] == '\0') | |
207 | rem = krb_get_lrealm(krb_realm,1); | |
208 | if(rem == KSUCCESS) { | |
209 | if(encrypt) { | |
210 | rem = krcmd_mutual( | |
211 | &host, port, | |
212 | tuser ? tuser | |
213 | : pwd->pw_name, | |
214 | buf, 0, krb_realm, | |
215 | &cred, schedule); | |
216 | } else { | |
217 | ||
218 | rem = krcmd( | |
219 | &host, | |
220 | port, | |
221 | tuser ? tuser | |
222 | : pwd->pw_name, | |
223 | buf, 0, | |
224 | krb_realm | |
225 | ); | |
226 | } | |
227 | } else { | |
228 | fprintf(stderr, | |
229 | "rcp: error getting local realm %s\n", | |
230 | krb_err_txt[rem]); | |
231 | exit(1); | |
232 | } | |
233 | if((rem < 0) && errno == ECONNREFUSED) { | |
234 | use_kerberos = 0; | |
235 | old_warning("remote host doesn't support Kerberos"); | |
236 | goto try_again; | |
237 | } | |
238 | } else { | |
239 | rem = rcmd(&host, port, pwd->pw_name, | |
240 | tuser ? tuser : pwd->pw_name, | |
241 | buf, 0); | |
242 | } | |
243 | #else | |
244 | ||
9e9252a8 JB |
245 | rem = rcmd(&host, port, pwd->pw_name, |
246 | tuser ? tuser : pwd->pw_name, | |
b7ff3bb3 | 247 | buf, 0); |
2b6b7619 | 248 | #endif |
b7ff3bb3 BJ |
249 | if (rem < 0) |
250 | exit(1); | |
251 | if (response() < 0) | |
252 | exit(1); | |
11bfe9c9 | 253 | (void) setuid(userid); |
b7ff3bb3 BJ |
254 | } |
255 | source(1, argv+i); | |
256 | } | |
257 | } | |
af86e024 | 258 | } else { /* ... to local */ |
b7ff3bb3 BJ |
259 | if (targetshouldbedirectory) |
260 | verifydir(argv[argc - 1]); | |
261 | for (i = 0; i < argc - 1; i++) { | |
262 | src = colon(argv[i]); | |
af86e024 JL |
263 | if (src == 0) { /* local to local */ |
264 | (void) sprintf(buf, "/bin/cp%s%s %s %s", | |
b7ff3bb3 | 265 | iamrecursive ? " -r" : "", |
af86e024 | 266 | pflag ? " -p" : "", |
b7ff3bb3 BJ |
267 | argv[i], argv[argc - 1]); |
268 | (void) susystem(buf); | |
af86e024 | 269 | } else { /* remote to local */ |
b7ff3bb3 | 270 | *src++ = 0; |
06fb72f5 SL |
271 | if (*src == 0) |
272 | src = "."; | |
9f526ab5 RC |
273 | host = index(argv[i], '@'); |
274 | if (host) { | |
275 | *host++ = 0; | |
276 | suser = argv[i]; | |
277 | if (*suser == '\0') | |
278 | suser = pwd->pw_name; | |
279 | else if (!okname(suser)) | |
b7ff3bb3 | 280 | continue; |
9f526ab5 RC |
281 | } else { |
282 | host = argv[i]; | |
b7ff3bb3 | 283 | suser = pwd->pw_name; |
9f526ab5 | 284 | } |
b7ff3bb3 | 285 | (void) sprintf(buf, "%s -f %s", cmd, src); |
2b6b7619 KF |
286 | #ifdef KERBEROS |
287 | one_more_time: | |
288 | if(use_kerberos) { | |
289 | rem = KSUCCESS; | |
290 | if(krb_realm[0] == '\0') | |
291 | rem = krb_get_lrealm(krb_realm,1); | |
292 | if(rem == KSUCCESS) { | |
293 | if(encrypt) { | |
294 | rem = krcmd_mutual( | |
295 | &host, | |
296 | port, | |
297 | suser, | |
298 | buf, 0, | |
299 | krb_realm, | |
300 | &cred, schedule | |
301 | ); | |
302 | } else { | |
303 | rem = krcmd( | |
304 | &host, | |
305 | port, | |
306 | suser, | |
307 | buf, 0, | |
308 | krb_realm | |
309 | ); | |
310 | } | |
311 | } else { | |
312 | fprintf(stderr, | |
313 | "rcp: error getting local realm %s\n", | |
314 | krb_err_txt[rem]); | |
315 | exit(1); | |
316 | } | |
317 | if((rem < 0) && errno == ECONNREFUSED) { | |
318 | use_kerberos = 0; | |
319 | old_warning("remote host doesn't suport Kerberos"); | |
320 | goto one_more_time; | |
321 | } | |
322 | } else { | |
323 | rem = rcmd(&host, port, pwd->pw_name, suser, | |
324 | buf, 0); | |
325 | } | |
326 | #else | |
11bfe9c9 | 327 | rem = rcmd(&host, port, pwd->pw_name, suser, |
2b6b7619 KF |
328 | buf, 0); |
329 | #endif | |
b7ff3bb3 | 330 | if (rem < 0) |
9f526ab5 | 331 | continue; |
11bfe9c9 | 332 | (void) setreuid(0, userid); |
b7ff3bb3 | 333 | sink(1, argv+argc-1); |
11bfe9c9 | 334 | (void) setreuid(userid, 0); |
b7ff3bb3 BJ |
335 | (void) close(rem); |
336 | rem = -1; | |
337 | } | |
338 | } | |
339 | } | |
340 | exit(errs); | |
341 | } | |
342 | ||
343 | verifydir(cp) | |
344 | char *cp; | |
345 | { | |
346 | struct stat stb; | |
347 | ||
11bfe9c9 RC |
348 | if (stat(cp, &stb) >= 0) { |
349 | if ((stb.st_mode & S_IFMT) == S_IFDIR) | |
350 | return; | |
351 | errno = ENOTDIR; | |
352 | } | |
b7ff3bb3 BJ |
353 | error("rcp: %s: %s.\n", cp, sys_errlist[errno]); |
354 | exit(1); | |
355 | } | |
356 | ||
357 | char * | |
358 | colon(cp) | |
359 | char *cp; | |
360 | { | |
361 | ||
362 | while (*cp) { | |
363 | if (*cp == ':') | |
364 | return (cp); | |
365 | if (*cp == '/') | |
366 | return (0); | |
367 | cp++; | |
368 | } | |
369 | return (0); | |
370 | } | |
371 | ||
372 | okname(cp0) | |
373 | char *cp0; | |
374 | { | |
375 | register char *cp = cp0; | |
376 | register int c; | |
377 | ||
378 | do { | |
379 | c = *cp; | |
380 | if (c & 0200) | |
381 | goto bad; | |
382 | if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') | |
383 | goto bad; | |
384 | cp++; | |
385 | } while (*cp); | |
386 | return (1); | |
387 | bad: | |
388 | fprintf(stderr, "rcp: invalid user name %s\n", cp0); | |
389 | return (0); | |
390 | } | |
391 | ||
48755084 S |
392 | susystem(s) |
393 | char *s; | |
b7ff3bb3 | 394 | { |
48755084 S |
395 | int status, pid, w; |
396 | register int (*istat)(), (*qstat)(); | |
b7ff3bb3 | 397 | |
48755084 | 398 | if ((pid = vfork()) == 0) { |
af86e024 | 399 | (void) setuid(userid); |
48755084 S |
400 | execl("/bin/sh", "sh", "-c", s, (char *)0); |
401 | _exit(127); | |
402 | } | |
403 | istat = signal(SIGINT, SIG_IGN); | |
404 | qstat = signal(SIGQUIT, SIG_IGN); | |
405 | while ((w = wait(&status)) != pid && w != -1) | |
406 | ; | |
407 | if (w == -1) | |
408 | status = -1; | |
af86e024 JL |
409 | (void) signal(SIGINT, istat); |
410 | (void) signal(SIGQUIT, qstat); | |
48755084 | 411 | return (status); |
b7ff3bb3 BJ |
412 | } |
413 | ||
414 | source(argc, argv) | |
415 | int argc; | |
416 | char **argv; | |
417 | { | |
418 | char *last, *name; | |
419 | struct stat stb; | |
371655cc KM |
420 | static struct buffer buffer; |
421 | struct buffer *bp; | |
f68c1c98 | 422 | int x, readerr, f, amt; |
5f10dd79 | 423 | off_t i; |
371655cc | 424 | char buf[BUFSIZ]; |
b7ff3bb3 BJ |
425 | |
426 | for (x = 0; x < argc; x++) { | |
427 | name = argv[x]; | |
11bfe9c9 | 428 | if ((f = open(name, 0)) < 0) { |
b7ff3bb3 BJ |
429 | error("rcp: %s: %s\n", name, sys_errlist[errno]); |
430 | continue; | |
431 | } | |
432 | if (fstat(f, &stb) < 0) | |
433 | goto notreg; | |
434 | switch (stb.st_mode&S_IFMT) { | |
435 | ||
436 | case S_IFREG: | |
437 | break; | |
438 | ||
439 | case S_IFDIR: | |
440 | if (iamrecursive) { | |
441 | (void) close(f); | |
af86e024 | 442 | rsource(name, &stb); |
b7ff3bb3 BJ |
443 | continue; |
444 | } | |
445 | /* fall into ... */ | |
446 | default: | |
447 | notreg: | |
448 | (void) close(f); | |
449 | error("rcp: %s: not a plain file\n", name); | |
450 | continue; | |
451 | } | |
452 | last = rindex(name, '/'); | |
453 | if (last == 0) | |
454 | last = name; | |
455 | else | |
456 | last++; | |
af86e024 JL |
457 | if (pflag) { |
458 | /* | |
459 | * Make it compatible with possible future | |
460 | * versions expecting microseconds. | |
461 | */ | |
462 | (void) sprintf(buf, "T%ld 0 %ld 0\n", | |
463 | stb.st_mtime, stb.st_atime); | |
464 | (void) write(rem, buf, strlen(buf)); | |
465 | if (response() < 0) { | |
466 | (void) close(f); | |
467 | continue; | |
468 | } | |
469 | } | |
470 | (void) sprintf(buf, "C%04o %ld %s\n", | |
b7ff3bb3 BJ |
471 | stb.st_mode&07777, stb.st_size, last); |
472 | (void) write(rem, buf, strlen(buf)); | |
473 | if (response() < 0) { | |
474 | (void) close(f); | |
475 | continue; | |
476 | } | |
809ba2d2 | 477 | if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) { |
371655cc KM |
478 | (void) close(f); |
479 | continue; | |
480 | } | |
f68c1c98 | 481 | readerr = 0; |
371655cc KM |
482 | for (i = 0; i < stb.st_size; i += bp->cnt) { |
483 | amt = bp->cnt; | |
b7ff3bb3 BJ |
484 | if (i + amt > stb.st_size) |
485 | amt = stb.st_size - i; | |
f68c1c98 KB |
486 | if (readerr == 0 && read(f, bp->buf, amt) != amt) |
487 | readerr = errno; | |
371655cc | 488 | (void) write(rem, bp->buf, amt); |
b7ff3bb3 BJ |
489 | } |
490 | (void) close(f); | |
f68c1c98 | 491 | if (readerr == 0) |
b7ff3bb3 BJ |
492 | ga(); |
493 | else | |
f68c1c98 | 494 | error("rcp: %s: %s\n", name, sys_errlist[readerr]); |
b7ff3bb3 BJ |
495 | (void) response(); |
496 | } | |
497 | } | |
498 | ||
06fb72f5 | 499 | #include <sys/dir.h> |
b7ff3bb3 | 500 | |
af86e024 | 501 | rsource(name, statp) |
b7ff3bb3 | 502 | char *name; |
af86e024 | 503 | struct stat *statp; |
b7ff3bb3 BJ |
504 | { |
505 | DIR *d = opendir(name); | |
506 | char *last; | |
507 | struct direct *dp; | |
508 | char buf[BUFSIZ]; | |
509 | char *bufv[1]; | |
510 | ||
511 | if (d == 0) { | |
11bfe9c9 | 512 | error("rcp: %s: %s\n", name, sys_errlist[errno]); |
b7ff3bb3 BJ |
513 | return; |
514 | } | |
515 | last = rindex(name, '/'); | |
516 | if (last == 0) | |
517 | last = name; | |
518 | else | |
519 | last++; | |
af86e024 JL |
520 | if (pflag) { |
521 | (void) sprintf(buf, "T%ld 0 %ld 0\n", | |
522 | statp->st_mtime, statp->st_atime); | |
523 | (void) write(rem, buf, strlen(buf)); | |
524 | if (response() < 0) { | |
525 | closedir(d); | |
526 | return; | |
527 | } | |
528 | } | |
529 | (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last); | |
b7ff3bb3 BJ |
530 | (void) write(rem, buf, strlen(buf)); |
531 | if (response() < 0) { | |
532 | closedir(d); | |
533 | return; | |
534 | } | |
535 | while (dp = readdir(d)) { | |
536 | if (dp->d_ino == 0) | |
537 | continue; | |
538 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) | |
539 | continue; | |
540 | if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { | |
541 | error("%s/%s: Name too long.\n", name, dp->d_name); | |
542 | continue; | |
543 | } | |
544 | (void) sprintf(buf, "%s/%s", name, dp->d_name); | |
545 | bufv[0] = buf; | |
546 | source(1, bufv); | |
547 | } | |
548 | closedir(d); | |
549 | (void) write(rem, "E\n", 2); | |
550 | (void) response(); | |
551 | } | |
552 | ||
553 | response() | |
554 | { | |
555 | char resp, c, rbuf[BUFSIZ], *cp = rbuf; | |
556 | ||
557 | if (read(rem, &resp, 1) != 1) | |
558 | lostconn(); | |
559 | switch (resp) { | |
560 | ||
af86e024 | 561 | case 0: /* ok */ |
b7ff3bb3 BJ |
562 | return (0); |
563 | ||
564 | default: | |
565 | *cp++ = resp; | |
566 | /* fall into... */ | |
af86e024 JL |
567 | case 1: /* error, followed by err msg */ |
568 | case 2: /* fatal error, "" */ | |
b7ff3bb3 BJ |
569 | do { |
570 | if (read(rem, &c, 1) != 1) | |
571 | lostconn(); | |
572 | *cp++ = c; | |
573 | } while (cp < &rbuf[BUFSIZ] && c != '\n'); | |
574 | if (iamremote == 0) | |
575 | (void) write(2, rbuf, cp - rbuf); | |
576 | errs++; | |
577 | if (resp == 1) | |
578 | return (-1); | |
579 | exit(1); | |
580 | } | |
581 | /*NOTREACHED*/ | |
582 | } | |
583 | ||
584 | lostconn() | |
585 | { | |
586 | ||
587 | if (iamremote == 0) | |
588 | fprintf(stderr, "rcp: lost connection\n"); | |
589 | exit(1); | |
590 | } | |
591 | ||
592 | sink(argc, argv) | |
593 | int argc; | |
594 | char **argv; | |
595 | { | |
371655cc KM |
596 | off_t i, j; |
597 | char *targ, *whopp, *cp; | |
598 | int of, mode, wrerr, exists, first, count, amt, size; | |
599 | struct buffer *bp; | |
600 | static struct buffer buffer; | |
601 | struct stat stb; | |
602 | int targisdir = 0; | |
b7ff3bb3 BJ |
603 | int mask = umask(0); |
604 | char *myargv[1]; | |
af86e024 JL |
605 | char cmdbuf[BUFSIZ], nambuf[BUFSIZ]; |
606 | int setimes = 0; | |
607 | struct timeval tv[2]; | |
608 | #define atime tv[0] | |
609 | #define mtime tv[1] | |
371655cc | 610 | #define SCREWUP(str) { whopp = str; goto screwup; } |
b7ff3bb3 | 611 | |
a4372a41 JL |
612 | if (!pflag) |
613 | (void) umask(mask); | |
11bfe9c9 | 614 | if (argc != 1) { |
b7ff3bb3 BJ |
615 | error("rcp: ambiguous target\n"); |
616 | exit(1); | |
617 | } | |
618 | targ = *argv; | |
619 | if (targetshouldbedirectory) | |
620 | verifydir(targ); | |
621 | ga(); | |
622 | if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) | |
623 | targisdir = 1; | |
4550ab9e | 624 | for (first = 1; ; first = 0) { |
b7ff3bb3 BJ |
625 | cp = cmdbuf; |
626 | if (read(rem, cp, 1) <= 0) | |
627 | return; | |
628 | if (*cp++ == '\n') | |
629 | SCREWUP("unexpected '\\n'"); | |
630 | do { | |
631 | if (read(rem, cp, 1) != 1) | |
632 | SCREWUP("lost connection"); | |
633 | } while (*cp++ != '\n'); | |
634 | *cp = 0; | |
635 | if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { | |
636 | if (iamremote == 0) | |
06fb72f5 | 637 | (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); |
b7ff3bb3 BJ |
638 | if (cmdbuf[0] == '\02') |
639 | exit(1); | |
640 | errs++; | |
641 | continue; | |
642 | } | |
643 | *--cp = 0; | |
644 | cp = cmdbuf; | |
645 | if (*cp == 'E') { | |
646 | ga(); | |
647 | return; | |
648 | } | |
af86e024 JL |
649 | |
650 | #define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); | |
651 | if (*cp == 'T') { | |
652 | setimes++; | |
653 | cp++; | |
654 | getnum(mtime.tv_sec); | |
655 | if (*cp++ != ' ') | |
656 | SCREWUP("mtime.sec not delimited"); | |
657 | getnum(mtime.tv_usec); | |
658 | if (*cp++ != ' ') | |
659 | SCREWUP("mtime.usec not delimited"); | |
660 | getnum(atime.tv_sec); | |
661 | if (*cp++ != ' ') | |
662 | SCREWUP("atime.sec not delimited"); | |
663 | getnum(atime.tv_usec); | |
664 | if (*cp++ != '\0') | |
665 | SCREWUP("atime.usec not delimited"); | |
666 | ga(); | |
667 | continue; | |
668 | } | |
4550ab9e RC |
669 | if (*cp != 'C' && *cp != 'D') { |
670 | /* | |
671 | * Check for the case "rcp remote:foo\* local:bar". | |
672 | * In this case, the line "No match." can be returned | |
673 | * by the shell before the rcp command on the remote is | |
674 | * executed so the ^Aerror_message convention isn't | |
675 | * followed. | |
676 | */ | |
677 | if (first) { | |
678 | error("%s\n", cp); | |
679 | exit(1); | |
680 | } | |
b7ff3bb3 | 681 | SCREWUP("expected control record"); |
4550ab9e | 682 | } |
b7ff3bb3 BJ |
683 | cp++; |
684 | mode = 0; | |
685 | for (; cp < cmdbuf+5; cp++) { | |
686 | if (*cp < '0' || *cp > '7') | |
687 | SCREWUP("bad mode"); | |
688 | mode = (mode << 3) | (*cp - '0'); | |
689 | } | |
690 | if (*cp++ != ' ') | |
691 | SCREWUP("mode not delimited"); | |
692 | size = 0; | |
a4372a41 | 693 | while (isdigit(*cp)) |
b7ff3bb3 BJ |
694 | size = size * 10 + (*cp++ - '0'); |
695 | if (*cp++ != ' ') | |
696 | SCREWUP("size not delimited"); | |
697 | if (targisdir) | |
698 | (void) sprintf(nambuf, "%s%s%s", targ, | |
699 | *targ ? "/" : "", cp); | |
700 | else | |
701 | (void) strcpy(nambuf, targ); | |
702 | exists = stat(nambuf, &stb) == 0; | |
11bfe9c9 RC |
703 | if (cmdbuf[0] == 'D') { |
704 | if (exists) { | |
b7ff3bb3 BJ |
705 | if ((stb.st_mode&S_IFMT) != S_IFDIR) { |
706 | errno = ENOTDIR; | |
707 | goto bad; | |
708 | } | |
a4372a41 JL |
709 | if (pflag) |
710 | (void) chmod(nambuf, mode); | |
11bfe9c9 | 711 | } else if (mkdir(nambuf, mode) < 0) |
b7ff3bb3 BJ |
712 | goto bad; |
713 | myargv[0] = nambuf; | |
714 | sink(1, myargv); | |
af86e024 JL |
715 | if (setimes) { |
716 | setimes = 0; | |
717 | if (utimes(nambuf, tv) < 0) | |
718 | error("rcp: can't set times on %s: %s\n", | |
719 | nambuf, sys_errlist[errno]); | |
720 | } | |
b7ff3bb3 | 721 | continue; |
11bfe9c9 | 722 | } |
5f286db0 | 723 | if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) { |
b7ff3bb3 | 724 | bad: |
b7ff3bb3 BJ |
725 | error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); |
726 | continue; | |
b7ff3bb3 | 727 | } |
a4372a41 JL |
728 | if (exists && pflag) |
729 | (void) fchmod(of, mode); | |
b7ff3bb3 | 730 | ga(); |
809ba2d2 | 731 | if ((bp = allocbuf(&buffer, of, BUFSIZ)) == 0) { |
af86e024 | 732 | (void) close(of); |
371655cc KM |
733 | continue; |
734 | } | |
735 | cp = bp->buf; | |
736 | count = 0; | |
b7ff3bb3 BJ |
737 | wrerr = 0; |
738 | for (i = 0; i < size; i += BUFSIZ) { | |
371655cc | 739 | amt = BUFSIZ; |
b7ff3bb3 BJ |
740 | if (i + amt > size) |
741 | amt = size - i; | |
371655cc | 742 | count += amt; |
b7ff3bb3 | 743 | do { |
371655cc | 744 | j = read(rem, cp, amt); |
9e9252a8 JB |
745 | if (j <= 0) { |
746 | if (j == 0) | |
747 | error("rcp: dropped connection"); | |
748 | else | |
749 | error("rcp: %s\n", | |
750 | sys_errlist[errno]); | |
b7ff3bb3 | 751 | exit(1); |
9e9252a8 | 752 | } |
b7ff3bb3 BJ |
753 | amt -= j; |
754 | cp += j; | |
755 | } while (amt > 0); | |
371655cc KM |
756 | if (count == bp->cnt) { |
757 | if (wrerr == 0 && | |
758 | write(of, bp->buf, count) != count) | |
759 | wrerr++; | |
760 | count = 0; | |
761 | cp = bp->buf; | |
762 | } | |
b7ff3bb3 | 763 | } |
371655cc KM |
764 | if (count != 0 && wrerr == 0 && |
765 | write(of, bp->buf, count) != count) | |
766 | wrerr++; | |
5f286db0 KB |
767 | if (ftruncate(of, size)) |
768 | error("rcp: can't truncate %s: %s\n", | |
769 | nambuf, sys_errlist[errno]); | |
b7ff3bb3 BJ |
770 | (void) close(of); |
771 | (void) response(); | |
af86e024 JL |
772 | if (setimes) { |
773 | setimes = 0; | |
774 | if (utimes(nambuf, tv) < 0) | |
775 | error("rcp: can't set times on %s: %s\n", | |
776 | nambuf, sys_errlist[errno]); | |
777 | } | |
b7ff3bb3 | 778 | if (wrerr) |
af86e024 | 779 | error("rcp: %s: %s\n", nambuf, sys_errlist[errno]); |
b7ff3bb3 BJ |
780 | else |
781 | ga(); | |
782 | } | |
783 | screwup: | |
784 | error("rcp: protocol screwup: %s\n", whopp); | |
785 | exit(1); | |
786 | } | |
787 | ||
371655cc KM |
788 | struct buffer * |
789 | allocbuf(bp, fd, blksize) | |
790 | struct buffer *bp; | |
791 | int fd, blksize; | |
792 | { | |
793 | struct stat stb; | |
794 | int size; | |
795 | ||
796 | if (fstat(fd, &stb) < 0) { | |
797 | error("rcp: fstat: %s\n", sys_errlist[errno]); | |
809ba2d2 | 798 | return ((struct buffer *)0); |
371655cc KM |
799 | } |
800 | size = roundup(stb.st_blksize, blksize); | |
801 | if (size == 0) | |
802 | size = blksize; | |
803 | if (bp->cnt < size) { | |
804 | if (bp->buf != 0) | |
805 | free(bp->buf); | |
af86e024 | 806 | bp->buf = (char *)malloc((unsigned) size); |
371655cc KM |
807 | if (bp->buf == 0) { |
808 | error("rcp: malloc: out of memory\n"); | |
809ba2d2 | 809 | return ((struct buffer *)0); |
371655cc KM |
810 | } |
811 | } | |
812 | bp->cnt = size; | |
813 | return (bp); | |
814 | } | |
815 | ||
af86e024 | 816 | /*VARARGS1*/ |
b7ff3bb3 BJ |
817 | error(fmt, a1, a2, a3, a4, a5) |
818 | char *fmt; | |
819 | int a1, a2, a3, a4, a5; | |
820 | { | |
821 | char buf[BUFSIZ], *cp = buf; | |
822 | ||
823 | errs++; | |
824 | *cp++ = 1; | |
825 | (void) sprintf(cp, fmt, a1, a2, a3, a4, a5); | |
826 | (void) write(rem, buf, strlen(buf)); | |
827 | if (iamremote == 0) | |
828 | (void) write(2, buf+1, strlen(buf+1)); | |
829 | } | |
5f286db0 KB |
830 | |
831 | usage() | |
832 | { | |
833 | fputs("usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n", stderr); | |
834 | exit(1); | |
835 | } | |
2b6b7619 KF |
836 | |
837 | #ifdef KERBEROS | |
838 | old_warning(str) | |
839 | char *str; | |
840 | { | |
841 | fprintf(stderr,"Warning: %s, using standard rcp", str); | |
842 | } | |
843 | #endif |