#!/usr/bin/python -t # -*- mode: Python; indent-tabs-mode: nil; -*- # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import errno import fnmatch import os, sys import shutil import stat, tempfile import rpmUtils, rpmUtils.miscutils, rpmUtils.transaction compsname = 'comps.xml' ts = rpmUtils.transaction.initReadOnlyTransaction() def run(cmd): result = os.system(cmd) if result != 0: print >> sys.stderr, 'Error running command: %s ' % cmd if result > 127: return 1 return result def run_and_check(cmd): result = run(cmd) if result: sys.exit(result) def load_config_module(project): import imp name = 'Config_'+project try: (fileobj, pathname, description) = imp.find_module(name) cfg = imp.load_module(name,fileobj,pathname,description) fileobj.close() return cfg except ImportError, e: print e sys.exit(errno.EIO) def signer_gid_check(gid): if os.getgid() != gid: grpname = '(unknown)' try: import grp grpname = grp.getgrgid(gid)[0] except: print 'WARNING: Could not get group name for gid %d' % gid print 'ERROR: Change into the %s group before running this! Use "newgrp %s".' % (grpname, grpname) sys.exit(errno.EPERM) def sign_key_check(keyname,gpgpath=None): if gpgpath: homearg = '--homedir %s' % gpgpath else: homearg = '' gpg = os.popen("gpg %s --list-secret-keys --with-colons '%s'" % (homearg,keyname)) gpgout = gpg.readlines() seckeys = 0 for l in gpgout: if l.startswith('sec:'): seckeys += 1 rv = gpg.close() if (seckeys!=1 or rv): print 'ERROR: GPG secret key check failed: %s' % keyname sys.exit(errno.EPERM) def sign_key_check_all(cfg): # Check old single sign-key, if defined. if hasattr(cfg,'signkeyname'): gpgpath = os.popen('rpm --eval %_gpg_path','r').read().rstrip() if (gpgpath == '%_gpg_path'): gpgpath = None sign_key_check(cfg.signkeyname,gpgpath) # Check any sign-keys defined in new config dict. if not hasattr(cfg,'rpmsigncmds'): return for d in cfg.alldists+['DEFAULT']: if d not in cfg.rpmsigncmds.keys(): continue _cmdargs = cfg.rpmsigncmds[d].split() gpgpath = None keyname = None for i in range( len(_cmdargs) ): _arg = _cmdargs[i] if (_arg=='--homedir' and (i+1)0: if nodeltas: # no deltarpms for debuginfo pkgs and srpms cmd = cmd.replace('--deltas','') if arch and hasattr(cfg,'oldpackagedirs'): dirs = cfg.oldpackagedirs.get(reldist,None) if not dirs or not isinstance(dirs,list): dirs = [] for dir in dirs: oldpackagedirs.append( dir.replace('$arch',arch) ) if not nodeltas: for path in oldpackagedirs: cmd += ' --oldpackagedirs %s' % path if os.path.exists( os.path.join(repodir,compsname) ): cmd += ' -g %s' % compsname if (not debuginfo) and os.path.exists( os.path.join(repodir,'debug') ): cmd += " -x \'*debuginfo*\'" cachedir = os.path.join(cfg.cr_cachedir,dist) if not os.path.exists(cachedir): os.makedirs(cachedir) cmd += ' -c %s' % cachedir cmd += ' %s' % repodir if run(cmd): raise Exception def _restore_repodata_dir(tmpdir,targetdir): # helper function for f in os.listdir(tmpdir): if f not in ['index.html','repoview']: continue targetfile = os.path.join(targetdir,f) if not os.path.exists(targetfile): # don't overwrite new files sourcefile = os.path.join(tmpdir,f) shutil.move(sourcefile,targetfile) def create_repository(cfg,dist,repodir,debuginfo=False,nodeltas=False,arch=None): """create/update repository metadata for a given directory and exclude debuginfo packages in a 'debug' sub-repository by default""" # Create dirs, if missing, so we would create empty backups, too. rpdata = os.path.join(repodir,'repodata') for d in [repodir,rpdata]: if not os.path.exists(d): os.makedirs(d) # Not only help createrepo < 0.4.5, but more important, create # a backup of the "repodata" directory and restore repoview files, # because it sucks to recreate them everytime from scratch. # We used to remove the entire repodata directory because of: # https://devel.linux.duke.edu/bugzilla/show_bug.cgi?id=595 tmpdir = tempfile.mkdtemp('','.push',repodir) if tmpdir == repodir: # paranoid, should never happen sys.exit(errno.EPERM) try: for f in os.listdir(rpdata): sourcefile = os.path.join(rpdata,f) targetfile = os.path.join(tmpdir,f) shutil.move(sourcefile,targetfile) _create_repository(cfg,dist,repodir,debuginfo,nodeltas,arch) _restore_repodata_dir(tmpdir,rpdata) # Remove old detached GPG sig (if any). signedrepomdfile = os.path.join(rpdata,'repomd.xml.asc') if os.path.exists(signedrepomdfile): os.remove(signedrepomdfile) shutil.rmtree(tmpdir) except: # everything is bad at this point print 'ERROR: Exception during "safe createrepo". Cleaning up for exit.' _restore_repodata_dir(tmpdir,rpdata) shutil.rmtree(tmpdir) sys.exit(errno.EPERM) def is_repo_changed(repodir): """Checks if the repository has changed and needs to be reindexed""" ref_file = os.path.join(repodir, 'repodata', 'repomd.xml') if not os.path.exists(ref_file): return True ref_mtime = os.path.getmtime(ref_file) if os.path.getmtime(repodir) > ref_mtime+1: # +1 => allow dirs to be 1s off return True ignored_dirs = ( os.path.join(repodir, 'repodata'), os.path.join(repodir, 'repoview'), os.path.join(repodir, 'debug'), ) for root, dirs, files in os.walk(repodir): next = False for ignored in ignored_dirs: if (root + "/").find(ignored + "/") == 0: next = True if next: continue for d in map(lambda x: os.path.join(root, x), dirs): if d in ignored_dirs: continue elif os.path.getmtime(d) > ref_mtime: return True for f in fnmatch.filter(files, '*.rpm'): if os.path.getmtime(os.path.join(root, f)) > ref_mtime: return True return False def get_reporoot(cfg,dist): reporoot = os.path.join(cfg.treedir,dist) if hasattr(cfg,'reporoots'): return cfg.reporoots.get(dist,reporoot) return reporoot def is_any_repo_changed(cfg,dist,reporoot=None): """Check a repository and its sub-repositories for external changes such as file removals.""" for repodir in get_std_repodirs(cfg,dist,reporoot): if not os.path.exists(repodir): return True if is_repo_changed(repodir): #print("Dist %s (%s) was changed" % (dist, repodir)) return True return False def get_std_repodirs(cfg,dist,reporoot=None): """return list of all repository directories (below reporoot) for this distribution version""" repodirs = [ srpm_repodir(cfg,dist,reporoot) ] for arch in cfg.archdict[dist]: repodirs.append( rpm_repodir(cfg,dist,arch,reporoot) ) repodirs.append( debug_repodir(cfg,dist,arch,reporoot) ) return repodirs def srpm_repodir(cfg,dist,reporoot=None): if not reporoot: reporoot = get_reporoot(cfg,dist) if dist == 'development': # hack for rawhide/releases repo path scheme srpmspath = os.path.join(reporoot,'source/SRPMS') else: # old Extras/EPEL/updates/testing scheme srpmspath = os.path.join(reporoot,'SRPMS') return srpmspath def rpm_repodir(cfg,dist,arch,reporoot=None): if not reporoot: reporoot = get_reporoot(cfg,dist) if dist == 'development': # hack for rawhide/releases repo path scheme rpmspath = os.path.join(reporoot,'$arch/os') else: # old Extras/EPEL/updates/testing scheme rpmspath = os.path.join(reporoot,'$arch') return rpmspath.replace('$arch',arch) def debug_repodir(cfg,dist,arch,reporoot=None): if not reporoot: reporoot = get_reporoot(cfg,dist) debugrpmspath = os.path.join(reporoot,'$arch/debug') return debugrpmspath.replace('$arch',arch) def make_std_repodirs(cfg,dist,reporoot=None): """create all repository directories (below reporoot) for this distribution version, if they don't exist""" for repodir in get_std_repodirs(cfg,dist,reporoot): if not os.path.exists(repodir): os.makedirs(repodir) def fix_mdcache_access(workdir,cachedir): # Work around explicit directory mode 0755 in yum/repos.py (that # is too restrictive for our extras_signers group) and an utime # access denied problem in urlgrabber (file ownership is needed, # group ownership is insufficient). # # We copy the mdcache tree to a temporary directory in order to # become the owner of all files, then remove the copied tree and fix # up directory modes. For this to work flawlessly, all files must be # readable by our group. if not os.path.exists(workdir): os.makedirs(workdir) try: tmpdir = tempfile.mkdtemp('','.mdcache.tmp',workdir) if tmpdir == workdir: # paranoid, should never happen raise Exception except OSError, e: print e sys.exit(errno.EIO) except Exception: sys.exit(errno.EIO) if os.path.isdir(cachedir): tmpcachedir = os.path.join(tmpdir,'mdcache') try: shutil.copytree(src=cachedir,dst=tmpcachedir,symlinks=True) except: print 'FATAL ERROR: mdcache copytree failed' print '%s must be writable\n%s must be readable' % (workdir, cachedir) shutil.rmtree(tmpdir) sys.exit(1) for root, dirs, files in os.walk(tmpcachedir): for file in files: fullpath = os.path.join(root,file) filemode = stat.S_IMODE( os.stat(fullpath).st_mode ) filemode |= stat.S_IWGRP os.chmod(fullpath,filemode) shutil.rmtree(cachedir) shutil.move(src=tmpcachedir,dst=cachedir) shutil.rmtree(tmpdir) def get_pkg_header(pkg): """return RPM header dict for a package""" try: hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg) except rpmUtils.RpmUtilsError: print 'WARNING: %s is BAD' % pkg raise return hdr def naevr(pkg): """return nevra from the package header""" hdr = get_pkg_header(pkg) name = hdr['name'] ver = hdr['version'] rel = hdr['release'] arch = hdr['arch'] epoch = hdr['epoch'] if epoch is None: epoch = 0 return (name, arch, epoch, ver, rel) def compareEVR(tup1,tup2): return rpmUtils.miscutils.compareEVR(tup1,tup2) def find_files(rootpath,pattern='*'): """returns list of paths in given tree which point to a file matching the fnmatch pattern""" rv = [] if not os.path.isdir(rootpath): return rv for root, dirs, files in os.walk(rootpath): for f in fnmatch.filter(files,pattern): rv.append(os.path.join(root,f)) return rv def mark_pkg(pkgrelroot,flag): fname = os.path.join(pkgrelroot,flag) if os.path.isfile(fname): return #print 'Creating %s' % fname try: f = open(fname,'w') f.close() except IOError, (err, strerr): print 'ERROR: %s: %s' % (strerr,fname) raise IOError, (err, strerr) def unmark_pkg(pkgrelroot,flag): fname = os.path.join(pkgrelroot,flag) if not os.path.isfile(fname): return #print 'Deleting %s' % fname os.remove(fname)