doc non-empty dirs (mis?)feature; version # mismatch might be something else;
[unix-history] / usr / src / usr.bin / rdist / server.c
CommitLineData
7172eb74
DF
1/*
2 * Copyright (c) 1983 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
2f8aab68 7#ifndef lint
ac288309 8static char sccsid[] = "@(#)server.c 5.2 (Berkeley) %G%";
7172eb74 9#endif not lint
2f8aab68
RC
10
11#include "defs.h"
12
d6bccb44
RC
13#define ack() (void) write(rem, "\0\n", 2)
14#define err() (void) write(rem, "\1\n", 2)
2f8aab68 15
a3e6fd64 16struct linkbuf *ihead; /* list of files with more than one link */
82572cb6 17char buf[BUFSIZ]; /* general purpose buffer */
2f8aab68
RC
18char target[BUFSIZ]; /* target/source directory name */
19char *tp; /* pointer to end of target name */
ac288309 20char *Tdest; /* pointer to last T dest*/
2f8aab68 21int catname; /* cat name to target name */
3024eb6f 22char *stp[32]; /* stack of saved tp's for directories */
e8109cf8 23int oumask; /* old umask for creating files */
82572cb6
RC
24
25extern FILE *lfp; /* log file for mailing changes */
26
0fccdfef 27int cleanup();
a3e6fd64 28struct linkbuf *savelink();
0fccdfef 29
2f8aab68
RC
30/*
31 * Server routine to read requests and process them.
32 * Commands are:
33 * Tname - Transmit file if out of date
34 * Vname - Verify if file out of date or not
35 * Qname - Query if file exists. Return mtime & size if it does.
36 */
37server()
38{
39 char cmdbuf[BUFSIZ];
40 register char *cp;
2f8aab68 41
0fccdfef
RC
42 signal(SIGHUP, cleanup);
43 signal(SIGINT, cleanup);
44 signal(SIGQUIT, cleanup);
45 signal(SIGTERM, cleanup);
46 signal(SIGPIPE, cleanup);
47
48 rem = 0;
e8109cf8 49 oumask = umask(0);
6e6d779a
RC
50 (void) sprintf(buf, "V%d\n", VERSION);
51 (void) write(rem, buf, strlen(buf));
2f8aab68
RC
52
53 for (;;) {
54 cp = cmdbuf;
55 if (read(rem, cp, 1) <= 0)
56 return;
57 if (*cp++ == '\n') {
e8109cf8 58 error("server: expected control record\n");
2f8aab68
RC
59 continue;
60 }
61 do {
62 if (read(rem, cp, 1) != 1)
0fccdfef 63 cleanup();
82572cb6 64 } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
2f8aab68
RC
65 *--cp = '\0';
66 cp = cmdbuf;
67 switch (*cp++) {
2f8aab68 68 case 'T': /* init target file/directory name */
82572cb6
RC
69 catname = 1; /* target should be directory */
70 goto dotarget;
71
72 case 't': /* init target file/directory name */
2f8aab68 73 catname = 0;
82572cb6 74 dotarget:
e8109cf8
RC
75 if (exptilde(target, cp) == NULL)
76 continue;
2f8aab68
RC
77 tp = target;
78 while (*tp)
79 tp++;
d6bccb44 80 ack();
2f8aab68
RC
81 continue;
82
a3e6fd64
RC
83 case 'R': /* Transfer a regular file. */
84 recvf(cp, S_IFREG);
2f8aab68
RC
85 continue;
86
a3e6fd64
RC
87 case 'D': /* Transfer a directory. */
88 recvf(cp, S_IFDIR);
89 continue;
90
91 case 'K': /* Transfer symbolic link. */
92 recvf(cp, S_IFLNK);
93 continue;
94
95 case 'k': /* Transfer hard link. */
96 hardlink(cp);
2f8aab68
RC
97 continue;
98
99 case 'E': /* End. (of directory) */
100 *tp = '\0';
e8109cf8
RC
101 if (catname <= 0) {
102 error("server: too many 'E's\n");
2f8aab68
RC
103 continue;
104 }
e8109cf8 105 tp = stp[--catname];
3024eb6f 106 *tp = '\0';
d6bccb44 107 ack();
2f8aab68
RC
108 continue;
109
d1dee8e8 110 case 'C': /* Clean. Cleanup a directory */
d6bccb44 111 clean(cp);
e8109cf8
RC
112 continue;
113
114 case 'Q': /* Query. Does the file/directory exist? */
115 query(cp);
116 continue;
117
d6bccb44
RC
118 case 'S': /* Special. Execute commands */
119 dospecial(cp);
120 continue;
121
e8109cf8
RC
122#ifdef notdef
123 /*
124 * These entries are reserved but not currently used.
125 * The intent is to allow remote hosts to have master copies.
126 * Currently, only the host rdist runs on can have masters.
127 */
128 case 'X': /* start a new list of files to exclude */
129 except = bp = NULL;
130 case 'x': /* add name to list of files to exclude */
131 if (*cp == '\0') {
d6bccb44 132 ack();
d1dee8e8
RC
133 continue;
134 }
e8109cf8
RC
135 if (*cp == '~') {
136 if (exptilde(buf, cp) == NULL)
137 continue;
138 cp = buf;
d1dee8e8 139 }
e8109cf8 140 if (bp == NULL)
d6bccb44 141 except = bp = expand(makeblock(NAME, cp), E_VARS);
e8109cf8 142 else
d6bccb44 143 bp->b_next = expand(makeblock(NAME, cp), E_VARS);
e8109cf8
RC
144 while (bp->b_next != NULL)
145 bp = bp->b_next;
d6bccb44 146 ack();
3024eb6f
RC
147 continue;
148
d6bccb44 149 case 'I': /* Install. Transfer file if out of date. */
e8109cf8
RC
150 opts = 0;
151 while (*cp >= '0' && *cp <= '7')
152 opts = (opts << 3) | (*cp++ - '0');
153 if (*cp++ != ' ') {
154 error("server: options not delimited\n");
155 return;
156 }
d6bccb44 157 install(cp, opts);
2f8aab68
RC
158 continue;
159
160 case 'L': /* Log. save message in log file */
3024eb6f 161 log(lfp, cp);
2f8aab68 162 continue;
e8109cf8 163#endif
2f8aab68 164
82572cb6 165 case '\1':
0fccdfef 166 nerrs++;
82572cb6
RC
167 continue;
168
169 case '\2':
170 return;
171
2f8aab68 172 default:
e8109cf8 173 error("server: unknown command '%s'\n", cp);
2f8aab68
RC
174 case '\0':
175 continue;
176 }
177 }
178}
179
180/*
e8109cf8
RC
181 * Update the file(s) if they are different.
182 * destdir = 1 if destination should be a directory
183 * (i.e., more than one source is being copied to the same destination).
2f8aab68 184 */
e8109cf8
RC
185install(src, dest, destdir, opts)
186 char *src, *dest;
187 int destdir, opts;
2f8aab68 188{
e8109cf8 189 char *rname;
ac288309 190 char destcopy[BUFSIZ];
2f8aab68 191
e8109cf8
RC
192 if (dest == NULL) {
193 opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
194 dest = src;
195 }
2f8aab68 196
e8109cf8
RC
197 if (nflag || debug) {
198 printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
199 opts & WHOLE ? " -w" : "",
200 opts & YOUNGER ? " -y" : "",
201 opts & COMPARE ? " -b" : "",
d6bccb44 202 opts & REMOVE ? " -R" : "", src, dest);
e8109cf8
RC
203 if (nflag)
204 return;
205 }
206
207 rname = exptilde(target, src);
208 if (rname == NULL)
209 return;
210 tp = target;
211 while (*tp)
212 tp++;
82572cb6 213 /*
e8109cf8 214 * If we are renaming a directory and we want to preserve
d6bccb44 215 * the directory heirarchy (-w), we must strip off the leading
e8109cf8 216 * directory name and preserve the rest.
82572cb6 217 */
e8109cf8 218 if (opts & WHOLE) {
d6bccb44
RC
219 while (*rname == '/')
220 rname++;
221 destdir = 1;
e8109cf8
RC
222 } else {
223 rname = rindex(target, '/');
224 if (rname == NULL)
225 rname = target;
226 else
227 rname++;
82572cb6 228 }
e8109cf8
RC
229 if (debug)
230 printf("target = %s, rname = %s\n", target, rname);
231 /*
232 * Pass the destination file/directory name to remote.
233 */
234 (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
235 if (debug)
236 printf("buf = %s", buf);
237 (void) write(rem, buf, strlen(buf));
238 if (response() < 0)
239 return;
240
ac288309
KS
241 if (destdir) {
242 strcpy(destcopy, dest);
243 Tdest = destcopy;
244 }
e8109cf8 245 sendf(rname, opts);
ac288309 246 Tdest = 0;
e8109cf8
RC
247}
248
ac288309
KS
249#define protoname() (pw ? pw->pw_name : user)
250#define protogroup() (gr ? gr->gr_name : group)
e8109cf8
RC
251/*
252 * Transfer the file or directory in target[].
253 * rname is the name of the file on the remote host.
254 */
255sendf(rname, opts)
256 char *rname;
257 int opts;
258{
0fccdfef 259 register struct subcmd *sc;
e8109cf8 260 struct stat stb;
a3e6fd64 261 int sizerr, f, u, len;
e8109cf8 262 off_t i;
a3e6fd64
RC
263 DIR *d;
264 struct direct *dp;
265 char *otp, *cp;
4085f36f 266 extern struct subcmd *subcmds;
ac288309 267 static char user[15], group[15];
e8109cf8
RC
268
269 if (debug)
270 printf("sendf(%s, %x)\n", rname, opts);
271
4085f36f 272 if (except(target))
024fde5b 273 return;
80babee1 274 if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
e8109cf8 275 error("%s: %s\n", target, sys_errlist[errno]);
2f8aab68
RC
276 return;
277 }
a3e6fd64
RC
278 if ((u = update(rname, opts, &stb)) == 0) {
279 if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
280 (void) savelink(&stb);
2f8aab68 281 return;
a3e6fd64 282 }
2f8aab68 283
e8109cf8
RC
284 if (pw == NULL || pw->pw_uid != stb.st_uid)
285 if ((pw = getpwuid(stb.st_uid)) == NULL) {
ac288309
KS
286 log(lfp, "%s: no password entry for uid \n", target);
287 pw = NULL;
288 sprintf(user, ":%d", stb.st_uid);
82572cb6 289 }
e8109cf8
RC
290 if (gr == NULL || gr->gr_gid != stb.st_gid)
291 if ((gr = getgrgid(stb.st_gid)) == NULL) {
ac288309
KS
292 log(lfp, "%s: no name for group %d\n", target);
293 gr = NULL;
294 sprintf(group, ":%d", stb.st_gid);
82572cb6 295 }
024fde5b 296 if (u == 1) {
d6bccb44
RC
297 if (opts & VERIFY) {
298 log(lfp, "need to install: %s\n", target);
299 goto dospecial;
300 }
e8109cf8 301 log(lfp, "installing: %s\n", target);
d6bccb44 302 opts &= ~(COMPARE|REMOVE);
024fde5b 303 }
2f8aab68
RC
304
305 switch (stb.st_mode & S_IFMT) {
a3e6fd64
RC
306 case S_IFDIR:
307 if ((d = opendir(target)) == NULL) {
308 error("%s: %s\n", target, sys_errlist[errno]);
309 return;
310 }
311 (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
ac288309 312 stb.st_mode & 07777, protoname(), protogroup(), rname);
a3e6fd64 313 if (debug)
85b60cd9 314 printf("buf = %s", buf);
a3e6fd64
RC
315 (void) write(rem, buf, strlen(buf));
316 if (response() < 0) {
317 closedir(d);
318 return;
319 }
2f8aab68 320
a3e6fd64
RC
321 if (opts & REMOVE)
322 rmchk(opts);
d6bccb44 323
a3e6fd64
RC
324 otp = tp;
325 len = tp - target;
326 while (dp = readdir(d)) {
327 if (!strcmp(dp->d_name, ".") ||
328 !strcmp(dp->d_name, ".."))
329 continue;
330 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
331 error("%s/%s: Name too long\n", target,
332 dp->d_name);
333 continue;
334 }
335 tp = otp;
336 *tp++ = '/';
337 cp = dp->d_name;
338 while (*tp++ = *cp++)
339 ;
340 tp--;
341 sendf(dp->d_name, opts);
342 }
343 closedir(d);
344 (void) write(rem, "E\n", 2);
345 (void) response();
346 tp = otp;
347 *tp = '\0';
2f8aab68
RC
348 return;
349
a3e6fd64
RC
350 case S_IFLNK:
351 if (u != 1)
352 opts |= COMPARE;
ac288309
KS
353 if (stb.st_nlink > 1) {
354 struct linkbuf *lp;
355
356 if ((lp = savelink(&stb)) != NULL) {
357 /* install link */
358 if (*lp->target == 0)
359 (void) sprintf(buf, "k%o %s %s\n", opts,
360 lp->pathname, rname);
361 else
362 (void) sprintf(buf, "k%o %s/%s %s\n", opts,
363 lp->target, lp->pathname, rname);
364 if (debug)
365 printf("buf = %s", buf);
366 (void) write(rem, buf, strlen(buf));
367 (void) response();
368 return;
369 }
370 }
80babee1 371 (void) sprintf(buf, "K%o %o %ld %ld %s %s %s\n", opts,
a3e6fd64 372 stb.st_mode & 07777, stb.st_size, stb.st_mtime,
ac288309 373 protoname(), protogroup(), rname);
a3e6fd64
RC
374 if (debug)
375 printf("buf = %s", buf);
376 (void) write(rem, buf, strlen(buf));
377 if (response() < 0)
378 return;
379 sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
380 (void) write(rem, buf, stb.st_size);
381 if (debug)
382 printf("readlink = %.*s\n", stb.st_size, buf);
383 goto done;
384
385 case S_IFREG:
386 break;
387
2f8aab68 388 default:
a3e6fd64 389 error("%s: not a file or directory\n", target);
2f8aab68
RC
390 return;
391 }
392
024fde5b 393 if (u == 2) {
d6bccb44
RC
394 if (opts & VERIFY) {
395 log(lfp, "need to update: %s\n", target);
396 goto dospecial;
397 }
e8109cf8 398 log(lfp, "updating: %s\n", target);
024fde5b 399 }
a3e6fd64
RC
400
401 if (stb.st_nlink > 1) {
402 struct linkbuf *lp;
403
404 if ((lp = savelink(&stb)) != NULL) {
405 /* install link */
ac288309 406 if (*lp->target == 0)
a3e6fd64
RC
407 (void) sprintf(buf, "k%o %s %s\n", opts,
408 lp->pathname, rname);
ac288309
KS
409 else
410 (void) sprintf(buf, "k%o %s/%s %s\n", opts,
411 lp->target, lp->pathname, rname);
a3e6fd64
RC
412 if (debug)
413 printf("buf = %s", buf);
414 (void) write(rem, buf, strlen(buf));
415 (void) response();
416 return;
417 }
418 }
2f8aab68 419
e8109cf8
RC
420 if ((f = open(target, 0)) < 0) {
421 error("%s: %s\n", target, sys_errlist[errno]);
82572cb6
RC
422 return;
423 }
80babee1 424 (void) sprintf(buf, "R%o %o %ld %ld %s %s %s\n", opts,
f7770429 425 stb.st_mode & 07777, stb.st_size, stb.st_mtime,
ac288309 426 protoname(), protogroup(), rname);
2f8aab68
RC
427 if (debug)
428 printf("buf = %s", buf);
429 (void) write(rem, buf, strlen(buf));
430 if (response() < 0) {
431 (void) close(f);
432 return;
433 }
434 sizerr = 0;
435 for (i = 0; i < stb.st_size; i += BUFSIZ) {
436 int amt = BUFSIZ;
437 if (i + amt > stb.st_size)
438 amt = stb.st_size - i;
439 if (sizerr == 0 && read(f, buf, amt) != amt)
440 sizerr = 1;
441 (void) write(rem, buf, amt);
442 }
443 (void) close(f);
a3e6fd64 444done:
d6bccb44 445 if (sizerr) {
e8109cf8 446 error("%s: file changed size\n", target);
d6bccb44
RC
447 err();
448 } else
449 ack();
a3e6fd64
RC
450 f = response();
451 if (f < 0 || f == 0 && (opts & COMPARE))
d6bccb44
RC
452 return;
453dospecial:
4085f36f 454 for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
0fccdfef 455 if (sc->sc_type != SPECIAL)
d6bccb44 456 continue;
80babee1 457 if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
d6bccb44 458 continue;
0fccdfef 459 log(lfp, "special \"%s\"\n", sc->sc_name);
d6bccb44
RC
460 if (opts & VERIFY)
461 continue;
80babee1 462 (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
d6bccb44
RC
463 if (debug)
464 printf("buf = %s", buf);
465 (void) write(rem, buf, strlen(buf));
466 while (response() > 0)
467 ;
468 }
2f8aab68
RC
469}
470
a3e6fd64
RC
471struct linkbuf *
472savelink(stp)
473 struct stat *stp;
2f8aab68 474{
a3e6fd64
RC
475 struct linkbuf *lp;
476 int found = 0;
2f8aab68 477
a3e6fd64
RC
478 for (lp = ihead; lp != NULL; lp = lp->nextp)
479 if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
480 lp->count--;
481 return(lp);
2f8aab68 482 }
a3e6fd64
RC
483 lp = (struct linkbuf *) malloc(sizeof(*lp));
484 if (lp == NULL)
485 log(lfp, "out of memory, link information lost\n");
486 else {
487 lp->nextp = ihead;
488 ihead = lp;
489 lp->inum = stp->st_ino;
490 lp->devnum = stp->st_dev;
491 lp->count = stp->st_nlink - 1;
492 strcpy(lp->pathname, target);
ac288309
KS
493 if (Tdest)
494 strcpy(lp->target, Tdest);
495 else
496 *lp->target = 0;
2f8aab68 497 }
a3e6fd64 498 return(NULL);
2f8aab68
RC
499}
500
501/*
502 * Check to see if file needs to be updated on the remote machine.
e8109cf8
RC
503 * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
504 * and 3 if comparing binaries to determine if out of date.
2f8aab68 505 */
a3e6fd64 506update(rname, opts, stp)
e8109cf8 507 char *rname;
f7770429 508 int opts;
a3e6fd64 509 struct stat *stp;
2f8aab68 510{
e8109cf8 511 register char *cp, *s;
2f8aab68
RC
512 register off_t size;
513 register time_t mtime;
514
515 if (debug)
a3e6fd64 516 printf("update(%s, %x, %x)\n", rname, opts, stp);
2f8aab68
RC
517
518 /*
519 * Check to see if the file exists on the remote machine.
520 */
e8109cf8 521 (void) sprintf(buf, "Q%s\n", rname);
2f8aab68
RC
522 if (debug)
523 printf("buf = %s", buf);
524 (void) write(rem, buf, strlen(buf));
ac288309 525again:
e8109cf8 526 cp = s = buf;
2f8aab68
RC
527 do {
528 if (read(rem, cp, 1) != 1)
346dad94 529 lostconn();
82572cb6 530 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
2f8aab68 531
e8109cf8 532 switch (*s++) {
2f8aab68
RC
533 case 'Y':
534 break;
535
82572cb6 536 case 'N': /* file doesn't exist so install it */
2f8aab68
RC
537 return(1);
538
539 case '\1':
0fccdfef 540 nerrs++;
e8109cf8 541 if (*s != '\n') {
82572cb6
RC
542 if (!iamremote) {
543 fflush(stdout);
e8109cf8 544 (void) write(2, s, cp - s);
82572cb6
RC
545 }
546 if (lfp != NULL)
e8109cf8 547 (void) fwrite(s, 1, cp - s, lfp);
82572cb6 548 }
2f8aab68
RC
549 return(0);
550
ac288309
KS
551 case '\3':
552 *--cp = '\0';
553 if (lfp != NULL)
554 log(lfp, "update: note: %s\n", s);
555 goto again;
556
2f8aab68 557 default:
85b60cd9 558 *--cp = '\0';
ac288309 559 error("update: unexpected response '%s'\n", s);
2f8aab68
RC
560 return(0);
561 }
562
e8109cf8 563 if (*s == '\n')
3024eb6f 564 return(2);
2f8aab68 565
024fde5b
RC
566 if (opts & COMPARE)
567 return(3);
568
2f8aab68 569 size = 0;
e8109cf8
RC
570 while (isdigit(*s))
571 size = size * 10 + (*s++ - '0');
572 if (*s++ != ' ') {
573 error("update: size not delimited\n");
2f8aab68
RC
574 return(0);
575 }
576 mtime = 0;
e8109cf8
RC
577 while (isdigit(*s))
578 mtime = mtime * 10 + (*s++ - '0');
579 if (*s != '\n') {
580 error("update: mtime not delimited\n");
2f8aab68
RC
581 return(0);
582 }
583 /*
584 * File needs to be updated?
585 */
f7770429 586 if (opts & YOUNGER) {
a3e6fd64 587 if (stp->st_mtime == mtime)
3024eb6f 588 return(0);
a3e6fd64 589 if (stp->st_mtime < mtime) {
e8109cf8 590 log(lfp, "Warning: %s: remote copy is newer\n", target);
3024eb6f
RC
591 return(0);
592 }
a3e6fd64 593 } else if (stp->st_mtime == mtime && stp->st_size == size)
2f8aab68 594 return(0);
82572cb6 595 return(2);
2f8aab68
RC
596}
597
598/*
599 * Query. Check to see if file exists. Return one of the following:
600 * N\n - doesn't exist
601 * Ysize mtime\n - exists and its a regular file (size & mtime of file)
a3e6fd64 602 * Y\n - exists and its a directory or symbolic link
2f8aab68
RC
603 * ^Aerror message\n
604 */
e8109cf8 605query(name)
2f8aab68
RC
606 char *name;
607{
608 struct stat stb;
609
610 if (catname)
611 (void) sprintf(tp, "/%s", name);
3024eb6f 612
a3e6fd64 613 if (lstat(target, &stb) < 0) {
80babee1
RC
614 if (errno == ENOENT)
615 (void) write(rem, "N\n", 2);
616 else
617 error("%s:%s: %s\n", host, target, sys_errlist[errno]);
2f8aab68
RC
618 *tp = '\0';
619 return;
620 }
621
622 switch (stb.st_mode & S_IFMT) {
623 case S_IFREG:
80babee1 624 (void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
2f8aab68
RC
625 (void) write(rem, buf, strlen(buf));
626 break;
627
a3e6fd64 628 case S_IFLNK:
2f8aab68
RC
629 case S_IFDIR:
630 (void) write(rem, "Y\n", 2);
631 break;
632
633 default:
a3e6fd64 634 error("%s: not a file or directory\n", name);
2f8aab68
RC
635 break;
636 }
637 *tp = '\0';
638}
639
a3e6fd64 640recvf(cmd, type)
2f8aab68 641 char *cmd;
a3e6fd64 642 int type;
2f8aab68
RC
643{
644 register char *cp;
6e570eb0 645 int f, mode, opts, wrerr, olderrno;
2f8aab68
RC
646 off_t i, size;
647 time_t mtime;
648 struct stat stb;
649 struct timeval tvp[2];
80babee1 650 char *owner, *group;
2f8aab68 651 char new[BUFSIZ];
82572cb6 652 extern char *tmpname;
2f8aab68 653
f7770429 654 cp = cmd;
024fde5b
RC
655 opts = 0;
656 while (*cp >= '0' && *cp <= '7')
657 opts = (opts << 3) | (*cp++ - '0');
f7770429 658 if (*cp++ != ' ') {
e8109cf8 659 error("recvf: options not delimited\n");
f7770429
RC
660 return;
661 }
2f8aab68 662 mode = 0;
024fde5b 663 while (*cp >= '0' && *cp <= '7')
f7770429 664 mode = (mode << 3) | (*cp++ - '0');
2f8aab68 665 if (*cp++ != ' ') {
e8109cf8 666 error("recvf: mode not delimited\n");
2f8aab68
RC
667 return;
668 }
669 size = 0;
670 while (isdigit(*cp))
671 size = size * 10 + (*cp++ - '0');
672 if (*cp++ != ' ') {
e8109cf8 673 error("recvf: size not delimited\n");
2f8aab68
RC
674 return;
675 }
676 mtime = 0;
677 while (isdigit(*cp))
678 mtime = mtime * 10 + (*cp++ - '0');
679 if (*cp++ != ' ') {
e8109cf8 680 error("recvf: mtime not delimited\n");
2f8aab68
RC
681 return;
682 }
683 owner = cp;
684 while (*cp && *cp != ' ')
685 cp++;
686 if (*cp != ' ') {
e8109cf8 687 error("recvf: owner name not delimited\n");
2f8aab68
RC
688 return;
689 }
690 *cp++ = '\0';
691 group = cp;
692 while (*cp && *cp != ' ')
693 cp++;
694 if (*cp != ' ') {
e8109cf8 695 error("recvf: group name not delimited\n");
2f8aab68
RC
696 return;
697 }
698 *cp++ = '\0';
699
a3e6fd64 700 if (type == S_IFDIR) {
3024eb6f 701 if (catname >= sizeof(stp)) {
a3e6fd64
RC
702 error("%s:%s: too many directory levels\n",
703 host, target);
3024eb6f
RC
704 return;
705 }
706 stp[catname] = tp;
2f8aab68
RC
707 if (catname++) {
708 *tp++ = '/';
709 while (*tp++ = *cp++)
710 ;
711 tp--;
712 }
f7770429 713 if (opts & VERIFY) {
d6bccb44 714 ack();
82572cb6
RC
715 return;
716 }
a3e6fd64 717 if (lstat(target, &stb) == 0) {
d6bccb44 718 if (ISDIR(stb.st_mode)) {
80babee1 719 if ((stb.st_mode & 07777) == mode) {
48568d94
RC
720 ack();
721 return;
722 }
723 buf[0] = '\0';
724 (void) sprintf(buf + 1,
80babee1
RC
725 "%s:%s: Warning: remote mode %o != local mode %o\n",
726 host, target, stb.st_mode & 07777, mode);
48568d94 727 (void) write(rem, buf, strlen(buf + 1) + 1);
82572cb6 728 return;
d6bccb44 729 }
80babee1
RC
730 errno = ENOTDIR;
731 } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
732 chkparent(target) == 0 && mkdir(target, mode) == 0)) {
733 if (chog(target, owner, group, mode) == 0)
734 ack();
d6bccb44 735 return;
2f8aab68 736 }
80babee1 737 error("%s:%s: %s\n", host, target, sys_errlist[errno]);
d6bccb44
RC
738 tp = stp[--catname];
739 *tp = '\0';
2f8aab68
RC
740 return;
741 }
742
743 if (catname)
744 (void) sprintf(tp, "/%s", cp);
2f8aab68
RC
745 cp = rindex(target, '/');
746 if (cp == NULL)
80babee1
RC
747 strcpy(new, tmpname);
748 else if (cp == target)
749 (void) sprintf(new, "/%s", tmpname);
750 else {
2f8aab68 751 *cp = '\0';
80babee1 752 (void) sprintf(new, "%s/%s", target, tmpname);
2f8aab68 753 *cp = '/';
80babee1 754 }
a3e6fd64
RC
755
756 if (type == S_IFLNK) {
757 int j;
758
759 ack();
760 cp = buf;
761 for (i = 0; i < size; i += j) {
762 if ((j = read(rem, cp, size - i)) <= 0)
763 cleanup();
764 cp += j;
765 }
766 *cp = '\0';
80babee1
RC
767 if (response() < 0) {
768 err();
769 return;
770 }
d7c49b27
RC
771 if (symlink(buf, new) < 0) {
772 if (errno != ENOENT || chkparent(new) < 0 ||
773 symlink(buf, new) < 0)
774 goto badn;
775 }
a3e6fd64
RC
776 mode &= 0777;
777 if (opts & COMPARE) {
778 char tbuf[BUFSIZ];
779
d7c49b27
RC
780 if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
781 i == size && strncmp(buf, tbuf, size) == 0) {
a3e6fd64
RC
782 (void) unlink(new);
783 ack();
784 return;
785 }
85b60cd9
RC
786 if (opts & VERIFY)
787 goto differ;
a3e6fd64
RC
788 }
789 goto fixup;
790 }
791
80babee1
RC
792 if ((f = creat(new, mode)) < 0) {
793 if (errno != ENOENT || chkparent(new) < 0 ||
794 (f = creat(new, mode)) < 0)
d7c49b27 795 goto badn;
80babee1
RC
796 }
797
d6bccb44 798 ack();
2f8aab68
RC
799 wrerr = 0;
800 for (i = 0; i < size; i += BUFSIZ) {
801 int amt = BUFSIZ;
2f8aab68 802
3024eb6f 803 cp = buf;
2f8aab68
RC
804 if (i + amt > size)
805 amt = size - i;
806 do {
807 int j = read(rem, cp, amt);
808
82572cb6
RC
809 if (j <= 0) {
810 (void) close(f);
811 (void) unlink(new);
2f8aab68 812 cleanup();
82572cb6 813 }
2f8aab68
RC
814 amt -= j;
815 cp += j;
816 } while (amt > 0);
817 amt = BUFSIZ;
818 if (i + amt > size)
819 amt = size - i;
820 if (wrerr == 0 && write(f, buf, amt) != amt) {
821 olderrno = errno;
822 wrerr++;
823 }
824 }
024fde5b 825 (void) close(f);
80babee1
RC
826 if (response() < 0) {
827 err();
828 (void) unlink(new);
829 return;
830 }
2f8aab68 831 if (wrerr) {
6da2b540 832 error("%s:%s: %s\n", host, new, sys_errlist[olderrno]);
82572cb6 833 (void) unlink(new);
2f8aab68
RC
834 return;
835 }
024fde5b
RC
836 if (opts & COMPARE) {
837 FILE *f1, *f2;
838 int c;
839
840 if ((f1 = fopen(target, "r")) == NULL)
80babee1 841 goto badt;
ac288309
KS
842 if ((f2 = fopen(new, "r")) == NULL) {
843 badn:
844 error("%s:%s: %s\n", host, new, sys_errlist[errno]);
845 (void) unlink(new);
846 return;
847 }
024fde5b
RC
848 while ((c = getc(f1)) == getc(f2))
849 if (c == EOF) {
850 (void) fclose(f1);
851 (void) fclose(f2);
852 (void) unlink(new);
d6bccb44 853 ack();
024fde5b
RC
854 return;
855 }
856 (void) fclose(f1);
857 (void) fclose(f2);
858 if (opts & VERIFY) {
a3e6fd64 859 differ:
024fde5b
RC
860 (void) unlink(new);
861 buf[0] = '\0';
80babee1 862 (void) sprintf(buf + 1, "need to update: %s\n",target);
024fde5b
RC
863 (void) write(rem, buf, strlen(buf + 1) + 1);
864 return;
865 }
866 }
2f8aab68
RC
867
868 /*
869 * Set last modified time
870 */
a3e6fd64 871 tvp[0].tv_sec = stb.st_atime; /* old atime from target */
2f8aab68
RC
872 tvp[0].tv_usec = 0;
873 tvp[1].tv_sec = mtime;
874 tvp[1].tv_usec = 0;
3f32107d 875 if (utimes(new, tvp) < 0) {
ac288309 876 note("%s:utimes failed %s: %s\n", host, new, sys_errlist[errno]);
3f32107d
RC
877 }
878 if (chog(new, owner, group, mode) < 0) {
879 (void) unlink(new);
2f8aab68
RC
880 return;
881 }
d7c49b27 882fixup:
2f8aab68 883 if (rename(new, target) < 0) {
80babee1 884badt:
a3e6fd64 885 error("%s:%s: %s\n", host, target, sys_errlist[errno]);
80babee1 886 (void) unlink(new);
2f8aab68
RC
887 return;
888 }
024fde5b
RC
889 if (opts & COMPARE) {
890 buf[0] = '\0';
85b60cd9 891 (void) sprintf(buf + 1, "%s: updated %s\n", host, target);
024fde5b
RC
892 (void) write(rem, buf, strlen(buf + 1) + 1);
893 } else
d6bccb44 894 ack();
2f8aab68
RC
895}
896
a3e6fd64
RC
897/*
898 * Creat a hard link to existing file.
899 */
900hardlink(cmd)
901 char *cmd;
902{
903 register char *cp;
904 struct stat stb;
905 char *oldname;
906 int opts, exists = 0;
907
908 cp = cmd;
909 opts = 0;
910 while (*cp >= '0' && *cp <= '7')
911 opts = (opts << 3) | (*cp++ - '0');
912 if (*cp++ != ' ') {
913 error("hardlink: options not delimited\n");
914 return;
915 }
916 oldname = cp;
917 while (*cp && *cp != ' ')
918 cp++;
919 if (*cp != ' ') {
920 error("hardlink: oldname name not delimited\n");
921 return;
922 }
923 *cp++ = '\0';
924
ac288309 925 if (catname) {
a3e6fd64 926 (void) sprintf(tp, "/%s", cp);
ac288309 927 }
a3e6fd64 928 if (lstat(target, &stb) == 0) {
ac288309
KS
929 int mode = stb.st_mode & S_IFMT;
930 if (mode != S_IFREG && mode != S_IFLNK) {
a3e6fd64
RC
931 error("%s:%s: not a regular file\n", host, target);
932 return;
933 }
934 exists = 1;
935 }
ac288309
KS
936 if (chkparent(target) < 0 ) {
937 error("%s:%s: %s (no parent)\n",
938 host, target, sys_errlist[errno]);
939 return;
940 }
941 if (exists && (unlink(target) < 0)) {
942 error("%s:%s: %s (unlink)\n",
943 host, target, sys_errlist[errno]);
944 return;
945 }
946 if (link(oldname, target) < 0) {
947 error("%s:can't link %s to %s\n",
948 host, target, oldname);
a3e6fd64
RC
949 return;
950 }
951 ack();
952}
953
3024eb6f 954/*
80babee1 955 * Check to see if parent directory exists and create one if not.
3024eb6f
RC
956 */
957chkparent(name)
958 char *name;
959{
80babee1
RC
960 register char *cp;
961 struct stat stb;
3024eb6f
RC
962
963 cp = rindex(name, '/');
80babee1 964 if (cp == NULL || cp == name)
3024eb6f 965 return(0);
80babee1
RC
966 *cp = '\0';
967 if (lstat(name, &stb) < 0) {
968 if (errno == ENOENT && chkparent(name) >= 0 &&
969 mkdir(name, 0777 & ~oumask) >= 0) {
3024eb6f 970 *cp = '/';
80babee1
RC
971 return(0);
972 }
973 } else if (ISDIR(stb.st_mode)) {
974 *cp = '/';
3024eb6f
RC
975 return(0);
976 }
80babee1 977 *cp = '/';
3024eb6f
RC
978 return(-1);
979}
980
2f8aab68 981/*
d6bccb44 982 * Change owner, group and mode of file.
2f8aab68 983 */
82572cb6 984chog(file, owner, group, mode)
2f8aab68 985 char *file, *owner, *group;
82572cb6 986 int mode;
2f8aab68 987{
2f8aab68
RC
988 register int i;
989 int uid, gid;
80babee1
RC
990 extern char user[];
991 extern int userid;
2f8aab68
RC
992
993 uid = userid;
994 if (userid == 0) {
ac288309
KS
995 if (*owner == ':') {
996 uid = atoi(owner + 1);
997 } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
e8109cf8 998 if ((pw = getpwnam(owner)) == NULL) {
82572cb6 999 if (mode & 04000) {
ac288309 1000 note("%s:%s: unknown login name, clearing setuid",
a3e6fd64 1001 host, owner);
ac288309
KS
1002 mode &= ~04000;
1003 uid = 0;
82572cb6
RC
1004 }
1005 } else
e8109cf8 1006 uid = pw->pw_uid;
82572cb6 1007 } else
e8109cf8 1008 uid = pw->pw_uid;
ac288309
KS
1009 if (*group == ':') {
1010 gid = atoi(group + 1);
1011 goto ok;
1012 }
d6bccb44
RC
1013 } else if ((mode & 04000) && strcmp(user, owner) != 0)
1014 mode &= ~04000;
80babee1 1015 gid = -1;
e8109cf8 1016 if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
ac288309
KS
1017 if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
1018 || ((gr = getgrnam(group)) == NULL)) {
82572cb6 1019 if (mode & 02000) {
ac288309
KS
1020 note("%s:%s: unknown group", host, group);
1021 mode &= ~02000;
82572cb6
RC
1022 }
1023 } else
e8109cf8 1024 gid = gr->gr_gid;
82572cb6 1025 } else
e8109cf8 1026 gid = gr->gr_gid;
80babee1 1027 if (userid && gid >= 0) {
ac288309 1028 if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
e8109cf8 1029 if (!(strcmp(user, gr->gr_mem[i])))
2f8aab68 1030 goto ok;
d6bccb44 1031 mode &= ~02000;
80babee1 1032 gid = -1;
2f8aab68
RC
1033 }
1034ok:
80babee1
RC
1035 if (userid)
1036 setreuid(userid, 0);
1037 if (chown(file, uid, gid) < 0 ||
1038 (mode & 06000) && chmod(file, mode) < 0) {
ac288309
KS
1039 note("%s: chown or chmod failed: file %s: %s",
1040 host, file, sys_errlist[errno]);
d6bccb44 1041 }
80babee1
RC
1042 if (userid)
1043 setreuid(0, userid);
2f8aab68
RC
1044 return(0);
1045}
1046
d1dee8e8
RC
1047/*
1048 * Check for files on the machine being updated that are not on the master
1049 * machine and remove them.
1050 */
d6bccb44 1051rmchk(opts)
d1dee8e8
RC
1052 int opts;
1053{
e8109cf8 1054 register char *cp, *s;
d1dee8e8
RC
1055 struct stat stb;
1056
1057 if (debug)
d6bccb44 1058 printf("rmchk()\n");
024fde5b 1059
d1dee8e8
RC
1060 /*
1061 * Tell the remote to clean the files from the last directory sent.
1062 */
d6bccb44 1063 (void) sprintf(buf, "C%o\n", opts & VERIFY);
d1dee8e8
RC
1064 if (debug)
1065 printf("buf = %s", buf);
1066 (void) write(rem, buf, strlen(buf));
1067 if (response() < 0)
1068 return;
d1dee8e8 1069 for (;;) {
e8109cf8 1070 cp = s = buf;
d1dee8e8
RC
1071 do {
1072 if (read(rem, cp, 1) != 1)
346dad94 1073 lostconn();
d1dee8e8
RC
1074 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1075
e8109cf8 1076 switch (*s++) {
d6bccb44 1077 case 'Q': /* Query if file should be removed */
024fde5b
RC
1078 /*
1079 * Return the following codes to remove query.
d6bccb44
RC
1080 * N\n -- file exists - DON'T remove.
1081 * Y\n -- file doesn't exist - REMOVE.
024fde5b 1082 */
d1dee8e8 1083 *--cp = '\0';
e8109cf8 1084 (void) sprintf(tp, "/%s", s);
d1dee8e8
RC
1085 if (debug)
1086 printf("check %s\n", target);
4085f36f 1087 if (except(target))
d1dee8e8 1088 (void) write(rem, "N\n", 2);
a3e6fd64 1089 else if (lstat(target, &stb) < 0)
d1dee8e8 1090 (void) write(rem, "Y\n", 2);
d6bccb44
RC
1091 else
1092 (void) write(rem, "N\n", 2);
d1dee8e8
RC
1093 break;
1094
1095 case '\0':
1096 *--cp = '\0';
e8109cf8
RC
1097 if (*s != '\0')
1098 log(lfp, "%s\n", s);
d1dee8e8
RC
1099 break;
1100
d6bccb44
RC
1101 case 'E':
1102 *tp = '\0';
1103 ack();
1104 return;
1105
d1dee8e8
RC
1106 case '\1':
1107 case '\2':
0fccdfef 1108 nerrs++;
e8109cf8 1109 if (*s != '\n') {
d1dee8e8
RC
1110 if (!iamremote) {
1111 fflush(stdout);
e8109cf8 1112 (void) write(2, s, cp - s);
d1dee8e8
RC
1113 }
1114 if (lfp != NULL)
e8109cf8 1115 (void) fwrite(s, 1, cp - s, lfp);
d1dee8e8
RC
1116 }
1117 if (buf[0] == '\2')
346dad94 1118 lostconn();
d1dee8e8
RC
1119 break;
1120
1121 default:
d6bccb44
RC
1122 error("rmchk: unexpected response '%s'\n", buf);
1123 err();
d1dee8e8
RC
1124 }
1125 }
1126}
1127
1128/*
d6bccb44 1129 * Check the current directory (initialized by the 'T' command to server())
024fde5b 1130 * for extraneous files and remove them.
d1dee8e8 1131 */
855cc4f4
RC
1132clean(cp)
1133 register char *cp;
d1dee8e8
RC
1134{
1135 DIR *d;
d6bccb44 1136 register struct direct *dp;
d1dee8e8 1137 struct stat stb;
d6bccb44
RC
1138 char *otp;
1139 int len, opts;
d1dee8e8 1140
d6bccb44
RC
1141 opts = 0;
1142 while (*cp >= '0' && *cp <= '7')
1143 opts = (opts << 3) | (*cp++ - '0');
1144 if (*cp != '\0') {
1145 error("clean: options not delimited\n");
1146 return;
d1dee8e8 1147 }
80babee1 1148 if ((d = opendir(target)) == NULL) {
a3e6fd64 1149 error("%s:%s: %s\n", host, target, sys_errlist[errno]);
e8109cf8
RC
1150 return;
1151 }
d6bccb44 1152 ack();
d1dee8e8
RC
1153
1154 otp = tp;
1155 len = tp - target;
1156 while (dp = readdir(d)) {
1157 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1158 continue;
1159 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
a3e6fd64
RC
1160 error("%s:%s/%s: Name too long\n",
1161 host, target, dp->d_name);
d1dee8e8
RC
1162 continue;
1163 }
1164 tp = otp;
1165 *tp++ = '/';
1166 cp = dp->d_name;;
1167 while (*tp++ = *cp++)
1168 ;
1169 tp--;
a3e6fd64
RC
1170 if (lstat(target, &stb) < 0) {
1171 error("%s:%s: %s\n", host, target, sys_errlist[errno]);
d1dee8e8
RC
1172 continue;
1173 }
d6bccb44 1174 (void) sprintf(buf, "Q%s\n", dp->d_name);
d1dee8e8
RC
1175 (void) write(rem, buf, strlen(buf));
1176 cp = buf;
1177 do {
1178 if (read(rem, cp, 1) != 1)
0fccdfef 1179 cleanup();
d1dee8e8
RC
1180 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1181 *--cp = '\0';
1182 cp = buf;
d6bccb44 1183 if (*cp != 'Y')
d1dee8e8 1184 continue;
e8109cf8
RC
1185 if (opts & VERIFY) {
1186 cp = buf;
1187 *cp++ = '\0';
80babee1 1188 (void) sprintf(cp, "need to remove: %s\n", target);
e8109cf8
RC
1189 (void) write(rem, buf, strlen(cp) + 1);
1190 } else
d1dee8e8
RC
1191 remove(&stb);
1192 }
1193 closedir(d);
d1dee8e8
RC
1194 (void) write(rem, "E\n", 2);
1195 (void) response();
d6bccb44 1196 tp = otp;
e8109cf8 1197 *tp = '\0';
d1dee8e8
RC
1198}
1199
e8109cf8
RC
1200/*
1201 * Remove a file or directory (recursively) and send back an acknowledge
1202 * or an error message.
1203 */
a3e6fd64
RC
1204remove(stp)
1205 struct stat *stp;
d1dee8e8
RC
1206{
1207 DIR *d;
1208 struct direct *dp;
1209 register char *cp;
1210 struct stat stb;
1211 char *otp;
1212 int len;
1213
a3e6fd64 1214 switch (stp->st_mode & S_IFMT) {
d1dee8e8 1215 case S_IFREG:
a3e6fd64 1216 case S_IFLNK:
d1dee8e8
RC
1217 if (unlink(target) < 0)
1218 goto bad;
1219 goto removed;
1220
1221 case S_IFDIR:
1222 break;
1223
1224 default:
a3e6fd64 1225 error("%s:%s: not a plain file\n", host, target);
d1dee8e8
RC
1226 return;
1227 }
1228
80babee1 1229 if ((d = opendir(target)) == NULL)
d1dee8e8
RC
1230 goto bad;
1231
1232 otp = tp;
1233 len = tp - target;
1234 while (dp = readdir(d)) {
1235 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1236 continue;
1237 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
a3e6fd64
RC
1238 error("%s:%s/%s: Name too long\n",
1239 host, target, dp->d_name);
d1dee8e8
RC
1240 continue;
1241 }
1242 tp = otp;
1243 *tp++ = '/';
1244 cp = dp->d_name;;
1245 while (*tp++ = *cp++)
1246 ;
1247 tp--;
a3e6fd64
RC
1248 if (lstat(target, &stb) < 0) {
1249 error("%s:%s: %s\n", host, target, sys_errlist[errno]);
d1dee8e8
RC
1250 continue;
1251 }
1252 remove(&stb);
1253 }
1254 closedir(d);
1255 tp = otp;
1256 *tp = '\0';
1257 if (rmdir(target) < 0) {
1258bad:
a3e6fd64 1259 error("%s:%s: %s\n", host, target, sys_errlist[errno]);
d1dee8e8
RC
1260 return;
1261 }
1262removed:
1263 cp = buf;
1264 *cp++ = '\0';
1265 (void) sprintf(cp, "removed %s\n", target);
1266 (void) write(rem, buf, strlen(cp) + 1);
1267}
1268
d6bccb44
RC
1269/*
1270 * Execute a shell command to handle special cases.
1271 */
1272dospecial(cmd)
1273 char *cmd;
1274{
1275 int fd[2], status, pid, i;
1276 register char *cp, *s;
1277 char sbuf[BUFSIZ];
80babee1 1278 extern int userid, groupid;
d6bccb44
RC
1279
1280 if (pipe(fd) < 0) {
1281 error("%s\n", sys_errlist[errno]);
1282 return;
1283 }
1284 if ((pid = fork()) == 0) {
1285 /*
1286 * Return everything the shell commands print.
1287 */
1288 (void) close(0);
1289 (void) close(1);
1290 (void) close(2);
1291 (void) open("/dev/null", 0);
1292 (void) dup(fd[1]);
1293 (void) dup(fd[1]);
1294 (void) close(fd[0]);
1295 (void) close(fd[1]);
80babee1 1296 setgid(groupid);
b5644799 1297 setuid(userid);
d6bccb44
RC
1298 execl("/bin/sh", "sh", "-c", cmd, 0);
1299 _exit(127);
1300 }
1301 (void) close(fd[1]);
1302 s = sbuf;
1303 *s++ = '\0';
1304 while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
1305 cp = buf;
1306 do {
1307 *s++ = *cp++;
1308 if (cp[-1] != '\n') {
1309 if (s < &sbuf[sizeof(sbuf)-1])
1310 continue;
1311 *s++ = '\n';
1312 }
1313 /*
1314 * Throw away blank lines.
1315 */
1316 if (s == &sbuf[2]) {
1317 s--;
1318 continue;
1319 }
1320 (void) write(rem, sbuf, s - sbuf);
1321 s = &sbuf[1];
1322 } while (--i);
1323 }
1324 if (s > &sbuf[1]) {
1325 *s++ = '\n';
1326 (void) write(rem, sbuf, s - sbuf);
1327 }
1328 while ((i = wait(&status)) != pid && i != -1)
1329 ;
1330 if (i == -1)
1331 status = -1;
80babee1 1332 (void) close(fd[0]);
d6bccb44
RC
1333 if (status)
1334 error("shell returned %d\n", status);
1335 else
1336 ack();
1337}
1338
3024eb6f 1339/*VARARGS2*/
82572cb6
RC
1340log(fp, fmt, a1, a2, a3)
1341 FILE *fp;
2f8aab68
RC
1342 char *fmt;
1343 int a1, a2, a3;
1344{
1345 /* Print changes locally if not quiet mode */
1346 if (!qflag)
1347 printf(fmt, a1, a2, a3);
1348
1349 /* Save changes (for mailing) if really updating files */
f7770429 1350 if (!(options & VERIFY) && fp != NULL)
82572cb6 1351 fprintf(fp, fmt, a1, a2, a3);
2f8aab68
RC
1352}
1353
3024eb6f 1354/*VARARGS1*/
2f8aab68
RC
1355error(fmt, a1, a2, a3)
1356 char *fmt;
1357 int a1, a2, a3;
1358{
0fccdfef 1359 nerrs++;
2f8aab68
RC
1360 strcpy(buf, "\1rdist: ");
1361 (void) sprintf(buf+8, fmt, a1, a2, a3);
3024eb6f
RC
1362 if (!iamremote) {
1363 fflush(stdout);
1364 (void) write(2, buf+1, strlen(buf+1));
d6bccb44
RC
1365 } else
1366 (void) write(rem, buf, strlen(buf));
3024eb6f
RC
1367 if (lfp != NULL)
1368 (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
2f8aab68
RC
1369}
1370
3024eb6f 1371/*VARARGS1*/
2f8aab68
RC
1372fatal(fmt, a1, a2,a3)
1373 char *fmt;
1374 int a1, a2, a3;
1375{
0fccdfef 1376 nerrs++;
2f8aab68
RC
1377 strcpy(buf, "\2rdist: ");
1378 (void) sprintf(buf+8, fmt, a1, a2, a3);
3024eb6f
RC
1379 if (!iamremote) {
1380 fflush(stdout);
1381 (void) write(2, buf+1, strlen(buf+1));
d6bccb44
RC
1382 } else
1383 (void) write(rem, buf, strlen(buf));
3024eb6f
RC
1384 if (lfp != NULL)
1385 (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
2f8aab68
RC
1386 cleanup();
1387}
1388
1389response()
1390{
024fde5b 1391 char *cp, *s;
85b60cd9 1392 char resp[BUFSIZ];
2f8aab68
RC
1393
1394 if (debug)
1395 printf("response()\n");
1396
85b60cd9 1397 cp = s = resp;
024fde5b
RC
1398 do {
1399 if (read(rem, cp, 1) != 1)
346dad94 1400 lostconn();
85b60cd9 1401 } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
2f8aab68 1402
024fde5b 1403 switch (*s++) {
2f8aab68 1404 case '\0':
024fde5b 1405 *--cp = '\0';
d6bccb44 1406 if (*s != '\0') {
024fde5b 1407 log(lfp, "%s\n", s);
d6bccb44
RC
1408 return(1);
1409 }
2f8aab68 1410 return(0);
ac288309
KS
1411 case '\3':
1412 *--cp = '\0';
1413 log(lfp, "Note: %s\n",s);
1414 return(response());
2f8aab68
RC
1415
1416 default:
024fde5b 1417 s--;
2f8aab68
RC
1418 /* fall into... */
1419 case '\1':
1420 case '\2':
0fccdfef 1421 nerrs++;
024fde5b 1422 if (*s != '\n') {
82572cb6
RC
1423 if (!iamremote) {
1424 fflush(stdout);
024fde5b 1425 (void) write(2, s, cp - s);
82572cb6 1426 }
2f8aab68 1427 if (lfp != NULL)
024fde5b 1428 (void) fwrite(s, 1, cp - s, lfp);
2f8aab68 1429 }
85b60cd9 1430 if (resp[0] == '\2')
346dad94 1431 lostconn();
e8109cf8 1432 return(-1);
2f8aab68 1433 }
2f8aab68
RC
1434}
1435
0fccdfef
RC
1436/*
1437 * Remove temporary files and do any cleanup operations before exiting.
1438 */
1439cleanup()
2f8aab68 1440{
0fccdfef
RC
1441 (void) unlink(tmpfile);
1442 exit(1);
2f8aab68 1443}
ac288309
KS
1444
1445note(fmt, a1, a2, a3)
1446{
1447 static char buf[BUFSIZ];
1448 sprintf(buf, fmt, a1, a2, a3);
1449 comment(buf);
1450}
1451
1452comment(s)
1453char *s;
1454{
1455 char c = '\3';
1456 write(rem, &c, 1);
1457 write(rem, s, strlen(s));
1458 c = '\n';
1459 write(rem, &c, 1);
1460}