| 
									
										
										
										
											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
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2005-12-30 21:18:06 +00:00
										 |  |  |  * \author Mark Spencer <markster@digium.com>  | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \note DB3 is licensed under Sleepycat Public License and is thus incompatible | 
					
						
							| 
									
										
										
										
											2005-09-14 20:46:50 +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
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-06-07 18:54:56 +00:00
										 |  |  | #include "asterisk.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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>
 | 
					
						
							|  |  |  | #include <signal.h>
 | 
					
						
							|  |  |  | #include <dirent.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/lock.h"
 | 
					
						
							|  |  |  | #include "asterisk/manager.h"
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | #include "db1-ast/include/db.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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> | 
					
						
							|  |  |  | 	<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:29:00 +00:00
										 |  |  | #define MAX_DB_FIELD 256
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | static DB *astdb; | 
					
						
							| 
									
										
										
										
											2004-06-09 01:45:08 +00:00
										 |  |  | AST_MUTEX_DEFINE_STATIC(dblock); | 
					
						
							| 
									
										
										
										
											2010-09-10 20:31:58 +00:00
										 |  |  | static ast_cond_t dbcond; | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | typedef int (*process_keys_cb)(DBT *key, DBT *value, const char *filter, void *data); | 
					
						
							| 
									
										
										
										
											2010-09-10 20:31:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void db_sync(void); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int dbinit(void)  | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-12-18 09:46:18 +00:00
										 |  |  | 	if (!astdb && !(astdb = dbopen(ast_config_AST_DB, O_CREAT | O_RDWR, AST_FILE_MODE, DB_BTREE, NULL))) { | 
					
						
							| 
									
										
										
										
											2007-10-05 19:48:10 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", ast_config_AST_DB, strerror(errno)); | 
					
						
							| 
									
										
										
										
											2006-02-09 16:59:50 +00:00
										 |  |  | 		return -1; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2006-02-09 16:59:50 +00:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int keymatch(const char *key, const char *prefix) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	int preflen = strlen(prefix); | 
					
						
							|  |  |  | 	if (!preflen) | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		return 1; | 
					
						
							|  |  |  | 	if (!strcasecmp(key, prefix)) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	if ((strlen(key) > preflen) && !strncasecmp(key, prefix, preflen)) { | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 		if (key[preflen] == '/') | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 			return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-12-22 01:07:02 +00:00
										 |  |  | static inline int subkeymatch(const char *key, const char *suffix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int suffixlen = strlen(suffix); | 
					
						
							|  |  |  | 	if (suffixlen) { | 
					
						
							|  |  |  | 		const char *subkey = key + strlen(key) - suffixlen; | 
					
						
							|  |  |  | 		if (subkey < key) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		if (!strcasecmp(subkey, suffix)) | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | static const char *dbt_data2str(DBT *dbt) | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	char *data = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dbt->size) { | 
					
						
							|  |  |  | 		data = dbt->data; | 
					
						
							|  |  |  | 		data[dbt->size - 1] = '\0'; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline const char *dbt_data2str_full(DBT *dbt, const char *def) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return S_OR(dbt_data2str(dbt), def); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int process_db_keys(process_keys_cb cb, void *data, const char *filter, int sync) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	DBT key = { 0, }, value = { 0, }, last_key = { 0, }; | 
					
						
							| 
									
										
										
										
											2007-09-10 17:39:08 +00:00
										 |  |  | 	int counter = 0; | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	int res, last = 0; | 
					
						
							|  |  |  | 	char last_key_s[MAX_DB_FIELD]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_lock(&dblock); | 
					
						
							|  |  |  | 	if (dbinit()) { | 
					
						
							|  |  |  | 		ast_mutex_unlock(&dblock); | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Somehow, the database can become corrupted such that astdb->seq will continue looping through
 | 
					
						
							|  |  |  | 	 * the database indefinitely. The pointer to last_key.data ends up getting re-used by the BDB lib | 
					
						
							|  |  |  | 	 * so this specifically queries for the last entry, makes a copy of the key, and then uses it as | 
					
						
							|  |  |  | 	 * a sentinel to avoid repeatedly looping over the list. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (astdb->seq(astdb, &last_key, &value, R_LAST)) { | 
					
						
							|  |  |  | 		/* Empty database */ | 
					
						
							|  |  |  | 		ast_mutex_unlock(&dblock); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memcpy(last_key_s, last_key.data, MIN(last_key.size - 1, sizeof(last_key_s))); | 
					
						
							|  |  |  | 	last_key_s[last_key.size - 1] = '\0'; | 
					
						
							|  |  |  | 	for (res = astdb->seq(astdb, &key, &value, R_FIRST); | 
					
						
							|  |  |  | 			!res; | 
					
						
							|  |  |  | 			res = astdb->seq(astdb, &key, &value, R_NEXT)) { | 
					
						
							|  |  |  | 		/* The callback might delete the key, so we have to check it before calling */ | 
					
						
							|  |  |  | 		last = !strcmp(dbt_data2str_full(&key, "<bad key>"), last_key_s); | 
					
						
							|  |  |  | 		counter += cb(&key, &value, filter, data); | 
					
						
							|  |  |  | 		if (last) { | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (sync) { | 
					
						
							|  |  |  | 		db_sync(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_mutex_unlock(&dblock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return counter; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int db_deltree_cb(DBT *key, DBT *value, const char *filter, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int res = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (keymatch(dbt_data2str_full(key, "<bad key>"), filter)) { | 
					
						
							|  |  |  | 		astdb->del(astdb, key, 0); | 
					
						
							|  |  |  | 		res = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ast_db_deltree(const char *family, const char *keytree) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char prefix[MAX_DB_FIELD]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	if (family) { | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 		if (keytree) { | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 			snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 			snprintf(prefix, sizeof(prefix), "/%s", family); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} else if (keytree) { | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		return -1; | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2004-07-09 10:08:09 +00:00
										 |  |  | 		prefix[0] = '\0'; | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return process_db_keys(db_deltree_cb, NULL, prefix, 1); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-01-05 23:32:42 +00:00
										 |  |  | int ast_db_put(const char *family, const char *keys, const char *value) | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	char fullkey[MAX_DB_FIELD]; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	DBT key, data; | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	int res, fullkeylen; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_lock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	if (dbinit()) { | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 		ast_mutex_unlock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	memset(&key, 0, sizeof(key)); | 
					
						
							|  |  |  | 	memset(&data, 0, sizeof(data)); | 
					
						
							|  |  |  | 	key.data = fullkey; | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	key.size = fullkeylen + 1; | 
					
						
							| 
									
										
										
										
											2007-01-05 23:32:42 +00:00
										 |  |  | 	data.data = (char *) value; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	data.size = strlen(value) + 1; | 
					
						
							|  |  |  | 	res = astdb->put(astdb, &key, &data, 0); | 
					
						
							| 
									
										
										
										
											2010-09-10 20:31:58 +00:00
										 |  |  | 	db_sync(); | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_unlock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	if (res) | 
					
						
							|  |  |  | 		ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family); | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ast_db_get(const char *family, const char *keys, char *value, int valuelen) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	char fullkey[MAX_DB_FIELD] = ""; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	DBT key, data; | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	int res, fullkeylen; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_lock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	if (dbinit()) { | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 		ast_mutex_unlock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	memset(&key, 0, sizeof(key)); | 
					
						
							|  |  |  | 	memset(&data, 0, sizeof(data)); | 
					
						
							| 
									
										
										
										
											2003-08-13 04:36:06 +00:00
										 |  |  | 	memset(value, 0, valuelen); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	key.data = fullkey; | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	key.size = fullkeylen + 1; | 
					
						
							| 
									
										
										
										
											2009-03-17 05:51:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	res = astdb->get(astdb, &key, &data, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Be sure to NULL terminate our data either way */ | 
					
						
							|  |  |  | 	if (res) { | 
					
						
							| 
									
										
										
										
											2007-06-14 19:39:12 +00:00
										 |  |  | 		ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2003-08-13 03:34:44 +00:00
										 |  |  | #if 0
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		printf("Got value of size %d\n", data.size); | 
					
						
							| 
									
										
										
										
											2003-08-13 03:34:44 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		if (data.size) { | 
					
						
							|  |  |  | 			((char *)data.data)[data.size - 1] = '\0'; | 
					
						
							| 
									
										
										
										
											2003-08-13 04:36:06 +00:00
										 |  |  | 			/* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */ | 
					
						
							| 
									
										
										
										
											2006-10-25 14:44:50 +00:00
										 |  |  | 			ast_copy_string(value, data.data, (valuelen > data.size) ? data.size : valuelen); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-03-17 05:51:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Data is not fully isolated for concurrency, so the lock must be extended
 | 
					
						
							|  |  |  | 	 * to after the copy to the output buffer. */ | 
					
						
							|  |  |  | 	ast_mutex_unlock(&dblock); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ast_db_del(const char *family, const char *keys) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	char fullkey[MAX_DB_FIELD]; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	DBT key; | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	int res, fullkeylen; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_lock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	if (dbinit()) { | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 		ast_mutex_unlock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	memset(&key, 0, sizeof(key)); | 
					
						
							|  |  |  | 	key.data = fullkey; | 
					
						
							| 
									
										
										
										
											2004-05-28 18:56:23 +00:00
										 |  |  | 	key.size = fullkeylen + 1; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	res = astdb->del(astdb, &key, 0); | 
					
						
							| 
									
										
										
										
											2010-09-10 20:31:58 +00:00
										 |  |  | 	db_sync(); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2003-08-13 15:25:16 +00:00
										 |  |  | 	ast_mutex_unlock(&dblock); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-03 15:53:07 +00:00
										 |  |  | 	if (res) { | 
					
						
							| 
									
										
										
										
											2007-06-14 19:39:12 +00:00
										 |  |  | 		ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family); | 
					
						
							| 
									
										
										
										
											2006-10-03 15:53:07 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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:29:00 +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; | 
					
						
							|  |  |  | 	res = ast_db_del(a->argv[2], a->argv[3]); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	if (res) { | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 		ast_cli(a->fd, "Database entry does not exist.\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
										 |  |  | { | 
					
						
							|  |  |  | 	int res; | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (cmd) { | 
					
						
							|  |  |  | 	case CLI_INIT: | 
					
						
							|  |  |  | 		e->command = "database deltree"; | 
					
						
							|  |  |  | 		e->usage = | 
					
						
							|  |  |  | 			"Usage: database deltree <family> [keytree]\n" | 
					
						
							| 
									
										
										
										
											2011-11-19 15:08:03 +00:00
										 |  |  | 			"   OR: database deltree <family>[/keytree]\n" | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 			"       Deletes a family or specific keytree within a family\n" | 
					
						
							| 
									
										
										
										
											2011-11-19 15:08:03 +00:00
										 |  |  | 			"       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) { | 
					
						
							|  |  |  | 		res = ast_db_deltree(a->argv[2], a->argv[3]); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 		res = ast_db_deltree(a->argv[2], NULL); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2007-09-10 17:39:08 +00:00
										 |  |  | 	if (res < 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 { | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 		ast_cli(a->fd, "%d database entries removed.\n",res); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | static int db_show_cb(DBT *key, DBT *value, const char *filter, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_cli_args *a = data; | 
					
						
							|  |  |  | 	const char *key_s = dbt_data2str_full(key, "<bad key>"); | 
					
						
							|  |  |  | 	const char *value_s = dbt_data2str_full(value, "<bad value>"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (keymatch(key_s, filter)) { | 
					
						
							|  |  |  | 		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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:29:00 +00:00
										 |  |  | 	char prefix[MAX_DB_FIELD]; | 
					
						
							| 
									
										
										
										
											2007-09-10 16:24:29 +00:00
										 |  |  | 	int counter = 0; | 
					
						
							| 
									
										
										
										
											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 = | 
					
						
							|  |  |  | 			"Usage: database show [family [keytree]]\n" | 
					
						
							| 
									
										
										
										
											2011-11-19 15:08:03 +00:00
										 |  |  | 			"   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:08:03 +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'; | 
					
						
							| 
									
										
										
										
											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:29:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if((counter = process_db_keys(db_show_cb, a, prefix, 0)) < 0) { | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 		ast_cli(a->fd, "Database unavailable\n"); | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 		return CLI_SUCCESS; | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 	ast_cli(a->fd, "%d results found.\n", counter); | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	return CLI_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int db_showkey_cb(DBT *key, DBT *value, const char *filter, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_cli_args *a = data; | 
					
						
							|  |  |  | 	const char *key_s = dbt_data2str_full(key, "<bad key>"); | 
					
						
							|  |  |  | 	const char *value_s = dbt_data2str_full(value, "<bad value>"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (subkeymatch(key_s, filter)) { | 
					
						
							|  |  |  | 		ast_cli(a->fd, "%-50s: %-25s\n", key_s, value_s); | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											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
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	char suffix[MAX_DB_FIELD]; | 
					
						
							| 
									
										
										
										
											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 = | 
					
						
							|  |  |  | 			"Usage: database showkey <keytree>\n" | 
					
						
							|  |  |  | 			"       Shows Asterisk database contents, restricted to a given key.\n"; | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	case CLI_GENERATE: | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (a->argc == 3) { | 
					
						
							| 
									
										
										
										
											2004-12-22 01:07:02 +00:00
										 |  |  | 		/* Key only */ | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 		snprintf(suffix, sizeof(suffix), "/%s", a->argv[2]); | 
					
						
							| 
									
										
										
										
											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:29:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if ((counter = process_db_keys(db_showkey_cb, a, suffix, 0)) < 0) { | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 		ast_cli(a->fd, "Database unavailable\n"); | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 		return CLI_SUCCESS; | 
					
						
							| 
									
										
										
										
											2004-12-22 01:07:02 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-10-11 19:03:06 +00:00
										 |  |  | 	ast_cli(a->fd, "%d results found.\n", counter); | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	return CLI_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int db_gettree_cb(DBT *key, DBT *value, const char *filter, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_db_entry **ret = data; | 
					
						
							|  |  |  | 	struct ast_db_entry *cur; | 
					
						
							|  |  |  | 	const char *key_s = dbt_data2str_full(key, "<bad key>"); | 
					
						
							|  |  |  | 	const char *value_s = dbt_data2str_full(value, "<bad value>"); | 
					
						
							|  |  |  | 	size_t key_slen = strlen(key_s) + 1, value_slen = strlen(value_s) + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (keymatch(key_s, filter) && (cur = ast_malloc(sizeof(*cur) + key_slen + value_slen))) { | 
					
						
							|  |  |  | 		cur->next = *ret; | 
					
						
							|  |  |  | 		cur->key = cur->data + value_slen; | 
					
						
							|  |  |  | 		strcpy(cur->data, value_s); | 
					
						
							|  |  |  | 		strcpy(cur->key, key_s); | 
					
						
							|  |  |  | 		*ret = cur; | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2004-12-22 01:07:02 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 	char prefix[MAX_DB_FIELD]; | 
					
						
							|  |  |  | 	struct ast_db_entry *ret = NULL; | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-10-26 18:54:24 +00:00
										 |  |  | 	if (!ast_strlen_zero(family)) { | 
					
						
							|  |  |  | 		if (!ast_strlen_zero(keytree)) { | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | 			/* Family and key tree */ | 
					
						
							| 
									
										
										
										
											2009-01-06 21:36:44 +00:00
										 |  |  | 			snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | 			/* Family only */ | 
					
						
							|  |  |  | 			snprintf(prefix, sizeof(prefix), "/%s", family); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2004-07-09 10:08:09 +00:00
										 |  |  | 		prefix[0] = '\0'; | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (process_db_keys(db_gettree_cb, &ret, prefix, 0) < 0) { | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | 		ast_log(LOG_WARNING, "Database unavailable\n"); | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 		return NULL; | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-06-10 15:29:00 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ast_db_freetree(struct ast_db_entry *dbe) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ast_db_entry *last; | 
					
						
							| 
									
										
										
										
											2005-07-20 00:09:39 +00:00
										 |  |  | 	while (dbe) { | 
					
						
							| 
									
										
										
										
											2003-04-13 04:17:45 +00:00
										 |  |  | 		last = dbe; | 
					
						
							|  |  |  | 		dbe = dbe->next; | 
					
						
							| 
									
										
										
										
											2007-06-06 21:20:11 +00:00
										 |  |  | 		ast_free(last); | 
					
						
							| 
									
										
										
										
											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"), | 
					
						
							|  |  |  | 	AST_CLI_DEFINE(handle_cli_database_deltree, "Removes database keytree/values") | 
					
						
							| 
									
										
										
										
											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"); | 
					
						
							| 
									
										
										
										
											2005-07-20 00:38:20 +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:29:00 +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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2005-01-29 23:33:14 +00:00
										 |  |  | 		astman_send_ack(s, m, "Result will follow"); | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											2010-06-11 18:17:28 +00:00
										 |  |  | 		astman_append(s, "Event: DBGetComplete\r\n" | 
					
						
							|  |  |  | 				"%s" | 
					
						
							|  |  |  | 				"\r\n", | 
					
						
							|  |  |  | 				idText); | 
					
						
							| 
									
										
										
										
											2005-01-29 23:33:14 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = ast_db_del(family, key); | 
					
						
							|  |  |  | 	if (res) | 
					
						
							|  |  |  | 		astman_send_error(s, m, "Database entry not found"); | 
					
						
							|  |  |  | 	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"); | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ast_strlen_zero(family)) { | 
					
						
							|  |  |  | 		astman_send_error(s, m, "No family specified."); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!ast_strlen_zero(key)) | 
					
						
							|  |  |  | 		res = ast_db_deltree(family, key); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		res = ast_db_deltree(family, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-10 17:39:08 +00:00
										 |  |  | 	if (res < 0) | 
					
						
							| 
									
										
										
										
											2007-01-25 22:49:24 +00:00
										 |  |  | 		astman_send_error(s, m, "Database entry not found"); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		astman_send_ack(s, m, "Key tree deleted successfully"); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-10 20:31:58 +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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	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); | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		ast_cond_wait(&dbcond, &dblock); | 
					
						
							|  |  |  | 		ast_mutex_unlock(&dblock); | 
					
						
							|  |  |  | 		sleep(1); | 
					
						
							|  |  |  | 		ast_mutex_lock(&dblock); | 
					
						
							|  |  |  | 		astdb->sync(astdb, 0); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | int astdb_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-09-10 20:31:58 +00:00
										 |  |  | 	pthread_t dont_care; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ast_cond_init(&dbcond, NULL); | 
					
						
							|  |  |  | 	if (ast_pthread_create_background(&dont_care, NULL, db_sync_thread, NULL)) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	dbinit(); | 
					
						
							| 
									
										
										
										
											2008-12-05 10:31:25 +00:00
										 |  |  | 	ast_cli_register_multiple(cli_database, ARRAY_LEN(cli_database)); | 
					
						
							| 
									
										
										
										
											2009-05-22 17:52:35 +00:00
										 |  |  | 	ast_manager_register_xml("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget); | 
					
						
							|  |  |  | 	ast_manager_register_xml("DBPut", EVENT_FLAG_SYSTEM, manager_dbput); | 
					
						
							|  |  |  | 	ast_manager_register_xml("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel); | 
					
						
							|  |  |  | 	ast_manager_register_xml("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree); | 
					
						
							| 
									
										
										
										
											2003-01-30 15:03:20 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } |