Skip to content
Snippets Groups Projects
asterisk.c 67.9 KiB
Newer Older
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";

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 },
	{ { "!", 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 },
#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");
Mark Spencer's avatar
Mark Spencer committed
	if (!version)
		version = "<Version Unknown>";
Mark Spencer's avatar
Mark Spencer committed
	strsep(&stringp, ".");
Mark Spencer's avatar
Mark Spencer committed
	if (cpid)
		pid = atoi(cpid);
	else
		pid = -1;
	snprintf(tmp, sizeof(tmp), "set verbose atleast %d", option_verbose);
Mark Spencer's avatar
Mark Spencer committed
	fdprint(ast_consock, tmp);
	snprintf(tmp, sizeof(tmp), "set debug atleast %d", option_debug);
Mark Spencer's avatar
Mark Spencer committed
	ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
Mark Spencer's avatar
Mark Spencer committed
	remotehostname = hostname;
Mark Spencer's avatar
Mark Spencer committed
	if (getenv("HOME")) 
		snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
Mark Spencer's avatar
Mark Spencer committed
	if (el_hist == NULL || el == NULL)
		ast_el_initialize();

	el_set(el, EL_GETCFN, ast_el_read_char);

	if (!ast_strlen_zero(filename))
Mark Spencer's avatar
Mark Spencer committed
		ast_el_read_history(filename);

	if (ast_opt_exec && data) {  /* hack to print output then exit if asterisk -rx is used */
Mark Spencer's avatar
Mark Spencer committed
		char tempchar;
		struct pollfd fds[0];
		fds[0].fd = ast_consock;
		fds[0].events = POLLIN;
		fds[0].revents = 0;
		while(poll(fds, 1, 100) > 0) {
			ast_el_read_char(el, &tempchar);
		}
Mark Spencer's avatar
Mark Spencer committed
		return;
	}
Mark Spencer's avatar
Mark Spencer committed
		ebuf = (char *)el_gets(el, &num);

Mark Spencer's avatar
Mark Spencer committed
			if (ebuf[strlen(ebuf)-1] == '\n')
				ebuf[strlen(ebuf)-1] = '\0';
			if (!remoteconsolehandler(ebuf)) {
				res = write(ast_consock, ebuf, strlen(ebuf) + 1);
Mark Spencer's avatar
Mark Spencer committed
				if (res < 1) {
					ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
					break;
				}
			}
		}
	}
	printf("\nDisconnected from Asterisk server\n");
}

Mark Spencer's avatar
Mark Spencer committed
static int show_version(void)
{
	printf("Asterisk " ASTERISK_VERSION "\n");
	return 0;
}

Mark Spencer's avatar
Mark Spencer committed
static int show_cli_help(void) {
	printf("Asterisk " ASTERISK_VERSION ", Copyright (C) 1999 - 2006, Digium, Inc. and others.\n");
Mark Spencer's avatar
Mark Spencer committed
	printf("Usage: asterisk [OPTIONS]\n");
	printf("Valid Options:\n");
Mark Spencer's avatar
Mark Spencer committed
	printf("   -V              Display version number and exit\n");
	printf("   -C <configfile> Use an alternate configuration file\n");
	printf("   -G <group>      Run as a group other than the caller\n");
	printf("   -U <user>       Run as a user other than the caller\n");
	printf("   -c              Provide console CLI\n");
	printf("   -d              Enable extra debugging\n");
	printf("   -f              Do not fork\n");
	printf("   -g              Dump core in case of a crash\n");
	printf("   -h              This help screen\n");
	printf("   -i              Initialize crypto keys at startup\n");
	printf("   -I              Enable internal timing if Zaptel timer is available\n");
	printf("   -L <load>       Limit the maximum load average before rejecting new calls\n");
	printf("   -M <value>      Limit the maximum number of calls to the specified value\n");
	printf("   -n              Disable console colorization\n");
	printf("   -p              Run as pseudo-realtime thread\n");
	printf("   -q              Quiet mode (suppress output)\n");
	printf("   -r              Connect to Asterisk on this machine\n");
	printf("   -R              Connect to Asterisk, and attempt to reconnect if disconnected\n");
	printf("   -t              Record soundfiles in /var/tmp and move them where they belong after they are done.\n");
	printf("   -T              Display the time in [Mmm dd hh:mm:ss] format for each line 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");
Mark Spencer's avatar
Mark Spencer committed
	printf("\n");
	return 0;
}

