Standardized routines for forking processes (keeps all the specialized code in one...
authorTilghman Lesher <tilghman@meg.abyt.es>
Wed, 16 Apr 2008 22:57:54 +0000 (22:57 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Wed, 16 Apr 2008 22:57:54 +0000 (22:57 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@114188 65c4cc65-6c06-0410-ace0-fbb531ad65f3

13 files changed:
apps/app_externalivr.c
apps/app_festival.c
apps/app_ices.c
apps/app_mp3.c
apps/app_nbscat.c
apps/app_zapras.c
include/asterisk/app.h
include/asterisk/logger.h
main/app.c
main/asterisk.c
main/logger.c
res/res_agi.c
res/res_musiconhold.c

index b01253d..1012376 100644 (file)
@@ -319,14 +319,10 @@ static int app_exec(struct ast_channel *chan, void *data)
                .finishlist = AST_LIST_HEAD_INIT_VALUE,
        };
        struct ivr_localuser *u = &foo;
-       sigset_t fullset, oldset;
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(cmd)[32];
        );
 
-       sigfillset(&fullset);
-       pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
        u->abort_current_sound = 0;
        u->chan = chan;
 
@@ -405,7 +401,7 @@ static int app_exec(struct ast_channel *chan, void *data)
                        gen_active = 1;
                }
        
