memory leaks: Memory leak cleanup patch by Corey Farrell (second set)
[asterisk/asterisk.git] / main / asterisk.c
index acddc96..2e5ffa7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2010, 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 - 2009, 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 - 2011 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];
@@ -242,6 +387,7 @@ struct _cfg_paths {
 
        char config_file[PATH_MAX];
        char db_path[PATH_MAX];
+       char sbin_dir[PATH_MAX];
        char pid_path[PATH_MAX];
        char socket_path[PATH_MAX];
        char run_user[PATH_MAX];
@@ -256,12 +402,14 @@ 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;
 const char *ast_config_AST_AGI_DIR     = cfg_paths.agi_dir;
 const char *ast_config_AST_KEY_DIR     = cfg_paths.key_dir;
 const char *ast_config_AST_RUN_DIR     = cfg_paths.run_dir;
+const char *ast_config_AST_SBIN_DIR = cfg_paths.sbin_dir;
 
 const char *ast_config_AST_DB          = cfg_paths.db_path;
 const char *ast_config_AST_PID         = cfg_paths.pid_path;
@@ -278,12 +426,22 @@ static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl";
 extern unsigned int ast_FD_SETSIZE;
 
 static char *_argv[256];
-static int shuttingdown;
+typedef enum {
+       NOT_SHUTTING_DOWN = -2,
+       SHUTTING_DOWN = -1,
+       /* Valid values for quit_handler niceness below: */
+       SHUTDOWN_FAST,
+       SHUTDOWN_NORMAL,
+       SHUTDOWN_NICE,
+       SHUTDOWN_REALLY_NICE
+} shutdown_nice_t;
+static shutdown_nice_t shuttingdown = NOT_SHUTTING_DOWN;
 static int restartnow;
 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];
 
@@ -312,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;
 
@@ -374,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;
@@ -388,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 */
@@ -443,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
@@ -468,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");
@@ -476,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  */
@@ -511,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;
@@ -524,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;
 }
@@ -743,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));
@@ -866,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";
@@ -934,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;
+       RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
 
-       if (!(ae = ast_calloc(1, sizeof(*ae))))
-               return -1;
-
-       ae->func = func;
-
-       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);
+
+       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);
+}
 
-       free(ae);
+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 */
@@ -984,7 +1181,6 @@ static int fdprint(int fd, const char *s)
 /*! \brief NULL handler so we can collect the child exit status */
 static void _null_sig_handler(int sig)
 {
-
 }
 
 static struct sigaction null_sig_handler = {
@@ -998,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;
 
@@ -1046,7 +1243,7 @@ int ast_safe_system(const char *s)
        pid = fork();
 #else
        pid = vfork();
-#endif 
+#endif
 
        if (pid == 0) {
 #ifdef HAVE_CAP
@@ -1072,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 {
@@ -1094,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) {
                        /*
@@ -1109,7 +1311,8 @@ void ast_console_toggle_loglevel(int fd, int level, int state)
 /*!
  * \brief mute or unmute a console from logging
  */
-void ast_console_toggle_mute(int fd, int silent) {
+void ast_console_toggle_mute(int fd, int silent)
+{
        int x;
        for (x = 0;x < AST_MAX_CONNECTS; x++) {
                if (fd == consoles[x].fd) {
@@ -1138,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);
                }
        }
@@ -1162,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)
@@ -1198,7 +1401,12 @@ static pthread_t lthread;
 static int read_credentials(int fd, char *buffer, size_t size, struct console *con)
 {
 #if defined(SO_PEERCRED)
+#ifdef HAVE_STRUCT_SOCKPEERCRED_UID
+#define HAVE_STRUCT_UCRED_UID
+       struct sockpeercred cred;
+#else
        struct ucred cred;
+#endif
        socklen_t len = sizeof(cred);
 #endif
 #if defined(HAVE_GETPEEREID)
@@ -1243,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;
@@ -1266,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;
                }
@@ -1295,7 +1531,7 @@ static void *netconsole(void *vconsole)
        close(con->p[0]);
        close(con->p[1]);
        con->fd = -1;
-       
+
        return NULL;
 }
 
@@ -1384,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));
@@ -1413,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;
@@ -1422,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)
@@ -1452,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));
@@ -1468,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)
 {
@@ -1486,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;
@@ -1514,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;
 }
