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/term.h"
74 #include "asterisk/astobj2.h"
75 #include "asterisk/features.h"
76 #include "asterisk/security_events.h"
79 <manager name="Ping" language="en_US">
84 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
87 <para>A 'Ping' action will ellicit a 'Pong' response. Used to keep the
88 manager connection open.</para>
91 <manager name="Events" language="en_US">
96 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
97 <parameter name="EventMask" required="true">
100 <para>If all events should be sent.</para>
103 <para>If no events should be sent.</para>
105 <enum name="system,call,log,...">
106 <para>To select which flags events should have to be sent.</para>
112 <para>Enable/Disable sending of events to this manager client.</para>
115 <manager name="Logoff" language="en_US">
120 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
123 <para>Logoff the current manager session.</para>
126 <manager name="Login" language="en_US">
131 <parameter name="ActionID">
132 <para>ActionID for this transaction. Will be returned.</para>
136 <para>Login Manager.</para>
139 <manager name="Challenge" language="en_US">
141 Generate Challenge for MD5 Auth.
144 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
147 <para>Generate a challenge for MD5 authentication.</para>
150 <manager name="Hangup" language="en_US">
155 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
156 <parameter name="Channel" required="true">
157 <para>The channel name to be hangup.</para>
159 <parameter name="Cause">
160 <para>Numeric hangup cause.</para>
164 <para>Hangup a channel.</para>
167 <manager name="Status" language="en_US">
172 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
173 <parameter name="Channel" required="true">
174 <para>The name of the channel to query for status.</para>
176 <parameter name="Variables">
177 <para>Comma <literal>,</literal> separated list of variable to include.</para>
181 <para>Will return the status information of each channel along with the
182 value for the specified channel variables.</para>
185 <manager name="Setvar" language="en_US">
187 Set a channel variable.
190 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
191 <parameter name="Channel">
192 <para>Channel to set variable for.</para>
194 <parameter name="Variable" required="true">
195 <para>Variable name.</para>
197 <parameter name="Value" required="true">
198 <para>Variable value.</para>
202 <para>Set a global or local channel variable.</para>
205 <manager name="Getvar" language="en_US">
207 Gets a channel variable.
210 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
211 <parameter name="Channel">
212 <para>Channel to read variable from.</para>
214 <parameter name="Variable" required="true">
215 <para>Variable name.</para>
219 <para>Get the value of a global or local channel variable.</para>
222 <manager name="GetConfig" language="en_US">
224 Retrieve configuration.
227 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
228 <parameter name="Filename" required="true">
229 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
231 <parameter name="Category">
232 <para>Category in configuration file.</para>
236 <para>This action will dump the contents of a configuration
237 file by category and contents or optionally by specified category only.</para>
240 <manager name="GetConfigJSON" language="en_US">
242 Retrieve configuration (JSON format).
245 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
246 <parameter name="Filename" required="true">
247 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
251 <para>This action will dump the contents of a configuration file by category
252 and contents in JSON format. This only makes sense to be used using rawman over
253 the HTTP interface.</para>
256 <manager name="UpdateConfig" language="en_US">
258 Update basic configuration.
261 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
262 <parameter name="SrcFilename" required="true">
263 <para>Configuration filename to read (e.g. <filename>foo.conf</filename>).</para>
265 <parameter name="DstFilename" required="true">
266 <para>Configuration filename to write (e.g. <filename>foo.conf</filename>)</para>
268 <parameter name="Reload">
269 <para>Whether or not a reload should take place (or name of specific module).</para>
271 <parameter name="Action-XXXXXX">
272 <para>Action to take.</para>
273 <para>X's represent 6 digit number beginning with 000000.</para>
275 <enum name="NewCat" />
276 <enum name="RenameCat" />
277 <enum name="DelCat" />
278 <enum name="EmptyCat" />
279 <enum name="Update" />
280 <enum name="Delete" />
281 <enum name="Append" />
282 <enum name="Insert" />
285 <parameter name="Cat-XXXXXX">
286 <para>Category to operate on.</para>
287 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
289 <parameter name="Var-XXXXXX">
290 <para>Variable to work on.</para>
291 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
293 <parameter name="Value-XXXXXX">
294 <para>Value to work on.</para>
295 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
297 <parameter name="Match-XXXXXX">
298 <para>Extra match required to match line.</para>
299 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
301 <parameter name="Line-XXXXXX">
302 <para>Line in category to operate on (used with delete and insert actions).</para>
303 <xi:include xpointer="xpointer(/docs/manager[@name='UpdateConfig']/syntax/parameter[@name='Action-XXXXXX']/para[2])" />
307 <para>This action will modify, create, or delete configuration elements
308 in Asterisk configuration files.</para>
311 <manager name="CreateConfig" language="en_US">
313 Creates an empty file in the configuration directory.
316 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
317 <parameter name="Filename" required="true">
318 <para>The configuration filename to create (e.g. <filename>foo.conf</filename>).</para>
322 <para>This action will create an empty file in the configuration
323 directory. This action is intended to be used before an UpdateConfig
327 <manager name="ListCategories" language="en_US">
329 List categories in configuration file.
332 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
333 <parameter name="Filename" required="true">
334 <para>Configuration filename (e.g. <filename>foo.conf</filename>).</para>
338 <para>This action will dump the categories in a given file.</para>
341 <manager name="Redirect" language="en_US">
343 Redirect (transfer) a call.
346 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
347 <parameter name="Channel" required="true">
348 <para>Channel to redirect.</para>
350 <parameter name="ExtraChannel">
351 <para>Second call leg to transfer (optional).</para>
353 <parameter name="Exten" required="true">
354 <para>Extension to transfer to.</para>
356 <parameter name="Context" required="true">
357 <para>Context to transfer to.</para>
359 <parameter name="Priority" required="true">
360 <para>Priority to transfer to.</para>
364 <para>Redirect (transfer) a call.</para>
367 <manager name="Atxfer" language="en_US">
372 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
373 <parameter name="Channel" required="true">
374 <para>Transferer's channel.</para>
376 <parameter name="Exten" required="true">
377 <para>Extension to transfer to.</para>
379 <parameter name="Context" required="true">
380 <para>Context to transfer to.</para>
382 <parameter name="Priority" required="true">
383 <para>Priority to transfer to.</para>
387 <para>Attended transfer.</para>
390 <manager name="Originate" language="en_US">
395 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
396 <parameter name="Channel" required="true">
397 <para>Channel name to call.</para>
399 <parameter name="Exten">
400 <para>Extension to use (requires <literal>Context</literal> and
401 <literal>Priority</literal>)</para>
403 <parameter name="Context">
404 <para>Context to use (requires <literal>Exten</literal> and
405 <literal>Priority</literal>)</para>
407 <parameter name="Priority">
408 <para>Priority to use (requires <literal>Exten</literal> and
409 <literal>Context</literal>)</para>
411 <parameter name="Application">
412 <para>Application to execute.</para>
414 <parameter name="Data">
415 <para>Data to use (requires <literal>Application</literal>).</para>
417 <parameter name="Timeout" default="30000">
418 <para>How long to wait for call to be answered (in ms.).</para>
420 <parameter name="CallerID">
421 <para>Caller ID to be set on the outgoing channel.</para>
423 <parameter name="Variable">
424 <para>Channel variable to set, multiple Variable: headers are allowed.</para>
426 <parameter name="Account">
427 <para>Account code.</para>
429 <parameter name="Async">
430 <para>Set to <literal>true</literal> for fast origination.</para>
434 <para>Generates an outgoing call to a
435 <replaceable>Extension</replaceable>/<replaceable>Context</replaceable>/<replaceable>Priority</replaceable>
436 or <replaceable>Application</replaceable>/<replaceable>Data</replaceable></para>
439 <manager name="Command" language="en_US">
441 Execute Asterisk CLI Command.
444 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
445 <parameter name="Command" required="true">
446 <para>Asterisk CLI command to run.</para>
450 <para>Run a CLI command.</para>
453 <manager name="ExtensionState" language="en_US">
455 Check Extension Status.
458 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
459 <parameter name="Exten" required="true">
460 <para>Extension to check state on.</para>
462 <parameter name="Context" required="true">
463 <para>Context for extension.</para>
467 <para>Report the extension state for given extension. If the extension has a hint,
468 will use devicestate to check the status of the device connected to the extension.</para>
469 <para>Will return an <literal>Extension Status</literal> message. The response will include
470 the hint for the extension and the status.</para>
473 <manager name="AbsoluteTimeout" language="en_US">
475 Set absolute timeout.
478 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
479 <parameter name="Channel" required="true">
480 <para>Channel name to hangup.</para>
482 <parameter name="Timeout" required="true">
483 <para>Maximum duration of the call (sec).</para>
487 <para>Hangup a channel after a certain time. Acknowledges set time with
488 <literal>Timeout Set</literal> message.</para>
491 <manager name="MailboxStatus" language="en_US">
496 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
497 <parameter name="Mailbox" required="true">
498 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
502 <para>Checks a voicemail account for status.</para>
503 <para>Returns number of messages.</para>
504 <para>Message: Mailbox Status.</para>
505 <para>Mailbox: <replaceable>mailboxid</replaceable>.</para>
506 <para>Waiting: <replaceable>count</replaceable>.</para>
509 <manager name="MailboxCount" language="en_US">
511 Check Mailbox Message Count.
514 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
515 <parameter name="Mailbox" required="true">
516 <para>Full mailbox ID <replaceable>mailbox</replaceable>@<replaceable>vm-context</replaceable>.</para>
520 <para>Checks a voicemail account for new messages.</para>
521 <para>Returns number of urgent, new and old messages.</para>
522 <para>Message: Mailbox Message Count</para>
523 <para>Mailbox: <replaceable>mailboxid</replaceable></para>
524 <para>UrgentMessages: <replaceable>count</replaceable></para>
525 <para>NewMessages: <replaceable>count</replaceable></para>
526 <para>OldMessages: <replaceable>count</replaceable></para>
529 <manager name="ListCommands" language="en_US">
531 List available manager commands.
534 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
537 <para>Returns the action name and synopsis for every action that
538 is available to the user.</para>
541 <manager name="SendText" language="en_US">
543 Send text message to channel.
546 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
547 <parameter name="Channel" required="true">
548 <para>Channel to send message to.</para>
550 <parameter name="Message" required="true">
551 <para>Message to send.</para>
555 <para>Sends A Text Message to a channel while in a call.</para>
558 <manager name="UserEvent" language="en_US">
560 Send an arbitrary event.
563 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
564 <parameter name="UserEvent" required="true">
565 <para>Event string to send.</para>
567 <parameter name="Header1">
568 <para>Content1.</para>
570 <parameter name="HeaderN">
571 <para>ContentN.</para>
575 <para>Send an event to manager sessions.</para>
578 <manager name="WaitEvent" language="en_US">
580 Wait for an event to occur.
583 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
584 <parameter name="Timeout" required="true">
585 <para>Maximum time (in seconds) to wait for events, <literal>-1</literal> means forever.</para>
589 <para>This action will ellicit a <literal>Success</literal> response. Whenever
590 a manager event is queued. Once WaitEvent has been called on an HTTP manager
591 session, events will be generated and queued.</para>
594 <manager name="CoreSettings" language="en_US">
596 Show PBX core settings (version etc).
599 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
602 <para>Query for Core PBX settings.</para>
605 <manager name="CoreStatus" language="en_US">
607 Show PBX core status variables.
610 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
613 <para>Query for Core PBX status.</para>
616 <manager name="Reload" language="en_US">
621 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
622 <parameter name="Module">
623 <para>Name of the module to reload.</para>
627 <para>Send a reload event.</para>
630 <manager name="CoreShowChannels" language="en_US">
632 List currently active channels.
635 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
638 <para>List currently defined channels and some information about them.</para>
641 <manager name="ModuleLoad" language="en_US">
646 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
647 <parameter name="Module">
648 <para>Asterisk module name (including .so extension) or subsystem identifier:</para>
652 <enum name="dnsmgr" />
653 <enum name="extconfig" />
654 <enum name="manager" />
659 <parameter name="LoadType" required="true">
660 <para>The operation to be done on module.</para>
663 <enum name="unload" />
664 <enum name="reload" />
666 <para>If no module is specified for a <literal>reload</literal> loadtype,
667 all modules are reloaded.</para>
671 <para>Loads, unloads or reloads an Asterisk module in a running system.</para>
674 <manager name="ModuleCheck" language="en_US">
676 Check if module is loaded.
679 <parameter name="Module" required="true">
680 <para>Asterisk module name (not including extension).</para>
684 <para>Checks if Asterisk module is loaded. Will return Success/Failure.
685 For success returns, the module revision number is included.</para>
693 UNSPECIFIED_CATEGORY,
694 UNSPECIFIED_ARGUMENT,
706 * Linked list of events.
707 * Global events are appended to the list by append_event().
708 * The usecount is the number of stored pointers to the element,
709 * excluding the list pointers. So an element that is only in
710 * the list has a usecount of 0, not 1.
712 * Clients have a pointer to the last event processed, and for each
713 * of these clients we track the usecount of the elements.
714 * If we have a pointer to an entry in the list, it is safe to navigate
715 * it forward because elements will not be deleted, but only appended.
716 * The worst that can happen is seeing the pointer still NULL.
718 * When the usecount of an element drops to 0, and the element is the
719 * first in the list, we can remove it. Removal is done within the
720 * main thread, which is woken up for the purpose.
722 * For simplicity of implementation, we make sure the list is never empty.
725 int usecount; /*!< # of clients who still need the event */
727 unsigned int seq; /*!< sequence number */
728 AST_LIST_ENTRY(eventqent) eq_next;
729 char eventdata[1]; /*!< really variable size, allocated by append_event() */
732 static AST_LIST_HEAD_STATIC(all_events, eventqent);
734 static int displayconnects = 1;
735 static int allowmultiplelogin = 1;
736 static int timestampevents;
737 static int httptimeout = 60;
738 static int manager_enabled = 0;
739 static int webmanager_enabled = 0;
741 #define DEFAULT_REALM "asterisk"
742 static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */
744 static int block_sockets;
746 static int manager_debug; /*!< enable some debugging code in the manager */
749 * Descriptor for a manager session, either on the AMI socket or over HTTP.
752 * AMI session have managerid == 0; the entry is created upon a connect,
753 * and destroyed with the socket.
754 * HTTP sessions have managerid != 0, the value is used as a search key
755 * to lookup sessions (using the mansession_id cookie, or nonce key from
756 * Digest Authentication http header).
758 #define MAX_BLACKLIST_CMD_LEN 2
759 static const struct {
760 const char *words[AST_MAX_CMD_LEN];
761 } command_blacklist[] = {
762 {{ "module", "load", NULL }},
763 {{ "module", "unload", NULL }},
764 {{ "restart", "gracefully", NULL }},
767 /* In order to understand what the heck is going on with the
768 * mansession_session and mansession structs, we need to have a bit of a history
771 * In the beginning, there was the mansession. The mansession contained data that was
772 * intrinsic to a manager session, such as the time that it started, the name of the logged-in
773 * user, etc. In addition to these parameters were the f and fd parameters. For typical manager
774 * sessions, these were used to represent the TCP socket over which the AMI session was taking
775 * place. It makes perfect sense for these fields to be a part of the session-specific data since
776 * the session actually defines this information.
778 * Then came the HTTP AMI sessions. With these, the f and fd fields need to be opened and closed
779 * for every single action that occurs. Thus the f and fd fields aren't really specific to the session
780 * but rather to the action that is being executed. Because a single session may execute many commands
781 * at once, some sort of safety needed to be added in order to be sure that we did not end up with fd
782 * leaks from one action overwriting the f and fd fields used by a previous action before the previous action
783 * has had a chance to properly close its handles.
785 * The initial idea to solve this was to use thread synchronization, but this prevented multiple actions
786 * from being run at the same time in a single session. Some manager actions may block for a long time, thus
787 * creating a large queue of actions to execute. In addition, this fix did not address the basic architectural
788 * issue that for HTTP manager sessions, the f and fd variables are not really a part of the session, but are
789 * part of the action instead.
791 * The new idea was to create a structure on the stack for each HTTP Manager action. This structure would
792 * contain the action-specific information, such as which file to write to. In order to maintain expectations
793 * of action handlers and not have to change the public API of the manager code, we would need to name this
794 * new stacked structure 'mansession' and contain within it the old mansession struct that we used to use.
795 * We renamed the old mansession struct 'mansession_session' to hopefully convey that what is in this structure
796 * is session-specific data. The structure that it is wrapped in, called a 'mansession' really contains action-specific
799 struct mansession_session {
800 pthread_t ms_t; /*!< Execution thread, basically useless */
801 /* XXX need to document which fields it is protecting */
802 struct sockaddr_in sin; /*!< address we are connecting from */
803 FILE *f; /*!< fdopen() on the underlying fd */
804 int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
805 int inuse; /*!< number of HTTP sessions using this entry */
806 int needdestroy; /*!< Whether an HTTP session should be destroyed */
807 pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
808 uint32_t managerid; /*!< Unique manager identifier, 0 for AMI sessions */
809 time_t sessionstart; /*!< Session start time */
810 struct timeval sessionstart_tv; /*!< Session start time */
811 time_t sessiontimeout; /*!< Session timeout if HTTP */
812 char username[80]; /*!< Logged in username */
813 char challenge[10]; /*!< Authentication challenge */
814 int authenticated; /*!< Authentication status */
815 int readperm; /*!< Authorization for reading */
816 int writeperm; /*!< Authorization for writing */
817 char inbuf[1025]; /*!< Buffer */
818 /* we use the extra byte to add a '\0' and simplify parsing */
819 int inlen; /*!< number of buffered bytes */
820 int send_events; /*!< XXX what ? */
821 struct eventqent *last_ev; /*!< last event processed. */
822 int writetimeout; /*!< Timeout for ast_carefulwrite() */
823 int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */
824 time_t noncetime; /*!< Timer for nonce value expiration */
825 unsigned long oldnonce; /*!< Stale nonce value */
826 unsigned long nc; /*!< incremental nonce counter */
827 AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */
828 AST_LIST_ENTRY(mansession_session) list;
831 /* In case you didn't read that giant block of text above the mansession_session struct, the
832 * 'mansession' struct is named this solely to keep the API the same in Asterisk. This structure really
833 * represents data that is different from Manager action to Manager action. The mansession_session pointer
834 * contained within points to session-specific data.
837 struct mansession_session *session;
838 struct ast_tcptls_session_instance *tcptls_session;
841 struct manager_custom_hook *hook;
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)) {
1155 /*! \brief Get displayconnects config option.
1156 * \param session manager session to get parameter from.
1157 * \return displayconnects config option value.
1159 static int manager_displayconnects (struct mansession_session *session)
1161 struct ast_manager_user *user = NULL;
1164 AST_RWLIST_RDLOCK(&users);
1165 if ((user = get_manager_by_name_locked (session->username))) {
1166 ret = user->displayconnects;
1168 AST_RWLIST_UNLOCK(&users);
1173 static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1175 struct manager_action *cur;
1176 struct ast_str *authority;
1180 char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64];
1185 e->command = "manager show command";
1187 "Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
1188 " Shows the detailed description for a specific Asterisk manager interface command.\n";
1191 l = strlen(a->word);
1193 AST_RWLIST_RDLOCK(&actions);
1194 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1195 if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
1196 ret = ast_strdup(cur->action);
1197 break; /* make sure we exit even if ast_strdup() returns NULL */
1200 AST_RWLIST_UNLOCK(&actions);
1203 authority = ast_str_alloca(80);
1205 return CLI_SHOWUSAGE;
1209 /* setup the titles */
1210 term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
1211 term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
1212 term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
1213 term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
1214 term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
1217 AST_RWLIST_RDLOCK(&actions);
1218 AST_RWLIST_TRAVERSE(&actions, cur, list) {
1219 for (num = 3; num < a->argc; num++) {
1220 if (!strcasecmp(cur->action, a->argv[num])) {
1222 if (cur->docsrc == AST_XML_DOC) {
1223 ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n",
1225 ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1),
1227 ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1),
1229 ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1),
1231 ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1),
1233 ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1));
1236 ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
1237 cur->action, cur->synopsis,
1238 authority_to_str(cur->authority, &authority),
1239 S_OR(cur->description, ""));
1246 AST_RWLIST_UNLOCK(&actions);
1251 static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1255 e->command = "manager set debug [on|off]";
1256 e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
1263 ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
1264 } else if (a->argc == 4) {
1265 if (!strcasecmp(a->argv[3], "on")) {
1267 } else if (!strcasecmp(a->argv[3], "off")) {
1270 return CLI_SHOWUSAGE;
1276 static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1278 struct ast_manager_user *user = NULL;
1281 struct ast_str *rauthority = ast_str_alloca(128);
1282 struct ast_str *wauthority = ast_str_alloca(128);
1286 e->command = "manager show user";
1288 " Usage: manager show user <user>\n"
1289 " Display all information related to the manager user specified.\n";
1292 l = strlen(a->word);
1297 AST_RWLIST_RDLOCK(&users);
1298 AST_RWLIST_TRAVERSE(&users, user, list) {
1299 if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
1300 ret = ast_strdup(user->username);
1304 AST_RWLIST_UNLOCK(&users);
1309 return CLI_SHOWUSAGE;
1312 AST_RWLIST_RDLOCK(&users);
1314 if (!(user = get_manager_by_name_locked(a->argv[3]))) {
1315 ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
1316 AST_RWLIST_UNLOCK(&users);
1320 ast_cli(a->fd, "\n");
1327 "displayconnects: %s\n",
1328 (user->username ? user->username : "(N/A)"),
1329 (user->secret ? "<Set>" : "(N/A)"),
1330 (user->ha ? "yes" : "no"),
1331 authority_to_str(user->readperm, &rauthority),
1332 authority_to_str(user->writeperm, &wauthority),
1333 (user->displayconnects ? "yes" : "no"));
1335 AST_RWLIST_UNLOCK(&users);
1341 static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1343 struct ast_manager_user *user = NULL;
1347 e->command = "manager show users";
1349 "Usage: manager show users\n"
1350 " Prints a listing of all managers that are currently configured on that\n"
1357 return CLI_SHOWUSAGE;
1360 AST_RWLIST_RDLOCK(&users);
1362 /* If there are no users, print out something along those lines */
1363 if (AST_RWLIST_EMPTY(&users)) {
1364 ast_cli(a->fd, "There are no manager users.\n");
1365 AST_RWLIST_UNLOCK(&users);
1369 ast_cli(a->fd, "\nusername\n--------\n");
1371 AST_RWLIST_TRAVERSE(&users, user, list) {
1372 ast_cli(a->fd, "%s\n", user->username);
1376 AST_RWLIST_UNLOCK(&users);
1378 ast_cli(a->fd,"-------------------\n"
1379 "%d manager users configured.\n", count_amu);
1384 /*! \brief CLI command manager list commands */
1385 static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1387 struct manager_action *cur;
1388 struct ast_str *authority;
1389 #define HSMC_FORMAT " %-15.15s %-15.15s %-55.55s\n"
1392 e->command = "manager show commands";
1394 "Usage: manager show commands\n"
1395 " Prints a listing of all the available Asterisk manager interface commands.\n";
1400 authority = ast_str_alloca(80);
1401 ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis");
1402 ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------");
1404 AST_RWLIST_RDLOCK(&actions);
1405 AST_RWLIST_TRAVERSE(&actions, cur, list)
1406 ast_cli(a->fd, HSMC_FORMAT, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
1407 AST_RWLIST_UNLOCK(&actions);
1412 /*! \brief CLI command manager list connected */
1413 static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1415 struct mansession_session *session;
1416 time_t now = time(NULL);
1417 #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
1418 #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
1420 struct ao2_iterator i;
1424 e->command = "manager show connected";
1426 "Usage: manager show connected\n"
1427 " Prints a listing of the users that are currently connected to the\n"
1428 "Asterisk manager interface.\n";
1434 ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
1436 i = ao2_iterator_init(sessions, 0);
1437 while ((session = ao2_iterator_next(&i))) {
1439 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);
1441 ao2_unlock(session);
1442 unref_mansession(session);
1444 ao2_iterator_destroy(&i);
1445 ast_cli(a->fd, "%d users connected.\n", count);
1450 /*! \brief CLI command manager list eventq */
1451 /* Should change to "manager show connected" */
1452 static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1454 struct eventqent *s;
1457 e->command = "manager show eventq";
1459 "Usage: manager show eventq\n"
1460 " Prints a listing of all events pending in the Asterisk manger\n"
1466 AST_LIST_LOCK(&all_events);
1467 AST_LIST_TRAVERSE(&all_events, s, eq_next) {
1468 ast_cli(a->fd, "Usecount: %d\n", s->usecount);
1469 ast_cli(a->fd, "Category: %d\n", s->category);
1470 ast_cli(a->fd, "Event:\n%s", s->eventdata);
1472 AST_LIST_UNLOCK(&all_events);
1477 /*! \brief CLI command manager reload */
1478 static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1482 e->command = "manager reload";
1484 "Usage: manager reload\n"
1485 " Reloads the manager configuration.\n";
1491 return CLI_SHOWUSAGE;
1498 static struct ast_cli_entry cli_manager[] = {
1499 AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
1500 AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
1501 AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
1502 AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
1503 AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
1504 AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
1505 AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
1506 AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
1509 static struct eventqent *unref_event(struct eventqent *e)
1511 ast_atomic_fetchadd_int(&e->usecount, -1);
1512 return AST_LIST_NEXT(e, eq_next);
1515 static void ref_event(struct eventqent *e)
1517 ast_atomic_fetchadd_int(&e->usecount, 1);
1521 * Generic function to return either the first or the last matching header
1522 * from a list of variables, possibly skipping empty strings.
1523 * At the moment there is only one use of this function in this file,
1524 * so we make it static.
1526 #define GET_HEADER_FIRST_MATCH 0
1527 #define GET_HEADER_LAST_MATCH 1
1528 #define GET_HEADER_SKIP_EMPTY 2
1529 static const char *__astman_get_header(const struct message *m, char *var, int mode)
1531 int x, l = strlen(var);
1532 const char *result = "";
1534 for (x = 0; x < m->hdrcount; x++) {
1535 const char *h = m->headers[x];
1536 if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ') {
1537 const char *value = h + l + 2;
1538 /* found a potential candidate */
1539 if (mode & GET_HEADER_SKIP_EMPTY && ast_strlen_zero(value))
1540 continue; /* not interesting */
1541 if (mode & GET_HEADER_LAST_MATCH)
1542 result = value; /* record the last match so far */
1552 * Return the first matching variable from an array.
1553 * This is the legacy function and is implemented in therms of
1554 * __astman_get_header().
1556 const char *astman_get_header(const struct message *m, char *var)
1558 return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
1562 struct ast_variable *astman_get_variables(const struct message *m)
1565 struct ast_variable *head = NULL, *cur;
1567 AST_DECLARE_APP_ARGS(args,
1568 AST_APP_ARG(vars)[32];
1571 varlen = strlen("Variable: ");
1573 for (x = 0; x < m->hdrcount; x++) {
1574 char *parse, *var, *val;
1576 if (strncasecmp("Variable: ", m->headers[x], varlen)) {
1579 parse = ast_strdupa(m->headers[x] + varlen);
1581 AST_STANDARD_APP_ARGS(args, parse);
1585 for (y = 0; y < args.argc; y++) {
1586 if (!args.vars[y]) {
1589 var = val = ast_strdupa(args.vars[y]);
1591 if (!val || ast_strlen_zero(var)) {
1594 cur = ast_variable_new(var, val, "");
1603 /* access for hooks to send action messages to ami */
1605 int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
1609 struct manager_action *tmp;
1610 struct mansession s = {.session = NULL, };
1611 struct message m = { 0 };
1612 char header_buf[1025] = { '\0' };
1613 const char *src = msg;
1621 /* convert msg string to message struct */
1622 curlen = strlen(msg);
1623 for (x = 0; x < curlen; x++) {
1624 int cr; /* set if we have \r */
1625 if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
1626 cr = 2; /* Found. Update length to include \r\n */
1627 else if (src[x] == '\n')
1628 cr = 1; /* also accept \n only */
1631 /* don't copy empty lines */
1633 memmove(header_buf, src, x); /*... but trim \r\n */
1634 header_buf[x] = '\0'; /* terminate the string */
1635 m.headers[m.hdrcount++] = ast_strdupa(header_buf);
1638 curlen -= x; /* remaining size */
1639 src += x; /* update pointer */
1640 x = -1; /* reset loop */
1643 action = astman_get_header(&m,"Action");
1644 if (action && strcasecmp(action,"login")) {
1646 AST_RWLIST_RDLOCK(&actions);
1647 AST_RWLIST_TRAVERSE(&actions, tmp, list) {
1648 if (strcasecmp(action, tmp->action))
1651 * we have to simulate a session for this action request
1652 * to be able to pass it down for processing
1653 * This is necessary to meet the previous design of manager.c
1656 s.f = (void*)1; /* set this to something so our request will make it through all functions that test it*/
1657 ret = tmp->func(&s, &m);
1660 AST_RWLIST_UNLOCK(&actions);
1667 * helper function to send a string to the socket.
1668 * Return -1 on error (e.g. buffer full).
1670 static int send_string(struct mansession *s, char *string)
1672 /* It's a result from one of the hook's action invocation */
1675 * to send responses, we're using the same function
1676 * as for receiving events. We call the event "HookResponse"
1678 s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
1681 return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout);
1683 return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout);
1688 * \brief thread local buffer for astman_append
1690 * \note This can not be defined within the astman_append() function
1691 * because it declares a couple of functions that get used to
1692 * initialize the thread local storage key.
1694 AST_THREADSTORAGE(astman_append_buf);
1695 AST_THREADSTORAGE(userevent_buf);
1697 /*! \brief initial allocated size for the astman_append_buf */
1698 #define ASTMAN_APPEND_BUF_INITSIZE 256
1701 * utility functions for creating AMI replies
1703 void astman_append(struct mansession *s, const char *fmt, ...)
1706 struct ast_str *buf;
1708 if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
1713 ast_str_set_va(&buf, 0, fmt, ap);
1716 if (s->f != NULL || s->session->f != NULL) {
1717 send_string(s, ast_str_buffer(buf));
1719 ast_verbose("fd == -1 in astman_append, should not happen\n");
1723 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
1724 Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
1725 hold the session lock _or_ be running in an action callback (in which case s->session->busy will
1726 be non-zero). In either of these cases, there is no need to lock-protect the session's
1727 fd, since no other output will be sent (events will be queued), and no input will
1728 be read until either the current action finishes or get_input() obtains the session
1732 /*! \brief send a response with an optional message,
1733 * and terminate it with an empty line.
1734 * m is used only to grab the 'ActionID' field.
1736 * Use the explicit constant MSG_MOREDATA to remove the empty line.
1737 * XXX MSG_MOREDATA should go to a header file.
1739 #define MSG_MOREDATA ((char *)astman_send_response)
1740 static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
1742 const char *id = astman_get_header(m, "ActionID");
1744 astman_append(s, "Response: %s\r\n", resp);
1745 if (!ast_strlen_zero(id)) {
1746 astman_append(s, "ActionID: %s\r\n", id);
1749 astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
1751 if (msg == MSG_MOREDATA) {
1754 astman_append(s, "Message: %s\r\n\r\n", msg);
1756 astman_append(s, "\r\n");
1760 void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
1762 astman_send_response_full(s, m, resp, msg, NULL);
1765 void astman_send_error(struct mansession *s, const struct message *m, char *error)
1767 astman_send_response_full(s, m, "Error", error, NULL);
1770 void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
1772 astman_send_response_full(s, m, "Success", msg, NULL);
1775 static void astman_start_ack(struct mansession *s, const struct message *m)
1777 astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
1780 void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
1782 astman_send_response_full(s, m, "Success", msg, listflag);
1785 /*! \brief Lock the 'mansession' structure. */
1786 static void mansession_lock(struct mansession *s)
1788 ast_mutex_lock(&s->lock);
1791 /*! \brief Unlock the 'mansession' structure. */
1792 static void mansession_unlock(struct mansession *s)
1794 ast_mutex_unlock(&s->lock);
1798 Rather than braindead on,off this now can also accept a specific int mask value
1799 or a ',' delim list of mask strings (the same as manager.conf) -anthm
1801 static int set_eventmask(struct mansession *s, const char *eventmask)
1803 int maskint = strings_to_mask(eventmask);
1807 s->session->send_events = maskint;
1809 mansession_unlock(s);
1814 static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s)
1816 return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS :
1817 AST_SECURITY_EVENT_TRANSPORT_TCP;
1820 static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s,
1821 struct sockaddr_in *sin_local)
1823 *sin_local = s->tcptls_session->parent->local_address;
1828 static void report_invalid_user(const struct mansession *s, const char *username)
1830 struct sockaddr_in sin_local;
1831 char session_id[32];
1832 struct ast_security_event_inval_acct_id inval_acct_id = {
1833 .common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
1834 .common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
1835 .common.service = "AMI",
1836 .common.account_id = username,
1837 .common.session_tv = &s->session->sessionstart_tv,
1838 .common.local_addr = {
1839 .sin = mansession_encode_sin_local(s, &sin_local),
1840 .transport = mansession_get_transport(s),
1842 .common.remote_addr = {
1843 .sin = &s->session->sin,
1844 .transport = mansession_get_transport(s),
1846 .common.session_id = session_id,
1849 snprintf(session_id, sizeof(session_id), "%p", s);
1851 ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
1854 static void report_failed_acl(const struct mansession *s, const char *username)
1856 struct sockaddr_in sin_local;
1857 char session_id[32];
1858 struct ast_security_event_failed_acl failed_acl_event = {
1859 .common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
1860 .common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
1861 .common.service = "AMI",
1862 .common.account_id = username,
1863 .common.session_tv = &s->session->sessionstart_tv,
1864 .common.local_addr = {
1865 .sin = mansession_encode_sin_local(s, &sin_local),
1866 .transport = mansession_get_transport(s),
1868 .common.remote_addr = {
1869 .sin = &s->session->sin,
1870 .transport = mansession_get_transport(s),
1872 .common.session_id = session_id,
1875 snprintf(session_id, sizeof(session_id), "%p", s->session);
1877 ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
1880 static void report_inval_password(const struct mansession *s, const char *username)
1882 struct sockaddr_in sin_local;
1883 char session_id[32];
1884 struct ast_security_event_inval_password inval_password = {
1885 .common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
1886 .common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
1887 .common.service = "AMI",
1888 .common.account_id = username,
1889 .common.session_tv = &s->session->sessionstart_tv,
1890 .common.local_addr = {
1891 .sin = mansession_encode_sin_local(s, &sin_local),
1892 .transport = mansession_get_transport(s),
1894 .common.remote_addr = {
1895 .sin = &s->session->sin,
1896 .transport = mansession_get_transport(s),
1898 .common.session_id = session_id,
1901 snprintf(session_id, sizeof(session_id), "%p", s->session);
1903 ast_security_event_report(AST_SEC_EVT(&inval_password));
1906 static void report_auth_success(const struct mansession *s)
1908 struct sockaddr_in sin_local;
1909 char session_id[32];
1910 struct ast_security_event_successful_auth successful_auth = {
1911 .common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
1912 .common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
1913 .common.service = "AMI",
1914 .common.account_id = s->session->username,
1915 .common.session_tv = &s->session->sessionstart_tv,
1916 .common.local_addr = {
1917 .sin = mansession_encode_sin_local(s, &sin_local),
1918 .transport = mansession_get_transport(s),
1920 .common.remote_addr = {
1921 .sin = &s->session->sin,
1922 .transport = mansession_get_transport(s),
1924 .common.session_id = session_id,
1927 snprintf(session_id, sizeof(session_id), "%p", s->session);
1929 ast_security_event_report(AST_SEC_EVT(&successful_auth));
1932 static void report_req_not_allowed(const struct mansession *s, const char *action)
1934 struct sockaddr_in sin_local;
1935 char session_id[32];
1936 char request_type[64];
1937 struct ast_security_event_req_not_allowed req_not_allowed = {
1938 .common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
1939 .common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
1940 .common.service = "AMI",
1941 .common.account_id = s->session->username,
1942 .common.session_tv = &s->session->sessionstart_tv,
1943 .common.local_addr = {
1944 .sin = mansession_encode_sin_local(s, &sin_local),
1945 .transport = mansession_get_transport(s),
1947 .common.remote_addr = {
1948 .sin = &s->session->sin,
1949 .transport = mansession_get_transport(s),
1951 .common.session_id = session_id,
1953 .request_type = request_type,
1956 snprintf(session_id, sizeof(session_id), "%p", s->session);
1957 snprintf(request_type, sizeof(request_type), "Action: %s", action);
1959 ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
1962 static void report_req_bad_format(const struct mansession *s, const char *action)
1964 struct sockaddr_in sin_local;
1965 char session_id[32];
1966 char request_type[64];
1967 struct ast_security_event_req_bad_format req_bad_format = {
1968 .common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
1969 .common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
1970 .common.service = "AMI",
1971 .common.account_id = s->session->username,
1972 .common.session_tv = &s->session->sessionstart_tv,
1973 .common.local_addr = {
1974 .sin = mansession_encode_sin_local(s, &sin_local),
1975 .transport = mansession_get_transport(s),
1977 .common.remote_addr = {
1978 .sin = &s->session->sin,
1979 .transport = mansession_get_transport(s),
1981 .common.session_id = session_id,
1983 .request_type = request_type,
1986 snprintf(session_id, sizeof(session_id), "%p", s->session);
1987 snprintf(request_type, sizeof(request_type), "Action: %s", action);
1989 ast_security_event_report(AST_SEC_EVT(&req_bad_format));
1992 static void report_failed_challenge_response(const struct mansession *s,
1993 const char *response, const char *expected_response)
1995 struct sockaddr_in sin_local;
1996 char session_id[32];
1997 struct ast_security_event_chal_resp_failed chal_resp_failed = {
1998 .common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
1999 .common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
2000 .common.service = "AMI",
2001 .common.account_id = s->session->username,
2002 .common.session_tv = &s->session->sessionstart_tv,
2003 .common.local_addr = {
2004 .sin = mansession_encode_sin_local(s, &sin_local),
2005 .transport = mansession_get_transport(s),
2007 .common.remote_addr = {
2008 .sin = &s->session->sin,
2009 .transport = mansession_get_transport(s),
2011 .common.session_id = session_id,
2013 .challenge = s->session->challenge,
2014 .response = response,
2015 .expected_response = expected_response,
2018 snprintf(session_id, sizeof(session_id), "%p", s->session);
2020 ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
2023 static void report_session_limit(const struct mansession *s)
2025 struct sockaddr_in sin_local;
2026 char session_id[32];
2027 struct ast_security_event_session_limit session_limit = {
2028 .common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
2029 .common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
2030 .common.service = "AMI",
2031 .common.account_id = s->session->username,
2032 .common.session_tv = &s->session->sessionstart_tv,
2033 .common.local_addr = {
2034 .sin = mansession_encode_sin_local(s, &sin_local),
2035 .transport = mansession_get_transport(s),
2037 .common.remote_addr = {
2038 .sin = &s->session->sin,
2039 .transport = mansession_get_transport(s),
2041 .common.session_id = session_id,
2044 snprintf(session_id, sizeof(session_id), "%p", s->session);
2046 ast_security_event_report(AST_SEC_EVT(&session_limit));
2050 * Here we start with action_ handlers for AMI actions,
2051 * and the internal functions used by them.
2052 * Generally, the handlers are called action_foo()
2055 /* helper function for action_login() */
2056 static int authenticate(struct mansession *s, const struct message *m)
2058 const char *username = astman_get_header(m, "Username");
2059 const char *password = astman_get_header(m, "Secret");
2061 struct ast_manager_user *user = NULL;
2063 if (ast_strlen_zero(username)) { /* missing username */
2067 /* locate user in locked state */
2068 AST_RWLIST_WRLOCK(&users);
2070 if (!(user = get_manager_by_name_locked(username))) {
2071 report_invalid_user(s, username);
2072 ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2073 } else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
2074 report_failed_acl(s, username);
2075 ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2076 } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
2077 const char *key = astman_get_header(m, "Key");
2078 if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
2081 char md5key[256] = "";
2082 struct MD5Context md5;
2083 unsigned char digest[16];
2086 MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
2087 MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
2088 MD5Final(digest, &md5);
2089 for (x = 0; x < 16; x++)
2090 len += sprintf(md5key + len, "%2.2x", digest[x]);
2091 if (!strcmp(md5key, key)) {
2094 report_failed_challenge_response(s, key, md5key);
2097 ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
2098 S_OR(s->session->challenge, ""));
2100 } else if (user->secret) {
2101 if (!strcmp(password, user->secret)) {
2104 report_inval_password(s, username);
2109 ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
2110 AST_RWLIST_UNLOCK(&users);
2116 ast_copy_string(s->session->username, username, sizeof(s->session->username));
2117 s->session->readperm = user->readperm;
2118 s->session->writeperm = user->writeperm;
2119 s->session->writetimeout = user->writetimeout;
2120 s->session->sessionstart = time(NULL);
2121 s->session->sessionstart_tv = ast_tvnow();
2122 set_eventmask(s, astman_get_header(m, "Events"));
2124 report_auth_success(s);
2126 AST_RWLIST_UNLOCK(&users);
2130 static int action_ping(struct mansession *s, const struct message *m)
2132 const char *actionid = astman_get_header(m, "ActionID");
2133 struct timeval now = ast_tvnow();
2135 astman_append(s, "Response: Success\r\n");
2136 if (!ast_strlen_zero(actionid)){
2137 astman_append(s, "ActionID: %s\r\n", actionid);
2142 "Timestamp: %ld.%06lu\r\n"
2144 now.tv_sec, (unsigned long) now.tv_usec);
2148 static int action_getconfig(struct mansession *s, const struct message *m)
2150 struct ast_config *cfg;
2151 const char *fn = astman_get_header(m, "Filename");
2152 const char *category = astman_get_header(m, "Category");
2155 char *cur_category = NULL;
2156 struct ast_variable *v;
2157 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2159 if (ast_strlen_zero(fn)) {
2160 astman_send_error(s, m, "Filename not specified");
2163 cfg = ast_config_load2(fn, "manager", config_flags);
2164 if (cfg == CONFIG_STATUS_FILEMISSING) {
2165 astman_send_error(s, m, "Config file not found");
2167 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2168 astman_send_error(s, m, "Config file has invalid format");
2172 astman_start_ack(s, m);
2173 while ((cur_category = ast_category_browse(cfg, cur_category))) {
2174 if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) {
2176 astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category);
2177 for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) {
2178 astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
2183 if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2184 astman_append(s, "No categories found\r\n");
2186 ast_config_destroy(cfg);
2187 astman_append(s, "\r\n");
2192 static int action_listcategories(struct mansession *s, const struct message *m)
2194 struct ast_config *cfg;
2195 const char *fn = astman_get_header(m, "Filename");
2196 char *category = NULL;
2197 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2200 if (ast_strlen_zero(fn)) {
2201 astman_send_error(s, m, "Filename not specified");
2204 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2205 astman_send_error(s, m, "Config file not found");
2207 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2208 astman_send_error(s, m, "Config file has invalid format");
2211 astman_start_ack(s, m);
2212 while ((category = ast_category_browse(cfg, category))) {
2213 astman_append(s, "Category-%06d: %s\r\n", catcount, category);
2216 if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
2217 astman_append(s, "Error: no categories found\r\n");
2219 ast_config_destroy(cfg);
2220 astman_append(s, "\r\n");
2228 /*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
2229 static void json_escape(char *out, const char *in)
2232 if (*in == '\\' || *in == '\"') {
2240 static int action_getconfigjson(struct mansession *s, const struct message *m)
2242 struct ast_config *cfg;
2243 const char *fn = astman_get_header(m, "Filename");
2244 char *category = NULL;
2245 struct ast_variable *v;
2248 unsigned int buf_len = 0;
2249 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2251 if (ast_strlen_zero(fn)) {
2252 astman_send_error(s, m, "Filename not specified");
2256 if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
2257 astman_send_error(s, m, "Config file not found");
2259 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2260 astman_send_error(s, m, "Config file has invalid format");
2265 buf = alloca(buf_len);
2267 astman_start_ack(s, m);
2268 astman_append(s, "JSON: {");
2269 while ((category = ast_category_browse(cfg, category))) {
2271 if (buf_len < 2 * strlen(category) + 1) {
2273 buf = alloca(buf_len);
2275 json_escape(buf, category);
2276 astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
2280 for (v = ast_variable_browse(cfg, category); v; v = v->next) {
2282 astman_append(s, ",");
2284 if (buf_len < 2 * strlen(v->name) + 1) {
2286 buf = alloca(buf_len);
2288 json_escape(buf, v->name);
2289 astman_append(s, "\"%s", buf);
2290 if (buf_len < 2 * strlen(v->value) + 1) {
2292 buf = alloca(buf_len);
2294 json_escape(buf, v->value);
2295 astman_append(s, "%s\"", buf);
2300 astman_append(s, "]");
2302 astman_append(s, "}\r\n\r\n");
2304 ast_config_destroy(cfg);
2309 /* helper function for action_updateconfig */
2310 static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
2314 const char *action, *cat, *var, *value, *match, *line;
2315 struct ast_category *category;
2316 struct ast_variable *v;
2317 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
2318 enum error_type result = 0;
2320 for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
2321 unsigned int object = 0;
2323 snprintf(hdr, sizeof(hdr), "Action-%06d", x);
2324 action = astman_get_header(m, hdr);
2325 if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
2326 break; /* this could cause problems if actions come in misnumbered */
2328 snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
2329 cat = astman_get_header(m, hdr);
2330 if (ast_strlen_zero(cat)) { /* every action needs a category */
2331 result = UNSPECIFIED_CATEGORY;
2335 snprintf(hdr, sizeof(hdr), "Var-%06d", x);
2336 var = astman_get_header(m, hdr);
2338 snprintf(hdr, sizeof(hdr), "Value-%06d", x);
2339 value = astman_get_header(m, hdr);
2341 if (!ast_strlen_zero(value) && *value == '>') {
2346 snprintf(hdr, sizeof(hdr), "Match-%06d", x);
2347 match = astman_get_header(m, hdr);
2349 snprintf(hdr, sizeof(hdr), "Line-%06d", x);
2350 line = astman_get_header(m, hdr);
2352 if (!strcasecmp(action, "newcat")) {
2353 if (ast_category_get(cfg,cat)) { /* check to make sure the cat doesn't */
2354 result = FAILURE_NEWCAT; /* already exist */
2357 if (!(category = ast_category_new(cat, dfn, -1))) {
2358 result = FAILURE_ALLOCATION;
2361 if (ast_strlen_zero(match)) {
2362 ast_category_append(cfg, category);
2364 ast_category_insert(cfg, category, match);
2366 } else if (!strcasecmp(action, "renamecat")) {
2367 if (ast_strlen_zero(value)) {
2368 result = UNSPECIFIED_ARGUMENT;
2371 if (!(category = ast_category_get(cfg, cat))) {
2372 result = UNKNOWN_CATEGORY;
2375 ast_category_rename(category, value);
2376 } else if (!strcasecmp(action, "delcat")) {
2377 if (ast_category_delete(cfg, cat)) {
2378 result = FAILURE_DELCAT;
2381 } else if (!strcasecmp(action, "emptycat")) {
2382 if (ast_category_empty(cfg, cat)) {
2383 result = FAILURE_EMPTYCAT;
2386 } else if (!strcasecmp(action, "update")) {
2387 if (ast_strlen_zero(var)) {
2388 result = UNSPECIFIED_ARGUMENT;
2391 if (!(category = ast_category_get(cfg,cat))) {
2392 result = UNKNOWN_CATEGORY;
2395 if (ast_variable_update(category, var, value, match, object)) {
2396 result = FAILURE_UPDATE;
2399 } else if (!strcasecmp(action, "delete")) {
2400 if ((ast_strlen_zero(var) && ast_strlen_zero(line))) {
2401 result = UNSPECIFIED_ARGUMENT;
2404 if (!(category = ast_category_get(cfg, cat))) {
2405 result = UNKNOWN_CATEGORY;
2408 if (ast_variable_delete(category, var, match, line)) {
2409 result = FAILURE_DELETE;
2412 } else if (!strcasecmp(action, "append")) {
2413 if (ast_strlen_zero(var)) {
2414 result = UNSPECIFIED_ARGUMENT;
2417 if (!(category = ast_category_get(cfg, cat))) {
2418 result = UNKNOWN_CATEGORY;
2421 if (!(v = ast_variable_new(var, value, dfn))) {
2422 result = FAILURE_ALLOCATION;
2425 if (object || (match && !strcasecmp(match, "object"))) {
2428 ast_variable_append(category, v);
2429 } else if (!strcasecmp(action, "insert")) {
2430 if (ast_strlen_zero(var) || ast_strlen_zero(line)) {
2431 result = UNSPECIFIED_ARGUMENT;
2434 if (!(category = ast_category_get(cfg, cat))) {
2435 result = UNKNOWN_CATEGORY;
2438 if (!(v = ast_variable_new(var, value, dfn))) {
2439 result = FAILURE_ALLOCATION;
2442 ast_variable_insert(category, v, line);
2445 ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
2446 result = UNKNOWN_ACTION;
2455 static int action_updateconfig(struct mansession *s, const struct message *m)
2457 struct ast_config *cfg;
2458 const char *sfn = astman_get_header(m, "SrcFilename");
2459 const char *dfn = astman_get_header(m, "DstFilename");
2461 const char *rld = astman_get_header(m, "Reload");
2462 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
2463 enum error_type result;
2465 if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
2466 astman_send_error(s, m, "Filename not specified");
2469 if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
2470 astman_send_error(s, m, "Config file not found");
2472 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2473 astman_send_error(s, m, "Config file has invalid format");
2476 result = handle_updates(s, m, cfg, dfn);
2478 ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
2479 res = ast_config_text_file_save(dfn, cfg, "Manager");
2480 ast_config_destroy(cfg);
2482 astman_send_error(s, m, "Save of config failed");
2485 astman_send_ack(s, m, NULL);
2486 if (!ast_strlen_zero(rld)) {
2487 if (ast_true(rld)) {
2490 ast_module_reload(rld);
2493 ast_config_destroy(cfg);
2495 case UNKNOWN_ACTION:
2496 astman_send_error(s, m, "Unknown action command");
2498 case UNKNOWN_CATEGORY:
2499 astman_send_error(s, m, "Given category does not exist");
2501 case UNSPECIFIED_CATEGORY:
2502 astman_send_error(s, m, "Category not specified");
2504 case UNSPECIFIED_ARGUMENT:
2505 astman_send_error(s, m, "Problem with category, value, or line (if required)");
2507 case FAILURE_ALLOCATION:
2508 astman_send_error(s, m, "Memory allocation failure, this should not happen");
2510 case FAILURE_NEWCAT:
2511 astman_send_error(s, m, "Create category did not complete successfully");
2513 case FAILURE_DELCAT:
2514 astman_send_error(s, m, "Delete category did not complete successfully");
2516 case FAILURE_EMPTYCAT:
2517 astman_send_error(s, m, "Empty category did not complete successfully");
2519 case FAILURE_UPDATE:
2520 astman_send_error(s, m, "Update did not complete successfully");
2522 case FAILURE_DELETE:
2523 astman_send_error(s, m, "Delete did not complete successfully");
2525 case FAILURE_APPEND:
2526 astman_send_error(s, m, "Append did not complete successfully");
2533 static int action_createconfig(struct mansession *s, const struct message *m)
2536 const char *fn = astman_get_header(m, "Filename");
2537 struct ast_str *filepath = ast_str_alloca(PATH_MAX);
2538 ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
2539 ast_str_append(&filepath, 0, "%s", fn);
2541 if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
2543 astman_send_ack(s, m, "New configuration file created successfully");
2545 astman_send_error(s, m, strerror(errno));
2551 static int action_waitevent(struct mansession *s, const struct message *m)
2553 const char *timeouts = astman_get_header(m, "Timeout");
2557 const char *id = astman_get_header(m, "ActionID");
2560 if (!ast_strlen_zero(id)) {
2561 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2566 if (!ast_strlen_zero(timeouts)) {
2567 sscanf(timeouts, "%30i", &timeout);
2571 /* XXX maybe put an upper bound, or prevent the use of 0 ? */
2575 if (s->session->waiting_thread != AST_PTHREADT_NULL) {
2576 pthread_kill(s->session->waiting_thread, SIGURG);
2579 if (s->session->managerid) { /* AMI-over-HTTP session */
2581 * Make sure the timeout is within the expire time of the session,
2582 * as the client will likely abort the request if it does not see
2583 * data coming after some amount of time.
2585 time_t now = time(NULL);
2586 int max = s->session->sessiontimeout - now - 10;
2588 if (max < 0) { /* We are already late. Strange but possible. */
2591 if (timeout < 0 || timeout > max) {
2594 if (!s->session->send_events) { /* make sure we record events */
2595 s->session->send_events = -1;
2598 mansession_unlock(s);
2600 /* XXX should this go inside the lock ? */
2601 s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */
2602 ast_debug(1, "Starting waiting for an event!\n");
2604 for (x = 0; x < timeout || timeout < 0; x++) {
2609 /* We can have multiple HTTP session point to the same mansession entry.
2610 * The way we deal with it is not very nice: newcomers kick out the previous
2611 * HTTP session. XXX this needs to be improved.
2613 if (s->session->waiting_thread != pthread_self()) {
2616 if (s->session->needdestroy) {
2619 mansession_unlock(s);
2623 if (s->session->managerid == 0) { /* AMI session */
2624 if (ast_wait_for_input(s->session->fd, 1000)) {
2627 } else { /* HTTP session */
2631 ast_debug(1, "Finished waiting for an event!\n");
2634 if (s->session->waiting_thread == pthread_self()) {
2635 struct eventqent *eqe;
2636 astman_send_response(s, m, "Success", "Waiting for Event completed.");
2637 while ( (eqe = NEW_EVENT(s)) ) {
2639 if (((s->session->readperm & eqe->category) == eqe->category) &&
2640 ((s->session->send_events & eqe->category) == eqe->category)) {
2641 astman_append(s, "%s", eqe->eventdata);
2643 s->session->last_ev = unref_event(s->session->last_ev);
2646 "Event: WaitEventComplete\r\n"
2649 s->session->waiting_thread = AST_PTHREADT_NULL;
2651 ast_debug(1, "Abandoning event request!\n");
2653 mansession_unlock(s);
2657 /*! \note The actionlock is read-locked by the caller of this function */
2658 static int action_listcommands(struct mansession *s, const struct message *m)
2660 struct manager_action *cur;
2661 struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
2663 astman_start_ack(s, m);
2664 AST_RWLIST_TRAVERSE(&actions, cur, list) {
2665 if (s->session->writeperm & cur->authority || cur->authority == 0) {
2666 astman_append(s, "%s: %s (Priv: %s)\r\n",
2667 cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
2670 astman_append(s, "\r\n");
2675 static int action_events(struct mansession *s, const struct message *m)
2677 const char *mask = astman_get_header(m, "EventMask");
2680 res = set_eventmask(s, mask);
2682 astman_append(s, "Response: Success\r\n"
2685 astman_append(s, "Response: Success\r\n"
2690 static int action_logoff(struct mansession *s, const struct message *m)
2692 astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
2696 static int action_login(struct mansession *s, const struct message *m)
2699 /* still authenticated - don't process again */
2700 if (s->session->authenticated) {
2701 astman_send_ack(s, m, "Already authenticated");
2705 if (authenticate(s, m)) {
2707 astman_send_error(s, m, "Authentication failed");
2710 s->session->authenticated = 1;
2711 if (manager_displayconnects(s->session)) {
2712 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));
2714 astman_send_ack(s, m, "Authentication accepted");
2718 static int action_challenge(struct mansession *s, const struct message *m)
2720 const char *authtype = astman_get_header(m, "AuthType");
2722 if (!strcasecmp(authtype, "MD5")) {
2723 if (ast_strlen_zero(s->session->challenge)) {
2724 snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
2727 astman_start_ack(s, m);
2728 astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
2729 mansession_unlock(s);
2731 astman_send_error(s, m, "Must specify AuthType");
2736 static int action_hangup(struct mansession *s, const struct message *m)
2738 struct ast_channel *c = NULL;
2739 int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
2740 const char *name = astman_get_header(m, "Channel");
2741 const char *cause = astman_get_header(m, "Cause");
2743 if (ast_strlen_zero(name)) {
2744 astman_send_error(s, m, "No channel specified");
2748 if (!ast_strlen_zero(cause)) {
2750 causecode = strtol(cause, &endptr, 10);
2751 if (causecode < 0 || causecode > 127 || *endptr != '\0') {
2752 ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
2753 /* keep going, better to hangup without cause than to not hang up at all */
2754 causecode = 0; /* do not set channel's hangupcause */
2758 if (!(c = ast_channel_get_by_name(name))) {
2759 astman_send_error(s, m, "No such channel");
2763 ast_channel_lock(c);
2764 if (causecode > 0) {
2765 ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
2766 c->name, causecode, c->hangupcause);
2767 c->hangupcause = causecode;
2769 ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT);
2770 ast_channel_unlock(c);
2772 c = ast_channel_unref(c);
2774 astman_send_ack(s, m, "Channel Hungup");
2779 static int action_setvar(struct mansession *s, const struct message *m)
2781 struct ast_channel *c = NULL;
2782 const char *name = astman_get_header(m, "Channel");
2783 const char *varname = astman_get_header(m, "Variable");
2784 const char *varval = astman_get_header(m, "Value");
2786 if (ast_strlen_zero(varname)) {
2787 astman_send_error(s, m, "No variable specified");
2791 if (!ast_strlen_zero(name)) {
2792 if (!(c = ast_channel_get_by_name(name))) {
2793 astman_send_error(s, m, "No such channel");
2798 pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
2801 c = ast_channel_unref(c);
2804 astman_send_ack(s, m, "Variable Set");
2809 static int action_getvar(struct mansession *s, const struct message *m)
2811 struct ast_channel *c = NULL;
2812 const char *name = astman_get_header(m, "Channel");
2813 const char *varname = astman_get_header(m, "Variable");
2815 char workspace[1024] = "";
2817 if (ast_strlen_zero(varname)) {
2818 astman_send_error(s, m, "No variable specified");
2822 if (!ast_strlen_zero(name)) {
2823 if (!(c = ast_channel_get_by_name(name))) {
2824 astman_send_error(s, m, "No such channel");
2829 if (varname[strlen(varname) - 1] == ')') {
2831 c = ast_dummy_channel_alloc();
2833 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
2834 c = ast_channel_release(c);
2836 ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
2838 ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
2842 pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
2846 c = ast_channel_unref(c);
2849 astman_start_ack(s, m);
2850 astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
2855 /*! \brief Manager "status" command to show channels */
2856 /* Needs documentation... */
2857 static int action_status(struct mansession *s, const struct message *m)
2859 const char *name = astman_get_header(m, "Channel");
2860 const char *cvariables = astman_get_header(m, "Variables");
2861 char *variables = ast_strdupa(S_OR(cvariables, ""));
2862 struct ast_channel *c;
2864 struct timeval now = ast_tvnow();
2865 long elapsed_seconds = 0;
2867 int all = ast_strlen_zero(name); /* set if we want all channels */
2868 const char *id = astman_get_header(m, "ActionID");
2870 AST_DECLARE_APP_ARGS(vars,
2871 AST_APP_ARG(name)[100];
2873 struct ast_str *str = ast_str_create(1000);
2874 struct ast_channel_iterator *iter = NULL;
2876 if (!ast_strlen_zero(id)) {
2877 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
2883 if (!(iter = ast_channel_iterator_all_new())) {
2885 astman_send_error(s, m, "Memory Allocation Failure");
2888 c = ast_channel_iterator_next(iter);
2890 if (!(c = ast_channel_get_by_name(name))) {
2891 astman_send_error(s, m, "No such channel");
2897 astman_send_ack(s, m, "Channel status will follow");
2899 if (!ast_strlen_zero(cvariables)) {
2900 AST_STANDARD_APP_ARGS(vars, variables);
2903 /* if we look by name, we break after the first iteration */
2904 for (; c; c = ast_channel_iterator_next(iter)) {
2905 ast_channel_lock(c);
2907 if (!ast_strlen_zero(cvariables)) {
2910 for (i = 0; i < vars.argc; i++) {
2911 char valbuf[512], *ret = NULL;
2913 if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
2914 if (ast_func_read(c, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
2919 pbx_retrieve_variable(c, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
2922 ast_str_append(&str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
2928 snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
2934 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
2938 "Privilege: Call\r\n"
2940 "CallerIDNum: %s\r\n"
2941 "CallerIDName: %s\r\n"
2942 "Accountcode: %s\r\n"
2943 "ChannelState: %d\r\n"
2944 "ChannelStateDesc: %s\r\n"
2955 S_OR(c->cid.cid_num, ""),
2956 S_OR(c->cid.cid_name, ""),
2959 ast_state2str(c->_state), c->context,
2960 c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, ast_str_buffer(str), idText);
2964 "Privilege: Call\r\n"
2966 "CallerIDNum: %s\r\n"
2967 "CallerIDName: %s\r\n"
2976 S_OR(c->cid.cid_num, "<unknown>"),
2977 S_OR(c->cid.cid_name, "<unknown>"),
2979 ast_state2str(c->_state), bridge, c->uniqueid,
2980 ast_str_buffer(str), idText);
2983 ast_channel_unlock(c);
2984 c = ast_channel_unref(c);
2992 ast_channel_iterator_destroy(iter);
2996 "Event: StatusComplete\r\n"
2999 "\r\n", idText, channels);
3006 static int action_sendtext(struct mansession *s, const struct message *m)
3008 struct ast_channel *c = NULL;
3009 const char *name = astman_get_header(m, "Channel");
3010 const char *textmsg = astman_get_header(m, "Message");
3013 if (ast_strlen_zero(name)) {
3014 astman_send_error(s, m, "No channel specified");
3018 if (ast_strlen_zero(textmsg)) {
3019 astman_send_error(s, m, "No Message specified");
3023 if (!(c = ast_channel_get_by_name(name))) {
3024 astman_send_error(s, m, "No such channel");
3028 ast_channel_lock(c);
3029 res = ast_sendtext(c, textmsg);
3030 ast_channel_unlock(c);
3031 c = ast_channel_unref(c);
3034 astman_send_ack(s, m, "Success");
3036 astman_send_error(s, m, "Failure");
3042 /*! \brief action_redirect: The redirect manager command */
3043 static int action_redirect(struct mansession *s, const struct message *m)
3045 const char *name = astman_get_header(m, "Channel");
3046 const char *name2 = astman_get_header(m, "ExtraChannel");
3047 const char *exten = astman_get_header(m, "Exten");
3048 const char *context = astman_get_header(m, "Context");
3049 const char *priority = astman_get_header(m, "Priority");
3050 struct ast_channel *chan, *chan2 = NULL;
3054 if (ast_strlen_zero(name)) {
3055 astman_send_error(s, m, "Channel not specified");
3059 if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
3060 if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
3061 astman_send_error(s, m, "Invalid priority");
3066 if (!(chan = ast_channel_get_by_name(name))) {
3068 snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
3069 astman_send_error(s, m, buf);
3073 if (ast_check_hangup_locked(chan)) {
3074 astman_send_error(s, m, "Redirect failed, channel not up.");
3075 chan = ast_channel_unref(chan);
3079 if (!ast_strlen_zero(name2)) {
3080 chan2 = ast_channel_get_by_name(name2);
3083 if (chan2 && ast_check_hangup_locked(chan2)) {
3084 astman_send_error(s, m, "Redirect failed, extra channel not up.");
3085 chan = ast_channel_unref(chan);
3086 chan2 = ast_channel_unref(chan2);