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

738 lines
22 KiB
C

/* $NetBSD: supcmain.c,v 1.21 2006/04/02 01:39:48 christos 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 -- Software Upgrade Protocol client process
*
* Usage: sup [ flags ] [ supfile ] [ collection ... ]
*
* The only required argument to sup is the name of a supfile. It
* must either be given explicitly on the command line, or the -s
* flag must be specified. If the -s flag is given, the system
* supfile will be used and a supfile command argument should not be
* specified. The list of collections is optional and if specified
* will be the only collections upgraded. The following flags affect
* all collections specified.
*
* -s "system upgrade" flag
* As described above.
*
* -t "upgrade time" flag
* When this flag is given, Sup will print the time
* that each collection was last upgraded, rather than
* performing actual upgrades.
*
* -u "utimes" flag
* When this flags is given sup will not attempt to
* restore access and modification files on the collection
* files from the server.
*
* -R "resource pause" flag
* Sup will not disable resource pausing and will not
* make filesystem space checks.
*
* -N "debug network" flag
* Sup will trace messages sent and received that
* implement the Sup network protocol.
*
* -P "debug ports" flag
* Sup will use a set of non-privileged network
* ports reserved for debugging purposes.
*
* -X "crosspatch" flag
* Sup is being run remotely with a crosspatch.
* Need to be carefull as we may be running as root
* instead of collection owner.
*
* The remaining flags affect all collections unless an explicit list
* of collections are given with the flags. Multiple flags may be
* specified together that affect the same collections. For the sake
* of convience, any flags that always affect all collections can be
* specified with flags that affect only some collections. For
* example, "sup -sde=coll1,coll2" would perform a system upgrade,
* and the first two collections would allow both file deletions and
* command executions. Note that this is not the same command as
* "sup -sde=coll1 coll2", which would perform a system upgrade of
* just the coll2 collection and would ignore the flags given for the
* coll1 collection.
*
* -a "all files" flag
* All files in the collection will be copied from
* the repository, regardless of their status on the
* current machine. Because of this, it is a very
* expensive operation and should only be done for
* small collections if data corruption is suspected
* and been confirmed. In most cases, the -o flag
* should be sufficient.
*
* -b "backup files" flag
* If the -b flag if given, or the "backup" supfile
* option is specified, the contents of regular files
* on the local system will be saved before they are
* overwritten with new data. The data will be saved
* in a subdirectory called "BACKUP" in the directory
* containing the original version of the file, in a
* file with the same non-directory part of the file
* name. The files to backup are specified by the
* list file on the repository.
*
* -B "don't backup files" flag
* The -B flag overrides and disables the -b flag and
* the "backup" supfile option.
*
* -d "delete files" flag
* Files that are no longer in the collection on the
* repository will be deleted if present on the local
* machine. This may also be specified in a supfile
* with the "delete" option.
*
* -D "don't delete files" flag
* The -D flag overrides and disables the -d flag and
* the "delete" supfile option.
*
* -e "execute files" flag
* Sup will execute commands sent from the repository
* that should be run when a file is upgraded. If
* the -e flag is omitted, Sup will print a message
* that specifies the command to execute. This may
* also be specified in a supfile with the "execute"
* option.
*
* -E "don't execute files" flag
* The -E flag overrides and disables the -e flag and
* the "execute" supfile option.
*
* -f "file listing" flag
* A "list-only" upgrade will be performed. Messages
* will be printed that indicate what would happen if
* an actual upgrade were done.
*
* -k "keep newer files" flag
* The -k flag, or "keep" supfile option, will cause
* Sup to check to see whether there is a newer file on
* the local disk before updating files. Only files
* which are newer on the repository will be updated.
*
* -K "don't keep newer files" flag
* The -K flag overrides and disables the -k flag and
* the "keep" supfile option.
*
* -l "local upgrade" flag
* Normally, Sup will not upgrade a collection if the
* repository is on the same machine. This allows
* users to run upgrades on all machines without
* having to make special checks for the repository
* machine. If the -l flag is specified, collections
* will be upgraded even if the repository is local.
*
* -m "mail" flag
* Normally, Sup used standard output for messages.
* If the -m flag if given, Sup will send mail to the
* user running Sup, or a user specified with the
* "notify" supfile option, that contains messages
* printed by Sup.
*
* -o "old files" flag
* Sup will normally only upgrade files that have
* changed on the repository since the last time an
* upgrade was performed. The -o flag, or the "old"
* supfile option, will cause Sup to check all files
* in the collection for changes instead of just the
* new ones.
*
* -O "not old files" flag
* The -O flag overrides and disables the -o flag and
* the "old" supfile option.
*
* -v "verbose" flag
* Normally, Sup will only print messages if there
* are problems. This flag causes Sup to also print
* messages during normal progress showing what Sup
* is doing.
*
**********************************************************************
* 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.6 92/08/11 12:06:59 mrt
* Merged in Brad's changes. Made resource pausing code conditional
* on MACH, rather than CMUCS. Fixed some calls to sprintf to
* return void.
* [92/08/09 mrt]
*
* Revision 1.5 92/02/08 19:01:18 mja
* Correct oldsigsys type when ANSI C.
* [92/02/08 18:59:47 mja]
*
* Revision 1.4 92/02/08 18:24:01 mja
* Added -k and -K switches.
* [92/01/17 vdelvecc]
*
* 27-Dec-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added crosspatch support (is currently ignored).
*
* 28-Jun-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added code for "release" support.
*
* 25-May-87 Doug Philips (dwp) at Carnegie-Mellon University
* Split into several files. This is the main program and
* command line processing and old history log. [V5.21]
*
* 21-May-87 Chriss Stephens (chriss) at Carnegie Mellon University
* Merged divergent CS and ECE versions. ifdeffed out the resource
* pausing code - only compiled in if CMUCS defined. [V5.21a]
*
* 20-May-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Removed support for version 3 of SUP protocol. Added changes
* to make lint happy. Added calls to new logging routines. [V5.20]
*
* 01-Apr-87 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added -R switch to reenable resource pausing, which is currently
* disabled by default. Added code to check for free disk space
* available on the target filesystem so that sup shouldn't run the
* system out of disk space as frequently. [V5.19]
*
* 19-Sep-86 Mike Accetta (mja) at Carnegie-Mellon University
* Changed default supfile name for system collections when -t
* is specified to use FILESUPTDEFAULT; added missing new-line
* in retry message. [V5.18]
*
* 21-Jun-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Missed a caller to a routine which had an extra argument added
* to it last edit. [V5.17]
*
* 07-Jun-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Changed getcoll() so that fatal errors are checked immediately
* instead of after sleeping for a little while. Changed all
* rm -rf commands to rmdir since the Mach folks keep deleting
* their root and /usr directory trees. Reversed the order of
* delete commands to that directories will possibly empty so
* that the rmdir's work. [V5.16]
*
* 30-May-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Changed temporary file names to #n.sup format. [V5.15]
*
* 19-Feb-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Moved PGMVERSION to supvers.c module. [V5.14]
*
* 06-Feb-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added check for file type before unlink when receiving a
* symbolic link. Now runs "rm -rf" if the file type is a
* directory. [V5.13]
*
* 03-Feb-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Fixed small bug in signon that didn't retry connections if an
* error occurred on the first attempt to connect. [V5.12]
*
* 26-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* New command interface. Added -bBDEO flags and "delete",
* "execute" and "old" supfile options. Changed -d to work
* correctly without implying -o. [V5.11]
*
* 21-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Fix incorrect check for supfile changing. Flush output buffers
* before restart. [V5.10]
*
* 17-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Add call to requestend() after connection errors are retried to
* free file descriptors. [V5.9]
*
* 15-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Fix SERIOUS merge error from previous edit. Added notify
* when execute command fails. [V5.8]
*
* 11-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Changed ugconvert to clear setuid/setgid bits if it doesn't use
* the user and group specified by the remote system. Changed
* execute code to invalidate collection if execute command returns
* with a non-zero exit status. Added support for execv() of
* original arguments of supfile is upgraded successfully. Changed
* copyfile to always use a temp file if possible. [V5.7]
*
* 04-Jan-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added support for fileserver busy messages and new nameserver
* protocol to support multiple repositories per collection.
* Added code to lock collections with lock files. [V5.6]
*
* 29-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Major rewrite for protocol version 4. [V4.5]
*
* 12-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Changed to check for DIFFERENT mtime (again). [V3.4]
*
* 08-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Replaced [ug]convert routines with ugconvert routine so that an
* appropriate group will be used if the default user is used.
* Changed switch parsing to allow multiple switches to be specified
* at the same time. [V3.3]
*
* 04-Dec-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added test to request a new copy of an old file that already
* exists if the mtime is different. [V3.2]
*
* 24-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added -l switch to enable upgrades from local repositories.
*
* 03-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Minor change in order -t prints so that columns line up.
*
* 22-Oct-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added code to implement retry flag and pass this on to request().
*
* 22-Sep-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Merged 4.1 and 4.2 versions together.
*
* 04-Jun-85 Steven Shafer (sas) at Carnegie-Mellon University
* Created for 4.2 BSD.
*
**********************************************************************
*/
#define MSGFILE
#include "supcdefs.h"
#if MACH
#include <sys/syscall.h>
#ifndef SYS_rpause
#define SYS_rpause (-5)
#endif
#endif
#include "supextern.h"
/*********************************************
*** G L O B A L V A R I A B L E S ***
*********************************************/
char program[] = "SUP"; /* program name for SCM messages */
int progpid = -1; /* and process id */
COLLECTION *firstC, *thisC; /* collection list pointer */
extern int dontjump; /* disable longjmp */
extern int scmdebug; /* SCM debugging flag */
int silent; /* Silent run, print only errors */
int sysflag; /* system upgrade flag */
int timeflag; /* print times flag */
int noutime; /* Don't preserve utimes */
#if MACH
int rpauseflag; /* don't disable resource pausing */
#endif /* MACH */
int xpatchflag; /* crosspatched with remote system */
int portdebug; /* network debugging ports */
int main(int, char **);
static int checkcoll(TREE *, void *);
static void doswitch(char *, TREE **, int *, int *);
static char *init(int, char **);
/*************************************
*** M A I N R O U T I N E ***
*************************************/
int
main(int argc, char **argv)
{
char *progname, *supfname;
int restart, sfdev = 0, sfino = 0;
time_t sfmtime = 0;
struct stat sbuf;
struct sigaction ign;
/* initialize global variables */
pgmversion = PGMVERSION;/* export version number */
server = FALSE; /* export that we're not a server */
collname = NULL; /* no current collection yet */
dontjump = TRUE; /* clear setjmp buffer */
progname = estrdup(argv[0]);
supfname = init(argc, argv);
restart = -1; /* don't make restart checks */
if (*progname == '/' && *supfname == '/') {
if (stat(supfname, &sbuf) < 0) {
logerr("Can't stat supfile %s", supfname);
} else {
sfdev = sbuf.st_dev;
sfino = sbuf.st_ino;
sfmtime = sbuf.st_mtime;
restart = 0;
}
}
if (timeflag) {
for (thisC = firstC; thisC; thisC = thisC->Cnext)
prtime();
} else {
/* ignore network pipe signals */
ign.sa_handler = SIG_IGN;
ign.sa_flags = 0;
sigemptyset(&ign.sa_mask);
(void) sigaction(SIGPIPE, &ign, NULL);
getnams(); /* find unknown repositories */
for (thisC = firstC; thisC; thisC = thisC->Cnext) {
getcoll(); /* upgrade each collection */
if (restart == 0) {
if (stat(supfname, &sbuf) < 0)
logerr("Can't stat supfile %s",
supfname);
else if (sfmtime != sbuf.st_mtime ||
sfino != sbuf.st_ino ||
sfdev != sbuf.st_dev) {
restart = 1;
break;
}
}
}
endpwent(); /* close /etc/passwd */
(void) endgrent(); /* close /etc/group */
if (restart == 1) {
int fd;
if (!silent)
loginfo("SUP Restarting %s with new supfile %s",
progname, supfname);
for (fd = getdtablesize(); fd > 3; fd--)
(void) close(fd);
execv(progname, argv);
logquit(1, "Restart failed");
}
}
while ((thisC = firstC) != NULL) {
firstC = firstC->Cnext;
free(thisC->Cname);
Tfree(&thisC->Chtree);
free(thisC->Cbase);
if (thisC->Chbase)
free(thisC->Chbase);
if (thisC->Cprefix)
free(thisC->Cprefix);
if (thisC->Crelease)
free(thisC->Crelease);
if (thisC->Cnotify)
free(thisC->Cnotify);
if (thisC->Clogin)
free(thisC->Clogin);
if (thisC->Cpswd)
free(thisC->Cpswd);
if (thisC->Ccrypt)
free(thisC->Ccrypt);
free(thisC);
}
exit(0);
}
/*****************************************
*** I N I T I A L I Z A T I O N ***
*****************************************/
/* Set up collection list from supfile. Check all fields except
* hostname to be sure they make sense.
*/
#define Toflags Tflags
#define Taflags Tmode
#define Twant Tuid
#define Tcount Tgid
static void
doswitch(char *argp, TREE ** collTp, int *oflagsp, int *aflagsp)
{
TREE *t;
char *coll;
int oflags, aflags;
oflags = aflags = 0;
for (;;) {
switch (*argp) {
default:
logerr("Invalid flag '%c' ignored", *argp);
break;
case '\0':
case '=':
if (*argp++ == '\0' || *argp == '\0') {
*oflagsp |= oflags;
*oflagsp &= ~aflags;
*aflagsp |= aflags;
*aflagsp &= ~oflags;
return;
}
do {
coll = nxtarg(&argp, ", \t");
t = Tinsert(collTp, coll, TRUE);
t->Toflags |= oflags;
t->Toflags &= ~aflags;
t->Taflags |= aflags;
t->Taflags &= ~oflags;
argp = skipover(argp, ", \t");
} while (*argp);
return;
case 'N':
scmdebug++;
break;
case 'P':
portdebug = TRUE;
break;
case 'R':
#if MACH
rpauseflag = TRUE;
#endif /* MACH */
break;
case 'X':
xpatchflag = TRUE;
break;
case 'S':
silent = TRUE;
break;
case 's':
sysflag = TRUE;
break;
case 't':
timeflag = TRUE;
break;
case 'a':
oflags |= CFALL;
break;
case 'b':
oflags |= CFBACKUP;
aflags &= ~CFBACKUP;
break;
case 'B':
oflags &= ~CFBACKUP;
aflags |= CFBACKUP;
break;
case 'd':
oflags |= CFDELETE;
aflags &= ~CFDELETE;
break;
case 'D':
oflags &= ~CFDELETE;
aflags |= CFDELETE;
break;
case 'e':
oflags |= CFEXECUTE;
aflags &= ~CFEXECUTE;
break;
case 'E':
oflags &= ~CFEXECUTE;
aflags |= CFEXECUTE;
break;
case 'f':
oflags |= CFLIST;
break;
case 'k':
oflags |= CFKEEP;
aflags &= ~CFKEEP;
break;
case 'K':
oflags &= ~CFKEEP;
aflags |= CFKEEP;
break;
case 'l':
oflags |= CFLOCAL;
break;
case 'm':
oflags |= CFMAIL;
break;
case 'o':
oflags |= CFOLD;
aflags &= ~CFOLD;
break;
case 'O':
oflags &= ~CFOLD;
aflags |= CFOLD;
break;
case 'u':
noutime = TRUE;
break;
case 'v':
oflags |= CFVERBOSE;
break;
case 'z':
oflags |= CFCOMPRESS;
break;
case 'Z':
oflags &= ~CFCOMPRESS;
break;
}
argp++;
}
}
static char *
init(int argc, char **argv)
{
char buf[STRINGLENGTH], *p;
const char *u;
char username[STRINGLENGTH];
char *supfname, *arg;
COLLECTION *c, *lastC;
FILE *f;
int bogus;
struct passwd *pw;
TREE *t;
TREE *collT; /* collections we are interested in */
time_t timenow; /* startup time */
int oflags, aflags;
int cwant;
#ifdef MACH
void (*oldsigsys) ();
#endif /* MACH */
sysflag = FALSE; /* not system upgrade */
timeflag = FALSE; /* don't print times */
#if MACH
rpauseflag = FALSE; /* don't disable resource pausing */
#endif /* MACH */
xpatchflag = FALSE; /* not normally crosspatched */
scmdebug = 0; /* level zero, no SCM debugging */
portdebug = FALSE; /* no debugging ports */
collT = NULL;
oflags = aflags = 0;
while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
doswitch(&argv[1][1], &collT, &oflags, &aflags);
--argc;
argv++;
}
if (argc == 1 && !sysflag)
logquit(1, "Need either -s or supfile");
#if MACH
oldsigsys = signal(SIGSYS, SIG_IGN);
if (rpauseflag != TRUE)
if (syscall(SYS_rpause, ENOSPC, RPAUSE_ALL, RPAUSE_DISABLE) < 0)
rpauseflag = TRUE;
(void) signal(SIGSYS, oldsigsys);
#endif /* MACH */
if (sysflag)
(void) sprintf(supfname = buf,
timeflag ? FILESUPTDEFAULT : FILESUPDEFAULT,
DEFDIR);
else {
supfname = argv[1];
if (strcmp(supfname, "-") == 0)
supfname = "";
--argc;
argv++;
}
cwant = argc > 1;
while (argc > 1) {
t = Tinsert(&collT, argv[1], TRUE);
t->Twant = TRUE;
--argc;
argv++;
}
if ((u = getlogin()) ||
((pw = getpwuid((int) getuid())) && (u = pw->pw_name)))
(void) strcpy(username, u);
else
*username = '\0';
if (*supfname) {
f = fopen(supfname, "r");
if (f == NULL)
logquit(1, "Can't open supfile %s", supfname);
} else
f = stdin;
firstC = NULL;
lastC = NULL;
bogus = FALSE;
while ((p = read_line(f, NULL, NULL, NULL, 0)) != NULL) {
if (index("#;:", *p))
continue;
arg = nxtarg(&p, " \t");
if (*arg == '\0') {
logerr("Missing collection name in supfile");
bogus = TRUE;
continue;
}
if (cwant) {
TREE *mytree;
if ((mytree = Tsearch(collT, arg)) == NULL)
continue;
mytree->Tcount++;
}
c = (COLLECTION *) malloc(sizeof(COLLECTION));
if (c == NULL) {
logerr("Cannot allocate memory");
exit(1);
}
if (firstC == NULL)
firstC = c;
if (lastC != NULL)
lastC->Cnext = c;
lastC = c;
if (parsecoll(c, arg, p) < 0) {
bogus = TRUE;
continue;
}
c->Cflags |= oflags;
c->Cflags &= ~aflags;
if ((t = Tsearch(collT, c->Cname)) != NULL) {
c->Cflags |= t->Toflags;
c->Cflags &= ~t->Taflags;
}
if ((c->Cflags & CFMAIL) && c->Cnotify == NULL) {
if (*username == '\0')
logerr("User unknown, notification disabled");
else
c->Cnotify = estrdup(username);
}
if (c->Cbase == NULL) {
(void) sprintf(buf, FILEBASEDEFAULT, c->Cname);
c->Cbase = estrdup(buf);
}
}
if (bogus)
logquit(1, "Aborted due to supfile errors");
if (f != stdin)
(void) fclose(f);
if (cwant)
(void) Tprocess(collT, checkcoll, NULL);
Tfree(&collT);
if (firstC == NULL)
logquit(1, "No collections to upgrade");
timenow = time((time_t *) NULL);
if (*supfname == '\0')
p = "standard input";
else if (sysflag)
p = "system software";
else
(void) sprintf(p = buf, "file %s", supfname);
if (!silent)
loginfo("SUP %d.%d (%s) for %s at %s", PROTOVERSION, PGMVERSION,
scmversion, p, fmttime(timenow));
return (estrdup(supfname));
}
static int
checkcoll(TREE * t, void *dummy)
{
if (!t->Twant)
return (SCMOK);
if (t->Tcount == 0)
logerr("Collection %s not found", t->Tname);
if (t->Tcount > 1)
logerr("Collection %s found more than once", t->Tname);
return (SCMOK);
}