From b5d45edefecb90d5b34ac1185999c14a2a0a1b86 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 18 Oct 2012 23:30:30 -0400 Subject: [PATCH] Convert to Cython. With Cython we get easier memory handling and Python 3 compatibility. Signed-off-by: W. Trevor King --- .gitignore | 6 + kmod/__init__.py | 17 +++ kmod/_libkmod_h.pxd | 77 +++++++++++++ kmod/error.py | 2 + kmod/kmod.pxd | 5 + kmod/kmod.pyx | 101 +++++++++++++++++ kmod/list.pxd | 9 ++ kmod/list.pyx | 29 +++++ kmod/module.pxd | 8 ++ kmod/module.pyx | 65 +++++++++++ kmod/version.py | 1 + libkmod.c | 269 -------------------------------------------- setup.py | 44 ++++++-- 13 files changed, 356 insertions(+), 277 deletions(-) create mode 100644 .gitignore create mode 100644 kmod/__init__.py create mode 100644 kmod/_libkmod_h.pxd create mode 100644 kmod/error.py create mode 100644 kmod/kmod.pxd create mode 100644 kmod/kmod.pyx create mode 100644 kmod/list.pxd create mode 100644 kmod/list.pyx create mode 100644 kmod/module.pxd create mode 100644 kmod/module.pyx create mode 100644 kmod/version.py delete mode 100644 libkmod.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c074348 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +__pycache__ +build +dist +*.c +*.pyc +*.so diff --git a/kmod/__init__.py b/kmod/__init__.py new file mode 100644 index 0000000..5d3f129 --- /dev/null +++ b/kmod/__init__.py @@ -0,0 +1,17 @@ +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# 2012 W. Trevor King +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions +# of the GNU Lesser General Public License v.2.1. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Author: Andy Grover + +"Libkmod -- Python interface to kmod API." + +from .version import __version__ +from .kmod import Kmod diff --git a/kmod/_libkmod_h.pxd b/kmod/_libkmod_h.pxd new file mode 100644 index 0000000..975dfa3 --- /dev/null +++ b/kmod/_libkmod_h.pxd @@ -0,0 +1,77 @@ +cdef extern from *: + ctypedef char* const_char_ptr 'const char *' + ctypedef char* const_char_const_ptr 'const char const *' + ctypedef void* const_void_ptr 'const void *' + +cdef extern from 'errno.h': + enum: EEXIST + +cdef extern from 'stdbool.h': + ctypedef struct bool: + pass + + +cdef extern from 'libkmod.h': + # library user context - reads the config and system + # environment, user variables, allows custom logging + cdef struct kmod_ctx: + pass + + kmod_ctx *kmod_new( + const_char_ptr dirname, const_char_const_ptr config_paths) + kmod_ctx *kmod_ref(kmod_ctx *ctx) + kmod_ctx *kmod_unref(kmod_ctx *ctx) + + # Management of libkmod's resources + int kmod_load_resources(kmod_ctx *ctx) + void kmod_unload_resources(kmod_ctx *ctx) + + # access to kmod generated lists + cdef struct kmod_list: + pass + ctypedef kmod_list* const_kmod_list_ptr 'const struct kmod_list *' + kmod_list *kmod_list_next( + const_kmod_list_ptr list, const_kmod_list_ptr curr) + kmod_list *kmod_list_prev( + const_kmod_list_ptr list, const_kmod_list_ptr curr) + kmod_list *kmod_list_last(const_kmod_list_ptr list) + + # Operate on kernel modules + cdef struct kmod_module: + pass + ctypedef kmod_module* const_kmod_module_ptr 'const struct kmod_module *' + int kmod_module_new_from_name( + kmod_ctx *ctx, const_char_ptr name, kmod_module **mod) + int kmod_module_new_from_lookup( + kmod_ctx *ctx, const_char_ptr given_alias, kmod_list **list) + int kmod_module_new_from_loaded(kmod_ctx *ctx, kmod_list **list) + + kmod_module *kmod_module_ref(kmod_module *mod) + kmod_module *kmod_module_unref(kmod_module *mod) + int kmod_module_unref_list(kmod_list *list) + kmod_module *kmod_module_get_module(kmod_list *entry) + + # Flags to kmod_module_probe_insert_module + # codes below can be used in return value, too + enum: KMOD_PROBE_APPLY_BLACKLIST + + #ctypedef int (*install_callback_t)( + # kmod_module *m, const_char_ptr cmdline, const_void_ptr data) + #ctypedef void (*print_action_callback_t)( + # kmod_module *m, bool install, const_char_ptr options) + + int kmod_module_remove_module( + kmod_module *mod, unsigned int flags) + int kmod_module_insert_module( + kmod_module *mod, unsigned int flags, const_char_ptr options) + int kmod_module_probe_insert_module( + kmod_module *mod, unsigned int flags, const_char_ptr extra_options, + int (*run_install)( + kmod_module *m, const_char_ptr cmdline, void *data), + const_void_ptr data, + void (*print_action)( + kmod_module *m, bool install, const_char_ptr options), + ) + + const_char_ptr kmod_module_get_name(const_kmod_module_ptr mod) + long kmod_module_get_size(const_kmod_module_ptr mod) diff --git a/kmod/error.py b/kmod/error.py new file mode 100644 index 0000000..d976bb6 --- /dev/null +++ b/kmod/error.py @@ -0,0 +1,2 @@ +class KmodError (Exception): + pass diff --git a/kmod/kmod.pxd b/kmod/kmod.pxd new file mode 100644 index 0000000..2933724 --- /dev/null +++ b/kmod/kmod.pxd @@ -0,0 +1,5 @@ +cimport _libkmod_h + +cdef class Kmod (object): + cdef _libkmod_h.kmod_ctx *_kmod_ctx + cdef object mod_dir diff --git a/kmod/kmod.pyx b/kmod/kmod.pyx new file mode 100644 index 0000000..fdba4c6 --- /dev/null +++ b/kmod/kmod.pyx @@ -0,0 +1,101 @@ +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# 2012 W. Trevor King +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions +# of the GNU Lesser General Public License v.2.1. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +cimport cython as _cython +cimport _libkmod_h +from error import KmodError as _KmodError +cimport module as _module +import module as _module +cimport list as _list +import list as _list + + +cdef class Kmod (object): + def __cinit__(self): + self._kmod_ctx = NULL + + def __dealloc__(self): + self._cleanup() + + def __init__(self, mod_dir=None): + self.set_mod_dir(mod_dir=mod_dir) + + def set_mod_dir(self, mod_dir=None): + self.mod_dir = mod_dir + self._setup() + + def _setup(self): + cdef char *mod_dir = NULL + self._cleanup() + if self.mod_dir: + mod_dir = self.mod_dir + self._kmod_ctx = _libkmod_h.kmod_new(mod_dir, NULL); + if self._kmod_ctx is NULL: + raise _KmodError('Could not initialize') + _libkmod_h.kmod_load_resources(self._kmod_ctx) + + def _cleanup(self): + if self._kmod_ctx is not NULL: + _libkmod_h.kmod_unload_resources(self._kmod_ctx); + self._kmod_ctx = NULL + + def loaded(self): + "iterate through currently loaded modules" + cdef _list.ModList ml = _list.ModList() + cdef _list.ModListItem mli + err = _libkmod_h.kmod_module_new_from_loaded(self._kmod_ctx, &ml.list) + if err < 0: + raise _KmodError('Could not get loaded modules') + for item in ml: + mli = <_list.ModListItem> item + mod = _module.Module() + mod.from_mod_list_item(item) + yield mod + + def lookup(self, alias_name, flags=_libkmod_h.KMOD_PROBE_APPLY_BLACKLIST): + "iterate through modules matching `alias_name`" + cdef _list.ModList ml = _list.ModList() + cdef _list.ModListItem mli + if hasattr(alias_name, 'encode'): + alias_name = alias_name.encode('ascii') + err = _libkmod_h.kmod_module_new_from_lookup( + self._kmod_ctx, alias_name, &ml.list) + if err < 0: + raise _KmodError('Could not modprobe') + for item in ml: + mli = <_list.ModListItem> item + mod = _module.Module() + mod.from_mod_list_item(item) + yield mod + + @_cython.always_allow_keywords(True) + def module_from_name(self, name): + cdef _module.Module mod = _module.Module() + if hasattr(name, 'encode'): + name = name.encode('ascii') + err = _libkmod_h.kmod_module_new_from_name( + self._kmod_ctx, name, &mod.module) + if err < 0: + raise _KmodError('Could not get module') + return mod + + def list(self): + "iterate through currently loaded modules and sizes" + for mod in self.loaded(): + yield (mod.name, mod.size) + + def modprobe(self, alias_name, *args, **kwargs): + for mod in self.lookup(alias_name=alias_name): + mod.insert(*args, **kwargs) + + def rmmod(self, module_name, *args, **kwargs): + mod = self.module_from_name(name=module_name) + mod.remove(*args, **kwargs) diff --git a/kmod/list.pxd b/kmod/list.pxd new file mode 100644 index 0000000..6c3725b --- /dev/null +++ b/kmod/list.pxd @@ -0,0 +1,9 @@ +cimport _libkmod_h + + +cdef class ModListItem (object): + cdef _libkmod_h.kmod_list *list + + +cdef class ModList (ModListItem): + cdef _libkmod_h.kmod_list *_next diff --git a/kmod/list.pyx b/kmod/list.pyx new file mode 100644 index 0000000..e2bf04f --- /dev/null +++ b/kmod/list.pyx @@ -0,0 +1,29 @@ +cimport _libkmod_h + + +cdef class ModListItem (object): + "Wrap a struct kmod_list* list item" + def __cinit__(self): + self.list = NULL + + +cdef class ModList (ModListItem): + "Wrap a struct kmod_list* list with iteration" + def __cinit__(self): + self._next = NULL + + def __dealloc__(self): + if self.list is not NULL: + _libkmod_h.kmod_module_unref_list(self.list) + + def __iter__(self): + self._next = self.list + return self + + def __next__(self): + if self._next is NULL: + raise StopIteration() + mli = ModListItem() + mli.list = self._next + self._next = _libkmod_h.kmod_list_next(self.list, self._next) + return mli diff --git a/kmod/module.pxd b/kmod/module.pxd new file mode 100644 index 0000000..3cc4d63 --- /dev/null +++ b/kmod/module.pxd @@ -0,0 +1,8 @@ +cimport _libkmod_h +cimport list as _list + + +cdef class Module (object): + cdef _libkmod_h.kmod_module *module + + cpdef from_mod_list_item(self, _list.ModListItem item) diff --git a/kmod/module.pyx b/kmod/module.pyx new file mode 100644 index 0000000..1e66772 --- /dev/null +++ b/kmod/module.pyx @@ -0,0 +1,65 @@ +import sys as _sys + +cimport _libkmod_h +from error import KmodError as _KmodError +cimport list as _list +import list as _list + + +cdef class Module (object): + "Wrap a struct kmod_module* item" + def __cinit__(self): + self.module = NULL + + def __dealloc__(self): + self._cleanup() + + def _cleanup(self): + if self.module is not NULL: + _libkmod_h.kmod_module_unref(self.module) + self.module = NULL + + cpdef from_mod_list_item(self, _list.ModListItem item): + self._cleanup() + self.module = _libkmod_h.kmod_module_get_module(item.list) + + def _name_get(self): + name = _libkmod_h.kmod_module_get_name(self.module) + if _sys.version_info >= (3,): # Python 3 + name = str(name, 'ascii') + else: # Python 2 + name = unicode(name, 'ascii') + return name + name = property(fget=_name_get) + + def _size_get(self): + return _libkmod_h.kmod_module_get_size(self.module) + size = property(fget=_size_get) + + def insert(self, flags=0, extra_options=None, install_callback=None, + data=None, print_action_callback=None): + cdef char *opt = NULL + #cdef _libkmod_h.install_callback_t install = NULL + cdef int (*install)( + _libkmod_h.kmod_module *, _libkmod_h.const_char_ptr, void *) + install = NULL + cdef void *d = NULL + #cdef _libkmod_h.print_action_callback_t print_action = NULL + cdef void (*print_action)( + _libkmod_h.kmod_module *, _libkmod_h.bool, + _libkmod_h.const_char_ptr) + print_action = NULL + if extra_options: + opt = extra_options + # TODO: convert callbacks and data from Python object to C types + err = _libkmod_h.kmod_module_probe_insert_module( + self.module, flags, opt, install, d, print_action) + if err == -_libkmod_h.EEXIST: + raise _KmodError('Module already loaded') + elif err < 0: + raise _KmodError('Could not load module') + + def remove(self, flags=0): + err = _libkmod_h.kmod_module_remove_module(self.module, flags) + if err < 0: + raise _KmodError('Could not remove module') diff --git a/kmod/version.py b/kmod/version.py new file mode 100644 index 0000000..11d27f8 --- /dev/null +++ b/kmod/version.py @@ -0,0 +1 @@ +__version__ = '0.1' diff --git a/libkmod.c b/libkmod.c deleted file mode 100644 index 82f0de8..0000000 --- a/libkmod.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Libkmod -- Python interface to kmod API. - * - * Copyright (C) 2012 Red Hat, Inc. All rights reserved. - * - * This copyrighted material is made available to anyone wishing to use, - * modify, copy, or redistribute it subject to the terms and conditions - * of the GNU Lesser General Public License v.2.1. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Andy Grover - * - */ - -#include -#include - -typedef struct { - PyObject_HEAD - struct kmod_ctx *ctx; -} kmodobject; - -static PyTypeObject KmodObjType; - -static PyObject *KmodError; - -/* ---------------------------------------------------------------------- - * kmod toplevel module methods - */ -static PyObject * -kmod_library_get_version(PyObject *self, PyObject *unused_args) -{ - return Py_BuildValue("s", "0.1"); -} - -/* ---------------------------------------------------------------------- - * Kmod object initialization/deallocation - */ -static int -kmod_obj_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - kmodobject *kmod = (kmodobject *)self; - char *mod_dir = NULL; - - if (!PyArg_ParseTuple(args, "|s", &mod_dir)) - return -1; - - /* init can be called multiple times */ - if (kmod->ctx) { - kmod_unload_resources(kmod->ctx); - kmod_unref(kmod->ctx); - } - - kmod->ctx = kmod_new(mod_dir, NULL); - if (!kmod->ctx) { - PyErr_SetString(KmodError, "Could not initialize"); - return -1; - } - - kmod_load_resources(kmod->ctx); - - return 0; -} - -static void -kmod_obj_dealloc(PyObject *self) -{ - kmodobject *kmod = (kmodobject *)self; - - kmod_unload_resources(kmod->ctx); - - /* if already closed, don't reclose it */ - if (kmod->ctx != NULL){ - kmod_unref(kmod->ctx); - } - //self->ob_type->tp_free((PyObject*)self); - PyObject_Del(self); -} - -/* - * list currently loaded modules and sizes - */ -static PyObject * -kmod_obj_loaded_modules(PyObject *self, PyObject *unused_args) -{ - kmodobject *kmod = (kmodobject *)self; - struct kmod_list *list, *itr; - int err; - - err = kmod_module_new_from_loaded(kmod->ctx, &list); - if (err < 0) { - PyErr_SetString(KmodError, "Could not get loaded modules"); - return NULL; - } - - PyObject *pylist = PyList_New(0); - if (!pylist) { - kmod_module_unref_list(list); - return PyErr_NoMemory(); - } - - /* refcountapallooza. */ - kmod_list_foreach(itr, list) { - struct kmod_module *mod = kmod_module_get_module(itr); - const char *name = kmod_module_get_name(mod); - long size = kmod_module_get_size(mod); - - PyObject *entry = Py_BuildValue("(sl)", name, size); - if (!entry) { - Py_DECREF(pylist); - kmod_module_unref(mod); - kmod_module_unref_list(list); - return NULL; - } - - if (PyList_Append(pylist, entry) == -1) { - Py_DECREF(entry); - Py_DECREF(pylist); - kmod_module_unref(mod); - kmod_module_unref_list(list); - return NULL; - } - - Py_DECREF(entry); - kmod_module_unref(mod); - } - kmod_module_unref_list(list); - - return pylist; -} - -static PyObject * -kmod_obj_modprobe(PyObject *self, PyObject *args) -{ - kmodobject *kmod = (kmodobject *)self; - struct kmod_list *list = NULL, *itr; - struct kmod_module *mod; - char *alias_name; - unsigned int flags = KMOD_PROBE_APPLY_BLACKLIST; - int err; - - if (!PyArg_ParseTuple(args, "s", &alias_name)) - return NULL; - - err = kmod_module_new_from_lookup(kmod->ctx, alias_name, &list); - if (err < 0) { - PyErr_SetString(KmodError, "Could not modprobe"); - return NULL; - } - - kmod_list_foreach(itr, list) { - mod = kmod_module_get_module(itr); - - err = kmod_module_probe_insert_module(mod, flags, - NULL, NULL, NULL, NULL); - - if (err < 0) { - if (err == -EEXIST) { - PyErr_SetString(KmodError, "Module already loaded"); - goto err; - } else { - PyErr_SetString(KmodError, "Could not load module"); - goto err; - } - } - - kmod_module_unref(mod); - } - kmod_module_unref_list(list); - - Py_INCREF(Py_None); - return Py_None; - -err: - kmod_module_unref(mod); - kmod_module_unref_list(list); - return NULL; -} - -static PyObject * -kmod_obj_rmmod(PyObject *self, PyObject *args) -{ - kmodobject *kmod = (kmodobject *)self; - struct kmod_module *mod; - char *module_name; - int err; - - if (!PyArg_ParseTuple(args, "s", &module_name)) - return NULL; - - err = kmod_module_new_from_name(kmod->ctx, module_name, &mod); - if (err < 0) { - PyErr_SetString(KmodError, "Could get module"); - return NULL; - } - - err = kmod_module_remove_module(mod, 0); - if (err < 0) { - PyErr_SetString(KmodError, "Could not remove module"); - kmod_module_unref(mod); - return NULL; - } - kmod_module_unref(mod); - - Py_INCREF(Py_None); - return Py_None; -} - -/* ---------------------------------------------------------------------- - * Method tables and other bureaucracy - */ - -static PyMethodDef kmod_lib_methods[] = { - {"getVersion", kmod_library_get_version, METH_NOARGS }, - {NULL, NULL} /* sentinel */ -}; - -static PyMethodDef kmod_obj_methods[] = { - {"loaded_modules", kmod_obj_loaded_modules, METH_NOARGS, - "List loaded kernel modules, and their sizes"}, - {"modprobe", kmod_obj_modprobe, METH_VARARGS, - "Load a kernel module"}, - {"rmmod", kmod_obj_rmmod, METH_VARARGS, - "Unload a kernel module"}, - {NULL, NULL} /* sentinel */ -}; - - -static PyTypeObject KmodObjType = { - PyObject_HEAD_INIT(NULL) - .tp_name = "kmod.Kmod", - .tp_basicsize = sizeof(kmodobject), - .tp_new = PyType_GenericNew, - .tp_init = kmod_obj_init, - .tp_dealloc = kmod_obj_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = "kmod.Kmod object", - .tp_methods = kmod_obj_methods, -}; - -#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ -#define PyMODINIT_FUNC void -#endif -PyMODINIT_FUNC -initkmod(void) -{ - PyObject *m; - - if (PyType_Ready(&KmodObjType) < 0) - return; - - m = Py_InitModule3("kmod", kmod_lib_methods, "kmod module"); - if (!m) - return; - - Py_INCREF(&KmodObjType); - PyModule_AddObject(m, "Kmod", (PyObject *)&KmodObjType); - - KmodError = PyErr_NewException("kmod.KmodError", - NULL, NULL); - if (KmodError) { - /* Each call to PyModule_AddObject decrefs it; compensate: */ - Py_INCREF(KmodError); - PyModule_AddObject(m, "KmodError", KmodError); - } -} diff --git a/setup.py b/setup.py index 09c6675..301967c 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,38 @@ -from distutils.core import setup, Extension +from distutils.core import setup +from distutils.extension import Extension as _Extension +import os as _os +import sys as _sys -libkmod = Extension('kmod', - sources = ['libkmod.c'], - libraries= ['kmod']) +from Cython.Distutils import build_ext as _build_ext -setup (name = 'kmod', - version = '0.1', - description = 'Python binding for kmod', - ext_modules = [libkmod]) + +package_name = 'kmod' + +# read version from local kmod/version.py without pulling in +# kmod/__init__.py +_sys.path.insert(0, package_name) +from version import __version__ + + +_this_dir = _os.path.dirname(__file__) + +ext_modules = [] +for filename in sorted(_os.listdir(package_name)): + basename,extension = _os.path.splitext(filename) + if extension == '.pyx': + ext_modules.append( + _Extension( + '{}.{}'.format(package_name, basename), + [_os.path.join(package_name, filename)], + libraries=['kmod'], + )) + +setup( + name=package_name, + version=__version__, + description='Python binding for kmod', + packages=[package_name], + provides=[package_name], + cmdclass = {'build_ext': _build_ext}, + ext_modules=ext_modules, + ) -- 2.26.2