[svn-commits] jrose: trunk r390849 - in /trunk: ./ include/asterisk/ main/ res/ res/parking/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jun 7 11:07:22 CDT 2013


Author: jrose
Date: Fri Jun  7 11:07:18 2013
New Revision: 390849

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=390849
Log:
res_parking: Automatically generate extensions, hints, etc.

(closes issue ASTERISK-21645)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2545/

Modified:
    trunk/CHANGES
    trunk/include/asterisk/pbx.h
    trunk/main/features.c
    trunk/main/pbx.c
    trunk/res/parking/parking_bridge.c
    trunk/res/parking/parking_bridge_features.c
    trunk/res/parking/parking_controller.c
    trunk/res/parking/res_parking.h
    trunk/res/res_parking.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Jun  7 11:07:18 2013
@@ -187,6 +187,11 @@
    The preferred way to configure parking is now through res_parking.conf while
    configuration through features.conf is not currently supported.
 
+ * res_parking uses the configuration framework. If an invalid configuration is
+   supplied, res_parking will fail to load or fail to reload. Previously parking
+   lots that were misconfigured would generally be accepted with certain
+   configuration problems leading to individual disabled parking lots.
+
  * Parked calls are now placed in bridges. This is a largely architectural change,
    but it could have some implications in allowing for new parked call retrieval
    methods and the contents of parking lots will be visible though certain bridge
@@ -225,6 +230,11 @@
  * ParkAndAnnounce will no longer go to the next position in dialplan on timeout
    by default. Instead, it will follow the timeout rules of the parking lot. The
    old behavior can be reproduced by using the 'c' option.
+
+ * Added a channel variable PARKER_FLAT which stores the name of the extension
+   that would be used to come back to if comebacktoorigin was set to use. This can
+   be useful when comebacktoorigin is off if you still want to use the extensions
+   in the park-dial context that are generated to redial the parker on timeout.
 
 Queue
 -------------------

Modified: trunk/include/asterisk/pbx.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/pbx.h?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/include/asterisk/pbx.h (original)
+++ trunk/include/asterisk/pbx.h Fri Jun  7 11:07:18 2013
@@ -470,6 +470,17 @@
 	const char *application, void *data, void (*datad)(void *), const char *registrar);
 
 /*!
+ * \brief Same as ast_add_extension2, but assumes you have already locked context
+ * \since 12.0.0
+ *
+ * \note con must be write locked prior to calling. For details about the arguments,
+ *       check ast_add_extension2()
+ */
+int ast_add_extension2_nolock(struct ast_context *con, int replace, const char *extension,
+	int priority, const char *label, const char *callerid,
+	const char *application, void *data, void (*datad)(void *), const char *registrar);
+
+/*!
  * \brief Map devstate to an extension state.
  *
  * \param[in] devstate device state

Modified: trunk/main/features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/features.c?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/main/features.c (original)
+++ trunk/main/features.c Fri Jun  7 11:07:18 2013
@@ -362,8 +362,6 @@
 	FEATURE_INTERPRET_CHECK,  /* Used by feature_check */
 } feature_interpret_op;
 
-static const char *parkedcall = "ParkedCall";
-
 /*! Parking lot access ramp dialplan usage entry. */
 struct parking_dp_ramp {
 	/*! Next node in the parking lot spaces dialplan list. */
@@ -1482,13 +1480,6 @@
 
 	snprintf(app_data, sizeof(app_data), "%s,%s", pu->parkingexten,
 		pu->parkinglot->name);
-	if (ast_add_extension(pu->parkinglot->cfg.parking_con, 1, pu->parkingexten, 1,
-		NULL, NULL, parkedcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
-		ast_log(LOG_ERROR, "Could not create parked call exten: %s@%s\n",
-			pu->parkingexten, pu->parkinglot->cfg.parking_con);
-	} else {
-		notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con, AST_DEVICE_INUSE);
-	}
 
 	AST_LIST_UNLOCK(&pu->parkinglot->parkings);
 
@@ -3939,12 +3930,6 @@
 
 				pbx_builtin_setvar_helper(chan, "PARKER", peername);
 
-				if (ast_add_extension(parking_con_dial, 1, peername_flat, 1, NULL, NULL,
-					"Dial", ast_strdup(returnexten), ast_free_ptr, registrar)) {
-					ast_log(LOG_ERROR,
-						"Could not create parking return dial exten: %s@%s\n",
-						peername_flat, parking_con_dial);
-				}
 			}
 
 			snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
