Version 0.1.8 from FTP
authorMark Spencer <markster@digium.com>
Wed, 9 May 2001 03:11:22 +0000 (03:11 +0000)
committerMark Spencer <markster@digium.com>
Wed, 9 May 2001 03:11:22 +0000 (03:11 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@320 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
Makefile
asterisk.c
audiofile.patch [deleted file]
cli.c
make_build_h [new file with mode: 0755]

diff --git a/CHANGES b/CHANGES
index 945c568..1cc225f 100755 (executable)
--- 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
index 260d782..55b57b2 100755 (executable)
--- 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
index 94e6802..a73307b 100755 (executable)
 #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(&lthread, 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 (executable)
index c013489..0000000
+++ /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 aa41b0e..98e16a0 100755 (executable)
--- a/cli.c
+++ b/cli.c
 #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 (executable)
index 0000000..f98f549
--- /dev/null
@@ -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