From 72b522a24f2dcde856f1d66b34476aebe13ec4fc Mon Sep 17 00:00:00 2001 From: Marcin Krol Date: Thu, 28 Jul 2016 23:38:56 +0200 Subject: [PATCH 1/1] - raw from PLD --- bin/pfa-checksign | 76 ++ bin/pfa-checktree | 299 +++++ bin/pfa-dump-locks | 11 + bin/pfa-from-incoming | 279 +++++ bin/pfa-genindex | 141 +++ bin/pfa-lintpkg | 200 ++++ bin/pfa-maintainer | 17 + bin/pfa-mvpkg | 120 ++ bin/pfa-rmpkg | 35 + bin/pfa-signpkg | 116 ++ bin/pfa-testmvpkg | 65 ++ bin/pfa-unlocktree | 17 + cgi-bin/index.py | 43 + doc/README | 3 + etc/rpmlint | 233 ++++ ftpiod/ftpiod.py | 36 + html/footer.html | 3 + html/header.html | 10 + html/index.html | 48 + html/layout.css | 82 ++ html/loggedinmenu.html | 1 + html/menufooter.html | 2 + html/qa.php | 103 ++ html/regform.html | 8 + modules/.gitignore | 1 + modules/baseftptree.py | 68 ++ modules/cmds.py | 188 +++ modules/common.py | 26 + modules/config.py | 76 ++ modules/cons.py | 73 ++ modules/ftpio.py | 92 ++ modules/ftptree.py | 561 +++++++++ modules/mailer.py | 63 + modules/sign.py | 59 + modules/user.py | 44 + modules/wwwcmds.py | 26 + modules/wwwiface.py | 77 ++ repodata/comps.xml | 2134 ++++++++++++++++++++++++++++++++++ shell/bash-completion.sh | 125 ++ shell/bash_profile | 46 + shell/bashrc | 37 + ucred/make.sh | 4 + ucred/setup.py | 4 + ucred/ucred.c | 162 +++ var/.keep | 0 wwwbin/ac-th-diff.py | 57 + wwwbin/by-group.sh | 23 + wwwbin/checkrepo.sh | 28 + wwwbin/clean-dups-archive.py | 78 ++ wwwbin/clean-dups-old.py | 69 ++ wwwbin/clean-dups.py | 127 ++ wwwbin/clean-test.sh | 16 + wwwbin/consistency-check.sh | 89 ++ wwwbin/dump-packagenames.py | 59 + wwwbin/freshness.sh | 6 + wwwbin/ftp-freshness.py | 122 ++ wwwbin/obsolete-check.sh | 46 + wwwbin/rpmcheck.sh | 66 ++ 58 files changed, 6600 insertions(+) create mode 100755 bin/pfa-checksign create mode 100755 bin/pfa-checktree create mode 100755 bin/pfa-dump-locks create mode 100755 bin/pfa-from-incoming create mode 100755 bin/pfa-genindex create mode 100755 bin/pfa-lintpkg create mode 100755 bin/pfa-maintainer create mode 100755 bin/pfa-mvpkg create mode 100755 bin/pfa-rmpkg create mode 100755 bin/pfa-signpkg create mode 100755 bin/pfa-testmvpkg create mode 100755 bin/pfa-unlocktree create mode 100755 cgi-bin/index.py create mode 100644 doc/README create mode 100644 etc/rpmlint create mode 100755 ftpiod/ftpiod.py create mode 100644 html/footer.html create mode 100644 html/header.html create mode 100644 html/index.html create mode 100644 html/layout.css create mode 100644 html/loggedinmenu.html create mode 100644 html/menufooter.html create mode 100644 html/qa.php create mode 100644 html/regform.html create mode 100644 modules/.gitignore create mode 100644 modules/baseftptree.py create mode 100644 modules/cmds.py create mode 100644 modules/common.py create mode 100644 modules/config.py create mode 100644 modules/cons.py create mode 100644 modules/ftpio.py create mode 100644 modules/ftptree.py create mode 100644 modules/mailer.py create mode 100644 modules/sign.py create mode 100644 modules/user.py create mode 100644 modules/wwwcmds.py create mode 100644 modules/wwwiface.py create mode 100644 repodata/comps.xml create mode 100644 shell/bash-completion.sh create mode 100644 shell/bash_profile create mode 100644 shell/bashrc create mode 100755 ucred/make.sh create mode 100644 ucred/setup.py create mode 100644 ucred/ucred.c create mode 100644 var/.keep create mode 100755 wwwbin/ac-th-diff.py create mode 100755 wwwbin/by-group.sh create mode 100755 wwwbin/checkrepo.sh create mode 100755 wwwbin/clean-dups-archive.py create mode 100755 wwwbin/clean-dups-old.py create mode 100755 wwwbin/clean-dups.py create mode 100755 wwwbin/clean-test.sh create mode 100755 wwwbin/consistency-check.sh create mode 100755 wwwbin/dump-packagenames.py create mode 100755 wwwbin/freshness.sh create mode 100755 wwwbin/ftp-freshness.py create mode 100755 wwwbin/obsolete-check.sh create mode 100755 wwwbin/rpmcheck.sh diff --git a/bin/pfa-checksign b/bin/pfa-checksign new file mode 100755 index 0000000..14060e2 --- /dev/null +++ b/bin/pfa-checksign @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +import getopt +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +import getpass +from common import checkdir +import ftpio +from config import sign_key +from sign import is_signed, signpkgs + +try: + opts, args = getopt.getopt(sys.argv[1:], '') +except getopt.GetoptError: + print >>sys.stderr, "ERR: options error" + print >>sys.stderr, "checksign.py tree package1 [package2...]" + sys.exit(1) + +if len(args) < 1: + print >>sys.stderr, "ERR: missing tree name" + print >>sys.stderr, "checksign.py tree package1 [package2...]" + sys.exit(1) + +if sign_key == None: + print >>sys.stderr, "ERR: sign_key not defined in config" + sys.exit(1) + +treename = args[0] +packages = args[1:] + +checkdir(treename) + +ftpio.connect('sign') + +if not ftpio.lock(treename, True): + print >>sys.stderr, "ERR: %s tree already locked" % treename + sys.exit(1) + +files = [] +try: + if len(packages) < 1: + loadall = True + else: + loadall = False + + # if no files specified, grab whole tree contents + tree = ftptree.FtpTree(treename, loadall = loadall) + if loadall: + # this is hack, should be a param, not access private .loadedpkgs element + tree.mark4moving(tree.loadedpkgs) + else: + tree.mark4moving(packages) + +except ftptree.SomeError: + # In case of problems we need to unlock the tree before exiting + ftpio.unlock(treename) + sys.exit(1) + +ftpio.unlock(treename) + +print "Checking signatures of %d packages" % len(tree.loadedpkgs) +sign = [] +for pkg in tree.marked4moving: + unsigned = 0 + for file in pkg.rpmfiles(): + if not is_signed(file): + unsigned += 1 + + if unsigned != 0: + print '%s: %d files NOT signed' % (pkg.nvr, unsigned) + else: + print '%s signed' % pkg.nvr + +sys.exit(0) diff --git a/bin/pfa-checktree b/bin/pfa-checktree new file mode 100755 index 0000000..226e4df --- /dev/null +++ b/bin/pfa-checktree @@ -0,0 +1,299 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +ftptree.quietmode=True +from common import checkdir +import ftpio +import config +cval=config.value + +import curses + +if len(sys.argv) != 3: + print "ERR: wrong number of arguments" + print "check-tree.py srctree dsttree" + sys.exit(1) + +checkdir(sys.argv[1]) + +ftpio.connect('check-tree') + +if not ftpio.lock(sys.argv[1], True): + print "ERR: %s tree already locked" % sys.argv[1] + sys.exit(1) + +if not ftpio.lock(sys.argv[2], True): + ftpio.unlock(sys.argv[1]) + print "ERR: %s tree already locked" % sys.argv[2] + sys.exit(1) + +srctree=ftptree.FtpTree(sys.argv[1], loadall=True) +dsttree=ftptree.FtpTree(sys.argv[2]) + +srctree.checktree(dsttree) + +# We've loaded all interesting info, so we can continue without locks +ftpio.unlock(sys.argv[1]) +ftpio.unlock(sys.argv[2]) + +# The list(set(list)) cast is a fancy way to remove dups from a list +names=sorted(list(set([pkg.name for pkg in srctree.values()]))) + +pkgtree={} + +for n in names: + pkgtree[n]=[] + +for pkg in srctree.values(): + pkgtree[pkg.name].append(pkg) + +for key in pkgtree.keys(): + pkgtree[key]=sorted(pkgtree[key], reverse=True) + +for pkg in srctree.values(): + pkg.marked4movingpool=pkgtree[pkg.name] + +pkglist=[] + +for key in sorted(pkgtree.keys()): + pkglist=pkglist+pkgtree[key] + +DoQuit="OMG!" + +class Screen: + def __init__(self, srctreename, dsttreename, elements): + self.srctree=srctreename + self.dsttree=dsttreename + self.elements=elements + self.pkglistselected=0 + self.pkgliststartpad=0 + self.win2startpad=0 + self.window=1 + try: + # initial + self.screen=curses.initscr() + self.screen.keypad(1) + curses.start_color() + curses.noecho() + curses.cbreak() + curses.meta(1) + curses.curs_set(0) + + # Without this refresh(), nothing will show up before first getch() + self.screen.refresh() + + self.__init_colors() + self.__init_windows() + self.__bind_keys() + + try: + # Main loop + while True: + if self.window==1: #pkglist + self.__redraw_pkglist() + self.__redraw_statusbox() + elif self.window==2: + pass + curses.doupdate() + self.__handle_input() + except DoQuit: + pass + finally: + curses.echo() + curses.nocbreak() + curses.curs_set(1) + curses.endwin() + + def __bind_keys(self): + self.globalkeys=(('qQ', 'quit'), ('KEY_RESIZE', 'resize'), + ('KEY_CLOSE', 'quit')) + self.perwindowkeys=[(), ()] + self.perwindowkeys[0]=(('k', 'move_up'), ('J', 'move_pgdown'), + ('K', 'move_pgup'), (' ', 'mark'), ('j', 'move_down'), + ('KEY_DOWN', 'move_down'), ('KEY_UP', 'move_up'), + ('KEY_HOME', 'move_home'), ('KEY_END', 'move_end'), + ('KEY_NPAGE', 'move_pgdown'), ('KEY_PPAGE', 'move_pgup')) + + + def __init_colors(self): + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN) + self.COLOR_TITLEBAR=curses.color_pair(1) + curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE) + self.COLOR_STANDARD=curses.color_pair(2) + curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLUE) + self.COLOR_ERROR=curses.color_pair(3) + curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLUE) + self.COLOR_WARNING=curses.color_pair(4) + curses.init_pair(5, curses.COLOR_WHITE, curses.COLOR_RED) + self.COLOR_ERRORMSG=curses.color_pair(5) + curses.init_pair(6, curses.COLOR_WHITE, curses.COLOR_YELLOW) + self.COLOR_WARNINGMSG=curses.color_pair(6) + curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_WHITE) + self.COLOR_WHITEBG=curses.color_pair(7) + + def __init_windows(self): + self.__set_screen_coordinates() + + # Titlebar + self.titlebar = curses.newpad(1, 200) + self.titlebar.bkgdset(ord(' '), self.COLOR_TITLEBAR) + self.__redraw_titlebar() + + # Pkg list pad + self.pkglist = curses.newpad(len(self.elements), 200) + self.pkglist.bkgdset(ord(' '), self.COLOR_STANDARD) + self.pkglist.clrtobot() + + # Status bar + self.statusbar = curses.newpad(1, 200) + + # Pkg status box + self.statusbox = curses.newpad(5, 200) + self.statusbox.bkgdset(ord(' '), self.COLOR_STANDARD) + self.statusbox.clrtobot() + self.__redraw_statusbox() + + def __set_screen_coordinates(self): + self.scr_h, self.scr_w = self.screen.getmaxyx() + self.pad_h = self.scr_h-5 + self.sbar_y = self.scr_h-4 + self.sbox_y = self.scr_h-3 + + def __redraw_titlebar(self): + self.titlebar.clrtobot() +# self.titlebar.addstr(0, self.scr_w-14, "pld-ftp-admin") + self.titlebar.addstr(0, 1, 'Window #%s' % self.window) + tmp="%s => %s" % (self.srctree, self.dsttree) + self.titlebar.addstr(0, int(self.scr_w/2-len(tmp)/2)-1, tmp) + self.titlebar.addstr(0, self.scr_w-20, "Press 'h' for help.") + self.titlebar.noutrefresh(0, 0, 0, 0, 1, self.scr_w-1) + + def __redraw_statusbox(self): + self.statusbox.erase() + pkg=self.elements[self.pkglistselected] + i=0 + for error in pkg.errors: + self.statusbox.bkgdset(ord(' '), self.COLOR_ERRORMSG) + self.statusbox.addstr(i, 0, 'ERR: '+error, curses.A_BOLD) + self.statusbox.bkgdset(ord(' '), self.COLOR_STANDARD) + i+=1 + for warning in pkg.warnings: + self.statusbox.bkgdset(ord(' '), self.COLOR_WARNINGMSG) + self.statusbox.addstr(i, 0, 'WARN: '+warning, curses.A_BOLD) + self.statusbox.bkgdset(ord(' '), self.COLOR_STANDARD) + i+=1 + self.statusbox.noutrefresh(0,0,self.sbox_y,0,self.scr_h-1,self.scr_w-1) + + def __redraw_pkglist(self): + # Safety in case there's a bug somewhere + if self.pkgliststartpad>len(self.elements)-self.pad_h: + self.pkgliststartpad=len(self.elements)-self.pad_h + if self.pkgliststartpad<0: + self.pkgliststartpad=0 + + self.pkglist.erase() + for i in range(len(self.elements)): + if self.elements[i].marked4moving: + if self.elements[i].warnings: + self.pkglist.bkgdset(ord(' '), self.COLOR_WARNINGMSG) + self.pkglist.addstr(i, 0, '+', curses.A_BOLD) + else: + self.pkglist.bkgdset(ord(' '), self.COLOR_WHITEBG) + self.pkglist.addstr(i, 0, '+') + + if self.elements[i].errors: + self.pkglist.bkgdset(ord(' '), self.COLOR_ERROR) + elif self.elements[i].warnings: + self.pkglist.bkgdset(ord(' '), self.COLOR_WARNING) + else: + self.pkglist.bkgdset(ord(' '), self.COLOR_STANDARD) + if i == self.pkglistselected: + self.pkglist.addstr(i, 2, `self.elements[i]`, curses.A_REVERSE) + else: + self.pkglist.addstr(i, 2, `self.elements[i]`, curses.A_BOLD) + self.pkglist.bkgdset(ord(' '), self.COLOR_STANDARD) + self.pkglist.noutrefresh(self.pkgliststartpad,0,1,0,self.pad_h,self.scr_w-1) + + def __do_resize(self): + self.__set_screen_coordinates() + self.__redraw_titlebar() + self.__redraw_statusbox() + + def __do_move_down(self): + if 0 <= self.pkglistselected < len(self.elements)-1: + self.pkglistselected+=1 + if (self.pkgliststartpad!=len(self.elements)-self.pad_h and + self.pad_h-(self.pkglistselected-self.pkgliststartpad)<5): + self.pkgliststartpad+=1 + + def __do_move_up(self): + if 0 < self.pkglistselected <= len(self.elements)-1: + self.pkglistselected-=1 + if self.pkgliststartpad!=0 and self.pkglistselected-self.pkgliststartpad<4: + self.pkgliststartpad-=1 + + def __do_move_home(self): + self.pkglistselected=0 + self.pkgliststartpad=0 + + def __do_move_end(self): + self.pkglistselected=len(self.elements)-1 + self.pkgliststartpad=len(self.elements)-self.pad_h + + def __do_move_pgdown(self): + self.pkglistselected+=self.pad_h + if self.pkglistselected >= len(self.elements): + self.pkglistselected = len(self.elements)-1 + self.pkgliststartpad+=self.pad_h + if self.pkgliststartpad > len(self.elements)-self.pad_h: + self.pkgliststartpad=len(self.elements)-self.pad_h + + def __do_move_pgup(self): + self.pkglistselected-=self.pad_h + if self.pkglistselected < 0: + self.pkglistselected = 0 + self.pkgliststartpad-=self.pad_h + if self.pkgliststartpad < 0: + self.pkgliststartpad = 0 + + def __do_quit(self): + raise DoQuit + + def __do_mark(self): + pkg=self.elements[self.pkglistselected] + if pkg.errors: + return + elif pkg.marked4moving: + pkg.unmark4moving() + else: + pkg.mark4moving() + + def __handle_input(self): + key=self.screen.getch() + if key==27: + # Switch window + metakey=self.screen.getch() + if 01: + for bid in bids: + newcc=pkg.build[bid].requester_email + if req_email!=newcc and newcc not in cc_list: + cc_list.append(newcc) + + msg="From: %s\nTo: %s\n" % (cval['from_field'], req_email) + if cc_list: + msg=msg+"Cc: %s\n" % ", ".join(cc_list) + msg=msg+"""X-PLD-Builder: %s +References: <%s@pld.src.builder> +In-Reply-To: <%s@pld.src.builder> +Subject: %s + +""" % (cval['xpldbuilder'], req_bid, req_bid, m_subject) + + sm = os.popen("/usr/sbin/sendmail -t", "w") + + sm.write(msg) + + if files_differ: + f=open("%s/files.diff" % tmpdir, 'r') + sm.write("Difference between %s (currently in %s) and %s FILES\n" % + (pkg.noarch_arch[rpmfile], `ftptree`, arch)), + for line in f.readlines(True)[2:]: + sm.write(line) + f.close() + + sm.write('\n') + + if reqs_differ: + f=open("%s/reqs.diff" % tmpdir, 'r') + sm.write("Difference between %s (currently in %s) and %s REQS\n" % + (pkg.noarch_arch[rpmfile], `ftptree`, arch)), + for line in f.readlines(True)[2:]: + sm.write(line) + f.close() + + sm.close() + +def move_noarch(f, arch, rpmfile, dstpkg): + if dstpkg.noarch_arch.has_key(rpmfile): + os.system("LC_ALL=C rpm -qlp %s | LC_ALL=C sort > %s/files.new" % + (incoming_dir + arch + '/' + rpmfile, tmpdir)) + os.system("rpm -qRp %s | LC_ALL=C sort | LC_ALL=C uniq > %s/reqs.new" % + (incoming_dir + arch + '/' + rpmfile, tmpdir)) + + files_differ = False + reqs_differ = False + + if os.system("diff -u %s/%s.filelist %s/files.new > %s/files.diff" % + (noarchcachedir, rpmfile, tmpdir, tmpdir)): + files_differ = True + if os.system("diff -u %s/%s.reqlist %s/reqs.new > %s/reqs.diff" % + (noarchcachedir, rpmfile, tmpdir, tmpdir)): + reqs_differ = True + + if files_differ or reqs_differ: + send_noarch_msg(files_differ, reqs_differ, dstpkg, rpmfile, arch) + + rm(incoming_dir + arch + '/' + rpmfile) + else: + os.system("LC_ALL=C rpm -qlp %s | LC_ALL=C sort > %s/%s.filelist" % + (incoming_dir + arch + '/' + rpmfile, noarchcachedir, rpmfile)) + os.system("rpm -qRp %s | LC_ALL=C sort | LC_ALL=C uniq > %s/%s.reqlist" % + (incoming_dir + arch + '/' + rpmfile, noarchcachedir, rpmfile)) + if not dstpkg.files.has_key(arch): + f.write("file:noarch:%s\ninfo:noarch_arch:%s:%s\n" % (rpmfile, rpmfile, arch)) + mv(incoming_dir + arch + '/' + rpmfile, default_to + 'noarch/RPMS') + +def send_vr_msg(snvr, anvr, pkg, arch): + req_email=pkg.build[pkg.lastbid].requester_email + req_bid=pkg.lastbid + cc_list=[] + if 'logs_list' in cval: + cc_list.append(cval['logs_list']) + m_subject="NVR error: %s version or relese differ among subpackages" % snvr[0] + bids=pkg.build.keys() + if len(bids)>1: + for bid in bids: + newcc=pkg.build[bid].requester_email + if req_email!=newcc and newcc not in cc_list: + cc_list.append(newcc) + + msg="From: %s\nTo: %s\n" % (cval['from_field'], req_email) + if cc_list: + msg=msg+"Cc: %s\n" % ", ".join(cc_list) + msg=msg+"""X-PLD-Builder: %s +References: <%s@pld.src.builder> +In-Reply-To: <%s@pld.src.builder> +Subject: %s + +""" % (cval['xpldbuilder'], req_bid, req_bid, m_subject) + + sm = os.popen("/usr/sbin/sendmail -t", "w") + + sm.write(msg) + + sm.write("Difference between %s SRPM (currently in %s) and %s RPM NVR:\n\n" % + (snvr[0], `ftptree`, arch)), + sm.write("Expected (%s):\nV: %s\nR: %s\n\n" % snvr) + sm.write("RPM:\nN: %s\nV: %s\nR: %s\n" % anvr) + sm.write('\n') + + sm.close() + +# main() +try: + ftpio.connect('from-incoming-pid-%s' % os.getpid()) +except: + print "Can't get ftpiod connection" + sys.exit(1) + +ftptree = BaseFtpTree(cval['default_to']) + +if not ftpio.lock(cval['default_to']): + print "Can't get lock: %s" % cval['default_to'] + sys.exit(1) + +moved_anything = False + +for uploadinfo in findfiles(incoming_dir + 'SRPMS'): + content = getcontent(incoming_dir + 'SRPMS/' + uploadinfo) + if not content: + continue # Uploading not finished + + pkg = BasePkg(uploadinfo[:-19], content = content) + srpm = pkg.files['SRPMS'][0] + + if not os.path.exists(incoming_dir + 'SRPMS/' + srpm): + ftpio.log("%s file missing; skipping move until next round" % (srpm)) + continue + + if ftptree.has_key(`pkg`): + ftpio.log("%s already present in %s; removing older files" % (srpm, ftptree)) + rm(default_to + 'SRPMS/RPMS/' + srpm) + f = open(default_to + 'SRPMS/.metadata/' + srpm+'.info', 'a') + bid = pkg.build.keys()[0] + build = pkg.build[bid] + f.write("info:build:%s:requester:%s\ninfo:build:%s:requester_email:%s\n" + % (bid, build.requester, bid, build.requester_email)) + f.close() + else: + f = open(default_to + 'SRPMS/.metadata/' + srpm + '.info', 'w') + f.write(content) + f.close() + + mv(incoming_dir + 'SRPMS/' + srpm, default_to + 'SRPMS/RPMS') + rm(incoming_dir + 'SRPMS/' + uploadinfo) + +for arch in ftp_archs: + for uploadinfo in findfiles(incoming_dir + arch): + content = getcontent(incoming_dir + arch + '/' + uploadinfo) + if not content: + ftpio.log("%s not finished uploading" % uploadinfo) + continue # Uploading not finished + + srcpkg = BasePkg(uploadinfo[:-19], content = content) + srpm = srcpkg.files['SRPMS'][0] + + if not ftptree.has_key(`srcpkg`): + continue # We require the src.rpm to be present + + renvr = re.compile(r'(.*)-(.*)-(.*)\.[^.]*\.rpm') + srcnvr = renvr.match(srpm).groups() + + rpmfile_missing = [f for f in srcpkg.files['ARCH'] if not os.path.exists(incoming_dir + arch + '/'+f)] + if len(rpmfile_missing): + for filem in rpmfile_missing: + ftpio.log("%s file missing; skipping move until next round" % (filem)) + continue + + dstpkg = BasePkg(`srcpkg`, ftptree) + + if dstpkg.files.has_key(arch): + ftpio.log("files from %s for arch %s already present in %s; removing older files" % (`srcpkg`, arch, ftptree)) + for rpmfile in dstpkg.files[arch]: + if is_debuginfo(rpmfile): + dstfile = default_to + arch + '/debuginfo' + else: + dstfile = default_to + arch + '/RPMS' + try: + rm(dstfile + '/' + rpmfile) + except OSError, e: + l = "Removing %s problem: %s" % (dstfile + '/' + rpmfile, e) + ftpio.log(l) + print l + + f = open(default_to + 'SRPMS/.metadata/' + srpm + '.info', 'a') + for rpmfile in srcpkg.files['ARCH']: + moved_anything = True + +# Too much noise, too little use +# archnvr = renvr.match(rpmfile).groups() +# if srcnvr[1] != archnvr[1] or srcnvr[2] != archnvr[2]: +# send_vr_msg(srcnvr, archnvr, dstpkg, arch) + + if rpmfile[-11:] == '.noarch.rpm' and config.separate_noarch: + move_noarch(f, arch, rpmfile, dstpkg) + else: + if not dstpkg.files.has_key(arch): + f.write("file:%s:%s\n" % (arch, rpmfile)) + srcfile = incoming_dir + arch + '/' + rpmfile + + if is_debuginfo(rpmfile): + dstfile = default_to + arch + '/debuginfo' + else: + dstfile = default_to + arch + '/RPMS' + + try: + mv(srcfile, dstfile) + except OSError, e: + l = "Moving %s to %s problem: %s" % (srcfile, dstfile, e) + ftpio.log(l) + print l + f.close() + + rm(incoming_dir + arch + '/' + uploadinfo) + +ftpio.unlock(cval['default_to']) + +if moved_anything: + os.system("%s/pld-ftp-admin/bin/pfa-genindex --quiet test > /dev/null" % (os.getenv("HOME"))) diff --git a/bin/pfa-genindex b/bin/pfa-genindex new file mode 100755 index 0000000..dc12b64 --- /dev/null +++ b/bin/pfa-genindex @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import getopt +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +from common import checkdir +from config import ftp_dir,all_ftp_archs +import config +import ftpio + +try: + opts, args = getopt.getopt(sys.argv[1:], 'q', + [ + "quiet", "index=", + "nopoldek", "noyum", "norpmrepo", + "poldek", "yum", "rpmrepo" + ] + ) +except getopt.GetoptError: + print >>sys.stderr, "ERR: not enough parameters given" + print >>sys.stderr, "gen-indexes.py [--quiet] [--[no]poldek] [--[no]yum] [--[no]rpmrepo] tree [tree2...]" + sys.exit(1) + +do_poldek = True +do_yum = False +do_rpmrepo = False +quiet = False +# update only if changed (applicable to yum) +freshen = True + +for o, a in opts: + if o == "--nopoldek": + do_poldek = False + if o == "--noyum": + do_yum = False + if o == "--norpmrepo": + do_rpmrepo = False + + if o == "--poldek": + do_poldek = True + if o == "--yum": + do_yum = True + if o == "--rpmrepo": + do_rpmrepo = True + + if o == "-q" or o == "--quiet": + quiet = True + + if o == "--index": + do_poldek = do_yum = do_rpmrepo = False + for v in a.split(','): + if v == 'poldek': + do_poldek = True + if v == 'yum': + do_yum = True + if v == 'rpmrepo': + do_rpmrepo = True + +if not quiet: + print "poldek: %s; yum: %s; rpmrepo: %s" % (do_poldek, do_yum, do_rpmrepo) + +if not do_poldek and not do_yum and not do_rpmrepo: + print >>sys.stderr, "ERR: speciy at least one action" + sys.exit(1) + +trees = args + +for tree in trees: + checkdir(tree) + +ftpio.connect('gen-indexes') + +locked = [] + +for tree in trees: + if ftpio.lock(tree, True): + locked.append(tree) + else: + if not quiet: + print >>sys.stderr, "ERR: %s tree already locked" % tree + for i in locked: + ftpio.unlock(i) + sys.exit(1) + +home = os.environ['HOME'] + +os.umask(022) +os.nice(19) + +if do_poldek: + poldek = '%s.stat/bin/poldek-new --cachedir=%s/tmp/poldek --conf %s.stat/etc/poldek.conf --mkidxz' % (ftp_dir, home, ftp_dir) + + for tree in trees: + print '\n-------------------------- %s --------------------------' % tree + for arch in all_ftp_archs: + print '\ngenerate poldek index for %s' % arch + print '%s -s %s%s/%s/RPMS/ --mkidxz --mkidx-type pndir' % (poldek, ftp_dir, tree, arch) + os.system('%s -s %s%s/%s/RPMS/ --mkidxz --mkidx-type pndir' % (poldek, ftp_dir, tree, arch)) + if arch != 'noarch' and config.separate_debuginfo: + os.system('%s -s %s%s/%s/debuginfo/ --mkidxz --mkidx-type pndir' % (poldek, ftp_dir, tree, arch)) + +if do_yum: + os.system('%s cd %s.stat/repodata && cvs %s up comps.xml' % ("" if quiet else "set -x;", ftp_dir, "" if quiet else "-Q")) + yum = '%s.stat/bin/createrepo -d -v --update --checkts --skip-stat --workers=12 -g %s.stat/repodata/comps.xml' % (ftp_dir, ftp_dir) + comps_file = '%s.stat/repodata/comps.xml' % ftp_dir + for tree in trees: + print '\n-------------------------- %s --------------------------' % tree + cachedir = '%s/tmp/createrepo/%s' % (home, tree) + treedir = "%s%s" % (ftp_dir, tree) + for arch in all_ftp_archs: + print '\ngenerate repodata for %s using createrepo' % arch + archdir = "%s/%s" % (treedir, arch) + poldek_idx = "%s/RPMS/packages.ndir.md" % archdir + repodata_idx = "%s/RPMS/repodata/repomd.xml" % archdir + if freshen and os.path.exists(poldek_idx) and os.path.exists(repodata_idx) \ + and not os.path.getmtime(comps_file) > os.path.getmtime(repodata_idx) \ + and not os.path.getmtime(poldek_idx) > os.path.getmtime(repodata_idx): + print "repodata indexes already fresh" + continue + print ('%s %s --cache %s-%s %s/RPMS' % ("" if quiet else "time", yum, cachedir, arch, archdir)) + os.system('%s %s --cache %s-%s %s/RPMS' % ("" if quiet else "time", yum, cachedir, arch, archdir)) + if arch != 'noarch' and config.separate_debuginfo: + os.system('%s %s --cache %s-%s %s/debuginfo' % ("" if quiet else "time", yum, cachedir, arch, archdir)) + +if do_rpmrepo: + os.system('%s cd %s.stat/repodata && cvs %s up comps.xml' % ("" if quiet else "set -x;", ftp_dir, "" if quiet else "-Q")) + for tree in trees: + print '\n-------------------------- %s --------------------------' % tree + for arch in all_ftp_archs: + dir = '%s/%s/%s/RPMS' % (ftp_dir, tree, arch) + if not quiet: + print '\ngenerate repodata for %s using rpmrepo (in %s)' % (arch, dir) + os.system('%s rpmrepo %s -o %s' % ("" if quiet else "set -x;", dir, dir)) + if not quiet: + print 'copy comps.xml' + comps = '%s.stat/repodata/comps.xml' % ftp_dir + os.system('%s cp -p %s %s/repodata' % ("" if quiet else "set -x;", comps, dir)) + +for tree in trees: + ftpio.unlock(tree) diff --git a/bin/pfa-lintpkg b/bin/pfa-lintpkg new file mode 100755 index 0000000..c9ae537 --- /dev/null +++ b/bin/pfa-lintpkg @@ -0,0 +1,200 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os, re +import getopt +import subprocess +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +from common import checkdir +import ftpio + +try: + opts, args = getopt.getopt(sys.argv[1:], 'qsdo:', [ "quiet" ]) +except getopt.GetoptError: + print >>sys.stderr, "ERR: options error" + print >>sys.stderr, "rpmlint.py tree package1 [package2...]" + sys.exit(1) + +quiet = False +show = False +debugfiles = False +outstream = sys.stdout +for o, a in opts: + if o == "-q" or o == "--quiet": + quiet = True + if o == "-s": + show = True + if o == "-d": + debugfiles = True + if o == "-o": + outstream = open(a, 'w') + +if len(args) < 1: + print >>sys.stderr, "ERR: missing tree name" + print >>sys.stderr, "rpmlint.py tree package1 [package2...]" + sys.exit(1) + +treename = args[0] +packages = args[1:] + +checkdir(treename) + +ftpio.connect('rpmlint') + +if not ftpio.lock(treename, True): + print >>sys.stderr, "ERR: %s tree already locked" % treename + sys.exit(1) + +files = [] +try: + if len(packages) < 1: + loadall = True + else: + loadall = False + + # if no files specified, grab whole tree contents + tree = ftptree.FtpTree(treename, loadall = loadall) + tree.do_checkbuild = False + if loadall: + # this is hack, should be a param, not access private .loadedpkgs element + tree.mark4moving(tree.loadedpkgs) + else: + tree.mark4moving(packages) + files = tree.rpmfiles(debugfiles = debugfiles, sourcefiles = False) + +except (ftptree.SomeError, KeyboardInterrupt), e: + # In case of problems we need to unlock the tree before exiting + ftpio.unlock(treename) + sys.exit(1) + +ftpio.unlock(treename) + +class LintPkg: + def __init__(self, cachedir): + self.outstream = sys.stdout + + # for rpmlint stats + self.packages = self.specfiles = self.errors = self.warnings = 0 + # 1 packages and 0 specfiles checked; 0 errors, 0 warnings. + self.lintre = re.compile('(?P\d+) packages and (?P\d+) specfiles checked; (?P\d+) errors, (?P\d+) warnings.') + + self._rpmlint = '/usr/bin/rpmlint' + + # mtime, which invalidates all caches + self.mtime = None + rpmlintrc = os.path.expanduser("~/.config/rpmlint") + if os.path.exists(rpmlintrc): + self.mtime = os.stat(rpmlintrc).st_mtime + + self.cachedir = os.path.expanduser(cachedir) + if not os.path.isdir(self.cachedir): + os.makedirs(self.cachedir) + + def cachefile(self, file): + (dirname, filename) = os.path.split(file) + return os.path.join(self.cachedir, filename+'.txt') + + def get_stats(self, file): + cachefile = self.cachefile(file) + if not os.path.exists(cachefile): + return None + + # show last line (that contains status) + l = (open(cachefile, 'r').readlines())[-1] + m = self.lintre.match(l) + if not m: + return None + + return { + 'packages': int(m.group('packages')), + 'specfiles': int(m.group('specfiles')), + 'errors': int(m.group('errors')), + 'warnings': int(m.group('warnings')), + } + + """ + update stats from cachefile + """ + def update_stats(self, file): + m = self.get_stats(file) + if not m: + return False + self.packages += m['packages'] + self.specfiles += m['specfiles'] + self.errors += m['errors'] + self.warnings += m['warnings'] + return True + + def print_stats(self, file = None): + if file: + (dirname, filename) = os.path.split(file) + print >>self.outstream, "\r\033[0K%d packages and %d specfiles checked; %d errors, %d warnings. [%s]" % (self.packages, self.specfiles, self.errors, self.warnings, filename), + else: + print >>self.outstream, "\r\033[0K%d packages and %d specfiles checked; %d errors, %d warnings." % (self.packages, self.specfiles, self.errors, self.warnings) + sys.stdout.flush() + + def cat(self, file): + print >>self.outstream, "".join(open(file, 'r').readlines()) + + def show_results(self, file): + m = self.get_stats(file) + if not m: + return False + + cachefile = self.cachefile(file) + if not os.path.exists(cachefile): + print >>self.outsteram, "MISSING: report: %s" % file + + if m['errors'] > 0 or m['warnings'] > 0: + (dirname, filename) = os.path.split(file) + print >>self.outstream, "rpmlint: %s" % filename + self.cat(cachefile) + + def rpmlint(self, file): + cachefile = self.cachefile(file) + + rc = None + if not os.path.exists(cachefile) \ + or os.stat(file).st_mtime > os.stat(cachefile).st_mtime \ + or (self.mtime and self.mtime > os.stat(cachefile).st_mtime): + cmd = [self._rpmlint, file] + outfd = open(cachefile, 'w') + try: + env = {'TZ': 'GMT'} + rc = subprocess.call(cmd, stdin = subprocess.PIPE, stdout = outfd, stderr = outfd, env = env, close_fds = True) + except KeyboardInterrupt: + os.unlink(cachefile) + raise + outfd.close() + if not self.update_stats(file): + # update failed, dump cache and remove it + self.cat(cachefile) + os.unlink(cachefile) + rc = 1 + return rc == 0 + +try: + lock = 'rpmlint:'+treename + if not ftpio.lock(lock, True): + print >>sys.stderr, "ERR: %s tree already locked for rpmlint" % treename + sys.exit(1) + + if not quiet: + print >>outstream, "rpmlint of %d files from %d packages" % (len(files), len(tree.loadedpkgs)) + lint = LintPkg("~/tmp/rpmlint") + lint.outstream = outstream + for file in files: + lint.rpmlint(file) + if not quiet: + lint.print_stats(file) + if show: + lint.show_results(file) + + if not quiet: + lint.print_stats() + + ftpio.unlock(lock) +except (Exception, KeyboardInterrupt): + ftpio.unlock(lock) + raise diff --git a/bin/pfa-maintainer b/bin/pfa-maintainer new file mode 100755 index 0000000..0d2ea04 --- /dev/null +++ b/bin/pfa-maintainer @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import time +from config import test_builds_dir, ftp_archs + +def clean_dir(path, max): + curtime=time.time() + for i in os.listdir(path): + if curtime - os.path.getmtime(path+'/'+i) > max: + os.unlink(path+'/'+i) + +for arch in ftp_archs + ['SRPMS']: + clean_dir(test_builds_dir+arch, 60*60*24*3) + diff --git a/bin/pfa-mvpkg b/bin/pfa-mvpkg new file mode 100755 index 0000000..5693465 --- /dev/null +++ b/bin/pfa-mvpkg @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +from common import checkdir +import ftpio +from mailer import Message +from config import archived_trees, logs_list + +os.umask(022) + +nocheckbuild = False +if len(sys.argv) > 4 and sys.argv[1] == '-nb': + nocheckbuild = True + sys.argv = sys.argv[1:] + +if len(sys.argv) < 4: + print >>sys.stderr, "ERR: not enough parameters given" + print >>sys.stderr, "move.py [options] src-tree dst-tree package [package2 package3 ...]" + print >>sys.stderr, "\nOptions:" + print >>sys.stderr, " -nb Do not check if builds are finished.\n" + sys.exit(1) + +checkdir(sys.argv[1]) +checkdir(sys.argv[2]) + +if sys.argv[2] in archived_trees: + archivetreename = ".archive/" + sys.argv[2] + checkdir(archivetreename) +else: + archivetreename = None + +ftpio.connect('move') + +if not ftpio.lock(sys.argv[1], True): + print >>sys.stderr, "ERR: %s tree already locked" % sys.argv[1] + sys.exit(1) + +if not ftpio.lock(sys.argv[2], True): + ftpio.unlock(sys.argv[1]) + print >>sys.stderr, "ERR: %s tree already locked" % sys.argv[2] + sys.exit(1) + +if archivetreename != None and archivetreename != sys.argv[1] and not ftpio.lock(archivetreename, True): + ftpio.unlock(sys.argv[2]) + ftpio.unlock(sys.argv[1]) + print "ERR: %s tree already locked" % archivetreename + sys.exit(1) + +try: + srctree = ftptree.FtpTree(sys.argv[1], loadall = True) + dsttree = ftptree.FtpTree(sys.argv[2]) + if archivetreename != None: + archivetree = ftptree.FtpTree(archivetreename) + else: + archivetree = None + if nocheckbuild: + srctree.do_checkbuild = False + pkgs = list(set(sys.argv[3:])) + srctree.mark4moving(pkgs) + + srctree.movepkgs(dsttree, archivetree = archivetree) +except ftptree.SomeError: + # In case of problems we need to unlock the trees before exiting + ftpio.unlock(sys.argv[1]) + ftpio.unlock(sys.argv[2]) + if archivetreename != None: + ftpio.unlock(archivetreename) + sys.exit(1) + +ftpio.unlock(sys.argv[1]) +ftpio.unlock(sys.argv[2]) +if archivetreename != None and archivetreename != sys.argv[1]: + ftpio.unlock(archivetreename) + +if logs_list == None: + sys.exit(0) + +print 'Sending mail notification to %s...' % logs_list + +pkgs = {} + +for pkg in srctree.marked4moving: + requesters = [] + for bid in pkg.build.keys(): + if pkg.build[bid].requester not in requesters: + requesters.append(pkg.build[bid].requester) + pkgs[pkg.nvr] = requesters + +ftpadmin = "(unknown)" +try: + ftpadmin = os.environ['FTPADM'] +except KeyError, e: + pass +m = Message() +m.set_headers( + to = logs_list, + subject = 'MOVED: %s => %s... %d packages' % (sys.argv[1], sys.argv[2], len(pkgs)) +) + +m.write( + ( + 'FTP admin %(ftpadmin)s moved from *%(srctree)s* to *%(dsttree)s* %(count)d packages\n\n' + + '---- Package name ---- Package built by:\n\n' + ) % { + 'ftpadmin' : ftpadmin, + 'srctree' : sys.argv[1], + 'dsttree' : sys.argv[2], + 'count' : len(pkgs), + } +) + +for nvr in sorted(pkgs.keys()): + m.write_line('%s ---- %s' % (nvr, ', '.join(pkgs[nvr]))) + +m.send() + +print 'done.' diff --git a/bin/pfa-rmpkg b/bin/pfa-rmpkg new file mode 100755 index 0000000..8054135 --- /dev/null +++ b/bin/pfa-rmpkg @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +from common import checkdir +import ftpio + +if len(sys.argv) < 3: + print "ERR: not enough parameters given" + print "remove.py tree package1 [package2...]" + sys.exit(1) + +checkdir(sys.argv[1]) + +ftpio.connect('remove') + +if not ftpio.lock(sys.argv[1], True): + print "ERR: %s tree already locked" % sys.argv[1] + sys.exit(1) + +try: + tree=ftptree.FtpTree(sys.argv[1]) + #tree.do_checkbuild=False + pkgs = list(set(sys.argv[2:])) + tree.mark4removal(pkgs) + tree.removepkgs() +except ftptree.SomeError: + # In case of problems we need to unlock the tree before exiting + ftpio.unlock(sys.argv[1]) + sys.exit(1) + +ftpio.unlock(sys.argv[1]) + diff --git a/bin/pfa-signpkg b/bin/pfa-signpkg new file mode 100755 index 0000000..d45fc7c --- /dev/null +++ b/bin/pfa-signpkg @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +import getopt +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +import getpass +from common import checkdir +import ftpio +from config import sign_key +from sign import is_signed, signpkgs + +os.umask(022) + +try: + opts, args = getopt.getopt(sys.argv[1:], '') +except getopt.GetoptError: + print >>sys.stderr, "ERR: options error" + print >>sys.stderr, "sign.py tree package1 [package2...]" + sys.exit(1) + +if len(args) < 1: + print >>sys.stderr, "ERR: missing tree name" + print >>sys.stderr, "sign.py tree package1 [package2...]" + sys.exit(1) + +if sign_key == None: + print >>sys.stderr, "ERR: sign_key not defined in config" + sys.exit(1) + +treename = args[0] +packages = args[1:] + +checkdir(treename) + +ftpio.connect('sign') + +if not ftpio.lock(treename, True): + print >>sys.stderr, "ERR: %s tree already locked" % treename + sys.exit(1) + +files = [] +try: + if len(packages) < 1: + loadall = True + else: + loadall = False + + # if no files specified, grab whole tree contents + tree = ftptree.FtpTree(treename, loadall = loadall) + if loadall: + # this is hack, should be a param, not access private .loadedpkgs element + tree.mark4moving(tree.loadedpkgs) + else: + tree.mark4moving(packages) + files = tree.rpmfiles() + +except ftptree.SomeError: + # In case of problems we need to unlock the tree before exiting + ftpio.unlock(treename) + sys.exit(1) + +ftpio.unlock(treename) + +print "Checking signatures of %d files from %d packages" % (len(files), len(tree.loadedpkgs)) +sign = [] +n = c = 0 +for file in files: + if not is_signed(file): + sign.append(file) + c += 1 + n += 1 + print "\r%d/%d %s\033[0K" % (n, c, file), + +print "" + +if len(sign) == 0: + print "No files to sign" + sys.exit(0) + +# http://mail.python.org/pipermail/python-list/2009-February/700658.html +def chunk(seq, size, pad=None): + ''' + Slice a list into consecutive disjoint 'chunks' of + length equal to size. The last chunk is padded if necessary. + + >>> list(chunk(range(1,10),3)) + [[1, 2, 3], [4, 5, 6], [7, 8, 9]] + >>> list(chunk(range(1,9),3)) + [[1, 2, 3], [4, 5, 6], [7, 8, None]] + >>> list(chunk(range(1,8),3)) + [[1, 2, 3], [4, 5, 6], [7, None, None]] + >>> list(chunk(range(1,10),1)) + [[1], [2], [3], [4], [5], [6], [7], [8], [9]] + >>> list(chunk(range(1,10),9)) + [[1, 2, 3, 4, 5, 6, 7, 8, 9]] + >>> for X in chunk([],3): print X + >>> + ''' + n = len(seq) + mod = n % size + for i in xrange(0, n - mod, size): + yield seq[i : i + size] + if mod: + yield seq[-mod:] + +print "Total %d files to sign" % len(sign) +password = getpass.getpass("Enter signing password: ") +try: + for x in chunk(sign, 512): + print "Signing %d files" % len(x) + signpkgs(x, password) +except OSError, e: + print >>sys.stderr, "ERR: %s" % e + exit(1) diff --git a/bin/pfa-testmvpkg b/bin/pfa-testmvpkg new file mode 100755 index 0000000..53a0e48 --- /dev/null +++ b/bin/pfa-testmvpkg @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +from common import checkdir +import ftpio +from config import archived_trees, logs_list + +if len(sys.argv) < 4: + print "ERR: not enough parameters given" + print "test-move.py src-tree dst-tree package [package2 package3 ...]" + sys.exit(1) + +checkdir(sys.argv[1]) +checkdir(sys.argv[2]) + +if sys.argv[2] in archived_trees: + archivetreename = ".archive/" + sys.argv[2] + checkdir(archivetreename) +else: + archivetreename = None + +ftpio.connect('test-move') + +if not ftpio.lock(sys.argv[1], True): + print "ERR: %s tree already locked" % sys.argv[1] + sys.exit(1) + +if not ftpio.lock(sys.argv[2], True): + ftpio.unlock(sys.argv[1]) + print "ERR: %s tree already locked" % sys.argv[2] + sys.exit(1) + +if archivetreename != None and archivetreename != sys.argv[1] and not ftpio.lock(archivetreename, True): + ftpio.unlock(sys.argv[2]) + ftpio.unlock(sys.argv[1]) + print "ERR: %s tree already locked" % archivetreename + sys.exit(1) + +try: + srctree = ftptree.FtpTree(sys.argv[1], loadall = True) + dsttree = ftptree.FtpTree(sys.argv[2]) + if archivetreename != None: + archivetree = ftptree.FtpTree(archivetreename) + else: + archivetree = None + pkgs = list(set(sys.argv[3:])) + srctree.mark4moving(pkgs) +except ftptree.SomeError: + # In case of problems we need to unlock the trees before exiting + ftpio.unlock(sys.argv[1]) + ftpio.unlock(sys.argv[2]) + if archivetreename != None: + ftpio.unlock(archivetreename) + sys.exit(1) + +# We don't 'try' as in move.py cause this function doesn't force exit +srctree.testmove(dsttree, archivetree = archivetree) + +ftpio.unlock(sys.argv[1]) +ftpio.unlock(sys.argv[2]) +if archivetreename != None and archivetreename != sys.argv[1]: + ftpio.unlock(archivetreename) diff --git a/bin/pfa-unlocktree b/bin/pfa-unlocktree new file mode 100755 index 0000000..43acb09 --- /dev/null +++ b/bin/pfa-unlocktree @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import ftptree +import ftpio +import config + +if len(sys.argv) < 2: + print "ERR: not enough parameters given" + print "unlock.py tree" + sys.exit(1) + +for tree in sys.argv[1:]: + ftpio.connect() + print "Unlock %s: %s" % (tree, ftpio.unlock(sys.argv[1])) diff --git a/cgi-bin/index.py b/cgi-bin/index.py new file mode 100755 index 0000000..208a66a --- /dev/null +++ b/cgi-bin/index.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +# Printing errors (no SyntaxErrors though, but that's not a problem) +import sys, os +sys.stderr=sys.stdout + +contenttypesent=False + +def myexceptionhandler(type, value, traceback): + if contenttypesent: + print '
'
+        sys.__excepthook__(type, value, traceback)
+        print '
' + else: + print "Content-Type: text/plain\n" + sys.__excepthook__(type, value, traceback) + sys.exit(1) + +sys.excepthook=myexceptionhandler +# End printing errors + +sys.path.insert(0, '../modules') + +import wwwiface, user + +opts, cks = wwwiface.getopts() +u=user.User(cks, opts) + +if u.loggedin: + wwwiface.addheader('Logged in: '+u.login) + wwwiface.addmenu(file='loggedinmenu') + import wwwcmds + wwwcmds.handlecmds(opts) +else: + wwwiface.addheader('Login form') + wwwiface.addcontent(file='regform') + +wwwiface.sendhttpheaders() +contenttypesent=True + +wwwiface.sendhtml() + diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..5b15bc4 --- /dev/null +++ b/doc/README @@ -0,0 +1,3 @@ +You can find a comprehensive description of current features at +http://cvs.pld-linux.org/cgi-bin/cvsweb/PLD-doc/PLD_2.0_ftp_administration + diff --git a/etc/rpmlint b/etc/rpmlint new file mode 100644 index 0000000..3a84113 --- /dev/null +++ b/etc/rpmlint @@ -0,0 +1,233 @@ +# vim:ft=python +# see global config for more options: /etc/rpmlint/config + +# Configure the checks if you don't want the default ones. +# -------------------------------------------------------- + +#addCheck("FHSCheck") +#addCheck("BinariesCheck") + +# Configuration options used by the checks shipped with rpmlint. +# The values in the commented out setOption() calls represent default +# or typical example values for the option. +# ------------------------------------------------------------------- +setOption("UseEnchant", False) +setOption("UseVersionInChangelog", False) + +# Base directory where to extract uninstalled packages while checking. +# Type: string, default: tempfile.gettempdir() +#setOption("ExtractDir", "/tmp") + +# Output filters. +# --------------- +#UseVersionInChangelog + +# these are against pld own policy +addFilter("E: incoherent-version-in-name") +addFilter("E: no-packager-tag") +addFilter("E: no-signature") +addFilter("W: macro-in-%changelog") +addFilter("W: no-major-in-name") +addFilter("W: python-bytecode-without-source") +addFilter("W: requires-on-release") +addFilter("E: init-script-non-executable /etc/rc.d/init.d/functions") + +# hard to fix at this time +addFilter("E: non-executable-script") + +setOption("ValidLicenses", ( + # These are the short names for all of the PLD Linux approved licenses. + 'BSD', + 'GPL, Open Data License', + 'GPL v2', + 'LGPL v2.1', + 'LGPL v3', + 'MPL v1.1', + 'LGPL', +)) + +# filter out stuff that is not critical for pkg move bypass +addFilter("W: no-documentation") +addFilter("W: no-manual-page-for-binary") + +# epoch match error or sth +#dirac-libs.i486: W: ghost-files-without-postin +#dirac-devel.i486: W: incoherent-version-dependency-on dirac/dirac-libs/libdirac 1.0.2 0:1.0.2 +addFilter("W: ghost-files-without-postin") +addFilter("W: incoherent-version-dependency-on") + +# we have no strict lib package policy +addFilter("E: outside-libdir-files") +addFilter("E: executable-in-library-package") + +# not our problem to fix +addFilter("E: incorrect-fsf-address") + +#addFilter("E: backup-file-in-package") +#addFilter("E: arch-dependent-file-in-usr-share") +#addFilter("E: world-writable") +#addFilter("W: unexpanded-macro") +#addFilter("E: init-script-non-executable") +#addFilter("E: filename-not-utf8") +#addFilter("E: info-dir-file") +#addFilter("E: info-files-without-install-info-post") +#addFilter("E: init-script-without-chkconfig-postin") +#addFilter("E: init-script-without-chkconfig-preun") +#addFilter("E: invalid-desktopfile /usr/share/applications") +#addFilter("E: invalid-ldconfig-symlink") +#addFilter("E: invalid-shell-in-") +#addFilter("E: no-chkconfig-line") +#addFilter("E: no-description-tag") +#addFilter("E: no-ldconfig-symlink") +#addFilter("E: non-empty-%postun") +#addFilter("E: non-executable-script") +#addFilter("E: non-ghost-file") +#addFilter("E: non-root-group-log-file") +#addFilter("E: non-utf8-desktopfile") +#addFilter("E: tag-not-utf8") +#addFilter("W: non-etc-or-var-file-marked-as-conffile") +#addFilter("W: one-line-command-in-%post") +#addFilter("W: one-line-command-in-%postun") +#addFilter("W: one-line-command-in-%trigger") +#addFilter("W: spurious-bracket-in-%post") +#addFilter("W: spurious-bracket-in-%trigger") +#addFilter("W: spurious-executable-perm") + +# to get important errors out +addFilter("E: arch-dependent-file-in-usr-share") +addFilter("E: arch-independent-package-contains-binary-or-object") +addFilter("E: binary-or-shlib-defines-rpath") +addFilter("E: compressed-symlink-with-wrong-ext") +addFilter("E: description-line-too-long") +addFilter("E: devel-dependency") +addFilter("E: dir-or-file-in-home") +addFilter("E: dir-or-file-in-tmp") +addFilter("E: dir-or-file-in-usr-local") +addFilter("E: executable-crontab-file") +addFilter("E: executable-marked-as-config-file") +addFilter("E: executable-sourced-script") +addFilter("E: explicit-lib-dependency") +addFilter("E: htaccess-file") +addFilter("E: incoherent-logrotate-file") +addFilter("E: incoherent-subsys") +addFilter("E: incorrect-locale-subdir") +addFilter("E: invalid-directory-reference") +addFilter("E: invalid-soname") +addFilter("E: invalid-version") +addFilter("E: library-not-linked-against-libc") +addFilter("E: library-without-ldconfig-postin") +addFilter("E: library-without-ldconfig-postun") +addFilter("E: menu-in-wrong-dir") +addFilter("E: missing-PT_GNU_STACK-section") +addFilter("E: no-binary") +addFilter("E: no-dependency-on") +addFilter("E: no-dependency-on locales-") +addFilter("E: non-root-user-log-file") +addFilter("E: non-standard-dir-perm") +addFilter("E: non-standard-executable-perm") +addFilter("E: non-standard-executable-perm /etc/rc.d/init.d/") +addFilter("E: non-versioned-file-in-library-package") +addFilter("E: no-signature") +addFilter("E: no-status-entry") +addFilter("E: no-summary-tag") +addFilter("E: postin-without-chkconfig") +addFilter("E: postin-without-install-info /usr/share/info") +addFilter("E: postin-without-ldconfig") +addFilter("E: postun-without-ldconfig") +addFilter("E: preun-without-chkconfig") +addFilter("E: python-bytecode-inconsistent-mtime") +addFilter("E: rpath-in-buildconfig") +addFilter("E: script-without-shebang") +addFilter("E: setgid-binary") +addFilter("E: setuid-binary") +addFilter("E: shared-lib-without-dependency-information") +addFilter("E: shlib-with-non-pic-code") +addFilter("E: sourced-script-with-shebang") +addFilter("E: standard-dir-owned-by-package") +addFilter("E: statically-linked-binary") +addFilter("E: subdir-in-bin") +addFilter("E: subsys-not-used") +addFilter("E: summary-too-long") +addFilter("E: unknown-key GPG#e4f1bc2d") +addFilter("E: unknown-lsb-keyword") +addFilter("E: useless-provides") +addFilter("E: use-of-home-in-%post") +addFilter("E: use-old-pam-stack") +addFilter("E: use-tmp-in-%pre") +addFilter("E: version-control-internal-file") +addFilter("E: wrong-script-end-of-line-encoding") +addFilter("E: wrong-script-interpreter") +addFilter("E: zero-length") +addFilter("W: binaryinfo-readelf-failed") +addFilter("W: binaryinfo-tail-failed") +addFilter("W: class-path-in-manifest") +addFilter("W: conffile-without-noreplace-flag") +addFilter("W: cross-directory-hard-link") +addFilter("W: dangerous-command-in-") +addFilter("W: dangling-relative-symlink") +addFilter("W: dangling-symlink") +addFilter("W: devel-file-in-non-devel-package") +addFilter("W: doc-file-dependency") +addFilter("W: duplicate-executable") +addFilter("W: executable-stack") +addFilter("W: filename-too-long-for-joliet") +addFilter("W: file-not-in-%lang") +addFilter("W: file-not-utf8") +addFilter("W: file-not-utf8 /usr/share/doc/") +addFilter("W: file-not-utf8 /usr/share/man/") +addFilter("W: hidden-file-or-dir") +addFilter("W: incoherent-init-script-name") +addFilter("W: incoherent-subsys") +addFilter("W: infopage-not-compressed") +addFilter("W: invalid-license") +addFilter("W: invalid-url") +addFilter("W: jar-not-indexed") +addFilter("W: log-files-without-logrotate") +addFilter("W: manpage-not-compressed") +addFilter("W: manual-page-warning /usr/share/man/") +addFilter("W: missing-lsb-keyword") +addFilter("W: name-repeated-in-summary") +addFilter("W: no-default-runlevel") +addFilter("W: no-dependency-on") +addFilter("W: non-conffile-in-etc") +addFilter("W: non-executable-in-bin") +addFilter("W: non-standard-dir-in-usr") +addFilter("W: non-standard-dir-in-var") +addFilter("W: non-standard-gid") +addFilter("W: non-standard-group") +addFilter("W: non-standard-uid") +addFilter("W: no-provides") +addFilter("W: no-reload-entry") +addFilter("W: no-soname") +addFilter("W: no-url-tag") +addFilter("W: no-version-in-last-changelog") +addFilter("W: obsolete-not-provided") +addFilter("W: ocaml-naming-policy-not-applied") +addFilter("W: only-non-binary-in-usr-lib") +addFilter("W: percent-in-%post") +addFilter("W: percent-in-%trigger") +addFilter("W: perl5-naming-policy-not-applied") +addFilter("W: postin-without-ghost-file-creation") +addFilter("W: private-shared-object-provides") +addFilter("W: python-naming-policy-not-applied") +addFilter("W: read-error") +addFilter("W: requires-on-release") +addFilter("W: service-default-enabled") +addFilter("W: shared-lib-calls-exit") +addFilter("W: spelling-error") +addFilter("W: summary-ended-with-dot") +addFilter("W: summary-not-capitalized") +addFilter("W: symlink-should-be-relative") +addFilter("W: tag-in-description") +addFilter("W: unable-to-read-zip") +addFilter("W: uncompressed-zip") +addFilter("W: unexpanded-macro /usr/share/ri/"); +addFilter("W: unexpanded-macro /var/lib/gdm/.gconf.mandatory/%gconf-tree.xml") +addFilter("W: unstripped-binary-or-object") +addFilter("W: wrong-file-end-of-line-encoding") +addFilter("W: xmms-naming-policy-not-applied") +addFilter("E: non-readable") +# kde4-kdenetwork-kppp.x86_64 +addFilter("W: unexpanded-macro /usr/share/apps/kppp/"); +addFilter("kde4-.* W: self-obsoletion"); + diff --git a/ftpiod/ftpiod.py b/ftpiod/ftpiod.py new file mode 100755 index 0000000..1726904 --- /dev/null +++ b/ftpiod/ftpiod.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import sys, os +sys.path.insert(0, os.environ['HOME']+'/pld-ftp-admin/modules') +import cons + +def daemonize(): + sys.stdin.close() + sys.stdout.close() + sys.stderr.close() + for fd in range(256): + try: + os.close(fd) + except: + pass + pid=os.fork() + if pid!=0: + sys.exit(0) + os.setsid() + + +#daemonize() + + +while True: + for readable in cons.readables(): + if readable==cons.privlistener: + newsock,addr=readable.accept() + cons.add(cons.Connection(newsock, True)) + elif readable==cons.publistener: + newsock,addr=readable.accept() + cons.add(cons.Connection(newsock, False)) + else: + readable.handleinput() + diff --git a/html/footer.html b/html/footer.html new file mode 100644 index 0000000..69aae56 --- /dev/null +++ b/html/footer.html @@ -0,0 +1,3 @@ + + + diff --git a/html/header.html b/html/header.html new file mode 100644 index 0000000..80a3694 --- /dev/null +++ b/html/header.html @@ -0,0 +1,10 @@ + + + + +PLD ftp manager - Th + + + + + diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..b54a43e --- /dev/null +++ b/html/index.html @@ -0,0 +1,48 @@ + + + + + + PLD Th :: Info Page + + + +

