BuildSystem: Remove unused variables.
[asterisk/asterisk.git] / main / aoc.c
index 8a51509..253c745 100644 (file)
@@ -28,7 +28,6 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 
 #include "asterisk/aoc.h"
 #include "asterisk/utils.h"
@@ -36,6 +35,138 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
 #include "asterisk/_private.h"
 #include "asterisk/cli.h"
 #include "asterisk/manager.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/stasis_message_router.h"
+
+/*** DOCUMENTATION
+       <managerEvent language="en_US" name="AOC-S">
+               <managerEventInstance class="EVENT_FLAG_AOC">
+                       <synopsis>Raised when an Advice of Charge message is sent at the beginning of a call.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <parameter name="Chargeable" />
+                               <parameter name="RateType">
+                                       <enumlist>
+                                               <enum name="NotAvailable" />
+                                               <enum name="Free" />
+                                               <enum name="FreeFromBeginning" />
+                                               <enum name="Duration" />
+                                               <enum name="Flag" />
+                                               <enum name="Volume" />
+                                               <enum name="SpecialCode" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Currency" />
+                               <parameter name="Name" />
+                               <parameter name="Cost" />
+                               <parameter name="Multiplier">
+                                       <enumlist>
+                                               <enum name="1/1000" />
+                                               <enum name="1/100" />
+                                               <enum name="1/10" />
+                                               <enum name="1" />
+                                               <enum name="10" />
+                                               <enum name="100" />
+                                               <enum name="1000" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="ChargingType" />
+                               <parameter name="StepFunction" />
+                               <parameter name="Granularity" />
+                               <parameter name="Length" />
+                               <parameter name="Scale" />
+                               <parameter name="Unit">
+                                       <enumlist>
+                                               <enum name="Octect" />
+                                               <enum name="Segment" />
+                                               <enum name="Message" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="SpecialCode" />
+                       </syntax>
+                       <see-also>
+                               <ref type="managerEvent">AOC-D</ref>
+                               <ref type="managerEvent">AOC-E</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="AOC-D">
+               <managerEventInstance class="EVENT_FLAG_AOC">
+                       <synopsis>Raised when an Advice of Charge message is sent during a call.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <parameter name="Charge" />
+                               <parameter name="Type">
+                                       <enumlist>
+                                               <enum name="NotAvailable" />
+                                               <enum name="Free" />
+                                               <enum name="Currency" />
+                                               <enum name="Units" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="BillingID">
+                                       <enumlist>
+                                               <enum name="Normal" />
+                                               <enum name="Reverse" />
+                                               <enum name="CreditCard" />
+                                               <enum name="CallForwardingUnconditional" />
+                                               <enum name="CallForwardingBusy" />
+                                               <enum name="CallForwardingNoReply" />
+                                               <enum name="CallDeflection" />
+                                               <enum name="CallTransfer" />
+                                               <enum name="NotAvailable" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="TotalType">
+                                       <enumlist>
+                                               <enum name="SubTotal" />
+                                               <enum name="Total" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Currency" />
+                               <parameter name="Name" />
+                               <parameter name="Cost" />
+                               <parameter name="Multiplier">
+                                       <enumlist>
+                                               <enum name="1/1000" />
+                                               <enum name="1/100" />
+                                               <enum name="1/10" />
+                                               <enum name="1" />
+                                               <enum name="10" />
+                                               <enum name="100" />
+                                               <enum name="1000" />
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Units" />
+                               <parameter name="NumberOf" />
+                               <parameter name="TypeOf" />
+                       </syntax>
+                       <see-also>
+                               <ref type="manager">AOCMessage</ref>
+                               <ref type="managerEvent">AOC-S</ref>
+                               <ref type="managerEvent">AOC-E</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="AOC-E">
+               <managerEventInstance class="EVENT_FLAG_AOC">
+                       <synopsis>Raised when an Advice of Charge message is sent at the end of a call.</synopsis>
+                       <syntax>
+                               <channel_snapshot/>
+                               <parameter name="ChargingAssociation" />
+                               <parameter name="Number" />
+                               <parameter name="Plan" />
+                               <parameter name="ID" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='AOC-D']/managerEventInstance/syntax/parameter)" />
+                       </syntax>
+                       <see-also>
+                               <ref type="manager">AOCMessage</ref>
+                               <ref type="managerEvent">AOC-S</ref>
+                               <ref type="managerEvent">AOC-D</ref>
+                       </see-also>
+               </managerEventInstance>
+       </managerEvent>
+***/
 
 /* Encoded Payload Flags */
 #define AST_AOC_ENCODED_TYPE_REQUEST    (0 << 0)