@@ -4274,25 +4259,6 @@
 	return newlot;
 }
 
-/*!
- * \brief Add parking hints for all defined parking spaces.
- * \param context Dialplan context to add the hints.
- * \param start Starting space in parkinglot.
- * \param stop Ending space in parkinglot.
- */
-static void park_add_hints(const char *context, int start, int stop)
-{
-	int numext;
-	char device[AST_MAX_EXTENSION];
-	char exten[10];
-
-	for (numext = start; numext <= stop; numext++) {
-		snprintf(exten, sizeof(exten), "%d", numext);
-		snprintf(device, sizeof(device), "park:%s@%s", exten, context);
-		ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar);
-	}
-}
-
 /*! Default configuration for default parking lot. */
 static const struct parkinglot_cfg parkinglot_cfg_default_default = {
 	.mohclass = "default",
@@ -4330,50 +4296,9 @@
  */
 static int parkinglot_activate(struct ast_parkinglot *parkinglot)
 {
-	int disabled = 0;
-	char app_data[5 + AST_MAX_CONTEXT];
-
-	/* Create Park option list.  Must match with struct park_app_args options. */
-	if (parkinglot->cfg.parkext_exclusive) {
-		/* Specify the parking lot this parking extension parks calls. */
-		snprintf(app_data, sizeof(app_data), ",,,,,%s", parkinglot->name);
-	} else {
-		/* The dialplan must specify which parking lot to use. */
-		app_data[0] = '\0';
-	}
-
-	/* Create context */
-	if (!ast_context_find_or_create(NULL, NULL, parkinglot->cfg.parking_con, registrar)) {
-		ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n",
-			parkinglot->cfg.parking_con);
-		disabled = 1;
-
-	/* Add a parking extension into the context */
-	} else if (ast_add_extension(parkinglot->cfg.parking_con, 1, parkinglot->cfg.parkext,
-		1, NULL, NULL, parkcall, ast_strdup(app_data), ast_free_ptr, registrar)) {
-		ast_log(LOG_ERROR, "Could not create parking lot %s access exten %s@%s\n",
-			parkinglot->name, parkinglot->cfg.parkext, parkinglot->cfg.parking_con);
-		disabled = 1;
-	} else {
-		/* Add parking hints */
-		if (parkinglot->cfg.parkaddhints) {
-			park_add_hints(parkinglot->cfg.parking_con, parkinglot->cfg.parking_start,
-				parkinglot->cfg.parking_stop);
-		}
-
-		/*
-		 * XXX Not sure why we should need to notify the metermaids for
-		 * this exten.  It was originally done for the default parking
-		 * lot entry exten only but should be done for all entry extens
-		 * if we do it for one.
-		 */
-		/* Notify metermaids about parking lot entry exten state. */
-		notify_metermaids(parkinglot->cfg.parkext, parkinglot->cfg.parking_con,
-			AST_DEVICE_INUSE);
-	}
-
-	parkinglot->disabled = disabled;
-	return disabled ? -1 : 0;
+	/* XXX All parking stuff is being replaced by res_parking */
+	parkinglot->disabled = 1;
+	return -1;
 }
 
 int ast_features_reload(void)

Modified: trunk/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/pbx.c?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/main/pbx.c (original)
+++ trunk/main/pbx.c Fri Jun  7 11:07:18 2013
@@ -9612,6 +9612,16 @@
 		application, data, datad, registrar, 1);
 }
 
