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