@@ -1289,13 +1420,8 @@ static void aoc_amount_str(struct ast_str **msg, const char *prefix, unsigned in
                aoc_multiplier_str(mult));
 }
 
-static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
+static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
 {
-       if (chan) {
-               ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
-               ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(chan));
-       }
-
        if (decoded->request_flag) {
                ast_str_append(msg, 0, "AOCRequest:");
                if (decoded->request_flag & AST_AOC_REQUEST_S) {
@@ -1314,17 +1440,12 @@ static void aoc_request_event(const struct ast_aoc_decoded *decoded, struct ast_
        }
 }
 
-static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_channel *owner, struct ast_str **msg)
+static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
 {
        const char *rate_str;
        char prefix[32];
        int idx;
 
-       if (owner) {
-               ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(owner));
-               ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(owner));
-       }
-
        ast_str_append(msg, 0, "NumberRates: %d\r\n", decoded->aoc_s_count);
        for (idx = 0; idx < decoded->aoc_s_count; ++idx) {
                snprintf(prefix, sizeof(prefix), "Rate(%d)", idx);
@@ -1387,17 +1508,12 @@ static void aoc_s_event(const struct ast_aoc_decoded *decoded, struct ast_channe
        }
 }
 
-static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
+static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
 {
        const char *charge_str;
        int idx;
        char prefix[32];
 
-       if (chan) {
-               ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
-               ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(chan));
-       }
-
        charge_str = aoc_charge_type_str(decoded->charge_type);
        ast_str_append(msg, 0, "Type: %s\r\n", charge_str);
 
@@ -1431,7 +1547,7 @@ static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_channe
                                        decoded->unit_list[idx].amount);
                        }
                        if (decoded->unit_list[idx].valid_type) {
-                               ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
+                               ast_str_append(msg, 0, "%s/TypeOf: %u\r\n", prefix,
                                        decoded->unit_list[idx].type);
                        }
                }
@@ -1441,17 +1557,12 @@ static void aoc_d_event(const struct ast_aoc_decoded *decoded, struct ast_channe
        }
 }
 
-static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan, struct ast_str **msg)
+static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_str **msg)
 {
        const char *charge_str;
        int idx;
        char prefix[32];
 
-       if (chan) {
-               ast_str_append(msg, 0, "Channel: %s\r\n", ast_channel_name(chan));
-               ast_str_append(msg, 0, "UniqueID: %s\r\n", ast_channel_uniqueid(chan));
-       }
-
        charge_str = "ChargingAssociation";
 
        switch (decoded->charging_association.charging_type) {
@@ -1499,7 +1610,7 @@ static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_channe
                                        decoded->unit_list[idx].amount);
                        }
                        if (decoded->unit_list[idx].valid_type) {
-                               ast_str_append(msg, 0, "%s/TypeOf: %d\r\n", prefix,
+                               ast_str_append(msg, 0, "%s/TypeOf: %u\r\n", prefix,
                                        decoded->unit_list[idx].type);
                        }
                }
@@ -1509,41 +1620,338 @@ static void aoc_e_event(const struct ast_aoc_decoded *decoded, struct ast_channe
        }
 }
 