-               pid = fork();
+               pid = ast_safe_fork(0);
                if (pid < 0) {
                        ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
                        goto exit;
@@ -413,19 +409,13 @@ static int app_exec(struct ast_channel *chan, void *data)
        
                if (!pid) {
                        /* child process */
-                       int i;
-       
-                       signal(SIGPIPE, SIG_DFL);
-                       pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-       
                        if (ast_opt_high_priority)
                                ast_set_priority(0);
        
                        dup2(child_stdin[0], STDIN_FILENO);
                        dup2(child_stdout[1], STDOUT_FILENO);
                        dup2(child_stderr[1], STDERR_FILENO);
-                       for (i = STDERR_FILENO + 1; i < 1024; i++)
-                               close(i);
+                       ast_close_fds_above_n(STDERR_FILENO);
                        execv(args.cmd[0], args.cmd);
                        fprintf(stderr, "Failed to execute '%s': %s\n", args.cmd[0], strerror(errno));
                        _exit(1);
index e788291..096b34c 100644 (file)
@@ -117,30 +117,20 @@ static char *socket_receive_file_to_buff(int fd, int *size)
 static int send_waveform_to_fd(char *waveform, int length, int fd)
 {
        int res;
-       int x;
 #ifdef __PPC__ 
        char c;
 #endif
-       sigset_t fullset, oldset;
 
-       sigfillset(&fullset);
-       pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
-       res = fork();
+       res = ast_safe_fork(0);
        if (res < 0)
                ast_log(LOG_WARNING, "Fork failed\n");
        if (res) {
-               pthread_sigmask(SIG_SETMASK, &oldset, NULL);
                return res;
        }
-       for (x = 0; x < 256; x++) {
-               if (x != fd)
-                       close(x);
-       }
+       dup2(fd, 0);
+       ast_close_fds_above_n(0);
        if (ast_opt_high_priority)
                ast_set_priority(0);
-       signal(SIGPIPE, SIG_DFL);
-       pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
 #ifdef __PPC__  
        for (x = 0; x < length; x += 2) {
                c = *(waveform + x + 1);
index b2a4e9b..0a011b3 100644 (file)
@@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
 #include "asterisk/translate.h"
+#include "asterisk/app.h"
 
 #define ICES "/usr/bin/ices"
 #define LOCAL_ICES "/usr/local/bin/ices"
@@ -60,31 +61,18 @@ static char *descrip =
 static int icesencode(char *filename, int fd)
 {
        int res;
-       int x;
-       sigset_t fullset, oldset;
 
-       sigfillset(&fullset);
-       pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
-       res = fork();
+       res = ast_safe_fork(0);
        if (res < 0) 
                ast_log(LOG_WARNING, "Fork failed\n");
        if (res) {
-               pthread_sigmask(SIG_SETMASK, &oldset, NULL);
                return res;
        }
 
-       /* Stop ignoring PIPE */
-       signal(SIGPIPE, SIG_DFL);
-       pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-
        if (ast_opt_high_priority)
                ast_set_priority(0);
        dup2(fd, STDIN_FILENO);
-       for (x=STDERR_FILENO + 1;x<1024;x++) {
-               if ((x != STDIN_FILENO) && (x != STDOUT_FILENO))
-                       close(x);
-       }
+       ast_close_fds_above_n(STDERR_FILENO);
        /* Most commonly installed in /usr/local/bin */
        execl(ICES, "ices", filename, (char *)NULL);
        /* But many places has it in /usr/bin */
@@ -183,7 +171,7 @@ static int ices_exec(struct ast_channel *chan, void *data)
                }
        }
        close(fds[1]);
-       
+
        if (pid > -1)
                kill(pid, SIGKILL);
        if (!res && oreadformat)
index d2f2f5c..33bee62 100644 (file)
@@ -39,6 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
 #include "asterisk/translate.h"
+#include "asterisk/app.h"
 
 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
 #define MPG_123 "/usr/bin/mpg123"
@@ -56,29 +57,19 @@ static char *descrip =
 static int mp3play(char *filename, int fd)
 {
        int res;
-       int x;
-       sigset_t fullset, oldset;
 
-       sigfillset(&fullset);
-       pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
-       res = fork();
+       res = ast_safe_fork(0);
        if (res < 0) 
                ast_log(LOG_WARNING, "Fork failed\n");
        if (res) {
-               pthread_sigmask(SIG_SETMASK, &oldset, NULL);
                return res;
        }
        if (ast_opt_high_priority)
                ast_set_priority(0);
-       signal(SIGPIPE, SIG_DFL);
-       pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
 
        dup2(fd, STDOUT_FILENO);
-       for (x=STDERR_FILENO + 1;x<256;x++) {
-               if (x != STDOUT_FILENO)
-                       close(x);
-       }
+       ast_close_fds_above_n(STDERR_FILENO);
+
        /* Execute mpg123, but buffer if it's a net connection */
        if (!strncasecmp(filename, "http://", 7)) {
                /* Most commonly installed in /usr/local/bin */
@@ -96,7 +87,8 @@ static int mp3play(char *filename, int fd)
                /* As a last-ditch effort, try to use PATH */
            execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
        }
-       ast_log(LOG_WARNING, "Execute of mpg123 failed\n");
+       /* Can't use ast_log since FD's are closed */
+       fprintf(stderr, "Execute of mpg123 failed\n");
        _exit(0);
 }
 
index 4e72033..f471296 100644 (file)
@@ -41,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
 #include "asterisk/translate.h"
+#include "asterisk/app.h"
 
 #define LOCAL_NBSCAT "/usr/local/bin/nbscat8k"
 #define NBSCAT "/usr/bin/nbscat8k"
@@ -61,34 +62,25 @@ static char *descrip =
 static int NBScatplay(int fd)
 {
        int res;
-       int x;
-       sigset_t fullset, oldset;
 
-       sigfillset(&fullset);
-       pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
-
-       res = fork();
-       if (res < 0) 
+       res = ast_safe_fork(0);
+       if (res < 0) {
                ast_log(LOG_WARNING, "Fork failed\n");
+       }
+
        if (res) {
-               pthread_sigmask(SIG_SETMASK, &oldset, NULL);
                return res;
        }
-       signal(SIGPIPE, SIG_DFL);
-       pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
 
        if (ast_opt_high_priority)
                ast_set_priority(0);
 
        dup2(fd, STDOUT_FILENO);
-       for (x = STDERR_FILENO + 1; x < 1024; x++) {
-               if (x != STDOUT_FILENO)
-                       close(x);
-       }
+       ast_close_fds_above_n(STDERR_FILENO);
        /* Most commonly installed in /usr/local/bin */
        execl(NBSCAT, "nbscat8k", "-d", (char *)NULL);
        execl(LOCAL_NBSCAT, "nbscat8k", "-d", (char *)NULL);
-       ast_log(LOG_WARNING, "Execute of nbscat8k failed\n");
+       fprintf(stderr, "Execute of nbscat8k failed\n");
        _exit(0);
 }
 
index 231a9f2..6e079e3 100644 (file)
@@ -50,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
+#include "asterisk/app.h"
 
 static char *app = "ZapRAS";
 
@@ -69,30 +70,18 @@ static char *descrip =
 static pid_t spawn_ras(struct ast_channel *chan, char *args)
 {
        pid_t pid;
-       int x;  
        char *c;
 
        char *argv[PPP_MAX_ARGS];
        int argc = 0;
        char *stringp=NULL;
-       sigset_t fullset, oldset;
-
-       sigfillset(&fullset);
-       pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
 
        /* Start by forking */
-       pid = fork();
+       pid = ast_safe_fork(1);
        if (pid) {
-               pthread_sigmask(SIG_SETMASK, &oldset, NULL);
                return pid;
        }
 
-       /* Restore original signal handlers */
-       for (x=0;x<NSIG;x++)
-               signal(x, SIG_DFL);
-
-       pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
-
        /* Execute RAS on File handles */
        dup2(chan->fds[0], STDIN_FILENO);
 
@@ -101,8 +90,7 @@ static pid_t spawn_ras(struct ast_channel *chan, char *args)
                ast_set_priority(0);
 
        /* Close other file descriptors */
-       for (x=STDERR_FILENO + 1;x<1024;x++) 
-               close(x);
+       ast_close_fds_above_n(STDERR_FILENO);
 
        /* Reset all arguments */
        memset(argv, 0, sizeof(argv));
@@ -185,6 +173,7 @@ static void run_ras(struct ast_channel *chan, char *args)
                        break;
                }
        }
+       ast_safe_fork_cleanup();
 }
 
 static int zapras_exec(struct ast_channel *chan, void *data)
index 7c66d47..12d41d7 100644 (file)
@@ -480,6 +480,15 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char
 /*! \brief Decode an encoded control or extended ASCII character */
 int ast_get_encoded_char(const char *stream, char *result, size_t *consumed);
 
+/*! \brief Common routine for child processes, to close all fds prior to exec(2) */
+void ast_close_fds_above_n(int n);
+
+/*! \brief Common routine to safely fork without a chance of a signal handler firing badly in the child */
+int ast_safe_fork(int stop_reaper);
+
+/*! \brief Common routine to cleanup after fork'ed process is complete (if reaping was stopped) */
+void ast_safe_fork_cleanup(void);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index d76aa93..5ea81ac 100644 (file)
@@ -78,6 +78,9 @@ void ast_queue_log(const char *queuename, const char *callid, const char *agent,
 void ast_verbose(const char *fmt, ...)
        __attribute__ ((format (printf, 1, 2)));
 
+void ast_child_verbose(int level, const char *fmt, ...)
+       __attribute__ ((format (printf, 2, 3)));
+
 int ast_register_verbose(void (*verboser)(const char *string));
 int ast_unregister_verbose(void (*verboser)(const char *string));
 
index ee72d10..05cdf17 100644 (file)
@@ -32,6 +32,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #endif
 #include <regex.h>
 #include <sys/file.h> /* added this to allow to compile, sorry! */
+#include <signal.h>
 
 #include "asterisk/paths.h"    /* use ast_config_AST_DATA_DIR */
 #include "asterisk/channel.h"
@@ -1759,3 +1760,65 @@ int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
        return 0;
 }
 
+void ast_close_fds_above_n(int n)
+{
+       int x, null;
+       null = open("/dev/null", O_RDONLY);
+       for (x = n + 1; x <= (null >= 8192 ? null : 8192); x++) {
+               if (x != null) {
+                       /* Side effect of dup2 is that it closes any existing fd without error.
+                        * This prevents valgrind and other debugging tools from sending up
+                        * false error reports. */
+                       dup2(null, x);
+                       close(x);
+               }
+       }
+       close(null);
+}
+
+int ast_safe_fork(int stop_reaper)
+{
+       sigset_t signal_set, old_set;
+       int pid;
+
+       /* Don't let the default signal handler for children reap our status */
+       if (stop_reaper) {
+               ast_replace_sigchld();
+       }
+
+       sigfillset(&signal_set);
+       pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
+
+       pid = fork();
+
+       if (pid != 0) {
+               /* Fork failed or parent */
+               pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+               return pid;
+       } else {
+               /* Child */
+
+               /* Before we unblock our signals, return our trapped signals back to the defaults */
+               signal(SIGHUP, SIG_DFL);
+               signal(SIGCHLD, SIG_DFL);
+               signal(SIGINT, SIG_DFL);
+               signal(SIGURG, SIG_DFL);
+               signal(SIGTERM, SIG_DFL);
+               signal(SIGPIPE, SIG_DFL);
+               signal(SIGXFSZ, SIG_DFL);
+
+               /* unblock important signal handlers */
+               if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) {
+                       ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno));
+                       _exit(1);
+               }
+
+               return pid;
+       }
+}
+
+void ast_safe_fork_cleanup(void)
+{
+       ast_unreplace_sigchld();
+}
+
index f08a4d0..99df3ef 100644 (file)
@@ -848,9 +848,6 @@ void ast_unreplace_sigchld(void)
 int ast_safe_system(const char *s)
 {
        pid_t pid;
-#ifdef HAVE_WORKING_FORK
-       int x;
-#endif
        int res;
        struct rusage rusage;
        int status;
@@ -869,8 +866,7 @@ int ast_safe_system(const char *s)
                if (ast_opt_high_priority)
                        ast_set_priority(0);
                /* Close file descriptors and launch system command */
-               for (x = STDERR_FILENO + 1; x < 4096; x++)
-                       close(x);
+               ast_close_fds_above_n(STDERR_FILENO);
 #endif
                execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
                _exit(1);
index b7c517f..dce2097 100644 (file)
@@ -413,6 +413,55 @@ static void init_logger_chain(int reload, int locked)
        ast_config_destroy(cfg);
 }
 
