Asterisk - The Open Source Telephony Project  21.4.1
res_parking.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Jonathan Rose <jrose@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Call Parking Resource
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25 
26 /*** MODULEINFO
27  <depend>bridge_holding</depend>
28  <support_level>core</support_level>
29  ***/
30 
31 /*** DOCUMENTATION
32  <configInfo name="res_parking" language="en_US">
33  <configFile name="res_parking.conf">
34  <configObject name="globals">
35  <synopsis>Options that apply to every parking lot</synopsis>
36  <configOption name="parkeddynamic">
37  <synopsis>Enables dynamically created parkinglots.</synopsis>
38  <description>
39  <para>If the option is enabled then the following variables can
40  be used to dynamically create new parking lots.
41  </para>
42  <para>The <variable>PARKINGDYNAMIC</variable> variable specifies the
43  parking lot to use as a template to create a dynamic parking lot. It
44  is an error to specify a non-existent parking lot for the template.
45  If not set then the default parking lot is used as the template.
46  </para>
47  <para>The <variable>PARKINGDYNCONTEXT</variable> variable specifies the
48  dialplan context to use for the newly created dynamic parking lot. If
49  not set then the context from the parking lot template is used. The
50  context is created if it does not already exist and the new parking lot
51  needs to create extensions.
52  </para>
53  <para>The <variable>PARKINGDYNEXTEN</variable> variable specifies the
54  <literal>parkext</literal> to use for the newly created dynamic
55  parking lot. If not set then the <literal>parkext</literal> is used from
56  the parking lot template. If the template does not specify a
57  <literal>parkext</literal> then no extensions are created for the newly
58  created parking lot. The dynamic parking lot cannot be created if it
59  needs to create extensions that overlap existing parking lot extensions.
60  The only exception to this is for the <literal>parkext</literal>
61  extension and only if neither of the overlaping parking lot's
62  <literal>parkext</literal> is exclusive.
63  </para>
64  <para>The <variable>PARKINGDYNPOS</variable> variable specifies the
65  parking positions to use for the newly created dynamic parking lot. If
66  not set then the <literal>parkpos</literal> from the parking lot template
67  is used.
68  </para>
69  </description>
70  </configOption>
71  </configObject>
72  <configObject name="parking_lot">
73  <synopsis>Defined parking lots for res_parking to use to park calls on</synopsis>
74  <configOption name="context" default="parkedcalls">
75  <synopsis>The name of the context where calls are parked and picked up from.</synopsis>
76  <description><para>This option is only used if parkext is set.</para></description>
77  </configOption>
78  <configOption name="parkext">
79  <synopsis>Extension to park calls to this parking lot.</synopsis>
80  <description>
81  <para>If this option is used, this extension will automatically
82  be created to place calls into parking lots. In addition, if
83  <literal>parkext_exclusive</literal> is set for this parking
84  lot, the name of the parking lot will be included in the
85  application's arguments so that it only parks to this parking
86  lot. The extension will be created in <literal>context</literal>.
87  Using this option also creates extensions for retrieving
88  parked calls from the parking spaces in the same context.
89  </para>
90  <note>
91  <para>Generated parking extensions cannot overlap.
92  The only exception is if neither overlapping
93  <literal>parkext</literal> is exclusive.
94  </para>
95  </note>
96  </description>
97  </configOption>
98  <configOption name="parkext_exclusive" default="no">
99  <synopsis>If yes, the extension registered as parkext will park exclusively to this parking lot.</synopsis>
100  </configOption>
101  <configOption name="parkpos" default="701-750">
102  <synopsis>Numerical range of parking spaces which can be used to retrieve parked calls.</synopsis>
103  <description>
104  <para>If <literal>parkext</literal> is set, these extensions
105  will automatically be mapped in <literal>context</literal>
106  in order to pick up calls parked to these parking spaces.
107  </para>
108  </description>
109  </configOption>
110  <configOption name="parkinghints" default="no">
111  <synopsis>If yes, this parking lot will add hints automatically for parking spaces.</synopsis>
112  </configOption>
113  <configOption name="parkingtime" default="45">
114  <synopsis>Amount of time a call will remain parked before giving up (in seconds).</synopsis>
115  </configOption>
116  <configOption name="parkedmusicclass">
117  <synopsis>Which music class to use for parked calls. They will use the default if unspecified.</synopsis>
118  </configOption>
119  <configOption name="comebacktoorigin" default="yes">
120  <synopsis>Determines what should be done with the parked channel if no one picks it up before it times out.</synopsis>
121  <description><para>Valid Options:</para>
122  <enumlist>
123  <enum name="yes">
124  <para>Automatically have the parked channel dial the device that parked the call with dial
125  timeout set by the <literal>parkingtime</literal> option. When the call times out an extension
126  to dial the PARKER will automatically be created in the <literal>park-dial</literal> context with
127  an extension of the flattened parker device name. If the call is not answered, the parked channel
128  that is timing out will continue in the dial plan at that point if there are more priorities in
129  the extension (which won't be the case unless the dialplan deliberately includes such priorities
130  in the <literal>park-dial</literal> context through pattern matching or deliberately written
131  flattened peer extensions).</para>
132  </enum>
133  <enum name="no">
134  <para>Place the call into the PBX at <literal>comebackcontext</literal> instead. The extension will
135  still be set as the flattened peer name. If an extension the flattened peer name isn't available
136  then it will fall back to the <literal>s</literal> extension. If that also is unavailable it will
137  attempt to fall back to <literal>s@default</literal>. The normal dial extension will still be
138  created in the <literal>park-dial</literal> context with the extension also being the flattened
139  peer name.</para>
140  </enum>
141  </enumlist>
142  <note><para>Flattened Peer Names - Extensions can not include slash characters since those are used for pattern
143  matching. When a peer name is flattened, slashes become underscores. For example if the parker of a call
144  is called <literal>SIP/0004F2040001</literal> then flattened peer name and therefor the extensions created
145  and used on timeouts will be <literal>SIP_0004F204001</literal>.</para></note>
146  <note><para>When parking times out and the channel returns to the dial plan, the following variables are set:
147  </para></note>
148  <variablelist>
149  <variable name="PARKING_SPACE">
150  <para>extension that the call was parked in prior to timing out.</para>
151  </variable>
152  <variable name="PARKEDLOT">
153  <para>name of the lot that the call was parked in prior to timing out.</para>
154  </variable>
155  <variable name="PARKER">
156  <para>The device that parked the call</para>
157  </variable>
158  <variable name="PARKER_FLAT">
159  <para>The flat version of <variable>PARKER</variable></para>
160  </variable>
161  </variablelist>
162  </description>
163  </configOption>
164  <configOption name="comebackdialtime" default="30">
165  <synopsis>Timeout for the Dial extension created to call back the parker when a parked call times out.</synopsis>
166  </configOption>
167  <configOption name="comebackcontext" default="parkedcallstimeout">
168  <synopsis>Context where parked calls will enter the PBX on timeout when comebacktoorigin=no</synopsis>
169  <description><para>The extension the call enters will prioritize the flattened peer name in this context.
170  If the flattened peer name extension is unavailable, then the 's' extension in this context will be
171  used. If that also is unavailable, the 's' extension in the 'default' context will be used.</para>
172  </description>
173  </configOption>
174  <configOption name="courtesytone">
175  <synopsis>If the name of a sound file is provided, use this as the courtesy tone</synopsis>
176  <description><para>By default, this tone is only played to the caller of a parked call. Who receives the tone
177  can be changed using the <literal>parkedplay</literal> option.</para>
178  </description>
179  </configOption>
180  <configOption name="parkedplay" default="caller">
181  <synopsis>Who we should play the courtesytone to on the pickup of a parked call from this lot</synopsis>
182  <description>
183  <enumlist>
184  <enum name="no"><para>Apply to neither side.</para></enum>
185  <enum name="caller"><para>Apply only to the call connecting with the call coming out of the parking lot.</para></enum>
186  <enum name="callee"><para>Apply only to the call coming out of the parking lot.</para></enum>
187  <enum name="both"><para>Apply to both sides.</para></enum>
188  </enumlist>
189  <note><para>If courtesy tone is not specified then this option will be ignored.</para></note>
190  </description>
191  </configOption>
192  <configOption name="parkedcalltransfers" default="no">
193  <synopsis>Who to apply the DTMF transfer features to when parked calls are picked up or timeout.</synopsis>
194  <description>
195  <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
196  </description>
197  </configOption>
198  <configOption name="parkedcallreparking" default="no">
199  <synopsis>Who to apply the DTMF parking feature to when parked calls are picked up or timeout.</synopsis>
200  <description>
201  <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
202  </description>
203  </configOption>
204  <configOption name="parkedcallhangup" default="no">
205  <synopsis>Who to apply the DTMF hangup feature to when parked calls are picked up or timeout.</synopsis>
206  <description>
207  <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
208  </description>
209  </configOption>
210  <configOption name="parkedcallrecording" default="no">
211  <synopsis>Who to apply the DTMF MixMonitor recording feature to when parked calls are picked up or timeout.</synopsis>
212  <description>
213  <xi:include xpointer="xpointer(/docs/configInfo[@name='res_parking']/configFile[@name='res_parking.conf']/configObject[@name='parking_lot']/configOption[@name='parkedplay']/description/enumlist)" />
214  </description>
215  </configOption>
216  <configOption name="findslot" default="first">
217  <synopsis>Rule to use when trying to figure out which parking space a call should be parked with.</synopsis>
218  <description>
219  <enumlist>
220  <enum name="first"><para>Always try to place in the lowest available space in the parking lot</para></enum>
221  <enum name="next"><para>Track the last parking space used and always attempt to use the one immediately after.
222  </para></enum>
223  </enumlist>
224  </description>
225  </configOption>
226  </configObject>
227  </configFile>
228  </configInfo>
229  ***/
230 
231 #include "asterisk.h"
232 
233 #include "parking/res_parking.h"
234 #include "asterisk/config.h"
235 #include "asterisk/config_options.h"
236 #include "asterisk/utils.h"
237 #include "asterisk/module.h"
238 #include "asterisk/cli.h"
239 #include "asterisk/astobj2.h"
240 #include "asterisk/features.h"
241 #include "asterisk/manager.h"
242 #include "asterisk/pbx.h"
243 
244 static int parking_lot_sort_fn(const void *obj_left, const void *obj_right, int flags)
245 {
246  const struct parking_lot *left = obj_left;
247  const struct parking_lot *right = obj_right;
248  const char *right_key = obj_right;
249  int cmp;
250 
251  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
252  default:
253  case OBJ_POINTER:
254  right_key = right->name;
255  /* Fall through */
256  case OBJ_KEY:
257  cmp = strcmp(left->name, right_key);
258  break;
259  case OBJ_PARTIAL_KEY:
260  cmp = strncmp(left->name, right_key, strlen(right_key));
261  }
262  return cmp;
263 }
264 
265 /*! All parking lots that are currently alive in some fashion can be obtained from here */
267 
268 static void *parking_config_alloc(void);
269 
270 static void *parking_lot_cfg_alloc(const char *cat);
271 static void *named_item_find(struct ao2_container *container, const char *name); /* XXX This is really just a generic string find. Move to astobj2.c? */
272 
273 static int config_parking_preapply(void);
274 static void link_configured_disable_marked_lots(void);
275 
277  int parkeddynamic;
278 };
279 
281  struct parking_global_config *global;
282  struct ao2_container *parking_lots;
283 };
284 
285 static struct aco_type global_option = {
286  .type = ACO_GLOBAL,
287  .name = "globals",
288  .item_offset = offsetof(struct parking_config, global),
289  .category_match = ACO_WHITELIST_EXACT,
290  .category = "general",
291 };
292 
293 struct aco_type *global_options[] = ACO_TYPES(&global_option);
294 
295 static struct aco_type parking_lot_type = {
296  .type = ACO_ITEM,
297  .name = "parking_lot",
298  .category_match = ACO_BLACKLIST_EXACT,
299  .category = "general",
300  .item_alloc = parking_lot_cfg_alloc,
301  .item_find = named_item_find,
302  .item_offset = offsetof(struct parking_config, parking_lots),
303 };
304 
305 struct aco_type *parking_lot_types[] = ACO_TYPES(&parking_lot_type);
306 
307 struct aco_file parking_lot_conf = {
308  .filename = "res_parking.conf",
309  .types = ACO_TYPES(&global_option, &parking_lot_type),
310 };
311 
312 static AO2_GLOBAL_OBJ_STATIC(globals);
313 
314 CONFIG_INFO_STANDARD(cfg_info, globals, parking_config_alloc,
315  .files = ACO_FILES(&parking_lot_conf),
316  .pre_apply_config = config_parking_preapply,
317  .post_apply_config = link_configured_disable_marked_lots,
318 );
319 
320 static int parking_lot_cfg_hash_fn(const void *obj, const int flags)
321 {
322  const struct parking_lot_cfg *entry;
323  const char *key;
324 
325  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
326  case OBJ_KEY:
327  key = obj;
328  return ast_str_hash(key);
329  case OBJ_PARTIAL_KEY:
330  ast_assert(0);
331  return 0;
332  default:
333  entry = obj;
334  return ast_str_hash(entry->name);
335  }
336 }
337 
338 static int parking_lot_cfg_cmp_fn(void *obj, void *arg, const int flags)
339 {
340  struct parking_lot_cfg *entry1 = obj;
341 
342  char *key;
343  size_t key_size;
344  struct parking_lot_cfg *entry2;
345 
346  switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
347  case OBJ_KEY:
348  key = arg;
349  return (!strcmp(entry1->name, key)) ? CMP_MATCH : 0;
350  case OBJ_PARTIAL_KEY:
351  key = arg;
352  key_size = strlen(key);
353  return (!strncmp(entry1->name, key, key_size)) ? CMP_MATCH : 0;
354  case OBJ_POINTER:
355  entry2 = arg;
356  return (!strcmp(entry1->name, entry2->name)) ? CMP_MATCH : 0;
357  default:
358  return CMP_STOP;
359  }
360 }
361 
362 /*! \brief destructor for parking_config */
363 static void parking_config_destructor(void *obj)
364 {
365  struct parking_config *cfg = obj;
366  ao2_cleanup(cfg->parking_lots);
367  ao2_cleanup(cfg->global);
368 }
369 
370 /*! \brief destructor for parking_global_config */
371 static void parking_global_config_destructor(void *obj)
372 {
373  /* For now, do nothing. */
374 }
375 
376 /*! \brief allocator callback for parking_config. Notice it returns void * since it is only used by the backend config code */
377 static void *parking_config_alloc(void)
378 {
379  RAII_VAR(struct parking_config *, cfg, NULL, ao2_cleanup);
380 
381  if (!(cfg = ao2_alloc(sizeof(*cfg), parking_config_destructor))) {
382  return NULL;
383  }
384 
385  cfg->parking_lots = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 37,
386  parking_lot_cfg_hash_fn, NULL, parking_lot_cfg_cmp_fn);
387  if (!cfg->parking_lots) {
388  return NULL;
389  }
390 
391  if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), parking_global_config_destructor))) {
392  return NULL;
393  }
394 
395  /* Bump the ref count since RAII_VAR is going to eat one */
396  ao2_ref(cfg, +1);
397  return cfg;
398 }
399 
401 {
402  if (lot->mode != PARKINGLOT_DISABLED) {
403  return -1;
404  }
405 
406  if (!ao2_container_count(lot->parked_users)) {
407  ao2_unlink(parking_lot_container, lot);
408  return 0;
409  }
410 
411  return -1;
412 }
413 
414 static void parking_lot_disable(struct parking_lot *lot)
415 {
416  /* If a dynamic lot wasn't removed, we need to restore it to full functionality afterwards. */
417  int was_dynamic = (lot->mode == PARKINGLOT_DYNAMIC);
418 
419  lot->mode = PARKINGLOT_DISABLED;
420  if (parking_lot_remove_if_unused(lot) && was_dynamic) {
421  lot->mode = PARKINGLOT_DYNAMIC;
422  lot->disable_mark = 0;
423  }
424 }
425 
426 /*! \brief Destroy a parking lot cfg object */
427 static void parking_lot_cfg_destructor(void *obj)
428 {
429  struct parking_lot_cfg *lot_cfg = obj;
432 }
433 
434 /* The arg just needs to have the parking space with it */
435 static int parked_user_cmp_fn(void *obj, void *arg, int flags)
436 {
437  int *search_space = arg;
438  struct parked_user *user = obj;
439  int object_space = user->parking_space;
440 
441  if (*search_space == object_space) {
442  return CMP_MATCH;
443  }
444  return 0;
445 }
446 
447 static int parked_user_sort_fn(const void *obj_left, const void *obj_right, int flags)
448 {
449  const struct parked_user *left = obj_left;
450  const struct parked_user *right = obj_right;
451 
452  return left->parking_space - right->parking_space;
453 }
454 
455 /*!
456  * \brief create a parking lot structure
457  * \param cat name given to the parking lot
458  * \retval NULL failure
459  * \retval non-NULL successfully allocated parking lot
460  */
461 static void *parking_lot_cfg_alloc(const char *cat)
462 {
463  struct parking_lot_cfg *lot_cfg;
464 
465  lot_cfg = ao2_alloc(sizeof(*lot_cfg), parking_lot_cfg_destructor);
466  if (!lot_cfg) {
467  return NULL;
468  }
469 
470  if (ast_string_field_init(lot_cfg, 32)) {
471  ao2_cleanup(lot_cfg);
472  return NULL;
473  }
474 
475  ast_string_field_set(lot_cfg, name, cat);
476 
477  return lot_cfg;
478 }
479 
480 #if defined(TEST_FRAMEWORK)
481 struct parking_lot_cfg *parking_lot_cfg_create(const char *cat)
482 {
483  return parking_lot_cfg_alloc(cat);
484 }
485 #endif
486 
487 /*!
488  * XXX This is actually incredibly generic and might be better placed in something like astobj2 if there isn't already an equivalent
489  * \brief find an item in a container by its name
490  *
491  * \param container ao2container where we want the item from
492  * \param name name of the item wanted to be found
493  *
494  * \return pointer to the parking lot if available.
495  * \retval NULL if not found.
496  */
497 static void *named_item_find(struct ao2_container *container, const char *name)
498 {
499  return ao2_find(container, name, OBJ_KEY);
500 }
501 
502 /*!
503  * \brief Custom field handler for parking positions
504  */
505 static int option_handler_parkpos(const struct aco_option *opt, struct ast_variable *var, void *obj)
506 {
507  struct parking_lot_cfg *lot_cfg = obj;
508  int low;
509  int high;
510 
511  if (sscanf(var->value, "%30d-%30d", &low, &high) != 2) {
512  ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers\n");
513  } else if (high < low || low <= 0 || high <= 0) {
514  ast_log(LOG_WARNING, "Format for parking positions is a-b, where a <= b\n");
515  } else {
516  lot_cfg->parking_start = low;
517  lot_cfg->parking_stop = high;
518  return 0;
519  }
520  return -1;
521 }
522 
523 /*!
524  * \brief Custom field handler for the findslot option
525  */
526 static int option_handler_findslot(const struct aco_option *opt, struct ast_variable *var, void *obj)
527 {
528  struct parking_lot_cfg *lot_cfg = obj;
529 
530  if (!strcmp(var->value, "first")) {
531  lot_cfg->parkfindnext = 0;
532  } else if (!strcmp(var->value, "next")) {
533  lot_cfg->parkfindnext = 1;
534  } else {
535  ast_log(LOG_WARNING, "value '%s' is not valid for findslot option.\n", var->value);
536  return -1;
537  }
538 
539  return 0;
540 }
541 
542 /*!
543  * \brief Maps string values for option_handler_parkedfeature to their ENUM values
544  */
545 static int parking_feature_flag_cfg(int *param, const char *var)
546 {
547  if (ast_false(var)) {
548  *param = 0;
549  } else if (!strcasecmp(var, "both")) {
550  *param = AST_FEATURE_FLAG_BYBOTH;
551  } else if (!strcasecmp(var, "caller")) {
552  *param = AST_FEATURE_FLAG_BYCALLER;
553  } else if (!strcasecmp(var, "callee")) {
554  *param = AST_FEATURE_FLAG_BYCALLEE;
555  } else {
556  return -1;
557  }
558 
559  return 0;
560 }
561 
562 /*!
563  * \brief Custom field handler for feature mapping on parked call pickup options
564  */
565 static int option_handler_parkedfeature(const struct aco_option *opt, struct ast_variable *var, void *obj)
566 {
567  struct parking_lot_cfg *cfg = obj;
568  enum parked_call_feature_options option = aco_option_get_flags(opt);
569  int *parameter = NULL;
570 
571  switch (option) {
572  case OPT_PARKEDPLAY:
573  parameter = &cfg->parkedplay;
574  break;
575  case OPT_PARKEDTRANSFERS:
576  parameter = &cfg->parkedcalltransfers;
577  break;
578  case OPT_PARKEDREPARKING:
579  parameter = &cfg->parkedcallreparking;
580  break;
581  case OPT_PARKEDHANGUP:
582  parameter = &cfg->parkedcallhangup;
583  break;
584  case OPT_PARKEDRECORDING:
585  parameter = &cfg->parkedcallrecording;
586  break;
587  }
588 
589  ast_assert(parameter != NULL);
590  if (!parameter || parking_feature_flag_cfg(parameter, var->value)) {
591  return -1;
592  }
593 
594  return 0;
595 }
596 
598 {
599  return parking_lot_container;
600 }
601 
602 struct parking_lot *parking_lot_find_by_name(const char *lot_name)
603 {
604  struct parking_lot *lot = named_item_find(parking_lot_container, lot_name);
605  return lot;
606 }
607 
609 {
610  const char *name;
611 
612  /* The channel variable overrides everything */
613  name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
614  if (ast_strlen_zero(name)) {
615  /* Try the channel's parking lot. */
616  name = ast_channel_parkinglot(chan);
617  if (ast_strlen_zero(name)) {
618  /* Fall back to the default parking lot. */
619  name = DEFAULT_PARKING_LOT;
620  }
621  }
622 
623  return name;
624 }
625 
626 static void parking_lot_destructor(void *obj)
627 {
628  struct parking_lot *lot = obj;
629 
630  if (lot->parking_bridge) {
632  }
633  ao2_cleanup(lot->parked_users);
634  ao2_cleanup(lot->cfg);
636 }
637 
638 static struct parking_lot *alloc_new_parking_lot(struct parking_lot_cfg *lot_cfg)
639 {
640  struct parking_lot *lot;
641  if (!(lot = ao2_alloc(sizeof(*lot), parking_lot_destructor))) {
642  return NULL;
643  }
644 
645  if (ast_string_field_init(lot, 32)) {
646  return NULL;
647  }
648 
649  /* Create parked user ordered list */
652  parked_user_sort_fn,
653  parked_user_cmp_fn);
654 
655  if (!lot->parked_users) {
656  ao2_cleanup(lot);
657  return NULL;
658  }
659 
660  ast_string_field_set(lot, name, lot_cfg->name);
661  return lot;
662 }
663 
665 {
666  if (!ast_strlen_zero(lot_cfg->registrar)) {
667  /* Although the function is called ast_context_destroy, the use of this funtion is
668  * intended only to remove extensions, hints, etc registered by the parking lot's registrar.
669  * It won't actually destroy the context unless that context is empty afterwards and it is
670  * unreferenced.
671  */
672  ast_context_destroy(NULL, lot_cfg->registrar);
673  }
674 
675  /* If we come back for a second pass, someone else has this registrar now. */
676  ast_string_field_set(lot_cfg, registrar, "");
677 }
678 
679 static void remove_all_configured_parking_lot_extensions(void)
680 {
681  RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
682  struct parking_lot_cfg *lot_cfg;
683  struct ao2_iterator iter;
684 
685  if (!cfg) {
686  return;
687  }
688 
689  for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
691  }
692 
693  ast_context_destroy(NULL, BASE_REGISTRAR);
694 
695  ao2_iterator_destroy(&iter);
696 }
697 
698 /*!
699  * \internal
700  * \since 12
701  * \brief Create an extension using ast_add_extension2_nolock. This function automatically allocates a duplicate
702  * of the data string so that whatever calls it doesn't have to deal with freeing it if the ast_add_extension2_nolock
703  * fails.
704  *
705  * \param context a write locked ast_context. Make certain it is write locked prior to calling this function
706  * \param replace whether the extension should replace existing extensions
707  * \param extension name of the extension desired
708  * \param priority priority of the extension we are registering
709  * \param application name of the application being used for the extension
710  * \param data application arguments
711  * \param registrar name of the registrar you should use for the extension.
712  * Make sure this string doesn't go anywhere while there are still extensions using it.
713  */
714 static int parking_add_extension(struct ast_context *context, int replace, const char *extension,
715  int priority, const char *application, const char *data, const char *registrar)
716 {
717  char *data_duplicate = ast_strdup(data);
718 
719  if (!data_duplicate) {
720  return -1;
721  }
722 
723  if (ast_add_extension2_nolock(context, replace, extension, priority, NULL, NULL,
724  application, data_duplicate, ast_free_ptr, registrar, NULL, 0)) {
725  return -1;
726  }
727 
728  return 0;
729 }
730 
731 static int extension_is_compatible(struct parking_lot_cfg *lot_cfg, const char *app_type, struct ast_exten *extension)
732 {
733  const char *extension_registrar = ast_get_extension_registrar(extension);
734  const char *extension_context = ast_get_context_name(ast_get_extension_context(extension));
735  const char *extension_name = ast_get_extension_name(extension);
736  const char *extension_application = ast_get_extension_app(extension);
737 
738  ast_assert(extension_registrar && extension_context && extension_name && extension_application);
739 
740  if (strcmp(extension_registrar, BASE_REGISTRAR)) {
741  ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s', but that extension is already owned by %s.\n",
742  lot_cfg->name, extension_name, extension_context, extension_registrar);
743  return 0;
744  }
745 
746  if (strcmp(extension_application, app_type)) {
747  ast_log(LOG_ERROR, "Parking lot '%s' -- Needs an extension '%s@%s' with a non-exclusive %s application, "
748  "but a/an %s application is already registered to that extension by %s.\n",
749  lot_cfg->name, extension_name, extension_context, app_type,
750  extension_application, BASE_REGISTRAR);
751  return 0;
752  }
753 
754  ast_debug(3, "Parking lot '%s' -- extension '%s@%s' with application %s is compatible.\n",
755  lot_cfg->name, extension_name, extension_context, app_type);
756  return 1;
757 }
758 
760 {
761  int parkingspace;
762  struct ast_exten *existing_exten;
763  struct ast_context *lot_context;
764  struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
765  const char *parkext_registrar_pointer; /* Used for park extension */
766  const char *parkedcall_registrar_pointer; /* Used for parkedcall extensions/hints */
767 
768  if (ast_strlen_zero(lot_cfg->parkext)) {
769  return 0;
770  }
771 
772  ast_string_field_build(lot_cfg, registrar, "%s/%s", BASE_REGISTRAR, lot_cfg->name);
773  parkedcall_registrar_pointer = lot_cfg->registrar;
774 
775  if (lot_cfg->parkext_exclusive) {
776  parkext_registrar_pointer = lot_cfg->registrar;
777  } else {
778  parkext_registrar_pointer = BASE_REGISTRAR;
779  }
780 
781  /* We need the contexts list locked to safely be able to both read and lock the specific context within */
783 
784  if (!(lot_context = ast_context_find_or_create(NULL, NULL, lot_cfg->parking_con, parkext_registrar_pointer))) {
785  ast_log(LOG_ERROR, "Parking lot '%s' -- Needs a context '%s' which does not exist and Asterisk was unable to create\n",
786  lot_cfg->name, lot_cfg->parking_con);
788  return -1;
789  }
790 
791  /* Once we know what context we will be modifying, we need to write lock it because we will be reading extensions
792  * and we don't want something else to destroy them while we are looking at them.
793  */
794  ast_wrlock_context(lot_context);
795 
797 
798  /* Handle generation/confirmation for the Park extension */
799  if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, lot_cfg->parkext, 1, NULL, NULL, E_MATCH))) {
800  if (lot_cfg->parkext_exclusive || !extension_is_compatible(lot_cfg, PARK_APPLICATION, existing_exten)) {
801  ast_unlock_context(lot_context);
802  return -1;
803  }
804  } else if (parking_add_extension(lot_context, 0, lot_cfg->parkext, 1, PARK_APPLICATION,
805  lot_cfg->parkext_exclusive ? lot_cfg->name : "", parkext_registrar_pointer)) {
806  ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
807  lot_cfg->name, PARK_APPLICATION, lot_cfg->parkext, lot_cfg->parking_con);
808  ast_unlock_context(lot_context);
809  return -1;
810  }
811 
812  /* Handle generation/confirmation for the ParkedCall extensions and hints */
813  for (parkingspace = lot_cfg->parking_start; parkingspace <= lot_cfg->parking_stop; parkingspace++) {
814  char space[AST_MAX_EXTENSION];
815  RAII_VAR(struct ast_str *, arguments_string, NULL, ast_free);
816  find_info.stacklen = 0; /* reset for pbx_find_exten */
817 
818  snprintf(space, sizeof(space), "%d", parkingspace);
819 
820  /* Unlike the Park extensions, ParkedCall extensions and their hints may never be shared for any reason. */
821  if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, 1, NULL, NULL, E_MATCH))) {
822  ast_unlock_context(lot_context);
823  return -1;
824  }
825 
826  arguments_string = ast_str_create(32);
827  if (!arguments_string) {
828  ast_unlock_context(lot_context);
829  return -1;
830  }
831 
832  ast_str_set(&arguments_string, 0, "%s,%s", lot_cfg->name, space);
833  if (parking_add_extension(lot_context, 0, space, 1, PARKED_CALL_APPLICATION,
834  ast_str_buffer(arguments_string), parkedcall_registrar_pointer)) {
835  ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add %s extension '%s@%s' to the PBX.\n",
836  lot_cfg->name, PARKED_CALL_APPLICATION, space, lot_cfg->parking_con);
837  ast_unlock_context(lot_context);
838  return -1;
839  }
840 
841  find_info.stacklen = 0; /* reset for pbx_find_exten */
842 
843  if (lot_cfg->parkaddhints) {
844  char hint_device[AST_MAX_EXTENSION];
845 
846  snprintf(hint_device, sizeof(hint_device), "park:%s@%s", space, lot_cfg->parking_con);
847 
848  if ((existing_exten = pbx_find_extension(NULL, NULL, &find_info, lot_cfg->parking_con, space, PRIORITY_HINT, NULL, NULL, E_MATCH))) {
849  ast_log(LOG_ERROR, "Parking lot '%s' -- Needs to add a hint '%s' at '%s@%s' but one already exists owned by %s\n",
850  lot_cfg->name, hint_device, space, lot_cfg->parking_con, ast_get_extension_registrar(existing_exten));
851  ast_unlock_context(lot_context);
852  return -1;
853  }
854 
855  if (parking_add_extension(lot_context, 0, space, PRIORITY_HINT, hint_device, "", parkedcall_registrar_pointer)) {
856  ast_log(LOG_ERROR, "Parking lot '%s' -- Failed to add hint '%s@%s' to the PBX.\n",
857  lot_cfg->name, space, lot_cfg->parking_con);
858  ast_unlock_context(lot_context);
859  return -1;
860  }
861  }
862  }
863 
864  ast_unlock_context(lot_context);
865 
866  return 0;
867 }
868 
869 struct parking_lot *parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg, int dynamic)
870 {
871  struct parking_lot *lot;
872  struct parking_lot_cfg *replaced_cfg = NULL;
873  int found = 0;
874 
875  /* Start by trying to find it. If that works we can skip the rest. */
876  lot = named_item_find(parking_lot_container, lot_cfg->name);
877  if (!lot) {
878  lot = alloc_new_parking_lot(lot_cfg);
879 
880  /* If we still don't have a lot, we failed to alloc one. */
881  if (!lot) {
882  return NULL;
883  }
884  } else {
885  found = 1;
886 
887  if (dynamic) {
888  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);
889  ao2_cleanup(lot);
890  return NULL;
891  }
892  }
893 
894  /* Set the configuration reference. Unref the one currently in the lot if it's there. */
895  if (lot->cfg) {
896  replaced_cfg = lot->cfg;
897  }
898 
899  ao2_ref(lot_cfg, +1);
900  lot->cfg = lot_cfg;
901 
902  ao2_cleanup(replaced_cfg);
903 
904  /* Set the operating mode to normal since the parking lot has a configuration. */
905  lot->disable_mark = 0;
906  lot->mode = dynamic ? PARKINGLOT_DYNAMIC : PARKINGLOT_NORMAL;
907 
908  if (!found) {
909  /* Link after configuration is set since a lot without configuration will cause all kinds of trouble. */
910  ao2_link(parking_lot_container, lot);
911  };
912 
913  return lot;
914 }
915 
916 static void generate_or_link_lots_to_configs(void)
917 {
918  RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
919  struct parking_lot_cfg *lot_cfg;
920  struct ao2_iterator iter;
921 
922  iter = ao2_iterator_init(cfg->parking_lots, 0);
923  for (; (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
924  ao2_cleanup(parking_lot_build_or_update(lot_cfg, 0));
925  }
926  ao2_iterator_destroy(&iter);
927 }
928 
930 {
931  RAII_VAR(struct parking_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
932 
933  if (!cfg) {
934  return 0;
935  }
936 
937  return cfg->global->parkeddynamic;
938 }
939 
940 static struct parking_lot_cfg *clone_parkinglot_cfg(struct parking_lot_cfg *source, const char *name)
941 {
942  struct parking_lot_cfg *cfg = parking_lot_cfg_alloc(name);
943 
944  if (!cfg) {
945  return NULL;
946  }
947 
948  ast_string_fields_copy(cfg, source);
949 
950  /* Needs to be reset after being copied */
951  ast_string_field_set(cfg, name, name);
952 
953  /* Stuff that should be cloned that isn't hit by string field copy */
954  cfg->parking_start = source->parking_start;
955  cfg->parking_stop = source->parking_stop;
956  cfg->parkingtime = source->parkingtime;
957  cfg->comebackdialtime = source->comebackdialtime;
958  cfg->parkfindnext = source->parkfindnext;
959  cfg->parkext_exclusive = source->parkext_exclusive;
960  cfg->parkaddhints = source->parkaddhints;
961  cfg->comebacktoorigin = source->comebacktoorigin;
962  cfg->parkedplay = source->parkedplay;
965  cfg->parkedcallhangup = source->parkedcallhangup;
967 
968  return cfg;
969 }
970 
971 static struct parking_lot *create_dynamic_lot_full(const char *name, struct ast_channel *chan, int forced)
972 {
973  RAII_VAR(struct parking_lot_cfg *, cfg, NULL, ao2_cleanup);
974  RAII_VAR(struct parking_lot *, template_lot, NULL, ao2_cleanup);
975 
976  struct parking_lot *lot;
977  const char *dyn_context;
978  const char *dyn_exten;
979  const char *dyn_range;
980  const char *template_name;
981  const char *chan_template_name;
982  int dyn_start;
983  int dyn_end;
984 
985  if (!forced && !parking_dynamic_lots_enabled()) {
986  return NULL;
987  }
988 
989  ast_channel_lock(chan);
990  chan_template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
991  dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
992  dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
993  dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
994  ast_channel_unlock(chan);
995 
996  template_name = S_OR(chan_template_name, DEFAULT_PARKING_LOT);
997 
998  template_lot = parking_lot_find_by_name(template_name);
999  if (!template_lot) {
1000  ast_log(LOG_ERROR, "Lot %s does not exist. Can not use it as a dynamic parking lot template.\n",
1001  template_name);
1002  return NULL;
1003  }
1004 
1005  cfg = clone_parkinglot_cfg(template_lot->cfg, name);
1006 
1007  if (!cfg) {
1008  ast_log(LOG_ERROR, "Failed to allocate dynamic parking lot configuration.\n");
1009  return NULL;
1010  }
1011 
1012  if (!ast_strlen_zero(dyn_exten)) {
1013  ast_string_field_set(cfg, parkext, dyn_exten);
1014  }
1015 
1016  if (!ast_strlen_zero(dyn_context)) {
1017  ast_string_field_set(cfg, parking_con, dyn_context);
1018  }
1019 
1020  if (!ast_strlen_zero(dyn_range)) {
1021  if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
1022  ast_log(LOG_ERROR,
1023  "Invalid parking range %s specified in PARKINGDYNPOS: could not parse minimum/maximum parking space range\n", dyn_range);
1024  return NULL;
1025  }
1026  if (dyn_end < dyn_start || dyn_start < 0) {
1027  ast_log(LOG_ERROR,
1028  "Invalid parking range %s specified for PARKINGDYNPOS: end parking space must be greater than starting parking space.\n", dyn_range);
1029  return NULL;
1030  }
1031 
1032  cfg->parking_start = dyn_start;
1033  cfg->parking_stop = dyn_end;
1034  }
1035 
1037  ast_log(LOG_ERROR, "Extensions for dynamic parking lot '%s' could not be registered. Dynamic lot creation failed.\n", name);
1038  return NULL;
1039  }
1040 
1041  ao2_lock(parking_lot_container);
1042 
1043  if ((lot = parking_lot_find_by_name(name))) {
1044  ao2_unlock(parking_lot_container);
1045  ast_log(LOG_ERROR, "Started creating dynamic parking lot '%s', but a parking lot with that name already exists.\n", name);
1046  ao2_ref(lot, -1);
1047  return NULL;
1048  }
1049 
1050  lot = parking_lot_build_or_update(cfg, 1);
1051  ao2_unlock(parking_lot_container);
1052 
1053  if (!lot) {
1054  ast_log(LOG_NOTICE, "Failed to build dynamic parking lot '%s'\n", name);
1055  }
1056 
1057  return lot;
1058 }
1059 
1060 struct parking_lot *parking_create_dynamic_lot(const char *name, struct ast_channel *chan){
1061  return create_dynamic_lot_full(name, chan, 0);
1062 }
1063 
1064 #if defined(TEST_FRAMEWORK)
1065 struct parking_lot *parking_create_dynamic_lot_forced(const char *name, struct ast_channel *chan) {
1066  return create_dynamic_lot_full(name, chan, 1);
1067 }
1068 #endif
1069 
1070 /* Preapply */
1071 
1072 static int verify_default_parking_lot(void)
1073 {
1074  struct parking_config *cfg = aco_pending_config(&cfg_info);
1075  RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1076 
1077  if (!cfg) {
1078  return 0;
1079  }
1080 
1081  lot_cfg = ao2_find(cfg->parking_lots, DEFAULT_PARKING_LOT, OBJ_KEY);
1082  if (!lot_cfg) {
1083  lot_cfg = parking_lot_cfg_alloc(DEFAULT_PARKING_LOT);
1084  if (!lot_cfg) {
1085  return -1;
1086  }
1087  ast_log(AST_LOG_NOTICE, "Adding %s profile to res_parking\n", DEFAULT_PARKING_LOT);
1088  aco_set_defaults(&parking_lot_type, DEFAULT_PARKING_LOT, lot_cfg);
1089  ast_string_field_set(lot_cfg, parkext, DEFAULT_PARKING_EXTEN);
1090  ao2_link(cfg->parking_lots, lot_cfg);
1091  }
1092 
1093  return 0;
1094 }
1095 
1096 static void remove_pending_parking_lot_extensions(struct parking_config *cfg_pending)
1097 {
1098  struct parking_lot_cfg *lot_cfg;
1099  struct ao2_iterator iter;
1100 
1101  for (iter = ao2_iterator_init(cfg_pending->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1103  }
1104 
1105  ao2_iterator_destroy(&iter);
1106 
1107  ast_context_destroy(NULL, BASE_REGISTRAR);
1108 
1109 }
1110 
1111 static int configure_parking_extensions(void)
1112 {
1113  struct parking_config *cfg = aco_pending_config(&cfg_info);
1114  struct ao2_iterator iter;
1115  RAII_VAR(struct parking_lot_cfg *, lot_cfg, NULL, ao2_cleanup);
1116  int res = 0;
1117 
1118  if (!cfg) {
1119  return 0;
1120  }
1121 
1122  /* Clear existing extensions */
1123  remove_all_configured_parking_lot_extensions();
1124 
1125  /* Attempt to build new extensions for each lot */
1126  for (iter = ao2_iterator_init(cfg->parking_lots, 0); (lot_cfg = ao2_iterator_next(&iter)); ao2_ref(lot_cfg, -1)) {
1127  if (parking_lot_cfg_create_extensions(lot_cfg)) {
1128  ao2_cleanup(lot_cfg);
1129  lot_cfg = NULL;
1130  res = -1;
1131  break;
1132  }
1133  }
1134  ao2_iterator_destroy(&iter);
1135 
1136  if (res) {
1137  remove_pending_parking_lot_extensions(cfg);
1138  ast_log(LOG_ERROR, "Extension registration failed. Previously configured lot extensions were removed and can not be safely restored.\n");
1139  }
1140 
1141  return res;
1142 }
1143 
1144 static void mark_lots_as_disabled(void)
1145 {
1146  struct ao2_iterator iter;
1147  struct parking_lot *lot;
1148 
1149  for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1150  lot->disable_mark = 1;
1151  }
1152 
1153  ao2_iterator_destroy(&iter);
1154 }
1155 
1156 static int config_parking_preapply(void)
1157 {
1158  mark_lots_as_disabled();
1159 
1160  if (verify_default_parking_lot()) {
1161  return -1;
1162  }
1163 
1164  if (configure_parking_extensions()) {
1165  return -1;
1166  }
1167 
1168  return 0;
1169 }
1170 
1171 static void disable_marked_lots(void)
1172 {
1173  struct ao2_iterator iter;
1174  struct parking_lot *lot;
1175 
1176  for (iter = ao2_iterator_init(parking_lot_container, 0); (lot = ao2_iterator_next(&iter)); ao2_ref(lot, -1)) {
1177  if (lot->disable_mark) {
1178  parking_lot_disable(lot);
1179  }
1180  }
1181 
1182  ao2_iterator_destroy(&iter);
1183 }
1184 
1185 static void link_configured_disable_marked_lots(void)
1186 {
1187  generate_or_link_lots_to_configs();
1188  disable_marked_lots();
1189 }
1190 
1191 static int unload_module(void)
1192 {
1194  remove_all_configured_parking_lot_extensions();
1200  ao2_cleanup(parking_lot_container);
1201  parking_lot_container = NULL;
1202  aco_info_destroy(&cfg_info);
1203  ao2_global_obj_release(globals);
1204 
1205  return 0;
1206 }
1207 
1208 static int load_module(void)
1209 {
1210  parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
1212  parking_lot_sort_fn,
1213  NULL);
1214  if (!parking_lot_container) {
1215  goto error;
1216  }
1217 
1218  if (aco_info_init(&cfg_info)) {
1219  goto error;
1220  }
1221 
1222  /* Global options */
1223  aco_option_register(&cfg_info, "parkeddynamic", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct parking_global_config, parkeddynamic));
1224 
1225  /* Register the per parking lot options. */
1226  aco_option_register(&cfg_info, "parkext", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parkext));
1227  aco_option_register(&cfg_info, "context", ACO_EXACT, parking_lot_types, "parkedcalls", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, parking_con));
1228  aco_option_register(&cfg_info, "parkingtime", ACO_EXACT, parking_lot_types, "45", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, parkingtime));
1229  aco_option_register(&cfg_info, "comebacktoorigin", ACO_EXACT, parking_lot_types, "yes", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, comebacktoorigin));
1230  aco_option_register(&cfg_info, "comebackcontext", ACO_EXACT, parking_lot_types, "parkedcallstimeout", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, comebackcontext));
1231  aco_option_register(&cfg_info, "comebackdialtime", ACO_EXACT, parking_lot_types, "30", OPT_UINT_T, 0, FLDSET(struct parking_lot_cfg, comebackdialtime));
1232  aco_option_register(&cfg_info, "parkedmusicclass", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, mohclass));
1233  aco_option_register(&cfg_info, "parkext_exclusive", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkext_exclusive));
1234  aco_option_register(&cfg_info, "parkinghints", ACO_EXACT, parking_lot_types, "no", OPT_BOOL_T, 1, FLDSET(struct parking_lot_cfg, parkaddhints));
1235  aco_option_register(&cfg_info, "courtesytone", ACO_EXACT, parking_lot_types, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct parking_lot_cfg, courtesytone));
1236 
1237  /* More complicated parking lot options that require special handling */
1238  aco_option_register_custom(&cfg_info, "parkpos", ACO_EXACT, parking_lot_types, "701-750", option_handler_parkpos, 0);
1239  aco_option_register_custom(&cfg_info, "findslot", ACO_EXACT, parking_lot_types, "first", option_handler_findslot, 0);
1240  aco_option_register_custom(&cfg_info, "parkedplay", ACO_EXACT, parking_lot_types, "caller", option_handler_parkedfeature, OPT_PARKEDPLAY);
1241  aco_option_register_custom(&cfg_info, "parkedcalltransfers", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDTRANSFERS);
1242  aco_option_register_custom(&cfg_info, "parkedcallreparking", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDREPARKING);
1243  aco_option_register_custom(&cfg_info, "parkedcallhangup", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDHANGUP);
1244  aco_option_register_custom(&cfg_info, "parkedcallrecording", ACO_EXACT, parking_lot_types, "no", option_handler_parkedfeature, OPT_PARKEDRECORDING);
1245 
1246  if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
1247  goto error;
1248  }
1249 
1250  if (load_parking_applications()) {
1251  goto error;
1252  }
1253 
1254  if (load_parking_ui()) {
1255  goto error;
1256  }
1257 
1258  if (load_parking_manager()) {
1259  goto error;
1260  }
1261 
1263  goto error;
1264  }
1265 
1266  if (load_parking_devstate()) {
1267  goto error;
1268  }
1269 
1270  if (load_parking_tests()) {
1271  goto error;
1272  }
1273 
1274  return AST_MODULE_LOAD_SUCCESS;
1275 
1276 error:
1277  unload_module();
1278  return AST_MODULE_LOAD_DECLINE;
1279 }
1280 
1281 static int reload_module(void)
1282 {
1283  if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
1284  return AST_MODULE_LOAD_DECLINE;
1285  }
1286 
1287  return 0;
1288 }
1289 
1290 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Call Parking Resource",
1291  .support_level = AST_MODULE_SUPPORT_CORE,
1292  .load = load_module,
1293  .unload = unload_module,
1294  .reload = reload_module,
1295  .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
1296 );
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8491
Main Channel structure associated with a channel.
int load_parking_manager(void)
Register manager actions and setup subscriptions for stasis events.
Asterisk main include file. File version handling, generic pbx functions.
ast_exten: An extension The dialplan is saved as a linked list with each context having it's own link...
Definition: pbx.c:237
unsigned int comebackdialtime
Definition: res_parking.h:70
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
static void parking_global_config_destructor(void *obj)
destructor for parking_global_config
Definition: res_parking.c:371
int parkedcallreparking
Definition: res_parking.h:77
struct parking_lot * parking_create_dynamic_lot(const char *name, struct ast_channel *chan)
Create a dynamic parking lot.
Definition: res_parking.c:1060
#define aco_option_register_custom(info, name, matchtype, types, default_val, handler, flags)
Register a config option.
unsigned int aco_option_get_flags(const struct aco_option *option)
Read the flags of a config option - useful when using a custom callback for a config option...
void unload_parking_ui(void)
Unregister CLI commands.
Definition: parking_ui.c:203
void unload_parking_applications(void)
Unregister parking applications.
#define OBJ_KEY
Definition: astobj2.h:1151
struct ast_bridge * parking_bridge
Definition: res_parking.h:94
#define OBJ_POINTER
Definition: astobj2.h:1150
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
struct ao2_container * parked_users
Definition: res_parking.h:95
int load_parking_tests(void)
Register parking unit tests.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
unsigned int parkingtime
Definition: res_parking.h:69
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
Structure for variables, used for configurations and for channel variables.
int parking_lot_remove_if_unused(struct parking_lot *lot)
Remove a parking lot from the usable lists if it is no longer involved in any calls and no configurat...
Definition: res_parking.c:400
void parking_lot_cfg_remove_extensions(struct parking_lot_cfg *lot_cfg)
Remove extensions belonging to a parking lot configuration.
Definition: res_parking.c:664
int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
Destroy a bridge.
Definition: bridge.c:944
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
Definition: astobj2.h:918
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int ast_wrlock_contexts(void)
Write locks the context list.
Definition: pbx.c:8463
static int parking_feature_flag_cfg(int *param, const char *var)
Maps string values for option_handler_parkedfeature to their ENUM values.
Definition: res_parking.c:545
int load_parking_ui(void)
Register CLI commands.
Definition: parking_ui.c:198
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
static void * named_item_find(struct ao2_container *container, const char *name)
find an item in a container by its name
Definition: res_parking.c:497
int disable_mark
Definition: res_parking.h:98
The representation of a single configuration file to be processed.
enum aco_type_t type
void ast_free_ptr(void *ptr)
free() wrapper
Definition: main/astmm.c:1739
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
void unload_parking_bridge_features(void)
Unregister features registered by load_parking_bridge_features.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define OBJ_PARTIAL_KEY
Definition: astobj2.h:1152
Utility functions.
static int option_handler_findslot(const struct aco_option *opt, struct ast_variable *var, void *obj)
Custom field handler for the findslot option.
Definition: res_parking.c:526
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
struct ao2_container * get_parking_lot_container(void)
Get a pointer to the parking lot container for purposes such as iteration.
Definition: res_parking.c:597
void unload_parking_devstate(void)
Unregister Parking devstate handler.
Configuration File Parser.
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
const ast_string_field name
Definition: res_parking.h:89
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
const ast_string_field parking_con
Definition: res_parking.h:89
int load_parking_bridge_features(void)
Register bridge features for parking.
void * aco_pending_config(struct aco_info *info)
Get pending config changes.
int parking_dynamic_lots_enabled(void)
Check global configuration to see if dynamic parking is enabled.
Definition: res_parking.c:929
Type for default option handler for unsigned integers.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
unsigned int parkaddhints
Definition: res_parking.h:73
struct parking_lot * parking_lot_find_by_name(const char *lot_name)
Find a parking lot based on its name.
Definition: res_parking.c:602
static struct ao2_container * parking_lot_container
Definition: res_parking.c:266
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define AST_MAX_EXTENSION
Definition: channel.h:134
const char * find_channel_parking_lot_name(struct ast_channel *chan)
Find parking lot name from channel.
Definition: res_parking.c:608
int parking_space
Definition: res_parking.h:107
structure to hold extensions
static int option_handler_parkedfeature(const struct aco_option *opt, struct ast_variable *var, void *obj)
Custom field handler for feature mapping on parked call pickup options.
Definition: res_parking.c:565
struct ao2_container * container
Definition: res_fax.c:501
#define ast_debug(level,...)
Log a DEBUG message.
#define PRIORITY_HINT
Definition: pbx.h:54
Core PBX routines and definitions.
unsigned int comebacktoorigin
Definition: res_parking.h:74
static void * parking_lot_cfg_alloc(const char *cat)
create a parking lot structure
Definition: res_parking.c:461
static void parking_config_destructor(void *obj)
destructor for parking_config
Definition: res_parking.c:363
Their was an error and no changes were applied.
int load_parking_applications(void)
Register parking applications.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
Configuration option-handling.
#define ast_string_fields_copy(copy, orig)
Copy all string fields from one instance to another of the same structure.
Definition: stringfields.h:630
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
Support for dynamic strings.
Definition: strings.h:623
const ast_string_field registrar
Definition: res_parking.h:89
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
Definition: astobj2.h:859
Type for default option handler for bools (ast_true/ast_false)
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8473
void unload_parking_tests(void)
Unregister parking unit tests.
int ast_wrlock_context(struct ast_context *con)
Write locks a given context.
Definition: pbx.c:8481
#define PARK_APPLICATION
The default parking application that Asterisk expects.
Definition: parking.h:35
static int option_handler_parkpos(const struct aco_option *opt, struct ast_variable *var, void *obj)
Custom field handler for parking positions.
Definition: res_parking.c:505
unsigned int parkfindnext
Definition: res_parking.h:71
void unload_parking_manager(void)
Unregister manager actions and remove subscriptions for stasis events.
int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
Set all default options of obj.
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
const ast_string_field name
Definition: res_parking.h:100
static void * parking_config_alloc(void)
allocator callback for parking_config. Notice it returns void * since it is only used by the backend ...
Definition: res_parking.c:377
static struct aco_type global_option
An aco_type structure to link the "general" category to the skel_global_config type.
Definition: app_skel.c:241
structure to hold users read from users.conf
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
int parkedcalltransfers
Definition: res_parking.h:76
enum parking_lot_modes mode
Definition: res_parking.h:97
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Standard Command Line Interface.
Type information about a category-level configurable object.
const ast_string_field parkext
Definition: res_parking.h:89
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: utils.c:2216
const char * filename
struct parking_lot * parking_lot_build_or_update(struct parking_lot_cfg *lot_cfg, int dynamic)
If a parking lot exists in the parking lot list already, update its status to match the provided conf...
Definition: res_parking.c:869
int parkedcallrecording
Definition: res_parking.h:79
void ast_context_destroy(struct ast_context *con, const char *registrar)
Destroy a context (matches the specified context or ANY context if NULL)
Definition: pbx.c:8221
Type for default option handler for stringfields.
int load_parking_devstate(void)
Register Parking devstate handler.
Definition: search.h:40
Reject objects with duplicate keys in container.
Definition: astobj2.h:1188
Call Parking Resource Internal API.
Generic container type.
Call Parking and Pickup API Includes code and algorithms from the Zapata library. ...
struct ast_context * ast_context_find_or_create(struct ast_context **extcontexts, struct ast_hashtab *exttable, const char *name, const char *registrar)
Register a new context or find an existing one.
Definition: pbx.c:6149
#define AO2_GLOBAL_OBJ_STATIC(name)
Define a global object holder to be used to hold an ao2 object, statically initialized.
Definition: astobj2.h:847
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
#define CONFIG_INFO_STANDARD(name, arr, alloc,...)
Declare an aco_info struct with default module and preload values.
ast_context: An extension context
Definition: pbx.c:284
int parking_lot_cfg_create_extensions(struct parking_lot_cfg *lot_cfg)
Add extensions for a parking lot configuration.
Definition: res_parking.c:759
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
unsigned int parkext_exclusive
Definition: res_parking.h:72
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, const char *registrar_file, int registrar_line)
Same as ast_add_extension2, but assumes you have already locked context.
Definition: pbx.c:7266
static void parking_lot_cfg_destructor(void *obj)
Destroy a parking lot cfg object.
Definition: res_parking.c:427
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
struct parking_lot_cfg * cfg
Definition: res_parking.h:96
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532