Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:47 2007

Asterisk developer's documentation :: Codename Pineapple


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: 52331 $")
00033 
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <sys/time.h>
00038 #include <signal.h>
00039 #include <errno.h>
00040 #include <unistd.h>
00041 #include <dirent.h>
00042 
00043 #include "asterisk/channel.h"
00044 #include "asterisk/file.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/dsp.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/options.h"
00049 #include "asterisk/astdb.h"
00050 #include "asterisk/cli.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/lock.h"
00053 #include "asterisk/manager.h"
00054 #include "db1-ast/include/db.h"
00055 
00056 #ifdef __CYGWIN__
00057 #define dbopen __dbopen
00058 #endif
00059 
00060 static DB *astdb;
00061 AST_MUTEX_DEFINE_STATIC(dblock);
00062 
00063 static int dbinit(void) 
00064 {
00065    if (!astdb && !(astdb = dbopen((char *)ast_config_AST_DB, O_CREAT | O_RDWR, AST_FILE_MODE, DB_BTREE, NULL))) {
00066       ast_log(LOG_WARNING, "Unable to open Asterisk database\n");
00067       return -1;
00068    }
00069    return 0;
00070 }
00071 
00072 
00073 static inline int keymatch(const char *key, const char *prefix)
00074 {
00075    int preflen = strlen(prefix);
00076    if (!preflen)
00077       return 1;
00078    if (!strcasecmp(key, prefix))
00079       return 1;
00080    if ((strlen(key) > preflen) && !strncasecmp(key, prefix, preflen)) {
00081       if (key[preflen] == '/')
00082          return 1;
00083    }
00084    return 0;
00085 }
00086 
00087 static inline int subkeymatch(const char *key, const char *suffix)
00088 {
00089    int suffixlen = strlen(suffix);
00090    if (suffixlen) {
00091       const char *subkey = key + strlen(key) - suffixlen;
00092       if (subkey < key)
00093          return 0;
00094       if (!strcasecmp(subkey, suffix))
00095          return 1;
00096    }
00097    return 0;
00098 }
00099 
00100 int ast_db_deltree(const char *family, const char *keytree)
00101 {
00102    char prefix[256];
00103    DBT key, data;
00104    char *keys;
00105    int res;
00106    int pass;
00107    
00108    if (family) {
00109       if (keytree) {
00110          snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
00111       } else {
00112          snprintf(prefix, sizeof(prefix), "/%s", family);
00113       }
00114    } else if (keytree) {
00115       return -1;
00116    } else {
00117       prefix[0] = '\0';
00118    }
00119    
00120    ast_mutex_lock(&dblock);
00121    if (dbinit()) {
00122       ast_mutex_unlock(&dblock);
00123       return -1;
00124    }
00125    
00126    memset(&key, 0, sizeof(key));
00127    memset(&data, 0, sizeof(data));
00128    pass = 0;
00129    while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
00130       if (key.size) {
00131          keys = key.data;
00132          keys[key.size - 1] = '\0';
00133       } else {
00134          keys = "<bad key>";
00135       }
00136       if (keymatch(keys, prefix)) {
00137          astdb->del(astdb, &key, 0);
00138       }
00139    }
00140    astdb->sync(astdb, 0);
00141    ast_mutex_unlock(&dblock);
00142    return 0;
00143 }
00144 
00145 int ast_db_put(const char *family, const char *keys, const char *value)
00146 {
00147    char fullkey[256];
00148    DBT key, data;
00149    int res, fullkeylen;
00150 
00151    ast_mutex_lock(&dblock);
00152    if (dbinit()) {
00153       ast_mutex_unlock(&dblock);
00154       return -1;
00155    }
00156 
00157    fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
00158    memset(&key, 0, sizeof(key));
00159    memset(&data, 0, sizeof(data));
00160    key.data = fullkey;
00161    key.size = fullkeylen + 1;
00162    data.data = (char *) value;
00163    data.size = strlen(value) + 1;
00164    res = astdb->put(astdb, &key, &data, 0);
00165    astdb->sync(astdb, 0);
00166    ast_mutex_unlock(&dblock);
00167    if (res)
00168       ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
00169    return res;
00170 }
00171 
00172 int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
00173 {
00174    char fullkey[256] = "";
00175    DBT key, data;
00176    int res, fullkeylen;
00177 
00178    ast_mutex_lock(&dblock);
00179    if (dbinit()) {
00180       ast_mutex_unlock(&dblock);
00181       return -1;
00182    }
00183 
00184    fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
00185    memset(&key, 0, sizeof(key));
00186    memset(&data, 0, sizeof(data));
00187    memset(value, 0, valuelen);
00188    key.data = fullkey;
00189    key.size = fullkeylen + 1;
00190    
00191    res = astdb->get(astdb, &key, &data, 0);
00192    
00193    ast_mutex_unlock(&dblock);
00194 
00195    /* Be sure to NULL terminate our data either way */
00196    if (res) {
00197       if (option_debug)
00198          ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
00199    } else {
00200 #if 0
00201       printf("Got value of size %d\n", data.size);
00202 #endif
00203       if (data.size) {
00204          ((char *)data.data)[data.size - 1] = '\0';
00205          /* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */
00206          ast_copy_string(value, data.data, (valuelen > data.size) ? data.size : valuelen);
00207       } else {
00208          ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
00209       }
00210    }
00211    return res;
00212 }
00213 
00214 int ast_db_del(const char *family, const char *keys)
00215 {
00216    char fullkey[256];
00217    DBT key;
00218    int res, fullkeylen;
00219 
00220    ast_mutex_lock(&dblock);
00221    if (dbinit()) {
00222       ast_mutex_unlock(&dblock);
00223       return -1;
00224    }
00225    
00226    fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
00227    memset(&key, 0, sizeof(key));
00228    key.data = fullkey;
00229    key.size = fullkeylen + 1;
00230    
00231    res = astdb->del(astdb, &key, 0);
00232    astdb->sync(astdb, 0);
00233    
00234    ast_mutex_unlock(&dblock);
00235 
00236    if (res) {
00237       if (option_debug)
00238          ast_log(LOG_DEBUG, "Unable to find key '%s' in family '%s'\n", keys, family);
00239    }
00240    return res;
00241 }
00242 
00243 static int database_put(int fd, int argc, char *argv[])
00244 {
00245    int res;
00246    if (argc != 5)
00247       return RESULT_SHOWUSAGE;
00248    res = ast_db_put(argv[2], argv[3], argv[4]);
00249    if (res)  {
00250       ast_cli(fd, "Failed to update entry\n");
00251    } else {
00252       ast_cli(fd, "Updated database successfully\n");
00253    }
00254    return RESULT_SUCCESS;
00255 }
00256 
00257 static int database_get(int fd, int argc, char *argv[])
00258 {
00259    int res;
00260    char tmp[256];
00261    if (argc != 4)
00262       return RESULT_SHOWUSAGE;
00263    res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
00264    if (res) {
00265       ast_cli(fd, "Database entry not found.\n");
00266    } else {
00267       ast_cli(fd, "Value: %s\n", tmp);
00268    }
00269    return RESULT_SUCCESS;
00270 }
00271 
00272 static int database_del(int fd, int argc, char *argv[])
00273 {
00274    int res;
00275    if (argc != 4)
00276       return RESULT_SHOWUSAGE;
00277    res = ast_db_del(argv[2], argv[3]);
00278    if (res) {
00279       ast_cli(fd, "Database entry does not exist.\n");
00280    } else {
00281       ast_cli(fd, "Database entry removed.\n");
00282    }
00283    return RESULT_SUCCESS;
00284 }
00285 
00286 static int database_deltree(int fd, int argc, char *argv[])
00287 {
00288    int res;
00289    if ((argc < 3) || (argc > 4))
00290       return RESULT_SHOWUSAGE;
00291    if (argc == 4) {
00292       res = ast_db_deltree(argv[2], argv[3]);
00293    } else {
00294       res = ast_db_deltree(argv[2], NULL);
00295    }
00296    if (res) {
00297       ast_cli(fd, "Database entries do not exist.\n");
00298    } else {
00299       ast_cli(fd, "Database entries removed.\n");
00300    }
00301    return RESULT_SUCCESS;
00302 }
00303 
00304 static int database_show(int fd, int argc, char *argv[])
00305 {
00306    char prefix[256];
00307    DBT key, data;
00308    char *keys, *values;
00309    int res;
00310    int pass;
00311 
00312    if (argc == 4) {
00313       /* Family and key tree */
00314       snprintf(prefix, sizeof(prefix), "/%s/%s", argv[2], argv[3]);
00315    } else if (argc == 3) {
00316       /* Family only */
00317       snprintf(prefix, sizeof(prefix), "/%s", argv[2]);
00318    } else if (argc == 2) {
00319       /* Neither */
00320       prefix[0] = '\0';
00321    } else {
00322       return RESULT_SHOWUSAGE;
00323    }
00324    ast_mutex_lock(&dblock);
00325    if (dbinit()) {
00326       ast_mutex_unlock(&dblock);
00327       ast_cli(fd, "Database unavailable\n");
00328       return RESULT_SUCCESS;  
00329    }
00330    memset(&key, 0, sizeof(key));
00331    memset(&data, 0, sizeof(data));
00332    pass = 0;
00333    while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
00334       if (key.size) {
00335          keys = key.data;
00336          keys[key.size - 1] = '\0';
00337       } else {
00338          keys = "<bad key>";
00339       }
00340       if (data.size) {
00341          values = data.data;
00342          values[data.size - 1]='\0';
00343       } else {
00344          values = "<bad value>";
00345       }
00346       if (keymatch(keys, prefix)) {
00347             ast_cli(fd, "%-50s: %-25s\n", keys, values);
00348       }
00349    }
00350    ast_mutex_unlock(&dblock);
00351    return RESULT_SUCCESS;  
00352 }
00353 
00354 static int database_showkey(int fd, int argc, char *argv[])
00355 {
00356    char suffix[256];
00357    DBT key, data;
00358    char *keys, *values;
00359    int res;
00360    int pass;
00361 
00362    if (argc == 3) {
00363       /* Key only */
00364       snprintf(suffix, sizeof(suffix), "/%s", argv[2]);
00365    } else {
00366       return RESULT_SHOWUSAGE;
00367    }
00368    ast_mutex_lock(&dblock);
00369    if (dbinit()) {
00370       ast_mutex_unlock(&dblock);
00371       ast_cli(fd, "Database unavailable\n");
00372       return RESULT_SUCCESS;  
00373    }
00374    memset(&key, 0, sizeof(key));
00375    memset(&data, 0, sizeof(data));
00376    pass = 0;
00377    while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
00378       if (key.size) {
00379          keys = key.data;
00380          keys[key.size - 1] = '\0';
00381       } else {
00382          keys = "<bad key>";
00383       }
00384       if (data.size) {
00385          values = data.data;
00386          values[data.size - 1]='\0';
00387       } else {
00388          values = "<bad value>";
00389       }
00390       if (subkeymatch(keys, suffix)) {
00391             ast_cli(fd, "%-50s: %-25s\n", keys, values);
00392       }
00393    }
00394    ast_mutex_unlock(&dblock);
00395    return RESULT_SUCCESS;  
00396 }
00397 
00398 struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
00399 {
00400    char prefix[256];
00401    DBT key, data;
00402    char *keys, *values;
00403    int values_len;
00404    int res;
00405    int pass;
00406    struct ast_db_entry *last = NULL;
00407    struct ast_db_entry *cur, *ret=NULL;
00408 
00409    if (!ast_strlen_zero(family)) {
00410       if (!ast_strlen_zero(keytree)) {
00411          /* Family and key tree */
00412          snprintf(prefix, sizeof(prefix), "/%s/%s", family, prefix);
00413       } else {
00414          /* Family only */
00415          snprintf(prefix, sizeof(prefix), "/%s", family);
00416       }
00417    } else {
00418       prefix[0] = '\0';
00419    }
00420    ast_mutex_lock(&dblock);
00421    if (dbinit()) {
00422       ast_mutex_unlock(&dblock);
00423       ast_log(LOG_WARNING, "Database unavailable\n");
00424       return NULL;   
00425    }
00426    memset(&key, 0, sizeof(key));
00427    memset(&data, 0, sizeof(data));
00428    pass = 0;
00429    while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
00430       if (key.size) {
00431          keys = key.data;
00432          keys[key.size - 1] = '\0';
00433       } else {
00434          keys = "<bad key>";
00435       }
00436       if (data.size) {
00437          values = data.data;
00438          values[data.size - 1] = '\0';
00439       } else {
00440          values = "<bad value>";
00441       }
00442       values_len = strlen(values) + 1;
00443       if (keymatch(keys, prefix) && (cur = ast_malloc(sizeof(*cur) + strlen(keys) + 1 + values_len))) {
00444          cur->next = NULL;
00445          cur->key = cur->data + values_len;
00446          strcpy(cur->data, values);
00447          strcpy(cur->key, keys);
00448          if (last) {
00449             last->next = cur;
00450          } else {
00451             ret = cur;
00452          }
00453          last = cur;
00454       }
00455    }
00456    ast_mutex_unlock(&dblock);
00457    return ret; 
00458 }
00459 
00460 void ast_db_freetree(struct ast_db_entry *dbe)
00461 {
00462    struct ast_db_entry *last;
00463    while (dbe) {
00464       last = dbe;
00465       dbe = dbe->next;
00466       free(last);
00467    }
00468 }
00469 
00470 static const char database_show_usage[] =
00471 "Usage: database show [family [keytree]]\n"
00472 "       Shows Asterisk database contents, optionally restricted\n"
00473 "to a given family, or family and keytree.\n";
00474 
00475 static const char database_showkey_usage[] =
00476 "Usage: database showkey <keytree>\n"
00477 "       Shows Asterisk database contents, restricted to a given key.\n";
00478 
00479 static const char database_put_usage[] =
00480 "Usage: database put <family> <key> <value>\n"
00481 "       Adds or updates an entry in the Asterisk database for\n"
00482 "a given family, key, and value.\n";
00483 
00484 static const char database_get_usage[] =
00485 "Usage: database get <family> <key>\n"
00486 "       Retrieves an entry in the Asterisk database for a given\n"
00487 "family and key.\n";
00488 
00489 static const char database_del_usage[] =
00490 "Usage: database del <family> <key>\n"
00491 "       Deletes an entry in the Asterisk database for a given\n"
00492 "family and key.\n";
00493 
00494 static const char database_deltree_usage[] =
00495 "Usage: database deltree <family> [keytree]\n"
00496 "       Deletes a family or specific keytree within a family\n"
00497 "in the Asterisk database.\n";
00498 
00499 struct ast_cli_entry cli_database[] = {
00500    { { "database", "show", NULL },
00501    database_show, "Shows database contents",
00502    database_show_usage },
00503 
00504    { { "database", "showkey", NULL },
00505    database_showkey, "Shows database contents",
00506    database_showkey_usage },
00507 
00508    { { "database", "get", NULL },
00509    database_get, "Gets database value",
00510    database_get_usage },
00511 
00512    { { "database", "put", NULL },
00513    database_put, "Adds/updates database value",
00514    database_put_usage },
00515 
00516    { { "database", "del", NULL },
00517    database_del, "Removes database key/value",
00518    database_del_usage },
00519 
00520    { { "database", "deltree", NULL },
00521    database_deltree, "Removes database keytree/values",
00522    database_deltree_usage },
00523 };
00524 
00525 static int manager_dbput(struct mansession *s, const struct message *m)
00526 {
00527    const char *family = astman_get_header(m, "Family");
00528    const char *key = astman_get_header(m, "Key");
00529    const char *val = astman_get_header(m, "Val");
00530    int res;
00531 
00532    if (ast_strlen_zero(family)) {
00533       astman_send_error(s, m, "No family specified");
00534       return 0;
00535    }
00536    if (ast_strlen_zero(key)) {
00537       astman_send_error(s, m, "No key specified");
00538       return 0;
00539    }
00540    if (ast_strlen_zero(val)) {
00541       astman_send_error(s, m, "No val specified");
00542       return 0;
00543    }
00544 
00545    res = ast_db_put(family, key, val);
00546    if (res) {
00547       astman_send_error(s, m, "Failed to update entry");
00548    } else {
00549       astman_send_ack(s, m, "Updated database successfully");
00550    }
00551    return 0;
00552 }
00553 
00554 static int manager_dbget(struct mansession *s, const struct message *m)
00555 {
00556    const char *id = astman_get_header(m,"ActionID");
00557    char idText[256] = "";
00558    const char *family = astman_get_header(m, "Family");
00559    const char *key = astman_get_header(m, "Key");
00560    char tmp[256];
00561    int res;
00562 
00563    if (ast_strlen_zero(family)) {
00564       astman_send_error(s, m, "No family specified.");
00565       return 0;
00566    }
00567    if (ast_strlen_zero(key)) {
00568       astman_send_error(s, m, "No key specified.");
00569       return 0;
00570    }
00571 
00572    if (!ast_strlen_zero(id))
00573       snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
00574 
00575    res = ast_db_get(family, key, tmp, sizeof(tmp));
00576    if (res) {
00577       astman_send_error(s, m, "Database entry not found");
00578    } else {
00579       astman_send_ack(s, m, "Result will follow");
00580       astman_append(s, "Event: DBGetResponse\r\n"
00581             "Family: %s\r\n"
00582             "Key: %s\r\n"
00583             "Val: %s\r\n"
00584             "%s"
00585             "\r\n",
00586             family, key, tmp, idText);
00587    }
00588    return 0;
00589 }
00590 
00591 static int manager_dbdel(struct mansession *s, const struct message *m)
00592 {
00593    const char *family = astman_get_header(m, "Family");
00594    const char *key = astman_get_header(m, "Key");
00595    int res;
00596 
00597    if (ast_strlen_zero(family)) {
00598       astman_send_error(s, m, "No family specified.");
00599       return 0;
00600    }
00601 
00602    if (ast_strlen_zero(key)) {
00603       astman_send_error(s, m, "No key specified.");
00604       return 0;
00605    }
00606 
00607    res = ast_db_del(family, key);
00608    if (res)
00609       astman_send_error(s, m, "Database entry not found");
00610    else
00611       astman_send_ack(s, m, "Key deleted successfully");
00612 
00613    return 0;
00614 }
00615 
00616 static int manager_dbdeltree(struct mansession *s, const struct message *m)
00617 {
00618    const char *family = astman_get_header(m, "Family");
00619    const char *key = astman_get_header(m, "Key");
00620    int res;
00621 
00622    if (ast_strlen_zero(family)) {
00623       astman_send_error(s, m, "No family specified.");
00624       return 0;
00625    }
00626 
00627    if (!ast_strlen_zero(key))
00628       res = ast_db_deltree(family, key);
00629    else
00630       res = ast_db_deltree(family, NULL);
00631 
00632    if (res)
00633       astman_send_error(s, m, "Database entry not found");
00634    else
00635       astman_send_ack(s, m, "Key tree deleted successfully");
00636    
00637    return 0;
00638 }
00639 
00640 int astdb_init(void)
00641 {
00642    dbinit();
00643    ast_cli_register_multiple(cli_database, sizeof(cli_database) / sizeof(struct ast_cli_entry));
00644    ast_manager_register("DBGet", EVENT_FLAG_SYSTEM, manager_dbget, "Get DB Entry");
00645    ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry");
00646    ast_manager_register("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel, "Delete DB Entry");
00647    ast_manager_register("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree, "Delete DB Tree");
00648    return 0;
00649 }

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen