Enhance AGI with several fixes:
authorTilghman Lesher <tilghman@meg.abyt.es>
Mon, 23 Jul 2007 22:02:05 +0000 (22:02 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Mon, 23 Jul 2007 22:02:05 +0000 (22:02 +0000)
 - Makes the structures handling external AGI commands a bit more thread-safe
 - Makes AGI transparently work with both live and hungup channels
 - DeadAGI is hence no longer necessary and is deprecated
 - CLI bug fixes
 - Commands will refuse to run if the channel is dead and the command is nonsensical
   for dead channels.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@76707 65c4cc65-6c06-0410-ace0-fbb531ad65f3

include/asterisk/agi.h
res/res_agi.c

index 5a0edc8..ac85db1 100644 (file)
@@ -43,11 +43,14 @@ typedef struct agi_command {
        char *summary;
        /* Detailed usage information */
        char *usage;
-       struct agi_command *next;
+       /* Does this application run dead */
+       int dead;
+       /* Linked list pointer */
+       AST_LIST_ENTRY(agi_command) list;
 } agi_command;
 
 int ast_agi_register(agi_command *cmd);
-void ast_agi_unregister(agi_command *cmd);
+int ast_agi_unregister(agi_command *cmd);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
index 8b04910..47c8ba8 100644 (file)
@@ -117,6 +117,8 @@ enum agi_result {
        AGI_RESULT_HANGUP,
 };
 
+static agi_command *find_command(char *cmds[], int exact);
+
 static void agi_debug_cli(int fd, char *fmt, ...)
 {
        char *stuff;
@@ -391,7 +393,7 @@ static void setup_env(struct ast_channel *chan, char *request, int fd, int enhan
 
        /* User information */
        fdprintf(fd, "agi_accountcode: %s\n", chan->accountcode ? chan->accountcode : "");
-    
+
        /* Send any parameters to the fastagi server that have been passed via the agi application */
        /* Agi application paramaters take the form of: AGI(/path/to/example/script|${EXTEN}) */
        for(count = 1; count < argc; count++)
@@ -669,7 +671,7 @@ static int handle_getoption(struct ast_channel *chan, AGI *agi, int argc, char *
                        res=0;
        }
 
-        fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
+       fdprintf(agi->fd, "200 result=%d endpos=%ld\n", res, sample_offset);
        return (res >= 0) ? RESULT_SUCCESS : RESULT_FAILURE;
 }
 
@@ -873,13 +875,13 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char
        int res = 0;
        int ms;
 
-        struct ast_dsp *sildet=NULL;         /* silence detector dsp */
-        int totalsilence = 0;
-        int dspsilence = 0;
-        int silence = 0;                /* amount of silence to allow */
-        int gotsilence = 0;             /* did we timeout for silence? */
-        char *silencestr=NULL;
-        int rfmt=0;
+       struct ast_dsp *sildet=NULL;         /* silence detector dsp */
+       int totalsilence = 0;
+       int dspsilence = 0;
+       int silence = 0;                /* amount of silence to allow */
+       int gotsilence = 0;             /* did we timeout for silence? */
+       char *silencestr=NULL;
+       int rfmt=0;
 
 
        /* XXX EAGI FIXME XXX */
@@ -902,27 +904,27 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char
                                silencestr++;
                                silencestr++;
                                if (silencestr)
-                                       silence = atoi(silencestr);
-                               if (silence > 0)
-                                       silence *= 1000;
-                       }
+                                       silence = atoi(silencestr);
+                               if (silence > 0)
+                                       silence *= 1000;
+                       }
                }
        }
 
-        if (silence > 0) {
-               rfmt = chan->readformat;
-                res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
-                if (res < 0) {
-                       ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
-                        return -1;
-                }
-                       sildet = ast_dsp_new();
-                if (!sildet) {
-                       ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
-                        return -1;
-                }
-                       ast_dsp_set_threshold(sildet, 256);
-       }
+       if (silence > 0) {
+               rfmt = chan->readformat;
+               res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+                       return -1;
+               }
+               sildet = ast_dsp_new();
+               if (!sildet) {
+                       ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+                       return -1;
+               }
+               ast_dsp_set_threshold(sildet, 256);
+       }
 
        /* backward compatibility, if no offset given, arg[6] would have been
         * caught below and taken to be a beep, else if it is a digit then it is a
@@ -997,20 +999,20 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char
                                 * is valid after a write, and it will then have our current
                                 * location */
                                sample_offset = ast_tellstream(fs);
