1499 lines
39 KiB
C
1499 lines
39 KiB
C
/* $NetBSD: supcmeat.c,v 1.28 2003/07/28 23:13:17 itojun 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(TREE *, void *);
|
|
static int recvone(TREE *, va_list);
|
|
static int denyone(TREE *, void *);
|
|
static int deleteone(TREE *, void *);
|
|
static int linkone(TREE *, void *);
|
|
static int execone(TREE *, void *);
|
|
static int finishone(TREE *, void *);
|
|
|
|
|
|
/* The next two routines define the fsm to support multiple fileservers
|
|
* per collection.
|
|
*/
|
|
int
|
|
getonehost(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(int *tout, int *backoff, long int *state, int *nhostsp)
|
|
{
|
|
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)
|
|
{
|
|
TREE *t;
|
|
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(TREE * t, int nhosts, int *tout)
|
|
{
|
|
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(TREE * t)
|
|
{
|
|
char relsufix[STRINGLENGTH];
|
|
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];
|
|
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(void)
|
|
{
|
|
char buf[STRINGLENGTH];
|
|
char relsufix[STRINGLENGTH];
|
|
char *p, *q;
|
|
FILE *f;
|
|
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(TREE * t, void *dummy)
|
|
{
|
|
TREE *newt;
|
|
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(TREE * t, void *v)
|
|
{
|
|
vnotify("SUP: Access denied to %s\n", t->Tname);
|
|
return (SCMOK);
|
|
}
|
|
|
|
static int
|
|
deleteone(TREE * t, void *v)
|
|
{
|
|
struct stat sbuf, pbuf;
|
|
int x;
|
|
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)
|
|
{
|
|
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(char *name, int mode, int *newp, struct stat * statp)
|
|
{
|
|
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(TREE * t, va_list ap)
|
|
{
|
|
int x = 0;
|
|
int new;
|
|
struct stat sbuf;
|
|
int *recvmore;
|
|
|
|
recvmore = va_arg(ap, int *);
|
|
/* 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(TREE * t, int new, struct stat * statp)
|
|
{ /* receive directory from network */
|
|
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(TREE * t, int new, struct stat * statp)
|
|
{ /* receive symbolic link */
|
|
char buf[STRINGLENGTH];
|
|
int n;
|
|
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;
|
|
n = -1;
|
|
if (!new && (t->Tflags & FNEW) == 0 &&
|
|
(n = readlink(t->Tname, buf, sizeof(buf) - 1)) >= 0 &&
|
|
(n == strlen(linkname)) && (strncmp(linkname, buf, n) == 0))
|
|
return (FALSE);
|
|
if (n >= 0)
|
|
t->Tname[n] = '\0';
|
|
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(TREE * t, int new, struct stat * statp)
|
|
{ /* receive file from network */
|
|
FILE *fin, *fout;
|
|
char dirpart[STRINGLENGTH], filepart[STRINGLENGTH];
|
|
char filename[STRINGLENGTH], buf[STRINGLENGTH];
|
|
struct timeval tbuf[2];
|
|
int x;
|
|
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(TREE * t, void *fv)
|
|
{ /* link to file already received */
|
|
char *fname = fv;
|
|
struct stat fbuf, sbuf;
|
|
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(TREE * t, void *v)
|
|
{ /* execute command for file */
|
|
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);
|
|
}
|
|
|
|
/* from will be 0 if reading from network */
|
|
int
|
|
copyfile(char *to, char *from)
|
|
{
|
|
int fromf, tof, istemp, x;
|
|
char dpart[STRINGLENGTH], fpart[STRINGLENGTH];
|
|
char tname[STRINGLENGTH];
|
|
static int returntrue = 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 | O_EXCL), 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 | O_EXCL), 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 | O_EXCL), 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 | O_EXCL), 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 | O_EXCL), 0600);
|
|
if (tof >= 0)
|
|
break;
|
|
#endif
|
|
/* try /tmp */
|
|
(void) sprintf(tname, "/tmp/#%d.sup", thispid);
|
|
tof = open(tname, (O_WRONLY | O_CREAT | O_TRUNC | O_EXCL), 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 | O_EXCL), 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 (returntrue)
|
|
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, 0)) == -1 ||
|
|
unlink(tname) == -1 ||
|
|
(outfd = open(tname, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL)) == -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 | O_EXCL), 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(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(TREE * t, void *fv)
|
|
{
|
|
FILE *finishfile = fv;
|
|
if ((thisC->Cflags & CFDELETE) == 0 || (t->Tflags & FUPDATE))
|
|
fprintf(finishfile, "%s\n", t->Tname);
|
|
return (SCMOK);
|
|
}
|
|
|
|
void
|
|
done(int value, char *fmt, ...)
|
|
{
|
|
char buf[STRINGLENGTH];
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
(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
|
|
goaway(char *fmt, ...)
|
|
{
|
|
char buf[STRINGLENGTH];
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
(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);
|
|
}
|