@@ -1528,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;
 }
 
@@ -1560,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. */
@@ -1586,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);
@@ -1596,68 +1835,102 @@ int ast_set_priority(int pri)
        return 0;
 }
 
-static void ast_run_atexits(void)
+static int can_safely_quit(shutdown_nice_t niceness, int restart);
+static void really_quit(int num, shutdown_nice_t niceness, int restart);
+
+static void quit_handler(int num, shutdown_nice_t niceness, int restart)
 {
-       struct ast_atexit *ae;
-       AST_RWLIST_RDLOCK(&atexits);
-       AST_RWLIST_TRAVERSE(&atexits, ae, list) {
-               if (ae->func) 
-                       ae->func();
+       if (can_safely_quit(niceness, restart)) {
+               really_quit(num, niceness, restart);
+               /* No one gets here. */
        }
-       AST_RWLIST_UNLOCK(&atexits);
+       /* It wasn't our time. */
 }
 
-static void quit_handler(int num, int niceness, int safeshutdown, int restart)
+static int can_safely_quit(shutdown_nice_t niceness, int restart)
 {
-       char filename[80] = "";
-       time_t s,e;
-       int x;
-       /* Try to get as many CDRs as possible submitted to the backend engines (if in batch mode) */
+       /* Check if someone else isn't already doing this. */
+       ast_mutex_lock(&safe_system_lock);
+       if (shuttingdown != NOT_SHUTTING_DOWN && niceness >= shuttingdown) {
+               /* Already in progress and other request was less nice. */
+               ast_mutex_unlock(&safe_system_lock);
+               ast_verbose("Ignoring asterisk %s request, already in progress.\n", restart ? "restart" : "shutdown");
+               return 0;
+       }
+       shuttingdown = niceness;
+       ast_mutex_unlock(&safe_system_lock);
+
+       /* Try to get as many CDRs as possible submitted to the backend engines
+        * (if in batch mode). really_quit happens to call it again when running
+        * the atexit handlers, otherwise this would be a bit early. */
        ast_cdr_engine_term();
-       if (safeshutdown) {
-               shuttingdown = 1;
-               if (!niceness) {
-                       /* 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");
-                       time(&s);
-                       for (;;) {
-                               time(&e);
-                               /* Wait up to 15 seconds for all channels to go away */
-                               if ((e - s) > 15)
-                                       break;
-                               if (!ast_active_channels())
-                                       break;
-                               if (!shuttingdown)
-                                       break;
-                               /* Sleep 1/10 of a second */
-                               usleep(100000);
+
+       /* 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 (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_undestroyed_channels() || shuttingdown != niceness) {
+                               break;
                        }
-               } else {
-                       if (niceness < 2)
-                               ast_begin_shutdown(0);
-                       if (option_verbose && ast_opt_console)
-                               ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
-                       for (;;) {
-                               if (!ast_active_channels())
-                                       break;
-                               if (!shuttingdown)
-                                       break;
-                               sleep(1);
+                       /* Sleep 1/10 of a second */
+                       usleep(100000);
+               }
+       } else if (niceness >= SHUTDOWN_NICE) {
+               if (niceness != SHUTDOWN_REALLY_NICE) {
+                       ast_begin_shutdown(0);
+               }
+               if (ast_opt_console) {
+                       ast_verb(0, "Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
+               }
+               for (;;) {
+                       if (!ast_undestroyed_channels() || shuttingdown != niceness) {
+                               break;
                        }
+                       sleep(1);
                }
+       }
 
-               if (!shuttingdown) {
-                       if (option_verbose && ast_opt_console)
-                               ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
-                       return;
+       /* Re-acquire lock and check if someone changed the niceness, in which
+        * case someone else has taken over the shutdown.
+        */
+       ast_mutex_lock(&safe_system_lock);
+       if (shuttingdown != niceness) {
+               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;
+       }
+       shuttingdown = SHUTTING_DOWN;
+       ast_mutex_unlock(&safe_system_lock);
+
+       return 1;
+}
+
+/*! Called when exiting is certain. */
+static void really_quit(int num, shutdown_nice_t niceness, int restart)
+{
+       int active_channels;
+       struct ast_json *json_object = NULL;
+       int run_cleanups = niceness >= SHUTDOWN_NICE;
 
-               if (niceness)
-                       ast_module_shutdown();
+       if (run_cleanups) {
+               ast_module_shutdown();
        }
+
        if (ast_opt_console || (ast_opt_remote && !ast_opt_exec)) {
+               char filename[80] = "";
                if (getenv("HOME")) {
                        snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
                }
@@ -1678,40 +1951,57 @@ static void quit_handler(int num, int niceness, int safeshutdown, 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) {
-               if (option_verbose || ast_opt_console)
-                       ast_verbose("Preparing for Asterisk restart...\n");
+               int i;
+               ast_verb(0, "Preparing for Asterisk restart...\n");
                /* Mark all FD's for closing on exec */
-               for (x=3; x < 32768; x++) {
-                       fcntl(x, F_SETFD, FD_CLOEXEC);
+               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);
@@ -1719,11 +2009,13 @@ static void quit_handler(int num, int niceness, int safeshutdown, int restart)
                        sleep(2);
                } else
                        execvp(_argv[0], _argv);
-       
+
        } else {
                /* close logger */
                close_logger();
+               clean_time_zones();
        }
+
        exit(0);
 }
 
@@ -1745,49 +2037,127 @@ 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;
+
+       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;
+}
+
+struct console_state_data {
+       char verbose_line_level;
+};
+
+static int console_state_init(void *ptr)
+{
+       struct console_state_data *state = ptr;
+       state->verbose_line_level = 0;
+       return 0;
 }
 
+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 void console_verboser(const char *s)
+static int console_print(const char *s, int local)
 {
-       char tmp[80];
-       const char *c = NULL;
-       char level = 0;
+       struct console_state_data *state =
+               ast_threadstorage_get(&console_state, sizeof(*state));
 
-       if (VERBOSE_HASMAGIC(s)) {
-               level = VERBOSE_MAGIC2LEVEL(s);
-               s++;
-               if (level > option_verbose) {
-                       return;
+       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';
+               }
+               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 ((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 {
-               fputs(s, stdout);
+       if (res) {
+               fflush(stdout);
        }
 
-       fflush(stdout);
+       return res;
+}
+
+static void console_verboser(const char *s)
+{
+       if (!console_print(s, 1)) {
+               return;
+       }
 
        /* Wake up a poll()ing console */
        if (ast_opt_console && consolethread != AST_PTHREADT_NULL) {
@@ -1800,7 +2170,7 @@ static int ast_all_zeros(char *s)
        while (*s) {
                if (*s > 32)
                        return 0;
-               s++;  
+               s++;
        }
        return 1;
 }
@@ -1819,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);
 }
 
@@ -1830,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])
@@ -1837,28 +2212,37 @@ static int remoteconsolehandler(char *s)
                else
                        ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
                ret = 1;
-       } else if (strncasecmp(s, "remote set verbose ", 19) == 0) {
-               if (strncasecmp(s + 19, "atleast ", 8) == 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 + 27, "%d", &tmp) != 1) {
-                               fprintf(stderr, "Usage: remote set verbose [atleast] <level>\n");
+                       if (sscanf(s + 25, "%d", &tmp) != 1) {
+                               fprintf(stderr, "Usage: core set verbose [atleast] <level>\n");
                        } else {
                                if (tmp > option_verbose) {
                                        option_verbose = tmp;
                                }
-                               fprintf(stdout, "Set remote console verbosity to %d\n", option_verbose);
+                               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 + 19, "%d", &option_verbose) != 1) {
-                               fprintf(stderr, "Usage: remote set verbose [atleast] <level>\n");
+                       if (sscanf(s + 17, "%d", &option_verbose) != 1) {
+                               fprintf(stderr, "Usage: core set verbose [atleast] <level>\n");
                        } else {
-                               fprintf(stdout, "Set remote console verbosity to %d\n", option_verbose);
+                               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, 0, 0, 0);
+               quit_handler(0, SHUTDOWN_FAST, 0);
                ret = 1;
        }
 
@@ -1870,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;
@@ -1891,7 +2275,7 @@ static int handle_quit(int fd, int argc, char *argv[])
 {
        if (argc != 1)
                return RESULT_SHOWUSAGE;
-       quit_handler(0, 0, 1, 0);
+       quit_handler(0, SHUTDOWN_NORMAL, 0);
        return RESULT_SUCCESS;
 }
 #endif
@@ -1901,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;
@@ -1911,7 +2295,7 @@ static char *handle_stop_now(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
 
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
-       quit_handler(0, 0 /* Not nice */, 1 /* safely */, 0 /* not restart */);
+       quit_handler(0, SHUTDOWN_NORMAL, 0 /* not restart */);
        return CLI_SUCCESS;
 }
 
@@ -1920,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";
@@ -1931,7 +2315,7 @@ static char *handle_stop_gracefully(struct ast_cli_entry *e, int cmd, struct ast
 
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
-       quit_handler(0, 1 /* nicely */, 1 /* safely */, 0 /* no restart */);
+       quit_handler(0, SHUTDOWN_NICE, 0 /* no restart */);
        return CLI_SUCCESS;
 }
 
@@ -1940,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;
@@ -1951,7 +2335,7 @@ static char *handle_stop_when_convenient(struct ast_cli_entry *e, int cmd, struc
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
        ast_cli(a->fd, "Waiting for inactivity to perform halt\n");
-       quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */);
+       quit_handler(0, SHUTDOWN_REALLY_NICE, 0 /* don't restart */);
        return CLI_SUCCESS;
 }
 
@@ -1960,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";
@@ -1971,7 +2355,7 @@ static char *handle_restart_now(struct ast_cli_entry *e, int cmd, struct ast_cli
 
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
-       quit_handler(0, 0 /* not nicely */, 1 /* safely */, 1 /* restart */);
+       quit_handler(0, SHUTDOWN_NORMAL, 1 /* restart */);
        return CLI_SUCCESS;
 }
 
@@ -1980,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";
@@ -1991,7 +2375,7 @@ static char *handle_restart_gracefully(struct ast_cli_entry *e, int cmd, struct
 
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
-       quit_handler(0, 1 /* nicely */, 1 /* safely */, 1 /* restart */);
+       quit_handler(0, SHUTDOWN_NICE, 1 /* restart */);
        return CLI_SUCCESS;
 }
 
@@ -2000,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;
@@ -2011,16 +2395,18 @@ static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, st
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
        ast_cli(a->fd, "Waiting for inactivity to perform restart\n");
-       quit_handler(0, 2 /* really nicely */, 1 /* safely */, 1 /* restart */);
+       quit_handler(0, SHUTDOWN_REALLY_NICE, 1 /* restart */);
        return CLI_SUCCESS;
 }
 
 static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
