X-Git-Url: http://git.asterisk.org/gitweb/?p=asterisk%2Fasterisk.git;a=blobdiff_plain;f=asterisk.c;h=ddfa0582a1f4877a9c2ac573773776a9a7d23c54;hp=a73307be006e316e777ad4d47b120aa6a32f5ebb;hb=877a281b742fc0d7cda64f34aa633d8b3972da47;hpb=7e5ebff4c45abbbdbe5d0eae555f71bd74190b0b;ds=sidebyside diff --git a/asterisk.c b/asterisk.c index a73307b..ddfa058 100755 --- a/asterisk.c +++ b/asterisk.c @@ -18,20 +18,30 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include #include #include #include #include #include #include -#include -#include +#include +#include "editline/histedit.h" #include "asterisk.h" +#include #define AST_MAX_CONNECTS 128 #define NUM_MSGS 64 @@ -44,6 +54,10 @@ int option_console=0; int option_highpriority=0; int option_remote=0; int option_exec=0; +int option_initcrypto=0; +int option_nocolor; +int option_dumpcore = 0; +int option_overrideconfig = 0; int fully_booted = 0; static int ast_socket = -1; /* UNIX Socket for allowing remote control */ @@ -55,10 +69,31 @@ struct console { pthread_t t; /* Thread of handler */ }; +static History *el_hist = NULL; +static EditLine *el = NULL; +static char *remotehostname; + struct console consoles[AST_MAX_CONNECTS]; char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE; +static int ast_el_add_history(char *); +static int ast_el_read_history(char *); +static int ast_el_write_history(char *); + +char ast_config_AST_CONFIG_DIR[AST_CONFIG_MAX_PATH]; +char ast_config_AST_CONFIG_FILE[AST_CONFIG_MAX_PATH]; +char ast_config_AST_MODULE_DIR[AST_CONFIG_MAX_PATH]; +char ast_config_AST_SPOOL_DIR[AST_CONFIG_MAX_PATH]; +char ast_config_AST_VAR_DIR[AST_CONFIG_MAX_PATH]; +char ast_config_AST_LOG_DIR[AST_CONFIG_MAX_PATH]; +char ast_config_AST_AGI_DIR[AST_CONFIG_MAX_PATH]; +char ast_config_AST_DB[AST_CONFIG_MAX_PATH]; +char ast_config_AST_KEY_DIR[AST_CONFIG_MAX_PATH]; +char ast_config_AST_PID[AST_CONFIG_MAX_PATH]; +char ast_config_AST_SOCKET[AST_CONFIG_MAX_PATH]; +char ast_config_AST_RUN_DIR[AST_CONFIG_MAX_PATH]; + static int fdprint(int fd, char *s) { return write(fd, s, strlen(s) + 1); @@ -85,7 +120,7 @@ static void *netconsole(void *vconsole) fd_set rfds; if (gethostname(hostname, sizeof(hostname))) - strncpy(hostname, "", sizeof(hostname)); + strncpy(hostname, "", sizeof(hostname)-1); snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, mainpid, ASTERISK_VERSION); fdprint(con->fd, tmp); for(;;) { @@ -102,8 +137,9 @@ static void *netconsole(void *vconsole) } if (FD_ISSET(con->fd, &rfds)) { res = read(con->fd, tmp, sizeof(tmp)); - if (res < 1) + if (res < 1) { break; + } tmp[res] = 0; ast_cli_command(con->fd, tmp); } @@ -134,10 +170,13 @@ static void *listener(void *unused) int s; int len; int x; + int flags; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); for(;;) { + if (ast_socket < 0) + return NULL; len = sizeof(sun); s = accept(ast_socket, (struct sockaddr *)&sun, &len); if (s < 0) { @@ -145,13 +184,15 @@ static void *listener(void *unused) } else { for (x=0;x 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); + if (option_verbose && option_console) + ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt"); + for(;;) { + if (!ast_active_channels()) + break; + if (!shuttingdown) + break; + sleep(1); + } + } + + if (!shuttingdown) { + if (option_verbose && option_console) + ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown"); + return; + } + } if (option_console || option_remote) { if (getenv("HOME")) snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); if (strlen(filename)) - write_history(filename); - rl_callback_handler_remove(); + ast_el_write_history(filename); + if (el != NULL) + el_end(el); + if (el_hist != NULL) + history_end(el_hist); } /* Called on exit */ if (option_verbose && option_console) - ast_verbose("Asterisk ending (%d).\n", num); + 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); - if (ast_socket > -1) + manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\nRestart: %s\r\n", ast_active_channels() ? "Uncleanly" : "Cleanly", restart ? "True" : "False"); + if (ast_socket > -1) { close(ast_socket); + ast_socket = -1; + } if (ast_consock > -1) close(ast_consock); if (ast_socket > -1) - unlink(AST_SOCKET); - - exit(0); + unlink((char *)ast_config_AST_SOCKET); + unlink((char *)ast_config_AST_PID); + printf(term_quit()); + if (restart) { + if (option_verbose || option_console) + ast_verbose("Preparing for Asterisk restart...\n"); + /* Mark all FD's for closing on exec */ + for (x=3;x<32768;x++) { + fcntl(x, F_SETFD, FD_CLOEXEC); + } + if (option_verbose || option_console) + ast_verbose("Restarting Asterisk NOW...\n"); + execvp(_argv[0], _argv); + } else + exit(0); +} + +static void __quit_handler(int num) +{ + quit_handler(num, 0, 1, 0); } static pthread_t consolethread = -1; +static int fix_header(char *outbuf, int maxout, char **s, char *cmp) +{ + if (!strncmp(*s, cmp, strlen(cmp))) { + *s += strlen(cmp); + term_color(outbuf, cmp, COLOR_GRAY, 0, maxout); + return 1; + } + return 0; +} + static void console_verboser(char *s, int pos, int replace, int complete) { + char tmp[80]; /* Return to the beginning of the line */ - if (!pos) + if (!pos) { fprintf(stdout, "\r"); - fprintf(stdout, s + pos); + if (fix_header(tmp, sizeof(tmp), &s, VERBOSE_PREFIX_4) || + fix_header(tmp, sizeof(tmp), &s, VERBOSE_PREFIX_3) || + fix_header(tmp, sizeof(tmp), &s, VERBOSE_PREFIX_2) || + fix_header(tmp, sizeof(tmp), &s, VERBOSE_PREFIX_1)) + fputs(tmp, stdout); + } + fputs(s + pos,stdout); fflush(stdout); if (complete) /* Wake up a select()ing console */ - pthread_kill(consolethread, SIGURG); + if (consolethread > -1) + pthread_kill(consolethread, SIGURG); } static void consolehandler(char *s) { + printf(term_end()); + fflush(stdout); /* Called when readline data is available */ if (s && strlen(s)) - add_history(s); + ast_el_add_history(s); /* Give the console access to the shell */ if (s) { if (s[0] == '!') { @@ -349,14 +479,12 @@ static void consolehandler(char *s) fprintf(stdout, "\nUse \"quit\" to exit\n"); } - -static char cmd[1024]; - -static void remoteconsolehandler(char *s) +static int remoteconsolehandler(char *s) { + int ret = 0; /* Called when readline data is available */ if (s && strlen(s)) - add_history(s); + ast_el_add_history(s); /* Give the console access to the shell */ if (s) { if (s[0] == '!') { @@ -364,29 +492,121 @@ static void remoteconsolehandler(char *s) system(s+1); else system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh"); - } else - strncpy(cmd, s, sizeof(cmd)); - if (!strcasecmp(s, "help")) + ret = 1; + } + if (!strcasecmp(s, "help")) { fprintf(stdout, " ! Executes a given shell command\n"); - if (!strcasecmp(s, "quit")) - quit_handler(0); + ret = 0; + } + if (!strcasecmp(s, "quit")) { + quit_handler(0, 0, 0, 0); + ret = 1; + } + if (!strcasecmp(s, "exit")) { + quit_handler(0, 0, 0, 0); + ret = 1; + } } else fprintf(stdout, "\nUse \"quit\" to exit\n"); + + return ret; } static char quit_help[] = "Usage: quit\n" " Exits Asterisk.\n"; -static char shutdown_help[] = -"Usage: shutdown\n" -" Shuts down a running Asterisk PBX.\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[] = +"Usage: stop now\n" +" Shuts down a running Asterisk immediately, hanging up all active calls .\n"; + +static char shutdown_gracefully_help[] = +"Usage: stop gracefully\n" +" Causes Asterisk to not accept new calls, and exit when all\n" +" active calls have terminated normally.\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 int handle_quit(int fd, int argc, char *argv[]) { if (argc != 1) return RESULT_SHOWUSAGE; - quit_handler(0); + quit_handler(0, 0, 1, 0); + return RESULT_SUCCESS; +} + +static int no_more_quit(int fd, int argc, char *argv[]) +{ + if (argc != 1) + return RESULT_SHOWUSAGE; + ast_cli(fd, "The QUIT and EXIT commands may no longer be used to shutdown the PBX.\n" + "Please use STOP NOW instead, if you wish to shutdown the PBX.\n"); + 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_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; + 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; return RESULT_SUCCESS; } @@ -394,59 +614,355 @@ static int handle_quit(int fd, int argc, char *argv[]) #define ASTERISK_PROMPT2 "%s*CLI> " -static struct ast_cli_entry quit = { { "quit", NULL }, handle_quit, "Exit Asterisk", quit_help }; +static struct ast_cli_entry aborthalt = { { "abort", "halt", NULL }, handle_abort_halt, "Cancel a running halt", abort_halt_help }; + +static struct ast_cli_entry quit = { { "quit", NULL }, no_more_quit, "Exit Asterisk", quit_help }; +static struct ast_cli_entry astexit = { { "exit", NULL }, no_more_quit, "Exit Asterisk", quit_help }; + +static struct ast_cli_entry astshutdownnow = { { "stop", "now", NULL }, handle_shutdown_now, "Shut down Asterisk imediately", shutdown_now_help }; +static struct ast_cli_entry astshutdowngracefully = { { "stop", "gracefully", NULL }, handle_shutdown_gracefully, "Gracefully shut down Asterisk", shutdown_gracefully_help }; +static struct ast_cli_entry astrestartnow = { { "restart", "now", NULL }, handle_restart_now, "Restart Asterisk immediately", restart_now_help }; +static struct ast_cli_entry astrestartgracefully = { { "restart", "gracefully", NULL }, handle_restart_gracefully, "Restart Asterisk gracefully", restart_gracefully_help }; +static struct ast_cli_entry astrestartwhenconvenient= { { "restart", "when", "convenient", NULL }, handle_restart_when_convenient, "Restart Asterisk at empty call volume", restart_when_convenient_help }; + +static int ast_el_read_char(EditLine *el, char *cp) +{ + int num_read=0; + int lastpos=0; + fd_set rfds; + int res; + int max; + char buf[512]; + + for (;;) { + FD_ZERO(&rfds); + FD_SET(ast_consock, &rfds); + FD_SET(STDIN_FILENO, &rfds); + max = ast_consock; + if (STDIN_FILENO > max) + max = STDIN_FILENO; + res = select(max+1, &rfds, NULL, NULL, NULL); + if (res < 0) { + if (errno == EINTR) + continue; + ast_log(LOG_ERROR, "select failed: %s\n", strerror(errno)); + break; + } + + if (FD_ISSET(STDIN_FILENO, &rfds)) { + num_read = read(STDIN_FILENO, cp, 1); + if (num_read < 1) { + break; + } else + return (num_read); + } + if (FD_ISSET(ast_consock, &rfds)) { + res = read(ast_consock, buf, sizeof(buf) - 1); + /* if the remote side disappears exit */ + if (res < 1) { + fprintf(stderr, "\nDisconnected from Asterisk server\n"); + quit_handler(0, 0, 0, 0); + } + + buf[res] = '\0'; + + if (!lastpos) + write(STDOUT_FILENO, "\r", 1); + write(STDOUT_FILENO, buf, res); + if ((buf[res-1] == '\n') || (buf[res-2] == '\n')) { + break; + } else { + lastpos = 1; + } + } + } + + *cp = '\0'; + return (0); +} -static struct ast_cli_entry astshutdown = { { "shutdown", NULL }, handle_quit, "Shut down an Asterisk PBX", shutdown_help }; +static char *cli_prompt(EditLine *el) +{ + char prompt[80]; -static char *cli_generator(char *text, int state) + if (remotehostname) + snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname); + else + snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT); + + return strdup(prompt); +} + +static char **ast_el_strtoarr(char *buf) { - return ast_cli_generator(rl_line_buffer, text, state); + char **match_list = NULL, *retstr; + size_t match_list_len; + int matches = 0; + + match_list_len = 1; + while ( (retstr = strsep(&buf, " ")) != NULL) { + + if (matches + 1 >= match_list_len) { + match_list_len <<= 1; + match_list = realloc(match_list, match_list_len * sizeof(char *)); + } + + match_list[matches++] = retstr; + } + + if (!match_list) + return (char **) NULL; + + if (matches>= match_list_len) + match_list = realloc(match_list, (match_list_len + 1) * sizeof(char *)); + + match_list[matches] = (char *) NULL; + + return match_list; } -static char *console_cli_generator(char *text, int state) +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 + 1), 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--; + continue; + } + + numoutput++; numoutputline++; + fprintf(stdout, "%-*s ", max, matches[idx]); + } + if (numoutputline > 0) + fprintf(stdout, "\n"); + } + + return numoutput; +} + + +static char *cli_complete(EditLine *el, int ch) +{ + int len=0; + char *ptr; + int nummatches = 0; + char **matches; + int retval = CC_ERROR; char buf[1024]; int res; -#if 0 - fprintf(stderr, "Searching for '%s', %s %d\n", rl_line_buffer, text, state); -#endif - snprintf(buf, sizeof(buf),"_COMMAND COMPLETE \"%s\" \"%s\" %d", rl_line_buffer, text, state); - fdprint(ast_consock, buf); - res = read(ast_consock, buf, sizeof(buf)); - buf[res] = '\0'; -#if 0 - printf("res is %d, buf is '%s'\n", res, buf); -#endif - if (strncmp(buf, "NULL", 4)) - return strdup(buf); - else - return NULL; + + LineInfo *lf = (LineInfo *)el_line(el); + + *lf->cursor = '\0'; + ptr = (char *)lf->cursor; + if (ptr) { + while (ptr > lf->buffer) { + if (isspace(*ptr)) { + ptr++; + break; + } + ptr--; + } + } + + len = lf->cursor - ptr; + + if (option_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) { + snprintf(buf, sizeof(buf),"_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr); + fdprint(ast_consock, buf); + res = read(ast_consock, buf, sizeof(buf)); + buf[res] = '\0'; + + matches = ast_el_strtoarr(buf); + } else + matches = (char **) NULL; + + + } else { + + nummatches = ast_cli_generatornummatches((char *)lf->buffer,ptr); + matches = ast_cli_completion_matches((char *)lf->buffer,ptr); + } + + 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, strdup(" ")); + 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,strdup(" ")); + retval = CC_REFRESH; + } + } + + } + + return (char *)retval; +} + +static int ast_el_initialize(void) +{ + HistEvent ev; + + 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, "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 to command completion */ + el_set(el, EL_BIND, "^I", "ed-complete", NULL); + /* Bind ? to command completion */ + el_set(el, EL_BIND, "?", "ed-complete", NULL); + + return 0; +} + +static int ast_el_add_history(char *buf) +{ + HistEvent ev; + + if (el_hist == NULL || el == NULL) + ast_el_initialize(); + + 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; + if ((ret = ast_el_add_history(buf)) == -1) + break; + } + fclose(f); + + return ret; } static void ast_remotecontrol(char * data) { char buf[80]; int res; - int max; - int lastpos = 0; - fd_set rfds; char filename[80] = ""; char *hostname; char *cpid; char *version; int pid; char tmp[80]; + char *stringp=NULL; + + char *ebuf; + int num = 0; + read(ast_consock, buf, sizeof(buf)); - if (data) { - write(ast_consock, data, strlen(data) + 1); - return; - } - hostname = strtok(buf, "/"); - cpid = strtok(NULL, "/"); - version = strtok(NULL, "/"); + if (data) + write(ast_consock, data, strlen(data) + 1); + stringp=buf; + hostname = strsep(&stringp, "/"); + cpid = strsep(&stringp, "/"); + version = strsep(&stringp, "/"); if (!version) version = ""; - strtok(hostname, "."); + stringp=hostname; + strsep(&stringp, "."); if (cpid) pid = atoi(cpid); else @@ -454,84 +970,156 @@ static void ast_remotecontrol(char * data) snprintf(tmp, sizeof(tmp), "set verbose atleast %d", option_verbose); fdprint(ast_consock, tmp); ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid); - snprintf(tmp, sizeof(tmp), ASTERISK_PROMPT2, hostname); + remotehostname = hostname; if (getenv("HOME")) snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); + if (el_hist == NULL || el == NULL) + ast_el_initialize(); + + el_set(el, EL_GETCFN, ast_el_read_char); + if (strlen(filename)) - read_history(filename); - ast_cli_register(&quit); - ast_cli_register(&astshutdown); - rl_callback_handler_install(tmp, remoteconsolehandler); - rl_completion_entry_function = (Function *)console_cli_generator; + ast_el_read_history(filename); + + ast_cli_register(&quit); + ast_cli_register(&astexit); +#if 0 + ast_cli_register(&astshutdown); +#endif for(;;) { - FD_ZERO(&rfds); - FD_SET(ast_consock, &rfds); - FD_SET(STDIN_FILENO, &rfds); - max = ast_consock; - if (STDIN_FILENO > max) - max = STDIN_FILENO; - res = select(max + 1, &rfds, NULL, NULL, NULL); - if (res < 0) { - if (errno == EINTR) - continue; - ast_log(LOG_ERROR, "select failed: %s\n", strerror(errno)); - break; - } - if (FD_ISSET(STDIN_FILENO, &rfds)) { - rl_callback_read_char(); - if (strlen(cmd)) { - res = write(ast_consock, cmd, strlen(cmd) + 1); + ebuf = (char *)el_gets(el, &num); + + if (data) /* hack to print output then exit if asterisk -rx is used */ + ebuf = strdup("quit"); + + if (ebuf && strlen(ebuf)) { + if (ebuf[strlen(ebuf)-1] == '\n') + ebuf[strlen(ebuf)-1] = '\0'; + if (!remoteconsolehandler(ebuf)) { + res = write(ast_consock, ebuf, strlen(ebuf) + 1); if (res < 1) { ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno)); break; } - strcpy(cmd, ""); - } - } - if (FD_ISSET(ast_consock, &rfds)) { - res = read(ast_consock, buf, sizeof(buf)); - if (res < 1) - break; - buf[res] = 0; - if (!lastpos) - write(STDOUT_FILENO, "\r", 2); - write(STDOUT_FILENO, buf, res); - if ((buf[res-1] == '\n') || (buf[res-2] == '\n')) { - rl_forced_update_display(); - lastpos = 0; - } else { - lastpos = 1; } } } printf("\nDisconnected from Asterisk server\n"); } +static int show_cli_help(void) { + printf("Asterisk " ASTERISK_VERSION ", Copyright (C) 2000-2002, Digium.\n"); + printf("Usage: asterisk [OPTIONS]\n"); + printf("Valid Options:\n"); + printf(" -h This help screen\n"); + printf(" -r Connect to Asterisk on this machine\n"); + printf(" -f Do not fork\n"); + printf(" -n Disable console colorization\n"); + printf(" -p Run as pseudo-realtime thread\n"); + printf(" -v Increase verbosity (multiple v's = more verbose)\n"); + printf(" -q Quiet mode (supress output)\n"); + printf(" -g Dump core in case of a crash\n"); + printf(" -x Execute command (only valid with -r)\n"); + printf(" -i Initializie crypto keys at startup\n"); + printf(" -c Provide console CLI\n"); + printf(" -d Enable extra debugging\n"); + printf("\n"); + return 0; +} + +static void ast_readconfig() { + struct ast_config *cfg; + struct ast_variable *v; + char *config = ASTCONFPATH; + + if (option_overrideconfig == 1) { + cfg = ast_load((char *)ast_config_AST_CONFIG_FILE); + } else { + cfg = ast_load(config); + } + + /* init with buildtime config */ + strncpy((char *)ast_config_AST_CONFIG_DIR,AST_CONFIG_DIR,sizeof(ast_config_AST_CONFIG_DIR)-1); + strncpy((char *)ast_config_AST_SPOOL_DIR,AST_SPOOL_DIR,sizeof(ast_config_AST_SPOOL_DIR)-1); + strncpy((char *)ast_config_AST_MODULE_DIR,AST_MODULE_DIR,sizeof(ast_config_AST_VAR_DIR)-1); + strncpy((char *)ast_config_AST_VAR_DIR,AST_VAR_DIR,sizeof(ast_config_AST_VAR_DIR)-1); + strncpy((char *)ast_config_AST_LOG_DIR,AST_LOG_DIR,sizeof(ast_config_AST_LOG_DIR)-1); + strncpy((char *)ast_config_AST_AGI_DIR,AST_AGI_DIR,sizeof(ast_config_AST_AGI_DIR)-1); + strncpy((char *)ast_config_AST_DB,AST_DB,sizeof(ast_config_AST_DB)-1); + strncpy((char *)ast_config_AST_KEY_DIR,AST_KEY_DIR,sizeof(ast_config_AST_KEY_DIR)-1); + strncpy((char *)ast_config_AST_PID,AST_PID,sizeof(ast_config_AST_PID)-1); + strncpy((char *)ast_config_AST_SOCKET,AST_SOCKET,sizeof(ast_config_AST_SOCKET)-1); + strncpy((char *)ast_config_AST_RUN_DIR,AST_RUN_DIR,sizeof(ast_config_AST_RUN_DIR)-1); + + /* no asterisk.conf? no problem, use buildtime config! */ + if (!cfg) { + return; + } + v = ast_variable_browse(cfg, "directories"); + while(v) { + if (!strcasecmp(v->name, "astetcdir")) { + strncpy((char *)ast_config_AST_CONFIG_DIR,v->value,sizeof(ast_config_AST_CONFIG_DIR)-1); + } else if (!strcasecmp(v->name, "astspooldir")) { + strncpy((char *)ast_config_AST_SPOOL_DIR,v->value,sizeof(ast_config_AST_SPOOL_DIR)-1); + } else if (!strcasecmp(v->name, "astvarlibdir")) { + strncpy((char *)ast_config_AST_VAR_DIR,v->value,sizeof(ast_config_AST_VAR_DIR)-1); + snprintf((char *)ast_config_AST_DB,sizeof(ast_config_AST_DB)-1,"%s/%s",v->value,"astdb"); + } else if (!strcasecmp(v->name, "astlogdir")) { + strncpy((char *)ast_config_AST_LOG_DIR,v->value,sizeof(ast_config_AST_LOG_DIR)-1); + } else if (!strcasecmp(v->name, "astagidir")) { + strncpy((char *)ast_config_AST_AGI_DIR,v->value,sizeof(ast_config_AST_AGI_DIR)-1); + } else if (!strcasecmp(v->name, "astrundir")) { + snprintf((char *)ast_config_AST_PID,sizeof(ast_config_AST_PID)-1,"%s/%s",v->value,"asterisk.pid"); + snprintf((char *)ast_config_AST_SOCKET,sizeof(ast_config_AST_SOCKET)-1,"%s/%s",v->value,"asterisk.ctl"); + strncpy((char *)ast_config_AST_RUN_DIR,v->value,sizeof(ast_config_AST_RUN_DIR)-1); + } else if (!strcasecmp(v->name, "astmoddir")) { + strncpy((char *)ast_config_AST_MODULE_DIR,v->value,sizeof(ast_config_AST_MODULE_DIR)-1); + } + v = v->next; + } + ast_destroy(cfg); +} + int main(int argc, char *argv[]) { char c; - fd_set rfds; - int res; - int pid; char filename[80] = ""; char hostname[256]; + char tmp[80]; char * xarg = NULL; + int x; + FILE *f; sigset_t sigs; + int num; + char *buf; + + /* Remember original args for restart */ + if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) { + fprintf(stderr, "Truncating argument size to %d\n", sizeof(_argv) / sizeof(_argv[0]) - 1); + argc = sizeof(_argv) / sizeof(_argv[0]) - 1; + } + for (x=0;x", sizeof(hostname)); + strncpy(hostname, "", sizeof(hostname)-1); mainpid = getpid(); ast_ulaw_init(); + ast_alaw_init(); callerid_init(); + tdd_init(); if (getenv("HOME")) snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); /* Check if we're root */ + /* if (geteuid()) { ast_log(LOG_ERROR, "Must be run as root\n"); exit(1); } + */ /* Check for options */ - while((c=getopt(argc, argv, "fdvqprcx:")) != EOF) { + while((c=getopt(argc, argv, "hfdvqprgcinx:C:")) != EOF) { switch(c) { case 'd': option_debug++; @@ -544,6 +1132,9 @@ int main(int argc, char *argv[]) case 'f': option_nofork++; break; + case 'n': + option_nocolor++; + break; case 'r': option_remote++; option_nofork++; @@ -562,43 +1153,97 @@ int main(int argc, char *argv[]) option_exec++; xarg = optarg; break; + case 'C': + strncpy((char *)ast_config_AST_CONFIG_FILE,optarg,sizeof(ast_config_AST_CONFIG_FILE)); + option_overrideconfig++; + break; + case 'i': + option_initcrypto++; + break; + case'g': + option_dumpcore++; + break; + case 'h': + show_cli_help(); + exit(0); case '?': exit(1); } } - + + if (option_dumpcore) { + struct rlimit l; + memset(&l, 0, sizeof(l)); + l.rlim_cur = RLIM_INFINITY; + l.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &l)) { + ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n", strerror(errno)); + } + } + + term_init(); + printf(term_end()); + fflush(stdout); + if (option_console && !option_verbose) + ast_verbose("[ Reading Master Configuration ]"); + ast_readconfig(); + + if (el_hist == NULL || el == NULL) + ast_el_initialize(); + + if (strlen(filename)) + ast_el_read_history(filename); + if (ast_tryconnect()) { /* One is already running */ if (option_remote) { if (option_exec) { ast_remotecontrol(xarg); - quit_handler(0); + quit_handler(0, 0, 0, 0); exit(0); } + printf(term_quit()); ast_register_verbose(console_verboser); ast_verbose( "Asterisk " ASTERISK_VERSION ", Copyright (C) 1999-2001 Linux Support Services, Inc.\n"); ast_verbose( "Written by Mark Spencer \n"); ast_verbose( "=========================================================================\n"); ast_remotecontrol(NULL); - quit_handler(0); + quit_handler(0, 0, 0, 0); exit(0); } else { - ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect.\n", AST_SOCKET); + ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect.\n", (char *)ast_config_AST_SOCKET); + printf(term_quit()); exit(1); } } else if (option_remote || option_exec) { ast_log(LOG_ERROR, "Unable to connect to remote asterisk\n"); + printf(term_quit()); exit(1); } - if (!option_verbose && !option_console && !option_debug) { + /* Blindly write pid file since we couldn't connect */ + unlink((char *)ast_config_AST_PID); + f = fopen((char *)ast_config_AST_PID, "w"); + if (f) { + fprintf(f, "%d\n", getpid()); + fclose(f); + } else + ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", (char *)ast_config_AST_PID, strerror(errno)); + + if (!option_verbose && !option_debug && !option_nofork && !option_console) { +#if 1 + daemon(0,0); +#else pid = fork(); if (pid < 0) { ast_log(LOG_ERROR, "Unable to fork(): %s\n", strerror(errno)); + printf(term_quit()); exit(1); } if (pid) exit(0); +#endif } + ast_makesocket(); sigemptyset(&sigs); sigaddset(&sigs, SIGHUP); @@ -618,27 +1263,56 @@ int main(int argc, char *argv[]) if (option_console && !option_verbose) ast_verbose("[ Booting..."); signal(SIGURG, urg_handler); - signal(SIGINT, quit_handler); - signal(SIGTERM, quit_handler); + signal(SIGINT, __quit_handler); + signal(SIGTERM, __quit_handler); signal(SIGHUP, hup_handler); signal(SIGPIPE, pipe_handler); - if (set_priority(option_highpriority)) + if (set_priority(option_highpriority)) { + printf(term_quit()); exit(1); - if (init_logger()) + } + if (init_logger()) { + printf(term_quit()); + exit(1); + } + if (init_manager()) { + printf(term_quit()); + exit(1); + } + if (ast_image_init()) { + printf(term_quit()); exit(1); - if (load_pbx()) + } + if (load_pbx()) { + printf(term_quit()); + exit(1); + } + if (load_modules()) { + printf(term_quit()); + exit(1); + } + if (init_framer()) { + printf(term_quit()); exit(1); - if (load_modules()) + } + if (astdb_init()) { + printf(term_quit()); exit(1); + } /* We might have the option of showing a console, but for now just do nothing... */ if (option_console && !option_verbose) ast_verbose(" ]\n"); if (option_verbose || option_console) - ast_verbose( "Asterisk Ready.\n"); + ast_verbose(term_color(tmp, "Asterisk Ready.\n", COLOR_BRWHITE, COLOR_BLACK, sizeof(tmp))); fully_booted = 1; pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); - ast_cli_register(&astshutdown); + ast_cli_register(&astshutdownnow); + ast_cli_register(&astshutdowngracefully); + ast_cli_register(&astrestartnow); + ast_cli_register(&astrestartgracefully); + ast_cli_register(&astrestartwhenconvenient); + ast_cli_register(&aborthalt); if (option_console) { /* Console stuff now... */ /* Register our quit function */ @@ -647,24 +1321,19 @@ int main(int argc, char *argv[]) snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, mainpid); set_title(title); ast_cli_register(&quit); + ast_cli_register(&astexit); consolethread = pthread_self(); - if (strlen(filename)) - read_history(filename); - rl_callback_handler_install(ASTERISK_PROMPT, consolehandler); - rl_completion_entry_function = (Function *)cli_generator; - for(;;) { - FD_ZERO(&rfds); - FD_SET(STDIN_FILENO, &rfds); - res = select(STDIN_FILENO + 1, &rfds, NULL, NULL, NULL); - if (res > 0) { - rl_callback_read_char(); - } else if (res < 1) { - rl_forced_update_display(); - } - - } + + while ( (buf = (char *)el_gets(el, &num) ) != NULL && num != 0) { + + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = '\0'; + + consolehandler((char *)buf); + } + } else { - /* Do nothing */ + /* Do nothing */ select(0,NULL,NULL,NULL,NULL); } return 0;