[asterisk-commits] russell: branch russell/sla_rewrite r52136 - in /team/russell/sla_rewrite: ap...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Wed Jan 24 17:15:25 MST 2007


Author: russell
Date: Wed Jan 24 18:15:24 2007
New Revision: 52136

URL: http://svn.digium.com/view/asterisk?view=rev&rev=52136
Log:
Commit progress on the SLA code.  I'm pretty close to getting the correct
behavior when a call comes in on a trunk ...

Modified:
    team/russell/sla_rewrite/apps/app_meetme.c
    team/russell/sla_rewrite/include/asterisk/dial.h
    team/russell/sla_rewrite/main/dial.c

Modified: team/russell/sla_rewrite/apps/app_meetme.c
URL: http://svn.digium.com/view/asterisk/team/russell/sla_rewrite/apps/app_meetme.c?view=diff&rev=52136&r1=52135&r2=52136
==============================================================================
--- team/russell/sla_rewrite/apps/app_meetme.c (original)
+++ team/russell/sla_rewrite/apps/app_meetme.c Wed Jan 24 18:15:24 2007
@@ -60,6 +60,7 @@
 #include "asterisk/ulaw.h"
 #include "asterisk/astobj.h"
 #include "asterisk/devicestate.h"
+#include "asterisk/dial.h"
 
 #include "enter.h"
 #include "leave.h"
@@ -182,7 +183,7 @@
 	AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
 	AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
 	AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
-END_OPTIONS);
+END_OPTIONS );
 
 static const char *app = "MeetMe";
 static const char *app2 = "MeetMeCount";
@@ -333,19 +334,13 @@
 	AST_LIST_ENTRY(ast_conf_user) list;
 };
 
-struct sla_trunk {
-	AST_RWLIST_ENTRY(sla_trunk) entry;
-	AST_DECLARE_STRING_FIELDS(
-		AST_STRING_FIELD(name);
-		AST_STRING_FIELD(device);
-		AST_STRING_FIELD(autocontext);	
-	);
+enum sla_trunk_state {
+	SLA_TRUNK_STATE_IDLE,
+	SLA_TRUNK_STATE_RINGING,
+	SLA_TRUNK_STATE_UP,
 };
 
-struct sla_trunk_ref {
-	struct sla_trunk *trunk;
-	AST_LIST_ENTRY(sla_trunk_ref) entry;
-};
+struct sla_trunk_ref;
 
 struct sla_station {
 	AST_RWLIST_ENTRY(sla_station) entry;
@@ -355,6 +350,30 @@
 		AST_STRING_FIELD(autocontext);	
 	);
 	AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
+};
+
+struct sla_station_ref {
+	AST_LIST_ENTRY(sla_station_ref) entry;
+	struct sla_station *station;
+};
+
+struct sla_trunk {
+	AST_RWLIST_ENTRY(sla_trunk) entry;
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(name);
+		AST_STRING_FIELD(device);
+		AST_STRING_FIELD(autocontext);	
+	);
+	AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
+	unsigned int num_stations;
+	enum sla_trunk_state state;
+	pthread_t station_thread;
+};
+
+struct sla_trunk_ref {
+	AST_LIST_ENTRY(sla_trunk_ref) entry;
+	struct sla_trunk *trunk;
+	enum sla_trunk_state state;
 };
 
 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
@@ -867,11 +886,18 @@
 	            "-------------------------------------------------------------\n\n");
 	AST_RWLIST_RDLOCK(&sla_trunks);
 	AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+		struct sla_station_ref *station_ref;
 		ast_cli(fd, "--- Trunk Name:      %s\n"
 		            "--- ==> Device:      %s\n"
-					"--- ==> AutoContext: %s\n\n", 
+					"--- ==> AutoContext: %s\n"
+					"--- ==> Stations ...\n",
 					trunk->name, trunk->device, 
 					S_OR(trunk->autocontext, "(none)"));