+       int aborting_shutdown = 0;
+
        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";
@@ -2031,8 +2417,17 @@ static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_
 
        if (a->argc != e->args)
                return CLI_SHOWUSAGE;
-       ast_cancel_shutdown();
-       shuttingdown = 0;
+
+       ast_mutex_lock(&safe_system_lock);
+       if (shuttingdown >= SHUTDOWN_FAST) {
+               aborting_shutdown = 1;
+               shuttingdown = NOT_SHUTTING_DOWN;
+       }
+       ast_mutex_unlock(&safe_system_lock);
+
+       if (aborting_shutdown) {
+               ast_cancel_shutdown();
+       }
        return CLI_SUCCESS;
 }
 
@@ -2041,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;
@@ -2081,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;
@@ -2118,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;
@@ -2135,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"),
@@ -2159,23 +2565,6 @@ static struct ast_cli_entry cli_asterisk[] = {
 #endif /* ! LOW_MEMORY */
 };
 
-struct el_read_char_state_struct {
-       unsigned int line_full:1;
-       unsigned int prev_line_full:1;
-       char prev_line_verbosity;
-};
-
-static int el_read_char_state_init(void *ptr)
-{
-       struct el_read_char_state_struct *state = ptr;
-       state->line_full = 1;
-       state->prev_line_full = 1;
-       state->prev_line_verbosity = 0;
-       return 0;
-}
-
-AST_THREADSTORAGE_CUSTOM(el_read_char_state, el_read_char_state_init, ast_free_ptr);
-
 static int ast_el_read_char(EditLine *editline, char *cp)
 {
        int num_read = 0;
@@ -2185,7 +2574,6 @@ static int ast_el_read_char(EditLine *editline, char *cp)
        int max;
 #define EL_BUF_SIZE 512
        char buf[EL_BUF_SIZE];
-       struct el_read_char_state_struct *state = ast_threadstorage_get(&el_read_char_state, sizeof(*state));
 
        for (;;) {
                max = 1;
@@ -2202,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;
                }
 
@@ -2215,14 +2603,12 @@ static int ast_el_read_char(EditLine *editline, char *cp)
                        }
                }
                if (fds[0].revents) {
-                       char level = 0;
-                       char *curline = buf, *nextline;
                        res = read(ast_consock, buf, sizeof(buf) - 1);
                        /* if the remote side disappears exit */
                        if (res < 1) {
                                fprintf(stderr, "\nDisconnected from Asterisk server\n");
                                if (!ast_opt_reconnect) {
-                                       quit_handler(0, 0, 0, 0);
+                                       quit_handler(0, SHUTDOWN_FAST, 0);
                                } else {
                                        int tries;
                                        int reconnects_per_second = 20;
@@ -2234,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
@@ -2242,9 +2628,10 @@ static int ast_el_read_char(EditLine *editline, char *cp)
                                        }
                                        if (tries >= 30 * reconnects_per_second) {
                                                fprintf(stderr, "Failed to reconnect for 30 seconds.  Quitting.\n");
-                                               quit_handler(0, 0, 0, 0);
+                                               quit_handler(0, SHUTDOWN_FAST, 0);
                                        }
                                }
+                               continue;
                        }
 
                        buf[res] = '\0';
