Merged revisions 127663 via svnmerge from
[asterisk/asterisk.git] / main / app.c
index f21a40e..0ea5cef 100644 (file)
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <signal.h>
-#include <errno.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #include <sys/stat.h>
-#include <sys/file.h>
+#endif
 #include <regex.h>
 #include <regex.h>
-#include <fcntl.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"
 #include "asterisk/pbx.h"
 #include "asterisk/file.h"
 #include "asterisk/app.h"
 #include "asterisk/dsp.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
 #include "asterisk/file.h"
 #include "asterisk/app.h"
 #include "asterisk/dsp.h"
-#include "asterisk/logger.h"
-#include "asterisk/options.h"
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
 #include "asterisk/indications.h"
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
 #include "asterisk/indications.h"
@@ -80,14 +72,14 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
                maxlen = size;
        
        if (!timeout && chan->pbx)
                maxlen = size;
        
        if (!timeout && chan->pbx)
-               timeout = chan->pbx->dtimeout;
+               timeout = chan->pbx->dtimeoutms / 1000.0;
        else if (!timeout)
                timeout = 5;
 
        if ((ts = ast_get_indication_tone(chan->zone, "dial")) && ts->data[0])
                res = ast_playtones_start(chan, 0, ts->data, 0);
        else 
        else if (!timeout)
                timeout = 5;
 
        if ((ts = ast_get_indication_tone(chan->zone, "dial")) && ts->data[0])
                res = ast_playtones_start(chan, 0, ts->data, 0);
        else 
-               ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
+               ast_log(LOG_NOTICE, "Huh....? no dial for indications?\n");
        
        for (x = strlen(collect); x < maxlen; ) {
                res = ast_waitfordigit(chan, timeout);
        
        for (x = strlen(collect); x < maxlen; ) {
                res = ast_waitfordigit(chan, timeout);
@@ -127,7 +119,7 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
                s[0] = '\0';
 
        if (!prompt)
                s[0] = '\0';
 
        if (!prompt)
-               prompt="";
+               prompt = "";
 
        filename = ast_strdupa(prompt);
        while ((front = strsep(&filename, "&"))) {
 
        filename = ast_strdupa(prompt);
        while ((front = strsep(&filename, "&"))) {
@@ -138,8 +130,8 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
                }
                if (ast_strlen_zero(filename)) {
                        /* set timeouts for the last prompt */
                }
                if (ast_strlen_zero(filename)) {
                        /* set timeouts for the last prompt */
-                       fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
-                       to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+                       fto = c->pbx ? c->pbx->rtimeoutms : 6000;
+                       to = c->pbx ? c->pbx->dtimeoutms : 2000;
 
                        if (timeout > 0) 
                                fto = to = timeout;
 
                        if (timeout > 0) 
                                fto = to = timeout;
@@ -150,7 +142,7 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
                           get rid of the long timeout between 
                           prompts, and make it 50ms */
                        fto = 50;
                           get rid of the long timeout between 
                           prompts, and make it 50ms */
                        fto = 50;
-                       to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+                       to = c->pbx ? c->pbx->dtimeoutms : 2000;
                }
                res = ast_readstring(c, s, maxlen, to, fto, "#");
                if (!ast_strlen_zero(s))
                }
                res = ast_readstring(c, s, maxlen, to, fto, "#");
                if (!ast_strlen_zero(s))
@@ -185,22 +177,30 @@ int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxle
 
 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
 static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
 
 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
 static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
+static int (*ast_inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs) = NULL;
+static int (*ast_sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context) = NULL;
 static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
 
 void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
                              int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
 static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
 
 void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
                              int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
-                             int (*messagecount_func)(const char *context, const char *mailbox, const char *folder))
+                             int (*inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs),
+                             int (*messagecount_func)(const char *context, const char *mailbox, const char *folder),
+                             int (*sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context))
 {
        ast_has_voicemail_func = has_voicemail_func;
        ast_inboxcount_func = inboxcount_func;
 {
        ast_has_voicemail_func = has_voicemail_func;
        ast_inboxcount_func = inboxcount_func;
+       ast_inboxcount2_func = inboxcount2_func;
        ast_messagecount_func = messagecount_func;
        ast_messagecount_func = messagecount_func;
+       ast_sayname_func = sayname_func;
 }
 
 void ast_uninstall_vm_functions(void)
 {
        ast_has_voicemail_func = NULL;
        ast_inboxcount_func = NULL;
 }
 
 void ast_uninstall_vm_functions(void)
 {
        ast_has_voicemail_func = NULL;
        ast_inboxcount_func = NULL;
+       ast_inboxcount2_func = NULL;
        ast_messagecount_func = NULL;
        ast_messagecount_func = NULL;
+       ast_sayname_func = NULL;
 }
 
 int ast_app_has_voicemail(const char *mailbox, const char *folder)
 }
 
 int ast_app_has_voicemail(const char *mailbox, const char *folder)