+		AST_RWLIST_RDLOCK(&sla_stations);
+		AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
+			ast_cli(fd, "--- =====> Station name: %s\n", station_ref->station->name);
+		AST_RWLIST_UNLOCK(&sla_stations);
+		ast_cli(fd, "\n");
 	}
 	AST_RWLIST_UNLOCK(&sla_trunks);
 	ast_cli(fd, "-------------------------------------------------------------\n");
@@ -2726,9 +2752,162 @@
 	ast_config_destroy(cfg);
 }
 
+static const char *trunkstate2str(enum sla_trunk_state state)
+{
+#define S(e) case e: return # e;
+	switch (state) {
+	S(SLA_TRUNK_STATE_IDLE)
+	S(SLA_TRUNK_STATE_RINGING)
+	S(SLA_TRUNK_STATE_UP)
+	}
+	return "Uknown State";
+#undef S
+}
+
+/*! \brief Find an SLA trunk by name
+ * \note This must be called with the sla_trunks container locked
+ */
+static struct sla_trunk *find_trunk(const char *name)
+{
+	struct sla_trunk *trunk = NULL;
+
+	AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+		if (!strcasecmp(trunk->name, name))
+			break;
+	}
+
+	return trunk;
+}
+
 static int slastation_exec(struct ast_channel *chan, void *data)
 {
 	return 0;
+}
+
+static void *dial_stations(void *data)
+{
+	struct sla_trunk *trunk = data;
+	struct sla_station_ref *station_ref;
+	struct ast_dial **dials;
+	struct sla_trunk_ref **trunk_refs;
+	struct ast_channel *chan;
+	struct ast_flags conf_flags;
+	struct ast_conference *conf;
+	int res = 0, len, num_dials = 0, i, winner;
+	char conf_name[MAX_CONFNUM];
+
+	len = (trunk->num_stations + 1) * sizeof(*dials);
+	ast_log(LOG_DEBUG, "len is %d\n", len);
+	dials = alloca(len);
+	memset(dials, 0, len);
+	trunk_refs = alloca(len);
+	memset(trunk_refs, 0, len);
+
+	AST_RWLIST_RDLOCK(&sla_stations);
+	AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+		struct ast_dial *dial;
+		char *tech, *tech_data;
+		struct sla_trunk_ref *trunk_ref;
+
+		if (!(dial = ast_dial_create())) {
+			res = -1;
+			break;
+		}
+
+		tech_data = ast_strdupa(station_ref->station->device);
+		tech = strsep(&tech_data, "/");
+		ast_dial_append(dial, tech, tech_data);
+
+		AST_LIST_TRAVERSE(&station_ref->station->trunks, trunk_ref, entry) {
+			if (trunk == trunk_ref->trunk)
+				break;
+		}
+		if (!trunk_ref) {
+			res = -1;
+			ast_log(LOG_ERROR, "Trunk referenced this station, "
+				"but the station didn't reference the trunk!\n");
+			break;
+		}
+		trunk_refs[num_dials] = trunk_ref;
+		dials[num_dials++] = dial;
+	}
+	AST_RWLIST_UNLOCK(&sla_stations);
+
+	if (res) {
+		for (i = 0; dials[i]; i++)
+			ast_dial_destroy(dials[i]);
+		return NULL;
+	}
+
+	for (i = 0; dials[i]; i++) {
+		enum ast_dial_result dial_res;
+		dial_res = ast_dial_run(dials[i], NULL, 1);
+		if (dial_res != AST_DIAL_RESULT_TRYING) {
+			ast_log(LOG_ERROR, "Failed to begin dial to station!\n");
+			res = -1;
+			break;
+		}
+	}
+
+	if (res) {
+		for (i = 0; dials[i]; i++) {
+			ast_dial_join(dials[i]);
+			ast_dial_destroy(dials[i]);
+		}
+		return NULL;
+	}
+
+	for (i = 0, winner = -1; ; i = (i + 1) % num_dials) {
+		if (!dials[i])
+			continue;
+
+		switch (ast_dial_status(dials[i])) {
+		case AST_DIAL_RESULT_HANGUP:
+			trunk_refs[i]->state = SLA_TRUNK_STATE_IDLE;
+		case AST_DIAL_RESULT_INVALID:
+		case AST_DIAL_RESULT_FAILED:
+		case AST_DIAL_RESULT_TIMEOUT:
+		case AST_DIAL_RESULT_UNANSWERED:
+			ast_dial_join(dials[i]);
+			ast_dial_destroy(dials[i]);
+			dials[i] = NULL;
+			break;
+		case AST_DIAL_RESULT_TRYING:
+			break;
+		case AST_DIAL_RESULT_RINGING:
+		case AST_DIAL_RESULT_PROGRESS:
+		case AST_DIAL_RESULT_PROCEEDING:
+			trunk_refs[i]->state = SLA_TRUNK_STATE_RINGING;
+			break;
+		case AST_DIAL_RESULT_ANSWERED:
+			winner = i;
+			break;
+		}
+		if (winner > -1)
+			break;
+	}
+
+	for (i = 0; i < num_dials; i++) {
+		if (!dials[i] || i == winner)
+			continue;
+		ast_dial_join(dials[i]);
+		ast_dial_destroy(dials[i]);
+		dials[i] = NULL;
+	}
+
+	chan = ast_dial_answered(dials[winner]);
+	ast_set_flag(&conf_flags,
+		CONFFLAG_QUIET |
+		CONFFLAG_MARKEDEXIT);
+	snprintf(conf_name, sizeof(conf_name), "SLA-%s", trunk->name);
+	conf = build_conf(conf_name, "", "", 0, 0, 1);
+	if (conf)
+		res = conf_run(chan, conf, conf_flags.flags, NULL);
+	trunk_refs[winner]->state = SLA_TRUNK_STATE_IDLE;
+	ast_dial_join(dials[winner]);
+	ast_dial_destroy(dials[winner]);
+
+	return NULL;
 }
 
 static int slatrunk_exec(struct ast_channel *chan, void *data)
