pjsip_options: wrongly applied "UNKNOWN" status
[asterisk/asterisk.git] / main / stasis_endpoints.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Stasis endpoint API.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 #include "asterisk/astobj2.h"
33 #include "asterisk/stasis.h"
34 #include "asterisk/stasis_endpoints.h"
35
36 /*** DOCUMENTATION
37         <managerEvent language="en_US" name="PeerStatus">
38                 <managerEventInstance class="EVENT_FLAG_SYSTEM">
39                         <synopsis>Raised when the state of a peer changes.</synopsis>
40                         <syntax>
41                                 <parameter name="ChannelType">
42                                         <para>The channel technology of the peer.</para>
43                                 </parameter>
44                                 <parameter name="Peer">
45                                         <para>The name of the peer (including channel technology).</para>
46                                 </parameter>
47                                 <parameter name="PeerStatus">
48                                         <para>New status of the peer.</para>
49                                         <enumlist>
50                                                 <enum name="Unknown"/>
51                                                 <enum name="Registered"/>
52                                                 <enum name="Unregistered"/>
53                                                 <enum name="Rejected"/>
54                                                 <enum name="Lagged"/>
55                                         </enumlist>
56                                 </parameter>
57                                 <parameter name="Cause">
58                                         <para>The reason the status has changed.</para>
59                                 </parameter>
60                                 <parameter name="Address">
61                                         <para>New address of the peer.</para>
62                                 </parameter>
63                                 <parameter name="Port">
64                                         <para>New port for the peer.</para>
65                                 </parameter>
66                                 <parameter name="Time">
67                                         <para>Time it takes to reach the peer and receive a response.</para>
68                                 </parameter>
69                         </syntax>
70                 </managerEventInstance>
71         </managerEvent>
72         <managerEvent language="en_US" name="ContactStatus">
73                 <managerEventInstance class="EVENT_FLAG_SYSTEM">
74                         <synopsis>Raised when the state of a contact changes.</synopsis>
75                         <syntax>
76                                 <parameter name="URI">
77                                         <para>This contact's URI.</para>
78                                 </parameter>
79                                 <parameter name="ContactStatus">
80                                         <para>New status of the contact.</para>
81                                         <enumlist>
82                                                 <enum name="Unknown"/>
83                                                 <enum name="Unreachable"/>
84                                                 <enum name="Reachable"/>
85                                                 <enum name="Unqualified"/>
86                                                 <enum name="Removed"/>
87                                                 <enum name="Updated"/>
88                                         </enumlist>
89                                 </parameter>
90                                 <parameter name="AOR">
91                                         <para>The name of the associated aor.</para>
92                                 </parameter>
93                                 <parameter name="EndpointName">
94                                         <para>The name of the associated endpoint.</para>
95                                 </parameter>
96                                 <parameter name="RoundtripUsec">
97                                         <para>The RTT measured during the last qualify.</para>
98                                 </parameter>
99                                 <parameter name="UserAgent">
100                                         <para>Content of the User-Agent header in REGISTER request</para>
101                                 </parameter>
102                                 <parameter name="RegExpire">
103                                         <para>Absolute time that this contact is no longer valid after</para>
104                                 </parameter>
105                                 <parameter name="ViaAddress">
106                                         <para>IP address:port of the last Via header in REGISTER request</para>
107                                 </parameter>
108                                 <parameter name="CallID">
109                                         <para>Content of the Call-ID header in REGISTER request</para>
110                                 </parameter>
111                         </syntax>
112                 </managerEventInstance>
113         </managerEvent>
114 ***/
115
116 static struct stasis_cp_all *endpoint_cache_all;
117
118 struct stasis_cp_all *ast_endpoint_cache_all(void)
119 {
120         return endpoint_cache_all;
121 }
122
123 struct stasis_cache *ast_endpoint_cache(void)
124 {
125         return stasis_cp_all_cache(endpoint_cache_all);
126 }
127
128 struct stasis_topic *ast_endpoint_topic_all(void)
129 {
130         return stasis_cp_all_topic(endpoint_cache_all);
131 }
132
133 struct stasis_topic *ast_endpoint_topic_all_cached(void)
134 {
135         return stasis_cp_all_topic_cached(endpoint_cache_all);
136 }
137
138 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_snapshot_type);
139
140 static struct ast_manager_event_blob *peerstatus_to_ami(struct stasis_message *msg)
141 {
142         struct ast_endpoint_blob *obj = stasis_message_data(msg);
143         RAII_VAR(struct ast_str *, peerstatus_event_string, ast_str_create(64), ast_free);
144         const char *value;
145
146         /* peer_status is the only *required* thing */
147         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "peer_status")))) {
148                 return NULL;
149         }
150         ast_str_append(&peerstatus_event_string, 0, "PeerStatus: %s\r\n", value);
151
152         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "cause")))) {
153                 ast_str_append(&peerstatus_event_string, 0, "Cause: %s\r\n", value);
154         }
155         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "address")))) {
156                 ast_str_append(&peerstatus_event_string, 0, "Address: %s\r\n", value);
157         }
158         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "port")))) {
159                 ast_str_append(&peerstatus_event_string, 0, "Port: %s\r\n", value);
160         }
161         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "time")))) {
162                 ast_str_append(&peerstatus_event_string, 0, "Time: %s\r\n", value);
163         }
164
165         return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "PeerStatus",
166                 "ChannelType: %s\r\n"
167                 "Peer: %s/%s\r\n"
168                 "%s",
169                 obj->snapshot->tech,
170                 obj->snapshot->tech,
171                 obj->snapshot->resource,
172                 ast_str_buffer(peerstatus_event_string));
173 }
174
175 static struct ast_json *peerstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
176 {
177         struct ast_endpoint_blob *obj = stasis_message_data(msg);
178         struct ast_json *json_endpoint;
179         struct ast_json *json_peer;
180         struct ast_json *json_final;
181         const struct timeval *tv = stasis_message_timestamp(msg);
182
183         json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
184         if (!json_endpoint) {
185                 return NULL;
186         }
187
188         json_peer = ast_json_object_create();
189         if (!json_peer) {
190                 ast_json_unref(json_endpoint);
191                 return NULL;
192         }
193
194         /* Copy all fields from the blob */
195         ast_json_object_update(json_peer, obj->blob);
196
197         json_final = ast_json_pack("{s: s, s: o, s: o, s: o }",
198                 "type", "PeerStatusChange",
199                 "timestamp", ast_json_timeval(*tv, NULL),
200                 "endpoint", json_endpoint,
201                 "peer", json_peer);
202         if (!json_final) {
203                 ast_json_unref(json_endpoint);
204                 ast_json_unref(json_peer);
205         }
206
207         return json_final;
208 }
209
210 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_state_type,
211         .to_ami = peerstatus_to_ami,
212         .to_json = peerstatus_to_json,
213 );
214
215 static struct ast_manager_event_blob *contactstatus_to_ami(struct stasis_message *msg)
216 {
217         struct ast_endpoint_blob *obj = stasis_message_data(msg);
218         RAII_VAR(struct ast_str *, contactstatus_event_string, ast_str_create(64), ast_free);
219         const char *value;
220
221         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "uri")))) {
222                 return NULL;
223         }
224         ast_str_append(&contactstatus_event_string, 0, "URI: %s\r\n", value);
225
226         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "contact_status")))) {
227                 return NULL;
228         }
229         ast_str_append(&contactstatus_event_string, 0, "ContactStatus: %s\r\n", value);
230
231         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "aor")))) {
232                 return NULL;
233         }
234         ast_str_append(&contactstatus_event_string, 0, "AOR: %s\r\n", value);
235
236         if (!(value = ast_json_string_get(ast_json_object_get(obj->blob, "endpoint_name")))) {
237                 return NULL;
238         }
239         ast_str_append(&contactstatus_event_string, 0, "EndpointName: %s\r\n", value);
240
241         if ((value = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec")))) {
242                 ast_str_append(&contactstatus_event_string, 0, "RoundtripUsec: %s\r\n", value);
243         }
244
245         return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "ContactStatus",
246                 "%s", ast_str_buffer(contactstatus_event_string));
247 }
248
249 static struct ast_json *contactstatus_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize)
250 {
251         struct ast_endpoint_blob *obj = stasis_message_data(msg);
252         struct ast_json *json_endpoint;
253         struct ast_json *json_final;
254         const char *rtt;
255         const struct timeval *tv = stasis_message_timestamp(msg);
256
257         json_endpoint = ast_endpoint_snapshot_to_json(obj->snapshot, NULL);
258         if (!json_endpoint) {
259                 return NULL;
260         }
261
262         /* The roundtrip time is optional. */
263         rtt = ast_json_string_get(ast_json_object_get(obj->blob, "roundtrip_usec"));
264         if (!ast_strlen_zero(rtt)) {
265                 json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s, s: s } } ",
266                         "type", "ContactStatusChange",
267                         "timestamp", ast_json_timeval(*tv, NULL),
268                         "endpoint", json_endpoint,
269                         "contact_info",
270                         "uri", ast_json_string_get(ast_json_object_get(obj->blob, "uri")),
271                         "contact_status", ast_json_string_get(ast_json_object_get(obj->blob,
272                                 "contact_status")),
273                         "aor", ast_json_string_get(ast_json_object_get(obj->blob, "aor")),
274                         "roundtrip_usec", rtt);
275         } else {
276                 json_final = ast_json_pack("{s: s, s: o, s: o, s: { s: s, s: s, s: s } } ",
277                         "type", "ContactStatusChange",
278                         "timestamp", ast_json_timeval(*tv, NULL),
279                         "endpoint", json_endpoint,
280                         "contact_info",
281                         "uri", ast_json_string_get(ast_json_object_get(obj->blob, "uri")),
282                         "contact_status", ast_json_string_get(ast_json_object_get(obj->blob,
283                                 "contact_status")),
284                         "aor", ast_json_string_get(ast_json_object_get(obj->blob, "aor")));
285         }
286         if (!json_final) {
287                 ast_json_unref(json_endpoint);
288         }
289
290         return json_final;
291 }
292
293 STASIS_MESSAGE_TYPE_DEFN(ast_endpoint_contact_state_type,
294         .to_ami = contactstatus_to_ami,
295         .to_json = contactstatus_to_json
296 );
297
298 static void endpoint_blob_dtor(void *obj)
299 {
300         struct ast_endpoint_blob *event = obj;
301         ao2_cleanup(event->snapshot);
302         ast_json_unref(event->blob);
303 }
304
305 struct stasis_message *ast_endpoint_blob_create(struct ast_endpoint *endpoint,
306         struct stasis_message_type *type, struct ast_json *blob)
307 {
308         RAII_VAR(struct ast_endpoint_blob *, obj, NULL, ao2_cleanup);
309         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
310
311         if (!type) {
312                 return NULL;
313         }
314         if (!blob) {
315                 blob = ast_json_null();
316         }
317
318         if (!(obj = ao2_alloc(sizeof(*obj), endpoint_blob_dtor))) {
319                 return NULL;
320         }
321
322         if (endpoint) {
323                 if (!(obj->snapshot = ast_endpoint_snapshot_create(endpoint))) {
324                         return NULL;
325                 }
326         }
327
328         obj->blob = ast_json_ref(blob);
329
330         if (!(msg = stasis_message_create(type, obj))) {
331                 return NULL;
332         }
333
334         ao2_ref(msg, +1);
335         return msg;
336 }
337
338 void ast_endpoint_blob_publish(struct ast_endpoint *endpoint, struct stasis_message_type *type,
339         struct ast_json *blob)
340 {
341         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
342         if (blob) {
343                 message = ast_endpoint_blob_create(endpoint, type, blob);
344         }
345         if (message) {
346                 stasis_publish(ast_endpoint_topic(endpoint), message);
347         }
348 }
349
350 struct ast_endpoint_snapshot *ast_endpoint_latest_snapshot(const char *tech,
351         const char *name)
352 {
353         RAII_VAR(char *, id, NULL, ast_free);
354         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
355         struct ast_endpoint_snapshot *snapshot;
356
357         if (ast_strlen_zero(name)) {
358                 ast_asprintf(&id, "%s", tech);
359         } else {
360                 ast_asprintf(&id, "%s/%s", tech, name);
361         }
362         if (!id) {
363                 return NULL;
364         }
365         ast_tech_to_upper(id);
366
367         msg = stasis_cache_get(ast_endpoint_cache(),
368                 ast_endpoint_snapshot_type(), id);
369         if (!msg) {
370                 return NULL;
371         }
372
373         snapshot = stasis_message_data(msg);
374         ast_assert(snapshot != NULL);
375
376         ao2_ref(snapshot, +1);
377         return snapshot;
378 }
379
380 /*!
381  * \brief Callback extract a unique identity from a snapshot message.
382  *
383  * This identity is unique to the underlying object of the snapshot, such as the
384  * UniqueId field of a channel.
385  *
386  * \param message Message to extract id from.
387  * \return String representing the snapshot's id.
388  * \return \c NULL if the message_type of the message isn't a handled snapshot.
389  * \since 12
390  */
391 static const char *endpoint_snapshot_get_id(struct stasis_message *message)
392 {
393         struct ast_endpoint_snapshot *snapshot;
394
395         if (ast_endpoint_snapshot_type() != stasis_message_type(message)) {
396                 return NULL;
397         }
398
399         snapshot = stasis_message_data(message);
400
401         return snapshot->id;
402 }
403
404
405 struct ast_json *ast_endpoint_snapshot_to_json(
406         const struct ast_endpoint_snapshot *snapshot,
407         const struct stasis_message_sanitizer *sanitize)
408 {
409         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
410         struct ast_json *channel_array;
411         int i;
412
413         json = ast_json_pack("{s: s, s: s, s: s, s: []}",
414                 "technology", snapshot->tech,
415                 "resource", snapshot->resource,
416                 "state", ast_endpoint_state_to_string(snapshot->state),
417                 "channel_ids");
418
419         if (json == NULL) {
420                 return NULL;
421         }
422
423         if (snapshot->max_channels != -1) {
424                 int res = ast_json_object_set(json, "max_channels",
425                         ast_json_integer_create(snapshot->max_channels));
426                 if (res != 0) {
427                         return NULL;
428                 }
429         }
430
431         channel_array = ast_json_object_get(json, "channel_ids");
432         ast_assert(channel_array != NULL);
433         for (i = 0; i < snapshot->num_channels; ++i) {
434                 int res;
435
436                 if (sanitize && sanitize->channel_id
437                         && sanitize->channel_id(snapshot->channel_ids[i])) {
438                         continue;
439                 }
440
441                 res = ast_json_array_append(channel_array,
442                         ast_json_string_create(snapshot->channel_ids[i]));
443                 if (res != 0) {
444                         return NULL;
445                 }
446         }
447
448         return ast_json_ref(json);
449 }
450
451 static void endpoints_stasis_cleanup(void)
452 {
453         STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_snapshot_type);
454         STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_state_type);
455         STASIS_MESSAGE_TYPE_CLEANUP(ast_endpoint_contact_state_type);
456
457         ao2_cleanup(endpoint_cache_all);
458         endpoint_cache_all = NULL;
459 }
460
461 int ast_endpoint_stasis_init(void)
462 {
463         int res = 0;
464         ast_register_cleanup(endpoints_stasis_cleanup);
465
466         endpoint_cache_all = stasis_cp_all_create("endpoint_topic_all",
467                 endpoint_snapshot_get_id);
468         if (!endpoint_cache_all) {
469                 return -1;
470         }
471
472         res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_snapshot_type);
473         res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_state_type);
474         res |= STASIS_MESSAGE_TYPE_INIT(ast_endpoint_contact_state_type);
475
476         return res;
477 }