lib/tdb: include replace.h and system/filesys.h in pytdb.c
[ira/wip.git] / lib / tdb / pytdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Swig interface to tdb.
5
6    Copyright (C) 2004-2006 Tim Potter <tpot@samba.org>
7    Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
8
9      ** NOTE! The following LGPL license applies to the tdb
10      ** library. This does NOT imply that all of Samba is released
11      ** under the LGPL
12    
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 3 of the License, or (at your option) any later version.
17
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "replace.h"
28 #include "system/filesys.h"
29
30 #include <Python.h>
31 #ifndef Py_RETURN_NONE
32 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
33 #endif
34
35 #ifdef HAVE_FSTAT
36 #undef HAVE_FSTAT
37 #endif
38
39 /* Include tdb headers */
40 #include <tdb.h>
41
42 typedef struct {
43         PyObject_HEAD
44         TDB_CONTEXT *ctx;
45         bool closed;
46 } PyTdbObject;
47
48 PyAPI_DATA(PyTypeObject) PyTdb;
49
50 static void PyErr_SetTDBError(TDB_CONTEXT *tdb)
51 {
52         PyErr_SetObject(PyExc_RuntimeError, 
53                 Py_BuildValue("(i,s)", tdb_error(tdb), tdb_errorstr(tdb)));
54 }
55
56 static TDB_DATA PyString_AsTDB_DATA(PyObject *data)
57 {
58         TDB_DATA ret;
59         ret.dptr = (unsigned char *)PyString_AsString(data);
60         ret.dsize = PyString_Size(data);
61         return ret;
62 }
63
64 static PyObject *PyString_FromTDB_DATA(TDB_DATA data)
65 {
66         if (data.dptr == NULL && data.dsize == 0) {
67                 Py_RETURN_NONE;
68         } else {
69                 PyObject *ret = PyString_FromStringAndSize((const char *)data.dptr, 
70                                                                                                    data.dsize);
71                 free(data.dptr);
72                 return ret;
73     }
74 }
75
76 #define PyErr_TDB_ERROR_IS_ERR_RAISE(ret, tdb) \
77         if (ret != 0) { \
78                 PyErr_SetTDBError(tdb); \
79                 return NULL; \
80         }
81
82 static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs)
83 {
84         char *name;
85         int hash_size = 0, tdb_flags = TDB_DEFAULT, flags = O_RDWR, mode = 0600;
86         TDB_CONTEXT *ctx;
87         PyTdbObject *ret;
88         const char *kwnames[] = { "name", "hash_size", "tdb_flags", "flags", "mode", NULL };
89
90         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|iiii", (char **)kwnames, &name, &hash_size, &tdb_flags, &flags, &mode))
91                 return NULL;
92
93         ctx = tdb_open(name, hash_size, tdb_flags, flags, mode);
94         if (ctx == NULL) {
95                 PyErr_SetFromErrno(PyExc_IOError);
96                 return NULL;
97         }
98
99         ret = PyObject_New(PyTdbObject, &PyTdb);
100         ret->ctx = ctx;
101         ret->closed = false;
102         return (PyObject *)ret;
103 }
104
105 static PyObject *obj_transaction_cancel(PyTdbObject *self)
106 {
107         int ret = tdb_transaction_cancel(self->ctx);
108         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
109         Py_RETURN_NONE;
110 }
111
112 static PyObject *obj_transaction_commit(PyTdbObject *self)
113 {
114         int ret = tdb_transaction_commit(self->ctx);
115         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
116         Py_RETURN_NONE;
117 }
118
119 static PyObject *obj_transaction_recover(PyTdbObject *self)
120 {
121         int ret = tdb_transaction_recover(self->ctx);
122         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
123         Py_RETURN_NONE;
124 }
125
126 static PyObject *obj_transaction_start(PyTdbObject *self)
127 {
128         int ret = tdb_transaction_start(self->ctx);
129         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
130         Py_RETURN_NONE;
131 }
132
133 static PyObject *obj_reopen(PyTdbObject *self)
134 {
135         int ret = tdb_reopen(self->ctx);
136         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
137         Py_RETURN_NONE;
138 }
139
140 static PyObject *obj_lockall(PyTdbObject *self)
141 {
142         int ret = tdb_lockall(self->ctx);
143         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
144         Py_RETURN_NONE;
145 }
146
147 static PyObject *obj_unlockall(PyTdbObject *self)
148 {
149         int ret = tdb_unlockall(self->ctx);
150         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
151         Py_RETURN_NONE;
152 }
153
154 static PyObject *obj_lockall_read(PyTdbObject *self)
155 {
156         int ret = tdb_lockall_read(self->ctx);
157         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
158         Py_RETURN_NONE;
159 }
160
161 static PyObject *obj_unlockall_read(PyTdbObject *self)
162 {
163         int ret = tdb_unlockall_read(self->ctx);
164         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
165         Py_RETURN_NONE;
166 }
167
168 static PyObject *obj_close(PyTdbObject *self)
169 {
170         int ret;
171         if (self->closed)
172                 Py_RETURN_NONE;
173         ret = tdb_close(self->ctx);
174         self->closed = true;
175         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
176         Py_RETURN_NONE;
177 }
178
179 static PyObject *obj_get(PyTdbObject *self, PyObject *args)
180 {
181         TDB_DATA key;
182         PyObject *py_key;
183         if (!PyArg_ParseTuple(args, "O", &py_key))
184                 return NULL;
185
186         key = PyString_AsTDB_DATA(py_key);
187
188         return PyString_FromTDB_DATA(tdb_fetch(self->ctx, key));
189 }
190
191 static PyObject *obj_append(PyTdbObject *self, PyObject *args)
192 {
193         TDB_DATA key, data;
194         PyObject *py_key, *py_data;
195         int ret;
196         if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data))
197                 return NULL;
198
199         key = PyString_AsTDB_DATA(py_key);
200         data = PyString_AsTDB_DATA(py_data);
201
202         ret = tdb_append(self->ctx, key, data);
203         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
204         Py_RETURN_NONE;
205 }
206
207 static PyObject *obj_firstkey(PyTdbObject *self)
208 {
209         return PyString_FromTDB_DATA(tdb_firstkey(self->ctx));
210 }
211
212 static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args)
213 {
214         TDB_DATA key;
215         PyObject *py_key;
216         if (!PyArg_ParseTuple(args, "O", &py_key))
217                 return NULL;
218
219         key = PyString_AsTDB_DATA(py_key);
220         
221         return PyString_FromTDB_DATA(tdb_nextkey(self->ctx, key));
222 }
223
224 static PyObject *obj_delete(PyTdbObject *self, PyObject *args)
225 {
226         TDB_DATA key;
227         PyObject *py_key;
228         int ret;
229         if (!PyArg_ParseTuple(args, "O", &py_key))
230                 return NULL;
231
232         key = PyString_AsTDB_DATA(py_key);
233         ret = tdb_delete(self->ctx, key);
234         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
235         Py_RETURN_NONE;
236 }
237
238 static PyObject *obj_has_key(PyTdbObject *self, PyObject *args)
239 {
240         TDB_DATA key;
241         int ret;
242         PyObject *py_key;
243         if (!PyArg_ParseTuple(args, "O", &py_key))
244                 return NULL;
245
246         key = PyString_AsTDB_DATA(py_key);
247         ret = tdb_exists(self->ctx, key);
248         if (ret != TDB_ERR_NOEXIST) {
249                 PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
250         }
251
252         return (ret == TDB_ERR_NOEXIST)?Py_False:Py_True;
253 }
254
255 static PyObject *obj_store(PyTdbObject *self, PyObject *args)
256 {
257         TDB_DATA key, value;
258         int ret;
259         int flag = TDB_REPLACE;
260         PyObject *py_key, *py_value;
261
262         if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag))
263                 return NULL;
264
265         key = PyString_AsTDB_DATA(py_key);
266         value = PyString_AsTDB_DATA(py_value);
267
268         ret = tdb_store(self->ctx, key, value, flag);
269         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
270         Py_RETURN_NONE;
271 }
272
273
274 typedef struct {
275         PyObject_HEAD
276         TDB_DATA current;
277         PyTdbObject *iteratee;
278 } PyTdbIteratorObject;
279
280 static PyObject *tdb_iter_next(PyTdbIteratorObject *self)
281 {
282         TDB_DATA current;
283         PyObject *ret;
284         if (self->current.dptr == NULL && self->current.dsize == 0)
285                 return NULL;
286         current = self->current;
287         self->current = tdb_nextkey(self->iteratee->ctx, self->current);
288         ret = PyString_FromTDB_DATA(current);
289         return ret;
290 }
291
292 static void tdb_iter_dealloc(PyTdbIteratorObject *self)
293 {
294         Py_DECREF(self->iteratee);
295         PyObject_Del(self);
296 }
297
298 PyTypeObject PyTdbIterator = {
299         .tp_name = "Iterator",
300         .tp_basicsize = sizeof(PyTdbIteratorObject),
301         .tp_iternext = (iternextfunc)tdb_iter_next,
302         .tp_dealloc = (destructor)tdb_iter_dealloc,
303         .tp_flags = Py_TPFLAGS_DEFAULT,
304         .tp_iter = PyObject_SelfIter,
305 };
306
307 static PyObject *tdb_object_iter(PyTdbObject *self)
308 {
309         PyTdbIteratorObject *ret;       
310
311         ret = PyObject_New(PyTdbIteratorObject, &PyTdbIterator);
312         ret->current = tdb_firstkey(self->ctx);
313         ret->iteratee = self;
314         Py_INCREF(self);
315         return (PyObject *)ret;
316 }
317
318 static PyObject *obj_clear(PyTdbObject *self)
319 {
320         int ret = tdb_wipe_all(self->ctx);
321         PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx);
322         Py_RETURN_NONE;
323 }
324
325 static PyMethodDef tdb_object_methods[] = {
326         { "transaction_cancel", (PyCFunction)obj_transaction_cancel, METH_NOARGS, 
327                 "S.transaction_cancel() -> None\n"
328                 "Cancel the currently active transaction." },
329         { "transaction_commit", (PyCFunction)obj_transaction_commit, METH_NOARGS,
330                 "S.transaction_commit() -> None\n"
331                 "Commit the currently active transaction." },
332         { "transaction_recover", (PyCFunction)obj_transaction_recover, METH_NOARGS,
333                 "S.transaction_recover() -> None\n"
334                 "Recover the currently active transaction." },
335         { "transaction_start", (PyCFunction)obj_transaction_start, METH_NOARGS,
336                 "S.transaction_start() -> None\n"
337                 "Start a new transaction." },
338         { "reopen", (PyCFunction)obj_reopen, METH_NOARGS, "Reopen this file." },
339         { "lock_all", (PyCFunction)obj_lockall, METH_NOARGS, NULL },
340         { "unlock_all", (PyCFunction)obj_unlockall, METH_NOARGS, NULL },
341         { "read_lock_all", (PyCFunction)obj_lockall_read, METH_NOARGS, NULL },
342         { "read_unlock_all", (PyCFunction)obj_unlockall_read, METH_NOARGS, NULL },
343         { "close", (PyCFunction)obj_close, METH_NOARGS, NULL },
344         { "get", (PyCFunction)obj_get, METH_VARARGS, "S.fetch(key) -> value\n"
345                 "Fetch a value." },
346         { "append", (PyCFunction)obj_append, METH_VARARGS, "S.append(key, value) -> None\n"
347                 "Append data to an existing key." },
348         { "firstkey", (PyCFunction)obj_firstkey, METH_NOARGS, "S.firstkey() -> data\n"
349                 "Return the first key in this database." },
350         { "nextkey", (PyCFunction)obj_nextkey, METH_NOARGS, "S.nextkey(key) -> data\n"
351                 "Return the next key in this database." },
352         { "delete", (PyCFunction)obj_delete, METH_VARARGS, "S.delete(key) -> None\n"
353                 "Delete an entry." },
354         { "has_key", (PyCFunction)obj_has_key, METH_VARARGS, "S.has_key(key) -> None\n"
355                 "Check whether key exists in this database." },
356         { "store", (PyCFunction)obj_store, METH_VARARGS, "S.store(key, data, flag=REPLACE) -> None"
357                 "Store data." },
358         { "iterkeys", (PyCFunction)tdb_object_iter, METH_NOARGS, "S.iterkeys() -> iterator" },
359         { "clear", (PyCFunction)obj_clear, METH_NOARGS, "S.clear() -> None\n"
360                 "Wipe the entire database." },
361         { NULL }
362 };
363
364 static PyObject *obj_get_hash_size(PyTdbObject *self, void *closure)
365 {
366         return PyInt_FromLong(tdb_hash_size(self->ctx));
367 }
368
369 static int obj_set_max_dead(PyTdbObject *self, PyObject *max_dead, void *closure)
370 {
371         if (!PyInt_Check(max_dead))
372                 return -1;
373         tdb_set_max_dead(self->ctx, PyInt_AsLong(max_dead));
374         return 0;
375 }
376
377 static PyObject *obj_get_map_size(PyTdbObject *self, void *closure)
378 {
379         return PyInt_FromLong(tdb_map_size(self->ctx));
380 }
381
382 static PyObject *obj_get_flags(PyTdbObject *self, void *closure)
383 {
384         return PyInt_FromLong(tdb_get_flags(self->ctx));
385 }
386
387 static PyObject *obj_get_filename(PyTdbObject *self, void *closure)
388 {
389         return PyString_FromString(tdb_name(self->ctx));
390 }
391
392 static PyGetSetDef tdb_object_getsetters[] = {
393         { (char *)"hash_size", (getter)obj_get_hash_size, NULL, NULL },
394         { (char *)"map_size", (getter)obj_get_map_size, NULL, NULL },
395         { (char *)"flags", (getter)obj_get_flags, NULL, NULL },
396         { (char *)"max_dead", NULL, (setter)obj_set_max_dead, NULL },
397         { (char *)"filename", (getter)obj_get_filename, NULL, (char *)"The filename of this TDB file."},
398         { NULL }
399 };
400
401 static PyObject *tdb_object_repr(PyTdbObject *self)
402 {
403         return PyString_FromFormat("Tdb('%s')", tdb_name(self->ctx));
404 }
405
406 static void tdb_object_dealloc(PyTdbObject *self)
407 {
408         if (!self->closed)
409                 tdb_close(self->ctx);
410         PyObject_Del(self);
411 }
412
413 static PyObject *obj_getitem(PyTdbObject *self, PyObject *key)
414 {
415         TDB_DATA tkey, val;
416         if (!PyString_Check(key)) {
417                 PyErr_SetString(PyExc_TypeError, "Expected string as key");
418                 return NULL;
419         }
420
421         tkey.dptr = (unsigned char *)PyString_AsString(key);
422         tkey.dsize = PyString_Size(key);
423
424         val = tdb_fetch(self->ctx, tkey);
425         if (val.dptr == NULL) {
426                 PyErr_SetString(PyExc_KeyError, "No such TDB entry");
427                 return NULL;
428         } else {
429                 return PyString_FromTDB_DATA(val);
430         }
431 }
432
433 static int obj_setitem(PyTdbObject *self, PyObject *key, PyObject *value)
434 {
435         TDB_DATA tkey, tval;
436         int ret;
437         if (!PyString_Check(key)) {
438                 PyErr_SetString(PyExc_TypeError, "Expected string as key");
439                 return -1;
440         }
441
442         tkey = PyString_AsTDB_DATA(key);
443
444         if (value == NULL) { 
445                 ret = tdb_delete(self->ctx, tkey);
446         } else { 
447                 if (!PyString_Check(value)) {
448                         PyErr_SetString(PyExc_TypeError, "Expected string as value");
449                         return -1;
450                 }
451
452                 tval = PyString_AsTDB_DATA(value);
453
454                 ret = tdb_store(self->ctx, tkey, tval, TDB_REPLACE);
455         }
456
457         if (ret != 0) {
458                 PyErr_SetTDBError(self->ctx);
459                 return -1;
460         } 
461
462         return ret;
463 }
464
465 static PyMappingMethods tdb_object_mapping = {
466         .mp_subscript = (binaryfunc)obj_getitem,
467         .mp_ass_subscript = (objobjargproc)obj_setitem,
468 };
469 PyTypeObject PyTdb = {
470         .tp_name = "Tdb",
471         .tp_basicsize = sizeof(PyTdbObject),
472         .tp_methods = tdb_object_methods,
473         .tp_getset = tdb_object_getsetters,
474         .tp_new = py_tdb_open,
475         .tp_doc = "A TDB file",
476         .tp_repr = (reprfunc)tdb_object_repr,
477         .tp_dealloc = (destructor)tdb_object_dealloc,
478         .tp_as_mapping = &tdb_object_mapping,
479         .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_ITER,
480         .tp_iter = (getiterfunc)tdb_object_iter,
481 };
482
483 static PyMethodDef tdb_methods[] = {
484         { "open", (PyCFunction)py_tdb_open, METH_VARARGS|METH_KEYWORDS, "open(name, hash_size=0, tdb_flags=TDB_DEFAULT, flags=O_RDWR, mode=0600)\n"
485                 "Open a TDB file." },
486         { NULL }
487 };
488
489 void inittdb(void)
490 {
491         PyObject *m;
492
493         if (PyType_Ready(&PyTdb) < 0)
494                 return;
495
496         if (PyType_Ready(&PyTdbIterator) < 0)
497                 return;
498
499         m = Py_InitModule3("tdb", tdb_methods, "TDB is a simple key-value database similar to GDBM that supports multiple writers.");
500         if (m == NULL)
501                 return;
502
503         PyModule_AddObject(m, "REPLACE", PyInt_FromLong(TDB_REPLACE));
504         PyModule_AddObject(m, "INSERT", PyInt_FromLong(TDB_INSERT));
505         PyModule_AddObject(m, "MODIFY", PyInt_FromLong(TDB_MODIFY));
506
507         PyModule_AddObject(m, "DEFAULT", PyInt_FromLong(TDB_DEFAULT));
508         PyModule_AddObject(m, "CLEAR_IF_FIRST", PyInt_FromLong(TDB_CLEAR_IF_FIRST));
509         PyModule_AddObject(m, "INTERNAL", PyInt_FromLong(TDB_INTERNAL));
510         PyModule_AddObject(m, "NOLOCK", PyInt_FromLong(TDB_NOLOCK));
511         PyModule_AddObject(m, "NOMMAP", PyInt_FromLong(TDB_NOMMAP));
512         PyModule_AddObject(m, "CONVERT", PyInt_FromLong(TDB_CONVERT));
513         PyModule_AddObject(m, "BIGENDIAN", PyInt_FromLong(TDB_BIGENDIAN));
514         PyModule_AddObject(m, "__docformat__", PyString_FromString("restructuredText"));
515
516         Py_INCREF(&PyTdb);
517         PyModule_AddObject(m, "Tdb", (PyObject *)&PyTdb);
518
519         Py_INCREF(&PyTdbIterator);
520 }