@@ -2737,33 +2916,94 @@
 	char conf_name[MAX_CONFNUM];
 	struct ast_conference *conf;
 	int res;
-	struct ast_flags conf_flags = { 0 };
+	struct ast_flags conf_flags;
+	struct sla_trunk *trunk;
+
+	AST_RWLIST_RDLOCK(&sla_trunks);
+	trunk = find_trunk(trunk_name);
+	if (!trunk) {
+		ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name);
+		AST_RWLIST_UNLOCK(&sla_trunks);
+		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
+		return 0;
+	}
+	if (trunk->state != SLA_TRUNK_STATE_IDLE) {
+		ast_log(LOG_ERROR, "Call came in on %s, but the trunk is in state %s?!\n",
+			trunk_name, trunkstate2str(trunk->state));
+		AST_RWLIST_UNLOCK(&sla_trunks);
+		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
+		return 0;
+	}
+	AST_RWLIST_UNLOCK(&sla_trunks);
 
 	snprintf(conf_name, sizeof(conf_name), "SLA-%s", trunk_name);
-
-	conf = build_conf(conf_name, NULL, NULL, 
-		1 /* make it! */, 1 /* dynamic */, 1 /* refcount++ */); 
+	conf = build_conf(conf_name, "", "", 1, 1, 1);
 	if (!conf) {
 		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
 		return 0;
 	}
 
-	/* Set flags for the conf */
-
+	if (ast_pthread_create_background(&trunk->station_thread, NULL, dial_stations, trunk)) {
+		pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
+		return 0;
+	}
+
+	ast_set_flag(&conf_flags, 
+		CONFFLAG_QUIET |       /* We don't want sounds played to the SLA */
+		CONFFLAG_MARKEDEXIT |  /* Close the conference when the trunk leaves */
+		CONFFLAG_MARKEDUSER);  /* Set the trunk as the "marked" user, so others will
+		                        * exit when the trunk leaves. */
+
+	ast_indicate(chan, AST_CONTROL_RINGING);
+	trunk->state = SLA_TRUNK_STATE_RINGING;
 	res = conf_run(chan, conf, conf_flags.flags, NULL);
