diff --git a/CHANGES b/CHANGES index 945c568254cfc5fb52d9af4ffec4825c692fbc99..1cc225f023beea706215b0606c71334e6f7653d7 100755 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,38 @@ +Asterisk 0.1.8 + -- Keep track of version information + -- Add -f to cause Asterisk not to fork + -- Keep important information in voicemail .txt file + -- Adtran Voice over Frame Relay updates + -- Implement option setting/querying of channel drivers + -- IAX performance improvements and protocol fixes + -- Substantial enhancement of console channel driver + -- Add IAX registration. Now IAX can dynamically register + -- Add flash-hook transfer on tormenta channels + -- Added Three Way Calling on tormenta channels + -- Start on concept of zombie channel + -- Add Call Waiting CallerID + -- Keep track of who registeres contexts, includes, and extensions + -- Added Call Waiting(tm), *67, *70, and *82 codes + -- Move parked calls into "parkedcalls" context by default + -- Allow dialplan to be displayed + -- Allow "=>" instead of just "=" to make instantiation clearer + -- Asterisk forks if called with no arguments + -- Add remote control by running asterisk -vvvc + -- Adjust verboseness with "set verbose" now + -- No longer requires libaudiofile + -- Install beep + -- Make PBX Config module reload extensions on SIGHUP + -- Allow modules to be reloaded when SIGHUP is received + -- Variables now contain line numbers + -- Make dialer send in band signalling + -- Add record application + -- Added PRI signalling to Tormenta driver + -- Allow use of BYEXTENSION in "Goto" + -- Allow adjustment of gains on tormenta channels + -- Added raw PCM file format support + -- Add U-law translator + -- Fix DTMF handling in bridge code + -- Fix access control with IAX * Asterisk 0.1.7 -- Update configuration files and add some missing sounds -- Added ability to include one context in another diff --git a/Makefile b/Makefile index 260d782c475650195f75e299a73ac75960db90d8..55b57b28086abead4c943b8e7a9f9d343e224e22 100755 --- a/Makefile +++ b/Makefile @@ -23,11 +23,14 @@ PROC=i586 DEBUG=-g #-pg INCLUDE=-Iinclude -I../include -CFLAGS=-pipe -Wall -Werror -Wmissing-prototypes -Wmissing-declarations -fomit-frame-pointer -O6 $(DEBUG) $(INCLUDE) -D_REENTRANT +CFLAGS=-pipe -Wall -Werror -Wmissing-prototypes -Wmissing-declarations -O6 $(DEBUG) $(INCLUDE) -D_REENTRANT CFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) -CFLAGS += -DDO_CRASH +ASTERISKVERSION=$(shell cat .version) +CFLAGS+=-DASTERISK_VERSION=\"$(ASTERISKVERSION)\" +CFLAGS+= -DDO_CRASH -DDEBUG_THREADS +CFLAGS+=# -fomit-frame-pointer SUBDIRS=channels pbx apps codecs formats -LIBS=-ldl -lpthread -lreadline -lncurses -lm # -lefence +LIBS=-ldl -lpthread -lreadline -lncurses -lm OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \ translate.o file.o say.o pbx.o cli.o md5.o \ ulaw.o callerid.o fskmodem.o asterisk.o @@ -46,7 +49,13 @@ _all: all all: asterisk subdirs -asterisk: $(OBJS) +_version: + if [ -d CVS ]; then echo "CVS-`date +"%D-%T"`" > .version; fi + +build.h: + ./make_build_h + +asterisk: _version build.h $(OBJS) gcc -o asterisk -rdynamic $(OBJS) $(LIBS) subdirs: @@ -55,17 +64,20 @@ subdirs: clean: for x in $(SUBDIRS); do $(MAKE) -C $$x clean || exit 1 ; done rm -f *.o *.so asterisk + rm -f build.h datafiles: all mkdir -p /var/lib/asterisk/sounds/digits for x in sounds/digits/*; do \ install $$x /var/lib/asterisk/sounds/digits ; \ done - for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-*; do \ + for x in sounds/vm-* sounds/transfer* sounds/pbx-* sounds/ss-* sounds/beep*; do \ install $$x /var/lib/asterisk/sounds ; \ done install: all datafiles mkdir -p $(MODULES_DIR) + mkdir -p /usr/sbin + install -m 755 asterisk /usr/sbin/ for x in $(SUBDIRS); do $(MAKE) -C $$x install || exit 1 ; done install -d /usr/include/asterisk install include/asterisk/*.h /usr/include/asterisk diff --git a/asterisk.c b/asterisk.c index 94e6802d060dccda6c32b2db142d9fae00849456..a73307be006e316e777ad4d47b120aa6a32f5ebb 100755 --- a/asterisk.c +++ b/asterisk.c @@ -19,34 +19,240 @@ #include <asterisk/channel.h> #include <asterisk/ulaw.h> #include <asterisk/callerid.h> +#include <asterisk/module.h> #include <stdio.h> #include <signal.h> #include <sched.h> #include <pthread.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/select.h> +#include <string.h> +#include <errno.h> #include <readline/readline.h> #include <readline/history.h> #include "asterisk.h" +#define AST_MAX_CONNECTS 128 +#define NUM_MSGS 64 + int option_verbose=0; int option_debug=0; int option_nofork=0; int option_quiet=0; int option_console=0; int option_highpriority=0; +int option_remote=0; +int option_exec=0; int fully_booted = 0; +static int ast_socket = -1; /* UNIX Socket for allowing remote control */ +static int ast_consock = -1; /* UNIX Socket for controlling another asterisk */ +static int mainpid; +struct console { + int fd; /* File descriptor */ + int p[2]; /* Pipe */ + pthread_t t; /* Thread of handler */ +}; + +struct console consoles[AST_MAX_CONNECTS]; + char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE; +static int fdprint(int fd, char *s) +{ + return write(fd, s, strlen(s) + 1); +} + +static void network_verboser(char *s, int pos, int replace, int complete) +{ + int x; + for (x=0;x<AST_MAX_CONNECTS; x++) { + if (consoles[x].fd > -1) + fdprint(consoles[x].p[1], s); + } +} + +static pthread_t lthread; + +static void *netconsole(void *vconsole) +{ + struct console *con = vconsole; + char hostname[256]; + char tmp[512]; + int res; + int max; + fd_set rfds; + + if (gethostname(hostname, sizeof(hostname))) + strncpy(hostname, "<Unknown>", sizeof(hostname)); + snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, mainpid, ASTERISK_VERSION); + fdprint(con->fd, tmp); + for(;;) { + FD_ZERO(&rfds); + FD_SET(con->fd, &rfds); + FD_SET(con->p[0], &rfds); + max = con->fd; + if (con->p[0] > max) + max = con->p[0]; + res = select(max + 1, &rfds, NULL, NULL, NULL); + if (res < 0) { + ast_log(LOG_WARNING, "select returned < 0: %s\n", strerror(errno)); + continue; + } + if (FD_ISSET(con->fd, &rfds)) { + res = read(con->fd, tmp, sizeof(tmp)); + if (res < 1) + break; + tmp[res] = 0; + ast_cli_command(con->fd, tmp); + } + if (FD_ISSET(con->p[0], &rfds)) { + res = read(con->p[0], tmp, sizeof(tmp)); + if (res < 1) { + ast_log(LOG_ERROR, "read returned %d\n", res); + break; + } + res = write(con->fd, tmp, res); + if (res < 1) + break; + } + } + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection disconnected\n"); + close(con->fd); + close(con->p[0]); + close(con->p[1]); + con->fd = -1; + + return NULL; +} + +static void *listener(void *unused) +{ + struct sockaddr_un sun; + int s; + int len; + int x; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + for(;;) { + len = sizeof(sun); + s = accept(ast_socket, (struct sockaddr *)&sun, &len); + if (s < 0) { + ast_log(LOG_WARNING, "Accept retured %d: %s\n", s, strerror(errno)); + } else { + for (x=0;x<AST_MAX_CONNECTS;x++) { + if (consoles[x].fd < 0) { + if (pipe(consoles[x].p)) { + ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno)); + consoles[x].fd = -1; + fdprint(s, "Server failed to create pipe\n"); + close(s); + break; + } + consoles[x].fd = s; + if (pthread_create(&consoles[x].t, &attr, netconsole, &consoles[x])) { + ast_log(LOG_ERROR, "Unable to spawn thread to handle connection\n"); + consoles[x].fd = -1; + fdprint(s, "Server failed to spawn thread\n"); + close(s); + } + break; + } + } + if (x >= AST_MAX_CONNECTS) { + fdprint(s, "No more connections allowed\n"); + ast_log(LOG_WARNING, "No more connections allowed\n"); + close(s); + } else if (consoles[x].fd > -1) { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection\n"); + } + } + } + return NULL; +} + +static int ast_makesocket(void) +{ + struct sockaddr_un sun; + int res; + int x; + for (x=0;x<AST_MAX_CONNECTS;x++) + consoles[x].fd = -1; + unlink(AST_SOCKET); + ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0); + if (ast_socket < 0) { + ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno)); + return -1; + } + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_LOCAL; + strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path)); + res = bind(ast_socket, (struct sockaddr *)&sun, sizeof(sun)); + if (res) { + ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", AST_SOCKET, strerror(errno)); + close(ast_socket); + ast_socket = -1; + return -1; + } + res = listen(ast_socket, 2); + if (res < 0) { + ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", AST_SOCKET, strerror(errno)); + close(ast_socket); + ast_socket = -1; + return -1; + } + ast_register_verbose(network_verboser); + pthread_create(<hread, NULL, listener, NULL); + return 0; +} + +static int ast_tryconnect(void) +{ + struct sockaddr_un sun; + int res; + ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0); + if (ast_consock < 0) { + ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno)); + return 0; + } + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_LOCAL; + strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path)); + res = connect(ast_consock, (struct sockaddr *)&sun, sizeof(sun)); + if (res) { + close(ast_consock); + ast_consock = -1; + return 0; + } else + return 1; +} + static void urg_handler(int num) { /* Called by soft_hangup to interrupt the select, read, or other system call. We don't actually need to do anything though. */ - if (option_debug) + if (option_debug) ast_log(LOG_DEBUG, "Urgent handler\n"); signal(num, urg_handler); return; } +static void hup_handler(int num) +{ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Received HUP signal -- Reloading configs\n"); + ast_module_reload(); +} + + +static void pipe_handler(int num) +{ + /* Ignore sigpipe */ +} static void set_title(char *text) { /* Set an X-term or screen title */ @@ -86,19 +292,26 @@ static int set_priority(int pri) static void quit_handler(int num) { - static pthread_mutex_t quitlock = PTHREAD_MUTEX_INITIALIZER; char filename[80] = ""; - if (getenv("HOME")) - snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); - /* Quit only once */ - pthread_mutex_lock(&quitlock); + 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(); + } /* Called on exit */ - if (option_verbose) + if (option_verbose && option_console) ast_verbose("Asterisk ending (%d).\n", num); else if (option_debug) ast_log(LOG_DEBUG, "Asterisk ending (%d).\n", num); - if (strlen(filename)) - write_history(filename); + if (ast_socket > -1) + close(ast_socket); + if (ast_consock > -1) + close(ast_consock); + if (ast_socket > -1) + unlink(AST_SOCKET); + exit(0); } @@ -136,10 +349,39 @@ static void consolehandler(char *s) fprintf(stdout, "\nUse \"quit\" to exit\n"); } + +static char cmd[1024]; + +static void remoteconsolehandler(char *s) +{ + /* Called when readline data is available */ + if (s && strlen(s)) + add_history(s); + /* Give the console access to the shell */ + if (s) { + if (s[0] == '!') { + if (s[1]) + system(s+1); + else + system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh"); + } else + strncpy(cmd, s, sizeof(cmd)); + if (!strcasecmp(s, "help")) + fprintf(stdout, " !<command> Executes a given shell command\n"); + if (!strcasecmp(s, "quit")) + quit_handler(0); + } else + fprintf(stdout, "\nUse \"quit\" to exit\n"); +} + 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 int handle_quit(int fd, int argc, char *argv[]) { if (argc != 1) @@ -150,22 +392,135 @@ static int handle_quit(int fd, int argc, char *argv[]) #define ASTERISK_PROMPT "*CLI> " +#define ASTERISK_PROMPT2 "%s*CLI> " + static struct ast_cli_entry quit = { { "quit", NULL }, handle_quit, "Exit Asterisk", quit_help }; +static struct ast_cli_entry astshutdown = { { "shutdown", NULL }, handle_quit, "Shut down an Asterisk PBX", shutdown_help }; + static char *cli_generator(char *text, int state) { return ast_cli_generator(rl_line_buffer, text, state); } +static char *console_cli_generator(char *text, int state) +{ + 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; +} + +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]; + 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 (!version) + version = "<Version Unknown>"; + strtok(hostname, "."); + if (cpid) + pid = atoi(cpid); + else + pid = -1; + 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); + if (getenv("HOME")) + snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); + 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; + 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); + 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"); +} + int main(int argc, char *argv[]) { char c; fd_set rfds; int res; + int pid; char filename[80] = ""; char hostname[256]; + char * xarg = NULL; + sigset_t sigs; + if (gethostname(hostname, sizeof(hostname))) strncpy(hostname, "<Unknown>", sizeof(hostname)); + mainpid = getpid(); ast_ulaw_init(); callerid_init(); if (getenv("HOME")) @@ -176,7 +531,7 @@ int main(int argc, char *argv[]) exit(1); } /* Check for options */ - while((c=getopt(argc, argv, "dvqpc")) != EOF) { + while((c=getopt(argc, argv, "fdvqprcx:")) != EOF) { switch(c) { case 'd': option_debug++; @@ -186,6 +541,13 @@ int main(int argc, char *argv[]) option_console++; option_nofork++; break; + case 'f': + option_nofork++; + break; + case 'r': + option_remote++; + option_nofork++; + break; case 'p': option_highpriority++; break; @@ -196,14 +558,60 @@ int main(int argc, char *argv[]) case 'q': option_quiet++; break; + case 'x': + option_exec++; + xarg = optarg; + break; case '?': exit(1); } } - ast_register_verbose(console_verboser); + + if (ast_tryconnect()) { + /* One is already running */ + if (option_remote) { + if (option_exec) { + ast_remotecontrol(xarg); + quit_handler(0); + exit(0); + } + 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 <markster@linux-support.net>\n"); + ast_verbose( "=========================================================================\n"); + ast_remotecontrol(NULL); + quit_handler(0); + exit(0); + } else { + ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect.\n", AST_SOCKET); + exit(1); + } + } else if (option_remote || option_exec) { + ast_log(LOG_ERROR, "Unable to connect to remote asterisk\n"); + exit(1); + } + if (!option_verbose && !option_console && !option_debug) { + pid = fork(); + if (pid < 0) { + ast_log(LOG_ERROR, "Unable to fork(): %s\n", strerror(errno)); + exit(1); + } + if (pid) + exit(0); + } + ast_makesocket(); + sigemptyset(&sigs); + sigaddset(&sigs, SIGHUP); + sigaddset(&sigs, SIGTERM); + sigaddset(&sigs, SIGINT); + sigaddset(&sigs, SIGPIPE); + sigaddset(&sigs, SIGWINCH); + pthread_sigmask(SIG_BLOCK, &sigs, NULL); + if (option_console || option_verbose || option_remote) + ast_register_verbose(console_verboser); /* Print a welcome message if desired */ if (option_verbose || option_console) { - ast_verbose( "Asterisk, Copyright (C) 1999 Mark Spencer\n"); + ast_verbose( "Asterisk " ASTERISK_VERSION ", Copyright (C) 1999-2001 Linux Support Services, Inc.\n"); ast_verbose( "Written by Mark Spencer <markster@linux-support.net>\n"); ast_verbose( "=========================================================================\n"); } @@ -212,7 +620,8 @@ int main(int argc, char *argv[]) signal(SIGURG, urg_handler); signal(SIGINT, quit_handler); signal(SIGTERM, quit_handler); - signal(SIGHUP, quit_handler); + signal(SIGHUP, hup_handler); + signal(SIGPIPE, pipe_handler); if (set_priority(option_highpriority)) exit(1); if (init_logger()) @@ -228,12 +637,14 @@ int main(int argc, char *argv[]) if (option_verbose || option_console) ast_verbose( "Asterisk Ready.\n"); fully_booted = 1; + pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); + ast_cli_register(&astshutdown); if (option_console) { /* Console stuff now... */ /* Register our quit function */ char title[256]; set_icon("Asterisk"); - snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, getpid()); + snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, mainpid); set_title(title); ast_cli_register(&quit); consolethread = pthread_self(); diff --git a/audiofile.patch b/audiofile.patch deleted file mode 100755 index c0134895c1d793941bcd987cc42a5b65397745fa..0000000000000000000000000000000000000000 --- a/audiofile.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -uNr audiofile-0.1.9.old/libaudiofile/audiofile.c audiofile-0.1.9/libaudiofile/audiofile.c ---- audiofile-0.1.9.old/libaudiofile/audiofile.c Fri Jul 23 12:57:56 1999 -+++ audiofile-0.1.9/libaudiofile/audiofile.c Fri Dec 10 18:43:30 1999 -@@ -488,6 +488,20 @@ - return afOpenVirtualFile(af_virtual_file_new_for_file(fp), mode, setup); - } - -+AFfilehandle afOpenFD(int fd, const char *mode, AFfilesetup setup) -+{ -+ FILE *fp; -+ -+ fp = fdopen(fd, mode); -+ if (fp == NULL) -+ { -+ _af_error(AF_BAD_OPEN); -+ return AF_NULL_FILEHANDLE; -+ } -+ -+ return afOpenVirtualFile(af_virtual_file_new_for_file(fp), mode, setup); -+} -+ - int afGetFileFormat (AFfilehandle file, int *version) - { - assert(file); diff --git a/cli.c b/cli.c index aa41b0e7a7031b4e8b5c5e1e63427dce17c85efa..98e16a0c86f801a71db25f8ec17a0dd01ea1feb2 100755 --- a/cli.c +++ b/cli.c @@ -27,6 +27,11 @@ #include <readline/readline.h> /* For module directory */ #include "asterisk.h" +#include "build.h" + +#define VERSION_INFO "Asterisk " ASTERISK_VERSION " built by " BUILD_USER "@" BUILD_HOSTNAME \ + " on a " BUILD_MACHINE " running " BUILD_OS + void ast_cli(int fd, char *fmt, ...) { @@ -66,6 +71,21 @@ static char chanlist_help[] = " Lists currently defined channels and some information about\n" " them.\n"; +static char reload_help[] = +"Usage: reload\n" +" Reloads configuration files for all modules which support\n" +" reloading.\n"; + +static char set_verbose_help[] = +"Usage: set verbose <level>\n" +" Sets level of verbose messages to be displayed. 0 means\n" +" no messages should be displayed.\n"; + +static char softhangup_help[] = +"Usage: soft hangup <channel>\n" +" Request that a channel be hung up. The hangup takes effect\n" +" the next time the driver reads or writes from the channel\n"; + static int handle_load(int fd, int argc, char *argv[]) { if (argc != 2) @@ -77,6 +97,32 @@ static int handle_load(int fd, int argc, char *argv[]) return RESULT_SUCCESS; } +static int handle_reload(int fd, int argc, char *argv[]) +{ + if (argc != 1) + return RESULT_SHOWUSAGE; + ast_module_reload(); + return RESULT_SUCCESS; +} + +static int handle_set_verbose(int fd, int argc, char *argv[]) +{ + int val; + /* Has a hidden 'at least' argument */ + if ((argc != 3) && (argc != 4)) + return RESULT_SHOWUSAGE; + if ((argc == 4) && strcasecmp(argv[2], "atleast")) + return RESULT_SHOWUSAGE; + if (argc == 3) + option_verbose = atoi(argv[2]); + else { + val = atoi(argv[3]); + if (val > option_verbose) + option_verbose = val; + } + return RESULT_SUCCESS; +} + static int handle_unload(int fd, int argc, char *argv[]) { int x; @@ -122,30 +168,41 @@ static char modlist_help[] = " Shows Asterisk modules currently in use, and usage " "statistics.\n"; +static char version_help[] = +"Usage: show version\n" +" Shows Asterisk version information.\n "; + static int handle_modlist(int fd, int argc, char *argv[]) { if (argc != 2) return RESULT_SHOWUSAGE; - pthread_mutex_lock(&climodentrylock); + ast_pthread_mutex_lock(&climodentrylock); climodentryfd = fd; ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count"); ast_update_module_list(modlist_modentry); climodentryfd = -1; - pthread_mutex_unlock(&climodentrylock); + ast_pthread_mutex_unlock(&climodentrylock); return RESULT_SUCCESS; } +static int handle_version(int fd, int argc, char *argv[]) +{ + if (argc != 2) + return RESULT_SHOWUSAGE; + ast_cli(fd, VERSION_INFO); + return RESULT_SUCCESS; +} static int handle_chanlist(int fd, int argc, char *argv[]) { -#define FORMAT_STRING "%15s (%-10s %-12s %-4d) %-12s %-15s\n" -#define FORMAT_STRING2 "%15s (%-10s %-12s %-4s) %-12s %-15s\n" +#define FORMAT_STRING "%15s (%-10s %-12s %-4d) %7s %-12s %-15s\n" +#define FORMAT_STRING2 "%15s (%-10s %-12s %-4s) %7s %-12s %-15s\n" struct ast_channel *c=NULL; if (argc != 2) return RESULT_SHOWUSAGE; c = ast_channel_walk(NULL); - ast_cli(fd, FORMAT_STRING2, "Channel", "Context", "Extension", "Pri", "Appl.", "Data"); + ast_cli(fd, FORMAT_STRING2, "Channel", "Context", "Extension", "Pri", "State", "Appl.", "Data"); while(c) { - ast_cli(fd, FORMAT_STRING, c->name, c->context, c->exten, c->priority, + ast_cli(fd, FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->state), c->appl ? c->appl : "(None)", c->data ? ( strlen(c->data) ? c->data : "(Empty)" ): "(None)"); c = ast_channel_walk(c); } @@ -156,6 +213,52 @@ static char showchan_help[] = "Usage: show channel <channel>\n" " Shows lots of information about the specified channel.\n"; +static char commandcomplete_help[] = +"Usage: _command complete \"<line>\" text state\n" +" This function is used internally to help with command completion and should.\n" +" never be called by the user directly.\n"; + +static int handle_softhangup(int fd, int argc, char *argv[]) +{ + struct ast_channel *c=NULL; + if (argc != 3) + return RESULT_SHOWUSAGE; + c = ast_channel_walk(NULL); + while(c) { + if (!strcasecmp(c->name, argv[2])) { + ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name); + c->softhangup = 1; + break; + } + c = ast_channel_walk(c); + } + if (!c) + ast_cli(fd, "%s is not a known channel\n", argv[2]); + return RESULT_SUCCESS; +} + +static char *__ast_cli_generator(char *text, char *word, int state, int lock); + +static int handle_commandcomplete(int fd, int argc, char *argv[]) +{ + char *buf; +#if 0 + printf("Search for %d args: '%s', '%s', '%s', '%s'\n", argc, argv[0], argv[1], argv[2], argv[3]); +#endif + if (argc != 5) + return RESULT_SHOWUSAGE; + buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0); +#if 0 + printf("Search for '%s' %s %d got '%s'\n", argv[2], argv[3], atoi(argv[4]), buf); +#endif + if (buf) { + ast_cli(fd, buf); + free(buf); + } else + ast_cli(fd, "NULL\n"); + return RESULT_SUCCESS; +} + static int handle_showchan(int fd, int argc, char *argv[]) { struct ast_channel *c=NULL; @@ -170,12 +273,12 @@ static int handle_showchan(int fd, int argc, char *argv[]) " Type: %s\n" " Caller ID: %s\n" " DNID Digits: %s\n" - " State: %d\n" + " State: %s (%d)\n" " Rings: %d\n" " WriteFormat: %d\n" " ReadFormat: %d\n" " NativeFormat: %d\n" - "File Descriptor: %d\n" + "1st File Descriptor: %d\n" " -- PBX --\n" " Context: %s\n" " Extension: %s\n" @@ -186,8 +289,8 @@ static int handle_showchan(int fd, int argc, char *argv[]) " Blocking in: %s\n", c->name, c->type, (c->callerid ? c->callerid : "(N/A)"), - (c->dnid ? c->dnid : "(N/A)" ), c->state, c->rings, c->nativeformats, c->writeformat, c->readformat, - c->fd, c->context, c->exten, c->priority, ( c->appl ? c->appl : "(N/A)" ), + (c->dnid ? c->dnid : "(N/A)" ), ast_state2str(c->state), c->state, c->rings, c->nativeformats, c->writeformat, c->readformat, + c->fds[0], c->context, c->exten, c->priority, ( c->appl ? c->appl : "(N/A)" ), ( c-> data ? (strlen(c->data) ? c->data : "(Empty)") : "(None)"), c->stack, (c->blocking ? c->blockproc : "(Not Blocking)")); @@ -206,8 +309,10 @@ static char *complete_ch(char *line, char *word, int pos, int state) int which=0; c = ast_channel_walk(NULL); while(c) { - if (++which > state) - break; + if (!strncasecmp(word, c->name, strlen(word))) { + if (++which > state) + break; + } c = ast_channel_walk(c); } return c ? strdup(c->name) : NULL; @@ -223,7 +328,7 @@ static char *complete_fn(char *line, char *word, int pos, int state) strncpy(filename, word, sizeof(filename)); else snprintf(filename, sizeof(filename), "%s/%s", AST_MODULE_DIR, word); - c = filename_completion_function(filename, state); + c = (char*)filename_completion_function(filename, state); if (c && word[0] != '/') c += (strlen(AST_MODULE_DIR) + 1); return c ? strdup(c) : c; @@ -234,11 +339,16 @@ static int handle_help(int fd, int argc, char *argv[]); static struct ast_cli_entry builtins[] = { /* Keep alphabetized */ { { "help", NULL }, handle_help, "Display help list, or specific help on a command", help_help }, + { { "_command", "complete", NULL }, handle_commandcomplete, "Command complete", commandcomplete_help }, { { "load", NULL }, handle_load, "Load a dynamic module by name", load_help, complete_fn }, + { { "reload", NULL }, handle_reload, "Reload configuration", reload_help }, + { { "set", "verbose", NULL }, handle_set_verbose, "Set level of verboseness", set_verbose_help }, { { "show", "channel", NULL }, handle_showchan, "Display information on a specific channel", showchan_help, complete_ch }, { { "show", "channels", NULL }, handle_chanlist, "Display information on channels", chanlist_help }, { { "show", "modules", NULL }, handle_modlist, "List modules and info", modlist_help }, + { { "soft", "hangup", NULL }, handle_softhangup, "Request a hangup on a given channel", softhangup_help, complete_ch }, { { "unload", NULL }, handle_unload, "Unload a dynamic module by name", unload_help, complete_fn }, + { { "version", NULL }, handle_version, "Display version info", version_help }, { { NULL }, NULL, NULL, NULL } }; @@ -327,7 +437,7 @@ static char *find_best(char *argv[]) int ast_cli_unregister(struct ast_cli_entry *e) { struct ast_cli_entry *cur, *l=NULL; - pthread_mutex_lock(&clilock); + ast_pthread_mutex_lock(&clilock); cur = helpers; while(cur) { if (e == cur) { @@ -342,7 +452,7 @@ int ast_cli_unregister(struct ast_cli_entry *e) l = cur; cur = cur->next; } - pthread_mutex_unlock(&clilock); + ast_pthread_mutex_unlock(&clilock); return 0; } @@ -351,11 +461,11 @@ int ast_cli_register(struct ast_cli_entry *e) struct ast_cli_entry *cur, *l=NULL; char fulle[80], fulltst[80]; static int len; - pthread_mutex_lock(&clilock); + ast_pthread_mutex_lock(&clilock); join2(fulle, sizeof(fulle), e->cmda); if (find_cli(e->cmda, -1)) { ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle); - pthread_mutex_unlock(&clilock); + ast_pthread_mutex_unlock(&clilock); return -1; } cur = helpers; @@ -384,7 +494,7 @@ int ast_cli_register(struct ast_cli_entry *e) helpers = e; e->next = NULL; } - pthread_mutex_unlock(&clilock); + ast_pthread_mutex_unlock(&clilock); return 0; } @@ -417,6 +527,9 @@ static int help_workhorse(int fd, char *match[]) fullcmd = fullcmd1; e1++; } + /* Hide commands that start with '_' */ + if (fullcmd[0] == '_') + continue; if (match) { if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) { continue; @@ -469,6 +582,11 @@ static char *parse_args(char *s, int *max, char *argv[]) goto normal; else quoted = !quoted; + if (quoted && whitespace) { + /* If we're starting a quote, coming off white space start a new word, too */ + argv[x++] = cur; + whitespace=0; + } escaped = 0; break; case ' ': @@ -514,7 +632,7 @@ normal: return dup; } -char *ast_cli_generator(char *text, char *word, int state) +static char *__ast_cli_generator(char *text, char *word, int state, int lock) { char *argv[AST_MAX_ARGS]; struct ast_cli_entry *e, *e1, *e2; @@ -528,7 +646,8 @@ char *ast_cli_generator(char *text, char *word, int state) if ((dup = parse_args(text, &x, argv))) { join(matchstr, sizeof(matchstr), argv); - pthread_mutex_lock(&clilock); + if (lock) + ast_pthread_mutex_lock(&clilock); e1 = builtins; e2 = helpers; while(e1->cmda[0] || e2) { @@ -549,7 +668,7 @@ char *ast_cli_generator(char *text, char *word, int state) fullcmd = fullcmd1; e1++; } - if (!strncasecmp(matchstr, fullcmd, strlen(matchstr))) { + if ((fullcmd[0] != '_') && !strncasecmp(matchstr, fullcmd, strlen(matchstr))) { /* We contain the first part of one or more commands */ matchnum++; if (matchnum > state) { @@ -560,7 +679,8 @@ char *ast_cli_generator(char *text, char *word, int state) res = e->cmda[x]; } if (res) { - pthread_mutex_unlock(&clilock); + if (lock) + ast_pthread_mutex_unlock(&clilock); return res ? strdup(res) : NULL; } } @@ -569,17 +689,24 @@ char *ast_cli_generator(char *text, char *word, int state) /* We have a command in its entirity within us -- theoretically only one command can have this occur */ fullcmd = e->generator(text, word, (strlen(word) ? (x - 1) : (x)), state); - pthread_mutex_unlock(&clilock); + if (lock) + ast_pthread_mutex_unlock(&clilock); return fullcmd; } } - pthread_mutex_unlock(&clilock); + if (lock) + ast_pthread_mutex_unlock(&clilock); free(dup); } return NULL; } +char *ast_cli_generator(char *text, char *word, int state) +{ + return __ast_cli_generator(text, word, state, 1); +} + int ast_cli_command(int fd, char *s) { char *argv[AST_MAX_ARGS]; @@ -590,7 +717,7 @@ int ast_cli_command(int fd, char *s) if ((dup = parse_args(s, &x, argv))) { /* We need at least one entry, or ignore */ if (x > 0) { - pthread_mutex_lock(&clilock); + ast_pthread_mutex_lock(&clilock); e = find_cli(argv, 0); if (e) { switch(e->handler(fd, x, argv)) { @@ -601,7 +728,7 @@ int ast_cli_command(int fd, char *s) } } else ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv)); - pthread_mutex_unlock(&clilock); + ast_pthread_mutex_unlock(&clilock); } free(dup); } else { diff --git a/make_build_h b/make_build_h new file mode 100755 index 0000000000000000000000000000000000000000..f98f54991b7ad4f32ebaebcf5fba015a17a7007a --- /dev/null +++ b/make_build_h @@ -0,0 +1,20 @@ +#!/bin/sh +HOSTNAME=`uname -n` +KERNEL=`uname -r` +MACHINE=`uname -m` +OS=`uname -s` +USER=`whoami` +VERSION=`cat .version` +rm -f build.h +cat > build.h << END +/* + * build.h + * Automatically generated + */ +#define BUILD_HOSTNAME "${HOSTNAME}" +#define BUILD_KERNEL "${KERNEL}" +#define BUILD_MACHINE "${MACHINE}" +#define BUILD_OS "${OS}" +#define BUILD_VERSION "${VERSION}" +#define BUILD_USER "${USER}" +END