+ About PLD Th +

+ +

Builder queue

+

+

  • PLD Th
  • +

    + +

    Dep checks

    +

    +

  • Main deps x86_64
  • +
  • Main ready deps x86_64
  • +
  • Main+ready+test deps x86_64
  • + +
  • Main deps i686
  • +
  • Main ready deps i686
  • +
  • Main+ready+test deps i686
  • + +
  • Main deps x32
  • +
  • Main ready deps x32
  • +
  • Main+ready+test deps x32
  • + +

    + +

    Freshness checks

    +

    +

  • Differences between AC and TH
  • +
  • Th main+test freshness (compare to git)
  • +

    + +

    Rpmlint checks

    +

    +

  • rpmlint: PLD tree
  • +
  • rpmlint: ready tree
  • +
  • rpmlint: test tree
  • +

    + + diff --git a/html/layout.css b/html/layout.css new file mode 100644 index 0000000..d7ead8a --- /dev/null +++ b/html/layout.css @@ -0,0 +1,82 @@ +body { + margin:0px; + padding:0px; + font-family:verdana, arial, helvetica, sans-serif; + color:#333; + background-color:white; + } +h1 { + margin:0px 0px 15px 0px; + padding:0px; + font-size:28px; + line-height:28px; + font-weight:900; + color:#ccc; + } +p { + font:11px/20px verdana, arial, helvetica, sans-serif; + margin:0px 0px 16px 0px; + padding:0px; + } +#Content>p {margin:0px;} +#Content>p+p {text-indent:30px;} + +a { + color:#09c; + font-size:11px; + text-decoration:none; + font-weight:600; + font-family:verdana, arial, helvetica, sans-serif; + } +a:link {color:#09c;} +a:visited {color:#07a;} +a:hover {background-color:#eee;} + +#Header { + margin:25px 0px 10px 0px; + padding:17px 0px 0px 20px; + /* For IE5/Win's benefit height = [correct height] + [top padding] + [top and bottom border widths] */ + height:33px; /* 14px + 17px + 2px = 33px */ + border-style:solid; + border-color:black; + border-width:1px 0px; /* top and bottom borders: 1px; left and right borders: 0px */ + line-height:11px; + background-color:#eee; + +/* Here is the ugly brilliant hack that protects IE5/Win from its own stupidity. +Thanks to Tantek Celik for the hack and to Eric Costello for publicizing it. +IE5/Win incorrectly parses the "\"}"" value, prematurely closing the style +declaration. The incorrect IE5/Win value is above, while the correct value is +below. See http://glish.com/css/hacks.asp for details. */ + voice-family: "\"}\""; + voice-family:inherit; + height:14px; /* the correct height */ + } +/* I've heard this called the "be nice to Opera 5" rule. Basically, it feeds correct +length values to user agents that exhibit the parsing error exploited above yet get +the CSS box model right and understand the CSS2 parent-child selector. ALWAYS include +a "be nice to Opera 5" rule every time you use the Tantek Celik hack (above). */ +body>#Header {height:14px;} + +#Content { + margin:0px 50px 50px 200px; + padding:10px; + } + +#Menu { + position:absolute; + top:100px; + left:20px; + width:172px; + padding:10px; + background-color:#eee; + border:1px dashed #999; + line-height:17px; +/* Again, the ugly brilliant hack. */ + voice-family: "\"}\""; + voice-family:inherit; + width:150px; + } +/* Again, "be nice to Opera 5". */ +body>#Menu {width:150px;} + diff --git a/html/loggedinmenu.html b/html/loggedinmenu.html new file mode 100644 index 0000000..cd63ab1 --- /dev/null +++ b/html/loggedinmenu.html @@ -0,0 +1 @@ +Logout
    diff --git a/html/menufooter.html b/html/menufooter.html new file mode 100644 index 0000000..c1f08de --- /dev/null +++ b/html/menufooter.html @@ -0,0 +1,2 @@ +Th bugs
    +Buildlogs
    diff --git a/html/qa.php b/html/qa.php new file mode 100644 index 0000000..a24416e --- /dev/null +++ b/html/qa.php @@ -0,0 +1,103 @@ + + + + PLD QA Reports + + + + + + + "Main deps x86_64", + "main-ready" => "Main ready deps x86_64", + "main-ready-test" => "Main+ready+test deps x86_64", + + "main-i686" => "Main deps i686", + "main-ready-i686" => "Main ready deps i686", + "main-ready-test-i686" => "Main+ready+test deps i686", + + "main-x32" => "Main deps x32", + "main-ready-x32" => "Main ready deps x32", + "main-ready-test-x32" => "Main+ready+test deps x32", + + "freshness" => "GIT vs FTP freshness", + + "lint-PLD" => "rpmlint: main", + "lint-test" => "rpmlint: test", + "lint-ready" => "rpmlint: ready", +); + +function reports_selection($reports) { + global $report; + echo "Select report:"; + echo "\n"; +} + +/** + * Create text input for filtering results + */ +function filter_box() { + echo '
    Filter results: '; + echo '
    ? errors'; +} + +function format_report($report) { + echo "
    View the raw report
    \n"; + $file = "$report.txt"; + $giturl = 'http://git.pld-linux.org/gitweb.cgi?p=packages/%1$s.git;f=%1$s.spec;h=HEAD;a=shortlog'; + echo '
      '; + foreach (file($file) as $line) { + $line = preg_replace_callback('/^(?Perror:|GIT:)\s*\[(?P[^]]+)\]\s*(?P.+)$/', function($m) use ($giturl) { + $package = basename($m['spec'], '.spec'); + $url = sprintf($giturl, $package); + return sprintf('
    1. %s [%s] %s', $m['prefix'], $url, $m['spec'], $m['msg']); + }, $line); + echo $line, "
      \n"; + } + echo '
    '; +} + +reports_selection($reports); +filter_box(); +if (isset($reports[$report])) { + format_report($report); +} + +?> + + diff --git a/html/regform.html b/html/regform.html new file mode 100644 index 0000000..dde6db4 --- /dev/null +++ b/html/regform.html @@ -0,0 +1,8 @@ +
    + + +
    + +
    + +
    diff --git a/modules/.gitignore b/modules/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/modules/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/modules/baseftptree.py b/modules/baseftptree.py new file mode 100644 index 0000000..1f3febc --- /dev/null +++ b/modules/baseftptree.py @@ -0,0 +1,68 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import string, config +from common import fileexists + +class Build: + def __init__(self): + self.requester='' + self.requester_email='' + +class BasePkg: + def __init__(self, nvr, tree=None, content=None): + self.files={} + self.info={} + self.build={} + self.noarch_arch={} + self.tree=tree + self.nvr=nvr + self.load(content) + + def __repr__(self): + return self.nvr + + def load(self, content=None): + if content: + lines=content.splitlines() + else: + f=open("%s/SRPMS/.metadata/%s.src.rpm.info" % + (self.tree.basedir, self.nvr), 'r') + lines=f.readlines() + f.close() + + for entry in lines: + i=string.split(string.strip(entry), ':') + if i[0] == 'info': + if len(i)==3: + self.info[i[1]]=i[2] + elif i[1]=='build': + if not self.build.has_key(i[2]): + self.build[i[2]]=Build() + self.lastbid=i[2] + if i[3]=='requester': + self.build[i[2]].requester=i[4] + elif i[3]=='requester_email': + self.build[i[2]].requester_email=i[4] + elif i[1]=='noarch_arch': + self.noarch_arch[i[2]]=i[3] + else: + self.info[i[1]]=i[2:] + elif i[0] == 'file': + if not self.files.has_key(i[1]): + self.files[i[1]]=[] + self.files[i[1]].append(i[2]) + +class BaseFtpTree: + def __init__(self, tree): + self.basedir=config.value['ftp_dir']+'/'+tree + self.treename=tree + + def __repr__(self): + return self.treename + + def has_key(self, key): + if fileexists(self.basedir+'/SRPMS/.metadata/'+key+'.src.rpm.info'): + return True + else: + return False + diff --git a/modules/cmds.py b/modules/cmds.py new file mode 100644 index 0000000..192dbf6 --- /dev/null +++ b/modules/cmds.py @@ -0,0 +1,188 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import os +import time +import config +import common +import md5 +import ftptree + + +def parse(con): + if '\0' not in con.data: + return + cmds=con.data.split('\0')[:-1] + + for cmd in cmds: + con.data=con.data[len(cmd)+1:] + cmdname=cmd[:4] + if not con.authorized and cmdname not in ('linp', 'linc', 'name'): + raise BailOut + # TODO: log unauthorized access + if cmdname in cmdlist_noargs: + if len(cmd)==4: + cmdlist_noargs[cmdname](con) + else: + pass + # TODO: log malicious msg + elif cmdname in cmdlist_args: + if len(cmd)>5: + cmdlist_args[cmdname](con, cmd[5:]) + else: + pass + # TODO: log malicious msg + else: + raise BailOut + # TODO: log this + +def lock(con, arg, hard): + if arg not in locks: + locks[arg]={'hard': hard, 'name': con.name, 'time': int(time.time())} + con.sock.send("OK") + elif locks[arg]['hard']: + con.sock.send("HARD") # Hard lock - you can go get a cup of tea + else: + con.sock.send("SOFT") # Soft lock - try in a second or two + +def cmd_unlock(con, arg): + if arg in locks: + del locks[arg] + con.sock.send("OK") + else: + con.sock.send("FAIL") + +def cmd_lock_soft(con, arg): + lock(con, arg, False) + +def cmd_lock_hard(con, arg): + lock(con, arg, True) + +def cmd_show_locks(con): + cmd_log(con, "Dumping locks data:"); + if len(locks): + res = "" + for lockdata in locks.iteritems(): + tree, data = lockdata + msg = "Tree: %s, Conn name: %s, Hard Lock: %s, Time: %s" % ( + tree, data['name'], data['hard'], time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data['time']))) + cmd_log(con, msg) + res = res + msg +# con.sock.send("BLOB:%d" % len(res)) + con.sock.send(res) + else: + cmd_log(con, "No locks found."); + con.sock.send("NLCK"); + +def cmd_log(con, msg): + logfile.write('%s [%s] -- %s\n' % (time.strftime('%Y-%m-%d %H:%M:%S'), con.name, msg)) + logfile.flush() + +def cmd_name(con, name): + con.name=name + +def load_creds(): + global users, cookies + users={} + cookies={} + if not common.fileexists(common.ftpadmdir+'/var/passwd'): + return + else: + f=open(common.ftpadmdir+'/var/passwd', 'r') + for line in f.xreadlines(): + x=line.strip().split(':') + if len(x)>=2: + users[x[0]]=x[1] + f.close() + if not common.fileexists(common.ftpadmdir+'/var/cookies'): + return + else: + f=open(common.ftpadmdir+'/var/cookies', 'r') + for line in f.xreadlines(): + x=line.strip().split(':') + if len(x)>=2: + users[x[0]]=x[1] + f.close() + +def write_cookies(): + f=open(common.ftpadmdir+'/var/cookies', 'w') + for key in cookies.keys(): + f.write('%s:%s\n' % (key, cookies[key])) + f.close() + +def cmd_login_passwd(con, data): + tmp=data.split('\n') + if len(tmp)!=2: + raise BailOut + login=tmp[0] + passwd=tmp[1] + md5pass=md5.new(passwd).hexdigest() + if login in users and users[login]==md5pass: + cookie=`time.time()`.split('.')[0]+'_'+md5.new(md5pass+salt).hexdigest() + cookies[cookie]=login + write_cookies() + con.username=login + con.authorized=True + con.sock.send('OK '+cookie) + else: + # TODO: log this + con.sock.send('FAIL') + raise BailOut + +def cmd_login_cookie(con, cookie): + if cookie in cookies: + con.cookie=cookie + con.authorized=True + con.username=cookies[cookie] + con.sock.send('OK '+cookies[cookie]) + else: + # TODO: log this (or not) + con.sock.send('FAIL') + +def cmd_logout(con): + if con.cookie in cookies: + del cookies[con.cookie] + write_cookies() + +def reloadftptree(): + global srctree, pkglist + srctree=ftptree.FtpTree(config.value['default_to'], loadall=True) + pkglist=srctree.keys() + pkglist.sort() + +def cmd_gettree(con): + buf='' + for pkgnvr in pkglist: + # TODO: show only user's own pkgs + pkg=srctree[pkgnvr] + line=pkgnvr + if pkg.marked4moving: + line=line+'\n1' + else: + line=line+'\n0' + if pkg.marked4removal: + line=line+'\n1' + else: + line=line+'\n0' + buf=buf+'\0'+line + if buf: + con.sock.send('%.6d' % (len(buf)-1)) + con.sock.send(buf[1:]) + else: + con.sock.send('000000') + + +cmdlist_args={'lcks':cmd_lock_soft, 'lckh':cmd_lock_hard, 'ulck':cmd_unlock, + 'log1':cmd_log, 'name':cmd_name, 'linp':cmd_login_passwd, + 'linc':cmd_login_cookie} + +cmdlist_noargs={'lout':cmd_logout, 'gett':cmd_gettree, 'slck':cmd_show_locks} + +# Global stuff and initializations + +BailOut="BailOut" +locks={} +logfile=open(common.ftpadmdir+'/var/log', 'a') +load_creds() +reloadftptree() +salt=md5.new(`time.time()`).hexdigest() + diff --git a/modules/common.py b/modules/common.py new file mode 100644 index 0000000..19f3aa8 --- /dev/null +++ b/modules/common.py @@ -0,0 +1,26 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import os, sys, config + +def fileexists(path): + if path[0] == '/': + fullpath = path + else: + fullpath = config.ftp_dir + path + return os.path.exists(fullpath) + +def checkdir(dir): + if not fileexists(dir): + print >>sys.stderr, 'ERR: ' + config.value['ftp_dir']+'/' + dir + " does not exist" + sys.exit(1) + +if 'HOME' in os.environ: + ftpadmdir = os.environ['HOME'] + '/pld-ftp-admin' +else: + ftpadmdir = '..' + +# noarchcachedir is dir where noarch files contents are stored for AI +# XXX: file reference where the AI resides +noarchcachedir = ftpadmdir + '/var/noarch-cache/' + +tmpdir = ftpadmdir + '/var/tmp/' diff --git a/modules/config.py b/modules/config.py new file mode 100644 index 0000000..ee32dfa --- /dev/null +++ b/modules/config.py @@ -0,0 +1,76 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import string, os + +value = {} + +if os.environ.has_key('HOME'): + path = os.environ['HOME'] +else: + path = '../../' # cgi-bin interface + +f = open(path + '/.ftpadmrc', 'r') + +for line in f.readlines(): + if line[0] == '#' or string.find(line, '=') == -1: + continue + tuple = string.split(string.strip(line), '=') + if tuple[1][0] == '"': + value[string.strip(tuple[0])] = tuple[1][1:-1] + else: + value[string.strip(tuple[0])] = string.strip(tuple[1]) + +f.close() + +default_to = value['ftp_dir'] + '/' + value['default_to'] + '/' +ftp_dir = value['ftp_dir'] + '/' +incoming_dir = value['ftp_dir'] + '/' + value['incoming_dir'] + '/' +test_builds_dir = value['ftp_dir'] + '/' + value['test_builds_dir'] + '/' +ftp_archs = value['ftp_archs'].split(' ') + +builderqueue = value['builderqueue'] + +if 'sign_key' in value: + sign_key = value['sign_key'] +else: + sign_key = None + +if 'logs_list' in value: + logs_list = value['logs_list'] +else: + logs_list = None + +if 'signed_trees' in value: + signed_trees = value['signed_trees'].split(' ') +else: + signed_trees = None + +if 'archived_trees' in value: + archived_trees = value['archived_trees'].split(' ') +else: + archived_trees = None + +if 'old_poldek' in value and value['old_poldek'] == 'yes': + old_poldek = True +else: + old_poldek = False + +if value['separate_noarch'] == 'yes': + separate_noarch = True +else: + separate_noarch = False + +if value['separate_debuginfo'] == 'yes': + separate_debuginfo = True +else: + separate_debuginfo = False + +if separate_noarch: + all_ftp_archs = ['noarch'] + ftp_archs +else: + all_ftp_archs = ftp_archs + +if 'ftp_dist' in value: + ftp_dist = value['ftp_dist'] +else: + ftp_dist = 'pld' diff --git a/modules/cons.py b/modules/cons.py new file mode 100644 index 0000000..61569f6 --- /dev/null +++ b/modules/cons.py @@ -0,0 +1,73 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import socket +import os +import select +from common import fileexists +import ftpio +import cmds + +class Connection: + def __init__(self, sock, authorized): + sock.setblocking(False) + self.sock=sock + self.authorized=authorized + self.fileno=sock.fileno + self.name="" + self.data="" + + def destroy(self): + self.sock.close() + rm(self) + + def handleinput(self): + newdata = None + try: + newdata = self.sock.recv(8192) + except: + self.destroy() + + if not newdata: + self.destroy() + else: + self.data = self.data + newdata + + try: + cmds.parse(self) + except cmds.BailOut: + self.destroy() + +def add(con): + cons.append(con) + +def rm(con): + cons.remove(con) + +def readables(): + lst=cons[:] + lst.append(privlistener) + lst.append(publistener) + inlst,outlst,errlst = select.select(lst, [], [], 0.1) + return inlst + +def createlistener(path): + if fileexists(path): + os.remove(path) + + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.setblocking(False) + s.bind(path) + if path==ftpio.pubsock: + os.chmod(path, 0606) + else: + os.chmod(path, 0600) + s.listen(3) + return s + +cons=[] + +privlistener=createlistener(ftpio.privsock) +publistener=createlistener(ftpio.pubsock) + + + diff --git a/modules/ftpio.py b/modules/ftpio.py new file mode 100644 index 0000000..5e0132b --- /dev/null +++ b/modules/ftpio.py @@ -0,0 +1,92 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import os +import sys +import socket +import time +import config + +pubsock=config.value['pubsock'] + +if os.environ.has_key('HOME'): + privsock=os.environ['HOME']+'/pld-ftp-admin/var/privsock' + socketname=privsock +else: + socketname=pubsock + +def connect(name=None): + global sock + sock=socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(socketname) + if not name: + name = "pid_%d_name_%s" % (os.getpid(), sys.argv[0]) + sock.send('name %s\0' % name) + +def login_passwd(login, passwd): + 'Return cookie if ok' + sock.send('linp %s\n%s\0' % (login, passwd)) + retval=sock.recv(256) + if retval=='FAIL': + return '' + else: + return retval[3:] + +def login_cookie(cookie): + 'Return login if ok' + sock.send('linc %s\0' % cookie) + retval=sock.recv(256) + if retval=='FAIL': + return '' + else: + return retval[3:] + +def logout(): + sock.send('lout\0') + +def lock(path, hard=False): + def dolock(): + if hard: + sock.send('lckh %s\0' % path) + else: + sock.send('lcks %s\0' % path) + return sock.recv(20) + for i in range(3): + retcode=dolock() + if retcode=="OK": + return True + elif retcode=="HARD": + return False + if i!=2: + time.sleep(1) + return False + +def unlock(path): + sock.send('ulck %s\0' % path) + ret = sock.recv(20) + if ret == "OK": + return True + return False + +def log(msg): + sock.send('log1 %s\0' % msg) + +def locks_dump(): + sock.send('slck\0') + ret = sock.recv(4096) + if ret == "NLCK": + return "No locks held" + +# nbytes = int(ret.split("BLOB:")[1]) +# ret = sock.recv(nbytes) + return ret + +def gettree(): + sock.send('gett\0') + pkgs=[] + len=int(sock.recv(6)) + if len: + for pkg in sock.recv(len).split('\0'): + tmp=pkg.split('\n') + pkgs.append((tmp[0], int(tmp[1]), int(tmp[2]))) + return pkgs + diff --git a/modules/ftptree.py b/modules/ftptree.py new file mode 100644 index 0000000..70db8a0 --- /dev/null +++ b/modules/ftptree.py @@ -0,0 +1,561 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import os, config, string, urllib, re, rpm +from common import fileexists, noarchcachedir +from baseftptree import BasePkg, BaseFtpTree +from sign import is_signed + +errnum = 0 +quietmode = False + +class SomeError(Exception): + def __init__(self): + return + + def __str__(self): + print "","An Error occured!" + +def bailoutonerror(): + if not errnum == 0: + print "%d error(s) encountered... aborting" % errnum + raise SomeError + +def pinfo(msg): + print 'INFO: ' + msg + +def perror(msg): + global errnum + errnum = errnum + 1 + print 'ERR: ' + msg + +def pwarning(msg): + print 'WARN: ' + msg + +def rm(file, test = False): + if test: + if not os.path.exists(file): + pinfo("TEST os.remove(%s): file doesn't exists" % file) + else: + try: + os.remove(file) + except OSError, e: + pinfo("os.remove(%s): %s" % (file, e)) + #raise + +def mv(src, dst, test = False): + fsrc = src + fdst = dst + '/' + src.split('/')[-1] + if test: + if not os.path.exists(fsrc): + pinfo("TEST os.rename(%s, %s): source doesn't exists" % (fsrc, fdst)) + if not os.path.exists(dst): + pinfo("TEST destination doesn't exist: %s" % dst) + else: + try: + os.rename(fsrc, fdst) + except OSError, e: + pinfo("os.rename(%s, %s): %s" % (fsrc, fdst, e)) + raise + +class Pkg(BasePkg): + def __init__(self, nvr, tree): + BasePkg.__init__(self, nvr, tree) + self.name = string.join(nvr.split('-')[:-2], '-') + self.version = nvr.split('-')[-2] + self.release = nvr.split('-')[-1] + self.marked4removal = False + self.marked4moving = False + self.marked4movingpool = [] + self.errors = [] + self.warnings = [] + + def __cmp__(self, pkg): + if self.name > pkg.name: + return 1 + elif self.name < pkg.name: + return -1 + else: + return rpm.labelCompare(('0', self.version, self.release), + ('0', pkg.version, pkg.release)) + + + # unfortunately can't do new Pkg(NVR), and have no "tree" in this pkg context + # so this static function + def is_debuginfo(self, nvr): + """ + returns true if NVR is debuginfo package and separate debuginfo is enabled + """ + if not config.separate_debuginfo: + return False + pkg = nvr.split('-')[:-2] + return pkg[-1] == 'debuginfo' + + def is_sourcefile(self, file): + """ + returns true if file is source package + """ + return file[-8:] == '.src.rpm' + + # returns true if package build is integer + def is_release(self): + """ + To account Release tags with subver macros, we consider integer release + if it contains odd number of dots: + + 1 -> True + 0.1 -> False + 0.%{subver}.%{rel}, %{rel} = 1 -> 0.20010.1 -> True + 0.%{subver}.%{rel}, %{rel} = 0.1 -> 0.20010.0.1 -> False + """ + return self.release.count('.') % 2 == 0 + + def mark4moving(self): + if not self.marked4moving: + # Only one pkg in this pool can be marked for moving + for pkg in self.marked4movingpool: + pkg.unmark4moving() + self.tree.marked4moving.append(self) + self.marked4moving=True + + def unmark4moving(self): + if self.marked4moving: + self.tree.marked4moving.remove(self) + self.marked4moving=False + + def mark4removal(self): + if not self.marked4removal: + self.tree.marked4removal.append(self) + self.marked4removal=True + + def error(self, msg): + self.errors.append(msg) + if not quietmode: + perror('%s %s' % (self.nvr, msg)) + + def warning(self, msg): + self.warnings.append(msg) + if not quietmode: + pwarning('%s %s' % (self.nvr, msg)) + + def load(self, content=None): + BasePkg.load(self, content) + if self.info.has_key('move'): + self.mark4moving() + + def writeinfo(self): + f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w') + for bid in self.build.keys(): + f.write("info:build:%s:requester:%s\ninfo:build:%s:requester_email:%s\n" % (bid, self.build[bid].requester, bid, self.build[bid].requester_email)) + for key in self.info.keys(): + f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':'))) + for arch in self.files.keys(): + for rpm in self.files[arch]: + f.write("file:%s:%s\n" % (arch, rpm)) + + def remove(self, test = False): + """ + Remove package from ftp + """ + for arch in self.files.keys(): + for rpm in self.files[arch]: + if self.is_debuginfo(rpm): + rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test) + else: + rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test) + if arch == 'noarch': + if fileexists(noarchcachedir + rpm + '.filelist'): + rm(noarchcachedir + rpm + '.filelist', test) + if fileexists(noarchcachedir + rpm + '.reqlist'): + rm(noarchcachedir + rpm + '.reqlist', test) + rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test) + + def rpmfiles(self, debugfiles = True, sourcefiles = True): + """ + Return rpm files related to this package + """ + files = [] + for arch, rpms in self.files.items(): + for nvr in rpms: + if self.is_debuginfo(nvr): + if debugfiles: + files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr) + else: + if self.is_sourcefile(nvr): + if sourcefiles: + files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr) + else: + files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr) + return files + + def obsoletes(self): + """ + Return obsoletes for all packages in Pkg: + + {'php-geshi': set(['geshi'])} + + """ + def rpmhdr(pkg): + ts = rpm.ts() + ts.setVSFlags(rpm.RPMVSF_NODSAHEADER) + fdno = os.open(pkg, os.O_RDONLY) + hdr = ts.hdrFromFdno(fdno) + os.close(fdno) + return hdr + + obsoletes = {} + for rpmfile in self.rpmfiles(): + if not os.path.exists(rpmfile): + continue + hdr = rpmhdr(rpmfile) + if not hdr[rpm.RPMTAG_OBSOLETES]: + continue + + name = hdr[rpm.RPMTAG_NAME] + if not name in obsoletes: + obsoletes[name] = set() + + for tag in hdr[rpm.RPMTAG_OBSOLETES]: + obsoletes[name].add(tag) + + return obsoletes + + def move(self, dsttree, test = False): + if dsttree.has_key(self.nvr): + movedany = False + for arch in self.files.keys(): + if arch in dsttree[self.nvr].files.keys(): + msg = "" + if test: + msg = "TEST " + pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr)) + for rpm in self.files[arch]: + if self.is_debuginfo(rpm): + rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test) + else: + rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test) + else: + movedany = True + dsttree[self.nvr].files[arch] = self.files[arch] + for rpm in self.files[arch]: + if self.is_debuginfo(rpm): + mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test) + else: + mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test) + if not test and movedany: + for bid in self.build.keys(): + dsttree[self.nvr].build[bid] = self.build[bid] + dsttree[self.nvr].writeinfo() + rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test) + else: + # move files + for arch in self.files.keys(): + for rpm in self.files[arch]: + if self.is_debuginfo(rpm): + mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test) + else: + mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test) + + # move metadata + mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test) + +class FtpTree(BaseFtpTree): + def __init__(self, tree, loadall=False): + BaseFtpTree.__init__(self, tree) + self.loadedpkgs = {} + self.marked4removal = [] + self.marked4moving = [] + self.pkgnames = [] + self.__loadpkgnames() + if loadall: + for pkgname in self.pkgnames: + self.loadedpkgs[pkgname] = Pkg(pkgname, self) + # Tests: + self.do_checkbuild = True + + def __getitem__(self, key): + if self.loadedpkgs.has_key(key): + return self.loadedpkgs[key] + elif key in self.pkgnames: + pkg=Pkg(key, self) + self.loadedpkgs[key]=pkg + return pkg + else: + raise KeyError, key + + def has_key(self, key): + if key in self.pkgnames: + return True + else: + return False + + def keys(self): + return self.pkgnames + + def values(self): + return self.loadedpkgs.values() + + def checktree(self, dsttree): + self.__checkbuild(self.loadedpkgs.values()) + self.__checkarchs(dsttree, self.loadedpkgs.values()) + + def testmove(self, dsttree, archivetree = None): + self.__checkbuild(self.marked4moving) + self.__checkarchs(dsttree, self.marked4moving) + if not self.treename.count("archive"): + self.__checkduplicates(self.marked4moving) + + self.__checksigns(dsttree, self.marked4moving, test = True) + self.__checkforobsoletes(dsttree, self.marked4moving, test = True) + self.__checkforrelease(dsttree, self.marked4moving, test = True) + + if not self.treename.count("archive"): + self.__rmolderfromsrc(test = True) + self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree) + + for pkg in self.marked4moving: + pkg.move(dsttree, test = True) + + def movepkgs(self, dsttree, archivetree = None): + if self.do_checkbuild: + self.__checkbuild(self.marked4moving) + bailoutonerror() + + self.__checkarchs(dsttree, self.marked4moving) + bailoutonerror() + + self.__checksigns(dsttree, self.marked4moving) + bailoutonerror() + + if not self.treename.count("archive"): + self.__rmolderfromsrc() + self.__rmotherfromdst(dsttree, archivetree = archivetree) + + for pkg in self.marked4moving: + pkg.move(dsttree) + + def rpmfiles(self, debugfiles = True, sourcefiles = True): + if self.do_checkbuild: + self.__checkbuild(self.marked4moving) + + files = [] + for pkg in self.marked4moving: + files += pkg.rpmfiles(debugfiles = debugfiles, sourcefiles = sourcefiles) + return files + + def removepkgs(self): + if self.do_checkbuild: + self.__checkbuild(self.marked4removal) + bailoutonerror() + for pkg in self.marked4removal: + pkg.remove() + + def mark4removal(self, wannabepkgs): + self.__mark4something(wannabepkgs, Pkg.mark4removal) + + def mark4moving(self, wannabepkgs): + self.__mark4something(wannabepkgs, Pkg.mark4moving) + + # Internal functions below + def __arch_stringify(self, list): + ret = [] + dist = config.ftp_dist; + for arch in list: + ret.append(dist + '-' + arch) + return ' '.join(ret) + + def __loadpkgnames(self): + def checkfiletype(name): + if name[-13:]=='.src.rpm.info': + return True + else: + return False + list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata')) + self.pkgnames = map((lambda x: x[:-13]), list) + + def __mark4something(self, wannabepkgs, markfunction): + def chopoffextension(pkg): + found = pkg.find('.src.rpm') + if found == -1: + return pkg + else: + return pkg[:found] + + for wannabepkg in wannabepkgs: + pkgname = chopoffextension(wannabepkg) + if pkgname in self.pkgnames: + if not pkgname in self.loadedpkgs.keys(): + self.loadedpkgs[pkgname]=Pkg(pkgname, self) + markfunction(self.loadedpkgs[pkgname]) + else: + perror('%s not found in source tree' % pkgname) + bailoutonerror() + + def __checkbuild(self, marked): + """ + Checks queue file if all arches are built + + Reads config.builderqueue to grab the info + """ + f = urllib.urlopen(config.builderqueue) + requests = {} + reid = re.compile(r'^.*id=(.*) pri.*$') + regb = re.compile(r'^group:.*$|builders:.*$', re.M) + for i in re.findall(regb, f.read()): + if i[0] == 'g': + id = reid.sub(r'\1', i) + requests[id] = "" + elif i[0]=='b': + requests[id] = requests[id] + i + f.close() + + for pkg in marked: + for bid in pkg.build.keys(): + if requests.has_key(bid) and not requests[bid].find('?') == -1: + pkg.error("(buildid %s) building not finished" % bid) + + def __checkarchs(self, dsttree, marked): + """ + Checks marked pkgs it is built on all archs. + """ + for pkg in marked: + if len(pkg.files.keys()) <= 1: + pkg.error('has only src.rpm built') + continue + otherpkgnames = self.__find_other_pkgs(pkg, dsttree) + + # check if we're not removing some archs + if otherpkgnames: + curarchs = [] + missingarchs = [] + for somepkg in otherpkgnames: + curarchs.extend(Pkg(somepkg, dsttree).files.keys()) + for arch in curarchs: + if arch not in pkg.files.keys(): + missingarchs.append(arch) + if missingarchs: + pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs)) + else: + # warn if a package isn't built for all archs + # ftp_archs + SRPMS + ftp_archs_num = len(config.ftp_archs) + 1 + if (config.separate_noarch and 'noarch' in pkg.files.keys()): + # ftp_archs + SRPMS + noarch subpackages + ftp_archs_num += 1 + # plain simple noarch package + if (len(pkg.files.keys()) == 2): + continue + + if len(pkg.files.keys()) != ftp_archs_num: + missingarchs = [] + for arch in config.ftp_archs: + if arch not in pkg.files.keys(): + missingarchs.append(arch) + pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs)) + + def __checkduplicates(self, marked): + """ + Checks if marked packages contain duplicate packages (with different versions) + """ + for pkg in marked: + olderpkgnames = self.__find_older_pkgs(pkg) + for i in olderpkgnames: + markednames = [str(x) for x in marked] + if i in markednames: + pkg.error('duplicate package: %s' % i) + + def __rmolderfromsrc(self, test = False): + for pkg in self.marked4moving: + olderpkgnames = self.__find_older_pkgs(pkg) + for i in olderpkgnames: + Pkg(i, self).remove(test) + + def __rmotherfromdst(self, dsttree, test = False, archivetree = None): + for pkg in self.marked4moving: + pkgnames = self.__find_other_pkgs(pkg, dsttree) + for i in pkgnames: + if archivetree == None: + Pkg(i, dsttree).remove(test) + else: + Pkg(i, dsttree).move(archivetree, test = test) + + # Used more than once filter functions + def __find_other_pkgs(self, pkg, tree): + escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+') + ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$') + def filter_other_pkgs(x): + if ziewre.match(x) and not x == pkg.nvr: + return True + else: + return False + return filter(filter_other_pkgs, tree.pkgnames) + + def __find_older_pkgs(self, pkg): + def filter_older_pkgs(x): + c = x.split('-') + rc = rpm.labelCompare(('0', pkg.version, pkg.release), + ('0', c[-2], c[-1])) + if rc == 1: # pkg > x + return True + else: + return False + return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self)) + + def __checksigns(self, tree, pkgs, test = False): + """ + Checks if pkgs in tree are all signed. + + in case of test = true, error flag is set for unsigned packages + """ + if not tree.treename in config.signed_trees: + return + + for pkg in pkgs: + unsigned = 0 + for file in pkg.rpmfiles(): + if not is_signed(file): + unsigned += 1 + + if unsigned != 0: + if test == True: + if not quietmode: + pkg.warning('%d files not signed' % unsigned) + else: + pkg.error('%d files not signed' % unsigned) + + def __checkforobsoletes(self, tree, pkgs, test = False): + """ + Checks queue file if package obsoletes something in destination tree and suggest for removal. + + Only NAME tag is compared, i.e virtual packages do not get reported. + + """ + if test != True: + return + + def findbyname(name): + def x(nvr): + return '-'.join(nvr.split('-')[:-2]) == name + return filter(x, tree.pkgnames) + + for pkg in pkgs: + obsoletes = pkg.obsoletes() + if not obsoletes: + continue + + for pn, setlist in obsoletes.items(): + for item in setlist: + p = findbyname(item) + if p: + pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn)) + + def __checkforrelease(self, tree, pkgs, test = False): + """ + Checks queue file if package release is non integer. + + """ + if test != True: + return + + for pkg in pkgs: + if not pkg.is_release(): + pkg.warning('non-integer release: %s' % pkg.release) diff --git a/modules/mailer.py b/modules/mailer.py new file mode 100644 index 0000000..bf13ef5 --- /dev/null +++ b/modules/mailer.py @@ -0,0 +1,63 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import time +import os +import sys +import StringIO + +import config +cval=config.value + +class Message: + def __init__(self): + self.headers = {} + self.body = StringIO.StringIO() + self.__set_std_headers() + + def set_header(self, n, v): + self.headers[n] = v + + def set_headers(self, to = None, cc = None, subject = None): + if to != None: + self.set_header("To", to) + if cc != None: + self.set_header("Cc", cc) + if subject != None: + self.set_header("Subject", subject) + + def write_line(self, l): + self.body.write("%s\n" % l) + + def write(self, s): + self.body.write(s) + + def send(self): + send_sendmail = "/usr/sbin/sendmail -t" + f = os.popen(send_sendmail, "w") + self.__write_to(f) + f.close() + + def __set_std_headers(self): + self.headers["Date"] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) + self.headers["Message-ID"] = "" \ + % (time.time(), os.getpid(), os.uname()[1]) + self.headers["From"] = cval['from_field'] + self.headers["X-PLD-Builder"] = cval['xpldbuilder'] + + def __write_to(self, f): + for k, v in self.headers.items(): + f.write("%s: %s\n" % (k, v)) + f.write("\n") + self.body.seek(0) + self.__sendfile(self.body, f) + + def __sendfile(self, src, dst): + cnt = 0 + while 1: + s = src.read(10000) + if s == "": break + cnt += len(s) + dst.write(s) + return cnt + + diff --git a/modules/sign.py b/modules/sign.py new file mode 100644 index 0000000..377ee51 --- /dev/null +++ b/modules/sign.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import os +import sys +import rpm +import pexpect +from config import sign_key + +def getSigInfo(hdr): + """checks signature from an hdr hand back signature information and/or + an error code""" + # yum-3.2.22/rpmUtils/miscutils.py + + string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|' + siginfo = hdr.sprintf(string) + if siginfo == '(none)': + return None + + return siginfo.split(',')[2].lstrip() + +def is_signed(rpm_file): + """Returns rpm information is package signed by the same key""" + # http://code.activestate.com/recipes/306705/ + + if sign_key == None: + return None + + ts = rpm.ts() + ts.setVSFlags(rpm.RPMVSF_NODSAHEADER) + fdno = os.open(rpm_file, os.O_RDONLY) + hdr = ts.hdrFromFdno(fdno) + os.close(fdno) + + sigid = getSigInfo(hdr) + if sigid == None: + return None + + return sign_key == sigid[-len(sign_key):] + +def signpkgs(files, password): + if not os.path.isfile('/usr/bin/gpg'): + raise OSError, 'Missing gnupg binary' + if not os.path.isfile('/bin/rpm'): + raise OSError, 'Missing rpm binary' + + os.putenv('LC_ALL', 'C') + args = ['--resign', '--define', '_signature gpg', '--define', '_gpg_name ' + sign_key] + files + child = pexpect.spawn('/bin/rpm', args) + child.logfile_read = sys.stderr + child.expect('Enter pass phrase:', timeout=30) + child.sendline(password) + child.expect(pexpect.EOF, timeout=None) + child.close() + rc = child.exitstatus + if rc != 0: + raise OSError, 'package signing failed' + for rpm in files: + os.chmod(rpm, 0644) diff --git a/modules/user.py b/modules/user.py new file mode 100644 index 0000000..2b31f1a --- /dev/null +++ b/modules/user.py @@ -0,0 +1,44 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import Cookie, time, ftpio + +UserNotLoggedIn="UserNotLoggedIn" + +class User: + def __init__(self, cookies, options): + self.loggedin=False + ftpio.connect('wwwiface') + if 'ftpsessid' in cookies and cookies['ftpsessid']: + self.login=ftpio.login_cookie(cookies['ftpsessid']) + if self.login: + self.loggedin=True + + if 'action' in options: + if options['action'] == 'register': + self.checkloginpass(options) + elif options['action'] == 'logout': + self.logout() + + def checkloginpass(self, options): + if 'login' not in options or 'pass' not in options: + return + self.cookie=ftpio.login_passwd(options['login'], options['pass']) + if self.cookie: + self.login=options['login'] + self.loggedin=True + C = Cookie.SimpleCookie() + C['ftpsessid']=self.cookie + #C['ftpsessid']['expires']=time.strftime( + #"%a, %d-%b-%y %H:%M:%S GMT", + #time.gmtime(time.time()+86400)) + print C + + def logout(self): + self.loggedin=False + ftpio.logout() + C = Cookie.SimpleCookie() + C['ftpsessid']='' + C['ftpsessid']['expires']=time.strftime("%a, %d-%b-%y %H:%M:%S GMT", + time.gmtime(time.time()-31536000)) + print C + diff --git a/modules/wwwcmds.py b/modules/wwwcmds.py new file mode 100644 index 0000000..ea6c63c --- /dev/null +++ b/modules/wwwcmds.py @@ -0,0 +1,26 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import wwwiface, ftpio + + +def handlecmds(options): + def chkbox(pkgname, value): + retval='' + return retval + + if 'action' in options and options['action'] in actions: + pass + else: + wwwiface.addcontent('
    ') + wwwiface.addcontent('') + for pkg in ftpio.gettree(): + wwwiface.addcontent('' % (chkbox(pkg[0], pkg[1]), chkbox(pkg[0], pkg[2]), pkg[0])) + wwwiface.addcontent('') + wwwiface.addcontent('
    MvRmPackage
    %s%s%s
      
    ') + + +actions={} + diff --git a/modules/wwwiface.py b/modules/wwwiface.py new file mode 100644 index 0000000..bc215ae --- /dev/null +++ b/modules/wwwiface.py @@ -0,0 +1,77 @@ +# vi: encoding=utf-8 ts=8 sts=4 sw=4 et + +import cgi, Cookie, os + +menu=[] +content=[] +header=[] + +def getfile(file): + f=open("../html/" + file + ".html", 'r') + s=f.read() + f.close() + return s + +def catfile(file): + f=open("../html/" + file + ".html", 'r') + print f.read() + f.close() + +def sendhttpheaders(): + print "Content-Type: text/html\n" + +def getopts(): + form = cgi.FieldStorage() + opts = {} + for key in form.keys(): + opts[key] = form[key].value + + cookies = {} + + if os.environ.has_key('HTTP_COOKIE'): + c = Cookie.SimpleCookie() + c.load(os.environ['HTTP_COOKIE']) + for key in c.keys(): + cookies[key] = c[key].value + + return (opts, cookies) + +def sendhtml(): + catfile('header') + + print '' + + print '' + + print '
    ' + for i in content: + print i + print '
    ' + + catfile('footer') + +def addmenu(text=None, file=None): + if text: + menu.append(text) + else: + menu.append(getfile(file)) + +def addheader(text=None, file=None): + if text: + header.append(text) + else: + header.append(getfile(file)) + +def addcontent(text=None, file=None): + if text: + content.append(text) + else: + content.append(getfile(file)) + diff --git a/repodata/comps.xml b/repodata/comps.xml new file mode 100644 index 0000000..d437ad9 --- /dev/null +++ b/repodata/comps.xml @@ -0,0 +1,2134 @@ + + + + + base + Base packages + true + true + Base packages + + ntp-client + FHS + SysVinit + basesystem + chkconfig + cpio + cracklib + cracklib-dicts + dhcp-client + findutils + fix-info-dir + geninitrd + genromfs + gettext + glibc + glibc-localedb-all + grep + grubby + gzip + iproute2 + issue + kbd + kernel + libstdc++ + login + logrotate + losetup + mawk + mingetty + mktemp + module-init-tools + mount + net-tools + pam + pciutils + pdksh + poldek + procps + rc-scripts + rpm + sed + setserial + setup + syslog-ng + pwdutils + tar + tzdata + tzdata-zoneinfo_right + udev + utempter + util-linux-ng + vim-static + vixie-cron + which + + + + + basic + Programs you're unlikely not to need. + true + true + Programs you're unlikely not to need. + + mailx + nc + psmisc + terminfo + rpm-utils + telnet + tcp_wrappers + sharutils + file + ftp + less + time + iputils + iputils-ping + mtr + bash + bzip2 + vim + vim-rt + openssh-clients + tmpwatch + + + + + backup_tools + Backup tools + false + true + Backup tools + + afio + amanda-client + amanda-server + mt-st + dump + + + + + extras1 + Not essential, but *very* often needed. + false + true + Not essential, but *very* often needed. + + dhcpcd + pump + MAKEDEV + bind-utils + hexedit + info + lftp + ltrace + lsof + lynx + man + man-pages + mc + mutt + ncftp + ncompress + nmap + openssh-server + pcregrep + perl + pinfo + pine + acpid + quota + rsync + slocate + strace + tcpdump + unzip + wget + zsh + + + + + nfs + Network File Systems stuff + false + true + Network File Systems stuff + + portmap + nfs-utils + nfs-utils-clients + nfs-utils-lock + samba + samba-client + + + + + servers + Various servers + false + true + Various servers + + proftpd-standalone + proftpd-inetd + pure-ftpd + ftpd-BSD + exim + postfix + sendmail + omta + cfingerd + efingerd + ffingerd + bsd-fingerd + xinetd + rlinetd + inetd + telnetd + cvs-pserver + rsyncd-standalone + rsyncd-inetd + cups + dhcp + bircd + ircd + ircd-hybrid + ircd-ptlink + inn + imap + courier-imap + cyrus-imapd + imap-pop3 + courier-imap-pop3 + solid-pop3d + tpop3d + vm-pop3d-standalone + bind + pdns + + + + + irc + Client packages for IRC + false + true + Client packages for IRC + + ircii + irssi + BitchX + epic4 + + + + + console + Utilites usable at Linux' console + false + true + Utilites usable at Linux' console + + open + SVGATextMode + fbset + gpm + + + + + mp3 + MPEG II Layer 3 encoding/decoding + false + true + MPEG II Layer 3 encoding/decoding + + mpg123 + bladeenc + lame + cdparanoia-III + cdrtools-cdda2wav + mp3blaster + ripenc + + + + + apache_base + Apache 2.2.x web server base packages + false + true + Apache 2.2.x web server base packages + + apache + apache-errordocs + apache-index + apache-mod_alias + apache-mod_auth + apache-mod_auth_basic + apache-mod_authn_default + apache-mod_authn_file + apache-mod_authz_default + apache-mod_authz_groupfile + apache-mod_authz_host + apache-mod_authz_owner + apache-mod_authz_user + apache-mod_autoindex + apache-mod_deflate + apache-mod_dir + apache-mod_env + apache-mod_include + apache-mod_log_config + apache-mod_mime + apache-mod_mime_magic + apache-mod_negotiation + apache-mod_rewrite + apache-mod_setenvif + apache-mod_speling + apache-mod_userdir + apache-mod_version + apache-mod_vhost_alias + apache-tools + + + + + apache_mod + Apache 2.2.x additional packages + false + true + Apache 2.2.x additional packages + + apache-apxs + apache-cgi_test + apache-dbmtools + apache-doc + apache-mod_actions + apache-mod_cern_meta + apache-mod_asis + apache-mod_auth_dbm + apache-mod_auth_digest + apache-mod_authn_alias + apache-mod_authn_anon + apache-mod_authn_dbd + apache-mod_authn_dbm + apache-mod_authnz_ldap + apache-mod_authz_dbm + apache-mod_cache + apache-mod_case_filter + apache-mod_case_filter_in + apache-mod_cgi + apache-mod_cgid + apache-mod_charset_lite + apache-mod_dav + apache-mod_dbd + apache-mod_dumpio + apache-mod_echo + apache-mod_expires + apache-mod_ext_filter + apache-mod_file_cache + apache-mod_filter + apache-mod_headers + apache-mod_ident + apache-mod_imagemap + apache-mod_info + apache-mod_ldap + apache-mod_log_forensic + apache-mod_logio + apache-mod_proxy + apache-mod_ssl + apache-mod_status + apache-mod_unique_id + apache-mod_usertrack + apache-suexec + + + + + apache1_base + Apache 1.3.x web server base packages + false + true + Apache 1.3.x web server base packages + + apache1 + apache1-errordocs + apache1-index + apache1-mod_access + apache1-mod_asis + apache1-mod_auth + apache1-mod_autoindex + apache1-mod_cern_meta + apache1-mod_cgi + apache1-mod_dir + apache1-mod_env + apache1-mod_log_config + apache1-mod_mime + apache1-mod_mime_magic + apache1-mod_negotiation + apache1-mod_rewrite + apache1-mod_setenvif + apache1-mod_speling + apache1-mod_ssl + apache1-mod_userdir + apache1-mod_vhost_alias + apache1-tools + + + + + apache1_mod + Apache 1.3.x additional packages + false + true + Apache 1.3.x additional packages + + apache1-apxs + apache1-doc + apache1-mod_actions + apache1-mod_alias + apache1-mod_auth_anon + apache1-mod_auth_db + apache1-mod_auth_digest + apache1-mod_define + apache1-mod_digest + apache1-mod_expires + apache1-mod_headers + apache1-mod_imap + apache1-mod_include + apache1-mod_info + apache1-mod_log_agent + apache1-mod_log_forensic + apache1-mod_log_referer + apache1-mod_mmap_static + apache1-mod_proxy + apache1-mod_status + apache1-mod_sxnet + apache1-mod_unique_id + apache1-mod_usertrack + apache1-suexec + + + + + php_base + PHP 5.x, base packages + false + true + PHP 5.x, base packages + + apache-mod_php + apache1-mod_php + php-cgi + php-cli + php-common + php-curl + php-gettext + php-iconv + php-mbstring + php-mime_magic + php-msession + php-openssl + php-readline + php-sysvmsg + php-sysvsem + php-sysvshm + php-tokenizer + php-zlib + + + + + php_add + PHP 5.x, additional packages + false + true + PHP 5.x, additional packages + + php-bcmath + php-bzip2 + php-calendar + php-ctype + php-dba + php-dbase + php-dom + php-exif + php-fcgi + php-filepro + php-ftp + php-gd + php-gmp + php-imap + php-interbase + php-ldap + php-mcrypt + php-mhash + php-ming + php-mssql + php-mysql + php-mysqli + php-ncurses + php-odbc + php-pcntl + php-pdo + php-pdo-dblib + php-pdo-firebird + php-pdo-mysql + php-pdo-odbc + php-pdo-pgsql + php-pdo-sqlite + php-pgsql + php-posix + php-program + php-pspell + php-recode + php-shmop + php-snmp + php-soap + php-sockets + php-sqlite + php-sybase-ct + php-sybase + php-tidy + php-wddx + php-xml + php-xmlreader + php-xmlrpc + php-xsl + + + + + mysql + MySQL database server + false + true + MySQL database server + + mysql + mysql-client + mysql-libs + + + + + mysql_extras + MySQL extras + false + true + MySQL extras + + mysql-bench + mysql-extras + mysql-extras-perl + mysql-ndb + mysql-ndb-client + mysql-ndb-cpc + mysql-ndb-mgm + + + + + pgsql + PostgreSQL database server + false + true + PostgreSQL database server + + postgresql + postgresql-clients + postgresql-libs + + + + + pgsql_extras + PostgreSQL extras + false + true + PostgreSQL extras + + postgresql-doc + postgresql-ecpg + postgresql-module-lo + postgresql-module-pgcrypto + postgresql-module-plperl + postgresql-module-plpgsql + postgresql-module-plpython + postgresql-module-pltcl + postgresql-module-tsearch2 + + + + + crunchers + Packages for (de)compressing using strange formats + false + true + Packages for (de)compressing using strange formats + + zip + unzip + unace + unrar + unarj + + + + + xemacs + XEmacs editor/environment + false + true + XEmacs editor/environment + + xemacs + xemacs-Sun-pkg + xemacs-auctex-pkg + xemacs-cc-mode-pkg + xemacs-common + xemacs-debug-pkg + xemacs-dired-pkg + xemacs-edit-utils-pkg + xemacs-eterm-pkg + xemacs-extras + xemacs-fsf-compat-pkg + xemacs-games-pkg + xemacs-ispell-pkg + xemacs-jde-pkg + xemacs-mail-lib-pkg + xemacs-mailcrypt-pkg + xemacs-mh-e-pkg + xemacs-nox + xemacs-parigp-mode-pkg + xemacs-pc-pkg + xemacs-psgml-pkg + xemacs-sgml-pkg + xemacs-spec-mode-pkg + xemacs-speedbar-pkg + xemacs-text-modes-pkg + xemacs-vc-pkg + xemacs-w3-pkg + + + + + devel + Basic programs needed for compilation. + false + true + Basic programs needed for compilation. + + glibc-devel + autoconf + automake + bison + cpp + cproto + ctags + cvs + gcc + make + binutils + flex + kernel-headers + gcc-c++ + objc + gcc-objc + patch + diffutils + m4 + perl-devel + rpm-build + ncurses-devel + bin86 + + + + + devel_extras + Packages needed for real development. + false + true + Packages needed for real development. + + gdb + lcc + ElectricFence + ddd + diffstat + doxygen + indent + nasm + ocaml + python + python-devel + python-doc + + + + + java + Packages needed for JAVA development. + false + true + Packages needed for JAVA development. + + kaffe + jikes + gcc-java + + + + + xwindow + X Window System + false + true + X Window System + + xinitrc-ng + xorg-app-appres + xorg-app-bitmap + xorg-app-editres + xorg-app-iceauth + xorg-app-lbxproxy + xorg-app-luit + xorg-app-mkfontdir + xorg-app-mkfontscale + xorg-app-proxymngr + xorg-app-rgb + xorg-app-rstart + xorg-app-scripts + xorg-app-setxkbmap + xorg-app-smproxy + xorg-app-x11perf + xorg-app-xclipboard + xorg-app-xcmsdb + xorg-app-xconsole + xorg-app-xcursorgen + xorg-app-xditview + xorg-app-xdpyinfo + xorg-app-xf86dga + xorg-app-xfindproxy + xorg-app-xfwp + xorg-app-xgamma + xorg-app-xhost + xorg-app-xinit + xorg-app-xkbevd + xorg-app-xkbprint + xorg-app-xkbutils + xorg-app-xlsatoms + xorg-app-xlsclients + xorg-app-xlsfonts + xorg-app-xmh + xorg-app-xmodmap + xorg-app-xprop + xorg-app-xrandr + xorg-app-xrdb + xorg-app-xrefresh + xorg-app-xrx + xorg-app-xset + xorg-app-xsetmode + xorg-app-xsetpointer + xorg-app-xsetroot + xorg-app-xsm + xorg-app-xstdcmap + xorg-app-xvidtune + xorg-app-xvinfo + xorg-app-xwd + xorg-app-xwud + xorg-data-xbitmaps + xorg-docs + xorg-lib-libXpm-utils + xorg-util-imake + xorg-util-lndir + xorg-xserver-Xprt + xorg-xserver-server + Mesa-libGL + Mesa-libGLU + Mesa-utils + Mesa-libGL-devel + Mesa-libGL-static + xorg-xserver-Xnest + xorg-xserver-Xprt + xorg-xserver-server + xorg-xserver-Xvfb + xorg-app-bdftopcf + xorg-lib-libFS-devel + xorg-lib-libICE-devel + xorg-lib-libSM-devel + xorg-lib-libX11-devel + xorg-lib-libXau-devel + xorg-lib-libXaw-devel + xorg-lib-libXcomposite-devel + xorg-lib-libXcursor-devel + xorg-lib-libXdamage-devel + xorg-lib-libXdmcp-devel + xorg-lib-libXevie-devel + xorg-lib-libXfixes-devel + xorg-lib-libXfont-devel + xorg-lib-libXft-devel + xorg-lib-libXmu-devel + xorg-lib-libXpm-devel + xorg-lib-libXprintAppUtil-devel + xorg-lib-libXprintUtil-devel + xorg-lib-libXrandr-devel + xorg-lib-libXrender-devel + xorg-lib-libXres-devel + xorg-lib-libXt-devel + xorg-lib-libXv-devel + xorg-lib-libXvMC-devel + xorg-lib-libfontenc-devel + xorg-lib-liboldX-devel + xorg-lib-libxkbfile-devel + xorg-lib-libxkbui-devel + xorg-proto-applewmproto-devel + xorg-proto-bigreqsproto-devel + xorg-proto-compositeproto-devel + xorg-proto-damageproto-devel + xorg-proto-dmxproto-devel + xorg-proto-evieext-devel + xorg-proto-fixesproto-devel + xorg-proto-fontcacheproto-devel + xorg-proto-fontsproto-devel + xorg-proto-inputproto-devel + xorg-proto-kbproto-devel + xorg-proto-printproto-devel + xorg-proto-randrproto-devel + xorg-proto-recordproto-devel + xorg-proto-renderproto-devel + xorg-proto-resourceproto-devel + xorg-proto-scrnsaverproto-devel + xorg-proto-trapproto-devel + xorg-proto-videoproto-devel + xorg-proto-windowswmproto-devel + xorg-proto-xcmiscproto-devel + xorg-proto-xextproto-devel + xorg-proto-xf86bigfontproto-devel + xorg-proto-xf86dgaproto-devel + xorg-proto-xf86miscproto-devel + xorg-proto-xf86rushproto-devel + xorg-proto-xf86vidmodeproto-devel + xorg-proto-xineramaproto-devel + xorg-proto-xproto-devel + xorg-proto-xproxymanagementprotocol-devel + xorg-xserver-server-devel + xorg-driver-video-apm + xorg-driver-video-ark + xorg-driver-video-ati + xorg-driver-video-ati + xorg-driver-video-ati + Mesa-dri-driver-ati-rage128 + Mesa-dri-driver-ati-radeon-R100 + Mesa-dri-driver-ati-radeon-R200 + Mesa-dri-driver-ati-radeon-R300 + xorg-driver-video-chips + xorg-driver-video-cirrus + xorg-driver-video-cyrix + xorg-driver-video-fbdev + xorg-driver-video-voodoo + xorg-driver-video-glint + xorg-driver-video-i128 + xorg-xserver-server + xorg-driver-video-i740 + xorg-driver-video-i810 + Mesa-dri-driver-intel-i810 + Mesa-dri-driver-intel-i915 + Mesa-dri-driver-intel-i965 + xorg-driver-video-mga + Mesa-dri-driver-matrox + xorg-driver-video-neomagic + xorg-driver-video-newport + xorg-driver-video-nsc + xorg-driver-video-nv + xorg-driver-video-rendition + Mesa-dri-driver-s3virge + xorg-driver-video-s3virge + xorg-driver-video-s3 + Mesa-dri-driver-savage + xorg-driver-video-savage + xorg-driver-video-siliconmotion + xorg-driver-video-sis + Mesa-dri-driver-sis + xorg-driver-video-sisusb + xorg-driver-video-sunbw2 + xorg-driver-video-suncg14 + xorg-driver-video-suncg3 + xorg-driver-video-suncg6 + Mesa-dri-driver-ffb + xorg-driver-video-sunffb + xorg-driver-video-sunleo + xorg-driver-video-suntcx + xorg-driver-video-tdfx + Mesa-dri-driver-tdfx + xorg-driver-video-tga + Mesa-dri-driver-trident + xorg-driver-video-trident + xorg-driver-video-tseng + Mesa-dri-driver-via-unichrome + xorg-driver-video-via + xorg-driver-video-vmware + xorg-app-bitmap + xorg-app-xditview + xorg-app-xmh + xorg-data-xbitmaps + xorg-app-xkbcomp + xorg-driver-input-keyboard + xorg-driver-input-mouse + xorg-driver-video-v4l + xorg-driver-video-vesa + xorg-driver-video-vga + xorg-xserver-server + xorg-lib-libFS-static + xorg-lib-libICE-static + xorg-lib-libSM-static + xorg-lib-libX11-static + xorg-lib-libXScrnSaver-static + xorg-lib-libXTrap-static + xorg-lib-libXau-static + xorg-lib-libXcomposite-static + xorg-lib-libXcursor-static + xorg-lib-libXdamage-static + xorg-lib-libXdmcp-static + xorg-lib-libXevie-static + xorg-lib-libXext-static + xorg-lib-libXfixes-static + xorg-lib-libXfont-static + xorg-lib-libXfontcache-static + xorg-lib-libXft-static + xorg-lib-libXi-static + xorg-lib-libXinerama-static + xorg-lib-libXmu-static + xorg-lib-libXp-static + xorg-lib-libXpm-static + xorg-lib-libXrandr-static + xorg-lib-libXrender-static + xorg-lib-libXres-static + xorg-lib-libXt-static + xorg-lib-libXtst-static + xorg-lib-libXv-static + xorg-lib-libXvMC-static + xorg-lib-libXxf86dga-static + xorg-lib-libXxf86misc-static + xorg-lib-libXxf86vm-static + xorg-lib-libfontenc-static + xorg-lib-libxkbfile-static + xorg-lib-libxkbui-static + xorg-app-beforelight + xorg-app-ico + xorg-app-listres + xorg-app-oclock + xorg-app-showfont + xorg-app-viewres + xorg-app-x11perf + xorg-app-xbiff + xorg-app-xcalc + xorg-app-xclipboard + xorg-app-xclock + xorg-app-xdbedizzy + xorg-app-xditview + xorg-app-xdriinfo + xorg-app-xedit + xorg-app-xev + xorg-app-xeyes + xorg-app-xfd + xorg-app-xfontsel + xorg-app-xgc + xorg-app-xkill + xorg-app-xload + xorg-app-xlogo + xorg-app-xmag + xorg-app-xman + xorg-app-xmessage + xorg-app-xmh + xorg-app-xmore + xorg-app-xphelloworld + xorg-app-xplsprinters + xorg-app-xpr + xorg-app-xprehashprinterlist + xorg-app-xprop + xorg-app-xtrap + xorg-app-xwininfo + xorg-util-gccmakedep + xorg-util-imake + xorg-app-sessreg + xorg-app-twm + xorg-app-xauth + xorg-app-xdm + xorg-app-xfs + xorg-font-encodings + xorg-font-font-adobe-utopia-type1 + xorg-font-font-arabic-misc + xorg-font-font-bh-ttf + xorg-font-font-bh-type1 + xorg-font-font-bitstream-type1 + xorg-font-font-daewoo-misc + xorg-font-font-dec-misc + xorg-font-font-ibm-type1 + xorg-font-font-isas-misc + xorg-font-font-micro-misc + xorg-font-font-misc-ethiopic + xorg-font-font-misc-meltho + xorg-font-font-misc-misc + xorg-font-font-mutt-misc + xorg-font-font-schumacher-misc + xorg-font-font-sony-misc + xorg-font-font-sun-misc + xorg-font-font-xfree86-type1 + xorg-font-font-util + xorg-font-font-cursor-misc + xorg-font-font-misc-misc-base + xorg-font-font-misc-misc-ISO8859-1 + xorg-font-font-misc-misc-ISO8859-2 + xorg-font-font-misc-misc-ISO8859-3 + xorg-font-font-misc-misc-ISO8859-4 + xorg-font-font-misc-misc-ISO8859-5 + xorg-font-font-misc-misc-ISO8859-7 + xorg-font-font-misc-misc-ISO8859-8 + xorg-font-font-misc-misc-ISO8859-9 + xorg-font-font-misc-misc-ISO8859-10 + xorg-font-font-misc-misc-ISO8859-11 + xorg-font-font-misc-misc-ISO8859-13 + xorg-font-font-misc-misc-ISO8859-14 + xorg-font-font-misc-misc-ISO8859-15 + xorg-font-font-misc-misc-ISO8859-16 + xorg-font-font-adobe-75dpi + xorg-font-font-adobe-utopia-75dpi + xorg-font-font-bh-75dpi + xorg-font-font-bh-lucidatypewriter-75dpi + xorg-font-font-bitstream-75dpi + xorg-font-font-adobe-100dpi + xorg-font-font-adobe-utopia-100dpi + xorg-font-font-bh-100dpi + xorg-font-font-bh-lucidatypewriter-100dpi + xorg-font-font-bitstream-100dpi + xorg-font-font-jis-misc + xorg-font-font-cronyx-cyrillic + xorg-font-font-misc-cyrillic + xorg-font-font-screen-cyrillic + xorg-font-font-winitzki-cyrillic + xorg-font-font-misc-ethiopic + xorg-font-font-misc-meltho + X11-DPS + xorg-xserver-server + Mesa-libGL + Mesa-libGLU + Mesa-utils + xorg-xserver-Xnest + xorg-xserver-Xprt + xorg-xserver-Xprt + xorg-xserver-server + xorg-xserver-server-devel + xorg-xserver-Xvfb + X11-common + xorg-driver-video-apm + xorg-driver-video-ark + xorg-driver-video-ati + xorg-driver-video-chips + xorg-driver-video-cirrus + xorg-driver-video-cyrix + X11-driver-evdev + xorg-driver-video-fbdev + xorg-driver-video-voodoo + xorg-driver-video-glint + xorg-driver-video-i128 + xorg-driver-video-i740 + xorg-driver-video-i810 + Mesa-dri-driver-intel-i810 + Mesa-dri-driver-intel-i915 + Mesa-dri-driver-intel-i965 + xorg-driver-video-mga + Mesa-dri-driver-matrox + xorg-driver-video-neomagic + xorg-driver-video-nsc + xorg-driver-video-nv + xorg-driver-video-ati + Mesa-dri-driver-ati-rage128 + xorg-driver-video-ati + Mesa-dri-driver-ati-radeon-R100 + Mesa-dri-driver-ati-radeon-R200 + Mesa-dri-driver-ati-radeon-R300 + xorg-driver-video-rendition + Mesa-dri-driver-s3virge + xorg-driver-video-s3virge + xorg-driver-video-s3 + Mesa-dri-driver-s3virge + xorg-driver-video-s3virge + Mesa-dri-driver-savage + xorg-driver-video-savage + xorg-driver-video-siliconmotion + xorg-driver-video-sis + Mesa-dri-driver-sis + xorg-driver-video-sisusb + xorg-driver-video-tdfx + Mesa-dri-driver-tdfx + xorg-driver-video-tga + Mesa-dri-driver-trident + xorg-driver-video-trident + xorg-driver-video-tseng + Mesa-dri-driver-via-unichrome + xorg-driver-video-via + xorg-driver-video-vmware + xorg-font-font-adobe-100dpi + xorg-font-font-adobe-utopia-100dpi + xorg-font-font-bh-100dpi + xorg-font-font-bh-lucidatypewriter-100dpi + xorg-font-font-bitstream-100dpi + X11-fonts-100dpi-ISO8859-1 + X11-fonts-100dpi-ISO8859-10 + X11-fonts-100dpi-ISO8859-13 + X11-fonts-100dpi-ISO8859-14 + X11-fonts-100dpi-ISO8859-15 + X11-fonts-100dpi-ISO8859-2 + X11-fonts-100dpi-ISO8859-3 + X11-fonts-100dpi-ISO8859-4 + X11-fonts-100dpi-ISO8859-9 + xorg-font-encodings + xorg-font-font-adobe-utopia-type1 + xorg-font-font-arabic-misc + xorg-font-font-bh-ttf + xorg-font-font-bh-type1 + xorg-font-font-bitstream-type1 + xorg-font-font-daewoo-misc + xorg-font-font-dec-misc + xorg-font-font-ibm-type1 + xorg-font-font-isas-misc + xorg-font-font-micro-misc + xorg-font-font-misc-ethiopic + xorg-font-font-misc-meltho + xorg-font-font-misc-misc + xorg-font-font-mutt-misc + xorg-font-font-schumacher-misc + xorg-font-font-sony-misc + xorg-font-font-sun-misc + xorg-font-font-xfree86-type1 + xorg-font-font-util + xorg-font-font-cursor-misc + xorg-font-font-misc-misc-base + xorg-font-font-misc-misc-ISO8859-1 + xorg-font-font-misc-misc-ISO8859-2 + xorg-font-font-misc-misc-ISO8859-3 + xorg-font-font-misc-misc-ISO8859-4 + xorg-font-font-misc-misc-ISO8859-5 + xorg-font-font-misc-misc-ISO8859-7 + xorg-font-font-misc-misc-ISO8859-8 + xorg-font-font-misc-misc-ISO8859-9 + xorg-font-font-misc-misc-ISO8859-10 + xorg-font-font-misc-misc-ISO8859-11 + xorg-font-font-misc-misc-ISO8859-13 + xorg-font-font-misc-misc-ISO8859-14 + xorg-font-font-misc-misc-ISO8859-15 + xorg-font-font-misc-misc-ISO8859-16 + xorg-font-font-adobe-75dpi + xorg-font-font-adobe-utopia-75dpi + xorg-font-font-bh-75dpi + xorg-font-font-bh-lucidatypewriter-75dpi + xorg-font-font-bitstream-75dpi + xorg-font-font-adobe-100dpi + xorg-font-font-adobe-utopia-100dpi + xorg-font-font-bh-100dpi + xorg-font-font-bh-lucidatypewriter-100dpi + xorg-font-font-bitstream-100dpi + xorg-font-font-jis-misc + xorg-font-font-cronyx-cyrillic + xorg-font-font-misc-cyrillic + xorg-font-font-screen-cyrillic + xorg-font-font-winitzki-cyrillic + xorg-font-font-misc-ethiopic + xorg-font-font-misc-meltho + xorg-font-font-adobe-75dpi + xorg-font-font-adobe-utopia-75dpi + xorg-font-font-bh-75dpi + xorg-font-font-bh-lucidatypewriter-75dpi + xorg-font-font-bitstream-75dpi + X11-fonts-75dpi-ISO8859-1 + X11-fonts-75dpi-ISO8859-10 + X11-fonts-75dpi-ISO8859-13 + X11-fonts-75dpi-ISO8859-14 + X11-fonts-75dpi-ISO8859-15 + X11-fonts-75dpi-ISO8859-2 + X11-fonts-75dpi-ISO8859-3 + X11-fonts-75dpi-ISO8859-4 + X11-fonts-75dpi-ISO8859-9 + xorg-font-font-misc-ethiopic + xorg-font-font-misc-misc-ISO8859-1 + xorg-font-font-misc-misc-ISO8859-10 + xorg-font-font-misc-misc-ISO8859-11 + xorg-font-font-misc-misc-ISO8859-13 + xorg-font-font-misc-misc-ISO8859-14 + xorg-font-font-misc-misc-ISO8859-15 + xorg-font-font-misc-misc-ISO8859-16 + xorg-font-font-misc-misc-ISO8859-10 + xorg-font-font-misc-misc-ISO8859-11 + xorg-font-font-misc-misc-ISO8859-13 + xorg-font-font-misc-misc-ISO8859-14 + xorg-font-font-misc-misc-ISO8859-15 + xorg-font-font-misc-misc-ISO8859-16 + xorg-font-font-misc-misc-ISO8859-2 + xorg-font-font-misc-misc-ISO8859-3 + xorg-font-font-misc-misc-ISO8859-4 + xorg-font-font-misc-misc-ISO8859-5 + xorg-font-font-misc-misc-ISO8859-7 + xorg-font-font-misc-misc-ISO8859-8 + xorg-font-font-misc-misc-ISO8859-9 + xorg-font-font-jis-misc + xorg-font-font-cronyx-cyrillic + xorg-font-font-misc-cyrillic + xorg-font-font-screen-cyrillic + xorg-font-font-winitzki-cyrillic + xorg-font-font-misc-meltho + xorg-font-font-cursor-misc + xorg-font-font-misc-misc-base + xorg-font-font-util + xorg-util-gccmakedep + xorg-util-imake + X11-input-synaptics + xorg-app-bitmap + xorg-app-xditview + xorg-app-xmh + xorg-data-xbitmaps + xorg-app-xkbcomp + xorg-driver-input-keyboard + xorg-driver-input-mouse + xorg-driver-video-v4l + xorg-driver-video-vesa + xorg-driver-video-vga + xorg-app-sessreg + xorg-xserver-server + X11-synaptics + xorg-app-beforelight + xorg-app-ico + xorg-app-listres + xorg-app-oclock + xorg-app-showfont + xorg-app-viewres + xorg-app-x11perf + xorg-app-xbiff + xorg-app-xcalc + xorg-app-xclipboard + xorg-app-xclock + xorg-app-xdbedizzy + xorg-app-xditview + xorg-app-xdriinfo + xorg-app-xedit + xorg-app-xev + xorg-app-xeyes + xorg-app-xfd + xorg-app-xfontsel + xorg-app-xgc + xorg-app-xkill + xorg-app-xload + xorg-app-xlogo + xorg-app-xmag + xorg-app-xman + xorg-app-xmessage + xorg-app-xmh + xorg-app-xmore + xorg-app-xphelloworld + xorg-app-xplsprinters + xorg-app-xpr + xorg-app-xprehashprinterlist + xorg-app-xprop + xorg-app-xtrap + xorg-app-xwininfo + xorg-app-twm + xorg-app-xauth + xorg-app-xfs + + + + + gnome + Very basic GNOME Desktop part + false + true + Very basic GNOME Desktop part + + gnome-control-center + gedit2 + gnome-menus + gnome-panel + gnome-session + gnome-desktop + gnome-terminal + gnome-themes-Clearlooks + metacity + nautilus + hal + dbus + gdm + dbus-init + + + + + + gnome_complete + More complete GNOME Desktop part + false + true + More complete GNOME Desktop part + + bug-buddy + eog + epiphany + epiphany-extensions + evince + file-roller + gcalctool + gconf-editor + gnome-applets-accessx-status + gnome-applets-battstat + gnome-applets-charpicker + gnome-applets-cpufreq + gnome-applets-drivemount + gnome-applets-geyes + gnome-applets-gtik + gnome-applets-gweather + gnome-applets-keyboard + gnome-applets-minicommander + gnome-applets-mixer + gnome-applets-modemlights + gnome-applets-multiload + gnome-applets-stickynotes + gnome-applets-trash + gnome-backgrounds + gnome-cups-manager + gnome-keyring-manager + gnome-mail-notification + gnome-media-cd + gnome-media-cddb + gnome-media-sound-recorder + gnome-media-volume-control + gnome-media-vumeter + gnome-menus-editor + gnome-netstatus + gnome-nettool + gnome-system-monitor + gnome-utils-dict + gnome-utils-floppy + gnome-utils-logview + gnome-utils-screenshot + gnome-utils-search-tool + gnome-volume-manager + nautilus-cd-burner + rhythmbox + sound-juicer + totem + cheese + xscreensaver-gnome2 + yelp + gnome-packagekit + NetworkManager-applet + gstreamer + gstreamer-plugins-base + gstreamer-plugins-good + gstreamer-plugins-bad + gstreamer-plugins-ugly + gstreamer-audiosink-alsa + gstreamer-imagesink-xv + gstreamer-audio-formats + gstreamer-cdaudio + gstreamer-flac + gstreamer-GConf + gstreamer-cdio + gstreamer-lame + gstreamer-mad + gstreamer-mpeg + gstreamer-musicbrainz + gstreamer-soup + gstreamer-taglib + gstreamer-theora + gstreamer-vorbis + gstreamer-video-effects + gstreamer-visualisation + gstreamer-wavpack + gstreamer-x264 + gstreamer-ximagesrc + gstreamer-xvid + gstreamer-dvdread + gstreamer-ffmpeg + + + + + gnome_games + GNOME Games + false + true + GNOME Games + + gnome-games-blackjack + gnome-games-extra-data-glines + gnome-games-extra-data-gnobots2 + gnome-games-extra-data-gnometris + gnome-games-extra-data-iagno + gnome-games-extra-data-mahjongg + gnome-games-extra-data-same-gnome + gnome-games-gataxx + gnome-games-glines + gnome-games-gnect + gnome-games-gnibbles + gnome-games-gnobots2 + gnome-games-gnometris + gnome-games-gnomine + gnome-games-gnotravex + gnome-games-gnotski + gnome-games-gtal + gnome-games-iagno + gnome-games-mahjongg + gnome-games-same-gnome + gnome-games-sol + gnome-games-stones + + + + + gnome_themes + GNOME Themes and Icons + false + true + GNOME Themes and Icons + + gnome-theme-aqua + gnome-theme-Bluecurve + gnome-theme-Industrial + gnome-themes-Crux + gnome-themes-Flat-Blue + gnome-themes-Glider + gnome-themes-Grand-Canyon + gnome-themes-HighContrast + gnome-themes-HighContrastInverse + gnome-themes-HighContrastLargePrint + gnome-themes-HighContrastLargePrintInverse + gnome-themes-LargePrint + gnome-themes-LowContrast + gnome-themes-LowContrastLargePrint + gnome-themes-Mist + gnome-themes-Ocean-Dream + gnome-themes-Sandwish + gnome-themes-Sandy + gnome-themes-Simple + gnome-themes-Smokey + gnome-themes-Smokey-Blue + gnome-themes-Smokey-Red + gnome-themes-Traditional + gnome-themes-extras-Amaranth + gnome-themes-extras-Gorilla + gnome-themes-extras-Lush + gnome-themes-extras-Nuvola + gnome-themes-extras-Wasp + gnome-icons-gartoon + gnome-icons-gperfection2 + gnome-icons-graphite + + + + + gnome_office + GNOME Office Packages + false + true + GNOME Office Packages + + abiword + abiword-clipart + abiword-plugin-aiksaurus + abiword-plugin-applix + abiword-plugin-babelfish + abiword-plugin-bmp + abiword-plugin-bz2 + abiword-plugin-capi + abiword-plugin-clarisworks + abiword-plugin-collab + abiword-plugin-command + abiword-plugin-dash + abiword-plugin-docbook + abiword-plugin-eml + abiword-plugin-freetranslation + abiword-plugin-gda + abiword-plugin-gdict + abiword-plugin-gimp + abiword-plugin-goffice + abiword-plugin-google + abiword-plugin-hancom + abiword-plugin-hrtext + abiword-plugin-iscii + abiword-plugin-jpeg + abiword-plugin-kword + abiword-plugin-latex + abiword-plugin-link-grammar + abiword-plugin-mathview + abiword-plugin-mif + abiword-plugin-mswrite + abiword-plugin-nroff + abiword-plugin-opendocument + abiword-plugin-openwritter + abiword-plugin-ots + abiword-plugin-palmdoc + abiword-plugin-passepartout + abiword-plugin-pdf + abiword-plugin-psion + abiword-plugin-rsvg + abiword-plugin-scripthappy + abiword-plugin-sdw + abiword-plugin-t602 + abiword-plugin-urldict + abiword-plugin-wikipedia + abiword-plugin-wmf + abiword-plugin-wml + abiword-plugin-wordperfect + abiword-plugin-xhtml + abiword-plugin-xslfo + dia + evolution + gimp + glabels + gnome-spell + gnucash + gnumeric + gnumeric-plugin-applix + gnumeric-plugin-dif + gnumeric-plugin-excel + gnumeric-plugin-gdaif + gnumeric-plugin-gnomedb + gnumeric-plugin-gnuoleo + gnumeric-plugin-html + gnumeric-plugin-lotus123 + gnumeric-plugin-openoffice + gnumeric-plugin-paradox + gnumeric-plugin-perl + gnumeric-plugin-planperfect + gnumeric-plugin-psiconv + gnumeric-plugin-python + gnumeric-plugin-qpro + gnumeric-plugin-sample + gnumeric-plugin-sc + gnumeric-plugin-sylk + gnumeric-plugin-xbase + gthumb + Guppi + inkscape + ToutDoux + + + + + wmaker + WindowMaker + false + true + WindowMaker + + WindowMaker + + + + + kde + Basic KDE part + false + true + Basic KDE part + + konqueror + kdelibs + kdebase-core + kdebase-desktop + kdebase-desktop-libs + kdebase-konsole + kdebase-kpager + kdebase-kwrite + kdebase-screensavers + kdebase-common-konsole + kdebase-common-filemanagement + kdebase-colorschemes + kde-logoutpic-default + kde-splash-Default + kdm + + + + + kde_extras1 + KDE additional packages + false + true + KDE additional packages + + kdeaddons-ark + kdeaddons-atlantikdesigner + kdeaddons-fsview + kdeaddons-kaddressbook-plugins + kdeaddons-kate + kdeaddons-kicker + kdeaddons-knewsticker + kdeaddons-konqueror + kdeaddons-ksig + kdeaddons-lnkforward + kdeaddons-noatun + kdeartwork-kworldclock-3.5.0-1 + kdeartwork-screensavers-3.5.0-1 + kdeartwork-sounds-3.5.0-1 + kdeartwork-wallpapers-3.5.0-1 + + + + + kde_extras2 + KDE additional packages2 + false + true + KDE additional packages2 + + kdeutils-ark + kdeutils-kcalc + kdeutils-kcharselect + kdeutils-kdelirc + kdeutils-kdessh + kdeutils-kdf + kdeutils-kedit + kdeutils-kfloppy + kdeutils-kgpg + kdeutils-khexedit + kdeutils-kjots + kdeutils-klaptopdaemon + kdeutils-kmilo + kdeutils-kmilo-thinkpad + kdeutils-kregexpeditor + kdeutils-ksim + kdeutils-ktimer + kdeutils-kwalletmanager + kdeutils-superkaramba + + + + + kde_kdepim + KDE PIM Packages + false + true + KDE PIM Packages + + kdepim + kdepim-apidocs + kdepim-kaddressbook + kdepim-kalarm + kdepim-kandy + kdepim-karm + kdepim-kmail + kdepim-knode + kdepim-knotes + kdepim-konsolekalendar + kdepim-korn + kdepim-kpilot + kdepim-libs + + + + + kde_kdeedu + KDE Edu Packages + false + true + KDE Edu Packages + + kdeedu-blinken + kdeedu-kalzium + kdeedu-kanagram + kdeedu-kbruch + kdeedu-keduca + kdeedu-kgeography + kdeedu-khangman + kdeedu-kig + kdeedu-kiten + kdeedu-klatin + kdeedu-klettres + kdeedu-kmplot + kdeedu-kpercentage + kdeedu-kstars + kdeedu-ktouch + kdeedu-kturtle + kdeedu-kverbos + kdeedu-kvoctrain + kdeedu-kwordquiz + kdeedu-libextdate + kdeedu-libkdeeducore + kdeedu-libkdeeduplot + kdeedu-libkdeeduui + kdeedu-libkiten + + + + + kde_multimedia + KDE Multimedia Packages + false + true + KDE Multimedia Packages + + kdemultimedia-akode + kdemultimedia-arts + kdemultimedia-artsbuilder + kdemultimedia-artscontrol + kdemultimedia-artsplugin-xine + kdemultimedia-audiocd + kdemultimedia-cddb + kdemultimedia-juk + kdemultimedia-kaboodle + kdemultimedia-kappfinder + kdemultimedia-kaudiocreator + kdemultimedia-kfile + kdemultimedia-kmid + kdemultimedia-kmix + kdemultimedia-krec + kdemultimedia-kscd + kdemultimedia-libkcddb + kdemultimedia-mpeglib + kdemultimedia-mpeglib-examples + kdemultimedia-noatun + kdemultimedia-noatun-libs + + + + + kde_koffice + KOffice Packages + false + true + KOffice Packages + + koffice-apidocs + koffice-common + koffice-karbon + koffice-kchart + koffice-kexi + koffice-kformula + koffice-kivio + koffice-kpresenter + koffice-krita + koffice-kspread + koffice-kugar + koffice-kword + + + + + kde_network + KDE Network Packages + false + true + KDE Network Packages + + kdenetwork-filesharing + kdenetwork-kdict + kdenetwork-kdnssd + kdenetwork-kfile-torrent + kdenetwork-kget + kdenetwork-kinetd + kdenetwork-knewsticker + kdenetwork-kopete + kdenetwork-kopete-protocol-aim + kdenetwork-kopete-protocol-gg + kdenetwork-kopete-protocol-groupwise + kdenetwork-kopete-protocol-icq + kdenetwork-kopete-protocol-irc + kdenetwork-kopete-protocol-jabber + kdenetwork-kopete-protocol-meanwhile + kdenetwork-kopete-protocol-msn + kdenetwork-kopete-protocol-sms + kdenetwork-kopete-protocol-winpopup + kdenetwork-kopete-protocol-yahoo + kdenetwork-kopete-tool-alias + kdenetwork-kopete-tool-autoreplace + kdenetwork-kopete-tool-avdeviceconfig + kdenetwork-kopete-tool-conectionstatus + kdenetwork-kopete-tool-contactnotes + kdenetwork-kopete-tool-cryptography + kdenetwork-kopete-tool-highlight + kdenetwork-kopete-tool-history + kdenetwork-kopete-tool-latex + kdenetwork-kopete-tool-nowlistening + kdenetwork-kopete-tool-smpppdcs + kdenetwork-kopete-tool-texteffect + kdenetwork-kopete-tool-translator + kdenetwork-kopete-tool-webpresence + kdenetwork-kpf + kdenetwork-kppp + kdenetwork-krfb + kdenetwork-ksirc + kdenetwork-ktalkd + kdenetwork-kwifimanager + kdenetwork-lanbrowser + kdenetwork-libkopete + kdenetwork-libkopete_msn + kdenetwork-libkopete_oscar + kdenetwork-libkopete_videodevice + kdenetwork-librss + kdenetwork-rss + + + + + kde_graphics + KDE Graphics + false + true + KDE Graphics + + kdegraphics-daemonwatcher + kdegraphics-kamera + kdegraphics-kcolorchooser + kdegraphics-kcoloredit + kdegraphics-kdvi + kdegraphics-kfax + kdegraphics-kfile + kdegraphics-kgamma + kdegraphics-kghostview + kdegraphics-kiconedit + kdegraphics-kmrml + kdegraphics-kolourpaint + kdegraphics-kooka + kdegraphics-kpdf + kdegraphics-kpovmodeler + kdegraphics-kruler + kdegraphics-ksnapshot + kdegraphics-ksvg + kdegraphics-kuickshow + kdegraphics-kview + kdegraphics-kviewshell + + + + + kde_admin + KDE Admin packages + false + true + KDE Admin packages + + kdeadmin-kcmlilo + kdeadmin-kcron + kdeadmin-kdat + kdeadmin-knetworkconf + kdeadmin-kpackage + kdeadmin-ksysv + kdeadmin-kuser + + + + + kde_games + Games for KDE + false + true + Games for KDE + + kdegames + kdegames-apidocs + kdegames-atlantik + kdegames-carddecks + kdegames-kasteroids + kdegames-katomic + kdegames-kbackgammon + kdegames-kbattleship + kdegames-kblackbox + kdegames-kbounce + kdegames-kenolaba + kdegames-kfouleggs + kdegames-kgoldrunner + kdegames-kjumpingcube + kdegames-klickety + kdegames-klines + kdegames-kmahjongg + kdegames-kmines + kdegames-knetwalk + kdegames-kolf + kdegames-konquest + kdegames-kpat + kdegames-kpoker + kdegames-kreversi + kdegames-ksame + kdegames-kshisen + kdegames-ksirtet + kdegames-ksmiletris + kdegames-ksnake + kdegames-ksokoban + kdegames-kspaceduel + kdegames-ktron + kdegames-ktuberling + kdegames-kwin4 + kdegames-lskat + + + + + kde_accessibility + KDE Accessibility packages + false + true + KDE Accessibility packages + + kdeaccessibility-kbstateapplet + kdeaccessibility-kmag + kdeaccessibility-kmousetool + kdeaccessibility-kmouth + kdeaccessibility-ksayit + kdeaccessibility-kttsd + kdeaccessibility-kttsd-akode + kdeaccessibility-kttsd-gstreamer + + + + + kde_look + Packages for changing KDE look + false + true + Packages for changing KDE look + + kde-decoration-ActiveHeart + kde-decoration-alloy + kde-decoration-aqua + kde-decoration-b2 + kde-decoration-baghira + kde-decoration-cde + kde-decoration-crystal + kde-decoration-dotcurve + kde-decoration-fahrenheit + kde-decoration-glow + kde-decoration-gorilla + kde-decoration-icewm + kde-decoration-icewm-Suedstern + kde-decoration-icewm-Wasp + kde-decoration-icewm-xp + kde-decoration-kde1 + kde-decoration-keramik + kde-decoration-kstep + kde-decoration-laptop + kde-decoration-modernsys + kde-decoration-nvidia + kde-decoration-openlook + kde-decoration-quartz + kde-decoration-redmond + kde-decoration-ridge + kde-decoration-riscos + kde-decoration-smoothblend + kde-decoration-system + kde-decoration-thinkeramik + kde-decoration-web + kde-colorscheme-ActiveHeart + kde-colorscheme-alloy + kde-colorscheme-baghira + kde-colorscheme-gorilla + kde-colorscheme-konx + kde-colorscheme-nvidia + kde-colorscheme-ridge + kde-colorscheme-Suedstern + kde-colorscheme-thinkeramik + kde-colorscheme-Wasp + kde-colorscheme-Wasp-activeheart + kde-colorscheme-Wasp-thinkeramik + kde-icons-amaranth + kde-icons-amaranth-althaea + kde-icons-aqua + kde-icons-aquafusion + kde-icons-gorilla + kde-icons-ikons + kde-icons-kdeclassic + kde-icons-kids + kde-icons-Locolor + kde-icons-mono + kde-icons-noia + kde-icons-Nuvola + kde-icons-outline + kde-icons-Realistic + kde-icons-slick + kde-icons-sparkling + kde-icons-Suedstern + kde-icons-umicons + kde-icons-Wasp + kde-icons-xp + kde-splash-ac-chemician + kde-splash-ac-oiler + kde-splash-blue-bend + kde-splash-BlueTribe + kde-splash-Clock_K + kde-splash-Gears + kde-splash-gears_white + kde-splash-GNU_CooL + kde-splash-GNU_cool2 + kde-splash-gorilla + kde-splash-heregear + kde-splash-heregear-moodin + kde-splash-KDEGirl + kde-splash-keramik + kde-splash-Lila + kde-splash-login-scan-splash + kde-splash-moodin + kde-splash-MoonKDE + kde-splash-NWN + kde-splash-oldtimesplash + kde-splash-PhyNet + kde-splash-Rambo-tux + kde-splash-safari + kde-splash-Suedstern + kde-splash-think-linux + kde-splash-Wasp + kde-style-ActiveHeart + kde-style-alloy + kde-style-aqua + kde-style-baghira + kde-style-comix + kde-style-dotnet + kde-style-konx + kde-style-phase + kde-style-thinkeramik + kde-style-xp + kde-theme-ActiveHeart + kde-theme-alloy + kde-theme-aqua + kde-theme-baghira + kde-theme-Bluecurve + kde-theme-gorilla + kde-theme-konx + kde-theme-Suedstern + kde-theme-thinkeramik + kde-theme-umicons + kde-theme-Wasp + kde-theme-xp + + + + + kde_devel + KDE development tools + false + true + KDE development tools + + kdevelop + kdesdk-cervisia + kdesdk-completions-bash + kdesdk-completions-zsh + kdesdk-emacs + kdesdk-kapptemplate + kdesdk-kbabel + kdesdk-kbugbuster + kdesdk-kcachegrind + kdesdk-kde-resource-bugzilla + kdesdk-kde-resource-kdeaccounts + kdesdk-kfile + kdesdk-kmtrace + kdesdk-kompare + kdesdk-kprofilemethod + kdesdk-kspy + kdesdk-kstartperf + kdesdk-kuiviewer + kdesdk-kunittest + kdesdk-libcvsservice + kdesdk-pallette-gimp + kdesdk-pallette-xpaint + kdesdk-po2xml + kdesdk-scheck + kdesdk-scripts-cvs + kdesdk-scripts-doc + kdesdk-scripts-kdekillall + kdesdk-umbrello + kdesdk-xemacs + + + + + icewm + Basic IceWM + false + true + Basic IceWM + + icewm + + + + + science_apps + Scientific applications + false + true + Scientific applications + + parigp + calc + + + + + science_libs + Scientific libraries + false + true + Scientific libraries + + gmp + gsl + fftw + pari + + + + + science_devel + Scientific development packages + false + true + Scientific development packages + + gmp-devel + gsl-devel + gsl-progs + fftw-devel + pari-devel + parigp-gp2c + gcc-g77 + + + + + games + Textmode games + false + true + Textmode games + + bsd-games + fortune-mod + fortune-mod-bofh-excuses + + + + + games_x + Games for X. + false + true + Games for X. + + xbill + prboom + xpenguins + xsnow + bzflag + tuxkart + tuxracer + xmoto + + + + diff --git a/shell/bash-completion.sh b/shell/bash-completion.sh new file mode 100644 index 0000000..ca05c32 --- /dev/null +++ b/shell/bash-completion.sh @@ -0,0 +1,125 @@ +# various completions for pld-ftp-admin tools +# Author: Elan Ruusamäe + +# return list of pld ftp trees +_pfa_tree() { + local cur="$1" + if [ -z "$PFA_TREES" ]; then + local d + local -a trees + for d in ~/ftp/*/; do + d=${d%/} + d=${d##*/} + PFA_TREES+=" $d" + done + # kill leading space + PFA_TREES=${PFA_TREES# } + fi + + COMPREPLY=( $( compgen -W "$PFA_TREES" -- "$cur" ) ) +} + +# return list of files in a tree +_pfa_tree_files() { + local tree="$1" cur="$2" file i + local dir=~/ftp/$tree/SRPMS/.metadata + + # generate reply from dir + COMPREPLY=( $( compgen -f -X "*.(src.rpm.info)" -- "$dir/$cur" ) ) + # filter out dirname from results + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + file=${COMPREPLY[$i]##*/} + COMPREPLY[$i]=$file + done +} + +have pfa-genindex && +_pfa-genindex() +{ + local cur + + COMPREPLY=() + _get_comp_words_by_ref cur + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '\ + -q --quiet --index \ + --nopoldek --noyum --norpmrepo \ + --poldek --yum --rpmrepo \ + ' \ + -- "$cur" ) ) + else + _pfa_tree "$cur" + fi +} && +complete -F _pfa-genindex pfa-genindex + +have pfa-lintpkg && +_pfa-lintpkg() +{ + local cur arg + + COMPREPLY=() + _get_comp_words_by_ref cur + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-q -s --quiet' -- "$cur" ) ) + else + # The first argument is an tree; the rest are files in a dir + _count_args : + + if [[ $args == 1 ]]; then + _pfa_tree "$cur" + else + _get_first_arg + _pfa_tree_files "$arg" "$cur" + fi + fi +} && +complete -F _pfa-lintpkg pfa-lintpkg + +have pfa-signpkg && +_pfa-signpkg() +{ + local cur + + COMPREPLY=() + _get_comp_words_by_ref cur + + # The first argument is an tree; the rest are files in a dir + _count_args : + + if [[ $args == 1 ]]; then + _pfa_tree "$cur" + else + _pfa_tree_files "${COMP_WORDS[1]}" "$cur" + fi +} && +complete -F _pfa-signpkg pfa-signpkg pfa-rmpkg + +have pfa-mvpkg && +_pfa-mvpkg() +{ + local cur + + COMPREPLY=() + _get_comp_words_by_ref cur + + # The first two arguments are tree names; the rest are files in a dir + _count_args : + + if [[ $args == 1 ]] || [[ $args == 2 ]]; then + _pfa_tree "$cur" + else + _pfa_tree_files "${COMP_WORDS[1]}" "$cur" + fi +} && +complete -F _pfa-mvpkg pfa-mvpkg pfa-testmvpkg + +# Local variables: +# mode: shell-script +# sh-basic-offset: 4 +# sh-indent-comment: t +# indent-tabs-mode: nil +# End: +# ex: ts=4 sw=4 et filetype=sh diff --git a/shell/bash_profile b/shell/bash_profile new file mode 100644 index 0000000..8221875 --- /dev/null +++ b/shell/bash_profile @@ -0,0 +1,46 @@ +# .bash_profile - file executed when logging in + +# identify via remote addr +case "${SSH_CLIENT%% *}" in +195.222.9.201) # glen ;) + export FTPADM=glen + ;; +193.0.96.*|2001:6a0:5001:*) # baggins + export FTPADM=baggins + ;; +esac + +# identify via terminal last login (su, sudo) +if [ -z "$FTPADM" ]; then + LAST_TTY=$(tty) + LAST_LOGIN=$(last -if /var/run/utmp | awk -vtty="${LAST_TTY#/dev/}" '$2 == tty && $0 ~ /still logged in/ { print $1; exit; }') + export FTPADM=$LAST_LOGIN + unset LAST_TTY LAST_LOGIN +fi + +# per-admin defaults +case "$FTPADM" in +glen) + export TZ=EET + case $(id -un) in + pldth) + echo -ne "\033kth@ftp\033\\" + ;; + fpldac|pldac) + echo -ne "\033kac@ftp\033\\" + ;; + esac + ;; +'') + export FTPADM=$USER + ;; +esac + +# let each ftp admin have own history file +if [ "$FTPADM" ]; then + export HISTFILE=$HOME/.history-$FTPADM + export CDHISTFILE=$HOME/.cd_history-$FTPADM +fi +export EDITOR=vim + +PATH=$PATH:~/pld-ftp-admin/bin diff --git a/shell/bashrc b/shell/bashrc new file mode 100644 index 0000000..09ca3f5 --- /dev/null +++ b/shell/bashrc @@ -0,0 +1,37 @@ +# .bashrc - file executed when executing bash + +# ftp admin aliases +alias rmpkg=~/pld-ftp-admin/bin/pfa-rmpkg +alias gen-indexes=~/pld-ftp-admin/bin/pfa-genindex +alias dump-locks=~/pld-ftp-admin/bin/pfa-dump-locks +alias signpkg=~/pld-ftp-admin/bin/pfa-signpkg +alias mvpkg=~/pld-ftp-admin/bin/pfa-mvpkg +alias testmvpkg=~/pld-ftp-admin/bin/pfa-testmvpkg +alias lintpkg=~/pld-ftp-admin/bin/pfa-lintpkg + +# usual aliases to make your terminal usable +alias ls='ls --color=auto -BFN --show-control-chars' +alias l='ls -lh' +alias la='ls -la' +alias du='du -h' +alias df='df -Th' +alias vi='vim' +alias h='history $(($LINES-6))' + +# glen ;) +if [ "$FTPADM" = "glen" ]; then + [ -f /usr/share/okas/bashrc ] && . /usr/share/okas/bashrc +fi + +unlocktree() { + for tree in "$@"; do + ( + cd ~/pld-ftp-admin/modules + python -c " +import ftpio +ftpio.connect() +ftpio.unlock('$tree') +" + ) + done +} diff --git a/ucred/make.sh b/ucred/make.sh new file mode 100755 index 0000000..74c5f6f --- /dev/null +++ b/ucred/make.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +python setup.py build --build-lib ../modules + diff --git a/ucred/setup.py b/ucred/setup.py new file mode 100644 index 0000000..1730c01 --- /dev/null +++ b/ucred/setup.py @@ -0,0 +1,4 @@ +from distutils.core import setup, Extension +setup(name="ucred",#version="1.0", + ext_modules=[Extension("ucred", ["ucred.c"])]) + diff --git a/ucred/ucred.c b/ucred/ucred.c new file mode 100644 index 0000000..2428240 --- /dev/null +++ b/ucred/ucred.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +PyObject *g_socketerror; // socket.error + +static PyObject *ucred_sendcreds(PyObject *self, PyObject *args, PyObject *keywds); +static PyObject *ucred_recvcreds(PyObject *self, PyObject *args, PyObject *keywds); + +static PyMethodDef ucredMethods[] = { + {"sendcreds", (PyCFunction)ucred_sendcreds, METH_VARARGS|METH_KEYWORDS, NULL}, + {"recvcreds", (PyCFunction)ucred_recvcreds, METH_VARARGS|METH_KEYWORDS, NULL}, + {NULL, NULL, 0, NULL} +}; + +void initucred(void) { + PyObject *module; + module = Py_InitModule("ucred", ucredMethods); + + if(-1 == PyModule_AddIntConstant(module, "SO_PASSCRED", SO_PASSCRED)) { + return; + } + + module = PyImport_ImportModule("socket"); + if(!module) { + return; + } + g_socketerror = PyObject_GetAttrString(module, "error"); + if(!g_socketerror) { + return; + } +} + +static PyObject *ucred_sendcreds(PyObject *self, PyObject *args, PyObject *keywds) { + int fd; + int ret; + int pid=-1, uid=-1, gid=-1; + struct msghdr msg; + struct iovec iov[1]; + struct ucred uc; + unsigned char control[CMSG_SPACE(sizeof(struct ucred))]; + struct cmsghdr *cur; + + static char *kwlist[] = {"fd", + "pid", + "uid", + "gid", + NULL}; + +// kill(0, SIGTRAP); + if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|iii", kwlist, + &fd, + &pid, + &uid, + &gid)) { + return NULL; + } +// kill(0, SIGTRAP); + + if(pid!=-1) + uc.pid=pid; + else + uc.pid=getpid(); + if(uid!=-1) + uc.uid=uid; + else + uc.uid=getuid(); + if(gid!=-1) + uc.gid=gid; + else + uc.gid=getgid(); + + iov[0].iov_base="SCM_CREDENTIALS"; + iov[0].iov_len=15; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + cur = CMSG_FIRSTHDR(&msg); + cur->cmsg_level = SOL_SOCKET; + cur->cmsg_type = SCM_CREDENTIALS; + cur->cmsg_len = CMSG_SPACE(sizeof(struct ucred)); + memcpy(CMSG_DATA(cur), &uc, sizeof(struct ucred)); + + ret = sendmsg(fd, &msg, 0); + if(ret < 0) { + PyErr_SetFromErrno(g_socketerror); + return NULL; + } + + return Py_BuildValue("i", ret); +} + +static PyObject *ucred_recvcreds(PyObject *self, PyObject *args, PyObject *keywds) { + int fd; + int flags=0; + size_t maxsize=1024; + char control[CMSG_SPACE(sizeof(struct ucred))]; + int ret; + int pid=-1, uid=-1, gid=-1; + struct msghdr msg; + struct iovec iov[1]; + struct cmsghdr *cur; + struct ucred *uc; + + + static char *kwlist[] = {"fd", "flags", "maxsize", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|ii", kwlist, + &fd, &flags, &maxsize)) { + return NULL; + } + + memset(&msg, 0, sizeof(msg)); + + iov[0].iov_len = maxsize; + iov[0].iov_base = malloc(maxsize); + if (!iov[0].iov_base) { + PyErr_NoMemory(); + return NULL; + } + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + ret = recvmsg(fd, &msg, flags); + if (ret < 0) { + PyErr_SetFromErrno(g_socketerror); + free(iov[0].iov_base); + return NULL; + } + + cur=CMSG_FIRSTHDR(&msg); + + if(cur && cur->cmsg_type==SCM_CREDENTIALS && cur->cmsg_level==SOL_SOCKET) + { + assert(cur->cmsg_len >= sizeof(struct cmsghdr)); + // no null ancillary data messages? + + uc=(struct ucred*)CMSG_DATA(cur); + pid=uc->pid; + uid=uc->uid; + gid=uc->gid; + } + + { + PyObject *r; + r = Py_BuildValue("s#iii", + iov[0].iov_base, ret, + pid, uid, gid); + free(iov[0].iov_base); + return r; + } +} + + diff --git a/var/.keep b/var/.keep new file mode 100644 index 0000000..e69de29 diff --git a/wwwbin/ac-th-diff.py b/wwwbin/ac-th-diff.py new file mode 100755 index 0000000..b8a518d --- /dev/null +++ b/wwwbin/ac-th-diff.py @@ -0,0 +1,57 @@ +#!/usr/bin/python + +import os +import re +import struct + +acdir = "/home/ftp/pub/Linux/PLD/dists/ac/PLD/SRPMS/SRPMS" +thdir = "/home/ftp/pub/Linux/PLD/dists/th/PLD/SRPMS/RPMS/" + +thpkg = [] +acpkg = [] + +ign = '^(xorg-.*|X11-.*|XcursorTheme-.*)$' +re_c = re.compile(ign) + +re_n = re.compile('^(.*)-([^-]*)-([^-]*)$') + +def getname(file): + #f = os.popen('rpm --nomd5 --nodigest --nosignature -qp --queryformat "%{NAME}" ' + file, "r") + #name = f.read() + #f.close() + #f = open(file, 'rb') + #rpmlead = f.read(96) + #f.close() + #data = struct.unpack("6B2h66s2h16s", rpmlead) + #name = data[8].strip() + #print name + m = re_n.match(file) + name = m.group(1).strip() + return name + +for rpm in os.listdir(acdir): + if re_c.match(rpm): + continue + acpkg.append(getname(rpm)) + +for rpm in os.listdir(thdir): + if re_c.match(rpm): + continue + thpkg.append(getname(rpm)) + +thpkg.sort() +acpkg.sort() + +print "*****************************************************" +print "Packages in AC repo that are not in TH repo:" +for pkg in acpkg: + if pkg not in thpkg: + print pkg + +print +print +print "*****************************************************" +print "Packages in TH repo that are not in AC repo:" +for pkg in thpkg: + if pkg not in acpkg: + print pkg diff --git a/wwwbin/by-group.sh b/wwwbin/by-group.sh new file mode 100755 index 0000000..f00c93b --- /dev/null +++ b/wwwbin/by-group.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +dir="$1" +cd $dir || exit 1 + +for f in *.rpm ; do + g=$(rpm --qf '%{GROUP}' -qp $f) + level=".." + a="$g" + b= + while [ "$a" != "$b" ]; do + b="$a" + a=${a#*/} + level="${level}/.." + done + [ -d "../by-group/$g" ] || mkdir -p "../by-group/$g" + ln -sf "$level/RPMS/$f" "../by-group/$g/$f" +done + +# remove dangling symlinks... +symlinks -dr $dir/../by-group +# and empty directories +find $dir/../by-group -depth -type d -empty -exec rmdir {} \; diff --git a/wwwbin/checkrepo.sh b/wwwbin/checkrepo.sh new file mode 100755 index 0000000..db2cff1 --- /dev/null +++ b/wwwbin/checkrepo.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# stat(1) each file in .info to see if they exist + +REPODIR=${1:-/home/pld/admins/th/ftp/test} + +# expand files from .info file +expand_info() { + awk -F ':' -vD="$REPODIR/" ' + /file:/ { + if (/-debuginfo-/) { + print D $2 "/debuginfo/" $3 + } else { + print D $2 "/RPMS/" $3 + } + } + ' "$@" +} + +i=0 +nn= +ls -1 $REPODIR/SRPMS/RPMS | sed -rne "s/^((.+)-[^-]+-[^-]+\.src\.rpm)$/\1.info/p" | \ +while read pkg; do + for f in $(expand_info $REPODIR/SRPMS/.metadata/$pkg); do + if ! stat $f >/dev/null; then + echo "!!!: $pkg : $f" >&2 + fi + done +done diff --git a/wwwbin/clean-dups-archive.py b/wwwbin/clean-dups-archive.py new file mode 100755 index 0000000..4961c39 --- /dev/null +++ b/wwwbin/clean-dups-archive.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# arekm, 2008 +# remove + +import os +import re +import time +import rpm +import sys + +re_rpm = re.compile(r'.*\.rpm$') +re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.rpm$') +dir = '/home/pld/admins/th/ftp/.archive/PLD/SRPMS/RPMS' + +ts = rpm.TransactionSet("", (rpm.RPMVSF_NOHDRCHK or rpm.RPMVSF_NEEDPAYLOAD)) + +def compare(f1, f2): + try: + fd1 = os.open(os.path.join(dir, f1), os.O_RDONLY) + except Exception, e: + print e + # ignore non-files + return 0 + try: + fd2 = os.open(os.path.join(dir, f2), os.O_RDONLY) + except Exception, e: + print e + # ignore non-files + return 0 + h1 = ts.hdrFromFdno(fd1) + h2 = ts.hdrFromFdno(fd2) + os.close(fd1) + os.close(fd2) + + l1 = rpm.versionCompare(h1, h2) + l2 = rpm.versionCompare(h2, h1) + + if l1 > 0 and l2 > 0: + return 0 + + return -l1 + + +def find_old(files): + return sorted(files, compare) + +files = {} +dupes = {} + +for file in os.listdir(dir): + if not re_rpm.match(file): + continue + + m = re_nvr.match(file) + if not m: + print "problem with: %s" % file + sys.exit(1) + + if len(sys.argv) == 0: + p = os.path.join(dir, file) + mtime = os.stat(p).st_mtime + if mtime > time.time() - 3*86400: + continue + + name = m.group(1) + + if files.has_key(name): + if dupes.has_key(name): + dupes[name].append(file) + else: + dupes[name] = [ files[name] ] + dupes[name].append(file) + else: + files[name] = file + +for i in dupes.iterkeys(): + for old in find_old(dupes[i])[1:]: + os.system("/home/pld/admins/th/pld-ftp-admin/scripts/remove.py .archive/PLD %s" % old) diff --git a/wwwbin/clean-dups-old.py b/wwwbin/clean-dups-old.py new file mode 100755 index 0000000..e0ba2a0 --- /dev/null +++ b/wwwbin/clean-dups-old.py @@ -0,0 +1,69 @@ +#!/usr/bin/python +# arekm, 2008 +# remove + +import os +import re +import time +import rpm +import sys + +re_info = re.compile(r'.*\.info$') +re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.info$') +dir = '/home/pld/admins/th/ftp/test/SRPMS/.metadata' + +def compare(f1, f2): + m1 = re_nvr.match(f1) + n1 = m1.group(1) + v1 = m1.group(2) + r1 = m1.group(3) + + m2 = re_nvr.match(f2) + n2 = m2.group(1) + v2 = m2.group(2) + r2 = m2.group(3) + + l1 = rpm.labelCompare((n1, v1, r1), (n2, v2, r2)) + l2 = rpm.labelCompare((n2, v2, r2), (n1, v1, r1)) + + if l1 > 0 and l2 > 0: + return 0 + + return -l1 + + +def find_old(files): + return sorted(files, compare) + +files = {} +dupes = {} + +for file in os.listdir(dir): + if not re_info.match(file): + continue + + m = re_nvr.match(file) + if not m: + print "problem with: %s" % file + sys.exit(1) + + if len(sys.argv) == 0: + p = os.path.join(dir, file) + mtime = os.stat(p).st_mtime + if mtime > time.time() - 3*86400: + continue + + name = m.group(1) + + if files.has_key(name): + if dupes.has_key(name): + dupes[name].append(file) + else: + dupes[name] = [ files[name] ] + dupes[name].append(file) + else: + files[name] = file + +for i in dupes.iterkeys(): + for old in find_old(dupes[i])[1:]: + os.system("/home/pld/admins/th/pld-ftp-admin/scripts/remove.py test %s" % old) diff --git a/wwwbin/clean-dups.py b/wwwbin/clean-dups.py new file mode 100755 index 0000000..003b538 --- /dev/null +++ b/wwwbin/clean-dups.py @@ -0,0 +1,127 @@ +#!/usr/bin/python +# arekm, 2008 +# remove + +import os +import re +import time +import rpm +import sys + +re_rpm = re.compile(r'.*\.rpm$') +re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.rpm$') +dir = '/home/pld/admins/th/ftp/test/SRPMS/RPMS' + +ignore = re.compile('^(kernel-.*)$') +#|\ +#crash-.*|\ +#dahdi-linux-.*|\ +#e1000e-.*|\ +#igb-.*|\ +#ipset-.*|\ +#iscsitarget-.*|\ +#ixgbe-.*|\ +#kernel-net-wl-.*|\ +#lin_tape-.*|\ +#linux-fusion-.*|\ +#linuxrdac-.*|\ +#lirc-.*|\ +#lttng-modules-.*|\ +#madwifi-ng-.*|\ +#nvidiabl-.*|\ +#open-vm-tools-.*|\ +#openvswitch-.*|\ +#r8168-.*|\ +#spl-.*|\ +#tpm_emulator-.*|\ +#VirtualBox-.*|\ +#vpb-driver-.*|\ +#xorg-driver-video-fglrx-.*|\ +#xorg-driver-video-fglrx-legacy-.*|\ +#xorg-driver-video-nvidia-.*|\ +#xorg-driver-video-nvidia-legacy3-.*|\ +#xorg-driver-video-nvidia-legacy-304xx-.*|\ +#xtables-addons-.*)$') + +ts = rpm.TransactionSet("", (rpm.RPMVSF_NOHDRCHK or rpm.RPMVSF_NEEDPAYLOAD)) + +def compare(f1, f2): + try: + fd1 = os.open(os.path.join(dir, f1), os.O_RDONLY) + except Exception, e: + print e + # ignore non-files + return 0 + try: + fd2 = os.open(os.path.join(dir, f2), os.O_RDONLY) + except Exception, e: + print e + # ignore non-files + return 0 + try: + h1 = ts.hdrFromFdno(fd1) + except Exception, e: + print "hdrFromFdno for %s failed: %s" % (f1, e) + os.close(fd1) + os.close(fd2) + return 0 + + try: + h2 = ts.hdrFromFdno(fd2) + except Exception, e: + print "hdrFromFdno for %s failed: %s" % (f2, e) + os.close(fd1) + os.close(fd2) + return 0 + + os.close(fd1) + os.close(fd2) + + l1 = rpm.versionCompare(h1, h2) + l2 = rpm.versionCompare(h2, h1) + + if l1 > 0 and l2 > 0: + return 0 + + return -l1 + + +def find_old(files): + return sorted(files, compare) + +files = {} +dupes = {} + +for file in os.listdir(dir): + if not re_rpm.match(file): + continue + + if ignore.match(file): + continue + + m = re_nvr.match(file) + if not m: + print "problem with: %s" % file + sys.exit(1) + + if len(sys.argv) == 0: + p = os.path.join(dir, file) + mtime = os.stat(p).st_mtime + if mtime > time.time() - 3*86400: + continue + + name = m.group(1) + + if files.has_key(name): + if dupes.has_key(name): + dupes[name].append(file) + else: + dupes[name] = [ files[name] ] + dupes[name].append(file) + else: + files[name] = file + +for i in dupes.iterkeys(): + for old in find_old(dupes[i])[1:]: + print "removing: %s" % old + os.system("/home/pld/admins/th/pld-ftp-admin/scripts/remove.py test %s" % old) diff --git a/wwwbin/clean-test.sh b/wwwbin/clean-test.sh new file mode 100755 index 0000000..475f84f --- /dev/null +++ b/wwwbin/clean-test.sh @@ -0,0 +1,16 @@ +#!/bin/sh +LC_ALL=C; export LC_ALL +cd /home/pld/admins/th/ftp/test/SRPMS/.metadata || exit 1 +#for file in `find . -name '*.info' -print`; do +for file in `find . -name '*.info' -mtime +1 -print`; do + dfile=$(basename $file | sed -e 's#^\./##g') + if (~/pld-ftp-admin/scripts/test-move.py test ready "$dfile" | grep -q "has only src.rpm built"); then + echo "Removing $dfile..." + ~/pld-ftp-admin/scripts/remove.py test "$dfile" + fi +done +for file in `find . -name '*.info' -mtime +100`; do + dfile=$(basename $file | sed -e 's#^\./##g') + echo "Removing $dfile..." + ~/pld-ftp-admin/scripts/remove.py test "$dfile" +done diff --git a/wwwbin/consistency-check.sh b/wwwbin/consistency-check.sh new file mode 100755 index 0000000..25ed2c3 --- /dev/null +++ b/wwwbin/consistency-check.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +export LC_ALL=C + +filter_deps() { + if [ $1 = "x32" ]; then + grep -Ev "(uname\(release\)|-multilib-|\/eclipse\/|statifier|kernel-nopae-source-|kernel-tools-perf-vdso|libreoffice|iceape|iceweasel|icedove|nodejs)" + else + grep -Ev "(uname\(release\)|-multilib-|\/eclipse\/|statifier|kernel-nopae-source-|kernel-tools-perf-vdso)" + fi +} + +# group errors by same error kind +group_deps() { + local t=$(mktemp) + cat > $t + sed -ne 's/.*req \(.*\) not found.*/\1/p' $t | sort -u | while read dep; do + grep -F "req $dep not found" $t + done + rm -f $t +} + +# convert pkg name to src.rpm name +# uses poldek +pkg2src() { + local t=$(mktemp) + local t3=$(mktemp) + + # save input + cat > $t + + # create list of N-V-R.A.rpm -> src-N pairs from error report + # error: arcconf-7.0.18786-1: req libstdc++.so.5()(64bit) not found + sed -ne 's/error: \(.*\): req .* not found/\1/p' $t | sort -u | \ + xargs -d '\n' poldek --noignore -Q "$@" --cmd ls -q -s | \ + sed -rne "s/^([^\t ]+)[\t ]+(.+)-[^-]+-[^-]+\.src\.rpm$/\1\t\2/p" >$t3 + + local pkg error message srpm + while read error pkg message; do + # error: arcconf-7.0.18786-1: req libstdc++.so.5()(64bit) not found + srpm=$(awk -vpkg="${pkg%:}" '$1 == pkg {printf("%s.spec", $2)}' $t3) + if [ -z "$srpm" ]; then + echo >&2 "srpms: No match for [${pkg%:}]" + fi + echo "$error [$srpm] $pkg $message" + done < $t + + rm -f $t $t3 +} + +gen_list() { + arch=$1 + shift + date + poldek -O "auto directory dependencies = yes" \ + --ignore "*-debuginfo-*" \ + --ignore "opera-plugin32-*" \ + --ignore "nspluginwrapper-*" \ + --ignore "mbrola-voice-*" \ + --ignore "pysql-*" \ + --ignore "yasql-*" \ + --ignore "kde4-kdenetwork-kopete-protocol-skype-*.x86_64" \ + --ignore "libpurple-protocol-skype-*.x86_64" \ + --ignore "nagios-plugin-check_cciss-*" \ + --ignore "libpng1*" \ + --verify=deps -Q "$@" | filter_deps $arch | group_deps | pkg2src "$@" | sort +} + +gen_list_uniq() { + arch=$1 + shift + gen_list $arch -O"unique package names = yes" "$@" +} + +t=$(mktemp) +ftpdir=$HOME/ftp +for arch in x86_64 i686 x32 ; do + if [ "$arch" = x86_64 ]; then + outfext=.txt + else + outfext=-$arch.txt + fi + gen_list $arch -s $ftpdir/PLD/$arch/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ > $t && cat $t > $HOME/www/main$outfext + gen_list_uniq $arch -s $ftpdir/PLD/$arch/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ -s $ftpdir/ready/$arch/RPMS/ -s $ftpdir/ready/noarch/RPMS/ > $t && cat $t > $HOME/www/main-ready$outfext + gen_list_uniq $arch -s $ftpdir/PLD/$arch/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ -s $ftpdir/ready/$arch/RPMS/ -s $ftpdir/ready/noarch/RPMS/ -s $ftpdir/test/$arch/RPMS/ -s $ftpdir/test/noarch/RPMS/ > $t && cat $t > $HOME/www/main-ready-test$outfext +done + +chmod 644 $HOME/www/*.txt +rm -f $t diff --git a/wwwbin/dump-packagenames.py b/wwwbin/dump-packagenames.py new file mode 100755 index 0000000..59f9e89 --- /dev/null +++ b/wwwbin/dump-packagenames.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import os +import re + +import rpm + +dirs = ['/home/services/ftp/pld/dists/3.0/PLD/x32/RPMS', + '/home/services/ftp/pld/dists/3.0/PLD/i686/RPMS', + '/home/services/ftp/pld/dists/3.0/PLD/x86_64/RPMS', + '/home/services/ftp/pld/dists/3.0/ready/x32/RPMS', + '/home/services/ftp/pld/dists/3.0/ready/i686/RPMS', + '/home/services/ftp/pld/dists/3.0/ready/x86_64/RPMS', + '/home/services/ftp/pld/dists/3.0/test/x32/RPMS', + '/home/services/ftp/pld/dists/3.0/test/i686/RPMS', + '/home/services/ftp/pld/dists/3.0/test/x86_64/RPMS'] + +#dirs = ['/home/services/ftp/pld/dists/3.0/test/x86_64/RPMS'] + +outname = "/home/pld/admins/th/www/name-srcname.txt" + +re_rpm = re.compile(r'.*\.rpm$') +re_nvr = re.compile('^(.*)-([^-]*)-([^-]*)\.rpm$') + +ts = rpm.ts() +ts.setVSFlags(-1) +pkgs = {} +for dir in dirs: + for file in os.listdir(dir): + if not re_rpm.match(file): + continue + + rpm_file = os.path.join(dir, file) + + fdno = os.open(rpm_file, os.O_RDONLY) + try: + hdr = ts.hdrFromFdno(fdno) + except Exception, e: + print "hdrFromFdno: %s: %s" % (rpm_file, e) + os.close(fdno) + continue + os.close(fdno) + + name = hdr[rpm.RPMTAG_NAME] + sourcerpm = hdr[rpm.RPMTAG_SOURCERPM] + m = re_nvr.match(sourcerpm) + if not m: + print "%s: doesn't match src.rpm file name" % (sourcerpm) + continue + srcname = m.group(1) + pkgs[name] = srcname + +f = open(outname + ".tmp", "w") +for (pkg, spkg) in pkgs.iteritems(): + f.write("%s:%s\n" % (pkg, spkg)) +f.close() +os.chmod(outname + ".tmp", 0644) +os.rename(outname + ".tmp", outname) + diff --git a/wwwbin/freshness.sh b/wwwbin/freshness.sh new file mode 100755 index 0000000..4646650 --- /dev/null +++ b/wwwbin/freshness.sh @@ -0,0 +1,6 @@ +#!/bin/sh +date > $HOME/www/freshness.txt.new +/usr/bin/time -f '\nElapsed time: %E' $HOME/bin/ftp-freshness.py >> $HOME/www/freshness.txt.new 2>&1 +chmod 644 $HOME/www/freshness.txt.new +mv $HOME/www/freshness.txt.new $HOME/www/freshness.txt + diff --git a/wwwbin/ftp-freshness.py b/wwwbin/ftp-freshness.py new file mode 100755 index 0000000..aa27b37 --- /dev/null +++ b/wwwbin/ftp-freshness.py @@ -0,0 +1,122 @@ +#!/usr/bin/python +# arekm, 2007 + +import os +import re +import struct +import rpm +import subprocess + +# earlier == more important +dirs = [ "/home/ftp/pub/Linux/PLD/dists/th/test/SRPMS/RPMS/", + "/home/ftp/pub/Linux/PLD/dists/th/ready/SRPMS/RPMS/", + "/home/ftp/pub/Linux/PLD/dists/th/PLD/SRPMS/RPMS/" ] +#dirs = [ "/home/pld/admins/th/1" ] +specsdir = "/home/pld/admins/th/SPECS" + +if os.path.isdir(specsdir): + os.chdir(specsdir) + cmd = ['git', 'pull'] +else: + cmd = ['git', 'clone', '--depth=1', 'git://git.pld-linux.org/SPECS', specsdir] + +p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +(out, err) = p.communicate(None) + + +os.chdir(specsdir) +class Pkgs(object): + def __init__(self): + self.pkg = {} + self.cvs = {} + self.cvs_new_nvr = [] + self.cvs_new_nv = [] + self.re_n = re.compile('^(.*)-([^-]*)-([^-]*)\.src\.rpm$') + + def __get_from_rpm(self, file_name): + m = self.re_n.match(file_name) + if not m: + return False + name = m.group(1).strip() + version = m.group(2).strip() + release = m.group(3).strip() + return (name, version, release) + + def __get_from_cvs(self, name): + f = os.popen('rpm --specfile -q --queryformat "%{name}\n%{version}\n%{release}\n" ' + specsdir + '/' + name + '.spec 2> /dev/null', 'r') + name = f.readline().strip() + version = f.readline().strip() + release = f.readline().strip() + f.close() + return (name, version, release) + + def __update_cvs(self, name): + if not self.cvs.has_key(name): + self.cvs[name] = self.__get_from_cvs(name) + + def __update_new(self, name): + cvs_nvr = self.cvs[name] + pkg_nvr = self.pkg[name] + + cvs_rpm_vr = rpm.labelCompare((cvs_nvr[0], cvs_nvr[1], cvs_nvr[2]), (pkg_nvr[0], pkg_nvr[1], pkg_nvr[2])) + cvs_rpm_v = rpm.labelCompare((cvs_nvr[0], cvs_nvr[1], ""), (pkg_nvr[0], pkg_nvr[1], "")) + rpm_cvs_vr = rpm.labelCompare((pkg_nvr[0], pkg_nvr[1], pkg_nvr[2]), (cvs_nvr[0], cvs_nvr[1], cvs_nvr[2])) + rpm_cvs_v = rpm.labelCompare((pkg_nvr[0], pkg_nvr[1], ""), (cvs_nvr[0], cvs_nvr[1], "")) + + if rpm_cvs_v < 0 and cvs_rpm_v > 0: + self.cvs_new_nv.append(name) + else: + if rpm_cvs_vr < 0 and cvs_rpm_vr > 0 and cvs_nvr[1] == pkg_nvr[1]: + self.cvs_new_nvr.append(name) + + def prepare(self): + self.cvs_new_nvr.sort() + self.cvs_new_nvr = list(set(self.cvs_new_nvr)) + self.cvs_new_nv.sort() + self.cvs_new_nv = list(set(self.cvs_new_nv)) + + pkgs = list(self.pkg) + list(self.cvs) + + for name in list(set(pkgs)): + self.__update_new(name) + + def add_rpm(self, file_name): + nvr = self.__get_from_rpm(file_name) + if not nvr: + return False + name = nvr[0] + if self.pkg.has_key(name): + if rpm.labelCompare(nvr, self.pkg[name]) > 0: + del self.pkg[name] + self.pkg[name] = nvr + else: + self.pkg[name] = nvr + self.__update_cvs(name) + + def print_nvr(self): + print "*** VERSION-RELEASE COMPARE FOR THE SAME VERSIONS ONLY ***" + for name in self.cvs_new_nvr: + self.cvs_new_nvr.sort() + cvs_nvr = self.cvs[name] + pkg_nvr = self.pkg[name] + print "GIT: [%s.spec] %s-%s-%s vs FTP: %s-%s-%s" % (name, cvs_nvr[0], cvs_nvr[1], cvs_nvr[2], pkg_nvr[0], pkg_nvr[1], pkg_nvr[2]) + + def print_nv(self): + print "*** VERSION COMPARE ONLY ***" + self.cvs_new_nv.sort() + for name in self.cvs_new_nv: + cvs_nvr = self.cvs[name] + pkg_nvr = self.pkg[name] + print "GIT: [%s.spec] %s-%s-%s vs FTP: %s-%s-%s" % (name, cvs_nvr[0], cvs_nvr[1], cvs_nvr[2], pkg_nvr[0], pkg_nvr[1], pkg_nvr[2]) + +p = Pkgs() + +for d in dirs: + for rpm_file_name in os.listdir(d): + p.add_rpm(rpm_file_name) + +p.prepare() + +p.print_nv() +print "\n\n" +p.print_nvr() diff --git a/wwwbin/obsolete-check.sh b/wwwbin/obsolete-check.sh new file mode 100755 index 0000000..52a9f81 --- /dev/null +++ b/wwwbin/obsolete-check.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# check for packages on ftp whose .spec does not exist (anymore) +# this is extremely heavy on cvs server, so you run it once per month or so +# Author: Elan Ruusamäe + +export LC_ALL=C +ftpdir=$HOME/ftp +wwwdir=$HOME/www +CVSROOT=:pserver:cvs@cvs.pld-linux.org:/cvsroot +d=$- + +orphaned_pkgs() { + set -$d + [ -s $t/pkgs.desc ] || /usr/bin/poldek --skip-installed "$@" --cmd "desc *" > $t/pkgs.desc + [ -s $t/pkgs.lst ] || sed -n 's/^Source package: \(.*\)-[^-]\+-[^-]\+$/\1/p' $t/pkgs.desc | sort -u > $t/pkgs.lst + # {w32codec,acroread,...}-installer pkgs + sed -i -e 's,-installer$,,' $t/pkgs.lst + for pkg in $(cat $t/pkgs.lst); do + # use awk to match package without any regexp fuzz + awk -vpkg=$pkg.spec -vm=1 '$1 == pkg{m=0} END{exit m}' $t/cvs.lst || echo Obsolete: $pkg + done +} + +# generate list of .specs on ftp. needs cvsnt client +cvs_pkgs() { + set -$d + [ -s $t/cvs.raw ] || cvs -d $CVSROOT -Q ls -e packages > $t/cvs.raw 2>/dev/null + [ -s $t/cvs.dirs ] || awk -F/ '$1 == "D" { print $2 } ' $t/cvs.raw > $t/cvs.dirs + [ -s $t/cvs.specs ] || { + while read pkg; do + cvs -d $CVSROOT -Q ls -e packages/$pkg/$pkg.spec 2>/dev/null + done < $t/cvs.dirs > $t/cvs.lst.tmp && mv $t/cvs.lst.tmp $t/cvs.specs + } + [ -s $t/cvs.lst ] || awk -F/ '$1 == "" { print $2 } ' $t/cvs.specs > $t/cvs.lst +} + + +set -e +t=$(mktemp -d) +#t=/home/pld/admins/th/tmp/tmp.KOCrX7BtOy + +cvs_pkgs +orphaned_pkgs -s $ftpdir/PLD/i686/RPMS/ -s $ftpdir/PLD/noarch/RPMS/ > $t/orphaned.txt && cat $t/orphaned.txt > $wwwdir/main-orphaned4.txt + +chmod 644 $wwwdir/*.txt +#rm -rf $t diff --git a/wwwbin/rpmcheck.sh b/wwwbin/rpmcheck.sh new file mode 100755 index 0000000..35f4373 --- /dev/null +++ b/wwwbin/rpmcheck.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# check for packages on local system with PLD-doc/PLD-update-TODO +# Author: Elan Ruusamäe +# Date: 2012-04-05 + +export LC_ALL=C +CVSROOT=:pserver:cvs@cvs.pld-linux.org:/cvsroot +d=$- + +# generate list of package basenames from rpmdb +rpm_pkgs() { + set -$d + + [ -s $t/rpm.db ] || rpm -qa --qf '%{SOURCERPM} %{VERSION}\n' > $t/rpm.db + + # translate pkg names to basenames + [ -s $t/rpm.basenames ] || sed -re 's,^(.+)-[^-]+-[^-]+ ,\1 ,' $t/rpm.db > $t/rpm.basenames + # uniq + [ -s $t/rpm.lst ] || sort -u $t/rpm.basenames > $t/rpm.lst +} + +# fetch PLD-update-TODO +cvs_todo() { + set -$d + + [ -s $t/PLD-doc/PLD-update-TODO ] || (cd $t; cvs -d $CVSROOT -Q co PLD-doc/PLD-update-TODO) + + # reformat for easier parsing + # amaya(13) [OLD] 9.54 [NEW] 11.0 + # vim [OLD] 7.3.401 [NEW] 7.3.515 + [ -s $t/rpm.todo ] || sed -rne 's,^([^( ]+).*?\[NEW\] (.+)$,\1 \2,p' $t/PLD-doc/PLD-update-TODO > $t/rpm.todo + + # TODO: should uniq as well: + # $ grep links2 PLD-doc/PLD-update-TODO + # links2(22) [OLD] 2.2 [NEW] 2.6 + # links2(25) [OLD] 2.1pre28 [NEW] 2.6 +} + +rpm_diff() { +# set -$d + + rpm_pkgs + cvs_todo + + [ -s $t/rpm.diffs ] || { + set +e + while read pkg v2; do + v1=$(awk -vpkg=$pkg '$1 == pkg {print $2}' $t/rpm.lst) + [ "$v1" ] || continue + + cmp=$(rpmvercmp $v1 $v2) + if [ $? -eq 2 ]; then + echo "$pkg $cmp" + fi + done < $t/rpm.todo + set -e + } +} + +set -e +t=$(mktemp -d) +#t=. + +rpm_diff + +rm -rf $t -- 2.46.0