/* $NetBSD: supcmain.c,v 1.9 1997/11/13 01:05:30 thorpej 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. * * -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 occured 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 sucessfully. 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 #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 */ #if MACH int rpauseflag; /* don't disable resource pausing */ #endif /* MACH */ int xpatchflag; /* crosspatched with remote system */ int portdebug; /* network debugging ports */ int main __P((int, char **)); static int checkcoll __P((TREE *, void *)); static void doswitch __P((char *, TREE **, int *, int *)); static char *init __P((int, char **)); /************************************* *** M A I N R O U T I N E *** *************************************/ int main (argc, argv) int argc; char **argv; { char *progname,*supfname; int restart,sfdev = 0,sfino = 0, 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 = salloc (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 ((char *)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 (argp,collTp,oflagsp,aflagsp) char *argp; register TREE **collTp; int *oflagsp,*aflagsp; { register TREE *t; register char *coll; register 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 'v': oflags |= CFVERBOSE; break; case 'z': oflags |= CFCOMPRESS; break; case 'Z': oflags &= ~CFCOMPRESS; break; } argp++; } } static char *init (argc,argv) int argc; char **argv; { char buf[STRINGLENGTH],*p; char username[STRINGLENGTH]; register char *supfname,*arg; register COLLECTION *c,*lastC; register FILE *f; register int bogus; register struct passwd *pw; register TREE *t; TREE *collT; /* collections we are interested in */ time_t timenow; /* startup time */ int oflags,aflags; int cwant; #ifdef MACH #ifdef __STDC__ void (*oldsigsys)(); #else int (*oldsigsys)(); #endif #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 ((p = (char *)getlogin()) || ((pw = getpwuid ((int)getuid())) && (p = pw->pw_name))) (void) strcpy (username,p); 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) { if (index ("#;:",*p)) continue; arg = nxtarg (&p," \t"); if (*arg == '\0') { logerr ("Missing collection name in supfile"); bogus = TRUE; continue; } if (cwant) { register TREE *t; if ((t = Tsearch (collT,arg)) == NULL) continue; t->Tcount++; } c = (COLLECTION *) malloc (sizeof(COLLECTION)); 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 = salloc (username); } if (c->Cbase == NULL) { (void) sprintf (buf,FILEBASEDEFAULT,c->Cname); c->Cbase = salloc (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 (salloc (supfname)); } static int checkcoll (t, dummy) register 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); }