(void) snprintf(buf, BUFSIZ, "K%o %o %qd %ld %s %s %s\n",
(void) snprintf(buf, sizeof(buf), "K%o %o %qd %ld %s %s %s\n",
		    savelink __P((struct stat *));static void	sendf __P((char *, int));static int	update __P((char *, int, struct stat *));/* * Server routine to read requests and process them. * Commands are: *	Tname	- Transmit file if out of date *	Vname	- Verify if file out of date or not *	Qname	- Query if file exists. Return mtime & size if it does. */voidserver(){	char cmdbuf[BUFSIZ];	register char *cp;	signal(SIGHUP, cleanup);	signal(SIGINT, cleanup);	signal(SIGQUIT, cleanup);	signal(SIGTERM, cleanup);	signal(SIGPIPE, cleanup);	rem = 0;	oumask = umask(0);	(void) snprintf(buf, BUFSIZ, "V%d\n", VERSION);	(void) write(rem, buf, strlen(buf));	if (getuid() != geteuid()) {		error("This version of rdist should not be installed setuid.\n");		return;	}	for (;;) {		cp = cmdbuf;		if (read(rem, cp, 1) <= 0)			return;		if (*cp++ == '\n') {			error("server: expected control record\n");			continue;		}		do {			if (read(rem, cp, 1) != 1)				cleanup(0);		} while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);		*--cp = '\0';		cp = cmdbuf;		switch (*cp++) {		case 'T':  /* init target file/directory name */			catname = 1;	/* target should be directory */			goto dotarget;		case 't':  /* init target file/directory name */			catname = 0;		dotarget:			if (exptilde(target, cp) == NULL)				continue;			tp = target;			while (*tp)				tp++;			ack();			continue;		case 'R':  /* Transfer a regular file. */			recvf(cp, S_IFREG);			continue;		case 'D':  /* Transfer a directory. */			recvf(cp, S_IFDIR);			continue;		case 'K':  /* Transfer symbolic link. */			recvf(cp, S_IFLNK);			continue;		case 'k':  /* Transfer hard link. */			hardlink(cp);			continue;		case 'E':  /* End. (of directory) */			*tp = '\0';			if (catname <= 0) {				error("server: too many 'E's\n");				continue;			}			tp = stp[--catname];			*tp = '\0';			ack();			continue;		case 'C':  /* Clean. Cleanup a directory */			clean(cp);			continue;		case 'Q':  /* Query. Does the file/directory exist? */			query(cp);			continue;		case 'S':  /* Special. Execute commands */			dospecial(cp);			continue;#ifdef notdef		/*		 * These entries are reserved but not currently used.		 * The intent is to allow remote hosts to have master copies.		 * Currently, only the host rdist runs on can have masters.		 */		case 'X':  /* start a new list of files to exclude */			except = bp = NULL;		case 'x':  /* add name to list of files to exclude */			if (*cp == '\0') {				ack();				continue;			}			if (*cp == '~') {				if (exptilde(buf, cp) == NULL)					continue;				cp = buf;			}			if (bp == NULL)				except = bp = expand(makeblock(NAME, cp), E_VARS);			else				bp->b_next = expand(makeblock(NAME, cp), E_VARS);			while (bp->b_next != NULL)				bp = bp->b_next;			ack();			continue;		case 'I':  /* Install. Transfer file if out of date. */			opts = 0;			while (*cp >= '0' && *cp <= '7')				opts = (opts << 3) | (*cp++ - '0');			if (*cp++ != ' ') {				error("server: options not delimited\n");				return;			}			install(cp, opts);			continue;		case 'L':  /* Log. save message in log file */			log(lfp, cp);			continue;#endif		case '\1':			nerrs++;			continue;		case '\2':			return;		default:			error("server: unknown command '%s'\n", cp);		case '\0':			continue;		}	}}/* * Update the file(s) if they are different. * destdir = 1 if destination should be a directory * (i.e., more than one source is being copied to the same destination). */voidinstall(src, dest, destdir, opts)	char *src, *dest;	int destdir, opts;{	char *rname;	char destcopy[BUFSIZ];	if (dest == NULL) {		opts &= ~WHOLE; /* WHOLE mode only useful if renaming */		dest = src;	}	if (nflag || debug) {		printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",			opts & WHOLE ? " -w" : "",			opts & YOUNGER ? " -y" : "",			opts & COMPARE ? " -b" : "",			opts & REMOVE ? " -R" : "", src, dest);		if (nflag)			return;	}	rname = exptilde(target, src);	if (rname == NULL)		return;	tp = target;	while (*tp)		tp++;	/*	 * If we are renaming a directory and we want to preserve	 * the directory heirarchy (-w), we must strip off the leading	 * directory name and preserve the rest.	 */	if (opts & WHOLE) {		while (*rname == '/')			rname++;		destdir = 1;	} else {		rname = xbasename(target);	}	if (debug)		printf("target = %s, rname = %s\n", target, rname);	/*	 * Pass the destination file/directory name to remote.	 */	(void) snprintf(buf, BUFSIZ, "%c%s\n", destdir ? 'T' : 't', dest);	if (debug)		printf("buf = %s", buf);	(void) write(rem, buf, strlen(buf));	if (response() < 0)		return;	if (destdir) {		strcpy(destcopy, dest);		Tdest = destcopy;	}	sendf(rname, opts);	Tdest = 0;}#define protoname() (pw ? pw->pw_name : user)#define protogroup() (gr ? gr->gr_name : group)/* * Transfer the file or directory in target[]. * rname is the name of the file on the remote host. */static voidsendf(rname, opts)	char *rname;	int opts;{	register struct subcmd *sc;	struct stat stb;	int sizerr, f, u, len;	off_t i;	DIR *d;	struct direct *dp;	char *otp, *cp;	extern struct subcmd *subcmds;	static char user[15], group[15];	if (debug)		printf("sendf(%s, %x)\n", rname, opts);	if (except(target))		return;	if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {		error("%s: %s\n", target, strerror(errno));		return;	}	if ((u = update(rname, opts, &stb)) == 0) {		if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)			(void) savelink(&stb);		return;	}	if (pw == NULL || pw->pw_uid != stb.st_uid)		if ((pw = getpwuid(stb.st_uid)) == NULL) {			log(lfp, "%s: no password entry for uid %d \n",				target, stb.st_uid);			pw = NULL;			(void) snprintf(user, sizeof(user), ":%lu", stb.st_uid);		}	if (gr == NULL || gr->gr_gid != stb.st_gid)		if ((gr = getgrgid(stb.st_gid)) == NULL) {			log(lfp, "%s: no name for group %d\n",				target, stb.st_gid);			gr = NULL;			(void) snprintf(group, sizeof(group), ":%lu", stb.st_gid);		}	if (u == 1) {		if (opts & VERIFY) {			log(lfp, "need to install: %s\n", target);			goto dospecial;		}		log(lfp, "installing: %s\n", target);		opts &= ~(COMPARE|REMOVE);	}	switch (stb.st_mode & S_IFMT) {	case S_IFDIR:		if ((d = opendir(target)) == NULL) {			error("%s: %s\n", target, strerror(errno));			return;		}		(void) snprintf(buf, BUFSIZ, "D%o %04o 0 0 %s %s %s\n", opts,			stb.st_mode & 07777, protoname(), protogroup(), rname);		if (debug)			printf("buf = %s", buf);		(void) write(rem, buf, strlen(buf));		if (response() < 0) {			closedir(d);			return;		}		if (opts & REMOVE)			rmchk(opts);		otp = tp;		len = tp - target;		while (dp = readdir(d)) {			if (!strcmp(dp->d_name, ".") ||			    !strcmp(dp->d_name, ".."))				continue;			if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {				error("%s/%s: Name too long\n", target,					dp->d_name);				continue;			}			tp = otp;			*tp++ = '/';			cp = dp->d_name;			while (*tp++ = *cp++)				;			tp--;			sendf(dp->d_name, opts);		}		closedir(d);		(void) write(rem, "E\n", 2);		(void) response();		tp = otp;		*tp = '\0';		return;	case S_IFLNK:		if (u != 1)			opts |= COMPARE;		if (stb.st_nlink > 1) {			struct linkbuf *lp;			if ((lp = savelink(&stb)) != NULL) {				/* install link */				if (*lp->target == 0)				(void) snprintf(buf, BUFSIZ, "k%o %s %s\n",					opts, lp->pathname, rname);				else				(void) snprintf(buf, BUFSIZ, "k%o %s/%s %s\n",					opts, lp->target, lp->pathname, rname);				if (debug)					printf("buf = %s", buf);				(void) write(rem, buf, strlen(buf));				(void) response();				return;			}		}		(void) snprintf(buf, BUFSIZ, "K%o %o %qd %ld %s %s %s\n",			opts, stb.st_mode & 07777, stb.st_size, stb.st_mtime,			protoname(), protogroup(), rname);		if (debug)			printf("buf = %s", buf);		(void) write(rem, buf, strlen(buf));		if (response() < 0)			return;		sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);		(void) write(rem, buf, stb.st_size);		if (debug)			printf("readlink = %.*s\n", (int)stb.st_size, buf);		goto done;	case S_IFREG:		break;	default:		error("%s: not a file or directory\n", target);		return;	}	if (u == 2) {		if (opts & VERIFY) {			log(lfp, "need to update: %s\n", target);			goto dospecial;		}		log(lfp, "updating: %s\n", target);	}	if (stb.st_nlink > 1) {		struct linkbuf *lp;		if ((lp = savelink(&stb)) != NULL) {			/* install link */			if (*lp->target == 0)			(void) snprintf(buf, BUFSIZ, "k%o %s %s\n", opts,				lp->pathname, rname);			else			(void) snprintf(buf, BUFSIZ, "k%o %s/%s %s\n",				opts, lp->target, lp->pathname, rname);			if (debug)				printf("buf = %s", buf);			(void) write(rem, buf, strlen(buf));			(void) response();			return;		}	}	if ((f = open(target, O_RDONLY, 0)) < 0) {		error("%s: %s\n", target, strerror(errno));		return;	}	(void) snprintf(buf, BUFSIZ, "R%o %o %qd %ld %s %s %s\n", opts,		stb.st_mode & 07777, stb.st_size, stb.st_mtime,		protoname(), protogroup(), rname);	if (debug)		printf("buf = %s", buf);	(void) write(rem, buf, strlen(buf));	if (response() < 0) {		(void) close(f);		return;	}	sizerr = 0;	for (i = 0; i < stb.st_size; i += BUFSIZ) {		int amt = BUFSIZ;		if (i + amt > stb.st_size)			amt = stb.st_size - i;		if (sizerr == 0 && read(f, buf, amt) != amt)			sizerr = 1;		(void) write(rem, buf, amt);	}	(void) close(f);done:	if (sizerr) {		error("%s: file changed size\n", target);		err();	} else		ack();	f = response();	if (f < 0 || f == 0 && (opts & COMPARE))		return;dospecial:	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {		if (sc->sc_type != SPECIAL)			continue;		if (sc->sc_args != NULL && !inlist(sc->sc_args, target))			continue;		log(lfp, "special \"%s\"\n", sc->sc_name);		if (opts & VERIFY)			continue;		(void) snprintf(buf, BUFSIZ, "SFILE=%s;%s\n", target,			sc->sc_name);		if (debug)			printf("buf = %s", buf);		(void) write(rem, buf, strlen(buf));		while (response() > 0)			;	}}static struct linkbuf *savelink(stp)	struct stat *stp;{	struct linkbuf *lp;	for (lp = ihead; lp != NULL; lp = lp->nextp)		if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {			lp->count--;			return(lp);		}	lp = (struct linkbuf *) malloc(sizeof(*lp));	if (lp == NULL)		log(lfp, "out of memory, link information lost\n");	else {		lp->nextp = ihead;		ihead = lp;		lp->inum = stp->st_ino;		lp->devnum = stp->st_dev;		lp->count = stp->st_nlink - 1;		strcpy(lp->pathname, target);		if (Tdest)			strcpy(lp->target, Tdest);		else			*lp->target = 0;	}	return(NULL);}