Mark Spencer's avatar
Mark Spencer committed
	struct ast_config *cfg;
	struct ast_variable *v;
		cfg = ast_config_load(ast_config_AST_CONFIG_FILE);
			ast_log(LOG_WARNING, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
Mark Spencer's avatar
Mark Spencer committed
	} else {
		cfg = ast_config_load(config);
Mark Spencer's avatar
Mark Spencer committed
	}

	/* init with buildtime config */
	ast_copy_string(ast_config_AST_CONFIG_DIR, AST_CONFIG_DIR, sizeof(ast_config_AST_CONFIG_DIR));
	ast_copy_string(ast_config_AST_SPOOL_DIR, AST_SPOOL_DIR, sizeof(ast_config_AST_SPOOL_DIR));
	ast_copy_string(ast_config_AST_MODULE_DIR, AST_MODULE_DIR, sizeof(ast_config_AST_MODULE_DIR));
 	snprintf(ast_config_AST_MONITOR_DIR, sizeof(ast_config_AST_MONITOR_DIR) - 1, "%s/monitor", ast_config_AST_SPOOL_DIR);
	ast_copy_string(ast_config_AST_VAR_DIR, AST_VAR_DIR, sizeof(ast_config_AST_VAR_DIR));
	ast_copy_string(ast_config_AST_LOG_DIR, AST_LOG_DIR, sizeof(ast_config_AST_LOG_DIR));
	ast_copy_string(ast_config_AST_AGI_DIR, AST_AGI_DIR, sizeof(ast_config_AST_AGI_DIR));
	ast_copy_string(ast_config_AST_DB, AST_DB, sizeof(ast_config_AST_DB));
	ast_copy_string(ast_config_AST_KEY_DIR, AST_KEY_DIR, sizeof(ast_config_AST_KEY_DIR));
	ast_copy_string(ast_config_AST_PID, AST_PID, sizeof(ast_config_AST_PID));
	ast_copy_string(ast_config_AST_SOCKET, AST_SOCKET, sizeof(ast_config_AST_SOCKET));
	ast_copy_string(ast_config_AST_RUN_DIR, AST_RUN_DIR, sizeof(ast_config_AST_RUN_DIR));

