first working version.
[unix-history] / usr / src / bin / rcp / rcp.c
CommitLineData
b7ff3bb3 1#ifndef lint
04f0ebce 2static char sccsid[] = "@(#)rcp.c 4.10 84/02/13";
b7ff3bb3
BJ
3#endif
4
4ca10280
SL
5/*
6 * rcp
7 */
ed0072ae 8#include <sys/param.h>
b7ff3bb3
BJ
9#include <sys/stat.h>
10#include <sys/ioctl.h>
94a2d2a7
SL
11
12#include <netinet/in.h>
13
14#include <stdio.h>
15#include <signal.h>
b7ff3bb3
BJ
16#include <pwd.h>
17#include <ctype.h>
18#include <errno.h>
4ca10280 19
b7ff3bb3
BJ
20int rem;
21char *colon(), *index(), *rindex(), *malloc(), *strcpy(), *sprintf();
22int errs;
23int lostconn();
24int iamremote;
25
26int errno;
27char *sys_errlist[];
28int iamremote, targetshouldbedirectory;
29int iamrecursive;
30struct passwd *pwd;
31struct passwd *getpwuid();
32
33/*VARARGS*/
34int error();
35
36#define ga() (void) write(rem, "", 1)
37
38main(argc, argv)
39 int argc;
40 char **argv;
41{
42 char *targ, *host, *src;
43 char *suser, *tuser;
44 int i;
45 char buf[BUFSIZ], cmd[16];
46
47 setpwent();
48 pwd = getpwuid(getuid());
49 endpwent();
50 if (pwd == 0) {
51 fprintf(stderr, "who are you?\n");
52 exit(1);
53 }
54 argc--, argv++;
55 if (argc > 0 && !strcmp(*argv, "-r")) {
56 iamrecursive++;
57 argc--, argv++;
58 }
59 if (argc > 0 && !strcmp(*argv, "-d")) {
60 targetshouldbedirectory = 1;
61 argc--, argv++;
62 }
63 if (argc > 0 && !strcmp(*argv, "-f")) {
64 argc--, argv++; iamremote = 1;
65 (void) response();
66 (void) setuid(getuid());
67 source(argc, argv);
68 exit(errs);
69 }
70 if (argc > 0 && !strcmp(*argv, "-t")) {
71 argc--, argv++; iamremote = 1;
72 (void) setuid(getuid());
73 sink(argc, argv);
74 exit(errs);
75 }
76 rem = -1;
77 if (argc > 2)
78 targetshouldbedirectory = 1;
79 (void) sprintf(cmd, "rcp%s%s",
80 iamrecursive ? " -r" : "", targetshouldbedirectory ? " -d" : "");
4ca10280 81 signal(SIGPIPE, lostconn);
b7ff3bb3
BJ
82 targ = colon(argv[argc - 1]);
83 if (targ) {
84 *targ++ = 0;
06fb72f5
SL
85 if (*targ == 0)
86 targ = ".";
b7ff3bb3
BJ
87 tuser = rindex(argv[argc - 1], '.');
88 if (tuser) {
89 *tuser++ = 0;
90 if (!okname(tuser))
91 exit(1);
92 } else
93 tuser = pwd->pw_name;
94 for (i = 0; i < argc - 1; i++) {
95 src = colon(argv[i]);
96 if (src) {
97 *src++ = 0;
06fb72f5 98 if (*src == 0)
195c1de2 99 src = ".";
b7ff3bb3
BJ
100 suser = rindex(argv[i], '.');
101 if (suser) {
102 *suser++ = 0;
103 if (!okname(suser))
104 continue;
bd864c30
RC
105 (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s.%s:%s'",
106 argv[i], suser, cmd, src,
107 argv[argc - 1], tuser, targ);
b7ff3bb3 108 } else
bd864c30
RC
109 (void) sprintf(buf, "rsh %s -n %s %s '%s.%s:%s'",
110 argv[i], cmd, src,
111 argv[argc - 1], tuser, targ);
b7ff3bb3
BJ
112 (void) susystem(buf);
113 } else {
114 if (rem == -1) {
115 (void) sprintf(buf, "%s -t %s",
116 cmd, targ);
117 host = argv[argc - 1];
118 rem = rcmd(&host, IPPORT_CMDSERVER,
119 pwd->pw_name, tuser,
120 buf, 0);
121 if (rem < 0)
122 exit(1);
123 if (response() < 0)
124 exit(1);
125 }
126 source(1, argv+i);
127 }
128 }
129 } else {
130 if (targetshouldbedirectory)
131 verifydir(argv[argc - 1]);
132 for (i = 0; i < argc - 1; i++) {
133 src = colon(argv[i]);
134 if (src == 0) {
135 (void) sprintf(buf, "/bin/cp%s %s %s",
136 iamrecursive ? " -r" : "",
137 argv[i], argv[argc - 1]);
138 (void) susystem(buf);
139 } else {
140 *src++ = 0;
06fb72f5
SL
141 if (*src == 0)
142 src = ".";
b7ff3bb3
BJ
143 suser = rindex(argv[i], '.');
144 if (suser) {
145 *suser++ = 0;
146 if (!okname(suser))
147 continue;
148 } else
149 suser = pwd->pw_name;
150 (void) sprintf(buf, "%s -f %s", cmd, src);
151 host = argv[i];
152 rem = rcmd(&host, IPPORT_CMDSERVER,
153 pwd->pw_name, suser,
154 buf, 0);
155 if (rem < 0)
156 exit(1);
157 sink(1, argv+argc-1);
158 (void) close(rem);
159 rem = -1;
160 }
161 }
162 }
163 exit(errs);
164}
165
166verifydir(cp)
167 char *cp;
168{
169 struct stat stb;
170
171 if (stat(cp, &stb) < 0)
172 goto bad;
173 if ((stb.st_mode & S_IFMT) == S_IFDIR)
174 return;
175 errno = ENOTDIR;
176bad:
177 error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
178 exit(1);
179}
180
181char *
182colon(cp)
183 char *cp;
184{
185
186 while (*cp) {
187 if (*cp == ':')
188 return (cp);
189 if (*cp == '/')
190 return (0);
191 cp++;
192 }
193 return (0);
194}
195
196okname(cp0)
197 char *cp0;
198{
199 register char *cp = cp0;
200 register int c;
201
202 do {
203 c = *cp;
204 if (c & 0200)
205 goto bad;
206 if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
207 goto bad;
208 cp++;
209 } while (*cp);
210 return (1);
211bad:
212 fprintf(stderr, "rcp: invalid user name %s\n", cp0);
213 return (0);
214}
215
216susystem(buf)
217 char *buf;
218{
219
220 if (fork() == 0) {
221 (void) setuid(getuid());
222 (void) system(buf);
223 _exit(0);
224 } else
225 (void) wait((int *)0);
226}
227
228source(argc, argv)
229 int argc;
230 char **argv;
231{
232 char *last, *name;
233 struct stat stb;
234 char buf[BUFSIZ];
5f10dd79
KM
235 int x, sizerr, f;
236 off_t i;
b7ff3bb3
BJ
237
238 for (x = 0; x < argc; x++) {
239 name = argv[x];
240 if (access(name, 4) < 0 || (f = open(name, 0)) < 0) {
241 error("rcp: %s: %s\n", name, sys_errlist[errno]);
242 continue;
243 }
244 if (fstat(f, &stb) < 0)
245 goto notreg;
246 switch (stb.st_mode&S_IFMT) {
247
248 case S_IFREG:
249 break;
250
251 case S_IFDIR:
252 if (iamrecursive) {
253 (void) close(f);
254 rsource(name, (int)stb.st_mode);
255 continue;
256 }
257 /* fall into ... */
258 default:
259notreg:
260 (void) close(f);
261 error("rcp: %s: not a plain file\n", name);
262 continue;
263 }
264 last = rindex(name, '/');
265 if (last == 0)
266 last = name;
267 else
268 last++;
5f10dd79 269 (void) sprintf(buf, "C%04o %D %s\n",
b7ff3bb3
BJ
270 stb.st_mode&07777, stb.st_size, last);
271 (void) write(rem, buf, strlen(buf));
272 if (response() < 0) {
273 (void) close(f);
274 continue;
275 }
276 sizerr = 0;
277 for (i = 0; i < stb.st_size; i += BUFSIZ) {
278 int amt = BUFSIZ;
279 if (i + amt > stb.st_size)
280 amt = stb.st_size - i;
281 if (sizerr == 0 && read(f, buf, amt) != amt)
282 sizerr = 1;
283 (void) write(rem, buf, amt);
284 }
285 (void) close(f);
286 if (sizerr == 0)
287 ga();
288 else
289 error("rcp: %s: file changed size\n", name);
290 (void) response();
291 }
292}
293
06fb72f5 294#include <sys/dir.h>
b7ff3bb3
BJ
295
296rsource(name, mode)
297 char *name;
298 int mode;
299{
300 DIR *d = opendir(name);
301 char *last;
302 struct direct *dp;
303 char buf[BUFSIZ];
304 char *bufv[1];
305
306 if (d == 0) {
307 error("%s: %s\n", name, sys_errlist[errno]);
308 return;
309 }
310 last = rindex(name, '/');
311 if (last == 0)
312 last = name;
313 else
314 last++;
315 (void) sprintf(buf, "D%04o %d %s\n", mode&07777, 0, last);
316 (void) write(rem, buf, strlen(buf));
317 if (response() < 0) {
318 closedir(d);
319 return;
320 }
321 while (dp = readdir(d)) {
322 if (dp->d_ino == 0)
323 continue;
324 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
325 continue;
326 if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
327 error("%s/%s: Name too long.\n", name, dp->d_name);
328 continue;
329 }
330 (void) sprintf(buf, "%s/%s", name, dp->d_name);
331 bufv[0] = buf;
332 source(1, bufv);
333 }
334 closedir(d);
335 (void) write(rem, "E\n", 2);
336 (void) response();
337}
338
339response()
340{
341 char resp, c, rbuf[BUFSIZ], *cp = rbuf;
342
343 if (read(rem, &resp, 1) != 1)
344 lostconn();
345 switch (resp) {
346
347 case 0:
348 return (0);
349
350 default:
351 *cp++ = resp;
352 /* fall into... */
353 case 1:
354 case 2:
355 do {
356 if (read(rem, &c, 1) != 1)
357 lostconn();
358 *cp++ = c;
359 } while (cp < &rbuf[BUFSIZ] && c != '\n');
360 if (iamremote == 0)
361 (void) write(2, rbuf, cp - rbuf);
362 errs++;
363 if (resp == 1)
364 return (-1);
365 exit(1);
366 }
367 /*NOTREACHED*/
368}
369
370lostconn()
371{
372
373 if (iamremote == 0)
374 fprintf(stderr, "rcp: lost connection\n");
375 exit(1);
376}
377
378sink(argc, argv)
379 int argc;
380 char **argv;
381{
382 char *targ;
383 char cmdbuf[BUFSIZ], nambuf[BUFSIZ], buf[BUFSIZ], *cp;
4550ab9e 384 int of, mode, wrerr, exists, first;
5f10dd79 385 off_t i, size;
b7ff3bb3
BJ
386 char *whopp;
387 struct stat stb; int targisdir = 0;
388#define SCREWUP(str) { whopp = str; goto screwup; }
389 int mask = umask(0);
390 char *myargv[1];
391
392 umask(mask);
393 if (argc > 1) {
394 error("rcp: ambiguous target\n");
395 exit(1);
396 }
397 targ = *argv;
398 if (targetshouldbedirectory)
399 verifydir(targ);
400 ga();
401 if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
402 targisdir = 1;
4550ab9e 403 for (first = 1; ; first = 0) {
b7ff3bb3
BJ
404 cp = cmdbuf;
405 if (read(rem, cp, 1) <= 0)
406 return;
407 if (*cp++ == '\n')
408 SCREWUP("unexpected '\\n'");
409 do {
410 if (read(rem, cp, 1) != 1)
411 SCREWUP("lost connection");
412 } while (*cp++ != '\n');
413 *cp = 0;
414 if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
415 if (iamremote == 0)
06fb72f5 416 (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
b7ff3bb3
BJ
417 if (cmdbuf[0] == '\02')
418 exit(1);
419 errs++;
420 continue;
421 }
422 *--cp = 0;
423 cp = cmdbuf;
424 if (*cp == 'E') {
425 ga();
426 return;
427 }
4550ab9e
RC
428 if (*cp != 'C' && *cp != 'D') {
429 /*
430 * Check for the case "rcp remote:foo\* local:bar".
431 * In this case, the line "No match." can be returned
432 * by the shell before the rcp command on the remote is
433 * executed so the ^Aerror_message convention isn't
434 * followed.
435 */
436 if (first) {
437 error("%s\n", cp);
438 exit(1);
439 }
b7ff3bb3 440 SCREWUP("expected control record");
4550ab9e 441 }
b7ff3bb3
BJ
442 cp++;
443 mode = 0;
444 for (; cp < cmdbuf+5; cp++) {
445 if (*cp < '0' || *cp > '7')
446 SCREWUP("bad mode");
447 mode = (mode << 3) | (*cp - '0');
448 }
449 if (*cp++ != ' ')
450 SCREWUP("mode not delimited");
451 size = 0;
452 while (*cp >= '0' && *cp <= '9')
453 size = size * 10 + (*cp++ - '0');
454 if (*cp++ != ' ')
455 SCREWUP("size not delimited");
456 if (targisdir)
457 (void) sprintf(nambuf, "%s%s%s", targ,
458 *targ ? "/" : "", cp);
459 else
460 (void) strcpy(nambuf, targ);
461 exists = stat(nambuf, &stb) == 0;
462 if (exists && access(nambuf, 2) < 0)
463 goto bad2;
464 { char *slash = rindex(nambuf, '/'), *dir;
465 if (slash == 0) {
466 slash = "/";
467 dir = ".";
468 } else {
469 *slash = 0;
470 dir = nambuf;
04f0ebce
RC
471 if (*dir == '\0')
472 dir = "/";
b7ff3bb3
BJ
473 }
474 if (exists == 0 && access(dir, 2) < 0)
475 goto bad;
476 *slash = '/';
477 if (cmdbuf[0] == 'D') {
478 if (stat(nambuf, &stb) == 0) {
479 if ((stb.st_mode&S_IFMT) != S_IFDIR) {
480 errno = ENOTDIR;
481 goto bad;
482 }
483 } else if (mkdir(nambuf, mode) < 0)
484 goto bad;
485 myargv[0] = nambuf;
486 sink(1, myargv);
487 continue;
488 }
489 if ((of = creat(nambuf, mode)) < 0) {
490 bad:
491 *slash = '/';
492 bad2:
493 error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
494 continue;
495 }
496 }
497 if (exists == 0) {
498 (void) stat(nambuf, &stb);
499 (void) chown(nambuf, pwd->pw_uid, stb.st_gid);
500 (void) chmod(nambuf, mode &~ mask);
501 }
502 ga();
503 wrerr = 0;
504 for (i = 0; i < size; i += BUFSIZ) {
505 int amt = BUFSIZ;
506 char *cp = buf;
507
508 if (i + amt > size)
509 amt = size - i;
510 do {
511 int j = read(rem, cp, amt);
512
513 if (j <= 0)
514 exit(1);
515 amt -= j;
516 cp += j;
517 } while (amt > 0);
518 amt = BUFSIZ;
519 if (i + amt > size)
520 amt = size - i;
521 if (wrerr == 0 && write(of, buf, amt) != amt)
522 wrerr++;
523 }
524 (void) close(of);
525 (void) response();
526 if (wrerr)
527 error("rcp: %s: %s\n", cp, sys_errlist[errno]);
528 else
529 ga();
530 }
531screwup:
532 error("rcp: protocol screwup: %s\n", whopp);
533 exit(1);
534}
535
536/*VARARGS*/
537error(fmt, a1, a2, a3, a4, a5)
538 char *fmt;
539 int a1, a2, a3, a4, a5;
540{
541 char buf[BUFSIZ], *cp = buf;
542
543 errs++;
544 *cp++ = 1;
545 (void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
546 (void) write(rem, buf, strlen(buf));
547 if (iamremote == 0)
548 (void) write(2, buf+1, strlen(buf+1));
549}
550
551mkdir(name, mode)
552 char *name;
553 int mode;
554{
555 char *argv[4];
556 int pid, rc;
557
558 argv[0] = "mkdir";
559 argv[1] = name;
560 argv[2] = 0;
561 pid = fork();
562 if (pid < 0) {
563 perror("cp");
564 return (1);
565 }
566 if (pid) {
567 while (wait(&rc) != pid)
568 continue;
569 if (rc == 0)
570 if (chmod(name, mode) < 0) {
571 perror(name);
572 rc = 1;
573 }
574 return (rc);
575 }
576 (void) setuid(getuid());
577 execv("/bin/mkdir", argv);
578 execv("/usr/bin/mkdir", argv);
579 perror("mkdir");
580 _exit(1);
581 /*NOTREACHED*/
582}