[Asterisk-code-review] pbx: Add support for autohints. (asterisk[master])

Joshua Colp asteriskteam at digium.com
Wed Mar 30 14:56:00 CDT 2016


Joshua Colp has uploaded a new change for review.

  https://gerrit.asterisk.org/2499

Change subject: pbx: Add support for autohints.
......................................................................

pbx: Add support for autohints.

This change introduces the concept of autohints. These are hints
which are created as a result of device state changes occurring within
the core. When this happens a hint will be created (if it does not
exist already) using the device name as the extension.

For example if a device state change is received for "PJSIP/bob"
and autohints are enabled on a context then a hint will exist in
that context for "bob" with a device of "PJSIP/bob".

This functionality can be enabled in extensions.conf by placing
"autohints=yes" in a context.

ASTERISK-25881 #close

Change-Id: I7e444c7da41b7b7d33374420fec658beeb18584e
---
M CHANGES
M include/asterisk/pbx.h
M main/pbx.c
M pbx/pbx_config.c
4 files changed, 221 insertions(+), 13 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/99/2499/1

diff --git a/CHANGES b/CHANGES
index f44df32..900893f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -157,6 +157,11 @@
    - 'media cache delete <item>' - remove an item from the cache
    - 'media cache create <uri>' - retrieve a URI and store it in the cache
 
+ * The ability for hints to be automatically created as a result of device state
+   changes now exists in the PBX. This functionality is referred to as "autohints"
+   and is configurable in extensions.conf by placing "autohints=yes" in the
+   context. If enabled then a hint will be automatically created with the name of
+   device.
 
 Functions
 ------------------
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index f5feb93..0ee1159 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -288,6 +288,14 @@
 struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar);
 
 /*!
+ * \brief Enable autohints support on a context
+ *
+ * \param con pointer to the context
+ *
+ */
+void ast_context_enable_autohints(struct ast_context *con);
+
+/*!
  * \brief Merge the temporary contexts into a global contexts list and delete from the
  *        global list the ones that are being added
  *
diff --git a/main/pbx.c b/main/pbx.c
index 479455e..925c167 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -317,6 +317,7 @@
 	struct ast_ignorepat *ignorepats;	/*!< Patterns for which to continue playing dialtone */
 	char *registrar;			/*!< Registrar -- make sure you malloc this, as the registrar may have to survive module unloads */
 	int refcount;                   /*!< each module that would have created this context should inc/dec this as appropriate */
+	int autohints;                  /*!< Whether autohints support is enabled or not */
 	AST_LIST_HEAD_NOLOCK(, ast_sw) alts;	/*!< Alternative switches */
 	ast_mutex_t macrolock;			/*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
 	char name[0];				/*!< Name of the context */
@@ -400,6 +401,19 @@
 	char hintdevice[1];
 };
 
+/*! \brief Container for autohint contexts */
+static struct ao2_container *autohints;
+
+/*!
+ * \brief Structure for dial plan autohints
+ */
+struct ast_autohint {
+	/*! \brief Name of the registrar */
+	char *registrar;
+	/*! \brief Name of the context */
+	char context[1];
+};
+
 /*!
  * \note Using the device for hash
  */
@@ -423,6 +437,7 @@
 
 	return ast_str_case_hash(key);
 }
+
 /*!
  * \note Devices on hints are not unique so no CMP_STOP is returned
  * Dont use ao2_find against hintdevices container cause there always
@@ -455,6 +470,59 @@
 		break;
 	}
 	return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \note Using the context name for hash
+ */
+static int autohint_hash_cb(const void *obj, const int flags)
+{
+	const struct ast_autohint *autohint;
+	const char *key;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_KEY:
+		key = obj;
+		break;
+	case OBJ_SEARCH_OBJECT:
+		autohint = obj;
+		key = autohint->context;
+		break;
+	default:
+		ast_assert(0);
+		return 0;
+	}
+
+	return ast_str_case_hash(key);
+}
+
+static int autohint_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_autohint *left = obj;
+	struct ast_autohint *right = arg;
+	const char *right_key = arg;
+	int cmp;
+
+	switch (flags & OBJ_SEARCH_MASK) {
+	case OBJ_SEARCH_OBJECT:
+		right_key = right->context;
+		/* Fall through */
+	case OBJ_SEARCH_KEY:
+		cmp = strcasecmp(left->context, right_key);
+		break;
+	case OBJ_SEARCH_PARTIAL_KEY:
+		/*
+		* We could also use a partial key struct containing a length
+		* so strlen() does not get called for every comparison instead.
+		*/
+		cmp = strncmp(left->context, right_key, strlen(right_key));
+		break;
+	default:
+		ast_assert(0);
+		cmp = 0;
+		break;
+	}
+	return cmp ? 0 : CMP_MATCH | CMP_STOP;
 }
 
 /*! \internal \brief \c ao2_callback function to remove hintdevices */
@@ -2334,6 +2402,7 @@
 	struct ast_ignorepat *ignorepats;
 	const char *registrar;
 	int refcount;
