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