memory leaks: Memory leak cleanup patch by Corey Farrell (second set)
[asterisk/asterisk.git] / main / asterisk.c
index ebc9a8a..2e5ffa7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2012, Digium, Inc.
+ * Copyright (C) 1999 - 2013, Digium, Inc.
  *
  * Mark Spencer <markster@digium.com>
  *
 /*!
  * \mainpage Asterisk -- The Open Source Telephony Project
  *
+ * \par Welcome
+ *
+ * This documentation created by the Doxygen project clearly explains the
+ * internals of the Asterisk software. This documentation contains basic
+ * examples, developer documentation, support information, and information
+ * for upgrading.
+ *
+ * \section community Community
+ * Asterisk is a big project and has a busy community. Look at the
+ * resources for questions and stick around to help answer questions.
+ * \li \ref asterisk_community_resources
+ *
  * \par Developer Documentation for Asterisk
  *
- * This is the main developer documentation for Asterisk. It is 
- * generated by running "make progdocs" from the Asterisk source tree.  
+ * This is the main developer documentation for Asterisk. It is
+ * generated by running "make progdocs" from the Asterisk source tree.
  *
- * In addition to the information available on the Asterisk source code, 
- * please see the appendices for information on coding guidelines, 
+ * In addition to the information available on the Asterisk source code,
+ * please see the appendices for information on coding guidelines,
  * release management, commit policies, and more.
  *
  * \arg \ref AsteriskArchitecture
  *
  * \par Additional documentation
  * \arg \ref Licensing
- * \arg \ref DevDoc 
- * \arg \ref ConfigFiles
+ * \arg \ref DevDoc
+ * \arg \ref configuration_file
+ * \arg \ref channel_drivers
+ * \arg \ref applications
  *
  * \section copyright Copyright and Author
  *
- * Copyright (C) 1999 - 2012, Digium, Inc.
+ * Copyright (C) 1999 - 2013, Digium, Inc.
  * Asterisk is a <a href="http://www.digium.com/en/company/view-policy.php?id=Trademark-Policy">registered trademark</a>
  * of <a href="http://www.digium.com">Digium, Inc</a>.
  *
  * \author Mark Spencer <markster@digium.com>
- * Also see \ref AstCREDITS
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
  * any of the maintainers of this project for assistance;
  * the project provides a web site, mailing lists, and IRC
  * channels for your use.
+ *
+ */
+
+/*!
+ * \page asterisk_community_resources Asterisk Community Resources
+ * \par Websites
+ * \li http://www.asterisk.org Asterisk Homepage
+ * \li http://wiki.asterisk.org Asterisk Wiki
+ *
+ * \par Mailing Lists
+ * \par
+ * All lists: http://lists.digium.com/mailman/listinfo
+ * \li aadk-commits    SVN commits to the AADK repository
+ * \li asterisk-addons-commits SVN commits to the Asterisk addons project
+ * \li asterisk-announce       [no description available]
+ * \li asterisk-biz    Commercial and Business-Oriented Asterisk Discussion
+ * \li Asterisk-BSD    Asterisk on BSD discussion
+ * \li asterisk-bugs   [no description available]
+ * \li asterisk-commits        SVN commits to the Asterisk project
+ * \li asterisk-dev    Asterisk Developers Mailing List
+ * \li asterisk-doc    Discussions regarding The Asterisk Documentation Project
+ * \li asterisk-embedded       Asterisk Embedded Development
+ * \li asterisk-gui    Asterisk GUI project discussion
+ * \li asterisk-gui-commits    SVN commits to the Asterisk-GUI project
+ * \li asterisk-ha-clustering  Asterisk High Availability and Clustering List - Non-Commercial Discussion
+ * \li Asterisk-i18n   Discussion of Asterisk internationalization
+ * \li asterisk-r2     [no description available]
+ * \li asterisk-scf-commits    Commits to the Asterisk SCF project code repositories
+ * \li asterisk-scf-committee  Asterisk SCF Steering Committee discussions
+ * \li asterisk-scf-dev        Asterisk SCF Developers Mailing List
+ * \li asterisk-scf-wiki-changes       Changes to the Asterisk SCF space on wiki.asterisk.org
+ * \li asterisk-security       Asterisk Security Discussion
+ * \li asterisk-speech-rec     Use of speech recognition in Asterisk
+ * \li asterisk-ss7    [no description available]
+ * \li asterisk-users  Asterisk Users Mailing List - Non-Commercial Discussion
+ * \li asterisk-video  Development discussion of video media support in Asterisk
+ * \li asterisk-wiki-changes   Changes to the Asterisk space on wiki.asterisk.org
+ * \li asterisknow     AsteriskNOW Discussion
+ * \li dahdi-commits   SVN commits to the DAHDI project
+ * \li digium-announce Digium Product Announcements
+ * \li Dundi   Distributed Universal Number Discovery
+ * \li libiax2-commits SVN commits to the libiax2 project
+ * \li libpri-commits  SVN commits to the libpri project
+ * \li libss7-commits  SVN commits to the libss7 project
+ * \li svn-commits     SVN commits to the Digium repositories
+ * \li Test-results    Results from automated testing
+ * \li thirdparty-commits      SVN commits to the Digium third-party software repository
+ * \li zaptel-commits  SVN commits to the Zaptel project
+ *
+ * \par Forums
+ * \li Forums are located at http://forums.asterisk.org/
+ *
+ * \par IRC
+ * \par
+ * Use http://www.freenode.net IRC server to connect with Asterisk
+ * developers and users in realtime.
+ *
+ * \li \verbatim #asterisk \endverbatim Asterisk Users Room
+ * \li \verbatim #asterisk-dev \endverbatim Asterisk Developers Room
+ *
+ * \par More
+ * \par
+ * If you would like to add a resource to this list please create an issue
+ * on the issue tracker with a patch.
  */
 
 /*! \file
-  \brief Top level source file for Asterisk  - the Open Source PBX. Implementation
-  of PBX core functions and CLI interface.
-  
+ * \brief Top level source file for Asterisk - the Open Source PBX.
+ *     Implementation of PBX core functions and CLI interface.
+ */
+
+/*! \li \ref asterisk.c uses the configuration file \ref asterisk.conf
+ * \addtogroup configuration_file
  */
 
+/*! \page asterisk.conf asterisk.conf
+ * \verbinclude asterisk.conf.sample
+ */
+
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@@ -94,6 +182,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #endif
 #endif
 #include <regex.h>
+#include <histedit.h>
 
 #if defined(SOLARIS)
 int daemon(int, int);  /* defined in libresolv of all places */
@@ -107,12 +196,15 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #endif /* HAVE_CAP */
 #endif /* linux */
 
-#include "asterisk/paths.h"    /* we define here the variables so better agree on the prototype */
+/* we define here the variables so to better agree on the prototype */
+#include "asterisk/paths.h"
 #include "asterisk/network.h"
 #include "asterisk/cli.h"
 #include "asterisk/channel.h"
 #include "asterisk/translate.h"
+#include "asterisk/pickup.h"
 #include "asterisk/features.h"
+#include "asterisk/acl.h"
 #include "asterisk/ulaw.h"
 #include "asterisk/alaw.h"
 #include "asterisk/callerid.h"
@@ -136,6 +228,7 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #include "asterisk/ast_version.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/devicestate.h"
+#include "asterisk/presencestate.h"
 #include "asterisk/module.h"
 #include "asterisk/dsp.h"
 #include "asterisk/buildinfo.h"
@@ -146,9 +239,54 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #include "asterisk/rtp_engine.h"
 #include "asterisk/format.h"
 #include "asterisk/aoc.h"
+#include "asterisk/uuid.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/bucket.h"
+#include "asterisk/stasis.h"
+#include "asterisk/json.h"
+#include "asterisk/stasis_endpoints.h"
+#include "asterisk/stasis_system.h"
+#include "asterisk/security_events.h"
+#include "asterisk/endpoints.h"
 
 #include "../defaults.h"
 
+/*** DOCUMENTATION
+       <managerEvent language="en_US" name="FullyBooted">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when all Asterisk initialization procedures have finished.</synopsis>
+                       <syntax>
+                               <parameter name="Status">
+                                       <para>Informational message</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="Shutdown">
+               <managerEventInstance class="EVENT_FLAG_SYSTEM">
+                       <synopsis>Raised when Asterisk is shutdown or restarted.</synopsis>
+                       <syntax>
+                               <parameter name="Shutdown">
+                                       <para>Whether the shutdown is proceeding cleanly (all channels
+                                       were hungup successfully) or uncleanly (channels will be
+                                       terminated)</para>
+                                       <enumlist>
+                                               <enum name="Uncleanly"/>
+                                               <enum name="Cleanly"/>
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="Restart">
+                                       <para>Whether or not a restart will occur.</para>
+                                       <enumlist>
+                                               <enum name="True"/>
+                                               <enum name="False"/>
+                                       </enumlist>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+ ***/
+
 #ifndef AF_LOCAL
 #define AF_LOCAL AF_UNIX
 #define PF_LOCAL PF_UNIX
@@ -157,9 +295,13 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #define AST_MAX_CONNECTS 128
 #define NUM_MSGS 64
 
+/*! Default minimum DTMF digit length - 80ms */
+#define AST_MIN_DTMF_DURATION 80
+
+
 /*! \brief Welcome message when starting a CLI interface */
 #define WELCOME_MESSAGE \
