Commit | Line | Data |
---|---|---|
2f8aab68 RC |
1 | #ifndef lint |
2 | static 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 | ||
9 | char buf[BUFSIZ]; /* gerneral purpose buffer */ | |
10 | char target[BUFSIZ]; /* target/source directory name */ | |
11 | char *tp; /* pointer to end of target name */ | |
12 | int 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 | */ | |
21 | server() | |
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 | */ | |
125 | sendf(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 | ||
215 | rsendf(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 | */ | |
286 | update(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 | */ | |
368 | query(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 | ||
398 | recvf(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) { | |
577 | bad1: | |
578 | error("%s: %s\n", new, sys_errlist[errno]); | |
579 | return; | |
580 | } | |
581 | ||
582 | if (rename(new, target) < 0) { | |
583 | bad: | |
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 | */ | |
593 | chog(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 | } | |
626 | ok: | |
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 | ||
634 | extern FILE *lfp; /* log file for mailing changes */ | |
635 | ||
636 | /*VARARGS*/ | |
637 | log(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*/ | |
651 | error(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*/ | |
668 | fatal(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 | ||
685 | response() | |
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 | ||
722 | lostconn() | |
723 | { | |
724 | if (!iamremote) | |
725 | fprintf(stderr, "rdist: lost connection\n"); | |
726 | cleanup(); | |
727 | } |