Sun Oct 16 2011 08:41:39

Asterisk developer's documentation


db.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief ASTdb Management
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  *
00025  * \note DB3 is licensed under Sleepycat Public License and is thus incompatible
00026  * with GPL.  To avoid having to make another exception (and complicate 
00027  * licensing even further) we elect to use DB1 which is BSD licensed 
00028  */
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 322981 $")
00033 
00034 #include "asterisk/_private.h"
00035 #include "asterisk/paths.h"   /* use ast_config_AST_DB */
00036 #include <sys/time.h>
00037 #include <signal.h>
00038 #include <dirent.h>
00039 
00040 #include "asterisk/channel.h"
00041 #include "asterisk/file.h"
00042 #include "asterisk/app.h"
00043 #include "asterisk/dsp.h"
00044 #include "asterisk/astdb.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/lock.h"
00048 #include "asterisk/manager.h"
00049 #include "db1-ast/include/db.h"
00050 
00051 /*** DOCUMENTATION
00052    <manager name="DBGet" language="en_US">
00053       <synopsis>
00054          Get DB Entry.
00055       </synopsis>
00056       <syntax>
00057          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00058          <parameter name="Family" required="true" />
00059          <parameter name="Key" required="true" />
00060       </syntax>
00061       <description>
00062       </description>
00063    </manager>
00064    <manager name="DBPut" language="en_US">
00065       <synopsis>
00066          Put DB entry.
00067       </synopsis>
00068       <syntax>
00069          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00070          <parameter name="Family" required="true" />
00071          <parameter name="Key" required="true" />
00072          <parameter name="Val" />
00073       </syntax>
00074       <description>
00075       </description>
00076    </manager>
00077    <manager name="DBDel" language="en_US">
00078       <synopsis>
00079          Delete DB entry.
00080       </synopsis>
00081       <syntax>
00082          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00083          <parameter name="Family" required="true" />
00084          <parameter name="Key" required="true" />
00085       </syntax>
00086       <description>
00087       </description>
00088    </manager>
00089    <manager name="DBDelTree" language="en_US">
00090       <synopsis>
00091          Delete DB Tree.
00092       </synopsis>
00093       <syntax>
00094          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00095          <parameter name="Family" required="true" />
00096          <parameter name="Key" />
00097       </syntax>
00098       <description>
00099       </description>
00100    </manager>
00101  ***/
00102 
00103 #define MAX_DB_FIELD 256
00104 
00105 static DB *astdb;
00106 AST_MUTEX_DEFINE_STATIC(dblock);
00107 static ast_cond_t dbcond;
00108 typedef int (*process_keys_cb)(DBT *key, DBT *value, const char *filter, void *data);
00109 
00110 static void db_sync(void);
00111 
00112 static int dbinit(void) 
00113 {
00114    if (!astdb && !(astdb = dbopen(ast_config_AST_DB, O_CREAT | O_RDWR, AST_FILE_MODE, DB_BTREE, NULL))) {
00115       ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", ast_config_AST_DB, strerror(errno));
00116       return -1;
00117    }
00118    return 0;
00119 }
00120 
00121 
00122 static inline int keymatch(const char *key, const char *prefix)
00123 {
00124    int preflen = strlen(prefix);
00125    if (!preflen)
00126       return 1;
00127    if (!strcasecmp(key, prefix))
00128       return 1;
00129    if ((strlen(key) > preflen) && !strncasecmp(key, prefix, preflen)) {
00130       if (key[preflen] == '/')
00131          return 1;
00132    }
00133    return 0;
00134 }
00135 
00136 static inline int subkeymatch(const char *key, const char *suffix)
00137 {
00138    int suffixlen = strlen(suffix);
00139    if (suffixlen) {
00140       const char *subkey = key + strlen(key) - suffixlen;
00141       if (subkey < key)
00142          return 0;
00143       if (!strcasecmp(subkey, suffix))
00144          return 1;
00145    }
00146    return 0;
00147 }
00148 
00149 static const char *dbt_data2str(DBT *dbt)
00150 {
00151    char *data = "";
00152 
00153    if (dbt->size) {
00154       data = dbt->data;
00155       data[dbt->size - 1] = '\0';
00156    }
00157 
00158    return data;
00159 }
00160 
00161 static inline const char *dbt_data2str_full(DBT *dbt, const char *def)
00162 {
00163    return S_OR(dbt_data2str(dbt), def);
00164 }
00165 
00166 static int process_db_keys(process_keys_cb cb, void *data, const char *filter, int sync)
00167 {
00168    DBT key = { 0, }, value = { 0, }, last_key = { 0, };
00169    int counter = 0;
00170    int res, last = 0;
00171    char last_key_s[MAX_DB_FIELD];
00172 
00173    ast_mutex_lock(&dblock);
00174    if (dbinit()) {
00175       ast_mutex_unlock(&dblock);
00176       return -1;
00177    }
00178 
00179    /* Somehow, the database can become corrupted such that astdb->seq will continue looping through
00180     * the database indefinitely. The pointer to last_key.data ends up getting re-used by the BDB lib
00181     * so this specifically queries for the last entry, makes a copy of the key, and then uses it as
00182     * a sentinel to avoid repeatedly looping over the list. */
00183 
00184    if (astdb->seq(astdb, &last_key, &value, R_LAST)) {
00185       /* Empty database */
00186       ast_mutex_unlock(&dblock);
00187       return 0;
00188    }
00189 
00190    memcpy(last_key_s, last_key.data, MIN(last_key.size - 1, sizeof(last_key_s)));
00191    last_key_s[last_key.size - 1] = '\0';
00192    for (res = astdb->seq(astdb, &key, &value, R_FIRST);
00193          !res;
00194          res = astdb->seq(astdb, &key, &value, R_NEXT)) {
00195       /* The callback might delete the key, so we have to check it before calling */
00196       last = !strcmp(dbt_data2str_full(&key, "<bad key>"), last_key_s);
00197       counter += cb(&key, &value, filter, data);
00198       if (last) {
00199          break;
00200       }
00201    }
00202 
00203    if (sync) {
00204       db_sync();
00205    }
00206 
00207    ast_mutex_unlock(&dblock);
00208 
00209    return counter;
00210 }
00211 
00212 static int db_deltree_cb(DBT *key, DBT *value, const char *filter, void *data)
00213 {
00214    int res = 0;
00215 
00216    if (keymatch(dbt_data2str_full(key, "<bad key>"), filter)) {
00217       astdb->del(astdb, key, 0);
00218       res = 1;
00219    }
00220    return res;
00221 }
00222 
00223 int ast_db_deltree(const char *family, const char *keytree)
00224 {
00225    char prefix[MAX_DB_FIELD];
00226 
00227    if (family) {
00228       if (keytree) {
00229          snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
00230       } else {
00231          snprintf(prefix, sizeof(prefix), "/%s", family);
00232       }
00233    } else if (keytree) {
00234       return -1;
00235    } else {
00236       prefix[0] = '\0';
00237    }
00238 
00239    return process_db_keys(db_deltree_cb, NULL, prefix, 1);
00240 }
00241 
00242 int ast_db_put(const char *family, const char *keys, const char *value)
00243 {
00244    char fullkey[MAX_DB_FIELD];
00245    DBT key, data;
00246    int res, fullkeylen;
00247 
00248    ast_mutex_lock(&dblock);
00249    if (dbinit()) {
00250       ast_mutex_unlock(&dblock);
00251       return -1;
00252    }
00253 
00254    fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
00255    memset(&key, 0, sizeof(key));
00256    memset(&data, 0, sizeof(data));
00257    key.data = fullkey;
00258    key.size = fullkeylen + 1;
00259    data.data = (char *) value;
00260    data.size = strlen(value) + 1;
00261    res = astdb->put(astdb, &key, &data, 0);
00262    db_sync();
00263    ast_mutex_unlock(&dblock);
00264    if (res)
00265       ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
00266 
00267    return res;
00268 }
00269 
00270 int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
00271 {
00272    char fullkey[MAX_DB_FIELD] = "";
00273    DBT key, data;
00274    int res, fullkeylen;
00275 
00276    ast_mutex_lock(&dblock);
00277    if (dbinit()) {
00278       ast_mutex_unlock(&dblock);
00279       return -1;
00280    }
00281 
00282    fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
00283    memset(&key, 0, sizeof(key));
00284    memset(&data, 0, sizeof(data));
00285    memset(value, 0, valuelen);
00286    key.data = fullkey;
00287    key.size = fullkeylen + 1;
00288 
00289    res = astdb->get(astdb, &key, &data, 0);
00290 
00291    /* Be sure to NULL terminate our data either way */
00292    if (res) {
00293       ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
00294    } else {
00295 #if 0
00296       printf("Got value of size %d\n", data.size);
00297 #endif
00298       if (data.size) {
00299          ((char *)data.data)[data.size - 1] = '\0';
00300          /* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */
00301          ast_copy_string(value, data.data, (valuelen > data.size) ? data.size : valuelen);
00302       } else {
00303          ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
00304       }
00305    }
00306 
00307    /* Data is not fully isolated for concurrency, so the lock must be extended
00308     * to after the copy to the output buffer. */
00309    ast_mutex_unlock(&dblock);
00310 
00311    return res;
00312 }
00313 
00314 int ast_db_del(const char *family, const char *keys)
00315 {
00316    char fullkey[MAX_DB_FIELD];
00317    DBT key;
00318    int res, fullkeylen;
00319 
00320    ast_mutex_lock(&dblock);
00321    if (dbinit()) {
00322       ast_mutex_unlock(&dblock);
00323       return -1;
00324    }
00325    
00326    fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
00327    memset(&key, 0, sizeof(key));
00328    key.data = fullkey;
00329    key.size = fullkeylen + 1;
00330    
00331    res = astdb->del(astdb, &key, 0);
00332    db_sync();
00333    
00334    ast_mutex_unlock(&dblock);
00335 
00336    if (res) {
00337       ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
00338    }
00339    return res;
00340 }
00341 
00342 static char *handle_cli_database_put(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00343 {
00344    int res;
00345 
00346    switch (cmd) {
00347    case CLI_INIT:
00348       e->command = "database put";
00349       e->usage =
00350          "Usage: database put <family> <key> <value>\n"
00351          "       Adds or updates an entry in the Asterisk database for\n"
00352          "       a given family, key, and value.\n";
00353       return NULL;
00354    case CLI_GENERATE:
00355       return NULL;
00356    }
00357 
00358    if (a->argc != 5)
00359       return CLI_SHOWUSAGE;
00360    res = ast_db_put(a->argv[2], a->argv[3], a->argv[4]);
00361    if (res)  {
00362       ast_cli(a->fd, "Failed to update entry\n");
00363    } else {
00364       ast_cli(a->fd, "Updated database successfully\n");
00365    }
00366    return CLI_SUCCESS;
00367 }
00368 
00369 static char *handle_cli_database_get(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00370 {
00371    int res;
00372    char tmp[MAX_DB_FIELD];
00373 
00374    switch (cmd) {
00375    case CLI_INIT:
00376       e->command = "database get";
00377       e->usage =
00378          "Usage: database get <family> <key>\n"
00379          "       Retrieves an entry in the Asterisk database for a given\n"
00380          "       family and key.\n";
00381       return NULL;
00382    case CLI_GENERATE:
00383       return NULL;
00384    }
00385 
00386    if (a->argc != 4)
00387       return CLI_SHOWUSAGE;
00388    res = ast_db_get(a->argv[2], a->argv[3], tmp, sizeof(tmp));
00389    if (res) {
00390       ast_cli(a->fd, "Database entry not found.\n");
00391    } else {
00392       ast_cli(a->fd, "Value: %s\n", tmp);
00393    }
00394    return CLI_SUCCESS;
00395 }
00396 
00397 static char *handle_cli_database_del(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00398 {
00399    int res;
00400 
00401    switch (cmd) {
00402    case CLI_INIT:
00403       e->command = "database del";
00404       e->usage =
00405          "Usage: database del <family> <key>\n"
00406          "       Deletes an entry in the Asterisk database for a given\n"
00407          "       family and key.\n";
00408       return NULL;
00409    case CLI_GENERATE:
00410       return NULL;
00411    }
00412 
00413    if (a->argc != 4)
00414       return CLI_SHOWUSAGE;
00415    res = ast_db_del(a->argv[2], a->argv[3]);
00416    if (res) {
00417       ast_cli(a->fd, "Database entry does not exist.\n");
00418    } else {
00419       ast_cli(a->fd, "Database entry removed.\n");
00420    }
00421    return CLI_SUCCESS;
00422 }
00423 
00424 static char *handle_cli_database_deltree(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00425 {
00426    int res;
00427 
00428    switch (cmd) {
00429    case CLI_INIT:
00430       e->command = "database deltree";
00431       e->usage =
00432          "Usage: database deltree <family> [keytree]\n"
00433          "       Deletes a family or specific keytree within a family\n"
00434          "       in the Asterisk database.\n";
00435       return NULL;
00436    case CLI_GENERATE:
00437       return NULL;
00438    }
00439 
00440    if ((a->argc < 3) || (a->argc > 4))
00441       return CLI_SHOWUSAGE;
00442    if (a->argc == 4) {
00443       res = ast_db_deltree(a->argv[2], a->argv[3]);
00444    } else {
00445       res = ast_db_deltree(a->argv[2], NULL);
00446    }
00447    if (res < 0) {
00448       ast_cli(a->fd, "Database entries do not exist.\n");
00449    } else {
00450       ast_cli(a->fd, "%d database entries removed.\n",res);
00451    }
00452    return CLI_SUCCESS;
00453 }
00454 
00455 static int db_show_cb(DBT *key, DBT *value, const char *filter, void *data)
00456 {
00457    struct ast_cli_args *a = data;
00458    const char *key_s = dbt_data2str_full(key, "<bad key>");
00459    const char *value_s = dbt_data2str_full(value, "<bad value>");
00460 
00461    if (keymatch(key_s, filter)) {
00462       ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
00463       return 1;
00464    }
00465 
00466    return 0;
00467 }
00468 
00469 static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00470 {
00471    char prefix[MAX_DB_FIELD];
00472    int counter = 0;
00473 
00474    switch (cmd) {
00475    case CLI_INIT:
00476       e->command = "database show";
00477       e->usage =
00478          "Usage: database show [family [keytree]]\n"
00479          "       Shows Asterisk database contents, optionally restricted\n"
00480          "       to a given family, or family and keytree.\n";
00481       return NULL;
00482    case CLI_GENERATE:
00483       return NULL;
00484    }
00485 
00486    if (a->argc == 4) {
00487       /* Family and key tree */
00488       snprintf(prefix, sizeof(prefix), "/%s/%s", a->argv[2], a->argv[3]);
00489    } else if (a->argc == 3) {
00490       /* Family only */
00491       snprintf(prefix, sizeof(prefix), "/%s", a->argv[2]);
00492    } else if (a->argc == 2) {
00493       /* Neither */
00494       prefix[0] = '\0';
00495    } else {
00496       return CLI_SHOWUSAGE;
00497    }
00498 
00499    if((counter = process_db_keys(db_show_cb, a, prefix, 0)) < 0) {
00500       ast_cli(a->fd, "Database unavailable\n");
00501       return CLI_SUCCESS;
00502    }
00503 
00504    ast_cli(a->fd, "%d results found.\n", counter);
00505    return CLI_SUCCESS;
00506 }
00507 
00508 static int db_showkey_cb(DBT *key, DBT *value, const char *filter, void *data)
00509 {
00510    struct ast_cli_args *a = data;
00511    const char *key_s = dbt_data2str_full(key, "<bad key>");
00512    const char *value_s = dbt_data2str_full(value, "<bad value>");
00513 
00514    if (subkeymatch(key_s, filter)) {
00515       ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
00516       return 1;
00517    }
00518 
00519    return 0;
00520 }
00521 
00522 static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00523 {
00524    char suffix[MAX_DB_FIELD];
00525    int counter = 0;
00526 
00527    switch (cmd) {
00528    case CLI_INIT:
00529       e->command = "database showkey";
00530       e->usage =
00531          "Usage: database showkey <keytree>\n"
00532          "       Shows Asterisk database contents, restricted to a given key.\n";
00533       return NULL;
00534    case CLI_GENERATE:
00535       return NULL;
00536    }
00537 
00538    if (a->argc == 3) {
00539       /* Key only */
00540       snprintf(suffix, sizeof(suffix), "/%s", a->argv[2]);
00541    } else {
00542       return CLI_SHOWUSAGE;
00543    }
00544 
00545    if ((counter = process_db_keys(db_showkey_cb, a, suffix, 0)) < 0) {
00546       ast_cli(a->fd, "Database unavailable\n");
00547       return CLI_SUCCESS;
00548    }
00549 
00550    ast_cli(a->fd, "%d results found.\n", counter);
00551    return CLI_SUCCESS;
00552 }
00553 
00554 static int db_gettree_cb(DBT *key, DBT *value, const char *filter, void *data)
00555 {
00556    struct ast_db_entry **ret = data;
00557    struct ast_db_entry *cur;
00558    const char *key_s = dbt_data2str_full(key, "<bad key>");
00559    const char *value_s = dbt_data2str_full(value, "<bad value>");
00560    size_t key_slen = strlen(key_s) + 1, value_slen = strlen(value_s) + 1;
00561 
00562    if (keymatch(key_s, filter) && (cur = ast_malloc(sizeof(*cur) + key_slen + value_slen))) {
00563       cur->next = *ret;
00564       cur->key = cur->data + value_slen;
00565       strcpy(cur->data, value_s);
00566       strcpy(cur->key, key_s);
00567       *ret = cur;
00568       return 1;
00569    }
00570 
00571    return 0;
00572 }
00573 
00574 struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
00575 {
00576    char prefix[MAX_DB_FIELD];
00577    struct ast_db_entry *ret = NULL;
00578 
00579    if (!ast_strlen_zero(family)) {
00580       if (!ast_strlen_zero(keytree)) {
00581          /* Family and key tree */
00582          snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
00583       } else {
00584          /* Family only */
00585          snprintf(prefix, sizeof(prefix), "/%s", family);
00586       }
00587    } else {
00588       prefix[0] = '\0';
00589    }
00590 
00591    if (process_db_keys(db_gettree_cb, &ret, prefix, 0) < 0) {
00592       ast_log(LOG_WARNING, "Database unavailable\n");
00593       return NULL;
00594    }
00595 
00596    return ret;
00597 }
00598 
00599 void ast_db_freetree(struct ast_db_entry *dbe)
00600 {
00601    struct ast_db_entry *last;
00602    while (dbe) {
00603       last = dbe;
00604       dbe = dbe->next;
00605       ast_free(last);
00606    }
00607 }
00608 
00609 static struct ast_cli_entry cli_database[] = {
00610    AST_CLI_DEFINE(handle_cli_database_show,    "Shows database contents"),
00611    AST_CLI_DEFINE(handle_cli_database_showkey, "Shows database contents"),
00612    AST_CLI_DEFINE(handle_cli_database_get,     "Gets database value"),
00613    AST_CLI_DEFINE(handle_cli_database_put,     "Adds/updates database value"),
00614    AST_CLI_DEFINE(handle_cli_database_del,     "Removes database key/value"),
00615    AST_CLI_DEFINE(handle_cli_database_deltree, "Removes database keytree/values")
00616 };
00617 
00618 static int manager_dbput(struct mansession *s, const struct message *m)
00619 {
00620    const char *family = astman_get_header(m, "Family");
00621    const char *key = astman_get_header(m, "Key");
00622    const char *val = astman_get_header(m, "Val");
00623    int res;
00624 
00625    if (ast_strlen_zero(family)) {
00626       astman_send_error(s, m, "No family specified");
00627       return 0;
00628    }
00629    if (ast_strlen_zero(key)) {
00630       astman_send_error(s, m, "No key specified");
00631       return 0;
00632    }
00633 
00634    res = ast_db_put(family, key, S_OR(val, ""));
00635    if (res) {
00636       astman_send_error(s, m, "Failed to update entry");
00637    } else {
00638       astman_send_ack(s, m, "Updated database successfully");
00639    }
00640    return 0;
00641 }
00642 
00643 static int manager_dbget(struct mansession *s, const struct message *m)
00644 {
00645    const char *id = astman_get_header(m,"ActionID");
00646    char idText[256] = "";
00647    const char *family = astman_get_header(m, "Family");
00648    const char *key = astman_get_header(m, "Key");
00649    char tmp[MAX_DB_FIELD];
00650    int res;
00651 
00652    if (ast_strlen_zero(family)) {
00653       astman_send_error(s, m, "No family specified.");
00654       return 0;
00655    }
00656    if (ast_strlen_zero(key)) {
00657       astman_send_error(s, m, "No key specified.");
00658       return 0;
00659    }
00660 
00661    if (!ast_strlen_zero(id))
00662       snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
00663 
00664    res = ast_db_get(family, key, tmp, sizeof(tmp));
00665    if (res) {
00666       astman_send_error(s, m, "Database entry not found");
00667    } else {
00668       astman_send_ack(s, m, "Result will follow");
00669       astman_append(s, "Event: DBGetResponse\r\n"
00670             "Family: %s\r\n"
00671             "Key: %s\r\n"
00672             "Val: %s\r\n"
00673             "%s"
00674             "\r\n",
00675             family, key, tmp, idText);
00676       astman_append(s, "Event: DBGetComplete\r\n"
00677             "%s"
00678             "\r\n",
00679             idText);
00680    }
00681    return 0;
00682 }
00683 
00684 static int manager_dbdel(struct mansession *s, const struct message *m)
00685 {
00686    const char *family = astman_get_header(m, "Family");
00687    const char *key = astman_get_header(m, "Key");
00688    int res;
00689 
00690    if (ast_strlen_zero(family)) {
00691       astman_send_error(s, m, "No family specified.");
00692       return 0;
00693    }
00694 
00695    if (ast_strlen_zero(key)) {
00696       astman_send_error(s, m, "No key specified.");
00697       return 0;
00698    }
00699 
00700    res = ast_db_del(family, key);
00701    if (res)
00702       astman_send_error(s, m, "Database entry not found");
00703    else
00704       astman_send_ack(s, m, "Key deleted successfully");
00705 
00706    return 0;
00707 }
00708 
00709 static int manager_dbdeltree(struct mansession *s, const struct message *m)
00710 {
00711    const char *family = astman_get_header(m, "Family");
00712    const char *key = astman_get_header(m, "Key");
00713    int res;
00714 
00715    if (ast_strlen_zero(family)) {
00716       astman_send_error(s, m, "No family specified.");
00717       return 0;
00718    }
00719 
00720    if (!ast_strlen_zero(key))
00721       res = ast_db_deltree(family, key);
00722    else
00723       res = ast_db_deltree(family, NULL);
00724 
00725    if (res < 0)
00726       astman_send_error(s, m, "Database entry not found");
00727    else
00728       astman_send_ack(s, m, "Key tree deleted successfully");
00729    
00730    return 0;
00731 }
00732 
00733 /*!
00734  * \internal
00735  * \brief Signal the astdb sync thread to do its thing.
00736  *
00737  * \note dblock is assumed to be held when calling this function.
00738  */
00739 static void db_sync(void)
00740 {
00741    ast_cond_signal(&dbcond);
00742 }
00743 
00744 /*!
00745  * \internal
00746  * \brief astdb sync thread
00747  *
00748  * This thread is in charge of syncing astdb to disk after a change.
00749  * By pushing it off to this thread to take care of, this I/O bound operation
00750  * will not block other threads from performing other critical processing.
00751  * If changes happen rapidly, this thread will also ensure that the sync
00752  * operations are rate limited.
00753  */
00754 static void *db_sync_thread(void *data)
00755 {
00756    ast_mutex_lock(&dblock);
00757    for (;;) {
00758       ast_cond_wait(&dbcond, &dblock);
00759       ast_mutex_unlock(&dblock);
00760       sleep(1);
00761       ast_mutex_lock(&dblock);
00762       astdb->sync(astdb, 0);
00763    }
00764 
00765    return NULL;
00766 }
00767 
00768 int astdb_init(void)
00769 {
00770    pthread_t dont_care;
00771 
00772    ast_cond_init(&dbcond, NULL);
00773    if (ast_pthread_create_background(&dont_care, NULL, db_sync_thread, NULL)) {
00774       return -1;
00775    }
00776 
00777    dbinit();
00778    ast_cli_register_multiple(cli_database, ARRAY_LEN(cli_database));
00779    ast_manager_register_xml("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget);
00780    ast_manager_register_xml("DBPut", EVENT_FLAG_SYSTEM, manager_dbput);
00781    ast_manager_register_xml("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel);
00782    ast_manager_register_xml("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree);
00783    return 0;
00784 }