funcs/func_dbchan.c
static int db_chan_helper(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len, int prune)
{
	struct ast_db_entry *dbe, *orig_dbe;
	char *family;
	const char *last = "";
	int pruned = 0;
	int epochparse = 0, found = 0;

	if (prune == 2) { /* DB_CHANNEL_PRUNE_TIME */
		char *epochthreshold = strsep(&parse, ",");
		epochparse = atoi(epochthreshold);
		if (!epochparse) {
			ast_log(LOG_WARNING, "Invalid epoch threshold: %s\n", epochthreshold);
			return -1;
		}
		ast_debug(2, "Pruning entries with keys < %d\n", epochparse);
	}

	while ((family = strsep(&parse, ","))) {
		size_t parselen = strlen(family);
		char *parallel = family;
		family = strsep(&parallel, "|");
		ast_debug(1, "%s: original family: %s, parallel family: %s\n", cmd, family, parallel ? parallel : "(null)");
		if (parallel && !strcmp(family, parallel)) {
			ast_log(LOG_WARNING, "%s: original family '%s' is same as parallel family '%s', skipping\n", cmd, family, parallel);
			continue;
		}
		if (family[strlen(family) - 1] == '/') {
			ast_log(LOG_WARNING, "%s: family '%s' ends in a '/', truncated? Skipping...", cmd, family);
			continue;
		}
		if (parallel && parallel[strlen(parallel) - 1] == '/') {
			ast_log(LOG_WARNING, "%s: parallel family '%s' ends in a '/', truncated? Skipping...", cmd, parallel);
			continue;
		}
		/* Remove leading and trailing slashes */
		while (family[0] == '/') {
			family++;
			parselen--;
		}
		while (family[parselen - 1] == '/') {
			family[--parselen] = '\0';
		}

		/* Nothing within the database at that prefix? */
		if (!(orig_dbe = dbe = ast_db_gettree(family, NULL))) {
			ast_debug(1, "Nothing within database at prefix '%s'\n", family);
			continue;
		}

		for (; dbe; dbe = dbe->next) {
#define BUFFER_SIZE 256
			struct ast_channel *chan_found = NULL;
			char channel[BUFFER_SIZE];
			/* Find the current component */
			char *curkey = &dbe->key[parselen + 1], *slash;
			if (*curkey == '/') {
				curkey++;
			}
			/* Remove everything after the current component */
			if ((slash = strchr(curkey, '/'))) {
				*slash = '\0';
			}

			/* Skip duplicates */
			if (!strcasecmp(last, curkey)) {
				continue;
			}
			last = curkey;

			if (ast_strlen_zero(curkey)) {
				ast_debug(1, "Key is empty\n");
				continue;
			}

			if (ast_db_get(family, curkey, channel, BUFFER_SIZE)) {
				ast_log(LOG_WARNING, "%s: Couldn't find %s/%s in database\n", cmd, family, curkey);
				continue;
			}

			if (prune != 2) { /* skip for DB_CHANNEL_PRUNE_TIME, because the values aren't channel names */
				chan_found = ast_channel_get_by_name(channel);
				if (chan_found) {
					ast_channel_unref(chan_found);
					/* main difference between DB_CHANNEL and DB_CHANNEL_PRUNE is latter always checks all keys.
						Former stops if/when we find a match. */
					if (prune) {
						continue;
					}
					ast_copy_string(buf, curkey, len);
					found = 1;
					break;
				}
			}
			/* channel doesn't exist anymore: delete key and continue */
			if (prune == 2) { /* but only if it's staler than the required epoch time */
				int keyepoch = atoi(curkey);
				if (!(keyepoch < epochparse)) { /* if not older than threshold, don't delete */
					ast_debug(1, "%s: %d is newer than %d, not old enough to purge\n", cmd, keyepoch, epochparse);
					continue;
				}
				ast_debug(1, "%s: %d is older than %d, okay to purge\n", cmd, keyepoch, epochparse);
			} else {
				ast_debug(1, "%s: Channel %s doesn't exist, saying goodbye\n", cmd, channel);
			}
			pruned++;
			if (ast_db_del(family, curkey)) {
				ast_log(LOG_WARNING, "%s: %s/%s could not be deleted from the database\n", cmd, family, curkey);
			} else if (parallel) {
				ast_debug(1, "%s: Also saying goodbye to %s/%s, if it exists\n", cmd, parallel, curkey);
				if (!ast_db_get(parallel, curkey, buf, len - 1)) {
					if (ast_db_del(parallel, curkey)) {
						ast_debug(1, "%s/%s could not be deleted from the database\n", parallel, curkey);
					}
				}
			}
		}
		ast_db_freetree(orig_dbe);
		if (found) {
			break;
		}
	}
	if (prune) { /* DB_CHANNEL_PRUNE returns # of channels pruned. */
		snprintf(buf, len, "%d", pruned);
	}
	return 0;
}
/*** DOCUMENTATION
	<function name="DB_CHANNEL" language="en_US">
		<synopsis>
			Return the name of the oldest alive channel in an AstDB family
			where the values correspond to channels.
		</synopsis>
		<syntax argsep="/">
			<parameter name="families" required="true" />
		</syntax>
		<description>
			<para>Returns the key corresponding to the first channel that is still alive in a DB
			family or comma-separated list of DB families of keys corresponding to sequentially
			ordered IDs and values corresponding to channel names or unique IDs. Channels that no
			longer exist will be automatically purged if they are older than the first match. If no
			match is found or the DB family does not exist, an empty string is returned.</para>
			<para>This function is designed to facilitate easy search operations when channels are
			stored in AstDB and the oldest existing channel needs to be found.</para>
		</description>
		<see-also>
			<ref type="function">CHANNEL_EXISTS</ref>
			<ref type="function">DB_KEYS</ref>
		</see-also>
	</function>
	<function name="DB_CHANNEL_PRUNE" language="en_US">
		<synopsis>
			Deletes all entries in a family or families whose values are channels that
			no longer exist.
		</synopsis>
		<syntax argsep="/">
			<parameter name="families" required="true" argsep=",">
				<para>AstDB families. Individual families can be pipe-separated to
				delete an entry with the same key in the second family if an entry
				in the first family is deleted (synchronized delete).</para>
			</parameter>
		</syntax>
		<description>
			<para>Iterates through a DB family or comma-separated list of DB families and deletes any
			key-value pairs where the value is no longer a valid channel name or unique ID. Returns
			the number of key/value pairs that were deleted from the database.</para>
		</description>
		<see-also>
			<ref type="function">CHANNEL_EXISTS</ref>
			<ref type="function">DB_KEYS</ref>
			<ref type="function">DB_DELETE</ref>
			<ref type="function">DB_CHANNEL_PRUNE_TIME</ref>
		</see-also>
	</function>
	<function name="DB_CHANNEL_PRUNE_TIME" language="en_US">
		<synopsis>
			Deletes all entries in a DB family or families whose keys are epoch times older
			than the specified epoch. Note that unlike the other DB_CHANNEL functions, this
			requires that keys follow a certain format (i.e. that they correspond to an epoch timestamp).
		</synopsis>
		<syntax argsep="/">
			<parameter name="epoch" required="true" />
			<parameter name="families" required="true" argsep=",">
				<para>AstDB families. Individual families can be pipe-separated to
				delete an entry with the same key in the second family if an entry
				in the first family is deleted (synchronized delete).</para>
			</parameter>
		</syntax>
		<description>
			<para>Iterates through a DB family or comma-separated list of DB families and deletes any
			key-value pairs where the key is an epoch timestamp older than a specified epoch.</para>
			<para>This function should NOT be used if keys do not correspond to epochs.</para>
		</description>
		<see-also>
			<ref type="function">CHANNEL_EXISTS</ref>
			<ref type="function">DB_KEYS</ref>
			<ref type="function">DB_DELETE</ref>
			<ref type="function">DB_CHANNEL_PRUNE</ref>
		</see-also>
	</function>
	<function name="DB_MINKEY" language="en_US">
		<synopsis>
			Retrieves the smallest numerical key within specified comma-separated families
		</synopsis>
		<syntax argsep="/">
			<parameter name="families" required="true" argsep=",">
				<para>AstDB families.</para>
			</parameter>
		</syntax>
		<description>
			<para>Returns the family/key of the smallest numerical key from a list of AstDB families.</para>
		</description>
		<see-also>
			<ref type="function">DB_MAXKEY</ref>
			<ref type="function">MIN</ref>
			<ref type="function">MAX</ref>
		</see-also>
	</function>
	<function name="DB_MAXKEY" language="en_US">
		<synopsis>
			Retrieves the largest numerical key within specified comma-separated families
		</synopsis>
		<syntax argsep="/">
			<parameter name="families" required="true" argsep=",">
				<para>AstDB families.</para>
			</parameter>
		</syntax>
		<description>
			<para>Returns the family/key of the largest numerical key from a list of AstDB families.</para>
		</description>
		<see-also>
			<ref type="function">DB_MINKEY</ref>
			<ref type="function">MIN</ref>
			<ref type="function">MAX</ref>
		</see-also>
	</function>
	<function name="DB_UNIQUE" language="en_US">
		<synopsis>
			Returns a unique DB key that can be used to store a value in AstDB.
		</synopsis>
		<syntax argsep="/">
			<parameter name="familyandkey" required="true">
				<para>The family/key to use as the base for the unique keyname.</para>
			</parameter>
		</syntax>
		<description>
			<para>Computes a unique DB key name by using <literal>familyandkey</literal> as
			base and adding a unique zero-padded suffix, beginning with 0.</para>
			<para>This can be used to input data into AstDB with a guaranteed new/unique key
			name that will sort later than any existing keys with the same base key name.</para>
			<para>If this function is read, it will return a unique key name (NOT including
			the family name). If this function is written to, it will store a value at the
			determined unique key name, reducing the chances of data being overwritten
			due to a collision.</para>
			<para>This function will search up to suffix 999, after which time
			the function will abort the search for a unique key name.</para>
		</description>
		<see-also>
			<ref type="function">DB_KEYS</ref>
			<ref type="function">DB_EXISTS</ref>
		</see-also>
	</function>
 ***/
