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