Added a reminder about programs using the tdb bindings having to be
[samba.git] / source / python / py_tdb.c
1 /* 
2    Python wrappers for TDB module
3
4    Copyright (C) Tim Potter, 2002
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 licensed under the gdb, any program that uses these
23    bindings must also be licensed under the GPL. See the following URL for
24    details: 
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 /* 
485  * Method dispatch table for this module
486  */
487
488 static PyMethodDef tdb_methods[] = {
489         { "open", (PyCFunction)py_tdb_open, METH_VARARGS | METH_KEYWORDS },
490         { "close", (PyCFunction)py_tdb_close, METH_VARARGS },
491         { NULL }
492 };
493
494 /* 
495  * Methods on a tdb object
496  */
497
498 static PyMethodDef tdb_hnd_methods[] = {
499         { "keys", (PyCFunction)py_tdb_hnd_keys, METH_VARARGS },
500         { "has_key", (PyCFunction)py_tdb_hnd_has_key, METH_VARARGS },
501         { "first_key", (PyCFunction)py_tdb_hnd_first_key, METH_VARARGS },
502         { "next_key", (PyCFunction)py_tdb_hnd_next_key, METH_VARARGS },
503         { "lock_all", (PyCFunction)py_tdb_hnd_lock_all, METH_VARARGS },
504         { "unlock_all", (PyCFunction)py_tdb_hnd_unlock_all, METH_VARARGS },
505         { "lock", (PyCFunction)py_tdb_hnd_lock, METH_VARARGS },
506         { "unlock", (PyCFunction)py_tdb_hnd_unlock, METH_VARARGS },
507         { "traverse", (PyCFunction)py_tdb_hnd_traverse, METH_VARARGS | METH_KEYWORDS },
508         { NULL }
509 };
510
511 /* Deallocate a tdb handle object */
512
513 static void tdb_hnd_dealloc(PyObject* self)
514 {
515         tdb_hnd_object *hnd = (tdb_hnd_object *)self;
516
517         if (hnd->tdb) {
518                 tdb_close(hnd->tdb);
519                 hnd->tdb = NULL;
520         }
521 }
522
523 /* Return tdb handle attributes */
524
525 static PyObject *tdb_hnd_getattr(PyObject *self, char *attrname)
526 {
527         return Py_FindMethod(tdb_hnd_methods, self, attrname);
528 }
529
530 static char tdb_hnd_type_doc[] = 
531 "Python wrapper for tdb.";
532
533 PyTypeObject tdb_hnd_type = {
534         PyObject_HEAD_INIT(NULL)
535         0,
536         "tdb",
537         sizeof(tdb_hnd_object),
538         0,
539         tdb_hnd_dealloc,        /* tp_dealloc*/
540         0,                      /* tp_print*/
541         tdb_hnd_getattr,        /* tp_getattr*/
542         0,                      /* tp_setattr*/
543         0,                      /* tp_compare*/
544         0,                      /* tp_repr*/
545         0,                      /* tp_as_number*/
546         0,                      /* tp_as_sequence*/
547         &tdb_mapping,           /* tp_as_mapping*/
548         0,                      /* tp_hash */
549         0,                      /* tp_call */
550         0,                      /* tp_str */
551         0,                      /* tp_getattro */
552         0,                      /* tp_setattro */
553         0,                      /* tp_as_buffer*/
554         Py_TPFLAGS_DEFAULT,     /* tp_flags */
555         tdb_hnd_type_doc,       /* tp_doc */
556 };
557
558 /* Constants */
559
560 static struct const_vals {
561         char *name;
562         uint32 value;
563 } module_const_vals[] = {
564
565         /* Flags for tdb_open() */
566
567         { "TDB_DEFAULT", TDB_DEFAULT },
568         { "TDB_CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST },
569         { "TDB_INTERNAL", TDB_INTERNAL },
570         { "TDB_NOLOCK", TDB_NOLOCK },
571         { "TDB_NOMMAP", TDB_NOMMAP },
572         { "TDB_CONVERT", TDB_CONVERT },
573         { "TDB_BIGENDIAN", TDB_BIGENDIAN },
574         
575         { NULL },
576 };
577
578 static void const_init(PyObject *dict)
579 {
580         struct const_vals *tmp;
581         PyObject *obj;
582
583         for (tmp = module_const_vals; tmp->name; tmp++) {
584                 obj = PyInt_FromLong(tmp->value);
585                 PyDict_SetItemString(dict, tmp->name, obj);
586                 Py_DECREF(obj);
587         }
588 }
589
590 /* Module initialisation */
591
592 void inittdb(void)
593 {
594         PyObject *module, *dict;
595
596         /* Initialise module */
597
598         module = Py_InitModule("tdb", tdb_methods);
599         dict = PyModule_GetDict(module);
600
601         py_tdb_error = PyErr_NewException("tdb.error", NULL, NULL);
602         PyDict_SetItemString(dict, "error", py_tdb_error);
603
604         /* Initialise policy handle object */
605
606         tdb_hnd_type.ob_type = &PyType_Type;
607
608         PyDict_SetItemString(dict, "tdb.hnd", 
609                              (PyObject *)&tdb_hnd_type);
610
611         /* Initialise constants */
612
613         const_init(dict);
614 }