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