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 * \extref 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 /*! \addtogroup Group_AMI AMI functions
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/_private.h"
49 #include "asterisk/paths.h" /* use various ast_config_AST_* */
54 #include <sys/types.h>
57 #include "asterisk/channel.h"
58 #include "asterisk/file.h"
59 #include "asterisk/manager.h"
60 #include "asterisk/module.h"
61 #include "asterisk/config.h"
62 #include "asterisk/callerid.h"
63 #include "asterisk/lock.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/app.h"
66 #include "asterisk/pbx.h"
67 #include "asterisk/md5.h"
68 #include "asterisk/acl.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/tcptls.h"
71 #include "asterisk/http.h"
72 #include "asterisk/ast_version.h"
73 #include "asterisk/threadstorage.h"
74 #include "asterisk/linkedlists.h"
75 #include "asterisk/term.h"
76 #include "asterisk/astobj2.h"
77 #include "asterisk/features.h"
78 #include "asterisk/security_events.h"
79 #include "asterisk/aoc.h"
82 <manager name="Ping" language="en_US">
87 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
90 <para>A 'Ping' action will ellicit a 'Pong' response. Used to keep the
91 manager connection open.</para>
94 <manager name="Events" language="en_US">
99 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
100 <parameter name="EventMask" required="true">
103 <para>If all events should be sent.</para>
106 <para>If no events should be sent.</para>
108 <enum name="system,call,log,...">
109 <para>To select which flags events should have to be sent.</para>
115 <para>Enable/Disable sending of events to this manager client.</para>
118 <manager name="Logoff" language="en_US">
123 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
126 <para>Logoff the current manager session.</para>
129 <manager name="Login" language="en_US">
134 <parameter name="ActionID">
135 <para>ActionID for this transaction. Will be returned.</para>
139 <para>Login Manager.</para>
142 <manager name="Challenge" language="en_US">
144 Generate Challenge for MD5 Auth.
147 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
150 <para>Generate a challenge for MD5 authentication.</para>
153 <manager name="Hangup" language="en_US">
158 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
159 <parameter name="Channel" required="true">
160 <para>The channel name to be hangup.</para>
162 <parameter name="Cause">
163 <para>Numeric hangup cause.</para>
167 <para>Hangup a channel.</para>
170 <manager name="Status" language="en_US">
175 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
176 <parameter name="Channel" required="true">
177 <para>The name of the channel to query for status.</para>
179 <parameter name="Variables">
180 <para>Comma <literal>,</literal> separated list of variable to include.</para>
184 <para>Will return the status information of each channel along with the
185 value for the specified channel variables.</para>
188 <manager name="Setvar" language="en_US">
190 Set a channel variable.
193 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
194 <parameter name="Channel">
195 <para>Channel to set variable for.</para>
197 <parameter name="Variable" required="true">
198 <para>Variable name.</para>
200 <parameter name="Value" required="true">
201 <para>Variable value.</para>
205 <para>Set a global or local channel variable.</para>
208 <manager name="Getvar" language="en_US">
210 Gets a channel variable.
213 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
214 <parameter name="Channel">
215 <para>Channel to read variable from.</para>
217 <parameter name="Variable" required="true">
218 <para>Variable name.</para>
222 <para>Get the value of a global or local channel variable.</para>
225 <manager name="GetConfig" language="en_US">
227 Retrieve configuration.
230 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
231 <parameter name="Filename" required="true">
232 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
234 <parameter name="Category">
235 <para>Category in configuration file.</para>
239 <para>This action will dump the contents of a configuration
240 file by category and contents or optionally by specified category only.</para>
243 <manager name="GetConfigJSON" language="en_US">
245 Retrieve configuration (JSON format).
248 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
249 <parameter name="Filename" required="true">
250 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
254 <para>This action will dump the contents of a configuration file by category
255 and contents in JSON format. This only makes sense to be used using rawman over
256 the HTTP interface.</para>
259 <manager name="UpdateConfig" language="en_US">
261 Update basic configuration.
264 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
265 <parameter name="SrcFilename" required="true">
266 <para>Configuration filename to read (e.g. <filename>foo.conf</filename>).</para>
268 <parameter name="DstFilename" required="true">
269 <para>Configuration filename to write (e.g. <filename>foo.conf</filename>)</para>
271 <parameter name="Reload">
272 <para>Whether or not a reload should take place (or name of specific module).</para>
274 <parameter name="Action-XXXXXX">
275 <para>Action to take.</para>
276 <para>X's represent 6 digit number beginning with 000000.</para>
278 <enum name="NewCat" />
279 <enum name="RenameCat" />
280 <enum name="DelCat" />
281 <enum name="EmptyCat" />
282 <enum name="Update" />
283 <enum name="Delete" />
284 <enum name="Append" />
285 <enum name="Insert" />
288 <parameter name="Cat-XXXXXX">
289 <para>Category to operate on.</para>
290 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
292 <parameter name="Var-XXXXXX">
293 <para>Variable to work on.</para>
294 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
296 <parameter name="Value-XXXXXX">
297 <para>Value to work on.</para>
298 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
300 <parameter name="Match-XXXXXX">
301 <para>Extra match required to match line.</para>
302 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
304 <parameter name="Line-XXXXXX">
305 <para>Line in category to operate on (used with delete and insert actions).</para>
306 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
310 <para>This action will modify, create, or delete configuration elements
311 in Asterisk configuration files.</para>
314 <manager name="CreateConfig" language="en_US">
316 Creates an empty file in the configuration directory.
319 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
320 <parameter name="Filename" required="true">
321 <para>The configuration filename to create (e.g. <filename>foo.conf</filename>).</para>
325 <para>This action will create an empty file in the configuration
326 directory. This action is intended to be used before an UpdateConfig
330 <manager name="ListCategories" language="en_US">
332 List categories in configuration file.
335 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
336 <parameter name="Filename" required="true">
337 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
341 <para>This action will dump the categories in a given file.</para>
344 <manager name="Redirect" language="en_US">
346 Redirect (transfer) a call.
349 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
350 <parameter name="Channel" required="true">
351 <para>Channel to redirect.</para>
353 <parameter name="ExtraChannel">
354 <para>Second call leg to transfer (optional).</para>
356 <parameter name="Exten" required="true">
357 <para>Extension to transfer to.</para>
359 <parameter name="ExtraExten">
360 <para>Extension to transfer extrachannel to (optional).</para>
362 <parameter name="Context" required="true">
363 <para>Context to transfer to.</para>
365 <parameter name="ExtraContext">
366 <para>Context to transfer extrachannel to (optional).</para>
368 <parameter name="Priority" required="true">
369 <para>Priority to transfer to.</para>
371 <parameter name="ExtraPriority">
372 <para>Priority to transfer extrachannel to (optional).</para>
376 <para>Redirect (transfer) a call.</para>
379 <manager name="Atxfer" language="en_US">
384 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
385 <parameter name="Channel" required="true">
386 <para>Transferer's channel.</para>
388 <parameter name="Exten" required="true">
389 <para>Extension to transfer to.</para>
391 <parameter name="Context" required="true">
392 <para>Context to transfer to.</para>
394 <parameter name="Priority" required="true">
395 <para>Priority to transfer to.</para>
399 <para>Attended transfer.</para>
402 <manager name="Originate" language="en_US">
407 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
408 <parameter name="Channel" required="true">
409 <para>Channel name to call.</para>
411 <parameter name="Exten">
412 <para>Extension to use (requires <literal>Context</literal> and
413 <literal>Priority</literal>)</para>
415 <parameter name="Context">
416 <para>Context to use (requires <literal>Exten</literal> and
417 <literal>Priority</literal>)</para>
419 <parameter name="Priority">
420 <para>Priority to use (requires <literal>Exten</literal> and
421 <literal>Context</literal>)</para>
423 <parameter name="Application">
424 <para>Application to execute.</para>
426 <parameter name="Data">
427 <para>Data to use (requires <literal>Application</literal>).</para>
429 <parameter name="Timeout" default="30000">
430 <para>How long to wait for call to be answered (in ms.).</para>
432 <parameter name="CallerID">
433 <para>Caller ID to be set on the outgoing channel.</para>
435 <parameter name="Variable">
436 <para>Channel variable to set, multiple Variable: headers are allowed.</para>
438 <parameter name="Account">
439 <para>Account code.</para>
441 <parameter name="Async">
442 <para>Set to <literal>true</literal> for fast origination.</para>
444 <parameter name="Codecs">
445 <para>Comma-separated list of codecs to use for this call.</para>
449 <para>Generates an outgoing call to a
450 <replaceable>Extension</replaceable>/<replaceable>Context</replaceable>/<replaceable>Priority</replaceable>
451 or <replaceable>Application</replaceable>/<replaceable>Data</replaceable></para>
454 <manager name="Command" language="en_US">
456 Execute Asterisk CLI Command.
459 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
460 <parameter name="Command" required="true">
461 <para>Asterisk CLI command to run.</para>
465 <para>Run a CLI command.</para>
468 <manager name="ExtensionState" language="en_US">
470 Check Extension Status.
473 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
474 <parameter name="Exten" required="true">
475 <para>Extension to check state on.</para>
477 <parameter name="Context" required="true">
478 <para>Context for extension.</para>
482 <para>Report the extension state for given extension. If the extension has a hint,
483 will use devicestate to check the status of the device connected to the extension.</para>
484 <para>Will return an <literal>Extension Status</literal> message. The response will include
485 the hint for the extension and the status.</para>
488 <manager name="AbsoluteTimeout" language="en_US">
490 Set absolute timeout.
493 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
494 <parameter name="Channel" required="true">
495 <para>Channel name to hangup.</para>
497 <parameter name="Timeout" required="true">
498 <para>Maximum duration of the call (sec).</para>
502 <para>Hangup a channel after a certain time. Acknowledges set time with
503 <literal>Timeout Set</literal> message.</para>
506 <manager name="MailboxStatus" language="en_US">
511 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
512 <parameter name="Mailbox" required="true">
513 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
517 <para>Checks a voicemail account for status.</para>
518 <para>Returns number of messages.</para>
519 <para>Message: Mailbox Status.</para>
520 <para>Mailbox: <replaceable>mailboxid</replaceable>.</para>
521 <para>Waiting: <replaceable>count</replaceable>.</para>
524 <manager name="MailboxCount" language="en_US">
526 Check Mailbox Message Count.
529 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
530 <parameter name="Mailbox" required="true">
531 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
535 <para>Checks a voicemail account for new messages.</para>
536 <para>Returns number of urgent, new and old messages.</para>
537 <para>Message: Mailbox Message Count</para>
538 <para>Mailbox: <replaceable>mailboxid</replaceable></para>
539 <para>UrgentMessages: <replaceable>count</replaceable></para>
540 <para>NewMessages: <replaceable>count</replaceable></para>
541 <para>OldMessages: <replaceable>count</replaceable></para>
544 <manager name="ListCommands" language="en_US">
546 List available manager commands.
549 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
552 <para>Returns the action name and synopsis for every action that
553 is available to the user.</para>
556 <manager name="SendText" language="en_US">
558 Send text message to channel.
561 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
562 <parameter name="Channel" required="true">
563 <para>Channel to send message to.</para>
565 <parameter name="Message" required="true">
566 <para>Message to send.</para>
570 <para>Sends A Text Message to a channel while in a call.</para>
573 <manager name="UserEvent" language="en_US">
575 Send an arbitrary event.
578 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
579 <parameter name="UserEvent" required="true">
580 <para>Event string to send.</para>
582 <parameter name="Header1">
583 <para>Content1.</para>
585 <parameter name="HeaderN">
586 <para>ContentN.</para>
590 <para>Send an event to manager sessions.</para>
593 <manager name="WaitEvent" language="en_US">
595 Wait for an event to occur.
598 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
599 <parameter name="Timeout" required="true">
600 <para>Maximum time (in seconds) to wait for events, <literal>-1</literal> means forever.</para>
604 <para>This action will ellicit a <literal>Success</literal> response. Whenever
605 a manager event is queued. Once WaitEvent has been called on an HTTP manager
606 session, events will be generated and queued.</para>
609 <manager name="CoreSettings" language="en_US">
611 Show PBX core settings (version etc).
614 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
617 <para>Query for Core PBX settings.</para>
620 <manager name="CoreStatus" language="en_US">
622 Show PBX core status variables.
625 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
628 <para>Query for Core PBX status.</para>
631 <manager name="Reload" language="en_US">
636 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
637 <parameter name="Module">
638 <para>Name of the module to reload.</para>
642 <para>Send a reload event.</para>
645 <manager name="CoreShowChannels" language="en_US">
647 List currently active channels.
650 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
653 <para>List currently defined channels and some information about them.</para>
656 <manager name="ModuleLoad" language="en_US">
661 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
662 <parameter name="Module">
663 <para>Asterisk module name (including .so extension) or subsystem identifier:</para>
667 <enum name="dnsmgr" />
668 <enum name="extconfig" />
669 <enum name="manager" />
674 <parameter name="LoadType" required="true">
675 <para>The operation to be done on module.</para>
678 <enum name="unload" />
679 <enum name="reload" />
681 <para>If no module is specified for a <literal>reload</literal> loadtype,
682 all modules are reloaded.</para>
686 <para>Loads, unloads or reloads an Asterisk module in a running system.</para>
689 <manager name="ModuleCheck" language="en_US">
691 Check if module is loaded.
694 <parameter name="Module" required="true">
695 <para>Asterisk module name (not including extension).</para>
699 <para>Checks if Asterisk module is loaded. Will return Success/Failure.
700 For success returns, the module revision number is included.</para>
703 <manager name="AOCMessage" language="en_US">
705 Generate an Advice of Charge message on a channel.
708 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
709 <parameter name="Channel" required="true">
710 <para>Channel name to generate the AOC message on.</para>
712 <parameter name="ChannelPrefix">
713 <para>Partial channel prefix. By using this option one can match the beginning part
714 of a channel name without having to put the entire name in. For example
715 if a channel name is SIP/snom-00000001 and this value is set to SIP/snom, then
716 that channel matches and the message will be sent. Note however that only
717 the first matched channel has the message sent on it. </para>
719 <parameter name="MsgType" required="true">
720 <para>Defines what type of AOC message to create, AOC-D or AOC-E</para>
726 <parameter name="ChargeType" required="true">
727 <para>Defines what kind of charge this message represents.</para>
731 <enum name="Currency" />
735 <parameter name="UnitAmount(0)">
736 <para>This represents the amount of units charged. The ETSI AOC standard specifies that
737 this value along with the optional UnitType value are entries in a list. To accommodate this
738 these values take an index value starting at 0 which can be used to generate this list of
739 unit entries. For Example, If two unit entires were required this could be achieved by setting the
740 paramter UnitAmount(0)=1234 and UnitAmount(1)=5678. Note that UnitAmount at index 0 is
741 required when ChargeType=Unit, all other entries in the list are optional.
744 <parameter name="UnitType(0)">
745 <para>Defines the type of unit. ETSI AOC standard specifies this as an integer
746 value between 1 and 16, but this value is left open to accept any positive
747 integer. Like the UnitAmount parameter, this value represents a list entry
748 and has an index parameter that starts at 0.
751 <parameter name="CurrencyName">
752 <para>Specifies the currency's name. Note that this value is truncated after 10 characters.</para>
754 <parameter name="CurrencyAmount">
755 <para>Specifies the charge unit amount as a positive integer. This value is required
756 when ChargeType==Currency.</para>
758 <parameter name="CurrencyMultiplier">
759 <para>Specifies the currency multiplier. This value is required when ChargeType==Currency.</para>
761 <enum name="OneThousandth" />
762 <enum name="OneHundredth" />
763 <enum name="OneTenth" />
766 <enum name="Hundred" />
767 <enum name="Thousand" />
770 <parameter name="TotalType" default="Total">
771 <para>Defines what kind of AOC-D total is represented.</para>
773 <enum name="Total" />
774 <enum name="SubTotal" />
777 <parameter name="AOCBillingId">
778 <para>Represents a billing ID associated with an AOC-D or AOC-E message. Note
779 that only the first 3 items of the enum are valid AOC-D billing IDs</para>
781 <enum name="Normal" />
782 <enum name="ReverseCharge" />
783 <enum name="CreditCard" />
784 <enum name="CallFwdUnconditional" />
785 <enum name="CallFwdBusy" />
786 <enum name="CallFwdNoReply" />
787 <enum name="CallDeflection" />
788 <enum name="CallTransfer" />
791 <parameter name="ChargingAssociationId">
792 <para>Charging association identifier. This is optional for AOC-E and can be
793 set to any value between -32768 and 32767</para>
795 <parameter name="ChargingAssociationNumber">
796 <para>Represents the charging association party number. This value is optional
799 <parameter name="ChargingAssociationPlan">
800 <para>Integer representing the charging plan associated with the ChargingAssociationNumber.
801 The value is bits 7 through 1 of the Q.931 octet containing the type-of-number and
802 numbering-plan-identification fields.</para>
806 <para>Generates an AOC-D or AOC-E message on a channel.</para>
814 UNSPECIFIED_CATEGORY,
815 UNSPECIFIED_ARGUMENT,
827 * Linked list of events.
828 * Global events are appended to the list by append_event().
829 * The usecount is the number of stored pointers to the element,
830 * excluding the list pointers. So an element that is only in
831 * the list has a usecount of 0, not 1.
833 * Clients have a pointer to the last event processed, and for each
834 * of these clients we track the usecount of the elements.
835 * If we have a pointer to an entry in the list, it is safe to navigate
836 * it forward because elements will not be deleted, but only appended.
837 * The worst that can happen is seeing the pointer still NULL.
839 * When the usecount of an element drops to 0, and the element is the
840 * first in the list, we can remove it. Removal is done within the
841 * main thread, which is woken up for the purpose.
843 * For simplicity of implementation, we make sure the list is never empty.
846 int usecount; /*!< # of clients who still need the event */
848 unsigned int seq; /*!< sequence number */
849 struct timeval tv; /*!< When event was allocated */
850 AST_RWLIST_ENTRY(eventqent) eq_next;
851 char eventdata[1]; /*!< really variable size, allocated by append_event() */
854 static AST_RWLIST_HEAD_STATIC(all_events, eventqent);
856 static int displayconnects = 1;
857 static int allowmultiplelogin = 1;
858 static int timestampevents;
859 static int httptimeout = 60;
860 static int broken_events_action = 0;
861 static int manager_enabled = 0;
862 static int webmanager_enabled = 0;
863 static char *manager_channelvars;
865 #define DEFAULT_REALM "asterisk"
866 static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
868 static int block_sockets;
870 static int manager_debug; /*!< enable some debugging code in the manager */
873 * Descriptor for a manager session, either on the AMI socket or over HTTP.
876 * AMI session have managerid == 0; the entry is created upon a connect,
877 * and destroyed with the socket.
878 * HTTP sessions have managerid != 0, the value is used as a search key
879 * to lookup sessions (using the mansession_id cookie, or nonce key from
880 * Digest Authentication http header).
882 #define MAX_BLACKLIST_CMD_LEN 2
883 static const struct {
884 const char *words[AST_MAX_CMD_LEN];
885 } command_blacklist[] = {
886 {{ "module", "load", NULL }},
887 {{ "module", "unload", NULL }},
888 {{ "restart", "gracefully", NULL }},
891 /* In order to understand what the heck is going on with the
892 * mansession_session and mansession structs, we need to have a bit of a history
895 * In the beginning, there was the mansession. The mansession contained data that was
896 * intrinsic to a manager session, such as the time that it started, the name of the logged-in
897 * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
898 * sessions, these were used to represent the TCP socket over which the AMI session was taking
899 * place. It makes perfect sense for these fields to be a part of the session-specific data since
900 * the session actually defines this information.
902 * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
903 * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
904 * but rather to the action that is being executed. Because a single session may execute many commands
905 * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
906 * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
907 * has had a chance to properly close its handles.
909 * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
910 * from being run at the same time in a single session. Some manager actions may block for a long time, thus
911 * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
912 * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
913 * part of the action instead.
915 * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
916 * contain the action-specific information, such as which file to write to. In order to maintain expectations
917 * of action handlers and not have to change the public API of the manager code, we would need to name this
918 * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
919 * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
920 * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
923 struct mansession_session {
924 /*! \todo XXX need to document which fields it is protecting */
925 struct sockaddr_in sin; /*!< address we are connecting from */
926 FILE *f; /*!< fdopen() on the underlying fd */
927 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
928 int inuse; /*!< number of HTTP sessions using this entry */
929 int needdestroy; /*!< Whether an HTTP session should be destroyed */
930 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
931 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
932 time_t sessionstart; /*!< Session start time */
933 struct timeval sessionstart_tv; /*!< Session start time */
934 time_t sessiontimeout; /*!< Session timeout if HTTP */
935 char username[80]; /*!< Logged in username */
936 char challenge[10]; /*!< Authentication challenge */
937 int authenticated; /*!< Authentication status */
938 int readperm; /*!< Authorization for reading */
939 int writeperm; /*!< Authorization for writing */
940 char inbuf[1025]; /*!< Buffer - we use the extra byte to add a '\0' and simplify parsing */
941 int inlen; /*!< number of buffered bytes */
942 struct ao2_container *whitefilters; /*!< Manager event filters - white list */
943 struct ao2_container *blackfilters; /*!< Manager event filters - black list */
944 int send_events; /*!< XXX what ? */
945 struct eventqent *last_ev; /*!< last event processed. */
946 int writetimeout; /*!< Timeout for ast_carefulwrite() */
947 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
948 time_t noncetime; /*!< Timer for nonce value expiration */
949 unsigned long oldnonce; /*!< Stale nonce value */
950 unsigned long nc; /*!< incremental nonce counter */
951 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
952 AST_LIST_ENTRY(mansession_session) list;
955 /*! \brief In case you didn't read that giant block of text above the mansession_session struct, the
956 * \ref struct mansession is named this solely to keep the API the same in Asterisk. This structure really
957 * represents data that is different from Manager action to Manager action. The mansession_session pointer
958 * contained within points to session-specific data.
961 struct mansession_session *session;
962 struct ast_tcptls_session_instance *tcptls_session;
965 struct manager_custom_hook *hook;
969 static struct ao2_container *sessions = NULL;
971 struct manager_channel_variable {
972 AST_LIST_ENTRY(manager_channel_variable) entry;
973 unsigned int isfunc:1;
974 char name[0]; /* allocate off the end the real size. */
977 static AST_RWLIST_HEAD_STATIC(channelvars, manager_channel_variable);
979 /*! \brief user descriptor, as read from the config file.
981 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
982 * lines which are not supported here, and readperm/writeperm/writetimeout
985 struct ast_manager_user {
987 char *secret; /*!< Secret for logging in */
988 struct ast_ha *ha; /*!< ACL setting */
989 int readperm; /*!< Authorization for reading */
990 int writeperm; /*!< Authorization for writing */
991 int writetimeout; /*!< Per user Timeout for ast_carefulwrite() */
992 int displayconnects; /*!< XXX unused */
993 int keep; /*!< mark entries created on a reload */
994 struct ao2_container *whitefilters; /*!< Manager event filters - white list */
995 struct ao2_container *blackfilters; /*!< Manager event filters - black list */
996 char *a1_hash; /*!< precalculated A1 for Digest auth */
997 AST_RWLIST_ENTRY(ast_manager_user) list;
1000 /*! \brief list of users found in the config file */
1001 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
1003 /*! \brief list of actions registered */
1004 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
1006 /*! \brief list of hooks registered */
1007 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
1009 static void free_channelvars(void);
1011 /*! \brief Add a custom hook to be called when an event is fired */
1012 void ast_manager_register_hook(struct manager_custom_hook *hook)
1014 AST_RWLIST_WRLOCK(&manager_hooks);
1015 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
1016 AST_RWLIST_UNLOCK(&manager_hooks);
1019 /*! \brief Delete a custom hook to be called when an event is fired */
1020 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
1022 AST_RWLIST_WRLOCK(&manager_hooks);
1023 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
1024 AST_RWLIST_UNLOCK(&manager_hooks);
1027 int check_manager_enabled()
1029 return manager_enabled;
1032 int check_webmanager_enabled()
1034 return (webmanager_enabled && manager_enabled);
1038 * Grab a reference to the last event, update usecount as needed.
1039 * Can handle a NULL pointer.
1041 static struct eventqent *grab_last(void)
1043 struct eventqent *ret;
1045 AST_RWLIST_WRLOCK(&all_events);
1046 ret = AST_RWLIST_LAST(&all_events);
1047 /* the list is never empty now, but may become so when
1048 * we optimize it in the future, so be prepared.
1051 ast_atomic_fetchadd_int(&ret->usecount, 1);
1053 AST_RWLIST_UNLOCK(&all_events);
1058 * Purge unused events. Remove elements from the head
1059 * as long as their usecount is 0 and there is a next element.
1061 static void purge_events(void)
1063 struct eventqent *ev;
1064 struct timeval now = ast_tvnow();
1066 AST_RWLIST_WRLOCK(&all_events);
1067 while ( (ev = AST_RWLIST_FIRST(&all_events)) &&
1068 ev->usecount == 0 && AST_RWLIST_NEXT(ev, eq_next)) {
1069 AST_RWLIST_REMOVE_HEAD(&all_events, eq_next);
1073 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&all_events, ev, eq_next) {
1074 /* Never release the last event */
1075 if (!AST_RWLIST_NEXT(ev, eq_next)) {
1079 /* 2.5 times whatever the HTTP timeout is (maximum 2.5 hours) is the maximum time that we will definitely cache an event */
1080 if (ev->usecount == 0 && ast_tvdiff_sec(now, ev->tv) > (httptimeout > 3600 ? 3600 : httptimeout) * 2.5) {
1081 AST_RWLIST_REMOVE_CURRENT(eq_next);
1085 AST_RWLIST_TRAVERSE_SAFE_END;
1086 AST_RWLIST_UNLOCK(&all_events);
1090 * helper functions to convert back and forth between
1091 * string and numeric representation of set of flags
1093 static const struct permalias {
1097 { EVENT_FLAG_SYSTEM, "system" },
1098 { EVENT_FLAG_CALL, "call" },
1099 { EVENT_FLAG_LOG, "log" },
1100 { EVENT_FLAG_VERBOSE, "verbose" },
1101 { EVENT_FLAG_COMMAND, "command" },
1102 { EVENT_FLAG_AGENT, "agent" },
1103 { EVENT_FLAG_USER, "user" },
1104 { EVENT_FLAG_CONFIG, "config" },
1105 { EVENT_FLAG_DTMF, "dtmf" },
1106 { EVENT_FLAG_REPORTING, "reporting" },
1107 { EVENT_FLAG_CDR, "cdr" },
1108 { EVENT_FLAG_DIALPLAN, "dialplan" },
1109 { EVENT_FLAG_ORIGINATE, "originate" },
1110 { EVENT_FLAG_AGI, "agi" },
1111 { EVENT_FLAG_CC, "cc" },
1112 { EVENT_FLAG_AOC, "aoc" },
1117 /*! \brief Convert authority code to a list of options */
1118 static const char *authority_to_str(int authority, struct ast_str **res)
1123 ast_str_reset(*res);
1124 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
1125 if (authority & perms[i].num) {
1126 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
1131 if (ast_str_strlen(*res) == 0) /* replace empty string with something sensible */
1132 ast_str_append(res, 0, "<none>");
1134 return ast_str_buffer(*res);
1137 /*! Tells you if smallstr exists inside bigstr
1138 which is delim by delim and uses no buf or stringsep
1139 ast_instring("this|that|more","this",'|') == 1;
1141 feel free to move this to app.c -anthm */
1142 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
1144 const char *val = bigstr, *next;
1147 if ((next = strchr(val, delim))) {
1148 if (!strncmp(val, smallstr, (next - val))) {
1154 return !strcmp(smallstr, val);
1156 } while (*(val = (next + 1)));
1161 static int get_perm(const char *instr)
1169 for (x = 0; x < ARRAY_LEN(perms); x++) {
1170 if (ast_instring(instr, perms[x].label, ',')) {
1171 ret |= perms[x].num;
1179 * A number returns itself, false returns 0, true returns all flags,
1180 * other strings return the flags that are set.
1182 static int strings_to_mask(const char *string)
1186 if (ast_strlen_zero(string)) {
1190 for (p = string; *p; p++) {
1191 if (*p < '0' || *p > '9') {
1195 if (!*p) { /* all digits */
1196 return atoi(string);
1198 if (ast_false(string)) {
1201 if (ast_true(string)) { /* all permissions */
1203 for (x = 0; x < ARRAY_LEN(perms); x++) {
1204 ret |= perms[x].num;
1208 return get_perm(string);
1211 /*! \brief Unreference manager session object.
1212 If no more references, then go ahead and delete it */
1213 static struct mansession_session *unref_mansession(struct mansession_session *s)
1215 int refcount = ao2_ref(s, -1);
1216 if (manager_debug) {
1217 ast_debug(1, "Mansession: %p refcount now %d\n", s, refcount - 1);
1222 static void event_filter_destructor(void *obj)
1224 regex_t *regex_filter = obj;
1225 regfree(regex_filter);
1228 static void session_destructor(void *obj)
1230 struct mansession_session *session = obj;
1231 struct eventqent *eqe = session->last_ev;
1232 struct ast_datastore *datastore;
1234 /* Get rid of each of the data stores on the session */
1235 while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
1236 /* Free the data store */
1237 ast_datastore_free(datastore);
1240 if (session->f != NULL) {
1244 ast_atomic_fetchadd_int(&eqe->usecount, -1);
1247 if (session->whitefilters) {
1248 ao2_t_callback(session->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
1249 ao2_t_ref(session->whitefilters, -1 , "decrement ref for white container, should be last one");
1252 if (session->blackfilters) {
1253 ao2_t_callback(session->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
1254 ao2_t_ref(session->blackfilters, -1 , "decrement ref for black container, should be last one");
1258 /*! \brief Allocate manager session structure and add it to the list of sessions */
1259 static struct mansession_session *build_mansession(struct sockaddr_in sin)
1261 struct mansession_session *newsession;
1263 if (!(newsession = ao2_alloc(sizeof(*newsession), session_destructor))) {
1267 if (!(newsession->whitefilters = ao2_container_alloc(1, NULL, NULL))) {
1268 ao2_ref(newsession, -1);
1272 if (!(newsession->blackfilters = ao2_container_alloc(1, NULL, NULL))) {
1273 ao2_ref(newsession, -1); /* session_destructor will cleanup the other filter */
1277 newsession->fd = -1;
1278 newsession->waiting_thread = AST_PTHREADT_NULL;
1279 newsession->writetimeout = 100;
1280 newsession->send_events = -1;
1281 newsession->sin = sin;
1283 ao2_link(sessions, newsession);
1288 static int mansession_cmp_fn(void *obj, void *arg, int flags)
1290 struct mansession_session *s = obj;
1292 return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
1295 static void session_destroy(struct mansession_session *s)
1297 unref_mansession(s);
1298 ao2_unlink(sessions, s);
1302 static int check_manager_session_inuse(const char *name)
1304 struct mansession_session *session = ao2_find(sessions, (char *) name, 0);
1309 unref_mansession(session);
1316 * lookup an entry in the list of registered users.
1317 * must be called with the list lock held.
1319 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
1321 struct ast_manager_user *user = NULL;
1323 AST_RWLIST_TRAVERSE(&users, user, list) {
1324 if (!strcasecmp(user->username, name)) {
1332 /*! \brief Get displayconnects config option.
1333 * \param session manager session to get parameter from.
1334 * \return displayconnects config option value.
1336 static int manager_displayconnects (struct mansession_session *session)
1338 struct ast_manager_user *user = NULL;
1341 AST_RWLIST_RDLOCK(&users);
1342 if ((user = get_manager_by_name_locked (session->username))) {
1343 ret = user->displayconnects;
1345 AST_RWLIST_UNLOCK(&users);
1350 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1352 struct manager_action *cur;
1353 struct ast_str *authority;
1357 char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64];
1362 e->command = "manager show command";
1364 "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
1365 " Shows the detailed description for a specific Asterisk manager interface command.\n";
1368 l = strlen(a->word);
1370 AST_RWLIST_RDLOCK(&actions);
1371 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1372 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
1373 ret = ast_strdup(cur->action);
1374 break; /* make sure we exit even if ast_strdup() returns NULL */
1377 AST_RWLIST_UNLOCK(&actions);
1380 authority = ast_str_alloca(80);
1382 return CLI_SHOWUSAGE;
1386 /* setup the titles */
1387 term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1388 term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
1389 term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1390 term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
1391 term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
1394 AST_RWLIST_RDLOCK(&actions);
1395 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1396 for (num = 3; num < a->argc; num++) {
1397 if (!strcasecmp(cur->action, a->argv[num])) {
1399 if (cur->docsrc == AST_XML_DOC) {
1400 ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n",
1402 ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1),
1404 ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1),
1406 ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1),
1408 ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1),
1410 ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1));
1413 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
1414 cur->action, cur->synopsis,
1415 authority_to_str(cur->authority, &authority),
1416 S_OR(cur->description, ""));
1423 AST_RWLIST_UNLOCK(&actions);
1428 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1432 e->command = "manager set debug [on|off]";
1433 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
1440 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
1441 } else if (a->argc == 4) {
1442 if (!strcasecmp(a->argv[3], "on")) {
1444 } else if (!strcasecmp(a->argv[3], "off")) {
1447 return CLI_SHOWUSAGE;
1453 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1455 struct ast_manager_user *user = NULL;
1458 struct ast_str *rauthority = ast_str_alloca(128);
1459 struct ast_str *wauthority = ast_str_alloca(128);
1463 e->command = "manager show user";
1465 " Usage: manager show user <user>\n"
1466 " Display all information related to the manager user specified.\n";
1469 l = strlen(a->word);
1474 AST_RWLIST_RDLOCK(&users);
1475 AST_RWLIST_TRAVERSE(&users, user, list) {
1476 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
1477 ret = ast_strdup(user->username);
1481 AST_RWLIST_UNLOCK(&users);
1486 return CLI_SHOWUSAGE;
1489 AST_RWLIST_RDLOCK(&users);
1491 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
1492 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
1493 AST_RWLIST_UNLOCK(&users);
1497 ast_cli(a->fd, "\n");
1504 "displayconnects: %s\n",
1505 (user->username ? user->username : "(N/A)"),
1506 (user->secret ? "<Set>" : "(N/A)"),
1507 (user->ha ? "yes" : "no"),
1508 authority_to_str(user->readperm, &rauthority),
1509 authority_to_str(user->writeperm, &wauthority),
1510 (user->displayconnects ? "yes" : "no"));
1512 AST_RWLIST_UNLOCK(&users);
1517 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1519 struct ast_manager_user *user = NULL;
1523 e->command = "manager show users";
1525 "Usage: manager show users\n"
1526 " Prints a listing of all managers that are currently configured on that\n"
1533 return CLI_SHOWUSAGE;
1536 AST_RWLIST_RDLOCK(&users);
1538 /* If there are no users, print out something along those lines */
1539 if (AST_RWLIST_EMPTY(&users)) {
1540 ast_cli(a->fd, "There are no manager users.\n");
1541 AST_RWLIST_UNLOCK(&users);
1545 ast_cli(a->fd, "\nusername\n--------\n");
1547 AST_RWLIST_TRAVERSE(&users, user, list) {
1548 ast_cli(a->fd, "%s\n", user->username);
1552 AST_RWLIST_UNLOCK(&users);
1554 ast_cli(a->fd,"-------------------\n"
1555 "%d manager users configured.\n", count_amu);
1559 /*! \brief CLI command manager list commands */
1560 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1562 struct manager_action *cur;
1563 struct ast_str *authority;
1564 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
1567 e->command = "manager show commands";
1569 "Usage: manager show commands\n"
1570 " Prints a listing of all the available Asterisk manager interface commands.\n";
1575 authority = ast_str_alloca(80);
1576 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
1577 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
1579 AST_RWLIST_RDLOCK(&actions);
1580 AST_RWLIST_TRAVERSE(&actions, cur, list)
1581 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
1582 AST_RWLIST_UNLOCK(&actions);
1587 /*! \brief CLI command manager list connected */
1588 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1590 struct mansession_session *session;
1591 time_t now = time(NULL);
1592 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
1593 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
1595 struct ao2_iterator i;
1599 e->command = "manager show connected";
1601 "Usage: manager show connected\n"
1602 " Prints a listing of the users that are currently connected to the\n"
1603 "Asterisk manager interface.\n";
1609 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
1611 i = ao2_iterator_init(sessions, 0);
1612 while ((session = ao2_iterator_next(&i))) {
1614 ast_cli(a->fd, HSMCONN_FORMAT2, session->username, ast_inet_ntoa(session->sin.sin_addr), (int)(session->sessionstart), (int)(now - session->sessionstart), session->fd, session->inuse, session->readperm, session->writeperm);
1616 ao2_unlock(session);
1617 unref_mansession(session);
1619 ao2_iterator_destroy(&i);
1620 ast_cli(a->fd, "%d users connected.\n", count);
1625 /*! \brief CLI command manager list eventq */
1626 /* Should change to "manager show connected" */
1627 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1629 struct eventqent *s;
1632 e->command = "manager show eventq";
1634 "Usage: manager show eventq\n"
1635 " Prints a listing of all events pending in the Asterisk manger\n"
1641 AST_RWLIST_RDLOCK(&all_events);
1642 AST_RWLIST_TRAVERSE(&all_events, s, eq_next) {
1643 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
1644 ast_cli(a->fd, "Category: %d\n", s->category);
1645 ast_cli(a->fd, "Event:\n%s", s->eventdata);
1647 AST_RWLIST_UNLOCK(&all_events);
1652 /*! \brief CLI command manager reload */
1653 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1657 e->command = "manager reload";
1659 "Usage: manager reload\n"
1660 " Reloads the manager configuration.\n";
1666 return CLI_SHOWUSAGE;
1672 static struct eventqent *advance_event(struct eventqent *e)
1674 struct eventqent *next;
1676 AST_RWLIST_RDLOCK(&all_events);
1677 if ((next = AST_RWLIST_NEXT(e, eq_next))) {
1678 ast_atomic_fetchadd_int(&next->usecount, 1);
1679 ast_atomic_fetchadd_int(&e->usecount, -1);
1681 AST_RWLIST_UNLOCK(&all_events);
1685 #define GET_HEADER_FIRST_MATCH 0
1686 #define GET_HEADER_LAST_MATCH 1
1687 #define GET_HEADER_SKIP_EMPTY 2
1690 * Generic function to return either the first or the last matching header
1691 * from a list of variables, possibly skipping empty strings.
1692 * At the moment there is only one use of this function in this file,
1693 * so we make it static.
1695 static const char *__astman_get_header(const struct message *m, char *var, int mode)
1697 int x, l = strlen(var);
1698 const char *result = "";
1700 for (x = 0; x < m->hdrcount; x++) {
1701 const char *h = m->headers[x];
1702 if (!strncasecmp(var, h, l) && h[l] == ':') {
1703 const char *value = h + l + 1;
1704 value = ast_skip_blanks(value); /* ignore leading spaces in the value */
1705 /* found a potential candidate */
1706 if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
1707 continue; /* not interesting */
1708 if (mode & GET_HEADER_LAST_MATCH)
1709 result = value; /* record the last match so far */
1719 * Return the first matching variable from an array.
1720 * This is the legacy function and is implemented in therms of
1721 * __astman_get_header().
1723 const char *astman_get_header(const struct message *m, char *var)
1725 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
1729 struct ast_variable *astman_get_variables(const struct message *m)
1732 struct ast_variable *head = NULL, *cur;
1734 AST_DECLARE_APP_ARGS(args,
1735 AST_APP_ARG(vars)[32];
1738 varlen = strlen("Variable: ");
1740 for (x = 0; x < m->hdrcount; x++) {
1741 char *parse, *var, *val;
1743 if (strncasecmp("Variable: ", m->headers[x], varlen)) {
1746 parse = ast_strdupa(m->headers[x] + varlen);
1748 AST_STANDARD_APP_ARGS(args, parse);
1752 for (y = 0; y < args.argc; y++) {
1753 if (!args.vars[y]) {
1756 var = val = ast_strdupa(args.vars[y]);
1758 if (!val || ast_strlen_zero(var)) {
1761 cur = ast_variable_new(var, val, "");
1770 /*! \brief access for hooks to send action messages to ami */
1771 int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
1775 struct manager_action *tmp;
1776 struct mansession s = {.session = NULL, };
1777 struct message m = { 0 };
1778 char header_buf[1025] = { '\0' };
1779 const char *src = msg;
1787 /* convert msg string to message struct */
1788 curlen = strlen(msg);
1789 for (x = 0; x < curlen; x++) {
1790 int cr; /* set if we have \r */
1791 if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
1792 cr = 2; /* Found. Update length to include \r\n */
1793 else if (src[x] == '\n')
1794 cr = 1; /* also accept \n only */
1797 /* don't copy empty lines */
1799 memmove(header_buf, src, x); /*... but trim \r\n */
1800 header_buf[x] = '\0'; /* terminate the string */
1801 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
1804 curlen -= x; /* remaining size */
1805 src += x; /* update pointer */
1806 x = -1; /* reset loop */
1809 action = astman_get_header(&m,"Action");
1810 if (action && strcasecmp(action,"login")) {
1812 AST_RWLIST_RDLOCK(&actions);
1813 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
1814 if (strcasecmp(action, tmp->action))
1817 * we have to simulate a session for this action request
1818 * to be able to pass it down for processing
1819 * This is necessary to meet the previous design of manager.c
1822 s.f = (void*)1; /* set this to something so our request will make it through all functions that test it*/
1823 ret = tmp->func(&s, &m);
1826 AST_RWLIST_UNLOCK(&actions);
1833 * helper function to send a string to the socket.
1834 * Return -1 on error (e.g. buffer full).
1836 static int send_string(struct mansession *s, char *string)
1838 /* It's a result from one of the hook's action invocation */
1841 * to send responses, we're using the same function
1842 * as for receiving events. We call the event "HookResponse"
1844 s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
1847 return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout);
1849 return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout);
1854 * \brief thread local buffer for astman_append
1856 * \note This can not be defined within the astman_append() function
1857 * because it declares a couple of functions that get used to
1858 * initialize the thread local storage key.
1860 AST_THREADSTORAGE(astman_append_buf);
1862 AST_THREADSTORAGE(userevent_buf);
1864 /*! \brief initial allocated size for the astman_append_buf */
1865 #define ASTMAN_APPEND_BUF_INITSIZE 256
1868 * utility functions for creating AMI replies
1870 void astman_append(struct mansession *s, const char *fmt, ...)
1873 struct ast_str *buf;
1875 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
1880 ast_str_set_va(&buf, 0, fmt, ap);
1883 if (s->f != NULL || s->session->f != NULL) {
1884 send_string(s, ast_str_buffer(buf));
1886 ast_verbose("fd == -1 in astman_append, should not happen\n");
1890 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
1891 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
1892 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
1893 be non-zero). In either of these cases, there is no need to lock-protect the session's
1894 fd, since no other output will be sent (events will be queued), and no input will
1895 be read until either the current action finishes or get_input() obtains the session
1899 /*! \todo XXX MSG_MOREDATA should go to a header file. */
1900 #define MSG_MOREDATA ((char *)astman_send_response)
1902 /*! \brief send a response with an optional message,
1903 * and terminate it with an empty line.
1904 * m is used only to grab the 'ActionID' field.
1906 * Use the explicit constant MSG_MOREDATA to remove the empty line.
1907 * XXX MSG_MOREDATA should go to a header file.
1909 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
1911 const char *id = astman_get_header(m, "ActionID");
1913 astman_append(s, "Response: %s\r\n", resp);
1914 if (!ast_strlen_zero(id)) {
1915 astman_append(s, "ActionID: %s\r\n", id);
1918 astman_append(s, "EventList: %s\r\n", listflag); /* Start, complete, cancelled */
1920 if (msg == MSG_MOREDATA) {
1923 astman_append(s, "Message: %s\r\n\r\n", msg);
1925 astman_append(s, "\r\n");
1929 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
1931 astman_send_response_full(s, m, resp, msg, NULL);
1934 void astman_send_error(struct mansession *s, const struct message *m, char *error)
1936 astman_send_response_full(s, m, "Error", error, NULL);
1939 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
1941 astman_send_response_full(s, m, "Success", msg, NULL);
1944 static void astman_start_ack(struct mansession *s, const struct message *m)
1946 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
1949 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
1951 astman_send_response_full(s, m, "Success", msg, listflag);
1954 /*! \brief Lock the 'mansession' structure. */
1955 static void mansession_lock(struct mansession *s)
1957 ast_mutex_lock(&s->lock);
1960 /*! \brief Unlock the 'mansession' structure. */
1961 static void mansession_unlock(struct mansession *s)
1963 ast_mutex_unlock(&s->lock);
1967 Rather than braindead on,off this now can also accept a specific int mask value
1968 or a ',' delim list of mask strings (the same as manager.conf) -anthm
1970 static int set_eventmask(struct mansession *s, const char *eventmask)
1972 int maskint = strings_to_mask(eventmask);
1974 ao2_lock(s->session);
1976 s->session->send_events = maskint;
1978 ao2_unlock(s->session);
1983 static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s)
1985 return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS :
1986 AST_SECURITY_EVENT_TRANSPORT_TCP;
1989 static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s,
1990 struct sockaddr_in *sin_local)
1992 ast_sockaddr_to_sin(&s->tcptls_session->parent->local_address,
1998 static void report_invalid_user(const struct mansession *s, const char *username)
2000 struct sockaddr_in sin_local;
2001 char session_id[32];
2002 struct ast_security_event_inval_acct_id inval_acct_id = {
2003 .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
2004 .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
2005 .common.service = "AMI",
2006 .common.account_id = username,
2007 .common.session_tv = &s->session->sessionstart_tv,
2008 .common.local_addr = {
2009 .sin = mansession_encode_sin_local(s, &sin_local),
2010 .transport = mansession_get_transport(s),
2012 .common.remote_addr = {
2013 .sin = &s->session->sin,
2014 .transport = mansession_get_transport(s),
2016 .common.session_id = session_id,
2019 snprintf(session_id, sizeof(session_id), "%p", s);
2021 ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
2024 static void report_failed_acl(const struct mansession *s, const char *username)
2026 struct sockaddr_in sin_local;
2027 char session_id[32];
2028 struct ast_security_event_failed_acl failed_acl_event = {
2029 .common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
2030 .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
2031 .common.service = "AMI",
2032 .common.account_id = username,
2033 .common.session_tv = &s->session->sessionstart_tv,
2034 .common.local_addr = {
2035 .sin = mansession_encode_sin_local(s, &sin_local),
2036 .transport = mansession_get_transport(s),
2038 .common.remote_addr = {
2039 .sin = &s->session->sin,
2040 .transport = mansession_get_transport(s),
2042 .common.session_id = session_id,
2045 snprintf(session_id, sizeof(session_id), "%p", s->session);
2047 ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
2050 static void report_inval_password(const struct mansession *s, const char *username)
2052 struct sockaddr_in sin_local;
2053 char session_id[32];
2054 struct ast_security_event_inval_password inval_password = {
2055 .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
2056 .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
2057 .common.service = "AMI",
2058 .common.account_id = username,
2059 .common.session_tv = &s->session->sessionstart_tv,
2060 .common.local_addr = {
2061 .sin = mansession_encode_sin_local(s, &sin_local),
2062 .transport = mansession_get_transport(s),
2064 .common.remote_addr = {
2065 .sin = &s->session->sin,
2066 .transport = mansession_get_transport(s),
2068 .common.session_id = session_id,
2071 snprintf(session_id, sizeof(session_id), "%p", s->session);
2073 ast_security_event_report(AST_SEC_EVT(&inval_password));
2076 static void report_auth_success(const struct mansession *s)
2078 struct sockaddr_in sin_local;
2079 char session_id[32];
2080 struct ast_security_event_successful_auth successful_auth = {
2081 .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
2082 .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
2083 .common.service = "AMI",
2084 .common.account_id = s->session->username,
2085 .common.session_tv = &s->session->sessionstart_tv,
2086 .common.local_addr = {
2087 .sin = mansession_encode_sin_local(s, &sin_local),
2088 .transport = mansession_get_transport(s),
2090 .common.remote_addr = {
2091 .sin = &s->session->sin,
2092 .transport = mansession_get_transport(s),
2094 .common.session_id = session_id,
2097 snprintf(session_id, sizeof(session_id), "%p", s->session);
2099 ast_security_event_report(AST_SEC_EVT(&successful_auth));
2102 static void report_req_not_allowed(const struct mansession *s, const char *action)
2104 struct sockaddr_in sin_local;
2105 char session_id[32];
2106 char request_type[64];
2107 struct ast_security_event_req_not_allowed req_not_allowed = {
2108 .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
2109 .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
2110 .common.service = "AMI",
2111 .common.account_id = s->session->username,
2112 .common.session_tv = &s->session->sessionstart_tv,
2113 .common.local_addr = {
2114 .sin = mansession_encode_sin_local(s, &sin_local),
2115 .transport = mansession_get_transport(s),
2117 .common.remote_addr = {
2118 .sin = &s->session->sin,
2119 .transport = mansession_get_transport(s),
2121 .common.session_id = session_id,
2123 .request_type = request_type,
2126 snprintf(session_id, sizeof(session_id), "%p", s->session);
2127 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2129 ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
2132 static void report_req_bad_format(const struct mansession *s, const char *action)
2134 struct sockaddr_in sin_local;
2135 char session_id[32];
2136 char request_type[64];
2137 struct ast_security_event_req_bad_format req_bad_format = {
2138 .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
2139 .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
2140 .common.service = "AMI",
2141 .common.account_id = s->session->username,
2142 .common.session_tv = &s->session->sessionstart_tv,
2143 .common.local_addr = {
2144 .sin = mansession_encode_sin_local(s, &sin_local),
2145 .transport = mansession_get_transport(s),
2147 .common.remote_addr = {
2148 .sin = &s->session->sin,
2149 .transport = mansession_get_transport(s),
2151 .common.session_id = session_id,
2153 .request_type = request_type,
2156 snprintf(session_id, sizeof(session_id), "%p", s->session);
2157 snprintf(request_type, sizeof(request_type), "Action: %s", action);
2159 ast_security_event_report(AST_SEC_EVT(&req_bad_format));
2162 static void report_failed_challenge_response(const struct mansession *s,
2163 const char *response, const char *expected_response)
2165 struct sockaddr_in sin_local;
2166 char session_id[32];
2167 struct ast_security_event_chal_resp_failed chal_resp_failed = {
2168 .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
2169 .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
2170 .common.service = "AMI",
2171 .common.account_id = s->session->username,
2172 .common.session_tv = &s->session->sessionstart_tv,
2173 .common.local_addr = {
2174 .sin = mansession_encode_sin_local(s, &sin_local),
2175 .transport = mansession_get_transport(s),
2177 .common.remote_addr = {
2178 .sin = &s->session->sin,
2179 .transport = mansession_get_transport(s),
2181 .common.session_id = session_id,
2183 .challenge = s->session->challenge,
2184 .response = response,
2185 .expected_response = expected_response,
2188 snprintf(session_id, sizeof(session_id), "%p", s->session);
2190 ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
2193 static void report_session_limit(const struct mansession *s)
2195 struct sockaddr_in sin_local;
2196 char session_id[32];
2197 struct ast_security_event_session_limit session_limit = {
2198 .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
2199 .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
2200 .common.service = "AMI",
2201 .common.account_id = s->session->username,
2202 .common.session_tv = &s->session->sessionstart_tv,
2203 .common.local_addr = {
2204 .sin = mansession_encode_sin_local(s, &sin_local),
2205 .transport = mansession_get_transport(s),
2207 .common.remote_addr = {
2208 .sin = &s->session->sin,
2209 .transport = mansession_get_transport(s),
2211 .common.session_id = session_id,
2214 snprintf(session_id, sizeof(session_id), "%p", s->session);
2216 ast_security_event_report(AST_SEC_EVT(&session_limit));
2220 * Here we start with action_ handlers for AMI actions,
2221 * and the internal functions used by them.
2222 * Generally, the handlers are called action_foo()
2225 /* helper function for action_login() */
2226 static int authenticate(struct mansession *s, const struct message *m)
2228 const char *username = astman_get_header(m, "Username");
2229 const char *password = astman_get_header(m, "Secret");
2231 struct ast_manager_user *user = NULL;
2232 regex_t *regex_filter;
2233 struct ao2_iterator filter_iter;
2234 struct ast_sockaddr addr;
2236 if (ast_strlen_zero(username)) { /* missing username */
2240 /* locate user in locked state */
2241 AST_RWLIST_WRLOCK(&users);
2243 ast_sockaddr_from_sin(&addr, &s->session->sin);
2245 if (!(user = get_manager_by_name_locked(username))) {
2246 report_invalid_user(s, username);
2247 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2248 } else if (user->ha && !ast_apply_ha(user->ha, &addr)) {
2249 report_failed_acl(s, username);
2250 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2251 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
2252 const char *key = astman_get_header(m, "Key");
2253 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
2256 char md5key[256] = "";
2257 struct MD5Context md5;
2258 unsigned char digest[16];
2261 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
2262 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
2263 MD5Final(digest, &md5);
2264 for (x = 0; x < 16; x++)
2265 len += sprintf(md5key + len, "%2.2x", digest[x]);
2266 if (!strcmp(md5key, key)) {
2269 report_failed_challenge_response(s, key, md5key);
2272 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
2273 S_OR(s->session->challenge, ""));
2275 } else if (user->secret) {
2276 if (!strcmp(password, user->secret)) {
2279 report_inval_password(s, username);
2284 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2285 AST_RWLIST_UNLOCK(&users);
2291 /* All of the user parameters are copied to the session so that in the event
2292 * of a reload and a configuration change, the session parameters are not
2294 ast_copy_string(s->session->username, username, sizeof(s->session->username));
2295 s->session->readperm = user->readperm;
2296 s->session->writeperm = user->writeperm;
2297 s->session->writetimeout = user->writetimeout;
2299 filter_iter = ao2_iterator_init(user->whitefilters, 0);
2300 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2301 ao2_t_link(s->session->whitefilters, regex_filter, "add white user filter to session");
2302 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2304 ao2_iterator_destroy(&filter_iter);
2306 filter_iter = ao2_iterator_init(user->blackfilters, 0);
2307 while ((regex_filter = ao2_iterator_next(&filter_iter))) {
2308 ao2_t_link(s->session->blackfilters, regex_filter, "add black user filter to session");
2309 ao2_t_ref(regex_filter, -1, "remove iterator ref");
2311 ao2_iterator_destroy(&filter_iter);
2313 s->session->sessionstart = time(NULL);
2314 s->session->sessionstart_tv = ast_tvnow();
2315 set_eventmask(s, astman_get_header(m, "Events"));
2317 report_auth_success(s);
2319 AST_RWLIST_UNLOCK(&users);
2323 static int action_ping(struct mansession *s, const struct message *m)
2325 const char *actionid = astman_get_header(m, "ActionID");
2326 struct timeval now = ast_tvnow();
2328 astman_append(s, "Response: Success\r\n");
2329 if (!ast_strlen_zero(actionid)){
2330 astman_append(s, "ActionID: %s\r\n", actionid);
2335 "Timestamp: %ld.%06lu\r\n"
2337 (long) now.tv_sec, (unsigned long) now.tv_usec);
2341 static int action_getconfig(struct mansession *s, const struct message *m)
2343 struct ast_config *cfg;
2344 const char *fn = astman_get_header(m, "Filename");
2345 const char *category = astman_get_header(m, "Category");
2348 char *cur_category = NULL;
2349 struct ast_variable *v;
2350 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2352 if (ast_strlen_zero(fn)) {
2353 astman_send_error(s, m, "Filename not specified");
2356 cfg = ast_config_load2(fn, "manager", config_flags);
2357 if (cfg == CONFIG_STATUS_FILEMISSING) {
2358 astman_send_error(s, m, "Config file not found");
2360 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2361 astman_send_error(s, m, "Config file has invalid format");
2365 astman_start_ack(s, m);
2366 while ((cur_category = ast_category_browse(cfg, cur_category))) {
2367 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
2369 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
2370 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
2371 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
2376 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2377 astman_append(s, "No categories found\r\n");
2379 ast_config_destroy(cfg);
2380 astman_append(s, "\r\n");
2385 static int action_listcategories(struct mansession *s, const struct message *m)
2387 struct ast_config *cfg;
2388 const char *fn = astman_get_header(m, "Filename");
2389 char *category = NULL;
2390 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2393 if (ast_strlen_zero(fn)) {
2394 astman_send_error(s, m, "Filename not specified");
2397 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2398 astman_send_error(s, m, "Config file not found");
2400 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2401 astman_send_error(s, m, "Config file has invalid format");
2404 astman_start_ack(s, m);
2405 while ((category = ast_category_browse(cfg, category))) {
2406 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
2409 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2410 astman_append(s, "Error: no categories found\r\n");
2412 ast_config_destroy(cfg);
2413 astman_append(s, "\r\n");
2421 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
2422 static void json_escape(char *out, const char *in)
2425 if (*in == '\\' || *in == '\"') {
2433 static int action_getconfigjson(struct mansession *s, const struct message *m)
2435 struct ast_config *cfg;
2436 const char *fn = astman_get_header(m, "Filename");
2437 char *category = NULL;
2438 struct ast_variable *v;
2441 unsigned int buf_len = 0;
2442 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2444 if (ast_strlen_zero(fn)) {
2445 astman_send_error(s, m, "Filename not specified");
2449 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2450 astman_send_error(s, m, "Config file not found");
2452 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2453 astman_send_error(s, m, "Config file has invalid format");
2458 buf = alloca(buf_len);
2460 astman_start_ack(s, m);
2461 astman_append(s, "JSON: {");
2462 while ((category = ast_category_browse(cfg, category))) {
2464 if (buf_len < 2 * strlen(category) + 1) {
2466 buf = alloca(buf_len);
2468 json_escape(buf, category);
2469 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
2473 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
2475 astman_append(s, ",");
2477 if (buf_len < 2 * strlen(v->name) + 1) {
2479 buf = alloca(buf_len);
2481 json_escape(buf, v->name);
2482 astman_append(s, "\"%s", buf);
2483 if (buf_len < 2 * strlen(v->value) + 1) {
2485 buf = alloca(buf_len);
2487 json_escape(buf, v->value);
2488 astman_append(s, "%s\"", buf);
2493 astman_append(s, "]");
2495 astman_append(s, "}\r\n\r\n");
2497 ast_config_destroy(cfg);
2502 /*! \brief helper function for action_updateconfig */
2503 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
2507 const char *action, *cat, *var, *value, *match, *line;
2508 struct ast_category *category;
2509 struct ast_variable *v;
2510 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
2511 enum error_type result = 0;
2513 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
2514 unsigned int object = 0;
2516 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
2517 action = astman_get_header(m, hdr);
2518 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
2519 break; /* this could cause problems if actions come in misnumbered */
2521 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
2522 cat = astman_get_header(m, hdr);
2523 if (ast_strlen_zero(cat)) { /* every action needs a category */
2524 result = UNSPECIFIED_CATEGORY;
2528 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
2529 var = astman_get_header(m, hdr);
2531 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
2532 value = astman_get_header(m, hdr);
2534 if (!ast_strlen_zero(value) && *value == '>') {
2539 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
2540 match = astman_get_header(m, hdr);
2542 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
2543 line = astman_get_header(m, hdr);
2545 if (!strcasecmp(action, "newcat")) {
2546 if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
2547 result = FAILURE_NEWCAT; /* already exist */
2550 if (!(category = ast_category_new(cat, dfn, -1))) {
2551 result = FAILURE_ALLOCATION;
2554 if (ast_strlen_zero(match)) {
2555 ast_category_append(cfg, category);
2557 ast_category_insert(cfg, category, match);
2559 } else if (!strcasecmp(action, "renamecat")) {
2560 if (ast_strlen_zero(value)) {
2561 result = UNSPECIFIED_ARGUMENT;
2564 if (!(category = ast_category_get(cfg, cat))) {
2565 result = UNKNOWN_CATEGORY;
2568 ast_category_rename(category, value);
2569 } else if (!strcasecmp(action, "delcat")) {
2570 if (ast_category_delete(cfg, cat)) {
2571 result = FAILURE_DELCAT;
2574 } else if (!strcasecmp(action, "emptycat")) {
2575 if (ast_category_empty(cfg, cat)) {
2576 result = FAILURE_EMPTYCAT;
2579 } else if (!strcasecmp(action, "update")) {
2580 if (ast_strlen_zero(var)) {
2581 result = UNSPECIFIED_ARGUMENT;
2584 if (!(category = ast_category_get(cfg,cat))) {
2585 result = UNKNOWN_CATEGORY;
2588 if (ast_variable_update(category, var, value, match, object)) {
2589 result = FAILURE_UPDATE;
2592 } else if (!strcasecmp(action, "delete")) {
2593 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
2594 result = UNSPECIFIED_ARGUMENT;
2597 if (!(category = ast_category_get(cfg, cat))) {
2598 result = UNKNOWN_CATEGORY;
2601 if (ast_variable_delete(category, var, match, line)) {
2602 result = FAILURE_DELETE;
2605 } else if (!strcasecmp(action, "append")) {
2606 if (ast_strlen_zero(var)) {
2607 result = UNSPECIFIED_ARGUMENT;
2610 if (!(category = ast_category_get(cfg, cat))) {
2611 result = UNKNOWN_CATEGORY;
2614 if (!(v = ast_variable_new(var, value, dfn))) {
2615 result = FAILURE_ALLOCATION;
2618 if (object || (match && !strcasecmp(match, "object"))) {
2621 ast_variable_append(category, v);
2622 } else if (!strcasecmp(action, "insert")) {
2623 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
2624 result = UNSPECIFIED_ARGUMENT;
2627 if (!(category = ast_category_get(cfg, cat))) {
2628 result = UNKNOWN_CATEGORY;
2631 if (!(v = ast_variable_new(var, value, dfn))) {
2632 result = FAILURE_ALLOCATION;
2635 ast_variable_insert(category, v, line);
2638 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
2639 result = UNKNOWN_ACTION;
2648 static int action_updateconfig(struct mansession *s, const struct message *m)
2650 struct ast_config *cfg;
2651 const char *sfn = astman_get_header(m, "SrcFilename");
2652 const char *dfn = astman_get_header(m, "DstFilename");
2654 const char *rld = astman_get_header(m, "Reload");
2655 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2656 enum error_type result;
2658 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
2659 astman_send_error(s, m, "Filename not specified");
2662 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
2663 astman_send_error(s, m, "Config file not found");
2665 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2666 astman_send_error(s, m, "Config file has invalid format");
2669 result = handle_updates(s, m, cfg, dfn);
2671 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
2672 res = ast_config_text_file_save(dfn, cfg, "Manager");
2673 ast_config_destroy(cfg);
2675 astman_send_error(s, m, "Save of config failed");
2678 astman_send_ack(s, m, NULL);
2679 if (!ast_strlen_zero(rld)) {
2680 if (ast_true(rld)) {
2683 ast_module_reload(rld);
2686 ast_config_destroy(cfg);
2688 case UNKNOWN_ACTION:
2689 astman_send_error(s, m, "Unknown action command");
2691 case UNKNOWN_CATEGORY:
2692 astman_send_error(s, m, "Given category does not exist");
2694 case UNSPECIFIED_CATEGORY:
2695 astman_send_error(s, m, "Category not specified");
2697 case UNSPECIFIED_ARGUMENT:
2698 astman_send_error(s, m, "Problem with category, value, or line (if required)");
2700 case FAILURE_ALLOCATION:
2701 astman_send_error(s, m, "Memory allocation failure, this should not happen");
2703 case FAILURE_NEWCAT:
2704 astman_send_error(s, m, "Create category did not complete successfully");
2706 case FAILURE_DELCAT:
2707 astman_send_error(s, m, "Delete category did not complete successfully");
2709 case FAILURE_EMPTYCAT:
2710 astman_send_error(s, m, "Empty category did not complete successfully");
2712 case FAILURE_UPDATE:
2713 astman_send_error(s, m, "Update did not complete successfully");
2715 case FAILURE_DELETE:
2716 astman_send_error(s, m, "Delete did not complete successfully");
2718 case FAILURE_APPEND:
2719 astman_send_error(s, m, "Append did not complete successfully");
2726 static int action_createconfig(struct mansession *s, const struct message *m)
2729 const char *fn = astman_get_header(m, "Filename");
2730 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
2731 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
2732 ast_str_append(&filepath, 0, "%s", fn);
2734 if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
2736 astman_send_ack(s, m, "New configuration file created successfully");
2738 astman_send_error(s, m, strerror(errno));
2744 static int action_waitevent(struct mansession *s, const struct message *m)
2746 const char *timeouts = astman_get_header(m, "Timeout");
2750 const char *id = astman_get_header(m, "ActionID");
2753 if (!ast_strlen_zero(id)) {
2754 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2759 if (!ast_strlen_zero(timeouts)) {
2760 sscanf(timeouts, "%30i", &timeout);
2764 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
2767 ao2_lock(s->session);
2768 if (s->session->waiting_thread != AST_PTHREADT_NULL) {
2769 pthread_kill(s->session->waiting_thread, SIGURG);
2772 if (s->session->managerid) { /* AMI-over-HTTP session */
2774 * Make sure the timeout is within the expire time of the session,
2775 * as the client will likely abort the request if it does not see
2776 * data coming after some amount of time.
2778 time_t now = time(NULL);
2779 int max = s->session->sessiontimeout - now - 10;
2781 if (max < 0) { /* We are already late. Strange but possible. */
2784 if (timeout < 0 || timeout > max) {
2787 if (!s->session->send_events) { /* make sure we record events */
2788 s->session->send_events = -1;
2791 ao2_unlock(s->session);
2793 /* XXX should this go inside the lock ? */
2794 s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
2795 ast_debug(1, "Starting waiting for an event!\n");
2797 for (x = 0; x < timeout || timeout < 0; x++) {
2798 ao2_lock(s->session);
2799 if (AST_RWLIST_NEXT(s->session->last_ev, eq_next)) {
2802 /* We can have multiple HTTP session point to the same mansession entry.
2803 * The way we deal with it is not very nice: newcomers kick out the previous
2804 * HTTP session. XXX this needs to be improved.
2806 if (s->session->waiting_thread != pthread_self()) {
2809 if (s->session->needdestroy) {
2812 ao2_unlock(s->session);
2816 if (s->session->managerid == 0) { /* AMI session */
2817 if (ast_wait_for_input(s->session->fd, 1000)) {
2820 } else { /* HTTP session */
2824 ast_debug(1, "Finished waiting for an event!\n");
2826 ao2_lock(s->session);
2827 if (s->session->waiting_thread == pthread_self()) {
2828 struct eventqent *eqe = s->session->last_ev;
2829 astman_send_response(s, m, "Success", "Waiting for Event completed.");
2830 while ((eqe = advance_event(eqe))) {
2831 if (((s->session->readperm & eqe->category) == eqe->category) &&
2832 ((s->session->send_events & eqe->category) == eqe->category)) {
2833 astman_append(s, "%s", eqe->eventdata);
2835 s->session->last_ev = eqe;
2838 "Event: WaitEventComplete\r\n"
2841 s->session->waiting_thread = AST_PTHREADT_NULL;
2843 ast_debug(1, "Abandoning event request!\n");
2845 ao2_unlock(s->session);
2850 /*! \note The actionlock is read-locked by the caller of this function */
2851 static int action_listcommands(struct mansession *s, const struct message *m)
2853 struct manager_action *cur;
2854 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
2856 astman_start_ack(s, m);
2857 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2858 if (s->session->writeperm & cur->authority || cur->authority == 0) {
2859 astman_append(s, "%s: %s (Priv: %s)\r\n",
2860 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
2863 astman_append(s, "\r\n");
2868 static int action_events(struct mansession *s, const struct message *m)
2870 const char *mask = astman_get_header(m, "EventMask");
2873 res = set_eventmask(s, mask);
2874 if (broken_events_action) {
2875 /* if this option is set we should not return a response on
2876 * error, or when all events are set */
2879 for (x = 0; x < ARRAY_LEN(perms); x++) {
2880 if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
2884 astman_append(s, "Response: Success\r\n"
2885 "Events: On\r\n\r\n");
2886 } else if (res == 0)
2887 astman_append(s, "Response: Success\r\n"
2888 "Events: Off\r\n\r\n");
2893 astman_append(s, "Response: Success\r\n"
2894 "Events: On\r\n\r\n");
2896 astman_append(s, "Response: Success\r\n"
2897 "Events: Off\r\n\r\n");
2899 astman_send_error(s, m, "Invalid event mask");
2904 static int action_logoff(struct mansession *s, const struct message *m)
2906 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
2910 static int action_login(struct mansession *s, const struct message *m)
2913 /* still authenticated - don't process again */
2914 if (s->session->authenticated) {
2915 astman_send_ack(s, m, "Already authenticated");
2919 if (authenticate(s, m)) {
2921 astman_send_error(s, m, "Authentication failed");
2924 s->session->authenticated = 1;
2925 if (manager_displayconnects(s->session)) {
2926 ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr));
2928 astman_send_ack(s, m, "Authentication accepted");
2929 if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
2930 struct ast_str *auth = ast_str_alloca(80);
2931 const char *cat_str = authority_to_str(EVENT_FLAG_SYSTEM, &auth);
2932 astman_append(s, "Event: FullyBooted\r\n"
2934 "Status: Fully Booted\r\n\r\n", cat_str);
2939 static int action_challenge(struct mansession *s, const struct message *m)
2941 const char *authtype = astman_get_header(m, "AuthType");
2943 if (!strcasecmp(authtype, "MD5")) {
2944 if (ast_strlen_zero(s->session->challenge)) {
2945 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
2948 astman_start_ack(s, m);
2949 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
2950 mansession_unlock(s);
2952 astman_send_error(s, m, "Must specify AuthType");
2957 static int action_hangup(struct mansession *s, const struct message *m)
2959 struct ast_channel *c = NULL;
2960 int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
2961 const char *name = astman_get_header(m, "Channel");
2962 const char *cause = astman_get_header(m, "Cause");
2964 if (ast_strlen_zero(name)) {
2965 astman_send_error(s, m, "No channel specified");
2969 if (!ast_strlen_zero(cause)) {
2971 causecode = strtol(cause, &endptr, 10);
2972 if (causecode < 0 || causecode > 127 || *endptr != '\0') {
2973 ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
2974 /* keep going, better to hangup without cause than to not hang up at all */
2975 causecode = 0; /* do not set channel's hangupcause */
2979 if (!(c = ast_channel_get_by_name(name))) {
2980 astman_send_error(s, m, "No such channel");
2984 ast_channel_lock(c);
2985 if (causecode > 0) {
2986 ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
2987 c->name, causecode, c->hangupcause);
2988 c->hangupcause = causecode;
2990 ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT);
2991 ast_channel_unlock(c);
2993 c = ast_channel_unref(c);
2995 astman_send_ack(s, m, "Channel Hungup");
3000 static int action_setvar(struct mansession *s, const struct message *m)
3002 struct ast_channel *c = NULL;
3003 const char *name = astman_get_header(m, "Channel");
3004 const char *varname = astman_get_header(m, "Variable");
3005 const char *varval = astman_get_header(m, "Value");
3008 if (ast_strlen_zero(varname)) {
3009 astman_send_error(s, m, "No variable specified");
3013 if (!ast_strlen_zero(name)) {
3014 if (!(c = ast_channel_get_by_name(name))) {
3015 astman_send_error(s, m, "No such channel");
3020 res = pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
3023 c = ast_channel_unref(c);
3026 astman_send_ack(s, m, "Variable Set");
3028 astman_send_error(s, m, "Variable not set");
3033 static int action_getvar(struct mansession *s, const struct message *m)
3035 struct ast_channel *c = NULL;
3036 const char *name = astman_get_header(m, "Channel");
3037 const char *varname = astman_get_header(m, "Variable");
3039 char workspace[1024] = "";
3041 if (ast_strlen_zero(varname)) {
3042 astman_send_error(s, m, "No variable specified");
3046 if (!ast_strlen_zero(name)) {