git migration: Refactor the ASTERISK_FILE_VERSION macro
[asterisk/asterisk.git] / main / cel.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007 - 2009, Digium, Inc.
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16
17 /*!
18  * \file
19  *
20  * \brief Channel Event Logging API
21  *
22  * \author Steve Murphy <murf@digium.com>
23  * \author Russell Bryant <russell@digium.com>
24  */
25
26 /*! \li \ref cel.c uses the configuration file \ref cel.conf
27  * \addtogroup configuration_file Configuration Files
28  */
29
30 /*!
31  * \page cel.conf cel.conf
32  * \verbinclude cel.conf.sample
33  */
34
35 /*** MODULEINFO
36         <support_level>core</support_level>
37  ***/
38
39 #include "asterisk.h"
40
41 ASTERISK_REGISTER_FILE()
42
43 #include "asterisk/_private.h"
44
45 #include "asterisk/channel.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/cel.h"
48 #include "asterisk/logger.h"
49 #include "asterisk/linkedlists.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/config.h"
52 #include "asterisk/config_options.h"
53 #include "asterisk/cli.h"
54 #include "asterisk/astobj2.h"
55 #include "asterisk/stasis_message_router.h"
56 #include "asterisk/stasis_channels.h"
57 #include "asterisk/stasis_bridges.h"
58 #include "asterisk/bridge.h"
59 #include "asterisk/parking.h"
60 #include "asterisk/pickup.h"
61 #include "asterisk/core_local.h"
62
63 /*** DOCUMENTATION
64         <configInfo name="cel" language="en_US">
65                 <configFile name="cel.conf">
66                         <configObject name="general">
67                                 <synopsis>Options that apply globally to Channel Event Logging (CEL)</synopsis>
68                                 <configOption name="enable">
69                                         <synopsis>Determines whether CEL is enabled</synopsis>
70                                 </configOption>
71                                 <configOption name="dateformat">
72                                         <synopsis>The format to be used for dates when logging</synopsis>
73                                 </configOption>
74                                 <configOption name="apps">
75                                         <synopsis>List of apps for CEL to track</synopsis>
76                                         <description><para>A case-insensitive, comma-separated list of applications
77                                         to track when one or both of APP_START and APP_END events are flagged for
78                                         tracking</para></description>
79                                 </configOption>
80                                 <configOption name="events">
81                                         <synopsis>List of events for CEL to track</synopsis>
82                                         <description><para>A case-sensitive, comma-separated list of event names
83                                         to track. These event names do not include the leading <literal>AST_CEL</literal>.
84                                         </para>
85                                         <enumlist>
86                                                 <enum name="ALL">
87                                                         <para>Special value which tracks all events.</para>
88                                                 </enum>
89                                                 <enum name="CHAN_START"/>
90                                                 <enum name="CHAN_END"/>
91                                                 <enum name="ANSWER"/>
92                                                 <enum name="HANGUP"/>
93                                                 <enum name="APP_START"/>
94                                                 <enum name="APP_END"/>
95                                                 <enum name="PARK_START"/>
96                                                 <enum name="PARK_END"/>
97                                                 <enum name="USER_DEFINED"/>
98                                                 <enum name="BRIDGE_ENTER"/>
99                                                 <enum name="BRIDGE_EXIT"/>
100                                                 <enum name="BLINDTRANSFER"/>
101                                                 <enum name="ATTENDEDTRANSFER"/>
102                                                 <enum name="PICKUP"/>
103                                                 <enum name="FORWARD"/>
104                                                 <enum name="LINKEDID_END"/>
105                                                 <enum name="LOCAL_OPTIMIZE"/>
106                                         </enumlist>
107                                         </description>
108                                 </configOption>
109                         </configObject>
110                 </configFile>
111         </configInfo>
112  ***/
113
114 /*! Message router for state that CEL needs to know about */
115 static struct stasis_message_router *cel_state_router;
116
117 /*! Topic for CEL-specific messages */
118 static struct stasis_topic *cel_topic;
119
120 /*! Aggregation topic for all topics CEL needs to know about */
121 static struct stasis_topic *cel_aggregation_topic;
122
123 /*! Subscription for forwarding the channel caching topic */
124 static struct stasis_forward *cel_channel_forwarder;
125
126 /*! Subscription for forwarding the channel caching topic */
127 static struct stasis_forward *cel_bridge_forwarder;
128
129 /*! Subscription for forwarding the parking topic */
130 static struct stasis_forward *cel_parking_forwarder;
131
132 /*! Subscription for forwarding the CEL-specific topic */
133 static struct stasis_forward *cel_cel_forwarder;
134
135 struct stasis_message_type *cel_generic_type(void);
136 STASIS_MESSAGE_TYPE_DEFN(cel_generic_type);
137
138 /*! Container for CEL backend information */
139 static AO2_GLOBAL_OBJ_STATIC(cel_backends);
140
141 /*! The number of buckets into which backend names will be hashed */
142 #define BACKEND_BUCKETS 13
143
144 /*! Container for dial end multichannel blobs for holding on to dial statuses */
145 static AO2_GLOBAL_OBJ_STATIC(cel_dialstatus_store);
146
147 /*!
148  * \brief Maximum possible CEL event IDs
149  * \note This limit is currently imposed by the eventset definition
150  */
151 #define CEL_MAX_EVENT_IDS 64
152
153 /*!
154  * \brief Number of buckets for the appset container
155  */
156 #define NUM_APP_BUCKETS         97
157
158 /*!
159  * \brief Number of buckets for the dialstatus container
160  */
161 #define NUM_DIALSTATUS_BUCKETS  251
162
163 struct cel_linkedid {
164         /*! Number of channels with this linkedid. */
165         unsigned int count;
166         /*! Linkedid stored at end of struct. */
167         char id[0];
168 };
169
170 /*! Container of channel references to a linkedid for CEL purposes. */
171 static AO2_GLOBAL_OBJ_STATIC(cel_linkedids);
172
173 /*! \brief Destructor for cel_config */
174 static void cel_general_config_dtor(void *obj)
175 {
176         struct ast_cel_general_config *cfg = obj;
177         ast_string_field_free_memory(cfg);
178         ao2_cleanup(cfg->apps);
179         cfg->apps = NULL;
180 }
181
182 void *ast_cel_general_config_alloc(void)
183 {
184         RAII_VAR(struct ast_cel_general_config *, cfg, NULL, ao2_cleanup);
185
186         if (!(cfg = ao2_alloc(sizeof(*cfg), cel_general_config_dtor))) {
187                 return NULL;
188         }
189
190         if (ast_string_field_init(cfg, 64)) {
191                 return NULL;
192         }
193
194         if (!(cfg->apps = ast_str_container_alloc(NUM_APP_BUCKETS))) {
195                 return NULL;
196         }
197
198         ao2_ref(cfg, +1);
199         return cfg;
200 }
201
202 /*! \brief A container that holds all config-related information */
203 struct cel_config {
204         struct ast_cel_general_config *general;
205 };
206
207
208 static AO2_GLOBAL_OBJ_STATIC(cel_configs);
209
210 /*! \brief Destructor for cel_config */
211 static void cel_config_dtor(void *obj)
212 {
213         struct cel_config *cfg = obj;
214         ao2_cleanup(cfg->general);
215         cfg->general = NULL;
216 }
217
218 static void *cel_config_alloc(void)
219 {
220         RAII_VAR(struct cel_config *, cfg, NULL, ao2_cleanup);
221
222         if (!(cfg = ao2_alloc(sizeof(*cfg), cel_config_dtor))) {
223                 return NULL;
224         }
225
226         if (!(cfg->general = ast_cel_general_config_alloc())) {
227                 return NULL;
228         }
229
230         ao2_ref(cfg, +1);
231         return cfg;
232 }
233
234 /*! \brief An aco_type structure to link the "general" category to the ast_cel_general_config type */
235 static struct aco_type general_option = {
236         .type = ACO_GLOBAL,
237         .name = "general",
238         .item_offset = offsetof(struct cel_config, general),
239         .category_match = ACO_WHITELIST,
240         .category = "^general$",
241 };
242
243 /*! \brief The config file to be processed for the module. */
244 static struct aco_file cel_conf = {
245         .filename = "cel.conf",                  /*!< The name of the config file */
246         .types = ACO_TYPES(&general_option),     /*!< The mapping object types to be processed */
247         .skip_category = "(^manager$|^radius$)", /*!< Config sections used by existing modules. Do not add to this list. */
248 };
249
250 static int cel_pre_apply_config(void);
251
252 CONFIG_INFO_CORE("cel", cel_cfg_info, cel_configs, cel_config_alloc,
253         .files = ACO_FILES(&cel_conf),
254         .pre_apply_config = cel_pre_apply_config,
255 );
256
257 static int cel_pre_apply_config(void)
258 {
259         struct cel_config *cfg = aco_pending_config(&cel_cfg_info);
260
261         if (!cfg->general) {
262                 return -1;
263         }
264
265         if (!ao2_container_count(cfg->general->apps)) {
266                 return 0;
267         }
268
269         if (cfg->general->events & ((int64_t) 1 << AST_CEL_APP_START)) {
270                 return 0;
271         }
272
273         if (cfg->general->events & ((int64_t) 1 << AST_CEL_APP_END)) {
274                 return 0;
275         }
276
277         ast_log(LOG_ERROR, "Applications are listed to be tracked, but APP events are not tracked\n");
278         return -1;
279 }
280
281 static struct aco_type *general_options[] = ACO_TYPES(&general_option);
282
283 /*!
284  * \brief Map of ast_cel_event_type to strings
285  */
286 static const char * const cel_event_types[CEL_MAX_EVENT_IDS] = {
287         [AST_CEL_ALL]              = "ALL",
288         [AST_CEL_CHANNEL_START]    = "CHAN_START",
289         [AST_CEL_CHANNEL_END]      = "CHAN_END",
290         [AST_CEL_ANSWER]           = "ANSWER",
291         [AST_CEL_HANGUP]           = "HANGUP",
292         [AST_CEL_APP_START]        = "APP_START",
293         [AST_CEL_APP_END]          = "APP_END",
294         [AST_CEL_PARK_START]       = "PARK_START",
295         [AST_CEL_PARK_END]         = "PARK_END",
296         [AST_CEL_USER_DEFINED]     = "USER_DEFINED",
297         [AST_CEL_BRIDGE_ENTER]     = "BRIDGE_ENTER",
298         [AST_CEL_BRIDGE_EXIT]      = "BRIDGE_EXIT",
299         [AST_CEL_BLINDTRANSFER]    = "BLINDTRANSFER",
300         [AST_CEL_ATTENDEDTRANSFER] = "ATTENDEDTRANSFER",
301         [AST_CEL_PICKUP]           = "PICKUP",
302         [AST_CEL_FORWARD]          = "FORWARD",
303         [AST_CEL_LINKEDID_END]     = "LINKEDID_END",
304         [AST_CEL_LOCAL_OPTIMIZE]   = "LOCAL_OPTIMIZE",
305 };
306
307 struct cel_backend {
308         ast_cel_backend_cb callback; /*!< Callback for this backend */
309         char name[0];                /*!< Name of this backend */
310 };
311
312 /*! \brief Hashing function for cel_backend */
313 static int cel_backend_hash(const void *obj, int flags)
314 {
315         const struct cel_backend *backend;
316         const char *name;
317
318         switch (flags & OBJ_SEARCH_MASK) {
319         case OBJ_SEARCH_OBJECT:
320                 backend = obj;
321                 name = backend->name;
322                 break;
323         case OBJ_SEARCH_KEY:
324                 name = obj;
325                 break;
326         default:
327                 /* Hash can only work on something with a full key. */
328                 ast_assert(0);
329                 return 0;
330         }
331
332         return ast_str_hash(name);
333 }
334
335 /*! \brief Comparator function for cel_backend */
336 static int cel_backend_cmp(void *obj, void *arg, int flags)
337 {
338         const struct cel_backend *object_left = obj;
339         const struct cel_backend *object_right = arg;
340         const char *right_key = arg;
341         int cmp;
342
343         switch (flags & OBJ_SEARCH_MASK) {
344         case OBJ_SEARCH_OBJECT:
345                 right_key = object_right->name;
346                 /* Fall through */
347         case OBJ_SEARCH_KEY:
348                 cmp = strcmp(object_left->name, right_key);
349                 break;
350         case OBJ_SEARCH_PARTIAL_KEY:
351                 /*
352                  * We could also use a partial key struct containing a length
353                  * so strlen() does not get called for every comparison instead.
354                  */
355                 cmp = strncmp(object_left->name, right_key, strlen(right_key));
356                 break;
357         default:
358                 /*
359                  * What arg points to is specific to this traversal callback
360                  * and has no special meaning to astobj2.
361                  */
362                 cmp = 0;
363                 break;
364         }
365         if (cmp) {
366                 return 0;
367         }
368         /*
369          * At this point the traversal callback is identical to a sorted
370          * container.
371          */
372         return CMP_MATCH;
373 }
374
375 static const char *get_caller_uniqueid(struct ast_multi_channel_blob *blob)
376 {
377         struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
378         if (!caller) {
379                 return NULL;
380         }
381
382         return caller->uniqueid;
383 }
384
385 /*! \brief Hashing function for dialstatus container */
386 static int dialstatus_hash(const void *obj, int flags)
387 {
388         struct ast_multi_channel_blob *blob;
389         const char *key;
390
391         switch (flags & OBJ_SEARCH_MASK) {
392         case OBJ_SEARCH_KEY:
393                 key = obj;
394                 break;
395         case OBJ_SEARCH_OBJECT:
396                 blob = (void *) obj;
397                 key = get_caller_uniqueid(blob);
398                 break;
399         default:
400                 /* Hash can only work on something with a full key. */
401                 ast_assert(0);
402                 return 0;
403         }
404         return ast_str_hash(key);
405 }
406
407 /*! \brief Comparator function for dialstatus container */
408 static int dialstatus_cmp(void *obj, void *arg, int flags)
409 {
410         struct ast_multi_channel_blob *object_left = obj;
411         struct ast_multi_channel_blob *object_right = arg;
412         const char *right_key = arg;
413         int cmp;
414
415         switch (flags & OBJ_SEARCH_MASK) {
416         case OBJ_SEARCH_OBJECT:
417                 right_key = get_caller_uniqueid(object_right);
418                 /* Fall through */
419         case OBJ_SEARCH_KEY:
420                 cmp = strcmp(get_caller_uniqueid(object_left), right_key);
421                 break;
422         case OBJ_SEARCH_PARTIAL_KEY:
423                 /*
424                  * We could also use a partial key struct containing a length
425                  * so strlen() does not get called for every comparison instead.
426                  */
427                 cmp = strncmp(get_caller_uniqueid(object_left), right_key, strlen(right_key));
428                 break;
429         default:
430                 /*
431                  * What arg points to is specific to this traversal callback
432                  * and has no special meaning to astobj2.
433                  */
434                 cmp = 0;
435                 break;
436         }
437         if (cmp) {
438                 return 0;
439         }
440         /*
441          * At this point the traversal callback is identical to a sorted
442          * container.
443          */
444         return CMP_MATCH;
445 }
446
447 unsigned int ast_cel_check_enabled(void)
448 {
449         unsigned int enabled;
450         struct cel_config *cfg = ao2_global_obj_ref(cel_configs);
451
452         enabled = (!cfg || !cfg->general) ? 0 : cfg->general->enable;
453         ao2_cleanup(cfg);
454         return enabled;
455 }
456
457 static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
458 {
459         unsigned int i;
460         RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
461         RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
462         struct ao2_iterator iter;
463         char *app;
464
465         switch (cmd) {
466         case CLI_INIT:
467                 e->command = "cel show status";
468                 e->usage =
469                         "Usage: cel show status\n"
470                         "       Displays the Channel Event Logging system status.\n";
471                 return NULL;
472         case CLI_GENERATE:
473                 return NULL;
474         case CLI_HANDLER:
475                 break;
476         }
477
478         if (a->argc > 3) {
479                 return CLI_SHOWUSAGE;
480         }
481
482         ast_cli(a->fd, "CEL Logging: %s\n", ast_cel_check_enabled() ? "Enabled" : "Disabled");
483
484         if (!cfg || !cfg->general || !cfg->general->enable) {
485                 return CLI_SUCCESS;
486         }
487
488         for (i = 0; i < (sizeof(cfg->general->events) * 8); i++) {
489                 const char *name;
490
491                 if (!(cfg->general->events & ((int64_t) 1 << i))) {
492                         continue;
493                 }
494
495                 name = ast_cel_get_type_name(i);
496                 if (strcasecmp(name, "Unknown")) {
497                         ast_cli(a->fd, "CEL Tracking Event: %s\n", name);
498                 }
499         }
500
501         iter = ao2_iterator_init(cfg->general->apps, 0);
502         for (; (app = ao2_iterator_next(&iter)); ao2_ref(app, -1)) {
503                 ast_cli(a->fd, "CEL Tracking Application: %s\n", app);
504         }
505         ao2_iterator_destroy(&iter);
506
507         if (backends) {
508                 struct cel_backend *backend;
509
510                 iter = ao2_iterator_init(backends, 0);
511                 for (; (backend = ao2_iterator_next(&iter)); ao2_ref(backend, -1)) {
512                         ast_cli(a->fd, "CEL Event Subscriber: %s\n", backend->name);
513                 }
514                 ao2_iterator_destroy(&iter);
515         }
516
517         return CLI_SUCCESS;
518 }
519
520 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CEL status");
521
522 enum ast_cel_event_type ast_cel_str_to_event_type(const char *name)
523 {
524         unsigned int i;
525
526         for (i = 0; i < ARRAY_LEN(cel_event_types); i++) {
527                 if (cel_event_types[i] && !strcasecmp(name, cel_event_types[i])) {
528                         return i;
529                 }
530         }
531
532         ast_log(LOG_ERROR, "Unknown event name '%s'\n", name);
533         return AST_CEL_INVALID_VALUE;
534 }
535
536 static int ast_cel_track_event(enum ast_cel_event_type et)
537 {
538         RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
539
540         if (!cfg || !cfg->general) {
541                 return 0;
542         }
543
544         return (cfg->general->events & ((int64_t) 1 << et));
545 }
546
547 static int events_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
548 {
549         struct ast_cel_general_config *cfg = obj;
550         char *events = ast_strdupa(var->value);
551         char *cur_event;
552
553         while ((cur_event = strsep(&events, ","))) {
554                 enum ast_cel_event_type event_type;
555
556                 cur_event = ast_strip(cur_event);
557                 if (ast_strlen_zero(cur_event)) {
558                         continue;
559                 }
560
561                 event_type = ast_cel_str_to_event_type(cur_event);
562
563                 if (event_type == AST_CEL_ALL) {
564                         /* All events */
565                         cfg->events = (int64_t) -1;
566                 } else if (event_type == AST_CEL_INVALID_VALUE) {
567                         return -1;
568                 } else {
569                         cfg->events |= ((int64_t) 1 << event_type);
570                 }
571         }
572
573         return 0;
574 }
575
576 static int apps_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
577 {
578         struct ast_cel_general_config *cfg = obj;
579         char *apps = ast_strdupa(var->value);
580         char *cur_app;
581
582         while ((cur_app = strsep(&apps, ","))) {
583                 cur_app = ast_strip(cur_app);
584                 if (ast_strlen_zero(cur_app)) {
585                         continue;
586                 }
587
588                 cur_app = ast_str_to_lower(cur_app);
589                 ast_str_container_add(cfg->apps, cur_app);
590         }
591
592         return 0;
593 }
594
595 const char *ast_cel_get_type_name(enum ast_cel_event_type type)
596 {
597         return S_OR(cel_event_types[type], "Unknown");
598 }
599
600 static int cel_track_app(const char *const_app)
601 {
602         RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
603         RAII_VAR(char *, app, NULL, ao2_cleanup);
604         char *app_lower;
605
606         if (!cfg || !cfg->general) {
607                 return 0;
608         }
609
610         app_lower = ast_str_to_lower(ast_strdupa(const_app));
611         app = ao2_find(cfg->general->apps, app_lower, OBJ_SEARCH_KEY);
612         if (!app) {
613                 return 0;
614         }
615
616         return 1;
617 }
618
619 static int cel_linkedid_ref(const char *linkedid);
620
621 struct ast_event *ast_cel_create_event(struct ast_channel_snapshot *snapshot,
622                 enum ast_cel_event_type event_type, const char *userdefevname,
623                 struct ast_json *extra, const char *peer)
624 {
625         struct timeval eventtime = ast_tvnow();
626         RAII_VAR(char *, extra_txt, NULL, ast_json_free);
627         if (extra) {
628                 extra_txt = ast_json_dump_string(extra);
629         }
630         return ast_event_new(AST_EVENT_CEL,
631                 AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
632                 AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
633                 AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec,
634                 AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, S_OR(userdefevname, ""),
635                 AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_name,
636                 AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_number,
637                 AST_EVENT_IE_CEL_CIDANI, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_ani,
638                 AST_EVENT_IE_CEL_CIDRDNIS, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_rdnis,
639                 AST_EVENT_IE_CEL_CIDDNID, AST_EVENT_IE_PLTYPE_STR, snapshot->caller_dnid,
640                 AST_EVENT_IE_CEL_EXTEN, AST_EVENT_IE_PLTYPE_STR, snapshot->exten,
641                 AST_EVENT_IE_CEL_CONTEXT, AST_EVENT_IE_PLTYPE_STR, snapshot->context,
642                 AST_EVENT_IE_CEL_CHANNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->name,
643                 AST_EVENT_IE_CEL_APPNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->appl,
644                 AST_EVENT_IE_CEL_APPDATA, AST_EVENT_IE_PLTYPE_STR, snapshot->data,
645                 AST_EVENT_IE_CEL_AMAFLAGS, AST_EVENT_IE_PLTYPE_UINT, snapshot->amaflags,
646                 AST_EVENT_IE_CEL_ACCTCODE, AST_EVENT_IE_PLTYPE_STR, snapshot->accountcode,
647                 AST_EVENT_IE_CEL_PEERACCT, AST_EVENT_IE_PLTYPE_STR, snapshot->peeraccount,
648                 AST_EVENT_IE_CEL_UNIQUEID, AST_EVENT_IE_PLTYPE_STR, snapshot->uniqueid,
649                 AST_EVENT_IE_CEL_LINKEDID, AST_EVENT_IE_PLTYPE_STR, snapshot->linkedid,
650                 AST_EVENT_IE_CEL_USERFIELD, AST_EVENT_IE_PLTYPE_STR, snapshot->userfield,
651                 AST_EVENT_IE_CEL_EXTRA, AST_EVENT_IE_PLTYPE_STR, S_OR(extra_txt, ""),
652                 AST_EVENT_IE_CEL_PEER, AST_EVENT_IE_PLTYPE_STR, S_OR(peer, ""),
653                 AST_EVENT_IE_END);
654 }
655
656 static int cel_backend_send_cb(void *obj, void *arg, int flags)
657 {
658         struct cel_backend *backend = obj;
659
660         backend->callback(arg);
661         return 0;
662 }
663
664 static int cel_report_event(struct ast_channel_snapshot *snapshot,
665                 enum ast_cel_event_type event_type, const char *userdefevname,
666                 struct ast_json *extra, const char *peer_str)
667 {
668         struct ast_event *ev;
669         RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
670         RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
671
672         if (!cfg || !cfg->general || !cfg->general->enable || !backends) {
673                 return 0;
674         }
675
676         /* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't
677          * reporting on CHANNEL_START so we can track when to send LINKEDID_END */
678         if (event_type == AST_CEL_CHANNEL_START
679                 && ast_cel_track_event(AST_CEL_LINKEDID_END)) {
680                 if (cel_linkedid_ref(snapshot->linkedid)) {
681                         return -1;
682                 }
683         }
684
685         if (!ast_cel_track_event(event_type)) {
686                 return 0;
687         }
688
689         if ((event_type == AST_CEL_APP_START || event_type == AST_CEL_APP_END)
690                 && !cel_track_app(snapshot->appl)) {
691                 return 0;
692         }
693
694         ev = ast_cel_create_event(snapshot, event_type, userdefevname, extra, peer_str);
695         if (!ev) {
696                 return -1;
697         }
698
699         /* Distribute event to backends */
700         ao2_callback(backends, OBJ_MULTIPLE | OBJ_NODATA, cel_backend_send_cb, ev);
701         ast_event_destroy(ev);
702
703         return 0;
704 }
705
706 /* called whenever a channel is destroyed or a linkedid is changed to
707  * potentially emit a CEL_LINKEDID_END event */
708 static void check_retire_linkedid(struct ast_channel_snapshot *snapshot)
709 {
710         RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
711         struct cel_linkedid *lid;
712
713         if (!linkedids || ast_strlen_zero(snapshot->linkedid)) {
714                 /* The CEL module is shutdown.  Abort. */
715                 return;
716         }
717
718         ao2_lock(linkedids);
719
720         lid = ao2_find(linkedids, (void *) snapshot->linkedid, OBJ_SEARCH_KEY);
721         if (!lid) {
722                 ao2_unlock(linkedids);
723
724                 /*
725                  * The user may have done a reload to start tracking linkedids
726                  * when a call was already in progress.  This is an unusual kind
727                  * of change to make after starting Asterisk.
728                  */
729                 ast_log(LOG_ERROR, "Something weird happened, couldn't find linkedid %s\n",
730                         snapshot->linkedid);
731                 return;
732         }
733
734         if (!--lid->count) {
735                 /* No channels use this linkedid anymore. */
736                 ao2_unlink(linkedids, lid);
737                 ao2_unlock(linkedids);
738
739                 cel_report_event(snapshot, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
740         } else {
741                 ao2_unlock(linkedids);
742         }
743         ao2_ref(lid, -1);
744 }
745
746 /* Note that no 'chan_fixup' function is provided for this datastore type,
747  * because the channels that will use it will never be involved in masquerades.
748  */
749 static const struct ast_datastore_info fabricated_channel_datastore = {
750         .type = "CEL fabricated channel",
751         .destroy = ast_free_ptr,
752 };
753
754 struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event *event)
755 {
756         struct varshead *headp;
757         struct ast_var_t *newvariable;
758         const char *mixed_name;
759         char timebuf[30];
760         struct ast_channel *tchan;
761         struct ast_cel_event_record record = {
762                 .version = AST_CEL_EVENT_RECORD_VERSION,
763         };
764         struct ast_datastore *datastore;
765         char *app_data;
766         RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
767
768         if (!cfg || !cfg->general) {
769                 return NULL;
770         }
771
772         /* do not call ast_channel_alloc because this is not really a real channel */
773         if (!(tchan = ast_dummy_channel_alloc())) {
774                 return NULL;
775         }
776
777         headp = ast_channel_varshead(tchan);
778
779         /* first, get the variables from the event */
780         if (ast_cel_fill_record(event, &record)) {
781                 ast_channel_unref(tchan);
782                 return NULL;
783         }
784
785         /* next, fill the channel with their data */
786         mixed_name = (record.event_type == AST_CEL_USER_DEFINED)
787                 ? record.user_defined_name : record.event_name;
788         if ((newvariable = ast_var_assign("eventtype", mixed_name))) {
789                 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
790         }
791
792         if (ast_strlen_zero(cfg->general->date_format)) {
793                 snprintf(timebuf, sizeof(timebuf), "%ld.%06ld", (long) record.event_time.tv_sec,
794                                 (long) record.event_time.tv_usec);
795         } else {
796                 struct ast_tm tm;
797                 ast_localtime(&record.event_time, &tm, NULL);
798                 ast_strftime(timebuf, sizeof(timebuf), cfg->general->date_format, &tm);
799         }
800
801         if ((newvariable = ast_var_assign("eventtime", timebuf))) {
802                 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
803         }
804
805         if ((newvariable = ast_var_assign("eventenum", record.event_name))) {
806                 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
807         }
808         if ((newvariable = ast_var_assign("userdeftype", record.user_defined_name))) {
809                 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
810         }
811         if ((newvariable = ast_var_assign("eventextra", record.extra))) {
812                 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
813         }
814
815         ast_channel_caller(tchan)->id.name.valid = 1;
816         ast_channel_caller(tchan)->id.name.str = ast_strdup(record.caller_id_name);
817         ast_channel_caller(tchan)->id.number.valid = 1;
818         ast_channel_caller(tchan)->id.number.str = ast_strdup(record.caller_id_num);
819         ast_channel_caller(tchan)->ani.number.valid = 1;
820         ast_channel_caller(tchan)->ani.number.str = ast_strdup(record.caller_id_ani);
821         ast_channel_redirecting(tchan)->from.number.valid = 1;
822         ast_channel_redirecting(tchan)->from.number.str = ast_strdup(record.caller_id_rdnis);
823         ast_channel_dialed(tchan)->number.str = ast_strdup(record.caller_id_dnid);
824
825         ast_channel_exten_set(tchan, record.extension);
826         ast_channel_context_set(tchan, record.context);
827         ast_channel_name_set(tchan, record.channel_name);
828         ast_channel_internal_set_fake_ids(tchan, record.unique_id, record.linked_id);
829         ast_channel_accountcode_set(tchan, record.account_code);
830         ast_channel_peeraccount_set(tchan, record.peer_account);
831         ast_channel_userfield_set(tchan, record.user_field);
832
833         if ((newvariable = ast_var_assign("BRIDGEPEER", record.peer))) {
834                 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
835         }
836
837         ast_channel_amaflags_set(tchan, record.amaflag);
838
839         /* We need to store an 'application name' and 'application
840          * data' on the channel for logging purposes, but the channel
841          * structure only provides a place to store pointers, and it
842          * expects these pointers to be pointing to data that does not
843          * need to be freed. This means that the channel's destructor
844          * does not attempt to free any storage that these pointers
845          * point to. However, we can't provide data in that form directly for
846          * these structure members. In order to ensure that these data
847          * elements have a lifetime that matches the channel's
848          * lifetime, we'll put them in a datastore attached to the
849          * channel, and set's the channel's pointers to point into the
850          * datastore.  The datastore will then be automatically destroyed
851          * when the channel is destroyed.
852          */
853
854         if (!(datastore = ast_datastore_alloc(&fabricated_channel_datastore, NULL))) {
855                 ast_channel_unref(tchan);
856                 return NULL;
857         }
858
859         if (!(app_data = ast_malloc(strlen(record.application_name) + strlen(record.application_data) + 2))) {
860                 ast_datastore_free(datastore);
861                 ast_channel_unref(tchan);
862                 return NULL;
863         }
864
865         ast_channel_appl_set(tchan, strcpy(app_data, record.application_name));
866         ast_channel_data_set(tchan, strcpy(app_data + strlen(record.application_name) + 1,
867                 record.application_data));
868
869         datastore->data = app_data;
870         ast_channel_datastore_add(tchan, datastore);
871
872         return tchan;
873 }
874
875 static int cel_linkedid_ref(const char *linkedid)
876 {
877         RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
878         struct cel_linkedid *lid;
879
880         if (ast_strlen_zero(linkedid)) {
881                 ast_log(LOG_ERROR, "The linkedid should never be empty\n");
882                 return -1;
883         }
884         if (!linkedids) {
885                 /* The CEL module is shutdown.  Abort. */
886                 return -1;
887         }
888
889         ao2_lock(linkedids);
890         lid = ao2_find(linkedids, (void *) linkedid, OBJ_SEARCH_KEY);
891         if (!lid) {
892                 /*
893                  * Changes to the lid->count member are protected by the
894                  * container lock so the lid object does not need its own lock.
895                  */
896                 lid = ao2_alloc_options(sizeof(*lid) + strlen(linkedid) + 1, NULL,
897                         AO2_ALLOC_OPT_LOCK_NOLOCK);
898                 if (!lid) {
899                         ao2_unlock(linkedids);
900                         return -1;
901                 }
902                 strcpy(lid->id, linkedid);/* Safe */
903
904                 ao2_link(linkedids, lid);
905         }
906         ++lid->count;
907         ao2_unlock(linkedids);
908         ao2_ref(lid, -1);
909
910         return 0;
911 }
912
913 int ast_cel_fill_record(const struct ast_event *e, struct ast_cel_event_record *r)
914 {
915         if (r->version != AST_CEL_EVENT_RECORD_VERSION) {
916                 ast_log(LOG_ERROR, "Module ABI mismatch for ast_cel_event_record.  "
917                                 "Please ensure all modules were compiled for "
918                                 "this version of Asterisk.\n");
919                 return -1;
920         }
921
922         r->event_type = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TYPE);
923
924         r->event_time.tv_sec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME);
925         r->event_time.tv_usec = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_EVENT_TIME_USEC);
926
927         r->event_name = ast_cel_get_type_name(r->event_type);
928         if (r->event_type == AST_CEL_USER_DEFINED) {
929                 r->user_defined_name = ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USEREVENT_NAME);
930         } else {
931                 r->user_defined_name = "";
932         }
933
934         r->caller_id_name   = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNAME), "");
935         r->caller_id_num    = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDNUM), "");
936         r->caller_id_ani    = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDANI), "");
937         r->caller_id_rdnis  = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDRDNIS), "");
938         r->caller_id_dnid   = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CIDDNID), "");
939         r->extension        = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTEN), "");
940         r->context          = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CONTEXT), "");
941         r->channel_name     = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_CHANNAME), "");
942         r->application_name = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPNAME), "");
943         r->application_data = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_APPDATA), "");
944         r->account_code     = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_ACCTCODE), "");
945         r->peer_account     = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEERACCT), "");
946         r->unique_id        = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_UNIQUEID), "");
947         r->linked_id        = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_LINKEDID), "");
948         r->amaflag          = ast_event_get_ie_uint(e, AST_EVENT_IE_CEL_AMAFLAGS);
949         r->user_field       = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_USERFIELD), "");
950         r->peer             = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_PEER), "");
951         r->extra            = S_OR(ast_event_get_ie_str(e, AST_EVENT_IE_CEL_EXTRA), "");
952
953         return 0;
954 }
955
956 /*! \brief Typedef for callbacks that get called on channel snapshot updates */
957 typedef void (*cel_channel_snapshot_monitor)(
958         struct ast_channel_snapshot *old_snapshot,
959         struct ast_channel_snapshot *new_snapshot);
960
961 static struct ast_multi_channel_blob *get_dialstatus_blob(const char *uniqueid)
962 {
963         struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
964         struct ast_multi_channel_blob *blob = NULL;
965
966         if (dial_statuses) {
967                 blob = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK);
968                 ao2_ref(dial_statuses, -1);
969         }
970         return blob;
971 }
972
973 static const char *get_blob_variable(struct ast_multi_channel_blob *blob, const char *varname)
974 {
975         struct ast_json *json = ast_multi_channel_blob_get_json(blob);
976         if (!json) {
977                 return NULL;
978         }
979
980         json = ast_json_object_get(json, varname);
981         if (!json) {
982                 return NULL;
983         }
984
985         return ast_json_string_get(json);
986 }
987
988 /*! \brief Handle channel state changes */
989 static void cel_channel_state_change(
990         struct ast_channel_snapshot *old_snapshot,
991         struct ast_channel_snapshot *new_snapshot)
992 {
993         int is_hungup, was_hungup;
994
995         if (!new_snapshot) {
996                 cel_report_event(old_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
997                 if (ast_cel_track_event(AST_CEL_LINKEDID_END)) {
998                         check_retire_linkedid(old_snapshot);
999                 }
1000                 return;
1001         }
1002
1003         if (!old_snapshot) {
1004                 cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
1005                 return;
1006         }
1007
1008         was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
1009         is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
1010
1011         if (!was_hungup && is_hungup) {
1012                 struct ast_json *extra;
1013                 struct ast_multi_channel_blob *blob = get_dialstatus_blob(new_snapshot->uniqueid);
1014                 const char *dialstatus = "";
1015
1016                 if (blob && !ast_strlen_zero(get_blob_variable(blob, "dialstatus"))) {
1017                         dialstatus = get_blob_variable(blob, "dialstatus");
1018                 }
1019                 extra = ast_json_pack("{s: i, s: s, s: s}",
1020                         "hangupcause", new_snapshot->hangupcause,
1021                         "hangupsource", new_snapshot->hangupsource,
1022                         "dialstatus", dialstatus);
1023                 cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
1024                 ast_json_unref(extra);
1025                 ao2_cleanup(blob);
1026                 return;
1027         }
1028
1029         if (old_snapshot->state != new_snapshot->state && new_snapshot->state == AST_STATE_UP) {
1030                 cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
1031                 return;
1032         }
1033 }
1034
1035 static void cel_channel_linkedid_change(
1036         struct ast_channel_snapshot *old_snapshot,
1037         struct ast_channel_snapshot *new_snapshot)
1038 {
1039         if (!old_snapshot || !new_snapshot) {
1040                 return;
1041         }
1042
1043         ast_assert(!ast_strlen_zero(new_snapshot->linkedid));
1044         ast_assert(!ast_strlen_zero(old_snapshot->linkedid));
1045
1046         if (ast_cel_track_event(AST_CEL_LINKEDID_END)
1047                 && strcmp(old_snapshot->linkedid, new_snapshot->linkedid)) {
1048                 cel_linkedid_ref(new_snapshot->linkedid);
1049                 check_retire_linkedid(old_snapshot);
1050         }
1051 }
1052
1053 static void cel_channel_app_change(
1054         struct ast_channel_snapshot *old_snapshot,
1055         struct ast_channel_snapshot *new_snapshot)
1056 {
1057         if (new_snapshot && old_snapshot
1058                 && !strcmp(old_snapshot->appl, new_snapshot->appl)) {
1059                 return;
1060         }
1061
1062         /* old snapshot has an application, end it */
1063         if (old_snapshot && !ast_strlen_zero(old_snapshot->appl)) {
1064                 cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
1065         }
1066
1067         /* new snapshot has an application, start it */
1068         if (new_snapshot && !ast_strlen_zero(new_snapshot->appl)) {
1069                 cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
1070         }
1071 }
1072
1073 /* \brief Handlers for channel snapshot changes.
1074  * \note Order of the handlers matters. Application changes must come before state
1075  * changes to ensure that hangup notifications occur after application changes.
1076  * Linkedid checking should always come last.
1077  */
1078 cel_channel_snapshot_monitor cel_channel_monitors[] = {
1079         cel_channel_app_change,
1080         cel_channel_state_change,
1081         cel_channel_linkedid_change,
1082 };
1083
1084 static int cel_filter_channel_snapshot(struct ast_channel_snapshot *snapshot)
1085 {
1086         if (!snapshot) {
1087                 return 0;
1088         }
1089         return snapshot->tech_properties & AST_CHAN_TP_INTERNAL;
1090 }
1091
1092 static void cel_snapshot_update_cb(void *data, struct stasis_subscription *sub,
1093         struct stasis_message *message)
1094 {
1095         struct stasis_cache_update *update = stasis_message_data(message);
1096         if (ast_channel_snapshot_type() == update->type) {
1097                 struct ast_channel_snapshot *old_snapshot;
1098                 struct ast_channel_snapshot *new_snapshot;
1099                 size_t i;
1100
1101                 old_snapshot = stasis_message_data(update->old_snapshot);
1102                 new_snapshot = stasis_message_data(update->new_snapshot);
1103
1104                 if (cel_filter_channel_snapshot(old_snapshot) || cel_filter_channel_snapshot(new_snapshot)) {
1105                         return;
1106                 }
1107
1108                 for (i = 0; i < ARRAY_LEN(cel_channel_monitors); ++i) {
1109                         cel_channel_monitors[i](old_snapshot, new_snapshot);
1110                 }
1111         }
1112 }
1113
1114 static struct ast_str *cel_generate_peer_str(
1115         struct ast_bridge_snapshot *bridge,
1116         struct ast_channel_snapshot *chan)
1117 {
1118         struct ast_str *peer_str = ast_str_create(32);
1119         struct ao2_iterator i;
1120         char *current_chan = NULL;
1121
1122         if (!peer_str) {
1123                 return NULL;
1124         }
1125
1126         for (i = ao2_iterator_init(bridge->channels, 0);
1127                 (current_chan = ao2_iterator_next(&i));
1128                 ao2_cleanup(current_chan)) {
1129                 struct ast_channel_snapshot *current_snapshot;
1130
1131                 /* Don't add the channel for which this message is being generated */
1132                 if (!strcmp(current_chan, chan->uniqueid)) {
1133                         continue;
1134                 }
1135
1136                 current_snapshot = ast_channel_snapshot_get_latest(current_chan);
1137                 if (!current_snapshot) {
1138                         continue;
1139                 }
1140
1141                 ast_str_append(&peer_str, 0, "%s,", current_snapshot->name);
1142                 ao2_cleanup(current_snapshot);
1143         }
1144         ao2_iterator_destroy(&i);
1145
1146         /* Rip off the trailing comma */
1147         ast_str_truncate(peer_str, -1);
1148
1149         return peer_str;
1150 }
1151
1152 static void cel_bridge_enter_cb(
1153         void *data, struct stasis_subscription *sub,
1154         struct stasis_message *message)
1155 {
1156         struct ast_bridge_blob *blob = stasis_message_data(message);
1157         struct ast_bridge_snapshot *snapshot = blob->bridge;
1158         struct ast_channel_snapshot *chan_snapshot = blob->channel;
1159         RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
1160         RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
1161
1162         if (cel_filter_channel_snapshot(chan_snapshot)) {
1163                 return;
1164         }
1165
1166         extra = ast_json_pack("{s: s, s: s}",
1167                 "bridge_id", snapshot->uniqueid,
1168                 "bridge_technology", snapshot->technology);
1169         if (!extra) {
1170                 return;
1171         }
1172
1173         peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
1174         if (!peer_str) {
1175                 return;
1176         }
1177
1178         cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, NULL, extra, ast_str_buffer(peer_str));
1179 }
1180
1181 static void cel_bridge_leave_cb(
1182         void *data, struct stasis_subscription *sub,
1183         struct stasis_message *message)
1184 {
1185         struct ast_bridge_blob *blob = stasis_message_data(message);
1186         struct ast_bridge_snapshot *snapshot = blob->bridge;
1187         struct ast_channel_snapshot *chan_snapshot = blob->channel;
1188         RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
1189         RAII_VAR(struct ast_str *, peer_str, NULL, ast_free);
1190
1191         if (cel_filter_channel_snapshot(chan_snapshot)) {
1192                 return;
1193         }
1194
1195         extra = ast_json_pack("{s: s, s: s}",
1196                 "bridge_id", snapshot->uniqueid,
1197                 "bridge_technology", snapshot->technology);
1198         if (!extra) {
1199                 return;
1200         }
1201
1202         peer_str = cel_generate_peer_str(snapshot, chan_snapshot);
1203         if (!peer_str) {
1204                 return;
1205         }
1206
1207         cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, NULL, extra, ast_str_buffer(peer_str));
1208 }
1209
1210 static void cel_parking_cb(
1211         void *data, struct stasis_subscription *sub,
1212         struct stasis_message *message)
1213 {
1214         struct ast_parked_call_payload *parked_payload = stasis_message_data(message);
1215         RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
1216         const char *reason = NULL;
1217
1218         switch (parked_payload->event_type) {
1219         case PARKED_CALL:
1220                 extra = ast_json_pack("{s: s, s: s}",
1221                         "parker_dial_string", parked_payload->parker_dial_string,
1222                         "parking_lot", parked_payload->parkinglot);
1223                 if (extra) {
1224                         cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra, NULL);
1225                 }
1226                 return;
1227         case PARKED_CALL_TIMEOUT:
1228                 reason = "ParkedCallTimeOut";
1229                 break;
1230         case PARKED_CALL_GIVEUP:
1231                 reason = "ParkedCallGiveUp";
1232                 break;
1233         case PARKED_CALL_UNPARKED:
1234                 reason = "ParkedCallUnparked";
1235                 break;
1236         case PARKED_CALL_FAILED:
1237                 reason = "ParkedCallFailed";
1238                 break;
1239         case PARKED_CALL_SWAP:
1240                 reason = "ParkedCallSwap";
1241                 break;
1242         }
1243
1244         if (parked_payload->retriever) {
1245                 extra = ast_json_pack("{s: s, s: s}",
1246                         "reason", reason,
1247                         "retriever", parked_payload->retriever->name);
1248         } else {
1249                 extra = ast_json_pack("{s: s}", "reason", reason);
1250         }
1251
1252         if (extra) {
1253                 cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra, NULL);
1254         }
1255 }
1256
1257 static void save_dialstatus(struct ast_multi_channel_blob *blob)
1258 {
1259         struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
1260
1261         ast_assert(blob != NULL);
1262
1263         if (dial_statuses) {
1264                 ao2_link(dial_statuses, blob);
1265                 ao2_ref(dial_statuses, -1);
1266         }
1267 }
1268
1269 static int is_valid_dialstatus(struct ast_multi_channel_blob *blob)
1270 {
1271         const char *dialstatus = get_blob_variable(blob, "dialstatus");
1272         int res = 0;
1273
1274         if (ast_strlen_zero(dialstatus)) {
1275                 res = 0;
1276         } else if (!strcasecmp(dialstatus, "CHANUNAVAIL")) {
1277                 res = 1;
1278         } else if (!strcasecmp(dialstatus, "CONGESTION")) {
1279                 res = 1;
1280         } else if (!strcasecmp(dialstatus, "NOANSWER")) {
1281                 res = 1;
1282         } else if (!strcasecmp(dialstatus, "BUSY")) {
1283                 res = 1;
1284         } else if (!strcasecmp(dialstatus, "ANSWER")) {
1285                 res = 1;
1286         } else if (!strcasecmp(dialstatus, "CANCEL")) {
1287                 res = 1;
1288         } else if (!strcasecmp(dialstatus, "DONTCALL")) {
1289                 res = 1;
1290         } else if (!strcasecmp(dialstatus, "TORTURE")) {
1291                 res = 1;
1292         } else if (!strcasecmp(dialstatus, "INVALIDARGS")) {
1293                 res = 1;
1294         }
1295         return res;
1296 }
1297
1298 static void cel_dial_cb(void *data, struct stasis_subscription *sub,
1299         struct stasis_message *message)
1300 {
1301         struct ast_multi_channel_blob *blob = stasis_message_data(message);
1302
1303         if (cel_filter_channel_snapshot(ast_multi_channel_blob_get_channel(blob, "caller"))) {
1304                 return;
1305         }
1306
1307         if (!get_caller_uniqueid(blob)) {
1308                 return;
1309         }
1310
1311         if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
1312                 struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
1313                 struct ast_json *extra;
1314
1315                 if (!caller) {
1316                         return;
1317                 }
1318
1319                 extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
1320                 if (extra) {
1321                         cel_report_event(caller, AST_CEL_FORWARD, NULL, extra, NULL);
1322                         ast_json_unref(extra);
1323                 }
1324         }
1325
1326         if (is_valid_dialstatus(blob)) {
1327                 save_dialstatus(blob);
1328         }
1329 }
1330
1331 static void cel_generic_cb(
1332         void *data, struct stasis_subscription *sub,
1333         struct stasis_message *message)
1334 {
1335         struct ast_channel_blob *obj = stasis_message_data(message);
1336         int event_type = ast_json_integer_get(ast_json_object_get(obj->blob, "event_type"));
1337         struct ast_json *event_details = ast_json_object_get(obj->blob, "event_details");
1338
1339         switch (event_type) {
1340         case AST_CEL_USER_DEFINED:
1341                 {
1342                         const char *event = ast_json_string_get(ast_json_object_get(event_details, "event"));
1343                         struct ast_json *extra = ast_json_object_get(event_details, "extra");
1344                         cel_report_event(obj->snapshot, event_type, event, extra, NULL);
1345                         break;
1346                 }
1347         default:
1348                 ast_log(LOG_ERROR, "Unhandled %s event blob\n", ast_cel_get_type_name(event_type));
1349                 break;
1350         }
1351 }
1352
1353 static void cel_blind_transfer_cb(
1354         void *data, struct stasis_subscription *sub,
1355         struct stasis_message *message)
1356 {
1357         struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
1358         struct ast_channel_snapshot *chan_snapshot = transfer_msg->transferer;
1359         struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->bridge;
1360         struct ast_json *extra;
1361
1362         if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
1363                 return;
1364         }
1365
1366         extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
1367                 "extension", transfer_msg->exten,
1368                 "context", transfer_msg->context,
1369                 "bridge_id", bridge_snapshot->uniqueid,
1370                 "transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->name : "N/A",
1371                 "transferee_channel_uniqueid", transfer_msg->transferee ? transfer_msg->transferee->uniqueid  : "N/A");
1372         if (extra) {
1373                 cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
1374                 ast_json_unref(extra);
1375         }
1376 }
1377
1378 static void cel_attended_transfer_cb(
1379         void *data, struct stasis_subscription *sub,
1380         struct stasis_message *message)
1381 {
1382         struct ast_attended_transfer_message *xfer = stasis_message_data(message);
1383         struct ast_json *extra = NULL;
1384         struct ast_bridge_snapshot *bridge1, *bridge2;
1385         struct ast_channel_snapshot *channel1, *channel2;
1386
1387         /* Make sure bridge1 is always non-NULL */
1388         if (!xfer->to_transferee.bridge_snapshot) {
1389                 bridge1 = xfer->to_transfer_target.bridge_snapshot;
1390                 bridge2 = xfer->to_transferee.bridge_snapshot;
1391                 channel1 = xfer->to_transfer_target.channel_snapshot;
1392                 channel2 = xfer->to_transferee.channel_snapshot;
1393         } else {
1394                 bridge1 = xfer->to_transferee.bridge_snapshot;
1395                 bridge2 = xfer->to_transfer_target.bridge_snapshot;
1396                 channel1 = xfer->to_transferee.channel_snapshot;
1397                 channel2 = xfer->to_transfer_target.channel_snapshot;
1398         }
1399
1400         switch (xfer->dest_type) {
1401         case AST_ATTENDED_TRANSFER_DEST_FAIL:
1402                 return;
1403                 /* handle these three the same */
1404         case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
1405         case AST_ATTENDED_TRANSFER_DEST_LINK:
1406         case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
1407                 extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
1408                         "bridge1_id", bridge1->uniqueid,
1409                         "channel2_name", channel2->name,
1410                         "channel2_uniqueid", channel2->uniqueid,
1411                         "bridge2_id", bridge2->uniqueid,
1412                         "transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
1413                         "transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
1414                         "transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
1415                         "transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
1416                 if (!extra) {
1417                         return;
1418                 }
1419                 break;
1420         case AST_ATTENDED_TRANSFER_DEST_APP:
1421         case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
1422                 extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s, s: s, s: s, s: s}",
1423                         "bridge1_id", bridge1->uniqueid,
1424                         "channel2_name", channel2->name,
1425                         "channel2_uniqueid", channel2->uniqueid,
1426                         "app", xfer->dest.app,
1427                         "transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
1428                         "transferee_channel_uniqueid", xfer->transferee ? xfer->transferee->uniqueid : "N/A",
1429                         "transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A",
1430                         "transfer_target_channel_uniqueid", xfer->target ? xfer->target->uniqueid : "N/A");
1431                 if (!extra) {
1432                         return;
1433                 }
1434                 break;
1435         }
1436         cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL);
1437         ast_json_unref(extra);
1438 }
1439
1440 static void cel_pickup_cb(
1441         void *data, struct stasis_subscription *sub,
1442         struct stasis_message *message)
1443 {
1444         struct ast_multi_channel_blob *obj = stasis_message_data(message);
1445         struct ast_channel_snapshot *channel = ast_multi_channel_blob_get_channel(obj, "channel");
1446         struct ast_channel_snapshot *target = ast_multi_channel_blob_get_channel(obj, "target");
1447         struct ast_json *extra;
1448
1449         if (!channel || !target) {
1450                 return;
1451         }
1452
1453         extra = ast_json_pack("{s: s, s: s}",
1454                 "pickup_channel", channel->name,
1455                 "pickup_channel_uniqueid", channel->uniqueid);
1456         if (!extra) {
1457                 return;
1458         }
1459
1460         cel_report_event(target, AST_CEL_PICKUP, NULL, extra, NULL);
1461         ast_json_unref(extra);
1462 }
1463
1464 static void cel_local_cb(
1465         void *data, struct stasis_subscription *sub,
1466         struct stasis_message *message)
1467 {
1468         struct ast_multi_channel_blob *obj = stasis_message_data(message);
1469         struct ast_channel_snapshot *localone = ast_multi_channel_blob_get_channel(obj, "1");
1470         struct ast_channel_snapshot *localtwo = ast_multi_channel_blob_get_channel(obj, "2");
1471         struct ast_json *extra;
1472
1473         if (!localone || !localtwo) {
1474                 return;
1475         }
1476
1477         extra = ast_json_pack("{s: s, s: s}",
1478                 "local_two", localtwo->name,
1479                 "local_two_uniqueid", localtwo->uniqueid);
1480         if (!extra) {
1481                 return;
1482         }
1483
1484         cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
1485         ast_json_unref(extra);
1486 }
1487
1488 static void destroy_routes(void)
1489 {
1490         stasis_message_router_unsubscribe_and_join(cel_state_router);
1491         cel_state_router = NULL;
1492 }
1493
1494 static void destroy_subscriptions(void)
1495 {
1496         ao2_cleanup(cel_aggregation_topic);
1497         cel_aggregation_topic = NULL;
1498         ao2_cleanup(cel_topic);
1499         cel_topic = NULL;
1500
1501         cel_channel_forwarder = stasis_forward_cancel(cel_channel_forwarder);
1502         cel_bridge_forwarder = stasis_forward_cancel(cel_bridge_forwarder);
1503         cel_parking_forwarder = stasis_forward_cancel(cel_parking_forwarder);
1504         cel_cel_forwarder = stasis_forward_cancel(cel_cel_forwarder);
1505 }
1506
1507 static void cel_engine_cleanup(void)
1508 {
1509         destroy_routes();
1510         destroy_subscriptions();
1511         STASIS_MESSAGE_TYPE_CLEANUP(cel_generic_type);
1512
1513         ast_cli_unregister(&cli_status);
1514         aco_info_destroy(&cel_cfg_info);
1515         ao2_global_obj_release(cel_configs);
1516         ao2_global_obj_release(cel_dialstatus_store);
1517         ao2_global_obj_release(cel_linkedids);
1518         ao2_global_obj_release(cel_backends);
1519 }
1520
1521 /*!
1522  * \brief Create the Stasis subscriptions for CEL
1523  */
1524 static int create_subscriptions(void)
1525 {
1526         cel_aggregation_topic = stasis_topic_create("cel_aggregation_topic");
1527         if (!cel_aggregation_topic) {
1528                 return -1;
1529         }
1530
1531         cel_topic = stasis_topic_create("cel_topic");
1532         if (!cel_topic) {
1533                 return -1;
1534         }
1535
1536         cel_channel_forwarder = stasis_forward_all(
1537                 ast_channel_topic_all_cached(),
1538                 cel_aggregation_topic);
1539         if (!cel_channel_forwarder) {
1540                 return -1;
1541         }
1542
1543         cel_bridge_forwarder = stasis_forward_all(
1544                 ast_bridge_topic_all_cached(),
1545                 cel_aggregation_topic);
1546         if (!cel_bridge_forwarder) {
1547                 return -1;
1548         }
1549
1550         cel_parking_forwarder = stasis_forward_all(
1551                 ast_parking_topic(),
1552                 cel_aggregation_topic);
1553         if (!cel_parking_forwarder) {
1554                 return -1;
1555         }
1556
1557         cel_cel_forwarder = stasis_forward_all(
1558                 ast_cel_topic(),
1559                 cel_aggregation_topic);
1560         if (!cel_cel_forwarder) {
1561                 return -1;
1562         }
1563
1564         return 0;
1565 }
1566
1567 /*!
1568  * \brief Create the Stasis message router and routes for CEL
1569  */
1570 static int create_routes(void)
1571 {
1572         int ret = 0;
1573
1574         cel_state_router = stasis_message_router_create(cel_aggregation_topic);
1575         if (!cel_state_router) {
1576                 return -1;
1577         }
1578
1579         ret |= stasis_message_router_add(cel_state_router,
1580                 stasis_cache_update_type(),
1581                 cel_snapshot_update_cb,
1582                 NULL);
1583
1584         ret |= stasis_message_router_add(cel_state_router,
1585                 ast_channel_dial_type(),
1586                 cel_dial_cb,
1587                 NULL);
1588
1589         ret |= stasis_message_router_add(cel_state_router,
1590                 ast_channel_entered_bridge_type(),
1591                 cel_bridge_enter_cb,
1592                 NULL);
1593
1594         ret |= stasis_message_router_add(cel_state_router,
1595                 ast_channel_left_bridge_type(),
1596                 cel_bridge_leave_cb,
1597                 NULL);
1598
1599         ret |= stasis_message_router_add(cel_state_router,
1600                 ast_parked_call_type(),
1601                 cel_parking_cb,
1602                 NULL);
1603
1604         ret |= stasis_message_router_add(cel_state_router,
1605                 cel_generic_type(),
1606                 cel_generic_cb,
1607                 NULL);
1608
1609         ret |= stasis_message_router_add(cel_state_router,
1610                 ast_blind_transfer_type(),
1611                 cel_blind_transfer_cb,
1612                 NULL);
1613
1614         ret |= stasis_message_router_add(cel_state_router,
1615                 ast_attended_transfer_type(),
1616                 cel_attended_transfer_cb,
1617                 NULL);
1618
1619         ret |= stasis_message_router_add(cel_state_router,
1620                 ast_call_pickup_type(),
1621                 cel_pickup_cb,
1622                 NULL);
1623
1624         ret |= stasis_message_router_add(cel_state_router,
1625                 ast_local_optimization_end_type(),
1626                 cel_local_cb,
1627                 NULL);
1628
1629         if (ret) {
1630                 ast_log(AST_LOG_ERROR, "Failed to register for Stasis messages\n");
1631         }
1632
1633         return ret;
1634 }
1635
1636 static int lid_hash(const void *obj, const int flags)
1637 {
1638         const struct cel_linkedid *lid;
1639         const char *key;
1640
1641         switch (flags & OBJ_SEARCH_MASK) {
1642         case OBJ_SEARCH_KEY:
1643                 key = obj;
1644                 break;
1645         case OBJ_SEARCH_OBJECT:
1646                 lid = obj;
1647                 key = lid->id;
1648                 break;
1649         default:
1650                 /* Hash can only work on something with a full key. */
1651                 ast_assert(0);
1652                 return 0;
1653         }
1654         return ast_str_hash(key);
1655 }
1656
1657 static int lid_cmp(void *obj, void *arg, int flags)
1658 {
1659         const struct cel_linkedid *object_left = obj;
1660         const struct cel_linkedid *object_right = arg;
1661         const char *right_key = arg;
1662         int cmp;
1663
1664         switch (flags & OBJ_SEARCH_MASK) {
1665         case OBJ_SEARCH_OBJECT:
1666                 right_key = object_right->id;
1667                 /* Fall through */
1668         case OBJ_SEARCH_KEY:
1669                 cmp = strcmp(object_left->id, right_key);
1670                 break;
1671         case OBJ_SEARCH_PARTIAL_KEY:
1672                 /*
1673                  * We could also use a partial key struct containing a length
1674                  * so strlen() does not get called for every comparison instead.
1675                  */
1676                 cmp = strncmp(object_left->id, right_key, strlen(right_key));
1677                 break;
1678         default:
1679                 /*
1680                  * What arg points to is specific to this traversal callback
1681                  * and has no special meaning to astobj2.
1682                  */
1683                 cmp = 0;
1684                 break;
1685         }
1686         if (cmp) {
1687                 return 0;
1688         }
1689         /*
1690          * At this point the traversal callback is identical to a sorted
1691          * container.
1692          */
1693         return CMP_MATCH;
1694 }
1695
1696 int ast_cel_engine_init(void)
1697 {
1698         struct ao2_container *container;
1699
1700         container = ao2_container_alloc(NUM_APP_BUCKETS, lid_hash, lid_cmp);
1701         ao2_global_obj_replace_unref(cel_linkedids, container);
1702         ao2_cleanup(container);
1703         if (!container) {
1704                 cel_engine_cleanup();
1705                 return -1;
1706         }
1707
1708         container = ao2_container_alloc(NUM_DIALSTATUS_BUCKETS,
1709                 dialstatus_hash, dialstatus_cmp);
1710         ao2_global_obj_replace_unref(cel_dialstatus_store, container);
1711         ao2_cleanup(container);
1712         if (!container) {
1713                 cel_engine_cleanup();
1714                 return -1;
1715         }
1716
1717         if (STASIS_MESSAGE_TYPE_INIT(cel_generic_type)) {
1718                 cel_engine_cleanup();
1719                 return -1;
1720         }
1721
1722         if (ast_cli_register(&cli_status)) {
1723                 cel_engine_cleanup();
1724                 return -1;
1725         }
1726
1727         container = ao2_container_alloc(BACKEND_BUCKETS, cel_backend_hash, cel_backend_cmp);
1728         ao2_global_obj_replace_unref(cel_backends, container);
1729         ao2_cleanup(container);
1730         if (!container) {
1731                 cel_engine_cleanup();
1732                 return -1;
1733         }
1734
1735         if (aco_info_init(&cel_cfg_info)) {
1736                 cel_engine_cleanup();
1737                 return -1;
1738         }
1739
1740         aco_option_register(&cel_cfg_info, "enable", ACO_EXACT, general_options, "no", OPT_BOOL_T, 1, FLDSET(struct ast_cel_general_config, enable));
1741         aco_option_register(&cel_cfg_info, "dateformat", ACO_EXACT, general_options, "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_cel_general_config, date_format));
1742         aco_option_register_custom(&cel_cfg_info, "apps", ACO_EXACT, general_options, "", apps_handler, 0);
1743         aco_option_register_custom(&cel_cfg_info, "events", ACO_EXACT, general_options, "", events_handler, 0);
1744
1745         if (aco_process_config(&cel_cfg_info, 0)) {
1746                 struct cel_config *cel_cfg = cel_config_alloc();
1747
1748                 if (!cel_cfg) {
1749                         cel_engine_cleanup();
1750                         return -1;
1751                 }
1752
1753                 /* We couldn't process the configuration so create a default config. */
1754                 if (!aco_set_defaults(&general_option, "general", cel_cfg->general)) {
1755                         ast_log(LOG_NOTICE, "Failed to process CEL configuration; using defaults\n");
1756                         ao2_global_obj_replace_unref(cel_configs, cel_cfg);
1757                 }
1758                 ao2_ref(cel_cfg, -1);
1759         }
1760
1761         if (create_subscriptions()) {
1762                 cel_engine_cleanup();
1763                 return -1;
1764         }
1765
1766         if (ast_cel_check_enabled() && create_routes()) {
1767                 cel_engine_cleanup();
1768                 return -1;
1769         }
1770
1771         ast_register_cleanup(cel_engine_cleanup);
1772         return 0;
1773 }
1774
1775 int ast_cel_engine_reload(void)
1776 {
1777         unsigned int was_enabled = ast_cel_check_enabled();
1778         unsigned int is_enabled;
1779
1780         if (aco_process_config(&cel_cfg_info, 1) == ACO_PROCESS_ERROR) {
1781                 return -1;
1782         }
1783
1784         is_enabled = ast_cel_check_enabled();
1785
1786         if (!was_enabled && is_enabled) {
1787                 if (create_routes()) {
1788                         return -1;
1789                 }
1790         } else if (was_enabled && !is_enabled) {
1791                 destroy_routes();
1792         }
1793
1794         ast_verb(3, "CEL logging %sabled.\n", is_enabled ? "en" : "dis");
1795
1796         return 0;
1797 }
1798
1799 void ast_cel_publish_event(struct ast_channel *chan,
1800         enum ast_cel_event_type event_type,
1801         struct ast_json *blob)
1802 {
1803         struct ast_json *cel_blob;
1804         struct stasis_message *message;
1805
1806         cel_blob = ast_json_pack("{s: i, s: O}",
1807                 "event_type", event_type,
1808                 "event_details", blob);
1809
1810         message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan), cel_generic_type(), cel_blob);
1811         if (message) {
1812                 stasis_publish(ast_cel_topic(), message);
1813         }
1814         ao2_cleanup(message);
1815         ast_json_unref(cel_blob);
1816 }
1817
1818 struct stasis_topic *ast_cel_topic(void)
1819 {
1820         return cel_topic;
1821 }
1822
1823 struct ast_cel_general_config *ast_cel_get_config(void)
1824 {
1825         RAII_VAR(struct cel_config *, mod_cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
1826
1827         if (!mod_cfg || !mod_cfg->general) {
1828                 return NULL;
1829         }
1830
1831         ao2_ref(mod_cfg->general, +1);
1832         return mod_cfg->general;
1833 }
1834
1835 void ast_cel_set_config(struct ast_cel_general_config *config)
1836 {
1837         int was_enabled;
1838         int is_enabled;
1839         struct ast_cel_general_config *cleanup_config;
1840         struct cel_config *mod_cfg = ao2_global_obj_ref(cel_configs);
1841
1842         if (mod_cfg) {
1843                 was_enabled = ast_cel_check_enabled();
1844
1845                 cleanup_config = mod_cfg->general;
1846                 ao2_bump(config);
1847                 mod_cfg->general = config;
1848                 ao2_cleanup(cleanup_config);
1849
1850                 is_enabled = ast_cel_check_enabled();
1851                 if (!was_enabled && is_enabled) {
1852                         create_routes();
1853                 } else if (was_enabled && !is_enabled) {
1854                         destroy_routes();
1855                 }
1856
1857                 ao2_ref(mod_cfg, -1);
1858         }
1859 }
1860
1861 int ast_cel_backend_unregister(const char *name)
1862 {
1863         struct ao2_container *backends = ao2_global_obj_ref(cel_backends);
1864
1865         if (backends) {
1866                 ao2_find(backends, name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
1867                 ao2_ref(backends, -1);
1868         }
1869
1870         return 0;
1871 }
1872
1873 int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
1874 {
1875         RAII_VAR(struct ao2_container *, backends, ao2_global_obj_ref(cel_backends), ao2_cleanup);
1876         struct cel_backend *backend;
1877
1878         if (!backends || ast_strlen_zero(name) || !backend_callback) {
1879                 return -1;
1880         }
1881
1882         /* The backend object is immutable so it doesn't need a lock of its own. */
1883         backend = ao2_alloc_options(sizeof(*backend) + 1 + strlen(name), NULL,
1884                 AO2_ALLOC_OPT_LOCK_NOLOCK);
1885         if (!backend) {
1886                 return -1;
1887         }
1888         strcpy(backend->name, name);/* Safe */
1889         backend->callback = backend_callback;
1890
1891         ao2_link(backends, backend);
1892         ao2_ref(backend, -1);
1893         return 0;
1894 }