@@ -220,12 +220,39 @@ int ast_app_has_voicemail(const char *mailbox, const char *folder)
 int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 {
        static int warned = 0;
 int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 {
        static int warned = 0;
-       if (newmsgs)
+       if (newmsgs) {
                *newmsgs = 0;
                *newmsgs = 0;
-       if (oldmsgs)
+       }
+       if (oldmsgs) {
                *oldmsgs = 0;
                *oldmsgs = 0;
-       if (ast_inboxcount_func)
+       }
+       if (ast_inboxcount_func) {
                return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
                return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
+       }
+
+       if (!warned) {
+               warned++;
+               ast_verb(3, "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
+       }
+
+       return 0;
+}
+
+int ast_app_inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+{
+       static int warned = 0;
+       if (newmsgs) {
+               *newmsgs = 0;
+       }
+       if (oldmsgs) {
+               *oldmsgs = 0;
+       }
+       if (urgentmsgs) {
+               *urgentmsgs = 0;
+       }
+       if (ast_inboxcount_func) {
+               return ast_inboxcount2_func(mailbox, urgentmsgs, newmsgs, oldmsgs);
+       }
 
        if (!warned) {
                warned++;
 
        if (!warned) {
                warned++;
@@ -235,6 +262,13 @@ int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
        return 0;
 }
 
        return 0;
 }
 
+int ast_app_sayname(struct ast_channel *chan, const char *mailbox, const char *context)
+{
+       if (ast_sayname_func)
+               return ast_sayname_func(chan, mailbox, context);
+       return -1;
+}
+
 int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
 {
        static int warned = 0;
 int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
 {
        static int warned = 0;
@@ -253,6 +287,7 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
 {
        const char *ptr;
        int res = 0;
 {
        const char *ptr;
        int res = 0;
+       struct ast_silence_generator *silgen = NULL;
 
        if (!between)
                between = 100;
 
        if (!between)
                between = 100;
@@ -267,6 +302,10 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
        if (res < 0)
                return res;
 
        if (res < 0)
                return res;
 
+       if (ast_opt_transmit_silence) {
+               silgen = ast_channel_start_silence_generator(chan);
+       }
+
        for (ptr = digits; *ptr; ptr++) {
                if (*ptr == 'w') {
                        /* 'w' -- wait half a second */
        for (ptr = digits; *ptr; ptr++) {
                if (*ptr == 'w') {
                        /* 'w' -- wait half a second */
@@ -283,7 +322,7 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
                        if ((res = ast_safe_sleep(chan, between)))
                                break;
                } else
                        if ((res = ast_safe_sleep(chan, between)))
                                break;
                } else
-                       ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
+                       ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n", *ptr);
        }
 
        if (peer) {
        }
 
        if (peer) {
@@ -293,6 +332,10 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch
                        res = -1;
        }
 
                        res = -1;
        }
 
+       if (silgen) {
+               ast_channel_stop_silence_generator(chan, silgen);
+       }
+
        return res;
 }
 
        return res;
 }
 
@@ -321,16 +364,16 @@ static int linear_generator(struct ast_channel *chan, void *data, int len, int s
        short buf[2048 + AST_FRIENDLY_OFFSET / 2];
        struct linear_state *ls = data;
        struct ast_frame f = {
        short buf[2048 + AST_FRIENDLY_OFFSET / 2];
        struct linear_state *ls = data;
        struct ast_frame f = {
-                .frametype = AST_FRAME_VOICE,
-                .subclass = AST_FORMAT_SLINEAR,
-                .data = buf + AST_FRIENDLY_OFFSET / 2,
+               .frametype = AST_FRAME_VOICE,
+               .subclass = AST_FORMAT_SLINEAR,
+               .data.ptr = buf + AST_FRIENDLY_OFFSET / 2,
                .offset = AST_FRIENDLY_OFFSET,
                .offset = AST_FRIENDLY_OFFSET,
-        };
+       };
        int res;
 
        len = samples * 2;
        if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
        int res;
 
        len = samples * 2;
        if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
-               ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
+               ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" , len);
                len = sizeof(buf) - AST_FRIENDLY_OFFSET;
        }
        res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
                len = sizeof(buf) - AST_FRIENDLY_OFFSET;
        }
        res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
@@ -439,7 +482,7 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
                res = ast_answer(chan);
 
        if (file) {
                res = ast_answer(chan);
 
        if (file) {
-               if ((end = strchr(file,':'))) {
+               if ((end = strchr(file, ':'))) {
                        if (!strcasecmp(end, ":end")) {
                                *end = '\0';
                                end++;
                        if (!strcasecmp(end, ":end")) {
                                *end = '\0';
                                end++;
@@ -751,8 +794,6 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                } else {
                        ast_frfree(f);
                }
                } else {
                        ast_frfree(f);
                }
-               if (end == start)
-                       end = time(NULL);
        } else {
                ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
        }
        } else {
                ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
        }
@@ -761,14 +802,29 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                if (silgen)
                        ast_channel_stop_silence_generator(chan, silgen);
        }
                if (silgen)
                        ast_channel_stop_silence_generator(chan, silgen);
        }
-       *duration = end - start;
+
+       /*!\note
+        * Instead of asking how much time passed (end - start), calculate the number
+        * of seconds of audio which actually went into the file.  This fixes a
+        * problem where audio is stopped up on the network and never gets to us.
+        *
+        * Note that we still want to use the number of seconds passed for the max
+        * message, otherwise we could get a situation where this stream is never
+        * closed (which would create a resource leak).
+        */
+       *duration = others[0] ? ast_tellstream(others[0]) / 8000 : 0;
 
        if (!prepend) {
                for (x = 0; x < fmtcnt; x++) {
                        if (!others[x])
                                break;
 
        if (!prepend) {
                for (x = 0; x < fmtcnt; x++) {
                        if (!others[x])
                                break;
-                       if (res > 0)
-                               ast_stream_rewind(others[x], totalsilence ? totalsilence - 200 : 200);
+                       /*!\note
+                        * If we ended with silence, trim all but the first 200ms of silence
+                        * off the recording.  However, if we ended with '#', we don't want
+                        * to trim ANY part of the recording.
+                        */
+                       if (res > 0 && totalsilence)
+                               ast_stream_rewind(others[x], totalsilence - 200);
                        ast_truncstream(others[x]);
                        ast_closestream(others[x]);
                }
                        ast_truncstream(others[x]);
                        ast_closestream(others[x]);
                }
@@ -783,7 +839,9 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                        realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
                        if (!others[x] || !realfiles[x])
                                break;
                        realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
                        if (!others[x] || !realfiles[x])
                                break;
-                       ast_stream_rewind(others[x], totalsilence ? totalsilence - 200 : 200);
+                       /*!\note Same logic as above. */
+                       if (totalsilence)
+                               ast_stream_rewind(others[x], totalsilence - 200);
                        ast_truncstream(others[x]);
                        /* add the original file too */
                        while ((fr = ast_readframe(realfiles[x]))) {
                        ast_truncstream(others[x]);
                        /* add the original file too */
                        while ((fr = ast_readframe(realfiles[x]))) {
@@ -968,7 +1026,7 @@ int ast_app_group_discard(struct ast_channel *chan)
                        ast_free(gi);
                }
        }
                        ast_free(gi);
                }
        }
-        AST_RWLIST_TRAVERSE_SAFE_END;
+       AST_RWLIST_TRAVERSE_SAFE_END;
        AST_RWLIST_UNLOCK(&groups);
        
        return 0;
        AST_RWLIST_UNLOCK(&groups);
        
        return 0;
@@ -997,7 +1055,7 @@ int ast_app_group_list_unlock(void)
 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
 {
        int argc;
 unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
 {
        int argc;
-       char *scan;
+       char *scan, *wasdelim = NULL;
        int paren = 0, quote = 0;
 
        if (!buf || !array || !arraylen)
        int paren = 0, quote = 0;
 
        if (!buf || !array || !arraylen)
@@ -1024,14 +1082,18 @@ unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arra
                                /* Literal character, don't parse */
                                memmove(scan, scan + 1, strlen(scan));
                        } else if ((*scan == delim) && !paren && !quote) {
                                /* Literal character, don't parse */
                                memmove(scan, scan + 1, strlen(scan));
                        } else if ((*scan == delim) && !paren && !quote) {
+                               wasdelim = scan;
                                *scan++ = '\0';
                                break;
                        }
                }
        }
 
                                *scan++ = '\0';
                                break;
                        }
                }
        }
 
