First stable version.
[unix-history] / usr / src / usr.bin / rdist / server.c
CommitLineData
2f8aab68 1#ifndef lint
82572cb6 2static char *sccsid = "@(#)server.c 4.2 (Berkeley) 83/09/27";
2f8aab68
RC
3#endif
4
5#include "defs.h"
6
7#define ga() (void) write(rem, "", 1)
8
82572cb6 9char buf[BUFSIZ]; /* general purpose buffer */
2f8aab68
RC
10char target[BUFSIZ]; /* target/source directory name */
11char *tp; /* pointer to end of target name */
12int catname; /* cat name to target name */
13
82572cb6
RC
14static struct passwd *p = NULL;
15static struct group *g = NULL;
16
17extern FILE *lfp; /* log file for mailing changes */
18
2f8aab68
RC
19/*
20 * Server routine to read requests and process them.
21 * Commands are:
22 * Tname - Transmit file if out of date
23 * Vname - Verify if file out of date or not
24 * Qname - Query if file exists. Return mtime & size if it does.
25 */
26server()
27{
28 char cmdbuf[BUFSIZ];
29 register char *cp;
30 register struct block *bp, *last = NULL;
31 register int n;
32 static struct block cmdblk = { EXCEPT };
33
34 (void) umask(0);
35 ga();
36
37 for (;;) {
38 cp = cmdbuf;
39 if (read(rem, cp, 1) <= 0)
40 return;
41 if (*cp++ == '\n') {
42 error("expected control record\n");
43 continue;
44 }
45 do {
46 if (read(rem, cp, 1) != 1)
47 lostconn();
82572cb6 48 } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
2f8aab68
RC
49 *--cp = '\0';
50 cp = cmdbuf;
51 switch (*cp++) {
52 case 'X': /* add name to list of files to exclude */
53 if (*cp == '\0')
54 continue;
55 bp = ALLOC(block);
56 if (bp == NULL)
57 fatal("ran out of memory\n");
58 bp->b_type = NAME;
59 bp->b_next = bp->b_args = NULL;
60 bp->b_name = cp = (char *) malloc(strlen(cp) + 1);
61 if (cp == NULL)
62 fatal("ran out of memory\n");
63 strcpy(cp, &cmdbuf[1]);
64 if (last == NULL) {
65 except = &cmdblk;
66 cmdblk.b_args = last = bp;
67 } else {
68 last->b_next = bp;
69 last = bp;
70 }
71 continue;
72
73 case 'T': /* init target file/directory name */
82572cb6
RC
74 catname = 1; /* target should be directory */
75 goto dotarget;
76
77 case 't': /* init target file/directory name */
2f8aab68 78 catname = 0;
82572cb6
RC
79 dotarget:
80 exptilde(target, cp);
2f8aab68
RC
81 tp = target;
82 while (*tp)
83 tp++;
84 continue;
85
86 case 'S': /* Send. Transfer file if out of date. */
87 tp = NULL;
88 sendf(cp, 0);
89 continue;
90
91 case 'V': /* Verify. See if file is out of date. */
92 tp = NULL;
93 sendf(cp, 1);
94 continue;
95
96 case 'R': /* Receive. Transfer file. */
97 recvf(cp, 0);
98 continue;
99
100 case 'D': /* Directory. Transfer file. */
101 recvf(cp, 1);
102 continue;
103
104 case 'E': /* End. (of directory) */
105 *tp = '\0';
82572cb6 106 if (--catname < 0) {
2f8aab68
RC
107 error("too many 'E's\n");
108 continue;
109 }
82572cb6
RC
110 cp = rindex(target, '/');
111 if (cp == NULL)
112 tp = NULL;
113 else {
114 *cp = '\0';
115 tp = cp;
116 }
2f8aab68
RC
117 ga();
118 continue;
119
120 case 'Q': /* Query. Does file exist? */
121 query(cp);
122 continue;
123
124 case 'L': /* Log. save message in log file */
125 query(cp);
126 continue;
127
82572cb6
RC
128 case '\1':
129 errs++;
130 continue;
131
132 case '\2':
133 return;
134
2f8aab68
RC
135 default:
136 error("unknown command type %s\n", cp);
137 case '\0':
138 continue;
139 }
140 }
141}
142
143/*
144 * Transfer the file or directory 'name'.
145 */
146sendf(name, verify)
147 char *name;
148 int verify;
149{
150 register char *last;
2f8aab68 151 struct stat stb;
82572cb6 152 int sizerr, f, u;
2f8aab68
RC
153 off_t i;
154
155 if (debug)
156 printf("sendf(%s, %d)\n", name, verify);
157
158 if (exclude(name))
159 return;
160
82572cb6
RC
161 /*
162 * first time sendf() is called?
163 */
164 if (tp == NULL) {
165 exptilde(target, name);
166 tp = name = target;
167 while (*tp)
168 tp++;
169 }
2f8aab68
RC
170 if (access(name, 4) < 0 || stat(name, &stb) < 0) {
171 error("%s: %s\n", name, sys_errlist[errno]);
172 return;
173 }
174 last = rindex(name, '/');
175 if (last == NULL)
176 last = name;
177 else
178 last++;
82572cb6 179 if ((u = update(last, &stb)) == 0)
2f8aab68
RC
180 return;
181
82572cb6
RC
182 if (p == NULL || p->pw_uid != stb.st_uid)
183 if ((p = getpwuid(stb.st_uid)) == NULL) {
184 error("no password entry for uid %d\n", stb.st_uid);
185 return;
186 }
187 if (g == NULL || g->gr_gid != stb.st_gid)
188 if ((g = getgrgid(stb.st_gid)) == NULL) {
189 error("no name for group %d\n", stb.st_gid);
190 return;
191 }
2f8aab68
RC
192
193 switch (stb.st_mode & S_IFMT) {
194 case S_IFREG:
195 break;
196
197 case S_IFDIR:
198 rsendf(name, verify, &stb, p->pw_name, g->gr_name);
199 return;
200
201 default:
202 error("%s: not a plain file\n", name);
203 return;
204 }
205
82572cb6 206 log(lfp, "%s: %s\n", u == 2 ? "updating" : "installing", name);
2f8aab68
RC
207
208 if (verify || vflag)
209 return;
210
82572cb6
RC
211 if ((f = open(name, 0)) < 0) {
212 error("%s: %s\n", name, sys_errlist[errno]);
213 return;
214 }
2f8aab68
RC
215 (void) sprintf(buf, "R%04o %D %D %s %s %s\n", stb.st_mode & 07777,
216 stb.st_size, stb.st_mtime, p->pw_name, g->gr_name, last);
217 if (debug)
218 printf("buf = %s", buf);
219 (void) write(rem, buf, strlen(buf));
220 if (response() < 0) {
221 (void) close(f);
222 return;
223 }
224 sizerr = 0;
225 for (i = 0; i < stb.st_size; i += BUFSIZ) {
226 int amt = BUFSIZ;
227 if (i + amt > stb.st_size)
228 amt = stb.st_size - i;
229 if (sizerr == 0 && read(f, buf, amt) != amt)
230 sizerr = 1;
231 (void) write(rem, buf, amt);
232 }
233 (void) close(f);
234 if (sizerr)
235 error("%s: file changed size\n", name);
236 else
237 ga();
238 (void) response();
239}
240
241rsendf(name, verify, st, owner, group)
242 char *name;
243 int verify;
244 struct stat *st;
245 char *owner, *group;
246{
247 DIR *d;
248 struct direct *dp;
249 register char *last;
250 char *otp;
82572cb6 251 int len;
2f8aab68
RC
252
253 if (debug)
254 printf("rsendf(%s, %d, %x, %s, %s)\n", name, verify, st,
255 owner, group);
256
257 if ((d = opendir(name)) == NULL) {
258 error("%s: %s\n", name, sys_errlist[errno]);
259 return;
260 }
261 last = rindex(name, '/');
262 if (last == NULL)
263 last = name;
264 else
265 last++;
266 (void) sprintf(buf, "D%04o 0 0 %s %s %s\n", st->st_mode & 07777,
267 owner, group, last);
268 if (debug)
269 printf("buf = %s", buf);
270 (void) write(rem, buf, strlen(buf));
271 if (response() < 0) {
272 closedir(d);
273 return;
274 }
2f8aab68 275 otp = tp;
82572cb6 276 len = tp - target;
2f8aab68 277 while (dp = readdir(d)) {
2f8aab68
RC
278 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
279 continue;
82572cb6 280 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
2f8aab68
RC
281 error("%s/%s: Name too long\n", name, dp->d_name);
282 continue;
283 }
284 tp = otp;
285 *tp++ = '/';
286 last = dp->d_name;
287 while (*tp++ = *last++)
288 ;
289 tp--;
290 sendf(target, verify);
291 }
292 closedir(d);
293 (void) write(rem, "E\n", 2);
294 (void) response();
295 tp = otp;
296 *tp = '\0';
297}
298
299/*
300 * Check to see if file needs to be updated on the remote machine.
82572cb6 301 * Returns 0 if no update, 1 if remote doesn't exist, and 2 if out of date.
2f8aab68
RC
302 */
303update(name, st)
304 char *name;
305 struct stat *st;
306{
307 register char *cp;
308 register off_t size;
309 register time_t mtime;
310
311 if (debug)
312 printf("update(%s, %x)\n", name, st);
313
314 /*
315 * Check to see if the file exists on the remote machine.
316 */
317 (void) sprintf(buf, "Q%s\n", name);
318 if (debug)
319 printf("buf = %s", buf);
320 (void) write(rem, buf, strlen(buf));
321 cp = buf;
322 do {
323 if (read(rem, cp, 1) != 1)
324 lostconn();
82572cb6 325 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
2f8aab68
RC
326 *--cp = '\0';
327 cp = buf;
328 if (debug)
329 printf("resp = %s\n", cp);
330
331 switch (*cp++) {
332 case 'Y':
333 break;
334
82572cb6 335 case 'N': /* file doesn't exist so install it */
2f8aab68
RC
336 return(1);
337
338 case '\1':
82572cb6
RC
339 errs++;
340 if (*cp != '\0') {
341 if (!iamremote) {
342 fflush(stdout);
343 (void) write(2, cp, strlen(cp));
344 }
345 if (lfp != NULL)
346 (void) fwrite(cp, 1, strlen(cp), lfp);
347 }
2f8aab68
RC
348 return(0);
349
350 default:
351 error("unexpected response '%c' to query\n", *--cp);
352 return(0);
353 }
354
355 if (*cp == '\0') {
356 if ((st->st_mode & S_IFMT) == S_IFDIR)
82572cb6
RC
357 return(2);
358 return(1);
2f8aab68
RC
359 }
360
361 size = 0;
362 while (isdigit(*cp))
363 size = size * 10 + (*cp++ - '0');
364 if (*cp++ != ' ') {
365 error("size not delimited\n");
366 return(0);
367 }
368 mtime = 0;
369 while (isdigit(*cp))
370 mtime = mtime * 10 + (*cp++ - '0');
371 if (*cp != '\0') {
372 error("mtime not delimited\n");
373 return(0);
374 }
375 /*
376 * File needs to be updated?
377 */
378 if (st->st_mtime == mtime && st->st_size == size ||
82572cb6 379 yflag && st->st_mtime < mtime)
2f8aab68 380 return(0);
82572cb6 381 return(2);
2f8aab68
RC
382}
383
384/*
385 * Query. Check to see if file exists. Return one of the following:
386 * N\n - doesn't exist
387 * Ysize mtime\n - exists and its a regular file (size & mtime of file)
388 * Y\n - exists and its a directory
389 * ^Aerror message\n
390 */
391query(name)
392 char *name;
393{
394 struct stat stb;
395
396 if (catname)
397 (void) sprintf(tp, "/%s", name);
398 if (stat(target, &stb) < 0) {
399 (void) write(rem, "N\n", 2);
400 *tp = '\0';
401 return;
402 }
403
404 switch (stb.st_mode & S_IFMT) {
405 case S_IFREG:
406 (void) sprintf(buf, "Y%D %D\n", stb.st_size, stb.st_mtime);
407 (void) write(rem, buf, strlen(buf));
408 break;
409
410 case S_IFDIR:
411 (void) write(rem, "Y\n", 2);
412 break;
413
414 default:
415 error("%s: not a plain file\n", name);
416 break;
417 }
418 *tp = '\0';
419}
420
421recvf(cmd, isdir)
422 char *cmd;
423 int isdir;
424{
425 register char *cp;
426 int f, mode, wrerr, exists, olderrno;
427 off_t i, size;
428 time_t mtime;
429 struct stat stb;
430 struct timeval tvp[2];
431 char *owner, *group, *dir;
432 char new[BUFSIZ];
82572cb6 433 extern char *tmpname;
2f8aab68
RC
434
435 mode = 0;
436 for (cp = cmd; cp < cmd+4; cp++) {
437 if (*cp < '0' || *cp > '7') {
438 error("bad mode\n");
439 return;
440 }
441 mode = (mode << 3) | (*cp - '0');
442 }
443 if (*cp++ != ' ') {
444 error("mode not delimited\n");
445 return;
446 }
447 size = 0;
448 while (isdigit(*cp))
449 size = size * 10 + (*cp++ - '0');
450 if (*cp++ != ' ') {
451 error("size not delimited\n");
452 return;
453 }
454 mtime = 0;
455 while (isdigit(*cp))
456 mtime = mtime * 10 + (*cp++ - '0');
457 if (*cp++ != ' ') {
458 error("mtime not delimited\n");
459 return;
460 }
461 owner = cp;
462 while (*cp && *cp != ' ')
463 cp++;
464 if (*cp != ' ') {
465 error("owner name not delimited\n");
466 return;
467 }
468 *cp++ = '\0';
469 group = cp;
470 while (*cp && *cp != ' ')
471 cp++;
472 if (*cp != ' ') {
473 error("group name not delimited\n");
474 return;
475 }
476 *cp++ = '\0';
477
82572cb6 478 new[0] = '\0';
2f8aab68
RC
479 if (isdir) {
480 if (catname++) {
481 *tp++ = '/';
482 while (*tp++ = *cp++)
483 ;
484 tp--;
485 }
82572cb6
RC
486 if (vflag) {
487 ga();
488 return;
489 }
2f8aab68
RC
490 if (stat(target, &stb) == 0) {
491 if ((stb.st_mode & S_IFMT) != S_IFDIR) {
492 errno = ENOTDIR;
493 goto bad;
494 }
495 } else {
496 /*
497 * Check parent directory for write permission.
498 */
499 cp = rindex(target, '/');
500 if (cp == NULL)
501 dir = ".";
502 else if (cp == target) {
503 dir = "/";
504 cp = NULL;
505 } else {
506 dir = target;
507 *cp = '\0';
508 }
82572cb6
RC
509 if (access(dir, 2) < 0)
510 goto bad2;
2f8aab68
RC
511 if (cp != NULL)
512 *cp = '/';
2f8aab68
RC
513 if (mkdir(target, mode) < 0)
514 goto bad;
82572cb6
RC
515 if (chog(target, owner, group, mode) < 0)
516 return;
2f8aab68 517 }
2f8aab68
RC
518 ga();
519 return;
520 }
521
522 if (catname)
523 (void) sprintf(tp, "/%s", cp);
524 if (stat(target, &stb) == 0) {
525 switch (stb.st_mode & S_IFMT) {
526 case S_IFREG:
527 break;
528
529 case S_IFDIR:
530 if (!catname) {
531 (void) sprintf(tp, "/%s", cp);
532 break;
533 }
534
535 default:
536 error("%s: not a regular file\n", target);
537 return;
538 }
539 }
540 /*
541 * Check parent directory for write permission.
542 */
543 cp = rindex(target, '/');
544 if (cp == NULL)
545 dir = ".";
546 else if (cp == target) {
547 dir = "/";
548 cp = NULL;
549 } else {
550 dir = target;
551 *cp = '\0';
552 }
82572cb6
RC
553 if (access(dir, 2) < 0) {
554bad2:
555 error("%s: %s\n", dir, sys_errlist[errno]);
556 if (cp != NULL)
557 *cp = '/';
558 return;
559 }
560 (void) sprintf(new, "%s/%s", dir, tmpname);
2f8aab68
RC
561 if (cp != NULL)
562 *cp = '/';
2f8aab68
RC
563 if ((f = creat(new, mode)) < 0)
564 goto bad1;
82572cb6
RC
565 if (chog(new, owner, group, mode) < 0) {
566 (void) close(f);
567 (void) unlink(new);
2f8aab68 568 return;
82572cb6 569 }
2f8aab68
RC
570 ga();
571
572 wrerr = 0;
573 for (i = 0; i < size; i += BUFSIZ) {
574 int amt = BUFSIZ;
575 char *cp = buf;
576
577 if (i + amt > size)
578 amt = size - i;
579 do {
580 int j = read(rem, cp, amt);
581
82572cb6
RC
582 if (j <= 0) {
583 (void) close(f);
584 (void) unlink(new);
2f8aab68 585 cleanup();
82572cb6 586 }
2f8aab68
RC
587 amt -= j;
588 cp += j;
589 } while (amt > 0);
590 amt = BUFSIZ;
591 if (i + amt > size)
592 amt = size - i;
593 if (wrerr == 0 && write(f, buf, amt) != amt) {
594 olderrno = errno;
595 wrerr++;
596 }
597 }
598 (void) response();
599 if (wrerr) {
600 error("%s: %s\n", cp, sys_errlist[olderrno]);
82572cb6
RC
601 (void) close(f);
602 (void) unlink(new);
2f8aab68
RC
603 return;
604 }
605
606 /*
607 * Set last modified time
608 */
609 (void) fstat(f, &stb);
610 (void) close(f);
611 tvp[0].tv_sec = stb.st_atime;
612 tvp[0].tv_usec = 0;
613 tvp[1].tv_sec = mtime;
614 tvp[1].tv_usec = 0;
615 if (utimes(new, tvp) < 0) {
616bad1:
617 error("%s: %s\n", new, sys_errlist[errno]);
82572cb6
RC
618 if (new[0])
619 (void) unlink(new);
2f8aab68
RC
620 return;
621 }
622
623 if (rename(new, target) < 0) {
624bad:
625 error("%s: %s\n", target, sys_errlist[errno]);
82572cb6
RC
626 if (new[0])
627 (void) unlink(new);
2f8aab68
RC
628 return;
629 }
630 ga();
631}
632
633/*
634 * Change owner and group of file.
635 */
82572cb6 636chog(file, owner, group, mode)
2f8aab68 637 char *file, *owner, *group;
82572cb6 638 int mode;
2f8aab68 639{
82572cb6
RC
640 extern int userid, groupid;
641 extern char user[];
2f8aab68
RC
642 register int i;
643 int uid, gid;
644
645 uid = userid;
646 if (userid == 0) {
82572cb6
RC
647 if (p == NULL || strcmp(owner, p->pw_name) != 0) {
648 if ((p = getpwnam(owner)) == NULL) {
649 if (mode & 04000) {
650 error("%s: unknown login name\n", owner);
651 return(-1);
652 }
653 } else
654 uid = p->pw_uid;
655 } else
656 uid = p->pw_uid;
657 }
658 gid = groupid;
659 if (g == NULL || strcmp(group, g->gr_name) != 0) {
660 if ((g = getgrnam(group)) == NULL) {
661 if (mode & 02000) {
662 error("%s: unknown group\n", group);
663 return(-1);
664 }
665 } else
666 gid = g->gr_gid;
667 } else
668 gid = g->gr_gid;
669 if (userid && groupid != gid) {
670 for (i = 0; g->gr_mem[i] != NULL; i++)
2f8aab68
RC
671 if (!(strcmp(user, g->gr_mem[i])))
672 goto ok;
82572cb6 673 gid = groupid;
2f8aab68
RC
674 }
675ok:
676 if (chown(file, uid, gid) < 0) {
677 error("%s: %s\n", file, sys_errlist[errno]);
678 return(-1);
679 }
680 return(0);
681}
682
2f8aab68 683/*VARARGS*/
82572cb6
RC
684log(fp, fmt, a1, a2, a3)
685 FILE *fp;
2f8aab68
RC
686 char *fmt;
687 int a1, a2, a3;
688{
689 /* Print changes locally if not quiet mode */
690 if (!qflag)
691 printf(fmt, a1, a2, a3);
692
693 /* Save changes (for mailing) if really updating files */
82572cb6
RC
694 if (!vflag && fp != NULL)
695 fprintf(fp, fmt, a1, a2, a3);
2f8aab68
RC
696}
697
698/*VARARGS*/
699error(fmt, a1, a2, a3)
700 char *fmt;
701 int a1, a2, a3;
702{
703 errs++;
704 strcpy(buf, "\1rdist: ");
705 (void) sprintf(buf+8, fmt, a1, a2, a3);
706 (void) write(rem, buf, strlen(buf));
707 if (buf[1] != '\0') {
82572cb6
RC
708 if (!iamremote) {
709 fflush(stdout);
2f8aab68 710 (void) write(2, buf+1, strlen(buf+1));
82572cb6 711 }
2f8aab68
RC
712 if (lfp != NULL)
713 (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
714 }
715}
716
717/*VARARGS*/
718fatal(fmt, a1, a2,a3)
719 char *fmt;
720 int a1, a2, a3;
721{
722 errs++;
723 strcpy(buf, "\2rdist: ");
724 (void) sprintf(buf+8, fmt, a1, a2, a3);
725 (void) write(rem, buf, strlen(buf));
726 if (buf[1] != '\0') {
82572cb6
RC
727 if (!iamremote) {
728 fflush(stdout);
2f8aab68 729 (void) write(2, buf+1, strlen(buf+1));
82572cb6 730 }
2f8aab68
RC
731 if (lfp != NULL)
732 (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
733 }
734 cleanup();
735}
736
737response()
738{
739 char resp, c, *cp = buf;
740
741 if (debug)
742 printf("response()\n");
743
744 if (read(rem, &resp, 1) != 1)
745 lostconn();
746
747 switch (resp) {
748 case '\0':
749 return(0);
750
751 default:
752 *cp++ = resp;
753 /* fall into... */
754 case '\1':
755 case '\2':
756 errs++;
757 do {
758 if (read(rem, cp, 1) != 1)
759 lostconn();
82572cb6
RC
760 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
761 if (buf[0] != '\n') {
762 if (!iamremote) {
763 fflush(stdout);
2f8aab68 764 (void) write(2, buf, cp - buf);
82572cb6 765 }
2f8aab68
RC
766 if (lfp != NULL)
767 (void) fwrite(buf, 1, cp - buf, lfp);
768 }
769 if (resp == '\1')
770 return(-1);
771 cleanup();
772 }
773 /*NOTREACHED*/
774}
775
776lostconn()
777{
778 if (!iamremote)
779 fprintf(stderr, "rdist: lost connection\n");
780 cleanup();
781}