2003-01-30 15:03:20 +00:00
|
|
|
/*
|
2005-09-14 20:46:50 +00:00
|
|
|
* Asterisk -- An open source telephony toolkit.
|
2003-01-30 15:03:20 +00:00
|
|
|
*
|
2005-09-14 20:46:50 +00:00
|
|
|
* Copyright (C) 1999 - 2005, Digium, Inc.
|
|
|
|
*
|
|
|
|
* Mark Spencer <markster@digium.com>
|
2003-01-30 15:03:20 +00:00
|
|
|
*
|
2005-09-14 20:46:50 +00:00
|
|
|
* See http://www.asterisk.org for more information about
|
|
|
|
* the Asterisk project. Please do not directly contact
|
|
|
|
* any of the maintainers of this project for assistance;
|
|
|
|
* the project provides a web site, mailing lists and IRC
|
|
|
|
* channels for your use.
|
2003-01-30 15:03:20 +00:00
|
|
|
*
|
|
|
|
* This program is free software, distributed under the terms of
|
2005-09-14 20:46:50 +00:00
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
2005-10-24 20:12:06 +00:00
|
|
|
/*! \file
|
2005-09-14 20:46:50 +00:00
|
|
|
*
|
2005-10-24 20:12:06 +00:00
|
|
|
* \brief ASTdb Management
|
2005-09-14 20:46:50 +00:00
|
|
|
*
|
2011-07-06 20:58:12 +00:00
|
|
|
* \author Mark Spencer <markster@digium.com>
|
2005-12-30 21:18:06 +00:00
|
|
|
*
|
|
|
|
* \note DB3 is licensed under Sleepycat Public License and is thus incompatible
|
2011-07-06 20:58:12 +00:00
|
|
|
* with GPL. To avoid having to make another exception (and complicate
|
|
|
|
* licensing even further) we elect to use DB1 which is BSD licensed
|
2003-01-30 15:03:20 +00:00
|
|
|
*/
|
|
|
|
|
2012-06-15 16:20:16 +00:00
|
|
|
/*** MODULEINFO
|
|
|
|
<support_level>core</support_level>
|
|
|
|
***/
|
|
|
|
|
2006-06-07 18:54:56 +00:00
|
|
|
#include "asterisk.h"
|
|
|
|
|
2007-11-20 22:18:21 +00:00
|
|
|
#include "asterisk/_private.h"
|
2007-11-20 23:16:15 +00:00
|
|
|
#include "asterisk/paths.h" /* use ast_config_AST_DB */
|
2003-01-30 15:03:20 +00:00
|
|
|
#include <sys/time.h>
|
2011-07-06 20:58:12 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2003-01-30 15:03:20 +00:00
|
|
|
#include <signal.h>
|
|
|
|
#include <dirent.h>
|
2011-07-06 20:58:12 +00:00
|
|
|
#include <sqlite3.h>
|
2005-04-22 13:11:34 +00:00
|
|
|
|
2005-04-21 06:02:45 +00:00
|
|
|
#include "asterisk/channel.h"
|
|
|
|
#include "asterisk/file.h"
|
|
|
|
#include "asterisk/app.h"
|
|
|
|
#include "asterisk/dsp.h"
|
|
|
|
#include "asterisk/astdb.h"
|
|
|
|
#include "asterisk/cli.h"
|
|
|
|
#include "asterisk/utils.h"
|
|
|
|
#include "asterisk/manager.h"
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2009-05-22 17:52:35 +00:00
|
|
|
/*** DOCUMENTATION
|
|
|
|
<manager name="DBGet" language="en_US">
|
|
|
|
<synopsis>
|
|
|
|
Get DB Entry.
|
|
|
|
</synopsis>
|
|
|
|
<syntax>
|
|
|
|
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
|
|
|
<parameter name="Family" required="true" />
|
|
|
|
<parameter name="Key" required="true" />
|
|
|
|
</syntax>
|
|
|
|
<description>
|
|
|
|
</description>
|
|
|
|
</manager>
|
2022-07-11 11:32:39 +00:00
|
|
|
<manager name="DBGetTree" language="en_US">
|
|
|
|
<synopsis>
|
|
|
|
Get DB entries, optionally at a particular family/key
|
|
|
|
</synopsis>
|
|
|
|
<syntax>
|
|
|
|
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
|
|
|
<parameter name="Family" required="false" />
|
|
|
|
<parameter name="Key" required="false" />
|
|
|
|
</syntax>
|
|
|
|
<description>
|
|
|
|
</description>
|
|
|
|
</manager>
|
2009-05-22 17:52:35 +00:00
|
|
|
<manager name="DBPut" language="en_US">
|
|
|
|
<synopsis>
|
|
|
|
Put DB entry.
|
|
|
|
</synopsis>
|
|
|
|
<syntax>
|
|
|
|
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
|
|
|
<parameter name="Family" required="true" />
|
|
|
|
<parameter name="Key" required="true" />
|
|
|
|
<parameter name="Val" />
|
|
|
|
</syntax>
|
|
|
|
<description>
|
|
|
|
</description>
|
|
|
|
</manager>
|
|
|
|
<manager name="DBDel" language="en_US">
|
|
|
|
<synopsis>
|
|
|
|
Delete DB entry.
|
|
|
|
</synopsis>
|
|
|
|
<syntax>
|
|
|
|
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
|
|
|
<parameter name="Family" required="true" />
|
|
|
|
<parameter name="Key" required="true" />
|
|
|
|
</syntax>
|
|
|
|
<description>
|
|
|
|
</description>
|
|
|
|
</manager>
|
|
|
|
<manager name="DBDelTree" language="en_US">
|
|
|
|
<synopsis>
|
|
|
|
Delete DB Tree.
|
|
|
|
</synopsis>
|
|
|
|
<syntax>
|
|
|
|
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
|
|
|
<parameter name="Family" required="true" />
|
|
|
|
<parameter name="Key" />
|
|
|
|
</syntax>
|
|
|
|
<description>
|
|
|
|
</description>
|
|
|
|
</manager>
|
|
|
|
***/
|
|
|
|
|
2011-06-10 15:30:50 +00:00
|
|
|
#define MAX_DB_FIELD 256
|
2004-06-09 01:45:08 +00:00
|
|
|
AST_MUTEX_DEFINE_STATIC(dblock);
|
2010-09-13 22:13:27 +00:00
|
|
|
static ast_cond_t dbcond;
|
2011-07-06 20:58:12 +00:00
|
|
|
static sqlite3 *astdb;
|
|
|
|
static pthread_t syncthread;
|
|
|
|
static int doexit;
|
2014-03-14 16:19:21 +00:00
|
|
|
static int dosync;
|
2010-09-13 22:13:27 +00:00
|
|
|
|
|
|
|
static void db_sync(void);
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
#define DEFINE_SQL_STATEMENT(stmt,sql) static sqlite3_stmt *stmt; \
|
|
|
|
const char stmt##_sql[] = sql;
|
|
|
|
|
2014-07-24 21:01:37 +00:00
|
|
|
DEFINE_SQL_STATEMENT(put_stmt, "INSERT OR REPLACE INTO astdb (key, value) VALUES (?, ?)")
|
2011-07-06 20:58:12 +00:00
|
|
|
DEFINE_SQL_STATEMENT(get_stmt, "SELECT value FROM astdb WHERE key=?")
|
Stir/Shaken Refactor
Why do we need a refactor?
The original stir/shaken implementation was started over 3 years ago
when little was understood about practical implementation. The
result was an implementation that wouldn't actually interoperate
with any other stir-shaken implementations.
There were also a number of stir-shaken features and RFC
requirements that were never implemented such as TNAuthList
certificate validation, sending Reason headers in SIP responses
when verification failed but we wished to continue the call, and
the ability to send Media Key(mky) grants in the Identity header
when the call involved DTLS.
Finally, there were some performance concerns around outgoing
calls and selection of the correct certificate and private key.
The configuration was keyed by an arbitrary name which meant that
for every outgoing call, we had to scan the entire list of
configured TNs to find the correct cert to use. With only a few
TNs configured, this wasn't an issue but if you have a thousand,
it could be.
What's changed?
* Configuration objects have been refactored to be clearer about
their uses and to fix issues.
* The "general" object was renamed to "verification" since it
contains parameters specific to the incoming verification
process. It also never handled ca_path and crl_path
correctly.
* A new "attestation" object was added that controls the
outgoing attestation process. It sets default certificates,
keys, etc.
* The "certificate" object was renamed to "tn" and had it's key
change to telephone number since outgoing call attestation
needs to look up certificates by telephone number.
* The "profile" object had more parameters added to it that can
override default parameters specified in the "attestation"
and "verification" objects.
* The "store" object was removed altogther as it was never
implemented.
* We now use libjwt to create outgoing Identity headers and to
parse and validate signatures on incoming Identiy headers. Our
previous custom implementation was much of the source of the
interoperability issues.
* General code cleanup and refactor.
* Moved things to better places.
* Separated some of the complex functions to smaller ones.
* Using context objects rather than passing tons of parameters
in function calls.
* Removed some complexity and unneeded encapsuation from the
config objects.
Resolves: #351
Resolves: #46
UserNote: Asterisk's stir-shaken feature has been refactored to
correct interoperability, RFC compliance, and performance issues.
See https://docs.asterisk.org/Deployment/STIR-SHAKEN for more
information.
UpgradeNote: The stir-shaken refactor is a breaking change but since
it's not working now we don't think it matters. The
stir_shaken.conf file has changed significantly which means that
existing ones WILL need to be changed. The stir_shaken.conf.sample
file in configs/samples/ has quite a bit more information. This is
also an ABI breaking change since some of the existing objects
needed to be changed or removed, and new ones added. Additionally,
if res_stir_shaken is enabled in menuselect, you'll need to either
have the development package for libjwt v1.15.3 installed or use
the --with-libjwt-bundled option with ./configure.
2023-10-26 16:27:35 +00:00
|
|
|
DEFINE_SQL_STATEMENT(exists_stmt, "SELECT CAST(COUNT(1) AS INTEGER) AS 'exists' FROM astdb WHERE key=?")
|
2011-07-06 20:58:12 +00:00
|
|
|
DEFINE_SQL_STATEMENT(del_stmt, "DELETE FROM astdb WHERE key=?")
|
2011-11-18 22:20:47 +00:00
|
|
|
DEFINE_SQL_STATEMENT(deltree_stmt, "DELETE FROM astdb WHERE key || '/' LIKE ? || '/' || '%'")
|
2011-07-06 20:58:12 +00:00
|
|
|
DEFINE_SQL_STATEMENT(deltree_all_stmt, "DELETE FROM astdb")
|
2012-01-16 14:31:37 +00:00
|
|
|
DEFINE_SQL_STATEMENT(gettree_stmt, "SELECT key, value FROM astdb WHERE key || '/' LIKE ? || '/' || '%' ORDER BY key")
|
|
|
|
DEFINE_SQL_STATEMENT(gettree_all_stmt, "SELECT key, value FROM astdb ORDER BY key")
|
2012-01-16 17:12:36 +00:00
|
|
|
DEFINE_SQL_STATEMENT(showkey_stmt, "SELECT key, value FROM astdb WHERE key LIKE '%' || '/' || ? ORDER BY key")
|
2011-07-06 20:58:12 +00:00
|
|
|
DEFINE_SQL_STATEMENT(create_astdb_stmt, "CREATE TABLE IF NOT EXISTS astdb(key VARCHAR(256), value VARCHAR(256), PRIMARY KEY(key))")
|
|
|
|
|
2017-12-07 20:19:40 +00:00
|
|
|
/* This query begs an explanation:
|
|
|
|
*
|
2017-12-20 17:14:54 +00:00
|
|
|
* First, the parameter binding syntax used here is slightly different than the other
|
2017-12-07 20:19:40 +00:00
|
|
|
* queries in that we use a numbered parameter so that we can bind once and get the same
|
|
|
|
* value substituted multiple times within the executed query.
|
|
|
|
*
|
|
|
|
* Second, the key comparison is being used to find all keys that are lexicographically
|
|
|
|
* greater than the provided key, but less than the provided key with a high (but
|
|
|
|
* invalid) Unicode codepoint appended to it. This will give us all keys in the database
|
|
|
|
* that have 'key' as a prefix and performs much better than the equivalent "LIKE key ||
|
|
|
|
* '%'" operation.
|
|
|
|
*/
|
|
|
|
DEFINE_SQL_STATEMENT(gettree_prefix_stmt, "SELECT key, value FROM astdb WHERE key > ?1 AND key <= ?1 || X'ffff'")
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int init_stmt(sqlite3_stmt **stmt, const char *sql, size_t len)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
2011-07-07 16:50:54 +00:00
|
|
|
if (sqlite3_prepare(astdb, sql, len, stmt, NULL) != SQLITE_OK) {
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_log(LOG_WARNING, "Couldn't prepare statement '%s': %s\n", sql, sqlite3_errmsg(astdb));
|
|
|
|
ast_mutex_unlock(&dblock);
|
2006-02-09 16:59:50 +00:00
|
|
|
return -1;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
2006-02-09 16:59:50 +00:00
|
|
|
return 0;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2012-11-04 01:19:43 +00:00
|
|
|
/*! \internal
|
|
|
|
* \brief Clean up the prepared SQLite3 statement
|
|
|
|
* \note dblock should already be locked prior to calling this method
|
|
|
|
*/
|
2013-12-19 16:33:09 +00:00
|
|
|
static int clean_stmt(sqlite3_stmt **stmt, const char *sql)
|
2012-11-04 01:19:43 +00:00
|
|
|
{
|
2013-12-19 16:33:09 +00:00
|
|
|
if (sqlite3_finalize(*stmt) != SQLITE_OK) {
|
2012-11-04 01:19:43 +00:00
|
|
|
ast_log(LOG_WARNING, "Couldn't finalize statement '%s': %s\n", sql, sqlite3_errmsg(astdb));
|
2013-12-19 16:33:09 +00:00
|
|
|
*stmt = NULL;
|
2012-11-04 01:19:43 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-12-19 16:33:09 +00:00
|
|
|
*stmt = NULL;
|
2012-11-04 01:19:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \internal
|
|
|
|
* \brief Clean up all prepared SQLite3 statements
|
|
|
|
* \note dblock should already be locked prior to calling this method
|
|
|
|
*/
|
|
|
|
static void clean_statements(void)
|
|
|
|
{
|
2013-12-19 16:33:09 +00:00
|
|
|
clean_stmt(&get_stmt, get_stmt_sql);
|
Stir/Shaken Refactor
Why do we need a refactor?
The original stir/shaken implementation was started over 3 years ago
when little was understood about practical implementation. The
result was an implementation that wouldn't actually interoperate
with any other stir-shaken implementations.
There were also a number of stir-shaken features and RFC
requirements that were never implemented such as TNAuthList
certificate validation, sending Reason headers in SIP responses
when verification failed but we wished to continue the call, and
the ability to send Media Key(mky) grants in the Identity header
when the call involved DTLS.
Finally, there were some performance concerns around outgoing
calls and selection of the correct certificate and private key.
The configuration was keyed by an arbitrary name which meant that
for every outgoing call, we had to scan the entire list of
configured TNs to find the correct cert to use. With only a few
TNs configured, this wasn't an issue but if you have a thousand,
it could be.
What's changed?
* Configuration objects have been refactored to be clearer about
their uses and to fix issues.
* The "general" object was renamed to "verification" since it
contains parameters specific to the incoming verification
process. It also never handled ca_path and crl_path
correctly.
* A new "attestation" object was added that controls the
outgoing attestation process. It sets default certificates,
keys, etc.
* The "certificate" object was renamed to "tn" and had it's key
change to telephone number since outgoing call attestation
needs to look up certificates by telephone number.
* The "profile" object had more parameters added to it that can
override default parameters specified in the "attestation"
and "verification" objects.
* The "store" object was removed altogther as it was never
implemented.
* We now use libjwt to create outgoing Identity headers and to
parse and validate signatures on incoming Identiy headers. Our
previous custom implementation was much of the source of the
interoperability issues.
* General code cleanup and refactor.
* Moved things to better places.
* Separated some of the complex functions to smaller ones.
* Using context objects rather than passing tons of parameters
in function calls.
* Removed some complexity and unneeded encapsuation from the
config objects.
Resolves: #351
Resolves: #46
UserNote: Asterisk's stir-shaken feature has been refactored to
correct interoperability, RFC compliance, and performance issues.
See https://docs.asterisk.org/Deployment/STIR-SHAKEN for more
information.
UpgradeNote: The stir-shaken refactor is a breaking change but since
it's not working now we don't think it matters. The
stir_shaken.conf file has changed significantly which means that
existing ones WILL need to be changed. The stir_shaken.conf.sample
file in configs/samples/ has quite a bit more information. This is
also an ABI breaking change since some of the existing objects
needed to be changed or removed, and new ones added. Additionally,
if res_stir_shaken is enabled in menuselect, you'll need to either
have the development package for libjwt v1.15.3 installed or use
the --with-libjwt-bundled option with ./configure.
2023-10-26 16:27:35 +00:00
|
|
|
clean_stmt(&exists_stmt, exists_stmt_sql);
|
2013-12-19 16:33:09 +00:00
|
|
|
clean_stmt(&del_stmt, del_stmt_sql);
|
|
|
|
clean_stmt(&deltree_stmt, deltree_stmt_sql);
|
|
|
|
clean_stmt(&deltree_all_stmt, deltree_all_stmt_sql);
|
|
|
|
clean_stmt(&gettree_stmt, gettree_stmt_sql);
|
|
|
|
clean_stmt(&gettree_all_stmt, gettree_all_stmt_sql);
|
2017-12-07 20:19:40 +00:00
|
|
|
clean_stmt(&gettree_prefix_stmt, gettree_prefix_stmt_sql);
|
2013-12-19 16:33:09 +00:00
|
|
|
clean_stmt(&showkey_stmt, showkey_stmt_sql);
|
|
|
|
clean_stmt(&put_stmt, put_stmt_sql);
|
|
|
|
clean_stmt(&create_astdb_stmt, create_astdb_stmt_sql);
|
2012-11-04 01:19:43 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int init_statements(void)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2018-10-18 17:32:26 +00:00
|
|
|
/* Don't initialize create_astdb_statement here as the astdb table needs to exist
|
|
|
|
* brefore these statements can be initialized */
|
2011-07-06 20:58:12 +00:00
|
|
|
return init_stmt(&get_stmt, get_stmt_sql, sizeof(get_stmt_sql))
|
Stir/Shaken Refactor
Why do we need a refactor?
The original stir/shaken implementation was started over 3 years ago
when little was understood about practical implementation. The
result was an implementation that wouldn't actually interoperate
with any other stir-shaken implementations.
There were also a number of stir-shaken features and RFC
requirements that were never implemented such as TNAuthList
certificate validation, sending Reason headers in SIP responses
when verification failed but we wished to continue the call, and
the ability to send Media Key(mky) grants in the Identity header
when the call involved DTLS.
Finally, there were some performance concerns around outgoing
calls and selection of the correct certificate and private key.
The configuration was keyed by an arbitrary name which meant that
for every outgoing call, we had to scan the entire list of
configured TNs to find the correct cert to use. With only a few
TNs configured, this wasn't an issue but if you have a thousand,
it could be.
What's changed?
* Configuration objects have been refactored to be clearer about
their uses and to fix issues.
* The "general" object was renamed to "verification" since it
contains parameters specific to the incoming verification
process. It also never handled ca_path and crl_path
correctly.
* A new "attestation" object was added that controls the
outgoing attestation process. It sets default certificates,
keys, etc.
* The "certificate" object was renamed to "tn" and had it's key
change to telephone number since outgoing call attestation
needs to look up certificates by telephone number.
* The "profile" object had more parameters added to it that can
override default parameters specified in the "attestation"
and "verification" objects.
* The "store" object was removed altogther as it was never
implemented.
* We now use libjwt to create outgoing Identity headers and to
parse and validate signatures on incoming Identiy headers. Our
previous custom implementation was much of the source of the
interoperability issues.
* General code cleanup and refactor.
* Moved things to better places.
* Separated some of the complex functions to smaller ones.
* Using context objects rather than passing tons of parameters
in function calls.
* Removed some complexity and unneeded encapsuation from the
config objects.
Resolves: #351
Resolves: #46
UserNote: Asterisk's stir-shaken feature has been refactored to
correct interoperability, RFC compliance, and performance issues.
See https://docs.asterisk.org/Deployment/STIR-SHAKEN for more
information.
UpgradeNote: The stir-shaken refactor is a breaking change but since
it's not working now we don't think it matters. The
stir_shaken.conf file has changed significantly which means that
existing ones WILL need to be changed. The stir_shaken.conf.sample
file in configs/samples/ has quite a bit more information. This is
also an ABI breaking change since some of the existing objects
needed to be changed or removed, and new ones added. Additionally,
if res_stir_shaken is enabled in menuselect, you'll need to either
have the development package for libjwt v1.15.3 installed or use
the --with-libjwt-bundled option with ./configure.
2023-10-26 16:27:35 +00:00
|
|
|
|| init_stmt(&exists_stmt, exists_stmt_sql, sizeof(exists_stmt_sql))
|
2011-07-06 20:58:12 +00:00
|
|
|
|| init_stmt(&del_stmt, del_stmt_sql, sizeof(del_stmt_sql))
|
|
|
|
|| init_stmt(&deltree_stmt, deltree_stmt_sql, sizeof(deltree_stmt_sql))
|
|
|
|
|| init_stmt(&deltree_all_stmt, deltree_all_stmt_sql, sizeof(deltree_all_stmt_sql))
|
|
|
|
|| init_stmt(&gettree_stmt, gettree_stmt_sql, sizeof(gettree_stmt_sql))
|
|
|
|
|| init_stmt(&gettree_all_stmt, gettree_all_stmt_sql, sizeof(gettree_all_stmt_sql))
|
2017-12-07 20:19:40 +00:00
|
|
|
|| init_stmt(&gettree_prefix_stmt, gettree_prefix_stmt_sql, sizeof(gettree_prefix_stmt_sql))
|
2011-07-06 20:58:12 +00:00
|
|
|
|| init_stmt(&showkey_stmt, showkey_stmt_sql, sizeof(showkey_stmt_sql))
|
|
|
|
|| init_stmt(&put_stmt, put_stmt_sql, sizeof(put_stmt_sql));
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int convert_bdb_to_sqlite3(void)
|
2004-12-22 01:07:02 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
char *cmd;
|
|
|
|
int res;
|
|
|
|
|
2017-11-02 23:40:20 +00:00
|
|
|
res = ast_asprintf(&cmd, "%s/astdb2sqlite3 '%s'\n", ast_config_AST_SBIN_DIR, ast_config_AST_DB);
|
|
|
|
if (0 <= res) {
|
|
|
|
res = ast_safe_system(cmd);
|
|
|
|
ast_free(cmd);
|
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
|
|
|
|
return res;
|
2004-12-22 01:07:02 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int db_create_astdb(void)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
int res = 0;
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (!create_astdb_stmt) {
|
|
|
|
init_stmt(&create_astdb_stmt, create_astdb_stmt_sql, sizeof(create_astdb_stmt_sql));
|
2011-06-10 15:30:50 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (sqlite3_step(create_astdb_stmt) != SQLITE_DONE) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't create astdb table: %s\n", sqlite3_errmsg(astdb));
|
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
sqlite3_reset(create_astdb_stmt);
|
|
|
|
db_sync();
|
|
|
|
ast_mutex_unlock(&dblock);
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
return res;
|
2011-06-10 15:30:50 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int db_open(void)
|
2011-06-10 15:30:50 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
char *dbname;
|
|
|
|
struct stat dont_care;
|
|
|
|
|
2012-07-31 20:21:43 +00:00
|
|
|
if (!(dbname = ast_alloca(strlen(ast_config_AST_DB) + sizeof(".sqlite3")))) {
|
2011-07-06 20:58:12 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcpy(dbname, ast_config_AST_DB);
|
|
|
|
strcat(dbname, ".sqlite3");
|
|
|
|
|
|
|
|
if (stat(dbname, &dont_care) && !stat(ast_config_AST_DB, &dont_care)) {
|
|
|
|
if (convert_bdb_to_sqlite3()) {
|
|
|
|
ast_log(LOG_ERROR, "*** Database conversion failed!\n");
|
|
|
|
ast_log(LOG_ERROR, "*** Asterisk now uses SQLite3 for its internal\n");
|
|
|
|
ast_log(LOG_ERROR, "*** database. Conversion from the old astdb\n");
|
|
|
|
ast_log(LOG_ERROR, "*** failed. Most likely the astdb2sqlite3 utility\n");
|
|
|
|
ast_log(LOG_ERROR, "*** was not selected for build. To convert the\n");
|
|
|
|
ast_log(LOG_ERROR, "*** old astdb, please delete '%s'\n", dbname);
|
|
|
|
ast_log(LOG_ERROR, "*** and re-run 'make menuselect' and select astdb2sqlite3\n");
|
|
|
|
ast_log(LOG_ERROR, "*** in the Utilities section, then 'make && make install'.\n");
|
2011-10-10 22:58:10 +00:00
|
|
|
ast_log(LOG_ERROR, "*** It is also imperative that the user under which\n");
|
|
|
|
ast_log(LOG_ERROR, "*** Asterisk runs have write permission to the directory\n");
|
|
|
|
ast_log(LOG_ERROR, "*** where the database resides.\n");
|
2011-07-06 20:58:12 +00:00
|
|
|
sleep(5);
|
|
|
|
} else {
|
|
|
|
ast_log(LOG_NOTICE, "Database conversion succeeded!\n");
|
|
|
|
}
|
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
|
|
|
ast_mutex_lock(&dblock);
|
2011-07-07 16:50:54 +00:00
|
|
|
if (sqlite3_open(dbname, &astdb) != SQLITE_OK) {
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", dbname, sqlite3_errmsg(astdb));
|
|
|
|
sqlite3_close(astdb);
|
2011-06-10 15:30:50 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-06-16 09:04:05 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int db_init(void)
|
|
|
|
{
|
|
|
|
if (astdb) {
|
2011-06-10 15:30:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (db_open() || db_create_astdb() || init_statements()) {
|
|
|
|
return -1;
|
2011-06-10 15:30:50 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We purposely don't lock around the sqlite3 call because the transaction
|
|
|
|
* calls will be called with the database lock held. For any other use, make
|
|
|
|
* sure to take the dblock yourself. */
|
|
|
|
static int db_execute_sql(const char *sql, int (*callback)(void *, int, char **, char **), void *arg)
|
|
|
|
{
|
|
|
|
char *errmsg = NULL;
|
|
|
|
int res =0;
|
|
|
|
|
2014-06-16 09:04:05 +00:00
|
|
|
if (sqlite3_exec(astdb, sql, callback, arg, &errmsg) != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Error executing SQL (%s): %s\n", sql, errmsg);
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_free(errmsg);
|
|
|
|
res = -1;
|
2011-06-10 15:30:50 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
return res;
|
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int ast_db_begin_transaction(void)
|
|
|
|
{
|
|
|
|
return db_execute_sql("BEGIN TRANSACTION", NULL, NULL);
|
2011-06-10 15:30:50 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int ast_db_commit_transaction(void)
|
2011-06-10 15:30:50 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
return db_execute_sql("COMMIT", NULL, NULL);
|
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int ast_db_rollback_transaction(void)
|
|
|
|
{
|
|
|
|
return db_execute_sql("ROLLBACK", NULL, NULL);
|
2011-06-10 15:30:50 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
int ast_db_put(const char *family, const char *key, const char *value)
|
2011-06-10 15:30:50 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
char fullkey[MAX_DB_FIELD];
|
|
|
|
size_t fullkey_len;
|
|
|
|
int res = 0;
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) - 1) {
|
|
|
|
ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
|
2003-01-30 15:03:20 +00:00
|
|
|
return -1;
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
|
|
|
|
|
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (sqlite3_bind_text(put_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
|
|
|
|
res = -1;
|
|
|
|
} else if (sqlite3_bind_text(put_stmt, 2, value, -1, SQLITE_STATIC) != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't bind value to stmt: %s\n", sqlite3_errmsg(astdb));
|
|
|
|
res = -1;
|
|
|
|
} else if (sqlite3_step(put_stmt) != SQLITE_DONE) {
|
2018-10-18 17:32:26 +00:00
|
|
|
ast_log(LOG_WARNING, "Couldn't execute statement: %s\n", sqlite3_errmsg(astdb));
|
2011-07-06 20:58:12 +00:00
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_reset(put_stmt);
|
|
|
|
db_sync();
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
|
|
|
return res;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2012-10-01 20:36:25 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Get key value specified by family/key.
|
|
|
|
*
|
|
|
|
* Gets the value associated with the specified \a family and \a key, and
|
|
|
|
* stores it, either into the fixed sized buffer specified by \a buffer
|
|
|
|
* and \a bufferlen, or as a heap allocated string if \a bufferlen is -1.
|
|
|
|
*
|
|
|
|
* \note If \a bufferlen is -1, \a buffer points to heap allocated memory
|
|
|
|
* and must be freed by calling ast_free().
|
|
|
|
*
|
|
|
|
* \retval -1 An error occurred
|
|
|
|
* \retval 0 Success
|
|
|
|
*/
|
|
|
|
static int db_get_common(const char *family, const char *key, char **buffer, int bufferlen)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
const unsigned char *result;
|
2011-06-10 15:30:50 +00:00
|
|
|
char fullkey[MAX_DB_FIELD];
|
2011-07-06 20:58:12 +00:00
|
|
|
size_t fullkey_len;
|
|
|
|
int res = 0;
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) - 1) {
|
|
|
|
ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
|
2003-01-30 15:03:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
|
|
|
|
|
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (sqlite3_bind_text(get_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
|
|
|
|
res = -1;
|
|
|
|
} else if (sqlite3_step(get_stmt) != SQLITE_ROW) {
|
|
|
|
ast_debug(1, "Unable to find key '%s' in family '%s'\n", key, family);
|
|
|
|
res = -1;
|
|
|
|
} else if (!(result = sqlite3_column_text(get_stmt, 0))) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't get value\n");
|
|
|
|
res = -1;
|
|
|
|
} else {
|
2012-10-01 20:36:25 +00:00
|
|
|
const char *value = (const char *) result;
|
|
|
|
|
|
|
|
if (bufferlen == -1) {
|
|
|
|
*buffer = ast_strdup(value);
|
|
|
|
} else {
|
|
|
|
ast_copy_string(*buffer, value, bufferlen);
|
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
}
|
|
|
|
sqlite3_reset(get_stmt);
|
2003-08-13 15:25:16 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2003-01-30 15:03:20 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2012-10-01 20:36:25 +00:00
|
|
|
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
|
|
|
|
{
|
|
|
|
ast_assert(value != NULL);
|
|
|
|
|
|
|
|
/* Make sure we initialize */
|
|
|
|
value[0] = 0;
|
|
|
|
|
|
|
|
return db_get_common(family, key, &value, valuelen);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ast_db_get_allocated(const char *family, const char *key, char **out)
|
|
|
|
{
|
|
|
|
*out = NULL;
|
|
|
|
|
|
|
|
return db_get_common(family, key, out, -1);
|
|
|
|
}
|
|
|
|
|
Stir/Shaken Refactor
Why do we need a refactor?
The original stir/shaken implementation was started over 3 years ago
when little was understood about practical implementation. The
result was an implementation that wouldn't actually interoperate
with any other stir-shaken implementations.
There were also a number of stir-shaken features and RFC
requirements that were never implemented such as TNAuthList
certificate validation, sending Reason headers in SIP responses
when verification failed but we wished to continue the call, and
the ability to send Media Key(mky) grants in the Identity header
when the call involved DTLS.
Finally, there were some performance concerns around outgoing
calls and selection of the correct certificate and private key.
The configuration was keyed by an arbitrary name which meant that
for every outgoing call, we had to scan the entire list of
configured TNs to find the correct cert to use. With only a few
TNs configured, this wasn't an issue but if you have a thousand,
it could be.
What's changed?
* Configuration objects have been refactored to be clearer about
their uses and to fix issues.
* The "general" object was renamed to "verification" since it
contains parameters specific to the incoming verification
process. It also never handled ca_path and crl_path
correctly.
* A new "attestation" object was added that controls the
outgoing attestation process. It sets default certificates,
keys, etc.
* The "certificate" object was renamed to "tn" and had it's key
change to telephone number since outgoing call attestation
needs to look up certificates by telephone number.
* The "profile" object had more parameters added to it that can
override default parameters specified in the "attestation"
and "verification" objects.
* The "store" object was removed altogther as it was never
implemented.
* We now use libjwt to create outgoing Identity headers and to
parse and validate signatures on incoming Identiy headers. Our
previous custom implementation was much of the source of the
interoperability issues.
* General code cleanup and refactor.
* Moved things to better places.
* Separated some of the complex functions to smaller ones.
* Using context objects rather than passing tons of parameters
in function calls.
* Removed some complexity and unneeded encapsuation from the
config objects.
Resolves: #351
Resolves: #46
UserNote: Asterisk's stir-shaken feature has been refactored to
correct interoperability, RFC compliance, and performance issues.
See https://docs.asterisk.org/Deployment/STIR-SHAKEN for more
information.
UpgradeNote: The stir-shaken refactor is a breaking change but since
it's not working now we don't think it matters. The
stir_shaken.conf file has changed significantly which means that
existing ones WILL need to be changed. The stir_shaken.conf.sample
file in configs/samples/ has quite a bit more information. This is
also an ABI breaking change since some of the existing objects
needed to be changed or removed, and new ones added. Additionally,
if res_stir_shaken is enabled in menuselect, you'll need to either
have the development package for libjwt v1.15.3 installed or use
the --with-libjwt-bundled option with ./configure.
2023-10-26 16:27:35 +00:00
|
|
|
int ast_db_exists(const char *family, const char *key)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
char fullkey[MAX_DB_FIELD];
|
|
|
|
size_t fullkey_len;
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
|
|
|
|
if (fullkey_len >= sizeof(fullkey)) {
|
|
|
|
ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
res = sqlite3_bind_text(exists_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC);
|
|
|
|
if (res != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %d:%s\n", res, sqlite3_errmsg(astdb));
|
|
|
|
res = 0;
|
|
|
|
} else if (sqlite3_step(exists_stmt) != SQLITE_ROW) {
|
|
|
|
res = 0;
|
|
|
|
} else if (!(result = sqlite3_column_int(exists_stmt, 0))) {
|
|
|
|
res = 0;
|
|
|
|
} else {
|
|
|
|
res = result;
|
|
|
|
}
|
|
|
|
sqlite3_reset(exists_stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
int ast_db_del(const char *family, const char *key)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
char fullkey[MAX_DB_FIELD];
|
|
|
|
size_t fullkey_len;
|
|
|
|
int res = 0;
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) - 1) {
|
|
|
|
ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
|
2003-01-30 15:03:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
|
2009-03-17 05:51:54 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (sqlite3_bind_text(del_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
|
|
|
|
res = -1;
|
|
|
|
} else if (sqlite3_step(del_stmt) != SQLITE_DONE) {
|
|
|
|
ast_debug(1, "Unable to find key '%s' in family '%s'\n", key, family);
|
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
sqlite3_reset(del_stmt);
|
|
|
|
db_sync();
|
|
|
|
ast_mutex_unlock(&dblock);
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-04-01 19:49:31 +00:00
|
|
|
int ast_db_del2(const char *family, const char *key)
|
|
|
|
{
|
|
|
|
char fullkey[MAX_DB_FIELD];
|
|
|
|
char tmp[1];
|
|
|
|
size_t fullkey_len;
|
|
|
|
int mres, res = 0;
|
|
|
|
|
|
|
|
if (strlen(family) + strlen(key) + 2 > sizeof(fullkey) - 1) {
|
|
|
|
ast_log(LOG_WARNING, "Family and key length must be less than %zu bytes\n", sizeof(fullkey) - 3);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fullkey_len = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, key);
|
|
|
|
|
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (ast_db_get(family, key, tmp, sizeof(tmp))) {
|
|
|
|
ast_log(LOG_WARNING, "AstDB key %s does not exist\n", fullkey);
|
|
|
|
res = -1;
|
|
|
|
} else if (sqlite3_bind_text(del_stmt, 1, fullkey, fullkey_len, SQLITE_STATIC) != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't bind key to stmt: %s\n", sqlite3_errmsg(astdb));
|
|
|
|
res = -1;
|
|
|
|
} else if ((mres = sqlite3_step(del_stmt) != SQLITE_DONE)) {
|
|
|
|
ast_log(LOG_WARNING, "AstDB error (%s): %s\n", fullkey, sqlite3_errstr(mres));
|
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
sqlite3_reset(del_stmt);
|
|
|
|
db_sync();
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2011-11-19 15:11:45 +00:00
|
|
|
int ast_db_deltree(const char *family, const char *keytree)
|
2011-07-06 20:58:12 +00:00
|
|
|
{
|
|
|
|
sqlite3_stmt *stmt = deltree_stmt;
|
|
|
|
char prefix[MAX_DB_FIELD];
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(family)) {
|
2011-11-19 15:11:45 +00:00
|
|
|
if (!ast_strlen_zero(keytree)) {
|
2011-07-06 20:58:12 +00:00
|
|
|
/* Family and key tree */
|
2011-11-19 15:11:45 +00:00
|
|
|
snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
|
2003-01-30 15:03:20 +00:00
|
|
|
} else {
|
2011-07-06 20:58:12 +00:00
|
|
|
/* Family only */
|
|
|
|
snprintf(prefix, sizeof(prefix), "/%s", family);
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
} else {
|
|
|
|
prefix[0] = '\0';
|
|
|
|
stmt = deltree_all_stmt;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
2009-03-17 05:51:54 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (!ast_strlen_zero(prefix) && (sqlite3_bind_text(stmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK)) {
|
2022-06-01 00:49:12 +00:00
|
|
|
ast_log(LOG_WARNING, "Couldn't bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
|
2011-07-06 20:58:12 +00:00
|
|
|
res = -1;
|
|
|
|
} else if (sqlite3_step(stmt) != SQLITE_DONE) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't execute stmt: %s\n", sqlite3_errmsg(astdb));
|
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
res = sqlite3_changes(astdb);
|
|
|
|
sqlite3_reset(stmt);
|
|
|
|
db_sync();
|
2009-03-17 05:51:54 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
2003-01-30 15:03:20 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:19:40 +00:00
|
|
|
static struct ast_db_entry *db_gettree_common(sqlite3_stmt *stmt)
|
|
|
|
{
|
|
|
|
struct ast_db_entry *head = NULL, *prev = NULL, *cur;
|
|
|
|
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
|
|
const char *key, *value;
|
|
|
|
size_t key_len, value_len;
|
|
|
|
|
|
|
|
key = (const char *) sqlite3_column_text(stmt, 0);
|
|
|
|
value = (const char *) sqlite3_column_text(stmt, 1);
|
|
|
|
|
|
|
|
if (!key || !value) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
key_len = strlen(key);
|
|
|
|
value_len = strlen(value);
|
|
|
|
|
|
|
|
cur = ast_malloc(sizeof(*cur) + key_len + value_len + 2);
|
|
|
|
if (!cur) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cur->next = NULL;
|
|
|
|
cur->key = cur->data + value_len + 1;
|
|
|
|
memcpy(cur->data, value, value_len + 1);
|
|
|
|
memcpy(cur->key, key, key_len + 1);
|
|
|
|
|
|
|
|
if (prev) {
|
|
|
|
prev->next = cur;
|
|
|
|
} else {
|
|
|
|
head = cur;
|
|
|
|
}
|
|
|
|
prev = cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
return head;
|
|
|
|
}
|
|
|
|
|
2011-11-19 15:11:45 +00:00
|
|
|
struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
char prefix[MAX_DB_FIELD];
|
|
|
|
sqlite3_stmt *stmt = gettree_stmt;
|
2017-12-07 20:19:40 +00:00
|
|
|
size_t res = 0;
|
|
|
|
struct ast_db_entry *ret;
|
2011-07-06 20:58:12 +00:00
|
|
|
|
|
|
|
if (!ast_strlen_zero(family)) {
|
2011-11-19 15:11:45 +00:00
|
|
|
if (!ast_strlen_zero(keytree)) {
|
2011-07-06 20:58:12 +00:00
|
|
|
/* Family and key tree */
|
2017-12-07 20:19:40 +00:00
|
|
|
res = snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
|
2011-07-06 20:58:12 +00:00
|
|
|
} else {
|
|
|
|
/* Family only */
|
2017-12-07 20:19:40 +00:00
|
|
|
res = snprintf(prefix, sizeof(prefix), "/%s", family);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res >= sizeof(prefix)) {
|
|
|
|
ast_log(LOG_WARNING, "Requested prefix is too long: %s\n", keytree);
|
|
|
|
return NULL;
|
2011-07-06 20:58:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prefix[0] = '\0';
|
|
|
|
stmt = gettree_all_stmt;
|
|
|
|
}
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2003-08-13 15:25:16 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
2017-12-07 20:19:40 +00:00
|
|
|
if (res && (sqlite3_bind_text(stmt, 1, prefix, res, SQLITE_STATIC) != SQLITE_OK)) {
|
|
|
|
ast_log(LOG_WARNING, "Could not bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_reset(stmt);
|
2003-08-13 15:25:16 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
2011-07-06 20:58:12 +00:00
|
|
|
return NULL;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
|
2017-12-07 20:19:40 +00:00
|
|
|
ret = db_gettree_common(stmt);
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_reset(stmt);
|
2003-08-13 15:25:16 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:19:40 +00:00
|
|
|
struct ast_db_entry *ast_db_gettree_by_prefix(const char *family, const char *key_prefix)
|
|
|
|
{
|
|
|
|
char prefix[MAX_DB_FIELD];
|
|
|
|
size_t res;
|
|
|
|
struct ast_db_entry *ret;
|
|
|
|
|
|
|
|
res = snprintf(prefix, sizeof(prefix), "/%s/%s", family, key_prefix);
|
|
|
|
if (res >= sizeof(prefix)) {
|
|
|
|
ast_log(LOG_WARNING, "Requested key prefix is too long: %s\n", key_prefix);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (sqlite3_bind_text(gettree_prefix_stmt, 1, prefix, res, SQLITE_STATIC) != SQLITE_OK) {
|
|
|
|
ast_log(LOG_WARNING, "Could not bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
|
|
|
|
sqlite3_reset(gettree_prefix_stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = db_gettree_common(gettree_prefix_stmt);
|
|
|
|
sqlite3_reset(gettree_prefix_stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
void ast_db_freetree(struct ast_db_entry *dbe)
|
|
|
|
{
|
|
|
|
struct ast_db_entry *last;
|
|
|
|
while (dbe) {
|
|
|
|
last = dbe;
|
|
|
|
dbe = dbe->next;
|
|
|
|
ast_free(last);
|
2006-10-03 15:53:07 +00:00
|
|
|
}
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_database_put(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
|
|
|
int res;
|
2007-10-11 19:03:06 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "database put";
|
|
|
|
e->usage =
|
|
|
|
"Usage: database put <family> <key> <value>\n"
|
|
|
|
" Adds or updates an entry in the Asterisk database for\n"
|
|
|
|
" a given family, key, and value.\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc != 5)
|
|
|
|
return CLI_SHOWUSAGE;
|
|
|
|
res = ast_db_put(a->argv[2], a->argv[3], a->argv[4]);
|
2005-07-20 00:09:39 +00:00
|
|
|
if (res) {
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "Failed to update entry\n");
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "Updated database successfully\n");
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SUCCESS;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_database_get(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
|
|
|
int res;
|
2011-06-10 15:30:50 +00:00
|
|
|
char tmp[MAX_DB_FIELD];
|
2007-10-11 19:03:06 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "database get";
|
|
|
|
e->usage =
|
|
|
|
"Usage: database get <family> <key>\n"
|
|
|
|
" Retrieves an entry in the Asterisk database for a given\n"
|
|
|
|
" family and key.\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc != 4)
|
|
|
|
return CLI_SHOWUSAGE;
|
|
|
|
res = ast_db_get(a->argv[2], a->argv[3], tmp, sizeof(tmp));
|
2005-07-20 00:09:39 +00:00
|
|
|
if (res) {
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "Database entry not found.\n");
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "Value: %s\n", tmp);
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SUCCESS;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_database_del(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
|
|
|
int res;
|
2007-10-11 19:03:06 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "database del";
|
|
|
|
e->usage =
|
|
|
|
"Usage: database del <family> <key>\n"
|
|
|
|
" Deletes an entry in the Asterisk database for a given\n"
|
|
|
|
" family and key.\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc != 4)
|
|
|
|
return CLI_SHOWUSAGE;
|
2022-04-01 19:49:31 +00:00
|
|
|
res = ast_db_del2(a->argv[2], a->argv[3]);
|
2005-07-20 00:09:39 +00:00
|
|
|
if (res) {
|
2022-04-01 19:49:31 +00:00
|
|
|
ast_cli(a->fd, "Database entry could not be removed.\n");
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "Database entry removed.\n");
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SUCCESS;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_database_deltree(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2012-10-04 15:48:24 +00:00
|
|
|
int num_deleted;
|
2007-10-11 19:03:06 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "database deltree";
|
|
|
|
e->usage =
|
2011-11-19 15:11:45 +00:00
|
|
|
"Usage: database deltree <family> [keytree]\n"
|
|
|
|
" OR: database deltree <family>[/keytree]\n"
|
|
|
|
" Deletes a family or specific keytree within a family\n"
|
|
|
|
" in the Asterisk database. The two arguments may be\n"
|
|
|
|
" separated by either a space or a slash.\n";
|
2007-10-11 19:03:06 +00:00
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((a->argc < 3) || (a->argc > 4))
|
|
|
|
return CLI_SHOWUSAGE;
|
|
|
|
if (a->argc == 4) {
|
2012-10-04 15:48:24 +00:00
|
|
|
num_deleted = ast_db_deltree(a->argv[2], a->argv[3]);
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2012-10-04 15:48:24 +00:00
|
|
|
num_deleted = ast_db_deltree(a->argv[2], NULL);
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2012-10-04 15:48:24 +00:00
|
|
|
if (num_deleted < 0) {
|
|
|
|
ast_cli(a->fd, "Database unavailable.\n");
|
|
|
|
} else if (num_deleted == 0) {
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "Database entries do not exist.\n");
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2012-10-04 15:48:24 +00:00
|
|
|
ast_cli(a->fd, "%d database entries removed.\n",num_deleted);
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SUCCESS;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2003-01-30 15:03:20 +00:00
|
|
|
{
|
2011-06-10 15:30:50 +00:00
|
|
|
char prefix[MAX_DB_FIELD];
|
2007-09-10 16:24:29 +00:00
|
|
|
int counter = 0;
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_stmt *stmt = gettree_stmt;
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "database show";
|
|
|
|
e->usage =
|
2011-11-19 15:11:45 +00:00
|
|
|
"Usage: database show [family [keytree]]\n"
|
|
|
|
" OR: database show [family[/keytree]]\n"
|
2007-10-11 19:03:06 +00:00
|
|
|
" Shows Asterisk database contents, optionally restricted\n"
|
2011-11-19 15:11:45 +00:00
|
|
|
" to a given family, or family and keytree. The two arguments\n"
|
|
|
|
" may be separated either by a space or by a slash.\n";
|
2007-10-11 19:03:06 +00:00
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a->argc == 4) {
|
2003-01-30 15:03:20 +00:00
|
|
|
/* Family and key tree */
|
2007-10-11 19:03:06 +00:00
|
|
|
snprintf(prefix, sizeof(prefix), "/%s/%s", a->argv[2], a->argv[3]);
|
|
|
|
} else if (a->argc == 3) {
|
2003-01-30 15:03:20 +00:00
|
|
|
/* Family only */
|
2007-10-11 19:03:06 +00:00
|
|
|
snprintf(prefix, sizeof(prefix), "/%s", a->argv[2]);
|
|
|
|
} else if (a->argc == 2) {
|
2003-01-30 15:03:20 +00:00
|
|
|
/* Neither */
|
2004-07-09 10:08:09 +00:00
|
|
|
prefix[0] = '\0';
|
2011-07-06 20:58:12 +00:00
|
|
|
stmt = gettree_all_stmt;
|
|
|
|
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SHOWUSAGE;
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (!ast_strlen_zero(prefix) && (sqlite3_bind_text(stmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK)) {
|
2022-06-01 00:49:12 +00:00
|
|
|
ast_log(LOG_WARNING, "Couldn't bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_reset(stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
return NULL;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
|
|
const char *key_s, *value_s;
|
|
|
|
if (!(key_s = (const char *) sqlite3_column_text(stmt, 0))) {
|
|
|
|
ast_log(LOG_WARNING, "Skipping invalid key!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!(value_s = (const char *) sqlite3_column_text(stmt, 1))) {
|
|
|
|
ast_log(LOG_WARNING, "Skipping invalid value!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++counter;
|
2011-06-10 15:30:50 +00:00
|
|
|
ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_reset(stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
|
|
|
ast_cli(a->fd, "%d results found.\n", counter);
|
|
|
|
return CLI_SUCCESS;
|
2003-01-30 15:03:20 +00:00
|
|
|
}
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2004-12-22 01:07:02 +00:00
|
|
|
{
|
2007-03-21 02:46:02 +00:00
|
|
|
int counter = 0;
|
2004-12-22 01:07:02 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
2007-11-03 04:55:06 +00:00
|
|
|
e->command = "database showkey";
|
2007-10-11 19:03:06 +00:00
|
|
|
e->usage =
|
2011-11-19 15:11:45 +00:00
|
|
|
"Usage: database showkey <keytree>\n"
|
2007-10-11 19:03:06 +00:00
|
|
|
" Shows Asterisk database contents, restricted to a given key.\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (a->argc != 3) {
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SHOWUSAGE;
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (!ast_strlen_zero(a->argv[2]) && (sqlite3_bind_text(showkey_stmt, 1, a->argv[2], -1, SQLITE_STATIC) != SQLITE_OK)) {
|
2022-06-01 00:49:12 +00:00
|
|
|
ast_log(LOG_WARNING, "Couldn't bind %s to stmt: %s\n", a->argv[2], sqlite3_errmsg(astdb));
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_reset(showkey_stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (sqlite3_step(showkey_stmt) == SQLITE_ROW) {
|
|
|
|
const char *key_s, *value_s;
|
|
|
|
if (!(key_s = (const char *) sqlite3_column_text(showkey_stmt, 0))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!(value_s = (const char *) sqlite3_column_text(showkey_stmt, 1))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++counter;
|
|
|
|
ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s);
|
2004-12-22 01:07:02 +00:00
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
sqlite3_reset(showkey_stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "%d results found.\n", counter);
|
2011-06-10 15:30:50 +00:00
|
|
|
return CLI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static int display_results(void *arg, int columns, char **values, char **colnames)
|
2011-06-10 15:30:50 +00:00
|
|
|
{
|
2011-07-06 20:58:12 +00:00
|
|
|
struct ast_cli_args *a = arg;
|
|
|
|
size_t x;
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
for (x = 0; x < columns; x++) {
|
|
|
|
ast_cli(a->fd, "%-5s: %-50s\n", colnames[x], values[x]);
|
2011-06-10 15:30:50 +00:00
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_cli(a->fd, "\n");
|
2011-06-10 15:30:50 +00:00
|
|
|
|
|
|
|
return 0;
|
2004-12-22 01:07:02 +00:00
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
static char *handle_cli_database_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2003-04-13 04:17:45 +00:00
|
|
|
{
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "database query";
|
|
|
|
e->usage =
|
|
|
|
"Usage: database query \"<SQL Statement>\"\n"
|
|
|
|
" Run a user-specified SQL query on the database. Be careful.\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (a->argc != 3) {
|
|
|
|
return CLI_SHOWUSAGE;
|
2003-04-13 04:17:45 +00:00
|
|
|
}
|
2011-06-10 15:30:50 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
db_execute_sql(a->argv[2], display_results, a);
|
|
|
|
db_sync(); /* Go ahead and sync the db in case they write */
|
|
|
|
ast_mutex_unlock(&dblock);
|
2003-04-13 04:17:45 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
return CLI_SUCCESS;
|
2003-04-13 04:17:45 +00:00
|
|
|
}
|
|
|
|
|
2009-06-15 17:34:30 +00:00
|
|
|
static struct ast_cli_entry cli_database[] = {
|
2007-10-22 20:05:18 +00:00
|
|
|
AST_CLI_DEFINE(handle_cli_database_show, "Shows database contents"),
|
|
|
|
AST_CLI_DEFINE(handle_cli_database_showkey, "Shows database contents"),
|
|
|
|
AST_CLI_DEFINE(handle_cli_database_get, "Gets database value"),
|
|
|
|
AST_CLI_DEFINE(handle_cli_database_put, "Adds/updates database value"),
|
|
|
|
AST_CLI_DEFINE(handle_cli_database_del, "Removes database key/value"),
|
2011-11-19 15:11:45 +00:00
|
|
|
AST_CLI_DEFINE(handle_cli_database_deltree, "Removes database keytree/values"),
|
2011-07-06 20:58:12 +00:00
|
|
|
AST_CLI_DEFINE(handle_cli_database_query, "Run a user-specified query on the astdb"),
|
2006-09-18 19:54:18 +00:00
|
|
|
};
|
2003-01-30 15:03:20 +00:00
|
|
|
|
2007-01-05 22:43:18 +00:00
|
|
|
static int manager_dbput(struct mansession *s, const struct message *m)
|
2005-01-29 23:33:14 +00:00
|
|
|
{
|
2007-01-05 22:43:18 +00:00
|
|
|
const char *family = astman_get_header(m, "Family");
|
|
|
|
const char *key = astman_get_header(m, "Key");
|
|
|
|
const char *val = astman_get_header(m, "Val");
|
2005-01-29 23:33:14 +00:00
|
|
|
int res;
|
|
|
|
|
2006-03-19 15:53:40 +00:00
|
|
|
if (ast_strlen_zero(family)) {
|
2005-01-29 23:33:14 +00:00
|
|
|
astman_send_error(s, m, "No family specified");
|
|
|
|
return 0;
|
|
|
|
}
|
2006-03-19 15:53:40 +00:00
|
|
|
if (ast_strlen_zero(key)) {
|
2005-01-29 23:33:14 +00:00
|
|
|
astman_send_error(s, m, "No key specified");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-06-24 17:42:39 +00:00
|
|
|
res = ast_db_put(family, key, S_OR(val, ""));
|
2005-07-20 00:09:39 +00:00
|
|
|
if (res) {
|
2005-01-29 23:33:14 +00:00
|
|
|
astman_send_error(s, m, "Failed to update entry");
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2005-01-29 23:33:14 +00:00
|
|
|
astman_send_ack(s, m, "Updated database successfully");
|
2005-07-20 00:09:39 +00:00
|
|
|
}
|
2005-01-29 23:33:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-05 22:43:18 +00:00
|
|
|
static int manager_dbget(struct mansession *s, const struct message *m)
|
2005-01-29 23:33:14 +00:00
|
|
|
{
|
2007-01-05 22:43:18 +00:00
|
|
|
const char *id = astman_get_header(m,"ActionID");
|
2015-01-09 18:16:54 +00:00
|
|
|
char idText[256];
|
2007-01-05 22:43:18 +00:00
|
|
|
const char *family = astman_get_header(m, "Family");
|
|
|
|
const char *key = astman_get_header(m, "Key");
|
2011-06-10 15:30:50 +00:00
|
|
|
char tmp[MAX_DB_FIELD];
|
2005-01-29 23:33:14 +00:00
|
|
|
int res;
|
|
|
|
|
2006-03-19 15:53:40 +00:00
|
|
|
if (ast_strlen_zero(family)) {
|
2005-01-29 23:33:14 +00:00
|
|
|
astman_send_error(s, m, "No family specified.");
|
|
|
|
return 0;
|
|
|
|
}
|
2006-03-19 15:53:40 +00:00
|
|
|
if (ast_strlen_zero(key)) {
|
2005-01-29 23:33:14 +00:00
|
|
|
astman_send_error(s, m, "No key specified.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-09 18:16:54 +00:00
|
|
|
idText[0] = '\0';
|
2005-10-26 18:54:24 +00:00
|
|
|
if (!ast_strlen_zero(id))
|
2005-07-20 00:38:20 +00:00
|
|
|
snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
|
|
|
|
|
2005-01-29 23:33:14 +00:00
|
|
|
res = ast_db_get(family, key, tmp, sizeof(tmp));
|
2005-07-20 00:09:39 +00:00
|
|
|
if (res) {
|
2005-01-29 23:33:14 +00:00
|
|
|
astman_send_error(s, m, "Database entry not found");
|
2005-07-20 00:09:39 +00:00
|
|
|
} else {
|
2015-01-12 18:09:27 +00:00
|
|
|
astman_send_listack(s, m, "Result will follow", "start");
|
2015-01-09 18:16:54 +00:00
|
|
|
|
2006-03-25 23:50:09 +00:00
|
|
|
astman_append(s, "Event: DBGetResponse\r\n"
|
2005-01-29 23:33:14 +00:00
|
|
|
"Family: %s\r\n"
|
|
|
|
"Key: %s\r\n"
|
2005-07-20 00:38:20 +00:00
|
|
|
"Val: %s\r\n"
|
|
|
|
"%s"
|
|
|
|
"\r\n",
|
|
|
|
family, key, tmp, idText);
|
2015-01-09 18:16:54 +00:00
|
|
|
|
|
|
|
astman_send_list_complete_start(s, m, "DBGetComplete", 1);
|
|
|
|
astman_send_list_complete_end(s);
|
2005-01-29 23:33:14 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-11 11:32:39 +00:00
|
|
|
static int manager_db_tree_get(struct mansession *s, const struct message *m)
|
|
|
|
{
|
|
|
|
char prefix[MAX_DB_FIELD];
|
|
|
|
char idText[256];
|
|
|
|
const char *id = astman_get_header(m,"ActionID");
|
|
|
|
const char *family = astman_get_header(m, "Family");
|
|
|
|
const char *key = astman_get_header(m, "Key");
|
|
|
|
sqlite3_stmt *stmt = gettree_stmt;
|
2022-09-24 12:08:14 +00:00
|
|
|
int count = 0;
|
2022-07-11 11:32:39 +00:00
|
|
|
|
|
|
|
if (!ast_strlen_zero(family) && !ast_strlen_zero(key)) {
|
|
|
|
/* Family and key tree */
|
|
|
|
snprintf(prefix, sizeof(prefix), "/%s/%s", family, key);
|
|
|
|
} else if (!ast_strlen_zero(family)) {
|
|
|
|
/* Family only */
|
|
|
|
snprintf(prefix, sizeof(prefix), "/%s", family);
|
|
|
|
} else {
|
|
|
|
/* Neither */
|
|
|
|
prefix[0] = '\0';
|
|
|
|
stmt = gettree_all_stmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
idText[0] = '\0';
|
|
|
|
if (!ast_strlen_zero(id)) {
|
|
|
|
snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
if (!ast_strlen_zero(prefix) && (sqlite3_bind_text(stmt, 1, prefix, -1, SQLITE_STATIC) != SQLITE_OK)) {
|
|
|
|
ast_log(LOG_WARNING, "Couldn't bind %s to stmt: %s\n", prefix, sqlite3_errmsg(astdb));
|
|
|
|
sqlite3_reset(stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
astman_send_error(s, m, "Unable to search database");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
astman_send_listack(s, m, "Result will follow", "start");
|
|
|
|
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
|
|
const char *key_s, *value_s;
|
|
|
|
if (!(key_s = (const char *) sqlite3_column_text(stmt, 0))) {
|
|
|
|
ast_log(LOG_WARNING, "Skipping invalid key!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!(value_s = (const char *) sqlite3_column_text(stmt, 1))) {
|
|
|
|
ast_log(LOG_WARNING, "Skipping invalid value!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
astman_append(s, "Event: DBGetTreeResponse\r\n"
|
|
|
|
"Key: %s\r\n"
|
|
|
|
"Val: %s\r\n"
|
|
|
|
"%s"
|
|
|
|
"\r\n",
|
|
|
|
key_s, value_s, idText);
|
2022-09-24 12:08:14 +00:00
|
|
|
count++;
|
2022-07-11 11:32:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sqlite3_reset(stmt);
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
2022-09-24 12:08:14 +00:00
|
|
|
astman_send_list_complete_start(s, m, "DBGetTreeComplete", count);
|
2022-07-11 11:32:39 +00:00
|
|
|
astman_send_list_complete_end(s);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-01-25 22:49:24 +00:00
|
|
|
static int manager_dbdel(struct mansession *s, const struct message *m)
|
|
|
|
{
|
|
|
|
const char *family = astman_get_header(m, "Family");
|
|
|
|
const char *key = astman_get_header(m, "Key");
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (ast_strlen_zero(family)) {
|
|
|
|
astman_send_error(s, m, "No family specified.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ast_strlen_zero(key)) {
|
|
|
|
astman_send_error(s, m, "No key specified.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-01 19:49:31 +00:00
|
|
|
res = ast_db_del2(family, key);
|
2007-01-25 22:49:24 +00:00
|
|
|
if (res)
|
2022-04-01 19:49:31 +00:00
|
|
|
astman_send_error(s, m, "Database entry could not be deleted");
|
2007-01-25 22:49:24 +00:00
|
|
|
else
|
|
|
|
astman_send_ack(s, m, "Key deleted successfully");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int manager_dbdeltree(struct mansession *s, const struct message *m)
|
|
|
|
{
|
|
|
|
const char *family = astman_get_header(m, "Family");
|
|
|
|
const char *key = astman_get_header(m, "Key");
|
2012-10-04 15:48:24 +00:00
|
|
|
int num_deleted;
|
2007-01-25 22:49:24 +00:00
|
|
|
|
|
|
|
if (ast_strlen_zero(family)) {
|
|
|
|
astman_send_error(s, m, "No family specified.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-04 15:48:24 +00:00
|
|
|
if (!ast_strlen_zero(key)) {
|
|
|
|
num_deleted = ast_db_deltree(family, key);
|
|
|
|
} else {
|
|
|
|
num_deleted = ast_db_deltree(family, NULL);
|
|
|
|
}
|
2007-01-25 22:49:24 +00:00
|
|
|
|
2012-10-04 15:48:24 +00:00
|
|
|
if (num_deleted < 0) {
|
|
|
|
astman_send_error(s, m, "Database unavailable");
|
|
|
|
} else if (num_deleted == 0) {
|
2007-01-25 22:49:24 +00:00
|
|
|
astman_send_error(s, m, "Database entry not found");
|
2012-10-04 15:48:24 +00:00
|
|
|
} else {
|
2007-01-25 22:49:24 +00:00
|
|
|
astman_send_ack(s, m, "Key tree deleted successfully");
|
2012-10-04 15:48:24 +00:00
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
|
2007-01-25 22:49:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-09-13 22:13:27 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Signal the astdb sync thread to do its thing.
|
|
|
|
*
|
|
|
|
* \note dblock is assumed to be held when calling this function.
|
|
|
|
*/
|
|
|
|
static void db_sync(void)
|
|
|
|
{
|
2014-03-14 16:19:21 +00:00
|
|
|
dosync = 1;
|
2010-09-13 22:13:27 +00:00
|
|
|
ast_cond_signal(&dbcond);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief astdb sync thread
|
|
|
|
*
|
|
|
|
* This thread is in charge of syncing astdb to disk after a change.
|
|
|
|
* By pushing it off to this thread to take care of, this I/O bound operation
|
|
|
|
* will not block other threads from performing other critical processing.
|
|
|
|
* If changes happen rapidly, this thread will also ensure that the sync
|
|
|
|
* operations are rate limited.
|
|
|
|
*/
|
|
|
|
static void *db_sync_thread(void *data)
|
|
|
|
{
|
|
|
|
ast_mutex_lock(&dblock);
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_db_begin_transaction();
|
2010-09-13 22:13:27 +00:00
|
|
|
for (;;) {
|
2017-12-22 14:23:22 +00:00
|
|
|
/* If dosync is set, db_sync() was called during sleep(1),
|
|
|
|
* and the pending transaction should be committed.
|
2014-03-14 16:19:21 +00:00
|
|
|
* Otherwise, block until db_sync() is called.
|
|
|
|
*/
|
|
|
|
while (!dosync) {
|
|
|
|
ast_cond_wait(&dbcond, &dblock);
|
|
|
|
}
|
|
|
|
dosync = 0;
|
2011-07-06 20:58:12 +00:00
|
|
|
if (ast_db_commit_transaction()) {
|
|
|
|
ast_db_rollback_transaction();
|
|
|
|
}
|
|
|
|
if (doexit) {
|
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ast_db_begin_transaction();
|
2010-09-13 22:13:27 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
sleep(1);
|
|
|
|
ast_mutex_lock(&dblock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-08-16 17:33:21 +00:00
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
* \brief Clean up resources on Asterisk shutdown
|
|
|
|
*/
|
2011-07-06 20:58:12 +00:00
|
|
|
static void astdb_atexit(void)
|
|
|
|
{
|
2012-11-04 01:19:43 +00:00
|
|
|
ast_cli_unregister_multiple(cli_database, ARRAY_LEN(cli_database));
|
2012-10-02 01:47:16 +00:00
|
|
|
ast_manager_unregister("DBGet");
|
2022-07-11 11:32:39 +00:00
|
|
|
ast_manager_unregister("DBGetTree");
|
2012-10-02 01:47:16 +00:00
|
|
|
ast_manager_unregister("DBPut");
|
|
|
|
ast_manager_unregister("DBDel");
|
|
|
|
ast_manager_unregister("DBDelTree");
|
|
|
|
|
2012-01-09 19:37:23 +00:00
|
|
|
/* Set doexit to 1 to kill thread. db_sync must be called with
|
|
|
|
* mutex held. */
|
|
|
|
ast_mutex_lock(&dblock);
|
2014-03-14 16:19:21 +00:00
|
|
|
doexit = 1;
|
2011-07-06 20:58:12 +00:00
|
|
|
db_sync();
|
2012-01-09 19:37:23 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
pthread_join(syncthread, NULL);
|
|
|
|
ast_mutex_lock(&dblock);
|
2012-11-04 01:19:43 +00:00
|
|
|
clean_statements();
|
|
|
|
if (sqlite3_close(astdb) == SQLITE_OK) {
|
|
|
|
astdb = NULL;
|
|
|
|
}
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_mutex_unlock(&dblock);
|
|
|
|
}
|
|
|
|
|
2003-01-30 15:03:20 +00:00
|
|
|
int astdb_init(void)
|
|
|
|
{
|
2019-12-27 23:29:45 +00:00
|
|
|
ast_cond_init(&dbcond, NULL);
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (db_init()) {
|
|
|
|
return -1;
|
|
|
|
}
|
2010-09-13 22:13:27 +00:00
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
if (ast_pthread_create_background(&syncthread, NULL, db_sync_thread, NULL)) {
|
2010-09-13 22:13:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-07-06 20:58:12 +00:00
|
|
|
ast_register_atexit(astdb_atexit);
|
2008-12-05 10:31:25 +00:00
|
|
|
ast_cli_register_multiple(cli_database, ARRAY_LEN(cli_database));
|
2012-03-20 17:31:28 +00:00
|
|
|
ast_manager_register_xml_core("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget);
|
2022-07-11 11:32:39 +00:00
|
|
|
ast_manager_register_xml_core("DBGetTree", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_db_tree_get);
|
2012-03-20 17:31:28 +00:00
|
|
|
ast_manager_register_xml_core("DBPut", EVENT_FLAG_SYSTEM, manager_dbput);
|
|
|
|
ast_manager_register_xml_core("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel);
|
|
|
|
ast_manager_register_xml_core("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree);
|
2003-01-30 15:03:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|