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