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