+void ast_child_verbose(int level, const char *fmt, ...)
+{
+       char *msg = NULL, *emsg = NULL, *sptr, *eptr;
+       va_list ap, aq;
+       int size;
+
+       /* Don't bother, if the level isn't that high */
+       if (option_verbose < level) {
+               return;
+       }
+
+       va_start(ap, fmt);
+       va_copy(aq, ap);
+       if ((size = vsnprintf(msg, 0, fmt, ap)) < 0) {
+               va_end(ap);
+               va_end(aq);
+               return;
+       }
+       va_end(ap);
+
+       if (!(msg = ast_malloc(size + 1))) {
+               va_end(aq);
+               return;
+       }
+
+       vsnprintf(msg, size + 1, fmt, aq);
+       va_end(aq);
+
+       if (!(emsg = ast_malloc(size * 2 + 1))) {
+               ast_free(msg);
+               return;
+       }
+
+       for (sptr = msg, eptr = emsg; ; sptr++) {
+               if (*sptr == '"') {
+                       *eptr++ = '\\';
+               }
+               *eptr++ = *sptr;
+               if (*sptr == '\0') {
+                       break;
+               }
+       }
+       ast_free(msg);
+
+       fprintf(stdout, "verbose \"%s\" %d\n", emsg, level);
+       fflush(stdout);
+       ast_free(emsg);
+}
+
 void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
 {
        va_list ap;
index 49aa2cf..caa015a 100644 (file)
@@ -603,8 +603,7 @@ static enum agi_result launch_netscript(char *agiurl, char *argv[], int *fds, in
 static enum agi_result launch_script(struct ast_channel *chan, char *script, char *argv[], int *fds, int *efd, int *opid)
 {
        char tmp[256];
-       int pid, toast[2], fromast[2], audio[2], x, res;
-       sigset_t signal_set, old_set;
+       int pid, toast[2], fromast[2], audio[2], res;
        struct stat st;
 
        if (!strncasecmp(script, "agi://", 6))
@@ -657,12 +656,8 @@ static enum agi_result launch_script(struct ast_channel *chan, char *script, cha
                }
        }
 
-       /* Block SIGHUP during the fork - prevents a race */
-       sigfillset(&signal_set);
-       pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
-       if ((pid = fork()) < 0) {
+       if ((pid = ast_safe_fork(1)) < 0) {
                ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
-               pthread_sigmask(SIG_SETMASK, &old_set, NULL);
                return AGI_RESULT_FAILURE;
        }
        if (!pid) {
@@ -690,34 +685,17 @@ static enum agi_result launch_script(struct ast_channel *chan, char *script, cha
                else
                        close(STDERR_FILENO + 1);
 
-               /* Before we unblock our signals, return our trapped signals back to the defaults */
-               signal(SIGHUP, SIG_DFL);
-               signal(SIGCHLD, SIG_DFL);
-               signal(SIGINT, SIG_DFL);
-               signal(SIGURG, SIG_DFL);
-               signal(SIGTERM, SIG_DFL);
-               signal(SIGPIPE, SIG_DFL);
-               signal(SIGXFSZ, SIG_DFL);
-
-               /* unblock important signal handlers */
-               if (pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL)) {
-                       ast_log(LOG_WARNING, "unable to unblock signals for AGI script: %s\n", strerror(errno));
-                       _exit(1);
-               }
-
                /* Close everything but stdin/out/error */
-               for (x = STDERR_FILENO + 2; x < 1024; x++) 
-                       close(x);
+               ast_close_fds_above_n(STDERR_FILENO + 1);
 
                /* Execute script */
                /* XXX argv should be deprecated in favor of passing agi_argX paramaters */
                execv(script, argv);
                /* Can't use ast_log since FD's are closed */
-               fprintf(stdout, "verbose \"Failed to execute '%s': %s\" 2\n", script, strerror(errno));
+               ast_child_verbose(1, "Failed to execute '%s': %s", script, strerror(errno));
                fflush(stdout);
                _exit(1);
        }
-       pthread_sigmask(SIG_SETMASK, &old_set, NULL);
        ast_verb(3, "Launched AGI Script %s\n", script);
        fds[0] = toast[0];
        fds[1] = fromast[1];
@@ -2908,8 +2886,8 @@ static int agi_exec_full(struct ast_channel *chan, void *data, int enhanced, int
                        close(fds[1]);
                if (efd > -1)
                        close(efd);
-               ast_unreplace_sigchld();
        } 
+       ast_safe_fork_cleanup();
 
        switch (res) {
        case AGI_RESULT_SUCCESS:
index e7044ef..e456b3c 100644 (file)
@@ -404,7 +404,6 @@ static int spawn_mp3(struct mohclass *class)
        int argc = 0;
        DIR *dir = NULL;
        struct dirent *de;
-       sigset_t signal_set, old_set;
 
        
        if (!strcasecmp(class->dir, "nodir")) {
@@ -490,12 +489,8 @@ static int spawn_mp3(struct mohclass *class)
                sleep(respawn_time - (time(NULL) - class->start));
        }
 
-       /* Block signals during the fork() */
-       sigfillset(&signal_set);
-       pthread_sigmask(SIG_BLOCK, &signal_set, &old_set);
-
        time(&class->start);
-       class->pid = fork();
+       class->pid = ast_safe_fork(0);
        if (class->pid < 0) {
                close(fds[0]);
                close(fds[1]);
@@ -503,24 +498,16 @@ static int spawn_mp3(struct mohclass *class)
                return -1;
        }
        if (!class->pid) {
-               int x;
-
                if (ast_opt_high_priority)
                        ast_set_priority(0);
 
-               /* Reset ignored signals back to default */
-               signal(SIGPIPE, SIG_DFL);
-               pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
-
                close(fds[0]);
                /* Stdout goes to pipe */
                dup2(fds[1], STDOUT_FILENO);
-               /* Close unused file descriptors */
-               for (x=3;x<8192;x++) {
-                       if (-1 != fcntl(x, F_GETFL)) {
-                               close(x);
-                       }
-               }
+
+               /* Close everything else */
+               ast_close_fds_above_n(STDERR_FILENO);
+
                /* Child */
                chdir(class->dir);
                if (ast_test_flag(class, MOH_CUSTOM)) {
@@ -533,12 +520,12 @@ static int spawn_mp3(struct mohclass *class)
                        /* Check PATH as a last-ditch effort */
                        execvp("mpg123", argv);
                }
-               ast_log(LOG_WARNING, "Exec failed: %s\n", strerror(errno));
+               /* Can't use logger, since log FDs are closed */
+               fprintf(stderr, "MOH: exec failed: %s\n", strerror(errno));
                close(fds[1]);
                _exit(1);
        } else {
                /* Parent */
-               pthread_sigmask(SIG_SETMASK, &old_set, NULL);
                close(fds[1]);
        }
        return fds[0];