Replace direct access to channel name with accessor functions
[asterisk/asterisk.git] / main / app.c
index 1c17984..ca25964 100644 (file)
@@ -30,12 +30,15 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
-#include <regex.h>
-#include <sys/file.h> /* added this to allow to compile, sorry! */
-#include <signal.h>
-#include <sys/time.h>       /* for getrlimit(2) */
-#include <sys/resource.h>   /* for getrlimit(2) */
+#include <regex.h>          /* for regcomp(3) */
+#include <sys/file.h>       /* for flock(2) */
+#include <signal.h>         /* for pthread_sigmask(3) */
 #include <stdlib.h>         /* for closefrom(3) */
+#include <sys/types.h>
+#include <sys/wait.h>       /* for waitpid(2) */
+#ifndef HAVE_CLOSEFROM
+#include <dirent.h>         /* for opendir(3)   */
+#endif
 #ifdef HAVE_CAP
 #include <sys/capability.h>
 #endif /* HAVE_CAP */
@@ -51,9 +54,45 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/indications.h"
 #include "asterisk/linkedlists.h"
 #include "asterisk/threadstorage.h"
+#include "asterisk/test.h"
 
 AST_THREADSTORAGE_PUBLIC(ast_str_thread_global_buf);
 
+static pthread_t shaun_of_the_dead_thread = AST_PTHREADT_NULL;
+
+struct zombie {
+       pid_t pid;
+       AST_LIST_ENTRY(zombie) list;
+};
+
+static AST_LIST_HEAD_STATIC(zombies, zombie);
+
+static void *shaun_of_the_dead(void *data)
+{
+       struct zombie *cur;
+       int status;
+       for (;;) {
+               if (!AST_LIST_EMPTY(&zombies)) {
+                       /* Don't allow cancellation while we have a lock. */
+                       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+                       AST_LIST_LOCK(&zombies);
+                       AST_LIST_TRAVERSE_SAFE_BEGIN(&zombies, cur, list) {
+                               if (waitpid(cur->pid, &status, WNOHANG) != 0) {
+                                       AST_LIST_REMOVE_CURRENT(list);
+                                       ast_free(cur);
+                               }
+                       }
+                       AST_LIST_TRAVERSE_SAFE_END
+                       AST_LIST_UNLOCK(&zombies);
+                       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+               }
+               pthread_testcancel();
+               /* Wait for 60 seconds, without engaging in a busy loop. */
+               ast_poll(NULL, 0, AST_LIST_FIRST(&zombies) ? 5000 : 60000);
+       }
+       return NULL;
+}
+
 
 #define AST_MAX_FORMATS 10
 
@@ -107,13 +146,15 @@ int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect,
                        break;
                }
                collect[x++] = res;
-               if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num)) {
+               if (!ast_matchmore_extension(chan, context, collect, 1,
+                       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
                        break;
                }
        }
 
        if (res >= 0) {
-               res = ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num) ? 1 : 0;
+               res = ast_exists_extension(chan, context, collect, 1,
+                       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL)) ? 1 : 0;
        }
 
        return res;
