fix silly indirect-through-zero bug
[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
8b8447e0 9static char sccsid[] = "@(#)server.c 5.16 (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
bc0c28c1 28void 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];
e385b216 655 extern char *tempname;
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)) {
8b8447e0 736 if (fchog(-1, target, owner, group, mode) == 0)
80babee1 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)
e385b216 750 strcpy(new, tempname);
80babee1 751 else if (cp == target)
e385b216 752 (void) sprintf(new, "/%s", tempname);
80babee1 753 else {
2f8aab68 754 *cp = '\0';
e385b216 755 (void) sprintf(new, "%s/%s", target, tempname);
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)
8b8447e0 777 goto badnew1;
d7c49b27 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)
8b8447e0 798 goto badnew1;
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 }
80babee1
RC
828 if (response() < 0) {
829 err();
8b8447e0 830 goto badnew2;
2f8aab68 831 }
8b8447e0
KB
832 if (wrerr)
833 goto badnew1;
024fde5b
RC
834 if (opts & COMPARE) {
835 FILE *f1, *f2;
836 int c;
837
838 if ((f1 = fopen(target, "r")) == NULL)
8b8447e0 839 goto badtarget;
ac288309 840 if ((f2 = fopen(new, "r")) == NULL) {
8b8447e0
KB
841badnew1: error("%s:%s: %s\n", host, new, strerror(errno));
842 goto badnew2;
ac288309 843 }
024fde5b
RC
844 while ((c = getc(f1)) == getc(f2))
845 if (c == EOF) {
846 (void) fclose(f1);
847 (void) fclose(f2);
d6bccb44 848 ack();
8b8447e0 849 goto badnew2;
024fde5b
RC
850 }
851 (void) fclose(f1);
852 (void) fclose(f2);
853 if (opts & VERIFY) {
8b8447e0 854differ: buf[0] = '\0';
80babee1 855 (void) sprintf(buf + 1, "need to update: %s\n",target);
024fde5b 856 (void) write(rem, buf, strlen(buf + 1) + 1);
8b8447e0 857 goto badnew2;
024fde5b
RC
858 }
859 }
2f8aab68
RC
860
861 /*
862 * Set last modified time
863 */
8b8447e0 864 tvp[0].tv_sec = time(0);
2f8aab68
RC
865 tvp[0].tv_usec = 0;
866 tvp[1].tv_sec = mtime;
867 tvp[1].tv_usec = 0;
8b8447e0
KB
868 if (utimes(new, tvp) < 0)
869 note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
870
871 if (fchog(f, new, owner, group, mode) < 0) {
872badnew2: (void) close(f);
3f32107d 873 (void) unlink(new);
2f8aab68
RC
874 return;
875 }
8b8447e0
KB
876 (void) close(f);
877
878fixup: if (rename(new, target) < 0) {
879badtarget: error("%s:%s: %s\n", host, target, strerror(errno));
80babee1 880 (void) unlink(new);
2f8aab68
RC
881 return;
882 }
8b8447e0 883
024fde5b
RC
884 if (opts & COMPARE) {
885 buf[0] = '\0';
89b53363 886 (void) sprintf(buf + 1, "updated %s\n", target);
024fde5b
RC
887 (void) write(rem, buf, strlen(buf + 1) + 1);
888 } else
d6bccb44 889 ack();
2f8aab68
RC
890}
891
a3e6fd64
RC
892/*
893 * Creat a hard link to existing file.
894 */
895hardlink(cmd)
896 char *cmd;
897{
898 register char *cp;
899 struct stat stb;
900 char *oldname;
901 int opts, exists = 0;
902
903 cp = cmd;
904 opts = 0;
905 while (*cp >= '0' && *cp <= '7')
906 opts = (opts << 3) | (*cp++ - '0');
907 if (*cp++ != ' ') {
908 error("hardlink: options not delimited\n");
909 return;
910 }
911 oldname = cp;
912 while (*cp && *cp != ' ')
913 cp++;
914 if (*cp != ' ') {
915 error("hardlink: oldname name not delimited\n");
916 return;
917 }
918 *cp++ = '\0';
919
ac288309 920 if (catname) {
a3e6fd64 921 (void) sprintf(tp, "/%s", cp);
ac288309 922 }
a3e6fd64 923 if (lstat(target, &stb) == 0) {
ac288309
KS
924 int mode = stb.st_mode & S_IFMT;
925 if (mode != S_IFREG && mode != S_IFLNK) {
a3e6fd64
RC
926 error("%s:%s: not a regular file\n", host, target);
927 return;
928 }
929 exists = 1;
930 }
ac288309
KS
931 if (chkparent(target) < 0 ) {
932 error("%s:%s: %s (no parent)\n",
5f91cf7b 933 host, target, strerror(errno));
ac288309
KS
934 return;
935 }
936 if (exists && (unlink(target) < 0)) {
937 error("%s:%s: %s (unlink)\n",
5f91cf7b 938 host, target, strerror(errno));
ac288309
KS
939 return;
940 }
941 if (link(oldname, target) < 0) {
942 error("%s:can't link %s to %s\n",
943 host, target, oldname);
a3e6fd64
RC
944 return;
945 }
946 ack();
947}
948
3024eb6f 949/*
80babee1 950 * Check to see if parent directory exists and create one if not.
3024eb6f
RC
951 */
952chkparent(name)
953 char *name;
954{
80babee1
RC
955 register char *cp;
956 struct stat stb;
3024eb6f
RC
957
958 cp = rindex(name, '/');
80babee1 959 if (cp == NULL || cp == name)
3024eb6f 960 return(0);
80babee1
RC
961 *cp = '\0';
962 if (lstat(name, &stb) < 0) {
963 if (errno == ENOENT && chkparent(name) >= 0 &&
964 mkdir(name, 0777 & ~oumask) >= 0) {
3024eb6f 965 *cp = '/';
80babee1
RC
966 return(0);
967 }
968 } else if (ISDIR(stb.st_mode)) {
969 *cp = '/';
3024eb6f
RC
970 return(0);
971 }
80babee1 972 *cp = '/';
3024eb6f
RC
973 return(-1);
974}
975
2f8aab68 976/*
d6bccb44 977 * Change owner, group and mode of file.
2f8aab68 978 */
8b8447e0
KB
979fchog(fd, file, owner, group, mode)
980 int fd;
2f8aab68 981 char *file, *owner, *group;
82572cb6 982 int mode;
2f8aab68 983{
2f8aab68
RC
984 register int i;
985 int uid, gid;
80babee1
RC
986 extern char user[];
987 extern int userid;
2f8aab68
RC
988
989 uid = userid;
990 if (userid == 0) {
ac288309
KS
991 if (*owner == ':') {
992 uid = atoi(owner + 1);
993 } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
e8109cf8 994 if ((pw = getpwnam(owner)) == NULL) {
82572cb6 995 if (mode & 04000) {
ac288309 996 note("%s:%s: unknown login name, clearing setuid",
a3e6fd64 997 host, owner);
ac288309
KS
998 mode &= ~04000;
999 uid = 0;
82572cb6
RC
1000 }
1001 } else
e8109cf8 1002 uid = pw->pw_uid;
82572cb6 1003 } else
e8109cf8 1004 uid = pw->pw_uid;
ac288309
KS
1005 if (*group == ':') {
1006 gid = atoi(group + 1);
1007 goto ok;
1008 }
d6bccb44
RC
1009 } else if ((mode & 04000) && strcmp(user, owner) != 0)
1010 mode &= ~04000;
80babee1 1011 gid = -1;
e8109cf8 1012 if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
ac288309
KS
1013 if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
1014 || ((gr = getgrnam(group)) == NULL)) {
82572cb6 1015 if (mode & 02000) {
ac288309
KS
1016 note("%s:%s: unknown group", host, group);
1017 mode &= ~02000;
82572cb6
RC
1018 }
1019 } else
e8109cf8 1020 gid = gr->gr_gid;
82572cb6 1021 } else
e8109cf8 1022 gid = gr->gr_gid;
80babee1 1023 if (userid && gid >= 0) {
ac288309 1024 if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
e8109cf8 1025 if (!(strcmp(user, gr->gr_mem[i])))
2f8aab68 1026 goto ok;
d6bccb44 1027 mode &= ~02000;
80babee1 1028 gid = -1;
2f8aab68 1029 }
8b8447e0
KB
1030ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
1031 note("%s: %s chown: %s", host, file, strerror(errno));
1032 else if (mode & 07000 &&
1033 (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
1034 note("%s: %s chmod: %s", host, file, strerror(errno));
2f8aab68
RC
1035 return(0);
1036}
1037
d1dee8e8
RC
1038/*
1039 * Check for files on the machine being updated that are not on the master
1040 * machine and remove them.
1041 */
d6bccb44 1042rmchk(opts)
d1dee8e8
RC
1043 int opts;
1044{
e8109cf8 1045 register char *cp, *s;
d1dee8e8
RC
1046 struct stat stb;
1047
1048 if (debug)
d6bccb44 1049 printf("rmchk()\n");
024fde5b 1050
d1dee8e8
RC
1051 /*
1052 * Tell the remote to clean the files from the last directory sent.
1053 */
d6bccb44 1054 (void) sprintf(buf, "C%o\n", opts & VERIFY);
d1dee8e8
RC
1055 if (debug)
1056 printf("buf = %s", buf);
1057 (void) write(rem, buf, strlen(buf));
1058 if (response() < 0)
1059 return;
d1dee8e8 1060 for (;;) {
e8109cf8 1061 cp = s = buf;
d1dee8e8
RC
1062 do {
1063 if (read(rem, cp, 1) != 1)
346dad94 1064 lostconn();
d1dee8e8
RC
1065 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1066
e8109cf8 1067 switch (*s++) {
d6bccb44 1068 case 'Q': /* Query if file should be removed */
024fde5b
RC
1069 /*
1070 * Return the following codes to remove query.
d6bccb44
RC
1071 * N\n -- file exists - DON'T remove.
1072 * Y\n -- file doesn't exist - REMOVE.
024fde5b 1073 */
d1dee8e8 1074 *--cp = '\0';
e8109cf8 1075 (void) sprintf(tp, "/%s", s);
d1dee8e8
RC
1076 if (debug)
1077 printf("check %s\n", target);
4085f36f 1078 if (except(target))
d1dee8e8 1079 (void) write(rem, "N\n", 2);
a3e6fd64 1080 else if (lstat(target, &stb) < 0)
d1dee8e8 1081 (void) write(rem, "Y\n", 2);
d6bccb44
RC
1082 else
1083 (void) write(rem, "N\n", 2);
d1dee8e8
RC
1084 break;
1085
1086 case '\0':
1087 *--cp = '\0';
e8109cf8
RC
1088 if (*s != '\0')
1089 log(lfp, "%s\n", s);
d1dee8e8
RC
1090 break;
1091
d6bccb44
RC
1092 case 'E':
1093 *tp = '\0';
1094 ack();
1095 return;
1096
d1dee8e8
RC
1097 case '\1':
1098 case '\2':
0fccdfef 1099 nerrs++;
e8109cf8 1100 if (*s != '\n') {
d1dee8e8
RC
1101 if (!iamremote) {
1102 fflush(stdout);
e8109cf8 1103 (void) write(2, s, cp - s);
d1dee8e8
RC
1104 }
1105 if (lfp != NULL)
e8109cf8 1106 (void) fwrite(s, 1, cp - s, lfp);
d1dee8e8
RC
1107 }
1108 if (buf[0] == '\2')
346dad94 1109 lostconn();
d1dee8e8
RC
1110 break;
1111
1112 default:
d6bccb44
RC
1113 error("rmchk: unexpected response '%s'\n", buf);
1114 err();
d1dee8e8
RC
1115 }
1116 }
1117}
1118
1119/*
d6bccb44 1120 * Check the current directory (initialized by the 'T' command to server())
024fde5b 1121 * for extraneous files and remove them.
d1dee8e8 1122 */
855cc4f4
RC
1123clean(cp)
1124 register char *cp;
d1dee8e8
RC
1125{
1126 DIR *d;
d6bccb44 1127 register struct direct *dp;
d1dee8e8 1128 struct stat stb;
d6bccb44
RC
1129 char *otp;
1130 int len, opts;
d1dee8e8 1131
d6bccb44
RC
1132 opts = 0;
1133 while (*cp >= '0' && *cp <= '7')
1134 opts = (opts << 3) | (*cp++ - '0');
1135 if (*cp != '\0') {
1136 error("clean: options not delimited\n");
1137 return;
d1dee8e8 1138 }
80babee1 1139 if ((d = opendir(target)) == NULL) {
5f91cf7b 1140 error("%s:%s: %s\n", host, target, strerror(errno));
e8109cf8
RC
1141 return;
1142 }
d6bccb44 1143 ack();
d1dee8e8
RC
1144
1145 otp = tp;
1146 len = tp - target;
1147 while (dp = readdir(d)) {
1148 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1149 continue;
1150 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
a3e6fd64
RC
1151 error("%s:%s/%s: Name too long\n",
1152 host, target, dp->d_name);
d1dee8e8
RC
1153 continue;
1154 }
1155 tp = otp;
1156 *tp++ = '/';
1157 cp = dp->d_name;;
1158 while (*tp++ = *cp++)
1159 ;
1160 tp--;
a3e6fd64 1161 if (lstat(target, &stb) < 0) {
5f91cf7b 1162 error("%s:%s: %s\n", host, target, strerror(errno));
d1dee8e8
RC
1163 continue;
1164 }
d6bccb44 1165 (void) sprintf(buf, "Q%s\n", dp->d_name);
d1dee8e8
RC
1166 (void) write(rem, buf, strlen(buf));
1167 cp = buf;
1168 do {
1169 if (read(rem, cp, 1) != 1)
0fccdfef 1170 cleanup();
d1dee8e8
RC
1171 } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
1172 *--cp = '\0';
1173 cp = buf;
d6bccb44 1174 if (*cp != 'Y')
d1dee8e8 1175 continue;
e8109cf8
RC
1176 if (opts & VERIFY) {
1177 cp = buf;
1178 *cp++ = '\0';
80babee1 1179 (void) sprintf(cp, "need to remove: %s\n", target);
e8109cf8
RC
1180 (void) write(rem, buf, strlen(cp) + 1);
1181 } else
3a4e2988 1182 removeit(&stb);
d1dee8e8
RC
1183 }
1184 closedir(d);
d1dee8e8
RC
1185 (void) write(rem, "E\n", 2);
1186 (void) response();
d6bccb44 1187 tp = otp;
e8109cf8 1188 *tp = '\0';
d1dee8e8
RC
1189}
1190
e8109cf8
RC
1191/*
1192 * Remove a file or directory (recursively) and send back an acknowledge
1193 * or an error message.
1194 */
3a4e2988 1195removeit(stp)
a3e6fd64 1196 struct stat *stp;
d1dee8e8
RC
1197{
1198 DIR *d;
1199 struct direct *dp;
1200 register char *cp;
1201 struct stat stb;
1202 char *otp;
1203 int len;
1204
a3e6fd64 1205 switch (stp->st_mode & S_IFMT) {
d1dee8e8 1206 case S_IFREG:
a3e6fd64 1207 case S_IFLNK:
d1dee8e8
RC
1208 if (unlink(target) < 0)
1209 goto bad;
1210 goto removed;
1211
1212 case S_IFDIR:
1213 break;
1214
1215 default:
a3e6fd64 1216 error("%s:%s: not a plain file\n", host, target);
d1dee8e8
RC
1217 return;
1218 }
1219
80babee1 1220 if ((d = opendir(target)) == NULL)
d1dee8e8
RC
1221 goto bad;
1222
1223 otp = tp;
1224 len = tp - target;
1225 while (dp = readdir(d)) {
1226 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
1227 continue;
1228 if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
a3e6fd64
RC
1229 error("%s:%s/%s: Name too long\n",
1230 host, target, dp->d_name);
d1dee8e8
RC
1231 continue;
1232 }
1233 tp = otp;
1234 *tp++ = '/';
1235 cp = dp->d_name;;
1236 while (*tp++ = *cp++)
1237 ;
1238 tp--;
a3e6fd64 1239 if (lstat(target, &stb) < 0) {
5f91cf7b 1240 error("%s:%s: %s\n", host, target, strerror(errno));
d1dee8e8
RC
1241 continue;
1242 }
3a4e2988 1243 removeit(&stb);
d1dee8e8
RC
1244 }
1245 closedir(d);
1246 tp = otp;
1247 *tp = '\0';
1248 if (rmdir(target) < 0) {
1249bad:
5f91cf7b 1250 error("%s:%s: %s\n", host, target, strerror(errno));
d1dee8e8
RC
1251 return;
1252 }
1253removed:
1254 cp = buf;
1255 *cp++ = '\0';
1256 (void) sprintf(cp, "removed %s\n", target);
1257 (void) write(rem, buf, strlen(cp) + 1);
1258}
1259
d6bccb44
RC
1260/*
1261 * Execute a shell command to handle special cases.
1262 */
1263dospecial(cmd)
1264 char *cmd;
1265{
1266 int fd[2], status, pid, i;
1267 register char *cp, *s;
1268 char sbuf[BUFSIZ];
80babee1 1269 extern int userid, groupid;
d6bccb44
RC
1270
1271 if (pipe(fd) < 0) {
5f91cf7b 1272 error("%s\n", strerror(errno));
d6bccb44
RC
1273 return;
1274 }
1275 if ((pid = fork()) == 0) {
1276 /*
1277 * Return everything the shell commands print.
1278 */
1279 (void) close(0);
1280 (void) close(1);
1281 (void) close(2);
567e65c3 1282 (void) open(_PATH_DEVNULL, O_RDONLY);
d6bccb44
RC
1283 (void) dup(fd[1]);
1284 (void) dup(fd[1]);
1285 (void) close(fd[0]);
1286 (void) close(fd[1]);
80babee1 1287 setgid(groupid);
b5644799 1288 setuid(userid);
567e65c3 1289 execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
d6bccb44
RC
1290 _exit(127);
1291 }
1292 (void) close(fd[1]);
1293 s = sbuf;
1294 *s++ = '\0';
1295 while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
1296 cp = buf;
1297 do {
1298 *s++ = *cp++;
1299 if (cp[-1] != '\n') {
1300 if (s < &sbuf[sizeof(sbuf)-1])
1301 continue;
1302 *s++ = '\n';
1303 }
1304 /*
1305 * Throw away blank lines.
1306 */
1307 if (s == &sbuf[2]) {
1308 s--;
1309 continue;
1310 }
1311 (void) write(rem, sbuf, s - sbuf);
1312 s = &sbuf[1];
1313 } while (--i);
1314 }
1315 if (s > &sbuf[1]) {
1316 *s++ = '\n';
1317 (void) write(rem, sbuf, s - sbuf);
1318 }
1319 while ((i = wait(&status)) != pid && i != -1)
1320 ;
1321 if (i == -1)
1322 status = -1;
80babee1 1323 (void) close(fd[0]);
d6bccb44
RC
1324 if (status)
1325 error("shell returned %d\n", status);
1326 else
1327 ack();
1328}
1329
3024eb6f 1330/*VARARGS2*/
82572cb6
RC
1331log(fp, fmt, a1, a2, a3)
1332 FILE *fp;
2f8aab68
RC
1333 char *fmt;
1334 int a1, a2, a3;
1335{
1336 /* Print changes locally if not quiet mode */
1337 if (!qflag)
1338 printf(fmt, a1, a2, a3);
1339
1340 /* Save changes (for mailing) if really updating files */
f7770429 1341 if (!(options & VERIFY) && fp != NULL)
82572cb6 1342 fprintf(fp, fmt, a1, a2, a3);
2f8aab68
RC
1343}
1344
3024eb6f 1345/*VARARGS1*/
2f8aab68
RC
1346error(fmt, a1, a2, a3)
1347 char *fmt;
1348 int a1, a2, a3;
1349{
33b2a4be
KB
1350 static FILE *fp;
1351
1352 ++nerrs;
1353 if (!fp && !(fp = fdopen(rem, "w")))
1354 return;
1355 if (iamremote) {
1356 (void)fprintf(fp, "%crdist: ", 0x01);
1357 (void)fprintf(fp, fmt, a1, a2, a3);
1358 fflush(fp);
1359 }
1360 else {
3024eb6f 1361 fflush(stdout);
33b2a4be
KB
1362 (void)fprintf(stderr, "rdist: ");
1363 (void)fprintf(stderr, fmt, a1, a2, a3);
1364 fflush(stderr);
1365 }
1366 if (lfp != NULL) {
1367 (void)fprintf(lfp, "rdist: ");
1368 (void)fprintf(lfp, fmt, a1, a2, a3);
1369 fflush(lfp);
1370 }
2f8aab68
RC
1371}
1372
3024eb6f 1373/*VARARGS1*/
2f8aab68
RC
1374fatal(fmt, a1, a2,a3)
1375 char *fmt;
1376 int a1, a2, a3;
1377{
33b2a4be
KB
1378 static FILE *fp;
1379
1380 ++nerrs;
1381 if (!fp && !(fp = fdopen(rem, "w")))
1382 return;
1383 if (iamremote) {
1384 (void)fprintf(fp, "%crdist: ", 0x02);
1385 (void)fprintf(fp, fmt, a1, a2, a3);
1386 fflush(fp);
1387 }
1388 else {
3024eb6f 1389 fflush(stdout);
33b2a4be
KB
1390 (void)fprintf(stderr, "rdist: ");
1391 (void)fprintf(stderr, fmt, a1, a2, a3);
1392 fflush(stderr);
1393 }
1394 if (lfp != NULL) {
1395 (void)fprintf(lfp, "rdist: ");
1396 (void)fprintf(lfp, fmt, a1, a2, a3);
1397 fflush(lfp);
1398 }
2f8aab68
RC
1399 cleanup();
1400}
1401
1402response()
1403{
024fde5b 1404 char *cp, *s;
85b60cd9 1405 char resp[BUFSIZ];
2f8aab68
RC
1406
1407 if (debug)
1408 printf("response()\n");
1409
85b60cd9 1410 cp = s = resp;
024fde5b
RC
1411 do {
1412 if (read(rem, cp, 1) != 1)
346dad94 1413 lostconn();
85b60cd9 1414 } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
2f8aab68 1415
024fde5b 1416 switch (*s++) {
2f8aab68 1417 case '\0':
024fde5b 1418 *--cp = '\0';
d6bccb44 1419 if (*s != '\0') {
024fde5b 1420 log(lfp, "%s\n", s);
d6bccb44
RC
1421 return(1);
1422 }
2f8aab68 1423 return(0);
ac288309
KS
1424 case '\3':
1425 *--cp = '\0';
1426 log(lfp, "Note: %s\n",s);
1427 return(response());
2f8aab68
RC
1428
1429 default:
024fde5b 1430 s--;
2f8aab68
RC
1431 /* fall into... */
1432 case '\1':
1433 case '\2':
0fccdfef 1434 nerrs++;
024fde5b 1435 if (*s != '\n') {
82572cb6
RC
1436 if (!iamremote) {
1437 fflush(stdout);
024fde5b 1438 (void) write(2, s, cp - s);
82572cb6 1439 }
2f8aab68 1440 if (lfp != NULL)
024fde5b 1441 (void) fwrite(s, 1, cp - s, lfp);
2f8aab68 1442 }
85b60cd9 1443 if (resp[0] == '\2')
346dad94 1444 lostconn();
e8109cf8 1445 return(-1);
2f8aab68 1446 }
2f8aab68
RC
1447}
1448
0fccdfef
RC
1449/*
1450 * Remove temporary files and do any cleanup operations before exiting.
1451 */
bc0c28c1 1452void
0fccdfef 1453cleanup()
2f8aab68 1454{
e385b216 1455 (void) unlink(tempfile);
0fccdfef 1456 exit(1);
2f8aab68 1457}
ac288309
KS
1458
1459note(fmt, a1, a2, a3)
3a4e2988
CT
1460 char *fmt;
1461 int a1, a2, a3;
ac288309
KS
1462{
1463 static char buf[BUFSIZ];
1464 sprintf(buf, fmt, a1, a2, a3);
1465 comment(buf);
1466}
1467
1468comment(s)
1469char *s;
1470{
1471 char c = '\3';
1472 write(rem, &c, 1);
1473 write(rem, s, strlen(s));
1474 c = '\n';
1475 write(rem, &c, 1);
1476}