2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief The Asterisk Management Interface - AMI
23 * \author Mark Spencer <markster@digium.com>
25 * OpenSSL http://www.openssl.org - for AMI/SSL
27 * At the moment this file contains a number of functions, namely:
29 * - data structures storing AMI state
30 * - AMI-related API functions, used by internal asterisk components
31 * - handlers for AMI-related CLI functions
32 * - handlers for AMI functions (available through the AMI socket)
33 * - the code for the main AMI listener thread and individual session threads
34 * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
39 /*! \li \ref manager.c uses the configuration file \ref manager.conf and \ref users.conf
40 * \addtogroup configuration_file
43 /*! \page manager.conf manager.conf
44 * \verbinclude manager.conf.sample
47 /*! \page users.conf users.conf
48 * \verbinclude users.conf.sample
52 <support_level>core</support_level>
57 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
59 #include "asterisk/_private.h"
60 #include "asterisk/paths.h" /* use various ast_config_AST_* */
65 #include <sys/types.h>
68 #include "asterisk/channel.h"
69 #include "asterisk/file.h"
70 #include "asterisk/manager.h"
71 #include "asterisk/module.h"
72 #include "asterisk/config.h"
73 #include "asterisk/callerid.h"
74 #include "asterisk/lock.h"
75 #include "asterisk/cli.h"
76 #include "asterisk/app.h"
77 #include "asterisk/pbx.h"
78 #include "asterisk/md5.h"
79 #include "asterisk/acl.h"
80 #include "asterisk/utils.h"
81 #include "asterisk/tcptls.h"
82 #include "asterisk/http.h"
83 #include "asterisk/ast_version.h"
84 #include "asterisk/threadstorage.h"
85 #include "asterisk/linkedlists.h"
86 #include "asterisk/term.h"
87 #include "asterisk/astobj2.h"
88 #include "asterisk/features.h"
89 #include "asterisk/security_events.h"
90 #include "asterisk/aoc.h"
91 #include "asterisk/strings.h"
92 #include "asterisk/stringfields.h"
93 #include "asterisk/presencestate.h"
94 #include "asterisk/stasis_message_router.h"
95 #include "asterisk/stasis_channels.h"
96 #include "asterisk/stasis_bridges.h"
97 #include "asterisk/test.h"
98 #include "asterisk/json.h"
99 #include "asterisk/bridge.h"
100 #include "asterisk/features_config.h"
101 #include "asterisk/rtp_engine.h"
102 #include "asterisk/format_cache.h"
103 #include "asterisk/translate.h"
106 <manager name="Ping" language="en_US">
111 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
114 <para>A 'Ping' action will ellicit a 'Pong' response. Used to keep the
115 manager connection open.</para>
118 <manager name="Events" language="en_US">
123 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
124 <parameter name="EventMask" required="true">
127 <para>If all events should be sent.</para>
130 <para>If no events should be sent.</para>
132 <enum name="system,call,log,...">
133 <para>To select which flags events should have to be sent.</para>
139 <para>Enable/Disable sending of events to this manager client.</para>
142 <manager name="Logoff" language="en_US">
147 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
150 <para>Logoff the current manager session.</para>
153 <manager name="Login" language="en_US">
158 <parameter name="ActionID">
159 <para>ActionID for this transaction. Will be returned.</para>
161 <parameter name="Username" required="true">
162 <para>Username to login with as specified in manager.conf.</para>
164 <parameter name="Secret">
165 <para>Secret to login with as specified in manager.conf.</para>
169 <para>Login Manager.</para>
172 <manager name="Challenge" language="en_US">
174 Generate Challenge for MD5 Auth.
177 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
178 <parameter name="AuthType" required="true">
179 <para>Digest algorithm to use in the challenge. Valid values are:</para>
186 <para>Generate a challenge for MD5 authentication.</para>
189 <manager name="Hangup" language="en_US">
194 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
195 <parameter name="Channel" required="true">
196 <para>The exact channel name to be hungup, or to use a regular expression, set this parameter to: /regex/</para>
197 <para>Example exact channel: SIP/provider-0000012a</para>
198 <para>Example regular expression: /^SIP/provider-.*$/</para>
200 <parameter name="Cause">
201 <para>Numeric hangup cause.</para>
205 <para>Hangup a channel.</para>
208 <manager name="Status" language="en_US">
213 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
214 <parameter name="Channel" required="true">
215 <para>The name of the channel to query for status.</para>
217 <parameter name="Variables">
218 <para>Comma <literal>,</literal> separated list of variable to include.</para>
222 <para>Will return the status information of each channel along with the
223 value for the specified channel variables.</para>
226 <managerEvent language="en_US" name="Status">
227 <managerEventInstance class="EVENT_FLAG_CALL">
228 <synopsis>Raised in response to a Status command.</synopsis>
230 <parameter name="ActionID" required="false"/>
232 <parameter name="Type">
233 <para>Type of channel</para>
235 <parameter name="DNID">
236 <para>Dialed number identifier</para>
238 <parameter name="TimeToHangup">
239 <para>Absolute lifetime of the channel</para>
241 <parameter name="BridgeID">
242 <para>Identifier of the bridge the channel is in, may be empty if not in one</para>
244 <parameter name="Linkedid">
246 <parameter name="Application">
247 <para>Application currently executing on the channel</para>
249 <parameter name="Data">
250 <para>Data given to the currently executing channel</para>
252 <parameter name="Nativeformats">
253 <para>Media formats the connected party is willing to send or receive</para>
255 <parameter name="Readformat">
256 <para>Media formats that frames from the channel are received in</para>
258 <parameter name="Readtrans">
259 <para>Translation path for media received in native formats</para>
261 <parameter name="Writeformat">
262 <para>Media formats that frames to the channel are accepted in</para>
264 <parameter name="Writetrans">
265 <para>Translation path for media sent to the connected party</para>
267 <parameter name="Callgroup">
268 <para>Configured call group on the channel</para>
270 <parameter name="Pickupgroup">
271 <para>Configured pickup group on the channel</para>
273 <parameter name="Seconds">
274 <para>Number of seconds the channel has been active</para>
278 <ref type="manager">Status</ref>
280 </managerEventInstance>
282 <manager name="Setvar" language="en_US">
284 Set a channel variable.
287 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
288 <parameter name="Channel">
289 <para>Channel to set variable for.</para>
291 <parameter name="Variable" required="true">
292 <para>Variable name.</para>
294 <parameter name="Value" required="true">
295 <para>Variable value.</para>
299 <para>Set a global or local channel variable.</para>
301 <para>If a channel name is not provided then the variable is global.</para>
305 <manager name="Getvar" language="en_US">
307 Gets a channel variable.
310 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
311 <parameter name="Channel">
312 <para>Channel to read variable from.</para>
314 <parameter name="Variable" required="true">
315 <para>Variable name.</para>
319 <para>Get the value of a global or local channel variable.</para>
321 <para>If a channel name is not provided then the variable is global.</para>
325 <manager name="GetConfig" language="en_US">
327 Retrieve configuration.
330 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
331 <parameter name="Filename" required="true">
332 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
334 <parameter name="Category">
335 <para>Category in configuration file.</para>
339 <para>This action will dump the contents of a configuration
340 file by category and contents or optionally by specified category only.</para>
343 <manager name="GetConfigJSON" language="en_US">
345 Retrieve configuration (JSON format).
348 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
349 <parameter name="Filename" required="true">
350 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
354 <para>This action will dump the contents of a configuration file by category
355 and contents in JSON format. This only makes sense to be used using rawman over
356 the HTTP interface.</para>
359 <manager name="UpdateConfig" language="en_US">
361 Update basic configuration.
364 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
365 <parameter name="SrcFilename" required="true">
366 <para>Configuration filename to read (e.g. <filename>foo.conf</filename>).</para>
368 <parameter name="DstFilename" required="true">
369 <para>Configuration filename to write (e.g. <filename>foo.conf</filename>)</para>
371 <parameter name="Reload">
372 <para>Whether or not a reload should take place (or name of specific module).</para>
374 <parameter name="Action-XXXXXX">
375 <para>Action to take.</para>
376 <para>X's represent 6 digit number beginning with 000000.</para>
378 <enum name="NewCat" />
379 <enum name="RenameCat" />
380 <enum name="DelCat" />
381 <enum name="EmptyCat" />
382 <enum name="Update" />
383 <enum name="Delete" />
384 <enum name="Append" />
385 <enum name="Insert" />
388 <parameter name="Cat-XXXXXX">
389 <para>Category to operate on.</para>
390 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
392 <parameter name="Var-XXXXXX">
393 <para>Variable to work on.</para>
394 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
396 <parameter name="Value-XXXXXX">
397 <para>Value to work on.</para>
398 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
400 <parameter name="Match-XXXXXX">
401 <para>Extra match required to match line.</para>
402 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
404 <parameter name="Line-XXXXXX">
405 <para>Line in category to operate on (used with delete and insert actions).</para>
406 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
410 <para>This action will modify, create, or delete configuration elements
411 in Asterisk configuration files.</para>
414 <manager name="CreateConfig" language="en_US">
416 Creates an empty file in the configuration directory.
419 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
420 <parameter name="Filename" required="true">
421 <para>The configuration filename to create (e.g. <filename>foo.conf</filename>).</para>
425 <para>This action will create an empty file in the configuration
426 directory. This action is intended to be used before an UpdateConfig
430 <manager name="ListCategories" language="en_US">
432 List categories in configuration file.
435 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
436 <parameter name="Filename" required="true">
437 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
441 <para>This action will dump the categories in a given file.</para>
444 <manager name="Redirect" language="en_US">
446 Redirect (transfer) a call.
449 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
450 <parameter name="Channel" required="true">
451 <para>Channel to redirect.</para>
453 <parameter name="ExtraChannel">
454 <para>Second call leg to transfer (optional).</para>
456 <parameter name="Exten" required="true">
457 <para>Extension to transfer to.</para>
459 <parameter name="ExtraExten">
460 <para>Extension to transfer extrachannel to (optional).</para>
462 <parameter name="Context" required="true">
463 <para>Context to transfer to.</para>
465 <parameter name="ExtraContext">
466 <para>Context to transfer extrachannel to (optional).</para>
468 <parameter name="Priority" required="true">
469 <para>Priority to transfer to.</para>
471 <parameter name="ExtraPriority">
472 <para>Priority to transfer extrachannel to (optional).</para>
476 <para>Redirect (transfer) a call.</para>
479 <manager name="Atxfer" language="en_US">
484 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
485 <parameter name="Channel" required="true">
486 <para>Transferer's channel.</para>
488 <parameter name="Exten" required="true">
489 <para>Extension to transfer to.</para>
491 <parameter name="Context">
492 <para>Context to transfer to.</para>
496 <para>Attended transfer.</para>
499 <manager name="Originate" language="en_US">
504 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
505 <parameter name="Channel" required="true">
506 <para>Channel name to call.</para>
508 <parameter name="Exten">
509 <para>Extension to use (requires <literal>Context</literal> and
510 <literal>Priority</literal>)</para>
512 <parameter name="Context">
513 <para>Context to use (requires <literal>Exten</literal> and
514 <literal>Priority</literal>)</para>
516 <parameter name="Priority">
517 <para>Priority to use (requires <literal>Exten</literal> and
518 <literal>Context</literal>)</para>
520 <parameter name="Application">
521 <para>Application to execute.</para>
523 <parameter name="Data">
524 <para>Data to use (requires <literal>Application</literal>).</para>
526 <parameter name="Timeout" default="30000">
527 <para>How long to wait for call to be answered (in ms.).</para>
529 <parameter name="CallerID">
530 <para>Caller ID to be set on the outgoing channel.</para>
532 <parameter name="Variable">
533 <para>Channel variable to set, multiple Variable: headers are allowed.</para>
535 <parameter name="Account">
536 <para>Account code.</para>
538 <parameter name="EarlyMedia">
539 <para>Set to <literal>true</literal> to force call bridge on early media..</para>
541 <parameter name="Async">
542 <para>Set to <literal>true</literal> for fast origination.</para>
544 <parameter name="Codecs">
545 <para>Comma-separated list of codecs to use for this call.</para>
547 <parameter name="ChannelId">
548 <para>Channel UniqueId to be set on the channel.</para>
550 <parameter name="OtherChannelId">
551 <para>Channel UniqueId to be set on the second local channel.</para>
555 <para>Generates an outgoing call to a
556 <replaceable>Extension</replaceable>/<replaceable>Context</replaceable>/<replaceable>Priority</replaceable>
557 or <replaceable>Application</replaceable>/<replaceable>Data</replaceable></para>
560 <ref type="managerEvent">OriginateResponse</ref>
563 <managerEvent language="en_US" name="OriginateResponse">
564 <managerEventInstance class="EVENT_FLAG_CALL">
565 <synopsis>Raised in response to an Originate command.</synopsis>
567 <parameter name="ActionID" required="false"/>
568 <parameter name="Resonse">
570 <enum name="Failure"/>
571 <enum name="Success"/>
574 <parameter name="Channel"/>
575 <parameter name="Context"/>
576 <parameter name="Exten"/>
577 <parameter name="Reason"/>
578 <parameter name="Uniqueid"/>
579 <parameter name="CallerIDNum"/>
580 <parameter name="CallerIDName"/>
583 <ref type="manager">Originate</ref>
585 </managerEventInstance>
587 <manager name="Command" language="en_US">
589 Execute Asterisk CLI Command.
592 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
593 <parameter name="Command" required="true">
594 <para>Asterisk CLI command to run.</para>
598 <para>Run a CLI command.</para>
601 <manager name="ExtensionState" language="en_US">
603 Check Extension Status.
606 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
607 <parameter name="Exten" required="true">
608 <para>Extension to check state on.</para>
610 <parameter name="Context" required="true">
611 <para>Context for extension.</para>
615 <para>Report the extension state for given extension. If the extension has a hint,
616 will use devicestate to check the status of the device connected to the extension.</para>
617 <para>Will return an <literal>Extension Status</literal> message. The response will include
618 the hint for the extension and the status.</para>
621 <manager name="PresenceState" language="en_US">
626 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
627 <parameter name="Provider" required="true">
628 <para>Presence Provider to check the state of</para>
632 <para>Report the presence state for the given presence provider.</para>
633 <para>Will return a <literal>Presence State</literal> message. The response will include the
634 presence state and, if set, a presence subtype and custom message.</para>
637 <manager name="AbsoluteTimeout" language="en_US">
639 Set absolute timeout.
642 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
643 <parameter name="Channel" required="true">
644 <para>Channel name to hangup.</para>
646 <parameter name="Timeout" required="true">
647 <para>Maximum duration of the call (sec).</para>
651 <para>Hangup a channel after a certain time. Acknowledges set time with
652 <literal>Timeout Set</literal> message.</para>
655 <manager name="MailboxStatus" language="en_US">
660 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
661 <parameter name="Mailbox" required="true">
662 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
666 <para>Checks a voicemail account for status.</para>
667 <para>Returns whether there are messages waiting.</para>
668 <para>Message: Mailbox Status.</para>
669 <para>Mailbox: <replaceable>mailboxid</replaceable>.</para>
670 <para>Waiting: <literal>0</literal> if messages waiting, <literal>1</literal>
671 if no messages waiting.</para>
674 <manager name="MailboxCount" language="en_US">
676 Check Mailbox Message Count.
679 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
680 <parameter name="Mailbox" required="true">
681 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
685 <para>Checks a voicemail account for new messages.</para>
686 <para>Returns number of urgent, new and old messages.</para>
687 <para>Message: Mailbox Message Count</para>
688 <para>Mailbox: <replaceable>mailboxid</replaceable></para>
689 <para>UrgentMessages: <replaceable>count</replaceable></para>
690 <para>NewMessages: <replaceable>count</replaceable></para>
691 <para>OldMessages: <replaceable>count</replaceable></para>
694 <manager name="ListCommands" language="en_US">
696 List available manager commands.
699 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
702 <para>Returns the action name and synopsis for every action that
703 is available to the user.</para>
706 <manager name="SendText" language="en_US">
708 Send text message to channel.
711 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
712 <parameter name="Channel" required="true">
713 <para>Channel to send message to.</para>
715 <parameter name="Message" required="true">
716 <para>Message to send.</para>
720 <para>Sends A Text Message to a channel while in a call.</para>
723 <manager name="UserEvent" language="en_US">
725 Send an arbitrary event.
728 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
729 <parameter name="UserEvent" required="true">
730 <para>Event string to send.</para>
732 <parameter name="Header1">
733 <para>Content1.</para>
735 <parameter name="HeaderN">
736 <para>ContentN.</para>
740 <para>Send an event to manager sessions.</para>
743 <manager name="WaitEvent" language="en_US">
745 Wait for an event to occur.
748 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
749 <parameter name="Timeout" required="true">
750 <para>Maximum time (in seconds) to wait for events, <literal>-1</literal> means forever.</para>
754 <para>This action will ellicit a <literal>Success</literal> response. Whenever
755 a manager event is queued. Once WaitEvent has been called on an HTTP manager
756 session, events will be generated and queued.</para>
759 <manager name="CoreSettings" language="en_US">
761 Show PBX core settings (version etc).
764 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
767 <para>Query for Core PBX settings.</para>
770 <manager name="CoreStatus" language="en_US">
772 Show PBX core status variables.
775 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
778 <para>Query for Core PBX status.</para>
781 <manager name="Reload" language="en_US">
786 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
787 <parameter name="Module">
788 <para>Name of the module to reload.</para>
792 <para>Send a reload event.</para>
795 <manager name="CoreShowChannels" language="en_US">
797 List currently active channels.
800 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
803 <para>List currently defined channels and some information about them.</para>
806 <manager name="LoggerRotate" language="en_US">
808 Reload and rotate the Asterisk logger.
811 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
814 <para>Reload and rotate the logger. Analogous to the CLI command 'logger rotate'.</para>
817 <manager name="ModuleLoad" language="en_US">
822 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
823 <parameter name="Module">
824 <para>Asterisk module name (including .so extension) or subsystem identifier:</para>
827 <enum name="dnsmgr" />
828 <enum name="extconfig" />
831 <enum name="manager" />
833 <enum name="logger" />
834 <enum name="features" />
836 <enum name="udptl" />
837 <enum name="indications" />
842 <parameter name="LoadType" required="true">
843 <para>The operation to be done on module. Subsystem identifiers may only
847 <enum name="unload" />
848 <enum name="reload" />
850 <para>If no module is specified for a <literal>reload</literal> loadtype,
851 all modules are reloaded.</para>
855 <para>Loads, unloads or reloads an Asterisk module in a running system.</para>
858 <manager name="ModuleCheck" language="en_US">
860 Check if module is loaded.
863 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
864 <parameter name="Module" required="true">
865 <para>Asterisk module name (not including extension).</para>
869 <para>Checks if Asterisk module is loaded. Will return Success/Failure.
870 For success returns, the module revision number is included.</para>
873 <manager name="AOCMessage" language="en_US">
875 Generate an Advice of Charge message on a channel.
878 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
879 <parameter name="Channel" required="true">
880 <para>Channel name to generate the AOC message on.</para>
882 <parameter name="ChannelPrefix">
883 <para>Partial channel prefix. By using this option one can match the beginning part
884 of a channel name without having to put the entire name in. For example
885 if a channel name is SIP/snom-00000001 and this value is set to SIP/snom, then
886 that channel matches and the message will be sent. Note however that only
887 the first matched channel has the message sent on it. </para>
889 <parameter name="MsgType" required="true">
890 <para>Defines what type of AOC message to create, AOC-D or AOC-E</para>
896 <parameter name="ChargeType" required="true">
897 <para>Defines what kind of charge this message represents.</para>
901 <enum name="Currency" />
905 <parameter name="UnitAmount(0)">
906 <para>This represents the amount of units charged. The ETSI AOC standard specifies that
907 this value along with the optional UnitType value are entries in a list. To accommodate this
908 these values take an index value starting at 0 which can be used to generate this list of
909 unit entries. For Example, If two unit entires were required this could be achieved by setting the
910 paramter UnitAmount(0)=1234 and UnitAmount(1)=5678. Note that UnitAmount at index 0 is
911 required when ChargeType=Unit, all other entries in the list are optional.
914 <parameter name="UnitType(0)">
915 <para>Defines the type of unit. ETSI AOC standard specifies this as an integer
916 value between 1 and 16, but this value is left open to accept any positive
917 integer. Like the UnitAmount parameter, this value represents a list entry
918 and has an index parameter that starts at 0.
921 <parameter name="CurrencyName">
922 <para>Specifies the currency's name. Note that this value is truncated after 10 characters.</para>
924 <parameter name="CurrencyAmount">
925 <para>Specifies the charge unit amount as a positive integer. This value is required
926 when ChargeType==Currency.</para>
928 <parameter name="CurrencyMultiplier">
929 <para>Specifies the currency multiplier. This value is required when ChargeType==Currency.</para>
931 <enum name="OneThousandth" />
932 <enum name="OneHundredth" />
933 <enum name="OneTenth" />
936 <enum name="Hundred" />
937 <enum name="Thousand" />
940 <parameter name="TotalType" default="Total">
941 <para>Defines what kind of AOC-D total is represented.</para>
943 <enum name="Total" />
944 <enum name="SubTotal" />
947 <parameter name="AOCBillingId">
948 <para>Represents a billing ID associated with an AOC-D or AOC-E message. Note
949 that only the first 3 items of the enum are valid AOC-D billing IDs</para>
951 <enum name="Normal" />
952 <enum name="ReverseCharge" />
953 <enum name="CreditCard" />
954 <enum name="CallFwdUnconditional" />
955 <enum name="CallFwdBusy" />
956 <enum name="CallFwdNoReply" />
957 <enum name="CallDeflection" />
958 <enum name="CallTransfer" />
961 <parameter name="ChargingAssociationId">
962 <para>Charging association identifier. This is optional for AOC-E and can be
963 set to any value between -32768 and 32767</para>
965 <parameter name="ChargingAssociationNumber">
966 <para>Represents the charging association party number. This value is optional
969 <parameter name="ChargingAssociationPlan">
970 <para>Integer representing the charging plan associated with the ChargingAssociationNumber.
971 The value is bits 7 through 1 of the Q.931 octet containing the type-of-number and
972 numbering-plan-identification fields.</para>
976 <para>Generates an AOC-D or AOC-E message on a channel.</para>
979 <function name="AMI_CLIENT" language="en_US">
981 Checks attributes of manager accounts
984 <parameter name="loginname" required="true">
985 <para>Login name, specified in manager.conf</para>
987 <parameter name="field" required="true">
988 <para>The manager account attribute to return</para>
990 <enum name="sessions"><para>The number of sessions for this AMI account</para></enum>
996 Currently, the only supported parameter is "sessions" which will return the current number of
997 active sessions for this AMI account.
1001 <manager name="Filter" language="en_US">
1003 Dynamically add filters for the current manager session.
1006 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
1007 <parameter name="Operation">
1010 <para>Add a filter.</para>
1014 <parameter name="Filter">
1015 <para>Filters can be whitelist or blacklist</para>
1016 <para>Example whitelist filter: "Event: Newchannel"</para>
1017 <para>Example blacklist filter: "!Channel: DAHDI.*"</para>
1018 <para>This filter option is used to whitelist or blacklist events per user to be
1019 reported with regular expressions and are allowed if both the regex matches
1020 and the user has read access as defined in manager.conf. Filters are assumed to be for whitelisting
1021 unless preceeded by an exclamation point, which marks it as being black.
1022 Evaluation of the filters is as follows:</para>
1023 <para>- If no filters are configured all events are reported as normal.</para>
1024 <para>- If there are white filters only: implied black all filter processed first, then white filters.</para>
1025 <para>- If there are black filters only: implied white all filter processed first, then black filters.</para>
1026 <para>- If there are both white and black filters: implied black all filter processed first, then white
1027 filters, and lastly black filters.</para>
1031 <para>The filters added are only used for the current session.
1032 Once the connection is closed the filters are removed.</para>
1033 <para>This comand requires the system permission because
1034 this command can be used to create filters that may bypass
1035 filters defined in manager.conf</para>
1038 <manager name="FilterList" language="en_US">
1040 Show current event filters for this session
1043 <para>The filters displayed are for the current session. Only those filters defined in
1044 manager.conf will be present upon starting a new session.</para>
1047 <manager name="BlindTransfer" language="en_US">
1049 Blind transfer channel(s) to the given destination
1052 <parameter name="Channel" required="true">
1054 <parameter name="Context">
1056 <parameter name="Exten">
1060 <para>Redirect all channels currently bridged to the specified channel to the specified destination.</para>
1063 <ref type="manager">Redirect</ref>
1068 /*! \addtogroup Group_AMI AMI functions
1076 UNSPECIFIED_CATEGORY,
1077 UNSPECIFIED_ARGUMENT,
1087 enum add_filter_result {
1089 FILTER_ALLOC_FAILED,
1090 FILTER_COMPILE_FAIL,
1094 * Linked list of events.
1095 * Global events are appended to the list by append_event().
1096 * The usecount is the number of stored pointers to the element,
1097 * excluding the list pointers. So an element that is only in
1098 * the list has a usecount of 0, not 1.
1100 * Clients have a pointer to the last event processed, and for each
1101 * of these clients we track the usecount of the elements.
1102 * If we have a pointer to an entry in the list, it is safe to navigate
1103 * it forward because elements will not be deleted, but only appended.
1104 * The worst that can happen is seeing the pointer still NULL.
1106 * When the usecount of an element drops to 0, and the element is the
1107 * first in the list, we can remove it. Removal is done within the
1108 * main thread, which is woken up for the purpose.
1110 * For simplicity of implementation, we make sure the list is never empty.
1113 int usecount; /*!< # of clients who still need the event */
1115 unsigned int seq; /*!< sequence number */
1116 struct timeval tv; /*!< When event was allocated */
1117 AST_RWLIST_ENTRY(eventqent) eq_next;
1118 char eventdata[1]; /*!< really variable size, allocated by append_event() */
1121 static AST_RWLIST_HEAD_STATIC(all_events, eventqent);
1123 static int displayconnects = 1;
1124 static int allowmultiplelogin = 1;
1125 static int timestampevents;
1126 static int httptimeout = 60;
1127 static int broken_events_action = 0;
1128 static int manager_enabled = 0;
1129 static int subscribed = 0;
1130 static int webmanager_enabled = 0;
1131 static int manager_debug = 0; /*!< enable some debugging code in the manager */
1132 static int authtimeout;
1133 static int authlimit;
1134 static char *manager_channelvars;
1136 #define DEFAULT_REALM "asterisk"
1137 static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
1139 static int unauth_sessions = 0;
1140 static struct stasis_subscription *acl_change_sub;
1142 /*! \brief A \ref stasis_topic that all topics AMI cares about will be forwarded to */
1143 static struct stasis_topic *manager_topic;
1145 /*! \brief The \ref stasis_message_router for all \ref stasis messages */
1146 static struct stasis_message_router *stasis_router;
1148 /*! \brief The \ref stasis_subscription for forwarding the RTP topic to the AMI topic */
1149 static struct stasis_forward *rtp_topic_forwarder;
1151 /*! \brief The \ref stasis_subscription for forwarding the Security topic to the AMI topic */
1152 static struct stasis_forward *security_topic_forwarder;
1154 #ifdef TEST_FRAMEWORK
1155 struct stasis_subscription *test_suite_sub;
1158 #define MGR_SHOW_TERMINAL_WIDTH 80
1160 #define MAX_VARS 128
1163 * Descriptor for a manager session, either on the AMI socket or over HTTP.
1166 * AMI session have managerid == 0; the entry is created upon a connect,
1167 * and destroyed with the socket.
1168 * HTTP sessions have managerid != 0, the value is used as a search key
1169 * to lookup sessions (using the mansession_id cookie, or nonce key from
1170 * Digest Authentication http header).
1172 #define MAX_BLACKLIST_CMD_LEN 2
1173 static const struct {
1174 const char *words[AST_MAX_CMD_LEN];
1175 } command_blacklist[] = {
1176 {{ "module", "load", NULL }},
1177 {{ "module", "unload", NULL }},
1178 {{ "restart", "gracefully", NULL }},
1181 static void acl_change_stasis_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message);
1183 static void acl_change_stasis_subscribe(void)
1185 if (!acl_change_sub) {
1186 acl_change_sub = stasis_subscribe(ast_security_topic(),
1187 acl_change_stasis_cb, NULL);
1191 static void acl_change_stasis_unsubscribe(void)
1193 acl_change_sub = stasis_unsubscribe_and_join(acl_change_sub);
1196 /* In order to understand what the heck is going on with the
1197 * mansession_session and mansession structs, we need to have a bit of a history
1200 * In the beginning, there was the mansession. The mansession contained data that was
1201 * intrinsic to a manager session, such as the time that it started, the name of the logged-in
1202 * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
1203 * sessions, these were used to represent the TCP socket over which the AMI session was taking
1204 * place. It makes perfect sense for these fields to be a part of the session-specific data since
1205 * the session actually defines this information.
1207 * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
1208 * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
1209 * but rather to the action that is being executed. Because a single session may execute many commands
1210 * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
1211 * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
1212 * has had a chance to properly close its handles.
1214 * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
1215 * from being run at the same time in a single session. Some manager actions may block for a long time, thus
1216 * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
1217 * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
1218 * part of the action instead.
1220 * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
1221 * contain the action-specific information, such as which file to write to. In order to maintain expectations
1222 * of action handlers and not have to change the public API of the manager code, we would need to name this
1223 * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
1224 * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
1225 * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
1228 struct mansession_session {
1229 /*! \todo XXX need to document which fields it is protecting */
1230 struct ast_sockaddr addr; /*!< address we are connecting from */
1231 FILE *f; /*!< fdopen() on the underlying fd */
1232 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
1233 int inuse; /*!< number of HTTP sessions using this entry */
1234 int needdestroy; /*!< Whether an HTTP session should be destroyed */
1235 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
1236 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
1237 time_t sessionstart; /*!< Session start time */
1238 struct timeval sessionstart_tv; /*!< Session start time */
1239 time_t sessiontimeout; /*!< Session timeout if HTTP */
1240 char username[80]; /*!< Logged in username */
1241 char challenge[10]; /*!< Authentication challenge */
1242 int authenticated; /*!< Authentication status */
1243 int readperm; /*!< Authorization for reading */
1244 int writeperm; /*!< Authorization for writing */
1245 char inbuf[1025]; /*!< Buffer - we use the extra byte to add a '\\0' and simplify parsing */
1246 int inlen; /*!< number of buffered bytes */
1247 struct ao2_container *whitefilters; /*!< Manager event filters - white list */
1248 struct ao2_container *blackfilters; /*!< Manager event filters - black list */
1249 struct ast_variable *chanvars; /*!< Channel variables to set for originate */
1250 int send_events; /*!< XXX what ? */
1251 struct eventqent *last_ev; /*!< last event processed. */
1252 int writetimeout; /*!< Timeout for ast_carefulwrite() */
1254 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
1255 time_t noncetime; /*!< Timer for nonce value expiration */
1256 unsigned long oldnonce; /*!< Stale nonce value */
1257 unsigned long nc; /*!< incremental nonce counter */
1258 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
1259 AST_LIST_ENTRY(mansession_session) list;
1262 enum mansession_message_parsing {
1264 MESSAGE_LINE_TOO_LONG
1267 /*! \brief In case you didn't read that giant block of text above the mansession_session struct, the
1268 * \ref struct mansession is named this solely to keep the API the same in Asterisk. This structure really
1269 * represents data that is different from Manager action to Manager action. The mansession_session pointer
1270 * contained within points to session-specific data.
1273 struct mansession_session *session;
1274 struct ast_tcptls_session_instance *tcptls_session;
1277 enum mansession_message_parsing parsing;
1279 struct manager_custom_hook *hook;
1283 /*! Active manager connection sessions container. */
1284 static AO2_GLOBAL_OBJ_STATIC(mgr_sessions);
1286 /*! \brief user descriptor, as read from the config file.
1288 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
1289 * lines which are not supported here, and readperm/writeperm/writetimeout
1292 struct ast_manager_user {
1294 char *secret; /*!< Secret for logging in */
1295 int readperm; /*!< Authorization for reading */
1296 int writeperm; /*!< Authorization for writing */
1297 int writetimeout; /*!< Per user Timeout for ast_carefulwrite() */
1298 int displayconnects; /*!< XXX unused */
1299 int allowmultiplelogin; /*!< Per user option*/
1300 int keep; /*!< mark entries created on a reload */
1301 struct ao2_container *whitefilters; /*!< Manager event filters - white list */
1302 struct ao2_container *blackfilters; /*!< Manager event filters - black list */
1303 struct ast_acl_list *acl; /*!< ACL setting */
1304 char *a1_hash; /*!< precalculated A1 for Digest auth */
1305 struct ast_variable *chanvars; /*!< Channel variables to set for originate */
1306 AST_RWLIST_ENTRY(ast_manager_user) list;
1309 /*! \brief list of users found in the config file */
1310 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
1312 /*! \brief list of actions registered */
1313 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
1315 /*! \brief list of hooks registered */
1316 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
1318 /*! \brief A container of event documentation nodes */
1319 static AO2_GLOBAL_OBJ_STATIC(event_docs);
1321 static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters);
1324 * @{ \brief Define AMI message types.
1326 STASIS_MESSAGE_TYPE_DEFN(ast_manager_get_generic_type);
1331 * \brief Find a registered action object.
1333 * \param name Name of AMI action to find.
1335 * \return Reffed action found or NULL
1337 static struct manager_action *action_find(const char *name)
1339 struct manager_action *act;
1341 AST_RWLIST_RDLOCK(&actions);
1342 AST_RWLIST_TRAVERSE(&actions, act, list) {
1343 if (!strcasecmp(name, act->action)) {
1344 ao2_t_ref(act, +1, "found action object");
1348 AST_RWLIST_UNLOCK(&actions);
1353 struct stasis_topic *ast_manager_get_topic(void)
1355 return manager_topic;
1358 struct stasis_message_router *ast_manager_get_message_router(void)
1360 return stasis_router;
1363 static void manager_json_value_str_append(struct ast_json *value, const char *key,
1364 struct ast_str **res)
1366 switch (ast_json_typeof(value)) {
1367 case AST_JSON_STRING:
1368 ast_str_append(res, 0, "%s: %s\r\n", key, ast_json_string_get(value));
1370 case AST_JSON_INTEGER:
1371 ast_str_append(res, 0, "%s: %jd\r\n", key, ast_json_integer_get(value));
1374 ast_str_append(res, 0, "%s: True\r\n", key);
1376 case AST_JSON_FALSE:
1377 ast_str_append(res, 0, "%s: False\r\n", key);
1380 ast_str_append(res, 0, "%s: \r\n", key);
1385 static void manager_json_to_ast_str(struct ast_json *obj, const char *key,
1386 struct ast_str **res, key_exclusion_cb exclusion_cb);
1388 static void manager_json_array_with_key(struct ast_json *obj, const char* key,
1389 size_t index, struct ast_str **res,
1390 key_exclusion_cb exclusion_cb)
1392 struct ast_str *key_str = ast_str_alloca(64);
1393 ast_str_set(&key_str, 0, "%s(%zu)", key, index);
1394 manager_json_to_ast_str(obj, ast_str_buffer(key_str),
1398 static void manager_json_obj_with_key(struct ast_json *obj, const char* key,
1399 const char *parent_key, struct ast_str **res,
1400 key_exclusion_cb exclusion_cb)
1403 struct ast_str *key_str = ast_str_alloca(64);
1404 ast_str_set(&key_str, 0, "%s/%s", parent_key, key);
1405 manager_json_to_ast_str(obj, ast_str_buffer(key_str),
1410 manager_json_to_ast_str(obj, key, res, exclusion_cb);
1413 void manager_json_to_ast_str(struct ast_json *obj, const char *key,
1414 struct ast_str **res, key_exclusion_cb exclusion_cb)
1416 struct ast_json_iter *i;
1418 if (!obj || (!res && !(*res) && (!(*res = ast_str_create(1024))))) {
1422 if (exclusion_cb && key && exclusion_cb(key)) {
1426 if (ast_json_typeof(obj) != AST_JSON_OBJECT &&
1427 ast_json_typeof(obj) != AST_JSON_ARRAY) {
1428 manager_json_value_str_append(obj, key, res);
1432 if (ast_json_typeof(obj) == AST_JSON_ARRAY) {
1434 for (j = 0; j < ast_json_array_size(obj); ++j) {
1435 manager_json_array_with_key(ast_json_array_get(obj, j),
1436 key, j, res, exclusion_cb);
1441 for (i = ast_json_object_iter(obj); i;
1442 i = ast_json_object_iter_next(obj, i)) {
1443 manager_json_obj_with_key(ast_json_object_iter_value(i),
1444 ast_json_object_iter_key(i),
1445 key, res, exclusion_cb);
1450 struct ast_str *ast_manager_str_from_json_object(struct ast_json *blob, key_exclusion_cb exclusion_cb)
1452 struct ast_str *res = ast_str_create(1024);
1453 manager_json_to_ast_str(blob, NULL, &res, exclusion_cb);
1457 static void manager_default_msg_cb(void *data, struct stasis_subscription *sub,
1458 struct stasis_message *message)
1460 RAII_VAR(struct ast_manager_event_blob *, ev, NULL, ao2_cleanup);
1462 ev = stasis_message_to_ami(message);
1465 /* Not and AMI message; disregard */
1469 manager_event(ev->event_flags, ev->manager_event, "%s",
1473 static void manager_generic_msg_cb(void *data, struct stasis_subscription *sub,
1474 struct stasis_message *message)
1476 struct ast_json_payload *payload = stasis_message_data(message);
1477 int class_type = ast_json_integer_get(ast_json_object_get(payload->json, "class_type"));
1478 const char *type = ast_json_string_get(ast_json_object_get(payload->json, "type"));
1479 struct ast_json *event = ast_json_object_get(payload->json, "event");
1480 RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free);
1482 event_buffer = ast_manager_str_from_json_object(event, NULL);
1483 if (!event_buffer) {
1484 ast_log(AST_LOG_WARNING, "Error while creating payload for event %s\n", type);
1487 manager_event(class_type, type, "%s", ast_str_buffer(event_buffer));
1490 void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
1492 RAII_VAR(struct ast_json *, event_info, NULL, ast_json_unref);
1493 RAII_VAR(struct ast_json_payload *, payload, NULL, ao2_cleanup);
1494 RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
1501 event_info = ast_json_pack("{s: s, s: i, s: o}",
1503 "class_type", class_type,
1509 payload = ast_json_payload_create(event_info);
1513 message = stasis_message_create(ast_manager_get_generic_type(), payload);
1517 stasis_publish(ast_manager_get_topic(), message);
1520 /*! \brief Add a custom hook to be called when an event is fired */
1521 void ast_manager_register_hook(struct manager_custom_hook *hook)
1523 AST_RWLIST_WRLOCK(&manager_hooks);
1524 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
1525 AST_RWLIST_UNLOCK(&manager_hooks);
1528 /*! \brief Delete a custom hook to be called when an event is fired */
1529 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
1531 AST_RWLIST_WRLOCK(&manager_hooks);
1532 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
1533 AST_RWLIST_UNLOCK(&manager_hooks);
1536 int check_manager_enabled(void)
1538 return manager_enabled;
1541 int check_webmanager_enabled(void)
1543 return (webmanager_enabled && manager_enabled);
1547 * Grab a reference to the last event, update usecount as needed.
1548 * Can handle a NULL pointer.
1550 static struct eventqent *grab_last(void)
1552 struct eventqent *ret;
1554 AST_RWLIST_WRLOCK(&all_events);
1555 ret = AST_RWLIST_LAST(&all_events);
1556 /* the list is never empty now, but may become so when
1557 * we optimize it in the future, so be prepared.
1560 ast_atomic_fetchadd_int(&ret->usecount, 1);
1562 AST_RWLIST_UNLOCK(&all_events);
1567 * Purge unused events. Remove elements from the head
1568 * as long as their usecount is 0 and there is a next element.
1570 static void purge_events(void)
1572 struct eventqent *ev;
1573 struct timeval now = ast_tvnow();
1575 AST_RWLIST_WRLOCK(&all_events);
1576 while ( (ev = AST_RWLIST_FIRST(&all_events)) &&
1577 ev->usecount == 0 && AST_RWLIST_NEXT(ev, eq_next)) {
1578 AST_RWLIST_REMOVE_HEAD(&all_events, eq_next);
1582 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&all_events, ev, eq_next) {
1583 /* Never release the last event */
1584 if (!AST_RWLIST_NEXT(ev, eq_next)) {
1588 /* 2.5 times whatever the HTTP timeout is (maximum 2.5 hours) is the maximum time that we will definitely cache an event */
1589 if (ev->usecount == 0 && ast_tvdiff_sec(now, ev->tv) > (httptimeout > 3600 ? 3600 : httptimeout) * 2.5) {
1590 AST_RWLIST_REMOVE_CURRENT(eq_next);
1594 AST_RWLIST_TRAVERSE_SAFE_END;
1595 AST_RWLIST_UNLOCK(&all_events);
1599 * helper functions to convert back and forth between
1600 * string and numeric representation of set of flags
1602 static const struct permalias {
1606 { EVENT_FLAG_SYSTEM, "system" },
1607 { EVENT_FLAG_CALL, "call" },
1608 { EVENT_FLAG_LOG, "log" },
1609 { EVENT_FLAG_VERBOSE, "verbose" },
1610 { EVENT_FLAG_COMMAND, "command" },
1611 { EVENT_FLAG_AGENT, "agent" },
1612 { EVENT_FLAG_USER, "user" },
1613 { EVENT_FLAG_CONFIG, "config" },
1614 { EVENT_FLAG_DTMF, "dtmf" },
1615 { EVENT_FLAG_REPORTING, "reporting" },
1616 { EVENT_FLAG_CDR, "cdr" },
1617 { EVENT_FLAG_DIALPLAN, "dialplan" },
1618 { EVENT_FLAG_ORIGINATE, "originate" },
1619 { EVENT_FLAG_AGI, "agi" },
1620 { EVENT_FLAG_CC, "cc" },
1621 { EVENT_FLAG_AOC, "aoc" },
1622 { EVENT_FLAG_TEST, "test" },
1623 { EVENT_FLAG_SECURITY, "security" },
1624 { EVENT_FLAG_MESSAGE, "message" },
1629 /*! \brief Checks to see if a string which can be used to evaluate functions should be rejected */
1630 static int function_capable_string_allowed_with_auths(const char *evaluating, int writepermlist)
1632 if (!(writepermlist & EVENT_FLAG_SYSTEM)
1634 strstr(evaluating, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
1635 strstr(evaluating, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
1642 /*! \brief Convert authority code to a list of options for a user. This will only
1643 * display those authority codes that have an explicit match on authority */
1644 static const char *user_authority_to_str(int authority, struct ast_str **res)
1649 ast_str_reset(*res);
1650 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
1651 if ((authority & perms[i].num) == perms[i].num) {
1652 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
1657 if (ast_str_strlen(*res) == 0) /* replace empty string with something sensible */
1658 ast_str_append(res, 0, "<none>");
1660 return ast_str_buffer(*res);
1664 /*! \brief Convert authority code to a list of options. Note that the EVENT_FLAG_ALL
1665 * authority will always be returned. */
1666 static const char *authority_to_str(int authority, struct ast_str **res)
1671 ast_str_reset(*res);
1672 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
1673 if (authority & perms[i].num) {
1674 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
1679 if (ast_str_strlen(*res) == 0) /* replace empty string with something sensible */
1680 ast_str_append(res, 0, "<none>");
1682 return ast_str_buffer(*res);
1685 /*! Tells you if smallstr exists inside bigstr
1686 which is delim by delim and uses no buf or stringsep
1687 ast_instring("this|that|more","this",'|') == 1;
1689 feel free to move this to app.c -anthm */
1690 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
1692 const char *val = bigstr, *next;
1695 if ((next = strchr(val, delim))) {
1696 if (!strncmp(val, smallstr, (next - val))) {
1702 return !strcmp(smallstr, val);
1704 } while (*(val = (next + 1)));
1709 static int get_perm(const char *instr)
1717 for (x = 0; x < ARRAY_LEN(perms); x++) {
1718 if (ast_instring(instr, perms[x].label, ',')) {
1719 ret |= perms[x].num;
1727 * A number returns itself, false returns 0, true returns all flags,
1728 * other strings return the flags that are set.
1730 static int strings_to_mask(const char *string)
1734 if (ast_strlen_zero(string)) {
1738 for (p = string; *p; p++) {
1739 if (*p < '0' || *p > '9') {
1743 if (!*p) { /* all digits */
1744 return atoi(string);
1746 if (ast_false(string)) {
1749 if (ast_true(string)) { /* all permissions */
1751 for (x = 0; x < ARRAY_LEN(perms); x++) {
1752 ret |= perms[x].num;
1756 return get_perm(string);
1759 /*! \brief Unreference manager session object.
1760 If no more references, then go ahead and delete it */
1761 static struct mansession_session *unref_mansession(struct mansession_session *s)
1763 int refcount = ao2_ref(s, -1);
1764 if (manager_debug) {
1765 ast_debug(1, "Mansession: %p refcount now %d\n", s, refcount - 1);
1770 static void event_filter_destructor(void *obj)
1772 regex_t *regex_filter = obj;
1773 regfree(regex_filter);
1776 static void session_destructor(void *obj)
1778 struct mansession_session *session = obj;
1779 struct eventqent *eqe = session->last_ev;
1780 struct ast_datastore *datastore;
1782 /* Get rid of each of the data stores on the session */
1783 while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
1784 /* Free the data store */
1785 ast_datastore_free(datastore);
1788 if (session->f != NULL) {
1793 ast_atomic_fetchadd_int(&eqe->usecount, -1);
1795 if (session->chanvars) {
1796 ast_variables_destroy(session->chanvars);
1799 if (session->whitefilters) {
1800 ao2_t_ref(session->whitefilters, -1, "decrement ref for white container, should be last one");
1803 if (session->blackfilters) {
1804 ao2_t_ref(session->blackfilters, -1, "decrement ref for black container, should be last one");
1808 /*! \brief Allocate manager session structure and add it to the list of sessions */
1809 static struct mansession_session *build_mansession(const struct ast_sockaddr *addr)
1811 struct ao2_container *sessions;
1812 struct mansession_session *newsession;
1814 newsession = ao2_alloc(sizeof(*newsession), session_destructor);
1819 newsession->whitefilters = ao2_container_alloc(1, NULL, NULL);
1820 newsession->blackfilters = ao2_container_alloc(1, NULL, NULL);
1821 if (!newsession->whitefilters || !newsession->blackfilters) {
1822 ao2_ref(newsession, -1);
1826 newsession->fd = -1;
1827 newsession->waiting_thread = AST_PTHREADT_NULL;
1828 newsession->writetimeout = 100;
1829 newsession->send_events = -1;
1830 ast_sockaddr_copy(&newsession->addr, addr);
1832 sessions = ao2_global_obj_ref(mgr_sessions);
1834 ao2_link(sessions, newsession);
1835 ao2_ref(sessions, -1);
1841 static int mansession_cmp_fn(void *obj, void *arg, int flags)
1843 struct mansession_session *s = obj;
1845 return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
1848 static void session_destroy(struct mansession_session *s)
1850 struct ao2_container *sessions;
1852 sessions = ao2_global_obj_ref(mgr_sessions);
1854 ao2_unlink(sessions, s);
1855 ao2_ref(sessions, -1);
1857 unref_mansession(s);
1861 static int check_manager_session_inuse(const char *name)
1863 struct ao2_container *sessions;
1864 struct mansession_session *session;
1867 sessions = ao2_global_obj_ref(mgr_sessions);
1869 session = ao2_find(sessions, (char *) name, 0);
1870 ao2_ref(sessions, -1);
1872 unref_mansession(session);
1881 * lookup an entry in the list of registered users.
1882 * must be called with the list lock held.
1884 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
1886 struct ast_manager_user *user = NULL;
1888 AST_RWLIST_TRAVERSE(&users, user, list) {
1889 if (!strcasecmp(user->username, name)) {
1897 /*! \brief Get displayconnects config option.
1898 * \param session manager session to get parameter from.
1899 * \return displayconnects config option value.
1901 static int manager_displayconnects(struct mansession_session *session)
1903 struct ast_manager_user *user = NULL;
1906 AST_RWLIST_RDLOCK(&users);
1907 if ((user = get_manager_by_name_locked(session->username))) {
1908 ret = user->displayconnects;
1910 AST_RWLIST_UNLOCK(&users);
1915 static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance);
1917 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1919 struct manager_action *cur;
1920 struct ast_str *authority;
1924 char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64];
1925 char arguments_title[64], privilege_title[64], final_response_title[64], list_responses_title[64];
1930 e->command = "manager show command";
1932 "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
1933 " Shows the detailed description for a specific Asterisk manager interface command.\n";
1936 l = strlen(a->word);
1938 AST_RWLIST_RDLOCK(&actions);
1939 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1940 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
1941 ret = ast_strdup(cur->action);
1942 break; /* make sure we exit even if ast_strdup() returns NULL */
1945 AST_RWLIST_UNLOCK(&actions);
1948 authority = ast_str_alloca(80);
1950 return CLI_SHOWUSAGE;
1954 /* setup the titles */
1955 term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1956 term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
1957 term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1958 term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
1959 term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
1960 term_color(privilege_title, "[Privilege]\n", COLOR_MAGENTA, 0, 40);
1961 term_color(final_response_title, "[Final Response]\n", COLOR_MAGENTA, 0, 40);
1962 term_color(list_responses_title, "[List Responses]\n", COLOR_MAGENTA, 0, 40);
1965 AST_RWLIST_RDLOCK(&actions);
1966 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1967 for (num = 3; num < a->argc; num++) {
1968 if (!strcasecmp(cur->action, a->argv[num])) {
1969 authority_to_str(cur->authority, &authority);
1972 if (cur->docsrc == AST_XML_DOC) {
1973 char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
1974 char *synopsis = ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1);
1975 char *description = ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1);
1976 char *arguments = ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1);
1977 char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1);
1978 char *privilege = ast_xmldoc_printable(S_OR(authority->str, "Not available"), 1);
1979 char *responses = ast_xmldoc_printable("None", 1);
1980 ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s",
1981 syntax_title, syntax,
1982 synopsis_title, synopsis,
1983 description_title, description,
1984 arguments_title, arguments,
1985 seealso_title, seealso,
1986 privilege_title, privilege,
1987 list_responses_title);
1989 if (!cur->list_responses) {
1990 ast_cli(a->fd, "%s\n\n", responses);
1992 struct ast_xml_doc_item *temp;
1993 for (temp = cur->list_responses; temp; temp = AST_LIST_NEXT(temp, next)) {
1994 ast_cli(a->fd, "Event: %s\n", temp->name);
1995 print_event_instance(a, temp);
1999 ast_cli(a->fd, "%s", final_response_title);
2001 if (!cur->final_response) {
2002 ast_cli(a->fd, "%s\n\n", responses);
2004 ast_cli(a->fd, "Event: %s\n", cur->final_response->name);
2005 print_event_instance(a, cur->final_response);
2010 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
2011 cur->action, cur->synopsis,
2013 S_OR(cur->description, ""));
2018 AST_RWLIST_UNLOCK(&actions);
2023 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2027 e->command = "manager set debug [on|off]";
2028 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
2035 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
2036 } else if (a->argc == 4) {
2037 if (!strcasecmp(a->argv[3], "on")) {
2039 } else if (!strcasecmp(a->argv[3], "off")) {
2042 return CLI_SHOWUSAGE;
2048 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2050 struct ast_manager_user *user = NULL;
2053 struct ast_str *rauthority = ast_str_alloca(128);
2054 struct ast_str *wauthority = ast_str_alloca(128);
2055 struct ast_variable *v;
2059 e->command = "manager show user";
2061 " Usage: manager show user <user>\n"
2062 " Display all information related to the manager user specified.\n";
2065 l = strlen(a->word);
2070 AST_RWLIST_RDLOCK(&users);
2071 AST_RWLIST_TRAVERSE(&users, user, list) {
2072 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
2073 ret = ast_strdup(user->username);
2077 AST_RWLIST_UNLOCK(&users);
2082 return CLI_SHOWUSAGE;
2085 AST_RWLIST_RDLOCK(&users);
2087 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
2088 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
2089 AST_RWLIST_UNLOCK(&users);
2093 ast_cli(a->fd, "\n");
2100 " displayconnects: %s\n"
2101 "allowmultiplelogin: %s\n",
2102 (user->username ? user->username : "(N/A)"),
2103 (user->secret ? "<Set>" : "(N/A)"),
2104 ((user->acl && !ast_acl_list_is_empty(user->acl)) ? "yes" : "no"),
2105 user_authority_to_str(user->readperm, &rauthority),
2106 user_authority_to_str(user->writeperm, &wauthority),
2107 (user->displayconnects ? "yes" : "no"),
2108 (user->allowmultiplelogin ? "yes" : "no"));
2109 ast_cli(a->fd, " Variables: \n");
2110 for (v = user->chanvars ; v ; v = v->next) {
2111 ast_cli(a->fd, " %s = %s\n", v->name, v->value);
2114 AST_RWLIST_UNLOCK(&users);
2119 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2121 struct ast_manager_user *user = NULL;
2125 e->command = "manager show users";
2127 "Usage: manager show users\n"
2128 " Prints a listing of all managers that are currently configured on that\n"
2135 return CLI_SHOWUSAGE;
2138 AST_RWLIST_RDLOCK(&users);
2140 /* If there are no users, print out something along those lines */
2141 if (AST_RWLIST_EMPTY(&users)) {
2142 ast_cli(a->fd, "There are no manager users.\n");
2143 AST_RWLIST_UNLOCK(&users);
2147 ast_cli(a->fd, "\nusername\n--------\n");
2149 AST_RWLIST_TRAVERSE(&users, user, list) {
2150 ast_cli(a->fd, "%s\n", user->username);
2154 AST_RWLIST_UNLOCK(&users);
2156 ast_cli(a->fd,"-------------------\n"
2157 "%d manager users configured.\n", count_amu);
2161 /*! \brief CLI command manager list commands */
2162 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2164 struct manager_action *cur;
2166 int space_remaining;
2167 #define HSMC_FORMAT " %-*.*s %-.*s\n"
2170 e->command = "manager show commands";
2172 "Usage: manager show commands\n"
2173 " Prints a listing of all the available Asterisk manager interface commands.\n";
2179 AST_RWLIST_RDLOCK(&actions);
2180 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2181 int incoming_len = strlen(cur->action);
2182 if (incoming_len > name_len) {
2183 name_len = incoming_len;
2187 space_remaining = MGR_SHOW_TERMINAL_WIDTH - name_len - 4;
2188 if (space_remaining < 0) {
2189 space_remaining = 0;
2192 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "Action", space_remaining, "Synopsis");
2193 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "------", space_remaining, "--------");
2195 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2196 ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, cur->action, space_remaining, cur->synopsis);
2198 AST_RWLIST_UNLOCK(&actions);
2203 /*! \brief CLI command manager list connected */
2204 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2206 struct ao2_container *sessions;
2207 struct mansession_session *session;
2208 time_t now = time(NULL);
2209 #define HSMCONN_FORMAT1 " %-15.15s %-55.55s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
2210 #define HSMCONN_FORMAT2 " %-15.15s %-55.55s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
2212 struct ao2_iterator i;
2216 e->command = "manager show connected";
2218 "Usage: manager show connected\n"
2219 " Prints a listing of the users that are currently connected to the\n"
2220 "Asterisk manager interface.\n";
2226 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
2228 sessions = ao2_global_obj_ref(mgr_sessions);
2230 i = ao2_iterator_init(sessions, 0);
2231 ao2_ref(sessions, -1);
2232 while ((session = ao2_iterator_next(&i))) {
2234 ast_cli(a->fd, HSMCONN_FORMAT2, session->username,
2235 ast_sockaddr_stringify_addr(&session->addr),
2236 (int) (session->sessionstart),
2237 (int) (now - session->sessionstart),
2241 session->writeperm);
2243 ao2_unlock(session);
2244 unref_mansession(session);
2246 ao2_iterator_destroy(&i);
2248 ast_cli(a->fd, "%d users connected.\n", count);
2253 /*! \brief CLI command manager list eventq */
2254 /* Should change to "manager show connected" */
2255 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2257 struct eventqent *s;
2260 e->command = "manager show eventq";
2262 "Usage: manager show eventq\n"
2263 " Prints a listing of all events pending in the Asterisk manger\n"
2269 AST_RWLIST_RDLOCK(&all_events);
2270 AST_RWLIST_TRAVERSE(&all_events, s, eq_next) {
2271 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
2272 ast_cli(a->fd, "Category: %d\n", s->category);
2273 ast_cli(a->fd, "Event:\n%s", s->eventdata);
2275 AST_RWLIST_UNLOCK(&all_events);
2280 /*! \brief CLI command manager reload */
2281 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2285 e->command = "manager reload";
2287 "Usage: manager reload\n"
2288 " Reloads the manager configuration.\n";
2294 return CLI_SHOWUSAGE;
2300 static struct eventqent *advance_event(struct eventqent *e)
2302 struct eventqent *next;
2304 AST_RWLIST_RDLOCK(&all_events);
2305 if ((next = AST_RWLIST_NEXT(e, eq_next))) {
2306 ast_atomic_fetchadd_int(&next->usecount, 1);
2307 ast_atomic_fetchadd_int(&e->usecount, -1);
2309 AST_RWLIST_UNLOCK(&all_events);
2313 #define GET_HEADER_FIRST_MATCH 0
2314 #define GET_HEADER_LAST_MATCH 1
2315 #define GET_HEADER_SKIP_EMPTY 2
2318 * \brief Return a matching header value.
2321 * Generic function to return either the first or the last
2322 * matching header from a list of variables, possibly skipping
2325 * \note At the moment there is only one use of this function in
2326 * this file, so we make it static.
2328 * \note Never returns NULL.
2330 static const char *__astman_get_header(const struct message *m, char *var, int mode)
2332 int x, l = strlen(var);
2333 const char *result = "";
2339 for (x = 0; x < m->hdrcount; x++) {
2340 const char *h = m->headers[x];
2341 if (!strncasecmp(var, h, l) && h[l] == ':') {
2342 const char *value = h + l + 1;
2343 value = ast_skip_blanks(value); /* ignore leading spaces in the value */
2344 /* found a potential candidate */
2345 if ((mode & GET_HEADER_SKIP_EMPTY) && ast_strlen_zero(value)) {
2346 continue; /* not interesting */
2348 if (mode & GET_HEADER_LAST_MATCH) {
2349 result = value; /* record the last match so far */
2360 * \brief Return the first matching variable from an array.
2362 * \note This is the legacy function and is implemented in
2363 * therms of __astman_get_header().
2365 * \note Never returns NULL.
2367 const char *astman_get_header(const struct message *m, char *var)
2369 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
2374 * \brief Process one "Variable:" header value string.
2376 * \param head Current list of AMI variables to get new values added.
2377 * \param hdr_val Header value string to process.
2379 * \return New variable list head.
2381 static struct ast_variable *man_do_variable_value(struct ast_variable *head, const char *hdr_val)
2384 AST_DECLARE_APP_ARGS(args,
2385 AST_APP_ARG(vars)[64];
2388 hdr_val = ast_skip_blanks(hdr_val); /* ignore leading spaces in the value */
2389 parse = ast_strdupa(hdr_val);
2391 /* Break the header value string into name=val pair items. */
2392 AST_STANDARD_APP_ARGS(args, parse);
2396 /* Process each name=val pair item. */
2397 for (y = 0; y < args.argc; y++) {
2398 struct ast_variable *cur;
2402 if (!args.vars[y]) {
2405 var = val = args.vars[y];
2408 /* XXX We may wish to trim whitespace from the strings. */
2409 if (!val || ast_strlen_zero(var)) {
2413 /* Create new variable list node and prepend it to the list. */
2414 cur = ast_variable_new(var, val, "");
2425 struct ast_variable *astman_get_variables(const struct message *m)
2427 return astman_get_variables_order(m, ORDER_REVERSE);
2430 struct ast_variable *astman_get_variables_order(const struct message *m,
2431 enum variable_orders order)
2435 struct ast_variable *head = NULL;
2437 static const char var_hdr[] = "Variable:";
2439 /* Process all "Variable:" headers. */
2440 varlen = strlen(var_hdr);
2441 for (x = 0; x < m->hdrcount; x++) {
2442 if (strncasecmp(var_hdr, m->headers[x], varlen)) {
2445 head = man_do_variable_value(head, m->headers[x] + varlen);
2448 if (order == ORDER_NATURAL) {
2449 head = ast_variables_reverse(head);
2455 /*! \brief access for hooks to send action messages to ami */
2456 int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
2460 struct manager_action *act_found;
2461 struct mansession s = {.session = NULL, };
2462 struct message m = { 0 };
2472 /* Create our own copy of the AMI action msg string. */
2473 src = dup_str = ast_strdup(msg);
2478 /* convert msg string to message struct */
2479 curlen = strlen(src);
2480 for (x = 0; x < curlen; x++) {
2481 int cr; /* set if we have \r */
2482 if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
2483 cr = 2; /* Found. Update length to include \r\n */
2484 else if (src[x] == '\n')
2485 cr = 1; /* also accept \n only */
2488 /* don't keep empty lines */
2489 if (x && m.hdrcount < ARRAY_LEN(m.headers)) {
2490 /* ... but trim \r\n and terminate the header string */
2492 m.headers[m.hdrcount++] = src;
2495 curlen -= x; /* remaining size */
2496 src += x; /* update pointer */
2497 x = -1; /* reset loop */
2500 action = astman_get_header(&m, "Action");
2501 if (strcasecmp(action, "login")) {
2502 act_found = action_find(action);
2505 * we have to simulate a session for this action request
2506 * to be able to pass it down for processing
2507 * This is necessary to meet the previous design of manager.c
2510 s.f = (void*)1; /* set this to something so our request will make it through all functions that test it*/
2512 ao2_lock(act_found);
2513 if (act_found->registered && act_found->func) {
2514 if (act_found->module) {
2515 ast_module_ref(act_found->module);
2517 ao2_unlock(act_found);
2518 ret = act_found->func(&s, &m);
2519 ao2_lock(act_found);
2520 if (act_found->module) {
2521 ast_module_unref(act_found->module);
2526 ao2_unlock(act_found);
2527 ao2_t_ref(act_found, -1, "done with found action object");
2536 * helper function to send a string to the socket.
2537 * Return -1 on error (e.g. buffer full).
2539 static int send_string(struct mansession *s, char *string)
2542 FILE *f = s->f ? s->f : s->session->f;
2543 int fd = s->f ? s->fd : s->session->fd;
2545 /* It's a result from one of the hook's action invocation */
2548 * to send responses, we're using the same function
2549 * as for receiving events. We call the event "HookResponse"
2551 s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
2555 if ((res = ast_careful_fwrite(f, fd, string, strlen(string), s->session->writetimeout))) {
2563 * \brief thread local buffer for astman_append
2565 * \note This can not be defined within the astman_append() function
2566 * because it declares a couple of functions that get used to
2567 * initialize the thread local storage key.
2569 AST_THREADSTORAGE(astman_append_buf);
2571 AST_THREADSTORAGE(userevent_buf);
2573 /*! \brief initial allocated size for the astman_append_buf and astman_send_*_va */
2574 #define ASTMAN_APPEND_BUF_INITSIZE 256
2577 * utility functions for creating AMI replies
2579 void astman_append(struct mansession *s, const char *fmt, ...)
2582 struct ast_str *buf;
2584 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
2589 ast_str_set_va(&buf, 0, fmt, ap);
2592 if (s->f != NULL || s->session->f != NULL) {
2593 send_string(s, ast_str_buffer(buf));
2595 ast_verbose("fd == -1 in astman_append, should not happen\n");
2599 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
2600 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
2601 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
2602 be non-zero). In either of these cases, there is no need to lock-protect the session's
2603 fd, since no other output will be sent (events will be queued), and no input will
2604 be read until either the current action finishes or get_input() obtains the session
2608 /*! \todo XXX MSG_MOREDATA should go to a header file. */
2609 #define MSG_MOREDATA ((char *)astman_send_response)
2611 /*! \brief send a response with an optional message,
2612 * and terminate it with an empty line.
2613 * m is used only to grab the 'ActionID' field.
2615 * Use the explicit constant MSG_MOREDATA to remove the empty line.
2616 * XXX MSG_MOREDATA should go to a header file.
2618 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
2620 const char *id = astman_get_header(m, "ActionID");
2622 astman_append(s, "Response: %s\r\n", resp);
2623 if (!ast_strlen_zero(id)) {
2624 astman_append(s, "ActionID: %s\r\n", id);
2627 astman_append(s, "EventList: %s\r\n", listflag); /* Start, complete, cancelled */
2629 if (msg == MSG_MOREDATA) {
2632 astman_append(s, "Message: %s\r\n\r\n", msg);
2634 astman_append(s, "\r\n");
2638 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
2640 astman_send_response_full(s, m, resp, msg, NULL);
2643 void astman_send_error(struct mansession *s, const struct message *m, char *error)
2645 astman_send_response_full(s, m, "Error", error, NULL);
2648 void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
2651 struct ast_str *buf;
2654 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
2659 ast_str_set_va(&buf, 0, fmt, ap);
2662 /* astman_append will use the same underlying buffer, so copy the message out
2663 * before sending the response */
2664 msg = ast_str_buffer(buf);
2666 msg = ast_strdupa(msg);
2668 astman_send_response_full(s, m, "Error", msg, NULL);
2671 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
2673 astman_send_response_full(s, m, "Success", msg, NULL);
2676 static void astman_start_ack(struct mansession *s, const struct message *m)
2678 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
2681 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
2683 astman_send_response_full(s, m, "Success", msg, listflag);
2686 /*! \brief Lock the 'mansession' structure. */
2687 static void mansession_lock(struct mansession *s)
2689 ast_mutex_lock(&s->lock);
2692 /*! \brief Unlock the 'mansession' structure. */
2693 static void mansession_unlock(struct mansession *s)
2695 ast_mutex_unlock(&s->lock);
2699 Rather than braindead on,off this now can also accept a specific int mask value
2700 or a ',' delim list of mask strings (the same as manager.conf) -anthm
2702 static int set_eventmask(struct mansession *s, const char *eventmask)
2704 int maskint = strings_to_mask(eventmask);
2706 ao2_lock(s->session);
2708 s->session->send_events = maskint;
2710 ao2_unlock(s->session);
2715 static enum ast_transport mansession_get_transport(const struct mansession *s)
2717 return s->tcptls_session->parent->tls_cfg ? AST_TRANSPORT_TLS :
2721 static void report_invalid_user(const struct mansession *s, const char *username)
2723 char session_id[32];
2724 struct ast_security_event_inval_acct_id inval_acct_id = {
2725 .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
2726 .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
2727 .common.service = "AMI",
2728 .common.account_id = username,
2729 .common.session_tv = &s->session->sessionstart_tv,
2730 .common.local_addr = {
2731 .addr = &s->tcptls_session->parent->local_address,
2732 .transport = mansession_get_transport(s),
2734 .common.remote_addr = {
2735 .addr = &s->session->addr,
2736 .transport = mansession_get_transport(s),
2738 .common.session_id = session_id,
2741 snprintf(session_id, sizeof(session_id), "%p", s);
2743 ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
2746 static void report_failed_acl(const struct mansession *s, const char *username)
2748 char session_id[32];
2749 struct ast_security_event_failed_acl failed_acl_event = {
2750 .common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
2751 .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
2752 .common.service = "AMI",
2753 .common.account_id = username,
2754 .common.session_tv = &s->session->sessionstart_tv,
2755 .common.local_addr = {
2756 .addr = &s->tcptls_session->parent->local_address,
2757 .transport = mansession_get_transport(s),
2759 .common.remote_addr = {
2760 .addr = &s->session->addr,
2761 .transport = mansession_get_transport(s),
2763 .common.session_id = session_id,
2766 snprintf(session_id, sizeof(session_id), "%p", s->session);
2768 ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
2771 static void report_inval_password(const struct mansession *s, const char *username)
2773 char session_id[32];
2774 struct ast_security_event_inval_password inval_password = {
2775 .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
2776 .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
2777 .common.service = "AMI",
2778 .common.account_id = username,
2779 .common.session_tv = &s->session->sessionstart_tv,
2780 .common.local_addr = {
2781 .addr = &s->tcptls_session->parent->local_address,
2782 .transport = mansession_get_transport(s),
2784 .common.remote_addr = {
2785 .addr = &s->session->addr,
2786 .transport = mansession_get_transport(s),
2788 .common.session_id = session_id,
2791 snprintf(session_id, sizeof(session_id), "%p", s->session);
2793 ast_security_event_report(AST_SEC_EVT(&inval_password));
2796 static void report_auth_success(const struct mansession *s)
2798 char session_id[32];
2799 struct ast_security_event_successful_auth successful_auth = {
2800 .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
2801 .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
2802 .common.service = "AMI",
2803 .common.account_id = s->session->username,
2804 .common.session_tv = &s->session->sessionstart_tv,
2805 .common.local_addr = {
2806 .addr = &s->tcptls_session->parent->local_address,
2807 .transport = mansession_get_transport(s),
2809 .common.remote_addr = {
2810 .addr = &s->session->addr,
2811 .transport = mansession_get_transport(s),
2813 .common.session_id = session_id,
2816 snprintf(session_id, sizeof(session_id), "%p", s->session);
2818 ast_security_event_report(AST_SEC_EVT(&successful_auth));
2821 static void report_req_not_allowed(const struct mansession *s, const char *action)
2823 char session_id[32];
2824 char request_type[64];
2825 struct ast_security_event_req_not_allowed req_not_allowed = {
2826 .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
2827 .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
2828 .common.service = "AMI",
2829 .common.account_id = s->session->username,
2830 .common.session_tv = &s->session->sessionstart_tv,
2831 .common.local_addr = {
2832 .addr = &s->tcptls_session->parent->local_address,
2833 .transport = mansession_get_transport(s),
2835 .common.remote_addr = {
2836 .addr = &s->session->addr,
2837 .transport = mansession_get_transport(s),
2839 .common.session_id = session_id,
2841 .request_type = request_type,
2844 snprintf(session_id, sizeof(session_id), "%p", s->session);
2845 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2847 ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
2850 static void report_req_bad_format(const struct mansession *s, const char *action)
2852 char session_id[32];
2853 char request_type[64];
2854 struct ast_security_event_req_bad_format req_bad_format = {
2855 .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
2856 .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
2857 .common.service = "AMI",
2858 .common.account_id = s->session->username,
2859 .common.session_tv = &s->session->sessionstart_tv,
2860 .common.local_addr = {
2861 .addr = &s->tcptls_session->parent->local_address,
2862 .transport = mansession_get_transport(s),
2864 .common.remote_addr = {
2865 .addr = &s->session->addr,
2866 .transport = mansession_get_transport(s),
2868 .common.session_id = session_id,
2870 .request_type = request_type,
2873 snprintf(session_id, sizeof(session_id), "%p", s->session);
2874 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2876 ast_security_event_report(AST_SEC_EVT(&req_bad_format));
2879 static void report_failed_challenge_response(const struct mansession *s,
2880 const char *response, const char *expected_response)
2882 char session_id[32];
2883 struct ast_security_event_chal_resp_failed chal_resp_failed = {
2884 .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
2885 .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
2886 .common.service = "AMI",
2887 .common.account_id = s->session->username,
2888 .common.session_tv = &s->session->sessionstart_tv,
2889 .common.local_addr = {
2890 .addr = &s->tcptls_session->parent->local_address,
2891 .transport = mansession_get_transport(s),
2893 .common.remote_addr = {
2894 .addr = &s->session->addr,
2895 .transport = mansession_get_transport(s),
2897 .common.session_id = session_id,
2899 .challenge = s->session->challenge,
2900 .response = response,
2901 .expected_response = expected_response,
2904 snprintf(session_id, sizeof(session_id), "%p", s->session);
2906 ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
2909 static void report_session_limit(const struct mansession *s)
2911 char session_id[32];
2912 struct ast_security_event_session_limit session_limit = {
2913 .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
2914 .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
2915 .common.service = "AMI",
2916 .common.account_id = s->session->username,
2917 .common.session_tv = &s->session->sessionstart_tv,
2918 .common.local_addr = {
2919 .addr = &s->tcptls_session->parent->local_address,
2920 .transport = mansession_get_transport(s),
2922 .common.remote_addr = {
2923 .addr = &s->session->addr,
2924 .transport = mansession_get_transport(s),
2926 .common.session_id = session_id,
2929 snprintf(session_id, sizeof(session_id), "%p", s->session);
2931 ast_security_event_report(AST_SEC_EVT(&session_limit));
2935 * Here we start with action_ handlers for AMI actions,
2936 * and the internal functions used by them.
2937 * Generally, the handlers are called action_foo()
2940 /* helper function for action_login() */
2941 static int authenticate(struct mansession *s, const struct message *m)
2943 const char *username = astman_get_header(m, "Username");
2944 const char *password = astman_get_header(m, "Secret");
2946 struct ast_manager_user *user = NULL;
2947 regex_t *regex_filter;
2948 struct ao2_iterator filter_iter;
2950 if (ast_strlen_zero(username)) { /* missing username */
2954 /* locate user in locked state */
2955 AST_RWLIST_WRLOCK(&users);
2957 if (!(user = get_manager_by_name_locked(username))) {
2958 report_invalid_user(s, username);
2959 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2960 } else if (user->acl && (ast_apply_acl(user->acl, &s->session->addr, "Manager User ACL: ") == AST_SENSE_DENY)) {
2961 report_failed_acl(s, username);
2962 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2963 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
2964 const char *key = astman_get_header(m, "Key");
2965 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
2968 char md5key[256] = "";
2969 struct MD5Context md5;
2970 unsigned char digest[16];
2973 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
2974 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
2975 MD5Final(digest, &md5);
2976 for (x = 0; x < 16; x++)
2977 len += sprintf(md5key + len, "%2.2x", (unsigned)digest[x]);
2978 if (!strcmp(md5key, key)) {
2981 report_failed_challenge_response(s, key, md5key);
2984 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
2985 S_OR(s->session->challenge, ""));
2987 } else if (user->secret) {
2988 if (!strcmp(password, user->secret)) {
2991 report_inval_password(s, username);
2996 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
2997 AST_RWLIST_UNLOCK(&users);
3003 /* All of the user parameters are copied to the session so that in the event
3004 * of a reload and a configuration change, the session parameters are not
3006 ast_copy_string(s->session->username, username, sizeof(s->session->username));
3007 s->session->readperm = user->readperm;
3008 s->session->writeperm = user->writeperm;
3009 s->session->writetimeout = user->writetimeout;
3010 if (user->chanvars) {
3011 s->session->chanvars = ast_variables_dup(user->chanvars);
3014 filter_iter = ao2_iterator_init(user->whitefilters, 0);
3015 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
3016 ao2_t_link(s->session->whitefilters, regex_filter, "add white user filter to session");
3017 ao2_t_ref(regex_filter, -1, "remove iterator ref");
3019 ao2_iterator_destroy(&filter_iter);
3021 filter_iter = ao2_iterator_init(user->blackfilters, 0);
3022 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
3023 ao2_t_link(s->session->blackfilters, regex_filter, "add black user filter to session");
3024 ao2_t_ref(regex_filter, -1, "remove iterator ref");
3026 ao2_iterator_destroy(&filter_iter);
3028 s->session->sessionstart = time(NULL);
3029 s->session->sessionstart_tv = ast_tvnow();
3030 set_eventmask(s, astman_get_header(m, "Events"));
3032 report_auth_success(s);
3034 AST_RWLIST_UNLOCK(&users);
3038 static int action_ping(struct mansession *s, const struct message *m)
3040 const char *actionid = astman_get_header(m, "ActionID");
3041 struct timeval now = ast_tvnow();
3043 astman_append(s, "Response: Success\r\n");
3044 if (!ast_strlen_zero(actionid)){
3045 astman_append(s, "ActionID: %s\r\n", actionid);
3050 "Timestamp: %ld.%06lu\r\n"
3052 (long) now.tv_sec, (unsigned long) now.tv_usec);
3056 static int action_getconfig(struct mansession *s, const struct message *m)
3058 struct ast_config *cfg;
3059 const char *fn = astman_get_header(m, "Filename");
3060 const char *category = astman_get_header(m, "Category");
3063 char *cur_category = NULL;
3064 struct ast_variable *v;
3065 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
3067 if (ast_strlen_zero(fn)) {
3068 astman_send_error(s, m, "Filename not specified");
3071 cfg = ast_config_load2(fn, "manager", config_flags);
3072 if (cfg == CONFIG_STATUS_FILEMISSING) {
3073 astman_send_error(s, m, "Config file not found");
3075 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
3076 astman_send_error(s, m, "Config file has invalid format");
3080 astman_start_ack(s, m);
3081 while ((cur_category = ast_category_browse(cfg, cur_category))) {
3082 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
3084 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
3085 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
3086 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
3091 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
3092 astman_append(s, "No categories found\r\n");
3094 ast_config_destroy(cfg);
3095 astman_append(s, "\r\n");
3100 static int action_listcategories(struct mansession *s, const struct message *m)
3102 struct ast_config *cfg;
3103 const char *fn = astman_get_header(m, "Filename");
3104 char *category = NULL;
3105 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
3108 if (ast_strlen_zero(fn)) {
3109 astman_send_error(s, m, "Filename not specified");
3112 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
3113 astman_send_error(s, m, "Config file not found");
3115 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
3116 astman_send_error(s, m, "Config file has invalid format");
3119 astman_start_ack(s, m);
3120 while ((category = ast_category_browse(cfg, category))) {
3121 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
3124 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
3125 astman_append(s, "Error: no categories found\r\n");
3127 ast_config_destroy(cfg);
3128 astman_append(s, "\r\n");
3136 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
3137 static void json_escape(char *out, const char *in)
3140 if (*in == '\\' || *in == '\"') {
3150 * \brief Append a JSON escaped string to the manager stream.
3152 * \param s AMI stream to append a string.
3153 * \param str String to append to the stream after JSON escaping it.
3157 static void astman_append_json(struct mansession *s, const char *str)
3161 buf = ast_alloca(2 * strlen(str) + 1);
3162 json_escape(buf, str);
3163 astman_append(s, "%s", buf);
3166 static int action_getconfigjson(struct mansession *s, const struct message *m)
3168 struct ast_config *cfg;
3169 const char *fn = astman_get_header(m, "Filename");
3170 char *category = NULL;
3171 struct ast_variable *v;
3173 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
3175 if (ast_strlen_zero(fn)) {
3176 astman_send_error(s, m, "Filename not specified");
3180 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
3181 astman_send_error(s, m, "Config file not found");
3183 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
3184 astman_send_error(s, m, "Config file has invalid format");
3188 astman_start_ack(s, m);
3189 astman_append(s, "JSON: {");
3190 while ((category = ast_category_browse(cfg, category))) {
3193 astman_append(s, "%s\"", comma1 ? "," : "");
3194 astman_append_json(s, category);
3195 astman_append(s, "\":[");
3197 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
3198 astman_append(s, "%s\"", comma2 ? "," : "");
3199 astman_append_json(s, v->name);
3200 astman_append(s, "\":\"");
3201 astman_append_json(s, v->value);
3202 astman_append(s, "\"");
3205 astman_append(s, "]");
3207 astman_append(s, "}\r\n\r\n");
3209 ast_config_destroy(cfg);
3214 /*! \brief helper function for action_updateconfig */
3215 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
3219 const char *action, *cat, *var, *value, *match, *line;
3220 struct ast_category *category;
3221 struct ast_variable *v;
3222 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
3223 enum error_type result = 0;
3225 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
3226 unsigned int object = 0;
3228 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
3229 action = astman_get_header(m, hdr);
3230 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
3231 break; /* this could cause problems if actions come in misnumbered */
3233 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
3234 cat = astman_get_header(m, hdr);
3235 if (ast_strlen_zero(cat)) { /* every action needs a category */
3236 result = UNSPECIFIED_CATEGORY;
3240 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
3241 var = astman_get_header(m, hdr);
3243 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
3244 value = astman_get_header(m, hdr);
3246 if (!ast_strlen_zero(value) && *value == '>') {
3251 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
3252 match = astman_get_header(m, hdr);
3254 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
3255 line = astman_get_header(m, hdr);
3257 if (!strcasecmp(action, "newcat")) {
3258 if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
3259 result = FAILURE_NEWCAT; /* already exist */
3262 if (!(category = ast_category_new(cat, dfn, -1))) {
3263 result = FAILURE_ALLOCATION;
3266 if (ast_strlen_zero(match)) {
3267 ast_category_append(cfg, category);
3269 ast_category_insert(cfg, category, match);
3271 } else if (!strcasecmp(action, "renamecat")) {
3272 if (ast_strlen_zero(value)) {
3273 result = UNSPECIFIED_ARGUMENT;
3276 if (!(category = ast_category_get(cfg, cat))) {
3277 result = UNKNOWN_CATEGORY;
3280 ast_category_rename(category, value);
3281 } else if (!strcasecmp(action, "delcat")) {
3282 if (ast_category_delete(cfg, cat)) {
3283 result = FAILURE_DELCAT;
3286 } else if (!strcasecmp(action, "emptycat")) {
3287 if (ast_category_empty(cfg, cat)) {
3288 result = FAILURE_EMPTYCAT;
3291 } else if (!strcasecmp(action, "update")) {
3292 if (ast_strlen_zero(var)) {
3293 result = UNSPECIFIED_ARGUMENT;
3296 if (!(category = ast_category_get(cfg,cat))) {
3297 result = UNKNOWN_CATEGORY;
3300 if (ast_variable_update(category, var, value, match, object)) {
3301 result = FAILURE_UPDATE;
3304 } else if (!strcasecmp(action, "delete")) {
3305 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
3306 result = UNSPECIFIED_ARGUMENT;
3309 if (!(category = ast_category_get(cfg, cat))) {
3310 result = UNKNOWN_CATEGORY;
3313 if (ast_variable_delete(category, var, match, line)) {
3314 result = FAILURE_DELETE;
3317 } else if (!strcasecmp(action, "append")) {
3318 if (ast_strlen_zero(var)) {
3319 result = UNSPECIFIED_ARGUMENT;
3322 if (!(category = ast_category_get(cfg, cat))) {
3323 result = UNKNOWN_CATEGORY;
3326 if (!(v = ast_variable_new(var, value, dfn))) {
3327 result = FAILURE_ALLOCATION;
3330 if (object || (match && !strcasecmp(match, "object"))) {
3333 ast_variable_append(category, v);
3334 } else if (!strcasecmp(action, "insert")) {
3335 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
3336 result = UNSPECIFIED_ARGUMENT;
3339 if (!(category = ast_category_get(cfg, cat))) {
3340 result = UNKNOWN_CATEGORY;
3343 if (!(v = ast_variable_new(var, value, dfn))) {
3344 result = FAILURE_ALLOCATION;
3347 ast_variable_insert(category, v, line);
3350 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
3351 result = UNKNOWN_ACTION;
3360 static int action_updateconfig(struct mansession *s, const struct message *m)
3362 struct ast_config *cfg;
3363 const char *sfn = astman_get_header(m, "SrcFilename");
3364 const char *dfn = astman_get_header(m, "DstFilename");
3366 const char *rld = astman_get_header(m, "Reload");
3367 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
3368 enum error_type result;
3370 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
3371 astman_send_error(s, m, "Filename not specified");
3374 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
3375 astman_send_error(s, m, "Config file not found");
3377 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
3378 astman_send_error(s, m, "Config file has invalid format");
3381 result = handle_updates(s, m, cfg, dfn);
3383 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
3384 res = ast_config_text_file_save(dfn, cfg, "Manager");
3385 ast_config_destroy(cfg);
3387 astman_send_error(s, m, "Save of config failed");
3390 astman_send_ack(s, m, NULL);
3391 if (!ast_strlen_zero(rld)) {
3392 if (ast_true(rld)) {
3395 ast_module_reload(rld);
3398 ast_config_destroy(cfg);
3400 case UNKNOWN_ACTION:
3401 astman_send_error(s, m, "Unknown action command");
3403 case UNKNOWN_CATEGORY:
3404 astman_send_error(s, m, "Given category does not exist");
3406 case UNSPECIFIED_CATEGORY:
3407 astman_send_error(s, m, "Category not specified");
3409 case UNSPECIFIED_ARGUMENT:
3410 astman_send_error(s, m, "Problem with category, value, or line (if required)");
3412 case FAILURE_ALLOCATION:
3413 astman_send_error(s, m, "Memory allocation failure, this should not happen");
3415 case FAILURE_NEWCAT:
3416 astman_send_error(s, m, "Create category did not complete successfully");
3418 case FAILURE_DELCAT:
3419 astman_send_error(s, m, "Delete category did not complete successfully");
3421 case FAILURE_EMPTYCAT:
3422 astman_send_error(s, m, "Empty category did not complete successfully");
3424 case FAILURE_UPDATE:
3425 astman_send_error(s, m, "Update did not complete successfully");
3427 case FAILURE_DELETE:
3428 astman_send_error(s, m, "Delete did not complete successfully");
3430 case FAILURE_APPEND:
3431 astman_send_error(s, m, "Append did not complete successfully");
3438 static int action_createconfig(struct mansession *s, const struct message *m)
3441 const char *fn = astman_get_header(m, "Filename");
3442 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
3443 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
3444 ast_str_append(&filepath, 0, "%s", fn);
3446 if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
3448 astman_send_ack(s, m, "New configuration file created successfully");
3450 astman_send_error(s, m, strerror(errno));
3456 static int action_waitevent(struct mansession *s, const struct message *m)
3458 const char *timeouts = astman_get_header(m, "Timeout");
3462 const char *id = astman_get_header(m, "ActionID");