Fix "oopsie" (bug #3603)
[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 <string.h>
21 #include <sys/time.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <asterisk/channel.h>
27 #include <asterisk/file.h>
28 #include <asterisk/app.h>
29 #include <asterisk/dsp.h>
30 #include <asterisk/logger.h>
31 #include <asterisk/options.h>
32 #include <asterisk/astdb.h>
33 #include <asterisk/cli.h>
34 #include <asterisk/utils.h>
35 #include <asterisk/lock.h>
36 #include <asterisk/manager.h>
37 #include "db1-ast/include/db.h"
38 #include "asterisk.h"
39 #include "astconf.h"
40
41 static DB *astdb;
42 AST_MUTEX_DEFINE_STATIC(dblock);
43
44 static int dbinit(void) 
45 {
46         if (!astdb) {
47                 if (!(astdb = dbopen((char *)ast_config_AST_DB, O_CREAT | O_RDWR, 0664, DB_BTREE, NULL))) {
48                         ast_log(LOG_WARNING, "Unable to open Asterisk database\n");
49                 }
50         }
51         if (astdb)
52                 return 0;
53         return -1;
54 }
55
56
57 static inline int keymatch(const char *key, const char *prefix)
58 {
59         int preflen = strlen(prefix);
60         if (!preflen)
61                 return 1;
62         if (!strcasecmp(key, prefix))
63                 return 1;
64         if ((strlen(key) > preflen) &&
65                 !strncasecmp(key, prefix, preflen)) {
66                 if (key[preflen] == '/')
67                         return 1;
68         }
69         return 0;
70 }
71
72 static inline int subkeymatch(const char *key, const char *suffix)
73 {
74         int suffixlen = strlen(suffix);
75         if (suffixlen) {
76                 const char *subkey = key + strlen(key) - suffixlen;
77                 if (subkey < key)
78                         return 0;
79                 if (!strcasecmp(subkey, suffix))
80                         return 1;
81         }
82         return 0;
83 }
84
85 int ast_db_deltree(const char *family, const char *keytree)
86 {
87         char prefix[256];
88         DBT key, data;
89         char *keys;
90         int res;
91         int pass;
92         
93         if (family) {
94                 if (keytree)
95                         snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
96                 else
97                         snprintf(prefix, sizeof(prefix), "/%s", family);
98         } else if (keytree)
99                 return -1;
100         else
101                 prefix[0] = '\0';
102         
103         ast_mutex_lock(&dblock);
104         if (dbinit()) 
105                 return -1;
106         
107         memset(&key, 0, sizeof(key));
108         memset(&data, 0, sizeof(data));
109         pass = 0;
110         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
111                 if (key.size) {
112                         keys = key.data;
113                         keys[key.size - 1] = '\0';
114                 } else
115                         keys = "<bad key>";
116                 if (keymatch(keys, prefix)) {
117                         astdb->del(astdb, &key, 0);
118                 }
119         }
120         astdb->sync(astdb, 0);
121         ast_mutex_unlock(&dblock);
122         return 0;
123 }
124
125 int ast_db_put(const char *family, const char *keys, char *value)
126 {
127         char fullkey[256];
128         DBT key, data;
129         int res, fullkeylen;
130
131         ast_mutex_lock(&dblock);
132         if (dbinit()) {
133                 ast_mutex_unlock(&dblock);
134                 return -1;
135         }
136
137         fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
138         memset(&key, 0, sizeof(key));
139         memset(&data, 0, sizeof(data));
140         key.data = fullkey;
141         key.size = fullkeylen + 1;
142         data.data = value;
143         data.size = strlen(value) + 1;
144         res = astdb->put(astdb, &key, &data, 0);
145         astdb->sync(astdb, 0);
146         ast_mutex_unlock(&dblock);
147         if (res)
148                 ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
149         return res;
150 }
151
152 int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
153 {
154         char fullkey[256]="";
155         DBT key, data;
156         int res, fullkeylen;
157
158         ast_mutex_lock(&dblock);
159         if (dbinit()) {
160                 ast_mutex_unlock(&dblock);
161                 return -1;
162         }
163
164         fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
165         memset(&key, 0, sizeof(key));
166         memset(&data, 0, sizeof(data));
167         memset(value, 0, valuelen);
168         key.data = fullkey;
169         key.size = fullkeylen + 1;
170         
171         res = astdb->get(astdb, &key, &data, 0);
172         
173         ast_mutex_unlock(&dblock);
174
175         /* Be sure to NULL terminate our data either way */
176         if (res) {
177                 ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
178         } else {
179 #if 0
180                 printf("Got value of size %d\n", data.size);
181 #endif
182                 if (data.size) {
183                         ((char *)data.data)[data.size - 1] = '\0';
184                         /* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */
185                         strncpy(value, data.data, (valuelen > data.size) ? data.size : valuelen);
186                         value[valuelen - 1] = '\0';
187                 } else {
188                         ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
189                 }
190         }
191         return res;
192 }
193
194 int ast_db_del(const char *family, const char *keys)
195 {
196         char fullkey[256];
197         DBT key;
198         int res, fullkeylen;
199
200         ast_mutex_lock(&dblock);
201         if (dbinit()) {
202                 ast_mutex_unlock(&dblock);
203                 return -1;
204         }
205         
206         fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
207         memset(&key, 0, sizeof(key));
208         key.data = fullkey;
209         key.size = fullkeylen + 1;
210         
211         res = astdb->del(astdb, &key, 0);
212         astdb->sync(astdb, 0);
213         
214         ast_mutex_unlock(&dblock);
215
216         if (res) 
217                 ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
218         return res;
219 }
220
221 static int database_put(int fd, int argc, char *argv[])
222 {
223         int res;
224         if (argc != 5)
225                 return RESULT_SHOWUSAGE;
226         res = ast_db_put(argv[2], argv[3], argv[4]);
227         if (res) 
228                 ast_cli(fd, "Failed to update entry\n");
229         else
230                 ast_cli(fd, "Updated database successfully\n");
231         return RESULT_SUCCESS;
232 }
233
234 static int database_get(int fd, int argc, char *argv[])
235 {
236         int res;
237         char tmp[256];
238         if (argc != 4)
239                 return RESULT_SHOWUSAGE;
240         res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
241         if (res) 
242                 ast_cli(fd, "Database entry not found.\n");
243         else
244                 ast_cli(fd, "Value: %s\n", tmp);
245         return RESULT_SUCCESS;
246 }
247
248 static int database_del(int fd, int argc, char *argv[])
249 {
250         int res;
251         if (argc != 4)
252                 return RESULT_SHOWUSAGE;
253         res = ast_db_del(argv[2], argv[3]);
254         if (res) 
255                 ast_cli(fd, "Database entry does not exist.\n");
256         else
257                 ast_cli(fd, "Database entry removed.\n");
258         return RESULT_SUCCESS;
259 }
260
261 static int database_deltree(int fd, int argc, char *argv[])
262 {
263         int res;
264         if ((argc < 3) || (argc > 4))
265                 return RESULT_SHOWUSAGE;
266         if (argc == 4)
267                 res = ast_db_deltree(argv[2], argv[3]);
268         else
269                 res = ast_db_deltree(argv[2], NULL);
270         if (res) 
271                 ast_cli(fd, "Database entries do not exist.\n");
272         else
273                 ast_cli(fd, "Database entries removed.\n");
274         return RESULT_SUCCESS;
275 }
276
277 static int database_show(int fd, int argc, char *argv[])
278 {
279         char prefix[256];
280         DBT key, data;
281         char *keys, *values;
282         int res;
283         int pass;
284
285         if (argc == 4) {
286                 /* Family and key tree */
287                 snprintf(prefix, sizeof(prefix), "/%s/%s", argv[2], argv[3]);
288         } else if (argc == 3) {
289                 /* Family only */
290                 snprintf(prefix, sizeof(prefix), "/%s", argv[2]);
291         } else if (argc == 2) {
292                 /* Neither */
293                 prefix[0] = '\0';
294         } else
295                 return RESULT_SHOWUSAGE;
296         ast_mutex_lock(&dblock);
297         if (dbinit()) {
298                 ast_mutex_unlock(&dblock);
299                 ast_cli(fd, "Database unavailable\n");
300                 return RESULT_SUCCESS;  
301         }
302         memset(&key, 0, sizeof(key));
303         memset(&data, 0, sizeof(data));
304         pass = 0;
305         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
306                 if (key.size) {
307                         keys = key.data;
308                         keys[key.size - 1] = '\0';
309                 } else
310                         keys = "<bad key>";
311                 if (data.size) {
312                         values = data.data;
313                         values[data.size - 1]='\0';
314                 } else
315                         values = "<bad value>";
316                 if (keymatch(keys, prefix)) {
317                                 ast_cli(fd, "%-50s: %-25s\n", keys, values);
318                 }
319         }
320         ast_mutex_unlock(&dblock);
321         return RESULT_SUCCESS;  
322 }
323
324 static int database_showkey(int fd, int argc, char *argv[])
325 {
326         char suffix[256];
327         DBT key, data;
328         char *keys, *values;
329         int res;
330         int pass;
331
332         if (argc == 3) {
333                 /* Key only */
334                 snprintf(suffix, sizeof(suffix), "/%s", argv[2]);
335         } else
336                 return RESULT_SHOWUSAGE;
337         ast_mutex_lock(&dblock);
338         if (dbinit()) {
339                 ast_mutex_unlock(&dblock);
340                 ast_cli(fd, "Database unavailable\n");
341                 return RESULT_SUCCESS;  
342         }
343         memset(&key, 0, sizeof(key));
344         memset(&data, 0, sizeof(data));
345         pass = 0;
346         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
347                 if (key.size) {
348                         keys = key.data;
349                         keys[key.size - 1] = '\0';
350                 } else
351                         keys = "<bad key>";
352                 if (data.size) {
353                         values = data.data;
354                         values[data.size - 1]='\0';
355                 } else
356                         values = "<bad value>";
357                 if (subkeymatch(keys, suffix)) {
358                                 ast_cli(fd, "%-50s: %-25s\n", keys, values);
359                 }
360         }
361         ast_mutex_unlock(&dblock);
362         return RESULT_SUCCESS;  
363 }
364
365 struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
366 {
367         char prefix[256];
368         DBT key, data;
369         char *keys, *values;
370         int res;
371         int pass;
372         struct ast_db_entry *last = NULL;
373         struct ast_db_entry *cur, *ret=NULL;
374
375         if (family && !ast_strlen_zero(family)) {
376                 if (keytree && !ast_strlen_zero(keytree))
377                         /* Family and key tree */
378                         snprintf(prefix, sizeof(prefix), "/%s/%s", family, prefix);
379                 else
380                         /* Family only */
381                         snprintf(prefix, sizeof(prefix), "/%s", family);
382         } else
383                 prefix[0] = '\0';
384         ast_mutex_lock(&dblock);
385         if (dbinit()) {
386                 ast_mutex_unlock(&dblock);
387                 ast_log(LOG_WARNING, "Database unavailable\n");
388                 return NULL;    
389         }
390         memset(&key, 0, sizeof(key));
391         memset(&data, 0, sizeof(data));
392         pass = 0;
393         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
394                 if (key.size) {
395                         keys = key.data;
396                         keys[key.size - 1] = '\0';
397                 } else
398                         keys = "<bad key>";
399                 if (data.size) {
400                         values = data.data;
401                         values[data.size - 1]='\0';
402                 } else
403                         values = "<bad value>";
404                 if (keymatch(keys, prefix)) {
405                                 cur = malloc(sizeof(struct ast_db_entry) + strlen(keys) + strlen(values) + 2);
406                                 if (cur) {
407                                         cur->next = NULL;
408                                         cur->key = cur->data + strlen(values) + 1;
409                                         strcpy(cur->data, values);
410                                         strcpy(cur->key, keys);
411                                         if (last)
412                                                 last->next = cur;
413                                         else
414                                                 ret = cur;
415                                         last = cur;
416                                 }
417                 }
418         }
419         ast_mutex_unlock(&dblock);
420         return ret;     
421 }
422
423 void ast_db_freetree(struct ast_db_entry *dbe)
424 {
425         struct ast_db_entry *last;
426         while(dbe) {
427                 last = dbe;
428                 dbe = dbe->next;
429                 free(last);
430         }
431 }
432
433 static char database_show_usage[] =
434 "Usage: database show [family [keytree]]\n"
435 "       Shows Asterisk database contents, optionally restricted\n"
436 "to a given family, or family and keytree.\n";
437
438 static char database_showkey_usage[] =
439 "Usage: database showkey <keytree>\n"
440 "       Shows Asterisk database contents, restricted to a given key.\n";
441
442 static char database_put_usage[] =
443 "Usage: database put <family> <key> <value>\n"
444 "       Adds or updates an entry in the Asterisk database for\n"
445 "a given family, key, and value.\n";
446
447 static char database_get_usage[] =
448 "Usage: database get <family> <key>\n"
449 "       Retrieves an entry in the Asterisk database for a given\n"
450 "family and key.\n";
451
452 static char database_del_usage[] =
453 "Usage: database del <family> <key>\n"
454 "       Deletes an entry in the Asterisk database for a given\n"
455 "family and key.\n";
456
457 static char database_deltree_usage[] =
458 "Usage: database deltree <family> [keytree]\n"
459 "       Deletes a family or specific keytree within a family\n"
460 "in the Asterisk database.\n";
461
462 struct ast_cli_entry cli_database_show =
463 { { "database", "show", NULL }, database_show, "Shows database contents", database_show_usage };
464
465 struct ast_cli_entry cli_database_showkey =
466 { { "database", "showkey", NULL }, database_showkey, "Shows database contents", database_showkey_usage };
467
468 struct ast_cli_entry cli_database_get =
469 { { "database", "get", NULL }, database_get, "Gets database value", database_get_usage };
470
471 struct ast_cli_entry cli_database_put =
472 { { "database", "put", NULL }, database_put, "Adds/updates database value", database_put_usage };
473
474 struct ast_cli_entry cli_database_del =
475 { { "database", "del", NULL }, database_del, "Removes database key/value", database_del_usage };
476
477 struct ast_cli_entry cli_database_deltree =
478 { { "database", "deltree", NULL }, database_deltree, "Removes database keytree/values", database_deltree_usage };
479
480 static int manager_dbput(struct mansession *s, struct message *m)
481 {
482         char *family = astman_get_header(m, "Family");
483         char *key = astman_get_header(m, "Key");
484         char *val = astman_get_header(m, "Val");
485         int res;
486
487         if (!strlen(family)) {
488                 astman_send_error(s, m, "No family specified");
489                 return 0;
490         }
491         if (!strlen(key)) {
492                 astman_send_error(s, m, "No key specified");
493                 return 0;
494         }
495         if (!strlen(val)) {
496                 astman_send_error(s, m, "No val specified");
497                 return 0;
498         }
499
500         res = ast_db_put(family, key, val);
501         if (res)
502                 astman_send_error(s, m, "Failed to update entry");
503         else 
504                 astman_send_ack(s, m, "Updated database successfully");
505         return 0;
506 }
507
508 static int manager_dbget(struct mansession *s, struct message *m)
509 {
510         char *family = astman_get_header(m, "Family");
511         char *key = astman_get_header(m, "Key");
512         char tmp[256];
513         int res;
514
515         if (!strlen(family)) {
516                 astman_send_error(s, m, "No family specified.");
517                 return 0;
518         }
519         if (!strlen(key)) {
520                 astman_send_error(s, m, "No key specified.");
521                 return 0;
522         }
523
524         res = ast_db_get(family, key, tmp, sizeof(tmp));
525         if (res)
526                 astman_send_error(s, m, "Database entry not found");
527         else {
528                 astman_send_ack(s, m, "Result will follow");
529                 ast_cli(s->fd, "Event: DBGetResponse\r\n"
530                                 "Family: %s\r\n"
531                                 "Key: %s\r\n"
532                                 "Val: %s\r\n\r\n",
533                                 family, key, tmp);
534         }
535         return 0;
536 }
537
538 int astdb_init(void)
539 {
540         dbinit();
541         ast_cli_register(&cli_database_show);
542         ast_cli_register(&cli_database_showkey);
543         ast_cli_register(&cli_database_get);
544         ast_cli_register(&cli_database_put);
545         ast_cli_register(&cli_database_del);
546         ast_cli_register(&cli_database_deltree);
547         ast_manager_register("DBGet", EVENT_FLAG_SYSTEM, manager_dbget, "Get DB Entry");
548         ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry");
549         return 0;
550 }