diff options
author | Chinaman <root@chinaman> | 2011-11-29 02:00:22 +0100 |
---|---|---|
committer | Chinaman <root@chinaman> | 2011-11-29 02:00:22 +0100 |
commit | c4c5f5570ddfd4f76e9078826aeb3df16920f7ce (patch) | |
tree | c7ddafa500b98dd1a34d2157d0de98ed8ef2a132 /punani | |
parent | a95cb58ffa45cf2f25430592a7cc60b70e26f232 (diff) | |
parent | b1d71c062f1d29b2be3269a5e7e8a67030adfa6a (diff) |
Merge branch 'master' of github.com:krebscode/painload
Diffstat (limited to 'punani')
-rw-r--r-- | punani/README.md | 30 | ||||
-rwxr-xr-x | punani/bin/punani | 314 | ||||
-rwxr-xr-x | punani/bot/__init__.py | 99 | ||||
-rw-r--r-- | punani/db/punani | 64 | ||||
-rwxr-xr-x | punani/index.py | 96 |
5 files changed, 340 insertions, 263 deletions
diff --git a/punani/README.md b/punani/README.md new file mode 100644 index 00000000..1b70eab7 --- /dev/null +++ b/punani/README.md @@ -0,0 +1,30 @@ +Overview +======= +Punani is a meta packagemanager comprising a server which resolves package +requests and a client containing the logic to find the best suitable packer +on the host system. Packagenames in Punani are binaries in the PATH. All +library packages are named in the Principle of Least Surprise[1]. Different +package names can resolve into the same package. + +If you want to install the `hostname` tool, the query is + punani install hostname +on an archlinux this will result in the call : + pacman --noconfirm -Sy --needed inetutils + +[1] http://de.wikipedia.org/wiki/Principle_of_Least_Surprise + +Punani Client +============ +The punani client will determine which packer are available on the system +and then send a request to the punani server to find out how the given +package is called with the given packer. In addition to that, the client +will add flags to the packers call in order to install packages only when +needed and disable user interaction. + +Punani Server +============ + +The punani server is a web-service which resolves request in the following +manner: + localhost/$packer/$package +The result is the package-name with the given packer or 404 if not found. diff --git a/punani/bin/punani b/punani/bin/punani index 058331f1..4be74f77 100755 --- a/punani/bin/punani +++ b/punani/bin/punani @@ -1,276 +1,64 @@ #! /bin/sh -# -# punani - filesystem scienteer -# -# Engineering Operations -# -E -i spec insert a package to the target filesystem -# -E -r spec remove a package -# set -euf -godmode() { - if test "${nosudo-false}" != true -a `id -u` != 0; then - echo "!! we require god mode..." >&2 - exec sudo "$0" "$@" - exit 23 # go to hell - fi -} -# return the 'system' variable -# currently be: -# arch-like -# debian-like -guess_system() -{ - if [ -f "/etc/arch-release" ] ;then - system="${system+$system, }arch-like" - fi - if [ -f "/etc/lsb-release" -o -f "/etc/debian_version" ] ;then - system="${system+$system, }debian-like" - fi +PUNANI_HOST="${PUNANI_HOST-http://euer.krebsco.de:9111}" +ACTION="$1"; shift +PKGS="$*" -} +## find package manager +if ! :; then : # dummy case, so the rest has a common format -arch_aur_helper() -{ - # pacman is the last fallback helper - manager="yaourt clyde packer bauerbill tupac pacaur paktahn pbfetch aurget aurora cower powaur pacman" - for i in $manager;do - mgr=`which $i` - if [ "$mgr" ] ;then - echo $mgr - return 0 - fi - done - echo "!! no helper found, this should never happen(tm)." - return 1 -} - -handle_system () { - case "$system" in - (*arch-like*) - # dryrun - # TODO dryrun not dry enough for aur helper - if [ "${dryrun-}" ];then - pacman () { echo "pacman $@" ; } - pkgfile () { echo "pkgfile $@"; } - yaourt () { echo "yaourt $@" ; } - fi - - # get dependencies : - # we need pkgfile - if ! [ `which pkgfile` ] ; then - pacman -S --needed --noconfirm pkgtools - pkgfile -u - fi - punani_Scientist_update() { - pacman -Sy - pkgtool -u - } - punani_Scientist_search() { - pkgfile -s -b $1 - if [ "${hard-}" ] ; then - mgr=`arch_aur_helper` - $mgr -Ss $1 - fi +elif for PACKER_CMD in yum + do type $PACKER_CMD 2>/dev/null 1>&2 && break; done; then + INSTALL_PARAM='-y install' + REMOVE_PARAM='-y remove' - } - - punani_Engineer_insert() { - # punani under archlinux first tries to load the packages with the given file name - # it needs pkgfile for that - # - # when trying harder it tries to load the package with the given name directly via yaourt - echo "** trying to find package with given file" - pkgs=$(pkgfile -s -b -r $1 | tr "\n" "|" ) - if [ "$pkgs" ];then - echo "** found one or more packages matching, skip the ones you do not need!" - OLDIFS=$IFS - IFS='|' - for to_install in $pkgs;do - if pacman -S --needed "$to_install"; then - echo "++ finished" - fi - done - IFS=$OLDIFS - else - echo "!! nothing found in base repos" - if [ "${hard-}" ] ; then - echo "** trying harder" - echo "** trying yaourt directly with given package" - mgr=`arch_aur_helper` - if $mgr -S $1 ;then - echo "++ finished" - return 0 - else - echo "!! giving up...i am sorry" - return 1 - fi - echo - else - echo "?? When in doubt try $0 -h -Ei $1 " - fi - fi - } - punani_Engineer_remove() { - pacman -Rcs "`pacman -Ql | grep $1$ | awk '{print $1}'`" - if [ "${hard-}" ] ; then - echo "** trying harder" - echo "** directly delete given package name" - pacman -Rcs "$1" - fi - } - ;; - (*debian-like*) - #if [ "${dryrun-}" ]; then - # apt-file () { echo $@; } - # apt-get () { echo $@; } - #fi - if ! which apt-file; then - echo "** installing dependencies: apt-file" - apt-get install --yes apt-file - echo "** update the apt-file tool" - apt-file update - fi - punani_Scientist_update() { - apt-get update - apt-file update - } - - punani_Scientist_search() { - apt-file search -l -x $1\$ && exit 0 - if [ "${hard-}" ] ; then - apt-cache search $1 - fi - } - punani_Engineer_insert() { - echo "trying to install $1" - pkgs=$(apt-file search -l -x $1\$ | tr "\n" "|" ) - if [ "$pkgs" ];then - echo "** found one or more packages matching, skip the ones you do not need!" - OLDIFS=$IFS - IFS='|' - for to_install in $pkgs;do - if apt-get install $to_install;then - echo "++ finished" - fi - done - IFS=$OLDIFS - else - if [ "${hard-}" ] ; then - echo "** trying harder" - apt-get install $1 - fi - fi - } - punani_Engineer_remove() { - apt-get remove --purge "`apt-file search -l -x /$1\$`" - if [ "${hard-}" ] ; then - echo "** trying harder" - echo "** directly delete given package name" - apt-get remove --purge "$1" - fi - } - ;; - (*) - email='krebs@syntax-fehler.de' - irc_host='irc.freenode.org' - irc_channel='#tincspasm' - cat >&2 <<EOF -Error 2: Your System Will Be Supported ASAP -1. send us a bug report -1.1 your operating system's name and version -1.2 this message: $0 $* -1.3 mailto:$email -2. join the relevant IRC channel -2.1 /connect $irc_host -2.2 /join $irc_channel -EOF - exit 23 - esac -} -help(){ - cat <<EOF -Usage: $0 [Options] [role][command] +elif for PACKER_CMD in brew + do type $PACKER_CMD 2>/dev/null 1>&2 && break; done; then + INSTALL_PARAM='install' + REMOVE_PARAM='remove' -Options: - -f force - -h hard - -v verbose - -d dryrun - -? this message +elif for PACKER_CMD in bauerbill packer yaourt pacman + do type $PACKER_CMD 2>/dev/null 1>&2 && break; done; then + INSTALL_PARAM='--noconfirm -S --needed' + REMOVE_PARAM='-Rcs' -Role: - -E Engineer - -S Scientist +elif for PACKER_CMD in aptitude apt-get + do type $PACKER_CMD 2>/dev/null 1>&2 && break; done; then + INSTALL_PARAM='--yes install' + REMOVE_PARAM='--yes remove' -Engineer: - i insert - r remove +else + echo "Error 2: no known package manager found; no punani for you!" >&2 + exit 23 +fi -Scientist: - s search - y update -EOF - exit 1 -} -punani (){ - ns=punani - role=undefined - while getopts 'dfhvSsopEir?' OPT; do - case $OPT in - (f) force=true; continue;; - (h) hard=true; continue;; - (v) set -x; continue;; - (d) dryrun=true; continue;; - (\?) help;continue ;; - esac - case ${role-Manager} in - (Engineer) - case $OPT in - (i) command="${ns}_${role}_insert";; - (r) command="${ns}_${role}_remove";; - (*) - echo 'Error 1: You are made of stupid!' >&2 - exit 23;; - esac;; - (Scientist) - case $OPT in - (s) command="${ns}_${role}_search";; - (y) command="${ns}_${role}_update";; - (*) - echo 'Error 1: You are made of stupid!' >&2 - exit 23;; - esac ;; - (undefined) - case $OPT in - (E) role=Engineer;; - (S) role=Scientist;; - (*) - exit 23;; - esac - ;; - (*) - echo 'Error 1: You are made of stupid!' >&2 - exit 23 - ;; - esac +## find package name +if test -n "$PKGS"; then + for PKG in $PKGS; do + RES="`wget -O- $PUNANI_HOST/$PACKER_CMD/$PKG 2>/dev/null || :`" + if [ ! "$RES" ]; then + echo "Error 2: could not resolve '$PKG'; no punani for you!" >&2 + exit 23 + fi + RESOLVED="${RESOLVED+$RESOLVED }$RES" done -} -punani $@ +else + echo "Error 1: no PACKAGE specified." >&2 + ACTION="usage" +fi -case $role in - (Engineer) godmode $@;; - (Scientist) - case $command in - (*_update) godmode $@;; - esac;; +## dispatch +case "$ACTION" in + install) + set -x + exec sudo $PACKER_CMD $INSTALL_PARAM $RESOLVED + ;; + remove) + set -x + exec sudo $PACKER_CMD $REMOVE_PARAM $RESOLVED + ;; + *) + echo "usage: `basename $0` (install|remove) PACKAGE..." + exit 23 esac - -shift `echo $OPTIND-1 | bc` - -guess_system -handle_system - -for name in "$@"; do - "$command" "$name" || echo "!! could not install $name" -done diff --git a/punani/bot/__init__.py b/punani/bot/__init__.py new file mode 100755 index 00000000..13d4c20b --- /dev/null +++ b/punani/bot/__init__.py @@ -0,0 +1,99 @@ +#!/usr/bin/python +from Queue import Queue +from SocketServer import BaseRequestHandler, ThreadingTCPServer +from threading import Thread +from time import sleep, strftime, strptime + +from ircbot import SingleServerIRCBot +from irclib import nm_to_n + +class PunaniRequestHandler(BaseRequestHandler): + """Handler for Punani messages.""" + + def handle(self): + try: + msg = self.request.recv(1024).strip() + except ValueError: + msg = 'Invalid message.' + else: + self.server.queue.put((self.client_address, msg)) + print ('%s:%d' % self.client_address), str(msg) + + +class PunaniReceiveServer(ThreadingTCPServer): + """UDP server that waits for Punani messages.""" + + def __init__(self): + ThreadingTCPServer.__init__(self, ('127.0.0.1', 5555), PunaniRequestHandler) + self.queue = Queue() + +class PunaniBot(SingleServerIRCBot): + + def __init__(self, server_list, channel_list, nickname='punani-ircbot', + realname='Bob Ross'): + SingleServerIRCBot.__init__(self, server_list, nickname, realname) + self.channel_list = channel_list + + def on_welcome(self, conn, event): + """Join channels after connect.""" + print 'Connected to %s:%d.' % conn.socket.getsockname() + for channel, key in self.channel_list: + conn.join(channel, key) + + def on_nicknameinuse(self, conn, event): + """Choose another nickname if conflicting.""" + self._nickname += '_' + conn.nick(self._nickname) + + def on_ctcp(self, conn, event): + """Answer CTCP PING and VERSION queries.""" + whonick = nm_to_n(event.source()) + message = event.arguments()[0].lower() + if message == 'version': + conn.notice(whonick, 'Punani2irc') + elif message == 'ping': + conn.pong(whonick) + + def on_privmsg(self, conn, event): + """React on private messages. + + Die, for example. + """ + whonick = nm_to_n(event.source()) + message = event.arguments()[0] + if message == 'die!': + print 'Shutting down as requested by %s...' % whonick + self.die('Shutting down.') + + def say(self, msg): + """Say message to channels.""" + for channel, key in self.channel_list: + self.connection.privmsg(channel, msg) + +def process_queue(announce_callback, queue, delay=2): + """Process received messages in queue.""" + while True: + sleep(delay) + try: + addr, msg = queue.get() + except Empty: + continue + #do something with the addr? + announce_callback(str(msg)) +if __name__ == '__main__': + # Set IRC connection parameters. + irc_servers = [('supernode', 6667)] + irc_channels = [('#retiolum','')] + + # Prepare and start IRC bot. + bot = PunaniBot(irc_servers, irc_channels) + t = Thread(target=bot.start) + t.daemon = True + t.start() + announce = bot.say + + receiver = PunaniReceiveServer() + t = Thread(target=process_queue,args=(announce,receiver.queue)) + t.daemon = True + t.start() + receiver.serve_forever() diff --git a/punani/db/punani b/punani/db/punani new file mode 100644 index 00000000..df471f3a --- /dev/null +++ b/punani/db/punani @@ -0,0 +1,64 @@ +{ + "packer-symlinks" : { + "packer" : "yaourt", + "aptitude" : "apt-get", + "bauerbill" : "yaourt" + }, + "super-packer" : { + "yaourt" : "pacman" + }, + "vim" : { + "apt-get" : "vim", + "pacman" : "vim", + "brew" : "vim", + "yum" : "vim" + }, + "unison" : { + "apt-get" : "unison", + "pacman" : "unison" + }, + "python" : { + "apt-get" : "python", + "pacman" : "python2" + }, + "python2" : { + "apt-get" : "python", + "pacman" : "python2" + }, + "python3" : { + "apt-get" : "python3", + "pacman" : "python" + }, + "tinc" : { + "apt-get" : "tinc", + "yaourt" : "tinc" + }, + "python-notify" : { + "pacman" : "python-notify", + "apt-get" : "python-notify" + }, + "python-dbus" : { + "pacman" : "dbus-python", + "apt-get" : "python-dbus" + }, + "w3m" : { + "pacman" : "w3m", + "apt-get" : "w3m" + }, + "make" : { + "pacman" : "make", + "apt-get" : "make" + }, + "perl-xml-simple" : { + "apt-get" : "libxml-simple-perl", + "pacman" : "perl-xml-simple" + }, + "hostname" : { + "pacman" : "inetutils", + "apt-get" : "hostname" + }, + "pip" : { + "pacman" : "python-pip", + "apt-get" : "python-pip" + } +} diff --git a/punani/index.py b/punani/index.py new file mode 100755 index 00000000..ac19b2fb --- /dev/null +++ b/punani/index.py @@ -0,0 +1,96 @@ +#!/usr/bin/python + +import web +import json +import os +from bot import * +urls = ( + '/', 'Index', + '/dump','Dump', +# '/reload','Reload', + '/(.+)/(.+)', 'ArchFinder', +) + + +PDB_FILE="db/punani" +PORT="9111" +CHANNEL="#retiolum" +f = open(PDB_FILE) +pdb = json.load(f) +f.close() +polite = os.environ.get("polite",False) +from socket import * + +def local_announce(msg): + s = socket(AF_INET,SOCK_STREAM) + s.connect(('localhost',5555)) + s.send(msg) + s.close() +class Index: + def GET(self): + ret = """Welcome to the Tightnani API<br/> +Retrieve a package name for your distribution with: /PACKER/PKG""" + return ret + +class Reload: + def GET(self): + f = open(PDB_FILE) + pdb= json.load(f) + f.close() + return "DB reloaded" + + +class Dump: + def GET(self): + return json.dumps(pdb,sort_keys=True,indent=4) + +class ArchFinder: + def GET(self,request_packer,package): + if not request_packer or not package: web.BadRequest() + else: + packer = pdb['packer-symlinks'].get(request_packer,request_packer) #try to resolve similar packers + super_packer = pdb['super-packer'].get(packer,'') + ret = pdb.get(package,{}).get(packer,False) + ret = ret if ret else pdb.get(package,{}).get(super_packer,False) + + if not ret: + try: + if polite: + local_announce("Client `%s` asked for the tool `%s` in packer `%s` but i do not have it in my Database. Please update me!" %(web.ctx.ip, package,packer)) + else: + local_announce("404: no %s/%s for %s" % (request_packer,package,gethostbyaddr(web.ctx.ip)[0])) + except Exception,e: + print ("Got Exception %s: %s" % (str(Exception),(e))) + web.NotFound() + return "not found. i'm so sorry :(" + else: return ret + + + +if __name__ == "__main__": + import sys + # Set IRC connection parameters. + irc_servers = [('supernode', 6667)] + irc_channels = [('#retiolum','')] + + # Prepare and start IRC bot. + bot = PunaniBot(irc_servers, irc_channels) + t = Thread(target=bot.start) + t.daemon = True + t.start() + announce = bot.say + + receiver = PunaniReceiveServer() + t = Thread(target=receiver.serve_forever) + t.daemon = True + t.start() + + t = Thread(target=process_queue,args=(announce,receiver.queue)) + t.daemon = True + t.start() + + + sys.argv.append(PORT) + app = web.application(urls,globals()) + app.internalerror = web.debugerror + app.run() |