Asterisk - The Open Source Telephony Project  21.4.1
parking_manager.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 Manager Actions and Events
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25 
26 #include "asterisk.h"
27 
28 #include "res_parking.h"
29 #include "asterisk/config.h"
31 #include "asterisk/utils.h"
32 #include "asterisk/module.h"
33 #include "asterisk/cli.h"
34 #include "asterisk/astobj2.h"
35 #include "asterisk/features.h"
36 #include "asterisk/manager.h"
37 #include "asterisk/bridge.h"
38 #include "asterisk/module.h"
39 
40 /*** DOCUMENTATION
41  <manager name="Parkinglots" language="en_US">
42  <synopsis>
43  Get a list of parking lots
44  </synopsis>
45  <syntax>
46  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
47  </syntax>
48  <description>
49  <para>List all parking lots as a series of AMI events</para>
50  </description>
51  </manager>
52  <manager name="ParkedCalls" language="en_US">
53  <synopsis>
54  List parked calls.
55  </synopsis>
56  <syntax>
57  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
58  <parameter name="ParkingLot">
59  <para>If specified, only show parked calls from the parking lot with this name.</para>
60  </parameter>
61  </syntax>
62  <description>
63  <para>List parked calls.</para>
64  </description>
65  </manager>
66  <manager name="Park" language="en_US">
67  <synopsis>
68  Park a channel.
69  </synopsis>
70  <syntax>
71  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
72  <parameter name="Channel" required="true">
73  <para>Channel name to park.</para>
74  </parameter>
75  <parameter name="TimeoutChannel" required="false">
76  <para>Channel name to use when constructing the dial string that will be dialed if the parked channel
77  times out. If <literal>TimeoutChannel</literal> is in a two party bridge with
78  <literal>Channel</literal>, then <literal>TimeoutChannel</literal> will receive an announcement and be
79  treated as having parked <literal>Channel</literal> in the same manner as the Park Call DTMF feature.
80  </para>
81  </parameter>
82  <parameter name="AnnounceChannel" required="false">
83  <para>If specified, then this channel will receive an announcement when <literal>Channel</literal>
84  is parked if <literal>AnnounceChannel</literal> is in a state where it can receive announcements
85  (AnnounceChannel must be bridged). <literal>AnnounceChannel</literal> has no bearing on the actual
86  state of the parked call.</para>
87  </parameter>
88  <parameter name="Timeout" required="false">
89  <para>Overrides the timeout of the parking lot for this park action. Specified in milliseconds, but will be converted to
90  seconds. Use a value of 0 to disable the timeout.
91  </para>
92  </parameter>
93  <parameter name="Parkinglot" required="false">
94  <para>The parking lot to use when parking the channel</para>
95  </parameter>
96  <parameter name="ParkingSpace" required="false">
97  <para>The parking space extension in the parking lot.
98  If the space is already in use then execution will continue at the next priority.
99  </para>
100  </parameter>
101  </syntax>
102  <description>
103  <para>Park an arbitrary channel with optional arguments for specifying the parking lot used, how long
104  the channel should remain parked, and what dial string to use as the parker if the call times out.
105  </para>
106  </description>
107  </manager>
108  <managerEvent language="en_US" name="ParkedCall">
109  <managerEventInstance class="EVENT_FLAG_CALL">
110  <synopsis>Raised when a channel is parked.</synopsis>
111  <syntax>
112  <channel_snapshot prefix="Parkee"/>
113  <parameter name="ParkerDialString">
114  <para>Dial String that can be used to call back the parker on ParkingTimeout.</para>
115  </parameter>
116  <parameter name="Parkinglot">
117  <para>Name of the parking lot that the parkee is parked in</para>
118  </parameter>
119  <parameter name="ParkingSpace">
120  <para>Parking Space that the parkee is parked in</para>
121  </parameter>
122  <parameter name="ParkingTimeout">
123  <para>Time remaining until the parkee is forcefully removed from parking in seconds</para>
124  </parameter>
125  <parameter name="ParkingDuration">
126  <para>Time the parkee has been in the parking bridge (in seconds)</para>
127  </parameter>
128  </syntax>
129  </managerEventInstance>
130  </managerEvent>
131  <managerEvent language="en_US" name="ParkedCallTimeOut">
132  <managerEventInstance class="EVENT_FLAG_CALL">
133  <synopsis>Raised when a channel leaves a parking lot due to reaching the time limit of being parked.</synopsis>
134  <syntax>
135  <channel_snapshot prefix="Parkee"/>
136  <channel_snapshot prefix="Parker"/>
137  <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
138  </syntax>
139  </managerEventInstance>
140  </managerEvent>
141  <managerEvent language="en_US" name="ParkedCallGiveUp">
142  <managerEventInstance class="EVENT_FLAG_CALL">
143  <synopsis>Raised when a channel leaves a parking lot because it hung up without being answered.</synopsis>
144  <syntax>
145  <channel_snapshot prefix="Parkee"/>
146  <channel_snapshot prefix="Parker"/>
147  <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
148  </syntax>
149  </managerEventInstance>
150  </managerEvent>
151  <managerEvent language="en_US" name="UnParkedCall">
152  <managerEventInstance class="EVENT_FLAG_CALL">
153  <synopsis>Raised when a channel leaves a parking lot because it was retrieved from the parking lot and reconnected.</synopsis>
154  <syntax>
155  <channel_snapshot prefix="Parkee"/>
156  <channel_snapshot prefix="Parker"/>
157  <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
158  <channel_snapshot prefix="Retriever"/>
159  </syntax>
160  </managerEventInstance>
161  </managerEvent>
162  <managerEvent language="en_US" name="ParkedCallSwap">
163  <managerEventInstance class="EVENT_FLAG_CALL">
164  <synopsis>Raised when a channel takes the place of a previously parked channel</synopsis>
165  <syntax>
166  <channel_snapshot prefix="Parkee"/>
167  <channel_snapshot prefix="Parker"/>
168  <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
169  </syntax>
170  <description>
171  <para>This event is raised when a channel initially parked in the parking lot
172  is swapped out with a different channel. The most common case for this is when
173  an attended transfer to a parking lot occurs. The Parkee information in the event
174  will indicate the party that was swapped into the parking lot.</para>
175  </description>
176  </managerEventInstance>
177  </managerEvent>
178  ***/
179 
180 /*! \brief subscription to the parking lot topic */
182 
183 static struct ast_parked_call_payload *parked_call_payload_from_failure(struct ast_channel *chan)
184 {
185  RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
186 
187  ast_channel_lock(chan);
188  parkee_snapshot = ast_channel_snapshot_create(chan);
189  ast_channel_unlock(chan);
190  if (!parkee_snapshot) {
191  return NULL;
192  }
193 
194  return ast_parked_call_payload_create(PARKED_CALL_FAILED, parkee_snapshot, NULL, NULL, NULL, 0, 0, 0);
195 }
196 
197 static struct ast_parked_call_payload *parked_call_payload_from_parked_user(struct parked_user *pu, enum ast_parked_call_event_type event_type)
198 {
199  RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
200  long int timeout;
201  long int duration;
202  struct timeval now = ast_tvnow();
203  const char *lot_name = pu->lot->name;
204 
205  ast_channel_lock(pu->chan);
206  parkee_snapshot = ast_channel_snapshot_create(pu->chan);
207  ast_channel_unlock(pu->chan);
208  if (!parkee_snapshot) {
209  return NULL;
210  }
211 
212  timeout = pu->start.tv_sec + (long) pu->time_limit - now.tv_sec;
213  duration = now.tv_sec - pu->start.tv_sec;
214 
215  return ast_parked_call_payload_create(event_type, parkee_snapshot, pu->parker_dial_string, pu->retriever, lot_name, pu->parking_space, timeout, duration);
216 
217 }
218 
219 /*! \brief Builds a manager string based on the contents of a parked call payload */
221 {
222  struct ast_str *out = ast_str_create(1024);
223  RAII_VAR(struct ast_str *, parkee_string, NULL, ast_free);
224  RAII_VAR(struct ast_str *, retriever_string, NULL, ast_free);
225 
226  if (!out) {
227  return NULL;
228  }
229 
230  parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
231  if (!parkee_string) {
232  ast_free(out);
233  return NULL;
234  }
235 
236  if (payload->retriever) {
237  retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
238  if (!retriever_string) {
239  ast_free(out);
240  return NULL;
241  }
242  }
243 
244  ast_str_set(&out, 0,
245  "%s" /* parkee channel state */
246  "%s" /* retriever channel state (when available) */
247  "ParkerDialString: %s\r\n"
248  "Parkinglot: %s\r\n"
249  "ParkingSpace: %u\r\n"
250  "ParkingTimeout: %lu\r\n"
251  "ParkingDuration: %lu\r\n",
252 
253  ast_str_buffer(parkee_string),
254  retriever_string ? ast_str_buffer(retriever_string) : "",
255  payload->parker_dial_string,
256  payload->parkinglot,
257  payload->parkingspace,
258  payload->timeout,
259  payload->duration);
260 
261  return out;
262 }
263 
264 static void manager_parking_status_single_lot(struct mansession *s, const struct message *m, const char *id_text, const char *lot_name)
265 {
266  RAII_VAR(struct parking_lot *, curlot, NULL, ao2_cleanup);
267  struct parked_user *curuser;
268  struct ao2_iterator iter_users;
269  int total = 0;
270 
271  curlot = parking_lot_find_by_name(lot_name);
272  if (!curlot) {
273  astman_send_error(s, m, "Requested parking lot could not be found.");
274  return;
275  }
276 
277  astman_send_listack(s, m, "Parked calls will follow", "start");
278 
279  iter_users = ao2_iterator_init(curlot->parked_users, 0);
280  while ((curuser = ao2_iterator_next(&iter_users))) {
281  RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
282  RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
283 
284  payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
285  if (!payload) {
286  ao2_ref(curuser, -1);
287  break;
288  }
289 
290  parked_call_string = manager_build_parked_call_string(payload);
291  if (!parked_call_string) {
292  ao2_ref(curuser, -1);
293  break;
294  }
295 
296  total++;
297 
298  astman_append(s, "Event: ParkedCall\r\n"
299  "%s" /* The parked call string */
300  "%s" /* The action ID */
301  "\r\n",
302  ast_str_buffer(parked_call_string),
303  id_text);
304 
305  ao2_ref(curuser, -1);
306  }
307  ao2_iterator_destroy(&iter_users);
308 
309  astman_send_list_complete_start(s, m, "ParkedCallsComplete", total);
310  astman_append(s, "Total: %d\r\n", total);
312 }
313 
314 static void manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
315 {
316  struct parked_user *curuser;
317  struct ao2_container *lot_container;
318  struct ao2_iterator iter_lots;
319  struct ao2_iterator iter_users;
320  struct parking_lot *curlot;
321  int total = 0;
322 
323  lot_container = get_parking_lot_container();
324  if (!lot_container) {
325  ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
326  astman_send_error(s, m, "Could not create parking lot list");
327  return;
328  }
329 
330  astman_send_listack(s, m, "Parked calls will follow", "start");
331 
332  iter_lots = ao2_iterator_init(lot_container, 0);
333  while ((curlot = ao2_iterator_next(&iter_lots))) {
334  iter_users = ao2_iterator_init(curlot->parked_users, 0);
335  while ((curuser = ao2_iterator_next(&iter_users))) {
336  RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
337  RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
338 
339  payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
340  if (!payload) {
341  ao2_ref(curuser, -1);
342  ao2_iterator_destroy(&iter_users);
343  ao2_ref(curlot, -1);
344  goto abort_list;
345  }
346 
347  parked_call_string = manager_build_parked_call_string(payload);
348  if (!parked_call_string) {
349  ao2_ref(curuser, -1);
350  ao2_iterator_destroy(&iter_users);
351  ao2_ref(curlot, -1);
352  goto abort_list;
353  }
354 
355  total++;
356 
357  astman_append(s, "Event: ParkedCall\r\n"
358  "%s" /* The parked call string */
359  "%s" /* The action ID */
360  "\r\n",
361  ast_str_buffer(parked_call_string),
362  id_text);
363 
364  ao2_ref(curuser, -1);
365  }
366  ao2_iterator_destroy(&iter_users);
367  ao2_ref(curlot, -1);
368  }
369 abort_list:
370  ao2_iterator_destroy(&iter_lots);
371 
372  astman_send_list_complete_start(s, m, "ParkedCallsComplete", total);
373  astman_append(s, "Total: %d\r\n", total);
375 }
376 
377 static int manager_parking_status(struct mansession *s, const struct message *m)
378 {
379  const char *id = astman_get_header(m, "ActionID");
380  const char *lot_name = astman_get_header(m, "ParkingLot");
381  char id_text[256];
382 
383  id_text[0] = '\0';
384  if (!ast_strlen_zero(id)) {
385  snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
386  }
387 
388  if (!ast_strlen_zero(lot_name)) {
389  manager_parking_status_single_lot(s, m, id_text, lot_name);
390  } else {
391  manager_parking_status_all_lots(s, m, id_text);
392  }
393 
394  return 0;
395 }
396 
398  const char *id_text;
399  int count;
400 };
401 
402 static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *data, int flags)
403 {
404  struct parking_lot *curlot = obj;
405  struct mansession *s = arg;
406  struct park_list_data *list_data = data;
407 
408  astman_append(s, "Event: Parkinglot\r\n"
409  "%s" /* The Action ID */
410  "Name: %s\r\n"
411  "StartSpace: %d\r\n"
412  "StopSpace: %d\r\n"
413  "Timeout: %u\r\n"
414  "\r\n",
415  list_data->id_text,
416  curlot->name,
417  curlot->cfg->parking_start,
418  curlot->cfg->parking_stop,
419  curlot->cfg->parkingtime);
420  ++list_data->count;
421 
422  return 0;
423 }
424 
425 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
426 {
427  const char *id = astman_get_header(m, "ActionID");
428  struct ao2_container *lot_container;
429  char id_text[256];
430  struct park_list_data list_data;
431 
432  id_text[0] = '\0';
433  if (!ast_strlen_zero(id)) {
434  snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
435  }
436 
437  lot_container = get_parking_lot_container();
438  if (!lot_container) {
439  ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
440  astman_send_error(s, m, "Could not create parking lot list");
441  return 0;
442  }
443 
444  astman_send_listack(s, m, "Parking lots will follow", "start");
445 
446  list_data.id_text = id_text;
447  list_data.count = 0;
448  ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA,
449  manager_append_event_parking_lot_data_cb, s, &list_data);
450 
451  astman_send_list_complete_start(s, m, "ParkinglotsComplete", list_data.count);
453 
454  return 0;
455 }
456 
457 static void manager_park_unbridged(struct mansession *s, const struct message *m,
458  struct ast_channel *chan, const char *parkinglot, int timeout_override)
459 {
460  struct ast_bridge *parking_bridge = park_common_setup(chan,
461  chan, parkinglot, NULL, 0, 0, timeout_override, 1);
462 
463  if (!parking_bridge) {
464  astman_send_error(s, m, "Park action failed\n");
465  return;
466  }
467 
468  if (ast_bridge_add_channel(parking_bridge, chan, NULL, 0, NULL)) {
469  astman_send_error(s, m, "Park action failed\n");
470  ao2_cleanup(parking_bridge);
471  return;
472  }
473 
474  astman_send_ack(s, m, "Park successful\n");
475  ao2_cleanup(parking_bridge);
476 }
477 
478 static void manager_park_bridged(struct mansession *s, const struct message *m,
479  struct ast_channel *chan, struct ast_channel *parker_chan,
480  const char *parkinglot, int timeout_override)
481 {
482  struct ast_bridge_channel *bridge_channel;
483  char *app_data;
484 
485  if (timeout_override != -1) {
486  if (ast_asprintf(&app_data, "%s,t(%d)", parkinglot, timeout_override) == -1) {
487  astman_send_error(s, m, "Park action failed\n");
488  return;
489  }
490  } else {
491  if (ast_asprintf(&app_data, "%s", parkinglot) == -1) {
492  astman_send_error(s, m, "Park action failed\n");
493  return;
494  }
495  }
496 
497  ast_channel_lock(parker_chan);
498  bridge_channel = ast_channel_get_bridge_channel(parker_chan);
499  ast_channel_unlock(parker_chan);
500 
501  if (!bridge_channel) {
502  ast_free(app_data);
503  astman_send_error(s, m, "Park action failed\n");
504  return;
505  }
506 
507  /* Subscribe to park messages for the channel being parked */
508  if (create_parked_subscription(parker_chan, ast_channel_uniqueid(chan), 1)) {
509  ast_free(app_data);
510  astman_send_error(s, m, "Park action failed\n");
511  ao2_cleanup(bridge_channel);
512  return;
513  }
514 
515  ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(chan),
516  ast_channel_uniqueid(parker_chan), app_data);
517 
518  ast_free(app_data);
519 
520  astman_send_ack(s, m, "Park successful\n");
521  ao2_cleanup(bridge_channel);
522 }
523 
524 static int manager_park(struct mansession *s, const struct message *m)
525 {
526  const char *channel = astman_get_header(m, "Channel");
527  const char *timeout_channel = S_OR(astman_get_header(m, "TimeoutChannel"), astman_get_header(m, "Channel2"));
528  const char *announce_channel = astman_get_header(m, "AnnounceChannel");
529  const char *timeout = astman_get_header(m, "Timeout");
530  const char *parkinglot = astman_get_header(m, "Parkinglot");
531  const char *parkingspace = astman_get_header(m, "ParkingSpace");
532  char buf[BUFSIZ];
533  int timeout_override = -1;
534 
535  RAII_VAR(struct ast_channel *, parker_chan, NULL, ao2_cleanup);
536  RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
537 
538  if (ast_strlen_zero(channel)) {
539  astman_send_error(s, m, "Channel not specified");
540  return 0;
541  }
542 
543  if (!ast_strlen_zero(timeout)) {
544  if (sscanf(timeout, "%30d", &timeout_override) != 1 || timeout_override < 0) {
545  astman_send_error(s, m, "Invalid Timeout value.");
546  return 0;
547  }
548 
549  if (timeout_override) {
550  /* If greater than zero, convert to seconds for internal use. Must be >= 1 second. */
551  timeout_override = MAX(1, timeout_override / 1000);
552  }
553  }
554 
555  if (!(chan = ast_channel_get_by_name(channel))) {
556  snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
557  astman_send_error(s, m, buf);
558  return 0;
559  }
560 
561  if (!ast_strlen_zero(timeout_channel)) {
562  ast_channel_lock(chan);
563  ast_bridge_set_transfer_variables(chan, timeout_channel, 0);
564  ast_channel_unlock(chan);
565  }
566 
567  if (!ast_strlen_zero(parkingspace)) {
568  pbx_builtin_setvar_helper(chan, "PARKINGEXTEN", parkingspace);
569  }
570 
571  parker_chan = ast_channel_bridge_peer(chan);
572  if (!parker_chan || strcmp(ast_channel_name(parker_chan), timeout_channel)) {
573  if (!ast_strlen_zero(announce_channel)) {
574  struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel);
575  if (!announce_channel) {
576  astman_send_error(s, m, "AnnounceChannel does not exist");
577  return 0;
578  }
579 
580  create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0);
581  ast_channel_cleanup(announce_chan);
582  }
583 
584  manager_park_unbridged(s, m, chan, parkinglot, timeout_override);
585  return 0;
586  }
587 
588  if (!ast_strlen_zero(announce_channel) && strcmp(announce_channel, timeout_channel)) {
589  /* When using an announce_channel in bridge mode, only add the announce channel if it isn't
590  * the same as the timeout channel (which will play announcements anyway) */
591  struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel);
592  if (!announce_channel) {
593  astman_send_error(s, m, "AnnounceChannel does not exist");
594  return 0;
595  }
596 
597  create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0);
598  ast_channel_cleanup(announce_chan);
599  }
600 
601  manager_park_bridged(s, m, chan, parker_chan, parkinglot, timeout_override);
602  return 0;
603 }
604 
606 {
607  RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
608  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
609 
610  if (!ast_parked_call_type()) {
611  return;
612  }
613 
614  payload = parked_call_payload_from_failure(parkee);
615  if (!payload) {
616  return;
617  }
618 
620  if (!msg) {
621  return;
622  }
623 
625 }
626 
628 {
629  RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
630  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
631 
632  if (!ast_parked_call_type()) {
633  return;
634  }
635 
636  payload = parked_call_payload_from_parked_user(pu, event_type);
637  if (!payload) {
638  return;
639  }
640 
642  if (!msg) {
643  return;
644  }
645 
647 }
648 
649 static void parked_call_message_response(struct ast_parked_call_payload *parked_call)
650 {
651  char *event_type = "";
652  RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
653 
654  switch (parked_call->event_type) {
655  case PARKED_CALL:
656  event_type = "ParkedCall";
657  break;
658  case PARKED_CALL_TIMEOUT:
659  event_type = "ParkedCallTimeOut";
660  break;
661  case PARKED_CALL_GIVEUP:
662  event_type = "ParkedCallGiveUp";
663  break;
664  case PARKED_CALL_UNPARKED:
665  event_type = "UnParkedCall";
666  break;
667  case PARKED_CALL_SWAP:
668  event_type = "ParkedCallSwap";
669  break;
670  case PARKED_CALL_FAILED:
671  /* PARKED_CALL_FAILED doesn't currently get a message and is used exclusively for bridging */
672  return;
673  }
674 
675  parked_call_string = manager_build_parked_call_string(parked_call);
676  if (!parked_call_string) {
677  ast_log(LOG_ERROR, "Failed to issue an AMI event of '%s' in response to a stasis message.\n", event_type);
678  return;
679  }
680 
681  manager_event(EVENT_FLAG_CALL, event_type,
682  "%s",
683  ast_str_buffer(parked_call_string)
684  );
685 }
686 
687 static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
688 {
689  if (stasis_message_type(message) == ast_parked_call_type()) {
690  struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
691  parked_call_message_response(parked_call_message);
692  }
693 }
694 
695 static void parking_manager_enable_stasis(void)
696 {
697  if (!parking_sub) {
698  parking_sub = stasis_subscribe(ast_parking_topic(), parking_event_cb, NULL);
701  }
702 }
703 
705 {
706  int res;
707 
708  res = ast_manager_register_xml("Parkinglots", EVENT_FLAG_CALL, manager_parking_lot_list);
709  res |= ast_manager_register_xml("ParkedCalls", EVENT_FLAG_CALL, manager_parking_status);
710  res |= ast_manager_register_xml("Park", EVENT_FLAG_CALL, manager_park);
711  parking_manager_enable_stasis();
712  return res ? -1 : 0;
713 }
714 
715 static void parking_manager_disable_stasis(void)
716 {
717  parking_sub = stasis_unsubscribe_and_join(parking_sub);
718 }
719 
721 {
722  ast_manager_unregister("Parkinglots");
723  ast_manager_unregister("ParkedCalls");
724  ast_manager_unregister("Park");
725  parking_manager_disable_stasis();
726 }
Main Channel structure associated with a channel.
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3310
int load_parking_manager(void)
Register manager actions and setup subscriptions for stasis events.
Asterisk main include file. File version handling, generic pbx functions.
struct ast_parked_call_payload * ast_parked_call_payload_create(enum ast_parked_call_event_type event_type, struct ast_channel_snapshot *parkee_snapshot, const char *parker_dial_string, struct ast_channel_snapshot *retriever_snapshot, const char *parkinglot, unsigned int parkingspace, unsigned long int timeout, unsigned long int duration)
Constructor for parked_call_payload objects.
Definition: parking.c:82
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:3467
struct stasis_message_type * ast_parked_call_type(void)
accessor for the parked call stasis message type
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
unsigned int parkingtime
Definition: res_parking.h:69
int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
Have a bridge channel park a channel in the bridge.
Structure representing a snapshot of channel state.
unsigned int time_limit
Definition: res_parking.h:110
struct ast_str * ast_manager_build_channel_state_string_prefix(const struct ast_channel_snapshot *snapshot, const char *prefix)
Generate the AMI message body from a channel snapshot.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1077
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3421
A parked call message payload.
Definition: parking.h:59
const char * data
struct parking_lot * lot
Definition: res_parking.h:111
enum ast_parked_call_event_type event_type
Definition: parking.h:62
struct timeval start
Definition: res_parking.h:106
void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
Publish a stasis parked call message for a given parked user.
long unsigned int duration
Definition: parking.h:64
struct ast_channel_snapshot * ast_channel_snapshot_create(struct ast_channel *chan)
Generate a snapshot of the channel state. This is an ao2 object, so ao2_cleanup() to deallocate...
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
Utility functions.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3050
void publish_parked_call_failure(struct ast_channel *parkee)
Publish a stasis parked call message for the channel indicating failure to park.
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
Configuration File Parser.
struct ast_channel_snapshot * parkee
Definition: parking.h:60
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3475
char * parker_dial_string
Definition: res_parking.h:109
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
int parking_space
Definition: res_parking.h:107
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1785
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8057
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
Configuration option-handling.
Structure that contains information about a bridge.
Definition: bridge.h:349
int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_bridge_features *features, int play_tone, const char *xfersound)
Add an arbitrary channel to a bridge.
Definition: bridge.c:2471
Support for dynamic strings.
Definition: strings.h:623
static struct ast_str * manager_build_parked_call_string(const struct ast_parked_call_payload *payload)
Builds a manager string based on the contents of a parked call payload.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
unsigned int parkingspace
Definition: parking.h:65
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
long unsigned int timeout
Definition: parking.h:63
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1511
ast_parked_call_event_type
Defines the type of parked call message being published.
Definition: parking.h:46
void unload_parking_manager(void)
Unregister manager actions and remove subscriptions for stasis events.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1134
void ast_bridge_set_transfer_variables(struct ast_channel *chan, const char *value, int is_attended)
Set the relevant transfer variables for a single channel.
Definition: bridge.c:4352
const ast_string_field name
Definition: res_parking.h:100
const ast_string_field parkinglot
Definition: parking.h:69
static struct stasis_subscription * parking_sub
subscription to the parking lot topic
struct ast_bridge_channel * ast_channel_get_bridge_channel(struct ast_channel *chan)
Get a reference to the channel's bridge pointer.
Definition: channel.c:10582
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
struct stasis_topic * ast_parking_topic(void)
accessor for the parking stasis topic
Definition: parking.c:67
Structure that contains information regarding a channel in a bridge.
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.
#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
struct ast_channel * ast_channel_bridge_peer(struct ast_channel *chan)
Get the channel's bridge peer only if the bridge is two-party.
Definition: channel.c:10564
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition: stasis.c:1023
const ast_string_field parker_dial_string
Definition: parking.h:69
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
Call Parking Resource Internal API.
Generic container type.
Call Parking and Pickup API Includes code and algorithms from the Zapata library. ...
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:253
struct ast_channel_snapshot * retriever
Definition: parking.h:61
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
Bridging API.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3389
Asterisk module definitions.
struct ast_channel_snapshot * retriever
Definition: res_parking.h:105
#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
struct ast_channel * chan
Definition: res_parking.h:104
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct ast_bridge * park_common_setup(struct ast_channel *parkee, struct ast_channel *parker, const char *lot_name, const char *comeback_override, int use_ringing, int randomize, int time_limit, int silence_announcements)
Setup a parked call on a parking bridge without needing to parse appdata.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid, int hangup_after)
Create a parking announcement subscription.
struct parking_lot_cfg * cfg
Definition: res_parking.h:96
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:3431