@@ -2255,28 +2642,7 @@ static int ast_el_read_char(EditLine *editline, char *cp)
                                }
                        }
 
-                       do {
-                               state->prev_line_full = state->line_full;
-                               if ((nextline = strchr(curline, '\n'))) {
-                                       state->line_full = 1;
-                                       nextline++;
-                               } else {
-                                       state->line_full = 0;
-                                       nextline = strchr(curline, '\0');
-                               }
-
-                               if (state->prev_line_full && VERBOSE_HASMAGIC(curline)) {
-                                       level = VERBOSE_MAGIC2LEVEL(curline);
-                                       curline++;
-                               }
-                               if ((!state->prev_line_full && state->prev_line_verbosity <= option_verbose) || (state->prev_line_full && level <= option_verbose)) {
-                                       if (write(STDOUT_FILENO, curline, nextline - curline) < 0) {
-                                       }
-                               }
-
-                               state->prev_line_verbosity = level;
-                               curline = nextline;
-                       } while (!ast_strlen_zero(curline));
+                       console_print(buf, 0);
 
                        if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (buf[res-2] == '\n'))) {
                                *cp = CC_REFRESH;
@@ -2298,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;
 
@@ -2325,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;
                                        }
 
@@ -2408,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);
@@ -2416,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;
 }
