2002 lines
50 KiB
C
2002 lines
50 KiB
C
|
/*
|
||
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||
|
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
|
||
|
*
|
||
|
* Version: MPL 1.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Anthony Minessale II <anthm@freeswitch.org>
|
||
|
* Portions created by the Initial Developer are Copyright (C)
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Anthony Minessale II <anthm@freeswitch.org>
|
||
|
*
|
||
|
*
|
||
|
* switch_console.c -- Simple Console
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <switch.h>
|
||
|
#include <switch_console.h>
|
||
|
#ifndef _MSC_VER
|
||
|
#include <switch_private.h>
|
||
|
#endif
|
||
|
#define CMD_BUFLEN 1024
|
||
|
|
||
|
#ifdef HAVE_LIBEDIT
|
||
|
#include <histedit.h>
|
||
|
|
||
|
static EditLine *el;
|
||
|
static History *myhistory;
|
||
|
static HistEvent ev;
|
||
|
static char *hfile = NULL;
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define CC_NORM 0
|
||
|
#define CC_NEWLINE 1
|
||
|
#define CC_EOF 2
|
||
|
#define CC_ARGHACK 3
|
||
|
#define CC_REFRESH 4
|
||
|
#define CC_CURSOR 5
|
||
|
#define CC_ERROR 6
|
||
|
#define CC_FATAL 7
|
||
|
#define CC_REDISPLAY 8
|
||
|
#define CC_REFRESH_BEEP 9
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
#define HISTLEN 10
|
||
|
#define KEY_UP 1
|
||
|
#define KEY_DOWN 2
|
||
|
#define KEY_TAB 3
|
||
|
#define CLEAR_OP 4
|
||
|
#define DELETE_REFRESH_OP 5
|
||
|
#define KEY_LEFT 6
|
||
|
#define KEY_RIGHT 7
|
||
|
#define KEY_INSERT 8
|
||
|
#define PROMPT_OP 9
|
||
|
#define KEY_DELETE 10
|
||
|
|
||
|
static int console_bufferInput(char *buf, int len, char *cmd, int key);
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* store a strdup() of the string configured in XML
|
||
|
* bound to each of the 12 function key
|
||
|
*/
|
||
|
static char *console_fnkeys[12];
|
||
|
|
||
|
/*
|
||
|
* Load from console.conf XML file the section:
|
||
|
* <keybindings>
|
||
|
* <key name="1" value="show calls"/>
|
||
|
* </keybindings>
|
||
|
*/
|
||
|
static switch_status_t console_xml_config(void)
|
||
|
{
|
||
|
char *cf = "switch.conf";
|
||
|
switch_xml_t cfg, xml, settings, param;
|
||
|
|
||
|
/* clear the keybind array */
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 12; i++) {
|
||
|
console_fnkeys[i] = NULL;
|
||
|
}
|
||
|
|
||
|
if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
|
||
|
return SWITCH_STATUS_TERM;
|
||
|
}
|
||
|
|
||
|
if ((settings = switch_xml_child(cfg, "cli-keybindings"))) {
|
||
|
for (param = switch_xml_child(settings, "key"); param; param = param->next) {
|
||
|
char *var = (char *) switch_xml_attr_soft(param, "name");
|
||
|
char *val = (char *) switch_xml_attr_soft(param, "value");
|
||
|
i = atoi(var);
|
||
|
if ((i < 1) || (i > 12)) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Keybind %s is invalid, range is from 1 to 12\n", var);
|
||
|
} else {
|
||
|
/* Add the command to the fnkey array */
|
||
|
console_fnkeys[i - 1] = switch_core_permanent_strdup(val);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch_xml_free(xml);
|
||
|
|
||
|
return SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_stream_raw_write(switch_stream_handle_t *handle, uint8_t *data, switch_size_t datalen)
|
||
|
{
|
||
|
switch_size_t need = handle->data_len + datalen;
|
||
|
|
||
|
if (need >= handle->data_size) {
|
||
|
void *new_data;
|
||
|
need += handle->alloc_chunk;
|
||
|
|
||
|
if (!(new_data = realloc(handle->data, need))) {
|
||
|
return SWITCH_STATUS_MEMERR;
|
||
|
}
|
||
|
|
||
|
handle->data = new_data;
|
||
|
handle->data_size = need;
|
||
|
}
|
||
|
|
||
|
memcpy((uint8_t *) (handle->data) + handle->data_len, data, datalen);
|
||
|
handle->data_len += datalen;
|
||
|
handle->end = (uint8_t *) (handle->data) + handle->data_len;
|
||
|
*(uint8_t *) handle->end = '\0';
|
||
|
|
||
|
return SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_stream_write(switch_stream_handle_t *handle, const char *fmt, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
char *end = handle->end;
|
||
|
int ret = 0;
|
||
|
char *data = NULL;
|
||
|
|
||
|
if (handle->data_len >= handle->data_size) {
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
|
||
|
va_start(ap, fmt);
|
||
|
//ret = switch_vasprintf(&data, fmt, ap);
|
||
|
if (!(data = switch_vmprintf(fmt, ap))) {
|
||
|
ret = -1;
|
||
|
}
|
||
|
va_end(ap);
|
||
|
|
||
|
if (data) {
|
||
|
switch_size_t remaining = handle->data_size - handle->data_len;
|
||
|
switch_size_t need = strlen(data) + 1;
|
||
|
|
||
|
if ((remaining < need) && handle->alloc_len) {
|
||
|
switch_size_t new_len;
|
||
|
void *new_data;
|
||
|
|
||
|
new_len = handle->data_size + need + handle->alloc_chunk;
|
||
|
if ((new_data = realloc(handle->data, new_len))) {
|
||
|
handle->data_size = handle->alloc_len = new_len;
|
||
|
handle->data = new_data;
|
||
|
remaining = handle->data_size - handle->data_len;
|
||
|
handle->end = (uint8_t *) (handle->data) + handle->data_len;
|
||
|
end = handle->end;
|
||
|
} else {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
|
||
|
free(data);
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (remaining < need) {
|
||
|
ret = -1;
|
||
|
} else {
|
||
|
ret = 0;
|
||
|
switch_snprintf(end, remaining, "%s", data);
|
||
|
handle->data_len += strlen(data);
|
||
|
handle->end = (uint8_t *) (handle->data) + handle->data_len;
|
||
|
}
|
||
|
free(data);
|
||
|
}
|
||
|
|
||
|
return ret ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_stream_write_file_contents(switch_stream_handle_t *stream, const char *path)
|
||
|
{
|
||
|
char *dpath = NULL;
|
||
|
FILE *fd = NULL;
|
||
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
||
|
|
||
|
if (!switch_is_file_path(path)) {
|
||
|
dpath = switch_mprintf("%s%s%s", SWITCH_GLOBAL_dirs.conf_dir, SWITCH_PATH_SEPARATOR, path);
|
||
|
path = dpath;
|
||
|
}
|
||
|
|
||
|
if ((fd = fopen(path, "r"))) {
|
||
|
char *line_buf = NULL;
|
||
|
switch_size_t llen = 0;
|
||
|
|
||
|
while (switch_fp_read_dline(fd, &line_buf, &llen)) {
|
||
|
stream->write_function(stream, "%s", line_buf);
|
||
|
}
|
||
|
fclose(fd);
|
||
|
switch_safe_free(line_buf);
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
switch_safe_free(dpath);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static int alias_callback(void *pArg, int argc, char **argv, char **columnNames)
|
||
|
{
|
||
|
char **r = (char **) pArg;
|
||
|
*r = strdup(argv[0]);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(char *) switch_console_expand_alias(char *cmd, char *arg)
|
||
|
{
|
||
|
char *errmsg = NULL;
|
||
|
char *r = NULL;
|
||
|
char *sql = NULL;
|
||
|
char *exp = NULL;
|
||
|
switch_cache_db_handle_t *db = NULL;
|
||
|
switch_core_flag_t cflags = switch_core_flags();
|
||
|
int full = 0;
|
||
|
|
||
|
|
||
|
if (!(cflags & SCF_USE_SQL)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
|
||
|
sql = switch_mprintf("select command from aliases where alias='%q'", cmd);
|
||
|
} else {
|
||
|
sql = switch_mprintf("select command from aliases where alias='%w'", cmd);
|
||
|
}
|
||
|
|
||
|
switch_cache_db_execute_sql_callback(db, sql, alias_callback, &r, &errmsg);
|
||
|
|
||
|
if (errmsg) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", sql, errmsg);
|
||
|
free(errmsg);
|
||
|
}
|
||
|
|
||
|
switch_safe_free(sql);
|
||
|
|
||
|
if (!r) {
|
||
|
if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
|
||
|
sql = switch_mprintf("select command from aliases where alias='%q %q'", cmd, arg);
|
||
|
} else {
|
||
|
sql = switch_mprintf("select command from aliases where alias='%w %w'", cmd, arg);
|
||
|
}
|
||
|
|
||
|
switch_cache_db_execute_sql_callback(db, sql, alias_callback, &r, &errmsg);
|
||
|
|
||
|
if (errmsg) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", sql, errmsg);
|
||
|
free(errmsg);
|
||
|
}
|
||
|
if (r) {
|
||
|
full++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch_safe_free(sql);
|
||
|
|
||
|
if (r) {
|
||
|
if (arg && !full) {
|
||
|
exp = switch_mprintf("%s %s", r, arg);
|
||
|
free(r);
|
||
|
} else {
|
||
|
exp = r;
|
||
|
}
|
||
|
} else {
|
||
|
exp = cmd;
|
||
|
}
|
||
|
|
||
|
switch_cache_db_release_db_handle(&db);
|
||
|
|
||
|
return exp;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int switch_console_process(char *xcmd)
|
||
|
{
|
||
|
switch_stream_handle_t stream = { 0 };
|
||
|
switch_status_t status;
|
||
|
FILE *handle = switch_core_get_console();
|
||
|
int r = 1;
|
||
|
|
||
|
SWITCH_STANDARD_STREAM(stream);
|
||
|
switch_assert(stream.data);
|
||
|
|
||
|
status = switch_console_execute(xcmd, 0, &stream);
|
||
|
|
||
|
if (status == SWITCH_STATUS_SUCCESS) {
|
||
|
if (handle) {
|
||
|
fprintf(handle, "\n%s\n", (char *) stream.data);
|
||
|
fflush(handle);
|
||
|
}
|
||
|
} else {
|
||
|
if (!strcasecmp(xcmd, "...") || !strcasecmp(xcmd, "shutdown")) {
|
||
|
r = 0;
|
||
|
}
|
||
|
if (handle) {
|
||
|
fprintf(handle, "Unknown Command: %s\n", xcmd);
|
||
|
fflush(handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch_safe_free(stream.data);
|
||
|
|
||
|
return r;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_execute(char *xcmd, int rec, switch_stream_handle_t *istream)
|
||
|
{
|
||
|
char *arg = NULL, *alias = NULL;
|
||
|
|
||
|
char *delim = ";;";
|
||
|
int argc;
|
||
|
char *argv[128];
|
||
|
int x;
|
||
|
char *dup = NULL;
|
||
|
char *cmd;
|
||
|
|
||
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
||
|
|
||
|
|
||
|
if (rec > 100) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Too much recursion!\n");
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
while (*xcmd == ' ') xcmd++;
|
||
|
|
||
|
dup = strdup(xcmd);
|
||
|
|
||
|
if (!strncasecmp(xcmd, "alias", 5)) {
|
||
|
argc = 1;
|
||
|
argv[0] = xcmd;
|
||
|
} else {
|
||
|
argc = switch_separate_string_string(dup, delim, argv, 128);
|
||
|
}
|
||
|
|
||
|
for (x = 0; x < argc; x++) {
|
||
|
cmd = argv[x];
|
||
|
if ((arg = strchr(cmd, '\r')) != 0 || (arg = strchr(cmd, '\n')) != 0) {
|
||
|
*arg = '\0';
|
||
|
arg = NULL;
|
||
|
}
|
||
|
if ((arg = strchr(cmd, ' ')) != 0) {
|
||
|
*arg++ = '\0';
|
||
|
}
|
||
|
|
||
|
if ((alias = switch_console_expand_alias(cmd, arg)) && alias != cmd) {
|
||
|
istream->write_function(istream, "\nExpand Alias [%s]->[%s]\n\n", cmd, alias);
|
||
|
status = switch_console_execute(alias, ++rec, istream);
|
||
|
free(alias);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
status = switch_api_execute(cmd, arg, NULL, istream);
|
||
|
}
|
||
|
|
||
|
end:
|
||
|
|
||
|
switch_safe_free(dup);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_printf(switch_text_channel_t channel, const char *file, const char *func, int line, const char *fmt, ...)
|
||
|
{
|
||
|
char *data = NULL;
|
||
|
int ret = 0;
|
||
|
va_list ap;
|
||
|
FILE *handle = switch_core_data_channel(channel);
|
||
|
const char *filep = switch_cut_path(file);
|
||
|
char date[80] = "";
|
||
|
switch_size_t retsize;
|
||
|
switch_time_exp_t tm;
|
||
|
switch_event_t *event;
|
||
|
|
||
|
va_start(ap, fmt);
|
||
|
ret = switch_vasprintf(&data, fmt, ap);
|
||
|
va_end(ap);
|
||
|
|
||
|
if (ret == -1) {
|
||
|
fprintf(stderr, "Memory Error\n");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
if (channel == SWITCH_CHANNEL_ID_LOG_CLEAN) {
|
||
|
fprintf(handle, "%s", data);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
switch_time_exp_lt(&tm, switch_micro_time_now());
|
||
|
switch_strftime_nocheck(date, &retsize, sizeof(date), "%Y-%m-%d %T", &tm);
|
||
|
|
||
|
if (channel == SWITCH_CHANNEL_ID_LOG) {
|
||
|
fprintf(handle, "[%d] %s %s:%d %s() %s", (int) getpid(), date, filep, line, func, data);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
if (channel == SWITCH_CHANNEL_ID_EVENT &&
|
||
|
switch_event_running() == SWITCH_STATUS_SUCCESS && switch_event_create(&event, SWITCH_EVENT_LOG) == SWITCH_STATUS_SUCCESS) {
|
||
|
|
||
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Log-Data", data);
|
||
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Log-File", filep);
|
||
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Log-Function", func);
|
||
|
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Log-Line", "%d", line);
|
||
|
switch_event_fire(&event);
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
if (data) {
|
||
|
free(data);
|
||
|
}
|
||
|
fflush(handle);
|
||
|
}
|
||
|
|
||
|
static int32_t running = 1;
|
||
|
|
||
|
struct helper {
|
||
|
int len;
|
||
|
int hits;
|
||
|
int words;
|
||
|
char last[512];
|
||
|
char partial[512];
|
||
|
FILE *out;
|
||
|
switch_stream_handle_t *stream;
|
||
|
switch_xml_t xml;
|
||
|
int xml_off;
|
||
|
};
|
||
|
|
||
|
static int comp_callback(void *pArg, int argc, char **argv, char **columnNames)
|
||
|
{
|
||
|
struct helper *h = (struct helper *) pArg;
|
||
|
char *target = NULL, *str = NULL, *cur = NULL;
|
||
|
switch_size_t x, y, i;
|
||
|
|
||
|
|
||
|
if (argc > 0)
|
||
|
target = argv[0];
|
||
|
if (argc > 1)
|
||
|
str = argv[1];
|
||
|
if (argc > 2)
|
||
|
cur = argv[2];
|
||
|
|
||
|
if (cur) {
|
||
|
while (*cur == ' ')
|
||
|
cur++;
|
||
|
}
|
||
|
|
||
|
if (zstr(cur))
|
||
|
cur = NULL;
|
||
|
if (zstr(str))
|
||
|
str = NULL;
|
||
|
|
||
|
if (!target) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (!zstr(target) && *target == ':' && *(target + 1) == ':' && *(target + 2) == '[') {
|
||
|
char *p = target + 3, *list = NULL;
|
||
|
|
||
|
if (p) {
|
||
|
char *s_argv[100] = { 0 };
|
||
|
char *r_argv[1] = { 0 }, *r_cols[1] = {0};
|
||
|
list = strdup(p);
|
||
|
|
||
|
argc = switch_separate_string(list, ':', s_argv, (sizeof(s_argv) / sizeof(s_argv[0])));
|
||
|
|
||
|
for (i = 0; (int)i < argc; i++) {
|
||
|
if (!cur || !strncmp(s_argv[i], cur, strlen(cur))) {
|
||
|
r_argv[0] = s_argv[i];
|
||
|
comp_callback(h, 1, r_argv, r_cols);
|
||
|
}
|
||
|
}
|
||
|
switch_safe_free(list);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!zstr(target) && *target == ':' && *(target + 1) == ':') {
|
||
|
char *r_argv[1] = { 0 }, *r_cols[1] = {0};
|
||
|
switch_console_callback_match_t *matches;
|
||
|
if (switch_console_run_complete_func(target, str, cur, &matches) == SWITCH_STATUS_SUCCESS) {
|
||
|
switch_console_callback_match_node_t *m;
|
||
|
for (m = matches->head; m; m = m->next) {
|
||
|
if (!cur || !strncmp(m->val, cur, strlen(cur))) {
|
||
|
r_argv[0] = m->val;
|
||
|
comp_callback(h, 1, r_argv, r_cols);
|
||
|
}
|
||
|
}
|
||
|
switch_console_free_matches(&matches);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (!zstr(target)) {
|
||
|
if (h->out) {
|
||
|
fprintf(h->out, "[%20s]\t", target);
|
||
|
}
|
||
|
if (h->stream) {
|
||
|
h->stream->write_function(h->stream, "[%20s]\t", target);
|
||
|
}
|
||
|
if (h->xml) {
|
||
|
switch_xml_set_txt_d(switch_xml_add_child_d(h->xml, "match", h->xml_off++), target);
|
||
|
}
|
||
|
|
||
|
switch_copy_string(h->last, target, sizeof(h->last));
|
||
|
h->hits++;
|
||
|
}
|
||
|
|
||
|
x = strlen(h->last);
|
||
|
y = strlen(h->partial);
|
||
|
|
||
|
if (h->hits > 1) {
|
||
|
for (i = 0; i < x && i < y; i++) {
|
||
|
if (h->last[i] != h->partial[i]) {
|
||
|
h->partial[i] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else if (h->hits == 1) {
|
||
|
switch_copy_string(h->partial, target, sizeof(h->last));
|
||
|
}
|
||
|
|
||
|
if (!zstr(target)) {
|
||
|
#ifdef _MSC_VER
|
||
|
if ((h->hits % 3) == 0) {
|
||
|
#else
|
||
|
if ((h->hits % 4) == 0) {
|
||
|
#endif
|
||
|
if (h->out) {
|
||
|
fprintf(h->out, "\n");
|
||
|
}
|
||
|
if (h->stream) {
|
||
|
h->stream->write_function(h->stream, "\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
struct match_helper {
|
||
|
switch_console_callback_match_t *my_matches;
|
||
|
};
|
||
|
|
||
|
static int modulename_callback(void *pArg, const char *module_name)
|
||
|
{
|
||
|
struct match_helper *h = (struct match_helper *) pArg;
|
||
|
|
||
|
switch_console_push_match(&h->my_matches, module_name);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_available_modules(const char *line, const char *cursor, switch_console_callback_match_t **matches)
|
||
|
{
|
||
|
struct match_helper h = { 0 };
|
||
|
|
||
|
if (switch_loadable_module_enumerate_available(SWITCH_GLOBAL_dirs.mod_dir, modulename_callback, &h) != SWITCH_STATUS_SUCCESS) {
|
||
|
return SWITCH_STATUS_GENERR;
|
||
|
}
|
||
|
|
||
|
if (h.my_matches) {
|
||
|
*matches = h.my_matches;
|
||
|
return SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_loaded_modules(const char *line, const char *cursor, switch_console_callback_match_t **matches)
|
||
|
{
|
||
|
struct match_helper h = { 0 };
|
||
|
|
||
|
if (switch_loadable_module_enumerate_loaded(modulename_callback, &h) != SWITCH_STATUS_SUCCESS) {
|
||
|
return SWITCH_STATUS_GENERR;
|
||
|
}
|
||
|
|
||
|
if (h.my_matches) {
|
||
|
*matches = h.my_matches;
|
||
|
return SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_GETIFADDRS
|
||
|
#include <ifaddrs.h>
|
||
|
#include <net/if.h>
|
||
|
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches)
|
||
|
{
|
||
|
struct match_helper h = { 0 };
|
||
|
struct ifaddrs *addrs, *addr;
|
||
|
|
||
|
getifaddrs(&addrs);
|
||
|
for(addr = addrs; addr; addr = addr->ifa_next) {
|
||
|
if (addr->ifa_flags & IFF_UP) {
|
||
|
switch_console_push_match_unique(&h.my_matches, addr->ifa_name);
|
||
|
}
|
||
|
}
|
||
|
freeifaddrs(addrs);
|
||
|
|
||
|
if (h.my_matches) {
|
||
|
*matches = h.my_matches;
|
||
|
return SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
#else
|
||
|
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_interfaces(const char *line, const char *cursor, switch_console_callback_match_t **matches)
|
||
|
{
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int uuid_callback(void *pArg, int argc, char **argv, char **columnNames)
|
||
|
{
|
||
|
struct match_helper *h = (struct match_helper *) pArg;
|
||
|
|
||
|
switch_console_push_match(&h->my_matches, argv[0]);
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE_NONSTD(switch_status_t) switch_console_list_uuid(const char *line, const char *cursor, switch_console_callback_match_t **matches)
|
||
|
{
|
||
|
char *sql;
|
||
|
struct match_helper h = { 0 };
|
||
|
switch_cache_db_handle_t *db = NULL;
|
||
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
||
|
char *errmsg;
|
||
|
|
||
|
|
||
|
if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
|
||
|
return SWITCH_STATUS_GENERR;
|
||
|
}
|
||
|
|
||
|
if (!zstr(cursor)) {
|
||
|
sql = switch_mprintf("select distinct uuid from channels where uuid like '%q%%' and hostname='%q' order by uuid",
|
||
|
cursor, switch_core_get_switchname());
|
||
|
} else {
|
||
|
sql = switch_mprintf("select distinct uuid from channels where hostname='%q' order by uuid", switch_core_get_switchname());
|
||
|
}
|
||
|
|
||
|
switch_cache_db_execute_sql_callback(db, sql, uuid_callback, &h, &errmsg);
|
||
|
free(sql);
|
||
|
|
||
|
switch_cache_db_release_db_handle(&db);
|
||
|
|
||
|
if (h.my_matches) {
|
||
|
*matches = h.my_matches;
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
SWITCH_DECLARE(unsigned char) switch_console_complete(const char *line, const char *cursor, FILE * console_out,
|
||
|
switch_stream_handle_t *stream, switch_xml_t xml)
|
||
|
{
|
||
|
switch_cache_db_handle_t *db = NULL;
|
||
|
char *sql = NULL;
|
||
|
char *dup = strdup(line);
|
||
|
char *buf = dup;
|
||
|
char *p, *lp = NULL;
|
||
|
char *errmsg = NULL;
|
||
|
struct helper h = { 0 };
|
||
|
unsigned char ret = CC_REDISPLAY;
|
||
|
int pos = 0;
|
||
|
int sc = 0;
|
||
|
|
||
|
#ifndef HAVE_LIBEDIT
|
||
|
#ifndef _MSC_VER
|
||
|
if (!stream) {
|
||
|
return CC_ERROR;
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
|
||
|
switch_safe_free(dup);
|
||
|
return CC_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!zstr(cursor) && !zstr(line)) {
|
||
|
pos = (int)(cursor - line);
|
||
|
}
|
||
|
|
||
|
h.out = console_out;
|
||
|
h.stream = stream;
|
||
|
h.xml = xml;
|
||
|
|
||
|
if (pos > 0) {
|
||
|
*(buf + pos) = '\0';
|
||
|
}
|
||
|
|
||
|
if ((p = strchr(buf, '\r')) || (p = strchr(buf, '\n'))) {
|
||
|
*p = '\0';
|
||
|
}
|
||
|
|
||
|
while (*buf == ' ') {
|
||
|
sc++;
|
||
|
buf++;
|
||
|
}
|
||
|
|
||
|
if (!*buf) {
|
||
|
#ifdef HAVE_LIBEDIT
|
||
|
if (h.out && sc) {
|
||
|
el_deletestr(el, sc);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
sc = 0;
|
||
|
p = end_of_p(buf);
|
||
|
while (p >= buf && *p == ' ') {
|
||
|
sc++;
|
||
|
p--;
|
||
|
}
|
||
|
|
||
|
if (sc > 1) {
|
||
|
#ifdef HAVE_LIBEDIT
|
||
|
if (h.out) {
|
||
|
el_deletestr(el, sc - 1);
|
||
|
}
|
||
|
#endif
|
||
|
*(p + 2) = '\0';
|
||
|
}
|
||
|
|
||
|
for (p = buf; p && *p; p++) {
|
||
|
if (*p == ' ') {
|
||
|
lp = p;
|
||
|
h.words++;
|
||
|
while (*p == ' ')
|
||
|
p++;
|
||
|
if (!*p)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lp) {
|
||
|
buf = lp + 1;
|
||
|
}
|
||
|
|
||
|
h.len = (int)strlen(buf);
|
||
|
|
||
|
if (h.out) {
|
||
|
fprintf(h.out, "\n\n");
|
||
|
}
|
||
|
|
||
|
if (h.stream) {
|
||
|
h.stream->write_function(h.stream, "\n\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
if (h.words == 0) {
|
||
|
sql = switch_mprintf("select distinct name from interfaces where type='api' and name like '%q%%' and hostname='%q' order by name",
|
||
|
buf, switch_core_get_hostname());
|
||
|
}
|
||
|
|
||
|
if (sql) {
|
||
|
switch_cache_db_execute_sql_callback(db, sql, comp_callback, &h, &errmsg);
|
||
|
|
||
|
if (errmsg) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", sql, errmsg);
|
||
|
free(errmsg);
|
||
|
ret = CC_ERROR;
|
||
|
goto end;
|
||
|
}
|
||
|
free(sql);
|
||
|
sql = NULL;
|
||
|
}
|
||
|
|
||
|
if (h.hits != -1) {
|
||
|
char *dupdup = strdup(dup);
|
||
|
int x, argc = 0;
|
||
|
char *argv[10] = { 0 };
|
||
|
switch_stream_handle_t stream = { 0 };
|
||
|
SWITCH_STANDARD_STREAM(stream);
|
||
|
switch_assert(dupdup);
|
||
|
|
||
|
argc = switch_separate_string(dupdup, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
|
||
|
|
||
|
if (h.words == 0) {
|
||
|
stream.write_function(&stream, "select distinct a1 from complete where " "a1 not in (select name from interfaces where hostname='%q') %s ",
|
||
|
switch_core_get_hostname(), argc ? "and" : "");
|
||
|
} else {
|
||
|
if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
|
||
|
stream.write_function(&stream, "select distinct a%d,'%q','%q' from complete where ", h.words + 1, switch_str_nil(dup), switch_str_nil(lp));
|
||
|
} else {
|
||
|
stream.write_function(&stream, "select distinct a%d,'%q','%w' from complete where ", h.words + 1, switch_str_nil(dup), switch_str_nil(lp));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (x = 0; x < argc; x++) {
|
||
|
if (h.words + 1 > argc) {
|
||
|
if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
|
||
|
stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d = '%q')%q",
|
||
|
x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
|
||
|
} else {
|
||
|
stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d = '%w')%w",
|
||
|
x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
|
||
|
}
|
||
|
} else {
|
||
|
if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
|
||
|
stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d like '%q%%')%q",
|
||
|
x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
|
||
|
} else {
|
||
|
stream.write_function(&stream, "(a%d like '::%%' or a%d = '' or a%d like '%w%%')%w",
|
||
|
x + 1, x + 1, x + 1, switch_str_nil(argv[x]), x == argc - 1 ? "" : " and ");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stream.write_function(&stream, " and hostname='%s' order by a%d", switch_core_get_hostname(), h.words + 1);
|
||
|
|
||
|
switch_cache_db_execute_sql_callback(db, stream.data, comp_callback, &h, &errmsg);
|
||
|
|
||
|
if (errmsg) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error [%s][%s]\n", (char *) stream.data, errmsg);
|
||
|
free(errmsg);
|
||
|
ret = CC_ERROR;
|
||
|
}
|
||
|
|
||
|
switch_safe_free(dupdup);
|
||
|
switch_safe_free(stream.data);
|
||
|
|
||
|
if (ret == CC_ERROR) {
|
||
|
goto end;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (h.out) {
|
||
|
fprintf(h.out, "\n\n");
|
||
|
}
|
||
|
|
||
|
if (h.stream) {
|
||
|
h.stream->write_function(h.stream, "\n\n");
|
||
|
if (h.hits == 1 && !zstr(h.last)) {
|
||
|
h.stream->write_function(h.stream, "write=%d:%s ", h.len, h.last);
|
||
|
} else if (h.hits > 1 && !zstr(h.partial)) {
|
||
|
h.stream->write_function(h.stream, "write=%d:%s", h.len, h.partial);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (h.xml) {
|
||
|
switch_xml_t x_write = switch_xml_add_child_d(h.xml, "write", h.xml_off++);
|
||
|
char buf[32];
|
||
|
|
||
|
snprintf(buf, sizeof(buf), "%d", h.len);
|
||
|
switch_xml_set_attr_d_buf(x_write, "length", buf);
|
||
|
|
||
|
if (h.hits == 1 && !zstr(h.last)) {
|
||
|
switch_xml_set_txt_d(x_write, h.last);
|
||
|
} else if (h.hits > 1 && !zstr(h.partial)) {
|
||
|
switch_xml_set_txt_d(x_write, h.partial);
|
||
|
}
|
||
|
}
|
||
|
#ifdef HAVE_LIBEDIT
|
||
|
if (h.out) {
|
||
|
if (h.hits == 1 && !zstr(h.last)) {
|
||
|
el_deletestr(el, h.len);
|
||
|
el_insertstr(el, h.last);
|
||
|
el_insertstr(el, " ");
|
||
|
} else if (h.hits > 1 && !zstr(h.partial)) {
|
||
|
el_deletestr(el, h.len);
|
||
|
el_insertstr(el, h.partial);
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
#ifdef _MSC_VER
|
||
|
if (h.out) {
|
||
|
if (h.hits == 1 && !zstr(h.last)) {
|
||
|
console_bufferInput(0, h.len, (char *) line, DELETE_REFRESH_OP);
|
||
|
console_bufferInput(h.last, (int) strlen(h.last), (char *) line, 0);
|
||
|
console_bufferInput(" ", 1, (char *) line, 0);
|
||
|
} else if (h.hits > 1 && !zstr(h.partial)) {
|
||
|
console_bufferInput(0, h.len, (char *) line, DELETE_REFRESH_OP);
|
||
|
console_bufferInput(h.partial, (int) strlen(h.partial), (char *) line, 0);
|
||
|
} else {
|
||
|
console_bufferInput(0, 0, (char *) line, DELETE_REFRESH_OP);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
end:
|
||
|
|
||
|
if (h.out) {
|
||
|
fflush(h.out);
|
||
|
}
|
||
|
|
||
|
switch_safe_free(sql);
|
||
|
switch_safe_free(dup);
|
||
|
|
||
|
switch_cache_db_release_db_handle(&db);
|
||
|
|
||
|
return (ret);
|
||
|
}
|
||
|
|
||
|
|
||
|
#if defined(HAVE_LIBEDIT) || defined(_MSC_VER)
|
||
|
/*
|
||
|
* If a fnkey is configured then process the command
|
||
|
*/
|
||
|
static unsigned char console_fnkey_pressed(int i)
|
||
|
{
|
||
|
char *c, *cmd;
|
||
|
|
||
|
switch_assert((i > 0) && (i <= 12));
|
||
|
|
||
|
c = console_fnkeys[i - 1];
|
||
|
|
||
|
/* This new line is necessary to avoid output to begin after the ">" of the CLI's prompt */
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, "\n");
|
||
|
|
||
|
if (c == NULL) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "FUNCTION KEY F%d IS NOT BOUND, please edit switch.conf XML file\n", i);
|
||
|
return CC_REDISPLAY;
|
||
|
}
|
||
|
|
||
|
cmd = strdup(c);
|
||
|
switch_console_process(cmd);
|
||
|
free(cmd);
|
||
|
|
||
|
return CC_REDISPLAY;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_save_history(void)
|
||
|
{
|
||
|
#ifdef HAVE_LIBEDIT
|
||
|
history(myhistory, &ev, H_SAVE, hfile);
|
||
|
#else
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "NOT IMPLEMENTED!\n");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_LIBEDIT
|
||
|
static char prompt_str[512] = "";
|
||
|
|
||
|
static unsigned char console_f1key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(1);
|
||
|
}
|
||
|
static unsigned char console_f2key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(2);
|
||
|
}
|
||
|
static unsigned char console_f3key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(3);
|
||
|
}
|
||
|
static unsigned char console_f4key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(4);
|
||
|
}
|
||
|
static unsigned char console_f5key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(5);
|
||
|
}
|
||
|
static unsigned char console_f6key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(6);
|
||
|
}
|
||
|
static unsigned char console_f7key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(7);
|
||
|
}
|
||
|
static unsigned char console_f8key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(8);
|
||
|
}
|
||
|
static unsigned char console_f9key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(9);
|
||
|
}
|
||
|
static unsigned char console_f10key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(10);
|
||
|
}
|
||
|
static unsigned char console_f11key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(11);
|
||
|
}
|
||
|
static unsigned char console_f12key(EditLine * el, int ch)
|
||
|
{
|
||
|
return console_fnkey_pressed(12);
|
||
|
}
|
||
|
|
||
|
|
||
|
char *prompt(EditLine * e)
|
||
|
{
|
||
|
if (*prompt_str == '\0') {
|
||
|
switch_snprintf(prompt_str, sizeof(prompt_str), "freeswitch@%s> ", switch_core_get_switchname());
|
||
|
}
|
||
|
|
||
|
return prompt_str;
|
||
|
}
|
||
|
|
||
|
static void *SWITCH_THREAD_FUNC console_thread(switch_thread_t *thread, void *obj)
|
||
|
{
|
||
|
int count;
|
||
|
const char *line;
|
||
|
switch_memory_pool_t *pool = (switch_memory_pool_t *) obj;
|
||
|
|
||
|
while (running) {
|
||
|
int32_t arg = 0;
|
||
|
|
||
|
if (getppid() == 1) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "We've become an orphan, no more console for us.\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch_core_session_ctl(SCSC_CHECK_RUNNING, &arg);
|
||
|
if (!arg) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
line = el_gets(el, &count);
|
||
|
|
||
|
if (count > 1) {
|
||
|
if (!zstr(line)) {
|
||
|
char *cmd = strdup(line);
|
||
|
char *p;
|
||
|
const LineInfo *lf = el_line(el);
|
||
|
char *foo = (char *) lf->buffer;
|
||
|
if ((p = strrchr(cmd, '\r')) || (p = strrchr(cmd, '\n'))) {
|
||
|
*p = '\0';
|
||
|
}
|
||
|
assert(cmd != NULL);
|
||
|
history(myhistory, &ev, H_ENTER, line);
|
||
|
running = switch_console_process(cmd);
|
||
|
el_deletestr(el, strlen(foo) + 1);
|
||
|
memset(foo, 0, strlen(foo));
|
||
|
free(cmd);
|
||
|
}
|
||
|
}
|
||
|
switch_cond_next();
|
||
|
}
|
||
|
|
||
|
switch_core_destroy_memory_pool(&pool);
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Editline thread exiting\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static unsigned char complete(EditLine * el, int ch)
|
||
|
{
|
||
|
const LineInfo *lf = el_line(el);
|
||
|
|
||
|
return switch_console_complete(lf->buffer, lf->cursor, switch_core_get_console(), NULL, NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_loop(void)
|
||
|
{
|
||
|
switch_thread_t *thread;
|
||
|
switch_threadattr_t *thd_attr = NULL;
|
||
|
switch_memory_pool_t *pool;
|
||
|
|
||
|
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
el = el_init(__FILE__, switch_core_get_console(), switch_core_get_console(), switch_core_get_console());
|
||
|
el_set(el, EL_PROMPT, &prompt);
|
||
|
el_set(el, EL_EDITOR, "emacs");
|
||
|
/* AGX: Bind Keyboard function keys. This has been tested with:
|
||
|
* - linux console keyabord
|
||
|
* - putty.exe connected via ssh to linux
|
||
|
*/
|
||
|
/* Load/Init the config first */
|
||
|
console_xml_config();
|
||
|
/* Bind the functions to the key */
|
||
|
el_set(el, EL_ADDFN, "f1-key", "F1 KEY PRESS", console_f1key);
|
||
|
el_set(el, EL_ADDFN, "f2-key", "F2 KEY PRESS", console_f2key);
|
||
|
el_set(el, EL_ADDFN, "f3-key", "F3 KEY PRESS", console_f3key);
|
||
|
el_set(el, EL_ADDFN, "f4-key", "F4 KEY PRESS", console_f4key);
|
||
|
el_set(el, EL_ADDFN, "f5-key", "F5 KEY PRESS", console_f5key);
|
||
|
el_set(el, EL_ADDFN, "f6-key", "F6 KEY PRESS", console_f6key);
|
||
|
el_set(el, EL_ADDFN, "f7-key", "F7 KEY PRESS", console_f7key);
|
||
|
el_set(el, EL_ADDFN, "f8-key", "F8 KEY PRESS", console_f8key);
|
||
|
el_set(el, EL_ADDFN, "f9-key", "F9 KEY PRESS", console_f9key);
|
||
|
el_set(el, EL_ADDFN, "f10-key", "F10 KEY PRESS", console_f10key);
|
||
|
el_set(el, EL_ADDFN, "f11-key", "F11 KEY PRESS", console_f11key);
|
||
|
el_set(el, EL_ADDFN, "f12-key", "F12 KEY PRESS", console_f12key);
|
||
|
|
||
|
el_set(el, EL_BIND, "\033OP", "f1-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033OQ", "f2-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033OR", "f3-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033OS", "f4-key", NULL);
|
||
|
|
||
|
|
||
|
el_set(el, EL_BIND, "\033[11~", "f1-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[12~", "f2-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[13~", "f3-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[14~", "f4-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[15~", "f5-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[17~", "f6-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[18~", "f7-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[19~", "f8-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[20~", "f9-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[21~", "f10-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[23~", "f11-key", NULL);
|
||
|
el_set(el, EL_BIND, "\033[24~", "f12-key", NULL);
|
||
|
|
||
|
|
||
|
el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete);
|
||
|
el_set(el, EL_BIND, "^I", "ed-complete", NULL);
|
||
|
|
||
|
/* "Delete" key. */
|
||
|
el_set(el, EL_BIND, "\033[3~", "ed-delete-next-char", NULL);
|
||
|
|
||
|
myhistory = history_init();
|
||
|
if (myhistory == 0) {
|
||
|
fprintf(stderr, "history could not be initialized\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
hfile = switch_mprintf("%s%sfreeswitch.history", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR);
|
||
|
assert(hfile != NULL);
|
||
|
|
||
|
history(myhistory, &ev, H_SETSIZE, 800);
|
||
|
el_set(el, EL_HIST, history, myhistory);
|
||
|
history(myhistory, &ev, H_LOAD, hfile);
|
||
|
|
||
|
el_source(el, NULL);
|
||
|
|
||
|
switch_threadattr_create(&thd_attr, pool);
|
||
|
switch_threadattr_detach_set(thd_attr, 1);
|
||
|
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
|
||
|
switch_thread_create(&thread, thd_attr, console_thread, pool, pool);
|
||
|
|
||
|
while (running) {
|
||
|
int32_t arg = 0;
|
||
|
switch_core_session_ctl(SCSC_CHECK_RUNNING, &arg);
|
||
|
if (!arg) {
|
||
|
break;
|
||
|
}
|
||
|
switch_yield(1000000);
|
||
|
}
|
||
|
|
||
|
history(myhistory, &ev, H_SAVE, hfile);
|
||
|
free(hfile);
|
||
|
|
||
|
/* Clean up our memory */
|
||
|
history_end(myhistory);
|
||
|
el_end(el);
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
char history[HISTLEN][CMD_BUFLEN + 1];
|
||
|
int iHistory = 0;
|
||
|
int iHistorySel = 0;
|
||
|
|
||
|
static int console_history(char *cmd, int direction)
|
||
|
{
|
||
|
int i;
|
||
|
static int first;
|
||
|
|
||
|
if (direction == 0) {
|
||
|
first = 1;
|
||
|
if (iHistory < HISTLEN) {
|
||
|
if (iHistory && strcmp(history[iHistory - 1], cmd)) {
|
||
|
iHistorySel = iHistory;
|
||
|
strcpy(history[iHistory++], cmd);
|
||
|
} else if (iHistory == 0) {
|
||
|
iHistorySel = iHistory;
|
||
|
strcpy(history[iHistory++], cmd);
|
||
|
}
|
||
|
} else {
|
||
|
iHistory = HISTLEN - 1;
|
||
|
for (i = 0; i < HISTLEN - 1; i++) {
|
||
|
strcpy(history[i], history[i + 1]);
|
||
|
}
|
||
|
iHistorySel = iHistory;
|
||
|
strcpy(history[iHistory++], cmd);
|
||
|
}
|
||
|
} else {
|
||
|
if (!first) {
|
||
|
iHistorySel += direction;
|
||
|
}
|
||
|
first = 0;
|
||
|
if (iHistorySel < 0) {
|
||
|
iHistorySel = 0;
|
||
|
}
|
||
|
if (iHistory && iHistorySel >= iHistory) {
|
||
|
iHistorySel = iHistory - 1;
|
||
|
}
|
||
|
strcpy(cmd, history[iHistorySel]);
|
||
|
}
|
||
|
return (SWITCH_STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
static int console_bufferInput(char *addchars, int len, char *cmd, int key)
|
||
|
{
|
||
|
static int iCmdBuffer = 0;
|
||
|
static int iCmdCursor = 0;
|
||
|
static int ignoreNext = 0;
|
||
|
static int insertMode = 1;
|
||
|
static COORD orgPosition;
|
||
|
static char prompt[80];
|
||
|
int iBuf;
|
||
|
int i;
|
||
|
|
||
|
HANDLE hOut;
|
||
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
||
|
COORD position;
|
||
|
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||
|
GetConsoleScreenBufferInfo(hOut, &info);
|
||
|
position = info.dwCursorPosition;
|
||
|
if (iCmdCursor == 0) {
|
||
|
orgPosition = position;
|
||
|
}
|
||
|
|
||
|
if (key == PROMPT_OP) {
|
||
|
if (strlen(cmd) < sizeof(prompt)) {
|
||
|
strcpy(prompt, cmd);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (key == KEY_TAB) {
|
||
|
switch_console_complete(cmd, cmd + iCmdBuffer, switch_core_get_console(), NULL, NULL);
|
||
|
return 0;
|
||
|
}
|
||
|
if (key == KEY_UP || key == KEY_DOWN || key == CLEAR_OP) {
|
||
|
SetConsoleCursorPosition(hOut, orgPosition);
|
||
|
for (i = 0; i < (int) strlen(cmd); i++) {
|
||
|
printf(" ");
|
||
|
}
|
||
|
SetConsoleCursorPosition(hOut, orgPosition);
|
||
|
iCmdBuffer = 0;
|
||
|
iCmdCursor = 0;
|
||
|
memset(cmd, 0, CMD_BUFLEN);
|
||
|
}
|
||
|
if (key == DELETE_REFRESH_OP) {
|
||
|
int l = len < (int) strlen(cmd) ? len : (int) strlen(cmd);
|
||
|
for (i = 0; i < l; i++) {
|
||
|
cmd[--iCmdBuffer] = 0;
|
||
|
}
|
||
|
iCmdCursor = (int) strlen(cmd);
|
||
|
printf("%s", prompt);
|
||
|
GetConsoleScreenBufferInfo(hOut, &info);
|
||
|
orgPosition = info.dwCursorPosition;
|
||
|
printf("%s", cmd);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (key == KEY_LEFT) {
|
||
|
if (iCmdCursor) {
|
||
|
if (position.X == 0) {
|
||
|
position.Y -= 1;
|
||
|
position.X = info.dwSize.X - 1;
|
||
|
} else {
|
||
|
position.X -= 1;
|
||
|
}
|
||
|
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
iCmdCursor--;
|
||
|
}
|
||
|
}
|
||
|
if (key == KEY_RIGHT) {
|
||
|
if (iCmdCursor < (int) strlen(cmd)) {
|
||
|
if (position.X == info.dwSize.X - 1) {
|
||
|
position.Y += 1;
|
||
|
position.X = 0;
|
||
|
} else {
|
||
|
position.X += 1;
|
||
|
}
|
||
|
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
iCmdCursor++;
|
||
|
}
|
||
|
}
|
||
|
if (key == KEY_INSERT) {
|
||
|
insertMode = !insertMode;
|
||
|
}
|
||
|
if (key == KEY_DELETE) {
|
||
|
if (iCmdCursor < iCmdBuffer) {
|
||
|
int pos;
|
||
|
for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
|
||
|
cmd[pos] = cmd[pos + 1];
|
||
|
}
|
||
|
cmd[pos] = 0;
|
||
|
iCmdBuffer--;
|
||
|
|
||
|
for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
|
||
|
printf("%c", cmd[pos]);
|
||
|
}
|
||
|
printf(" ");
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
}
|
||
|
}
|
||
|
for (iBuf = 0; iBuf < len; iBuf++) {
|
||
|
switch (addchars[iBuf]) {
|
||
|
case '\r':
|
||
|
case '\n':
|
||
|
if (ignoreNext) {
|
||
|
ignoreNext = 0;
|
||
|
} else {
|
||
|
int ret = iCmdBuffer;
|
||
|
if (iCmdBuffer == 0) {
|
||
|
strcpy(cmd, "Empty");
|
||
|
ret = (int) strlen(cmd);
|
||
|
} else {
|
||
|
console_history(cmd, 0);
|
||
|
cmd[iCmdBuffer] = 0;
|
||
|
}
|
||
|
iCmdBuffer = 0;
|
||
|
iCmdCursor = 0;
|
||
|
printf("\n");
|
||
|
return (ret);
|
||
|
}
|
||
|
break;
|
||
|
case '\b':
|
||
|
if (iCmdCursor) {
|
||
|
if (position.X == 0) {
|
||
|
position.Y -= 1;
|
||
|
position.X = info.dwSize.X - 1;
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
} else {
|
||
|
position.X -= 1;
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
}
|
||
|
printf(" ");
|
||
|
if (iCmdCursor < iCmdBuffer) {
|
||
|
int pos;
|
||
|
iCmdCursor--;
|
||
|
for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
|
||
|
cmd[pos] = cmd[pos + 1];
|
||
|
}
|
||
|
cmd[pos] = 0;
|
||
|
iCmdBuffer--;
|
||
|
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
|
||
|
printf("%c", cmd[pos]);
|
||
|
}
|
||
|
printf(" ");
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
} else {
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
iCmdBuffer--;
|
||
|
iCmdCursor--;
|
||
|
cmd[iCmdBuffer] = 0;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
if (!ignoreNext) {
|
||
|
if (iCmdCursor < iCmdBuffer) {
|
||
|
int pos;
|
||
|
|
||
|
if (position.X == info.dwSize.X - 1) {
|
||
|
position.Y += 1;
|
||
|
position.X = 0;
|
||
|
} else {
|
||
|
position.X += 1;
|
||
|
}
|
||
|
|
||
|
if (insertMode) {
|
||
|
for (pos = iCmdBuffer - 1; pos >= iCmdCursor; pos--) {
|
||
|
cmd[pos + 1] = cmd[pos];
|
||
|
}
|
||
|
}
|
||
|
iCmdBuffer++;
|
||
|
cmd[iCmdCursor++] = addchars[iBuf];
|
||
|
printf("%c", addchars[iBuf]);
|
||
|
for (pos = iCmdCursor; pos < iCmdBuffer; pos++) {
|
||
|
GetConsoleScreenBufferInfo(hOut, &info);
|
||
|
if (info.dwCursorPosition.X == info.dwSize.X - 1 && info.dwCursorPosition.Y == info.dwSize.Y - 1) {
|
||
|
orgPosition.Y -= 1;
|
||
|
position.Y -= 1;
|
||
|
}
|
||
|
printf("%c", cmd[pos]);
|
||
|
}
|
||
|
SetConsoleCursorPosition(hOut, position);
|
||
|
} else {
|
||
|
if (position.X == info.dwSize.X - 1 && position.Y == info.dwSize.Y - 1) {
|
||
|
orgPosition.Y -= 1;
|
||
|
}
|
||
|
cmd[iCmdBuffer++] = addchars[iBuf];
|
||
|
iCmdCursor++;
|
||
|
printf("%c", addchars[iBuf]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (iCmdBuffer == CMD_BUFLEN) {
|
||
|
printf("Read Console... BUFFER OVERRUN\n");
|
||
|
iCmdBuffer = 0;
|
||
|
ignoreNext = 1;
|
||
|
}
|
||
|
}
|
||
|
return (SWITCH_STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
|
||
|
static BOOL console_readConsole(HANDLE conIn, char *buf, int len, int *pRed, int *key)
|
||
|
{
|
||
|
DWORD recordIndex, bufferIndex, toRead = 0, red;
|
||
|
PINPUT_RECORD pInput;
|
||
|
|
||
|
GetNumberOfConsoleInputEvents(conIn, &toRead);
|
||
|
if (len < (int) toRead) {
|
||
|
toRead = len;
|
||
|
}
|
||
|
if (toRead == 0) {
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
if ((pInput = (PINPUT_RECORD) malloc(toRead * sizeof(INPUT_RECORD))) == NULL) {
|
||
|
return (FALSE);
|
||
|
}
|
||
|
*key = 0;
|
||
|
ReadConsoleInput(conIn, pInput, toRead, &red);
|
||
|
|
||
|
for (recordIndex = bufferIndex = 0; recordIndex < red; recordIndex++) {
|
||
|
KEY_EVENT_RECORD keyEvent = pInput[recordIndex].Event.KeyEvent;
|
||
|
if (pInput[recordIndex].EventType == KEY_EVENT && keyEvent.bKeyDown) {
|
||
|
if (keyEvent.wVirtualKeyCode == 38 && keyEvent.wVirtualScanCode == 72) {
|
||
|
buf[0] = 0;
|
||
|
console_history(buf, -1);
|
||
|
*key = KEY_UP;
|
||
|
bufferIndex += (DWORD) strlen(buf);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 40 && keyEvent.wVirtualScanCode == 80) {
|
||
|
buf[0] = 0;
|
||
|
console_history(buf, 1);
|
||
|
*key = KEY_DOWN;
|
||
|
bufferIndex += (DWORD) strlen(buf);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 112 && keyEvent.wVirtualScanCode == 59) {
|
||
|
console_fnkey_pressed(1);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 113 && keyEvent.wVirtualScanCode == 60) {
|
||
|
console_fnkey_pressed(2);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 114 && keyEvent.wVirtualScanCode == 61) {
|
||
|
console_fnkey_pressed(3);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 115 && keyEvent.wVirtualScanCode == 62) {
|
||
|
console_fnkey_pressed(4);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 116 && keyEvent.wVirtualScanCode == 63) {
|
||
|
console_fnkey_pressed(5);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 117 && keyEvent.wVirtualScanCode == 64) {
|
||
|
console_fnkey_pressed(6);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 118 && keyEvent.wVirtualScanCode == 65) {
|
||
|
console_fnkey_pressed(7);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 119 && keyEvent.wVirtualScanCode == 66) {
|
||
|
console_fnkey_pressed(8);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 120 && keyEvent.wVirtualScanCode == 67) {
|
||
|
console_fnkey_pressed(9);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 121 && keyEvent.wVirtualScanCode == 68) {
|
||
|
console_fnkey_pressed(10);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 122 && keyEvent.wVirtualScanCode == 87) {
|
||
|
console_fnkey_pressed(11);
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 123 && keyEvent.wVirtualScanCode == 88) {
|
||
|
console_fnkey_pressed(12);
|
||
|
}
|
||
|
if (keyEvent.uChar.AsciiChar == 9) {
|
||
|
*key = KEY_TAB;
|
||
|
break;
|
||
|
}
|
||
|
if (keyEvent.uChar.AsciiChar == 27) {
|
||
|
*key = CLEAR_OP;
|
||
|
break;
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 37 && keyEvent.wVirtualScanCode == 75) {
|
||
|
*key = KEY_LEFT;
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 39 && keyEvent.wVirtualScanCode == 77) {
|
||
|
*key = KEY_RIGHT;
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 45 && keyEvent.wVirtualScanCode == 82) {
|
||
|
*key = KEY_INSERT;
|
||
|
}
|
||
|
if (keyEvent.wVirtualKeyCode == 46 && keyEvent.wVirtualScanCode == 83) {
|
||
|
*key = KEY_DELETE;
|
||
|
}
|
||
|
while (keyEvent.wRepeatCount && keyEvent.uChar.AsciiChar) {
|
||
|
buf[bufferIndex] = keyEvent.uChar.AsciiChar;
|
||
|
if (buf[bufferIndex] == '\r') {
|
||
|
buf[bufferIndex] = '\n';
|
||
|
}
|
||
|
bufferIndex++;
|
||
|
keyEvent.wRepeatCount--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
free(pInput);
|
||
|
*pRed = bufferIndex;
|
||
|
return (TRUE);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_loop(void)
|
||
|
{
|
||
|
char cmd[CMD_BUFLEN + 1] = "";
|
||
|
int32_t activity = 1;
|
||
|
#ifndef _MSC_VER
|
||
|
int x = 0;
|
||
|
#else
|
||
|
char keys[CMD_BUFLEN];
|
||
|
#endif
|
||
|
|
||
|
/* Load/Init the config first */
|
||
|
console_xml_config();
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
sprintf(cmd, "\nfreeswitch@%s> ", switch_core_get_switchname());
|
||
|
console_bufferInput(0, 0, cmd, PROMPT_OP);
|
||
|
memset(cmd, 0, sizeof(cmd));
|
||
|
#endif
|
||
|
|
||
|
while (running) {
|
||
|
int32_t arg;
|
||
|
#ifdef _MSC_VER
|
||
|
int read, key;
|
||
|
HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
|
||
|
#else
|
||
|
fd_set rfds, efds;
|
||
|
struct timeval tv = { 0, 20000 };
|
||
|
#endif
|
||
|
|
||
|
switch_core_session_ctl(SCSC_CHECK_RUNNING, &arg);
|
||
|
if (!arg) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (activity) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, "\nfreeswitch@%s> ", switch_core_get_switchname());
|
||
|
}
|
||
|
#ifdef _MSC_VER
|
||
|
activity = 0;
|
||
|
if (console_readConsole(stdinHandle, keys, (int) sizeof(keys), &read, &key)) {
|
||
|
if (console_bufferInput(keys, read, cmd, key)) {
|
||
|
if (!strcmp(cmd, "Empty")) {
|
||
|
cmd[0] = 0;
|
||
|
}
|
||
|
activity = 1;
|
||
|
if (cmd[0]) {
|
||
|
running = switch_console_process(cmd);
|
||
|
}
|
||
|
memset(cmd, 0, sizeof(cmd));
|
||
|
}
|
||
|
}
|
||
|
Sleep(20);
|
||
|
#else
|
||
|
FD_ZERO(&rfds);
|
||
|
FD_ZERO(&efds);
|
||
|
FD_SET(fileno(stdin), &rfds);
|
||
|
FD_SET(fileno(stdin), &efds);
|
||
|
if ((activity = select(fileno(stdin) + 1, &rfds, NULL, &efds, &tv)) < 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (FD_ISSET(fileno(stdin), &efds)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!FD_ISSET(fileno(stdin), &rfds)) {
|
||
|
activity = 0;
|
||
|
}
|
||
|
|
||
|
if (activity == 0) {
|
||
|
fflush(stdout);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
memset(&cmd, 0, sizeof(cmd));
|
||
|
for (x = 0; x < (sizeof(cmd) - 1); x++) {
|
||
|
int c = getchar();
|
||
|
if (c < 0) {
|
||
|
int y = read(fileno(stdin), cmd, sizeof(cmd) - 1);
|
||
|
cmd[y - 1] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
cmd[x] = (char) c;
|
||
|
|
||
|
if (cmd[x] == '\n') {
|
||
|
cmd[x] = '\0';
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (cmd[0]) {
|
||
|
running = switch_console_process(cmd);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
static struct {
|
||
|
switch_hash_t *func_hash;
|
||
|
switch_mutex_t *func_mutex;
|
||
|
} globals;
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_init(switch_memory_pool_t *pool)
|
||
|
{
|
||
|
switch_mutex_init(&globals.func_mutex, SWITCH_MUTEX_NESTED, pool);
|
||
|
switch_core_hash_init(&globals.func_hash);
|
||
|
switch_console_add_complete_func("::console::list_available_modules", (switch_console_complete_callback_t) switch_console_list_available_modules);
|
||
|
switch_console_add_complete_func("::console::list_loaded_modules", (switch_console_complete_callback_t) switch_console_list_loaded_modules);
|
||
|
switch_console_add_complete_func("::console::list_interfaces", (switch_console_complete_callback_t) switch_console_list_interfaces);
|
||
|
switch_console_add_complete_func("::console::list_uuid", (switch_console_complete_callback_t) switch_console_list_uuid);
|
||
|
return SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_shutdown(void)
|
||
|
{
|
||
|
return switch_core_hash_destroy(&globals.func_hash);
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_add_complete_func(const char *name, switch_console_complete_callback_t cb)
|
||
|
{
|
||
|
switch_status_t status;
|
||
|
|
||
|
switch_mutex_lock(globals.func_mutex);
|
||
|
status = switch_core_hash_insert(globals.func_hash, name, (void *) (intptr_t) cb);
|
||
|
switch_mutex_unlock(globals.func_mutex);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_del_complete_func(const char *name)
|
||
|
{
|
||
|
switch_status_t status;
|
||
|
|
||
|
switch_mutex_lock(globals.func_mutex);
|
||
|
status = switch_core_hash_insert(globals.func_hash, name, NULL);
|
||
|
switch_mutex_unlock(globals.func_mutex);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_free_matches(switch_console_callback_match_t **matches)
|
||
|
{
|
||
|
switch_console_callback_match_t *my_match = *matches;
|
||
|
switch_console_callback_match_node_t *m, *cur;
|
||
|
|
||
|
/* Don't play with matches */
|
||
|
*matches = NULL;
|
||
|
|
||
|
m = my_match->head;
|
||
|
while (m) {
|
||
|
cur = m;
|
||
|
m = m->next;
|
||
|
free(cur->val);
|
||
|
free(cur);
|
||
|
}
|
||
|
|
||
|
if (my_match->dynamic) {
|
||
|
free(my_match);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_sort_matches(switch_console_callback_match_t *matches)
|
||
|
{
|
||
|
switch_console_callback_match_node_t *p = NULL, *sort[4] = { 0 };
|
||
|
int i, j;
|
||
|
|
||
|
switch_assert(matches);
|
||
|
|
||
|
if (matches->count < 2) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (i = 1; i < matches->count; i++) {
|
||
|
sort[0] = NULL;
|
||
|
sort[1] = matches->head;
|
||
|
sort[2] = sort[1] ? sort[1]->next : NULL;
|
||
|
sort[3] = sort[2] ? sort[2]->next : NULL;
|
||
|
|
||
|
for (j = 1; j <= (matches->count - i); j++) {
|
||
|
switch_assert(sort[1] && sort[2]);
|
||
|
if (strcmp(sort[1]->val, sort[2]->val) > 0) {
|
||
|
sort[1]->next = sort[3];
|
||
|
sort[2]->next = sort[1];
|
||
|
|
||
|
if (sort[0])
|
||
|
sort[0]->next = sort[2];
|
||
|
if (sort[1] == matches->head)
|
||
|
matches->head = sort[2];
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
sort[0] = sort[2];
|
||
|
sort[2] = sort[1]->next;
|
||
|
if (sort[3] && sort[3]->next)
|
||
|
sort[3] = sort[3]->next;
|
||
|
|
||
|
} else {
|
||
|
sort[0] = sort[1];
|
||
|
sort[1] = sort[2];
|
||
|
sort[2] = sort[3];
|
||
|
if (sort[3] && sort[3]->next)
|
||
|
sort[3] = sort[3]->next;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p = matches->head;
|
||
|
|
||
|
for (i = 1; i < matches->count; i++)
|
||
|
p = p->next;
|
||
|
|
||
|
if (p) {
|
||
|
p->next = NULL;
|
||
|
matches->end = p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_push_match_unique(switch_console_callback_match_t **matches, const char *new_val)
|
||
|
{
|
||
|
/* Ignore the entry if it is already in the list */
|
||
|
if (*matches) {
|
||
|
switch_console_callback_match_node_t *node;
|
||
|
|
||
|
for(node = (*matches)->head; node; node = node->next) {
|
||
|
if (!strcasecmp(node->val, new_val)) return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch_console_push_match(matches, new_val);
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(void) switch_console_push_match(switch_console_callback_match_t **matches, const char *new_val)
|
||
|
{
|
||
|
switch_console_callback_match_node_t *match;
|
||
|
|
||
|
if (!*matches) {
|
||
|
switch_zmalloc(*matches, sizeof(**matches));
|
||
|
(*matches)->dynamic = 1;
|
||
|
}
|
||
|
|
||
|
switch_zmalloc(match, sizeof(*match));
|
||
|
match->val = strdup(new_val);
|
||
|
|
||
|
if ((*matches)->head) {
|
||
|
(*matches)->end->next = match;
|
||
|
} else {
|
||
|
(*matches)->head = match;
|
||
|
}
|
||
|
|
||
|
(*matches)->count++;
|
||
|
|
||
|
(*matches)->end = match;
|
||
|
}
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_run_complete_func(const char *func, const char *line, const char *last_word,
|
||
|
switch_console_callback_match_t **matches)
|
||
|
{
|
||
|
switch_console_complete_callback_t cb;
|
||
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
||
|
|
||
|
switch_mutex_lock(globals.func_mutex);
|
||
|
if ((cb = (switch_console_complete_callback_t) (intptr_t) switch_core_hash_find(globals.func_hash, func))) {
|
||
|
if ((status = cb(line, last_word, matches)) == SWITCH_STATUS_SUCCESS) {
|
||
|
switch_console_sort_matches(*matches);
|
||
|
}
|
||
|
}
|
||
|
switch_mutex_unlock(globals.func_mutex);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_set_complete(const char *string)
|
||
|
{
|
||
|
char *mydata = NULL, *argv[11] = { 0 };
|
||
|
int argc, x;
|
||
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
||
|
switch_core_flag_t cflags = switch_core_flags();
|
||
|
|
||
|
if (!(cflags & SCF_USE_SQL)) {
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
|
||
|
if (string && (mydata = strdup(string))) {
|
||
|
if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
|
||
|
switch_stream_handle_t mystream = { 0 };
|
||
|
SWITCH_STANDARD_STREAM(mystream);
|
||
|
|
||
|
if (!strcasecmp(argv[0], "stickyadd")) {
|
||
|
mystream.write_function(&mystream, "insert into complete values (1,");
|
||
|
for (x = 0; x < 10; x++) {
|
||
|
if (argv[x + 1] && !strcasecmp(argv[x + 1], "_any_")) {
|
||
|
mystream.write_function(&mystream, "%s", "'', ");
|
||
|
} else {
|
||
|
if (switch_core_dbtype() == SCDB_TYPE_CORE_DB) {
|
||
|
mystream.write_function(&mystream, "'%q', ", switch_str_nil(argv[x + 1]));
|
||
|
} else {
|
||
|
mystream.write_function(&mystream, "'%w', ", switch_str_nil(argv[x + 1]));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
mystream.write_function(&mystream, " '%s')", switch_core_get_hostname());
|
||
|
switch_core_sql_exec(mystream.data);
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
} else if (!strcasecmp(argv[0], "add")) {
|
||
|
mystream.write_function(&mystream, "insert into complete values (0,");
|
||
|
for (x = 0; x < 10; x++) {
|
||
|
if (argv[x + 1] && !strcasecmp(argv[x + 1], "_any_")) {
|
||
|
mystream.write_function(&mystream, "%s", "'', ");
|
||
|
} else {
|
||
|
if (switch_core_dbtype() == SCDB_TYPE_CORE_DB) {
|
||
|
mystream.write_function(&mystream, "'%q', ", switch_str_nil(argv[x + 1]));
|
||
|
} else {
|
||
|
mystream.write_function(&mystream, "'%w', ", switch_str_nil(argv[x + 1]));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
mystream.write_function(&mystream, " '%s')", switch_core_get_hostname());
|
||
|
|
||
|
switch_core_sql_exec(mystream.data);
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
} else if (!strcasecmp(argv[0], "del")) {
|
||
|
char *what = argv[1];
|
||
|
if (zstr(what)) {
|
||
|
switch_safe_free(mystream.data);
|
||
|
switch_safe_free(mydata);
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
} else if (!strcasecmp(what, "*")) {
|
||
|
mystream.write_function(&mystream, "delete from complete where hostname='%q'", switch_core_get_hostname());
|
||
|
switch_core_sql_exec(mystream.data);
|
||
|
} else {
|
||
|
mystream.write_function(&mystream, "delete from complete where ");
|
||
|
for (x = 0; x < argc - 1; x++) {
|
||
|
if (switch_core_dbtype() == SCDB_TYPE_CORE_DB) {
|
||
|
mystream.write_function(&mystream, "a%d = '%q'%q", x + 1, switch_str_nil(argv[x + 1]), x == argc - 2 ? "" : " and ");
|
||
|
} else {
|
||
|
mystream.write_function(&mystream, "a%d = '%w'%w", x + 1, switch_str_nil(argv[x + 1]), x == argc - 2 ? "" : " and ");
|
||
|
}
|
||
|
}
|
||
|
mystream.write_function(&mystream, " and hostname='%q'", switch_core_get_hostname());
|
||
|
switch_core_sql_exec(mystream.data);
|
||
|
}
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
switch_safe_free(mystream.data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch_safe_free(mydata);
|
||
|
|
||
|
return status;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
SWITCH_DECLARE(switch_status_t) switch_console_set_alias(const char *string)
|
||
|
{
|
||
|
char *mydata = NULL, *argv[3] = { 0 };
|
||
|
int argc;
|
||
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
||
|
|
||
|
if (string && (mydata = strdup(string))) {
|
||
|
if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 2) {
|
||
|
switch_cache_db_handle_t *db = NULL;
|
||
|
char *sql = NULL;
|
||
|
|
||
|
if (argc > 2 && !strcmp(argv[1], argv[2])) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Alias and command cannot be the same, this will cause loop!\n");
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
|
||
|
if (switch_core_db_handle(&db) != SWITCH_STATUS_SUCCESS) {
|
||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Database Error\n");
|
||
|
free(mydata);
|
||
|
return SWITCH_STATUS_FALSE;
|
||
|
}
|
||
|
|
||
|
if (!strcasecmp(argv[0], "stickyadd") && argc == 3) {
|
||
|
sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_switchname());
|
||
|
switch_cache_db_persistant_execute(db, sql, 5);
|
||
|
switch_safe_free(sql);
|
||
|
if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
|
||
|
sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (1, '%q','%q','%q')",
|
||
|
argv[1], argv[2], switch_core_get_switchname());
|
||
|
} else {
|
||
|
sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (1, '%w','%w','%w')",
|
||
|
argv[1], argv[2], switch_core_get_switchname());
|
||
|
}
|
||
|
switch_cache_db_persistant_execute(db, sql, 5);
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
} else if (!strcasecmp(argv[0], "add") && argc == 3) {
|
||
|
sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_switchname());
|
||
|
switch_cache_db_persistant_execute(db, sql, 5);
|
||
|
switch_safe_free(sql);
|
||
|
if (switch_cache_db_get_type(db) == SCDB_TYPE_CORE_DB) {
|
||
|
sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (0, '%q','%q','%q')",
|
||
|
argv[1], argv[2], switch_core_get_switchname());
|
||
|
} else {
|
||
|
sql = switch_mprintf("insert into aliases (sticky, alias, command, hostname) values (0, '%w','%w','%w')",
|
||
|
argv[1], argv[2], switch_core_get_switchname());
|
||
|
}
|
||
|
switch_cache_db_persistant_execute(db, sql, 5);
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
} else if (!strcasecmp(argv[0], "del") && argc == 2) {
|
||
|
char *what = argv[1];
|
||
|
if (!strcasecmp(what, "*")) {
|
||
|
sql = switch_mprintf("delete from aliases where hostname='%q'", switch_core_get_switchname());
|
||
|
switch_cache_db_persistant_execute(db, sql, 1);
|
||
|
} else {
|
||
|
sql = switch_mprintf("delete from aliases where alias='%q' and hostname='%q'", argv[1], switch_core_get_switchname());
|
||
|
switch_cache_db_persistant_execute(db, sql, 5);
|
||
|
}
|
||
|
status = SWITCH_STATUS_SUCCESS;
|
||
|
}
|
||
|
switch_safe_free(sql);
|
||
|
switch_cache_db_release_db_handle(&db);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch_safe_free(mydata);
|
||
|
|
||
|
return status;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* For Emacs:
|
||
|
* Local Variables:
|
||
|
* mode:c
|
||
|
* indent-tabs-mode:t
|
||
|
* tab-width:4
|
||
|
* c-basic-offset:4
|
||
|
* End:
|
||
|
* For VIM:
|
||
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
||
|
*/
|