+int ast_add_extension2_nolock(struct ast_context *con,
+	int replace, const char *extension, int priority, const char *label, const char *callerid,
+	const char *application, void *data, void (*datad)(void *),
+	const char *registrar)
+{
+	return ast_add_extension2_lockopt(con, replace, extension, priority, label, callerid,
+		application, data, datad, registrar, 0);
+}
+
+
 /*!
  * \brief Same as ast_add_extension2() but controls the context locking.
  *

Modified: trunk/res/parking/parking_bridge.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_bridge.c?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/res/parking/parking_bridge.c (original)
+++ trunk/res/parking/parking_bridge.c Fri Jun  7 11:07:18 2013
@@ -290,6 +290,8 @@
 		COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name),
 		pu->parking_space);
 
+	parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_INUSE);
+
 	return 0;
 }
 
@@ -327,6 +329,8 @@
 		pu->resolution = PARK_ABANDON;
 	}
 	ao2_unlock(pu);
+
+	parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_NOT_INUSE);
 
 	switch (pu->resolution) {
 	case PARK_UNSET:

Modified: trunk/res/parking/parking_bridge_features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_bridge_features.c?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/res/parking/parking_bridge_features.c (original)
+++ trunk/res/parking/parking_bridge_features.c Fri Jun  7 11:07:18 2013
@@ -367,6 +367,23 @@
 	ao2_ref(user, -1);
 }
 
+/*!
+ * \brief Removes the identification information from a channel name string
+ * \since 12.0
+ *
+ * \param channel name string that you wish to turn into a dial string. This will be edited in place.
+ */
+static void channel_name_to_dial_string(char *peername)
+{
+	char *dash;
+
+	/* Truncate after the dash */
+	dash = strrchr(peername, '-');
+	if (dash) {
+		*dash = '\0';
+	}
+}
+
 /*! \internal
  * \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
  *
@@ -378,8 +395,16 @@
 {
 	struct parked_user *user = hook_pvt;
 	struct ast_channel *chan = user->chan;
+	struct ast_context *park_dial_context;
 	char *peername;
+	char *peername_flat;
 	char parking_space[AST_MAX_EXTENSION];
+
+	char returnexten[AST_MAX_EXTENSION];
+	char *duplicate_returnexten;
+	struct ast_exten *existing_exten;
+	struct pbx_find_info pbx_finder = { .stacklen = 0 }; /* The rest is reset in pbx_find_extension */
+
 
 	/* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
 	   to deal with this, lock the parked user, check and set resolution. */
@@ -402,11 +427,68 @@
 	pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot->name);
 
 	peername = ast_strdupa(user->parker->name);
-	flatten_peername(peername);
+	channel_name_to_dial_string(peername);
+
+	peername_flat = ast_strdupa(user->parker->name);
+	flatten_peername(peername_flat);
 
 	pbx_builtin_setvar_helper(chan, "PARKER", peername);