DOCUMENTATION


Return the name of the oldest alive channel in an AstDB family
where the values correspond to channels.





Returns the key corresponding to the first channel that is still alive in a DB
family or comma-separated list of DB families of keys corresponding to sequentially
ordered IDs and values corresponding to channel names or unique IDs. Channels that no
longer exist will be automatically purged if they are older than the first match. If no
match is found or the DB family does not exist, an empty string is returned.
This function is designed to facilitate easy search operations when channels are
stored in AstDB and the oldest existing channel needs to be found.

CHANNEL_EXISTS
DB_KEYS




Deletes all entries in a family or families whose values are channels that
no longer exist.
DOCUMENTATION Return the name of the oldest alive channel in an AstDB family where the values correspond to channels . Returns the key corresponding to the first channel that is still alive in a DB family or comma - separated list of DB families of keys corresponding to sequentially ordered IDs and values corresponding to channel names or unique IDs . Channels that no longer exist will be automatically purged if they are older than the first match . If no match is found or the DB family does not exist an empty string is returned . This function is designed to facilitate easy search operations when channels are stored in AstDB and the oldest existing channel needs to be found . CHANNEL_EXISTS DB_KEYS Deletes all entries in a family or families whose values are channels that no longer exist .
static int db_chan_helper(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len, int prune)
{
	struct ast_db_entry *dbe, *orig_dbe;
	char *family;
	const char *last = "";
	int pruned = 0;
	int epochparse = 0, found = 0;
	if (prune == 2) { 
		char *epochthreshold = strsep(&parse, ",");
		epochparse = atoi(epochthreshold);
		if (!epochparse) {
			ast_log(LOG_WARNING, "Invalid epoch threshold: %s\n", epochthreshold);
			return -1;
		}
		ast_debug(2, "Pruning entries with keys < %d\n", epochparse);
	}
	while ((family = strsep(&parse, ","))) {
		size_t parselen = strlen(family);
		char *parallel = family;
		family = strsep(&parallel, "|");
		ast_debug(1, "%s: original family: %s, parallel family: %s\n", cmd, family, parallel ? parallel : "(null)");
		if (parallel && !strcmp(family, parallel)) {
			ast_log(LOG_WARNING, "%s: original family '%s' is same as parallel family '%s', skipping\n", cmd, family, parallel);
			continue;
		}
		if (family[strlen(family) - 1] == '/') {
			ast_log(LOG_WARNING, "%s: family '%s' ends in a '/', truncated? Skipping...", cmd, family);
			continue;
		}
		if (parallel && parallel[strlen(parallel) - 1] == '/') {
			ast_log(LOG_WARNING, "%s: parallel family '%s' ends in a '/', truncated? Skipping...", cmd, parallel);
			continue;
		}
		while (family[0] == '/') {
			family++;
			parselen--;
		}
		while (family[parselen - 1] == '/') {
			family[--parselen] = '\0';
		}
		if (!(orig_dbe = dbe = ast_db_gettree(family, NULL))) {
			ast_debug(1, "Nothing within database at prefix '%s'\n", family);
			continue;
		}
		for (; dbe; dbe = dbe->next) {
#define BUFFER_SIZE 256
			struct ast_channel *chan_found = NULL;
			char channel[BUFFER_SIZE];
			char *curkey = &dbe->key[parselen + 1], *slash;
			if (*curkey == '/') {
				curkey++;
			}
			if ((slash = strchr(curkey, '/'))) {
				*slash = '\0';
			}
			if (!strcasecmp(last, curkey)) {
				continue;
			}
			last = curkey;
			if (ast_strlen_zero(curkey)) {
				ast_debug(1, "Key is empty\n");
				continue;
			}
			if (ast_db_get(family, curkey, channel, BUFFER_SIZE)) {
				ast_log(LOG_WARNING, "%s: Couldn't find %s/%s in database\n", cmd, family, curkey);
				continue;
			}
			if (prune != 2) { 
				chan_found = ast_channel_get_by_name(channel);
				if (chan_found) {
					ast_channel_unref(chan_found);
					if (prune) {
						continue;
					}
					ast_copy_string(buf, curkey, len);
					found = 1;
					break;
				}
			}
			if (prune == 2) { 
				int keyepoch = atoi(curkey);
				if (!(keyepoch < epochparse)) { 
					ast_debug(1, "%s: %d is newer than %d, not old enough to purge\n", cmd, keyepoch, epochparse);
					continue;
				}
				ast_debug(1, "%s: %d is older than %d, okay to purge\n", cmd, keyepoch, epochparse);
			} else {
				ast_debug(1, "%s: Channel %s doesn't exist, saying goodbye\n", cmd, channel);
			}
			pruned++;
			if (ast_db_del(family, curkey)) {
				ast_log(LOG_WARNING, "%s: %s/%s could not be deleted from the database\n", cmd, family, curkey);
			} else if (parallel) {
				ast_debug(1, "%s: Also saying goodbye to %s/%s, if it exists\n", cmd, parallel, curkey);
				if (!ast_db_get(parallel, curkey, buf, len - 1)) {
					if (ast_db_del(parallel, curkey)) {
						ast_debug(1, "%s/%s could not be deleted from the database\n", parallel, curkey);
					}
				}
			}
		}
		ast_db_freetree(orig_dbe);
		if (found) {
			break;
		}
	}
	if (prune) { 
		snprintf(buf, len, "%d", pruned);
	}
	return 0;
}
static int db_chan_helper ( struct ast_channel * chan , const char * cmd , char * parse , char * buf , size_t len , int prune ) { struct ast_db_entry * dbe , * orig_dbe ; char * family ; const char * last = " " ; int pruned = 0 ; int epochparse = 0 , found = 0 ; if ( prune == 2 ) { char * epochthreshold = strsep ( & parse , " " ) ; epochparse = atoi ( epochthreshold ) ; if ( ! epochparse ) { ast_log ( LOG_WARNING , " \n " , epochthreshold ) ; return -1 ; } ast_debug ( 2 , " \n " , epochparse ) ; } while ( ( family = strsep ( & parse , " " ) ) ) { size_t parselen = strlen ( family ) ; char * parallel = family ; family = strsep ( & parallel , " " ) ; ast_debug ( 1 , " \n " , cmd , family , parallel ? parallel : " " ) ; if ( parallel && ! strcmp ( family , parallel ) ) { ast_log ( LOG_WARNING , " \n " , cmd , family , parallel ) ; continue ; } if ( family [ strlen ( family ) - 1 ] == ' ' ) { ast_log ( LOG_WARNING , " " , cmd , family ) ; continue ; } if ( parallel && parallel [ strlen ( parallel ) - 1 ] == ' ' ) { ast_log ( LOG_WARNING , " " , cmd , parallel ) ; continue ; } while ( family [ 0 ] == ' ' ) { family ++ ; parselen -- ; } while ( family [ parselen - 1 ] == ' ' ) { family [ -- parselen ] = ' \0 ' ; } if ( ! ( orig_dbe = dbe = ast_db_gettree ( family , NULL ) ) ) { ast_debug ( 1 , " \n " , family ) ; continue ; } for ( ; dbe ; dbe = dbe -> next ) { #define BUFFER_SIZE  256 struct ast_channel * chan_found = NULL ; char channel [ BUFFER_SIZE ] ; char * curkey = & dbe -> key [ parselen + 1 ] , * slash ; if ( * curkey == ' ' ) { curkey ++ ; } if ( ( slash = strchr ( curkey , ' ' ) ) ) { * slash = ' \0 ' ; } if ( ! strcasecmp ( last , curkey ) ) { continue ; } last = curkey ; if ( ast_strlen_zero ( curkey ) ) { ast_debug ( 1 , " \n " ) ; continue ; } if ( ast_db_get ( family , curkey , channel , BUFFER_SIZE ) ) { ast_log ( LOG_WARNING , " \n " , cmd , family , curkey ) ; continue ; } if ( prune != 2 ) { chan_found = ast_channel_get_by_name ( channel ) ; if ( chan_found ) { ast_channel_unref ( chan_found ) ; if ( prune ) { continue ; } ast_copy_string ( buf , curkey , len ) ; found = 1 ; break ; } } if ( prune == 2 ) { int keyepoch = atoi ( curkey ) ; if ( ! ( keyepoch < epochparse ) ) { ast_debug ( 1 , " \n " , cmd , keyepoch , epochparse ) ; continue ; } ast_debug ( 1 , " \n " , cmd , keyepoch , epochparse ) ; } else { ast_debug ( 1 , " \n " , cmd , channel ) ; } pruned ++ ; if ( ast_db_del ( family , curkey ) ) { ast_log ( LOG_WARNING , " \n " , cmd , family , curkey ) ; } else if ( parallel ) { ast_debug ( 1 , " \n " , cmd , parallel , curkey ) ; if ( ! ast_db_get ( parallel , curkey , buf , len - 1 ) ) { if ( ast_db_del ( parallel , curkey ) ) { ast_debug ( 1 , " \n " , parallel , curkey ) ; } } } } ast_db_freetree ( orig_dbe ) ; if ( found ) { break ; } } if ( prune ) { snprintf ( buf , len , " " , pruned ) ; } return 0 ; }
DOCUMENTATION
<function name="DB_CHANNEL" language="en_US">
<synopsis>
Return the name of the oldest alive channel in an AstDB family
where the values correspond to channels.
DOCUMENTATION <function name = " DB_CHANNEL " language = " en_US " > <synopsis > Return the name of the oldest alive channel in an AstDB family where the values correspond to channels .
/* DB_CHANNEL_PRUNE_TIME */ /* Remove leading and trailing slashes */ /* Nothing within the database at that prefix? */ /* Find the current component */ /* Remove everything after the current component */ /* Skip duplicates */ /* skip for DB_CHANNEL_PRUNE_TIME, because the values aren't channel names */ /* main difference between DB_CHANNEL and DB_CHANNEL_PRUNE is latter always checks all keys.
						Former stops if/when we find a match. */ /* channel doesn't exist anymore: delete key and continue */ /* but only if it's staler than the required epoch time */ /* if not older than threshold, don't delete */ /* DB_CHANNEL_PRUNE returns # of channels pruned. */