summaryrefslogtreecommitdiff
path: root/usr.bin/rdist/client.c
diff options
context:
space:
mode:
authordm <dm@cvs.openbsd.org>1996-02-03 12:13:04 +0000
committerdm <dm@cvs.openbsd.org>1996-02-03 12:13:04 +0000
commitf34705a30c83ef27acf58b47c75c736e6858247a (patch)
tree2121eb2901c910af7604845f5538c2fc26d01ac3 /usr.bin/rdist/client.c
parentfdb8a9c5e300099e2c65b848550471fb1d9f0cd4 (diff)
rdist 6.1.1
Diffstat (limited to 'usr.bin/rdist/client.c')
-rw-r--r--usr.bin/rdist/client.c1239
1 files changed, 1239 insertions, 0 deletions
diff --git a/usr.bin/rdist/client.c b/usr.bin/rdist/client.c
new file mode 100644
index 00000000000..14bd028a9ec
--- /dev/null
+++ b/usr.bin/rdist/client.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char RCSid[] =
+"$Id: client.c,v 1.1 1996/02/03 12:12:11 dm Exp $";
+
+static char sccsid[] = "@(#)client.c";
+
+static char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * Routines used in client mode to communicate with remove server.
+ */
+
+#include "defs.h"
+#include "y.tab.h"
+
+/*
+ * Update status
+ */
+#define US_NOTHING 0 /* No update needed */
+#define US_NOENT 1 /* Entry does not exist */
+#define US_OUTDATE 2 /* Entry is out of date */
+#define US_DOCOMP 3 /* Do a binary comparison */
+#define US_MODE 4 /* Modes of file differ */
+
+struct linkbuf *ihead = NULL; /* list of files with more than one link */
+char buf[BUFSIZ]; /* general purpose buffer */
+u_char respbuff[BUFSIZ]; /* Response buffer */
+char target[BUFSIZ]; /* target/source directory name */
+char source[BUFSIZ]; /* source directory name */
+char *ptarget; /* pointer to end of target name */
+char *Tdest; /* pointer to last T dest*/
+struct namelist *updfilelist = NULL; /* List of updated files */
+
+static int sendit();
+
+/*
+ * return remote file pathname (relative from target)
+ */
+char *remfilename(src, dest, path, rname, destdir)
+ char *src, *dest, *path, *rname;
+ int destdir;
+{
+ extern struct namelist *filelist;
+ register char *lname, *cp;
+ static char buff[BUFSIZ];
+ int srclen, pathlen;
+ char *p;
+
+
+ debugmsg(DM_MISC,
+ "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n",
+ A(src), A(dest), A(path), A(rname), destdir);
+
+ if (!dest) {
+ debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path);
+ return(path);
+ }
+
+ if (!destdir) {
+ debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest);
+ return(dest);
+ }
+
+ buff[0] = CNULL;
+ lname = buff;
+ if (path && *path) {
+ cp = strrchr(path, '/');
+ if (cp == NULL)
+ (void) sprintf(buff, "%s/%s", dest, path);
+ else {
+ srclen = strlen(src);
+ pathlen = strlen(path);
+ if (srclen >= pathlen)
+ cp++; /* xbasename(path) */
+ else {
+ if (filelist && filelist->n_next == NULL)
+ /* path relative to src */
+ cp = path + srclen;
+ else {
+ if ((p = strrchr(src, '/')))
+ cp = path + srclen - strlen(p);
+ else
+ cp = path;
+ }
+ }
+ if ((*cp != '/') && *cp)
+ (void) sprintf(buff, "%s/%s", dest, cp);
+ else
+ (void) sprintf(buff, "%s%s", dest, cp);
+ }
+ } else
+ strcpy(lname, dest);
+
+ debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname);
+
+ return(lname);
+}
+
+/*
+ * Return true if name is in the list.
+ */
+int inlist(list, file)
+ struct namelist *list;
+ char *file;
+{
+ register struct namelist *nl;
+
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ if (strcmp(file, nl->n_name) == 0)
+ return(1);
+ return(0);
+}
+
+/*
+ * Run any special commands for this file
+ */
+static void runspecial(starget, opts, rname, destdir)
+ char *starget;
+ opt_t opts;
+ char *rname;
+ int destdir;
+{
+ register struct subcmd *sc;
+ extern struct subcmd *subcmds;
+ char *rfile;
+
+ rfile = remfilename(source, Tdest, target, rname, destdir);
+
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != SPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
+ continue;
+ message(MT_CHANGE, "special \"%s\"", sc->sc_name);
+ if (IS_ON(opts, DO_VERIFY))
+ continue;
+ (void) sendcmd(C_SPECIAL,
+ "%s=%s;%s=%s;%s=%s;export %s %s %s;%s",
+ E_LOCFILE, starget,
+ E_REMFILE, rfile,
+ E_BASEFILE, xbasename(rfile),
+ E_LOCFILE, E_REMFILE, E_BASEFILE,
+ sc->sc_name);
+ while (response() > 0)
+ ;
+ }
+}
+
+/*
+ * If we're doing a target with a "cmdspecial" in it, then
+ * save the name of the file being updated for use with "cmdspecial".
+ */
+static void addcmdspecialfile(starget, rname, destdir)
+ char *starget;
+ char *rname;
+ int destdir;
+{
+ char *rfile;
+ struct namelist *new;
+ register struct subcmd *sc;
+ extern struct subcmd *subcmds;
+ int isokay = 0;
+
+ rfile = remfilename(source, Tdest, target, rname, destdir);
+
+ for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) {
+ if (sc->sc_type != CMDSPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
+ continue;
+ isokay = TRUE;
+ }
+
+ if (isokay) {
+ new = (struct namelist *) xmalloc(sizeof(struct namelist));
+ new->n_name = strdup(rfile);
+ new->n_next = updfilelist;
+ updfilelist = new;
+ }
+}
+
+/*
+ * Free the file list
+ */
+static void freecmdspecialfiles()
+{
+ register struct namelist *ptr, *save;
+
+ for (ptr = updfilelist; ptr; ) {
+ if (ptr->n_name) (void) free(ptr->n_name);
+ save = ptr->n_next;
+ (void) free(ptr);
+ if (save)
+ ptr = save->n_next;
+ else
+ ptr = NULL;
+ }
+ updfilelist = NULL;
+}
+
+/*
+ * Run commands for an entire cmd
+ */
+extern void runcmdspecial(cmd, filev, opts)
+ struct cmd *cmd;
+ char **filev;
+ opt_t opts;
+{
+ register struct subcmd *sc;
+ register struct namelist *f;
+ register char **cpp;
+ char *file;
+ int first = TRUE;
+
+ for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != CMDSPECIAL)
+ continue;
+ message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name);
+ if (IS_ON(opts, DO_VERIFY))
+ continue;
+ /* Send all the file names */
+ for (f = updfilelist; f != NULL; f = f->n_next) {
+ if (first) {
+ (void) sendcmd(C_CMDSPECIAL, NULL);
+ if (response() < 0)
+ return;
+ first = FALSE;
+ }
+ (void) sendcmd(RC_FILE, f->n_name);
+ if (response() < 0)
+ return;
+ }
+ if (first) {
+ (void) sendcmd(C_CMDSPECIAL, NULL);
+ if (response() < 0)
+ return;
+ first = FALSE;
+ }
+ /* Send command to run and wait for it to complete */
+ (void) sendcmd(RC_COMMAND, sc->sc_name);
+ while (response() > 0)
+ ;
+ first = TRUE; /* Reset in case there are more CMDSPECIAL's */
+ }
+ freecmdspecialfiles();
+}
+
+/*
+ * For security, reject filenames that contains a newline
+ */
+int checkfilename(name)
+ char *name;
+{
+ register char *cp;
+
+ if (strchr(name, '\n')) {
+ for (cp = name; *cp; cp++)
+ if (*cp == '\n')
+ *cp = '?';
+ message(MT_NERROR,
+ "Refuse to handle filename containing newline: %s",
+ name);
+ return(-1);
+ }
+
+ return(0);
+}
+
+/*
+ * Save and retrieve hard link info
+ */
+static struct linkbuf *linkinfo(statp)
+ struct stat *statp;
+{
+ struct linkbuf *lp;
+
+ for (lp = ihead; lp != NULL; lp = lp->nextp)
+ if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) {
+ lp->count--;
+ return(lp);
+ }
+
+ lp = (struct linkbuf *) xmalloc(sizeof(*lp));
+ lp->nextp = ihead;
+ ihead = lp;
+ lp->inum = statp->st_ino;
+ lp->devnum = statp->st_dev;
+ lp->count = statp->st_nlink - 1;
+ (void) strcpy(lp->pathname, target);
+ (void) strcpy(lp->src, source);
+ if (Tdest)
+ (void) strcpy(lp->target, Tdest);
+ else
+ *lp->target = CNULL;
+
+ return((struct linkbuf *) NULL);
+}
+
+/*
+ * Send a hardlink
+ */
+static int sendhardlink(opts, lp, rname, destdir)
+ opt_t opts;
+ struct linkbuf *lp;
+ char *rname;
+ int destdir;
+{
+ static char buff[MAXPATHLEN];
+ char *lname; /* name of file to link to */
+
+ debugmsg(DM_MISC,
+ "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
+ rname, lp->pathname, lp->src, lp->target);
+
+ if (*lp->target == CNULL)
+ (void) sendcmd(C_RECVHARDLINK, "%o %s %s",
+ opts, lp->pathname, rname);
+ else {
+ lname = buff;
+ strcpy(lname, remfilename(lp->src, lp->target,
+ lp->pathname, rname,
+ destdir));
+ debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname);
+ (void) sendcmd(C_RECVHARDLINK, "%o %s %s",
+ opts, lname, rname);
+ }
+
+ return(response());
+}
+
+/*
+ * Send a file
+ */
+static int sendfile(rname, opts, stb, user, group, destdir)
+ char *rname;
+ opt_t opts;
+ struct stat *stb;
+ char *user, *group;
+ int destdir;
+{
+ int goterr, f;
+ off_t i;
+
+ if (stb->st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = linkinfo(stb)) != NULL)
+ return(sendhardlink(opts, lp, rname, destdir));
+ }
+
+ if ((f = open(target, O_RDONLY)) < 0) {
+ error("%s: open for read failed: %s", target, SYSERR);
+ return(-1);
+ }
+
+ /*
+ * Send file info
+ */
+ (void) sendcmd(C_RECVREG, "%o %04o %ld %ld %ld %s %s %s",
+ opts, stb->st_mode & 07777,
+ (long) stb->st_size,
+ stb->st_mtime, stb->st_atime,
+ user, group, rname);
+ if (response() < 0) {
+ (void) close(f);
+ return(-1);
+ }
+
+ debugmsg(DM_MISC, "Send file '%s' %d bytes\n",
+ rname, (long) stb->st_size);
+
+ /*
+ * Set remote time out alarm handler.
+ */
+ (void) signal(SIGALRM, sighandler);
+
+ /*
+ * Actually transfer the file
+ */
+ goterr = 0;
+ for (i = 0; i < stb->st_size; i += BUFSIZ) {
+ int amt = BUFSIZ;
+
+ (void) alarm(rtimeout);
+ if (i + amt > stb->st_size)
+ amt = stb->st_size - i;
+ if (read(f, buf, amt) != amt) {
+ error("%s: File changed size", target);
+ err();
+ ++goterr;
+ /*
+ * XXX - We have to keep going because the
+ * server expects to receive a fixed number
+ * of bytes that we specified as the file size.
+ * We need Out Of Band communication to handle
+ * this situation gracefully.
+ */
+ }
+ if (xwrite(rem_w, buf, amt) < 0) {
+ error("%s: Error writing to client: %s",
+ target, SYSERR);
+ err();
+ ++goterr;
+ break;
+ }
+ (void) alarm(0);
+ }
+
+ (void) alarm(0); /* Insure alarm is off */
+ (void) close(f);
+
+ debugmsg(DM_MISC, "Send file '%s' %s.\n",
+ (goterr) ? "failed" : "complete", rname);
+
+ /*
+ * Check for errors and end send
+ */
+ if (goterr)
+ return(-1);
+ else {
+ ack();
+ f = response();
+ if (f < 0)
+ return(-1);
+ else if (f == 0 && IS_ON(opts, DO_COMPARE))
+ return(0);
+
+ runspecial(target, opts, rname, destdir);
+ addcmdspecialfile(target, rname, destdir);
+
+ return(0);
+ }
+}
+
+/*
+ * Check for files on the machine being updated that are not on the master
+ * machine and remove them.
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing happened.
+ * Return > 0 if anything is updated.
+ */
+static int rmchk(opts)
+ opt_t opts;
+{
+ register u_char *s;
+ struct stat stb;
+ int didupdate = 0;
+ int n;
+
+ debugmsg(DM_CALL, "rmchk()\n");
+
+ /*
+ * Tell the remote to clean the files from the last directory sent.
+ */
+ (void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY));
+ if (response() < 0)
+ return(-1);
+
+ for ( ; ; ) {
+ n = remline(s = respbuff, sizeof(respbuff), TRUE);
+ if (n <= 0) {
+ error("rmchk: unexpected control record");
+ return(didupdate);
+ }
+
+ switch (*s++) {
+ case CC_QUERY: /* Query if file should be removed */
+ /*
+ * Return the following codes to remove query.
+ * CC_NO -- file exists - DON'T remove.
+ * CC_YES -- file doesn't exist - REMOVE.
+ */
+ (void) sprintf(ptarget, "%s%s",
+ (ptarget[-1] == '/' ? "" : "/"), s);
+ debugmsg(DM_MISC, "check %s\n", target);
+ if (except(target))
+ (void) sendcmd(CC_NO, NULL);
+ else if (lstat(target, &stb) < 0) {
+ if (sendcmd(CC_YES, NULL) == 0)
+ didupdate = 1;
+ } else
+ (void) sendcmd(CC_NO, NULL);
+ break;
+
+ case CC_END:
+ *ptarget = CNULL;
+ ack();
+ return(didupdate);
+
+ case C_LOGMSG:
+ if (n > 0)
+ message(MT_INFO, "%s", s);
+ break;
+
+ case C_ERRMSG:
+ message(MT_NERROR, "%s", s);
+ return(didupdate);
+
+ case C_FERRMSG:
+ message(MT_FERROR, "%s", s);
+ finish();
+
+ default:
+ error("rmchk: unexpected response '%s'", respbuff);
+ err();
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Send a directory
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing happened.
+ * Return > 0 if anything is updated.
+ */
+static int senddir(rname, opts, stb, user, group, destdir)
+ char *rname;
+ opt_t opts;
+ struct stat *stb;
+ char *user, *group;
+ int destdir;
+{
+ DIRENTRY *dp;
+ DIR *d;
+ char *optarget, *cp;
+ int len;
+ int didupdate = 0;
+
+ /*
+ * Send recvdir command in recvit() format.
+ */
+ (void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s",
+ opts, stb->st_mode & 07777, user, group, rname);
+ if (response() < 0)
+ return(-1);
+
+ /*
+ * Don't descend into directory
+ */
+ if (IS_ON(opts, DO_NODESCEND))
+ return(0);
+
+ if (IS_ON(opts, DO_REMOVE))
+ if (rmchk(opts) > 0)
+ ++didupdate;
+
+ if ((d = opendir(target)) == NULL) {
+ error("%s: opendir failed: %s", target, SYSERR);
+ return(-1);
+ }
+
+ optarget = ptarget;
+ len = ptarget - target;
+ while (dp = readdir(d)) {
+ if (!strcmp(dp->d_name, ".") ||
+ !strcmp(dp->d_name, ".."))
+ continue;
+ if (len + 1 + (int) strlen(dp->d_name) >= MAXPATHLEN - 1) {
+ error("%s/%s: Name too long", target,
+ dp->d_name);
+ continue;
+ }
+ ptarget = optarget;
+ if (ptarget[-1] != '/')
+ *ptarget++ = '/';
+ cp = dp->d_name;
+ while (*ptarget++ = *cp++)
+ ;
+ ptarget--;
+ if (sendit(dp->d_name, opts, destdir) > 0)
+ didupdate = 1;
+ }
+ (void) closedir(d);
+
+ (void) sendcmd(C_END, NULL);
+ (void) response();
+
+ ptarget = optarget;
+ *ptarget = CNULL;
+
+ return(didupdate);
+}
+
+/*
+ * Send a link
+ */
+static int sendlink(rname, opts, stb, user, group, destdir)
+ char *rname;
+ opt_t opts;
+ struct stat *stb;
+ char *user;
+ char *group;
+ int destdir;
+{
+ int sizerr, f, n;
+ static char tbuf[BUFSIZ];
+ char lbuf[MAXPATHLEN];
+ u_char *s;
+
+ debugmsg(DM_CALL, "sendlink(%s, %x, stb, %d)\n", rname, opts, destdir);
+
+ if (stb->st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = linkinfo(stb)) != NULL)
+ return(sendhardlink(opts, lp, rname, destdir));
+ }
+
+ /*
+ * Gather and send basic link info
+ */
+ (void) sendcmd(C_RECVSYMLINK, "%o %04o %ld %ld %ld %s %s %s",
+ opts, stb->st_mode & 07777,
+ (long) stb->st_size,
+ stb->st_mtime, stb->st_atime,
+ user, group, rname);
+ if (response() < 0)
+ return(-1);
+
+ /*
+ * Gather and send additional link info
+ */
+ sizerr = (readlink(target, lbuf, sizeof(lbuf)) != stb->st_size);
+ (void) sprintf(tbuf, "%.*s", (int) stb->st_size, lbuf);
+ (void) sendcmd(C_NONE, "%s\n", tbuf);
+
+ if (sizerr) {
+ error("%s: file changed size", target);
+ err();
+ } else
+ ack();
+
+ /*
+ * Check response
+ */
+ f = response();
+ if (f < 0)
+ return(-1);
+ else if (f == 0 && IS_ON(opts, DO_COMPARE))
+ return(0);
+
+ /*
+ * Read and process responses from server.
+ * The server may send multiple messages regarding
+ * file deletes if the remote target is a directory.
+ */
+ for (;;) {
+ n = remline(s = respbuff, sizeof(respbuff), TRUE);
+ if (n == -1) /* normal EOF */
+ return(0);
+ if (n == 0) {
+ error("expected control record");
+ continue;
+ }
+
+ switch (*s++) {
+ case C_END: /* End of send operation */
+ *ptarget = CNULL;
+ ack();
+ runspecial(target, opts, rname, destdir);
+ addcmdspecialfile(target, rname, destdir);
+ return(0);
+
+ case C_LOGMSG:
+ if (n > 0)
+ message(MT_INFO, "%s", s);
+ break;
+
+ case C_ERRMSG:
+ message(MT_NERROR, "%s", s);
+ return(-1);
+
+ case C_FERRMSG:
+ message(MT_FERROR, "%s", s);
+ finish();
+
+ default:
+ error("install link: unexpected response '%s'",
+ respbuff);
+ err();
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Check to see if file needs to be updated on the remote machine.
+ * Returns:
+ * US_NOTHING - no update
+ * US_NOENT - remote doesn't exist
+ * US_OUTDATE - out of date
+ * US_DOCOMP - comparing binaries to determine if out of date
+ * US_MODE - File modes do not match
+ */
+static int update(rname, opts, statp)
+ char *rname;
+ opt_t opts;
+ struct stat *statp;
+{
+ register off_t size;
+ register time_t mtime;
+ unsigned short lmode;
+ unsigned short rmode;
+ char *owner = NULL, *group = NULL;
+ int done, n;
+ u_char *cp;
+
+ debugmsg(DM_CALL, "update(%s, 0x%x, 0x%x)\n", rname, opts, statp);
+
+ if (IS_ON(opts, DO_NOEXEC))
+ if (isexec(target, statp)) {
+ debugmsg(DM_MISC, "%s is an executable\n", target);
+ return(US_NOTHING);
+ }
+
+ /*
+ * Check to see if the file exists on the remote machine.
+ */
+ (void) sendcmd(C_QUERY, "%s", rname);
+
+ for (done = 0; !done;) {
+ n = remline(cp = respbuff, sizeof(respbuff), TRUE);
+ if (n <= 0) {
+ error("update: unexpected control record in response to query");
+ return(US_NOTHING);
+ }
+
+ switch (*cp++) {
+ case QC_ONNFS: /* Resides on a NFS */
+ debugmsg(DM_MISC,
+ "update: %s is on a NFS. Skipping...\n",
+ rname);
+ return(US_NOTHING);
+
+ case QC_SYM: /* Is a symbolic link */
+ debugmsg(DM_MISC,
+ "update: %s is a symlink. Skipping...\n",
+ rname);
+ return(US_NOTHING);
+
+ case QC_ONRO: /* Resides on a Read-Only fs */
+ debugmsg(DM_MISC,
+ "update: %s is on a RO fs. Skipping...\n",
+ rname);
+ return(US_NOTHING);
+
+ case QC_YES:
+ done = 1;
+ break;
+
+ case QC_NO: /* file doesn't exist so install it */
+ return(US_NOENT);
+
+ case C_ERRMSG:
+ if (cp)
+ message(MT_NERROR, "%s", cp);
+ return(US_NOTHING);
+
+ case C_FERRMSG:
+ if (cp)
+ message(MT_FERROR, "%s", cp);
+ finish();
+
+ case C_NOTEMSG:
+ if (cp)
+ message(MT_NOTICE, "%s", cp);
+ break;
+ /* Goto top of loop */
+
+ default:
+ error("update: unexpected response to query '%s'", cp);
+ return(US_NOTHING);
+ }
+ }
+
+ /*
+ * Target exists, but no other info passed
+ */
+ if (n <= 1 || !S_ISREG(statp->st_mode))
+ return(US_OUTDATE);
+
+ if (IS_ON(opts, DO_COMPARE))
+ return(US_DOCOMP);
+
+ /*
+ * Parse size
+ */
+ size = strtol(cp, &cp, 10);
+ if (*cp++ != ' ') {
+ error("update: size not delimited");
+ return(US_NOTHING);
+ }
+
+ /*
+ * Parse mtime
+ */
+ mtime = strtol(cp, &cp, 10);
+ if (*cp++ != ' ') {
+ error("update: mtime not delimited");
+ return(US_NOTHING);
+ }
+
+ /*
+ * Parse remote file mode
+ */
+ rmode = strtol(cp, &cp, 8);
+ if (cp && *cp)
+ ++cp;
+
+ /*
+ * Be backwards compatible
+ */
+ if (cp && *cp != CNULL) {
+ /*
+ * Parse remote file owner
+ */
+ owner = strtok((char *)cp, " ");
+ if (owner == NULL) {
+ error("update: owner not delimited");
+ return(US_NOTHING);
+ }
+
+ /*
+ * Parse remote file group
+ */
+ group = strtok((char *) NULL, " ");
+ if (group == NULL) {
+ error("update: group not delimited");
+ return(US_NOTHING);
+ }
+ }
+
+ /*
+ * File needs to be updated?
+ */
+ lmode = statp->st_mode & 07777;
+
+ debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n",
+ rname, lmode, rmode);
+ debugmsg(DM_MISC, "update(%s,) size %d mtime %d owner '%s' grp '%s'\n",
+ rname, (int) size, mtime, owner, group);
+
+ if (statp->st_mtime != mtime) {
+ if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
+ message(MT_WARNING,
+ "%s: Warning: remote copy is newer",
+ target);
+ return(US_NOTHING);
+ }
+ return(US_OUTDATE);
+ }
+
+ /*
+ * If the mode of a file does not match the local mode, the
+ * whole file is updated. This is done both to insure that
+ * a bogus version of the file has not been installed and to
+ * avoid having to handle weird cases of chmod'ing symlinks
+ * and such.
+ */
+ if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
+ debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n",
+ lmode, rmode);
+ return(US_OUTDATE);
+ }
+
+ if (statp->st_size != size) {
+ debugmsg(DM_MISC, "size does not match (%d != %d).\n",
+ (int) statp->st_size, size);
+ return(US_OUTDATE);
+ }
+
+ /*
+ * Check ownership
+ */
+ if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
+ if (!IS_ON(opts, DO_NUMCHKOWNER)) {
+ /* Check by string compare */
+ if (strcmp(owner, getusername(statp->st_uid,
+ target, opts)) != 0) {
+ debugmsg(DM_MISC,
+ "owner does not match (%s != %s).\n",
+ getusername(statp->st_uid,
+ target, opts), owner);
+ return(US_OUTDATE);
+ }
+ } else {
+ /*
+ * Check numerically.
+ * Allow negative numbers.
+ */
+ while (*owner && !isdigit(*owner) && (*owner != '-'))
+ ++owner;
+ if (owner && atoi(owner) != statp->st_uid) {
+ debugmsg(DM_MISC,
+ "owner does not match (%d != %s).\n",
+ statp->st_uid, owner);
+ return(US_OUTDATE);
+ }
+ }
+ }
+
+ if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
+ if (!IS_ON(opts, DO_NUMCHKGROUP)) {
+ /* Check by string compare */
+ if (strcmp(group, getgroupname(statp->st_gid,
+ target, opts)) != 0) {
+ debugmsg(DM_MISC,
+ "group does not match (%s != %s).\n",
+ getgroupname(statp->st_gid,
+ target, opts), group);
+ return(US_OUTDATE);
+ }
+ } else {
+ /* Check numerically */
+ /* Allow negative gid */
+ while (*group && !isdigit(*group) && (*group != '-'))
+ ++group;
+ if (group && atoi(group) != statp->st_gid) {
+ debugmsg(DM_MISC,
+ "group does not match (%d != %s).\n",
+ statp->st_gid, group);
+ return(US_OUTDATE);
+ }
+ }
+ }
+
+ return(US_NOTHING);
+}
+
+/*
+ * Stat a file
+ */
+static int dostat(file, statbuf, opts)
+ char *file;
+ struct stat *statbuf;
+ opt_t opts;
+{
+ int s;
+
+ if (IS_ON(opts, DO_FOLLOW))
+ s = stat(file, statbuf);
+ else
+ s = lstat(file, statbuf);
+
+ if (s < 0)
+ error("%s: %s failed: %s", file,
+ IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
+ return(s);
+}
+
+/*
+ * Transfer the file or directory in target[].
+ * rname is the name of the file on the remote host.
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing happened.
+ * Return > 0 if anything is updated.
+ */
+static int sendit(rname, opts, destdir)
+ char *rname;
+ opt_t opts;
+ int destdir;
+{
+ static struct stat stb;
+ extern struct subcmd *subcmds;
+ char *user, *group;
+ int u, len;
+ int didupdate = 0;
+
+ /*
+ * Remove possible accidental newline
+ */
+ len = strlen(rname);
+ if (len > 0 && rname[len-1] == '\n')
+ rname[len-1] = CNULL;
+
+ if (checkfilename(rname) != 0)
+ return(-1);
+
+ debugmsg(DM_CALL, "sendit(%s, 0x%x) called\n", rname, opts);
+
+ if (except(target))
+ return(0);
+
+ if (dostat(target, &stb, opts) < 0)
+ return(-1);
+
+ /*
+ * Does rname need updating?
+ */
+ u = update(rname, opts, &stb);
+ debugmsg(DM_MISC, "sendit(%s, 0x%x): update status of %s is %d\n",
+ rname, opts, target, u);
+
+ /*
+ * Don't need to update the file, but we may need to save hardlink
+ * info.
+ */
+ if (u == US_NOTHING) {
+ if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
+ (void) linkinfo(&stb);
+ return(0);
+ }
+
+ /*
+ * File mode needs changing
+ */
+ if (u == US_MODE) {
+ if (IS_ON(opts, DO_VERIFY)) {
+ message(MT_INFO, "%s: need to chmod to %04o",
+ target, stb.st_mode & 07777);
+ runspecial(target, opts, rname, destdir);
+ return(1);
+ }
+ message(MT_CHANGE, "%s: chmod to %04o",
+ target, stb.st_mode & 07777);
+ (void) sendcmd(C_CHMOD, "%o %04o %s",
+ opts, stb.st_mode & 07777, rname);
+ (void) response();
+ return(1);
+ }
+
+ user = getusername(stb.st_uid, target, opts);
+ group = getgroupname(stb.st_gid, target, opts);
+
+ /*
+ * No entry - need to install
+ */
+ if (u == US_NOENT) {
+ if (IS_ON(opts, DO_VERIFY)) {
+ message(MT_INFO, "%s: need to install", target);
+ runspecial(target, opts, rname, destdir);
+ return(1);
+ }
+ if (!IS_ON(opts, DO_QUIET))
+ message(MT_CHANGE, "%s: installing", target);
+ FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
+ }
+
+ /*
+ * Handle special file types, including directories and symlinks
+ */
+ if (S_ISDIR(stb.st_mode)) {
+ if (senddir(rname, opts, &stb, user, group, destdir) > 0)
+ didupdate = 1;
+ } else if (S_ISLNK(stb.st_mode)) {
+ if (u != US_NOENT)
+ FLAG_ON(opts, DO_COMPARE);
+ /*
+ * Since we always send link info to the server
+ * so the server can determine if the remote link
+ * is correct, we never get any acknowledge meant
+ * from the server whether the link was really
+ * updated or not.
+ */
+ (void) sendlink(rname, opts, &stb, user, group, destdir);
+ } else if (S_ISREG(stb.st_mode)) {
+ if (u == US_OUTDATE) {
+ if (IS_ON(opts, DO_VERIFY)) {
+ message(MT_INFO, "%s: need to update", target);
+ runspecial(target, opts, rname, destdir);
+ return(1);
+ }
+ if (!IS_ON(opts, DO_QUIET))
+ message(MT_CHANGE, "%s: updating", target);
+ }
+ if (sendfile(rname, opts, &stb, user, group, destdir) == 0)
+ didupdate = 1;
+ } else
+ error("%s: unknown file type", target);
+
+ return(didupdate);
+}
+
+/*
+ * Remove temporary files and do any cleanup operations before exiting.
+ */
+extern void cleanup()
+{
+ char *file;
+#ifdef USE_STATDB
+ extern char statfile[];
+
+ (void) unlink(statfile);
+#endif
+
+ if (file = getnotifyfile())
+ (void) unlink(file);
+}
+
+/*
+ * 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).
+ *
+ * Return < 0 on error.
+ * Return 0 if nothing updated.
+ * Return > 0 if something was updated.
+ */
+extern int install(src, dest, ddir, destdir, opts)
+ char *src, *dest;
+ int ddir, destdir;
+ opt_t opts;
+{
+ static char destcopy[MAXPATHLEN];
+ char *rname;
+ int didupdate = 0;
+
+ debugmsg(DM_CALL,
+ "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%d) start\n",
+ (src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
+ /*
+ * Save source name
+ */
+ if (IS_ON(opts, DO_WHOLE))
+ source[0] = CNULL;
+ else
+ (void) strcpy(source, src);
+
+ if (dest == NULL) {
+ FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
+ dest = src;
+ }
+
+ if (checkfilename(dest) != 0)
+ return(-1);
+
+ if (nflag || debug) {
+ static char buff[BUFSIZ];
+ char *cp;
+
+ cp = getondistoptlist(opts);
+ (void) sprintf(buff, "%s%s%s %s %s",
+ IS_ON(opts, DO_VERIFY) ? "verify" : "install",
+ (cp) ? " -o" : "", (cp) ? cp : "",
+ src, dest);
+ if (nflag) {
+ printf("%s\n", buff);
+ return(0);
+ } else
+ debugmsg(DM_MISC, "%s\n", buff);
+ }
+
+ rname = exptilde(target, src);
+ if (rname == NULL)
+ return(-1);
+ ptarget = target;
+ while (*ptarget)
+ ptarget++;
+ /*
+ * 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 (IS_ON(opts, DO_WHOLE)) {
+ while (*rname == '/')
+ rname++;
+ ddir = 1;
+ destdir = 1;
+ } else {
+ rname = strrchr(target, '/');
+ /* Check if no '/' or target ends in '/' */
+ if (rname == NULL ||
+ rname+1 == NULL ||
+ *(rname+1) == CNULL)
+ rname = target;
+ else
+ rname++;
+ }
+
+ debugmsg(DM_MISC,
+ "install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n",
+ target, source, rname, dest, destdir, ddir);
+
+ /*
+ * Pass the destination file/directory name to remote.
+ */
+ if (ddir)
+ (void) sendcmd(C_DIRTARGET, "%o %s", opts, dest);
+ else
+ (void) sendcmd(C_TARGET, "%o %s", opts, dest);
+ if (response() < 0)
+ return(-1);
+
+ /*
+ * Save the name of the remote target destination if we are
+ * in WHOLE mode (destdir > 0) or if the source and destination
+ * are not the same. This info will be used later for maintaining
+ * hardlink info.
+ */
+ if (destdir || (src && dest && strcmp(src, dest))) {
+ (void) strcpy(destcopy, dest);
+ Tdest = destcopy;
+ }
+
+ didupdate = sendit(rname, opts, destdir);
+ Tdest = 0;
+
+ return(didupdate);
+}