[svn-commits] jrose: trunk r393197 - in /trunk: ./ res/ res/parking/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jun 28 14:22:19 CDT 2013


Author: jrose
Date: Fri Jun 28 14:22:16 2013
New Revision: 393197

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393197
Log:
res_parking: Dynamic Parking Lots

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

Modified:
    trunk/CHANGES
    trunk/res/parking/parking_applications.c
    trunk/res/parking/parking_ui.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=393197&r1=393196&r2=393197
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Jun 28 14:22:16 2013
@@ -341,6 +341,22 @@
  * 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.
+
+ * Dynamic parking lots will now fail to be created if the parking lot specified
+   by PARKINGDYNAMIC does not exist.
+
+ * Dynamic parking lots will also fail to be created now if they require exclusive
+   park and parkedcall extensions which overlap with other parking lots.
+
+ * Dynamic parking lots will be cleared on reload for dynamic parking lots that
+   currently contain no calls. Dynamic parking lots containing parked calls will
+   persist through the reloads without alteration.
+
+ * If parkext_exclusive is set for a parking lot and that extension is already in
+   use when that parking lot tries to register it, this is now considered a parking
+   system configuration error. Configurations which do this will be rejected.
+   Dynamic parking lots which try to register extensions that already exist will
+   also be rejected.
 
  * 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

Modified: trunk/res/parking/parking_applications.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_applications.c?view=diff&rev=393197&r1=393196&r2=393197
==============================================================================
--- trunk/res/parking/parking_applications.c (original)
+++ trunk/res/parking/parking_applications.c Fri Jun 28 14:22:16 2013
@@ -390,6 +390,9 @@
 	}
 
 	lot = parking_lot_find_by_name(lot_name);
+	if (!lot) {
+		lot = parking_create_dynamic_lot(lot_name, parkee);
+	}
 
 	if (!lot) {
 		ast_log(LOG_ERROR, "Could not find parking lot: '%s'\n", lot_name);
@@ -451,6 +454,11 @@
 	int silence_announcements = 0;
 	const char *blind_transfer;
 
+	/* Answer the channel if needed */
+	if (ast_channel_state(chan) != AST_STATE_UP) {
+		ast_answer(chan);
+	}
+
 	ast_channel_lock(chan);
 	if ((blind_transfer = pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"))) {
 		blind_transfer = ast_strdupa(blind_transfer);

Modified: trunk/res/parking/parking_ui.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/parking_ui.c?view=diff&rev=393197&r1=393196&r2=393197
==============================================================================
--- trunk/res/parking/parking_ui.c (original)
+++ trunk/res/parking/parking_ui.c Fri Jun 28 14:22:16 2013
@@ -66,6 +66,7 @@
 	ast_cli(fd, "Comeback Dial Time  :  %u sec\n", lot->cfg->comebackdialtime);
 	ast_cli(fd, "MusicOnHold Class   :  %s\n", lot->cfg->mohclass);
 	ast_cli(fd, "Enabled             :  %s\n", (lot->mode == PARKINGLOT_DISABLED) ? "no" : "yes");
+	ast_cli(fd, "Dynamic             :  %s\n", (lot->mode == PARKINGLOT_DYNAMIC) ? "yes" : "no");
 	ast_cli(fd, "\n");
 }
 
@@ -99,6 +100,14 @@
 	}
 
 	ao2_callback(lot->parked_users, OBJ_MULTIPLE | OBJ_NODATA, display_parked_users_cb, &fd);
+	ast_cli(fd, "\n");
+}
+
+static void cli_display_parking_global(int fd)
+{
+	ast_cli(fd, "Parking General Options\n"
+	            "-----------------------\n");
+	ast_cli(fd, "Dynamic Parking     :  %s\n", parking_dynamic_lots_enabled() ? "yes" : "no");
 	ast_cli(fd, "\n");
 }
 
@@ -172,6 +181,7 @@
 	ast_cli(a->fd, "\n");
 
 	if (a->argc == 2) {
+		cli_display_parking_global(a->fd);
 		cli_display_parking_lot_list(a->fd);
 		return CLI_SUCCESS;
 	}

