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