@@ -2548,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);
@@ -2623,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;
                        }
@@ -2633,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;
 }
@@ -2657,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)
@@ -2696,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;
-       return (history(el_hist, &ev, H_ENTER, ast_strip(ast_strdupa(buf))));
+       }
+
+       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, stripped_buf);
 }
 
 static int ast_el_write_history(char *filename)
@@ -2716,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)
@@ -2766,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));
@@ -2857,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();
@@ -2898,8 +3282,9 @@ static int show_version(void)
        return 0;
 }
 
-static int show_cli_help(void) {
-       printf("Asterisk %s, Copyright (C) 1999 - 2010, Digium, Inc. and others.\n", ast_get_version());
+static int show_cli_help(void)
+{
+       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");
@@ -2930,14 +3315,14 @@ static int show_cli_help(void) {
        printf("   -T              Display the time in [Mmm dd hh:mm:ss] format for each line\n");
        printf("                   of output to the CLI\n");
        printf("   -v              Increase verbosity (multiple v's = more verbose)\n");
-       printf("   -x <cmd>        Execute command <cmd> (only valid with -r)\n");
+       printf("   -x <cmd>        Execute command <cmd> (implies -r)\n");
        printf("   -X              Execute includes by default (allows #exec in asterisk.conf)\n");
        printf("   -W              Adjust terminal colors to compensate for a light background\n");
        printf("\n");
        return 0;
 }
 
-static void ast_readconfig(void) 
+static void ast_readconfig(void)
 {
        struct ast_config *cfg;
        struct ast_variable *v;
@@ -2949,23 +3334,30 @@ 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));
        ast_copy_string(cfg_paths.agi_dir, DEFAULT_AGI_DIR, sizeof(cfg_paths.agi_dir));
        ast_copy_string(cfg_paths.db_path, DEFAULT_DB, sizeof(cfg_paths.db_path));
+       ast_copy_string(cfg_paths.sbin_dir, DEFAULT_SBIN_DIR, sizeof(cfg_paths.sbin_dir));
        ast_copy_string(cfg_paths.key_dir, DEFAULT_KEY_DIR, sizeof(cfg_paths.key_dir));
        ast_copy_string(cfg_paths.pid_path, DEFAULT_PID, sizeof(cfg_paths.pid_path));
        ast_copy_string(cfg_paths.socket_path, DEFAULT_SOCKET, sizeof(cfg_paths.socket_path));
@@ -2995,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)
@@ -3019,6 +3412,8 @@ static void ast_readconfig(void)
                        ast_copy_string(cfg_paths.run_dir, v->value, sizeof(cfg_paths.run_dir));
                } else if (!strcasecmp(v->name, "astmoddir")) {
                        ast_copy_string(cfg_paths.module_dir, v->value, sizeof(cfg_paths.module_dir));
+               } else if (!strcasecmp(v->name, "astsbindir")) {
+                       ast_copy_string(cfg_paths.sbin_dir, v->value, sizeof(cfg_paths.sbin_dir));
                }
        }
 
