stasis.c 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670
  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. /*! \file
  19. *
  20. * \brief Stasis Message Bus API.
  21. *
  22. * \author David M. Lee, II <dlee@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <support_level>core</support_level>
  26. ***/
  27. #include "asterisk.h"
  28. ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
  29. #include "asterisk/astobj2.h"
  30. #include "asterisk/stasis_internal.h"
  31. #include "asterisk/stasis.h"
  32. #include "asterisk/taskprocessor.h"
  33. #include "asterisk/threadpool.h"
  34. #include "asterisk/utils.h"
  35. #include "asterisk/uuid.h"
  36. #include "asterisk/vector.h"
  37. #include "asterisk/stasis_channels.h"
  38. #include "asterisk/stasis_bridges.h"
  39. #include "asterisk/stasis_endpoints.h"
  40. #include "asterisk/config_options.h"
  41. #ifdef AST_DEVMODE
  42. #include "asterisk/cli.h"
  43. #endif
  44. /*** DOCUMENTATION
  45. <managerEvent language="en_US" name="UserEvent">
  46. <managerEventInstance class="EVENT_FLAG_USER">
  47. <synopsis>A user defined event raised from the dialplan.</synopsis>
  48. <syntax>
  49. <channel_snapshot/>
  50. <parameter name="UserEvent">
  51. <para>The event name, as specified in the dialplan.</para>
  52. </parameter>
  53. </syntax>
  54. <description>
  55. <para>Event may contain additional arbitrary parameters in addition to optional bridge and endpoint snapshots. Multiple snapshots of the same type are prefixed with a numeric value.</para>
  56. </description>
  57. <see-also>
  58. <ref type="application">UserEvent</ref>
  59. <ref type="managerEvent">UserEvent</ref>
  60. </see-also>
  61. </managerEventInstance>
  62. </managerEvent>
  63. <configInfo name="stasis" language="en_US">
  64. <configFile name="stasis.conf">
  65. <configObject name="threadpool">
  66. <synopsis>Settings that configure the threadpool Stasis uses to deliver some messages.</synopsis>
  67. <configOption name="initial_size" default="5">
  68. <synopsis>Initial number of threads in the message bus threadpool.</synopsis>
  69. </configOption>
  70. <configOption name="idle_timeout_sec" default="20">
  71. <synopsis>Number of seconds before an idle thread is disposed of.</synopsis>
  72. </configOption>
  73. <configOption name="max_size" default="50">
  74. <synopsis>Maximum number of threads in the threadpool.</synopsis>
  75. </configOption>
  76. </configObject>
  77. <configObject name="declined_message_types">
  78. <synopsis>Stasis message types for which to decline creation.</synopsis>
  79. <configOption name="decline">
  80. <synopsis>The message type to decline.</synopsis>
  81. <description>
  82. <para>This configuration option defines the name of the Stasis
  83. message type that Asterisk is forbidden from creating and can be
  84. specified as many times as necessary to achieve the desired result.</para>
  85. <enumlist>
  86. <enum name="stasis_app_recording_snapshot_type" />
  87. <enum name="stasis_app_playback_snapshot_type" />
  88. <enum name="stasis_test_message_type" />
  89. <enum name="confbridge_start_type" />
  90. <enum name="confbridge_end_type" />
  91. <enum name="confbridge_join_type" />
  92. <enum name="confbridge_leave_type" />
  93. <enum name="confbridge_start_record_type" />
  94. <enum name="confbridge_stop_record_type" />
  95. <enum name="confbridge_mute_type" />
  96. <enum name="confbridge_unmute_type" />
  97. <enum name="confbridge_talking_type" />
  98. <enum name="cel_generic_type" />
  99. <enum name="ast_bridge_snapshot_type" />
  100. <enum name="ast_bridge_merge_message_type" />
  101. <enum name="ast_channel_entered_bridge_type" />
  102. <enum name="ast_channel_left_bridge_type" />
  103. <enum name="ast_blind_transfer_type" />
  104. <enum name="ast_attended_transfer_type" />
  105. <enum name="ast_endpoint_snapshot_type" />
  106. <enum name="ast_endpoint_state_type" />
  107. <enum name="ast_device_state_message_type" />
  108. <enum name="ast_test_suite_message_type" />
  109. <enum name="ast_mwi_state_type" />
  110. <enum name="ast_mwi_vm_app_type" />
  111. <enum name="ast_format_register_type" />
  112. <enum name="ast_format_unregister_type" />
  113. <enum name="ast_manager_get_generic_type" />
  114. <enum name="ast_parked_call_type" />
  115. <enum name="ast_channel_snapshot_type" />
  116. <enum name="ast_channel_dial_type" />
  117. <enum name="ast_channel_varset_type" />
  118. <enum name="ast_channel_hangup_request_type" />
  119. <enum name="ast_channel_dtmf_begin_type" />
  120. <enum name="ast_channel_dtmf_end_type" />
  121. <enum name="ast_channel_hold_type" />
  122. <enum name="ast_channel_unhold_type" />
  123. <enum name="ast_channel_chanspy_start_type" />
  124. <enum name="ast_channel_chanspy_stop_type" />
  125. <enum name="ast_channel_fax_type" />
  126. <enum name="ast_channel_hangup_handler_type" />
  127. <enum name="ast_channel_moh_start_type" />
  128. <enum name="ast_channel_moh_stop_type" />
  129. <enum name="ast_channel_monitor_start_type" />
  130. <enum name="ast_channel_monitor_stop_type" />
  131. <enum name="ast_channel_agent_login_type" />
  132. <enum name="ast_channel_agent_logoff_type" />
  133. <enum name="ast_channel_talking_start" />
  134. <enum name="ast_channel_talking_stop" />
  135. <enum name="ast_security_event_type" />
  136. <enum name="ast_named_acl_change_type" />
  137. <enum name="ast_local_bridge_type" />
  138. <enum name="ast_local_optimization_begin_type" />
  139. <enum name="ast_local_optimization_end_type" />
  140. <enum name="stasis_subscription_change_type" />
  141. <enum name="ast_multi_user_event_type" />
  142. <enum name="stasis_cache_clear_type" />
  143. <enum name="stasis_cache_update_type" />
  144. <enum name="ast_network_change_type" />
  145. <enum name="ast_system_registry_type" />
  146. <enum name="ast_cc_available_type" />
  147. <enum name="ast_cc_offertimerstart_type" />
  148. <enum name="ast_cc_requested_type" />
  149. <enum name="ast_cc_requestacknowledged_type" />
  150. <enum name="ast_cc_callerstopmonitoring_type" />
  151. <enum name="ast_cc_callerstartmonitoring_type" />
  152. <enum name="ast_cc_callerrecalling_type" />
  153. <enum name="ast_cc_recallcomplete_type" />
  154. <enum name="ast_cc_failure_type" />
  155. <enum name="ast_cc_monitorfailed_type" />
  156. <enum name="ast_presence_state_message_type" />
  157. <enum name="ast_rtp_rtcp_sent_type" />
  158. <enum name="ast_rtp_rtcp_received_type" />
  159. <enum name="ast_call_pickup_type" />
  160. <enum name="aoc_s_type" />
  161. <enum name="aoc_d_type" />
  162. <enum name="aoc_e_type" />
  163. <enum name="dahdichannel_type" />
  164. <enum name="mcid_type" />
  165. <enum name="session_timeout_type" />
  166. <enum name="cdr_read_message_type" />
  167. <enum name="cdr_write_message_type" />
  168. <enum name="cdr_prop_write_message_type" />
  169. <enum name="corosync_ping_message_type" />
  170. <enum name="agi_exec_start_type" />
  171. <enum name="agi_exec_end_type" />
  172. <enum name="agi_async_start_type" />
  173. <enum name="agi_async_exec_type" />
  174. <enum name="agi_async_end_type" />
  175. <enum name="queue_caller_join_type" />
  176. <enum name="queue_caller_leave_type" />
  177. <enum name="queue_caller_abandon_type" />
  178. <enum name="queue_member_status_type" />
  179. <enum name="queue_member_added_type" />
  180. <enum name="queue_member_removed_type" />
  181. <enum name="queue_member_pause_type" />
  182. <enum name="queue_member_penalty_type" />
  183. <enum name="queue_member_ringinuse_type" />
  184. <enum name="queue_agent_called_type" />
  185. <enum name="queue_agent_connect_type" />
  186. <enum name="queue_agent_complete_type" />
  187. <enum name="queue_agent_dump_type" />
  188. <enum name="queue_agent_ringnoanswer_type" />
  189. <enum name="meetme_join_type" />
  190. <enum name="meetme_leave_type" />
  191. <enum name="meetme_end_type" />
  192. <enum name="meetme_mute_type" />
  193. <enum name="meetme_talking_type" />
  194. <enum name="meetme_talk_request_type" />
  195. <enum name="appcdr_message_type" />
  196. <enum name="forkcdr_message_type" />
  197. <enum name="cdr_sync_message_type" />
  198. </enumlist>
  199. </description>
  200. </configOption>
  201. </configObject>
  202. </configFile>
  203. </configInfo>
  204. ***/
  205. /*!
  206. * \page stasis-impl Stasis Implementation Notes
  207. *
  208. * \par Reference counting
  209. *
  210. * Stasis introduces a number of objects, which are tightly related to one
  211. * another. Because we rely on ref-counting for memory management, understanding
  212. * these relationships is important to understanding this code.
  213. *
  214. * \code{.txt}
  215. *
  216. * stasis_topic <----> stasis_subscription
  217. * ^ ^
  218. * \ /
  219. * \ /
  220. * dispatch
  221. * |
  222. * |
  223. * v
  224. * stasis_message
  225. * |
  226. * |
  227. * v
  228. * stasis_message_type
  229. *
  230. * \endcode
  231. *
  232. * The most troubling thing in this chart is the cyclic reference between
  233. * stasis_topic and stasis_subscription. This is both unfortunate, and
  234. * necessary. Topics need the subscription in order to dispatch messages;
  235. * subscriptions need the topics to unsubscribe and check subscription status.
  236. *
  237. * The cycle is broken by stasis_unsubscribe(). The unsubscribe will remove the
  238. * topic's reference to a subscription. When the subcription is destroyed, it
  239. * will remove its reference to the topic.
  240. *
  241. * This means that until a subscription has be explicitly unsubscribed, it will
  242. * not be destroyed. Neither will a topic be destroyed while it has subscribers.
  243. * The destructors of both have assertions regarding this to catch ref-counting
  244. * problems where a subscription or topic has had an extra ao2_cleanup().
  245. *
  246. * The \ref dispatch object is a transient object, which is posted to a
  247. * subscription's taskprocessor to send a message to the subscriber. They have
  248. * short life cycles, allocated on one thread, destroyed on another.
  249. *
  250. * During shutdown, or the deletion of a domain object, there are a flurry of
  251. * ao2_cleanup()s on subscriptions and topics, as the final in-flight messages
  252. * are processed. Any one of these cleanups could be the one to actually destroy
  253. * a given object, so care must be taken to ensure that an object isn't
  254. * referenced after an ao2_cleanup(). This includes the implicit ao2_unlock()
  255. * that might happen when a RAII_VAR() goes out of scope.
  256. *
  257. * \par Typical life cycles
  258. *
  259. * \li stasis_topic - There are several topics which live for the duration of
  260. * the Asterisk process (ast_channel_topic_all(), etc.) but most of these
  261. * are actually fed by shorter-lived topics whose lifetime is associated
  262. * with some domain object (like ast_channel_topic() for a given
  263. * ast_channel).
  264. *
  265. * \li stasis_subscription - Subscriptions have a similar mix of lifetimes as
  266. * topics, for similar reasons.
  267. *
  268. * \li dispatch - Very short lived; just long enough to post a message to a
  269. * subscriber.
  270. *
  271. * \li stasis_message - Short to intermediate lifetimes, but that is mostly
  272. * irrelevant. Messages are strictly data and have no behavior associated
  273. * with them, so it doesn't really matter if/when they are destroyed. By
  274. * design, a component could hold a ref to a message forever without any
  275. * ill consequences (aside from consuming more memory).
  276. *
  277. * \li stasis_message_type - Long life cycles, typically only destroyed on
  278. * module unloading or _clean_ process exit.
  279. *
  280. * \par Subscriber shutdown sequencing
  281. *
  282. * Subscribers are sensitive to shutdown sequencing, specifically in how the
  283. * reference message types. This is fully detailed on the wiki at
  284. * https://wiki.asterisk.org/wiki/x/K4BqAQ.
  285. *
  286. * In short, the lifetime of the \a data (and \a callback, if in a module) must
  287. * be held until the stasis_subscription_final_message() has been received.
  288. * Depending on the structure of the subscriber code, this can be handled by
  289. * using stasis_subscription_final_message() to free resources on the final
  290. * message, or using stasis_subscription_join()/stasis_unsubscribe_and_join() to
  291. * block until the unsubscribe has completed.
  292. */
  293. /*! Initial size of the subscribers list. */
  294. #define INITIAL_SUBSCRIBERS_MAX 4
  295. /*! The number of buckets to use for topic pools */
  296. #define TOPIC_POOL_BUCKETS 57
  297. /*! Thread pool for topics that don't want a dedicated taskprocessor */
  298. static struct ast_threadpool *pool;
  299. STASIS_MESSAGE_TYPE_DEFN(stasis_subscription_change_type);
  300. #ifdef AST_DEVMODE
  301. /*! The number of buckets to use for topic statistics */
  302. #define TOPIC_STATISTICS_BUCKETS 57
  303. /*! The number of buckets to use for subscription statistics */
  304. #define SUBSCRIPTION_STATISTICS_BUCKETS 57
  305. /*! Container which stores statistics for topics */
  306. static struct ao2_container *topic_statistics;
  307. /*! Container which stores statistics for subscriptions */
  308. static struct ao2_container *subscription_statistics;
  309. /*! \internal */
  310. struct stasis_message_type_statistics {
  311. /*! \brief The number of messages of this published */
  312. int published;
  313. /*! \brief The number of messages of this that did not reach a subscriber */
  314. int unused;
  315. /*! \brief The stasis message type */
  316. struct stasis_message_type *message_type;
  317. };
  318. /*! Lock to protect the message types vector */
  319. AST_MUTEX_DEFINE_STATIC(message_type_statistics_lock);
  320. /*! Vector containing message type information */
  321. static AST_VECTOR(, struct stasis_message_type_statistics) message_type_statistics;
  322. /*! \internal */
  323. struct stasis_topic_statistics {
  324. /*! \brief Highest time spent dispatching messages to subscribers */
  325. long highest_time_dispatched;
  326. /*! \brief Lowest time spent dispatching messages to subscribers */
  327. long lowest_time_dispatched;
  328. /*! \brief The number of messages that were not dispatched to any subscriber */
  329. int messages_not_dispatched;
  330. /*! \brief The number of messages that were dispatched to at least 1 subscriber */
  331. int messages_dispatched;
  332. /*! \brief The number of subscribers to this topic */
  333. int subscriber_count;
  334. /*! \brief Name of the topic */
  335. char name[0];
  336. };
  337. #endif
  338. /*! \internal */
  339. struct stasis_topic {
  340. /*! Variable length array of the subscribers */
  341. AST_VECTOR(, struct stasis_subscription *) subscribers;
  342. /*! Topics forwarding into this topic */
  343. AST_VECTOR(, struct stasis_topic *) upstream_topics;
  344. #ifdef AST_DEVMODE
  345. struct stasis_topic_statistics *statistics;
  346. #endif
  347. /*! Name of the topic */
  348. char name[0];
  349. };
  350. /* Forward declarations for the tightly-coupled subscription object */
  351. static int topic_add_subscription(struct stasis_topic *topic,
  352. struct stasis_subscription *sub);
  353. static int topic_remove_subscription(struct stasis_topic *topic, struct stasis_subscription *sub);
  354. /*! \brief Lock two topics. */
  355. #define topic_lock_both(topic1, topic2) \
  356. do { \
  357. ao2_lock(topic1); \
  358. while (ao2_trylock(topic2)) { \
  359. AO2_DEADLOCK_AVOIDANCE(topic1); \
  360. } \
  361. } while (0)
  362. static void topic_dtor(void *obj)
  363. {
  364. struct stasis_topic *topic = obj;
  365. /* Subscribers hold a reference to topics, so they should all be
  366. * unsubscribed before we get here. */
  367. ast_assert(AST_VECTOR_SIZE(&topic->subscribers) == 0);
  368. AST_VECTOR_FREE(&topic->subscribers);
  369. AST_VECTOR_FREE(&topic->upstream_topics);
  370. #ifdef AST_DEVMODE
  371. if (topic->statistics) {
  372. ao2_unlink(topic_statistics, topic->statistics);
  373. ao2_ref(topic->statistics, -1);
  374. }
  375. #endif
  376. }
  377. #ifdef AST_DEVMODE
  378. static struct stasis_topic_statistics *stasis_topic_statistics_create(const char *name)
  379. {
  380. struct stasis_topic_statistics *statistics;
  381. statistics = ao2_alloc(sizeof(*statistics) + strlen(name) + 1, NULL);
  382. if (!statistics) {
  383. return NULL;
  384. }
  385. strcpy(statistics->name, name); /* SAFE */
  386. ao2_link(topic_statistics, statistics);
  387. return statistics;
  388. }
  389. #endif
  390. struct stasis_topic *stasis_topic_create(const char *name)
  391. {
  392. struct stasis_topic *topic;
  393. int res = 0;
  394. topic = ao2_t_alloc(sizeof(*topic) + strlen(name) + 1, topic_dtor, name);
  395. if (!topic) {
  396. return NULL;
  397. }
  398. strcpy(topic->name, name); /* SAFE */
  399. res |= AST_VECTOR_INIT(&topic->subscribers, INITIAL_SUBSCRIBERS_MAX);
  400. res |= AST_VECTOR_INIT(&topic->upstream_topics, 0);
  401. #ifdef AST_DEVMODE
  402. topic->statistics = stasis_topic_statistics_create(name);
  403. if (!topic->name || !topic->statistics || res)
  404. #else
  405. if (!topic->name || res)
  406. #endif
  407. {
  408. ao2_ref(topic, -1);
  409. return NULL;
  410. }
  411. return topic;
  412. }
  413. const char *stasis_topic_name(const struct stasis_topic *topic)
  414. {
  415. return topic->name;
  416. }
  417. size_t stasis_topic_subscribers(const struct stasis_topic *topic)
  418. {
  419. return AST_VECTOR_SIZE(&topic->subscribers);
  420. }
  421. #ifdef AST_DEVMODE
  422. struct stasis_subscription_statistics {
  423. /*! \brief The filename where the subscription originates */
  424. const char *file;
  425. /*! \brief The function where the subscription originates */
  426. const char *func;
  427. /*! \brief Name of the topic we subscribed to */
  428. char *topic;
  429. /*! \brief The message type that currently took the longest to process */
  430. struct stasis_message_type *highest_time_message_type;
  431. /*! \brief Highest time spent invoking a message */
  432. long highest_time_invoked;
  433. /*! \brief Lowest time spent invoking a message */
  434. long lowest_time_invoked;
  435. /*! \brief The number of messages that were filtered out */
  436. int messages_dropped;
  437. /*! \brief The number of messages that passed filtering */
  438. int messages_passed;
  439. /*! \brief Using a mailbox to queue messages */
  440. int uses_mailbox;
  441. /*! \brief Using stasis threadpool for handling messages */
  442. int uses_threadpool;
  443. /*! \brief The line number where the subscription originates */
  444. int lineno;
  445. /*! \brief Unique ID of the subscription */
  446. char uniqueid[0];
  447. };
  448. #endif
  449. /*! \internal */
  450. struct stasis_subscription {
  451. /*! Unique ID for this subscription */
  452. char uniqueid[AST_UUID_STR_LEN];
  453. /*! Topic subscribed to. */
  454. struct stasis_topic *topic;
  455. /*! Mailbox for processing incoming messages. */
  456. struct ast_taskprocessor *mailbox;
  457. /*! Callback function for incoming message processing. */
  458. stasis_subscription_cb callback;
  459. /*! Data pointer to be handed to the callback. */
  460. void *data;
  461. /*! Condition for joining with subscription. */
  462. ast_cond_t join_cond;
  463. /*! Flag set when final message for sub has been received.
  464. * Be sure join_lock is held before reading/setting. */
  465. int final_message_rxed;
  466. /*! Flag set when final message for sub has been processed.
  467. * Be sure join_lock is held before reading/setting. */
  468. int final_message_processed;
  469. /*! The message types this subscription is accepting */
  470. AST_VECTOR(, char) accepted_message_types;
  471. /*! The message formatters this subscription is accepting */
  472. enum stasis_subscription_message_formatters accepted_formatters;
  473. /*! The message filter currently in use */
  474. enum stasis_subscription_message_filter filter;
  475. #ifdef AST_DEVMODE
  476. /*! Statistics information */
  477. struct stasis_subscription_statistics *statistics;
  478. #endif
  479. };
  480. static void subscription_dtor(void *obj)
  481. {
  482. struct stasis_subscription *sub = obj;
  483. /* Subscriptions need to be manually unsubscribed before destruction
  484. * b/c there's a cyclic reference between topics and subscriptions */
  485. ast_assert(!stasis_subscription_is_subscribed(sub));
  486. /* If there are any messages in flight to this subscription; that would
  487. * be bad. */
  488. ast_assert(stasis_subscription_is_done(sub));
  489. ao2_cleanup(sub->topic);
  490. sub->topic = NULL;
  491. ast_taskprocessor_unreference(sub->mailbox);
  492. sub->mailbox = NULL;
  493. ast_cond_destroy(&sub->join_cond);
  494. AST_VECTOR_FREE(&sub->accepted_message_types);
  495. #ifdef AST_DEVMODE
  496. if (sub->statistics) {
  497. ao2_unlink(subscription_statistics, sub->statistics);
  498. ao2_ref(sub->statistics, -1);
  499. }
  500. #endif
  501. }
  502. /*!
  503. * \brief Invoke the subscription's callback.
  504. * \param sub Subscription to invoke.
  505. * \param topic Topic message was published to.
  506. * \param message Message to send.
  507. */
  508. static void subscription_invoke(struct stasis_subscription *sub,
  509. struct stasis_message *message)
  510. {
  511. unsigned int final = stasis_subscription_final_message(sub, message);
  512. int message_type_id = stasis_message_type_id(stasis_subscription_change_type());
  513. #ifdef AST_DEVMODE
  514. struct timeval start;
  515. long elapsed;
  516. start = ast_tvnow();
  517. #endif
  518. /* Notify that the final message has been received */
  519. if (final) {
  520. ao2_lock(sub);
  521. sub->final_message_rxed = 1;
  522. ast_cond_signal(&sub->join_cond);
  523. ao2_unlock(sub);
  524. }
  525. /*
  526. * If filtering is turned on and this is a 'final' message, we only invoke the callback
  527. * if the subscriber accepts subscription_change message types.
  528. */
  529. if (!final || sub->filter != STASIS_SUBSCRIPTION_FILTER_SELECTIVE ||
  530. (message_type_id < AST_VECTOR_SIZE(&sub->accepted_message_types) && AST_VECTOR_GET(&sub->accepted_message_types, message_type_id))) {
  531. /* Since sub is mostly immutable, no need to lock sub */
  532. sub->callback(sub->data, sub, message);
  533. }
  534. /* Notify that the final message has been processed */
  535. if (final) {
  536. ao2_lock(sub);
  537. sub->final_message_processed = 1;
  538. ast_cond_signal(&sub->join_cond);
  539. ao2_unlock(sub);
  540. }
  541. #ifdef AST_DEVMODE
  542. elapsed = ast_tvdiff_ms(ast_tvnow(), start);
  543. if (elapsed > sub->statistics->highest_time_invoked) {
  544. sub->statistics->highest_time_invoked = elapsed;
  545. ao2_lock(sub->statistics);
  546. sub->statistics->highest_time_message_type = stasis_message_type(message);
  547. ao2_unlock(sub->statistics);
  548. }
  549. if (elapsed < sub->statistics->lowest_time_invoked) {
  550. sub->statistics->lowest_time_invoked = elapsed;
  551. }
  552. #endif
  553. }
  554. static void send_subscription_subscribe(struct stasis_topic *topic, struct stasis_subscription *sub);
  555. static void send_subscription_unsubscribe(struct stasis_topic *topic, struct stasis_subscription *sub);
  556. void stasis_subscription_cb_noop(void *data, struct stasis_subscription *sub, struct stasis_message *message)
  557. {
  558. }
  559. #ifdef AST_DEVMODE
  560. static struct stasis_subscription_statistics *stasis_subscription_statistics_create(const char *uniqueid,
  561. const char *topic, int needs_mailbox, int use_thread_pool, const char *file, int lineno,
  562. const char *func)
  563. {
  564. struct stasis_subscription_statistics *statistics;
  565. size_t uniqueid_len = strlen(uniqueid) + 1;
  566. statistics = ao2_alloc(sizeof(*statistics) + uniqueid_len + strlen(topic) + 1, NULL);
  567. if (!statistics) {
  568. return NULL;
  569. }
  570. statistics->file = file;
  571. statistics->lineno = lineno;
  572. statistics->func = func;
  573. statistics->uses_mailbox = needs_mailbox;
  574. statistics->uses_threadpool = use_thread_pool;
  575. strcpy(statistics->uniqueid, uniqueid); /* SAFE */
  576. statistics->topic = statistics->uniqueid + uniqueid_len;
  577. strcpy(statistics->topic, topic); /* SAFE */
  578. ao2_link(subscription_statistics, statistics);
  579. return statistics;
  580. }
  581. #endif
  582. struct stasis_subscription *internal_stasis_subscribe(
  583. struct stasis_topic *topic,
  584. stasis_subscription_cb callback,
  585. void *data,
  586. int needs_mailbox,
  587. int use_thread_pool,
  588. const char *file,
  589. int lineno,
  590. const char *func)
  591. {
  592. struct stasis_subscription *sub;
  593. if (!topic) {
  594. return NULL;
  595. }
  596. /* The ao2 lock is used for join_cond. */
  597. sub = ao2_t_alloc(sizeof(*sub), subscription_dtor, stasis_topic_name(topic));
  598. if (!sub) {
  599. return NULL;
  600. }
  601. ast_uuid_generate_str(sub->uniqueid, sizeof(sub->uniqueid));
  602. #ifdef AST_DEVMODE
  603. sub->statistics = stasis_subscription_statistics_create(sub->uniqueid, topic->name, needs_mailbox,
  604. use_thread_pool, file, lineno, func);
  605. if (!sub->statistics) {
  606. ao2_ref(sub, -1);
  607. return NULL;
  608. }
  609. #endif
  610. if (needs_mailbox) {
  611. char tps_name[AST_TASKPROCESSOR_MAX_NAME + 1];
  612. /* Create name with seq number appended. */
  613. ast_taskprocessor_build_name(tps_name, sizeof(tps_name), "sub%c:%s",
  614. use_thread_pool ? 'p' : 'm',
  615. stasis_topic_name(topic));
  616. /*
  617. * With a small number of subscribers, a thread-per-sub is
  618. * acceptable. For a large number of subscribers, a thread
  619. * pool should be used.
  620. */
  621. if (use_thread_pool) {
  622. sub->mailbox = ast_threadpool_serializer(tps_name, pool);
  623. } else {
  624. sub->mailbox = ast_taskprocessor_get(tps_name, TPS_REF_DEFAULT);
  625. }
  626. if (!sub->mailbox) {
  627. ao2_ref(sub, -1);
  628. return NULL;
  629. }
  630. ast_taskprocessor_set_local(sub->mailbox, sub);
  631. /* Taskprocessor has a reference */
  632. ao2_ref(sub, +1);
  633. }
  634. ao2_ref(topic, +1);
  635. sub->topic = topic;
  636. sub->callback = callback;
  637. sub->data = data;
  638. ast_cond_init(&sub->join_cond, NULL);
  639. sub->filter = STASIS_SUBSCRIPTION_FILTER_NONE;
  640. AST_VECTOR_INIT(&sub->accepted_message_types, 0);
  641. sub->accepted_formatters = STASIS_SUBSCRIPTION_FORMATTER_NONE;
  642. if (topic_add_subscription(topic, sub) != 0) {
  643. ao2_ref(sub, -1);
  644. return NULL;
  645. }
  646. send_subscription_subscribe(topic, sub);
  647. return sub;
  648. }
  649. struct stasis_subscription *__stasis_subscribe(
  650. struct stasis_topic *topic,
  651. stasis_subscription_cb callback,
  652. void *data,
  653. const char *file,
  654. int lineno,
  655. const char *func)
  656. {
  657. return internal_stasis_subscribe(topic, callback, data, 1, 0, file, lineno, func);
  658. }
  659. struct stasis_subscription *__stasis_subscribe_pool(
  660. struct stasis_topic *topic,
  661. stasis_subscription_cb callback,
  662. void *data,
  663. const char *file,
  664. int lineno,
  665. const char *func)
  666. {
  667. return internal_stasis_subscribe(topic, callback, data, 1, 1, file, lineno, func);
  668. }
  669. static int sub_cleanup(void *data)
  670. {
  671. struct stasis_subscription *sub = data;
  672. ao2_cleanup(sub);
  673. return 0;
  674. }
  675. struct stasis_subscription *stasis_unsubscribe(struct stasis_subscription *sub)
  676. {
  677. /* The subscription may be the last ref to this topic. Hold
  678. * the topic ref open until after the unlock. */
  679. struct stasis_topic *topic;
  680. if (!sub) {
  681. return NULL;
  682. }
  683. topic = ao2_bump(sub->topic);
  684. /* We have to remove the subscription first, to ensure the unsubscribe
  685. * is the final message */
  686. if (topic_remove_subscription(sub->topic, sub) != 0) {
  687. ast_log(LOG_ERROR,
  688. "Internal error: subscription has invalid topic\n");
  689. ao2_cleanup(topic);
  690. return NULL;
  691. }
  692. /* Now let everyone know about the unsubscribe */
  693. send_subscription_unsubscribe(topic, sub);
  694. /* When all that's done, remove the ref the mailbox has on the sub */
  695. if (sub->mailbox) {
  696. if (ast_taskprocessor_push(sub->mailbox, sub_cleanup, sub)) {
  697. /* Nothing we can do here, the conditional is just to keep
  698. * the compiler happy that we're not ignoring the result. */
  699. }
  700. }
  701. /* Unsubscribing unrefs the subscription */
  702. ao2_cleanup(sub);
  703. ao2_cleanup(topic);
  704. return NULL;
  705. }
  706. int stasis_subscription_set_congestion_limits(struct stasis_subscription *subscription,
  707. long low_water, long high_water)
  708. {
  709. int res = -1;
  710. if (subscription) {
  711. res = ast_taskprocessor_alert_set_levels(subscription->mailbox,
  712. low_water, high_water);
  713. }
  714. return res;
  715. }
  716. int stasis_subscription_accept_message_type(struct stasis_subscription *subscription,
  717. const struct stasis_message_type *type)
  718. {
  719. if (!subscription) {
  720. return -1;
  721. }
  722. ast_assert(type != NULL);
  723. ast_assert(stasis_message_type_name(type) != NULL);
  724. if (!type || !stasis_message_type_name(type)) {
  725. /* Filtering is unreliable as this message type is not yet initialized
  726. * so force all messages through.
  727. */
  728. subscription->filter = STASIS_SUBSCRIPTION_FILTER_FORCED_NONE;
  729. return 0;
  730. }
  731. ao2_lock(subscription->topic);
  732. if (AST_VECTOR_REPLACE(&subscription->accepted_message_types, stasis_message_type_id(type), 1)) {
  733. /* We do this for the same reason as above. The subscription can still operate, so allow
  734. * it to do so by forcing all messages through.
  735. */
  736. subscription->filter = STASIS_SUBSCRIPTION_FILTER_FORCED_NONE;
  737. }
  738. ao2_unlock(subscription->topic);
  739. return 0;
  740. }
  741. int stasis_subscription_decline_message_type(struct stasis_subscription *subscription,
  742. const struct stasis_message_type *type)
  743. {
  744. if (!subscription) {
  745. return -1;
  746. }
  747. ast_assert(type != NULL);
  748. ast_assert(stasis_message_type_name(type) != NULL);
  749. if (!type || !stasis_message_type_name(type)) {
  750. return 0;
  751. }
  752. ao2_lock(subscription->topic);
  753. if (stasis_message_type_id(type) < AST_VECTOR_SIZE(&subscription->accepted_message_types)) {
  754. /* The memory is already allocated so this can't fail */
  755. AST_VECTOR_REPLACE(&subscription->accepted_message_types, stasis_message_type_id(type), 0);
  756. }
  757. ao2_unlock(subscription->topic);
  758. return 0;
  759. }
  760. int stasis_subscription_set_filter(struct stasis_subscription *subscription,
  761. enum stasis_subscription_message_filter filter)
  762. {
  763. if (!subscription) {
  764. return -1;
  765. }
  766. ao2_lock(subscription->topic);
  767. if (subscription->filter != STASIS_SUBSCRIPTION_FILTER_FORCED_NONE) {
  768. subscription->filter = filter;
  769. }
  770. ao2_unlock(subscription->topic);
  771. return 0;
  772. }
  773. void stasis_subscription_accept_formatters(struct stasis_subscription *subscription,
  774. enum stasis_subscription_message_formatters formatters)
  775. {
  776. ast_assert(subscription != NULL);
  777. ao2_lock(subscription->topic);
  778. subscription->accepted_formatters = formatters;
  779. ao2_unlock(subscription->topic);
  780. return;
  781. }
  782. void stasis_subscription_join(struct stasis_subscription *subscription)
  783. {
  784. if (subscription) {
  785. ao2_lock(subscription);
  786. /* Wait until the processed flag has been set */
  787. while (!subscription->final_message_processed) {
  788. ast_cond_wait(&subscription->join_cond,
  789. ao2_object_get_lockaddr(subscription));
  790. }
  791. ao2_unlock(subscription);
  792. }
  793. }
  794. int stasis_subscription_is_done(struct stasis_subscription *subscription)
  795. {
  796. if (subscription) {
  797. int ret;
  798. ao2_lock(subscription);
  799. ret = subscription->final_message_rxed;
  800. ao2_unlock(subscription);
  801. return ret;
  802. }
  803. /* Null subscription is about as done as you can get */
  804. return 1;
  805. }
  806. struct stasis_subscription *stasis_unsubscribe_and_join(
  807. struct stasis_subscription *subscription)
  808. {
  809. if (!subscription) {
  810. return NULL;
  811. }
  812. /* Bump refcount to hold it past the unsubscribe */
  813. ao2_ref(subscription, +1);
  814. stasis_unsubscribe(subscription);
  815. stasis_subscription_join(subscription);
  816. /* Now decrement the refcount back */
  817. ao2_cleanup(subscription);
  818. return NULL;
  819. }
  820. int stasis_subscription_is_subscribed(const struct stasis_subscription *sub)
  821. {
  822. if (sub) {
  823. size_t i;
  824. struct stasis_topic *topic = sub->topic;
  825. ao2_lock(topic);
  826. for (i = 0; i < AST_VECTOR_SIZE(&topic->subscribers); ++i) {
  827. if (AST_VECTOR_GET(&topic->subscribers, i) == sub) {
  828. ao2_unlock(topic);
  829. return 1;
  830. }
  831. }
  832. ao2_unlock(topic);
  833. }
  834. return 0;
  835. }
  836. const char *stasis_subscription_uniqueid(const struct stasis_subscription *sub)
  837. {
  838. return sub->uniqueid;
  839. }
  840. int stasis_subscription_final_message(struct stasis_subscription *sub, struct stasis_message *msg)
  841. {
  842. struct stasis_subscription_change *change;
  843. if (stasis_message_type(msg) != stasis_subscription_change_type()) {
  844. return 0;
  845. }
  846. change = stasis_message_data(msg);
  847. if (strcmp("Unsubscribe", change->description)) {
  848. return 0;
  849. }
  850. if (strcmp(stasis_subscription_uniqueid(sub), change->uniqueid)) {
  851. return 0;
  852. }
  853. return 1;
  854. }
  855. /*!
  856. * \brief Add a subscriber to a topic.
  857. * \param topic Topic
  858. * \param sub Subscriber
  859. * \return 0 on success
  860. * \return Non-zero on error
  861. */
  862. static int topic_add_subscription(struct stasis_topic *topic, struct stasis_subscription *sub)
  863. {
  864. size_t idx;
  865. ao2_lock(topic);
  866. /* The reference from the topic to the subscription is shared with
  867. * the owner of the subscription, which will explicitly unsubscribe
  868. * to release it.
  869. *
  870. * If we bumped the refcount here, the owner would have to unsubscribe
  871. * and cleanup, which is a bit awkward. */
  872. AST_VECTOR_APPEND(&topic->subscribers, sub);
  873. for (idx = 0; idx < AST_VECTOR_SIZE(&topic->upstream_topics); ++idx) {
  874. topic_add_subscription(
  875. AST_VECTOR_GET(&topic->upstream_topics, idx), sub);
  876. }
  877. #ifdef AST_DEVMODE
  878. topic->statistics->subscriber_count += 1;
  879. #endif
  880. ao2_unlock(topic);
  881. return 0;
  882. }
  883. static int topic_remove_subscription(struct stasis_topic *topic, struct stasis_subscription *sub)
  884. {
  885. size_t idx;
  886. int res;
  887. ao2_lock(topic);
  888. for (idx = 0; idx < AST_VECTOR_SIZE(&topic->upstream_topics); ++idx) {
  889. topic_remove_subscription(
  890. AST_VECTOR_GET(&topic->upstream_topics, idx), sub);
  891. }
  892. res = AST_VECTOR_REMOVE_ELEM_UNORDERED(&topic->subscribers, sub,
  893. AST_VECTOR_ELEM_CLEANUP_NOOP);
  894. #ifdef AST_DEVMODE
  895. if (!res) {
  896. topic->statistics->subscriber_count -= 1;
  897. }
  898. #endif
  899. ao2_unlock(topic);
  900. return res;
  901. }
  902. /*!
  903. * \internal \brief Dispatch a message to a subscriber asynchronously
  904. * \param local \ref ast_taskprocessor_local object
  905. * \return 0
  906. */
  907. static int dispatch_exec_async(struct ast_taskprocessor_local *local)
  908. {
  909. struct stasis_subscription *sub = local->local_data;
  910. struct stasis_message *message = local->data;
  911. subscription_invoke(sub, message);
  912. ao2_cleanup(message);
  913. return 0;
  914. }
  915. /*!
  916. * \internal \brief Data passed to \ref dispatch_exec_sync to synchronize
  917. * a published message to a subscriber
  918. */
  919. struct sync_task_data {
  920. ast_mutex_t lock;
  921. ast_cond_t cond;
  922. int complete;
  923. void *task_data;
  924. };
  925. /*!
  926. * \internal \brief Dispatch a message to a subscriber synchronously
  927. * \param local \ref ast_taskprocessor_local object
  928. * \return 0
  929. */
  930. static int dispatch_exec_sync(struct ast_taskprocessor_local *local)
  931. {
  932. struct stasis_subscription *sub = local->local_data;
  933. struct sync_task_data *std = local->data;
  934. struct stasis_message *message = std->task_data;
  935. subscription_invoke(sub, message);
  936. ao2_cleanup(message);
  937. ast_mutex_lock(&std->lock);
  938. std->complete = 1;
  939. ast_cond_signal(&std->cond);
  940. ast_mutex_unlock(&std->lock);
  941. return 0;
  942. }
  943. /*!
  944. * \internal \brief Dispatch a message to a subscriber
  945. * \param sub The subscriber to dispatch to
  946. * \param message The message to send
  947. * \param synchronous If non-zero, synchronize on the subscriber receiving
  948. * the message
  949. * \retval 0 if message was not dispatched
  950. * \retval 1 if message was dispatched
  951. */
  952. static unsigned int dispatch_message(struct stasis_subscription *sub,
  953. struct stasis_message *message,
  954. int synchronous)
  955. {
  956. int is_final = stasis_subscription_final_message(sub, message);
  957. /*
  958. * The 'do while' gives us an easy way to skip remaining logic once
  959. * we determine the message should be accepted.
  960. * The code looks more verbose than it needs to be but it optimizes
  961. * down very nicely. It's just easier to understand and debug this way.
  962. */
  963. do {
  964. struct stasis_message_type *message_type = stasis_message_type(message);
  965. int type_id = stasis_message_type_id(message_type);
  966. int type_filter_specified = 0;
  967. int formatter_filter_specified = 0;
  968. int type_filter_passed = 0;
  969. int formatter_filter_passed = 0;
  970. /* We always accept final messages so only run the filter logic if not final */
  971. if (is_final) {
  972. break;
  973. }
  974. type_filter_specified = sub->filter & STASIS_SUBSCRIPTION_FILTER_SELECTIVE;
  975. formatter_filter_specified = sub->accepted_formatters != STASIS_SUBSCRIPTION_FORMATTER_NONE;
  976. /* Accept if no filters of either type were specified */
  977. if (!type_filter_specified && !formatter_filter_specified) {
  978. break;
  979. }
  980. type_filter_passed = type_filter_specified
  981. && type_id < AST_VECTOR_SIZE(&sub->accepted_message_types)
  982. && AST_VECTOR_GET(&sub->accepted_message_types, type_id);
  983. /*
  984. * Since the type and formatter filters are OR'd, we can skip
  985. * the formatter check if the type check passes.
  986. */
  987. if (type_filter_passed) {
  988. break;
  989. }
  990. formatter_filter_passed = formatter_filter_specified
  991. && (sub->accepted_formatters & stasis_message_type_available_formatters(message_type));
  992. if (formatter_filter_passed) {
  993. break;
  994. }
  995. #ifdef AST_DEVMODE
  996. ast_atomic_fetchadd_int(&sub->statistics->messages_dropped, +1);
  997. #endif
  998. return 0;
  999. } while (0);
  1000. #ifdef AST_DEVMODE
  1001. ast_atomic_fetchadd_int(&sub->statistics->messages_passed, +1);
  1002. #endif
  1003. if (!sub->mailbox) {
  1004. /* Dispatch directly */
  1005. subscription_invoke(sub, message);
  1006. return 1;
  1007. }
  1008. /* Bump the message for the taskprocessor push. This will get de-ref'd
  1009. * by the task processor callback.
  1010. */
  1011. ao2_bump(message);
  1012. if (!synchronous) {
  1013. if (ast_taskprocessor_push_local(sub->mailbox, dispatch_exec_async, message)) {
  1014. /* Push failed; ugh. */
  1015. ast_log(LOG_ERROR, "Dropping async dispatch\n");
  1016. ao2_cleanup(message);
  1017. return 0;
  1018. }
  1019. } else {
  1020. struct sync_task_data std;
  1021. ast_mutex_init(&std.lock);
  1022. ast_cond_init(&std.cond, NULL);
  1023. std.complete = 0;
  1024. std.task_data = message;
  1025. if (ast_taskprocessor_push_local(sub->mailbox, dispatch_exec_sync, &std)) {
  1026. /* Push failed; ugh. */
  1027. ast_log(LOG_ERROR, "Dropping sync dispatch\n");
  1028. ao2_cleanup(message);
  1029. ast_mutex_destroy(&std.lock);
  1030. ast_cond_destroy(&std.cond);
  1031. return 0;
  1032. }
  1033. ast_mutex_lock(&std.lock);
  1034. while (!std.complete) {
  1035. ast_cond_wait(&std.cond, &std.lock);
  1036. }
  1037. ast_mutex_unlock(&std.lock);
  1038. ast_mutex_destroy(&std.lock);
  1039. ast_cond_destroy(&std.cond);
  1040. }
  1041. return 1;
  1042. }
  1043. /*!
  1044. * \internal \brief Publish a message to a topic's subscribers
  1045. * \brief topic The topic to publish to
  1046. * \brief message The message to publish
  1047. * \brief sync_sub An optional subscriber of the topic to publish synchronously
  1048. * to
  1049. */
  1050. static void publish_msg(struct stasis_topic *topic,
  1051. struct stasis_message *message, struct stasis_subscription *sync_sub)
  1052. {
  1053. size_t i;
  1054. unsigned int dispatched = 0;
  1055. #ifdef AST_DEVMODE
  1056. int message_type_id = stasis_message_type_id(stasis_message_type(message));
  1057. struct stasis_message_type_statistics *statistics;
  1058. struct timeval start;
  1059. long elapsed;
  1060. #endif
  1061. ast_assert(topic != NULL);
  1062. ast_assert(message != NULL);
  1063. #ifdef AST_DEVMODE
  1064. ast_mutex_lock(&message_type_statistics_lock);
  1065. if (message_type_id >= AST_VECTOR_SIZE(&message_type_statistics)) {
  1066. struct stasis_message_type_statistics new_statistics = {
  1067. .published = 0,
  1068. };
  1069. if (AST_VECTOR_REPLACE(&message_type_statistics, message_type_id, new_statistics)) {
  1070. ast_mutex_unlock(&message_type_statistics_lock);
  1071. return;
  1072. }
  1073. }
  1074. statistics = AST_VECTOR_GET_ADDR(&message_type_statistics, message_type_id);
  1075. statistics->message_type = stasis_message_type(message);
  1076. ast_mutex_unlock(&message_type_statistics_lock);
  1077. ast_atomic_fetchadd_int(&statistics->published, +1);
  1078. #endif
  1079. /* If there are no subscribers don't bother */
  1080. if (!stasis_topic_subscribers(topic)) {
  1081. #ifdef AST_DEVMODE
  1082. ast_atomic_fetchadd_int(&statistics->unused, +1);
  1083. ast_atomic_fetchadd_int(&topic->statistics->messages_not_dispatched, +1);
  1084. #endif
  1085. return;
  1086. }
  1087. /*
  1088. * The topic may be unref'ed by the subscription invocation.
  1089. * Make sure we hold onto a reference while dispatching.
  1090. */
  1091. ao2_ref(topic, +1);
  1092. #ifdef AST_DEVMODE
  1093. start = ast_tvnow();
  1094. #endif
  1095. ao2_lock(topic);
  1096. for (i = 0; i < AST_VECTOR_SIZE(&topic->subscribers); ++i) {
  1097. struct stasis_subscription *sub = AST_VECTOR_GET(&topic->subscribers, i);
  1098. ast_assert(sub != NULL);
  1099. dispatched += dispatch_message(sub, message, (sub == sync_sub));
  1100. }
  1101. ao2_unlock(topic);
  1102. #ifdef AST_DEVMODE
  1103. elapsed = ast_tvdiff_ms(ast_tvnow(), start);
  1104. if (elapsed > topic->statistics->highest_time_dispatched) {
  1105. topic->statistics->highest_time_dispatched = elapsed;
  1106. }
  1107. if (elapsed < topic->statistics->lowest_time_dispatched) {
  1108. topic->statistics->lowest_time_dispatched = elapsed;
  1109. }
  1110. if (dispatched) {
  1111. ast_atomic_fetchadd_int(&topic->statistics->messages_dispatched, +1);
  1112. } else {
  1113. ast_atomic_fetchadd_int(&statistics->unused, +1);
  1114. ast_atomic_fetchadd_int(&topic->statistics->messages_not_dispatched, +1);
  1115. }
  1116. #endif
  1117. ao2_ref(topic, -1);
  1118. }
  1119. void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
  1120. {
  1121. publish_msg(topic, message, NULL);
  1122. }
  1123. void stasis_publish_sync(struct stasis_subscription *sub, struct stasis_message *message)
  1124. {
  1125. ast_assert(sub != NULL);
  1126. publish_msg(sub->topic, message, sub);
  1127. }
  1128. /*!
  1129. * \brief Forwarding information
  1130. *
  1131. * Any message posted to \a from_topic is forwarded to \a to_topic.
  1132. *
  1133. * In cases where both the \a from_topic and \a to_topic need to be locked,
  1134. * always lock the \a to_topic first, then the \a from_topic. Lest you deadlock.
  1135. */
  1136. struct stasis_forward {
  1137. /*! Originating topic */
  1138. struct stasis_topic *from_topic;
  1139. /*! Destination topic */
  1140. struct stasis_topic *to_topic;
  1141. };
  1142. static void forward_dtor(void *obj)
  1143. {
  1144. struct stasis_forward *forward = obj;
  1145. ao2_cleanup(forward->from_topic);
  1146. forward->from_topic = NULL;
  1147. ao2_cleanup(forward->to_topic);
  1148. forward->to_topic = NULL;
  1149. }
  1150. struct stasis_forward *stasis_forward_cancel(struct stasis_forward *forward)
  1151. {
  1152. int idx;
  1153. struct stasis_topic *from;
  1154. struct stasis_topic *to;
  1155. if (!forward) {
  1156. return NULL;
  1157. }
  1158. from = forward->from_topic;
  1159. to = forward->to_topic;
  1160. if (from && to) {
  1161. topic_lock_both(to, from);
  1162. AST_VECTOR_REMOVE_ELEM_UNORDERED(&to->upstream_topics, from,
  1163. AST_VECTOR_ELEM_CLEANUP_NOOP);
  1164. for (idx = 0; idx < AST_VECTOR_SIZE(&to->subscribers); ++idx) {
  1165. topic_remove_subscription(from, AST_VECTOR_GET(&to->subscribers, idx));
  1166. }
  1167. ao2_unlock(from);
  1168. ao2_unlock(to);
  1169. }
  1170. ao2_cleanup(forward);
  1171. return NULL;
  1172. }
  1173. struct stasis_forward *stasis_forward_all(struct stasis_topic *from_topic,
  1174. struct stasis_topic *to_topic)
  1175. {
  1176. int res;
  1177. size_t idx;
  1178. struct stasis_forward *forward;
  1179. if (!from_topic || !to_topic) {
  1180. return NULL;
  1181. }
  1182. forward = ao2_alloc_options(sizeof(*forward), forward_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
  1183. if (!forward) {
  1184. return NULL;
  1185. }
  1186. /* Forwards to ourselves are implicit. */
  1187. if (to_topic == from_topic) {
  1188. return forward;
  1189. }
  1190. forward->from_topic = ao2_bump(from_topic);
  1191. forward->to_topic = ao2_bump(to_topic);
  1192. topic_lock_both(to_topic, from_topic);
  1193. res = AST_VECTOR_APPEND(&to_topic->upstream_topics, from_topic);
  1194. if (res != 0) {
  1195. ao2_unlock(from_topic);
  1196. ao2_unlock(to_topic);
  1197. ao2_ref(forward, -1);
  1198. return NULL;
  1199. }
  1200. for (idx = 0; idx < AST_VECTOR_SIZE(&to_topic->subscribers); ++idx) {
  1201. topic_add_subscription(from_topic, AST_VECTOR_GET(&to_topic->subscribers, idx));
  1202. }
  1203. ao2_unlock(from_topic);
  1204. ao2_unlock(to_topic);
  1205. return forward;
  1206. }
  1207. static void subscription_change_dtor(void *obj)
  1208. {
  1209. struct stasis_subscription_change *change = obj;
  1210. ao2_cleanup(change->topic);
  1211. }
  1212. static struct stasis_subscription_change *subscription_change_alloc(struct stasis_topic *topic, const char *uniqueid, const char *description)
  1213. {
  1214. size_t description_len = strlen(description) + 1;
  1215. struct stasis_subscription_change *change;
  1216. change = ao2_alloc_options(sizeof(*change) + description_len + strlen(uniqueid) + 1,
  1217. subscription_change_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
  1218. if (!change) {
  1219. return NULL;
  1220. }
  1221. strcpy(change->description, description); /* SAFE */
  1222. change->uniqueid = change->description + description_len;
  1223. strcpy(change->uniqueid, uniqueid); /* SAFE */
  1224. ao2_ref(topic, +1);
  1225. change->topic = topic;
  1226. return change;
  1227. }
  1228. static void send_subscription_subscribe(struct stasis_topic *topic, struct stasis_subscription *sub)
  1229. {
  1230. struct stasis_subscription_change *change;
  1231. struct stasis_message *msg;
  1232. /* This assumes that we have already unsubscribed */
  1233. ast_assert(stasis_subscription_is_subscribed(sub));
  1234. if (!stasis_subscription_change_type()) {
  1235. return;
  1236. }
  1237. change = subscription_change_alloc(topic, sub->uniqueid, "Subscribe");
  1238. if (!change) {
  1239. return;
  1240. }
  1241. msg = stasis_message_create(stasis_subscription_change_type(), change);
  1242. if (!msg) {
  1243. ao2_cleanup(change);
  1244. return;
  1245. }
  1246. stasis_publish(topic, msg);
  1247. ao2_cleanup(msg);
  1248. ao2_cleanup(change);
  1249. }
  1250. static void send_subscription_unsubscribe(struct stasis_topic *topic,
  1251. struct stasis_subscription *sub)
  1252. {
  1253. struct stasis_subscription_change *change;
  1254. struct stasis_message *msg;
  1255. /* This assumes that we have already unsubscribed */
  1256. ast_assert(!stasis_subscription_is_subscribed(sub));
  1257. if (!stasis_subscription_change_type()) {
  1258. return;
  1259. }
  1260. change = subscription_change_alloc(topic, sub->uniqueid, "Unsubscribe");
  1261. if (!change) {
  1262. return;
  1263. }
  1264. msg = stasis_message_create(stasis_subscription_change_type(), change);
  1265. if (!msg) {
  1266. ao2_cleanup(change);
  1267. return;
  1268. }
  1269. stasis_publish(topic, msg);
  1270. /* Now we have to dispatch to the subscription itself */
  1271. dispatch_message(sub, msg, 0);
  1272. ao2_cleanup(msg);
  1273. ao2_cleanup(change);
  1274. }
  1275. struct topic_pool_entry {
  1276. struct stasis_forward *forward;
  1277. struct stasis_topic *topic;
  1278. };
  1279. static void topic_pool_entry_dtor(void *obj)
  1280. {
  1281. struct topic_pool_entry *entry = obj;
  1282. entry->forward = stasis_forward_cancel(entry->forward);
  1283. ao2_cleanup(entry->topic);
  1284. entry->topic = NULL;
  1285. }
  1286. static struct topic_pool_entry *topic_pool_entry_alloc(void)
  1287. {
  1288. return ao2_alloc_options(sizeof(struct topic_pool_entry), topic_pool_entry_dtor,
  1289. AO2_ALLOC_OPT_LOCK_NOLOCK);
  1290. }
  1291. struct stasis_topic_pool {
  1292. struct ao2_container *pool_container;
  1293. struct stasis_topic *pool_topic;
  1294. };
  1295. static void topic_pool_dtor(void *obj)
  1296. {
  1297. struct stasis_topic_pool *pool = obj;
  1298. #ifdef AO2_DEBUG
  1299. {
  1300. char *container_name =
  1301. ast_alloca(strlen(stasis_topic_name(pool->pool_topic)) + strlen("-pool") + 1);
  1302. sprintf(container_name, "%s-pool", stasis_topic_name(pool->pool_topic));
  1303. ao2_container_unregister(container_name);
  1304. }
  1305. #endif
  1306. ao2_cleanup(pool->pool_container);
  1307. pool->pool_container = NULL;
  1308. ao2_cleanup(pool->pool_topic);
  1309. pool->pool_topic = NULL;
  1310. }
  1311. static int topic_pool_entry_hash(const void *obj, const int flags)
  1312. {
  1313. const struct topic_pool_entry *object;
  1314. const char *key;
  1315. switch (flags & OBJ_SEARCH_MASK) {
  1316. case OBJ_SEARCH_KEY:
  1317. key = obj;
  1318. break;
  1319. case OBJ_SEARCH_OBJECT:
  1320. object = obj;
  1321. key = stasis_topic_name(object->topic);
  1322. break;
  1323. default:
  1324. /* Hash can only work on something with a full key. */
  1325. ast_assert(0);
  1326. return 0;
  1327. }
  1328. return ast_str_case_hash(key);
  1329. }
  1330. static int topic_pool_entry_cmp(void *obj, void *arg, int flags)
  1331. {
  1332. const struct topic_pool_entry *object_left = obj;
  1333. const struct topic_pool_entry *object_right = arg;
  1334. const char *right_key = arg;
  1335. int cmp;
  1336. switch (flags & OBJ_SEARCH_MASK) {
  1337. case OBJ_SEARCH_OBJECT:
  1338. right_key = stasis_topic_name(object_right->topic);
  1339. /* Fall through */
  1340. case OBJ_SEARCH_KEY:
  1341. cmp = strcasecmp(stasis_topic_name(object_left->topic), right_key);
  1342. break;
  1343. case OBJ_SEARCH_PARTIAL_KEY:
  1344. /* Not supported by container */
  1345. ast_assert(0);
  1346. cmp = -1;
  1347. break;
  1348. default:
  1349. /*
  1350. * What arg points to is specific to this traversal callback
  1351. * and has no special meaning to astobj2.
  1352. */
  1353. cmp = 0;
  1354. break;
  1355. }
  1356. if (cmp) {
  1357. return 0;
  1358. }
  1359. /*
  1360. * At this point the traversal callback is identical to a sorted
  1361. * container.
  1362. */
  1363. return CMP_MATCH;
  1364. }
  1365. #ifdef AO2_DEBUG
  1366. static void topic_pool_prnt_obj(void *v_obj, void *where, ao2_prnt_fn *prnt)
  1367. {
  1368. struct topic_pool_entry *entry = v_obj;
  1369. if (!entry) {
  1370. return;
  1371. }
  1372. prnt(where, "%s", stasis_topic_name(entry->topic));
  1373. }
  1374. #endif
  1375. struct stasis_topic_pool *stasis_topic_pool_create(struct stasis_topic *pooled_topic)
  1376. {
  1377. struct stasis_topic_pool *pool;
  1378. pool = ao2_alloc_options(sizeof(*pool), topic_pool_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
  1379. if (!pool) {
  1380. return NULL;
  1381. }
  1382. pool->pool_container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
  1383. TOPIC_POOL_BUCKETS, topic_pool_entry_hash, NULL, topic_pool_entry_cmp);
  1384. if (!pool->pool_container) {
  1385. ao2_cleanup(pool);
  1386. return NULL;
  1387. }
  1388. #ifdef AO2_DEBUG
  1389. {
  1390. char *container_name =
  1391. ast_alloca(strlen(stasis_topic_name(pooled_topic)) + strlen("-pool") + 1);
  1392. sprintf(container_name, "%s-pool", stasis_topic_name(pooled_topic));
  1393. ao2_container_register(container_name, pool->pool_container, topic_pool_prnt_obj);
  1394. }
  1395. #endif
  1396. ao2_ref(pooled_topic, +1);
  1397. pool->pool_topic = pooled_topic;
  1398. return pool;
  1399. }
  1400. void stasis_topic_pool_delete_topic(struct stasis_topic_pool *pool, const char *topic_name)
  1401. {
  1402. ao2_find(pool->pool_container, topic_name, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
  1403. }
  1404. struct stasis_topic *stasis_topic_pool_get_topic(struct stasis_topic_pool *pool, const char *topic_name)
  1405. {
  1406. RAII_VAR(struct topic_pool_entry *, topic_pool_entry, NULL, ao2_cleanup);
  1407. SCOPED_AO2LOCK(topic_container_lock, pool->pool_container);
  1408. topic_pool_entry = ao2_find(pool->pool_container, topic_name, OBJ_SEARCH_KEY | OBJ_NOLOCK);
  1409. if (topic_pool_entry) {
  1410. return topic_pool_entry->topic;
  1411. }
  1412. topic_pool_entry = topic_pool_entry_alloc();
  1413. if (!topic_pool_entry) {
  1414. return NULL;
  1415. }
  1416. topic_pool_entry->topic = stasis_topic_create(topic_name);
  1417. if (!topic_pool_entry->topic) {
  1418. return NULL;
  1419. }
  1420. topic_pool_entry->forward = stasis_forward_all(topic_pool_entry->topic, pool->pool_topic);
  1421. if (!topic_pool_entry->forward) {
  1422. return NULL;
  1423. }
  1424. if (!ao2_link_flags(pool->pool_container, topic_pool_entry, OBJ_NOLOCK)) {
  1425. return NULL;
  1426. }
  1427. return topic_pool_entry->topic;
  1428. }
  1429. int stasis_topic_pool_topic_exists(const struct stasis_topic_pool *pool, const char *topic_name)
  1430. {
  1431. struct topic_pool_entry *topic_pool_entry;
  1432. topic_pool_entry = ao2_find(pool->pool_container, topic_name, OBJ_SEARCH_KEY);
  1433. if (!topic_pool_entry) {
  1434. return 0;
  1435. }
  1436. ao2_ref(topic_pool_entry, -1);
  1437. return 1;
  1438. }
  1439. void stasis_log_bad_type_access(const char *name)
  1440. {
  1441. #ifdef AST_DEVMODE
  1442. if (!stasis_message_type_declined(name)) {
  1443. ast_log(LOG_ERROR, "Use of %s() before init/after destruction\n", name);
  1444. }
  1445. #endif
  1446. }
  1447. /*! \brief A multi object blob data structure to carry user event stasis messages */
  1448. struct ast_multi_object_blob {
  1449. struct ast_json *blob; /*< A blob of JSON data */
  1450. AST_VECTOR(, void *) snapshots[STASIS_UMOS_MAX]; /*< Vector of snapshots for each type */
  1451. };
  1452. /*!
  1453. * \internal
  1454. * \brief Destructor for \ref ast_multi_object_blob objects
  1455. */
  1456. static void multi_object_blob_dtor(void *obj)
  1457. {
  1458. struct ast_multi_object_blob *multi = obj;
  1459. int type;
  1460. int i;
  1461. for (type = 0; type < STASIS_UMOS_MAX; ++type) {
  1462. for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
  1463. ao2_cleanup(AST_VECTOR_GET(&multi->snapshots[type], i));
  1464. }
  1465. AST_VECTOR_FREE(&multi->snapshots[type]);
  1466. }
  1467. ast_json_unref(multi->blob);
  1468. }
  1469. /*! \brief Create a stasis user event multi object blob */
  1470. struct ast_multi_object_blob *ast_multi_object_blob_create(struct ast_json *blob)
  1471. {
  1472. int type;
  1473. struct ast_multi_object_blob *multi;
  1474. ast_assert(blob != NULL);
  1475. multi = ao2_alloc(sizeof(*multi), multi_object_blob_dtor);
  1476. if (!multi) {
  1477. return NULL;
  1478. }
  1479. for (type = 0; type < STASIS_UMOS_MAX; ++type) {
  1480. if (AST_VECTOR_INIT(&multi->snapshots[type], 0)) {
  1481. ao2_ref(multi, -1);
  1482. return NULL;
  1483. }
  1484. }
  1485. multi->blob = ast_json_ref(blob);
  1486. return multi;
  1487. }
  1488. /*! \brief Add an object (snapshot) to the blob */
  1489. void ast_multi_object_blob_add(struct ast_multi_object_blob *multi,
  1490. enum stasis_user_multi_object_snapshot_type type, void *object)
  1491. {
  1492. if (!multi || !object || AST_VECTOR_APPEND(&multi->snapshots[type], object)) {
  1493. ao2_cleanup(object);
  1494. }
  1495. }
  1496. /*! \brief Publish single channel user event (for app_userevent compatibility) */
  1497. void ast_multi_object_blob_single_channel_publish(struct ast_channel *chan,
  1498. struct stasis_message_type *type, struct ast_json *blob)
  1499. {
  1500. struct stasis_message *message;
  1501. struct ast_channel_snapshot *channel_snapshot;
  1502. struct ast_multi_object_blob *multi;
  1503. if (!type) {
  1504. return;
  1505. }
  1506. multi = ast_multi_object_blob_create(blob);
  1507. if (!multi) {
  1508. return;
  1509. }
  1510. channel_snapshot = ast_channel_snapshot_create(chan);
  1511. if (!channel_snapshot) {
  1512. ao2_ref(multi, -1);
  1513. return;
  1514. }
  1515. /* this call steals the channel_snapshot reference */
  1516. ast_multi_object_blob_add(multi, STASIS_UMOS_CHANNEL, channel_snapshot);
  1517. message = stasis_message_create(type, multi);
  1518. ao2_ref(multi, -1);
  1519. if (message) {
  1520. /* app_userevent still publishes to channel */
  1521. stasis_publish(ast_channel_topic(chan), message);
  1522. ao2_ref(message, -1);
  1523. }
  1524. }
  1525. /*! \internal \brief convert multi object blob to ari json */
  1526. static struct ast_json *multi_user_event_to_json(
  1527. struct stasis_message *message,
  1528. const struct stasis_message_sanitizer *sanitize)
  1529. {
  1530. struct ast_json *out;
  1531. struct ast_multi_object_blob *multi = stasis_message_data(message);
  1532. struct ast_json *blob = multi->blob;
  1533. const struct timeval *tv = stasis_message_timestamp(message);
  1534. enum stasis_user_multi_object_snapshot_type type;
  1535. int i;
  1536. out = ast_json_object_create();
  1537. if (!out) {
  1538. return NULL;
  1539. }
  1540. ast_json_object_set(out, "type", ast_json_string_create("ChannelUserevent"));
  1541. ast_json_object_set(out, "timestamp", ast_json_timeval(*tv, NULL));
  1542. ast_json_object_set(out, "eventname", ast_json_string_create(ast_json_string_get((ast_json_object_get(blob, "eventname")))));
  1543. ast_json_object_set(out, "userevent", ast_json_deep_copy(blob));
  1544. for (type = 0; type < STASIS_UMOS_MAX; ++type) {
  1545. for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
  1546. struct ast_json *json_object = NULL;
  1547. char *name = NULL;
  1548. void *snapshot = AST_VECTOR_GET(&multi->snapshots[type], i);
  1549. switch (type) {
  1550. case STASIS_UMOS_CHANNEL:
  1551. json_object = ast_channel_snapshot_to_json(snapshot, sanitize);
  1552. name = "channel";
  1553. break;
  1554. case STASIS_UMOS_BRIDGE:
  1555. json_object = ast_bridge_snapshot_to_json(snapshot, sanitize);
  1556. name = "bridge";
  1557. break;
  1558. case STASIS_UMOS_ENDPOINT:
  1559. json_object = ast_endpoint_snapshot_to_json(snapshot, sanitize);
  1560. name = "endpoint";
  1561. break;
  1562. }
  1563. if (json_object) {
  1564. ast_json_object_set(out, name, json_object);
  1565. }
  1566. }
  1567. }
  1568. return out;
  1569. }
  1570. /*! \internal \brief convert multi object blob to ami string */
  1571. static struct ast_str *multi_object_blob_to_ami(void *obj)
  1572. {
  1573. struct ast_str *ami_str=ast_str_create(1024);
  1574. struct ast_str *ami_snapshot;
  1575. const struct ast_multi_object_blob *multi = obj;
  1576. enum stasis_user_multi_object_snapshot_type type;
  1577. int i;
  1578. if (!ami_str) {
  1579. return NULL;
  1580. }
  1581. if (!multi) {
  1582. ast_free(ami_str);
  1583. return NULL;
  1584. }
  1585. for (type = 0; type < STASIS_UMOS_MAX; ++type) {
  1586. for (i = 0; i < AST_VECTOR_SIZE(&multi->snapshots[type]); ++i) {
  1587. char *name = NULL;
  1588. void *snapshot = AST_VECTOR_GET(&multi->snapshots[type], i);
  1589. ami_snapshot = NULL;
  1590. if (i > 0) {
  1591. ast_asprintf(&name, "%d", i + 1);
  1592. }
  1593. switch (type) {
  1594. case STASIS_UMOS_CHANNEL:
  1595. ami_snapshot = ast_manager_build_channel_state_string_prefix(snapshot, name ?: "");
  1596. break;
  1597. case STASIS_UMOS_BRIDGE:
  1598. ami_snapshot = ast_manager_build_bridge_state_string_prefix(snapshot, name ?: "");
  1599. break;
  1600. case STASIS_UMOS_ENDPOINT:
  1601. /* currently not sending endpoint snapshots to AMI */
  1602. break;
  1603. }
  1604. if (ami_snapshot) {
  1605. ast_str_append(&ami_str, 0, "%s", ast_str_buffer(ami_snapshot));
  1606. ast_free(ami_snapshot);
  1607. }
  1608. ast_free(name);
  1609. }
  1610. }
  1611. return ami_str;
  1612. }
  1613. /*! \internal \brief Callback to pass only user defined parameters from blob */
  1614. static int userevent_exclusion_cb(const char *key)
  1615. {
  1616. if (!strcmp("eventname", key)) {
  1617. return 1;
  1618. }
  1619. return 0;
  1620. }
  1621. static struct ast_manager_event_blob *multi_user_event_to_ami(
  1622. struct stasis_message *message)
  1623. {
  1624. RAII_VAR(struct ast_str *, object_string, NULL, ast_free);
  1625. RAII_VAR(struct ast_str *, body, NULL, ast_free);
  1626. struct ast_multi_object_blob *multi = stasis_message_data(message);
  1627. const char *eventname;
  1628. eventname = ast_json_string_get(ast_json_object_get(multi->blob, "eventname"));
  1629. body = ast_manager_str_from_json_object(multi->blob, userevent_exclusion_cb);
  1630. object_string = multi_object_blob_to_ami(multi);
  1631. if (!object_string || !body) {
  1632. return NULL;
  1633. }
  1634. return ast_manager_event_blob_create(EVENT_FLAG_USER, "UserEvent",
  1635. "%s"
  1636. "UserEvent: %s\r\n"
  1637. "%s",
  1638. ast_str_buffer(object_string),
  1639. eventname,
  1640. ast_str_buffer(body));
  1641. }
  1642. /*! \brief A structure to hold global configuration-related options */
  1643. struct stasis_declined_config {
  1644. /*! The list of message types to decline */
  1645. struct ao2_container *declined;
  1646. };
  1647. /*! \brief Threadpool configuration options */
  1648. struct stasis_threadpool_conf {
  1649. /*! Initial size of the thread pool */
  1650. int initial_size;
  1651. /*! Time, in seconds, before we expire a thread */
  1652. int idle_timeout_sec;
  1653. /*! Maximum number of thread to allow */
  1654. int max_size;
  1655. };
  1656. struct stasis_config {
  1657. /*! Thread pool configuration options */
  1658. struct stasis_threadpool_conf *threadpool_options;
  1659. /*! Declined message types */
  1660. struct stasis_declined_config *declined_message_types;
  1661. };
  1662. static struct aco_type threadpool_option = {
  1663. .type = ACO_GLOBAL,
  1664. .name = "threadpool",
  1665. .item_offset = offsetof(struct stasis_config, threadpool_options),
  1666. .category = "threadpool",
  1667. .category_match = ACO_WHITELIST_EXACT,
  1668. };
  1669. static struct aco_type *threadpool_options[] = ACO_TYPES(&threadpool_option);
  1670. /*! \brief An aco_type structure to link the "declined_message_types" category to the stasis_declined_config type */
  1671. static struct aco_type declined_option = {
  1672. .type = ACO_GLOBAL,
  1673. .name = "declined_message_types",
  1674. .item_offset = offsetof(struct stasis_config, declined_message_types),
  1675. .category_match = ACO_WHITELIST_EXACT,
  1676. .category = "declined_message_types",
  1677. };
  1678. struct aco_type *declined_options[] = ACO_TYPES(&declined_option);
  1679. struct aco_file stasis_conf = {
  1680. .filename = "stasis.conf",
  1681. .types = ACO_TYPES(&declined_option, &threadpool_option),
  1682. };
  1683. /*! \brief A global object container that will contain the stasis_config that gets swapped out on reloads */
  1684. static AO2_GLOBAL_OBJ_STATIC(globals);
  1685. static void *stasis_config_alloc(void);
  1686. /*! \brief Register information about the configs being processed by this module */
  1687. CONFIG_INFO_CORE("stasis", cfg_info, globals, stasis_config_alloc,
  1688. .files = ACO_FILES(&stasis_conf),
  1689. );
  1690. static void stasis_declined_config_destructor(void *obj)
  1691. {
  1692. struct stasis_declined_config *declined = obj;
  1693. ao2_cleanup(declined->declined);
  1694. }
  1695. static void stasis_config_destructor(void *obj)
  1696. {
  1697. struct stasis_config *cfg = obj;
  1698. ao2_cleanup(cfg->declined_message_types);
  1699. ast_free(cfg->threadpool_options);
  1700. }
  1701. static void *stasis_config_alloc(void)
  1702. {
  1703. struct stasis_config *cfg;
  1704. if (!(cfg = ao2_alloc(sizeof(*cfg), stasis_config_destructor))) {
  1705. return NULL;
  1706. }
  1707. cfg->threadpool_options = ast_calloc(1, sizeof(*cfg->threadpool_options));
  1708. if (!cfg->threadpool_options) {
  1709. ao2_ref(cfg, -1);
  1710. return NULL;
  1711. }
  1712. cfg->declined_message_types = ao2_alloc(sizeof(*cfg->declined_message_types),
  1713. stasis_declined_config_destructor);
  1714. if (!cfg->declined_message_types) {
  1715. ao2_ref(cfg, -1);
  1716. return NULL;
  1717. }
  1718. cfg->declined_message_types->declined = ast_str_container_alloc(13);
  1719. if (!cfg->declined_message_types->declined) {
  1720. ao2_ref(cfg, -1);
  1721. return NULL;
  1722. }
  1723. return cfg;
  1724. }
  1725. int stasis_message_type_declined(const char *name)
  1726. {
  1727. struct stasis_config *cfg = ao2_global_obj_ref(globals);
  1728. char *name_in_declined;
  1729. int res;
  1730. if (!cfg || !cfg->declined_message_types) {
  1731. ao2_cleanup(cfg);
  1732. return 0;
  1733. }
  1734. name_in_declined = ao2_find(cfg->declined_message_types->declined, name, OBJ_SEARCH_KEY);
  1735. res = name_in_declined ? 1 : 0;
  1736. ao2_cleanup(name_in_declined);
  1737. ao2_ref(cfg, -1);
  1738. if (res) {
  1739. ast_log(LOG_NOTICE, "Declining to allocate Stasis message type '%s' due to configuration\n", name);
  1740. }
  1741. return res;
  1742. }
  1743. static int declined_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
  1744. {
  1745. struct stasis_declined_config *declined = obj;
  1746. if (ast_strlen_zero(var->value)) {
  1747. return 0;
  1748. }
  1749. if (ast_str_container_add(declined->declined, var->value)) {
  1750. return -1;
  1751. }
  1752. return 0;
  1753. }
  1754. /*!
  1755. * @{ \brief Define multi user event message type(s).
  1756. */
  1757. STASIS_MESSAGE_TYPE_DEFN(ast_multi_user_event_type,
  1758. .to_json = multi_user_event_to_json,
  1759. .to_ami = multi_user_event_to_ami,
  1760. );
  1761. /*! @} */
  1762. #ifdef AST_DEVMODE
  1763. /*!
  1764. * \internal
  1765. * \brief CLI command implementation for 'stasis statistics show subscriptions'
  1766. */
  1767. static char *statistics_show_subscriptions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  1768. {
  1769. struct ao2_iterator iter;
  1770. struct stasis_subscription_statistics *statistics;
  1771. int count = 0;
  1772. int dropped = 0;
  1773. int passed = 0;
  1774. #define FMT_HEADERS "%-64s %10s %10s %16s %16s\n"
  1775. #define FMT_FIELDS "%-64s %10d %10d %16ld %16ld\n"
  1776. #define FMT_FIELDS2 "%-64s %10d %10d\n"
  1777. switch (cmd) {
  1778. case CLI_INIT:
  1779. e->command = "stasis statistics show subscriptions";
  1780. e->usage =
  1781. "Usage: stasis statistics show subscriptions\n"
  1782. " Shows a list of subscriptions and their general statistics\n";
  1783. return NULL;
  1784. case CLI_GENERATE:
  1785. return NULL;
  1786. }
  1787. if (a->argc != e->args) {
  1788. return CLI_SHOWUSAGE;
  1789. }
  1790. ast_cli(a->fd, "\n" FMT_HEADERS, "Subscription", "Dropped", "Passed", "Lowest Invoke", "Highest Invoke");
  1791. iter = ao2_iterator_init(subscription_statistics, 0);
  1792. while ((statistics = ao2_iterator_next(&iter))) {
  1793. ast_cli(a->fd, FMT_FIELDS, statistics->uniqueid, statistics->messages_dropped, statistics->messages_passed,
  1794. statistics->lowest_time_invoked, statistics->highest_time_invoked);
  1795. dropped += statistics->messages_dropped;
  1796. passed += statistics->messages_passed;
  1797. ao2_ref(statistics, -1);
  1798. ++count;
  1799. }
  1800. ao2_iterator_destroy(&iter);
  1801. ast_cli(a->fd, FMT_FIELDS2, "Total", dropped, passed);
  1802. ast_cli(a->fd, "\n%d subscriptions\n\n", count);
  1803. #undef FMT_HEADERS
  1804. #undef FMT_FIELDS
  1805. #undef FMT_FIELDS2
  1806. return CLI_SUCCESS;
  1807. }
  1808. /*!
  1809. * \internal
  1810. * \brief CLI tab completion for subscription statistics names
  1811. */
  1812. static char *subscription_statistics_complete_name(const char *word, int state)
  1813. {
  1814. struct stasis_subscription_statistics *statistics;
  1815. struct ao2_iterator it_statistics;
  1816. int wordlen = strlen(word);
  1817. int which = 0;
  1818. char *result = NULL;
  1819. it_statistics = ao2_iterator_init(subscription_statistics, 0);
  1820. while ((statistics = ao2_iterator_next(&it_statistics))) {
  1821. if (!strncasecmp(word, statistics->uniqueid, wordlen)
  1822. && ++which > state) {
  1823. result = ast_strdup(statistics->uniqueid);
  1824. }
  1825. ao2_ref(statistics, -1);
  1826. if (result) {
  1827. break;
  1828. }
  1829. }
  1830. ao2_iterator_destroy(&it_statistics);
  1831. return result;
  1832. }
  1833. /*!
  1834. * \internal
  1835. * \brief CLI command implementation for 'stasis statistics show subscription'
  1836. */
  1837. static char *statistics_show_subscription(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  1838. {
  1839. struct stasis_subscription_statistics *statistics;
  1840. switch (cmd) {
  1841. case CLI_INIT:
  1842. e->command = "stasis statistics show subscription";
  1843. e->usage =
  1844. "Usage: stasis statistics show subscription <uniqueid>\n"
  1845. " Show stasis subscription statistics.\n";
  1846. return NULL;
  1847. case CLI_GENERATE:
  1848. if (a->pos == 4) {
  1849. return subscription_statistics_complete_name(a->word, a->n);
  1850. } else {
  1851. return NULL;
  1852. }
  1853. }
  1854. if (a->argc != 5) {
  1855. return CLI_SHOWUSAGE;
  1856. }
  1857. statistics = ao2_find(subscription_statistics, a->argv[4], OBJ_SEARCH_KEY);
  1858. if (!statistics) {
  1859. ast_cli(a->fd, "Specified subscription '%s' does not exist\n", a->argv[4]);
  1860. return CLI_FAILURE;
  1861. }
  1862. ast_cli(a->fd, "Subscription: %s\n", statistics->uniqueid);
  1863. ast_cli(a->fd, "Topic: %s\n", statistics->topic);
  1864. ast_cli(a->fd, "Source filename: %s\n", S_OR(statistics->file, "<unavailable>"));
  1865. ast_cli(a->fd, "Source line number: %d\n", statistics->lineno);
  1866. ast_cli(a->fd, "Source function: %s\n", S_OR(statistics->func, "<unavailable>"));
  1867. ast_cli(a->fd, "Number of messages dropped due to filtering: %d\n", statistics->messages_dropped);
  1868. ast_cli(a->fd, "Number of messages passed to subscriber callback: %d\n", statistics->messages_passed);
  1869. ast_cli(a->fd, "Using mailbox to queue messages: %s\n", statistics->uses_mailbox ? "Yes" : "No");
  1870. ast_cli(a->fd, "Using stasis threadpool for handling messages: %s\n", statistics->uses_threadpool ? "Yes" : "No");
  1871. ast_cli(a->fd, "Lowest amount of time (in milliseconds) spent invoking message: %ld\n", statistics->lowest_time_invoked);
  1872. ast_cli(a->fd, "Highest amount of time (in milliseconds) spent invoking message: %ld\n", statistics->highest_time_invoked);
  1873. ao2_lock(statistics);
  1874. if (statistics->highest_time_message_type) {
  1875. ast_cli(a->fd, "Offender message type for highest invoking time: %s\n", stasis_message_type_name(statistics->highest_time_message_type));
  1876. }
  1877. ao2_unlock(statistics);
  1878. ao2_ref(statistics, -1);
  1879. return CLI_SUCCESS;
  1880. }
  1881. /*!
  1882. * \internal
  1883. * \brief CLI command implementation for 'stasis statistics show topics'
  1884. */
  1885. static char *statistics_show_topics(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  1886. {
  1887. struct ao2_iterator iter;
  1888. struct stasis_topic_statistics *statistics;
  1889. int count = 0;
  1890. int not_dispatched = 0;
  1891. int dispatched = 0;
  1892. #define FMT_HEADERS "%-64s %10s %10s %16s %16s\n"
  1893. #define FMT_FIELDS "%-64s %10d %10d %16ld %16ld\n"
  1894. #define FMT_FIELDS2 "%-64s %10d %10d\n"
  1895. switch (cmd) {
  1896. case CLI_INIT:
  1897. e->command = "stasis statistics show topics";
  1898. e->usage =
  1899. "Usage: stasis statistics show topics\n"
  1900. " Shows a list of topics and their general statistics\n";
  1901. return NULL;
  1902. case CLI_GENERATE:
  1903. return NULL;
  1904. }
  1905. if (a->argc != e->args) {
  1906. return CLI_SHOWUSAGE;
  1907. }
  1908. ast_cli(a->fd, "\n" FMT_HEADERS, "Topic", "Dropped", "Dispatched", "Lowest Dispatch", "Highest Dispatch");
  1909. iter = ao2_iterator_init(topic_statistics, 0);
  1910. while ((statistics = ao2_iterator_next(&iter))) {
  1911. ast_cli(a->fd, FMT_FIELDS, statistics->name, statistics->messages_not_dispatched, statistics->messages_dispatched,
  1912. statistics->lowest_time_dispatched, statistics->highest_time_dispatched);
  1913. not_dispatched += statistics->messages_not_dispatched;
  1914. dispatched += statistics->messages_dispatched;
  1915. ao2_ref(statistics, -1);
  1916. ++count;
  1917. }
  1918. ao2_iterator_destroy(&iter);
  1919. ast_cli(a->fd, FMT_FIELDS2, "Total", not_dispatched, dispatched);
  1920. ast_cli(a->fd, "\n%d topics\n\n", count);
  1921. #undef FMT_HEADERS
  1922. #undef FMT_FIELDS
  1923. #undef FMT_FIELDS2
  1924. return CLI_SUCCESS;
  1925. }
  1926. /*!
  1927. * \internal
  1928. * \brief CLI tab completion for topic statistics names
  1929. */
  1930. static char *topic_statistics_complete_name(const char *word, int state)
  1931. {
  1932. struct stasis_topic_statistics *statistics;
  1933. struct ao2_iterator it_statistics;
  1934. int wordlen = strlen(word);
  1935. int which = 0;
  1936. char *result = NULL;
  1937. it_statistics = ao2_iterator_init(topic_statistics, 0);
  1938. while ((statistics = ao2_iterator_next(&it_statistics))) {
  1939. if (!strncasecmp(word, statistics->name, wordlen)
  1940. && ++which > state) {
  1941. result = ast_strdup(statistics->name);
  1942. }
  1943. ao2_ref(statistics, -1);
  1944. if (result) {
  1945. break;
  1946. }
  1947. }
  1948. ao2_iterator_destroy(&it_statistics);
  1949. return result;
  1950. }
  1951. /*!
  1952. * \internal
  1953. * \brief CLI command implementation for 'stasis statistics show topic'
  1954. */
  1955. static char *statistics_show_topic(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  1956. {
  1957. struct stasis_topic_statistics *statistics;
  1958. switch (cmd) {
  1959. case CLI_INIT:
  1960. e->command = "stasis statistics show topic";
  1961. e->usage =
  1962. "Usage: stasis statistics show topic <name>\n"
  1963. " Show stasis topic statistics.\n";
  1964. return NULL;
  1965. case CLI_GENERATE:
  1966. if (a->pos == 4) {
  1967. return topic_statistics_complete_name(a->word, a->n);
  1968. } else {
  1969. return NULL;
  1970. }
  1971. }
  1972. if (a->argc != 5) {
  1973. return CLI_SHOWUSAGE;
  1974. }
  1975. statistics = ao2_find(topic_statistics, a->argv[4], OBJ_SEARCH_KEY);
  1976. if (!statistics) {
  1977. ast_cli(a->fd, "Specified topic '%s' does not exist\n", a->argv[4]);
  1978. return CLI_FAILURE;
  1979. }
  1980. ast_cli(a->fd, "Topic: %s\n", statistics->name);
  1981. ast_cli(a->fd, "Number of messages published that went to no subscriber: %d\n", statistics->messages_not_dispatched);
  1982. ast_cli(a->fd, "Number of messages that went to at least one subscriber: %d\n", statistics->messages_dispatched);
  1983. ast_cli(a->fd, "Lowest amount of time (in milliseconds) spent dispatching message: %ld\n", statistics->lowest_time_dispatched);
  1984. ast_cli(a->fd, "Highest amount of time (in milliseconds) spent dispatching messages: %ld\n", statistics->highest_time_dispatched);
  1985. ast_cli(a->fd, "Number of subscribers: %d\n", statistics->subscriber_count);
  1986. ao2_ref(statistics, -1);
  1987. return CLI_SUCCESS;
  1988. }
  1989. /*!
  1990. * \internal
  1991. * \brief CLI command implementation for 'stasis statistics show messages'
  1992. */
  1993. static char *statistics_show_messages(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  1994. {
  1995. int i;
  1996. int count = 0;
  1997. int published = 0;
  1998. int unused = 0;
  1999. #define FMT_HEADERS "%-64s %10s %10s\n"
  2000. #define FMT_FIELDS "%-64s %10d %10d\n"
  2001. switch (cmd) {
  2002. case CLI_INIT:
  2003. e->command = "stasis statistics show messages";
  2004. e->usage =
  2005. "Usage: stasis statistics show messages\n"
  2006. " Shows a list of message types and their general statistics\n";
  2007. return NULL;
  2008. case CLI_GENERATE:
  2009. return NULL;
  2010. }
  2011. if (a->argc != e->args) {
  2012. return CLI_SHOWUSAGE;
  2013. }
  2014. ast_cli(a->fd, "\n" FMT_HEADERS, "Message Type", "Published", "Unused");
  2015. ast_mutex_lock(&message_type_statistics_lock);
  2016. for (i = 0; i < AST_VECTOR_SIZE(&message_type_statistics); ++i) {
  2017. struct stasis_message_type_statistics *statistics = AST_VECTOR_GET_ADDR(&message_type_statistics, i);
  2018. if (!statistics->message_type) {
  2019. continue;
  2020. }
  2021. ast_cli(a->fd, FMT_FIELDS, stasis_message_type_name(statistics->message_type), statistics->published,
  2022. statistics->unused);
  2023. published += statistics->published;
  2024. unused += statistics->unused;
  2025. ++count;
  2026. }
  2027. ast_mutex_unlock(&message_type_statistics_lock);
  2028. ast_cli(a->fd, FMT_FIELDS, "Total", published, unused);
  2029. ast_cli(a->fd, "\n%d seen message types\n\n", count);
  2030. #undef FMT_HEADERS
  2031. #undef FMT_FIELDS
  2032. return CLI_SUCCESS;
  2033. }
  2034. static struct ast_cli_entry cli_stasis_statistics[] = {
  2035. AST_CLI_DEFINE(statistics_show_subscriptions, "Show subscriptions with general statistics"),
  2036. AST_CLI_DEFINE(statistics_show_subscription, "Show subscription statistics"),
  2037. AST_CLI_DEFINE(statistics_show_topics, "Show topics with general statistics"),
  2038. AST_CLI_DEFINE(statistics_show_topic, "Show topic statistics"),
  2039. AST_CLI_DEFINE(statistics_show_messages, "Show message types with general statistics"),
  2040. };
  2041. static int subscription_statistics_hash(const void *obj, const int flags)
  2042. {
  2043. const struct stasis_subscription_statistics *object;
  2044. const char *key;
  2045. switch (flags & OBJ_SEARCH_MASK) {
  2046. case OBJ_SEARCH_KEY:
  2047. key = obj;
  2048. break;
  2049. case OBJ_SEARCH_OBJECT:
  2050. object = obj;
  2051. key = object->uniqueid;
  2052. break;
  2053. default:
  2054. /* Hash can only work on something with a full key. */
  2055. ast_assert(0);
  2056. return 0;
  2057. }
  2058. return ast_str_case_hash(key);
  2059. }
  2060. static int subscription_statistics_cmp(void *obj, void *arg, int flags)
  2061. {
  2062. const struct stasis_subscription_statistics *object_left = obj;
  2063. const struct stasis_subscription_statistics *object_right = arg;
  2064. const char *right_key = arg;
  2065. int cmp;
  2066. switch (flags & OBJ_SEARCH_MASK) {
  2067. case OBJ_SEARCH_OBJECT:
  2068. right_key = object_right->uniqueid;
  2069. /* Fall through */
  2070. case OBJ_SEARCH_KEY:
  2071. cmp = strcasecmp(object_left->uniqueid, right_key);
  2072. break;
  2073. case OBJ_SEARCH_PARTIAL_KEY:
  2074. /* Not supported by container */
  2075. ast_assert(0);
  2076. cmp = -1;
  2077. break;
  2078. default:
  2079. /*
  2080. * What arg points to is specific to this traversal callback
  2081. * and has no special meaning to astobj2.
  2082. */
  2083. cmp = 0;
  2084. break;
  2085. }
  2086. if (cmp) {
  2087. return 0;
  2088. }
  2089. /*
  2090. * At this point the traversal callback is identical to a sorted
  2091. * container.
  2092. */
  2093. return CMP_MATCH;
  2094. }
  2095. static int topic_statistics_hash(const void *obj, const int flags)
  2096. {
  2097. const struct stasis_topic_statistics *object;
  2098. const char *key;
  2099. switch (flags & OBJ_SEARCH_MASK) {
  2100. case OBJ_SEARCH_KEY:
  2101. key = obj;
  2102. break;
  2103. case OBJ_SEARCH_OBJECT:
  2104. object = obj;
  2105. key = object->name;
  2106. break;
  2107. default:
  2108. /* Hash can only work on something with a full key. */
  2109. ast_assert(0);
  2110. return 0;
  2111. }
  2112. return ast_str_case_hash(key);
  2113. }
  2114. static int topic_statistics_cmp(void *obj, void *arg, int flags)
  2115. {
  2116. const struct stasis_topic_statistics *object_left = obj;
  2117. const struct stasis_topic_statistics *object_right = arg;
  2118. const char *right_key = arg;
  2119. int cmp;
  2120. switch (flags & OBJ_SEARCH_MASK) {
  2121. case OBJ_SEARCH_OBJECT:
  2122. right_key = object_right->name;
  2123. /* Fall through */
  2124. case OBJ_SEARCH_KEY:
  2125. cmp = strcasecmp(object_left->name, right_key);
  2126. break;
  2127. case OBJ_SEARCH_PARTIAL_KEY:
  2128. /* Not supported by container */
  2129. ast_assert(0);
  2130. cmp = -1;
  2131. break;
  2132. default:
  2133. /*
  2134. * What arg points to is specific to this traversal callback
  2135. * and has no special meaning to astobj2.
  2136. */
  2137. cmp = 0;
  2138. break;
  2139. }
  2140. if (cmp) {
  2141. return 0;
  2142. }
  2143. /*
  2144. * At this point the traversal callback is identical to a sorted
  2145. * container.
  2146. */
  2147. return CMP_MATCH;
  2148. }
  2149. #endif
  2150. /*! \brief Cleanup function for graceful shutdowns */
  2151. static void stasis_cleanup(void)
  2152. {
  2153. #ifdef AST_DEVMODE
  2154. ast_cli_unregister_multiple(cli_stasis_statistics, ARRAY_LEN(cli_stasis_statistics));
  2155. AST_VECTOR_FREE(&message_type_statistics);
  2156. ao2_cleanup(subscription_statistics);
  2157. ao2_cleanup(topic_statistics);
  2158. #endif
  2159. ast_threadpool_shutdown(pool);
  2160. pool = NULL;
  2161. STASIS_MESSAGE_TYPE_CLEANUP(stasis_subscription_change_type);
  2162. STASIS_MESSAGE_TYPE_CLEANUP(ast_multi_user_event_type);
  2163. aco_info_destroy(&cfg_info);
  2164. ao2_global_obj_release(globals);
  2165. }
  2166. int stasis_init(void)
  2167. {
  2168. struct stasis_config *cfg;
  2169. int cache_init;
  2170. struct ast_threadpool_options threadpool_opts = { 0, };
  2171. /* Be sure the types are cleaned up after the message bus */
  2172. ast_register_cleanup(stasis_cleanup);
  2173. if (aco_info_init(&cfg_info)) {
  2174. return -1;
  2175. }
  2176. aco_option_register_custom(&cfg_info, "decline", ACO_EXACT,
  2177. declined_options, "", declined_handler, 0);
  2178. aco_option_register(&cfg_info, "initial_size", ACO_EXACT,
  2179. threadpool_options, "5", OPT_INT_T, PARSE_IN_RANGE,
  2180. FLDSET(struct stasis_threadpool_conf, initial_size), 0,
  2181. INT_MAX);
  2182. aco_option_register(&cfg_info, "idle_timeout_sec", ACO_EXACT,
  2183. threadpool_options, "20", OPT_INT_T, PARSE_IN_RANGE,
  2184. FLDSET(struct stasis_threadpool_conf, idle_timeout_sec), 0,
  2185. INT_MAX);
  2186. aco_option_register(&cfg_info, "max_size", ACO_EXACT,
  2187. threadpool_options, "50", OPT_INT_T, PARSE_IN_RANGE,
  2188. FLDSET(struct stasis_threadpool_conf, max_size), 0,
  2189. INT_MAX);
  2190. if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
  2191. struct stasis_config *default_cfg = stasis_config_alloc();
  2192. if (!default_cfg) {
  2193. return -1;
  2194. }
  2195. if (aco_set_defaults(&threadpool_option, "threadpool", default_cfg->threadpool_options)) {
  2196. ast_log(LOG_ERROR, "Failed to initialize defaults on Stasis configuration object\n");
  2197. ao2_ref(default_cfg, -1);
  2198. return -1;
  2199. }
  2200. if (aco_set_defaults(&declined_option, "declined_message_types", default_cfg->declined_message_types)) {
  2201. ast_log(LOG_ERROR, "Failed to load stasis.conf and failed to initialize defaults.\n");
  2202. ao2_ref(default_cfg, -1);
  2203. return -1;
  2204. }
  2205. ast_log(LOG_NOTICE, "Could not load Stasis configuration; using defaults\n");
  2206. ao2_global_obj_replace_unref(globals, default_cfg);
  2207. cfg = default_cfg;
  2208. } else {
  2209. cfg = ao2_global_obj_ref(globals);
  2210. if (!cfg) {
  2211. ast_log(LOG_ERROR, "Failed to obtain Stasis configuration object\n");
  2212. return -1;
  2213. }
  2214. }
  2215. threadpool_opts.version = AST_THREADPOOL_OPTIONS_VERSION;
  2216. threadpool_opts.initial_size = cfg->threadpool_options->initial_size;
  2217. threadpool_opts.auto_increment = 1;
  2218. threadpool_opts.max_size = cfg->threadpool_options->max_size;
  2219. threadpool_opts.idle_timeout = cfg->threadpool_options->idle_timeout_sec;
  2220. pool = ast_threadpool_create("stasis-core", NULL, &threadpool_opts);
  2221. ao2_ref(cfg, -1);
  2222. if (!pool) {
  2223. ast_log(LOG_ERROR, "Failed to create 'stasis-core' threadpool\n");
  2224. return -1;
  2225. }
  2226. cache_init = stasis_cache_init();
  2227. if (cache_init != 0) {
  2228. return -1;
  2229. }
  2230. if (STASIS_MESSAGE_TYPE_INIT(stasis_subscription_change_type) != 0) {
  2231. return -1;
  2232. }
  2233. if (STASIS_MESSAGE_TYPE_INIT(ast_multi_user_event_type) != 0) {
  2234. return -1;
  2235. }
  2236. #ifdef AST_DEVMODE
  2237. /* Statistics information is stored separately so that we don't alter or interrupt the lifetime of the underlying
  2238. * topic or subscripton.
  2239. */
  2240. subscription_statistics = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, SUBSCRIPTION_STATISTICS_BUCKETS,
  2241. subscription_statistics_hash, 0, subscription_statistics_cmp);
  2242. if (!subscription_statistics) {
  2243. return -1;
  2244. }
  2245. topic_statistics = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, TOPIC_STATISTICS_BUCKETS,
  2246. topic_statistics_hash, 0, topic_statistics_cmp);
  2247. if (!topic_statistics) {
  2248. return -1;
  2249. }
  2250. AST_VECTOR_INIT(&message_type_statistics, 0);
  2251. if (ast_cli_register_multiple(cli_stasis_statistics, ARRAY_LEN(cli_stasis_statistics))) {
  2252. return -1;
  2253. }
  2254. #endif
  2255. return 0;
  2256. }
  2257. #ifdef AST_DEVMODE
  2258. #undef stasis_subscribe
  2259. struct stasis_subscription *stasis_subscribe(
  2260. struct stasis_topic *topic,
  2261. stasis_subscription_cb callback,
  2262. void *data);
  2263. #undef stasis_subscribe_pool
  2264. struct stasis_subscription *stasis_subscribe_pool(
  2265. struct stasis_topic *topic,
  2266. stasis_subscription_cb callback,
  2267. void *data);
  2268. #endif
  2269. struct stasis_subscription *stasis_subscribe(
  2270. struct stasis_topic *topic,
  2271. stasis_subscription_cb callback,
  2272. void *data)
  2273. {
  2274. return internal_stasis_subscribe(topic, callback, data, 1, 0, __FILE__, __LINE__, __PRETTY_FUNCTION__);
  2275. }
  2276. struct stasis_subscription *stasis_subscribe_pool(
  2277. struct stasis_topic *topic,
  2278. stasis_subscription_cb callback,
  2279. void *data)
  2280. {
  2281. return internal_stasis_subscribe(topic, callback, data, 1, 1, __FILE__, __LINE__, __PRETTY_FUNCTION__);
  2282. }