channel locking: Add locking for channel snapshot creation
[asterisk/asterisk.git] / res / parking / parking_manager.c
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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "res_parking.h"
31 #include "asterisk/config.h"
32 #include "asterisk/config_options.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/module.h"
35 #include "asterisk/cli.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/features.h"
38 #include "asterisk/manager.h"
39 #include "asterisk/bridge.h"
40
41 /*** DOCUMENTATION
42         <manager name="Parkinglots" language="en_US">
43                 <synopsis>
44                         Get a list of parking lots
45                 </synopsis>
46                 <syntax>
47                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
48                 </syntax>
49                 <description>
50                         <para>List all parking lots as a series of AMI events</para>
51                 </description>
52         </manager>
53         <manager name="ParkedCalls" language="en_US">
54                 <synopsis>
55                         List parked calls.
56                 </synopsis>
57                 <syntax>
58                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
59                         <parameter name="ParkingLot">
60                                 <para>If specified, only show parked calls from the parking lot with this name.</para>
61                         </parameter>
62                 </syntax>
63                 <description>
64                         <para>List parked calls.</para>
65                 </description>
66         </manager>
67         <manager name="Park" language="en_US">
68                 <synopsis>
69                         Park a channel.
70                 </synopsis>
71                 <syntax>
72                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
73                         <parameter name="Channel" required="true">
74                                 <para>Channel name to park.</para>
75                         </parameter>
76                         <parameter name="TimeoutChannel" required="false">
77                                 <para>Channel name to use when constructing the dial string that will be dialed if the parked channel times out.</para>
78                         </parameter>
79                         <parameter name="Timeout" required="false">
80                                 <para>Overrides the timeout of the parking lot for this park action. Specified in milliseconds, but will be converted to
81                                         seconds. Use a value of 0 to nullify the timeout.
82                                 </para>
83                         </parameter>
84                         <parameter name="Parkinglot" required="false">
85                                 <para>The parking lot to use when parking the channel</para>
86                         </parameter>
87                 </syntax>
88                 <description>
89                         <para>Park an arbitrary channel with optional arguments for specifying the parking lot used, how long
90                                 the channel should remain parked, and what dial string to use as the parker if the call times out.
91                         </para>
92                 </description>
93         </manager>
94         <managerEvent language="en_US" name="ParkedCall">
95                 <managerEventInstance class="EVENT_FLAG_CALL">
96                         <synopsis>Raised when a channel is parked.</synopsis>
97                         <syntax>
98                                 <channel_snapshot prefix="Parkee"/>
99                                 <parameter name="ParkerDialString">
100                                         <para>Dial String that can be used to call back the parker on ParkingTimeout.</para>
101                                 </parameter>
102                                 <parameter name="Parkinglot">
103                                         <para>Name of the parking lot that the parkee is parked in</para>
104                                 </parameter>
105                                 <parameter name="ParkingSpace">
106                                         <para>Parking Space that the parkee is parked in</para>
107                                 </parameter>
108                                 <parameter name="ParkingTimeout">
109                                 <para>Time remaining until the parkee is forcefully removed from parking in seconds</para>
110                                 </parameter>
111                                 <parameter name="ParkingDuration">
112                                         <para>Time the parkee has been in the parking bridge (in seconds)</para>
113                                 </parameter>
114                         </syntax>
115                 </managerEventInstance>
116         </managerEvent>
117         <managerEvent language="en_US" name="ParkedCallTimeOut">
118                 <managerEventInstance class="EVENT_FLAG_CALL">
119                         <synopsis>Raised when a channel leaves a parking lot due to reaching the time limit of being parked.</synopsis>
120                         <syntax>
121                                 <channel_snapshot prefix="Parkee"/>
122                                 <channel_snapshot prefix="Parker"/>
123                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
124                         </syntax>
125                 </managerEventInstance>
126         </managerEvent>
127         <managerEvent language="en_US" name="ParkedCallGiveUp">
128                 <managerEventInstance class="EVENT_FLAG_CALL">
129                         <synopsis>Raised when a channel leaves a parking lot because it hung up without being answered.</synopsis>
130                         <syntax>
131                                 <channel_snapshot prefix="Parkee"/>
132                                 <channel_snapshot prefix="Parker"/>
133                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
134                         </syntax>
135                 </managerEventInstance>
136         </managerEvent>
137         <managerEvent language="en_US" name="UnParkedCall">
138                 <managerEventInstance class="EVENT_FLAG_CALL">
139                         <synopsis>Raised when a channel leaves a parking lot because it was retrieved from the parking lot and reconnected.</synopsis>
140                         <syntax>
141                                 <channel_snapshot prefix="Parkee"/>
142                                 <channel_snapshot prefix="Parker"/>
143                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
144                                 <channel_snapshot prefix="Retriever"/>
145                         </syntax>
146                 </managerEventInstance>
147         </managerEvent>
148  ***/
149
150 /*! \brief subscription to the parking lot topic */
151 static struct stasis_subscription *parking_sub;
152
153 static struct ast_parked_call_payload *parked_call_payload_from_failure(struct ast_channel *chan)
154 {
155         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
156         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
157
158         ast_channel_lock(chan);
159         parkee_snapshot = ast_channel_snapshot_create(chan);
160         ast_channel_unlock(chan);
161         if (!parkee_snapshot) {
162                 return NULL;
163         }
164
165         return ast_parked_call_payload_create(PARKED_CALL_FAILED, parkee_snapshot, NULL, NULL, NULL, 0, 0, 0);
166 }
167
168 static struct ast_parked_call_payload *parked_call_payload_from_parked_user(struct parked_user *pu, enum ast_parked_call_event_type event_type)
169 {
170         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
171         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
172         long int timeout;
173         long int duration;
174         struct timeval now = ast_tvnow();
175         const char *lot_name = pu->lot->name;
176
177         ast_channel_lock(pu->chan);
178         parkee_snapshot = ast_channel_snapshot_create(pu->chan);
179         ast_channel_unlock(pu->chan);
180
181         if (!parkee_snapshot) {
182                 return NULL;
183         }
184
185         timeout = pu->start.tv_sec + (long) pu->time_limit - now.tv_sec;
186         duration = now.tv_sec - pu->start.tv_sec;
187
188         return ast_parked_call_payload_create(event_type, parkee_snapshot, pu->parker_dial_string, pu->retriever, lot_name, pu->parking_space, timeout, duration);
189
190 }
191
192 /*! \brief Builds a manager string based on the contents of a parked call payload */
193 static struct ast_str *manager_build_parked_call_string(const struct ast_parked_call_payload *payload)
194 {
195         struct ast_str *out = ast_str_create(1024);
196         RAII_VAR(struct ast_str *, parkee_string, NULL, ast_free);
197         RAII_VAR(struct ast_str *, retriever_string, NULL, ast_free);
198
199         if (!out) {
200                 return NULL;
201         }
202
203         parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
204         if (!parkee_string) {
205                 return NULL;
206         }
207
208         if (payload->retriever) {
209                 retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
210         }
211
212         ast_str_set(&out, 0,
213                 "%s" /* parkee channel state */
214                 "%s" /* retriever channel state (when available) */
215                 "ParkerDialString: %s\r\n"
216                 "Parkinglot: %s\r\n"
217                 "ParkingSpace: %u\r\n"
218                 "ParkingTimeout: %lu\r\n"
219                 "ParkingDuration: %lu\r\n",
220
221                 ast_str_buffer(parkee_string),
222                 retriever_string ? ast_str_buffer(retriever_string) : "",
223                 payload->parker_dial_string,
224                 payload->parkinglot,
225                 payload->parkingspace,
226                 payload->timeout,
227                 payload->duration);
228
229         return out;
230 }
231
232 static int manager_parking_status_single_lot(struct mansession *s, const struct message *m, const char *id_text, const char *lot_name)
233 {
234         RAII_VAR(struct parking_lot *, curlot, NULL, ao2_cleanup);
235         struct parked_user *curuser;
236         struct ao2_iterator iter_users;
237         int total = 0;
238
239         curlot = parking_lot_find_by_name(lot_name);
240
241         if (!curlot) {
242                 astman_send_error(s, m, "Requested parking lot could not be found.");
243                 return RESULT_SUCCESS;
244         }
245
246         astman_send_ack(s, m, "Parked calls will follow");
247
248         iter_users = ao2_iterator_init(curlot->parked_users, 0);
249         while ((curuser = ao2_iterator_next(&iter_users))) {
250                 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
251                 RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
252
253                 payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
254                 if (!payload) {
255                         astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
256                         return RESULT_FAILURE;
257                 }
258
259                 parked_call_string = manager_build_parked_call_string(payload);
260                 if (!parked_call_string) {
261                         astman_send_error(s, m, "Failed to retrieve parkingd ata about a parked user.");
262                         return RESULT_FAILURE;
263                 }
264
265                 total++;
266
267                 astman_append(s, "Event: ParkedCall\r\n"
268                         "%s" /* The parked call string */
269                         "%s" /* The action ID */
270                         "\r\n",
271                         ast_str_buffer(parked_call_string),
272                         id_text);
273
274                 ao2_ref(curuser, -1);
275         }
276
277         ao2_iterator_destroy(&iter_users);
278
279         astman_append(s,
280                 "Event: ParkedCallsComplete\r\n"
281                 "Total: %d\r\n"
282                 "%s"
283                 "\r\n",
284                 total, id_text);
285
286         return RESULT_SUCCESS;
287 }
288
289 static int manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
290 {
291         struct parked_user *curuser;
292         struct ao2_container *lot_container;
293         struct ao2_iterator iter_lots;
294         struct ao2_iterator iter_users;
295         struct parking_lot *curlot;
296         int total = 0;
297
298         lot_container = get_parking_lot_container();
299
300         if (!lot_container) {
301                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
302                 astman_send_error(s, m, "Could not create parking lot list");
303                 return RESULT_SUCCESS;
304         }
305
306         iter_lots = ao2_iterator_init(lot_container, 0);
307
308         astman_send_ack(s, m, "Parked calls will follow");
309
310         while ((curlot = ao2_iterator_next(&iter_lots))) {
311                 iter_users = ao2_iterator_init(curlot->parked_users, 0);
312                 while ((curuser = ao2_iterator_next(&iter_users))) {
313                         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
314                         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
315
316                         payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
317                         if (!payload) {
318                                 return RESULT_FAILURE;
319                         }
320
321                         parked_call_string = manager_build_parked_call_string(payload);
322                         if (!payload) {
323                                 return RESULT_FAILURE;
324                         }
325
326                         total++;
327
328                         astman_append(s, "Event: ParkedCall\r\n"
329                                 "%s" /* The parked call string */
330                                 "%s" /* The action ID */
331                                 "\r\n",
332                                 ast_str_buffer(parked_call_string),
333                                 id_text);
334
335                         ao2_ref(curuser, -1);
336                 }
337                 ao2_iterator_destroy(&iter_users);
338                 ao2_ref(curlot, -1);
339         }
340
341         ao2_iterator_destroy(&iter_lots);
342
343         astman_append(s,
344                 "Event: ParkedCallsComplete\r\n"
345                 "Total: %d\r\n"
346                 "%s"
347                 "\r\n",
348                 total, id_text);
349
350         return RESULT_SUCCESS;
351 }
352
353 static int manager_parking_status(struct mansession *s, const struct message *m)
354 {
355         const char *id = astman_get_header(m, "ActionID");
356         const char *lot_name = astman_get_header(m, "ParkingLot");
357         char id_text[256] = "";
358
359         if (!ast_strlen_zero(id)) {
360                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
361         }
362
363         if (!ast_strlen_zero(lot_name)) {
364                 return manager_parking_status_single_lot(s, m, id_text, lot_name);
365         }
366
367         return manager_parking_status_all_lots(s, m, id_text);
368
369 }
370
371 static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *data, int flags)
372 {
373         struct parking_lot *curlot = obj;
374         struct mansession *s = arg;
375         char *id_text = data;
376
377         astman_append(s, "Event: Parkinglot\r\n"
378                 "Name: %s\r\n"
379                 "StartSpace: %d\r\n"
380                 "StopSpace: %d\r\n"
381                 "Timeout: %d\r\n"
382                 "%s" /* The Action ID */
383                 "\r\n",
384                 curlot->name,
385                 curlot->cfg->parking_start,
386                 curlot->cfg->parking_stop,
387                 curlot->cfg->parkingtime,
388                 id_text);
389
390         return 0;
391 }
392
393 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
394 {
395         const char *id = astman_get_header(m, "ActionID");
396         char id_text[256] = "";
397         struct ao2_container *lot_container;
398
399         if (!ast_strlen_zero(id)) {
400                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
401         }
402
403         lot_container = get_parking_lot_container();
404
405         if (!lot_container) {
406                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
407                 astman_send_error(s, m, "Could not create parking lot list");
408                 return -1;
409         }
410
411         astman_send_ack(s, m, "Parking lots will follow");
412
413         ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA, manager_append_event_parking_lot_data_cb, s, id_text);
414
415         astman_append(s,
416                 "Event: ParkinglotsComplete\r\n"
417                 "%s"
418                 "\r\n",id_text);
419
420         return RESULT_SUCCESS;
421 }
422
423 static int manager_park(struct mansession *s, const struct message *m)
424 {
425         const char *channel = astman_get_header(m, "Channel");
426         const char *timeout_channel = S_OR(astman_get_header(m, "TimeoutChannel"), astman_get_header(m, "Channel2"));
427         const char *timeout = astman_get_header(m, "Timeout");
428         const char *parkinglot = astman_get_header(m, "Parkinglot");
429         char buf[BUFSIZ];
430         int timeout_override = -1;
431
432         RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
433         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
434
435         if (ast_strlen_zero(channel)) {
436                 astman_send_error(s, m, "Channel not specified");
437                 return 0;
438         }
439
440         if (!ast_strlen_zero(timeout)) {
441                 if (sscanf(timeout, "%30d", &timeout_override) != 1 || timeout < 0) {
442                         astman_send_error(s, m, "Invalid Timeout value.");
443                         return 0;
444                 }
445
446                 if (timeout_override > 0) {
447                         /* If greater than zero, convert to seconds for internal use. Must be >= 1 second. */
448                         timeout_override = MAX(1, timeout_override / 1000);
449                 }
450         }
451
452         if (!(chan = ast_channel_get_by_name(channel))) {
453                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
454                 astman_send_error(s, m, buf);
455                 return 0;
456         }
457
458         ast_channel_lock(chan);
459         if (!ast_strlen_zero(timeout_channel)) {
460                 ast_bridge_set_transfer_variables(chan, timeout_channel, 0);
461         }
462         ast_channel_unlock(chan);
463
464         if (!(parking_bridge = park_common_setup(chan, chan, parkinglot, NULL, 0, 0, timeout_override, 0))) {
465                 astman_send_error(s, m, "Park action failed\n");
466                 return 0;
467         }
468
469         if (ast_bridge_add_channel(parking_bridge, chan, NULL, 0, NULL)) {
470                 astman_send_error(s, m, "Park action failed\n");
471                 return 0;
472         }
473
474         astman_send_ack(s, m, "Park successful\n");
475         return 0;
476 }
477
478 void publish_parked_call_failure(struct ast_channel *parkee)
479 {
480         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
481         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
482
483         payload = parked_call_payload_from_failure(parkee);
484         if (!payload) {
485                 return;
486         }
487
488         msg = stasis_message_create(ast_parked_call_type(), payload);
489         if (!msg) {
490                 return;
491         }
492
493         stasis_publish(ast_parking_topic(), msg);
494 }
495
496 void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
497 {
498         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
499         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
500
501         payload = parked_call_payload_from_parked_user(pu, event_type);
502         if (!payload) {
503                 return;
504         }
505
506         msg = stasis_message_create(ast_parked_call_type(), payload);
507         if (!msg) {
508                 return;
509         }
510
511         stasis_publish(ast_parking_topic(), msg);
512 }
513
514 static void parked_call_message_response(struct ast_parked_call_payload *parked_call)
515 {
516         char *event_type = "";
517         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
518
519         switch (parked_call->event_type) {
520         case PARKED_CALL:
521                 event_type = "ParkedCall";
522                 break;
523         case PARKED_CALL_TIMEOUT:
524                 event_type = "ParkedCallTimeOut";
525                 break;
526         case PARKED_CALL_GIVEUP:
527                 event_type = "ParkedCallGiveUp";
528                 break;
529         case PARKED_CALL_UNPARKED:
530                 event_type = "UnParkedCall";
531                 break;
532         case PARKED_CALL_SWAP:
533                 event_type = "ParkedCallSwap";
534                 break;
535         case PARKED_CALL_FAILED:
536                 /* PARKED_CALL_FAILED doesn't currently get a message and is used exclusively for bridging */
537                 return;
538         }
539
540         parked_call_string = manager_build_parked_call_string(parked_call);
541         if (!parked_call_string) {
542                 ast_log(LOG_ERROR, "Failed to issue an AMI event of '%s' in response to a stasis message.\n", event_type);
543                 return;
544         }
545
546         manager_event(EVENT_FLAG_CALL, event_type,
547                         "%s",
548                         ast_str_buffer(parked_call_string)
549                 );
550 }
551
552 static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
553 {
554         if (stasis_message_type(message) == ast_parked_call_type()) {
555                 struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
556                 parked_call_message_response(parked_call_message);
557         }
558 }
559
560 static void parking_manager_enable_stasis(void)
561 {
562         if (!parking_sub) {
563                 parking_sub = stasis_subscribe(ast_parking_topic(), parking_event_cb, NULL);
564         }
565 }
566
567 int load_parking_manager(void)
568 {
569         int res;
570
571         res = ast_manager_register_xml_core("Parkinglots", EVENT_FLAG_CALL, manager_parking_lot_list);
572         res |= ast_manager_register_xml_core("ParkedCalls", EVENT_FLAG_CALL, manager_parking_status);
573         res |= ast_manager_register_xml_core("Park", EVENT_FLAG_CALL, manager_park);
574         parking_manager_enable_stasis();
575         return res ? -1 : 0;
576 }
577
578 static void parking_manager_disable_stasis(void)
579 {
580         parking_sub = stasis_unsubscribe(parking_sub);
581 }
582
583 void unload_parking_manager(void)
584 {
585         ast_manager_unregister("Parkinglots");
586         ast_manager_unregister("ParkedCalls");
587         ast_manager_unregister("Park");
588         parking_manager_disable_stasis();
589 }