-
-	/* TODO Dialplan generation for park-dial extensions */
+	pbx_builtin_setvar_helper(chan, "PARKER_FLAT", peername_flat);
+
+	/* Dialplan generation for park-dial extensions */
+
+	if (ast_wrlock_contexts()) {
+		ast_log(LOG_ERROR, "Failed to lock the contexts list. Can't add the park-dial extension.\n");
+		return -1;
+	}
+
+	if (!(park_dial_context = ast_context_find_or_create(NULL, NULL, PARK_DIAL_CONTEXT, BASE_REGISTRAR))) {
+		ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", PARK_DIAL_CONTEXT);
+		if (ast_unlock_contexts()) {
+			ast_assert(0);
+		}
+		goto abandon_extension_creation;
+	}
+
+	if (ast_wrlock_context(park_dial_context)) {
+		ast_log(LOG_ERROR, "failed to obtain write lock on context '%s'\n", PARK_DIAL_CONTEXT);
+		if (ast_unlock_contexts()) {
+			ast_assert(0);
+		}
+		goto abandon_extension_creation;
+	}
+
+	if (ast_unlock_contexts()) {
+		ast_assert(0);
+	}
+
+	snprintf(returnexten, sizeof(returnexten), "%s,%u", peername,
+		user->lot->cfg->comebackdialtime);
+
+	duplicate_returnexten = ast_strdup(returnexten);
+
+	if (!duplicate_returnexten) {
+		ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
+			peername_flat, PARK_DIAL_CONTEXT, returnexten);
+	}
+
+	/* If an extension already exists here because we registered it for another parked call timing out, then we may overwrite it. */
+	if ((existing_exten = pbx_find_extension(NULL, NULL, &pbx_finder, PARK_DIAL_CONTEXT, peername_flat, 1, NULL, NULL, E_MATCH)) &&
+	    (strcmp(ast_get_extension_registrar(existing_exten), BASE_REGISTRAR))) {
+		ast_debug(3, "An extension for '%s@%s' was already registered by another registrar '%s'\n",
+			peername_flat, PARK_DIAL_CONTEXT, ast_get_extension_registrar(existing_exten));
+	} else if (ast_add_extension2_nolock(park_dial_context, 1, peername_flat, 1, NULL, NULL,
+			"Dial", duplicate_returnexten, ast_free_ptr, BASE_REGISTRAR)) {
+			ast_free(duplicate_returnexten);
+		ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
+			peername_flat, PARK_DIAL_CONTEXT, returnexten);
+	}
+
+	if (ast_unlock_context(park_dial_context)) {
+		ast_assert(0);
+	}
+
+abandon_extension_creation:
 
 	/* async_goto the proper PBX destination - this should happen when we come out of the bridge */
 	if (!ast_strlen_zero(user->comeback)) {

Modified: trunk/res/parking/parking_controller.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_controller.c?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/res/parking/parking_controller.c (original)
+++ trunk/res/parking/parking_controller.c Fri Jun  7 11:07:18 2013
@@ -254,9 +254,6 @@
 
 	peername = blindtransfer ? ast_strdupa(blindtransfer) : ast_strdupa(pu->parker->name);
 
-	/* XXX Comeback to origin mode: Generate an extension in park-dial to Dial the peer */
-
-
 	/* Flatten the peername so that it can be used for performing the timeout PBX operations */
 	flatten_peername(peername);
 

Modified: trunk/res/parking/res_parking.h
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/res_parking.h?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/res/parking/res_parking.h (original)
+++ trunk/res/parking/res_parking.h Fri Jun  7 11:07:18 2013
@@ -30,6 +30,7 @@
 
 #define DEFAULT_PARKING_LOT "default"
 #define DEFAULT_PARKING_EXTEN "700"
+#define BASE_REGISTRAR "res_parking"
 #define PARK_DIAL_CONTEXT "park-dial"
 
 enum park_call_resolution {
@@ -75,6 +76,7 @@
 
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(name);               /*!< Name of the parking lot configuration object */
+		AST_STRING_FIELD(registrar);          /*!< Which registrar the lot uses if it isn't the default registrar */
 		AST_STRING_FIELD(mohclass);           /*!< Analogous to mohclass config option */
 		AST_STRING_FIELD(parkext);            /*!< Analogous to parkext config option */
 		AST_STRING_FIELD(parking_con);        /*!< Analogous to context config option */
@@ -109,7 +111,7 @@
 };
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief If a parking lot exists in the parking lot list already, update its status to match the provided
  *        configuration and return a reference return a reference to it. Otherwise, create a parking lot
  *        struct based on a parking lot configuration and return a reference to the new one.
@@ -125,7 +127,7 @@
 struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *cfg);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Remove a parking lot from the usable lists if it is no longer involved in any calls and no configuration currently claims it
  *
  * \param lot Which parking lot is being checked for elimination
@@ -136,7 +138,7 @@
 void parking_lot_remove_if_unused(struct parking_lot *lot);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Create a new parking bridge
  *
  * \param bridge_lot Parking lot which the new bridge should be based on
@@ -147,7 +149,7 @@
 struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Get a reference to a parking lot's bridge. If it doesn't exist, create it and get a reference.
  *
  * \param lot Which parking lot we need the bridge from. This parking lot must be locked before calling this function.
@@ -160,7 +162,7 @@
 struct ast_bridge *parking_lot_get_bridge(struct parking_lot *lot);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Get an available parking space within a parking lot.
  *
  * \param lot Which parking lot we are getting a space from
@@ -175,7 +177,7 @@
 int parking_lot_get_space(struct parking_lot *lot, int target_override);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Determine if there is a parked user in a parking space and pull it from the parking lot if there is.
  *
  * \param lot Parking lot being pulled from
@@ -191,7 +193,7 @@
 struct parked_user *parking_lot_retrieve_parked_user(struct parking_lot *lot, int target);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Apply features based on the parking lot feature options
  *
  * \param chan Which channel's feature set is being modified
@@ -202,7 +204,7 @@
 void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Set necessary bridge roles on a channel that is about to enter a parking lot
  *
  * \param chan Entering channel
@@ -212,14 +214,14 @@
 void parking_channel_set_roles(struct ast_channel *chan, struct parking_lot *lot, int force_ringing);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief custom callback function for ast_bridge_channel_queue_playfile which plays a parking space
  *        and optionally hangs up the call afterwards based on the payload in playfile.
  */
 void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *payload);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Setup timeout interval feature on an ast_bridge_features for parking
  *
  * \param features The ast_bridge_features we are establishing the interval hook on