+	int autohints;
 	AST_LIST_HEAD_NOLOCK(, ast_sw) alts;
 	ast_mutex_t macrolock;
 	char name[256];
@@ -3460,6 +3529,11 @@
 	struct ast_hintdevice *device;
 	struct ast_hintdevice *cmpdevice;
 	struct ao2_iterator *dev_iter;
+	struct ao2_iterator auto_iter;
+	struct ast_autohint *autohint;
+	char *virtual_device;
+	char *type;
+	char *device_name;
 
 	if (handle_hint_change_message_type(msg, AST_HINT_UPDATE_DEVICE)) {
 		return;
@@ -3475,7 +3549,7 @@
 		return;
 	}
 
-	if (ao2_container_count(hintdevices) == 0) {
+	if (ao2_container_count(hintdevices) == 0 && ao2_container_count(autohints) == 0) {
 		/* There are no hints monitoring devices. */
 		return;
 	}
@@ -3489,25 +3563,59 @@
 	strcpy(cmpdevice->hintdevice, dev_state->device);
 
 	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
+
+	/* Initially we find all hints for the device and notify them */
 	dev_iter = ao2_t_callback(hintdevices,
 		OBJ_SEARCH_OBJECT | OBJ_MULTIPLE,
 		hintdevice_cmp_multiple,
 		cmpdevice,
 		"find devices in container");
-	if (!dev_iter) {
-		ast_mutex_unlock(&context_merge_lock);
-		ast_free(hint_app);
-		return;
-	}
-
-	for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
-		if (device->hint) {
-			device_state_notify_callbacks(device->hint, &hint_app);
+	if (dev_iter) {
+		for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
+			if (device->hint) {
+				device_state_notify_callbacks(device->hint, &hint_app);
+			}
 		}
+		ao2_iterator_destroy(dev_iter);
 	}
-	ast_mutex_unlock(&context_merge_lock);
 
-	ao2_iterator_destroy(dev_iter);
+	/* Second stage we look for any autohint contexts and if the device is not already in the hints
+	 * we create it.
+	 */
+	type = ast_strdupa(dev_state->device);
+	if (ast_strlen_zero(type)) {
+		goto end;
+	}
+
+	/* We only want actual devices for autohint functionality */
+	device_name = strchr(type, '/');
+	if (ast_strlen_zero(device_name)) {
+		goto end;
+	}
+	*device_name++ = '\0';
+
+	virtual_device = strchr(type, ':');
+	if (virtual_device && (virtual_device < device_name)) {
+		goto end;
+	}
+
+	auto_iter = ao2_iterator_init(autohints, 0);
+	for (; (autohint = ao2_iterator_next(&auto_iter)); ao2_t_ref(autohint, -1, "Next autohint")) {
+		if (ast_get_hint(NULL, 0, NULL, 0, NULL, autohint->context, device_name)) {
+			continue;
+		}
+
+		/* The device has no hint in the context referenced by this autohint so create one */
+		ast_add_extension(autohint->context, 0, device_name,
+			PRIORITY_HINT, NULL, NULL, dev_state->device,
+			ast_strdup(dev_state->device), ast_free_ptr, autohint->registrar);
+
+		/* Since this hint was just created there are no watchers, so we don't need to notify anyone */
+	}
+	ao2_iterator_destroy(&auto_iter);
+
+end:
+	ast_mutex_unlock(&context_merge_lock);
 	ast_free(hint_app);
 	return;
 }
@@ -5322,6 +5430,9 @@
 			dpc->total_context++;
 			ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
 				ast_get_context_name(c), ast_get_context_registrar(c));
+			if (c->autohints) {
+				ast_cli(fd, "Autohints support enabled\n");
+			}
 			context_info_printed = 1;
 		}
 
@@ -5344,6 +5455,9 @@
 				} else {
 					ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
 						ast_get_context_name(c), ast_get_context_registrar(c));
+					if (c->autohints) {
+						ast_cli(fd, "Autohints support enabled\n");
+					}
 				}
 				context_info_printed = 1;
 			}
@@ -6016,6 +6130,11 @@
 	return tmp;
 }
 
+void ast_context_enable_autohints(struct ast_context *con)
+{
+	con->autohints = 1;
+}
+
 void __ast_context_destroy(struct ast_context *list, struct ast_hashtab *contexttab, struct ast_context *con, const char *registrar);
 
 struct store_hint {
@@ -6063,6 +6182,27 @@
 	}
 }
 
+/*! Set up an autohint placeholder in the hints container */
+static void context_create_autohint_placeholder(struct ast_context *con)
+{
+	struct ast_autohint *autohint;
+	size_t name_len = strlen(con->name) + 1;
+	size_t registrar_len = strlen(con->registrar) + 1;
+
+	autohint = ao2_alloc_options(sizeof(*autohint) + name_len + registrar_len, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!autohint) {
+		return;
+	}
+
+	ast_copy_string(autohint->context, con->name, name_len);
+	autohint->registrar = autohint->context + name_len;
+	ast_copy_string(autohint->registrar, con->registrar, registrar_len);
+
+	ao2_link(autohints, autohint);
+	ao2_ref(autohint, -1);
+
+	ast_verb(3, "Enabled autohints support on context '%s'\n", con->name);
+}
 
 /* the purpose of this routine is to duplicate a context, with all its substructure,
    except for any extens that have a matching registrar */