-       if (*scan)
+       /* If the last character in the original string was the delimiter, then
+        * there is one additional argument. */
+       if (*scan || (scan > buf && (scan - 1) == wasdelim)) {
                array[argc++] = scan;
                array[argc++] = scan;
+       }
 
        return argc;
 }
 
        return argc;
 }
@@ -1155,7 +1217,7 @@ static enum AST_LOCK_RESULT ast_lock_path_flock(const char *path)
        time(&start);
        while ((
                #ifdef SOLARIS
        time(&start);
        while ((
                #ifdef SOLARIS
-               (res = fcntl(pl->fd, F_SETLK, fcntl(pl->fd,F_GETFL)|O_NONBLOCK)) < 0) &&
+               (res = fcntl(pl->fd, F_SETLK, fcntl(pl->fd, F_GETFL) | O_NONBLOCK)) < 0) &&
                #else
                (res = flock(pl->fd, LOCK_EX | LOCK_NB)) < 0) &&
                #endif
                #else
                (res = flock(pl->fd, LOCK_EX | LOCK_NB)) < 0) &&
                #endif
@@ -1262,7 +1324,7 @@ int ast_unlock_path(const char *path)
 
 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path) 
 {
 
 int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path) 
 {
-       int silencethreshold = 128; 
+       int silencethreshold; 
        int maxsilence = 0;
        int res = 0;
        int cmd = 0;
        int maxsilence = 0;
        int res = 0;
        int cmd = 0;
@@ -1280,6 +1342,8 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char
 
        cmd = '3';       /* Want to start by recording */
 
 
        cmd = '3';       /* Want to start by recording */
 
+       silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
+
        while ((cmd >= 0) && (cmd != 't')) {
                switch (cmd) {
                case '1':
        while ((cmd >= 0) && (cmd != 't')) {
                switch (cmd) {
                case '1':
@@ -1402,7 +1466,7 @@ static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option,
                        res = 0;
                return res;
        case AST_ACTION_WAITOPTION:
                        res = 0;
                return res;
        case AST_ACTION_WAITOPTION:
-               res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
+               res = ast_waitfordigit(chan, chan->pbx ? chan->pbx->rtimeoutms : 10000);
                if (!res)
                        return 't';
                return res;
                if (!res)
                        return 't';
                return res;
@@ -1452,10 +1516,10 @@ static int option_matchmore(struct ast_ivr_menu *menu, char *option)
 
 static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
 {
 
 static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
 {
-       int res=0;
+       int res = 0;
        int ms;
        while (option_matchmore(menu, exten)) {
        int ms;
        while (option_matchmore(menu, exten)) {
-               ms = chan->pbx ? chan->pbx->dtimeout : 5000;
+               ms = chan->pbx ? chan->pbx->dtimeoutms : 5000;
                if (strlen(exten) >= maxexten - 1) 
                        break;
                res = ast_waitfordigit(chan, ms);
                if (strlen(exten) >= maxexten - 1) 
                        break;
                res = ast_waitfordigit(chan, ms);
@@ -1470,7 +1534,7 @@ static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, c
 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
 {
        /* Execute an IVR menu structure */
 static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
 {
        /* Execute an IVR menu structure */
-       int res=0;
+       int res = 0;
        int pos = 0;
        int retries = 0;
        char exten[AST_MAX_EXTENSION] = "s";
        int pos = 0;
        int retries = 0;
        char exten[AST_MAX_EXTENSION] = "s";
@@ -1602,7 +1666,6 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
        s = optstr;
        while (*s) {
                curarg = *s++ & 0x7f;   /* the array (in app.h) has 128 entries */
        s = optstr;
        while (*s) {
                curarg = *s++ & 0x7f;   /* the array (in app.h) has 128 entries */
-               ast_set_flag(flags, options[curarg].flag);
                argloc = options[curarg].arg_index;
                if (*s == '(') {
                        /* Has argument */
                argloc = options[curarg].arg_index;
                if (*s == '(') {
                        /* Has argument */
@@ -1617,8 +1680,9 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
                                break;
                        }
                } else if (argloc) {
                                break;
                        }
                } else if (argloc) {
-                       args[argloc - 1] = NULL;
+                       args[argloc - 1] = "";
                }
                }
+               ast_set_flag(flags, options[curarg].flag);
        }
 
        return res;
        }
 
        return res;
@@ -1664,6 +1728,17 @@ int ast_app_parse_options64(const struct ast_app_option *options, struct ast_fla
        return res;
 }
 
        return res;
 }
 
+void ast_app_options2str64(const struct ast_app_option *options, struct ast_flags64 *flags, char *buf, size_t len)
+{
+       unsigned int i, found = 0;
+       for (i = 32; i < 128 && found < len; i++) {
+               if (ast_test_flag64(flags, options[i].flag)) {
+                       buf[found++] = i;
+               }
+       }
+       buf[found] = '\0';
+}
+
 int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
 {
        int i;
 int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
 {
        int i;
@@ -1731,3 +1806,65 @@ int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
        return 0;
 }
 
        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: %s\n", strerror(errno));
+                       _exit(1);
+               }
+
+               return pid;
+       }
+}
+
+void ast_safe_fork_cleanup(void)
+{
+       ast_unreplace_sigchld();
+}
+