NetBSD/usr.sbin/sup/source/supcmeat.c

1511 lines
40 KiB
C

/* $NetBSD: supcmeat.c,v 1.21 1999/08/02 05:36:05 erh Exp $ */
/*
* Copyright (c) 1992 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
* sup "meat" routines
**********************************************************************
* HISTORY
*
* 7-July-93 Nate Williams at Montana State University
* Modified SUP to use gzip based compression when sending files
* across the network to save BandWidth
*
* Revision 1.16 92/09/09 22:04:51 mrt
* Really added bww's recvone changes this time.
* Added code to support non-crypting version of sup.
* [92/09/01 mrt]
*
* Revision 1.15 92/08/11 12:07:09 mrt
* Added support to add release to FILEWHEN name.
* Updated variable arguemnt list usage - bww
* Updated recvone() to take a va_list - bww
* Changed conditional for rpausing code from CMUCS to MACH
* [92/07/24 mrt]
*
* Revision 1.14 92/02/08 18:24:12 mja
* Only apply "keep" mode when local file is strictly newer
* otherwise allow update as before if necessary.
* [92/02/08 18:09:00 mja]
*
* Added support for -k (keep) option to needone(). Rewrote and
* commented other parts of needone().
* [92/01/17 vdelvecc]
*
* Revision 1.13 91/05/16 14:49:41 ern
* Add timestap to fileserver.
* Drop day of the week from 5 messages.
* [91/05/16 14:47:53 ern]
*
* Revision 1.12 89/08/23 14:55:44 gm0w
* Changed msgf routines to msg routines.
* [89/08/23 gm0w]
*
* Revision 1.11 89/08/03 19:49:10 mja
* Updated to use v*printf() in place of _doprnt().
* [89/04/19 mja]
*
* Revision 1.10 89/06/18 14:41:27 gm0w
* Fixed up some notify messages of errors to use "SUP:" prefix.
* [89/06/18 gm0w]
*
* Revision 1.9 89/06/10 15:12:17 gm0w
* Changed to always use rename to install targets. This breaks hard
* links and recreates those known to sup, other links will be orphaned.
* [89/06/10 gm0w]
*
* Revision 1.8 89/05/24 15:04:23 gm0w
* Added code to check for EINVAL from FSPARAM ioctl for disk
* space check failures when the ioctl is not implemented.
* [89/05/24 gm0w]
*
* Revision 1.7 89/01/16 18:22:28 gm0w
* Changed needone() to check that mode of files match before
* setting update if times also match.
* [89/01/16 gm0w]
*
* 10-Feb-88 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added timeout to backoff.
*
* 27-Dec-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added crosspatch support.
*
* 09-Sep-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added code to be less verbose when updating files that have
* already been successfully upgraded.
*
* 28-Jun-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added code for "release" support.
*
* 26-May-87 Doug Philips (dwp) at Carnegie-Mellon University
* Converted to end connection with more information.
* Added done routine. Modified goaway routine to free old
* goawayreason.
*
* 26-May-87 Doug Philips (dwp) at Carnegie-Mellon University
* Use computeBackoff from scm instead of doing it ourselves.
*
* 25-May-87 Doug Philips (dwp) at Carnegie-Mellon University
* Split off from sup.c and reindented goaway calls.
*
**********************************************************************
*/
#include "supcdefs.h"
#include "supextern.h"
#include <sys/param.h>
#include <sys/wait.h>
TREE *lastT; /* last filenames in collection */
jmp_buf sjbuf; /* jump location for network errors */
int dontjump; /* flag to void sjbuf */
int cancompress=FALSE; /* Can we do compression? */
int docompress=FALSE; /* Do we do compression? */
extern COLLECTION *thisC; /* collection list pointer */
extern int rpauseflag; /* don't disable resource pausing */
extern int portdebug; /* network debugging ports */
extern int noutime; /* don't set utimes */
/*************************************************
*** U P G R A D E C O L L E C T I O N ***
*************************************************/
static int needone __P((TREE *, void *));
static int recvone __P((TREE *, va_list));
static int denyone __P((TREE *, void *));
static int deleteone __P((TREE *, void *));
static int linkone __P((TREE *, void *));
static int execone __P((TREE *, void *));
static int finishone __P((TREE *, void *));
/* The next two routines define the fsm to support multiple fileservers
* per collection.
*/
int getonehost (t,v)
register TREE *t;
void *v;
{
long *state = v;
if (t->Tflags != *state)
return (SCMOK);
if (*state != 0 && t->Tmode == SCMEOF) {
t->Tflags = 0;
return (SCMOK);
}
if (*state == 2)
t->Tflags--;
else
t->Tflags++;
thisC->Chost = t;
return (SCMEOF);
}
TREE *getcollhost (tout,backoff,state,nhostsp)
int *tout,*backoff,*nhostsp;
long *state;
{
static long laststate = 0;
static int nhosts = 0;
if (*state != laststate) {
*nhostsp = nhosts;
laststate = *state;
nhosts = 0;
}
if (Tprocess (thisC->Chtree, getonehost, state) == SCMEOF) {
if (*state != 0 && nhosts == 0 && !dobackoff (tout,backoff))
return (NULL);
nhosts++;
return (thisC->Chost);
}
if (nhosts == 0)
return (NULL);
if (*state == 2)
(*state)--;
else
(*state)++;
return (getcollhost (tout,backoff,state,nhostsp));
}
/* Upgrade a collection from the file server on the appropriate
* host machine.
*/
void getcoll (void)
{
register TREE *t;
register int x;
int tout,backoff,nhosts;
long state;
collname = thisC->Cname;
tout = thisC->Ctimeout;
lastT = NULL;
backoff = 2;
state = 0;
nhosts = 0;
for (;;) {
t = getcollhost (&tout,&backoff,&state,&nhosts);
if (t == NULL) {
finishup (SCMEOF);
notify ((char *)NULL);
return;
}
t->Tmode = SCMEOF;
dontjump = FALSE;
if (!setjmp (sjbuf) && !signon (t,nhosts,&tout) && !setup (t))
break;
(void) requestend ();
}
dontjump = FALSE;
if (setjmp (sjbuf))
x = SCMERR;
else {
suplogin ();
listfiles ();
recvfiles ();
x = SCMOK;
}
if (thisC->Clockfd >= 0) {
(void) close (thisC->Clockfd);
thisC->Clockfd = -1;
}
finishup (x);
notify ((char *)NULL);
}
/*** Sign on to file server ***/
int signon (t,nhosts,tout)
register TREE *t;
int nhosts;
int *tout;
{
register int x;
int timeout;
time_t tloc;
if ((thisC->Cflags&CFLOCAL) == 0 && thishost (thisC->Chost->Tname)) {
vnotify ("SUP: Skipping local collection %s\n",collname);
t->Tmode = SCMEOF;
return (TRUE);
}
if (nhosts == 1)
timeout = *tout;
else
timeout = 0;
x = request (portdebug?DEBUGFPORT:FILEPORT,
thisC->Chost->Tname,&timeout);
if (nhosts == 1)
*tout = timeout;
if (x != SCMOK) {
if (nhosts) {
notify ("SUP: Can't connect to host %s\n",
thisC->Chost->Tname);
t->Tmode = SCMEOF;
} else
t->Tmode = SCMOK;
return (TRUE);
}
xpatch = FALSE;
x = msgsignon (); /* signon to fileserver */
if (x != SCMOK)
goaway ("Error sending signon request to fileserver");
x = msgsignonack (); /* receive signon ack from fileserver */
if (x != SCMOK)
goaway ("Error reading signon reply from fileserver");
tloc = time ((time_t *)NULL);
vnotify ("SUP Fileserver %d.%d (%s) %d on %s at %.8s\n",
protver,pgmver,scmver,fspid,remotehost(),ctime (&tloc) + 11);
free (scmver);
scmver = NULL;
if (protver < 4) {
dontjump = TRUE;
goaway ("Fileserver sup protocol version is obsolete.");
notify ("SUP: This version of sup can only communicate with a fileserver using at least\n");
notify ("SUP: version 4 of the sup network protocol. You should either run a newer\n");
notify ("SUP: version of the sup fileserver or find an older version of sup.\n");
t->Tmode = SCMEOF;
return (TRUE);
}
/* If protocol is > 7 then try compression */
if (protver > 7) {
cancompress = TRUE;
}
return (FALSE);
}
/*** Tell file server what to connect to ***/
int setup (t)
register TREE *t;
{
char relsufix[STRINGLENGTH];
register int x;
struct stat sbuf;
if (chdir (thisC->Cbase) < 0)
goaway ("Can't change to base directory %s",thisC->Cbase);
if (stat ("sup",&sbuf) < 0) {
(void) mkdir ("sup",0755);
if (stat("sup",&sbuf) < 0)
goaway ("Can't create directory %s/sup",thisC->Cbase);
vnotify ("SUP Created directory %s/sup\n",thisC->Cbase);
}
if (thisC->Cprefix && chdir (thisC->Cprefix) < 0)
goaway ("Can't change to %s from base directory %s",
thisC->Cprefix,thisC->Cbase);
if (stat (".",&sbuf) < 0)
goaway ("Can't stat %s directory %s",
thisC->Cprefix?"prefix":"base",
thisC->Cprefix?thisC->Cprefix:thisC->Cbase);
if (thisC->Cprefix) (void) chdir (thisC->Cbase);
/* read time of last upgrade from when file */
if ((thisC->Cflags&CFURELSUF) && thisC->Crelease)
(void) sprintf (relsufix,".%s",thisC->Crelease);
else
relsufix[0] = '\0';
lasttime = getwhen(collname,relsufix);
/* setup for msgsetup */
basedir = thisC->Chbase;
basedev = sbuf.st_dev;
baseino = sbuf.st_ino;
listonly = (thisC->Cflags&CFLIST);
newonly = ((thisC->Cflags&(CFALL|CFDELETE|CFOLD)) == 0);
release = thisC->Crelease;
x = msgsetup ();
if (x != SCMOK)
goaway ("Error sending setup request to file server");
x = msgsetupack ();
if (x != SCMOK)
goaway ("Error reading setup reply from file server");
if (setupack == FSETUPOK) {
/* Test encryption */
if (netcrypt (thisC->Ccrypt) != SCMOK)
goaway ("Running non-crypting sup");
crypttest = CRYPTTEST;
x = msgcrypt ();
if (x != SCMOK)
goaway ("Error sending encryption test request");
x = msgcryptok ();
if (x == SCMEOF)
goaway ("Data encryption test failed");
if (x != SCMOK)
goaway ("Error reading encryption test reply");
return (FALSE);
}
switch (setupack) {
case FSETUPSAME:
notify ("SUP: Attempt to upgrade from same host to same directory\n");
done (FDONESRVERROR,"Overwrite error");
case FSETUPHOST:
notify ("SUP: This host has no permission to access %s\n",
collname);
done (FDONESRVERROR,"Permission denied");
case FSETUPOLD:
notify ("SUP: This version of SUP is too old for the fileserver\n");
done (FDONESRVERROR,"Obsolete client");
case FSETUPRELEASE:
notify ("SUP: Invalid release %s for collection %s\n",
release == NULL ? DEFRELEASE : release,collname);
done (FDONESRVERROR,"Invalid release");
case FSETUPBUSY:
vnotify ("SUP Fileserver is currently busy\n");
t->Tmode = SCMOK;
doneack = FDONESRVERROR;
donereason = "Fileserver is busy";
(void) netcrypt ((char *)NULL);
(void) msgdone ();
return (TRUE);
default:
goaway ("Unrecognized file server setup status %d",setupack);
}
/* NOTREACHED */
return FALSE;
}
/*** Tell file server what account to use ***/
void suplogin (void)
{
char buf[STRINGLENGTH];
register int f,x;
/* lock collection if desired */
(void) sprintf (buf,FILELOCK,collname);
f = open (buf,O_RDONLY,0);
if (f >= 0) {
#if defined(LOCK_EX)
# define TESTLOCK(f) flock(f, LOCK_EX|LOCK_NB)
# define SHARELOCK(f) flock(f, LOCK_SH|LOCK_NB)
# define WAITLOCK(f) flock(f, LOCK_EX)
#elif defined(F_LOCK)
# define TESTLOCK(f) lockf(f, F_TLOCK, 0)
# define SHARELOCK(f) 1
# define WAITLOCK(f) lockf(f, F_LOCK, 0)
#else
# define TESTLOCK(f) (close(f), f = -1, 1)
# define SHARELOCK(f) 1
# define WAITLOCK(f) 1
#endif
if (TESTLOCK(f) < 0) {
if (errno != EWOULDBLOCK && errno != EAGAIN) {
(void) close(f);
goaway ("Can't lock collection %s",collname);
}
if (SHARELOCK(f) < 0) {
(void) close (f);
if (errno == EWOULDBLOCK && errno != EAGAIN)
goaway ("Collection %s is locked by another sup",collname);
goaway ("Can't lock collection %s",collname);
}
vnotify ("SUP Waiting for exclusive access lock\n");
if (WAITLOCK(f) < 0) {
(void) close (f);
goaway ("Can't lock collection %s",collname);
}
}
thisC->Clockfd = f;
vnotify ("SUP Locked collection %s for exclusive access\n",collname);
}
logcrypt = (char *) NULL;
loguser = thisC->Clogin;
logpswd = thisC->Cpswd;
#ifndef CRYPTING /* Define CRYPTING for backwards compatibility with old supfileservers */
if (thisC->Clogin != (char *) NULL) /* othewise we only encrypt if there is a login id */
#endif /* CRYPTING */
{
logcrypt = CRYPTTEST;
(void) netcrypt (PSWDCRYPT); /* encrypt password data */
}
x = msglogin ();
#ifndef CRYPTING
if (thisC->Clogin != (char *) NULL)
#endif
(void) netcrypt ((char *)NULL); /* turn off encryption */
if (x != SCMOK)
goaway ("Error sending login request to file server");
x = msglogack ();
if (x != SCMOK)
goaway ("Error reading login reply from file server");
if (logack == FLOGNG) {
notify ("SUP: %s\n",logerror);
free (logerror);
logerror = NULL;
notify ("SUP: Improper login to %s account",
thisC->Clogin ? thisC->Clogin : "default");
done (FDONESRVERROR,"Improper login");
}
if (netcrypt (thisC->Ccrypt) != SCMOK) /* restore encryption */
goaway("Running non-crypting sup");
}
/*
* send list of files that we are not interested in. receive list of
* files that are on the repository that could be upgraded. Find the
* ones that we need. Receive the list of files that the server could
* not access. Delete any files that have been upgraded in the past
* which are no longer on the repository.
*/
void listfiles ()
{
char buf[STRINGLENGTH];
char relsufix[STRINGLENGTH];
register char *p,*q;
register FILE *f;
register int x;
if ((thisC->Cflags&CFURELSUF) && release)
(void) sprintf (relsufix,".%s",release);
else
relsufix[0] = '\0';
(void) sprintf (buf,FILELAST,collname,relsufix);
f = fopen (buf,"r");
if (f) {
while ((p = fgets (buf,STRINGLENGTH,f))) {
if ((q = index (p,'\n'))) *q = '\0';
if (index ("#;:",*p)) continue;
(void) Tinsert (&lastT,p,FALSE);
}
(void) fclose (f);
}
refuseT = NULL;
(void) sprintf (buf,FILEREFUSE,collname);
f = fopen (buf,"r");
if (f) {
while ((p = fgets (buf,STRINGLENGTH,f))) {
if ((q = index (p,'\n'))) *q = '\0';
if (index ("#;:",*p)) continue;
(void) Tinsert (&refuseT,p,FALSE);
}
(void) fclose (f);
}
vnotify ("SUP Requesting changes since %s",ctime (&lasttime) + 4);
x = msgrefuse ();
if (x != SCMOK)
goaway ("Error sending refuse list to file server");
listT = NULL;
x = msglist ();
if (x != SCMOK)
goaway ("Error reading file list from file server");
if (thisC->Cprefix) (void) chdir (thisC->Cprefix);
needT = NULL;
(void) Tprocess (listT,needone, NULL);
Tfree (&listT);
x = msgneed ();
if (x != SCMOK)
goaway ("Error sending needed files list to file server");
Tfree (&needT);
denyT = NULL;
x = msgdeny ();
if (x != SCMOK)
goaway ("Error reading denied files list from file server");
if (thisC->Cflags&CFVERBOSE)
(void) Tprocess (denyT,denyone, NULL);
Tfree (&denyT);
if (thisC->Cflags&(CFALL|CFDELETE|CFOLD))
(void) Trprocess (lastT,deleteone, NULL);
Tfree (&refuseT);
}
static int needone (t, dummy)
register TREE *t;
void *dummy;
{
register TREE *newt;
register int exists, fetch;
struct stat sbuf;
newt = Tinsert (&lastT,t->Tname,TRUE);
newt->Tflags |= FUPDATE;
fetch = TRUE;
if ((thisC->Cflags&CFALL) == 0) {
if ((t->Tflags&FNEW) == 0 && (thisC->Cflags&CFOLD) == 0)
return (SCMOK);
if (S_ISLNK(t->Tmode))
exists = (lstat (t->Tname,&sbuf) == 0);
else
exists = (stat (t->Tname,&sbuf) == 0);
/* This is moderately complicated:
If the file is the wrong type or doesn't exist, we need to
fetch the whole file. If the file is a special file, we
rely solely on the server: if the file changed, we do an
update; otherwise nothing. If the file is a normal file,
we check timestamps. If we are in "keep" mode, we fetch if
the file on the server is newer, and do nothing otherwise.
Otherwise, we fetch if the timestamp is wrong; if the file
changed on the server but the timestamp is right, we do an
update. (Update refers to updating stat information, i.e.
timestamp, owner, mode bits, etc.) */
if (exists && (sbuf.st_mode&S_IFMT) == (t->Tmode&S_IFMT)) {
if (!S_ISREG(t->Tmode))
if (t->Tflags&FNEW)
fetch = FALSE;
else return (SCMOK);
else if ((thisC->Cflags&CFKEEP) &&
sbuf.st_mtime > t->Tmtime)
return (SCMOK);
else if (sbuf.st_mtime == t->Tmtime) {
if (t->Tflags&FNEW)
fetch = FALSE;
else return (SCMOK);
}
}
}
/* If we get this far, we're either doing an update or a full fetch. */
newt = Tinsert (&needT,t->Tname,TRUE);
if (!fetch && S_ISREG(t->Tmode))
newt->Tflags |= FUPDATE;
return (SCMOK);
}
static int denyone (t, v)
register TREE *t;
void *v;
{
vnotify ("SUP: Access denied to %s\n",t->Tname);
return (SCMOK);
}
static int deleteone (t, v)
TREE *t;
void *v;
{
struct stat sbuf, pbuf;
register int x;
register char *name = t->Tname;
char pname[MAXPATHLEN];
if (t->Tflags&FUPDATE) /* in current upgrade list */
return (SCMOK);
if (lstat(name,&sbuf) < 0) /* doesn't exist */
return (SCMOK);
/* is it a symbolic link ? */
if (S_ISLNK(sbuf.st_mode)) {
if (Tlookup (refuseT,name)) {
vnotify ("SUP Would not delete symbolic link %s\n",
name);
return (SCMOK);
}
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would delete symbolic link %s\n",name);
return (SCMOK);
}
if ((thisC->Cflags&CFDELETE) == 0) {
notify ("SUP Please delete symbolic link %s\n",name);
t->Tflags |= FUPDATE;
return (SCMOK);
}
x = unlink (name);
if (x < 0) {
notify ("SUP: Unable to delete symbolic link %s\n",
name);
t->Tflags |= FUPDATE;
return (SCMOK);
}
vnotify ("SUP Deleted symbolic link %s\n",name);
return (SCMOK);
}
/* is it a directory ? */
if (S_ISDIR(sbuf.st_mode)) {
if (Tlookup (refuseT,name)) {
vnotify ("SUP Would not delete directory %s\n",name);
return (SCMOK);
}
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would delete directory %s\n",name);
return (SCMOK);
}
if ((thisC->Cflags&CFDELETE) == 0) {
notify ("SUP Please delete directory %s\n",name);
t->Tflags |= FUPDATE;
return (SCMOK);
}
if (rmdir (name) < 0) {
(void) chmod (name,sbuf.st_mode|S_IRWXU);
if (strlen(name) < MAXPATHLEN - 3) {
sprintf (pname,"%s/..",name);
if (stat (pname,&pbuf) == 0)
(void) chmod (pname,pbuf.st_mode|S_IRWXU);
}
runp ("rm","rm","-rf",name,0);
}
if (lstat(name,&sbuf) == 0) {
notify ("SUP: Unable to delete directory %s\n",name);
t->Tflags |= FUPDATE;
return (SCMOK);
}
vnotify ("SUP Deleted directory %s\n",name);
return (SCMOK);
}
/* it is a file */
if (Tlookup (refuseT,name)) {
vnotify ("SUP Would not delete file %s\n",name);
return (SCMOK);
}
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would delete file %s\n",name);
return (SCMOK);
}
if ((thisC->Cflags&CFDELETE) == 0) {
notify ("SUP Please delete file %s\n",name);
t->Tflags |= FUPDATE;
return (SCMOK);
}
x = unlink (name);
if (x < 0) {
notify ("SUP: Unable to delete file %s\n",name);
t->Tflags |= FUPDATE;
return (SCMOK);
}
vnotify ("SUP Deleted file %s\n",name);
return (SCMOK);
}
/***************************************
*** R E C E I V E F I L E S ***
***************************************/
/* Note for these routines, return code SCMOK generally means
* NETWORK communication is OK; it does not mean that the current
* file was correctly received and stored. If a file gets messed
* up, too bad, just print a message and go on to the next one;
* but if the network gets messed up, the whole sup program loses
* badly and best just stop the program as soon as possible.
*/
void recvfiles (void)
{
register int x;
int recvmore;
/* Does the protocol support compression */
if (cancompress) {
/* Check for compression on sending files */
docompress = (thisC->Cflags&CFCOMPRESS);
x = msgcompress();
if ( x != SCMOK)
goaway ("Error sending compression check to server");
if (docompress)
vnotify("SUP Using compressed file transfer\n");
}
recvmore = TRUE;
upgradeT = NULL;
do {
x = msgsend ();
if (x != SCMOK)
goaway ("Error sending receive file request to file server");
(void) Tinsert (&upgradeT,(char *)NULL,FALSE);
x = msgrecv (recvone,&recvmore);
if (x != SCMOK)
goaway ("Error receiving file from file server");
Tfree (&upgradeT);
} while (recvmore);
}
/* prepare the target, if necessary */
int prepare (name,mode,newp,statp)
char *name;
int mode,*newp;
struct stat *statp;
{
register char *type;
char pname[MAXPATHLEN];
struct stat pbuf;
if (mode == S_IFLNK)
*newp = (lstat (name,statp) < 0);
else
*newp = (stat (name,statp) < 0);
if (*newp) {
if (thisC->Cflags&CFLIST)
return (FALSE);
if (establishdir (name))
return (TRUE);
return (FALSE);
}
if (mode == (statp->st_mode&S_IFMT))
return (FALSE);
*newp = TRUE;
switch (statp->st_mode&S_IFMT) {
case S_IFDIR:
type = "directory";
break;
case S_IFLNK:
type = "symbolic link";
break;
case S_IFREG:
type = "regular file";
break;
default:
type = "unknown file";
break;
}
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would remove %s %s\n",type,name);
return (FALSE);
}
if (S_ISDIR(statp->st_mode)) {
if (rmdir (name) < 0) {
(void) chmod (name,statp->st_mode|S_IRWXU);
if (strlen(name) < MAXPATHLEN - 3) {
sprintf(pname,"%s/..",name);
if (stat(pname,&pbuf) == 0)
(void) chmod (pname,pbuf.st_mode|S_IRWXU);
}
runp ("rm","rm","-rf",name,0);
}
} else
(void) unlink (name);
if (stat (name,statp) < 0) {
vnotify ("SUP Removed %s %s\n",type,name);
return (FALSE);
}
notify ("SUP: Couldn't remove %s %s\n",type,name);
return (TRUE);
}
static int
recvone (t, ap)
register TREE *t;
va_list ap;
{
int x = 0;
int new;
struct stat sbuf;
int *recvmore;
recvmore = va_arg(ap, int *);
va_end(ap);
/* check for end of file list */
if (t == NULL) {
*recvmore = FALSE;
return (SCMOK);
}
/* check for failed access at fileserver */
if (t->Tmode == 0) {
notify ("SUP: File server unable to transfer file %s\n",
t->Tname);
thisC->Cnogood = TRUE;
return (SCMOK);
}
if (prepare (t->Tname,t->Tmode&S_IFMT,&new,&sbuf)) {
notify ("SUP: Can't prepare path for %s\n",t->Tname);
if (S_ISREG(t->Tmode)) {
x = readskip (); /* skip over file */
if (x != SCMOK)
goaway ("Can't skip file transfer");
}
thisC->Cnogood = TRUE;
return (SCMOK);
}
/* make file mode specific changes */
switch (t->Tmode&S_IFMT) {
case S_IFDIR:
x = recvdir (t,new,&sbuf);
break;
case S_IFLNK:
x = recvsym (t,new,&sbuf);
break;
case S_IFREG:
x = recvreg (t,new,&sbuf);
break;
default:
goaway ("Unknown file type %o\n",t->Tmode&S_IFMT);
}
if (x) {
thisC->Cnogood = TRUE;
return (SCMOK);
}
if (S_ISREG(t->Tmode))
(void) Tprocess (t->Tlink,linkone,t->Tname);
(void) Tprocess (t->Texec,execone, NULL);
return (SCMOK);
}
int recvdir (t,new,statp) /* receive directory from network */
register TREE *t;
register int new;
register struct stat *statp;
{
struct timeval tbuf[2];
if (new) {
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would create directory %s\n",t->Tname);
return (FALSE);
}
if (makedir(t->Tname, 0755, statp) == -1) {
vnotify ("SUP: Can't create directory %s\n", t->Tname);
return TRUE;
}
}
if ((t->Tflags&FNOACCT) == 0) {
/* convert user and group names to local ids */
ugconvert (t->Tuser,t->Tgroup,&t->Tuid,&t->Tgid,&t->Tmode);
}
if (!new && (t->Tflags&FNEW) == 0 && statp->st_mtime == t->Tmtime) {
if (t->Tflags&FNOACCT)
return (FALSE);
if (statp->st_uid == t->Tuid && statp->st_gid == t->Tgid)
return (FALSE);
}
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would update directory %s\n",t->Tname);
return (FALSE);
}
if ((t->Tflags&FNOACCT) == 0) {
(void) chown (t->Tname,t->Tuid,t->Tgid);
(void) chmod (t->Tname,t->Tmode&S_IMODE);
}
tbuf[0].tv_sec = time((time_t *)NULL); tbuf[0].tv_usec = 0;
tbuf[1].tv_sec = t->Tmtime; tbuf[1].tv_usec = 0;
if (!noutime)
(void) utimes (t->Tname,tbuf);
vnotify ("SUP %s directory %s\n",new?"Created":"Updated",t->Tname);
return (FALSE);
}
int recvsym (t,new,statp) /* receive symbolic link */
register TREE *t;
register int new;
register struct stat *statp;
{
char buf[STRINGLENGTH];
int n;
register char *linkname;
if (t->Tlink == NULL || t->Tlink->Tname == NULL) {
notify ("SUP: Missing linkname for symbolic link %s\n",
t->Tname);
return (TRUE);
}
linkname = t->Tlink->Tname;
if (!new && (t->Tflags&FNEW) == 0 &&
(n = readlink (t->Tname,buf,sizeof(buf))) >= 0 &&
(n == strlen (linkname)) && (strncmp (linkname,buf,n) == 0))
return (FALSE);
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would %s symbolic link %s to %s\n",
new?"create":"update",t->Tname,linkname);
return (FALSE);
}
if (!new)
(void) unlink (t->Tname);
if (symlink (linkname,t->Tname) < 0 || lstat(t->Tname,statp) < 0) {
notify ("SUP: Unable to create symbolic link %s\n",t->Tname);
return (TRUE);
}
vnotify ("SUP Created symbolic link %s to %s\n",t->Tname,linkname);
return (FALSE);
}
int recvreg (t,new,statp) /* receive file from network */
register TREE *t;
register int new;
register struct stat *statp;
{
register FILE *fin,*fout;
char dirpart[STRINGLENGTH],filepart[STRINGLENGTH];
char filename[STRINGLENGTH],buf[STRINGLENGTH];
struct timeval tbuf[2];
register int x;
register char *p;
if (t->Tflags&FUPDATE) {
if ((t->Tflags&FNOACCT) == 0) {
/* convert user and group names to local ids */
ugconvert (t->Tuser,t->Tgroup,&t->Tuid,&t->Tgid,
&t->Tmode);
}
if (!new && (t->Tflags&FNEW) == 0 &&
statp->st_mtime == t->Tmtime) {
if (t->Tflags&FNOACCT)
return (FALSE);
if (statp->st_uid == t->Tuid &&
statp->st_gid == t->Tgid)
return (FALSE);
}
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would update file %s\n",t->Tname);
return (FALSE);
}
vnotify ("SUP Updating file %s\n",t->Tname);
if ((t->Tflags&FNOACCT) == 0) {
(void) chown (t->Tname,t->Tuid,t->Tgid);
(void) chmod (t->Tname,t->Tmode&S_IMODE);
}
tbuf[0].tv_sec = time((time_t *)NULL); tbuf[0].tv_usec = 0;
tbuf[1].tv_sec = t->Tmtime; tbuf[1].tv_usec = 0;
if (!noutime)
(void) utimes (t->Tname,tbuf);
return (FALSE);
}
if (thisC->Cflags&CFLIST) {
if (new)
p = "create";
else if (statp->st_mtime < t->Tmtime)
p = "receive new";
else if (statp->st_mtime > t->Tmtime)
p = "receive old";
else
p = "receive";
vnotify ("SUP Would %s file %s\n",p,t->Tname);
return (FALSE);
}
vnotify ("SUP Receiving file %s\n",t->Tname);
if (!new && S_ISREG(t->Tmode) &&
(t->Tflags&FBACKUP) && (thisC->Cflags&CFBACKUP)) {
fin = fopen (t->Tname,"r"); /* create backup */
if (fin == NULL) {
x = readskip (); /* skip over file */
if (x != SCMOK)
goaway ("Can't skip file transfer");
notify ("SUP: Can't open %s to create backup\n",
t->Tname);
return (TRUE); /* mark upgrade as nogood */
}
path (t->Tname,dirpart,filepart);
(void) sprintf (filename,FILEBACKUP,dirpart,filepart);
fout = fopen (filename,"w");
if (fout == NULL) {
(void) sprintf (buf,FILEBKDIR,dirpart);
(void) mkdir (buf,0755);
fout = fopen (filename,"w");
}
if (fout == NULL) {
x = readskip (); /* skip over file */
if (x != SCMOK)
goaway ("Can't skip file transfer");
notify ("SUP: Can't create %s for backup\n",filename);
(void) fclose (fin);
return (TRUE);
}
ffilecopy (fin,fout);
(void) fclose (fin);
(void) fclose (fout);
vnotify ("SUP Backup of %s created\n", t->Tname);
}
x = copyfile (t->Tname,(char *)NULL);
if (x)
return (TRUE);
if ((t->Tflags&FNOACCT) == 0) {
/* convert user and group names to local ids */
ugconvert (t->Tuser,t->Tgroup,&t->Tuid,&t->Tgid,&t->Tmode);
(void) chown (t->Tname,t->Tuid,t->Tgid);
(void) chmod (t->Tname,t->Tmode&S_IMODE);
}
tbuf[0].tv_sec = time((time_t *)NULL); tbuf[0].tv_usec = 0;
tbuf[1].tv_sec = t->Tmtime; tbuf[1].tv_usec = 0;
if (!noutime)
(void) utimes (t->Tname,tbuf);
return (FALSE);
}
static int linkone (t,fv) /* link to file already received */
register TREE *t;
void *fv;
{
register char *fname = fv;
struct stat fbuf,sbuf;
register char *name = t->Tname;
int new,x;
char *type;
if (stat(fname,&fbuf) < 0) { /* source file */
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would link %s to %s\n",name,fname);
return (SCMOK);
}
notify ("SUP: Can't link %s to missing file %s\n",name,fname);
thisC->Cnogood = TRUE;
return (SCMOK);
}
if (prepare (name,S_IFREG,&new,&sbuf)) {
notify ("SUP: Can't prepare path for link %s\n",name);
thisC->Cnogood = TRUE;
return (SCMOK);
}
if (!new && (t->Tflags&FNEW) == 0 &&
fbuf.st_dev == sbuf.st_dev && fbuf.st_ino == sbuf.st_ino)
return (SCMOK);
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would link %s to %s\n",name,fname);
return (SCMOK);
}
(void) unlink (name);
type = "";
if ((x = link (fname,name)) < 0) {
type = "symbolic ";
x = symlink (fname,name);
}
if (x < 0 || lstat(name,&sbuf) < 0) {
notify ("SUP: Unable to create %slink %s\n",type,name);
return (TRUE);
}
vnotify ("SUP Created %slink %s to %s\n",type,name,fname);
return (SCMOK);
}
static int execone (t, v) /* execute command for file */
register TREE *t;
void *v;
{
int w;
if (thisC->Cflags&CFLIST) {
vnotify ("SUP Would execute %s\n",t->Tname);
return (SCMOK);
}
if ((thisC->Cflags&CFEXECUTE) == 0) {
notify ("SUP Please execute %s\n",t->Tname);
return (SCMOK);
}
vnotify ("SUP Executing %s\n",t->Tname);
w = system (t->Tname);
if (WIFEXITED(w) && WEXITSTATUS(w) != 0) {
notify ("SUP: Execute command returned failure status %#o\n",
WEXITSTATUS(w));
thisC->Cnogood = TRUE;
} else if (WIFSIGNALED(w)) {
notify ("SUP: Execute command killed by signal %d\n",
WTERMSIG(w));
thisC->Cnogood = TRUE;
} else if (WIFSTOPPED(w)) {
notify ("SUP: Execute command stopped by signal %d\n",
WSTOPSIG(w));
thisC->Cnogood = TRUE;
}
return (SCMOK);
}
int copyfile (to,from)
char *to;
char *from; /* 0 if reading from network */
{
register int fromf,tof,istemp,x;
char dpart[STRINGLENGTH],fpart[STRINGLENGTH];
char tname[STRINGLENGTH];
static int true = 1;
static int thispid = 0; /* process id # */
if (from) { /* reading file */
fromf = open (from,O_RDONLY,0);
if (fromf < 0) {
notify ("SUP: Can't open %s to copy to %s: %s\n",
from,to,errmsg (-1));
return (TRUE);
}
} else /* reading network */
fromf = -1;
istemp = TRUE; /* try to create temp file */
lockout (TRUE); /* block interrupts */
if (thispid == 0) thispid = getpid ();
/* Now try hard to find a temp file name. Try VERY hard. */
for (;;) {
/* try destination directory */
path (to,dpart,fpart);
(void) sprintf (tname,"%s/#%d.sup",dpart,thispid);
tof = open (tname,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (tof >= 0) break;
/* try sup directory */
if (thisC->Cprefix) (void) chdir (thisC->Cbase);
(void) sprintf (tname,"sup/#%d.sup",thispid);
tof = open (tname,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (tof >= 0) {
if (thisC->Cprefix) (void) chdir (thisC->Cprefix);
break;
}
/* try base directory */
(void) sprintf (tname,"#%d.sup",thispid);
tof = open (tname,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (thisC->Cprefix) (void) chdir (thisC->Cprefix);
if (tof >= 0) break;
#ifdef VAR_TMP
/* try /var/tmp */
(void) sprintf (tname,"/var/tmp/#%d.sup",thispid);
tof = open (tname,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (tof >= 0) break;
#else
/* try /usr/tmp */
(void) sprintf (tname,"/usr/tmp/#%d.sup",thispid);
tof = open (tname,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (tof >= 0) break;
#endif
/* try /tmp */
(void) sprintf (tname,"/tmp/#%d.sup",thispid);
tof = open (tname,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (tof >= 0) break;
istemp = FALSE;
/* give up: try to create output file */
if (!docompress)
tof = open (to,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (tof >= 0) break;
/* no luck */
notify ("SUP: Can't create %s or temp file for it\n",to);
lockout (FALSE);
if (fromf >= 0)
(void) close (fromf);
else {
x = readskip ();
if (x != SCMOK)
goaway ("Can't skip file transfer");
}
if (true)
return (TRUE);
}
if (fromf >= 0) { /* read file */
x = filecopy (fromf,tof);
(void) close (fromf);
(void) close (tof);
if (x < 0) {
notify ("SUP: Error in copying %s to %s\n",from,to);
if (istemp) (void) unlink (tname);
lockout (FALSE);
return (TRUE);
}
} else { /* read network */
#if MACH
if (!rpauseflag) {
int fsize;
struct fsparam fsp;
x = prereadcount (&fsize);
if (x != SCMOK) {
if (istemp) (void) unlink (tname);
lockout (FALSE);
x = readskip ();
if (x != SCMOK)
goaway ("Can't skip file transfer");
goaway ("Error in server space check");
logquit (1,"Error in server space check");
}
errno = 0;
if (ioctl (tof,FIOCFSPARAM,(char *)&fsp) < 0 &&
errno != EINVAL) {
if (istemp) (void) unlink (tname);
lockout (FALSE);
x = readskip ();
if (x != SCMOK)
goaway ("Can't skip file transfer");
goaway ("Error in disk space check");
logquit (1,"Error in disk space check");
}
if (errno == 0) {
fsize = (fsize + 1023) / 1024;
x = fsp.fsp_size * MAX (fsp.fsp_minfree,1) / 100;
fsp.fsp_free -= x;
if (fsize > MAX (fsp.fsp_free,0)) {
if (istemp) (void) unlink (tname);
lockout (FALSE);
x = readskip ();
if (x != SCMOK)
goaway ("Can't skip file transfer");
goaway ("No disk space for file %s", to);
logquit (1,"No disk space for file %s",to);
}
}
}
#endif /* MACH */
x = readfile (tof);
(void) close (tof);
if (x != SCMOK) {
if (istemp) (void) unlink (tname);
lockout (FALSE);
goaway ("Error in receiving %s\n",to);
}
}
if (!istemp) { /* no temp file used */
lockout (FALSE);
return (FALSE);
}
/*
** If the file is compressed, uncompress it in place. We open the
** temp file for reading, unlink the file, and then open the same
** file again for writing. Then we pipe through gzip. When
** finished the temp file contains the uncompressed version and we
** can continue as before.
**
** Since sup prefers to write close to the original file the
** benefits of atomic updates probably outweigh the cost of the
** extra filecopy which occurs when the temp file is on a different
** filesystem from the original.
*/
if (docompress) {
char *av[4];
int ac = 0;
int infd = -1;
int outfd = -1;
av[ac++] = "gzip";
av[ac++] = "-d";
av[ac++] = NULL;
if ( (infd = open(tname, O_RDONLY)) == -1 ||
unlink(tname) == -1 ||
(outfd = open(tname, O_WRONLY|O_CREAT|O_TRUNC)) == -1 ||
runiofd( av, infd, outfd, 2 ) != 0 ) {
notify("SUP: Error in uncompressing file %s (%s)\n",
to, tname );
(void) unlink (tname);
if ( infd != -1 ) (void) close (infd);
if ( outfd != -1 ) (void) close (outfd);
lockout(FALSE);
return(TRUE);
}
(void) close(infd);
(void) close(outfd);
}
/* move to destination */
if (rename (tname,to) == 0) {
(void) unlink (tname);
lockout (FALSE);
return (FALSE);
}
fromf = open (tname,O_RDONLY,0);
if (fromf < 0) {
notify ("SUP: Error in moving temp file to %s: %s\n",
to,errmsg (-1));
(void) unlink (tname);
lockout (FALSE);
return (TRUE);
}
tof = open (to,(O_WRONLY|O_CREAT|O_TRUNC),0600);
if (tof < 0) {
(void) close (fromf);
notify ("SUP: Can't create %s from temp file: %s\n",
to,errmsg (-1));
(void) unlink (tname);
lockout (FALSE);
return (TRUE);
}
x = filecopy (fromf,tof);
(void) close (fromf);
(void) close (tof);
(void) unlink (tname);
lockout (FALSE);
if (x < 0) {
notify ("SUP: Error in storing data in %s\n",to);
return (TRUE);
}
return (FALSE);
}
/*** Finish connection with file server ***/
void finishup (x)
int x;
{
char tname[STRINGLENGTH],fname[STRINGLENGTH];
char relsufix[STRINGLENGTH];
char collrelname[STRINGLENGTH];
time_t tloc;
FILE *finishfile; /* record of all filenames */
if ((thisC->Cflags&CFURELSUF) && release) {
(void) sprintf (relsufix,".%s",release);
(void) sprintf (collrelname,"%s-%s",collname,release);
} else {
relsufix[0] = '\0';
(void) strcpy (collrelname,collname);
}
dontjump = TRUE; /* once here, no more longjmp */
(void) netcrypt ((char *)NULL);
if (protver < 6) {
/* done with server */
if (x == SCMOK)
goaway ((char *)NULL);
(void) requestend ();
}
tloc = time ((time_t *)NULL);
if (x != SCMOK) {
notify ("SUP: Upgrade of %s aborted at %s",
collrelname,ctime (&tloc) + 4);
Tfree (&lastT);
if (protver < 6) return;
/* if we've not been blown off, make sure he is! */
if (x != SCMEOF)
goaway ("Aborted");
(void) requestend ();
return;
}
if (thisC->Cnogood) {
notify ("SUP: Upgrade of %s completed with errors at %s",
collrelname,ctime (&tloc) + 4);
notify ("SUP: Upgrade time will not be updated\n");
Tfree (&lastT);
if (protver < 6) return;
done (FDONEUSRERROR,"Completed with errors");
(void) requestend ();
return;
}
if (thisC->Cprefix) (void) chdir (thisC->Cbase);
vnotify ("SUP Upgrade of %s completed at %s",
collrelname,ctime (&tloc) + 4);
if (thisC->Cflags&CFLIST) {
Tfree (&lastT);
if (protver < 6) return;
done (FDONEDONTLOG,"List only");
(void) requestend ();
return;
}
(void) sprintf (fname,FILEWHEN,collname,relsufix);
if (establishdir (fname)) {
notify ("SUP: Can't create directory for upgrade timestamp\n");
Tfree (&lastT);
if (protver < 6) return;
done (FDONEUSRERROR,"Couldn't timestamp");
(void) requestend ();
return;
}
if (!putwhen(fname, scantime)) {
notify ("SUP: Can't record current time in %s: %s\n",
fname,errmsg (-1));
Tfree (&lastT);
if (protver < 6) return;
done (FDONEUSRERROR,"Couldn't timestamp");
(void) requestend ();
return;
}
if (protver >= 6) {
/* At this point we have let the server go */
/* "I'm sorry, we've had to let you go" */
done (FDONESUCCESS,"Success");
(void) requestend ();
}
(void) sprintf (tname,FILELASTTEMP,collname,relsufix);
finishfile = fopen (tname,"w");
if (finishfile == NULL) {
notify ("SUP: Can't record list of all files in %s\n",tname);
Tfree (&lastT);
return;
}
(void) Tprocess (lastT,finishone,finishfile);
(void) fclose (finishfile);
(void) sprintf (fname,FILELAST,collname,relsufix);
if (rename (tname,fname) < 0)
notify ("SUP: Can't change %s to %s\n",tname,fname);
(void) unlink (tname);
Tfree (&lastT);
}
int finishone (t,fv)
TREE *t;
void *fv;
{
FILE *finishfile = fv;
if ((thisC->Cflags&CFDELETE) == 0 || (t->Tflags&FUPDATE))
fprintf (finishfile,"%s\n",t->Tname);
return (SCMOK);
}
void
#ifdef __STDC__
done (int value,char *fmt,...)
#else
/*VARARGS*//*ARGSUSED*/
done (va_alist)
va_dcl
#endif
{
char buf[STRINGLENGTH];
va_list ap;
#ifdef __STDC__
va_start(ap,fmt);
#else
int value;
char *fmt;
va_start(ap);
value = va_arg(ap,int);
fmt = va_arg(ap,char *);
#endif
(void) netcrypt ((char *)NULL);
if (fmt)
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (protver < 6) {
if (goawayreason)
free (goawayreason);
goawayreason = (fmt) ? salloc (buf) : (char *)NULL;
(void) msggoaway ();
}
else {
doneack = value;
donereason = (fmt) ? buf : (char *)NULL;
(void) msgdone ();
}
if (!dontjump)
longjmp (sjbuf,TRUE);
}
void
#ifdef __STDC__
goaway (char *fmt,...)
#else
/*VARARGS*//*ARGSUSED*/
goaway (va_alist)
va_dcl
#endif
{
char buf[STRINGLENGTH];
va_list ap;
#ifdef __STDC__
va_start(ap,fmt);
#else
register char *fmt;
va_start(ap);
fmt = va_arg(ap,char *);
#endif
(void) netcrypt ((char *)NULL);
if (fmt) {
vsnprintf(buf, sizeof(buf), fmt, ap);
goawayreason = buf;
} else
goawayreason = NULL;
va_end(ap);
(void) msggoaway ();
if (fmt) {
if (thisC)
notify ("SUP: %s\n",buf);
else
printf ("SUP: %s\n",buf);
}
if (!dontjump)
longjmp (sjbuf,TRUE);
}