Add the ability to retrieve the exit code of the forked AGI process. If there
authorRussell Bryant <russell@russellbryant.com>
Thu, 25 May 2006 18:31:19 +0000 (18:31 +0000)
committerRussell Bryant <russell@russellbryant.com>
Thu, 25 May 2006 18:31:19 +0000 (18:31 +0000)
is an error executing the AGI script, or the AGI script itself returns a
non-zero value, the AGISTATUS variable will now be set to FAILURE instead of
SUCCESS.

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

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

index 5c1807f..2ab474e 100644 (file)
@@ -608,21 +608,15 @@ static void null_sig_handler(int signal)
 }
 
 AST_MUTEX_DEFINE_STATIC(safe_system_lock);
+/*! Keep track of how many threads are currently trying to wait*() on
+ *  a child process */
 static unsigned int safe_system_level = 0;
 static void *safe_system_prev_handler;
 
-int ast_safe_system(const char *s)
+void ast_replace_sigchld(void)
 {
-       pid_t pid;
-       int x;
-       int res;
-       struct rusage rusage;
-       int status;
        unsigned int level;
 
-       /* keep track of how many ast_safe_system() functions
-          are running at this moment
-       */
        ast_mutex_lock(&safe_system_lock);
        level = safe_system_level++;
 
@@ -631,6 +625,31 @@ int ast_safe_system(const char *s)
                safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
 
        ast_mutex_unlock(&safe_system_lock);
+}
+
+void ast_unreplace_sigchld(void)
+{
+       unsigned int level;
+
+       ast_mutex_lock(&safe_system_lock);
+       level = --safe_system_level;
+
+       /* only restore the handler if we are the last one */
+       if (level == 0)
+               signal(SIGCHLD, safe_system_prev_handler);
+
+       ast_mutex_unlock(&safe_system_lock);
+}
+
+int ast_safe_system(const char *s)
+{
+       pid_t pid;
+       int x;
+       int res;
+       struct rusage rusage;
+       int status;
+
+       ast_replace_sigchld();
 
        pid = fork();
 
@@ -656,14 +675,7 @@ int ast_safe_system(const char *s)
                res = -1;
        }
 
-       ast_mutex_lock(&safe_system_lock);
-       level = --safe_system_level;
-
-       /* only restore the handler if we are the last one */
-       if (level == 0)
-               signal(SIGCHLD, safe_system_prev_handler);
-
-       ast_mutex_unlock(&safe_system_lock);
+       ast_unreplace_sigchld();
 
        return res;
 }
index af3aa31..4244c79 100644 (file)
@@ -120,6 +120,27 @@ int ast_app_messagecount(const char *context, const char *mailbox, const char *f
 int ast_safe_system(const char *s);
 
 /*!
+ * \brief Replace the SIGCHLD handler
+ *
+ * Normally, Asterisk has a SIGCHLD handler that is cleaning up all zombie
+ * processes from forking elsewhere in Asterisk.  However, if you want to
+ * wait*() on the process to retrieve information about it's exit status,
+ * then this signal handler needs to be temporaraly replaced.
+ *
+ * Code that executes this function *must* call ast_unreplace_sigchld()
+ * after it is finished doing the wait*().
+ */
+void ast_replace_sigchld(void);
+
+/*!
+ * \brief Restore the SIGCHLD handler
+ *
+ * This function is called after a call to ast_replace_sigchld.  It restores
+ * the SIGCHLD handler that cleans up any zombie processes.
+ */
+void ast_unreplace_sigchld(void);
+
+/*!
   \brief Send DTMF to a channel
 
   \param chan    The channel that will receive the DTMF frames
index 481edc5..7c7e5ce 100644 (file)
@@ -39,6 +39,7 @@
 #include <stdio.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/wait.h>
 
 #include "asterisk.h"
 
@@ -275,9 +276,11 @@ static enum agi_result launch_script(char *script, char *argv[], int *fds, int *
                        return AGI_RESULT_FAILURE;
                }
        }
+       ast_replace_sigchld();
        pid = fork();
        if (pid < 0) {
                ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
+               ast_unreplace_sigchld();
                return AGI_RESULT_FAILURE;
        }
        if (!pid) {
@@ -1781,7 +1784,7 @@ static int agi_handle_command(struct ast_channel *chan, AGI *agi, char *buf)
        return 0;
 }
 #define RETRY  3
-static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int dead)
+static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi, int pid, int *status, int dead)
 {
        struct ast_channel *c;
        int outfd;
@@ -1830,6 +1833,7 @@ static enum agi_result run_agi(struct ast_channel *chan, char *request, AGI *agi
                                        returnstatus = -1;
                                if (option_verbose > 2) 
                                        ast_verbose(VERBOSE_PREFIX_3 "AGI Script %s completed, returning %d\n", request, returnstatus);
+                               waitpid(pid, status, 0);
                                /* No need to kill the pid anymore, since they closed us */
                                pid = -1;
                                break;
@@ -1976,13 +1980,18 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
 #endif
        res = launch_script(argv[0], argv, fds, enhanced ? &efd : NULL, &pid);
        if (res == AGI_RESULT_SUCCESS) {
+               int status = 0;
                agi.fd = fds[1];
                agi.ctrl = fds[0];
                agi.audio = efd;
-               res = run_agi(chan, argv[0], &agi, pid, dead);
+               res = run_agi(chan, argv[0], &agi, pid, &status, dead);
+               /* If the fork'd process returns non-zero, set AGISTATUS to FAILURE */
+               if (res == AGI_RESULT_SUCCESS && status)
+                       res = AGI_RESULT_FAILURE;
                close(fds[1]);
                if (efd > -1)
                        close(efd);
+               ast_unreplace_sigchld();
        }
        ast_localuser_remove(me, u);