@@ -3082,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));
@@ -3121,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")) {
@@ -3155,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")) {
@@ -3186,11 +3597,11 @@ static void *monitor_sig_flags(void *unused)
                }
                if (sig_flags.need_quit) {
                        sig_flags.need_quit = 0;
-                       if (consolethread != AST_PTHREADT_NULL) {
+                       if ((consolethread != AST_PTHREADT_NULL) && (consolethread != pthread_self())) {
                                sig_flags.need_quit_handler = 1;
                                pthread_kill(consolethread, SIGURG);
                        } else {
-                               quit_handler(0, 0, 1, 0);
+                               quit_handler(0, SHUTDOWN_NORMAL, 0);
                        }
                }
                if (read(sig_alert_pipe[0], &a, sizeof(a)) != sizeof(a)) {
@@ -3209,10 +3620,16 @@ 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) {
-                       ast_log(LOG_WARNING, "The canary is no more.  He has ceased to be!  He's expired and gone to meet his maker!  He's a stiff!  Bereft of life, he rests in peace.  His metabolic processes are now history!  He's off the twig!  He's kicked the bucket.  He's shuffled off his mortal coil, run down the curtain, and joined the bleeding choir invisible!!  THIS is an EX-CANARY.  (Reducing priority)\n");
+               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!  "
+                               "He's a stiff!  Bereft of life, he rests in peace.  "
+                               "His metabolic processes are now history!  He's off the twig!  "
+                               "He's kicked the bucket.  He's shuffled off his mortal coil, "
+                               "run down the curtain, and joined the bleeding choir invisible!!  "
+                               "THIS is an EX-CANARY.  (Reducing priority)\n");
                        ast_set_priority(0);
                        pthread_exit(NULL);
                }