Modified: trunk/res/parking/res_parking.h
URL: http://svnview.digium.com/svn/asterisk/trunk/res/parking/res_parking.h?view=diff&rev=393197&r1=393196&r2=393197
==============================================================================
--- trunk/res/parking/res_parking.h (original)
+++ trunk/res/parking/res_parking.h Fri Jun 28 14:22:16 2013
@@ -118,6 +118,7 @@
  *        struct based on a parking lot configuration and return a reference to the new one.
  *
  * \param cfg The configuration being used as a reference to build the parking lot from.
+ * \param dynamic non-zero if creating a dynamic parking lot with this. Don't replace existing parking lots. Ever.
  *
  * \retval A reference to the new parking lot
  * \retval NULL if it was not found and could not be be allocated
@@ -125,18 +126,21 @@
  * \note The parking lot will need to be unreffed if it ever falls out of scope
  * \note The parking lot will automatically be added to the parking lot container if needed as part of this process
  */
-struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *cfg);
+struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *cfg, int dynamic);
 
 /*!
  * \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
+ *
+ * \retval 0 if the parking lot was removed
+ * \retval -1 if the parking lot wasn't removed.
  *
  * \note This should generally be called when something is happening that could cause a parking lot to die such as a call being unparked or
  *       a parking lot no longer existing in configurations.
  */
-void parking_lot_remove_if_unused(struct parking_lot *lot);
+int parking_lot_remove_if_unused(struct parking_lot *lot);
 
 /*!
  * \since 12.0.0
@@ -250,6 +254,21 @@
  * \note ao2_cleanup this reference when you are done using it or you'll cause leaks.
  */
 struct parking_lot *parking_lot_find_by_name(const char *lot_name);
+
+/*!
+ * \since 12.0.0
+ * \brief Create a dynamic parking lot
+ *
+ * \param name Dynamic parking lot name to create
+ * \param chan Channel parkee to get dynamic parking lot parameters from
+ *
+ * \retval dynamically created parking lot on success
+ * \retval NULL on error
+ *
+ * \note This should be called only after verifying that the named parking lot doesn't already exist in a non-dynamic way.
+ *       The parking lots container should be locked before verifying and remain locked until after this function is called.
+ */
+struct parking_lot *parking_create_dynamic_lot(const char *name, struct ast_channel *chan);
 
 /*!
  * \since 12.0.0
@@ -400,6 +419,15 @@
 
 /*!
  * \since 12.0.0
+ * \brief Check global configuration to see if dynamic parking is enabled
+ *
+ * \retval 1 if dynamic parking is enabled
+ * \retval 0 if dynamic parking is disabled
+ */
+int parking_dynamic_lots_enabled(void);
+
+/*!
+ * \since 12.0.0
  * \brief Execution function for the parking application
  *
  * \param chan ast_channel entering the application

Modified: trunk/res/res_parking.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_parking.c?view=diff&rev=393197&r1=393196&r2=393197
==============================================================================
--- trunk/res/res_parking.c (original)
+++ trunk/res/res_parking.c Fri Jun 28 14:22:16 2013
@@ -231,7 +231,6 @@
 static void link_configured_disable_marked_lots(void);
 
 struct parking_global_config {
-	/* TODO Implement dynamic parking lots. Entirely. */
 	int parkeddynamic;
 };
 
@@ -353,23 +352,30 @@
 	return cfg;
 }
 
-void parking_lot_remove_if_unused(struct parking_lot *lot)
-{
-
+int parking_lot_remove_if_unused(struct parking_lot *lot)
+{
 	if (lot->mode != PARKINGLOT_DISABLED) {
-		return;
-	}
-
+		return -1;
+	}
 
 	if (!ao2_container_count(lot->parked_users)) {
 		ao2_unlink(parking_lot_container, lot);
-	}
+		return 0;
+	}
+
+	return -1;
 }
 
 static void parking_lot_disable(struct parking_lot *lot)
 {
+	/* If a dynamic lot wasn't removed, we need to restore it to full functionality afterwards. */
+	int was_dynamic = (lot->mode == PARKINGLOT_DYNAMIC);
+
 	lot->mode = PARKINGLOT_DISABLED;
-	parking_lot_remove_if_unused(lot);
+	if (parking_lot_remove_if_unused(lot) && was_dynamic) {
+		lot->mode = PARKINGLOT_DYNAMIC;
+		lot->disable_mark = 0;
+	}
 }
 
 /*! \brief Destroy a parking lot cfg object */