Mark Spencer's avatar
Mark Spencer committed
	/* no asterisk.conf? no problem, use buildtime config! */
	if (!cfg) {
James Golovich's avatar
James Golovich committed
		return;
Mark Spencer's avatar
Mark Spencer committed
	}
	v = ast_variable_browse(cfg, "files");
	while (v) {
		if (!strcasecmp(v->name, "astctlpermissions")) {
			ast_copy_string(ast_config_AST_CTL_PERMISSIONS, v->value, sizeof(ast_config_AST_CTL_PERMISSIONS));
		} else if (!strcasecmp(v->name, "astctlowner")) {
			ast_copy_string(ast_config_AST_CTL_OWNER, v->value, sizeof(ast_config_AST_CTL_OWNER));
		} else if (!strcasecmp(v->name, "astctlgroup")) {
			ast_copy_string(ast_config_AST_CTL_GROUP, v->value, sizeof(ast_config_AST_CTL_GROUP));
		} else if (!strcasecmp(v->name, "astctl")) {
			ast_copy_string(ast_config_AST_CTL, v->value, sizeof(ast_config_AST_CTL));
		}
		v = v->next;
Mark Spencer's avatar
Mark Spencer committed
	v = ast_variable_browse(cfg, "directories");
	while(v) {
		if (!strcasecmp(v->name, "astetcdir")) {
			ast_copy_string(ast_config_AST_CONFIG_DIR, v->value, sizeof(ast_config_AST_CONFIG_DIR));
Mark Spencer's avatar
Mark Spencer committed
		} else if (!strcasecmp(v->name, "astspooldir")) {
			ast_copy_string(ast_config_AST_SPOOL_DIR, v->value, sizeof(ast_config_AST_SPOOL_DIR));
			snprintf(ast_config_AST_MONITOR_DIR, sizeof(ast_config_AST_MONITOR_DIR) - 1, "%s/monitor", v->value);
Mark Spencer's avatar
Mark Spencer committed
		} else if (!strcasecmp(v->name, "astvarlibdir")) {
			ast_copy_string(ast_config_AST_VAR_DIR, v->value, sizeof(ast_config_AST_VAR_DIR));
			snprintf(ast_config_AST_DB, sizeof(ast_config_AST_DB), "%s/astdb", v->value);
			snprintf(ast_config_AST_KEY_DIR, sizeof(ast_config_AST_KEY_DIR), "%s/keys", v->value);
Mark Spencer's avatar
Mark Spencer committed
		} else if (!strcasecmp(v->name, "astlogdir")) {
			ast_copy_string(ast_config_AST_LOG_DIR, v->value, sizeof(ast_config_AST_LOG_DIR));
Mark Spencer's avatar
Mark Spencer committed
		} else if (!strcasecmp(v->name, "astagidir")) {
			ast_copy_string(ast_config_AST_AGI_DIR, v->value, sizeof(ast_config_AST_AGI_DIR));
Mark Spencer's avatar
Mark Spencer committed
		} else if (!strcasecmp(v->name, "astrundir")) {
			snprintf(ast_config_AST_PID, sizeof(ast_config_AST_PID), "%s/%s", v->value, "asterisk.pid");
			snprintf(ast_config_AST_SOCKET, sizeof(ast_config_AST_SOCKET), "%s/%s", v->value, ast_config_AST_CTL);
			ast_copy_string(ast_config_AST_RUN_DIR, v->value, sizeof(ast_config_AST_RUN_DIR));
Mark Spencer's avatar
Mark Spencer committed
		} else if (!strcasecmp(v->name, "astmoddir")) {
			ast_copy_string(ast_config_AST_MODULE_DIR, v->value, sizeof(ast_config_AST_MODULE_DIR));
Mark Spencer's avatar
Mark Spencer committed
		}
		v = v->next;
	}
	v = ast_variable_browse(cfg, "options");
	while(v) {
		/* verbose level (-v at startup) */
		if (!strcasecmp(v->name, "verbose")) {
		/* whether or not to force timestamping. (-T at startup) */
		} else if (!strcasecmp(v->name, "timestamp")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TIMESTAMP);
		/* whether or not to support #exec in config files */
		} else if (!strcasecmp(v->name, "execincludes")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_EXEC_INCLUDES);
Mark Spencer's avatar
Mark Spencer committed
		/* debug level (-d at startup) */
		} else if (!strcasecmp(v->name, "debug")) {
			option_debug = 0;
			if (sscanf(v->value, "%d", &option_debug) != 1) {
				option_debug = ast_true(v->value);
			}
		/* Disable forking (-f at startup) */
		} else if (!strcasecmp(v->name, "nofork")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_FORK);
		/* Run quietly (-q at startup ) */
		} else if (!strcasecmp(v->name, "quiet")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_QUIET);
		/* Run as console (-c at startup, implies nofork) */
		} else if (!strcasecmp(v->name, "console")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CONSOLE);
		/* Run with high priority if the O/S permits (-p at startup) */
		} else if (!strcasecmp(v->name, "highpriority")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIGH_PRIORITY);
		/* Initialize RSA auth keys (IAX2) (-i at startup) */
		} else if (!strcasecmp(v->name, "initcrypto")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INIT_KEYS);
		/* Disable ANSI colors for console (-c at startup) */
		} else if (!strcasecmp(v->name, "nocolor")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_COLOR);
		/* Disable some usage warnings for picky people :p */
		} else if (!strcasecmp(v->name, "dontwarn")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DONT_WARN);
		/* Dump core in case of crash (-g) */
		} else if (!strcasecmp(v->name, "dumpcore")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DUMP_CORE);
		/* Cache recorded sound files to another directory during recording */
		} else if (!strcasecmp(v->name, "cache_record_files")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_RECORD_FILES);
		/* Specify cache directory */
		}  else if (!strcasecmp(v->name, "record_cache_dir")) {
			ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN);
		/* Build transcode paths via SLINEAR, instead of directly */
		} else if (!strcasecmp(v->name, "transcode_via_sln")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSCODE_VIA_SLIN);
		/* Transmit SLINEAR silence while a channel is being recorded */
		} else if (!strcasecmp(v->name, "transmit_silence_during_record")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSMIT_SILENCE);
		/* Enable internal timing */
		} else if (!strcasecmp(v->name, "internal_timing")) {
			ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INTERNAL_TIMING);
Mark Spencer's avatar
Mark Spencer committed
		} else if (!strcasecmp(v->name, "maxcalls")) {
			if ((sscanf(v->value, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0)) {
Mark Spencer's avatar
Mark Spencer committed
				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, "%lf", &option_maxload) != 1) || (option_maxload < 0.0)) {
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		/* What user to run as */
		} else if (!strcasecmp(v->name, "runuser")) {
			ast_copy_string(ast_config_AST_RUN_USER, v->value, sizeof(ast_config_AST_RUN_USER));
		/* What group to run as */
		} else if (!strcasecmp(v->name, "rungroup")) {
			ast_copy_string(ast_config_AST_RUN_GROUP, v->value, sizeof(ast_config_AST_RUN_GROUP));
		} else if (!strcasecmp(v->name, "systemname")) {
			ast_copy_string(ast_config_AST_SYSTEM_NAME, v->value, sizeof(ast_config_AST_SYSTEM_NAME));