Newer
Older
printf("Received HUP signal -- Reloading configs\n");
if (restartnow)
execvp(_argv[0], _argv);
/* XXX This could deadlock XXX */
Mark Spencer
committed
ast_module_reload(NULL);
Kevin P. Fleming
committed
signal(num, hup_handler);
static void child_handler(int sig)
/* Must not ever ast_log or ast_verbose within signal handler */
int n, status;
/*
* Reap all dead children -- not just one
*/
for (n = 0; wait4(-1, &status, WNOHANG, NULL) > 0; n++)
;
if (n == 0 && option_debug)
printf("Huh? Child handler, but nobody there?\n");
Kevin P. Fleming
committed
signal(sig, child_handler);
static void set_title(char *text)
{
if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
fprintf(stdout, "\033]2;%s\007", text);
}
static void set_icon(char *text)
{
if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
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. */
Kevin P. Fleming
committed
int ast_set_priority(int 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");
} else {
sched.sched_priority = 0;
if (sched_setscheduler(0, SCHED_OTHER, &sched)) {
ast_log(LOG_WARNING, "Unable to set normal priority\n");
return -1;
}
}
#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
static void ast_run_atexits(void)
{
struct ast_atexit *ae;
Russell Bryant
committed
AST_LIST_LOCK(&atexits);
AST_LIST_TRAVERSE(&atexits, ae, list) {
if (ae->func)
ae->func();
}
Russell Bryant
committed
AST_LIST_UNLOCK(&atexits);
}
static void quit_handler(int num, int nice, int safeshutdown, int restart)
/* Try to get as many CDRs as possible submitted to the backend engines (if in batch mode) */
ast_cdr_engine_term();
if (safeshutdown) {
shuttingdown = 1;
if (!nice) {
/* Begin shutdown routine, hanging up active channels */
ast_begin_shutdown(1);
Russell Bryant
committed
if (option_verbose && ast_opt_console)
ast_verbose("Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
time(&s);
Olle Johansson
committed
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);
}
} else {
if (nice < 2)
ast_begin_shutdown(0);
Russell Bryant
committed
if (option_verbose && ast_opt_console)
ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
Olle Johansson
committed
for (;;) {
if (!ast_active_channels())
break;
if (!shuttingdown)
break;
sleep(1);
}
}
if (!shuttingdown) {
Russell Bryant
committed
if (option_verbose && ast_opt_console)
ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
return;
}
}
Russell Bryant
committed
if (ast_opt_console || ast_opt_remote) {
if (getenv("HOME"))
snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
ast_el_write_history(filename);
if (el != NULL)
el_end(el);
if (el_hist != NULL)
history_end(el_hist);
if (option_verbose)
ast_verbose("Executing last minute cleanups\n");
ast_run_atexits();
Russell Bryant
committed
if (option_verbose && ast_opt_console)
ast_verbose("Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
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");
Kevin P. Fleming
committed
unlink(ast_config_AST_SOCKET);
pthread_cancel(lthread);
Russell Bryant
committed
if (!ast_opt_remote)
unlink(ast_config_AST_PID);
Russell Bryant
committed
if (option_verbose || ast_opt_console)
ast_verbose("Preparing for Asterisk restart...\n");
/* Mark all FD's for closing on exec */
Olle Johansson
committed
for (x=3; x < 32768; x++) {
Russell Bryant
committed
if (option_verbose || ast_opt_console)
restartnow = 1;
/* close logger */
close_logger();
/* 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);
/* Give the signal handler some time to complete */
sleep(2);
} else
execvp(_argv[0], _argv);
} else {
/* close logger */
close_logger();
}
exit(0);
}
static void __quit_handler(int num)
{
quit_handler(num, 0, 1, 0);
static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
const char *c;
if (!strncmp(s, cmp, strlen(cmp))) {
c = s + strlen(cmp);
static void console_verboser(const char *s, int pos, int replace, int complete)
Olle Johansson
committed
const char *c = NULL;
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)))
if (c)
fputs(c + pos,stdout);
else
fputs(s + pos,stdout);
if (complete) {
/* Wake up a poll()ing console */
Russell Bryant
committed
if (ast_opt_console && consolethread != AST_PTHREADT_NULL)
static int ast_all_zeros(char *s)
{
Olle Johansson
committed
while (*s) {
if (*s > 32)
return 0;
s++;
}
return 1;
}
if (s && !ast_all_zeros(s))
/* Give the console access to the shell */
if (s) {
Mark Spencer
committed
ast_safe_system(s+1);
Mark Spencer
committed
ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
} else
fprintf(stdout, "\nUse \"quit\" to exit\n");
Mark Spencer
committed
if (s && !ast_all_zeros(s))
/* Give the console access to the shell */
if (s) {
Mark Spencer
committed
ast_safe_system(s+1);
Mark Spencer
committed
ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
(s[4] == '\0' || isspace(s[4]))) {
} else
fprintf(stdout, "\nUse \"quit\" to exit\n");
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[] =
" Shuts down a running Asterisk immediately, hanging up all active calls .\n";
static char shutdown_gracefully_help[] =
" Causes Asterisk to not accept new calls, and exit when all\n"
" active calls have terminated normally.\n";
static char shutdown_when_convenient_help[] =
"Usage: stop when convenient\n"
" Causes Asterisk to perform a shutdown when all active calls have ended.\n";
static char restart_now_help[] =
"Usage: restart now\n"
" Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
" 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"
" 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";
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";
Russell Bryant
committed
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;
}
static int handle_quit(int fd, int argc, char *argv[])
{
if (argc != 1)
return RESULT_SHOWUSAGE;
quit_handler(0, 0, 1, 0);
return RESULT_SUCCESS;
}
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;
}
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");
quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */);
return RESULT_SUCCESS;
}
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");
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;
static int handle_bang(int fd, int argc, char *argv[])
{
return RESULT_SUCCESS;
}
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
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;
}
Kevin P. Fleming
committed
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 },
Russell Bryant
committed
{ { "show", "version", NULL }, handle_version,
"Display version info", version_help },
Kevin P. Fleming
committed
{ { "!", 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 */
Kevin P. Fleming
committed
};
static int ast_el_read_char(EditLine *el, char *cp)
{
Olle Johansson
committed
int num_read = 0;
int lastpos = 0;
struct pollfd fds[2];
int res;
int max;
char buf[512];
for (;;) {
max = 1;
fds[0].fd = ast_consock;
fds[0].events = POLLIN;
Russell Bryant
committed
if (!ast_opt_exec) {
fds[1].fd = STDIN_FILENO;
fds[1].events = POLLIN;
max++;
res = poll(fds, max, -1);
if (res < 0) {
if (errno == EINTR)
continue;
ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
Russell Bryant
committed
if (!ast_opt_exec && fds[1].revents) {
num_read = read(STDIN_FILENO, cp, 1);
if (num_read < 1) {
break;
} else
return (num_read);
}
if (fds[0].revents) {
res = read(ast_consock, buf, sizeof(buf) - 1);
/* if the remote side disappears exit */
if (res < 1) {
fprintf(stderr, "\nDisconnected from Asterisk server\n");
Russell Bryant
committed
if (!ast_opt_reconnect) {
quit_handler(0, 0, 0, 0);
} else {
int tries;
int reconnects_per_second = 20;
fprintf(stderr, "Attempting to reconnect for 30 seconds\n");
Olle Johansson
committed
for (tries=0; tries < 30 * reconnects_per_second; tries++) {
if (ast_tryconnect()) {
fprintf(stderr, "Reconnect succeeded after %.3f seconds\n", 1.0 / reconnects_per_second * tries);
printf(term_quit());
break;
} else {
usleep(1000000 / reconnects_per_second);
}
}
fprintf(stderr, "Failed to reconnect for 30 seconds. Quitting.\n");
quit_handler(0, 0, 0, 0);
}
}
Russell Bryant
committed
if (!ast_opt_exec && !lastpos)
write(STDOUT_FILENO, "\r", 1);
write(STDOUT_FILENO, buf, res);
if ((buf[res-1] == '\n') || (buf[res-2] == '\n')) {
} else {
lastpos = 1;
}
}
}
*cp = '\0';
return (0);
}
static char *cli_prompt(EditLine *el)
static char prompt[200];
char *pfmt;
Olle Johansson
committed
int color_used = 0;
char term_code[20];
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 i;
struct tm tm;
#ifdef linux
FILE *LOADAVG;
int fgcolor = COLOR_WHITE, bgcolor = COLOR_BLACK;
t++;
switch (*t) {
Olle Johansson
committed
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;
}
Olle Johansson
committed
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
/* 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;
}
}
Olle Johansson
committed
strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
} else {
strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
}
break;
#ifdef linux
Olle Johansson
committed
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
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;
}
}
Olle Johansson
committed
}
break;
#endif
case 's': /* Asterisk system name (from asterisk.conf) */
strncat(p, ast_config_AST_SYSTEM_NAME, sizeof(prompt) - strlen(prompt) - 1);
break;
Olle Johansson
committed
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;
}
while (*p != '\0') {
p++;
}
t++;
} else {
*p = *t;
p++;
t++;
}
}
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));
}
}
} else if (remotehostname)
snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname);
else
snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT);
}
static char **ast_el_strtoarr(char *buf)
{
char **match_list = NULL, *retstr;
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 */
}
}
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
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;
qsort(&matches[0], (size_t)(len), sizeof(char *), ast_el_sort_compare);
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;
free(matches[idx]);
matches[idx] = NULL;
}
if (numoutputline > 0)
fprintf(stdout, "\n");
}
return numoutput;
static char *cli_complete(EditLine *el, int ch)
Olle Johansson
committed
int len = 0;
char *ptr;
int nummatches = 0;
char **matches;
int retval = CC_ERROR;
James Golovich
committed
char buf[2048];
LineInfo *lf = (LineInfo *)el_line(el);
if (ptr) {
while (ptr > lf->buffer) {
if (isspace(*ptr)) {
ptr++;
break;
}
ptr--;
}
}
len = lf->cursor - ptr;
Russell Bryant
committed
if (ast_opt_remote) {
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);
snprintf(buf, sizeof(buf),"_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr);
fdprint(ast_consock, buf);
res = 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)))
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);
matches = ast_cli_completion_matches((char *)lf->buffer,ptr);
for (p = matches; p && *p; p++) {
if (!oldbuf || strcmp(*p,oldbuf))
nummatches++;
oldbuf = *p;
}
}
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 */
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 {
Russell Bryant
committed
free(matches);
}
static int ast_el_initialize(void)
{
HistEvent ev;
char *editor = getenv("AST_EDITOR");
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");
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);
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;