@@ -703,17 +709,20 @@
 	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;
+	const char *parkext_registrar_pointer; /* Used for park extension */
+	const char *parkedcall_registrar_pointer; /* Used for parkedcall extensions/hints */
 
 	if (ast_strlen_zero(lot_cfg->parkext)) {
 		return 0;
 	}
 
+	ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
+	parkedcall_registrar_pointer = lot_cfg->registrar;
+
 	if (lot_cfg->parkext_exclusive) {
-		ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
-		registrar_pointer = lot_cfg->registrar;
+		parkext_registrar_pointer = lot_cfg->registrar;
 	} else {
-		registrar_pointer = BASE_REGISTRAR;
+		parkext_registrar_pointer = BASE_REGISTRAR;
 	}
 
 	/* We need the contexts list locked to safely be able to both read and lock the specific context within */
@@ -722,7 +731,7 @@
 		return -1;
 	}
 
-	if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, registrar_pointer))) {
+	if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, parkext_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()) {
@@ -750,7 +759,7 @@
 			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)) {
+	           lot_cfg->parkext_exclusive ? lot_cfg->name : "", parkext_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);
@@ -779,7 +788,7 @@
 
 		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_str_buffer(arguments_string), parkedcall_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);
@@ -800,7 +809,7 @@
 					return -1;
 			}
 
-			if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", registrar_pointer)) {
+			if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", parkedcall_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);
@@ -816,7 +825,7 @@
 	return 0;
 }
 
-struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg)
+struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg, int dynamic)
 {
 	struct parking_lot *lot;
 	struct parking_lot_cfg *replaced_cfg = NULL;
@@ -833,6 +842,12 @@
 		}
 	} else {
 		found = 1;
+
+		if (dynamic) {
+			ast_log(LOG_ERROR, "Tried to create dynamic parking lot with name '%s' but a lot with that name already exists.\n", lot_cfg->name);
+			ao2_cleanup(lot);
+			return NULL;
+		}
 	}
 
 	/* Set the configuration reference. Unref the one currently in the lot if it's there. */
@@ -847,7 +862,7 @@
 
 	/* Set the operating mode to normal since the parking lot has a configuration. */
 	lot->disable_mark = 0;
-	lot->mode = PARKINGLOT_NORMAL;
+	lot->mode = dynamic ? PARKINGLOT_DYNAMIC : PARKINGLOT_NORMAL;
 
 	if (!found) {
 		/* Link after configuration is set since a lot without configuration will cause all kinds of trouble. */
@@ -865,10 +880,141 @@
 
 	for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
 		RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
-		lot = parking_lot_build_or_update(lot_cfg);
+		lot = parking_lot_build_or_update(lot_cfg, 0);
 	}
 
 	ao2_iterator_destroy(&iter);