@@ -142,6 +183,7 @@ enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *promp
 
        filename = ast_strdupa(prompt);
        while ((front = strsep(&filename, "&"))) {
+               ast_test_suite_event_notify("PLAYBACK", "Message: %s\r\nChannel: %s", front, ast_channel_name(c));
                if (!ast_strlen_zero(front)) {
                        res = ast_streamfile(c, front, c->language);
                        if (res)
@@ -333,7 +375,7 @@ int ast_app_messagecount(const char *context, const char *mailbox, const char *f
        return 0;
 }
 
-int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration) 
+int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
 {
        const char *ptr;
        int res = 0;
@@ -405,15 +447,15 @@ struct linear_state {
        int fd;
        int autoclose;
        int allowoverride;
-       int origwfmt;
+       struct ast_format origwfmt;
 };
 
 static void linear_release(struct ast_channel *chan, void *params)
 {
        struct linear_state *ls = params;
 
-       if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt)) {
-               ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
+       if (ls->origwfmt.id && ast_set_write_format(chan, &ls->origwfmt)) {
+               ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", ast_channel_name(chan), ls->origwfmt.id);
        }
 
        if (ls->autoclose) {
@@ -429,12 +471,13 @@ static int linear_generator(struct ast_channel *chan, void *data, int len, int s
        struct linear_state *ls = data;
        struct ast_frame f = {
                .frametype = AST_FRAME_VOICE,
-               .subclass.codec = AST_FORMAT_SLINEAR,
                .data.ptr = buf + AST_FRIENDLY_OFFSET / 2,
                .offset = AST_FRIENDLY_OFFSET,
        };
        int res;
 
+       ast_format_set(&f.subclass.format, AST_FORMAT_SLINEAR, 0);
+
        len = samples * 2;
        if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
                ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" , len);
@@ -467,10 +510,10 @@ static void *linear_alloc(struct ast_channel *chan, void *params)
                ast_clear_flag(chan, AST_FLAG_WRITE_INT);
        }
 
-       ls->origwfmt = chan->writeformat;
+       ast_format_copy(&ls->origwfmt, &chan->writeformat);
 
-       if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
-               ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
+       if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) {
+               ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", ast_channel_name(chan));
                ast_free(ls);
                ls = params = NULL;
        }
@@ -657,6 +700,7 @@ int ast_play_and_wait(struct ast_channel *chan, const char *fn)
 {
        int d = 0;
 
+       ast_test_suite_event_notify("PLAYBACK", "Message: %s\r\nChannel: %s", fn, ast_channel_name(chan));
        if ((d = ast_streamfile(chan, fn, chan->language))) {
                return d;
        }
@@ -675,19 +719,25 @@ static int global_maxsilence = 0;
  * \param chan Channel to playback to/record from.
  * \param playfile Filename of sound to play before recording begins.
  * \param recordfile Filename to record to.
- * \param maxtime Maximum length of recording (in milliseconds).
+ * \param maxtime Maximum length of recording (in seconds).
  * \param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
  * \param duration Where to store actual length of the recorded message (in milliseconds).
+ * \param sound_duration Where to store the length of the recorded message (in milliseconds), minus any silence
  * \param beep Whether to play a beep before starting to record.
  * \param silencethreshold
  * \param maxsilence Length of silence that will end a recording (in milliseconds).
  * \param path Optional filesystem path to unlock.
- * \param prepend If true, prepend the recorded audio to an existing file.
+ * \param prepend If true, prepend the recorded audio to an existing file and follow prepend mode recording rules
  * \param acceptdtmf DTMF digits that will end the recording.
  * \param canceldtmf DTMF digits that will cancel the recording.
+ * \param skip_confirmation_sound If true, don't play auth-thankyou at end. Nice for custom recording prompts in apps.
+ *
+ * \retval -1 failure or hangup
+ * \retval 'S' Recording ended from silence timeout
+ * \retval 't' Recording ended from the message exceeding the maximum duration, or via DTMF in prepend mode
+ * \retval dtmfchar Recording ended via the return value's DTMF character for either cancel or accept.
  */
-
-static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf)
+static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound)
 {
        int d = 0;
        char *fmts;
@@ -701,10 +751,11 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
        int totalsilence = 0;
        int dspsilence = 0;
        int olddspsilence = 0;
-       int rfmt = 0;
+       struct ast_format rfmt;
        struct ast_silence_generator *silgen = NULL;
-       char prependfile[80];
+       char prependfile[PATH_MAX];
 
+       ast_format_clear(&rfmt);
        if (silencethreshold < 0) {
                silencethreshold = global_silence_threshold;
        }
@@ -720,7 +771,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
        }
 
        ast_debug(1, "play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
-       snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
+       snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, ast_channel_name(chan));
 
        if (playfile || beep) {
                if (!beep) {
@@ -775,8 +826,8 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                        return -1;
                }
                ast_dsp_set_threshold(sildet, silencethreshold);
-               rfmt = chan->readformat;
-               res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+               ast_format_copy(&rfmt, &chan->readformat);
+               res = ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR);
                if (res < 0) {
                        ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
                        ast_dsp_free(sildet);
@@ -802,7 +853,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                                ast_debug(1, "One waitfor failed, trying another\n");
                                /* Try one more time in case of masq */
                                if (!(res = ast_waitfor(chan, 2000))) {
-                                       ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
+                                       ast_log(LOG_WARNING, "No audio available on %s??\n", ast_channel_name(chan));
                                        res = -1;
                                }
                        }
@@ -907,6 +958,9 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
         * closed (which would create a resource leak).
         */
        *duration = others[0] ? ast_tellstream(others[0]) / 8000 : 0;
+       if (sound_duration) {
+               *sound_duration = *duration;
+       }
 
        if (!prepend) {
                /* Reduce duration by a total silence amount */
@@ -914,11 +968,23 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                        totalsilence += dspsilence;
                }
 
-               if (totalsilence > 0)
-                       *duration -= (totalsilence - 200) / 1000;
+               if (sound_duration) {
+                       if (totalsilence > 0) {
+                               *sound_duration -= (totalsilence - 200) / 1000;
+                       }
+                       if (*sound_duration < 0) {
+                               *sound_duration = 0;
+                       }
+               }
+
+               if (dspsilence > 0) {
+                       *duration -= (dspsilence - 200) / 1000;
+               }
+
                if (*duration < 0) {
                        *duration = 0;
                }
+
                for (x = 0; x < fmtcnt; x++) {
                        if (!others[x]) {
                                break;
@@ -929,7 +995,7 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                         * to trim ANY part of the recording.
                         */
                        if (res > 0 && dspsilence) {
-                                /* rewind only the trailing silence */
+                               /* rewind only the trailing silence */
                                ast_stream_rewind(others[x], dspsilence - 200);
                        }
                        ast_truncstream(others[x]);
@@ -964,10 +1030,10 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
                        ast_filedelete(prependfile, sfmt[x]);
                }
        }
-       if (rfmt && ast_set_read_format(chan, rfmt)) {
-               ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
+       if (rfmt.id && ast_set_read_format(chan, &rfmt)) {
+               ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(&rfmt), ast_channel_name(chan));
        }
-       if (outmsg == 2) {
+       if ((outmsg == 2) && (!skip_confirmation_sound)) {
                ast_stream_and_wait(chan, "auth-thankyou", "");
        }
        if (sildet) {
@@ -979,19 +1045,19 @@ static int __ast_play_and_record(struct ast_channel *chan, const char *playfile,
 static const char default_acceptdtmf[] = "#";
 static const char default_canceldtmf[] = "";
 
-int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
 {
-       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf));
+       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf), 0);
 }
 