@@ -228,7 +230,7 @@
 void parking_set_duration(struct ast_bridge_features *features, struct parked_user *user);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Get a pointer to the parking lot container for purposes such as iteration
  *
  * \retval pointer to the parking lot container.
@@ -236,7 +238,7 @@
 struct ao2_container *get_parking_lot_container(void);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Find a parking lot based on its name
  *
  * \param lot_name Name of the parking lot sought
@@ -249,7 +251,7 @@
 struct parking_lot *parking_lot_find_by_name(const char *lot_name);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Find parking lot name from channel
  *
  * \param chan The channel we want the parking lot name for
@@ -262,7 +264,7 @@
 const char *find_channel_parking_lot_name(struct ast_channel *chan);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Flattens a peer name so that it can be written to/found from PBX extensions
  *
  * \param peername unflattened peer name. This will be flattened in place, so expect it to change.
@@ -270,7 +272,7 @@
 void flatten_peername(char *peername);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Set a channel's position in the PBX after timeout using the parking lot settings
  *
  * \param pu Parked user who is entering/reentering the PBX
@@ -282,7 +284,30 @@
 int comeback_goto(struct parked_user *pu, struct parking_lot *lot);
 
 /*!
- * \since 12
+ * \since 12.0.0
+ * \brief Add extensions for a parking lot configuration
+ *
+ * \param lot_cfg parking lot configuration to generate extensions for
+ *
+ * \retval 0 on success
+ * \retval non-zero on failure
+ */
+int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg);
+
+/*!
+ * \since 12.0.0
+ * \brief Remove extensions belonging to a parking lot configuration
+ *
+ * \param lot_cfg parking lot configuratin to remove extensions from
+ *
+ * \note This will not remove extensions registered non-exclusively even
+ *       if those extensions were registered by lot_cfg. Those are only
+ *       purged on a res_parking module reload.
+ */
+void parking_lot_cfg_remove_extensions(struct parking_lot_cfg *lot_cfg);
+
+/*!
+ * \since 12.0.0
  * \brief Pull a parked user out of its parking lot. Use this when you don't want to use the parked user afterwards.
  * \param user The parked user being pulled.
  *
@@ -292,7 +317,7 @@
 int unpark_parked_user(struct parked_user *user);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Publish a stasis parked call message for the channel indicating failure to park.
  *
  * \param parkee channel belonging to the failed parkee
@@ -300,7 +325,7 @@
 void publish_parked_call_failure(struct ast_channel *parkee);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Publish a stasis parked call message for a given parked user
  *
  * \param pu pointer to a parked_user that we are generating the message for
@@ -309,7 +334,7 @@
 void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Function to prepare a channel for parking by determining which parking bridge should
  *        be used, setting up a park common datastore so that the parking bridge will have access
  *        to necessary parking information when joining, and applying various bridge roles to the
@@ -338,7 +363,7 @@
 };
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Function that pulls data from the park common datastore on a channel in order to apply it to
  *        the parked user struct upon bridging.
  *
@@ -354,7 +379,17 @@
 		int *randomize, int *time_limit, int *silence_announce);
 
 /*!
- * \since 12
+ * \since 12.0.0
+ * \brief Notify metermaids that we've changed an extension
+ *
+ * \param exten Extension of the call parked/unparked
+ * \param context Context of the call parked/unparked
+ * \param state new device state
+ */
+void parking_notify_metermaids(int exten, const char *context, enum ast_device_state state);
+
+/*!
+ * \since 12.0.0
  * \brief Execution function for the parking application
  *
  * \param chan ast_channel entering the application
@@ -368,7 +403,7 @@
 int park_app_exec(struct ast_channel *chan, const char *data);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Execution function for the parked call application
  *
  * \param chan ast_channel entering the application
@@ -380,7 +415,7 @@
 int parked_call_app_exec(struct ast_channel *chan, const char *data);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Execution function for the park and retrieve application
  *
  * \param chan ast_channel entering the application
@@ -394,7 +429,7 @@
 int park_and_announce_app_exec(struct ast_channel *chan, const char *data);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Register CLI commands
  *
  * \retval 0 if successful
@@ -403,25 +438,25 @@
 int load_parking_ui(void);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Unregister CLI commands
  */
 void unload_parking_ui(void);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Register manager actions and setup subscriptions for stasis events
  */
 int load_parking_manager(void);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Unregister manager actions and remove subscriptions for stasis events
  */
 void unload_parking_manager(void);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Register bridge features for parking
  *
  * \retval 0 on success