+}
+
+int parking_dynamic_lots_enabled(void)
+{
+	RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+
+	if (!cfg) {
+		return 0;
+	}
+
+	return cfg->global->parkeddynamic;
+}
+
+static struct parking_lot_cfg *clone_parkinglot_cfg(struct parking_lot_cfg *source, const char *name)
+{
+	struct parking_lot_cfg *cfg = parking_lot_cfg_alloc(name);
+
+	if (!cfg) {
+		return NULL;
+	}
+
+	ast_string_fields_copy(cfg, source);
+
+	/* Needs to be reset after being copied */
+	ast_string_field_set(cfg, name, name);
+
+	/* Stuff that should be cloned that isn't hit by string field copy */
+	cfg->parking_start = source->parking_start;
+	cfg->parking_stop = source->parking_stop;
+	cfg->parkingtime = source->parkingtime;
+	cfg->comebackdialtime = source->comebackdialtime;
+	cfg->parkfindnext = source->parkfindnext;
+	cfg->parkext_exclusive = source->parkext_exclusive;
+	cfg->parkaddhints = source->parkaddhints;
+	cfg->comebacktoorigin = source->comebacktoorigin;
+	cfg->parkedplay = source->parkedplay;
+	cfg->parkedcalltransfers = source->parkedcalltransfers;
+	cfg->parkedcallreparking = source->parkedcallreparking;
+	cfg->parkedcallhangup = source->parkedcallhangup;
+	cfg->parkedcallrecording = source->parkedcallrecording;
+
+	return cfg;
+}
+
+struct parking_lot *parking_create_dynamic_lot(const char *name, struct ast_channel *chan)
+{
+	RAII_VAR(struct parking_lot_cfg *, cfg, NULL, ao2_cleanup);
+	RAII_VAR(struct parking_lot *, template_lot, NULL, ao2_cleanup);
+
+	struct parking_lot *lot;
+	const char *dyn_context;
+	const char *dyn_exten;
+	const char *dyn_range;
+	const char *template_name;
+	const char *chan_template_name;
+	int dyn_start;
+	int dyn_end;
+
+	if (!parking_dynamic_lots_enabled()) {
+		return NULL;
+	}
+
+	ast_channel_lock(chan);
+	chan_template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
+	dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
+	dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
+	dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
+	ast_channel_unlock(chan);
+
+	template_name = S_OR(chan_template_name, DEFAULT_PARKING_LOT);
+
+	template_lot = parking_lot_find_by_name(template_name);
+	if (!template_lot) {
+		ast_log(LOG_ERROR, "Lot %s does not exist. Can not use it as a dynamic parking lot template.\n",
+			template_name);
+		return NULL;
+	}
+
+	cfg = clone_parkinglot_cfg(template_lot->cfg, name);
+
+	if (!cfg) {
+		ast_log(LOG_ERROR, "Failed to allocate dynamic parking lot configuration.\n");
+		return NULL;
+	}
+
+	if (!ast_strlen_zero(dyn_exten)) {
+		ast_string_field_set(cfg, parkext, dyn_exten);
+	}
+
+	if (!ast_strlen_zero(dyn_context)) {
+		ast_string_field_set(cfg, parking_con, dyn_context);
+	}
+
+	if (!ast_strlen_zero(dyn_range)) {
+		if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
+			ast_log(LOG_ERROR,
+				"Invalid parking range %s specified in PARKINGDYNPOS: could not parse minimum/maximum parking space range\n", dyn_range);
+				return NULL;
+		}
+		if (dyn_end < dyn_start || dyn_start < 0) {
+			ast_log(LOG_ERROR,
+				"Invalid parking range %s specified for PARKINGDYNPOS: end parking space must be greater than starting parking space.\n", dyn_range);
+				return NULL;
+		}
+
+		cfg->parking_start = dyn_start;
+		cfg->parking_stop = dyn_end;
+	}
+
+	if (parking_lot_cfg_create_extensions(cfg)) {
+		ast_log(LOG_ERROR, "Extensions for dynamic parking lot '%s' could not be registered. Dynamic lot creation failed.\n", name);
+		return NULL;
+	}
+
+	ao2_lock(parking_lot_container);
+
+	if ((lot = parking_lot_find_by_name(name))) {
+		ao2_unlock(parking_lot_container);
+		ast_log(LOG_ERROR, "Started creating dynamic parking lot '%s', but a parking lot with that name already exists.\n", name);
+		ao2_ref(lot, -1);
+		return NULL;
+	}
+
+	lot = parking_lot_build_or_update(cfg, 1);
+	ao2_unlock(parking_lot_container);
+
+	if (!lot) {
+		ast_log(LOG_NOTICE, "Failed to build dynamic parking lot '%s'\n", name);
+	}
+
+	return lot;
 }
 
 /* Preapply */
@@ -951,11 +1097,6 @@
 	struct parking_lot *lot;
 
 	for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
-		/* We aren't concerned with dynamic lots */
-		if (lot->mode == PARKINGLOT_DYNAMIC) {
-			continue;
-		}
-
 		lot->disable_mark = 1;
 	}
 
@@ -1003,7 +1144,7 @@
 		goto error;
 	}
 
-	parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK,
+	parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
 		AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
 		parking_lot_sort_fn,
 		NULL);
@@ -1013,6 +1154,7 @@
 	}
 
 	/* Global options */
+	aco_option_register(&cfg_info, "parkeddynamic", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct parking_global_config, parkeddynamic));
 
 	/* Register the per parking lot options. */
 	aco_option_register(&cfg_info, "parkext", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parkext));




More information about the svn-commits mailing list