-int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
+int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence, const char *path)
 {
-       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf);
+       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf, 0);
 }
 
-int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
+int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence)
 {
-       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf);
+       return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf, 1);
 }
 
 /* Channel group core functions */
@@ -1093,27 +1159,38 @@ int ast_app_group_get_count(const char *group, const char *category)
 int ast_app_group_match_get_count(const char *groupmatch, const char *category)
 {
        struct ast_group_info *gi = NULL;
-       regex_t regexbuf;
+       regex_t regexbuf_group;
+       regex_t regexbuf_category;
        int count = 0;
 
        if (ast_strlen_zero(groupmatch)) {
+               ast_log(LOG_NOTICE, "groupmatch empty\n");
                return 0;
        }
 
        /* if regex compilation fails, return zero matches */
-       if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB)) {
+       if (regcomp(&regexbuf_group, groupmatch, REG_EXTENDED | REG_NOSUB)) {
+               ast_log(LOG_ERROR, "Regex compile failed on: %s\n", groupmatch);
+               return 0;
+       }
+
+       if (!ast_strlen_zero(category) && regcomp(&regexbuf_category, category, REG_EXTENDED | REG_NOSUB)) {
+               ast_log(LOG_ERROR, "Regex compile failed on: %s\n", category);
                return 0;
        }
 
        AST_RWLIST_RDLOCK(&groups);
        AST_RWLIST_TRAVERSE(&groups, gi, group_list) {
-               if (!regexec(&regexbuf, gi->group, 0, NULL, 0) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
+               if (!regexec(&regexbuf_group, gi->group, 0, NULL, 0) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !regexec(&regexbuf_category, gi->category, 0, NULL, 0)))) {
                        count++;
                }
        }
        AST_RWLIST_UNLOCK(&groups);
 
-       regfree(&regexbuf);
+       regfree(&regexbuf_group);
+       if (!ast_strlen_zero(category)) {
+               regfree(&regexbuf_category);
+       }
 
        return count;
 }