@@ -430,7 +465,19 @@
 int load_parking_bridge_features(void);
 
 /*!
- * \since 12
+ * \since 12.0.0
  * \brief Unregister features registered by load_parking_bridge_features
  */
 void unload_parking_bridge_features(void);
+
+/*!
+ * \since 12.0.0
+ * \brief Register Parking devstate handler
+ */
+int load_parking_devstate(void);
+
+/*!
+ * \since 12.0.0
+ * \brief Unregister Parking devstate handler
+ */
+void unload_parking_devstate(void);

Modified: trunk/res/res_parking.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_parking.c?view=diff&rev=390849&r1=390848&r2=390849
==============================================================================
--- trunk/res/res_parking.c (original)
+++ trunk/res/res_parking.c Fri Jun  7 11:07:18 2013
@@ -191,6 +191,7 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/features.h"
 #include "asterisk/manager.h"
+#include "asterisk/pbx.h"
 
 #define PARKED_CALL_APPLICATION "ParkedCall"
 #define PARK_AND_ANNOUNCE_APPLICATION "ParkAndAnnounce"
@@ -375,7 +376,7 @@
 static void parking_lot_cfg_destructor(void *obj)
 {
 	struct parking_lot_cfg *lot_cfg = obj;
-
+	parking_lot_cfg_remove_extensions(lot_cfg);
 	ast_string_field_free_memory(lot_cfg);
 }
 
@@ -607,6 +608,219 @@
 	return lot;
 }
 
