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