Asterisk - The Open Source Telephony Project  18.5.0
stasis_state.h
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2019, Sangoma Technologies Corporation
5  *
6  * Kevin Harwell <[email protected]>
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 #ifndef _STASIS_STATE_H
20 #define _STASIS_STATE_H
21 
22 /*! \file
23  *
24  * \brief Stasis State API.
25  *
26  * \par Intro
27  *
28  * This module defines the data structures, and handling of "state" for topics within
29  * stasis. State is defined as the last stasis message, and its contained message data,
30  * published on a given topic.
31  *
32  * Concepts to know:
33  * - \ref stasis_state_manager
34  * - \ref stasis_state_subscriber
35  * - \ref stasis_state_publisher
36  * - \ref stasis_state_observer
37  *
38  * \par stasis_state_manager
39  *
40  * The manager stores and well, manages state data. Each state is an association of
41  * a unique stasis topic, and the last known published stasis message on that topic.
42  * There is only ever one managed state object per topic. For each topic all messages
43  * are forwarded to an "all" topic also maintained by the manager. This allows
44  * subscriptions to all managed topics, and their state. Managed state is created in
45  * one of several ways:
46  *
47  * Adding an explicit subscriber
48  * Adding an explicit publisher
49  * Adding an implicit publisher
50  * Retrieving a stasis state topic from the manager via the \ref stasis_state_topic
51  * function prior to doing one of the above (DO NOT DO THIS).
52  *
53  * More on the first three options later (see relevant section descriptions below). The
54  * last option, creation through retrieving a topic is not only NOT recommended, but
55  * should NOT even BE DONE. Doing so will inevitably result in a memory leak. Why then
56  * is this even allowed? The short answer is backwards compatibility. The slightly longer
57  * answer is at the time of this module's creation that's how things were historically
58  * done using a combination of stasis topic management spread throughout various other
59  * modules, and stasis caching. And yes it did cause a memory leak.
60  *
61  * Preferably, any new code wishing to track topics and states should do so by adding
62  * either an explicit subscriber and/or publisher.
63  *
64  * \par stasis_state_subscriber
65  *
66  * As mentioned, topic and state can be created, or referenced within the manager by adding
67  * a \ref stasis_state_subscriber. When adding a subscriber if no state currently exists
68  * new managed state is immediately created. If managed state already exists then a new
69  * subscriber is created referencing that state. The managed state is guaranteed to live
70  * throughout the subscriber's lifetime. State is only removed from the manager when no
71  * other entities require it (no more subscribers, or publishers).
72  *
73  * Subscribers are ao2 objects. Therefore there is no explicit cleanup required aside from
74  * dereferencing the subscriber object using normal ao2 dereferencing methods.
75  *
76  * \par stasis_state_publisher
77  *
78  * There are two ways of tracking publishers: explicitly and implicitly.
79  *
80  * Topic and state can be created, or referenced within the manager by also explicitly adding
81  * a \ref stasis_state_publisher. When adding a publisher if no state currently exists new
82  * managed state is created. If managed state already exists then a new publisher is created
83  * referencing that state. The managed state is guaranteed to live throughout the publisher's
84  * lifetime. State is only removed from the manager when no other entities require it (no more
85  * publishers, or subscribers).
86  *
87  * Explicit publishers are ao2 objects. Therefore there is no cleanup required aside from
88  * dereferencing the publisher object using normal ao2 dereferencing methods.
89  *
90  * When adding an explicit publisher, messages should be published using the \ref
91  * stasis_state_publish function. This not only skips a lookup, but doesn't add an implicit
92  * publisher. They are not necessarily mutually exclusive it's just that the two ways exist
93  * to solve two different problems.
94  *
95  * For example (using an explicit publisher):
96  *
97  * // Add an explicit publisher to topic/state "8675309" within
98  * // a given manager context
99  * pub = stasis_state_add_publisher(manager, "8675309");
100  *
101  * // Publish a stasis message to the topic/state
102  * stasis_state_publish(pub, msg);
103  *
104  * // Publish another a stasis message to the topic/state
105  * stasis_state_publish(pub, msg);
106  *
107  * // Done with the publisher release the reference
108  * ao2_ref(pub, -1);
109  *
110  * An implicit publisher can also be created by calling \ref stasis_state_publish_by_id. Calling
111  * this function not only publishes the message within stasis (creating managed state if needed)
112  * it also sets up internal tracking of the publishing module using an \ref ast_eid. However, a
113  * final call to \ref stasis_state_remove_publish_by_id must be done in order to remove the eid
114  * reference, which will subsequently allow the underlying managed state to be eventually deleted.
115  *
116  * For example (using an implicit publisher):
117  *
118  * // Publish a stasis message to topic/state 8675309 within a
119  * // given manager context and use the system's default eid
120  * stasis_state_publish_by_id(manager, "8675309", NULL, msg);
121  *
122  * // Do some stuff and then publish again
123  * stasis_state_publish_by_id(manager, "8675309", NULL, msg);
124  *
125  * // Done with all our publishing, so post a final clearing
126  * // message and remove the implicit publisher
127  * stasis_state_remove_publish_by_id(manager, "8675309", NULL, msg);
128  *
129  * Explicit publisher/publishing is preferred. However, implicit publishing is allowed for those
130  * situations where it makes more sense to do so, but has been implemented mostly for backwards
131  * compatibility with some modules (using implicit publishing required less initial code changes
132  * to some legacy subsystems).
133  *
134  * \par stasis_state_observer
135  *
136  * Some modules may wish to watch for, and react to managed state events. By registering a state
137  * observer, and implementing handlers for the desired callbacks those modules can do so.
138  */
139 
140 #include "asterisk/stasis.h"
141 
142 struct ast_eid;
143 
144 /*!
145  * \brief Manages a collection of stasis states.
146  *
147  * Maintains data related to stasis state. Managed state is an association of a unique stasis
148  * topic (named by a given unique id), and the last known published message.
149  *
150  * \since 13.28.0
151  * \since 16.5.0
152  */
153 struct stasis_state_manager;
154 
155 /*!
156  * \brief Create a stasis state manager.
157  *
158  * \note The state manager is an ao2_object. When done simply decrement its reference
159  * for object cleanup.
160  *
161  * \param topic_name The name of the topic to create that all state topics
162  * get forwarded to
163  *
164  * \retval A stasis state manager
165  * \retval NULL if an error occurred
166  *
167  * \since 13.28.0
168  * \since 16.5.0
169  */
170 struct stasis_state_manager *stasis_state_manager_create(const char *topic_name);
171 
172 /*!
173  * \brief Retrieve the manager's topic (the topic that all state topics get forwarded to)
174  *
175  * \param manager The manager object
176  *
177  * \retval The manager's topic.
178  *
179  * \since 13.28.0
180  * \since 16.5.0
181  */
183 
184 /*!
185  * \brief Retrieve a managed topic creating one if not currently managed.
186  *
187  * WARNING This function should not be called before adding a publisher or subscriber or
188  * it will cause a memory leak within the stasis state manager. This function is here in
189  * order to allow for compatibility with how things used to work.
190  *
191  * Also much like the similar functionality from before it returns the stasis topic, but
192  * does not bump its reference.
193  *
194  * \param manager The manager object
195  * \param id The unique id of/for the topic
196  *
197  * \retval A managed stasis topic.
198  * \retval NULL if an error occurred
199  *
200  * \since 13.28.0
201  * \since 16.5.0
202  */
203 struct stasis_topic *stasis_state_topic(struct stasis_state_manager *manager, const char *id);
204 
205 /*!
206  * \brief A stasis state subscriber
207  *
208  * A subscriber to a particular stasis state. As such it holds a reference to the
209  * underlying stasis state, so that managed state is guaranteed to exist for the
210  * lifetime of the subscriber.
211  *
212  * \since 13.28.0
213  * \since 16.5.0
214  */
216 
217 /*!
218  * \brief Add a subscriber to the managed stasis state for the given id
219  *
220  * Adds a subscriber to a managed state based on id. If managed state does not already
221  * exists for the given id then new managed state is created. Otherwise the existing
222  * state is subscribed to.
223  *
224  * \param manager The manager object
225  * \param id The unique id of a managed state
226  *
227  * \retval A stasis state subscriber
228  * \retval NULL if an error occurred
229  *
230  * \since 13.28.0
231  * \since 16.5.0
232  */
234  struct stasis_state_manager *manager, const char *id);
235 
236 /*!
237  * \brief Add a subscriber, and subscribe to its underlying stasis topic.
238  *
239  * Adds a subscriber to a managed state based on id. If managed state does not already
240  * exists for the given id then new managed state is created. Otherwise the existing
241  * state is subscribed to. If the state is successfully subscribed to then a stasis
242  * subscription is subsequently created as well.
243  *
244  * \param manager The manager object
245  * \param id The unique id of a managed state
246  * \param callback The stasis subscription callback
247  * \param data A user data object passed to the stasis subscription
248  *
249  * \retval A stasis state subscriber
250  * \retval NULL if an error occurred
251  *
252  * \since 13.28.0
253  * \since 16.5.0
254  */
256  const char *id, stasis_subscription_cb callback, void *data);
257 
258 /*!
259  * \brief Unsubscribe from the stasis topic and stasis state.
260  *
261  * \param sub A stasis state subscriber
262  *
263  * \retval NULL
264  *
265  * \since 13.28.0
266  * \since 16.5.0
267  */
269 
270 /*!
271  * \brief Unsubscribe from the stasis topic, block until the final message
272  * is received, and then unsubscribe from stasis state.
273  *
274  * \param sub A stasis state subscriber
275  *
276  * \retval NULL
277  *
278  * \since 13.28.0
279  * \since 16.5.0
280  */
282 
283 /*!
284  * \brief Retrieve the underlying subscribed to state's unique id
285  *
286  * \param sub A stasis state subscriber
287  *
288  * \retval The managed state's id
289  *
290  * \since 13.28.0
291  * \since 16.5.0
292  */
293 const char *stasis_state_subscriber_id(const struct stasis_state_subscriber *sub);
294 
295 /*!
296  * \brief Retrieve the subscriber's topic
297  *
298  * \note Returned topic's reference count is NOT incremented. However, the topic is
299  * guaranteed to live for the lifetime of the subscriber.
300  *
301  * \param sub A stasis state subscriber
302  *
303  * \retval The subscriber's topic
304  *
305  * \since 13.28.0
306  * \since 16.5.0
307  */
309 
310 /*!
311  * \brief Retrieve the last known state stasis message payload for the subscriber
312  *
313  * If a stasis message has been published to this state, this function returns
314  * that message's payload object. If no stasis message has been published on the
315  * state, or the message's payload does not exist then NULL is returned.
316  *
317  * \note Returned data's reference count is incremented
318  *
319  * \param sub A stasis state subscriber
320  *
321  * \retval The subscriber's state message data
322  * \retval NULL if no data has been published yet
323  *
324  * \since 13.28.0
325  * \since 16.5.0
326  */
328 
329 /*!
330  * \brief Retrieve the stasis topic subscription if available.
331  *
332  * \param sub A stasis state subscriber
333  *
334  * \retval The subscriber's stasis subscription
335  * \retval NULL if no subscription available
336  *
337  * \since 13.28.0
338  * \since 16.5.0
339  */
341  struct stasis_state_subscriber *sub);
342 
343 /*!
344  * \brief A stasis state publisher
345  *
346  * A publisher to a particular stasis state and topic. As such it holds a reference to
347  * the underlying stasis state, so that managed state is guaranteed to exist for the
348  * lifetime of the publisher.
349  *
350  * \since 13.28.0
351  * \since 16.5.0
352  */
354 
355 /*!
356  * \brief Add a publisher to the managed state for the given id
357  *
358  * Adds a publisher to a managed state based on id. If managed state does not already
359  * exists for the given id then new managed state is created. Otherwise the existing
360  * state is used.
361  *
362  * \param manager The manager object
363  * \param id The unique id of a managed state
364  *
365  * \retval A stasis state publisher
366  * \retval NULL if an error occurred
367  *
368  * \since 13.28.0
369  * \since 16.5.0
370  */
372  struct stasis_state_manager *manager, const char *id);
373 
374 /*!
375  * \brief Retrieve the publisher's underlying state's unique id
376  *
377  * \param pub A stasis state publisher
378  *
379  * \retval The managed state's id
380  *
381  * \since 13.28.0
382  * \since 16.5.0
383  */
384 const char *stasis_state_publisher_id(const struct stasis_state_publisher *pub);
385 
386 /*!
387  * \brief Retrieve the publisher's topic
388  *
389  * \note Returned topic's reference count is NOT incremented. However, the topic is
390  * guaranteed to live for the lifetime of the publisher.
391  *
392  * \param pub A stasis state publisher
393  *
394  * \retval The publisher's topic
395  *
396  * \since 13.28.0
397  * \since 16.5.0
398  */
400 
401 /*!
402  * \brief Publish to a managed state (topic) using a publisher.
403  *
404  * \param pub The publisher to use to publish the message
405  * \param msg The message to publish
406  *
407  * \since 13.28.0
408  * \since 16.5.0
409  */
410 void stasis_state_publish(struct stasis_state_publisher *pub, struct stasis_message *msg);
411 
412 /*!
413  * \brief Publish to a managed named by id topic, and add an implicit subscriber.
414  *
415  * \note It is recommended when adding new publisher functionality within a module
416  * to create and use an explicit publisher instead of using this method.
417  *
418  * This creates an implicit publisher keyed off the eid. This ability was mainly
419  * implemented in order to maintain compatibility with already established code.
420  * Allowing the creation of an implicit publisher made is so less changes were
421  * required when stasis state module was initially added.
422  *
423  * There should only ever be one publisher for a specifically named managed topic
424  * within the system. This being the case we can use the eid to implicitly track
425  * the publisher. However once publishing is no longer needed for a topic a call
426  * to stasis_state_remove_publish_by_id is required in order to remove the implicit
427  * publisher. Thus allowing for its eventual destruction. Without the call to remove
428  * a memory leak will occur.
429  *
430  * \param manager The state manager
431  * \param id A state's unique id
432  * \param eid The unique system id
433  * \param msg The message to publish
434  *
435  * \since 13.28.0
436  * \since 16.5.0
437  */
438 void stasis_state_publish_by_id(struct stasis_state_manager *manager, const char *id,
439  const struct ast_eid *eid, struct stasis_message *msg);
440 
441 /*!
442  * \brief Publish to a managed named by id topic, and remove an implicit publisher.
443  *
444  * This function should be called after calling stasis_state_publish_by_id at least once
445  * for the same manager, id, and eid. If the given stasis message is NULL then the implicit
446  * publisher is removed, but no last message is published.
447  *
448  * See note and description on stasis_state_publish_by_id for more details about if, and
449  * when this function should be used.
450  *
451  * \param manager The state manager
452  * \param id A state's unique id
453  * \param eid The unique system id
454  * \param msg The message to publish (can be NULL)
455  *
456  * \since 13.28.0
457  * \since 16.5.0
458  */
460  const char *id, const struct ast_eid *eid, struct stasis_message *msg);
461 
462 /*! \brief Managed stasis state event interface */
464  /*!
465  * \brief Raised when any managed state is being subscribed.
466  *
467  * \param id The unique id of the managed state
468  * \param sub The subscriber subscribed
469  */
470  void (*on_subscribe)(const char *id, struct stasis_state_subscriber *sub);
471 
472  /*!
473  * \brief Raised when any managed state is being unsubscribed.
474  *
475  * \param id The unique id of the managed state
476  * \param sub The subscriber to unsubscribe
477  */
478  void (*on_unsubscribe)(const char *id, struct stasis_state_subscriber *sub);
479 };
480 
481 /*!
482  * \brief Add an observer to receive managed state related events.
483  *
484  * \param manager The state manager
485  * \param observer The observer handling events
486  *
487  * \retval 0 if successfully registered
488  * \retval -1 on failure
489  *
490  * \since 13.28.0
491  * \since 16.5.0
492  */
495 
496 /*!
497  * \brief Remove an observer (will no longer receive managed state related events).
498  *
499  * \param manager The state manager
500  * \param observer The observer being removed
501  *
502  * \since 13.28.0
503  * \since 16.5.0
504  */
507 
508 /*!
509  * \brief The delegate called for each managed state.
510  *
511  * \param id The unique id of a managed state object
512  * \param msg The last published message on the state, or NULL
513  * \param user_data Data object the user passed into the manager callback
514  *
515  * \retval 0 to continue traversing
516  * \retval CMP_STOP (2) to stop traversing
517  *
518  * \since 13.28.0
519  * \since 16.5.0
520  */
521 typedef int (*on_stasis_state)(const char *id, struct stasis_message *msg, void *user_data);
522 
523 /*!
524  * \brief For each managed state call the given handler.
525  *
526  * \param manager The state manager
527  * \param handler The handler to call for each managed state
528  * \param data User to data to pass on to the handler
529  *
530  * \since 13.28.0
531  * \since 16.5.0
532  */
534  void *data);
535 
536 /*!
537  * \brief For each managed, and explicitly subscribed state call the given handler.
538  *
539  * \param manager The state manager
540  * \param handler The handler to call for each managed state
541  * \param data User to data to pass on to the handler
542  *
543  * \since 13.28.0
544  * \since 16.5.0
545  */
547  void *data);
548 
549 #endif /* _STASIS_STATE_H */
Managed stasis state event interface.
Definition: stasis_state.h:463
void(* on_unsubscribe)(const char *id, struct stasis_state_subscriber *sub)
Raised when any managed state is being unsubscribed.
Definition: stasis_state.h:478
struct stasis_state_subscriber * stasis_state_subscribe_pool(struct stasis_state_manager *manager, const char *id, stasis_subscription_cb callback, void *data)
Add a subscriber, and subscribe to its underlying stasis topic.
Definition: stasis_state.c:446
struct stasis_state_subscriber * stasis_state_add_subscriber(struct stasis_state_manager *manager, const char *id)
Add a subscriber to the managed stasis state for the given id.
Definition: stasis_state.c:412
void * stasis_state_unsubscribe(struct stasis_state_subscriber *sub)
Unsubscribe from the stasis topic and stasis state.
Definition: stasis_state.c:470
void stasis_state_remove_publish_by_id(struct stasis_state_manager *manager, const char *id, const struct ast_eid *eid, struct stasis_message *msg)
Publish to a managed named by id topic, and remove an implicit publisher.
Definition: stasis_state.c:658
void stasis_state_remove_observer(struct stasis_state_manager *manager, struct stasis_state_observer *observer)
Remove an observer (will no longer receive managed state related events).
Definition: stasis_state.c:700
struct stasis_topic * stasis_state_subscriber_topic(struct stasis_state_subscriber *sub)
Retrieve the subscriber&#39;s topic.
Definition: stasis_state.c:492
void(* on_subscribe)(const char *id, struct stasis_state_subscriber *sub)
Raised when any managed state is being subscribed.
Definition: stasis_state.h:470
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
const char * stasis_state_publisher_id(const struct stasis_state_publisher *pub)
Retrieve the publisher&#39;s underlying state&#39;s unique id.
Definition: stasis_state.c:552
void * stasis_state_subscriber_data(struct stasis_state_subscriber *sub)
Retrieve the last known state stasis message payload for the subscriber.
Definition: stasis_state.c:497
int(* on_stasis_state)(const char *id, struct stasis_message *msg, void *user_data)
The delegate called for each managed state.
Definition: stasis_state.h:521
const char * stasis_state_subscriber_id(const struct stasis_state_subscriber *sub)
Retrieve the underlying subscribed to state&#39;s unique id.
Definition: stasis_state.c:487
void * stasis_state_unsubscribe_and_join(struct stasis_state_subscriber *sub)
Unsubscribe from the stasis topic, block until the final message is received, and then unsubscribe fr...
Definition: stasis_state.c:477
struct stasis_state_manager * stasis_state_manager_create(const char *topic_name)
Create a stasis state manager.
Definition: stasis_state.c:324
int stasis_state_add_observer(struct stasis_state_manager *manager, struct stasis_state_observer *observer)
Add an observer to receive managed state related events.
Definition: stasis_state.c:688
An Entity ID is essentially a MAC address, brief and unique.
Definition: utils.h:786
void stasis_state_callback_all(struct stasis_state_manager *manager, on_stasis_state handler, void *data)
For each managed state call the given handler.
Definition: stasis_state.c:740
struct stasis_state_publisher * stasis_state_add_publisher(struct stasis_state_manager *manager, const char *id)
Add a publisher to the managed state for the given id.
Definition: stasis_state.c:531
void stasis_state_callback_subscribed(struct stasis_state_manager *manager, on_stasis_state handler, void *data)
For each managed, and explicitly subscribed state call the given handler.
Definition: stasis_state.c:763
void stasis_state_publish_by_id(struct stasis_state_manager *manager, const char *id, const struct ast_eid *eid, struct stasis_message *msg)
Publish to a managed named by id topic, and add an implicit subscriber.
Definition: stasis_state.c:638
struct stasis_subscription * stasis_state_subscriber_subscription(struct stasis_state_subscriber *sub)
Retrieve the stasis topic subscription if available.
Definition: stasis_state.c:513
void(* stasis_subscription_cb)(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Callback function type for Stasis subscriptions.
Definition: stasis.h:615
struct stasis_topic * stasis_state_all_topic(struct stasis_state_manager *manager)
Retrieve the manager&#39;s topic (the topic that all state topics get forwarded to)
Definition: stasis_state.c:364
static void handler(const char *name, int response_code, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition: test_ari.c:59
struct ast_sorcery_instance_observer observer
struct stasis_forward * sub
Definition: res_corosync.c:240
enum queue_result id
Definition: app_queue.c:1507
struct stasis_topic * stasis_state_publisher_topic(struct stasis_state_publisher *pub)
Retrieve the publisher&#39;s topic.
Definition: stasis_state.c:557
void stasis_state_publish(struct stasis_state_publisher *pub, struct stasis_message *msg)
Publish to a managed state (topic) using a publisher.
Definition: stasis_state.c:562
struct stasis_topic * stasis_state_topic(struct stasis_state_manager *manager, const char *id)
Retrieve a managed topic creating one if not currently managed.
Definition: stasis_state.c:369