+void parking_lot_cfg_remove_extensions(struct parking_lot_cfg *lot_cfg)
+{
+	if (!ast_strlen_zero(lot_cfg->registrar)) {
+		/* Although the function is called ast_context_destroy, the use of this funtion is
+		 * intended only to remove extensions, hints, etc registered by the parking lot's registrar.
+		 * It won't actually destroy the context unless that context is empty afterwards and it is
+		 * unreferenced.
+		 */
+		ast_context_destroy(NULL, lot_cfg->registrar);
+	}
+}
+
+static void remove_all_configured_parking_lot_extensions(void)
+{
+	RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+	struct parking_lot_cfg *lot_cfg;
+	struct ao2_iterator iter;
+
+	if (!cfg) {
+		return;
+	}
+
+	for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
+		parking_lot_cfg_remove_extensions(lot_cfg);
+	}
+
+	ast_context_destroy(NULL, BASE_REGISTRAR);
+
+	ao2_iterator_destroy(&iter);
+}
+
+/*!
+ * \internal
+ * \since 12
+ * \brief Create an extension using ast_add_extension2_nolock. This function automatically allocates a duplicate
+ *        of the data string so that whatever calls it doesn't have to deal with freeing it if the ast_add_extension2_nolock
+ *        fails.
+ *
+ * \param context a write locked ast_context. Make certain it is write locked prior to calling this function
+ * \param replace whether the extension should replace existing extensions
+ * \param extension name of the extension desired
+ * \param priority priority of the extension we are registering
+ * \param application name of the application being used for the extension
+ * \param data application arguments
+ * \param registrar name of the registrar you should use for the extension.
+ *        Make sure this string doesn't go anywhere while there are still extensions using it.
+ */
+static int parking_add_extension(struct ast_context *context, int replace, const char *extension,
+	int priority, const char *application, const char *data, const char *registrar)
+{
+	char *data_duplicate = ast_strdup(data);
+
+	if (!data_duplicate) {
+		return -1;
+	}
+
+	if (ast_add_extension2_nolock(context, replace, extension, priority, NULL, NULL,
+			application, data_duplicate, ast_free_ptr, registrar)) {
+		ast_free(data_duplicate);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int extension_is_compatible(struct parking_lot_cfg *lot_cfg, const char *app_type, struct ast_exten *extension)
+{
+	RAII_VAR(struct parking_lot_cfg *, owner, NULL, ao2_cleanup);
+	const char *extension_registrar = ast_get_extension_registrar(extension);
+	const char *extension_context = ast_get_context_name(ast_get_extension_context(extension));
+	const char *extension_name = ast_get_extension_name(extension);
+	const char *extension_application = ast_get_extension_app(extension);
+
+	ast_assert(extension_registrar && extension_context && extension_name && extension_application);
+
+	if (strcmp(extension_registrar, BASE_REGISTRAR)) {
+		ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s', but that extension is already owned by %s.\n",
+		        lot_cfg->name, extension_name, extension_context, extension_registrar);
+		return 0;
+	}
+
+	if (strcmp(extension_application, app_type)) {
+		ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s' with a non-exclusive %s application, "
+		        "but a/an %s application is already registered to that extension by %s.\n",
+		        lot_cfg->name, extension_name, extension_context, app_type,
+		        extension_application, BASE_REGISTRAR);
+		return 0;
+	}
+
+	ast_debug(3, "Parking lot '%s' -- extension '%s@%s' with application %s is compatible.\n",
+	          lot_cfg->name, extension_name, extension_context, app_type);
+	return 1;
+}
+
+int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg)
+{
+	int parkingspace;
+	struct ast_exten *existing_exten;
+	struct ast_context *lot_context;
+	struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+	const char *registrar_pointer;
+
+	if (ast_strlen_zero(lot_cfg->parkext)) {
+		return 0;
+	}
+
+	if (lot_cfg->parkext_exclusive) {
+		ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
+		registrar_pointer = lot_cfg->registrar;
+	} else {
+		registrar_pointer = BASE_REGISTRAR;
+	}
+
+	/* We need the contexts list locked to safely be able to both read and lock the specific context within */
+	if (ast_wrlock_contexts()) {
+		ast_log(LOG_ERROR, "Failed to lock the contexts list.\n");
+		return -1;
+	}
+
+	if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, registrar_pointer))) {
+		ast_log(LOG_ERROR, "Parking lot '%s' -- Needs a context '%s' which does not exist and Asterisk was unable to create\n",
+			lot_cfg->name, lot_cfg->parking_con);
+		if (ast_unlock_contexts()) {
+			ast_assert(0);
+		}
+		return -1;
+	}
+
+	/* Once we know what context we will be modifying, we need to write lock it because we will be reading extensions
+	 * and we don't want something else to destroy them while we are looking at them.
+	 */
+	if (ast_wrlock_context(lot_context)) {
+		ast_log(LOG_ERROR, "failed to obtain write lock on context\n");
+		return -1;
+	}
+
+	if (ast_unlock_contexts()) {
+		ast_assert(0);
+	}
+
+	/* Handle generation/confirmation for the Park extension */
+	if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, lot_cfg->parkext, 1, NULL, NULL, E_MATCH))) {
+		if (lot_cfg->parkext_exclusive || !extension_is_compatible(lot_cfg, PARK_APPLICATION, existing_exten)) {
+			ast_unlock_context(lot_context);
+			return -1;
+		}
+	} else if (parking_add_extension(lot_context, 0, lot_cfg->parkext, 1, PARK_APPLICATION,
+	           lot_cfg->parkext_exclusive ? lot_cfg->name : "", registrar_pointer)) {
+		ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
+		        lot_cfg->name, PARK_APPLICATION, lot_cfg->parkext, lot_cfg->parking_con);
+		ast_unlock_context(lot_context);
+		return -1;
+	}
+
+	/* Handle generation/confirmation for the ParkedCall extensions and hints */
+	for (parkingspace = lot_cfg->parking_start; parkingspace <= lot_cfg->parking_stop; parkingspace++) {
+		char space[AST_MAX_EXTENSION];
+		RAII_VAR(struct ast_str *, arguments_string, NULL, ast_free);
+		find_info.stacklen = 0; /* reset for pbx_find_exten */
+
+		snprintf(space, sizeof(space), "%d", parkingspace);
+
+		/* Unlike the Park extensions, ParkedCall extensions and their hints may never be shared for any reason. */
+		if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, 1, NULL, NULL, E_MATCH))) {
+			ast_unlock_context(lot_context);
+			return -1;
+		}
+
+		arguments_string = ast_str_create(32);
+		if (!arguments_string) {
+			ast_unlock_context(lot_context);
+			return -1;
+		}
+
+		ast_str_set(&arguments_string, 0, "%s,%s", lot_cfg->name, space);
+		if (parking_add_extension(lot_context, 0, space, 1, PARKED_CALL_APPLICATION,
+		    ast_str_buffer(arguments_string), registrar_pointer)) {
+			ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
+			        lot_cfg->name, PARKED_CALL_APPLICATION, space, lot_cfg->parking_con);
+			ast_unlock_context(lot_context);
+			return -1;
+		}
+
+		find_info.stacklen = 0; /* reset for pbx_find_exten */
+
+		if (lot_cfg->parkaddhints) {
+			char hint_device[AST_MAX_EXTENSION];
+
+			snprintf(hint_device, sizeof(hint_device), "park:%s@%s", space, lot_cfg->parking_con);
+
+			if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, PRIORITY_HINT, NULL, NULL, E_MATCH))) {
+				ast_log(LOG_ERROR, "Parking lot '%s' -- Needs to add a hint '%s' at '%s@%s' but one already exists owned by %s\n",
+			        lot_cfg->name, hint_device, space, lot_cfg->parking_con, ast_get_extension_registrar(existing_exten));
+					ast_unlock_context(lot_context);
+					return -1;
+			}
+
+			if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", registrar_pointer)) {
+				ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add hint '%s@%s' to the PBX.\n",
+				        lot_cfg->name, space, lot_cfg->parking_con);
+				ast_unlock_context(lot_context);
+				return -1;
+			}
+		}
+	}
+
+	if (ast_unlock_context(lot_context)) {
+		ast_assert(0);
+	}
+
+	return 0;
+}
+
 struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg)
 {
 	struct parking_lot *lot;
@@ -688,6 +902,54 @@
 	return 0;
 }
 
