Skip to content
Snippets Groups Projects
asterisk.c 74.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    #else
    	if (pri) {
    		if (setpriority(PRIO_PROCESS, 0, -10) == -1) {
    			ast_log(LOG_WARNING, "Unable to set high priority\n");
    			return -1;
    		} else
    			if (option_verbose)
    				ast_verbose("Set to high priority\n");
    	} else {
    		if (setpriority(PRIO_PROCESS, 0, 0) == -1) {
    			ast_log(LOG_WARNING, "Unable to set normal priority\n");
    			return -1;
    		}
    	}
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static void ast_run_atexits(void)
    {
    	struct ast_atexit *ae;
    
    	AST_LIST_LOCK(&atexits);
    	AST_LIST_TRAVERSE(&atexits, ae, list) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void quit_handler(int num, int nice, int safeshutdown, int restart)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char filename[80] = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	time_t s,e;
    	int x;
    
    	/* Try to get as many CDRs as possible submitted to the backend engines (if in batch mode) */
    	ast_cdr_engine_term();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (safeshutdown) {
    		shuttingdown = 1;
    		if (!nice) {
    			/* Begin shutdown routine, hanging up active channels */
    			ast_begin_shutdown(1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose("Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
    			time(&s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				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);
    			}
    		} else {
    			if (nice < 2)
    				ast_begin_shutdown(0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (!ast_active_channels())
    					break;
    				if (!shuttingdown)
    					break;
    				sleep(1);
    			}
    		}
    
    		if (!shuttingdown) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
    			return;
    		}
    	}
    
    	if (ast_opt_console || ast_opt_remote) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (getenv("HOME")) 
    			snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
    
    		if (!ast_strlen_zero(filename))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_el_write_history(filename);
    		if (el != NULL)
    			el_end(el);
    		if (el_hist != NULL)
    			history_end(el_hist);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (option_verbose)
    		ast_verbose("Executing last minute cleanups\n");
    	ast_run_atexits();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Called on exit */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_verbose("Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else if (option_debug)
    		ast_log(LOG_DEBUG, "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");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ast_socket > -1) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		close(ast_socket);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_socket = -1;
    
    		unlink(ast_config_AST_SOCKET);
    		pthread_cancel(lthread);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ast_consock > -1)
    		close(ast_consock);
    
    	if (!ast_opt_remote)
    		unlink(ast_config_AST_PID);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	printf(term_quit());
    	if (restart) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_verbose("Preparing for Asterisk restart...\n");
    		/* Mark all FD's for closing on exec */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			fcntl(x, F_SETFD, FD_CLOEXEC);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_verbose("Restarting Asterisk NOW...\n");
    
    		/* 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);
    
    James Golovich's avatar
    James Golovich committed
    			/* Give the signal handler some time to complete */
    			sleep(2);
    		} else
    
    	} else {
    		/* close logger */
    		close_logger();
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static void __quit_handler(int num)
    {
    	quit_handler(num, 0, 1, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	const char *c;
    	if (!strncmp(s, cmp, strlen(cmp))) {
    		c = s + strlen(cmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		term_color(outbuf, cmp, COLOR_GRAY, 0, maxout);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return NULL;
    
    static void console_verboser(const char *s, int pos, int replace, int complete)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char tmp[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Return to the beginning of the line */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!pos) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		fprintf(stdout, "\r");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			fputs(tmp, stdout);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (c)
    		fputs(c + pos,stdout);
    	else
    		fputs(s + pos,stdout);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	fflush(stdout);
    
    	if (complete) {
    		/* Wake up a poll()ing console */
    
    		if (ast_opt_console && consolethread != AST_PTHREADT_NULL)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			pthread_kill(consolethread, SIGURG);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void consolehandler(char *s)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	printf(term_end());
    	fflush(stdout);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Called when readline data is available */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_el_add_history(s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Give the console access to the shell */
    	if (s) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* The real handler for bang */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (s[0] == '!') {
    			if (s[1])
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else
    
    				ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cli_command(STDOUT_FILENO, s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    		fprintf(stdout, "\nUse \"quit\" to exit\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int remoteconsolehandler(char *s)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int ret = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Called when readline data is available */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_el_add_history(s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Give the console access to the shell */
    	if (s) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* The real handler for bang */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (s[0] == '!') {
    			if (s[1])
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else
    
    				ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ret = 1;
    		}
    
    		if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
    		    (s[4] == '\0' || isspace(s[4]))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			quit_handler(0, 0, 0, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ret = 1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    		fprintf(stdout, "\nUse \"quit\" to exit\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return ret;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char abort_halt_help[] = 
    "Usage: abort shutdown\n"
    "       Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
    "       call operations.\n";
    
    static char shutdown_now_help[] = 
    
    Mark Spencer's avatar
    Mark Spencer committed
    "Usage: stop now\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "       Shuts down a running Asterisk immediately, hanging up all active calls .\n";
    
    static char shutdown_gracefully_help[] = 
    
    Mark Spencer's avatar
    Mark Spencer committed
    "Usage: stop gracefully\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "       Causes Asterisk to not accept new calls, and exit when all\n"
    "       active calls have terminated normally.\n";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char shutdown_when_convenient_help[] = 
    "Usage: stop when convenient\n"
    "       Causes Asterisk to perform a shutdown when all active calls have ended.\n";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char restart_now_help[] = 
    "Usage: restart now\n"
    
    "       Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "       restart.\n";
    
    static char restart_gracefully_help[] = 
    "Usage: restart gracefully\n"
    
    "       Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "       restart when all active calls have ended.\n";
    
    static char restart_when_convenient_help[] = 
    "Usage: restart when convenient\n"
    "       Causes Asterisk to perform a cold restart when all active calls have ended.\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char bang_help[] =
    "Usage: !<command>\n"
    "       Executes a given shell command\n";
    
    
    static char show_warranty_help[] =
    "Usage: show warranty\n"
    "	Shows the warranty (if any) for this copy of Asterisk.\n";
    
    static char show_license_help[] =
    "Usage: show license\n"
    "	Shows the license(s) for this copy of Asterisk.\n";
    
    
    static char version_help[] =
    "Usage: show version\n"
    "       Shows Asterisk version information.\n";
    
    static int handle_version(int fd, int argc, char *argv[])
    {
    	if (argc != 2)
    		return RESULT_SHOWUSAGE;
    	ast_cli(fd, "Asterisk %s built by %s @ %s on a %s running %s on %s\n",
    		ASTERISK_VERSION, ast_build_user, ast_build_hostname,
    		ast_build_machine, ast_build_os, ast_build_date);
    	return RESULT_SUCCESS;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int handle_quit(int fd, int argc, char *argv[])
    {
    	if (argc != 1)
    		return RESULT_SHOWUSAGE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	quit_handler(0, 0, 1, 0);
    	return RESULT_SUCCESS;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int handle_shutdown_now(int fd, int argc, char *argv[])
    {
    	if (argc != 2)
    		return RESULT_SHOWUSAGE;
    	quit_handler(0, 0 /* Not nice */, 1 /* safely */, 0 /* not restart */);
    	return RESULT_SUCCESS;
    }
    
    static int handle_shutdown_gracefully(int fd, int argc, char *argv[])
    {
    	if (argc != 2)
    		return RESULT_SHOWUSAGE;
    	quit_handler(0, 1 /* nicely */, 1 /* safely */, 0 /* no restart */);
    	return RESULT_SUCCESS;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int handle_shutdown_when_convenient(int fd, int argc, char *argv[])
    {
    	if (argc != 3)
    		return RESULT_SHOWUSAGE;
    
    	ast_cli(fd, "Waiting for inactivity to perform halt\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */);
    	return RESULT_SUCCESS;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int handle_restart_now(int fd, int argc, char *argv[])
    {
    	if (argc != 2)
    		return RESULT_SHOWUSAGE;
    	quit_handler(0, 0 /* not nicely */, 1 /* safely */, 1 /* restart */);
    	return RESULT_SUCCESS;
    }
    
    static int handle_restart_gracefully(int fd, int argc, char *argv[])
    {
    	if (argc != 2)
    		return RESULT_SHOWUSAGE;
    	quit_handler(0, 1 /* nicely */, 1 /* safely */, 1 /* restart */);
    	return RESULT_SUCCESS;
    }
    
    static int handle_restart_when_convenient(int fd, int argc, char *argv[])
    {
    	if (argc != 3)
    		return RESULT_SHOWUSAGE;
    
    	ast_cli(fd, "Waiting for inactivity to perform restart\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	quit_handler(0, 2 /* really nicely */, 1 /* safely */, 1 /* restart */);
    	return RESULT_SUCCESS;
    }
    
    static int handle_abort_halt(int fd, int argc, char *argv[])
    {
    	if (argc != 2)
    		return RESULT_SHOWUSAGE;
    	ast_cancel_shutdown();
    	shuttingdown = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return RESULT_SUCCESS;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int handle_bang(int fd, int argc, char *argv[])
    {
    	return RESULT_SUCCESS;
    }
    
    static const char *warranty_lines[] = {
    	"\n",
    	"			    NO WARRANTY\n",
    	"\n",
    	"BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n",
    	"FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\n",
    	"OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n",
    	"PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n",
    	"OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n",
    	"MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\n",
    	"TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\n",
    	"PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n",
    	"REPAIR OR CORRECTION.\n",
    	"\n",
    	"IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n",
    	"WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n",
    	"REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n",
    	"INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n",
    	"OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n",
    	"TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n",
    	"YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n",
    	"PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n",
    	"POSSIBILITY OF SUCH DAMAGES.\n",
    };
    
    static int show_warranty(int fd, int argc, char *argv[])
    {
    	int x;
    
    	for (x = 0; x < sizeof(warranty_lines) / sizeof(warranty_lines[0]); x++)
    		ast_cli(fd, (char *) warranty_lines[x]);
    
    	return RESULT_SUCCESS;
    }
    
    static const char *license_lines[] = {
    	"\n",
    	"This program is free software; you can redistribute it and/or modify\n",
    	"it under the terms of the GNU General Public License version 2 as\n",
    	"published by the Free Software Foundation.\n",
    	"\n",
    	"This program also contains components licensed under other licenses.\n",
    	"They include:\n",
    	"\n",
    	"This program is distributed in the hope that it will be useful,\n",
    	"but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
    	"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n",
    	"GNU General Public License for more details.\n",
    	"\n",
    	"You should have received a copy of the GNU General Public License\n",
    	"along with this program; if not, write to the Free Software\n",
    	"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n",
    };
    
    static int show_license(int fd, int argc, char *argv[])
    {
    	int x;
    
    	for (x = 0; x < sizeof(license_lines) / sizeof(license_lines[0]); x++)
    		ast_cli(fd, (char *) license_lines[x]);
    
    	return RESULT_SUCCESS;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define ASTERISK_PROMPT "*CLI> "
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define ASTERISK_PROMPT2 "%s*CLI> "
    
    
    static struct ast_cli_entry core_cli[] = {
    	{ { "abort", "halt", NULL }, handle_abort_halt,
    	  "Cancel a running halt", abort_halt_help },
    	{ { "stop", "now", NULL }, handle_shutdown_now,
    	  "Shut down Asterisk immediately", shutdown_now_help },
    	{ { "stop", "gracefully", NULL }, handle_shutdown_gracefully,
    	  "Gracefully shut down Asterisk", shutdown_gracefully_help },
    	{ { "stop", "when","convenient", NULL }, handle_shutdown_when_convenient,
    	  "Shut down Asterisk at empty call volume", shutdown_when_convenient_help },
    	{ { "restart", "now", NULL }, handle_restart_now,
    	  "Restart Asterisk immediately", restart_now_help },
    	{ { "restart", "gracefully", NULL }, handle_restart_gracefully,
    	  "Restart Asterisk gracefully", restart_gracefully_help },
    	{ { "restart", "when", "convenient", NULL }, handle_restart_when_convenient,
    	  "Restart Asterisk at empty call volume", restart_when_convenient_help },
    
    	{ { "show", "warranty", NULL }, show_warranty,
    	  "Show the warranty (if any) for this copy of Asterisk", show_warranty_help },
    	{ { "show", "license", NULL }, show_license,
    	  "Show the license(s) for this copy of Asterisk", show_license_help },
    
    	{ { "show", "version", NULL }, handle_version, 
    	  "Display version info", version_help },
    
    	{ { "!", NULL }, handle_bang,
    	  "Execute a shell command", bang_help },
    
    #if !defined(LOW_MEMORY)
    
    	{ { "show", "version", "files", NULL }, handle_show_version_files,
    	  "Show versions of files used to build Asterisk", show_version_files_help, complete_show_version_files },
    
    	{ { "show", "threads", NULL }, handle_show_threads,
    	  "Show running threads", show_threads_help, NULL },
    	{ { "show", "profile", NULL }, handle_show_profile,
    	  "Show profiling info"},
    	{ { "clear", "profile", NULL }, handle_show_profile,
    	  "Clear profiling info"},
    
    #endif /* ! LOW_MEMORY */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int ast_el_read_char(EditLine *el, char *cp)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    	int max;
    	char buf[512];
    
    	for (;;) {
    
    		max = 1;
    		fds[0].fd = ast_consock;
    		fds[0].events = POLLIN;
    
    			fds[1].fd = STDIN_FILENO;
    			fds[1].events = POLLIN;
    			max++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		res = poll(fds, max, -1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res < 0) {
    			if (errno == EINTR)
    				continue;
    
    			ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			num_read = read(STDIN_FILENO, cp, 1);
    			if (num_read < 1) {
    				break;
    			} else 
    				return (num_read);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = read(ast_consock, buf, sizeof(buf) - 1);
    			/* if the remote side disappears exit */
    			if (res < 1) {
    				fprintf(stderr, "\nDisconnected from Asterisk server\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    					int reconnects_per_second = 20;
    					fprintf(stderr, "Attempting to reconnect for 30 seconds\n");
    
    					for (tries=0; tries < 30 * reconnects_per_second; tries++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    							fprintf(stderr, "Reconnect succeeded after %.3f seconds\n", 1.0 / reconnects_per_second * tries);
    
    Mark Spencer's avatar
    Mark Spencer committed
    							WELCOME_MESSAGE;
    
    							break;
    						} else {
    							usleep(1000000 / reconnects_per_second);
    						}
    					}
    
    					if (tries >= 30 * reconnects_per_second) {
    
    						fprintf(stderr, "Failed to reconnect for 30 seconds.  Quitting.\n");
    						quit_handler(0, 0, 0, 0);
    					}
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			buf[res] = '\0';
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    				write(STDOUT_FILENO, "\r", 1);
    			write(STDOUT_FILENO, buf, res);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((buf[res-1] == '\n') || (buf[res-2] == '\n')) {
    
    				*cp = CC_REFRESH;
    				return(1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    				lastpos = 1;
    			}
    		}
    	}
    
    	*cp = '\0';
    	return (0);
    }
    
    static char *cli_prompt(EditLine *el)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    
    	if ((pfmt = getenv("ASTERISK_PROMPT"))) {
    		char *t = pfmt, *p = prompt;
    		memset(prompt, 0, sizeof(prompt));
    		while (*t != '\0' && *p < sizeof(prompt)) {
    			if (*t == '%') {
    
    				char hostname[MAXHOSTNAMELEN]="";
    
    				int fgcolor = COLOR_WHITE, bgcolor = COLOR_BLACK;
    
    
    				case 'C': /* color */
    					t++;
    					if (sscanf(t, "%d;%d%n", &fgcolor, &bgcolor, &i) == 2) {
    						strncat(p, term_color_code(term_code, fgcolor, bgcolor, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
    						t += i - 1;
    					} else if (sscanf(t, "%d%n", &fgcolor, &i) == 1) {
    						strncat(p, term_color_code(term_code, fgcolor, 0, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
    						t += i - 1;
    					}
    
    					/* If the color has been reset correctly, then there's no need to reset it later */
    					if ((fgcolor == COLOR_WHITE) && (bgcolor == COLOR_BLACK)) {
    						color_used = 0;
    					} else {
    						color_used = 1;
    					}
    					break;
    				case 'd': /* date */
    					memset(&tm, 0, sizeof(tm));
    					time(&ts);
    					if (localtime_r(&ts, &tm)) {
    						strftime(p, sizeof(prompt) - strlen(prompt), "%Y-%m-%d", &tm);
    					}
    					break;
    				case 'h': /* hostname */
    					if (!gethostname(hostname, sizeof(hostname) - 1)) {
    						strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
    					} else {
    						strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
    					}
    					break;
    				case 'H': /* short hostname */
    					if (!gethostname(hostname, sizeof(hostname) - 1)) {
    						for (i = 0; i < sizeof(hostname); i++) {
    							if (hostname[i] == '.') {
    								hostname[i] = '\0';
    								break;
    
    						strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
    					} else {
    						strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
    					}
    					break;
    
    				case 'l': /* load avg */
    					t++;
    					if ((LOADAVG = fopen("/proc/loadavg", "r"))) {
    						float avg1, avg2, avg3;
    						int actproc, totproc, npid, which;
    						fscanf(LOADAVG, "%f %f %f %d/%d %d",
    							&avg1, &avg2, &avg3, &actproc, &totproc, &npid);
    						if (sscanf(t, "%d", &which) == 1) {
    							switch (which) {
    							case 1:
    								snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg1);
    								break;
    							case 2:
    								snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg2);
    								break;
    							case 3:
    								snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg3);
    								break;
    							case 4:
    								snprintf(p, sizeof(prompt) - strlen(prompt), "%d/%d", actproc, totproc);
    								break;
    							case 5:
    								snprintf(p, sizeof(prompt) - strlen(prompt), "%d", npid);
    								break;
    
    				case 't': /* time */
    					memset(&tm, 0, sizeof(tm));
    					time(&ts);
    					if (localtime_r(&ts, &tm)) {
    						strftime(p, sizeof(prompt) - strlen(prompt), "%H:%M:%S", &tm);
    					}
    					break;
    				case '#': /* process console or remote? */
    					if (!ast_opt_remote) {
    						strncat(p, "#", sizeof(prompt) - strlen(prompt) - 1);
    					} else {
    						strncat(p, ">", sizeof(prompt) - strlen(prompt) - 1);
    					}
    					break;
    				case '%': /* literal % */
    					strncat(p, "%", sizeof(prompt) - strlen(prompt) - 1);
    					break;
    				case '\0': /* % is last character - prevent bug */
    					t--;
    					break;
    
    		if (color_used) {
    			/* Force colors back to normal at end */
    			term_color_code(term_code, COLOR_WHITE, COLOR_BLACK, sizeof(term_code));
    			if (strlen(term_code) > sizeof(prompt) - strlen(prompt)) {
    				strncat(prompt + sizeof(prompt) - strlen(term_code) - 1, term_code, strlen(term_code));
    			} else {
    				strncat(p, term_code, sizeof(term_code));
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname);
    	else
    		snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT);
    
    
    	return(prompt);	
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static char **ast_el_strtoarr(char *buf)
    {
    	char **match_list = NULL, *retstr;
    
    	size_t match_list_len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int matches = 0;
    
    
    	match_list_len = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	while ( (retstr = strsep(&buf, " ")) != NULL) {
    
    
    		if (!strcmp(retstr, AST_CLI_COMPLETE_EOF))
    			break;
    
    		if (matches + 1 >= match_list_len) {
    			match_list_len <<= 1;
    
    			if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(char *)))) {
    				/* TODO: Handle memory allocation failure */
    			}
    
    		match_list[matches++] = strdup(retstr);
    
    	if (!match_list)
    		return (char **) NULL;
    
    	if (matches >= match_list_len) {
    		if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *)))) {
    			/* TODO: Handle memory allocation failure */
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	match_list[matches] = (char *) NULL;
    
    	return match_list;
    }
    
    static int ast_el_sort_compare(const void *i1, const void *i2)
    {
    	char *s1, *s2;
    
    	s1 = ((char **)i1)[0];
    	s2 = ((char **)i2)[0];
    
    	return strcasecmp(s1, s2);
    }
    
    static int ast_cli_display_match_list(char **matches, int len, int max)
    {
    	int i, idx, limit, count;
    	int screenwidth = 0;
    	int numoutput = 0, numoutputline = 0;
    
    	screenwidth = ast_get_termcols(STDOUT_FILENO);
    
    	/* find out how many entries can be put on one line, with two spaces between strings */
    	limit = screenwidth / (max + 2);
    	if (limit == 0)
    		limit = 1;
    
    	/* how many lines of output */
    	count = len / limit;
    	if (count * limit < len)
    		count++;
    
    	idx = 1;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	qsort(&matches[0], (size_t)(len), sizeof(char *), ast_el_sort_compare);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	for (; count > 0; count--) {
    		numoutputline = 0;
    		for (i=0; i < limit && matches[idx]; i++, idx++) {
    
    			/* Don't print dupes */
    			if ( (matches[idx+1] != NULL && strcmp(matches[idx], matches[idx+1]) == 0 ) ) {
    				i--;
    
    				free(matches[idx]);
    				matches[idx] = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				continue;
    			}
    
    
    			numoutput++;
    			numoutputline++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			fprintf(stdout, "%-*s  ", max, matches[idx]);
    
    			free(matches[idx]);
    			matches[idx] = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		if (numoutputline > 0)
    			fprintf(stdout, "\n");
    	}
    
    	return numoutput;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char *cli_complete(EditLine *el, int ch)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *ptr;
    	int nummatches = 0;
    	char **matches;
    	int retval = CC_ERROR;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	LineInfo *lf = (LineInfo *)el_line(el);
    
    
    	*(char *)lf->cursor = '\0';
    
    	ptr = (char *)lf->cursor;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ptr) {
    		while (ptr > lf->buffer) {
    			if (isspace(*ptr)) {
    				ptr++;
    				break;
    			}
    			ptr--;
    		}
    	}
    
    	len = lf->cursor - ptr;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		snprintf(buf, sizeof(buf),"_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr); 
    		fdprint(ast_consock, buf);
    		res = read(ast_consock, buf, sizeof(buf));
    		buf[res] = '\0';
    		nummatches = atoi(buf);
    
    		if (nummatches > 0) {
    
    			char *mbuf;
    			int mlen = 0, maxmbuf = 2048;
    
    			/* Start with a 2048 byte buffer */			
    			if (!(mbuf = ast_malloc(maxmbuf)))
    
    				return (char *)(CC_ERROR);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			snprintf(buf, sizeof(buf),"_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr); 
    			fdprint(ast_consock, buf);
    
    			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)))
    
    						return (char *)(CC_ERROR);
    
    				}
    				/* Only read 1024 bytes at a time */
    				res = read(ast_consock, mbuf + mlen, 1024);
    				if (res > 0)
    					mlen += res;
    			}
    			mbuf[mlen] = '\0';
    
    			matches = ast_el_strtoarr(mbuf);
    			free(mbuf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else
    			matches = (char **) NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char **p, *oldbuf=NULL;
    		nummatches = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		matches = ast_cli_completion_matches((char *)lf->buffer,ptr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		for (p = matches; p && *p; p++) {
    			if (!oldbuf || strcmp(*p,oldbuf))
    				nummatches++;
    			oldbuf = *p;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (matches) {
    		int i;
    		int matches_num, maxlen, match_len;
    
    		if (matches[0][0] != '\0') {
    			el_deletestr(el, (int) len);
    			el_insertstr(el, matches[0]);
    			retval = CC_REFRESH;
    		}
    
    		if (nummatches == 1) {
    			/* Found an exact match */
    
    			el_insertstr(el, " ");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			retval = CC_REFRESH;
    		} else {
    			/* Must be more than one match */
    			for (i=1, maxlen=0; matches[i]; i++) {
    				match_len = strlen(matches[i]);
    				if (match_len > maxlen)
    					maxlen = match_len;
    			}
    			matches_num = i - 1;
    			if (matches_num >1) {
    				fprintf(stdout, "\n");
    				ast_cli_display_match_list(matches, nummatches, maxlen);
    				retval = CC_REDISPLAY;
    			} else { 
    
    				el_insertstr(el," ");
    
    Mark Spencer's avatar
    Mark Spencer committed
    				retval = CC_REFRESH;
    			}
    		}
    
    	return (char *)(long)retval;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int ast_el_initialize(void)
    {
    	HistEvent ev;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (el != NULL)
    		el_end(el);
    	if (el_hist != NULL)
    		history_end(el_hist);
    
    	el = el_init("asterisk", stdin, stdout, stderr);
    	el_set(el, EL_PROMPT, cli_prompt);
    
    	el_set(el, EL_EDITMODE, 1);		
    
    	el_set(el, EL_EDITOR, editor ? editor : "emacs");		
    
    Mark Spencer's avatar
    Mark Spencer committed
    	el_hist = history_init();
    	if (!el || !el_hist)
    		return -1;
    
    	/* setup history with 100 entries */
    	history(el_hist, &ev, H_SETSIZE, 100);
    
    	el_set(el, EL_HIST, history, el_hist);
    
    	el_set(el, EL_ADDFN, "ed-complete", "Complete argument", cli_complete);
    	/* Bind <tab> to command completion */
    	el_set(el, EL_BIND, "^I", "ed-complete", NULL);
    	/* Bind ? to command completion */
    	el_set(el, EL_BIND, "?", "ed-complete", NULL);
    
    	/* Bind ^D to redisplay */
    	el_set(el, EL_BIND, "^D", "ed-redisplay", NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 0;
    }
    
    static int ast_el_add_history(char *buf)
    {
    	HistEvent ev;
    
    	if (el_hist == NULL || el == NULL)
    		ast_el_initialize();
    
    	if (strlen(buf) > 256)
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return (history(el_hist, &ev, H_ENTER, buf));
    }
    
    static int ast_el_write_history(char *filename)
    {
    	HistEvent ev;
    
    	if (el_hist == NULL || el == NULL)
    		ast_el_initialize();
    
    	return (history(el_hist, &ev, H_SAVE, filename));
    }
    
    static int ast_el_read_history(char *filename)
    {
    	char buf[256];
    	FILE *f;
    	int ret = -1;
    
    	if (el_hist == NULL || el == NULL)
    		ast_el_initialize();
    
    	if ((f = fopen(filename, "r")) == NULL)
    		return ret;
    
    	while (!feof(f)) {
    		fgets(buf, sizeof(buf), f);
    		if (!strcmp(buf, "_HiStOrY_V2_\n"))
    			continue;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((ret = ast_el_add_history(buf)) == -1)
    			break;
    	}
    	fclose(f);
    
    	return ret;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static void ast_remotecontrol(char * data)
    {
    	char buf[80];
    	int res;
    	char filename[80] = "";
    	char *hostname;
    	char *cpid;
    	char *version;
    	int pid;
    	char tmp[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	char *ebuf;
    	int num = 0;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	read(ast_consock, buf, sizeof(buf));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (data)
    		write(ast_consock, data, strlen(data) + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	hostname = strsep(&stringp, "/");
    	cpid = strsep(&stringp, "/");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	version = strsep(&stringp, "\n");