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_* */
55 #include "asterisk/channel.h"
56 #include "asterisk/file.h"
57 #include "asterisk/manager.h"
58 #include "asterisk/module.h"
59 #include "asterisk/config.h"
60 #include "asterisk/callerid.h"
61 #include "asterisk/lock.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/app.h"
64 #include "asterisk/pbx.h"
65 #include "asterisk/md5.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/utils.h"
68 #include "asterisk/tcptls.h"
69 #include "asterisk/http.h"
70 #include "asterisk/ast_version.h"
71 #include "asterisk/threadstorage.h"
72 #include "asterisk/linkedlists.h"
73 #include "asterisk/version.h"
74 #include "asterisk/term.h"
75 #include "asterisk/astobj2.h"
76 #include "asterisk/features.h"
77 #include "asterisk/security_events.h"
80 <manager name="Ping" language="en_US">
85 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
88 <para>A 'Ping' action will ellicit a 'Pong' response. Used to keep the
89 manager connection open.</para>
92 <manager name="Events" language="en_US">
97 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
98 <parameter name="EventMask" required="true">
101 <para>If all events should be sent.</para>
104 <para>If no events should be sent.</para>
106 <enum name="system,call,log,...">
107 <para>To select which flags events should have to be sent.</para>
113 <para>Enable/Disable sending of events to this manager client.</para>
116 <manager name="Logoff" language="en_US">
121 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
124 <para>Logoff the current manager session.</para>
127 <manager name="Login" language="en_US">
132 <parameter name="ActionID">
133 <para>ActionID for this transaction. Will be returned.</para>
137 <para>Login Manager.</para>
140 <manager name="Challenge" language="en_US">
142 Generate Challenge for MD5 Auth.
145 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
148 <para>Generate a challenge for MD5 authentication.</para>
151 <manager name="Hangup" language="en_US">
156 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
157 <parameter name="Channel" required="true">
158 <para>The channel name to be hangup.</para>
160 <parameter name="Cause">
161 <para>Numeric hangup cause.</para>
165 <para>Hangup a channel.</para>
168 <manager name="Status" language="en_US">
173 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
174 <parameter name="Channel" required="true">
175 <para>The name of the channel to query for status.</para>
177 <parameter name="Variables">
178 <para>Comma <literal>,</literal> separated list of variable to include.</para>
182 <para>Will return the status information of each channel along with the
183 value for the specified channel variables.</para>
186 <manager name="Setvar" language="en_US">
188 Set a channel variable.
191 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
192 <parameter name="Channel">
193 <para>Channel to set variable for.</para>
195 <parameter name="Variable" required="true">
196 <para>Variable name.</para>
198 <parameter name="Value" required="true">
199 <para>Variable value.</para>
203 <para>Set a global or local channel variable.</para>
206 <manager name="Getvar" language="en_US">
208 Gets a channel variable.
211 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
212 <parameter name="Channel">
213 <para>Channel to read variable from.</para>
215 <parameter name="Variable" required="true">
216 <para>Variable name.</para>
220 <para>Get the value of a global or local channel variable.</para>
223 <manager name="GetConfig" language="en_US">
225 Retrieve configuration.
228 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
229 <parameter name="Filename" required="true">
230 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
232 <parameter name="Category">
233 <para>Category in configuration file.</para>
237 <para>This action will dump the contents of a configuration
238 file by category and contents or optionally by specified category only.</para>
241 <manager name="GetConfigJSON" language="en_US">
243 Retrieve configuration (JSON format).
246 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
247 <parameter name="Filename" required="true">
248 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
252 <para>This action will dump the contents of a configuration file by category
253 and contents in JSON format. This only makes sense to be used using rawman over
254 the HTTP interface.</para>
257 <manager name="UpdateConfig" language="en_US">
259 Update basic configuration.
262 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
263 <parameter name="SrcFilename" required="true">
264 <para>Configuration filename to read (e.g. <filename>foo.conf</filename>).</para>
266 <parameter name="DstFilename" required="true">
267 <para>Configuration filename to write (e.g. <filename>foo.conf</filename>)</para>
269 <parameter name="Reload">
270 <para>Whether or not a reload should take place (or name of specific module).</para>
272 <parameter name="Action-XXXXXX">
273 <para>Action to take.</para>
274 <para>X's represent 6 digit number beginning with 000000.</para>
276 <enum name="NewCat" />
277 <enum name="RenameCat" />
278 <enum name="DelCat" />
279 <enum name="EmptyCat" />
280 <enum name="Update" />
281 <enum name="Delete" />
282 <enum name="Append" />
283 <enum name="Insert" />
286 <parameter name="Cat-XXXXXX">
287 <para>Category to operate on.</para>
288 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
290 <parameter name="Var-XXXXXX">
291 <para>Variable to work on.</para>
292 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
294 <parameter name="Value-XXXXXX">
295 <para>Value to work on.</para>
296 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
298 <parameter name="Match-XXXXXX">
299 <para>Extra match required to match line.</para>
300 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
302 <parameter name="Line-XXXXXX">
303 <para>Line in category to operate on (used with delete and insert actions).</para>
304 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
308 <para>This action will modify, create, or delete configuration elements
309 in Asterisk configuration files.</para>
312 <manager name="CreateConfig" language="en_US">
314 Creates an empty file in the configuration directory.
317 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
318 <parameter name="Filename" required="true">
319 <para>The configuration filename to create (e.g. <filename>foo.conf</filename>).</para>
323 <para>This action will create an empty file in the configuration
324 directory. This action is intended to be used before an UpdateConfig
328 <manager name="ListCategories" language="en_US">
330 List categories in configuration file.
333 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
334 <parameter name="Filename" required="true">
335 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
339 <para>This action will dump the categories in a given file.</para>
342 <manager name="Redirect" language="en_US">
344 Redirect (transfer) a call.
347 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
348 <parameter name="Channel" required="true">
349 <para>Channel to redirect.</para>
351 <parameter name="ExtraChannel">
352 <para>Second call leg to transfer (optional).</para>
354 <parameter name="Exten" required="true">
355 <para>Extension to transfer to.</para>
357 <parameter name="Context" required="true">
358 <para>Context to transfer to.</para>
360 <parameter name="Priority" required="true">
361 <para>Priority to transfer to.</para>
365 <para>Redirect (transfer) a call.</para>
368 <manager name="Atxfer" language="en_US">
373 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
374 <parameter name="Channel" required="true">
375 <para>Transferer's channel.</para>
377 <parameter name="Exten" required="true">
378 <para>Extension to transfer to.</para>
380 <parameter name="Context" required="true">
381 <para>Context to transfer to.</para>
383 <parameter name="Priority" required="true">
384 <para>Priority to transfer to.</para>
388 <para>Attended transfer.</para>
391 <manager name="Originate" language="en_US">
396 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
397 <parameter name="Channel" required="true">
398 <para>Channel name to call.</para>
400 <parameter name="Exten">
401 <para>Extension to use (requires <literal>Context</literal> and
402 <literal>Priority</literal>)</para>
404 <parameter name="Context">
405 <para>Context to use (requires <literal>Exten</literal> and
406 <literal>Priority</literal>)</para>
408 <parameter name="Priority">
409 <para>Priority to use (requires <literal>Exten</literal> and
410 <literal>Context</literal>)</para>
412 <parameter name="Application">
413 <para>Application to execute.</para>
415 <parameter name="Data">
416 <para>Data to use (requires <literal>Application</literal>).</para>
418 <parameter name="Timeout" default="30000">
419 <para>How long to wait for call to be answered (in ms.).</para>
421 <parameter name="CallerID">
422 <para>Caller ID to be set on the outgoing channel.</para>
424 <parameter name="Variable">
425 <para>Channel variable to set, multiple Variable: headers are allowed.</para>
427 <parameter name="Account">
428 <para>Account code.</para>
430 <parameter name="Async">
431 <para>Set to <literal>true</literal> for fast origination.</para>
435 <para>Generates an outgoing call to a
436 <replaceable>Extension</replaceable>/<replaceable>Context</replaceable>/<replaceable>Priority</replaceable>
437 or <replaceable>Application</replaceable>/<replaceable>Data</replaceable></para>
440 <manager name="Command" language="en_US">
442 Execute Asterisk CLI Command.
445 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
446 <parameter name="Command" required="true">
447 <para>Asterisk CLI command to run.</para>
451 <para>Run a CLI command.</para>
454 <manager name="ExtensionState" language="en_US">
456 Check Extension Status.
459 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
460 <parameter name="Exten" required="true">
461 <para>Extension to check state on.</para>
463 <parameter name="Context" required="true">
464 <para>Context for extension.</para>
468 <para>Report the extension state for given extension. If the extension has a hint,
469 will use devicestate to check the status of the device connected to the extension.</para>
470 <para>Will return an <literal>Extension Status</literal> message. The response will include
471 the hint for the extension and the status.</para>
474 <manager name="AbsoluteTimeout" language="en_US">
476 Set absolute timeout.
479 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
480 <parameter name="Channel" required="true">
481 <para>Channel name to hangup.</para>
483 <parameter name="Timeout" required="true">
484 <para>Maximum duration of the call (sec).</para>
488 <para>Hangup a channel after a certain time. Acknowledges set time with
489 <literal>Timeout Set</literal> message.</para>
492 <manager name="MailboxStatus" language="en_US">
497 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
498 <parameter name="Mailbox" required="true">
499 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
503 <para>Checks a voicemail account for status.</para>
504 <para>Returns number of messages.</para>
505 <para>Message: Mailbox Status.</para>
506 <para>Mailbox: <replaceable>mailboxid</replaceable>.</para>
507 <para>Waiting: <replaceable>count</replaceable>.</para>
510 <manager name="MailboxCount" language="en_US">
512 Check Mailbox Message Count.
515 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
516 <parameter name="Mailbox" required="true">
517 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
521 <para>Checks a voicemail account for new messages.</para>
522 <para>Returns number of urgent, new and old messages.</para>
523 <para>Message: Mailbox Message Count</para>
524 <para>Mailbox: <replaceable>mailboxid</replaceable></para>
525 <para>UrgentMessages: <replaceable>count</replaceable></para>
526 <para>NewMessages: <replaceable>count</replaceable></para>
527 <para>OldMessages: <replaceable>count</replaceable></para>
530 <manager name="ListCommands" language="en_US">
532 List available manager commands.
535 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
538 <para>Returns the action name and synopsis for every action that
539 is available to the user.</para>
542 <manager name="SendText" language="en_US">
544 Send text message to channel.
547 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
548 <parameter name="Channel" required="true">
549 <para>Channel to send message to.</para>
551 <parameter name="Message" required="true">
552 <para>Message to send.</para>
556 <para>Sends A Text Message to a channel while in a call.</para>
559 <manager name="UserEvent" language="en_US">
561 Send an arbitrary event.
564 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
565 <parameter name="UserEvent" required="true">
566 <para>Event string to send.</para>
568 <parameter name="Header1">
569 <para>Content1.</para>
571 <parameter name="HeaderN">
572 <para>ContentN.</para>
576 <para>Send an event to manager sessions.</para>
579 <manager name="WaitEvent" language="en_US">
581 Wait for an event to occur.
584 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
585 <parameter name="Timeout" required="true">
586 <para>Maximum time (in seconds) to wait for events, <literal>-1</literal> means forever.</para>
590 <para>This action will ellicit a <literal>Success</literal> response. Whenever
591 a manager event is queued. Once WaitEvent has been called on an HTTP manager
592 session, events will be generated and queued.</para>
595 <manager name="CoreSettings" language="en_US">
597 Show PBX core settings (version etc).
600 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
603 <para>Query for Core PBX settings.</para>
606 <manager name="CoreStatus" language="en_US">
608 Show PBX core status variables.
611 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
614 <para>Query for Core PBX status.</para>
617 <manager name="Reload" language="en_US">
622 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
623 <parameter name="Module">
624 <para>Name of the module to reload.</para>
628 <para>Send a reload event.</para>
631 <manager name="CoreShowChannels" language="en_US">
633 List currently active channels.
636 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
639 <para>List currently defined channels and some information about them.</para>
642 <manager name="ModuleLoad" language="en_US">
647 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
648 <parameter name="Module">
649 <para>Asterisk module name (including .so extension) or subsystem identifier:</para>
653 <enum name="dnsmgr" />
654 <enum name="extconfig" />
655 <enum name="manager" />
660 <parameter name="LoadType" required="true">
661 <para>The operation to be done on module.</para>
664 <enum name="unload" />
665 <enum name="reload" />
667 <para>If no module is specified for a <literal>reload</literal> loadtype,
668 all modules are reloaded.</para>
672 <para>Loads, unloads or reloads an Asterisk module in a running system.</para>
675 <manager name="ModuleCheck" language="en_US">
677 Check if module is loaded.
680 <parameter name="Module" required="true">
681 <para>Asterisk module name (not including extension).</para>
685 <para>Checks if Asterisk module is loaded. Will return Success/Failure.
686 For success returns, the module revision number is included.</para>
694 UNSPECIFIED_CATEGORY,
695 UNSPECIFIED_ARGUMENT,
707 * Linked list of events.
708 * Global events are appended to the list by append_event().
709 * The usecount is the number of stored pointers to the element,
710 * excluding the list pointers. So an element that is only in
711 * the list has a usecount of 0, not 1.
713 * Clients have a pointer to the last event processed, and for each
714 * of these clients we track the usecount of the elements.
715 * If we have a pointer to an entry in the list, it is safe to navigate
716 * it forward because elements will not be deleted, but only appended.
717 * The worst that can happen is seeing the pointer still NULL.
719 * When the usecount of an element drops to 0, and the element is the
720 * first in the list, we can remove it. Removal is done within the
721 * main thread, which is woken up for the purpose.
723 * For simplicity of implementation, we make sure the list is never empty.
726 int usecount; /*!< # of clients who still need the event */
728 unsigned int seq; /*!< sequence number */
729 AST_LIST_ENTRY(eventqent) eq_next;
730 char eventdata[1]; /*!< really variable size, allocated by append_event() */
733 static AST_LIST_HEAD_STATIC(all_events, eventqent);
735 static int displayconnects = 1;
736 static int allowmultiplelogin = 1;
737 static int timestampevents;
738 static int httptimeout = 60;
739 static int manager_enabled = 0;
740 static int webmanager_enabled = 0;
742 #define DEFAULT_REALM "asterisk"
743 static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
745 static int block_sockets;
747 static int manager_debug; /*!< enable some debugging code in the manager */
750 * Descriptor for a manager session, either on the AMI socket or over HTTP.
753 * AMI session have managerid == 0; the entry is created upon a connect,
754 * and destroyed with the socket.
755 * HTTP sessions have managerid != 0, the value is used as a search key
756 * to lookup sessions (using the mansession_id cookie, or nonce key from
757 * Digest Authentication http header).
759 #define MAX_BLACKLIST_CMD_LEN 2
760 static const struct {
761 const char *words[AST_MAX_CMD_LEN];
762 } command_blacklist[] = {
763 {{ "module", "load", NULL }},
764 {{ "module", "unload", NULL }},
765 {{ "restart", "gracefully", NULL }},
768 /* In order to understand what the heck is going on with the
769 * mansession_session and mansession structs, we need to have a bit of a history
772 * In the beginning, there was the mansession. The mansession contained data that was
773 * intrinsic to a manager session, such as the time that it started, the name of the logged-in
774 * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
775 * sessions, these were used to represent the TCP socket over which the AMI session was taking
776 * place. It makes perfect sense for these fields to be a part of the session-specific data since
777 * the session actually defines this information.
779 * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
780 * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
781 * but rather to the action that is being executed. Because a single session may execute many commands
782 * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
783 * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
784 * has had a chance to properly close its handles.
786 * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
787 * from being run at the same time in a single session. Some manager actions may block for a long time, thus
788 * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
789 * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
790 * part of the action instead.
792 * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
793 * contain the action-specific information, such as which file to write to. In order to maintain expectations
794 * of action handlers and not have to change the public API of the manager code, we would need to name this
795 * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
796 * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
797 * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
800 struct mansession_session {
801 pthread_t ms_t; /*!< Execution thread, basically useless */
802 /* XXX need to document which fields it is protecting */
803 struct sockaddr_in sin; /*!< address we are connecting from */
804 FILE *f; /*!< fdopen() on the underlying fd */
805 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
806 int inuse; /*!< number of HTTP sessions using this entry */
807 int needdestroy; /*!< Whether an HTTP session should be destroyed */
808 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
809 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
810 time_t sessionstart; /*!< Session start time */
811 struct timeval sessionstart_tv; /*!< Session start time */
812 time_t sessiontimeout; /*!< Session timeout if HTTP */
813 char username[80]; /*!< Logged in username */
814 char challenge[10]; /*!< Authentication challenge */
815 int authenticated; /*!< Authentication status */
816 int readperm; /*!< Authorization for reading */
817 int writeperm; /*!< Authorization for writing */
818 char inbuf[1025]; /*!< Buffer */
819 /* we use the extra byte to add a '\0' and simplify parsing */
820 int inlen; /*!< number of buffered bytes */
821 int send_events; /*!< XXX what ? */
822 struct eventqent *last_ev; /*!< last event processed. */
823 int writetimeout; /*!< Timeout for ast_carefulwrite() */
824 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
825 time_t noncetime; /*!< Timer for nonce value expiration */
826 unsigned long oldnonce; /*!< Stale nonce value */
827 unsigned long nc; /*!< incremental nonce counter */
828 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
829 AST_LIST_ENTRY(mansession_session) list;
832 /* In case you didn't read that giant block of text above the mansession_session struct, the
833 * 'mansession' struct is named this solely to keep the API the same in Asterisk. This structure really
834 * represents data that is different from Manager action to Manager action. The mansession_session pointer
835 * contained within points to session-specific data.
838 struct mansession_session *session;
839 struct ast_tcptls_session_instance *tcptls_session;
845 static struct ao2_container *sessions = NULL;
847 #define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next))
849 /*! \brief user descriptor, as read from the config file.
851 * \note It is still missing some fields -- e.g. we can have multiple permit and deny
852 * lines which are not supported here, and readperm/writeperm/writetimeout
855 struct ast_manager_user {
858 struct ast_ha *ha; /*!< ACL setting */
859 int readperm; /*! Authorization for reading */
860 int writeperm; /*! Authorization for writing */
861 int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
862 int displayconnects; /*!< XXX unused */
863 int keep; /*!< mark entries created on a reload */
864 char *a1_hash; /*!< precalculated A1 for Digest auth */
865 AST_RWLIST_ENTRY(ast_manager_user) list;
868 /*! \brief list of users found in the config file */
869 static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
871 /*! \brief list of actions registered */
872 static AST_RWLIST_HEAD_STATIC(actions, manager_action);
874 /*! \brief list of hooks registered */
875 static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
877 static struct eventqent *unref_event(struct eventqent *e);
878 static void ref_event(struct eventqent *e);
880 /*! \brief Add a custom hook to be called when an event is fired */
881 void ast_manager_register_hook(struct manager_custom_hook *hook)
883 AST_RWLIST_WRLOCK(&manager_hooks);
884 AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
885 AST_RWLIST_UNLOCK(&manager_hooks);
888 /*! \brief Delete a custom hook to be called when an event is fired */
889 void ast_manager_unregister_hook(struct manager_custom_hook *hook)
891 AST_RWLIST_WRLOCK(&manager_hooks);
892 AST_RWLIST_REMOVE(&manager_hooks, hook, list);
893 AST_RWLIST_UNLOCK(&manager_hooks);
896 int check_manager_enabled()
898 return manager_enabled;
901 int check_webmanager_enabled()
903 return (webmanager_enabled && manager_enabled);
907 * Grab a reference to the last event, update usecount as needed.
908 * Can handle a NULL pointer.
910 static struct eventqent *grab_last(void)
912 struct eventqent *ret;
914 AST_LIST_LOCK(&all_events);
915 ret = AST_LIST_LAST(&all_events);
916 /* the list is never empty now, but may become so when
917 * we optimize it in the future, so be prepared.
920 ast_atomic_fetchadd_int(&ret->usecount, 1);
922 AST_LIST_UNLOCK(&all_events);
927 * Purge unused events. Remove elements from the head
928 * as long as their usecount is 0 and there is a next element.
930 static void purge_events(void)
932 struct eventqent *ev;
934 AST_LIST_LOCK(&all_events);
935 while ( (ev = AST_LIST_FIRST(&all_events)) &&
936 ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
937 AST_LIST_REMOVE_HEAD(&all_events, eq_next);
940 AST_LIST_UNLOCK(&all_events);
944 * helper functions to convert back and forth between
945 * string and numeric representation of set of flags
947 static const struct permalias {
951 { EVENT_FLAG_SYSTEM, "system" },
952 { EVENT_FLAG_CALL, "call" },
953 { EVENT_FLAG_LOG, "log" },
954 { EVENT_FLAG_VERBOSE, "verbose" },
955 { EVENT_FLAG_COMMAND, "command" },
956 { EVENT_FLAG_AGENT, "agent" },
957 { EVENT_FLAG_USER, "user" },
958 { EVENT_FLAG_CONFIG, "config" },
959 { EVENT_FLAG_DTMF, "dtmf" },
960 { EVENT_FLAG_REPORTING, "reporting" },
961 { EVENT_FLAG_CDR, "cdr" },
962 { EVENT_FLAG_DIALPLAN, "dialplan" },
963 { EVENT_FLAG_ORIGINATE, "originate" },
964 { EVENT_FLAG_AGI, "agi" },
969 /*! \brief Convert authority code to a list of options */
970 static const char *authority_to_str(int authority, struct ast_str **res)
976 for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
977 if (authority & perms[i].num) {
978 ast_str_append(res, 0, "%s%s", sep, perms[i].label);
983 if (ast_str_strlen(*res) == 0) /* replace empty string with something sensible */
984 ast_str_append(res, 0, "<none>");
986 return ast_str_buffer(*res);
989 /*! Tells you if smallstr exists inside bigstr
990 which is delim by delim and uses no buf or stringsep
991 ast_instring("this|that|more","this",'|') == 1;
993 feel free to move this to app.c -anthm */
994 static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
996 const char *val = bigstr, *next;
999 if ((next = strchr(val, delim))) {
1000 if (!strncmp(val, smallstr, (next - val))) {
1006 return !strcmp(smallstr, val);
1008 } while (*(val = (next + 1)));
1013 static int get_perm(const char *instr)
1021 for (x = 0; x < ARRAY_LEN(perms); x++) {
1022 if (ast_instring(instr, perms[x].label, ',')) {
1023 ret |= perms[x].num;
1031 * A number returns itself, false returns 0, true returns all flags,
1032 * other strings return the flags that are set.
1034 static int strings_to_mask(const char *string)
1038 if (ast_strlen_zero(string)) {
1042 for (p = string; *p; p++) {
1043 if (*p < '0' || *p > '9') {
1047 if (!p) { /* all digits */
1048 return atoi(string);
1050 if (ast_false(string)) {
1053 if (ast_true(string)) { /* all permissions */
1055 for (x = 0; x < ARRAY_LEN(perms); x++) {
1056 ret |= perms[x].num;
1060 return get_perm(string);
1063 /*! \brief Unreference manager session object.
1064 If no more references, then go ahead and delete it */
1065 static struct mansession_session *unref_mansession(struct mansession_session *s)
1067 int refcount = ao2_ref(s, -1);
1068 if (manager_debug) {
1069 ast_log(LOG_DEBUG, "Mansession: %p refcount now %d\n", s, refcount - 1);
1074 static void session_destructor(void *obj)
1076 struct mansession_session *session = obj;
1077 struct eventqent *eqe = session->last_ev;
1078 struct ast_datastore *datastore;
1080 /* Get rid of each of the data stores on the session */
1081 while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
1082 /* Free the data store */
1083 ast_datastore_free(datastore);
1086 if (session->f != NULL) {
1092 /*! \brief Allocate manager session structure and add it to the list of sessions */
1093 static struct mansession_session *build_mansession(struct sockaddr_in sin)
1095 struct mansession_session *newsession;
1097 if (!(newsession = ao2_alloc(sizeof(*newsession), session_destructor))) {
1100 newsession->fd = -1;
1101 newsession->waiting_thread = AST_PTHREADT_NULL;
1102 newsession->writetimeout = 100;
1103 newsession->send_events = -1;
1104 newsession->sin = sin;
1106 ao2_link(sessions, newsession);
1111 static int mansession_cmp_fn(void *obj, void *arg, int flags)
1113 struct mansession_session *s = obj;
1115 return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
1118 static void session_destroy(struct mansession_session *s)
1120 unref_mansession(s);
1121 ao2_unlink(sessions, s);
1125 static int check_manager_session_inuse(const char *name)
1127 struct mansession_session *session = ao2_find(sessions, (char *) name, 0);
1132 unref_mansession(session);
1139 * lookup an entry in the list of registered users.
1140 * must be called with the list lock held.
1142 static struct ast_manager_user *get_manager_by_name_locked(const char *name)
1144 struct ast_manager_user *user = NULL;
1146 AST_RWLIST_TRAVERSE(&users, user, list)
1147 if (!strcasecmp(user->username, name)) {
1153 /*! \brief Get displayconnects config option.
1154 * \param session manager session to get parameter from.
1155 * \return displayconnects config option value.
1157 static int manager_displayconnects (struct mansession_session *session)
1159 struct ast_manager_user *user = NULL;
1162 AST_RWLIST_RDLOCK(&users);
1163 if ((user = get_manager_by_name_locked (session->username))) {
1164 ret = user->displayconnects;
1166 AST_RWLIST_UNLOCK(&users);
1171 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1173 struct manager_action *cur;
1174 struct ast_str *authority;
1178 char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64];
1183 e->command = "manager show command";
1185 "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
1186 " Shows the detailed description for a specific Asterisk manager interface command.\n";
1189 l = strlen(a->word);
1191 AST_RWLIST_RDLOCK(&actions);
1192 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1193 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
1194 ret = ast_strdup(cur->action);
1195 break; /* make sure we exit even if ast_strdup() returns NULL */
1198 AST_RWLIST_UNLOCK(&actions);
1201 authority = ast_str_alloca(80);
1203 return CLI_SHOWUSAGE;
1207 /* setup the titles */
1208 term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1209 term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
1210 term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1211 term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
1212 term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
1215 AST_RWLIST_RDLOCK(&actions);
1216 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1217 for (num = 3; num < a->argc; num++) {
1218 if (!strcasecmp(cur->action, a->argv[num])) {
1220 if (cur->docsrc == AST_XML_DOC) {
1221 ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n",
1223 ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1),
1225 ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1),
1227 ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1),
1229 ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1),
1231 ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1));
1234 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
1235 cur->action, cur->synopsis,
1236 authority_to_str(cur->authority, &authority),
1237 S_OR(cur->description, ""));
1244 AST_RWLIST_UNLOCK(&actions);
1249 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1253 e->command = "manager set debug [on|off]";
1254 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
1261 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
1262 } else if (a->argc == 4) {
1263 if (!strcasecmp(a->argv[3], "on")) {
1265 } else if (!strcasecmp(a->argv[3], "off")) {
1268 return CLI_SHOWUSAGE;
1274 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1276 struct ast_manager_user *user = NULL;
1279 struct ast_str *rauthority = ast_str_alloca(128);
1280 struct ast_str *wauthority = ast_str_alloca(128);
1284 e->command = "manager show user";
1286 " Usage: manager show user <user>\n"
1287 " Display all information related to the manager user specified.\n";
1290 l = strlen(a->word);
1295 AST_RWLIST_RDLOCK(&users);
1296 AST_RWLIST_TRAVERSE(&users, user, list) {
1297 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
1298 ret = ast_strdup(user->username);
1302 AST_RWLIST_UNLOCK(&users);
1307 return CLI_SHOWUSAGE;
1310 AST_RWLIST_RDLOCK(&users);
1312 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
1313 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
1314 AST_RWLIST_UNLOCK(&users);
1318 ast_cli(a->fd, "\n");
1325 "displayconnects: %s\n",
1326 (user->username ? user->username : "(N/A)"),
1327 (user->secret ? "<Set>" : "(N/A)"),
1328 (user->ha ? "yes" : "no"),
1329 authority_to_str(user->readperm, &rauthority),
1330 authority_to_str(user->writeperm, &wauthority),
1331 (user->displayconnects ? "yes" : "no"));
1333 AST_RWLIST_UNLOCK(&users);
1339 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1341 struct ast_manager_user *user = NULL;
1345 e->command = "manager show users";
1347 "Usage: manager show users\n"
1348 " Prints a listing of all managers that are currently configured on that\n"
1355 return CLI_SHOWUSAGE;
1358 AST_RWLIST_RDLOCK(&users);
1360 /* If there are no users, print out something along those lines */
1361 if (AST_RWLIST_EMPTY(&users)) {
1362 ast_cli(a->fd, "There are no manager users.\n");
1363 AST_RWLIST_UNLOCK(&users);
1367 ast_cli(a->fd, "\nusername\n--------\n");
1369 AST_RWLIST_TRAVERSE(&users, user, list) {
1370 ast_cli(a->fd, "%s\n", user->username);
1374 AST_RWLIST_UNLOCK(&users);
1376 ast_cli(a->fd,"-------------------\n"
1377 "%d manager users configured.\n", count_amu);
1382 /*! \brief CLI command manager list commands */
1383 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1385 struct manager_action *cur;
1386 struct ast_str *authority;
1387 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
1390 e->command = "manager show commands";
1392 "Usage: manager show commands\n"
1393 " Prints a listing of all the available Asterisk manager interface commands.\n";
1398 authority = ast_str_alloca(80);
1399 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
1400 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
1402 AST_RWLIST_RDLOCK(&actions);
1403 AST_RWLIST_TRAVERSE(&actions, cur, list)
1404 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
1405 AST_RWLIST_UNLOCK(&actions);
1410 /*! \brief CLI command manager list connected */
1411 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1413 struct mansession_session *session;
1414 time_t now = time(NULL);
1415 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
1416 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
1418 struct ao2_iterator i;
1422 e->command = "manager show connected";
1424 "Usage: manager show connected\n"
1425 " Prints a listing of the users that are currently connected to the\n"
1426 "Asterisk manager interface.\n";
1432 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
1434 i = ao2_iterator_init(sessions, 0);
1435 while ((session = ao2_iterator_next(&i))) {
1437 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);
1439 ao2_unlock(session);
1440 unref_mansession(session);
1443 ast_cli(a->fd, "%d users connected.\n", count);
1448 /*! \brief CLI command manager list eventq */
1449 /* Should change to "manager show connected" */
1450 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1452 struct eventqent *s;
1455 e->command = "manager show eventq";
1457 "Usage: manager show eventq\n"
1458 " Prints a listing of all events pending in the Asterisk manger\n"
1464 AST_LIST_LOCK(&all_events);
1465 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
1466 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
1467 ast_cli(a->fd, "Category: %d\n", s->category);
1468 ast_cli(a->fd, "Event:\n%s", s->eventdata);
1470 AST_LIST_UNLOCK(&all_events);
1475 /*! \brief CLI command manager reload */
1476 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1480 e->command = "manager reload";
1482 "Usage: manager reload\n"
1483 " Reloads the manager configuration.\n";
1489 return CLI_SHOWUSAGE;
1496 static struct ast_cli_entry cli_manager[] = {
1497 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
1498 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
1499 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
1500 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
1501 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
1502 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
1503 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
1504 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
1507 static struct eventqent *unref_event(struct eventqent *e)
1509 ast_atomic_fetchadd_int(&e->usecount, -1);
1510 return AST_LIST_NEXT(e, eq_next);
1513 static void ref_event(struct eventqent *e)
1515 ast_atomic_fetchadd_int(&e->usecount, 1);
1519 * Generic function to return either the first or the last matching header
1520 * from a list of variables, possibly skipping empty strings.
1521 * At the moment there is only one use of this function in this file,
1522 * so we make it static.
1524 #define GET_HEADER_FIRST_MATCH 0
1525 #define GET_HEADER_LAST_MATCH 1
1526 #define GET_HEADER_SKIP_EMPTY 2
1527 static const char *__astman_get_header(const struct message *m, char *var, int mode)
1529 int x, l = strlen(var);
1530 const char *result = "";
1532 for (x = 0; x < m->hdrcount; x++) {
1533 const char *h = m->headers[x];
1534 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ') {
1535 const char *value = h + l + 2;
1536 /* found a potential candidate */
1537 if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
1538 continue; /* not interesting */
1539 if (mode & GET_HEADER_LAST_MATCH)
1540 result = value; /* record the last match so far */
1550 * Return the first matching variable from an array.
1551 * This is the legacy function and is implemented in therms of
1552 * __astman_get_header().
1554 const char *astman_get_header(const struct message *m, char *var)
1556 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
1560 struct ast_variable *astman_get_variables(const struct message *m)
1563 struct ast_variable *head = NULL, *cur;
1565 AST_DECLARE_APP_ARGS(args,
1566 AST_APP_ARG(vars)[32];
1569 varlen = strlen("Variable: ");
1571 for (x = 0; x < m->hdrcount; x++) {
1572 char *parse, *var, *val;
1574 if (strncasecmp("Variable: ", m->headers[x], varlen)) {
1577 parse = ast_strdupa(m->headers[x] + varlen);
1579 AST_STANDARD_APP_ARGS(args, parse);
1583 for (y = 0; y < args.argc; y++) {
1584 if (!args.vars[y]) {
1587 var = val = ast_strdupa(args.vars[y]);
1589 if (!val || ast_strlen_zero(var)) {
1592 cur = ast_variable_new(var, val, "");
1602 * helper function to send a string to the socket.
1603 * Return -1 on error (e.g. buffer full).
1605 static int send_string(struct mansession *s, char *string)
1608 return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout);
1610 return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout);
1615 * \brief thread local buffer for astman_append
1617 * \note This can not be defined within the astman_append() function
1618 * because it declares a couple of functions that get used to
1619 * initialize the thread local storage key.
1621 AST_THREADSTORAGE(astman_append_buf);
1622 AST_THREADSTORAGE(userevent_buf);
1624 /*! \brief initial allocated size for the astman_append_buf */
1625 #define ASTMAN_APPEND_BUF_INITSIZE 256
1628 * utility functions for creating AMI replies
1630 void astman_append(struct mansession *s, const char *fmt, ...)
1633 struct ast_str *buf;
1635 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
1640 ast_str_set_va(&buf, 0, fmt, ap);
1643 if (s->f != NULL || s->session->f != NULL) {
1644 send_string(s, ast_str_buffer(buf));
1646 ast_verbose("fd == -1 in astman_append, should not happen\n");
1650 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
1651 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
1652 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
1653 be non-zero). In either of these cases, there is no need to lock-protect the session's
1654 fd, since no other output will be sent (events will be queued), and no input will
1655 be read until either the current action finishes or get_input() obtains the session
1659 /*! \brief send a response with an optional message,
1660 * and terminate it with an empty line.
1661 * m is used only to grab the 'ActionID' field.
1663 * Use the explicit constant MSG_MOREDATA to remove the empty line.
1664 * XXX MSG_MOREDATA should go to a header file.
1666 #define MSG_MOREDATA ((char *)astman_send_response)
1667 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
1669 const char *id = astman_get_header(m, "ActionID");
1671 astman_append(s, "Response: %s\r\n", resp);
1672 if (!ast_strlen_zero(id)) {
1673 astman_append(s, "ActionID: %s\r\n", id);
1676 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
1678 if (msg == MSG_MOREDATA) {
1681 astman_append(s, "Message: %s\r\n\r\n", msg);
1683 astman_append(s, "\r\n");
1687 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
1689 astman_send_response_full(s, m, resp, msg, NULL);
1692 void astman_send_error(struct mansession *s, const struct message *m, char *error)
1694 astman_send_response_full(s, m, "Error", error, NULL);
1697 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
1699 astman_send_response_full(s, m, "Success", msg, NULL);
1702 static void astman_start_ack(struct mansession *s, const struct message *m)
1704 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
1707 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
1709 astman_send_response_full(s, m, "Success", msg, listflag);
1712 /*! \brief Lock the 'mansession' structure. */
1713 static void mansession_lock(struct mansession *s)
1715 ast_mutex_lock(&s->lock);
1718 /*! \brief Unlock the 'mansession' structure. */
1719 static void mansession_unlock(struct mansession *s)
1721 ast_mutex_unlock(&s->lock);
1725 Rather than braindead on,off this now can also accept a specific int mask value
1726 or a ',' delim list of mask strings (the same as manager.conf) -anthm
1728 static int set_eventmask(struct mansession *s, const char *eventmask)
1730 int maskint = strings_to_mask(eventmask);
1734 s->session->send_events = maskint;
1736 mansession_unlock(s);
1741 static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s)
1743 return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS :
1744 AST_SECURITY_EVENT_TRANSPORT_TCP;
1747 static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s,
1748 struct sockaddr_in *sin_local)
1750 *sin_local = s->tcptls_session->parent->local_address;
1755 static void report_invalid_user(const struct mansession *s, const char *username)
1757 struct sockaddr_in sin_local;
1758 char session_id[32];
1759 struct ast_security_event_inval_acct_id inval_acct_id = {
1760 .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
1761 .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
1762 .common.service = "AMI",
1763 .common.account_id = username,
1764 .common.session_tv = &s->session->sessionstart_tv,
1765 .common.local_addr = {
1766 .sin = mansession_encode_sin_local(s, &sin_local),
1767 .transport = mansession_get_transport(s),
1769 .common.remote_addr = {
1770 .sin = &s->session->sin,
1771 .transport = mansession_get_transport(s),
1773 .common.session_id = session_id,
1776 snprintf(session_id, sizeof(session_id), "%p", s);
1778 ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
1781 static void report_failed_acl(const struct mansession *s, const char *username)
1783 struct sockaddr_in sin_local;
1784 char session_id[32];
1785 struct ast_security_event_failed_acl failed_acl_event = {
1786 .common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
1787 .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
1788 .common.service = "AMI",
1789 .common.account_id = username,
1790 .common.session_tv = &s->session->sessionstart_tv,
1791 .common.local_addr = {
1792 .sin = mansession_encode_sin_local(s, &sin_local),
1793 .transport = mansession_get_transport(s),
1795 .common.remote_addr = {
1796 .sin = &s->session->sin,
1797 .transport = mansession_get_transport(s),
1799 .common.session_id = session_id,
1802 snprintf(session_id, sizeof(session_id), "%p", s->session);
1804 ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
1807 static void report_inval_password(const struct mansession *s, const char *username)
1809 struct sockaddr_in sin_local;
1810 char session_id[32];
1811 struct ast_security_event_inval_password inval_password = {
1812 .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
1813 .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
1814 .common.service = "AMI",
1815 .common.account_id = username,
1816 .common.session_tv = &s->session->sessionstart_tv,
1817 .common.local_addr = {
1818 .sin = mansession_encode_sin_local(s, &sin_local),
1819 .transport = mansession_get_transport(s),
1821 .common.remote_addr = {
1822 .sin = &s->session->sin,
1823 .transport = mansession_get_transport(s),
1825 .common.session_id = session_id,
1828 snprintf(session_id, sizeof(session_id), "%p", s->session);
1830 ast_security_event_report(AST_SEC_EVT(&inval_password));
1833 static void report_auth_success(const struct mansession *s)
1835 struct sockaddr_in sin_local;
1836 char session_id[32];
1837 struct ast_security_event_successful_auth successful_auth = {
1838 .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
1839 .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
1840 .common.service = "AMI",
1841 .common.account_id = s->session->username,
1842 .common.session_tv = &s->session->sessionstart_tv,
1843 .common.local_addr = {
1844 .sin = mansession_encode_sin_local(s, &sin_local),
1845 .transport = mansession_get_transport(s),
1847 .common.remote_addr = {
1848 .sin = &s->session->sin,
1849 .transport = mansession_get_transport(s),
1851 .common.session_id = session_id,
1854 snprintf(session_id, sizeof(session_id), "%p", s->session);
1856 ast_security_event_report(AST_SEC_EVT(&successful_auth));
1859 static void report_req_not_allowed(const struct mansession *s, const char *action)
1861 struct sockaddr_in sin_local;
1862 char session_id[32];
1863 char request_type[64];
1864 struct ast_security_event_req_not_allowed req_not_allowed = {
1865 .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
1866 .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
1867 .common.service = "AMI",
1868 .common.account_id = s->session->username,
1869 .common.session_tv = &s->session->sessionstart_tv,
1870 .common.local_addr = {
1871 .sin = mansession_encode_sin_local(s, &sin_local),
1872 .transport = mansession_get_transport(s),
1874 .common.remote_addr = {
1875 .sin = &s->session->sin,
1876 .transport = mansession_get_transport(s),
1878 .common.session_id = session_id,
1880 .request_type = request_type,
1883 snprintf(session_id, sizeof(session_id), "%p", s->session);
1884 snprintf(request_type, sizeof(request_type), "Action: %s", action);
1886 ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
1889 static void report_req_bad_format(const struct mansession *s, const char *action)
1891 struct sockaddr_in sin_local;
1892 char session_id[32];
1893 char request_type[64];
1894 struct ast_security_event_req_bad_format req_bad_format = {
1895 .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
1896 .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
1897 .common.service = "AMI",
1898 .common.account_id = s->session->username,
1899 .common.session_tv = &s->session->sessionstart_tv,
1900 .common.local_addr = {
1901 .sin = mansession_encode_sin_local(s, &sin_local),
1902 .transport = mansession_get_transport(s),
1904 .common.remote_addr = {
1905 .sin = &s->session->sin,
1906 .transport = mansession_get_transport(s),
1908 .common.session_id = session_id,
1910 .request_type = request_type,
1913 snprintf(session_id, sizeof(session_id), "%p", s->session);
1914 snprintf(request_type, sizeof(request_type), "Action: %s", action);
1916 ast_security_event_report(AST_SEC_EVT(&req_bad_format));
1919 static void report_failed_challenge_response(const struct mansession *s,
1920 const char *response, const char *expected_response)
1922 struct sockaddr_in sin_local;
1923 char session_id[32];
1924 struct ast_security_event_chal_resp_failed chal_resp_failed = {
1925 .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
1926 .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
1927 .common.service = "AMI",
1928 .common.account_id = s->session->username,
1929 .common.session_tv = &s->session->sessionstart_tv,
1930 .common.local_addr = {
1931 .sin = mansession_encode_sin_local(s, &sin_local),
1932 .transport = mansession_get_transport(s),
1934 .common.remote_addr = {
1935 .sin = &s->session->sin,
1936 .transport = mansession_get_transport(s),
1938 .common.session_id = session_id,
1940 .challenge = s->session->challenge,
1941 .response = response,
1942 .expected_response = expected_response,
1945 snprintf(session_id, sizeof(session_id), "%p", s->session);
1947 ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
1950 static void report_session_limit(const struct mansession *s)
1952 struct sockaddr_in sin_local;
1953 char session_id[32];
1954 struct ast_security_event_session_limit session_limit = {
1955 .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
1956 .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
1957 .common.service = "AMI",
1958 .common.account_id = s->session->username,
1959 .common.session_tv = &s->session->sessionstart_tv,
1960 .common.local_addr = {
1961 .sin = mansession_encode_sin_local(s, &sin_local),
1962 .transport = mansession_get_transport(s),
1964 .common.remote_addr = {
1965 .sin = &s->session->sin,
1966 .transport = mansession_get_transport(s),
1968 .common.session_id = session_id,
1971 snprintf(session_id, sizeof(session_id), "%p", s->session);
1973 ast_security_event_report(AST_SEC_EVT(&session_limit));
1977 * Here we start with action_ handlers for AMI actions,
1978 * and the internal functions used by them.
1979 * Generally, the handlers are called action_foo()
1982 /* helper function for action_login() */
1983 static int authenticate(struct mansession *s, const struct message *m)
1985 const char *username = astman_get_header(m, "Username");
1986 const char *password = astman_get_header(m, "Secret");
1988 struct ast_manager_user *user = NULL;
1990 if (ast_strlen_zero(username)) { /* missing username */
1994 /* locate user in locked state */
1995 AST_RWLIST_WRLOCK(&users);
1997 if (!(user = get_manager_by_name_locked(username))) {
1998 report_invalid_user(s, username);
1999 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2000 } else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
2001 report_failed_acl(s, username);
2002 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2003 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
2004 const char *key = astman_get_header(m, "Key");
2005 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
2008 char md5key[256] = "";
2009 struct MD5Context md5;
2010 unsigned char digest[16];
2013 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
2014 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
2015 MD5Final(digest, &md5);
2016 for (x = 0; x < 16; x++)
2017 len += sprintf(md5key + len, "%2.2x", digest[x]);
2018 if (!strcmp(md5key, key)) {
2021 report_failed_challenge_response(s, key, md5key);
2024 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
2025 S_OR(s->session->challenge, ""));
2027 } else if (user->secret) {
2028 if (!strcmp(password, user->secret)) {
2031 report_inval_password(s, username);
2036 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2037 AST_RWLIST_UNLOCK(&users);
2043 ast_copy_string(s->session->username, username, sizeof(s->session->username));
2044 s->session->readperm = user->readperm;
2045 s->session->writeperm = user->writeperm;
2046 s->session->writetimeout = user->writetimeout;
2047 s->session->sessionstart = time(NULL);
2048 s->session->sessionstart_tv = ast_tvnow();
2049 set_eventmask(s, astman_get_header(m, "Events"));
2051 report_auth_success(s);
2053 AST_RWLIST_UNLOCK(&users);
2057 static int action_ping(struct mansession *s, const struct message *m)
2059 const char *actionid = astman_get_header(m, "ActionID");
2060 struct timeval now = ast_tvnow();
2062 astman_append(s, "Response: Success\r\n");
2063 if (!ast_strlen_zero(actionid)){
2064 astman_append(s, "ActionID: %s\r\n", actionid);
2066 astman_append(s, "Ping: Pong\r\nTimestamp:%ld.%06lu\r\n", now.tv_sec, (unsigned long) now.tv_usec);
2070 static int action_getconfig(struct mansession *s, const struct message *m)
2072 struct ast_config *cfg;
2073 const char *fn = astman_get_header(m, "Filename");
2074 const char *category = astman_get_header(m, "Category");
2077 char *cur_category = NULL;
2078 struct ast_variable *v;
2079 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2081 if (ast_strlen_zero(fn)) {
2082 astman_send_error(s, m, "Filename not specified");
2085 cfg = ast_config_load2(fn, "manager", config_flags);
2086 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
2087 astman_send_error(s, m, "Config file not found");
2091 astman_start_ack(s, m);
2092 while ((cur_category = ast_category_browse(cfg, cur_category))) {
2093 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
2095 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
2096 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
2097 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
2102 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2103 astman_append(s, "No categories found\r\n");
2105 ast_config_destroy(cfg);
2106 astman_append(s, "\r\n");
2111 static int action_listcategories(struct mansession *s, const struct message *m)
2113 struct ast_config *cfg;
2114 const char *fn = astman_get_header(m, "Filename");
2115 char *category = NULL;
2116 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2119 if (ast_strlen_zero(fn)) {
2120 astman_send_error(s, m, "Filename not specified");
2123 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2124 astman_send_error(s, m, "Config file not found or file has invalid syntax");
2127 astman_start_ack(s, m);
2128 while ((category = ast_category_browse(cfg, category))) {
2129 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
2132 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2133 astman_append(s, "Error: no categories found\r\n");
2135 ast_config_destroy(cfg);
2136 astman_append(s, "\r\n");
2144 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
2145 static void json_escape(char *out, const char *in)
2148 if (*in == '\\' || *in == '\"') {
2156 static int action_getconfigjson(struct mansession *s, const struct message *m)
2158 struct ast_config *cfg;
2159 const char *fn = astman_get_header(m, "Filename");
2160 char *category = NULL;
2161 struct ast_variable *v;
2164 unsigned int buf_len = 0;
2165 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2167 if (ast_strlen_zero(fn)) {
2168 astman_send_error(s, m, "Filename not specified");
2172 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2173 astman_send_error(s, m, "Config file not found");
2178 buf = alloca(buf_len);
2180 astman_start_ack(s, m);
2181 astman_append(s, "JSON: {");
2182 while ((category = ast_category_browse(cfg, category))) {
2184 if (buf_len < 2 * strlen(category) + 1) {
2186 buf = alloca(buf_len);
2188 json_escape(buf, category);
2189 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
2193 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
2195 astman_append(s, ",");
2197 if (buf_len < 2 * strlen(v->name) + 1) {
2199 buf = alloca(buf_len);
2201 json_escape(buf, v->name);
2202 astman_append(s, "\"%s", buf);
2203 if (buf_len < 2 * strlen(v->value) + 1) {
2205 buf = alloca(buf_len);
2207 json_escape(buf, v->value);
2208 astman_append(s, "%s\"", buf);
2213 astman_append(s, "]");
2215 astman_append(s, "}\r\n\r\n");
2217 ast_config_destroy(cfg);
2222 /* helper function for action_updateconfig */
2223 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
2227 const char *action, *cat, *var, *value, *match, *line;
2228 struct ast_category *category;
2229 struct ast_variable *v;
2230 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
2231 enum error_type result = 0;
2233 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
2234 unsigned int object = 0;
2236 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
2237 action = astman_get_header(m, hdr);
2238 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
2239 break; /* this could cause problems if actions come in misnumbered */
2241 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
2242 cat = astman_get_header(m, hdr);
2243 if (ast_strlen_zero(cat)) { /* every action needs a category */
2244 result = UNSPECIFIED_CATEGORY;
2248 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
2249 var = astman_get_header(m, hdr);
2251 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
2252 value = astman_get_header(m, hdr);
2254 if (!ast_strlen_zero(value) && *value == '>') {
2259 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
2260 match = astman_get_header(m, hdr);
2262 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
2263 line = astman_get_header(m, hdr);
2265 if (!strcasecmp(action, "newcat")) {
2266 if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
2267 result = FAILURE_NEWCAT; /* already exist */
2270 if (!(category = ast_category_new(cat, dfn, -1))) {
2271 result = FAILURE_ALLOCATION;
2274 if (ast_strlen_zero(match)) {
2275 ast_category_append(cfg, category);
2277 ast_category_insert(cfg, category, match);
2279 } else if (!strcasecmp(action, "renamecat")) {
2280 if (ast_strlen_zero(value)) {
2281 result = UNSPECIFIED_ARGUMENT;
2284 if (!(category = ast_category_get(cfg, cat))) {
2285 result = UNKNOWN_CATEGORY;
2288 ast_category_rename(category, value);
2289 } else if (!strcasecmp(action, "delcat")) {
2290 if (ast_category_delete(cfg, cat)) {
2291 result = FAILURE_DELCAT;
2294 } else if (!strcasecmp(action, "emptycat")) {
2295 if (ast_category_empty(cfg, cat)) {
2296 result = FAILURE_EMPTYCAT;
2299 } else if (!strcasecmp(action, "update")) {
2300 if (ast_strlen_zero(var)) {
2301 result = UNSPECIFIED_ARGUMENT;
2304 if (!(category = ast_category_get(cfg,cat))) {
2305 result = UNKNOWN_CATEGORY;
2308 if (ast_variable_update(category, var, value, match, object)) {
2309 result = FAILURE_UPDATE;
2312 } else if (!strcasecmp(action, "delete")) {
2313 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
2314 result = UNSPECIFIED_ARGUMENT;
2317 if (!(category = ast_category_get(cfg, cat))) {
2318 result = UNKNOWN_CATEGORY;
2321 if (ast_variable_delete(category, var, match, line)) {
2322 result = FAILURE_DELETE;
2325 } else if (!strcasecmp(action, "append")) {
2326 if (ast_strlen_zero(var)) {
2327 result = UNSPECIFIED_ARGUMENT;
2330 if (!(category = ast_category_get(cfg, cat))) {
2331 result = UNKNOWN_CATEGORY;
2334 if (!(v = ast_variable_new(var, value, dfn))) {
2335 result = FAILURE_ALLOCATION;
2338 if (object || (match && !strcasecmp(match, "object"))) {
2341 ast_variable_append(category, v);
2342 } else if (!strcasecmp(action, "insert")) {
2343 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
2344 result = UNSPECIFIED_ARGUMENT;
2347 if (!(category = ast_category_get(cfg, cat))) {
2348 result = UNKNOWN_CATEGORY;
2351 if (!(v = ast_variable_new(var, value, dfn))) {
2352 result = FAILURE_ALLOCATION;
2355 ast_variable_insert(category, v, line);
2358 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
2359 result = UNKNOWN_ACTION;
2368 static int action_updateconfig(struct mansession *s, const struct message *m)
2370 struct ast_config *cfg;
2371 const char *sfn = astman_get_header(m, "SrcFilename");
2372 const char *dfn = astman_get_header(m, "DstFilename");
2374 const char *rld = astman_get_header(m, "Reload");
2375 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2376 enum error_type result;
2378 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
2379 astman_send_error(s, m, "Filename not specified");
2382 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
2383 astman_send_error(s, m, "Config file not found");
2386 result = handle_updates(s, m, cfg, dfn);
2388 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
2389 res = ast_config_text_file_save(dfn, cfg, "Manager");
2390 ast_config_destroy(cfg);
2392 astman_send_error(s, m, "Save of config failed");
2395 astman_send_ack(s, m, NULL);
2396 if (!ast_strlen_zero(rld)) {
2397 if (ast_true(rld)) {
2400 ast_module_reload(rld);
2403 ast_config_destroy(cfg);
2405 case UNKNOWN_ACTION:
2406 astman_send_error(s, m, "Unknown action command");
2408 case UNKNOWN_CATEGORY:
2409 astman_send_error(s, m, "Given category does not exist");
2411 case UNSPECIFIED_CATEGORY:
2412 astman_send_error(s, m, "Category not specified");
2414 case UNSPECIFIED_ARGUMENT:
2415 astman_send_error(s, m, "Problem with category, value, or line (if required)");
2417 case FAILURE_ALLOCATION:
2418 astman_send_error(s, m, "Memory allocation failure, this should not happen");
2420 case FAILURE_NEWCAT:
2421 astman_send_error(s, m, "Create category did not complete successfully");
2423 case FAILURE_DELCAT:
2424 astman_send_error(s, m, "Delete category did not complete successfully");
2426 case FAILURE_EMPTYCAT:
2427 astman_send_error(s, m, "Empty category did not complete successfully");
2429 case FAILURE_UPDATE:
2430 astman_send_error(s, m, "Update did not complete successfully");
2432 case FAILURE_DELETE:
2433 astman_send_error(s, m, "Delete did not complete successfully");
2435 case FAILURE_APPEND:
2436 astman_send_error(s, m, "Append did not complete successfully");
2443 static int action_createconfig(struct mansession *s, const struct message *m)
2446 const char *fn = astman_get_header(m, "Filename");
2447 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
2448 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
2449 ast_str_append(&filepath, 0, "%s", fn);
2451 if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
2453 astman_send_ack(s, m, "New configuration file created successfully");
2455 astman_send_error(s, m, strerror(errno));
2461 static int action_waitevent(struct mansession *s, const struct message *m)
2463 const char *timeouts = astman_get_header(m, "Timeout");
2467 const char *id = astman_get_header(m, "ActionID");
2470 if (!ast_strlen_zero(id)) {
2471 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2476 if (!ast_strlen_zero(timeouts)) {
2477 sscanf(timeouts, "%i", &timeout);
2481 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
2485 if (s->session->waiting_thread != AST_PTHREADT_NULL) {
2486 pthread_kill(s->session->waiting_thread, SIGURG);
2489 if (s->session->managerid) { /* AMI-over-HTTP session */
2491 * Make sure the timeout is within the expire time of the session,
2492 * as the client will likely abort the request if it does not see
2493 * data coming after some amount of time.
2495 time_t now = time(NULL);
2496 int max = s->session->sessiontimeout - now - 10;
2498 if (max < 0) { /* We are already late. Strange but possible. */
2501 if (timeout < 0 || timeout > max) {
2504 if (!s->session->send_events) { /* make sure we record events */
2505 s->session->send_events = -1;
2508 mansession_unlock(s);
2510 /* XXX should this go inside the lock ? */
2511 s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
2512 ast_debug(1, "Starting waiting for an event!\n");
2514 for (x = 0; x < timeout || timeout < 0; x++) {
2519 /* We can have multiple HTTP session point to the same mansession entry.
2520 * The way we deal with it is not very nice: newcomers kick out the previous
2521 * HTTP session. XXX this needs to be improved.
2523 if (s->session->waiting_thread != pthread_self()) {
2526 if (s->session->needdestroy) {
2529 mansession_unlock(s);
2533 if (s->session->managerid == 0) { /* AMI session */
2534 if (ast_wait_for_input(s->session->fd, 1000)) {
2537 } else { /* HTTP session */
2541 ast_debug(1, "Finished waiting for an event!\n");
2544 if (s->session->waiting_thread == pthread_self()) {
2545 struct eventqent *eqe;
2546 astman_send_response(s, m, "Success", "Waiting for Event completed.");
2547 while ( (eqe = NEW_EVENT(s)) ) {
2549 if (((s->session->readperm & eqe->category) == eqe->category) &&
2550 ((s->session->send_events & eqe->category) == eqe->category)) {
2551 astman_append(s, "%s", eqe->eventdata);
2553 s->session->last_ev = unref_event(s->session->last_ev);
2556 "Event: WaitEventComplete\r\n"
2559 s->session->waiting_thread = AST_PTHREADT_NULL;
2561 ast_debug(1, "Abandoning event request!\n");
2563 mansession_unlock(s);
2567 /*! \note The actionlock is read-locked by the caller of this function */
2568 static int action_listcommands(struct mansession *s, const struct message *m)
2570 struct manager_action *cur;
2571 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
2573 astman_start_ack(s, m);
2574 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2575 if (s->session->writeperm & cur->authority || cur->authority == 0) {
2576 astman_append(s, "%s: %s (Priv: %s)\r\n",
2577 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
2580 astman_append(s, "\r\n");
2585 static int action_events(struct mansession *s, const struct message *m)
2587 const char *mask = astman_get_header(m, "EventMask");
2590 res = set_eventmask(s, mask);
2592 astman_append(s, "Response: Success\r\n"
2595 astman_append(s, "Response: Success\r\n"
2600 static int action_logoff(struct mansession *s, const struct message *m)
2602 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
2606 static int action_login(struct mansession *s, const struct message *m)
2609 /* still authenticated - don't process again */
2610 if (s->session->authenticated) {
2611 astman_send_ack(s, m, "Already authenticated");
2615 if (authenticate(s, m)) {
2617 astman_send_error(s, m, "Authentication failed");
2620 s->session->authenticated = 1;
2621 if (manager_displayconnects(s->session)) {
2622 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));
2624 astman_send_ack(s, m, "Authentication accepted");
2628 static int action_challenge(struct mansession *s, const struct message *m)
2630 const char *authtype = astman_get_header(m, "AuthType");
2632 if (!strcasecmp(authtype, "MD5")) {
2633 if (ast_strlen_zero(s->session->challenge)) {
2634 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
2637 astman_start_ack(s, m);
2638 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
2639 mansession_unlock(s);
2641 astman_send_error(s, m, "Must specify AuthType");
2646 static int action_hangup(struct mansession *s, const struct message *m)
2648 struct ast_channel *c = NULL;
2649 int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
2650 const char *name = astman_get_header(m, "Channel");
2651 const char *cause = astman_get_header(m, "Cause");
2653 if (ast_strlen_zero(name)) {
2654 astman_send_error(s, m, "No channel specified");
2658 if (!ast_strlen_zero(cause)) {
2660 causecode = strtol(cause, &endptr, 10);
2661 if (causecode < 0 || causecode > 127 || *endptr != '\0') {
2662 ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
2663 /* keep going, better to hangup without cause than to not hang up at all */
2664 causecode = 0; /* do not set channel's hangupcause */
2668 if (!(c = ast_channel_get_by_name(name))) {
2669 astman_send_error(s, m, "No such channel");
2673 ast_channel_lock(c);
2674 if (causecode > 0) {
2675 ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
2676 c->name, causecode, c->hangupcause);
2677 c->hangupcause = causecode;
2679 ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT);
2680 ast_channel_unlock(c);
2682 c = ast_channel_unref(c);
2684 astman_send_ack(s, m, "Channel Hungup");
2689 static int action_setvar(struct mansession *s, const struct message *m)
2691 struct ast_channel *c = NULL;
2692 const char *name = astman_get_header(m, "Channel");
2693 const char *varname = astman_get_header(m, "Variable");
2694 const char *varval = astman_get_header(m, "Value");
2696 if (ast_strlen_zero(varname)) {
2697 astman_send_error(s, m, "No variable specified");
2701 if (!ast_strlen_zero(name)) {
2702 if (!(c = ast_channel_get_by_name(name))) {
2703 astman_send_error(s, m, "No such channel");
2708 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
2711 c = ast_channel_unref(c);
2714 astman_send_ack(s, m, "Variable Set");
2719 static int action_getvar(struct mansession *s, const struct message *m)
2721 struct ast_channel *c = NULL;
2722 const char *name = astman_get_header(m, "Channel");
2723 const char *varname = astman_get_header(m, "Variable");
2725 char workspace[1024] = "";
2727 if (ast_strlen_zero(varname)) {
2728 astman_send_error(s, m, "No variable specified");
2732 if (!ast_strlen_zero(name)) {
2733 if (!(c = ast_channel_get_by_name(name))) {
2734 astman_send_error(s, m, "No such channel");
2739 if (varname[strlen(varname) - 1] == ')') {
2741 c = ast_dummy_channel_alloc();
2743 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
2744 c = ast_channel_release(c);
2746 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
2748 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
2752 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
2756 c = ast_channel_unref(c);
2759 astman_start_ack(s, m);
2760 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
2765 /*! \brief Manager "status" command to show channels */
2766 /* Needs documentation... */
2767 static int action_status(struct mansession *s, const struct message *m)
2769 const char *name = astman_get_header(m, "Channel");
2770 const char *cvariables = astman_get_header(m, "Variables");
2771 char *variables = ast_strdupa(S_OR(cvariables, ""));
2772 struct ast_channel *c;
2774 struct timeval now = ast_tvnow();
2775 long elapsed_seconds = 0;
2777 int all = ast_strlen_zero(name); /* set if we want all channels */
2778 const char *id = astman_get_header(m, "ActionID");
2780 AST_DECLARE_APP_ARGS(vars,
2781 AST_APP_ARG(name)[100];
2783 struct ast_str *str = ast_str_create(1000);
2784 struct ast_channel_iterator *iter = NULL;
2786 if (!ast_strlen_zero(id)) {
2787 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2793 if (!(iter = ast_channel_iterator_all_new(0))) {
2795 astman_send_error(s, m, "Memory Allocation Failure");
2798 c = ast_channel_iterator_next(iter);
2800 if (!(c = ast_channel_get_by_name(name))) {
2801 astman_send_error(s, m, "No such channel");
2807 astman_send_ack(s, m, "Channel status will follow");
2809 if (!ast_strlen_zero(cvariables)) {
2810 AST_STANDARD_APP_ARGS(vars, variables);
2813 /* if we look by name, we break after the first iteration */
2814 for (; c; c = ast_channel_iterator_next(iter)) {
2815 ast_channel_lock(c);
2817 if (!ast_strlen_zero(cvariables)) {
2820 for (i = 0; i < vars.argc; i++) {
2821 char valbuf[512], *ret = NULL;
2823 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
2824 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
2829 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
2832 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
2838 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
2844 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
2848 "Privilege: Call\r\n"
2850 "CallerIDNum: %s\r\n"
2851 "CallerIDName: %s\r\n"
2852 "Accountcode: %s\r\n"
2853 "ChannelState: %d\r\n"
2854 "ChannelStateDesc: %s\r\n"
2865 S_OR(c->cid.cid_num, ""),
2866 S_OR(c->cid.cid_name, ""),
2869 ast_state2str(c->_state), c->context,
2870 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, ast_str_buffer(str), idText);
2874 "Privilege: Call\r\n"
2876 "CallerIDNum: %s\r\n"
2877 "CallerIDName: %s\r\n"
2886 S_OR(c->cid.cid_num, "<unknown>"),
2887 S_OR(c->cid.cid_name, "<unknown>"),
2889 ast_state2str(c->_state), bridge, c->uniqueid,
2890 ast_str_buffer(str), idText);
2893 ast_channel_unlock(c);
2894 c = ast_channel_unref(c);
2902 "Event: StatusComplete\r\n"
2905 "\r\n", idText, channels);
2912 static int action_sendtext(struct mansession *s, const struct message *m)
2914 struct ast_channel *c = NULL;
2915 const char *name = astman_get_header(m, "Channel");
2916 const char *textmsg = astman_get_header(m, "Message");
2919 if (ast_strlen_zero(name)) {
2920 astman_send_error(s, m, "No channel specified");
2924 if (ast_strlen_zero(textmsg)) {
2925 astman_send_error(s, m, "No Message specified");
2929 if (!(c = ast_channel_get_by_name(name))) {
2930 astman_send_error(s, m, "No such channel");
2934 ast_channel_lock(c);
2935 res = ast_sendtext(c, textmsg);
2936 ast_channel_unlock(c);
2937 c = ast_channel_unref(c);
2940 astman_send_ack(s, m, "Success");
2942 astman_send_error(s, m, "Failure");
2948 /*! \brief action_redirect: The redirect manager command */
2949 static int action_redirect(struct mansession *s, const struct message *m)
2951 const char *name = astman_get_header(m, "Channel");
2952 const char *name2 = astman_get_header(m, "ExtraChannel");
2953 const char *exten = astman_get_header(m, "Exten");
2954 const char *context = astman_get_header(m, "Context");
2955 const char *priority = astman_get_header(m, "Priority");
2956 struct ast_channel *chan, *chan2 = NULL;
2960 if (ast_strlen_zero(name)) {
2961 astman_send_error(s, m, "Channel not specified");
2965 if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
2966 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
2967 astman_send_error(s, m, "Invalid priority");
2972 if (!(chan = ast_channel_get_by_name(name))) {
2974 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
2975 astman_send_error(s, m, buf);
2979 if (ast_check_hangup_locked(chan)) {
2980 astman_send_error(s, m, "Redirect failed, channel not up.");
2981 chan = ast_channel_unref(chan);
2985 if (!ast_strlen_zero(name2)) {
2986 chan2 = ast_channel_get_by_name(name2);
2989 if (chan2 && ast_check_hangup_locked(chan2)) {
2990 astman_send_error(s, m, "Redirect failed, extra channel not up.");
2991 chan = ast_channel_unref(chan);
2992 chan2 = ast_channel_unref(chan2);
2997 ast_channel_lock(chan);
2998 ast_set_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
2999 ast_channel_unlock(chan);
3002 res = ast_async_goto(chan, context, exten, pi);
3004 if (!ast_strlen_zero(name2)) {
3007 ast_channel_lock(chan2);
3008 ast_set_flag(chan2, AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
3009 ast_channel_unlock(chan2);
3011 res = ast_async_goto(chan2, context, exten, pi);
3016 astman_send_ack(s, m, "Dual Redirect successful");
3018 astman_send_error(s, m, "Secondary redirect failed");
3021 astman_send_ack(s, m, "Redirect successful");
3024 astman_send_error(s, m, "Redirect failed");
3028 chan = ast_channel_unref(chan);
3032 chan2 = ast_channel_unref(chan2);
3038 static int action_atxfer(struct mansession *s, const struct message *m)
3040 const char *name = astman_get_header(m, "Channel");
3041 const char *exten = astman_get_header(m, "Exten");
3042 const char *context = astman_get_header(m, "Context");
3043 struct ast_channel *chan = NULL;
3044 struct ast_call_feature *atxfer_feature = NULL;
3045 char *feature_code = NULL;
3047 if (ast_strlen_zero(name)) {
3048 astman_send_error(s, m, "No channel specified");
3051 if (ast_strlen_zero(exten)) {
3052 astman_send_error(s, m, "No extension specified");
3056 if (!(atxfer_feature = ast_find_call_feature("atxfer"))) {
3057 astman_send_error(s, m, "No attended transfer feature found");
3061 if (!(chan = ast_channel_get_by_name(name))) {
3062 astman_send_error(s, m, "Channel specified does not exist");
3066 if (!ast_strlen_zero(context)) {
3067 pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
3070 for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
3071 struct ast_frame f = { AST_FRAME_DTMF, *feature_code };
3072 ast_queue_frame(chan, &f);
3075 for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
3076 struct ast_frame f = { AST_FRAME_DTMF, *feature_code };
3077 ast_queue_frame(chan, &f);
3080 chan = ast_channel_unref(chan);
3082 astman_send_ack(s, m, "Atxfer successfully queued");
3087 static int check_blacklist(const char *cmd)
3089 char *cmd_copy, *cur_cmd;
3090 char *cmd_words[MAX_BLACKLIST_CMD_LEN] = { NULL, };
3093 cmd_copy = ast_strdupa(cmd);
3094 for (i = 0; i < MAX_BLACKLIST_CMD_LEN && (cur_cmd = strsep(&cmd_copy, " ")); i++) {
3095 cur_cmd = ast_strip(cur_cmd);