-    ast_verbose("Asterisk %s, Copyright (C) 1999 - 2012 Digium, Inc. and others.\n" \
+    ast_verbose("Asterisk %s, Copyright (C) 1999 - 2013 Digium, Inc. and others.\n" \
                 "Created by Mark Spencer <markster@digium.com>\n" \
                 "Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.\n" \
                 "This is free software, with components licensed under the GNU General Public\n" \
@@ -170,7 +312,7 @@ int daemon(int, int);  /* defined in libresolv of all places */
 /*! \defgroup main_options Main Configuration Options
  * \brief Main configuration options from asterisk.conf or OS command line on starting Asterisk.
  * \arg \ref Config_ast "asterisk.conf"
- * \note Some of them can be changed in the CLI 
+ * \note Some of them can be changed in the CLI
  */
 /*! @{ */
 
@@ -179,9 +321,10 @@ struct ast_flags ast_compat = { 0 };
 
 int option_verbose;                            /*!< Verbosity level */
 int option_debug;                              /*!< Debug level */
-double option_maxload;                         /*!< Max load avg on system */
-int option_maxcalls;                           /*!< Max number of active calls */
-int option_maxfiles;                           /*!< Max number of open file handles (files, sockets) */
+double ast_option_maxload;                     /*!< Max load avg on system */
+int ast_option_maxcalls;                       /*!< Max number of active calls */
+int ast_option_maxfiles;                       /*!< Max number of open file handles (files, sockets) */
+unsigned int option_dtmfminduration;           /*!< Minimum duration of DTMF. */
 #if defined(HAVE_SYSINFO)
 long option_minmemfree;                                /*!< Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
 #endif
@@ -208,10 +351,11 @@ struct console {
 
 struct ast_atexit {
        void (*func)(void);
-       AST_RWLIST_ENTRY(ast_atexit) list;
+       int is_cleanup;
+       AST_LIST_ENTRY(ast_atexit) list;
 };
 
-static AST_RWLIST_HEAD_STATIC(atexits, ast_atexit);
+static AST_LIST_HEAD_STATIC(atexits, ast_atexit);
 
 struct timeval ast_startuptime;
 struct timeval ast_lastreloadtime;
@@ -222,7 +366,7 @@ static char *remotehostname;
 
 struct console consoles[AST_MAX_CONNECTS];
 
-char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
+char ast_defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
 
 static int ast_el_add_history(char *);
 static int ast_el_read_history(char *);
@@ -233,6 +377,7 @@ struct _cfg_paths {
        char module_dir[PATH_MAX];
        char spool_dir[PATH_MAX];
        char monitor_dir[PATH_MAX];
+       char recording_dir[PATH_MAX];
        char var_dir[PATH_MAX];
        char data_dir[PATH_MAX];
        char log_dir[PATH_MAX];
@@ -257,6 +402,7 @@ const char *ast_config_AST_CONFIG_FILE      = cfg_paths.config_file;
 const char *ast_config_AST_MODULE_DIR  = cfg_paths.module_dir;
 const char *ast_config_AST_SPOOL_DIR   = cfg_paths.spool_dir;
 const char *ast_config_AST_MONITOR_DIR = cfg_paths.monitor_dir;
+const char *ast_config_AST_RECORDING_DIR       = cfg_paths.recording_dir;
 const char *ast_config_AST_VAR_DIR     = cfg_paths.var_dir;
 const char *ast_config_AST_DATA_DIR    = cfg_paths.data_dir;
 const char *ast_config_AST_LOG_DIR     = cfg_paths.log_dir;
@@ -295,6 +441,7 @@ static pthread_t consolethread = AST_PTHREADT_NULL;
 static pthread_t mon_sig_flags;
 static int canary_pid = 0;
 static char canary_filename[128];
+static int multi_thread_safe;
 
 static char randompool[256];
 
@@ -323,7 +470,7 @@ void ast_register_file_version(const char *file, const char *version)
        work = ast_strdupa(version);
        work = ast_strip(ast_strip_quoted(work, "$", "$"));
        version_length = strlen(work) + 1;
-       
+
        if (!(new = ast_calloc(1, sizeof(*new) + version_length)))
                return;
 
@@ -385,9 +532,7 @@ const char *ast_file_version_find(const char *file)
        if (iterator)
                return iterator->version;
        return NULL;
-}      
-       
-
+}
 
 struct thread_list_t {
        AST_RWLIST_ENTRY(thread_list_t) list;
@@ -399,11 +544,13 @@ struct thread_list_t {
 static AST_RWLIST_HEAD_STATIC(thread_list, thread_list_t);
 
 void ast_register_thread(char *name)
-{ 
+{
        struct thread_list_t *new = ast_calloc(1, sizeof(*new));
 
        if (!new)
                return;
+
+       ast_assert(multi_thread_safe);
        new->id = pthread_self();
        new->lwp = ast_get_tid();
        new->name = name; /* steal the allocated memory for the thread name */
@@ -454,17 +601,17 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
        ast_cli(a->fd, "-----------------\n");
        ast_cli(a->fd, "  Version:                     %s\n", ast_get_version());
        ast_cli(a->fd, "  Build Options:               %s\n", S_OR(AST_BUILDOPTS, "(none)"));
-       if (option_maxcalls)
-               ast_cli(a->fd, "  Maximum calls:               %d (Current %d)\n", option_maxcalls, ast_active_channels());
+       if (ast_option_maxcalls)
+               ast_cli(a->fd, "  Maximum calls:               %d (Current %d)\n", ast_option_maxcalls, ast_active_channels());
        else
                ast_cli(a->fd, "  Maximum calls:               Not set\n");
-       if (option_maxfiles)
-               ast_cli(a->fd, "  Maximum open file handles:   %d\n", option_maxfiles); 
+       if (ast_option_maxfiles)
+               ast_cli(a->fd, "  Maximum open file handles:   %d\n", ast_option_maxfiles);
        else
                ast_cli(a->fd, "  Maximum open file handles:   Not set\n");
        ast_cli(a->fd, "  Verbosity:                   %d\n", option_verbose);
        ast_cli(a->fd, "  Debug level:                 %d\n", option_debug);
-       ast_cli(a->fd, "  Maximum load average:        %lf\n", option_maxload);
+       ast_cli(a->fd, "  Maximum load average:        %lf\n", ast_option_maxload);
 #if defined(HAVE_SYSINFO)
        ast_cli(a->fd, "  Minimum free memory:         %ld MB\n", option_minmemfree);
 #endif
@@ -479,7 +626,7 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
        ast_cli(a->fd, "  System:                      %s/%s built by %s on %s %s\n", ast_build_os, ast_build_kernel, ast_build_user, ast_build_machine, ast_build_date);
        ast_cli(a->fd, "  System name:                 %s\n", ast_config_AST_SYSTEM_NAME);
        ast_cli(a->fd, "  Entity ID:                   %s\n", eid_str);
-       ast_cli(a->fd, "  Default language:            %s\n", defaultlanguage);
+       ast_cli(a->fd, "  Default language:            %s\n", ast_defaultlanguage);
        ast_cli(a->fd, "  Language prefix:             %s\n", ast_language_is_prefix ? "Enabled" : "Disabled");
        ast_cli(a->fd, "  User name and group:         %s/%s\n", ast_config_AST_RUN_USER, ast_config_AST_RUN_GROUP);
        ast_cli(a->fd, "  Executable includes:         %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES) ? "Enabled" : "Disabled");
@@ -487,12 +634,13 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
        ast_cli(a->fd, "  Internal timing:             %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING) ? "Enabled" : "Disabled");
        ast_cli(a->fd, "  Transmit silence during rec: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE) ? "Enabled" : "Disabled");
        ast_cli(a->fd, "  Generic PLC:                 %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_GENERIC_PLC) ? "Enabled" : "Disabled");
+       ast_cli(a->fd, "  Min DTMF duration::          %u\n", option_dtmfminduration);
 
        ast_cli(a->fd, "\n* Subsystems\n");
        ast_cli(a->fd, "  -------------\n");
        ast_cli(a->fd, "  Manager (AMI):               %s\n", check_manager_enabled() ? "Enabled" : "Disabled");
        ast_cli(a->fd, "  Web Manager (AMI/HTTP):      %s\n", check_webmanager_enabled() ? "Enabled" : "Disabled");
-       ast_cli(a->fd, "  Call data records:           %s\n", check_cdr_enabled() ? "Enabled" : "Disabled");
+       ast_cli(a->fd, "  Call data records:           %s\n", ast_cdr_is_enabled() ? "Enabled" : "Disabled");
        ast_cli(a->fd, "  Realtime Architecture (ARA): %s\n", ast_realtime_enabled() ? "Enabled" : "Disabled");
 
        /*! \todo we could check musiconhold, voicemail, smdi, adsi, queues  */
@@ -522,7 +670,7 @@ static char *handle_show_threads(struct ast_cli_entry *e, int cmd, struct ast_cl
        switch (cmd) {
        case CLI_INIT:
                e->command = "core show threads";
-               e->usage = 
+               e->usage =
                        "Usage: core show threads\n"
                        "       List threads currently active in the system.\n";
                return NULL;
@@ -535,7 +683,7 @@ static char *handle_show_threads(struct ast_cli_entry *e, int cmd, struct ast_cl
                ast_cli(a->fd, "%p %d %s\n", (void *)cur->id, cur->lwp, cur->name);
                count++;
        }
-        AST_RWLIST_UNLOCK(&thread_list);
+       AST_RWLIST_UNLOCK(&thread_list);
        ast_cli(a->fd, "%d threads listed.\n", count);
        return CLI_SUCCESS;
 }
@@ -754,7 +902,7 @@ int64_t ast_profile(int i, int64_t delta)
 #elif defined(linux)
 static __inline uint64_t
 rdtsc(void)
-{ 
+{
        uint64_t rv;
 
        __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
@@ -877,7 +1025,7 @@ static char *handle_show_version_files(struct ast_cli_entry *e, int cmd, struct
        switch (cmd) {
        case CLI_INIT:
                e->command = "core show file version [like]";
-               e->usage = 
+               e->usage =
                        "Usage: core show file version [like <pattern>]\n"
                        "       Lists the revision numbers of the files used to build this copy of Asterisk.\n"
                        "       Optional regular expression pattern is used to filter the file list.\n";
@@ -945,39 +1093,77 @@ static char *handle_show_version_files(struct ast_cli_entry *e, int cmd, struct
 
 #endif /* ! LOW_MEMORY */
 
-int ast_register_atexit(void (*func)(void))
+static void publish_fully_booted(void)
 {
-       struct ast_atexit *ae;
-
-       if (!(ae = ast_calloc(1, sizeof(*ae))))
-               return -1;
-
-       ae->func = func;
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
 
-       ast_unregister_atexit(func);    
+       json_object = ast_json_pack("{s: s}",
+                       "Status", "Fully Booted");
+       ast_manager_publish_event("FullyBooted", EVENT_FLAG_SYSTEM, json_object);
+}
 
-       AST_RWLIST_WRLOCK(&atexits);
-       AST_RWLIST_INSERT_HEAD(&atexits, ae, list);
-       AST_RWLIST_UNLOCK(&atexits);
+static void ast_run_atexits(int run_cleanups)
+{
+       struct ast_atexit *ae;
 
-       return 0;
+       AST_LIST_LOCK(&atexits);
+       while ((ae = AST_LIST_REMOVE_HEAD(&atexits, list))) {
+               if (ae->func && (!ae->is_cleanup || run_cleanups)) {
+                       ae->func();
+               }
+               ast_free(ae);
+       }
+       AST_LIST_UNLOCK(&atexits);
 }
 
-void ast_unregister_atexit(void (*func)(void))
+static void __ast_unregister_atexit(void (*func)(void))
 {
-       struct ast_atexit *ae = NULL;
+       struct ast_atexit *ae;
 
-       AST_RWLIST_WRLOCK(&atexits);
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
                if (ae->func == func) {
-                       AST_RWLIST_REMOVE_CURRENT(list);
+                       AST_LIST_REMOVE_CURRENT(list);
+                       ast_free(ae);
                        break;
                }
        }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       AST_RWLIST_UNLOCK(&atexits);
+       AST_LIST_TRAVERSE_SAFE_END;
+}
+
+static int register_atexit(void (*func)(void), int is_cleanup)
+{
+       struct ast_atexit *ae;
+
+       ae = ast_calloc(1, sizeof(*ae));
+       if (!ae) {
+               return -1;
+       }
+       ae->func = func;
+       ae->is_cleanup = is_cleanup;
+
+       AST_LIST_LOCK(&atexits);
+       __ast_unregister_atexit(func);
+       AST_LIST_INSERT_HEAD(&atexits, ae, list);
+       AST_LIST_UNLOCK(&atexits);
 
-       free(ae);
+       return 0;
+}
+
+int ast_register_atexit(void (*func)(void))
+{
+       return register_atexit(func, 0);
+}
+
+int ast_register_cleanup(void (*func)(void))
+{
+       return register_atexit(func, 1);
+}
+
+void ast_unregister_atexit(void (*func)(void))
+{
+       AST_LIST_LOCK(&atexits);
+       __ast_unregister_atexit(func);
+       AST_LIST_UNLOCK(&atexits);
 }
 
 /* Sending commands from consoles back to the daemon requires a terminating NULL */
@@ -1008,7 +1194,8 @@ static struct sigaction ignore_sig_handler = {
 
 AST_MUTEX_DEFINE_STATIC(safe_system_lock);
 /*! \brief Keep track of how many threads are currently trying to wait*() on
- *  a child process */
+ *  a child process
+ */
 static unsigned int safe_system_level = 0;
 static struct sigaction safe_system_prev_handler;
 
@@ -1056,7 +1243,7 @@ int ast_safe_system(const char *s)
        pid = fork();
 #else
        pid = vfork();
-#endif 
+#endif
 
        if (pid == 0) {
 #ifdef HAVE_CAP
@@ -1082,7 +1269,7 @@ int ast_safe_system(const char *s)
                        if (res > -1) {
                                res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
                                break;
-                       } else if (errno != EINTR) 
+                       } else if (errno != EINTR)
                                break;
                }
        } else {
@@ -1104,6 +1291,11 @@ int ast_safe_system(const char *s)
 void ast_console_toggle_loglevel(int fd, int level, int state)
 {
        int x;
+
+       if (level >= NUMLOGLEVELS) {
+               level = NUMLOGLEVELS - 1;
+       }
+
        for (x = 0;x < AST_MAX_CONNECTS; x++) {
                if (fd == consoles[x].fd) {
                        /*
@@ -1149,7 +1341,7 @@ static void ast_network_puts_mutable(const char *string, int level)
                if (consoles[x].mute)
                        continue;
                if (consoles[x].fd > -1) {
-                       if (!consoles[x].levels[level]) 
+                       if (!consoles[x].levels[level])
                                fdprint(consoles[x].p[1], string);
                }
        }
@@ -1173,13 +1365,13 @@ static void ast_network_puts(const char *string)
 {
        int x;
        for (x = 0; x < AST_MAX_CONNECTS; x++) {
-               if (consoles[x].fd > -1) 
+               if (consoles[x].fd > -1)
                        fdprint(consoles[x].p[1], string);
        }
 }
 
 /*!
- * write the string to the console, and all attached
+ * \brief write the string to the console, and all attached
  * console clients
  */
 void ast_console_puts(const char *string)
@@ -1259,14 +1451,17 @@ static void *netconsole(void *vconsole)
 {
        struct console *con = vconsole;
        char hostname[MAXHOSTNAMELEN] = "";
-       char tmp[512];
+       char inbuf[512];
+       char outbuf[512];
+       const char * const end_buf = inbuf + sizeof(inbuf);
+       char *start_read = inbuf;
        int res;
        struct pollfd fds[2];
-       
+
        if (gethostname(hostname, sizeof(hostname)-1))
                ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
-       snprintf(tmp, sizeof(tmp), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version());
-       fdprint(con->fd, tmp);
+       snprintf(outbuf, sizeof(outbuf), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version());
+       fdprint(con->fd, outbuf);
        for (;;) {
                fds[0].fd = con->fd;
                fds[0].events = POLLIN;
@@ -1282,24 +1477,49 @@ static void *netconsole(void *vconsole)
                        continue;
                }
                if (fds[0].revents) {
-                       res = read_credentials(con->fd, tmp, sizeof(tmp) - 1, con);
-                       if (res < 1) {
+                       int cmds_read, bytes_read;
+                       if ((bytes_read = read_credentials(con->fd, start_read, end_buf - start_read, con)) < 1) {
                                break;
                        }
-                       tmp[res] = 0;
-                       if (strncmp(tmp, "cli quit after ", 15) == 0) {
-                               ast_cli_command_multiple_full(con->uid, con->gid, con->fd, res - 15, tmp + 15);
+                       /* XXX This will only work if it is the first command, and I'm not sure fixing it is worth the effort. */
+                       if (strncmp(inbuf, "cli quit after ", 15) == 0) {
+                               ast_cli_command_multiple_full(con->uid, con->gid, con->fd, bytes_read - 15, inbuf + 15);
                                break;
                        }
-                       ast_cli_command_multiple_full(con->uid, con->gid, con->fd, res, tmp);
+                       /* ast_cli_command_multiple_full will only process individual commands terminated by a
+                        * NULL and not trailing partial commands. */
+                       if (!(cmds_read = ast_cli_command_multiple_full(con->uid, con->gid, con->fd, bytes_read + start_read - inbuf, inbuf))) {
+                               /* No commands were read. We either have a short read on the first command
+                                * with space left, or a command that is too long */
+                               if (start_read + bytes_read < end_buf) {
+                                       start_read += bytes_read;
+                               } else {
+                                       ast_log(LOG_ERROR, "Command too long! Skipping\n");
+                                       start_read = inbuf;
+                               }
+                               continue;
+                       }
+                       if (start_read[bytes_read - 1] == '\0') {
+                               /* The read ended on a command boundary, start reading again at the head of inbuf */
+                               start_read = inbuf;
+                               continue;
+                       }
+                       /* If we get this far, we have left over characters that have not been processed.
+                        * Advance to the character after the last command read by ast_cli_command_multiple_full.
+                        * We are guaranteed to have at least cmds_read NULLs */
+                       while (cmds_read-- && (start_read = strchr(start_read, '\0'))) {
+                               start_read++;
+                       }
+                       memmove(inbuf, start_read, end_buf - start_read);
+                       start_read = end_buf - start_read + inbuf;
                }
                if (fds[1].revents) {
-                       res = read_credentials(con->p[0], tmp, sizeof(tmp), con);
+                       res = read_credentials(con->p[0], outbuf, sizeof(outbuf), con);
                        if (res < 1) {
                                ast_log(LOG_ERROR, "read returned %d\n", res);
                                break;
                        }
-                       res = write(con->fd, tmp, res);
+                       res = write(con->fd, outbuf, res);
                        if (res < 1)
                                break;
                }
@@ -1311,7 +1531,7 @@ static void *netconsole(void *vconsole)
        close(con->p[0]);
        close(con->p[1]);
        con->fd = -1;
-       
+
        return NULL;
 }
 
@@ -1400,14 +1620,14 @@ static int ast_makesocket(void)
        uid_t uid = -1;
        gid_t gid = -1;
 
-       for (x = 0; x < AST_MAX_CONNECTS; x++)  
+       for (x = 0; x < AST_MAX_CONNECTS; x++)
                consoles[x].fd = -1;
        unlink(ast_config_AST_SOCKET);
        ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
        if (ast_socket < 0) {
                ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
                return -1;
-       }               
+       }
        memset(&sunaddr, 0, sizeof(sunaddr));
        sunaddr.sun_family = AF_LOCAL;
        ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
@@ -1429,7 +1649,11 @@ static int ast_makesocket(void)
                ast_log(LOG_WARNING, "Unable to register network verboser?\n");
        }
 
-       ast_pthread_create_background(&lthread, NULL, listener, NULL);
+       if (ast_pthread_create_background(&lthread, NULL, listener, NULL)) {
+               ast_log(LOG_WARNING, "Unable to create listener thread.\n");
+               close(ast_socket);
+               return -1;
+       }
 
        if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
                struct passwd *pw;
@@ -1438,7 +1662,7 @@ static int ast_makesocket(void)
                else
                        uid = pw->pw_uid;
        }
-               
+
        if (!ast_strlen_zero(ast_config_AST_CTL_GROUP)) {
                struct group *grp;
                if ((grp = getgrnam(ast_config_AST_CTL_GROUP)) == NULL)
@@ -1468,7 +1692,7 @@ static int ast_tryconnect(void)
        int res;
        ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
        if (ast_consock < 0) {
-               ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
+               fprintf(stderr, "Unable to create socket: %s\n", strerror(errno));
                return 0;
        }
        memset(&sunaddr, 0, sizeof(sunaddr));
@@ -1484,10 +1708,10 @@ static int ast_tryconnect(void)
 }
 
 /*! \brief Urgent handler
-
- Called by soft_hangup to interrupt the poll, read, or other
- system call.  We don't actually need to do anything though.  
- Remember: Cannot EVER ast_log from within a signal handler 
+ *
+ * Called by soft_hangup to interrupt the poll, read, or other
+ * system call.  We don't actually need to do anything though.
+ * Remember: Cannot EVER ast_log from within a signal handler
  */
 static void _urg_handler(int num)
 {
@@ -1502,8 +1726,7 @@ static struct sigaction urg_handler = {
 static void _hup_handler(int num)
 {
        int a = 0, save_errno = errno;
-       if (option_verbose > 1) 
-               printf("Received HUP signal -- Reloading configs\n");
+       printf("Received HUP signal -- Reloading configs\n");
        if (restartnow)
                execvp(_argv[0], _argv);
        sig_flags.need_reload = 1;
@@ -1530,7 +1753,7 @@ static void _child_handler(int sig)
         */
        for (n = 0; wait4(-1, &status, WNOHANG, NULL) > 0; n++)
                ;
-       if (n == 0 && option_debug)     
+       if (n == 0 && option_debug)
                printf("Huh?  Child handler, but nobody there?\n");
        errno = save_errno;
 }
@@ -1544,22 +1767,22 @@ static struct sigaction child_handler = {
 static void set_ulimit(int value)
 {
        struct rlimit l = {0, 0};
-       
+
        if (value <= 0) {
                ast_log(LOG_WARNING, "Unable to change max files open to invalid value %i\n",value);
                return;
        }
-       
+
        l.rlim_cur = value;
        l.rlim_max = value;
-       
+
        if (setrlimit(RLIMIT_NOFILE, &l)) {
                ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n",strerror(errno));
                return;
        }
-       
+
        ast_log(LOG_NOTICE, "Setting max files open to %d\n",value);
-       
+
        return;
 }
 
@@ -1576,21 +1799,22 @@ static void set_icon(char *text)
                fprintf(stdout, "\033]1;%s\007", text);
 }
 
-/*! \brief We set ourselves to a high priority, that we might pre-empt everything
-   else.  If your PBX has heavy activity on it, this is a good thing.  */
+/*! \brief We set ourselves to a high priority, that we might pre-empt
+ * everything else.  If your PBX has heavy activity on it, this is a
+ * good thing.
+ */
 int ast_set_priority(int pri)
 {
        struct sched_param sched;
        memset(&sched, 0, sizeof(sched));
 #ifdef __linux__
-       if (pri) {  
+       if (pri) {
                sched.sched_priority = 10;
                if (sched_setscheduler(0, SCHED_RR, &sched)) {
                        ast_log(LOG_WARNING, "Unable to set high priority\n");
                        return -1;
                } else
-                       if (option_verbose)
-                               ast_verbose("Set to realtime thread\n");
+                       ast_verb(1, "Set to realtime thread\n");
        } else {
                sched.sched_priority = 0;
                /* According to the manpage, these parameters can never fail. */
@@ -1602,8 +1826,7 @@ int ast_set_priority(int pri)
                        ast_log(LOG_WARNING, "Unable to set high priority\n");
                        return -1;
                } else
-                       if (option_verbose)
-                               ast_verbose("Set to high priority\n");
+                       ast_verb(1, "Set to high priority\n");
        } else {
                /* According to the manpage, these parameters can never fail. */
                setpriority(PRIO_PROCESS, 0, 0);
@@ -1612,17 +1835,6 @@ int ast_set_priority(int pri)
        return 0;
 }
 
-static void ast_run_atexits(void)
-{
-       struct ast_atexit *ae;
-       AST_RWLIST_RDLOCK(&atexits);
-       AST_RWLIST_TRAVERSE(&atexits, ae, list) {
-               if (ae->func) 
-                       ae->func();
-       }
-       AST_RWLIST_UNLOCK(&atexits);
-}
-
 static int can_safely_quit(shutdown_nice_t niceness, int restart);
 static void really_quit(int num, shutdown_nice_t niceness, int restart);
 
@@ -1653,18 +1865,22 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart)
         * the atexit handlers, otherwise this would be a bit early. */
        ast_cdr_engine_term();
 
+       /* Shutdown the message queue for the technology agnostic message channel.
+        * This has to occur before we pause shutdown pending ast_undestroyed_channels. */
+       ast_msg_shutdown();
+
        if (niceness == SHUTDOWN_NORMAL) {
                time_t s, e;
                /* Begin shutdown routine, hanging up active channels */
                ast_begin_shutdown(1);
-               if (option_verbose && ast_opt_console) {
-                       ast_verbose("Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
+               if (ast_opt_console) {
+                       ast_verb(0, "Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
                }
                time(&s);
                for (;;) {
                        time(&e);
                        /* Wait up to 15 seconds for all channels to go away */
-                       if ((e - s) > 15 || !ast_active_channels() || shuttingdown != niceness) {
+                       if ((e - s) > 15 || !ast_undestroyed_channels() || shuttingdown != niceness) {
                                break;
                        }
                        /* Sleep 1/10 of a second */
@@ -1674,11 +1890,11 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart)
                if (niceness != SHUTDOWN_REALLY_NICE) {
                        ast_begin_shutdown(0);
                }
-               if (option_verbose && ast_opt_console) {
-                       ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
+               if (ast_opt_console) {
+                       ast_verb(0, "Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
                }
                for (;;) {
-                       if (!ast_active_channels() || shuttingdown != niceness) {
+                       if (!ast_undestroyed_channels() || shuttingdown != niceness) {
                                break;
                        }
                        sleep(1);
@@ -1686,11 +1902,12 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart)
        }
 
        /* Re-acquire lock and check if someone changed the niceness, in which
-        * case someone else has taken over the shutdown. */
+        * case someone else has taken over the shutdown.
+        */
        ast_mutex_lock(&safe_system_lock);
        if (shuttingdown != niceness) {
-               if (shuttingdown == NOT_SHUTTING_DOWN && option_verbose && ast_opt_console) {
-                       ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
+               if (shuttingdown == NOT_SHUTTING_DOWN && ast_opt_console) {
+                       ast_verb(0, "Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
                }
                ast_mutex_unlock(&safe_system_lock);
                return 0;
@@ -1701,9 +1918,14 @@ static int can_safely_quit(shutdown_nice_t niceness, int restart)
        return 1;
 }
 
+/*! Called when exiting is certain. */
 static void really_quit(int num, shutdown_nice_t niceness, int restart)
 {
-       if (niceness >= SHUTDOWN_NICE) {
+       int active_channels;
+       struct ast_json *json_object = NULL;
+       int run_cleanups = niceness >= SHUTDOWN_NICE;
+
+       if (run_cleanups) {
                ast_module_shutdown();
        }
 
@@ -1729,41 +1951,57 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
                        }
                }
        }
-       if (option_verbose)
-               ast_verbose("Executing last minute cleanups\n");
-       ast_run_atexits();
-       /* Called on exit */
-       if (option_verbose && ast_opt_console)
-               ast_verbose("Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
+       active_channels = ast_active_channels();
+       /* Don't publish messages if we're a remote console - we won't have all of the Stasis
+        * topics or message types
+        */
+       if (!ast_opt_remote) {
+               json_object = ast_json_pack("{s: s, s: s}",
+                               "Shutdown", active_channels ? "Uncleanly" : "Cleanly",
+                               "Restart", restart ? "True" : "False");
+               ast_manager_publish_event("Shutdown", EVENT_FLAG_SYSTEM, json_object);
+               ast_json_unref(json_object);
+               json_object = NULL;
+       }
+       ast_verb(0, "Asterisk %s ending (%d).\n",
+               active_channels ? "uncleanly" : "cleanly", num);
+
+       ast_verb(0, "Executing last minute cleanups\n");
+       ast_run_atexits(run_cleanups);
+
        ast_debug(1, "Asterisk ending (%d).\n", num);
-       manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\nRestart: %s\r\n", ast_active_channels() ? "Uncleanly" : "Cleanly", restart ? "True" : "False");
        if (ast_socket > -1) {
                pthread_cancel(lthread);
                close(ast_socket);
                ast_socket = -1;
                unlink(ast_config_AST_SOCKET);
+               pthread_kill(lthread, SIGURG);
+               pthread_join(lthread, NULL);
        }
        if (ast_consock > -1)
                close(ast_consock);
        if (!ast_opt_remote)
                unlink(ast_config_AST_PID);
+       if (sig_alert_pipe[0])
+               close(sig_alert_pipe[0]);
+       if (sig_alert_pipe[1])
+               close(sig_alert_pipe[1]);
        printf("%s", term_quit());
        if (restart) {
                int i;
-               if (option_verbose || ast_opt_console)
-                       ast_verbose("Preparing for Asterisk restart...\n");
+               ast_verb(0, "Preparing for Asterisk restart...\n");
                /* Mark all FD's for closing on exec */
                for (i = 3; i < 32768; i++) {
                        fcntl(i, F_SETFD, FD_CLOEXEC);
                }
-               if (option_verbose || ast_opt_console)
-                       ast_verbose("Asterisk is now restarting...\n");
+               ast_verb(0, "Asterisk is now restarting...\n");
                restartnow = 1;
 
                /* close logger */
                close_logger();
+               clean_time_zones();
 
-               /* If there is a consolethread running send it a SIGHUP 
+               /* If there is a consolethread running send it a SIGHUP
                   so it can execvp, otherwise we can do it ourselves */
                if ((consolethread != AST_PTHREADT_NULL) && (consolethread != pthread_self())) {
                        pthread_kill(consolethread, SIGHUP);
@@ -1771,10 +2009,11 @@ static void really_quit(int num, shutdown_nice_t niceness, int restart)
                        sleep(2);
                } else
                        execvp(_argv[0], _argv);
-       
+
        } else {
                /* close logger */
                close_logger();
+               clean_time_zones();
        }
 
        exit(0);
@@ -1798,43 +2037,128 @@ static void __remote_quit_handler(int num)
        sig_flags.need_quit = 1;
 }
 
-static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
+static const char *fix_header(char *outbuf, int maxout, const char *s, char level)
 {
-       const char *c;
+       const char *cmp;
 
-       /* Check for verboser preamble */
-       if (*s == 127) {
-               s++;
+       switch (level) {
+       case 0: *outbuf = '\0';
+               return s;
+       case 1: cmp = VERBOSE_PREFIX_1;
+               break;
+       case 2: cmp = VERBOSE_PREFIX_2;
+               break;
+       case 3: cmp = VERBOSE_PREFIX_3;
+               break;
+       default: cmp = VERBOSE_PREFIX_4;
+               break;
        }
 
        if (!strncmp(s, cmp, strlen(cmp))) {
-               c = s + strlen(cmp);
-               term_color(outbuf, cmp, COLOR_GRAY, 0, maxout);
-               return c;
+               s += strlen(cmp);
        }
-       return NULL;
+       term_color(outbuf, cmp, COLOR_GRAY, 0, maxout);
+
+       return s;
 }
 
-static void console_verboser(const char *s)
+struct console_state_data {
+       char verbose_line_level;
+};
+
+static int console_state_init(void *ptr)
 {
-       char tmp[80];
-       const char *c = NULL;
+       struct console_state_data *state = ptr;
+       state->verbose_line_level = 0;
+       return 0;
+}
 
-       if ((c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_4)) ||
-           (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_3)) ||
-           (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_2)) ||
-           (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_1))) {
-               fputs(tmp, stdout);
-               fputs(c, stdout);
-       } else {
-               if (*s == 127) {
+AST_THREADSTORAGE_CUSTOM(console_state, console_state_init, ast_free_ptr);
+
+/* These gymnastics are due to platforms which designate char as unsigned by
+ * default. Level is the negative character -- offset by 1, because \0 is the
+ * EOS delimiter. */
+#define VERBOSE_MAGIC2LEVEL(x) (((char) -*(signed char *) (x)) - 1)
+#define VERBOSE_HASMAGIC(x)    (*(signed char *) (x) < 0)
+
+static int console_print(const char *s, int local)
+{
+       struct console_state_data *state =
+               ast_threadstorage_get(&console_state, sizeof(*state));
+
+       char prefix[80];
+       const char *c;
+       int num, res = 0;
+       unsigned int newline;
+
+       do {
+               if (VERBOSE_HASMAGIC(s)) {
+                       /* always use the given line's level, otherwise
+                          we'll use the last line's level */
+                       state->verbose_line_level = VERBOSE_MAGIC2LEVEL(s);
+                       /* move past magic */
                        s++;
+
+                       if (local) {
+                               s = fix_header(prefix, sizeof(prefix), s,
+                                              state->verbose_line_level);
+                       }
+               } else {
+                       *prefix = '\0';
                }
-               fputs(s, stdout);
+               c = s;
+
+               /* for a given line separate on verbose magic, newline, and eol */
+               if ((s = strchr(c, '\n'))) {
+                       ++s;
+                       newline = 1;
+               } else {
+                       s = strchr(c, '\0');
+                       newline = 0;
+               }
+
+               /* check if we should write this line after calculating begin/end
+                  so we process the case of a higher level line embedded within
+                  two lower level lines */
+               if (state->verbose_line_level > option_verbose) {
+                       continue;
+               }
+
+               if (local && !ast_strlen_zero(prefix)) {
+                       fputs(prefix, stdout);
+               }
+
+               num = s - c;
+               if (fwrite(c, sizeof(char), num, stdout) < num) {
+                       break;
+               }
+
+               if (!res) {
+                       /* if at least some info has been written
+                          we'll want to return true */
+                       res = 1;
+               }
+       } while (*s);
+
+       if (newline) {
+               /* if ending on a newline then reset last level to zero
+                   since what follows may be not be logging output */
+               state->verbose_line_level = 0;
+       }
+
+       if (res) {
+               fflush(stdout);
+       }
+
+       return res;
+}
+
+static void console_verboser(const char *s)
+{
+       if (!console_print(s, 1)) {
+               return;
        }
 
-       fflush(stdout);
-       
        /* Wake up a poll()ing console */
        if (ast_opt_console && consolethread != AST_PTHREADT_NULL) {
                pthread_kill(consolethread, SIGURG);
@@ -1846,7 +2170,7 @@ static int ast_all_zeros(char *s)
        while (*s) {
                if (*s > 32)
                        return 0;
-               s++;  
+               s++;
        }
        return 1;
 }
@@ -1865,7 +2189,7 @@ static void consolehandler(char *s)
                        ast_safe_system(s+1);
                else
                        ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
-       } else 
+       } else
                ast_cli_command(STDOUT_FILENO, s);
 }
 
@@ -1876,6 +2200,11 @@ static int remoteconsolehandler(char *s)
        /* Called when readline data is available */
        if (!ast_all_zeros(s))
                ast_el_add_history(s);
+
+       while (isspace(*s)) {
+               s++;
+       }
+
        /* The real handler for bang */
        if (s[0] == '!') {
                if (s[1])
@@ -1883,8 +2212,35 @@ static int remoteconsolehandler(char *s)
                else
                        ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
                ret = 1;
-       }
-       if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
+       } else if (strncasecmp(s, "core set verbose ", 17) == 0) {
+               int old_verbose = option_verbose;
+               if (strncasecmp(s + 17, "atleast ", 8) == 0) {
+                       int tmp;
+                       if (sscanf(s + 25, "%d", &tmp) != 1) {
+                               fprintf(stderr, "Usage: core set verbose [atleast] <level>\n");
+                       } else {
+                               if (tmp > option_verbose) {
+                                       option_verbose = tmp;
+                               }
+                               if (old_verbose != option_verbose) {
+                                       fprintf(stdout, "Set remote console verbosity from %d to %d\n", old_verbose, option_verbose);
+                               } else {
+                                       fprintf(stdout, "Verbosity level unchanged.\n");
+                               }
+                       }
+               } else {
+                       if (sscanf(s + 17, "%d", &option_verbose) != 1) {
+                               fprintf(stderr, "Usage: core set verbose [atleast] <level>\n");
+                       } else {
+                               if (old_verbose != option_verbose) {
+                                       fprintf(stdout, "Set remote console verbosity to %d\n", option_verbose);
+                               } else {
+                                       fprintf(stdout, "Verbosity level unchanged.\n");
+                               }
+                       }
+               }
+               ret = 1;
+       } else if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
            (s[4] == '\0' || isspace(s[4]))) {
                quit_handler(0, SHUTDOWN_FAST, 0);
                ret = 1;
@@ -1898,7 +2254,7 @@ static char *handle_version(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
        switch (cmd) {
        case CLI_INIT:
                e->command = "core show version";
-               e->usage = 
+               e->usage =
                        "Usage: core show version\n"
                        "       Shows Asterisk version information.\n";
                return NULL;
@@ -1929,7 +2285,7 @@ static char *handle_stop_now(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
        switch (cmd) {
        case CLI_INIT:
                e->command = "core stop now";
-               e->usage = 
+               e->usage =
                        "Usage: core stop now\n"
                        "       Shuts down a running Asterisk immediately, hanging up all active calls .\n";
                return NULL;
@@ -1948,7 +2304,7 @@ static char *handle_stop_gracefully(struct ast_cli_entry *e, int cmd, struct ast
        switch (cmd) {
        case CLI_INIT:
                e->command = "core stop gracefully";
-               e->usage = 
+               e->usage =
                        "Usage: core stop gracefully\n"
                        "       Causes Asterisk to not accept new calls, and exit when all\n"
                        "       active calls have terminated normally.\n";
@@ -1968,7 +2324,7 @@ static char *handle_stop_when_convenient(struct ast_cli_entry *e, int cmd, struc
        switch (cmd) {
        case CLI_INIT:
                e->command = "core stop when convenient";
-               e->usage = 
+               e->usage =
                        "Usage: core stop when convenient\n"
                        "       Causes Asterisk to perform a shutdown when all active calls have ended.\n";
                return NULL;
@@ -1988,7 +2344,7 @@ static char *handle_restart_now(struct ast_cli_entry *e, int cmd, struct ast_cli
        switch (cmd) {
        case CLI_INIT:
                e->command = "core restart now";
-               e->usage = 
+               e->usage =
                        "Usage: core restart now\n"
                        "       Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
                        "       restart.\n";
@@ -2008,7 +2364,7 @@ static char *handle_restart_gracefully(struct ast_cli_entry *e, int cmd, struct
        switch (cmd) {
        case CLI_INIT:
                e->command = "core restart gracefully";
-               e->usage = 
+               e->usage =
                        "Usage: core restart gracefully\n"
                        "       Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n"
                        "       restart when all active calls have ended.\n";
@@ -2028,7 +2384,7 @@ static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, st
        switch (cmd) {
        case CLI_INIT:
                e->command = "core restart when convenient";
-               e->usage = 
+               e->usage =
                        "Usage: core restart when convenient\n"
                        "       Causes Asterisk to perform a cold restart when all active calls have ended.\n";
                return NULL;
@@ -2050,7 +2406,7 @@ static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_
        switch (cmd) {
        case CLI_INIT:
                e->command = "core abort shutdown";
-               e->usage = 
+               e->usage =
                        "Usage: core abort shutdown\n"
                        "       Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
                        "       call operations.\n";
@@ -2080,7 +2436,7 @@ static char *handle_bang(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
        switch (cmd) {
        case CLI_INIT:
                e->command = "!";
-               e->usage = 
+               e->usage =
                        "Usage: !<command>\n"
                        "       Executes a given shell command\n";
                return NULL;
@@ -2120,7 +2476,7 @@ static char *show_warranty(struct ast_cli_entry *e, int cmd, struct ast_cli_args
        switch (cmd) {
        case CLI_INIT:
                e->command = "core show warranty";
-               e->usage = 
+               e->usage =
                        "Usage: core show warranty\n"
                        "       Shows the warranty (if any) for this copy of Asterisk.\n";
                return NULL;
@@ -2157,7 +2513,7 @@ static char *show_license(struct ast_cli_entry *e, int cmd, struct ast_cli_args
        switch (cmd) {
        case CLI_INIT:
                e->command = "core show license";
-               e->usage = 
+               e->usage =
                        "Usage: core show license\n"
                        "       Shows the license(s) for this copy of Asterisk.\n";
                return NULL;
@@ -2174,14 +2530,25 @@ static char *show_license(struct ast_cli_entry *e, int cmd, struct ast_cli_args
 
 #define ASTERISK_PROMPT2 "%s*CLI> "
 
-static struct ast_cli_entry cli_asterisk[] = {
-       AST_CLI_DEFINE(handle_abort_shutdown, "Cancel a running shutdown"),
+/*!
+ * \brief Shutdown Asterisk CLI commands.
+ *
+ * \note These CLI commands cannot be unregistered at shutdown
+ * because one of them is likely the reason for the shutdown.
+ * The CLI generates a warning if a command is in-use when it is
+ * unregistered.
+ */
+static struct ast_cli_entry cli_asterisk_shutdown[] = {
        AST_CLI_DEFINE(handle_stop_now, "Shut down Asterisk immediately"),
        AST_CLI_DEFINE(handle_stop_gracefully, "Gracefully shut down Asterisk"),
        AST_CLI_DEFINE(handle_stop_when_convenient, "Shut down Asterisk at empty call volume"),
-       AST_CLI_DEFINE(handle_restart_now, "Restart Asterisk immediately"), 
+       AST_CLI_DEFINE(handle_restart_now, "Restart Asterisk immediately"),
        AST_CLI_DEFINE(handle_restart_gracefully, "Restart Asterisk gracefully"),
        AST_CLI_DEFINE(handle_restart_when_convenient, "Restart Asterisk at empty call volume"),
+};
+
+static struct ast_cli_entry cli_asterisk[] = {
+       AST_CLI_DEFINE(handle_abort_shutdown, "Cancel a running shutdown"),
        AST_CLI_DEFINE(show_warranty, "Show the warranty (if any) for this copy of Asterisk"),
        AST_CLI_DEFINE(show_license, "Show the license(s) for this copy of Asterisk"),
        AST_CLI_DEFINE(handle_version, "Display version info"),
@@ -2223,7 +2590,7 @@ static int ast_el_read_char(EditLine *editline, char *cp)
                                break;
                        if (errno == EINTR)
                                continue;
-                       ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
+                       fprintf(stderr, "poll failed: %s\n", strerror(errno));
                        break;
                }
 
@@ -2236,7 +2603,6 @@ static int ast_el_read_char(EditLine *editline, char *cp)
                        }
                }
                if (fds[0].revents) {
-                       char *tmp;
                        res = read(ast_consock, buf, sizeof(buf) - 1);
                        /* if the remote side disappears exit */
                        if (res < 1) {
@@ -2254,7 +2620,7 @@ static int ast_el_read_char(EditLine *editline, char *cp)
                                                        WELCOME_MESSAGE;
                                                        if (!ast_opt_mute)
                                                                fdsend(ast_consock, "logger mute silent");
-                                                       else 
+                                                       else
                                                                printf("log and verbose output currently muted ('logger mute' to unmute)\n");
                                                        break;
                                                } else
@@ -2265,26 +2631,19 @@ static int ast_el_read_char(EditLine *editline, char *cp)
                                                quit_handler(0, SHUTDOWN_FAST, 0);
                                        }
                                }
+                               continue;
                        }
 
                        buf[res] = '\0';
 
-                       /* Strip preamble from asynchronous events, too */
-                       for (tmp = buf; *tmp; tmp++) {
-                               if (*tmp == 127) {
-                                       memmove(tmp, tmp + 1, strlen(tmp));
-                                       tmp--;
-                                       res--;
-                               }
-                       }
-
                        /* Write over the CLI prompt */
                        if (!ast_opt_exec && !lastpos) {
                                if (write(STDOUT_FILENO, "\r\e[0K", 5) < 0) {
                                }
                        }
-                       if (write(STDOUT_FILENO, buf, res) < 0) {
-                       }
+
+                       console_print(buf, 0);
+
                        if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (buf[res-2] == '\n'))) {
                                *cp = CC_REFRESH;
                                return(1);
@@ -2305,7 +2664,6 @@ static char *cli_prompt(EditLine *editline)
        char *pfmt;
        int color_used = 0;
        static int cli_prompt_changes = 0;
-       char term_code[20];
        struct passwd *pw;
        struct group *gr;
 
@@ -2332,10 +2690,10 @@ static char *cli_prompt(EditLine *editline)
                                case 'C': /* color */
                                        t++;
                                        if (sscanf(t, "%30d;%30d%n", &fgcolor, &bgcolor, &i) == 2) {
-                                               ast_str_append(&prompt, 0, "%s", term_color_code(term_code, fgcolor, bgcolor, sizeof(term_code)));
+                                               ast_term_color_code(&prompt, fgcolor, bgcolor);
                                                t += i - 1;
                                        } else if (sscanf(t, "%30d%n", &fgcolor, &i) == 1) {
-                                               ast_str_append(&prompt, 0, "%s", term_color_code(term_code, fgcolor, 0, sizeof(term_code)));
+                                               ast_term_color_code(&prompt, fgcolor, 0);
                                                t += i - 1;
                                        }
 
@@ -2415,7 +2773,7 @@ static char *cli_prompt(EditLine *editline)
                }
                if (color_used) {
                        /* Force colors back to normal at end */
-                       ast_str_append(&prompt, 0, "%s", term_color_code(term_code, 0, 0, sizeof(term_code)));
+                       ast_term_color_code(&prompt, 0, 0);
                }
        } else if (remotehostname) {
                ast_str_set(&prompt, 0, ASTERISK_PROMPT2, remotehostname);
@@ -2423,48 +2781,65 @@ static char *cli_prompt(EditLine *editline)
                ast_str_set(&prompt, 0, "%s", ASTERISK_PROMPT);
        }
 
-       return ast_str_buffer(prompt);  
+       return ast_str_buffer(prompt);
+}
+
+static void destroy_match_list(char **match_list, int matches)
+{
+       if (match_list) {
+               int idx;
+
+               for (idx = 0; idx < matches; ++idx) {
+                       ast_free(match_list[idx]);
+               }
+               ast_free(match_list);
+       }
 }
 
 static char **ast_el_strtoarr(char *buf)
 {
-       char **match_list = NULL, **match_list_tmp, *retstr;
-       size_t match_list_len;
+       char *retstr;
+       char **match_list = NULL;
+       char **new_list;
+       size_t match_list_len = 1;
        int matches = 0;
 
-       match_list_len = 1;
-       while ( (retstr = strsep(&buf, " ")) != NULL) {
-
-               if (!strcmp(retstr, AST_CLI_COMPLETE_EOF))
+       while ((retstr = strsep(&buf, " "))) {
+               if (!strcmp(retstr, AST_CLI_COMPLETE_EOF)) {
                        break;
+               }
                if (matches + 1 >= match_list_len) {
                        match_list_len <<= 1;
-                       if ((match_list_tmp = ast_realloc(match_list, match_list_len * sizeof(char *)))) {
-                               match_list = match_list_tmp;
-                       } else {
-                               if (match_list)
-                                       ast_free(match_list);
-                               return (char **) NULL;
+                       new_list = ast_realloc(match_list, match_list_len * sizeof(char *));
+                       if (!new_list) {
+                               destroy_match_list(match_list, matches);
+                               return NULL;
                        }
+                       match_list = new_list;
                }
 
-               match_list[matches++] = ast_strdup(retstr);
+               retstr = ast_strdup(retstr);
+               if (!retstr) {
+                       destroy_match_list(match_list, matches);
+                       return NULL;
+               }
+               match_list[matches++] = retstr;
        }
 
-       if (!match_list)
-               return (char **) NULL;
+       if (!match_list) {
+               return NULL;
+       }
 
        if (matches >= match_list_len) {
-               if ((match_list_tmp = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *)))) {
-                       match_list = match_list_tmp;
-               } else {
-                       if (match_list)
-                               ast_free(match_list);
-                       return (char **) NULL;
+               new_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *));
+               if (!new_list) {
+                       destroy_match_list(match_list, matches);
+                       return NULL;
                }
+               match_list = new_list;
        }
 
-       match_list[matches] = (char *) NULL;
+       match_list[matches] = NULL;
 
        return match_list;
 }
@@ -2555,32 +2930,39 @@ static char *cli_complete(EditLine *editline, int ch)
        len = lf->cursor - ptr;
 
        if (ast_opt_remote) {
-               snprintf(buf, sizeof(buf), "_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr); 
+               snprintf(buf, sizeof(buf), "_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr);
                fdsend(ast_consock, buf);
-               res = read(ast_consock, buf, sizeof(buf) - 1);
+               if ((res = read(ast_consock, buf, sizeof(buf) - 1)) < 0) {
+                       return (char*)(CC_ERROR);
+               }
                buf[res] = '\0';
                nummatches = atoi(buf);
 
                if (nummatches > 0) {
                        char *mbuf;
+                       char *new_mbuf;
                        int mlen = 0, maxmbuf = 2048;
-                       /* Start with a 2048 byte buffer */                     
+
+                       /* Start with a 2048 byte buffer */
                        if (!(mbuf = ast_malloc(maxmbuf))) {
-                               lf->cursor[0] = savechr;
+                               *((char *) lf->cursor) = savechr;
                                return (char *)(CC_ERROR);
                        }
-                       snprintf(buf, sizeof(buf), "_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr); 
+                       snprintf(buf, sizeof(buf), "_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr);
                        fdsend(ast_consock, buf);
                        res = 0;
                        mbuf[0] = '\0';
                        while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) {
                                if (mlen + 1024 > maxmbuf) {
                                        /* Every step increment buffer 1024 bytes */
-                                       maxmbuf += 1024;                                        
-                                       if (!(mbuf = ast_realloc(mbuf, maxmbuf))) {
-                                               lf->cursor[0] = savechr;
+                                       maxmbuf += 1024;
+                                       new_mbuf = ast_realloc(mbuf, maxmbuf);
+                                       if (!new_mbuf) {
+                                               ast_free(mbuf);
+                                               *((char *) lf->cursor) = savechr;
                                                return (char *)(CC_ERROR);
                                        }
+                                       mbuf = new_mbuf;
                                }
                                /* Only read 1024 bytes at a time */
                                res = read(ast_consock, mbuf + mlen, 1024);
@@ -2630,7 +3012,7 @@ static char *cli_complete(EditLine *editline, int ch)
                                fprintf(stdout, "\n");
                                ast_cli_display_match_list(matches, nummatches, maxlen);
                                retval = CC_REDISPLAY;
-                       } else { 
+                       } else {
                                el_insertstr(editline," ");
                                retval = CC_REFRESH;
                        }
@@ -2640,7 +3022,7 @@ static char *cli_complete(EditLine *editline, int ch)
                ast_free(matches);
        }
 
-       lf->cursor[0] = savechr;
+       *((char *) lf->cursor) = savechr;
 
        return (char *)(long)retval;
 }
@@ -2664,7 +3046,7 @@ static int ast_el_initialize(void)
        el = el_init("asterisk", stdin, stdout, stderr);
        el_set(el, EL_PROMPT, cli_prompt);
 
-       el_set(el, EL_EDITMODE, 1);             
+       el_set(el, EL_EDITMODE, 1);
        el_set(el, EL_EDITOR, editor);
        el_hist = history_init();
        if (!el || !el_hist)
@@ -2703,12 +3085,23 @@ static int ast_el_initialize(void)
 static int ast_el_add_history(char *buf)
 {
        HistEvent ev;
+       char *stripped_buf;
 
-       if (el_hist == NULL || el == NULL)
+       if (el_hist == NULL || el == NULL) {
                ast_el_initialize();
-       if (strlen(buf) > (MAX_HISTORY_COMMAND_LENGTH - 1))
+       }
+       if (strlen(buf) > (MAX_HISTORY_COMMAND_LENGTH - 1)) {
+               return 0;
+       }
+
+       stripped_buf = ast_strip(ast_strdupa(buf));
+
+       /* HISTCONTROL=ignoredups */
+       if (!history(el_hist, &ev, H_FIRST) && strcmp(ev.str, stripped_buf) == 0) {
                return 0;
-       return (history(el_hist, &ev, H_ENTER, ast_strip(ast_strdupa(buf))));
+       }
+
+       return history(el_hist, &ev, H_ENTER, stripped_buf);
 }
 
 static int ast_el_write_history(char *filename)
@@ -2723,29 +3116,13 @@ static int ast_el_write_history(char *filename)
 
 static int ast_el_read_history(char *filename)
 {
-       char buf[MAX_HISTORY_COMMAND_LENGTH];
-       FILE *f;
-       int ret = -1;
+       HistEvent ev;
 
-       if (el_hist == NULL || el == NULL)
+       if (el_hist == NULL || el == NULL) {
                ast_el_initialize();
-
-       if ((f = fopen(filename, "r")) == NULL)
-               return ret;
-
-       while (!feof(f)) {
-               if (!fgets(buf, sizeof(buf), f))
-                       break;
-               if (!strcmp(buf, "_HiStOrY_V2_\n"))
-                       continue;
-               if (ast_all_zeros(buf))
-                       continue;
-               if ((ret = ast_el_add_history(buf)) == -1)
-                       break;
        }
-       fclose(f);
 
-       return ret;
+       return history(el_hist, &ev, H_LOAD, filename);
 }
 
 static void ast_remotecontrol(char *data)
@@ -2773,7 +3150,7 @@ static void ast_remotecontrol(char *data)
        }
        if (data) {
                char prefix[] = "cli quit after ";
-               char *tmp = alloca(strlen(data) + strlen(prefix) + 1);
+               char *tmp = ast_alloca(strlen(data) + strlen(prefix) + 1);
                sprintf(tmp, "%s%s", prefix, data);
                if (write(ast_consock, tmp, strlen(tmp) + 1) < 0) {
                        ast_log(LOG_ERROR, "write() failed: %s\n", strerror(errno));
@@ -2795,22 +3172,20 @@ static void ast_remotecontrol(char *data)
        else
                pid = -1;
        if (!data) {
-               char tmp[80];
-               snprintf(tmp, sizeof(tmp), "core set verbose atleast %d", option_verbose);
-               fdsend(ast_consock, tmp);
-               snprintf(tmp, sizeof(tmp), "core set debug atleast %d", option_debug);
-               fdsend(ast_consock, tmp);
-               if (!ast_opt_mute)
+               if (!ast_opt_mute) {
                        fdsend(ast_consock, "logger mute silent");
-               else 
+               } else {
                        printf("log and verbose output currently muted ('logger mute' to unmute)\n");
+               }
        }
 
        if (ast_opt_exec && data) {  /* hack to print output then exit if asterisk -rx is used */
+               int linefull = 1, prev_linefull = 1, prev_line_verbose = 0;
                struct pollfd fds;
                fds.fd = ast_consock;
                fds.events = POLLIN;
                fds.revents = 0;
+
                while (ast_poll(&fds, 1, 60000) > 0) {
                        char buffer[512] = "", *curline = buffer, *nextline;
                        int not_written = 1;
@@ -2824,18 +3199,34 @@ static void ast_remotecontrol(char *data)
                        }
 
                        do {
+                               prev_linefull = linefull;
                                if ((nextline = strchr(curline, '\n'))) {
+                                       linefull = 1;
                                        nextline++;
                                } else {
+                                       linefull = 0;
                                        nextline = strchr(curline, '\0');
                                }
 
                                /* Skip verbose lines */
-                               if (*curline != 127) {
+                               /* Prev line full? | Line is verbose | Last line verbose? | Print
+                                * TRUE            | TRUE*           | TRUE               | FALSE
+                                * TRUE            | TRUE*           | FALSE              | FALSE
+                                * TRUE            | FALSE*          | TRUE               | TRUE
+                                * TRUE            | FALSE*          | FALSE              | TRUE
+                                * FALSE           | TRUE            | TRUE*              | FALSE
+                                * FALSE           | TRUE            | FALSE*             | TRUE
+                                * FALSE           | FALSE           | TRUE*              | FALSE
+                                * FALSE           | FALSE           | FALSE*             | TRUE
+                                */
+                               if ((!prev_linefull && !prev_line_verbose) || (prev_linefull && *curline > 0)) {
+                                       prev_line_verbose = 0;
                                        not_written = 0;
                                        if (write(STDOUT_FILENO, curline, nextline - curline) < 0) {
                                                ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
                                        }
+                               } else {
+                                       prev_line_verbose = 1;
                                }
                                curline = nextline;
                        } while (!ast_strlen_zero(curline));
@@ -2850,7 +3241,7 @@ static void ast_remotecontrol(char *data)
 
        ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
        remotehostname = hostname;
-       if (getenv("HOME")) 
+       if (getenv("HOME"))
                snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
        if (el_hist == NULL || el == NULL)
                ast_el_initialize();
@@ -2874,14 +3265,6 @@ static void ast_remotecontrol(char *data)
                        if (ebuf[strlen(ebuf)-1] == '\n')
                                ebuf[strlen(ebuf)-1] = '\0';
                        if (!remoteconsolehandler(ebuf)) {
-                               /* Strip preamble from output */
-                               char *temp;
-                               for (temp = ebuf; *temp; temp++) {
-                                       if (*temp == 127) {
-                                               memmove(temp, temp + 1, strlen(temp));
-                                               temp--;
-                                       }
-                               }
                                res = write(ast_consock, ebuf, strlen(ebuf) + 1);
                                if (res < 1) {
                                        ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
@@ -2901,7 +3284,7 @@ static int show_version(void)
 
 static int show_cli_help(void)
 {
-       printf("Asterisk %s, Copyright (C) 1999 - 2012, Digium, Inc. and others.\n", ast_get_version());
+       printf("Asterisk %s, Copyright (C) 1999 - 2013, Digium, Inc. and others.\n", ast_get_version());
        printf("Usage: asterisk [OPTIONS]\n");
        printf("Valid Options:\n");
        printf("   -V              Display version number and exit\n");
@@ -2939,7 +3322,7 @@ static int show_cli_help(void)
        return 0;
 }
 
-static void ast_readconfig(void) 
+static void ast_readconfig(void)
 {
        struct ast_config *cfg;
        struct ast_variable *v;
@@ -2951,18 +3334,24 @@ static void ast_readconfig(void)
                unsigned int keydir:1;
        } found = { 0, 0 };
 
+       /* Set default value */
+       option_dtmfminduration = AST_MIN_DTMF_DURATION;
+
        if (ast_opt_override_config) {
                cfg = ast_config_load2(ast_config_AST_CONFIG_FILE, "" /* core, can't reload */, config_flags);
-               if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
-                       ast_log(LOG_WARNING, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
-       } else 
+               if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
+                       fprintf(stderr, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
+               }
+       } else {
                cfg = ast_config_load2(config, "" /* core, can't reload */, config_flags);
+       }
 
        /* init with buildtime config */
        ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
        ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
        ast_copy_string(cfg_paths.module_dir, DEFAULT_MODULE_DIR, sizeof(cfg_paths.module_dir));
-       snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", cfg_paths.spool_dir);
+       snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", cfg_paths.spool_dir);
+       snprintf(cfg_paths.recording_dir, sizeof(cfg_paths.recording_dir), "%s/recording", cfg_paths.spool_dir);
        ast_copy_string(cfg_paths.var_dir, DEFAULT_VAR_DIR, sizeof(cfg_paths.var_dir));
        ast_copy_string(cfg_paths.data_dir, DEFAULT_DATA_DIR, sizeof(cfg_paths.data_dir));
        ast_copy_string(cfg_paths.log_dir, DEFAULT_LOG_DIR, sizeof(cfg_paths.log_dir));
@@ -2998,6 +3387,7 @@ static void ast_readconfig(void)
                } else if (!strcasecmp(v->name, "astspooldir")) {
                        ast_copy_string(cfg_paths.spool_dir, v->value, sizeof(cfg_paths.spool_dir));
                        snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir), "%s/monitor", v->value);
+                       snprintf(cfg_paths.recording_dir, sizeof(cfg_paths.recording_dir), "%s/recording", v->value);
                } else if (!strcasecmp(v->name, "astvarlibdir")) {
                        ast_copy_string(cfg_paths.var_dir, v->value, sizeof(cfg_paths.var_dir));
                        if (!found.dbdir)
@@ -3087,23 +3477,27 @@ static void ast_readconfig(void)
                /* Enable internal timing */
                } else if (!strcasecmp(v->name, "internal_timing")) {
                        ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INTERNAL_TIMING);
+               } else if (!strcasecmp(v->name, "mindtmfduration")) {
+                       if (sscanf(v->value, "%30u", &option_dtmfminduration) != 1) {
+                               option_dtmfminduration = AST_MIN_DTMF_DURATION;
+                       }
                } else if (!strcasecmp(v->name, "maxcalls")) {
-                       if ((sscanf(v->value, "%30d", &option_maxcalls) != 1) || (option_maxcalls < 0)) {
-                               option_maxcalls = 0;
+                       if ((sscanf(v->value, "%30d", &ast_option_maxcalls) != 1) || (ast_option_maxcalls < 0)) {
+                               ast_option_maxcalls = 0;
                        }
                } else if (!strcasecmp(v->name, "maxload")) {
                        double test[1];
 
                        if (getloadavg(test, 1) == -1) {
                                ast_log(LOG_ERROR, "Cannot obtain load average on this system. 'maxload' option disabled.\n");
-                               option_maxload = 0.0;
-                       } else if ((sscanf(v->value, "%30lf", &option_maxload) != 1) || (option_maxload < 0.0)) {
-                               option_maxload = 0.0;
+                               ast_option_maxload = 0.0;
+                       } else if ((sscanf(v->value, "%30lf", &ast_option_maxload) != 1) || (ast_option_maxload < 0.0)) {
+                               ast_option_maxload = 0.0;
                        }
                /* Set the maximum amount of open files */
                } else if (!strcasecmp(v->name, "maxfiles")) {
-                       option_maxfiles = atoi(v->value);
-                       set_ulimit(option_maxfiles);
+                       ast_option_maxfiles = atoi(v->value);
+                       set_ulimit(ast_option_maxfiles);
                /* What user to run as */
                } else if (!strcasecmp(v->name, "runuser")) {
                        ast_copy_string(cfg_paths.run_user, v->value, sizeof(cfg_paths.run_user));
@@ -3126,16 +3520,16 @@ static void ast_readconfig(void)
                } else if (!strcasecmp(v->name, "languageprefix")) {
                        ast_language_is_prefix = ast_true(v->value);
                } else if (!strcasecmp(v->name, "defaultlanguage")) {
-                       ast_copy_string(defaultlanguage, v->value, MAX_LANGUAGE);
-               } else if (!strcasecmp(v->name, "lockmode")) {
-                       if (!strcasecmp(v->value, "lockfile")) {
-                               ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
-                       } else if (!strcasecmp(v->value, "flock")) {
-                               ast_set_lock_type(AST_LOCK_TYPE_FLOCK);
-                       } else {
+                       ast_copy_string(ast_defaultlanguage, v->value, MAX_LANGUAGE);
+               } else if (!strcasecmp(v->name, "lockmode")) {
+                       if (!strcasecmp(v->value, "lockfile")) {
+                               ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+                       } else if (!strcasecmp(v->value, "flock")) {
+                               ast_set_lock_type(AST_LOCK_TYPE_FLOCK);
+                       } else {
                                ast_log(LOG_WARNING, "'%s' is not a valid setting for the lockmode option, "
                                        "defaulting to 'lockfile'\n", v->value);
-                               ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+                               ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
                        }
 #if defined(HAVE_SYSINFO)
                } else if (!strcasecmp(v->name, "minmemfree")) {
@@ -3160,12 +3554,24 @@ static void ast_readconfig(void)
                        ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIDE_CONSOLE_CONNECT);
                } else if (!strcasecmp(v->name, "lockconfdir")) {
                        ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_LOCK_CONFIG_DIR);
+               } else if (!strcasecmp(v->name, "stdexten")) {
+                       /* Choose how to invoke the extensions.conf stdexten */
+                       if (!strcasecmp(v->value, "gosub")) {
+                               ast_clear_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
+                       } else if (!strcasecmp(v->value, "macro")) {
+                               ast_set_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
+                       } else {
+                               ast_log(LOG_WARNING,
+                                       "'%s' is not a valid setting for the stdexten option, defaulting to 'gosub'\n",
+                                       v->value);
+                               ast_clear_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO);
+                       }
                }
        }
        for (v = ast_variable_browse(cfg, "compat"); v; v = v->next) {
                float version;
                if (sscanf(v->value, "%30f", &version) != 1) {
-                       ast_log(LOG_WARNING, "Compatibility version for option '%s' is not a number: '%s'\n", v->name, v->value);
+                       fprintf(stderr, "Compatibility version for option '%s' is not a number: '%s'\n", v->name, v->value);
                        continue;
                }
                if (!strcasecmp(v->name, "app_set")) {
@@ -3214,9 +3620,8 @@ static void *canary_thread(void *unused)
        sleep(120);
 
        for (;;) {
-               stat(canary_filename, &canary_stat);
                now = ast_tvnow();
-               if (now.tv_sec > canary_stat.st_mtime + 60) {
+               if (stat(canary_filename, &canary_stat) || now.tv_sec > canary_stat.st_mtime + 60) {
                        ast_log(LOG_WARNING,
                                "The canary is no more.  He has ceased to be!  "
                                "He's expired and gone to meet his maker!  "
@@ -3281,12 +3686,33 @@ static void env_init(void)
        setenv("AST_VERSION", ast_get_version(), 1);
 }
 
+static void print_intro_message(const char *runuser, const char *rungroup)
+{
+       if (ast_opt_console || option_verbose || (ast_opt_remote && !ast_opt_exec)) {
+               if (ast_register_verbose(console_verboser)) {
+                       fprintf(stderr, "Unable to register console verboser?\n");
+                       return;
+               }
+               WELCOME_MESSAGE;
+               if (runuser) {
+                       ast_verbose("Running as user '%s'\n", runuser);
+               }
+               if (rungroup) {
+                       ast_verbose("Running under group '%s'\n", rungroup);
+               }
+       }
+}
+
+static void main_atexit(void)
+{
+       ast_cli_unregister_multiple(cli_asterisk, ARRAY_LEN(cli_asterisk));
+}
+
 int main(int argc, char *argv[])
 {
        int c;
        char filename[80] = "";
        char hostname[MAXHOSTNAMELEN] = "";
-       char tmp[80];
        char * xarg = NULL;
        int x;
        FILE *f;
@@ -3318,19 +3744,13 @@ int main(int argc, char *argv[])
        if (gethostname(hostname, sizeof(hostname)-1))
                ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
        ast_mainpid = getpid();
-       ast_ulaw_init();
-       ast_alaw_init();
-       callerid_init();
-       ast_builtins_init();
-       ast_utils_init();
-       tdd_init();
-       ast_tps_init();
-       ast_fd_init();
-       ast_pbx_init();
 
-       if (getenv("HOME")) 
+       if (getenv("HOME"))
                snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
-       /* Check for options */
+       /*! \brief Check for options
+        *
+        * \todo Document these options
+        */
        while ((c = getopt(argc, argv, "BC:cde:FfG:ghIiL:M:mnpqRrs:TtU:VvWXx:")) != -1) {
                /*!\note Please keep the ordering here to alphabetical, capital letters
                 * first.  This will make it easier in the future to select unused
@@ -3385,13 +3805,13 @@ int main(int argc, char *argv[])
                        ast_set_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS);
                        break;
                case 'L':
-                       if ((sscanf(optarg, "%30lf", &option_maxload) != 1) || (option_maxload < 0.0)) {
-                               option_maxload = 0.0;
+                       if ((sscanf(optarg, "%30lf", &ast_option_maxload) != 1) || (ast_option_maxload < 0.0)) {
+                               ast_option_maxload = 0.0;
                        }
                        break;
                case 'M':
-                       if ((sscanf(optarg, "%30d", &option_maxcalls) != 1) || (option_maxcalls < 0)) {
-                               option_maxcalls = 0;
+                       if ((sscanf(optarg, "%30d", &ast_option_maxcalls) != 1) || (ast_option_maxcalls < 0)) {
+                               ast_option_maxcalls = 0;
                        }
                        break;
                case 'm':
@@ -3447,16 +3867,6 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (ast_opt_console || option_verbose || (ast_opt_remote && !ast_opt_exec)) {
-               if (ast_register_verbose(console_verboser)) {
-                       ast_log(LOG_WARNING, "Unable to register console verboser?\n");
-               }
-               WELCOME_MESSAGE;
-       }
-
-       if (ast_opt_console && !option_verbose) 
-               ast_verbose("[ Booting...\n");
-
        /* For remote connections, change the name of the remote connection.
         * We do this for the benefit of init scripts (which need to know if/when
         * the main asterisk process has died yet). */
@@ -3467,21 +3877,18 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (ast_opt_console && !option_verbose) {
-               ast_verbose("[ Reading Master Configuration ]\n");
-       }
-
        ast_readconfig();
        env_init();
 
        if (ast_opt_remote && remotesock != NULL)
                ast_copy_string((char *) cfg_paths.socket_path, remotesock, sizeof(cfg_paths.socket_path));
 
-       if (!ast_language_is_prefix && !ast_opt_remote)
-               ast_log(LOG_WARNING, "The 'languageprefix' option in asterisk.conf is deprecated; in a future release it will be removed, and your sound files will need to be organized in the 'new style' language layout.\n");
+       if (!ast_language_is_prefix && !ast_opt_remote) {
+               fprintf(stderr, "The 'languageprefix' option in asterisk.conf is deprecated; in a future release it will be removed, and your sound files will need to be organized in the 'new style' language layout.\n");
+       }
 
        if (ast_opt_always_fork && (ast_opt_remote || ast_opt_console)) {
-               ast_log(LOG_WARNING, "'alwaysfork' is not compatible with console or remote console mode; ignored\n");
+               fprintf(stderr, "'alwaysfork' is not compatible with console or remote console mode; ignored\n");
                ast_clear_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);
        }
 
@@ -3490,12 +3897,12 @@ int main(int argc, char *argv[])
                l.rlim_cur = RLIM_INFINITY;
                l.rlim_max = RLIM_INFINITY;
                if (setrlimit(RLIMIT_CORE, &l)) {
-                       ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n", strerror(errno));
+                       fprintf(stderr, "Unable to disable core size resource limit: %s\n", strerror(errno));
                }
        }
 
        if (getrlimit(RLIMIT_NOFILE, &l)) {
-               ast_log(LOG_WARNING, "Unable to check file descriptor limit: %s\n", strerror(errno));
+               fprintf(stderr, "Unable to check file descriptor limit: %s\n", strerror(errno));
        }
 
 #if !defined(CONFIGURE_RAN_AS_ROOT)
@@ -3512,13 +3919,13 @@ int main(int argc, char *argv[])
                }
 
                if (!(fd = open("/dev/null", O_RDONLY))) {
-                       ast_log(LOG_ERROR, "Cannot open a file descriptor at boot? %s\n", strerror(errno));
+                       fprintf(stderr, "Cannot open a file descriptor at boot? %s\n", strerror(errno));
                        break; /* XXX Should we exit() here? XXX */
                }
 
-               fd2 = (l.rlim_cur > sizeof(readers) * 8 ? sizeof(readers) * 8 : l.rlim_cur) - 1;
+               fd2 = ((l.rlim_cur > sizeof(readers) * 8) ? sizeof(readers) * 8 : l.rlim_cur) - 1;
                if (dup2(fd, fd2) < 0) {
-                       ast_log(LOG_WARNING, "Cannot open maximum file descriptor %d at boot? %s\n", fd2, strerror(errno));
+                       fprintf(stderr, "Cannot open maximum file descriptor %d at boot? %s\n", fd2, strerror(errno));
                        close(fd);
                        break;
                }
@@ -3526,7 +3933,7 @@ int main(int argc, char *argv[])
                FD_ZERO(&readers);
                FD_SET(fd2, &readers);
                if (ast_select(fd2 + 1, &readers, NULL, NULL, &tv) < 0) {
-                       ast_log(LOG_WARNING, "Maximum select()able file descriptor is %d\n", FD_SETSIZE);
+                       fprintf(stderr, "Maximum select()able file descriptor is %d\n", FD_SETSIZE);
                }
                ast_FD_SETSIZE = l.rlim_cur > ast_FDMAX ? ast_FDMAX : l.rlim_cur;
                close(fd);
@@ -3552,7 +3959,7 @@ int main(int argc, char *argv[])
                if (errno == EEXIST) {
                        rundir_exists = 1;
                } else {
-                       ast_log(LOG_WARNING, "Unable to create socket file directory.  Remote consoles will not be able to connect! (%s)\n", strerror(x));
+                       fprintf(stderr, "Unable to create socket file directory.  Remote consoles will not be able to connect! (%s)\n", strerror(x));
                }
        }
 
@@ -3566,22 +3973,20 @@ int main(int argc, char *argv[])
                struct group *gr;
                gr = getgrnam(rungroup);
                if (!gr) {
-                       ast_log(LOG_WARNING, "No such group '%s'!\n", rungroup);
+                       fprintf(stderr, "No such group '%s'!\n", rungroup);
                        exit(1);
                }
                if (!rundir_exists && chown(ast_config_AST_RUN_DIR, -1, gr->gr_gid)) {
-                       ast_log(LOG_WARNING, "Unable to chgrp run directory to %d (%s)\n", (int) gr->gr_gid, rungroup);
+                       fprintf(stderr, "Unable to chgrp run directory to %d (%s)\n", (int) gr->gr_gid, rungroup);
                }
                if (setgid(gr->gr_gid)) {
-                       ast_log(LOG_WARNING, "Unable to setgid to %d (%s)\n", (int)gr->gr_gid, rungroup);
+                       fprintf(stderr, "Unable to setgid to %d (%s)\n", (int)gr->gr_gid, rungroup);
                        exit(1);
                }
                if (setgroups(0, NULL)) {
-                       ast_log(LOG_WARNING, "Unable to drop unneeded groups\n");
+                       fprintf(stderr, "Unable to drop unneeded groups\n");
                        exit(1);
                }
-               if (option_verbose)
-                       ast_verbose("Running as group '%s'\n", rungroup);
        }
 
        if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) {
@@ -3591,11 +3996,11 @@ int main(int argc, char *argv[])
                struct passwd *pw;
                pw = getpwnam(runuser);
                if (!pw) {
-                       ast_log(LOG_WARNING, "No such user '%s'!\n", runuser);
+                       fprintf(stderr, "No such user '%s'!\n", runuser);
                        exit(1);
                }
                if (chown(ast_config_AST_RUN_DIR, pw->pw_uid, -1)) {
-                       ast_log(LOG_WARNING, "Unable to chown run directory to %d (%s)\n", (int) pw->pw_uid, runuser);
+                       fprintf(stderr, "Unable to chown run directory to %d (%s)\n", (int) pw->pw_uid, runuser);
                }
 #ifdef HAVE_CAP
                if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
@@ -3604,36 +4009,35 @@ int main(int argc, char *argv[])
                }
 #endif /* HAVE_CAP */
                if (!isroot && pw->pw_uid != geteuid()) {
-                       ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser);
+                       fprintf(stderr, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser);
                        exit(1);
                }
                if (!rungroup) {
                        if (setgid(pw->pw_gid)) {
-                               ast_log(LOG_WARNING, "Unable to setgid to %d!\n", (int)pw->pw_gid);
+                               fprintf(stderr, "Unable to setgid to %d!\n", (int)pw->pw_gid);
                                exit(1);
                        }
                        if (isroot && initgroups(pw->pw_name, pw->pw_gid)) {
-                               ast_log(LOG_WARNING, "Unable to init groups for '%s'\n", runuser);
+                               fprintf(stderr, "Unable to init groups for '%s'\n", runuser);
                                exit(1);
                        }
                }
                if (setuid(pw->pw_uid)) {
-                       ast_log(LOG_WARNING, "Unable to setuid to %d (%s)\n", (int)pw->pw_uid, runuser);
+                       fprintf(stderr, "Unable to setuid to %d (%s)\n", (int)pw->pw_uid, runuser);
                        exit(1);
                }
-               if (option_verbose)
-                       ast_verbose("Running as user '%s'\n", runuser);
 #ifdef HAVE_CAP
                if (has_cap) {
                        cap_t cap;
 
                        cap = cap_from_text("cap_net_admin=eip");
 
-                       if (cap_set_proc(cap))
-                               ast_log(LOG_WARNING, "Unable to install capabilities.\n");
-
-                       if (cap_free(cap))
-                               ast_log(LOG_WARNING, "Unable to drop capabilities.\n");
+                       if (cap_set_proc(cap)) {
+                               fprintf(stderr, "Unable to install capabilities.\n");
+                       }
+                       if (cap_free(cap)) {
+                               fprintf(stderr, "Unable to drop capabilities.\n");
+                       }
                }
 #endif /* HAVE_CAP */
        }
@@ -3643,7 +4047,7 @@ int main(int argc, char *argv[])
 #ifdef linux
        if (geteuid() && ast_opt_dump_core) {
                if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
-                       ast_log(LOG_WARNING, "Unable to set the process for core dumps after changing to a non-root user. %s\n", strerror(errno));
+                       fprintf(stderr, "Unable to set the process for core dumps after changing to a non-root user. %s\n", strerror(errno));
                }
        }
 #endif
@@ -3655,93 +4059,76 @@ int main(int argc, char *argv[])
 #endif
                char dir[PATH_MAX];
                if (!getcwd(dir, sizeof(dir)) || eaccess(dir, R_OK | X_OK | F_OK)) {
-                       ast_log(LOG_ERROR, "Unable to access the running directory (%s).  Changing to '/' for compatibility.\n", strerror(errno));
+                       fprintf(stderr, "Unable to access the running directory (%s).  Changing to '/' for compatibility.\n", strerror(errno));
                        /* If we cannot access the CWD, then we couldn't dump core anyway,
                         * so chdir("/") won't break anything. */
                        if (chdir("/")) {
                                /* chdir(/) should never fail, so this ends up being a no-op */
-                               ast_log(LOG_ERROR, "chdir(\"/\") failed?!! %s\n", strerror(errno));
+                               fprintf(stderr, "chdir(\"/\") failed?!! %s\n", strerror(errno));
                        }
                } else
 #endif /* defined(HAVE_EACCESS) || defined(HAVE_EUIDACCESS) */
                if (!ast_opt_no_fork && !ast_opt_dump_core) {
                        /* Backgrounding, but no cores, so chdir won't break anything. */
                        if (chdir("/")) {
-                               ast_log(LOG_ERROR, "Unable to chdir(\"/\") ?!! %s\n", strerror(errno));
+                               fprintf(stderr, "Unable to chdir(\"/\") ?!! %s\n", strerror(errno));
                        }
                }
        }
 
-       ast_term_init();
-       printf("%s", term_end());
-       fflush(stdout);
-
-       if (ast_opt_console && !option_verbose) 
-               ast_verbose("[ Initializing Custom Configuration Options ]\n");
-       /* custom config setup */
-       register_config_cli();
-       read_config_maps();
-       
-       if (ast_opt_console) {
-               if (el_hist == NULL || el == NULL)
-                       ast_el_initialize();
-
-               if (!ast_strlen_zero(filename))
-                       ast_el_read_history(filename);
-       }
-
        if (ast_tryconnect()) {
                /* One is already running */
                if (ast_opt_remote) {
+                       multi_thread_safe = 1;
                        if (ast_opt_exec) {
                                ast_remotecontrol(xarg);
                                quit_handler(0, SHUTDOWN_FAST, 0);
                                exit(0);
                        }
+                       print_intro_message(runuser, rungroup);
                        printf("%s", term_quit());
                        ast_remotecontrol(NULL);
                        quit_handler(0, SHUTDOWN_FAST, 0);
                        exit(0);
                } else {
-                       ast_log(LOG_ERROR, "Asterisk already running on %s.  Use 'asterisk -r' to connect.\n", ast_config_AST_SOCKET);
+                       fprintf(stderr, "Asterisk already running on %s.  Use 'asterisk -r' to connect.\n", ast_config_AST_SOCKET);
                        printf("%s", term_quit());
                        exit(1);
                }
        } else if (ast_opt_remote || ast_opt_exec) {
-               ast_log(LOG_ERROR, "Unable to connect to remote asterisk (does %s exist?)\n", ast_config_AST_SOCKET);
+               fprintf(stderr, "Unable to connect to remote asterisk (does %s exist?)\n", ast_config_AST_SOCKET);
                printf("%s", term_quit());
                exit(1);
        }
-       /* Blindly write pid file since we couldn't connect */
-       unlink(ast_config_AST_PID);
-       f = fopen(ast_config_AST_PID, "w");
-       if (f) {
-               fprintf(f, "%ld\n", (long)getpid());
-               fclose(f);
-       } else
-               ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
 
+       /* This needs to remain as high up in the initial start up as possible.
+        * daemon causes a fork to occur, which has all sorts of unintended
+        * consequences for things that interact with threads.  This call *must*
+        * occur before anything in Asterisk spawns or manipulates thread related
+        * primitives. */
 #if HAVE_WORKING_FORK
        if (ast_opt_always_fork || !ast_opt_no_fork) {
 #ifndef HAVE_SBIN_LAUNCHD
                if (daemon(1, 0) < 0) {
-                       ast_log(LOG_ERROR, "daemon() failed: %s\n", strerror(errno));
+                       fprintf(stderr, "daemon() failed: %s\n", strerror(errno));
+               } else {
+                       ast_mainpid = getpid();
                }
-               ast_mainpid = getpid();
-               /* Blindly re-write pid file since we are forking */
-               unlink(ast_config_AST_PID);
-               f = fopen(ast_config_AST_PID, "w");
-               if (f) {
-                       fprintf(f, "%ld\n", (long)ast_mainpid);
-                       fclose(f);
-               } else
-                       ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
 #else
-               ast_log(LOG_WARNING, "Mac OS X detected.  Use 'launchctl load /Library/LaunchDaemon/org.asterisk.asterisk.plist'.\n");
+               fprintf(stderr, "Mac OS X detected.  Use 'launchctl load /Library/LaunchDaemon/org.asterisk.asterisk.plist'.\n");
 #endif
        }
 #endif
 
+       /* At this point everything has been forked successfully,
+        * we have determined that we aren't attempting to connect to
+        * an Asterisk instance, and that there isn't one already running. */
+       multi_thread_safe = 1;
+
+#if defined(__AST_DEBUG_MALLOC)
+       __ast_mm_init_phase_1();
+#endif /* defined(__AST_DEBUG_MALLOC) */
+
        /* Spawning of astcanary must happen AFTER the call to daemon(3) */
        if (isroot && ast_opt_high_priority) {
                snprintf(canary_filename, sizeof(canary_filename), "%s/alt.asterisk.canary.tweet.tweet.tweet", ast_config_AST_RUN_DIR);
@@ -3776,7 +4163,63 @@ int main(int argc, char *argv[])
                ast_register_atexit(canary_exit);
        }
 
-       if (ast_event_init()) {
+       /* Blindly write the PID file. */
+       unlink(ast_config_AST_PID);
+       f = fopen(ast_config_AST_PID, "w");
+       if (f) {
+               fprintf(f, "%ld\n", (long)ast_mainpid);
+               fclose(f);
+       } else {
+               fprintf(stderr, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
+       }
+
+       /* Initialize the terminal.  Since all processes have been forked,
+        * we can now start using the standard log messages.
+        */
+       ast_term_init();
+       printf("%s", term_end());
+       fflush(stdout);
+
+       print_intro_message(runuser, rungroup);
+
+       if (ast_opt_console) {
+               ast_verb(0, "[ Initializing Custom Configuration Options ]\n");
+       }
+       /* custom config setup */
+       register_config_cli();
+       read_config_maps();
+
+       if (ast_opt_console) {
+               if (el_hist == NULL || el == NULL)
+                       ast_el_initialize();
+
+               if (!ast_strlen_zero(filename))
+                       ast_el_read_history(filename);
+       }
+
+       ast_json_init();
+       ast_ulaw_init();
+       ast_alaw_init();
+       tdd_init();
+       callerid_init();
+       ast_builtins_init();
+
+       if (ast_utils_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_tps_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_fd_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_pbx_init()) {
                printf("%s", term_quit());
                exit(1);
        }
@@ -3794,6 +4237,39 @@ int main(int argc, char *argv[])
        }
 
        ast_aoc_cli_init();
+       ast_uuid_init();
+
+       if (ast_sorcery_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+#ifdef AST_XML_DOCS
+       /* Load XML documentation. */
+       ast_xmldoc_load_documentation();
+#endif
+
+       aco_init();
+
+       if (ast_bucket_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (stasis_init()) {
+               printf("Stasis initialization failed.\n%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_stasis_system_init()) {
+               printf("Stasis system-level information initialization failed.\n%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_endpoint_stasis_init()) {
+               printf("Endpoint initialization failed.\n%s", term_quit());
+               exit(1);
+       }
 
        ast_makesocket();
        sigemptyset(&sigs);
@@ -3826,7 +4302,10 @@ int main(int argc, char *argv[])
 
        ast_format_attr_init();
        ast_format_list_init();
-       ast_rtp_engine_init();
+       if (ast_rtp_engine_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
 
        ast_autoservice_init();
 
@@ -3840,10 +4319,15 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
-#ifdef AST_XML_DOCS
-       /* Load XML documentation. */
-       ast_xmldoc_load_documentation();
-#endif
+       if (app_init()) {
+               printf("App core initialization failed.\n%s", term_quit());
+               exit(1);
+       }
+
+       if (devstate_init()) {
+               printf("Device state core initialization failed.\n%s", term_quit());
+               exit(1);
+       }
 
        if (astdb_init()) {
                printf("%s", term_quit());
@@ -3863,6 +4347,11 @@ int main(int argc, char *argv[])
 
        ast_channels_init();
 
+       if (ast_endpoint_init()) {
+               printf ("%s", term_quit());
+               exit(1);
+       }
+
        if ((moduleresult = load_modules(1))) {         /* Load modules, pre-load only */
                printf("%s", term_quit());
                exit(moduleresult == -2 ? 2 : 1);
@@ -3873,19 +4362,44 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (ast_security_stasis_init()) {               /* Initialize Security Stasis Topic and Events */
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_named_acl_init()) { /* Initialize the Named ACL system */
+               printf("%s", term_quit());
+               exit(1);
+       }
+
        ast_http_init();                /* Start the HTTP server, if needed */
 
-       if (init_manager()) {
+       if (ast_indications_init()) {
                printf("%s", term_quit());
                exit(1);
        }
 
-       if (ast_cdr_engine_init()) {
+       if (ast_features_init()) {
                printf("%s", term_quit());
                exit(1);
        }
 
-       if (ast_cel_engine_init()) {
+       if (ast_pickup_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_bridging_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_parking_stasis_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
+       if (ast_cdr_engine_init()) {
                printf("%s", term_quit());
                exit(1);
        }
@@ -3895,6 +4409,11 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (ast_presence_state_engine_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
        ast_dsp_init();
        ast_udptl_init();
 
@@ -3913,17 +4432,17 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
-       if (ast_indications_init()) {
+       if (ast_local_init()) {
                printf("%s", term_quit());
                exit(1);
        }
 
-       if (ast_features_init()) {
+       if (ast_cel_engine_init()) {
                printf("%s", term_quit());
                exit(1);
        }
 
-       if (init_framer()) {
+       if (init_manager()) {
                printf("%s", term_quit());
                exit(1);
        }
@@ -3938,6 +4457,11 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (ast_sounds_index_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
        if ((moduleresult = load_modules(0))) {         /* Load modules */
                printf("%s", term_quit());
                exit(moduleresult == -2 ? 2 : 1);
@@ -3952,29 +4476,30 @@ int main(int argc, char *argv[])
 
        /* We might have the option of showing a console, but for now just
           do nothing... */
-       if (ast_opt_console && !option_verbose)
-               ast_verbose(" ]\n");
-       if (option_verbose || ast_opt_console)
-               ast_verbose("%s", term_color(tmp, "Asterisk Ready.\n", COLOR_BRWHITE, COLOR_BLACK, sizeof(tmp)));
-       if (ast_opt_no_fork)
+       ast_verb(0, COLORIZE_FMT "\n", COLORIZE(COLOR_BRWHITE, COLOR_BLACK, "Asterisk Ready."));
+       if (ast_opt_no_fork) {
                consolethread = pthread_self();
+       }
 
-       if (pipe(sig_alert_pipe))
+       if (pipe(sig_alert_pipe)) {
                sig_alert_pipe[0] = sig_alert_pipe[1] = -1;
+       }
 
        ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED);
-       manager_event(EVENT_FLAG_SYSTEM, "FullyBooted", "Status: Fully Booted\r\n");
+       publish_fully_booted();
 
        ast_process_pending_reloads();
 
        pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
 
-#ifdef __AST_DEBUG_MALLOC
-       __ast_mm_init();
-#endif
+#if defined(__AST_DEBUG_MALLOC)
+       __ast_mm_init_phase_2();
+#endif /* defined(__AST_DEBUG_MALLOC) */
 
        ast_lastreloadtime = ast_startuptime = ast_tvnow();
+       ast_cli_register_multiple(cli_asterisk_shutdown, ARRAY_LEN(cli_asterisk_shutdown));
        ast_cli_register_multiple(cli_asterisk, ARRAY_LEN(cli_asterisk));
+       ast_register_atexit(main_atexit);
 
        run_startup_commands();