+	trunk->state = SLA_TRUNK_STATE_IDLE;
 
 	return 0;
 }
 
 static int sla_state(const char *data)
 {
-	return AST_DEVICE_INUSE;
+	char *buf, *station_name, *trunk_name;
+	struct sla_station *station;
+	struct sla_trunk_ref *trunk_ref;
+	int res = AST_DEVICE_INVALID;
+
+	trunk_name = buf = ast_strdupa(data);
+	station_name = strsep(&trunk_name, "_");
+
+	AST_RWLIST_RDLOCK(&sla_stations);
+	AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+		if (strcasecmp(station_name, station->name))
+			continue;
+		AST_RWLIST_RDLOCK(&sla_trunks);
+		AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+			if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
+				break;
+		}
+		if (trunk_ref)
+			res = trunk_ref->state;
+		AST_RWLIST_UNLOCK(&sla_trunks);
+	}
+	AST_RWLIST_UNLOCK(&sla_stations);
+
+	if (res == AST_DEVICE_INVALID) {
+		ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
+			trunk_name, station_name);
+	}
+
+	return res;
 }
 
 static void destroy_trunk(struct sla_trunk *trunk)
 {
+	struct sla_station_ref *station_ref;
+
 	if (!ast_strlen_zero(trunk->autocontext))
 		ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
+
+	while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
+		free(station_ref);
 
 	ast_string_field_free_all(trunk);
 	free(trunk);
@@ -2811,6 +3051,19 @@
 	AST_RWLIST_UNLOCK(&sla_stations);
 }
 
+static int check_device(const char *device)
+{
+	char *tech, *tech_data;
+
+	tech_data = ast_strdupa(device);
+	tech = strsep(&tech_data, "/");
+
+	if (ast_strlen_zero(tech) || ast_strlen_zero(tech_data))
+		return -1;
+
+	return 0;
+}
+
 static int build_trunk(struct ast_config *cfg, const char *cat)
 {
 	struct sla_trunk *trunk;
@@ -2819,6 +3072,12 @@
 
 	if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
 		ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
+		return -1;
+	}
+
+	if (check_device(dev)) {
+		ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
+			cat, dev);
 		return -1;
 	}
 
