#!/usr/bin/env python # # Copyright 2013 John-Mark Gurney. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Id: //depot/python/misc/cuse4bsd.py#1 $ # '''A module that wraps the libcuse4bsd library.''' __author__ = 'John-Mark Gurney' __copyright__ = 'Copyright 2013, John-Mark Gurney' __license__ = '2-clause BSD' __email__ = 'jmg@funkthat.com' __status__ = 'Prototype' __all__ = [ 'CUSE' ] from ctypes import CDLL, Structure, pointer, POINTER, CFUNCTYPE, create_string_buffer, cast from ctypes import c_int, c_uint32, c_void_p, c_ulong, c_char_p, py_object from ctypes.util import find_library c_int_p = POINTER(c_int) _cl = CDLL(find_library('cuse4bsd')) cuseerr = { 'CUSE_ERR_NONE': 0, 'CUSE_ERR_BUSY': -1, 'CUSE_ERR_WOULDBLOCK': -2, 'CUSE_ERR_INVALID': -3, 'CUSE_ERR_NO_MEMORY': -4, 'CUSE_ERR_FAULT': -5, 'CUSE_ERR_SIGNAL': -6, 'CUSE_ERR_OTHER': -7, 'CUSE_ERR_NOT_LOADED': -8, } cuseerrnum = dict((y, x) for x, y in cuseerr.iteritems()) cusepoll = { 'CUSE_POLL_NONE': 0, 'CUSE_POLL_READ': 1, 'CUSE_POLL_WRITE': 2, 'CUSE_POLL_ERROR': 4, } cusefflag = { 'CUSE_FFLAG_NONE': 0, 'CUSE_FFLAG_READ': 1, 'CUSE_FFLAG_WRITE': 2, 'CUSE_FFLAG_NONBLOCK': 4, } cusedbg = { 'CUSE_DBG_NONE': 0, 'CUSE_DBG_FULL': 1, } class Cuse_Dev(Structure): pass Cuse_Dev_p = POINTER(Cuse_Dev) cuse_open_t = CFUNCTYPE(c_int, Cuse_Dev_p, c_int) cuse_close_t = CFUNCTYPE(c_int, Cuse_Dev_p, c_int) cuse_read_t = CFUNCTYPE(c_int, Cuse_Dev_p, c_int, c_void_p, c_int) cuse_write_t = CFUNCTYPE(c_int, Cuse_Dev_p, c_int, c_void_p, c_int) cuse_ioctl_t = CFUNCTYPE(c_int, Cuse_Dev_p, c_int, c_ulong, c_void_p) cuse_poll_t = CFUNCTYPE(c_int, Cuse_Dev_p, c_int, c_int) class Cuse_Methods(Structure): _fields_ = [('cm_open', cuse_open_t), ('cm_close', cuse_close_t), ('cm_read', cuse_read_t), ('cm_write', cuse_write_t), ('cm_ioctl', cuse_ioctl_t), ('cm_poll', cuse_poll_t), ] Cuse_Methods_p = POINTER(Cuse_Methods) for rtype, fun, args in ( (c_int, 'cuse_init', ()), (c_int, 'cuse_uninit', ()), (c_int, 'cuse_alloc_unit_number', (c_int_p,)), (c_int, 'cuse_alloc_unit_number_by_id', (c_int_p, c_int)), (c_int, 'cuse_free_unit_number', (c_int,)), (c_int, 'cuse_free_unit_number', (c_int,)), (c_int, 'cuse_free_unit_number_by_id', (c_int, c_int)), (c_void_p, 'cuse_vmalloc', (c_int,)), (None, 'cuse_vmfree', (c_void_p,)), (c_ulong, 'cuse_vmoffset', (c_void_p,)), (Cuse_Dev_p, 'cuse_dev_create', (Cuse_Methods_p, c_void_p, c_void_p, c_uint32, c_uint32, c_int, c_char_p,)), # XXX - can't do varargs, just use python's built-in % (None, 'cuse_dev_destroy', (Cuse_Dev_p,)), (c_void_p, 'cuse_dev_get_priv0', (Cuse_Dev_p,)), (c_void_p, 'cuse_dev_get_priv1', (Cuse_Dev_p,)), (None, 'cuse_dev_set_priv0', (Cuse_Dev_p, c_void_p)), (None, 'cuse_dev_set_priv1', (Cuse_Dev_p, c_void_p)), (c_int, 'cuse_wait_and_process', ()), (None, 'cuse_dev_set_per_file_handle', (Cuse_Dev_p, c_void_p)), (c_void_p, 'cuse_dev_get_per_file_handle', (Cuse_Dev_p,)), (None, 'cuse_set_local', (c_int,)), (c_int, 'cuse_copy_out', (c_void_p, c_void_p, c_int)), (c_int, 'cuse_copy_in', (c_void_p, c_void_p, c_int)), (c_int, 'cuse_got_peer_signal', ()), (Cuse_Dev_p, 'cuse_dev_get_current', (c_int_p,)), (None, 'cuse_poll_wakeup', ()), ): f = getattr(_cl, fun) f.restype = rtype f.argtypes = args locals()[fun] = f _r = cuse_init() if _r: raise RuntimeError(cuseerrnum[_r]) class CUSE: devimpl = None # XXX - this should be a factory so each open gets a new class def __init__(self, name, uid=0, gid=0, permission=0700): self._objs = {} if self.devimpl is None: raise RuntimeError('devimpl not set!') self._cdev = None self._methods = Cuse_Methods() for i in ('open', 'close', 'read', 'write', 'ioctl', 'poll'): if not hasattr(self.devimpl, i): continue setattr(self._methods, 'cm_%s' % i, globals()['cuse_%s_t' % i](getattr(self, i))) print 'b:', getattr(self._methods, 'cm_%s' % i) self._cdev = cuse_dev_create(self._methods, None, None, uid, gid, permission, name) print `self._cdev` if self._cdev == None: raise RuntimeError('failed to create: %s' % `name`) def buildImpl(self): '''Default Implementation builder. Instantiates devimpl. Override if your Implementation class needs arguments.''' return self.devimpl() def open(self, dev_p, *args): obj = py_object(self.buildImpl()) cuse_dev_set_per_file_handle(dev_p, pointer(obj)) try: print 'o:', `obj` r = obj.value.open(*args) except Exception, e: import traceback traceback.print_exc() return cuseerr['CUSE_ERR_OTHER'] if r >= 0: # XXX - py_objects don't compare equal even when # they contain the same object: # b = [] # c = ctypes.py_object(b) # d = ctypes.pointer(c) # d.contents == c # XXX - count since we could have the same impl? self._objs[id(obj.value)] = obj return r def _getobj(self, dev_p): obj = cuse_dev_get_per_file_handle(dev_p) obj = cast(obj, POINTER(py_object)).contents return obj.value def close(self, dev_p, *args): obj = cuse_dev_get_per_file_handle(dev_p) obj = cast(obj, POINTER(py_object)).contents try: r = obj.value.close(*args) except Exception: return cuseerr['CUSE_ERR_OTHER'] if r >= 0: print `obj` print `self._objs` del self._objs[id(obj.value)] print `self._objs` return r def read(self, dev_p, *args): obj = self._getobj(dev_p) try: return obj.read(*args) except Exception: return cuseerr['CUSE_ERR_OTHER'] def write(self, dev_p, *args): obj = self._getobj(dev_p) try: return obj.write(*args) except Exception: return cuseerr['CUSE_ERR_OTHER'] def ioctl(self, dev_p, *args): obj = self._getobj(dev_p) try: return obj.ioctl(*args) except Exception: return cuseerr['CUSE_ERR_OTHER'] def poll(self, dev_p, *args): obj = self._getobj(dev_p) try: return obj.poll(*args) except Exception: return cuseerr['CUSE_ERR_OTHER'] def __del__(self): if self._cdev != None: cuse_dev_destroy(self._cdev) class TestDev: def open(self, i): print 'open:', `i` return cuseerr['CUSE_ERR_NONE'] def write(self, fflags, ptr, i): print 'in write:', `ptr`, `i` buf = create_string_buffer(i) r = cuse_copy_in(ptr, buf, i) print 'r:', `r` print 'write:', `buf.value` return i def read(self, fflags, ptr, i): print 'in read:', `ptr`, `i` defstr = 'this is a string to be read.\n' r = cuse_copy_out(defstr, ptr, min(len(defstr), i)) return min(len(defstr), i) def close(self, i): print 'close:', `i` return cuseerr['CUSE_ERR_NONE'] class TestCuse(CUSE): devimpl = TestDev if __name__ == '__main__': a = TestCuse('foobar') while True: r = cuse_wait_and_process() #print 'wait:', `r`