-                                if (silence > 0) {
-                                       dspsilence = 0;
-                                        ast_dsp_silence(sildet, f, &dspsilence);
-                                        if (dspsilence) {
-                                                       totalsilence = dspsilence;
-                                        } else {
-                                               totalsilence = 0;
-                                        }
-                                        if (totalsilence > silence) {
-                                             /* Ended happily with silence */
-                                                gotsilence = 1;
-                                                break;
-                                        }
-                               }
+                               if (silence > 0) {
+                                       dspsilence = 0;
+                                       ast_dsp_silence(sildet, f, &dspsilence);
+                                       if (dspsilence) {
+                                               totalsilence = dspsilence;
+                                       } else {
+                                               totalsilence = 0;
+                                       }
+                                       if (totalsilence > silence) {
+                                               /* Ended happily with silence */
+                                               gotsilence = 1;
+                                               break;
+                                       }
+                               }
                                break;
                        case AST_FRAME_VIDEO:
                                ast_writestream(fs, f);
@@ -1021,7 +1023,7 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char
                        ast_frfree(f);
                        if (gotsilence)
                                break;
-               }
+               }
 
                if (gotsilence) {
                        ast_stream_rewind(fs, silence-1000);
@@ -1032,12 +1034,12 @@ static int handle_recordfile(struct ast_channel *chan, AGI *agi, int argc, char
                ast_closestream(fs);
        }
 
-        if (silence > 0) {
-                res = ast_set_read_format(chan, rfmt);
-                if (res)
-                        ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
-                ast_dsp_free(sildet);
-        }
+       if (silence > 0) {
+               res = ast_set_read_format(chan, rfmt);
+               if (res)
+                       ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
+                       ast_dsp_free(sildet);
+       }
        return RESULT_SUCCESS;
 }
 
@@ -1585,63 +1587,61 @@ static char usage_noop[] =
 "      Does nothing.\n";
 
 /*!
- * \brief AGI commands
- *
- * \todo XXX This array is not handled in a thread safe way.  There is no
- * synchronization done at all between the agi register and unregister functions
- * and the rest of this module which uses the entries here.
+ * \brief AGI commands list
  */