@@ -2891,6 +3150,7 @@
 		if (!strcasecmp(var->name, "trunk")) {
 			struct sla_trunk *trunk;
 			struct sla_trunk_ref *trunk_ref;
+			struct sla_station_ref *station_ref;
 			AST_RWLIST_RDLOCK(&sla_trunks);
 			AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
 				if (!strcasecmp(trunk->name, var->value))
@@ -2904,6 +3164,16 @@
 			if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
 				continue;
 			trunk_ref->trunk = trunk;
+			trunk_ref->state = AST_DEVICE_NOT_INUSE;
+			if (!(station_ref = ast_calloc(1, sizeof(*station_ref)))) {
+				free(trunk_ref);
+				continue;
+			}
+			station_ref->station = station;
+			AST_RWLIST_WRLOCK(&sla_trunks);
+			trunk->num_stations++;
+			AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
+			AST_RWLIST_UNLOCK(&sla_trunks);
 			AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
 		} else if (!strcasecmp(var->name, "autocontext")) {
 			ast_string_field_set(station, autocontext, var->value);

Modified: team/russell/sla_rewrite/include/asterisk/dial.h
URL: http://svn.digium.com/view/asterisk/team/russell/sla_rewrite/include/asterisk/dial.h?view=diff&rev=52136&r1=52135&r2=52136
==============================================================================
--- team/russell/sla_rewrite/include/asterisk/dial.h (original)
+++ team/russell/sla_rewrite/include/asterisk/dial.h Wed Jan 24 18:15:24 2007
@@ -42,7 +42,7 @@
 
 /*! \brief List of return codes for dial run API calls */
 enum ast_dial_result {
-	AST_DIAL_RESULT_INVALID = 0, /*!< Invalid options were passed to run function */
+	AST_DIAL_RESULT_INVALID,     /*!< Invalid options were passed to run function */
 	AST_DIAL_RESULT_FAILED,      /*!< Attempts to dial failed before reaching critical state */
 	AST_DIAL_RESULT_TRYING,      /*!< Currently trying to dial */
 	AST_DIAL_RESULT_RINGING,     /*!< Dial is presently ringing */

Modified: team/russell/sla_rewrite/main/dial.c
URL: http://svn.digium.com/view/asterisk/team/russell/sla_rewrite/main/dial.c?view=diff&rev=52136&r1=52135&r2=52136
==============================================================================
--- team/russell/sla_rewrite/main/dial.c (original)
+++ team/russell/sla_rewrite/main/dial.c Wed Jan 24 18:15:24 2007
@@ -228,33 +228,37 @@
 		ast_copy_string(numsubst, channel->device, sizeof(numsubst));
 
 		/* Request that the channel be created */
-		if (!(channel->owner = ast_request(channel->tech, chan->nativeformats, numsubst, &channel->cause)))
+		if (!(channel->owner = ast_request(channel->tech, 
+			chan ? chan->nativeformats : 0xFFFFFFFF, numsubst, &channel->cause))) {
 			continue;
+		}
 
 		channel->owner->appl = "AppDial2";
                 channel->owner->data = "(Outgoing Line)";
                 channel->owner->whentohangup = 0;
 
 		/* Inherit everything from he who spawned this Dial */
-		ast_channel_inherit_variables(chan, channel->owner);
-
-		/* Copy over callerid information */
-		S_REPLACE(channel->owner->cid.cid_num, ast_strdup(chan->cid.cid_num));
-		S_REPLACE(channel->owner->cid.cid_name, ast_strdup(chan->cid.cid_name));
-		S_REPLACE(channel->owner->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
-		S_REPLACE(channel->owner->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
-
-		ast_string_field_set(channel->owner, language, chan->language);
-		ast_string_field_set(channel->owner, accountcode, chan->accountcode);
-		channel->owner->cdrflags = chan->cdrflags;
-		if (ast_strlen_zero(channel->owner->musicclass))
-			ast_string_field_set(channel->owner, musicclass, chan->musicclass);
-
-		channel->owner->cid.cid_pres = chan->cid.cid_pres;
-		channel->owner->cid.cid_ton = chan->cid.cid_ton;
-		channel->owner->cid.cid_tns = chan->cid.cid_tns;
-		channel->owner->adsicpe = chan->adsicpe;
-		channel->owner->transfercapability = chan->transfercapability;
+		if (chan) {
+			ast_channel_inherit_variables(chan, channel->owner);
+
+			/* Copy over callerid information */
+			S_REPLACE(channel->owner->cid.cid_num, ast_strdup(chan->cid.cid_num));
+			S_REPLACE(channel->owner->cid.cid_name, ast_strdup(chan->cid.cid_name));
+			S_REPLACE(channel->owner->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
+			S_REPLACE(channel->owner->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
+	
+			ast_string_field_set(channel->owner, language, chan->language);
+			ast_string_field_set(channel->owner, accountcode, chan->accountcode);
+			channel->owner->cdrflags = chan->cdrflags;
+			if (ast_strlen_zero(channel->owner->musicclass))
+				ast_string_field_set(channel->owner, musicclass, chan->musicclass);
+	
+			channel->owner->cid.cid_pres = chan->cid.cid_pres;
+			channel->owner->cid.cid_ton = chan->cid.cid_ton;
+			channel->owner->cid.cid_tns = chan->cid.cid_tns;
+			channel->owner->adsicpe = chan->adsicpe;
+			channel->owner->transfercapability = chan->transfercapability;
+		}
 
 		/* Actually call the device */
 		if ((res = ast_call(channel->owner, numsubst, 0))) {
@@ -530,7 +534,7 @@
 	enum ast_dial_result res = AST_DIAL_RESULT_TRYING;
 
 	/* Ensure required arguments are passed */
-	if (!dial || !chan)
+	if (!dial || (!chan && !async))
 		return AST_DIAL_RESULT_INVALID;
 
 	/* If there are no channels to dial we can't very well try to dial them */



More information about the asterisk-commits mailing list