With Cython we get easier memory handling and Python 3 compatibility.
Signed-off-by: W. Trevor King <wking@tremily.us>
--- /dev/null
+__pycache__
+build
+dist
+*.c
+*.pyc
+*.so
--- /dev/null
+# 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 <agrover redhat com>
+
+"Libkmod -- Python interface to kmod API."
+
+from .version import __version__
+from .kmod import Kmod
--- /dev/null
+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)
--- /dev/null
+class KmodError (Exception):
+ pass
--- /dev/null
+cimport _libkmod_h
+
+cdef class Kmod (object):
+ cdef _libkmod_h.kmod_ctx *_kmod_ctx
+ cdef object mod_dir
--- /dev/null
+# 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)
--- /dev/null
+cimport _libkmod_h
+
+
+cdef class ModListItem (object):
+ cdef _libkmod_h.kmod_list *list
+
+
+cdef class ModList (ModListItem):
+ cdef _libkmod_h.kmod_list *_next
--- /dev/null
+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
--- /dev/null
+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)
--- /dev/null
+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')
--- /dev/null
+__version__ = '0.1'
+++ /dev/null
-/*
- * 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 <agrover redhat com>
- *
- */
-
-#include <Python.h>
-#include <libkmod.h>
-
-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);
- }
-}
-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,
+ )