9b56f092023f1525c3dd8a14b5822b56028687d7
[asterisk/asterisk.git] / db.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Channel Management
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 /* DB3 is licensed under Sleepycat Public License and is thus incompatible
15    with GPL.  To avoid having to make another exception (and complicate 
16    licensing even further) we elect to use DB1 which is BSD licensed */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <pthread.h>
21 #include <string.h>
22 #include <sys/time.h>
23 #include <signal.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <asterisk/channel.h>
28 #include <asterisk/file.h>
29 #include <asterisk/app.h>
30 #include <asterisk/dsp.h>
31 #include <asterisk/logger.h>
32 #include <asterisk/options.h>
33 #include <asterisk/astdb.h>
34 #include <asterisk/cli.h>
35 #include "db1-ast/include/db.h"
36 #include "asterisk.h"
37 #include "astconf.h"
38
39 static DB *astdb;
40 static pthread_mutex_t dblock = AST_MUTEX_INITIALIZER;
41
42 static int dbinit(void) 
43 {
44         if (!astdb) {
45                 if (!(astdb = dbopen((char *)ast_config_AST_DB, O_CREAT | O_RDWR, 0664, DB_BTREE, NULL))) {
46                         ast_log(LOG_WARNING, "Unable to open Asterisk database\n");
47                 }
48         }
49         if (astdb)
50                 return 0;
51         return -1;
52 }
53
54
55 static inline int keymatch(const char *key, const char *prefix)
56 {
57         if (!strlen(prefix))
58                 return 1;
59         if (!strcasecmp(key, prefix))
60                 return 1;
61         if ((strlen(key) > strlen(prefix)) &&
62                 !strncasecmp(key, prefix, strlen(prefix))) {
63                 if (key[strlen(prefix)] == '/')
64                         return 1;
65         }
66         return 0;
67 }
68
69 int ast_db_deltree(const char *family, const char *keytree)
70 {
71         char prefix[256];
72         DBT key, data;
73         char *keys;
74         int res;
75         int pass;
76         
77         if (family) {
78                 if (keytree)
79                         snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
80                 else
81                         snprintf(prefix, sizeof(prefix), "/%s", family);
82         } else if (keytree)
83                 return -1;
84         else
85                 strcpy(prefix, "");
86         
87         ast_pthread_mutex_lock(&dblock);
88         if (dbinit()) 
89                 return -1;
90         
91         memset(&key, 0, sizeof(key));
92         memset(&data, 0, sizeof(data));
93         pass = 0;
94         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
95                 if (key.size) {
96                         keys = key.data;
97                         keys[key.size - 1] = '\0';
98                 } else
99                         keys = "<bad key>";
100                 if (keymatch(keys, prefix)) {
101                         astdb->del(astdb, &key, 0);
102                 }
103         }
104         astdb->sync(astdb, 0);
105         ast_pthread_mutex_unlock(&dblock);
106         return 0;
107 }
108
109 int ast_db_put(const char *family, const char *keys, char *value)
110 {
111         char fullkey[256];
112         DBT key, data;
113         int res;
114
115         ast_pthread_mutex_lock(&dblock);
116         if (dbinit()) {
117                 ast_pthread_mutex_unlock(&dblock);
118                 return -1;
119         }
120
121         snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
122         memset(&key, 0, sizeof(key));
123         memset(&data, 0, sizeof(data));
124         key.data = fullkey;
125         key.size = strlen(fullkey) + 1;
126         data.data = value;
127         data.size = strlen(value) + 1;
128         res = astdb->put(astdb, &key, &data, 0);
129         astdb->sync(astdb, 0);
130         ast_pthread_mutex_unlock(&dblock);
131         if (res)
132                 ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
133         return res;
134 }
135
136 int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
137 {
138         char fullkey[256];
139         DBT key, data;
140         int res;
141
142         ast_pthread_mutex_lock(&dblock);
143         if (dbinit()) {
144                 ast_pthread_mutex_unlock(&dblock);
145                 return -1;
146         }
147
148         snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
149         memset(&key, 0, sizeof(key));
150         memset(&data, 0, sizeof(data));
151         key.data = fullkey;
152         key.size = strlen(fullkey) + 1;
153         
154         res = astdb->get(astdb, &key, &data, 0);
155         
156         ast_pthread_mutex_unlock(&dblock);
157
158         /* Be sure to NULL terminate our data either way */
159         if (res) {
160                 value[0] = 0;
161                 ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
162         } else {
163 #if 0
164                 printf("Got value of size %d\n", data.size);
165 #endif
166                 if (data.size) {
167                         ((char *)data.data)[data.size - 1] = '\0';
168                         strncpy(value, data.data, valuelen - 1);
169                         value[valuelen - 1] = '\0';
170                 } else {
171                         ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
172                         value[0] = '\0';
173                 }
174         }
175         return res;
176 }
177
178 int ast_db_del(const char *family, const char *keys)
179 {
180         char fullkey[256];
181         DBT key;
182         int res;
183
184         ast_pthread_mutex_lock(&dblock);
185         if (dbinit()) {
186                 ast_pthread_mutex_unlock(&dblock);
187                 return -1;
188         }
189         
190         snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
191         memset(&key, 0, sizeof(key));
192         key.data = fullkey;
193         key.size = strlen(fullkey) + 1;
194         
195         res = astdb->del(astdb, &key, 0);
196         astdb->sync(astdb, 0);
197         
198         ast_pthread_mutex_unlock(&dblock);
199
200         if (res) 
201                 ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
202         return res;
203 }
204
205 static int database_put(int fd, int argc, char *argv[])
206 {
207         int res;
208         if (argc != 5)
209                 return RESULT_SHOWUSAGE;
210         res = ast_db_put(argv[2], argv[3], argv[4]);
211         if (res) 
212                 ast_cli(fd, "Failed to update entry\n");
213         else
214                 ast_cli(fd, "Updated database successfully\n");
215         return RESULT_SUCCESS;
216 }
217
218 static int database_get(int fd, int argc, char *argv[])
219 {
220         int res;
221         char tmp[256];
222         if (argc != 4)
223                 return RESULT_SHOWUSAGE;
224         res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
225         if (res) 
226                 ast_cli(fd, "Database entry not found.\n");
227         else
228                 ast_cli(fd, "Value: %s\n", tmp);
229         return RESULT_SUCCESS;
230 }
231
232 static int database_del(int fd, int argc, char *argv[])
233 {
234         int res;
235         if (argc != 4)
236                 return RESULT_SHOWUSAGE;
237         res = ast_db_del(argv[2], argv[3]);
238         if (res) 
239                 ast_cli(fd, "Database entry does not exist.\n");
240         else
241                 ast_cli(fd, "Database entry removed.\n");
242         return RESULT_SUCCESS;
243 }
244
245 static int database_deltree(int fd, int argc, char *argv[])
246 {
247         int res;
248         if ((argc < 3) || (argc > 4))
249                 return RESULT_SHOWUSAGE;
250         if (argc == 4)
251                 res = ast_db_deltree(argv[2], argv[3]);
252         else
253                 res = ast_db_deltree(argv[2], NULL);
254         if (res) 
255                 ast_cli(fd, "Database entries do not exist.\n");
256         else
257                 ast_cli(fd, "Database entries removed.\n");
258         return RESULT_SUCCESS;
259 }
260
261 static int database_show(int fd, int argc, char *argv[])
262 {
263         char prefix[256];
264         DBT key, data;
265         char *keys, *values;
266         int res;
267         int pass;
268
269         if (argc == 4) {
270                 /* Family and key tree */
271                 snprintf(prefix, sizeof(prefix), "/%s/%s", argv[2], argv[3]);
272         } else if (argc == 3) {
273                 /* Family only */
274                 snprintf(prefix, sizeof(prefix), "/%s", argv[2]);
275         } else if (argc == 2) {
276                 /* Neither */
277                 strcpy(prefix, "");
278         } else
279                 return RESULT_SHOWUSAGE;
280         ast_pthread_mutex_lock(&dblock);
281         if (dbinit()) {
282                 ast_pthread_mutex_unlock(&dblock);
283                 ast_cli(fd, "Database unavailable\n");
284                 return RESULT_SUCCESS;  
285         }
286         memset(&key, 0, sizeof(key));
287         memset(&data, 0, sizeof(data));
288         pass = 0;
289         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
290                 if (key.size) {
291                         keys = key.data;
292                         keys[key.size - 1] = '\0';
293                 } else
294                         keys = "<bad key>";
295                 if (data.size) {
296                         values = data.data;
297                         values[data.size - 1]='\0';
298                 } else
299                         values = "<bad value>";
300                 if (keymatch(keys, prefix)) {
301                                 ast_cli(fd, "%-50s: %-25s\n", keys, values);
302                 }
303         }
304         ast_pthread_mutex_unlock(&dblock);
305         return RESULT_SUCCESS;  
306 }
307
308 struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
309 {
310         char prefix[256];
311         DBT key, data;
312         char *keys, *values;
313         int res;
314         int pass;
315         struct ast_db_entry *last = NULL;
316         struct ast_db_entry *cur, *ret=NULL;
317
318         if (family && strlen(family)) {
319                 if (keytree && strlen(keytree))
320                         /* Family and key tree */
321                         snprintf(prefix, sizeof(prefix), "/%s/%s", family, prefix);
322                 else
323                         /* Family only */
324                         snprintf(prefix, sizeof(prefix), "/%s", family);
325         } else
326                 strcpy(prefix, "");
327         ast_pthread_mutex_lock(&dblock);
328         if (dbinit()) {
329                 ast_pthread_mutex_unlock(&dblock);
330                 ast_log(LOG_WARNING, "Database unavailable\n");
331                 return NULL;    
332         }
333         memset(&key, 0, sizeof(key));
334         memset(&data, 0, sizeof(data));
335         pass = 0;
336         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
337                 if (key.size) {
338                         keys = key.data;
339                         keys[key.size - 1] = '\0';
340                 } else
341                         keys = "<bad key>";
342                 if (data.size) {
343                         values = data.data;
344                         values[data.size - 1]='\0';
345                 } else
346                         values = "<bad value>";
347                 if (keymatch(keys, prefix)) {
348                                 cur = malloc(sizeof(struct ast_db_entry) + strlen(keys) + strlen(values) + 2);
349                                 if (cur) {
350                                         cur->next = NULL;
351                                         cur->key = cur->data + strlen(values) + 1;
352                                         strcpy(cur->data, values);
353                                         strcpy(cur->key, keys);
354                                         if (last)
355                                                 last->next = cur;
356                                         else
357                                                 ret = cur;
358                                         last = cur;
359                                 }
360                 }
361         }
362         ast_pthread_mutex_unlock(&dblock);
363         return ret;     
364 }
365
366 void ast_db_freetree(struct ast_db_entry *dbe)
367 {
368         struct ast_db_entry *last;
369         while(dbe) {
370                 last = dbe;
371                 dbe = dbe->next;
372                 free(last);
373         }
374 }
375
376 static char database_show_usage[] =
377 "Usage: database show [family [keytree]]\n"
378 "       Shows Asterisk database contents, optionally restricted\n"
379 "to a given family, or family and keytree.\n";
380
381 static char database_put_usage[] =
382 "Usage: database put <family> <key> <value>\n"
383 "       Adds or updates an entry in the Asterisk database for\n"
384 "a given family, key, and value.\n";
385
386 static char database_get_usage[] =
387 "Usage: database get <family> <key>\n"
388 "       Retrieves an entry in the Asterisk database for a given\n"
389 "family and key.\n";
390
391 static char database_del_usage[] =
392 "Usage: database del <family> <key>\n"
393 "       Deletes an entry in the Asterisk database for a given\n"
394 "family and key.\n";
395
396 static char database_deltree_usage[] =
397 "Usage: database deltree <family> [keytree]\n"
398 "       Deletes a family or specific keytree within a family\n"
399 "in the Asterisk database.\n";
400
401 struct ast_cli_entry cli_database_show =
402 { { "database", "show", NULL }, database_show, "Shows database contents", database_show_usage };
403
404 struct ast_cli_entry cli_database_get =
405 { { "database", "get", NULL }, database_get, "Gets database value", database_get_usage };
406
407 struct ast_cli_entry cli_database_put =
408 { { "database", "put", NULL }, database_put, "Adds/updates database value", database_put_usage };
409
410 struct ast_cli_entry cli_database_del =
411 { { "database", "del", NULL }, database_del, "Removes database key/value", database_del_usage };
412
413 struct ast_cli_entry cli_database_deltree =
414 { { "database", "deltree", NULL }, database_deltree, "Removes database keytree/values", database_deltree_usage };
415
416 int astdb_init(void)
417 {
418         dbinit();
419         ast_cli_register(&cli_database_show);
420         ast_cli_register(&cli_database_get);
421         ast_cli_register(&cli_database_put);
422         ast_cli_register(&cli_database_del);
423         ast_cli_register(&cli_database_deltree);
424         return 0;
425 }