Make sure that we don't read too much from the data.data pointer
[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         memset(value, 0, valuelen);
152         key.data = fullkey;
153         key.size = strlen(fullkey) + 1;
154         
155         res = astdb->get(astdb, &key, &data, 0);
156         
157         ast_pthread_mutex_unlock(&dblock);
158
159         /* Be sure to NULL terminate our data either way */
160         if (res) {
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                         /* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */
169                         strncpy(value, data.data, (valuelen > data.size) ? data.size : valuelen);
170                 } else {
171                         ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
172                 }
173         }
174         return res;
175 }
176
177 int ast_db_del(const char *family, const char *keys)
178 {
179         char fullkey[256];
180         DBT key;
181         int res;
182
183         ast_pthread_mutex_lock(&dblock);
184         if (dbinit()) {
185                 ast_pthread_mutex_unlock(&dblock);
186                 return -1;
187         }
188         
189         snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
190         memset(&key, 0, sizeof(key));
191         key.data = fullkey;
192         key.size = strlen(fullkey) + 1;
193         
194         res = astdb->del(astdb, &key, 0);
195         astdb->sync(astdb, 0);
196         
197         ast_pthread_mutex_unlock(&dblock);
198
199         if (res) 
200                 ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
201         return res;
202 }
203
204 static int database_put(int fd, int argc, char *argv[])
205 {
206         int res;
207         if (argc != 5)
208                 return RESULT_SHOWUSAGE;
209         res = ast_db_put(argv[2], argv[3], argv[4]);
210         if (res) 
211                 ast_cli(fd, "Failed to update entry\n");
212         else
213                 ast_cli(fd, "Updated database successfully\n");
214         return RESULT_SUCCESS;
215 }
216
217 static int database_get(int fd, int argc, char *argv[])
218 {
219         int res;
220         char tmp[256];
221         if (argc != 4)
222                 return RESULT_SHOWUSAGE;
223         res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
224         if (res) 
225                 ast_cli(fd, "Database entry not found.\n");
226         else
227                 ast_cli(fd, "Value: %s\n", tmp);
228         return RESULT_SUCCESS;
229 }
230
231 static int database_del(int fd, int argc, char *argv[])
232 {
233         int res;
234         if (argc != 4)
235                 return RESULT_SHOWUSAGE;
236         res = ast_db_del(argv[2], argv[3]);
237         if (res) 
238                 ast_cli(fd, "Database entry does not exist.\n");
239         else
240                 ast_cli(fd, "Database entry removed.\n");
241         return RESULT_SUCCESS;
242 }
243
244 static int database_deltree(int fd, int argc, char *argv[])
245 {
246         int res;
247         if ((argc < 3) || (argc > 4))
248                 return RESULT_SHOWUSAGE;
249         if (argc == 4)
250                 res = ast_db_deltree(argv[2], argv[3]);
251         else
252                 res = ast_db_deltree(argv[2], NULL);
253         if (res) 
254                 ast_cli(fd, "Database entries do not exist.\n");
255         else
256                 ast_cli(fd, "Database entries removed.\n");
257         return RESULT_SUCCESS;
258 }
259
260 static int database_show(int fd, int argc, char *argv[])
261 {
262         char prefix[256];
263         DBT key, data;
264         char *keys, *values;
265         int res;
266         int pass;
267
268         if (argc == 4) {
269                 /* Family and key tree */
270                 snprintf(prefix, sizeof(prefix), "/%s/%s", argv[2], argv[3]);
271         } else if (argc == 3) {
272                 /* Family only */
273                 snprintf(prefix, sizeof(prefix), "/%s", argv[2]);
274         } else if (argc == 2) {
275                 /* Neither */
276                 strcpy(prefix, "");
277         } else
278                 return RESULT_SHOWUSAGE;
279         ast_pthread_mutex_lock(&dblock);
280         if (dbinit()) {
281                 ast_pthread_mutex_unlock(&dblock);
282                 ast_cli(fd, "Database unavailable\n");
283                 return RESULT_SUCCESS;  
284         }
285         memset(&key, 0, sizeof(key));
286         memset(&data, 0, sizeof(data));
287         pass = 0;
288         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
289                 if (key.size) {
290                         keys = key.data;
291                         keys[key.size - 1] = '\0';
292                 } else
293                         keys = "<bad key>";
294                 if (data.size) {
295                         values = data.data;
296                         values[data.size - 1]='\0';
297                 } else
298                         values = "<bad value>";
299                 if (keymatch(keys, prefix)) {
300                                 ast_cli(fd, "%-50s: %-25s\n", keys, values);
301                 }
302         }
303         ast_pthread_mutex_unlock(&dblock);
304         return RESULT_SUCCESS;  
305 }
306
307 struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
308 {
309         char prefix[256];
310         DBT key, data;
311         char *keys, *values;
312         int res;
313         int pass;
314         struct ast_db_entry *last = NULL;
315         struct ast_db_entry *cur, *ret=NULL;
316
317         if (family && strlen(family)) {
318                 if (keytree && strlen(keytree))
319                         /* Family and key tree */
320                         snprintf(prefix, sizeof(prefix), "/%s/%s", family, prefix);
321                 else
322                         /* Family only */
323                         snprintf(prefix, sizeof(prefix), "/%s", family);
324         } else
325                 strcpy(prefix, "");
326         ast_pthread_mutex_lock(&dblock);
327         if (dbinit()) {
328                 ast_pthread_mutex_unlock(&dblock);
329                 ast_log(LOG_WARNING, "Database unavailable\n");
330                 return NULL;    
331         }
332         memset(&key, 0, sizeof(key));
333         memset(&data, 0, sizeof(data));
334         pass = 0;
335         while(!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
336                 if (key.size) {
337                         keys = key.data;
338                         keys[key.size - 1] = '\0';
339                 } else
340                         keys = "<bad key>";
341                 if (data.size) {
342                         values = data.data;
343                         values[data.size - 1]='\0';
344                 } else
345                         values = "<bad value>";
346                 if (keymatch(keys, prefix)) {
347                                 cur = malloc(sizeof(struct ast_db_entry) + strlen(keys) + strlen(values) + 2);
348                                 if (cur) {
349                                         cur->next = NULL;
350                                         cur->key = cur->data + strlen(values) + 1;
351                                         strcpy(cur->data, values);
352                                         strcpy(cur->key, keys);
353                                         if (last)
354                                                 last->next = cur;
355                                         else
356                                                 ret = cur;
357                                         last = cur;
358                                 }
359                 }
360         }
361         ast_pthread_mutex_unlock(&dblock);
362         return ret;     
363 }
364
365 void ast_db_freetree(struct ast_db_entry *dbe)
366 {
367         struct ast_db_entry *last;
368         while(dbe) {
369                 last = dbe;
370                 dbe = dbe->next;
371                 free(last);
372         }
373 }
374
375 static char database_show_usage[] =
376 "Usage: database show [family [keytree]]\n"
377 "       Shows Asterisk database contents, optionally restricted\n"
378 "to a given family, or family and keytree.\n";
379
380 static char database_put_usage[] =
381 "Usage: database put <family> <key> <value>\n"
382 "       Adds or updates an entry in the Asterisk database for\n"
383 "a given family, key, and value.\n";
384
385 static char database_get_usage[] =
386 "Usage: database get <family> <key>\n"
387 "       Retrieves an entry in the Asterisk database for a given\n"
388 "family and key.\n";
389
390 static char database_del_usage[] =
391 "Usage: database del <family> <key>\n"
392 "       Deletes an entry in the Asterisk database for a given\n"
393 "family and key.\n";
394
395 static char database_deltree_usage[] =
396 "Usage: database deltree <family> [keytree]\n"
397 "       Deletes a family or specific keytree within a family\n"
398 "in the Asterisk database.\n";
399
400 struct ast_cli_entry cli_database_show =
401 { { "database", "show", NULL }, database_show, "Shows database contents", database_show_usage };
402
403 struct ast_cli_entry cli_database_get =
404 { { "database", "get", NULL }, database_get, "Gets database value", database_get_usage };
405
406 struct ast_cli_entry cli_database_put =
407 { { "database", "put", NULL }, database_put, "Adds/updates database value", database_put_usage };
408
409 struct ast_cli_entry cli_database_del =
410 { { "database", "del", NULL }, database_del, "Removes database key/value", database_del_usage };
411
412 struct ast_cli_entry cli_database_deltree =
413 { { "database", "deltree", NULL }, database_deltree, "Removes database keytree/values", database_deltree_usage };
414
415 int astdb_init(void)
416 {
417         dbinit();
418         ast_cli_register(&cli_database_show);
419         ast_cli_register(&cli_database_get);
420         ast_cli_register(&cli_database_put);
421         ast_cli_register(&cli_database_del);
422         ast_cli_register(&cli_database_deltree);
423         return 0;
424 }