File: [local] / src / usr.bin / rdist / client.c (download)
Revision 1.2, Tue Mar 5 03:15:57 1996 UTC (28 years, 3 months ago) by dm
Branch: MAIN
Changes since 1.1: +12 -12 lines
rdist 6.1.2
|
/*
* 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.2 1996/03/05 03:15:57 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;
/*
* Don't descend into directory
*/
if (IS_ON(opts, DO_NODESCEND))
return(0);
if ((d = opendir(target)) == NULL) {
error("%s: opendir failed: %s", target, SYSERR);
return(-1);
}
/*
* 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);
if (IS_ON(opts, DO_REMOVE))
if (rmchk(opts) > 0)
++didupdate;
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);
}