+static void remove_pending_parking_lot_extensions(struct parking_config *cfg_pending)
+{
+	struct parking_lot_cfg *lot_cfg;
+	struct ao2_iterator iter;
+
+	for (iter = ao2_iterator_init(cfg_pending->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
+		parking_lot_cfg_remove_extensions(lot_cfg);
+	}
+
+	ao2_iterator_destroy(&iter);
+
+	ast_context_destroy(NULL, BASE_REGISTRAR);
+
+}
+
+static int configure_parking_extensions(void)
+{
+	struct parking_config *cfg = aco_pending_config(&cfg_info);
+	struct ao2_iterator iter;
+	RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
+	int res = 0;
+
+	if (!cfg) {
+		return 0;
+	}
+
+	/* Clear existing extensions */
+	remove_all_configured_parking_lot_extensions();
+
+	/* Attempt to build new extensions for each lot */
+	for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
+		if (parking_lot_cfg_create_extensions(lot_cfg)) {
+			ao2_cleanup(lot_cfg);
+			lot_cfg = NULL;
+			res = -1;
+			break;
+		}
+	}
+	ao2_iterator_destroy(&iter);
+
+	if (res) {
+		remove_pending_parking_lot_extensions(cfg);
+		ast_log(LOG_ERROR, "Extension registration failed. Previously configured lot extensions were removed and can not be safely restored.\n");
+	}
+
+	return res;
+}
+
 static void mark_lots_as_disabled(void)
 {
 	struct ao2_iterator iter;
@@ -708,7 +970,16 @@
 static int config_parking_preapply(void)
 {
 	mark_lots_as_disabled();
-	return verify_default_parking_lot();
+
+	if (verify_default_parking_lot()) {
+		return -1;
+	}
+
+	if (configure_parking_extensions()) {
+		return -1;
+	}
+
+	return 0;
 }
 
 static void disable_marked_lots(void)
@@ -797,12 +1068,14 @@
 		goto error;
 	}
 
-	/* TODO Dialplan generation for parking lots that set parkext */
-	/* TODO Generate hints for parking lots that set parkext and have hints enabled */
+	if (load_parking_devstate()) {
+		goto error;
+	}
 
 	return AST_MODULE_LOAD_SUCCESS;
 
 error:
+	ao2_cleanup(parking_lot_container);
 	aco_info_destroy(&cfg_info);
 	return AST_MODULE_LOAD_DECLINE;
 }
@@ -818,10 +1091,17 @@
 
 static int unload_module(void)
 {
-	/* XXX Parking is currently unloadable due to the fact that it loads features which could cause
+	/* XXX Parking is currently not unloadable due to the fact that it loads features which could cause
 	 *     significant problems if they disappeared while a channel still had access to them.
 	 */
 	return -1;
+
+	/* TODO Things we will need to do here:
+	 *
+	 *  destroy existing parking lots
+	 *  uninstall parking related bridge features
+	 *  remove extensions owned by the parking registrar
+	 */
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",




More information about the svn-commits mailing list