@@ -6104,6 +6244,9 @@
 				/* make sure the new context exists, so we have somewhere to stick this exten/prio */
 				if (!new) {
 					new = ast_context_find_or_create(extcontexts, exttable, context->name, prio_item->registrar); /* a new context created via priority from a different context in the old dialplan, gets its registrar from the prio's registrar */
+					if (new) {
+						new->autohints = context->autohints;
+					}
 				}
 
 				/* copy in the includes, switches, and ignorepats */
@@ -6150,6 +6293,10 @@
 		   but that's not available, so we give it the registrar we know about */
 		new = ast_context_find_or_create(extcontexts, exttable, context->name, context->registrar);
 
+		if (new) {
+			new->autohints = context->autohints;
+		}
+
 		/* copy in the includes, switches, and ignorepats */
 		context_merge_incls_swits_igps_other_registrars(new, context, registrar);
 	}
@@ -6194,6 +6341,16 @@
 	ast_wrlock_contexts();
 
 	if (!contexts_table) {
+		/* Create any autohint contexts */
+		iter = ast_hashtab_start_traversal(exttable);
+		while ((tmp = ast_hashtab_next(iter))) {
+			if (!tmp->autohints) {
+				continue;
+			}
+			context_create_autohint_placeholder(tmp);
+		}
+		ast_hashtab_end_traversal(iter);
+
 		/* Well, that's odd. There are no contexts. */
 		contexts_table = exttable;
 		contexts = *extcontexts;
@@ -6315,6 +6472,19 @@
 			ast_free(saved_hint);
 		}
 	}
+
+	/* Remove all autohints as the below iteration will recreate them */
+	ao2_callback(autohints, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
+
+	/* Create all applicable autohint contexts */
+	iter = ast_hashtab_start_traversal(contexts_table);
+	while ((tmp = ast_hashtab_next(iter))) {
+		if (!tmp->autohints) {
+			continue;
+		}
+		context_create_autohint_placeholder(tmp);
+	}
+	ast_hashtab_end_traversal(iter);
 
 	ao2_unlock(hints);
 	ast_unlock_contexts();
@@ -8526,6 +8696,11 @@
 		ao2_ref(hintdevices, -1);
 		hintdevices = NULL;
 	}
+	if (autohints) {
+		ao2_container_unregister("autohints");
+		ao2_ref(autohints, -1);
+		autohints = NULL;
+	}
 	if (statecbs) {
 		ao2_container_unregister("statecbs");
 		ao2_ref(statecbs, -1);
@@ -8559,6 +8734,16 @@
 		ast_get_context_name(ast_get_extension_context(hintdevice->hint->exten)));
 }
 
+static void print_autohint_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
+{
+	struct ast_autohint *autohint = v_obj;
+
+	if (!autohint) {
+		return;
+	}
+	prnt(where, "%s", autohint->context);
+}
+
 static void print_statecbs_key(void *v_obj, void *where, ao2_prnt_fn *prnt)
 {
 	struct ast_state_cb *state_cb = v_obj;
@@ -8579,6 +8764,12 @@
 	if (hintdevices) {
 		ao2_container_register("hintdevices", hintdevices, print_hintdevices_key);
 	}
+	/* This is protected by the context_and_merge lock */
+	autohints = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, HASH_EXTENHINT_SIZE,
+		autohint_hash_cb, autohint_cmp);
+	if (hintdevices) {
+		ao2_container_register("autohints", autohints, print_autohint_key);
+	}
 	statecbs = ao2_container_alloc(1, NULL, statecbs_cmp);
 	if (statecbs) {
 		ao2_container_register("statecbs", statecbs, print_statecbs_key);
@@ -8590,5 +8781,5 @@
 		return -1;
 	}
 
-	return (hints && hintdevices && statecbs) ? 0 : -1;
+	return (hints && hintdevices && autohints && statecbs) ? 0 : -1;
 }
diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c
index 5848e91..d4bac6a 100644
--- a/pbx/pbx_config.c
+++ b/pbx/pbx_config.c
@@ -1900,6 +1900,10 @@
 						"Unable to include switch '%s' in context '%s' at line %d of %s\n",
 						v->value, cxt, v->lineno, vfile);
 				}
+			} else if (!strcasecmp(v->name, "autohints")) {
+				if (ast_true(v->value)) {
+					ast_context_enable_autohints(con);
+				}
 			} else {
 				ast_log(LOG_WARNING,
 					"==!!== Unknown directive: %s at line %d of %s -- IGNORING!!!\n",

-- 
To view, visit https://gerrit.asterisk.org/2499
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I7e444c7da41b7b7d33374420fec658beeb18584e
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Joshua Colp <jcolp at digium.com>



More information about the asterisk-code-review mailing list