-static agi_command commands[MAX_COMMANDS] = {
-       { { "answer", NULL }, handle_answer, "Answer channel", usage_answer },
-       { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
-       { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
-       { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree },
-       { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
-       { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
-       { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
-       { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata },
-       { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull },
-       { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption },
-       { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
-       { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
-       { { "noop", NULL }, handle_noop, "Does nothing", usage_noop },
-       { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar },
-       { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext },
-       { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile },
-       { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha },
-       { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits },
-       { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber },
-       { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic },
-       { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate },
-       { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime },
-       { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime },
-       { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage },
-       { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
-       { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup },
-       { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
-       { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext },
-       { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension },
-       { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic },
-       { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority },
-       { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
-       { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile },
-       { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile },
-       { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode },
-       { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
-       { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
+static struct agi_command commands[MAX_COMMANDS] = {
+       { { "answer", NULL }, handle_answer, "Answer channel", usage_answer , 0 },
+       { { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus , 0 },
+       { { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel , 1 },
+       { { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree , 1 },
+       { { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget , 1 },
+       { { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput , 1 },
+       { { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec , 1 },
+       { { "get", "data", NULL }, handle_getdata, "Prompts for DTMF on a channel", usage_getdata , 0 },
+       { { "get", "full", "variable", NULL }, handle_getvariablefull, "Evaluates a channel expression", usage_getvariablefull , 1 },
+       { { "get", "option", NULL }, handle_getoption, "Stream file, prompt for DTMF, with timeout", usage_getoption , 0 },
+       { { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable , 1 },
+       { { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup , 0 },
+       { { "noop", NULL }, handle_noop, "Does nothing", usage_noop , 1 },
+       { { "receive", "char", NULL }, handle_recvchar, "Receives one character from channels supporting it", usage_recvchar , 0 },
+       { { "receive", "text", NULL }, handle_recvtext, "Receives text from channels supporting it", usage_recvtext , 0 },
+       { { "record", "file", NULL }, handle_recordfile, "Records to a given file", usage_recordfile , 0 },
+       { { "say", "alpha", NULL }, handle_sayalpha, "Says a given character string", usage_sayalpha , 0 },
+       { { "say", "digits", NULL }, handle_saydigits, "Says a given digit string", usage_saydigits , 0 },
+       { { "say", "number", NULL }, handle_saynumber, "Says a given number", usage_saynumber , 0 },
+       { { "say", "phonetic", NULL }, handle_sayphonetic, "Says a given character string with phonetics", usage_sayphonetic , 0 },
+       { { "say", "date", NULL }, handle_saydate, "Says a given date", usage_saydate , 0 },
+       { { "say", "time", NULL }, handle_saytime, "Says a given time", usage_saytime , 0 },
+       { { "say", "datetime", NULL }, handle_saydatetime, "Says a given time as specfied by the format given", usage_saydatetime , 0 },
+       { { "send", "image", NULL }, handle_sendimage, "Sends images to channels supporting it", usage_sendimage , 0 },
+       { { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext , 0 },
+       { { "set", "autohangup", NULL }, handle_autohangup, "Autohangup channel in some time", usage_autohangup , 0 },
+       { { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid , 0 },
+       { { "set", "context", NULL }, handle_setcontext, "Sets channel context", usage_setcontext , 0 },
+       { { "set", "extension", NULL }, handle_setextension, "Changes channel extension", usage_setextension , 0 },
+       { { "set", "music", NULL }, handle_setmusic, "Enable/Disable Music on hold generator", usage_setmusic , 0 },
+       { { "set", "priority", NULL }, handle_setpriority, "Set channel dialplan priority", usage_setpriority , 0 },
+       { { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable , 1 },
+       { { "stream", "file", NULL }, handle_streamfile, "Sends audio file on channel", usage_streamfile , 0 },
+       { { "control", "stream", "file", NULL }, handle_controlstreamfile, "Sends audio file on channel and allows the listner to control the stream", usage_controlstreamfile , 0 },
+       { { "tdd", "mode", NULL }, handle_tddmode, "Toggles TDD mode (for the deaf)", usage_tddmode , 0 },
+       { { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose , 1 },
+       { { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit , 0 },
 };
 
+AST_RWLIST_HEAD_STATIC(agi_commands, agi_command);
+
 static int help_workhorse(int fd, char *match[])
 {
        char fullcmd[80], matchstr[80];
-       int x;
        struct agi_command *e;
 
        if (match)
                ast_join(matchstr, sizeof(matchstr), match);
 
-       for (x = 0; x < sizeof(commands) / sizeof(commands[0]); x++) {
-               e = &commands[x]; 
+       ast_cli(fd, "%5.5s %20.20s   %s\n","Dead","Command","Description");
+       AST_RWLIST_RDLOCK(&agi_commands);
+       AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
                if (!e->cmda[0])
                        break;
                /* Hide commands that start with '_' */
@@ -1650,73 +1650,79 @@ static int help_workhorse(int fd, char *match[])
                ast_join(fullcmd, sizeof(fullcmd), e->cmda);
                if (match && strncasecmp(matchstr, fullcmd, strlen(matchstr)))
                        continue;
-               ast_cli(fd, "%20.20s   %s\n", fullcmd, e->summary);
+               ast_cli(fd, "%5.5s %20.20s   %s\n", e->dead ? "Yes" : "No" , fullcmd, e->summary);
        }
+       AST_RWLIST_UNLOCK(&agi_commands);
        return 0;
 }
 
 int ast_agi_register(agi_command *agi)
 {
-       int x;
-
-       for (x = 0; x < MAX_COMMANDS - 1; x++) {
-               if (commands[x].cmda[0] == agi->cmda[0]) {
-                       ast_log(LOG_WARNING, "Command already registered!\n");
-                       return -1;
-               }
-       }
-       for (x = 0; x < MAX_COMMANDS - 1; x++) {
-               if (!commands[x].cmda[0]) {
-                       commands[x] = *agi;
-                       return 0;
-               }
+       if (!find_command(agi->cmda,1)) {
+               AST_RWLIST_WRLOCK(&agi_commands);
+               AST_LIST_INSERT_TAIL(&agi_commands, agi, list);
+               AST_RWLIST_UNLOCK(&agi_commands);
+               return 1;
+       } else {
+               ast_log(LOG_WARNING, "Command already registered!\n");
+               return 0;
        }
-       ast_log(LOG_WARNING, "No more room for new commands!\n");
-       return -1;
 }
 
-void ast_agi_unregister(agi_command *agi)
+int ast_agi_unregister(agi_command *cmd)
 {
-       int x;
-       for (x = 0; x < MAX_COMMANDS - 1; x++) {
-               if (commands[x].cmda[0] == agi->cmda[0]) {
-                       memset(&commands[x], 0, sizeof(agi_command));
+       struct agi_command *e;
+       int unregistered = 0;
+
+       AST_RWLIST_WRLOCK(&agi_commands);
+       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&agi_commands, e, list) {
+               if (cmd == e) {
+                       AST_RWLIST_REMOVE_CURRENT(&agi_commands, list);
+                       unregistered=1;
+                       break;
                }
        }
+       AST_RWLIST_TRAVERSE_SAFE_END
+       AST_RWLIST_UNLOCK(&agi_commands);
+       if (!unregistered)
+               ast_log(LOG_WARNING, "Unable to unregister command!\n");
+       return unregistered;
 }
 
 static agi_command *find_command(char *cmds[], int exact)
 {
-       int x, y, match;
+       int y, match;
+       struct agi_command *e;
 
-       for (x = 0; x < sizeof(commands) / sizeof(commands[0]); x++) {
-               if (!commands[x].cmda[0])
+       AST_RWLIST_RDLOCK(&agi_commands);
+       AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
+               if (!e->cmda[0])
                        break;
-               /* start optimistic */
-               match = 1;
-               for (y = 0; match && cmds[y]; y++) {
-                       /* If there are no more words in the command (and we're looking for
-                          an exact match) or there is a difference between the two words,
-                          then this is not a match */
-                       if (!commands[x].cmda[y] && !exact)
-                               break;
-                       /* don't segfault if the next part of a command doesn't exist */
-                       if (!commands[x].cmda[y])
-                               return NULL;
-                       if (strcasecmp(commands[x].cmda[y], cmds[y]))
+                       /* start optimistic */
+                       match = 1;
+                       for (y = 0; match && cmds[y]; y++) {
+                               /* If there are no more words in the command (and we're looking for
+                                  an exact match) or there is a difference between the two words,
+                                  then this is not a match */
+                               if (!e->cmda[y] && !exact)
+                                       break;
+                               /* don't segfault if the next part of a command doesn't exist */
+                               if (!e->cmda[y])
+                                       return NULL;
+                               if (strcasecmp(e->cmda[y], cmds[y]))
+                                       match = 0;
+                       }
+                       /* If more words are needed to complete the command then this is not
+                          a candidate (unless we're looking for a really inexact answer  */
+                       if ((exact > -1) && e->cmda[y])
                                match = 0;
-               }
-               /* If more words are needed to complete the command then this is not
-                  a candidate (unless we're looking for a really inexact answer  */
-               if ((exact > -1) && commands[x].cmda[y])
-                       match = 0;
-               if (match)
-                       return &commands[x];
+                       if (match)
+                               return e;
        }
+       AST_RWLIST_UNLOCK(&agi_commands);
        return NULL;
 }
 
-
 static int parse_args(char *s, int *max, char *argv[])
 {
        int x = 0, quoted = 0, escaped = 0, whitespace = 1;
@@ -1780,14 +1786,14 @@ normal:
        return 0;
 }
 
-static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
+static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf, int dead)
 {
        char *argv[MAX_ARGS];
        int argc = MAX_ARGS, res;
        agi_command *c;
 
        parse_args(buf, &argc, argv);
-       if ((c = find_command(argv, 0))) {
+       if ((c = find_command(argv, 0)) && (!dead || (dead && c->dead))) {
                res = c->handler(chan, agi, argc, argv);
                switch(res) {
                case RESULT_SHOWUSAGE:
@@ -1804,6 +1810,8 @@ static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
                           appropriately */
                        return -1;
                }
+       } else if ((c = find_command(argv, 0))) {
+               fdprintf(agi->fd, "511 Command Not Permitted on a dead channel\n");
        } else {
                fdprintf(agi->fd, "510 Invalid or unknown command\n");
        }
@@ -1813,7 +1821,7 @@ static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
 static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead, int argc, char *argv[])
 {
        struct ast_channel *c;
-       int outfd, ms;
+       int outfd, ms, needhup = 0;
        enum agi_result returnstatus = AGI_RESULT_SUCCESS;
        struct ast_frame *f;
        char buf[2048];
@@ -1832,6 +1840,11 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
        setlinebuf(readf);
        setup_env(chan, request, agi->fd, (agi->audio > -1), argc, argv);
        for (;;) {
+               if (needhup) {
+                       needhup = 0;
+                       dead = 1;
+                       kill(pid, SIGHUP);
+               }
                ms = -1;
                c = ast_waitfor_nandfds(&chan, dead ? 0 : 1, &agi->ctrl, 1, NULL, &outfd, &ms);
                if (c) {
@@ -1841,7 +1854,8 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                        if (!f) {
                                ast_debug(1, "%s hungup\n", chan->name);
                                returnstatus = AGI_RESULT_HANGUP;
-                               break;
+                               needhup = 1;
+                               continue;
                        } else {
                                /* If it's voice, write it to the audio pipe */
                                if ((agi->audio > -1) && (f->frametype == AST_FRAME_VOICE)) {
@@ -1854,7 +1868,7 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                        retry = RETRY;
                        if (!fgets(buf, sizeof(buf), readf)) {
                                /* Program terminated */
-                               if (returnstatus)
+                               if (returnstatus && returnstatus != AST_PBX_KEEPALIVE)
                                        returnstatus = -1;
                                if (option_verbose > 2) 
                                        ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
@@ -1869,10 +1883,11 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                                buf[strlen(buf) - 1] = 0;
                        if (agidebug)
                                ast_verbose("AGI Rx << %s\n", buf);
-                       returnstatus |= agi_handle_command(chan, agi, buf);
+                       returnstatus |= agi_handle_command(chan, agi, buf, dead);
                        /* If the handle_command returns -1, we need to stop */
                        if ((returnstatus < 0) || (returnstatus == AST_PBX_KEEPALIVE)) {
-                               break;
+                               needhup = 1;
+                               continue;
                        }
                } else {
                        if (--retry <= 0) {
@@ -1904,13 +1919,14 @@ static int handle_showagi(int fd, int argc, char *argv[])
 
        if (argc > 2) {
                e = find_command(argv + 2, 1);
-               if (e) 
+               if (e) {
                        ast_cli(fd, e->usage);
-               else {
+                       ast_cli(fd, " Runs Dead : %s\n", e->dead ? "Yes" : "No");
+               } else {
                        if (find_command(argv + 2, -1)) {
-                               return help_workhorse(fd, argv + 1);
+                               return help_workhorse(fd, argv + 2);
                        } else {
-                               ast_join(fullcmd, sizeof(fullcmd), argv+1);
+                               ast_join(fullcmd, sizeof(fullcmd), argv + 2);
                                ast_cli(fd, "No such command '%s'.\n", fullcmd);
                        }
                }
@@ -1955,7 +1971,6 @@ static int handle_agidumphtml(int fd, int argc, char *argv[])
 {
        struct agi_command *e;
        char fullcmd[80];
-       int x;
        FILE *htmlfile;
 
        if ((argc < 3))
@@ -1972,10 +1987,10 @@ static int handle_agidumphtml(int fd, int argc, char *argv[])
 
        fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
 
-       for (x = 0; x < sizeof(commands) / sizeof(commands[0]); x++) {
+       AST_RWLIST_RDLOCK(&agi_commands);
+       AST_RWLIST_TRAVERSE(&agi_commands, e, list) {
                char *stringp, *tempstr;
-
-               e = &commands[x]; 
                if (!e->cmda[0])        /* end ? */
                        break;
                /* Hide commands that start with '_' */
@@ -2001,9 +2016,8 @@ static int handle_agidumphtml(int fd, int argc, char *argv[])
                }
                fprintf(htmlfile, "</TD></TR>\n");
                fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
-
        }
-
+       AST_RWLIST_UNLOCK(&agi_commands);
        fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
        fclose(htmlfile);
        ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
@@ -2026,8 +2040,9 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
                ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
                return -1;
        }
+       if (dead)
+               ast_log(LOG_NOTICE, "Hungup channel detected, running agi in dead mode.");
        ast_copy_string(buf, data, sizeof(buf));
-
        memset(&agi, 0, sizeof(agi));
        AST_STANDARD_APP_ARGS(args, tmp);
        args.argv[args.argc] = NULL;
@@ -2035,7 +2050,7 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
        u = ast_module_user_add(chan);
 #if 0
         /* Answer if need be */
-        if (chan->_state != AST_STATE_UP) {
+       if (chan->_state != AST_STATE_UP) {
                if (ast_answer(chan)) {
                        LOCAL_USER_REMOVE(u);
                        return -1;
@@ -2080,12 +2095,10 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
 
 static int agi_exec(struct ast_channel *chan, void *data)
 {
-       if (ast_check_hangup(chan)) {
-               ast_log(LOG_ERROR, "If you want to run AGI on hungup channels you should use DeadAGI!\n");
-               return 0;
-       }
-       
-       return agi_exec_full(chan, data, 0, 0);
+       if (!ast_check_hangup(chan))
+               return agi_exec_full(chan, data, 0, 0);
+       else
+               return agi_exec_full(chan, data, 0, 1);
 }
 
 static int eagi_exec(struct ast_channel *chan, void *data)
@@ -2112,11 +2125,8 @@ static int eagi_exec(struct ast_channel *chan, void *data)
 
 static int deadagi_exec(struct ast_channel *chan, void *data)
 {
-       if (!ast_check_hangup(chan)) {
-               ast_log(LOG_ERROR,"Running DeadAGI on a live channel is not permitted, please use AGI\n");
-               return 0;
-       }
-       return agi_exec_full(chan, data, 0, 1);
+       ast_log(LOG_WARNING, "DeadAGI has been deprecated, please use AGI in all cases!");
+       return agi_exec(chan, data);
 }
 
 static char showagi_help[] =
@@ -2150,7 +2160,16 @@ static struct ast_cli_entry cli_agi[] = {
 
 static int unload_module(void)
 {
+       struct agi_command *e;
+
        ast_cli_unregister_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
+       AST_RWLIST_WRLOCK(&agi_commands);
+       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&agi_commands, e, list) {
+               AST_RWLIST_REMOVE_CURRENT(&agi_commands, list);
+       }
+       AST_RWLIST_TRAVERSE_SAFE_END
+       AST_RWLIST_UNLOCK(&agi_commands);
+       AST_RWLIST_HEAD_DESTROY(&agi_commands);
        ast_unregister_application(eapp);
        ast_unregister_application(deadapp);
        return ast_unregister_application(app);
@@ -2158,13 +2177,18 @@ static int unload_module(void)
 
 static int load_module(void)
 {
+       int i;
+
        ast_cli_register_multiple(cli_agi, sizeof(cli_agi) / sizeof(struct ast_cli_entry));
+       for (i=0; i < (sizeof(commands) / sizeof(struct agi_command)); i++) {
+               ast_agi_register(&commands[i]);
+       }
        ast_register_application(deadapp, deadagi_exec, deadsynopsis, descrip);
        ast_register_application(eapp, eagi_exec, esynopsis, descrip);
        return ast_register_application(app, agi_exec, synopsis, descrip);
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Asterisk Gateway Interface (AGI)",
-                .load = load_module,
-                .unload = unload_module,
+               .load = load_module,
+               .unload = unload_module,
                );