@@ -1181,14 +1258,18 @@ unsigned int __ast_app_separate_args(char *buf, char delim, int remove_chars, ch
 {
        int argc;
        char *scan, *wasdelim = NULL;
-       int paren = 0, quote = 0;
+       int paren = 0, quote = 0, bracket = 0;
 
-       if (!buf || !array || !arraylen) {
+       if (!array || !arraylen) {
                return 0;
        }
 
        memset(array, 0, arraylen * sizeof(*array));
 
+       if (!buf) {
+               return 0;
+       }
+
        scan = buf;
 
        for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
@@ -1200,6 +1281,12 @@ unsigned int __ast_app_separate_args(char *buf, char delim, int remove_chars, ch
                                if (paren) {
                                        paren--;
                                }
+                       } else if (*scan == '[') {
+                               bracket++;
+                       } else if (*scan == ']') {
+                               if (bracket) {
+                                       bracket--;
+                               }
                        } else if (*scan == '"' && delim != '"') {
                                quote = quote ? 0 : 1;
                                if (remove_chars) {
@@ -1214,7 +1301,7 @@ unsigned int __ast_app_separate_args(char *buf, char delim, int remove_chars, ch
                                } else {
                                        scan++;
                                }
-                       } else if ((*scan == delim) && !paren && !quote) {
+                       } else if ((*scan == delim) && !paren && !quote && !bracket) {
                                wasdelim = scan;
                                *scan++ = '\0';
                                break;
@@ -1417,9 +1504,9 @@ static int ast_unlock_path_flock(const char *path)
                snprintf(s, strlen(path) + 19, "%s/lock", path);
                unlink(s);
                path_lock_destroy(p);
-               ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
+               ast_debug(1, "Unlocked path '%s'\n", path);
        } else {
-               ast_log(LOG_DEBUG, "Failed to unlock path '%s': "
+               ast_debug(1, "Failed to unlock path '%s': "
                                "lock not found\n", path);
        }
 
@@ -1507,7 +1594,7 @@ int ast_record_review(struct ast_channel *chan, const char *playfile, const char
                        /* Record */
                        ast_verb(3, "R%secording\n", recorded == 1 ? "e-r" : "");
                        recorded = 1;
-                       if ((cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path)) == -1) {
+                       if ((cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, NULL, silencethreshold, maxsilence, path)) == -1) {
                                /* User has hung up, no options to give */
                                return cmd;
                        }
@@ -1800,13 +1887,19 @@ char *ast_read_textfile(const char *filename)
        return output;
 }
 
-int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
+static int parse_options(const struct ast_app_option *options, void *_flags, char **args, char *optstr, int flaglen)
 {
        char *s, *arg;
        int curarg, res = 0;
        unsigned int argloc;
+       struct ast_flags *flags = _flags;
+       struct ast_flags64 *flags64 = _flags;
 
-       ast_clear_flag(flags, AST_FLAGS_ALL);
+       if (flaglen == 32) {
+               ast_clear_flag(flags, AST_FLAGS_ALL);
+       } else {
+               flags64->flags = 0;
+       }
 
        if (!optstr) {
                return 0;
@@ -1817,8 +1910,40 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
                curarg = *s++ & 0x7f;   /* the array (in app.h) has 128 entries */
                argloc = options[curarg].arg_index;
                if (*s == '(') {
+                       int paren = 1, quote = 0;
+                       int parsequotes = (s[1] == '"') ? 1 : 0;
+
                        /* Has argument */
                        arg = ++s;
+                       for (; *s; s++) {
+                               if (*s == '(' && !quote) {
+                                       paren++;
+                               } else if (*s == ')' && !quote) {
+                                       /* Count parentheses, unless they're within quotes (or backslashed, below) */
+                                       paren--;
+                               } else if (*s == '"' && parsequotes) {
+                                       /* Leave embedded quotes alone, unless they are the first character */
+                                       quote = quote ? 0 : 1;
+                                       ast_copy_string(s, s + 1, INT_MAX);
+                                       s--;
+                               } else if (*s == '\\') {
+                                       if (!quote) {
+                                               /* If a backslash is found outside of quotes, remove it */
+                                               ast_copy_string(s, s + 1, INT_MAX);
+                                       } else if (quote && s[1] == '"') {
+                                               /* Backslash for a quote character within quotes, remove the backslash */
+                                               ast_copy_string(s, s + 1, INT_MAX);
+                                       } else {
+                                               /* Backslash within quotes, keep both characters */
+                                               s++;
+                                       }
+                               }
+
+                               if (paren == 0) {
+                                       break;
+                               }
+                       }
+                       /* This will find the closing paren we found above, or none, if the string ended before we found one. */
                        if ((s = strchr(s, ')'))) {
                                if (argloc) {
                                        args[argloc - 1] = arg;
@@ -1832,52 +1957,24 @@ int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags
                } else if (argloc) {
                        args[argloc - 1] = "";
                }
-               ast_set_flag(flags, options[curarg].flag);
+               if (flaglen == 32) {
+                       ast_set_flag(flags, options[curarg].flag);
+               } else {
+                       ast_set_flag64(flags64, options[curarg].flag);
+               }
        }
 
        return res;
 }
 
-/* the following function will probably only be used in app_dial, until app_dial is reorganized to
-   better handle the large number of options it provides. After it is, you need to get rid of this variant 
-   -- unless, of course, someone else digs up some use for large flag fields. */
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
+{
+       return parse_options(options, flags, args, optstr, 32);
+}
 
 int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr)
 {
-       char *s, *arg;
-       int curarg, res = 0;
-       unsigned int argloc;
-
-       flags->flags = 0;
-
-       if (!optstr) {
-               return 0;
-       }
-
-       s = optstr;
-       while (*s) {
-               curarg = *s++ & 0x7f;   /* the array (in app.h) has 128 entries */
-               ast_set_flag64(flags, options[curarg].flag);
-               argloc = options[curarg].arg_index;
-               if (*s == '(') {
-                       /* Has argument */
-                       arg = ++s;
-                       if ((s = strchr(s, ')'))) {
-                               if (argloc) {
-                                       args[argloc - 1] = arg;
-                               }
-                               *s++ = '\0';
-                       } else {
-                               ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
-                               res = -1;
-                               break;
-                       }
-               } else if (argloc) {
-                       args[argloc - 1] = NULL;
-               }
-       }
-
-       return res;
+       return parse_options(options, flags, args, optstr, 64);
 }
 
 void ast_app_options2str64(const struct ast_app_option *options, struct ast_flags64 *flags, char *buf, size_t len)
@@ -2008,24 +2105,7 @@ int ast_str_get_encoded_str(struct ast_str **str, int maxlen, const char *stream
 
 void ast_close_fds_above_n(int n)
 {
-#ifdef HAVE_CLOSEFROM
        closefrom(n + 1);
-#else
-       int x, null;
-       struct rlimit rl;
-       getrlimit(RLIMIT_NOFILE, &rl);
-       null = open("/dev/null", O_RDONLY);
-       for (x = n + 1; x < rl.rlim_cur; 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. */
-                       while (dup2(null, x) < 0 && errno == EINTR);
-                       close(x);
-               }
-       }
-       close(null);
-#endif
 }
 
 int ast_safe_fork(int stop_reaper)
@@ -2046,6 +2126,21 @@ int ast_safe_fork(int stop_reaper)
        if (pid != 0) {
                /* Fork failed or parent */
                pthread_sigmask(SIG_SETMASK, &old_set, NULL);
+               if (!stop_reaper && pid > 0) {
+                       struct zombie *cur = ast_calloc(1, sizeof(*cur));
+                       if (cur) {
+                               cur->pid = pid;
+                               AST_LIST_LOCK(&zombies);
+                               AST_LIST_INSERT_TAIL(&zombies, cur, list);
+                               AST_LIST_UNLOCK(&zombies);
+                               if (shaun_of_the_dead_thread == AST_PTHREADT_NULL) {
+                                       if (ast_pthread_create_background(&shaun_of_the_dead_thread, NULL, shaun_of_the_dead, NULL)) {
+                                               ast_log(LOG_ERROR, "Shaun of the Dead wants to kill zombies, but can't?!!\n");
+                                               shaun_of_the_dead_thread = AST_PTHREADT_NULL;
+                                       }
+                               }
+                       }
+               }
                return pid;
        } else {
                /* Child */