@@ -3269,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;
@@ -3306,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
@@ -3373,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':
@@ -3424,6 +3856,9 @@ int main(int argc, char *argv[])
                        ast_clear_flag(&ast_options, AST_OPT_FLAG_FORCE_BLACK_BACKGROUND);
                        break;
                case 'x':
+                       /* -r is implied by -x so set the flags -r sets as well. */
+                       ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
+
                        ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC | AST_OPT_FLAG_NO_COLOR);
                        xarg = ast_strdupa(optarg);
                        break;
@@ -3432,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). */
@@ -3452,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);
        }
 
@@ -3475,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)
@@ -3497,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;
                }
@@ -3511,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);
@@ -3537,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));
                }
        }
 
@@ -3551,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)) {
@@ -3576,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)) {
@@ -3589,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 */
        }
@@ -3628,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
@@ -3640,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, 0, 0, 0);
+                               quit_handler(0, SHUTDOWN_FAST, 0);
                                exit(0);
                        }
+                       print_intro_message(runuser, rungroup);
                        printf("%s", term_quit());
                        ast_remotecontrol(NULL);
-                       quit_handler(0, 0, 0, 0);
+                       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);
@@ -3736,7 +4138,7 @@ int main(int argc, char *argv[])
 
                canary_pid = fork();
                if (canary_pid == 0) {
-                       char canary_binary[128], *lastslash, ppid[12];
+                       char canary_binary[PATH_MAX], ppid[12];
 
                        /* Reset signal handler */
                        signal(SIGCHLD, SIG_DFL);
@@ -3746,14 +4148,9 @@ int main(int argc, char *argv[])
                        ast_set_priority(0);
                        snprintf(ppid, sizeof(ppid), "%d", (int) ast_mainpid);
 
-                       execlp("astcanary", "astcanary", canary_filename, ppid, (char *)NULL);
-
-                       /* If not found, try the same path as used to execute asterisk */
-                       ast_copy_string(canary_binary, argv[0], sizeof(canary_binary));
-                       if ((lastslash = strrchr(canary_binary, '/'))) {
-                               ast_copy_string(lastslash + 1, "astcanary", sizeof(canary_binary) + canary_binary - (lastslash + 1));
-                               execl(canary_binary, "astcanary", canary_filename, ppid, (char *)NULL);
-                       }
+                       /* Use the astcanary binary that we installed */
+                       snprintf(canary_binary, sizeof(canary_binary), "%s/astcanary", ast_config_AST_SBIN_DIR);
+                       execl(canary_binary, "astcanary", canary_filename, ppid, (char *)NULL);
 
                        /* Should never happen */
                        _exit(1);
@@ -3766,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);
        }
@@ -3784,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);
@@ -3816,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();
 
@@ -3830,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());
@@ -3853,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);
@@ -3863,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);
        }
@@ -3885,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();
 
@@ -3903,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);
        }
@@ -3928,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);
@@ -3942,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();
 
@@ -3983,7 +4518,7 @@ int main(int argc, char *argv[])
 
                for (;;) {
                        if (sig_flags.need_quit || sig_flags.need_quit_handler) {
-                               quit_handler(0, 0, 0, 0);
+                               quit_handler(0, SHUTDOWN_FAST, 0);
                                break;
                        }
                        buf = (char *) el_gets(el, &num);