+static struct ast_json *units_to_json(const struct ast_aoc_decoded *decoded)
+{
+       int i;
+       struct ast_json *units = ast_json_array_create();
+
+       if (!units) {
+               return ast_json_null();
+       }
+
+       for (i = 0; i < decoded->unit_count; ++i) {
+               struct ast_json *unit = ast_json_object_create();
+
+               if (decoded->unit_list[i].valid_amount) {
+                       ast_json_object_set(
+                               unit, "NumberOf", ast_json_stringf(
+                                       "%u", decoded->unit_list[i].amount));
+                       }
+
+               if (decoded->unit_list[i].valid_type) {
+                       ast_json_object_set(
+                               unit, "TypeOf", ast_json_stringf(
+                                       "%u", decoded->unit_list[i].type));
+               }
+
+               if (ast_json_array_append(units, unit)) {
+                       break;
+               }
+       }
+
+       return units;
+}
+
+static struct ast_json *currency_to_json(const char *name, int cost,
+                                        enum ast_aoc_currency_multiplier mult)
+{
+       return ast_json_pack("{s:s, s:i, s:s}",
+               "Name", AST_JSON_UTF8_VALIDATE(name),
+               "Cost", cost,
+               "Multiplier", aoc_multiplier_str(mult));
+}
+
+static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded)
+{
+       struct ast_json *obj;
+       const char *obj_type;
+
+       if (decoded->charge_type != AST_AOC_CHARGE_CURRENCY &&
+           decoded->charge_type != AST_AOC_CHARGE_UNIT) {
+               return ast_json_pack("{s:s}",
+                       "Type", aoc_charge_type_str(decoded->charge_type));
+       }
+
+       if (decoded->charge_type == AST_AOC_CHARGE_CURRENCY) {
+               obj_type = "Currency";
+               obj = currency_to_json(decoded->currency_name, decoded->currency_amount,
+                                      decoded->multiplier);
+       } else { /* decoded->charge_type == AST_AOC_CHARGE_UNIT */
+               obj_type = "Units";
+               obj = units_to_json(decoded);
+       }
+
+       return ast_json_pack("{s:s, s:s, s:s, s:o}",
+               "Type", aoc_charge_type_str(decoded->charge_type),
+               "BillingID", aoc_billingid_str(decoded->billing_id),
+               "TotalType", aoc_type_of_totaling_str(decoded->total_type),
+               obj_type, obj);
+}
+
+static struct ast_json *association_to_json(const struct ast_aoc_decoded *decoded)
+{
+       switch (decoded->charging_association.charging_type) {
+       case AST_AOC_CHARGING_ASSOCIATION_NUMBER:
+               return ast_json_pack("{s:s, s:i}",
+                       "Number", AST_JSON_UTF8_VALIDATE(decoded->charging_association.charge.number.number),
+                       "Plan", decoded->charging_association.charge.number.plan);
+       case AST_AOC_CHARGING_ASSOCIATION_ID:
+               return ast_json_pack("{s:i}", "ID", decoded->charging_association.charge.id);
+       case AST_AOC_CHARGING_ASSOCIATION_NA:
+       default:
+               return ast_json_null();
+       }
+}
+
+static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded)
+{
+       int i;
+       struct ast_json *rates = ast_json_array_create();
+
+       if (!rates) {
+               return ast_json_null();
+       }
+
+       for (i = 0; i < decoded->aoc_s_count; ++i) {
+               struct ast_json *rate;
+               struct ast_json *type = NULL;
+               struct ast_json *currency;
+               const char *charge_item = aoc_charged_item_str(
+                       decoded->aoc_s_entries[i].charged_item);
+
+               if (decoded->aoc_s_entries[i].charged_item == AST_AOC_CHARGED_ITEM_NA) {
+                       rate = ast_json_pack("{s:s}", "Chargeable", charge_item);
+                       if (ast_json_array_append(rates, rate)) {
+                               break;
+                       }
+                       continue;
+               }
+
+               switch (decoded->aoc_s_entries[i].rate_type) {
+               case AST_AOC_RATE_TYPE_DURATION:
+               {
+                       struct ast_json *time;
+                       struct ast_json *granularity = NULL;
+
+                       currency = currency_to_json(
+                               decoded->aoc_s_entries[i].rate.duration.currency_name,
+                               decoded->aoc_s_entries[i].rate.duration.amount,
+                               decoded->aoc_s_entries[i].rate.duration.multiplier);
+
+                       time = ast_json_pack("{s:i, s:i}",
+                               "Length", decoded->aoc_s_entries[i].rate.duration.time,
+                               "Scale", decoded->aoc_s_entries[i].rate.duration.time_scale);
+
+                       if (decoded->aoc_s_entries[i].rate.duration.granularity_time) {
+                               granularity = ast_json_pack("{s:i, s:i}",
+                                       "Length", decoded->aoc_s_entries[i].rate.duration.granularity_time,
+                                       "Scale", decoded->aoc_s_entries[i].rate.duration.granularity_time_scale);
+                       }
+
+                       type = ast_json_pack("{s:o, s:s, s:o, s:o}",
+                               "Currency", currency,
+                               "ChargingType", decoded->aoc_s_entries[i].rate.duration.charging_type
+                                       ? "StepFunction" : "ContinuousCharging",
+                               "Time", time,
+                               "Granularity", granularity ?: ast_json_null());
+
+                       break;
+               }
+               case AST_AOC_RATE_TYPE_FLAT:
+                       currency = currency_to_json(
+                               decoded->aoc_s_entries[i].rate.flat.currency_name,
+                               decoded->aoc_s_entries[i].rate.flat.amount,
+                               decoded->aoc_s_entries[i].rate.flat.multiplier);
+
+                       type = ast_json_pack("{s:o}", "Currency", currency);
+                       break;
+               case AST_AOC_RATE_TYPE_VOLUME:
+                       currency = currency_to_json(
+                               decoded->aoc_s_entries[i].rate.volume.currency_name,
+                               decoded->aoc_s_entries[i].rate.volume.amount,
+                               decoded->aoc_s_entries[i].rate.volume.multiplier);
+
+                       type = ast_json_pack("{s:s, s:o}",
+                               "Unit", aoc_volume_unit_str(
+                                       decoded->aoc_s_entries[i].rate.volume.volume_unit),
+                               "Currency", currency);
+                       break;
+               case AST_AOC_RATE_TYPE_SPECIAL_CODE:
+                       type = ast_json_pack("{s:i}",
+                               "SpecialCode", decoded->aoc_s_entries[i].rate.special_code);
+                       break;
+               default:
+                       break;
+               }
+
+               rate = ast_json_pack("{s:s, s:o}",
+                       "Chargeable", charge_item,
+                       aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), type);
+               if (ast_json_array_append(rates, rate)) {
+                       break;
+               }
+       }
+       return rates;
+}
+
+static struct ast_json *d_to_json(const struct ast_aoc_decoded *decoded)
+{
+       return ast_json_pack("{s:o}", "Charge", charge_to_json(decoded));
+}
+
+static struct ast_json *e_to_json(const struct ast_aoc_decoded *decoded)
+{
+       return ast_json_pack("{s:o, s:o}",
+                            "ChargingAssociation", association_to_json(decoded),
+                            "Charge", charge_to_json(decoded));
+}
+
+struct aoc_event_blob {
+       /*! Channel AOC event is associated with (NULL for unassociated) */
+       struct ast_channel_snapshot *snapshot;
+       /*! AOC JSON blob of data */
+       struct ast_json *blob;
+};
+
+static void aoc_event_blob_dtor(void *obj)
+{
+       struct aoc_event_blob *aoc_event = obj;
+
+       ao2_cleanup(aoc_event->snapshot);
+       ast_json_unref(aoc_event->blob);
+}
+
+/*!
+ * \internal
+ * \brief Publish an AOC event.
+ * \since 13.3.0
+ *
+ * \param chan Channel associated with the AOC event. (May be NULL if no channel)
+ * \param msg_type What kind of AOC event.
+ * \param blob AOC data blob to publish.
+ *
+ * \return Nothing
+ */
+static void aoc_publish_blob(struct ast_channel *chan, struct stasis_message_type *msg_type, struct ast_json *blob)
+{
+       struct stasis_message *msg;
+       struct aoc_event_blob *aoc_event;
+
+       if (!blob || ast_json_is_null(blob)) {
+               /* No AOC blob information?  Nothing to send an event about. */
+               return;
+       }
+
+       aoc_event = ao2_alloc_options(sizeof(*aoc_event), aoc_event_blob_dtor,
+               AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!aoc_event) {
+               return;
+       }
+
+       if (chan) {
+               aoc_event->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
+               if (!aoc_event->snapshot) {
+                       ao2_ref(aoc_event, -1);
+                       return;
+               }
+       }
+       aoc_event->blob = ast_json_ref(blob);
+
+       msg = stasis_message_create(msg_type, aoc_event);
+       ao2_ref(aoc_event, -1);
+
+       if (msg) {
+               stasis_publish(ast_manager_get_topic(), msg);
+               ao2_ref(msg, -1);
+       }
+}
+
+static struct ast_manager_event_blob *aoc_to_ami(struct stasis_message *message,
+                                                const char *event_name)
+{
+       struct aoc_event_blob *aoc_event = stasis_message_data(message);
+       struct ast_str *channel = NULL;
+       struct ast_str *aoc;
+       struct ast_manager_event_blob *ev = NULL;
+
+       if (aoc_event->snapshot) {
+               channel = ast_manager_build_channel_state_string(aoc_event->snapshot);
+               if (!channel) {
+                       return NULL;
+               }
+       }
+
+       aoc = ast_manager_str_from_json_object(aoc_event->blob, NULL);
+       if (aoc && !ast_strlen_zero(ast_str_buffer(aoc))) {
+               ev = ast_manager_event_blob_create(EVENT_FLAG_AOC, event_name, "%s%s",
+                       AS_OR(channel, ""), ast_str_buffer(aoc));
+       }
+
+       ast_free(aoc);
+       ast_free(channel);
+       return ev;
+}
+
+static struct ast_manager_event_blob *aoc_s_to_ami(struct stasis_message *message)
+{
+       return aoc_to_ami(message, "AOC-S");
+}
+
+static struct ast_manager_event_blob *aoc_d_to_ami(struct stasis_message *message)
+{
+       return aoc_to_ami(message, "AOC-D");
+}
+
+static struct ast_manager_event_blob *aoc_e_to_ami(struct stasis_message *message)
+{
+       return aoc_to_ami(message, "AOC-E");
+}
+
+struct stasis_message_type *aoc_s_type(void);
+struct stasis_message_type *aoc_d_type(void);
+struct stasis_message_type *aoc_e_type(void);
+
+STASIS_MESSAGE_TYPE_DEFN(
+       aoc_s_type,
+       .to_ami = aoc_s_to_ami);
+
+STASIS_MESSAGE_TYPE_DEFN(
+       aoc_d_type,
+       .to_ami = aoc_d_to_ami);
+
+STASIS_MESSAGE_TYPE_DEFN(
+       aoc_e_type,
+       .to_ami = aoc_e_to_ami);
+
 int ast_aoc_manager_event(const struct ast_aoc_decoded *decoded, struct ast_channel *chan)
 {
-       struct ast_str *msg;
+       struct ast_json *blob;
+       struct stasis_message_type *msg_type;
 
-       if (!decoded || !(msg = ast_str_create(1024))) {
+       if (!decoded) {
                return -1;
        }
 
        switch (decoded->msg_type) {
        case AST_AOC_S:
-               if (chan) {
-                       aoc_s_event(decoded, chan, &msg);
-                       ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-S", "%s", ast_str_buffer(msg));
-               }
+               blob = s_to_json(decoded);
+               msg_type = aoc_s_type();
                break;
        case AST_AOC_D:
-               if (chan) {
-                       aoc_d_event(decoded, chan, &msg);
-                       ast_manager_event(chan, EVENT_FLAG_AOC, "AOC-D", "%s", ast_str_buffer(msg));
-               }
+               blob = d_to_json(decoded);
+               msg_type = aoc_d_type();
                break;
        case AST_AOC_E:
-               {
-                       struct ast_channel *chans[1];
-                       aoc_e_event(decoded, chan, &msg);
-                       chans[0] = chan;
-                       ast_manager_event_multichan(EVENT_FLAG_AOC, "AOC-E", chan ? 1 : 0, chans, "%s", ast_str_buffer(msg));
-               }
+               blob = e_to_json(decoded);
+               msg_type = aoc_e_type();
                break;
        default:
                /* events for AST_AOC_REQUEST are not generated here */
-               break;
+               return 0;
        }
 
-       ast_free(msg);
+       aoc_publish_blob(chan, msg_type, blob);
+       ast_json_unref(blob);
        return 0;
 }
 
@@ -1556,19 +1964,19 @@ int ast_aoc_decoded2str(const struct ast_aoc_decoded *decoded, struct ast_str **
        switch (decoded->msg_type) {
        case AST_AOC_S:
                ast_str_append(msg, 0, "AOC-S\r\n");
-               aoc_s_event(decoded, NULL, msg);
+               aoc_s_event(decoded, msg);
                break;
        case AST_AOC_D:
                ast_str_append(msg, 0, "AOC-D\r\n");
-               aoc_d_event(decoded, NULL, msg);
+               aoc_d_event(decoded, msg);
                break;
        case AST_AOC_E:
                ast_str_append(msg, 0, "AOC-E\r\n");
-               aoc_e_event(decoded, NULL, msg);
+               aoc_e_event(decoded, msg);
                break;
        case AST_AOC_REQUEST:
                ast_str_append(msg, 0, "AOC-Request\r\n");
-               aoc_request_event(decoded, NULL, msg);
+               aoc_request_event(decoded, msg);
                break;
        }
 
@@ -1607,10 +2015,18 @@ static struct ast_cli_entry aoc_cli[] = {
 
 static void aoc_shutdown(void)
 {
+       STASIS_MESSAGE_TYPE_CLEANUP(aoc_s_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(aoc_d_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(aoc_e_type);
+
        ast_cli_unregister_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
 }
 int ast_aoc_cli_init(void)
 {
-       ast_register_atexit(aoc_shutdown);
+       STASIS_MESSAGE_TYPE_INIT(aoc_s_type);
+       STASIS_MESSAGE_TYPE_INIT(aoc_d_type);
+       STASIS_MESSAGE_TYPE_INIT(aoc_e_type);
+
+       ast_register_cleanup(aoc_shutdown);
        return ast_cli_register_multiple(aoc_cli, ARRAY_LEN(aoc_cli));
 }