diff options
author | Jonathan Rose <jrose@digium.com> | 2013-06-28 19:22:16 +0000 |
---|---|---|
committer | Jonathan Rose <jrose@digium.com> | 2013-06-28 19:22:16 +0000 |
commit | 50ff1f5fc19f4251cabc8733755e12aba1d673c4 (patch) | |
tree | 56bc4ca76ec12137b6a46edeba661c68a9da2d09 /res | |
parent | 84395ff04239e3864b1e1cee69b87690a235477a (diff) |
res_parking: Dynamic Parking Lots
(closes issue ASTERISK-21644)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2615/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393197 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res')
-rw-r--r-- | res/parking/parking_applications.c | 8 | ||||
-rw-r--r-- | res/parking/parking_ui.c | 10 | ||||
-rw-r--r-- | res/parking/res_parking.h | 32 | ||||
-rw-r--r-- | res/res_parking.c | 188 |
4 files changed, 213 insertions, 25 deletions
diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c index 2b921259f..eceeaca18 100644 --- a/res/parking/parking_applications.c +++ b/res/parking/parking_applications.c @@ -390,6 +390,9 @@ struct ast_bridge *park_common_setup(struct ast_channel *parkee, struct ast_chan } 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 park_app_exec(struct ast_channel *chan, const char *data) 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); diff --git a/res/parking/parking_ui.c b/res/parking/parking_ui.c index 5b432b689..f3eeafa4c 100644 --- a/res/parking/parking_ui.c +++ b/res/parking/parking_ui.c @@ -66,6 +66,7 @@ static void display_parking_lot(struct parking_lot *lot, int fd) 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"); } @@ -102,6 +103,14 @@ static void cli_display_parking_lot(int fd, const char *name) 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"); +} + static void cli_display_parking_lot_list(int fd) { struct ao2_container *lot_container; @@ -172,6 +181,7 @@ static char *handle_show_parking_lot_cmd(struct ast_cli_entry *e, int cmd, struc 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; } diff --git a/res/parking/res_parking.h b/res/parking/res_parking.h index a026be41a..2955f87e1 100644 --- a/res/parking/res_parking.h +++ b/res/parking/res_parking.h @@ -118,6 +118,7 @@ struct parked_user { * 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,7 +126,7 @@ struct parked_user { * \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 @@ -133,10 +134,13 @@ struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *cfg); * * \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 @@ -253,6 +257,21 @@ 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 * \brief Find parking lot name from channel * * \param chan The channel we want the parking lot name for @@ -400,6 +419,15 @@ void parking_notify_metermaids(int exten, const char *context, enum ast_device_s /*! * \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 diff --git a/res/res_parking.c b/res/res_parking.c index 42ae76894..9a13db098 100644 --- a/res/res_parking.c +++ b/res/res_parking.c @@ -231,7 +231,6 @@ static int config_parking_preapply(void); static void link_configured_disable_marked_lots(void); struct parking_global_config { - /* TODO Implement dynamic parking lots. Entirely. */ int parkeddynamic; }; @@ -353,23 +352,30 @@ static void *parking_config_alloc(void) 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 @@ int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg) 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 @@ int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg) 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 @@ int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg) 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 @@ int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg) 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 @@ int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg) 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 @@ int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg) 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 @@ struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg) } } 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 @@ struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg) /* 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,12 +880,143 @@ static void generate_or_link_lots_to_configs(void) 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 */ static int verify_default_parking_lot(void) @@ -951,11 +1097,6 @@ static void mark_lots_as_disabled(void) 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 @@ static int load_module(void) 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 @@ static int load_module(void) } /* 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)); |