Added chainlock, chainunlock, lock_bystring and unlock_bystring methods.
[samba.git] / source / python / py_tdb.c
1 /* 
2    Python wrappers for TDB module
3
4    Copyright (C) Tim Potter, 2002-2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /* 
22    NOTE: Since tdb is licenced under the GPL any program that uses these bindings
23    must be distributed under the GPL license terms since this is what
24    the GPL requires.
25
26    http://www.gnu.org/licenses/gpl-faq.html#IfInterpreterIsGPL 
27 */
28
29 #include "includes.h"
30 #include "Python.h"
31
32 /* Tdb exception */
33
34 PyObject *py_tdb_error;
35
36 /* tdb handle object */
37
38 typedef struct {
39         PyObject_HEAD
40         TDB_CONTEXT *tdb;
41 } tdb_hnd_object;
42
43 PyTypeObject tdb_hnd_type;
44      
45 PyObject *new_tdb_hnd_object(TDB_CONTEXT *tdb)
46 {
47         tdb_hnd_object *obj;
48
49         obj = PyObject_New(tdb_hnd_object, &tdb_hnd_type);
50         obj->tdb = tdb;
51
52         return (PyObject *)obj;
53 }
54
55 PyObject *py_tdb_close(PyObject *self, PyObject *args)
56 {
57         tdb_hnd_object *obj;
58
59         if (!PyArg_ParseTuple(args, "O!", &tdb_hnd_type, &obj))
60                 return NULL;
61
62         if (tdb_close(obj->tdb) == -1) {
63                 obj->tdb = NULL;
64                 PyErr_SetString(py_tdb_error, strerror(errno));
65                 return NULL;
66         }
67
68         obj->tdb = NULL;
69
70         Py_INCREF(Py_None);
71         return Py_None;
72 }
73
74 PyObject *py_tdb_open(PyObject *self, PyObject *args, PyObject *kw)
75 {
76         static char *kwlist[] = { "name", "hash_size", "tdb_flags",
77                                   "open_flags", "mode", NULL };
78         char *name;
79         int hash_size = 0, flags = TDB_DEFAULT, open_flags = -1, open_mode = 0600;      
80         TDB_CONTEXT *tdb;
81
82         if (!PyArg_ParseTupleAndKeywords(
83                     args, kw, "s|iiii", kwlist, &name, &hash_size, &flags,
84                     &open_flags, &open_mode))
85                 return NULL;
86
87         /* Default open_flags to read/write */
88
89         if (open_flags == -1) {
90                 if (access(name, W_OK) == -1)
91                         open_flags = O_RDONLY;
92                 else
93                         open_flags = O_RDWR;
94         }
95
96         if (!(tdb = tdb_open(name, hash_size, flags, open_flags, open_mode))) {
97                 PyErr_SetString(py_tdb_error, strerror(errno));
98                 return NULL;
99         }
100
101         return new_tdb_hnd_object(tdb);
102 }
103
104 /*
105  * Allow a tdb to act as a python mapping (dictionary)
106  */
107
108 static int tdb_traverse_count(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
109                               void *state)
110 {
111         /* Do nothing - tdb_traverse will return the number of records
112            traversed. */
113
114         return 0;
115 }
116
117 static int tdb_hnd_length(tdb_hnd_object *obj)
118 {
119         int result;
120
121         result = tdb_traverse(obj->tdb, tdb_traverse_count, NULL);
122
123         return result;
124 }
125
126 static PyObject *tdb_hnd_subscript(tdb_hnd_object *obj, PyObject *key)
127 {
128         TDB_DATA drec, krec;
129         PyObject *result;
130
131         if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize))
132                 return NULL;
133
134         drec = tdb_fetch(obj->tdb, krec);
135
136         if (!drec.dptr) {
137                 PyErr_SetString(PyExc_KeyError,
138                                 PyString_AsString(key));
139                 return NULL;
140         }
141
142         result = PyString_FromStringAndSize(drec.dptr, drec.dsize);
143         free(drec.dptr);
144
145         return result;
146 }
147         
148 static int tdb_ass_subscript(tdb_hnd_object *obj, PyObject *key, PyObject *value)
149 {
150         TDB_DATA krec, drec;
151
152         if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) {
153                 PyErr_SetString(PyExc_TypeError,
154                                 "tdb mappings have string indices only");
155                 return -1;
156         }
157
158         if (!obj->tdb) {
159                 PyErr_SetString(
160                         py_tdb_error, "tdb object has been closed"); 
161                 return -1; 
162         }
163
164         if (!value) {
165
166                 /* Delete value */
167
168                 if (tdb_delete(obj->tdb, krec) == -1) {
169                         PyErr_SetString(PyExc_KeyError,
170                                         PyString_AsString(value));
171                         return -1;
172                 }
173
174         } else {
175
176                 /* Set value */
177
178                 if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) {
179                         PyErr_SetString(PyExc_TypeError,
180                                     "tdb mappings have string elements only");
181                         return -1;
182                 }
183
184                 errno = 0;
185
186                 if (tdb_store(obj->tdb, krec, drec, 0) < 0 ) {
187                         if (errno != 0)
188                                 PyErr_SetFromErrno(py_tdb_error);
189                         else
190                                 PyErr_SetString(
191                                         py_tdb_error, 
192                                         (char *)tdb_errorstr(obj->tdb));
193
194                         return -1;
195                 }
196         }
197
198         return 0;
199
200
201 static PyMappingMethods tdb_mapping = {
202         (inquiry) tdb_hnd_length,
203         (binaryfunc) tdb_hnd_subscript,
204         (objobjargproc) tdb_ass_subscript
205 };
206
207 /*
208  * Utility methods
209  */
210
211 /* Return non-zero if a given key exists in the tdb */
212
213 PyObject *py_tdb_hnd_has_key(PyObject *self, PyObject *args)
214 {
215         tdb_hnd_object *obj = (tdb_hnd_object *)self;
216         TDB_DATA key;
217
218         if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
219                 return NULL;
220
221         if (!obj->tdb) {
222                 PyErr_SetString(
223                         py_tdb_error, "tdb object has been closed"); 
224                 return NULL;
225         }       
226
227         return PyInt_FromLong(tdb_exists(obj->tdb, key));
228 }
229
230 /* Return a list of keys in the tdb */
231
232 static int tdb_traverse_keys(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
233                              void *state)
234 {
235         PyObject *key_list = (PyObject *)state;
236
237         PyList_Append(key_list, 
238                       PyString_FromStringAndSize(key.dptr, key.dsize));
239
240         return 0;
241 }
242
243 PyObject *py_tdb_hnd_keys(PyObject *self, PyObject *args)
244 {
245         tdb_hnd_object *obj = (tdb_hnd_object *)self;
246         PyObject *key_list = PyList_New(0);
247
248         if (!obj->tdb) {
249                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
250                 return NULL;
251         }       
252
253         if (tdb_traverse(obj->tdb, tdb_traverse_keys, key_list) == -1) {
254                 PyErr_SetString(py_tdb_error, "error traversing tdb");
255                 Py_DECREF(key_list);
256                 return NULL;
257         }
258
259         return key_list;        
260 }
261
262 PyObject *py_tdb_hnd_first_key(PyObject *self, PyObject *args)
263 {
264         tdb_hnd_object *obj = (tdb_hnd_object *)self;
265         TDB_DATA key;
266
267         if (!obj->tdb) {
268                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
269                 return NULL;
270         }       
271
272         key = tdb_firstkey(obj->tdb);
273
274         return Py_BuildValue("s#", key.dptr, key.dsize);
275 }
276
277 PyObject *py_tdb_hnd_next_key(PyObject *self, PyObject *py_oldkey)
278 {
279         tdb_hnd_object *obj = (tdb_hnd_object *)self;
280         TDB_DATA key, oldkey;
281
282         if (!obj->tdb) {
283                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
284                 return NULL;
285         }       
286
287         if (!PyArg_Parse(py_oldkey, "s#", &oldkey.dptr, &oldkey.dsize))
288                 return NULL;
289
290         key = tdb_nextkey(obj->tdb, oldkey);
291
292         return Py_BuildValue("s#", key.dptr, key.dsize);
293 }
294
295 /*
296  * Locking routines
297  */
298
299 PyObject *py_tdb_hnd_lock_all(PyObject *self, PyObject *args)
300 {
301         tdb_hnd_object *obj = (tdb_hnd_object *)self;
302         int result;
303
304         if (!obj->tdb) {
305                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
306                 return NULL;
307         }       
308
309         result = tdb_lockall(obj->tdb);
310
311         return PyInt_FromLong(result != -1);
312 }
313
314 PyObject *py_tdb_hnd_unlock_all(PyObject *self, PyObject *args)
315 {
316         tdb_hnd_object *obj = (tdb_hnd_object *)self;
317
318         if (!obj->tdb) {
319                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
320                 return NULL;
321         }       
322
323         tdb_unlockall(obj->tdb);
324
325         Py_INCREF(Py_None);
326         return Py_None;
327 }
328
329 /* Return an array of keys from a python object which must be a string or a
330    list of strings. */
331
332 static BOOL make_lock_list(PyObject *py_keys, TDB_DATA **keys, int *num_keys)
333 {
334         /* Are we a list or a string? */
335
336         if (!PyList_Check(py_keys) && !PyString_Check(py_keys)) {
337                 PyErr_SetString(PyExc_TypeError, "arg must be list of string");
338                 return False;
339         }
340
341         if (PyList_Check(py_keys)) {
342                 int i;
343
344                 /* Turn python list into array of keys */
345                 
346                 *num_keys = PyList_Size(py_keys);
347                 *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA) * (*num_keys));
348                 
349                 for (i = 0; i < *num_keys; i++) {
350                         PyObject *key = PyList_GetItem(py_keys, i);
351                         
352                         if (!PyString_Check(key)) {
353                                 PyErr_SetString(
354                                         PyExc_TypeError,
355                                         "list elements must be strings");
356                                 return False;
357                         }
358
359                         PyArg_Parse(key, "s#", &(*keys)[i].dptr, 
360                                     &(*keys)[i].dsize);
361                 }
362
363         } else {
364
365                 /* Turn python string into a single key */
366
367                 *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA));
368                 *num_keys = 1;
369                 PyArg_Parse(py_keys, "s#", &(*keys)->dptr, &(*keys)->dsize);
370         }
371
372         return True;
373 }
374
375 PyObject *py_tdb_hnd_lock(PyObject *self, PyObject *args)
376 {
377         tdb_hnd_object *obj = (tdb_hnd_object *)self;
378         PyObject *py_keys;
379         TDB_DATA *keys;
380         int num_keys, result;
381
382         if (!obj->tdb) {
383                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
384                 return NULL;
385         }       
386
387         if (!PyArg_ParseTuple(args, "O", &py_keys))
388                 return NULL;
389
390         if (!make_lock_list(py_keys, &keys, &num_keys))
391                 return NULL;
392
393         result = tdb_lockkeys(obj->tdb, num_keys, keys);
394
395         free(keys);
396
397         return PyInt_FromLong(result != -1);
398 }
399
400 PyObject *py_tdb_hnd_unlock(PyObject *self, PyObject *args)
401 {
402         tdb_hnd_object *obj = (tdb_hnd_object *)self;
403         
404         if (!obj->tdb) {
405                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
406                 return NULL;
407         }       
408         
409         if (!PyArg_ParseTuple(args, ""))
410                 return NULL;
411         
412         tdb_unlockkeys(obj->tdb);
413
414         Py_INCREF(Py_None);
415         return Py_None;
416 }
417
418 /*
419  * tdb traversal
420  */
421
422 struct traverse_info {
423         PyObject *callback;
424         PyObject *state;
425 };
426
427 static int tdb_traverse_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
428                                  void *state)
429 {
430         struct traverse_info *info = state;
431         PyObject *arglist, *py_result;
432         int result;
433
434         arglist = Py_BuildValue("(s#s#O)", key.dptr, key.dsize, value.dptr,
435                                 value.dsize, info->state);
436
437         py_result = PyEval_CallObject(info->callback, arglist);
438
439         Py_DECREF(arglist);
440         
441         if (!PyInt_Check(py_result)) {
442                 result = 1;     /* Hmm - non-integer object returned by callback */
443                 goto done;
444         }
445
446         result = PyInt_AsLong(py_result);
447
448 done:
449         Py_DECREF(py_result);
450         return result;
451 }
452
453 PyObject *py_tdb_hnd_traverse(PyObject *self, PyObject *args, PyObject *kw)
454 {
455         tdb_hnd_object *obj = (tdb_hnd_object *)self;
456         static char *kwlist[] = { "traverse_fn", "state", NULL };
457         PyObject *state = Py_None, *callback;
458         struct traverse_info info;
459         int result;
460
461         if (!PyArg_ParseTupleAndKeywords(
462                     args, kw, "O|O", kwlist, &callback, &state))
463                 return NULL;
464
465         if (!PyCallable_Check(callback)) {
466                 PyErr_SetString(PyExc_TypeError, "parameter must be callable");
467                 return NULL;
468         }
469
470         Py_INCREF(callback);
471         Py_INCREF(state);
472
473         info.callback = callback;
474         info.state = state;
475
476         result = tdb_traverse(obj->tdb, tdb_traverse_traverse, &info);
477
478         Py_DECREF(callback);
479         Py_DECREF(state);
480
481         return PyInt_FromLong(result);
482 }
483
484 PyObject *py_tdb_hnd_chainlock(PyObject *self, PyObject *args)
485 {
486         tdb_hnd_object *obj = (tdb_hnd_object *)self;
487         TDB_DATA key;
488         int result;
489
490         if (!obj->tdb) {
491                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
492                 return NULL;
493         }       
494
495         if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
496                 return NULL;
497
498         result = tdb_chainlock(obj->tdb, key);
499
500         return PyInt_FromLong(result != -1);
501 }
502
503 PyObject *py_tdb_hnd_chainunlock(PyObject *self, PyObject *args)
504 {
505         tdb_hnd_object *obj = (tdb_hnd_object *)self;
506         TDB_DATA key;
507         int result;
508
509         if (!obj->tdb) {
510                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
511                 return NULL;
512         }       
513
514         if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
515                 return NULL;
516
517         result = tdb_chainunlock(obj->tdb, key);
518
519         return PyInt_FromLong(result != -1);
520 }
521
522 PyObject *py_tdb_hnd_lock_bystring(PyObject *self, PyObject *args)
523 {
524         tdb_hnd_object *obj = (tdb_hnd_object *)self;
525         int result, timeout = 30;
526         char *s;
527
528         if (!obj->tdb) {
529                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
530                 return NULL;
531         }       
532
533         if (!PyArg_ParseTuple(args, "s|i", &s, &timeout))
534                 return NULL;
535
536         result = tdb_lock_bystring(obj->tdb, s, timeout);
537
538         return PyInt_FromLong(result != -1);
539 }
540
541 PyObject *py_tdb_hnd_unlock_bystring(PyObject *self, PyObject *args)
542 {
543         tdb_hnd_object *obj = (tdb_hnd_object *)self;
544         char *s;
545
546         if (!obj->tdb) {
547                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
548                 return NULL;
549         }       
550
551         if (!PyArg_ParseTuple(args, "s", &s))
552                 return NULL;
553
554         tdb_unlock_bystring(obj->tdb, s);
555
556         Py_INCREF(Py_None);
557         return Py_None;
558 }
559
560 /* 
561  * Method dispatch table for this module
562  */
563
564 static PyMethodDef tdb_methods[] = {
565         { "open", (PyCFunction)py_tdb_open, METH_VARARGS | METH_KEYWORDS },
566         { "close", (PyCFunction)py_tdb_close, METH_VARARGS },
567         { NULL }
568 };
569
570 /* 
571  * Methods on a tdb object
572  */
573
574 static PyMethodDef tdb_hnd_methods[] = {
575         { "keys", (PyCFunction)py_tdb_hnd_keys, METH_VARARGS },
576         { "has_key", (PyCFunction)py_tdb_hnd_has_key, METH_VARARGS },
577         { "first_key", (PyCFunction)py_tdb_hnd_first_key, METH_VARARGS },
578         { "next_key", (PyCFunction)py_tdb_hnd_next_key, METH_VARARGS },
579         { "lock_all", (PyCFunction)py_tdb_hnd_lock_all, METH_VARARGS },
580         { "unlock_all", (PyCFunction)py_tdb_hnd_unlock_all, METH_VARARGS },
581         { "lock", (PyCFunction)py_tdb_hnd_lock, METH_VARARGS },
582         { "unlock", (PyCFunction)py_tdb_hnd_unlock, METH_VARARGS },
583         { "traverse", (PyCFunction)py_tdb_hnd_traverse, METH_VARARGS | METH_KEYWORDS },
584         { "chainlock", (PyCFunction)py_tdb_hnd_chainlock, METH_VARARGS | METH_KEYWORDS },
585         { "chainunlock", (PyCFunction)py_tdb_hnd_chainunlock, METH_VARARGS | METH_KEYWORDS },
586         { "lock_bystring", (PyCFunction)py_tdb_hnd_lock_bystring, METH_VARARGS | METH_KEYWORDS },
587         { "unlock_bystring", (PyCFunction)py_tdb_hnd_unlock_bystring, METH_VARARGS | METH_KEYWORDS },
588         { NULL }
589 };
590
591 /* Deallocate a tdb handle object */
592
593 static void tdb_hnd_dealloc(PyObject* self)
594 {
595         tdb_hnd_object *hnd = (tdb_hnd_object *)self;
596
597         if (hnd->tdb) {
598                 tdb_close(hnd->tdb);
599                 hnd->tdb = NULL;
600         }
601 }
602
603 /* Return tdb handle attributes */
604
605 static PyObject *tdb_hnd_getattr(PyObject *self, char *attrname)
606 {
607         return Py_FindMethod(tdb_hnd_methods, self, attrname);
608 }
609
610 static char tdb_hnd_type_doc[] = 
611 "Python wrapper for tdb.";
612
613 PyTypeObject tdb_hnd_type = {
614         PyObject_HEAD_INIT(NULL)
615         0,
616         "tdb",
617         sizeof(tdb_hnd_object),
618         0,
619         tdb_hnd_dealloc,        /* tp_dealloc*/
620         0,                      /* tp_print*/
621         tdb_hnd_getattr,        /* tp_getattr*/
622         0,                      /* tp_setattr*/
623         0,                      /* tp_compare*/
624         0,                      /* tp_repr*/
625         0,                      /* tp_as_number*/
626         0,                      /* tp_as_sequence*/
627         &tdb_mapping,           /* tp_as_mapping*/
628         0,                      /* tp_hash */
629         0,                      /* tp_call */
630         0,                      /* tp_str */
631         0,                      /* tp_getattro */
632         0,                      /* tp_setattro */
633         0,                      /* tp_as_buffer*/
634         Py_TPFLAGS_DEFAULT,     /* tp_flags */
635         tdb_hnd_type_doc,       /* tp_doc */
636 };
637
638 /* Constants */
639
640 static struct const_vals {
641         char *name;
642         uint32 value;
643 } module_const_vals[] = {
644
645         /* Flags for tdb_open() */
646
647         { "TDB_DEFAULT", TDB_DEFAULT },
648         { "TDB_CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST },
649         { "TDB_INTERNAL", TDB_INTERNAL },
650         { "TDB_NOLOCK", TDB_NOLOCK },
651         { "TDB_NOMMAP", TDB_NOMMAP },
652         { "TDB_CONVERT", TDB_CONVERT },
653         { "TDB_BIGENDIAN", TDB_BIGENDIAN },
654         
655         { NULL },
656 };
657
658 static void const_init(PyObject *dict)
659 {
660         struct const_vals *tmp;
661         PyObject *obj;
662
663         for (tmp = module_const_vals; tmp->name; tmp++) {
664                 obj = PyInt_FromLong(tmp->value);
665                 PyDict_SetItemString(dict, tmp->name, obj);
666                 Py_DECREF(obj);
667         }
668 }
669
670 /* Module initialisation */
671
672 void inittdb(void)
673 {
674         PyObject *module, *dict;
675
676         /* Initialise module */
677
678         module = Py_InitModule("tdb", tdb_methods);
679         dict = PyModule_GetDict(module);
680
681         py_tdb_error = PyErr_NewException("tdb.error", NULL, NULL);
682         PyDict_SetItemString(dict, "error", py_tdb_error);
683
684         /* Initialise policy handle object */
685
686         tdb_hnd_type.ob_type = &PyType_Type;
687
688         PyDict_SetItemString(dict, "tdb.hnd", 
689                              (PyObject *)&tdb_hnd_type);
690
691         /* Initialise constants */
692
693         const_init(dict);
694 }