diff --git a/python/COPYING b/python/COPYING new file mode 100644 index 0000000..787504c --- /dev/null +++ b/python/COPYING @@ -0,0 +1,22 @@ +Copyright (C) 2008 Evan Broder + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/python/MANIFEST.in b/python/MANIFEST.in new file mode 100644 index 0000000..eb762f3 --- /dev/null +++ b/python/MANIFEST.in @@ -0,0 +1 @@ +include COPYING diff --git a/python/README b/python/README new file mode 100644 index 0000000..93deb59 --- /dev/null +++ b/python/README @@ -0,0 +1,39 @@ +=================== +Installing PyHesiod +=================== + +To install PyHesiod, you will first need to install Pyrex_. It's +always a good idea to install Pyrex through your package manager, if +possible. Your system's Pyrex package may be named ``python-pyrex`` or +``pyrex-py25``. If your package manager doesn't have a package for +Pyrex, or if you wish to install Pyrex by hand anyway, you can do so +by running:: + + $ easy_install Pyrex + +Once you've done that, to install PyHesiod globally, run:: + + $ python setup.py install + +If you want to build PyHesiod without installing it globally, you may +want to run:: + + $ python setup.py build_ext --inplace + +which will build the C extensions in place next to their source, +allowing you to import the various modules, so long as your current +working directory is the root of the PyHesiod source tree. + +Alternatively, PyHesiod has been packaged for Debian and Ubuntu. To +build the Debian package of the latest release, run:: + + $ git checkout debian + $ git buildpackage + $ sudo debi + +You will need the devscripts and git-buildpackage packages installed, +as well as this package's build dependencies (cdbs, debhelper, +python-all-dev, python-support, python-pyrex, python-setuptools, +libhesiod-dev). + +.. _Pyrex: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/ diff --git a/python/_hesiod.pyx b/python/_hesiod.pyx new file mode 100644 index 0000000..f0b41d9 --- /dev/null +++ b/python/_hesiod.pyx @@ -0,0 +1,81 @@ +cdef extern from "hesiod.h": + int hesiod_init(void **context) + void hesiod_end(void *context) + char *hesiod_to_bind(void *context, char *name, char *type) + char **hesiod_resolve(void *context, char *name, char *type) + void hesiod_free_list(void *context, char **list) + # This function isn't defined in 3.0.2, which is what Debian/Ubuntu use + #void hesiod_free_string(void *context, char *str) + +cdef extern from "errno.h": + int errno + +cdef extern from "string.h": + char * strerror(int errnum) + +cdef extern from "stdlib.h": + void free(void *) + +cdef void * __context + +cdef class __ContextManager: + def __init__(self): + if hesiod_init(&__context) == -1: + raise IOError(errno, strerror(errno)) + + def __del__(self): + hesiod_end(__context) + +cdef object __cm +__cm = __ContextManager() + +import threading +__lookup_lock = threading.Lock() + +def bind(hes_name, hes_type): + """ + Convert the provided arguments into a DNS name. + + The DNS name derived from the name and type provided is used to + actually execute the Hesiod lookup. + """ + cdef object py_result + cdef char * c_result + + name_str, type_str = map(str, (hes_name, hes_type)) + + c_result = hesiod_to_bind(__context, name_str, type_str) + if c_result is NULL: + raise IOError(errno, strerror(errno)) + py_result = c_result + + free(c_result) + return py_result + +def resolve(hes_name, hes_type): + """ + Return a list of records matching the given name and type. + """ + cdef int i + cdef object py_result + py_result = list() + cdef char ** c_result + + name_str, type_str = map(str, (hes_name, hes_type)) + + __lookup_lock.acquire() + c_result = hesiod_resolve(__context, name_str, type_str) + err = errno + __lookup_lock.release() + + if c_result is NULL: + raise IOError(err, strerror(err)) + i = 0 + while True: + if c_result[i] is NULL: + break + py_result.append(c_result[i]) + i = i + 1 + + hesiod_free_list(__context, c_result) + return py_result diff --git a/python/hesiod.py b/python/hesiod.py new file mode 100755 index 0000000..5cb3d1b --- /dev/null +++ b/python/hesiod.py @@ -0,0 +1,114 @@ +""" +Present both functional and object-oriented interfaces for executing +lookups in Hesiod, Project Athena's service name resolution protocol. +""" + +from _hesiod import bind, resolve + +from pwd import struct_passwd +from grp import struct_group + +class HesiodParseError(Exception): + pass + +class Lookup(object): + """ + A Generic Hesiod lookup + """ + def __init__(self, hes_name, hes_type): + self.results = resolve(hes_name, hes_type) + self.parseRecords() + + def parseRecords(self): + pass + +class FilsysLookup(Lookup): + def __init__(self, name): + Lookup.__init__(self, name, 'filsys') + + def parseRecords(self): + Lookup.parseRecords(self) + + self.filsys = [] + self.multiRecords = (len(self.results) > 1) + + for result in self.results: + priority = 0 + if self.multiRecords: + result, priority = result.rsplit(" ", 1) + priority = int(priority) + + parts = result.split(" ") + type = parts[0] + if type == 'AFS': + self.filsys.append(dict(type=type, + location=parts[1], + mode=parts[2], + mountpoint=parts[3], + priority=priority)) + elif type == 'NFS': + self.filsys.append(dict(type=type, + remote_location=parts[1], + server=parts[2], + mode=parts[3], + mountpoint=parts[4], + priority=priority)) + elif type == 'ERR': + self.filsys.append(dict(type=type, + message=parts[1], + priority=priority)) + elif type == 'UFS': + self.filsys.append(dict(type=type, + device=parts[1], + mode=parts[2], + mountpoint=parts[3], + priority=priority)) + elif type == 'LOC': + self.filsys.append(dict(type=type, + location=parts[1], + mode=parts[2], + mountpoint=parts[3], + priority=priority)) + else: + raise HesiodParseError('Unknown filsys type: %s' % type) + + self.filsys.sort(key=(lambda x: x['priority'])) + +class PasswdLookup(Lookup): + def __init__(self, name): + Lookup.__init__(self, name, 'passwd') + + def parseRecords(self): + passwd_info = self.results[0].split(':') + passwd_info[2] = int(passwd_info[2]) + passwd_info[3] = int(passwd_info[3]) + self.passwd = struct_passwd(passwd_info) + +class UidLookup(PasswdLookup): + def __init__(self, uid): + Lookup.__init__(self, uid, 'uid') + +class GroupLookup(Lookup): + def __init__(self, group): + Lookup.__init__(self, group, 'group') + + def parseRecords(self): + group_info = self.results[0].split(':') + group_info[2] = int(group_info[2]) + members = group_info[3] + if members != '': + members = members.split(',') + else: + members = [] + group_info[3] = members + + self.group = struct_group(group_info) + +class GidLookup(GroupLookup): + def __init__(self, gid): + Lookup.__init__(self, gid, 'gid') + +__all__ = ['bind', 'resolve', + 'Lookup', 'FilsysLookup', 'PasswdLookup', 'UidLookup', + 'GroupLookup', 'GidLookup', + 'HesiodParseError'] diff --git a/python/setup.py b/python/setup.py new file mode 100755 index 0000000..8b499a4 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,23 @@ +#!/usr/bin/python + +from setuptools import setup +from distutils.extension import Extension +from Pyrex.Distutils import build_ext + +setup( + name="PyHesiod", + version="0.2.10", + description="PyHesiod - Python bindings for the Heisod naming library", + author="Evan Broder", + author_email="broder@mit.edu", + url="http://ebroder.net/code/PyHesiod", + license="MIT", + requires=['Pyrex'], + py_modules=['hesiod'], + ext_modules=[ + Extension("_hesiod", + ["_hesiod.pyx"], + libraries=["hesiod"]) + ], + cmdclass= {"build_ext": build_ext} +)