Initial import
[samba] / source / python / py_tdb.c
diff --git a/source/python/py_tdb.c b/source/python/py_tdb.c
new file mode 100644 (file)
index 0000000..f73aa0b
--- /dev/null
@@ -0,0 +1,653 @@
+/* 
+   Python wrappers for TDB module
+
+   Copyright (C) Tim Potter, 2002-2003
+   
+     ** NOTE! The following LGPL license applies to the tdb python
+     ** scripting library. This does NOT imply that all of Samba is 
+     ** released under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "includes.h"
+
+/* This symbol is used in both includes.h and Python.h which causes an
+   annoying compiler warning. */
+
+#ifdef HAVE_FSTAT
+#undef HAVE_FSTAT
+#endif
+
+#include "Python.h"
+
+/* Tdb exception */
+
+PyObject *py_tdb_error;
+
+/* tdb handle object */
+
+typedef struct {
+       PyObject_HEAD
+       TDB_CONTEXT *tdb;
+} tdb_hnd_object;
+
+PyTypeObject tdb_hnd_type;
+     
+PyObject *new_tdb_hnd_object(TDB_CONTEXT *tdb)
+{
+       tdb_hnd_object *obj;
+
+       obj = PyObject_New(tdb_hnd_object, &tdb_hnd_type);
+       obj->tdb = tdb;
+
+       return (PyObject *)obj;
+}
+
+PyObject *py_tdb_close(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj;
+
+       if (!PyArg_ParseTuple(args, "O!", &tdb_hnd_type, &obj))
+               return NULL;
+
+       if (tdb_close(obj->tdb) == -1) {
+               obj->tdb = NULL;
+               PyErr_SetString(py_tdb_error, strerror(errno));
+               return NULL;
+       }
+
+       obj->tdb = NULL;
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+PyObject *py_tdb_open(PyObject *self, PyObject *args, PyObject *kw)
+{
+       static char *kwlist[] = { "name", "hash_size", "tdb_flags",
+                                 "open_flags", "mode", NULL };
+       char *name;
+       int hash_size = 0, flags = TDB_DEFAULT, open_flags = -1, open_mode = 0600;      
+       TDB_CONTEXT *tdb;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "s|iiii", kwlist, &name, &hash_size, &flags,
+                   &open_flags, &open_mode))
+               return NULL;
+
+       /* Default open_flags to read/write */
+
+       if (open_flags == -1) {
+               if (access(name, W_OK) == -1)
+                       open_flags = O_RDONLY;
+               else
+                       open_flags = O_RDWR;
+       }
+
+       if (!(tdb = tdb_open(name, hash_size, flags, open_flags, open_mode))) {
+               PyErr_SetString(py_tdb_error, strerror(errno));
+               return NULL;
+       }
+
+       return new_tdb_hnd_object(tdb);
+}
+
+/*
+ * Allow a tdb to act as a python mapping (dictionary)
+ */
+
+static int tdb_traverse_count(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+                             void *state)
+{
+       /* Do nothing - tdb_traverse will return the number of records
+           traversed. */
+
+       return 0;
+}
+
+static int tdb_hnd_length(tdb_hnd_object *obj)
+{
+       int result;
+
+       result = tdb_traverse(obj->tdb, tdb_traverse_count, NULL);
+
+       return result;
+}
+
+static PyObject *tdb_hnd_subscript(tdb_hnd_object *obj, PyObject *key)
+{
+       TDB_DATA drec, krec;
+       PyObject *result;
+
+       if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize))
+               return NULL;
+
+       drec = tdb_fetch(obj->tdb, krec);
+
+       if (!drec.dptr) {
+               PyErr_SetString(PyExc_KeyError,
+                               PyString_AsString(key));
+               return NULL;
+       }
+
+       result = PyString_FromStringAndSize(drec.dptr, drec.dsize);
+       free(drec.dptr);
+
+       return result;
+}
+       
+static int tdb_ass_subscript(tdb_hnd_object *obj, PyObject *key, PyObject *value)
+{
+       TDB_DATA krec, drec;
+
+        if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) {
+               PyErr_SetString(PyExc_TypeError,
+                               "tdb mappings have string indices only");
+               return -1;
+       }
+
+        if (!obj->tdb) {
+               PyErr_SetString(
+                       py_tdb_error, "tdb object has been closed"); 
+               return -1; 
+        }
+
+       if (!value) {
+
+               /* Delete value */
+
+               if (tdb_delete(obj->tdb, krec) == -1) {
+                       PyErr_SetString(PyExc_KeyError,
+                                       PyString_AsString(value));
+                       return -1;
+               }
+
+       } else {
+
+               /* Set value */
+
+               if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) {
+                       PyErr_SetString(PyExc_TypeError,
+                                   "tdb mappings have string elements only");
+                       return -1;
+               }
+
+               errno = 0;
+
+               if (tdb_store(obj->tdb, krec, drec, 0) < 0 ) {
+                       if (errno != 0)
+                               PyErr_SetFromErrno(py_tdb_error);
+                       else
+                               PyErr_SetString(
+                                       py_tdb_error, 
+                                       (char *)tdb_errorstr(obj->tdb));
+
+                       return -1;
+               }
+       }
+
+       return 0;
+} 
+
+static PyMappingMethods tdb_mapping = {
+       (inquiry) tdb_hnd_length,
+       (binaryfunc) tdb_hnd_subscript,
+       (objobjargproc) tdb_ass_subscript
+};
+
+/*
+ * Utility methods
+ */
+
+/* Return non-zero if a given key exists in the tdb */
+
+PyObject *py_tdb_hnd_has_key(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key;
+
+       if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+               return NULL;
+
+        if (!obj->tdb) {
+               PyErr_SetString(
+                       py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       return PyInt_FromLong(tdb_exists(obj->tdb, key));
+}
+
+/* Return a list of keys in the tdb */
+
+static int tdb_traverse_keys(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+                            void *state)
+{
+       PyObject *key_list = (PyObject *)state;
+
+       PyList_Append(key_list, 
+                     PyString_FromStringAndSize(key.dptr, key.dsize));
+
+       return 0;
+}
+
+PyObject *py_tdb_hnd_keys(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       PyObject *key_list = PyList_New(0);
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (tdb_traverse(obj->tdb, tdb_traverse_keys, key_list) == -1) {
+               PyErr_SetString(py_tdb_error, "error traversing tdb");
+               Py_DECREF(key_list);
+               return NULL;
+       }
+
+       return key_list;        
+}
+
+PyObject *py_tdb_hnd_first_key(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       key = tdb_firstkey(obj->tdb);
+
+       return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+PyObject *py_tdb_hnd_next_key(PyObject *self, PyObject *py_oldkey)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key, oldkey;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (!PyArg_Parse(py_oldkey, "s#", &oldkey.dptr, &oldkey.dsize))
+               return NULL;
+
+       key = tdb_nextkey(obj->tdb, oldkey);
+
+       return Py_BuildValue("s#", key.dptr, key.dsize);
+}
+
+/*
+ * Locking routines
+ */
+
+PyObject *py_tdb_hnd_lock_all(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       int result;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       result = tdb_lockall(obj->tdb);
+
+       return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock_all(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       tdb_unlockall(obj->tdb);
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* Return an array of keys from a python object which must be a string or a
+   list of strings. */
+
+static BOOL make_lock_list(PyObject *py_keys, TDB_DATA **keys, int *num_keys)
+{
+       /* Are we a list or a string? */
+
+       if (!PyList_Check(py_keys) && !PyString_Check(py_keys)) {
+               PyErr_SetString(PyExc_TypeError, "arg must be list of string");
+               return False;
+       }
+
+       if (PyList_Check(py_keys)) {
+               int i;
+
+               /* Turn python list into array of keys */
+               
+               *num_keys = PyList_Size(py_keys);
+               *keys = (TDB_DATA *)SMB_XMALLOC_ARRAY(TDB_DATA, (*num_keys));
+               
+               for (i = 0; i < *num_keys; i++) {
+                       PyObject *key = PyList_GetItem(py_keys, i);
+                       
+                       if (!PyString_Check(key)) {
+                               PyErr_SetString(
+                                       PyExc_TypeError,
+                                       "list elements must be strings");
+                               return False;
+                       }
+
+                       PyArg_Parse(key, "s#", &(*keys)[i].dptr, 
+                                   &(*keys)[i].dsize);
+               }
+
+       } else {
+
+               /* Turn python string into a single key */
+
+               *keys = (TDB_DATA *)SMB_XMALLOC_P(TDB_DATA);
+               *num_keys = 1;
+               PyArg_Parse(py_keys, "s#", &(*keys)->dptr, &(*keys)->dsize);
+       }
+
+       return True;
+}
+
+/*
+ * tdb traversal
+ */
+
+struct traverse_info {
+       PyObject *callback;
+       PyObject *state;
+};
+
+static int tdb_traverse_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
+                                void *state)
+{
+       struct traverse_info *info = state;
+       PyObject *arglist, *py_result;
+       int result;
+
+       arglist = Py_BuildValue("(s#s#O)", key.dptr, key.dsize, value.dptr,
+                               value.dsize, info->state);
+
+       py_result = PyEval_CallObject(info->callback, arglist);
+
+       Py_DECREF(arglist);
+       
+       if (!PyInt_Check(py_result)) {
+               result = 1;     /* Hmm - non-integer object returned by callback */
+               goto done;
+       }
+
+       result = PyInt_AsLong(py_result);
+
+done:
+       Py_DECREF(py_result);
+       return result;
+}
+
+PyObject *py_tdb_hnd_traverse(PyObject *self, PyObject *args, PyObject *kw)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       static char *kwlist[] = { "traverse_fn", "state", NULL };
+       PyObject *state = Py_None, *callback;
+       struct traverse_info info;
+       int result;
+
+       if (!PyArg_ParseTupleAndKeywords(
+                   args, kw, "O|O", kwlist, &callback, &state))
+               return NULL;
+
+       if (!PyCallable_Check(callback)) {
+               PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+               return NULL;
+        }
+
+       Py_INCREF(callback);
+       Py_INCREF(state);
+
+       info.callback = callback;
+       info.state = state;
+
+       result = tdb_traverse(obj->tdb, tdb_traverse_traverse, &info);
+
+       Py_DECREF(callback);
+       Py_DECREF(state);
+
+       return PyInt_FromLong(result);
+}
+
+PyObject *py_tdb_hnd_chainlock(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key;
+       int result;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+               return NULL;
+
+       result = tdb_chainlock(obj->tdb, key);
+
+       return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_chainunlock(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       TDB_DATA key;
+       int result;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
+               return NULL;
+
+       result = tdb_chainunlock(obj->tdb, key);
+
+       return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_lock_bystring(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       int result, timeout = 30;
+       char *s;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (!PyArg_ParseTuple(args, "s|i", &s, &timeout))
+               return NULL;
+
+       result = tdb_lock_bystring(obj->tdb, s, timeout);
+
+       return PyInt_FromLong(result != -1);
+}
+
+PyObject *py_tdb_hnd_unlock_bystring(PyObject *self, PyObject *args)
+{
+       tdb_hnd_object *obj = (tdb_hnd_object *)self;
+       char *s;
+
+        if (!obj->tdb) {
+               PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
+               return NULL;
+        }      
+
+       if (!PyArg_ParseTuple(args, "s", &s))
+               return NULL;
+
+       tdb_unlock_bystring(obj->tdb, s);
+
+       Py_INCREF(Py_None);
+       return Py_None;
+}
+
+/* 
+ * Method dispatch table for this module
+ */
+
+static PyMethodDef tdb_methods[] = {
+       { "open", (PyCFunction)py_tdb_open, METH_VARARGS | METH_KEYWORDS },
+       { "close", (PyCFunction)py_tdb_close, METH_VARARGS },
+       { NULL }
+};
+
+/* 
+ * Methods on a tdb object
+ */
+
+static PyMethodDef tdb_hnd_methods[] = {
+       { "keys", (PyCFunction)py_tdb_hnd_keys, METH_VARARGS },
+       { "has_key", (PyCFunction)py_tdb_hnd_has_key, METH_VARARGS },
+       { "first_key", (PyCFunction)py_tdb_hnd_first_key, METH_VARARGS },
+       { "next_key", (PyCFunction)py_tdb_hnd_next_key, METH_VARARGS },
+       { "lock_all", (PyCFunction)py_tdb_hnd_lock_all, METH_VARARGS },
+       { "unlock_all", (PyCFunction)py_tdb_hnd_unlock_all, METH_VARARGS },
+       { "traverse", (PyCFunction)py_tdb_hnd_traverse, METH_VARARGS | METH_KEYWORDS },
+       { "chainlock", (PyCFunction)py_tdb_hnd_chainlock, METH_VARARGS | METH_KEYWORDS },
+       { "chainunlock", (PyCFunction)py_tdb_hnd_chainunlock, METH_VARARGS | METH_KEYWORDS },
+       { "lock_bystring", (PyCFunction)py_tdb_hnd_lock_bystring, METH_VARARGS | METH_KEYWORDS },
+       { "unlock_bystring", (PyCFunction)py_tdb_hnd_unlock_bystring, METH_VARARGS | METH_KEYWORDS },
+       { NULL }
+};
+
+/* Deallocate a tdb handle object */
+
+static void tdb_hnd_dealloc(PyObject* self)
+{
+        tdb_hnd_object *hnd = (tdb_hnd_object *)self;
+
+       if (hnd->tdb) {
+               tdb_close(hnd->tdb);
+               hnd->tdb = NULL;
+       }
+}
+
+/* Return tdb handle attributes */
+
+static PyObject *tdb_hnd_getattr(PyObject *self, char *attrname)
+{
+       return Py_FindMethod(tdb_hnd_methods, self, attrname);
+}
+
+static char tdb_hnd_type_doc[] = 
+"Python wrapper for tdb.";
+
+PyTypeObject tdb_hnd_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,
+       "tdb",
+       sizeof(tdb_hnd_object),
+       0,
+       tdb_hnd_dealloc,        /* tp_dealloc*/
+       0,                      /* tp_print*/
+       tdb_hnd_getattr,        /* tp_getattr*/
+       0,                      /* tp_setattr*/
+       0,                      /* tp_compare*/
+       0,                      /* tp_repr*/
+       0,                      /* tp_as_number*/
+       0,                      /* tp_as_sequence*/
+       &tdb_mapping,           /* tp_as_mapping*/
+       0,                      /* tp_hash */
+       0,                      /* tp_call */
+       0,                      /* tp_str */
+       0,                      /* tp_getattro */
+       0,                      /* tp_setattro */
+       0,                      /* tp_as_buffer*/
+       Py_TPFLAGS_DEFAULT,     /* tp_flags */
+       tdb_hnd_type_doc,       /* tp_doc */
+};
+
+/* Constants */
+
+static struct const_vals {
+       char *name;
+       uint32 value;
+} module_const_vals[] = {
+
+        /* Flags for tdb_open() */
+
+       { "TDB_DEFAULT", TDB_DEFAULT },
+       { "TDB_CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST },
+       { "TDB_INTERNAL", TDB_INTERNAL },
+       { "TDB_NOLOCK", TDB_NOLOCK },
+       { "TDB_NOMMAP", TDB_NOMMAP },
+       { "TDB_CONVERT", TDB_CONVERT },
+       { "TDB_BIGENDIAN", TDB_BIGENDIAN },
+       
+       { NULL },
+};
+
+static void const_init(PyObject *dict)
+{
+       struct const_vals *tmp;
+       PyObject *obj;
+
+       for (tmp = module_const_vals; tmp->name; tmp++) {
+               obj = PyInt_FromLong(tmp->value);
+               PyDict_SetItemString(dict, tmp->name, obj);
+               Py_DECREF(obj);
+       }
+}
+
+/* Module initialisation */
+
+void inittdb(void)
+{
+       PyObject *module, *dict;
+
+       /* Initialise module */
+
+       module = Py_InitModule("tdb", tdb_methods);
+       dict = PyModule_GetDict(module);
+
+       py_tdb_error = PyErr_NewException("tdb.error", NULL, NULL);
+       PyDict_SetItemString(dict, "error", py_tdb_error);
+
+       /* Initialise policy handle object */
+
+       tdb_hnd_type.ob_type = &PyType_Type;
+
+       PyDict_SetItemString(dict, "tdb.hnd", 
+                            (PyObject *)&tdb_hnd_type);
+
+       /* Initialise constants */
+
+       const_init(dict);
+}