diff --git a/apps/app_externalivr.c b/apps/app_externalivr.c index b01253d5c0..101237680e 100644 --- a/apps/app_externalivr.c +++ b/apps/app_externalivr.c @@ -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); diff --git a/apps/app_festival.c b/apps/app_festival.c index e788291edb..096b34c257 100644 --- a/apps/app_festival.c +++ b/apps/app_festival.c @@ -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); diff --git a/apps/app_ices.c b/apps/app_ices.c index b2a4e9b914..0a011b3b33 100644 --- a/apps/app_ices.c +++ b/apps/app_ices.c @@ -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) diff --git a/apps/app_mp3.c b/apps/app_mp3.c index d2f2f5c0e2..33bee620a6 100644 --- a/apps/app_mp3.c +++ b/apps/app_mp3.c @@ -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); } diff --git a/apps/app_nbscat.c b/apps/app_nbscat.c index 4e7203319c..f471296745 100644 --- a/apps/app_nbscat.c +++ b/apps/app_nbscat.c @@ -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); } diff --git a/apps/app_zapras.c b/apps/app_zapras.c index 231a9f2a64..6e079e32db 100644 --- a/apps/app_zapras.c +++ b/apps/app_zapras.c @@ -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;xfds[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) diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 7c66d47a1d..12d41d7dd7 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -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 diff --git a/include/asterisk/logger.h b/include/asterisk/logger.h index d76aa932f1..5ea81aca81 100644 --- a/include/asterisk/logger.h +++ b/include/asterisk/logger.h @@ -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)); diff --git a/main/app.c b/main/app.c index ee72d1078b..05cdf1748c 100644 --- a/main/app.c +++ b/main/app.c @@ -32,6 +32,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #endif #include #include /* added this to allow to compile, sorry! */ +#include #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(); +} + diff --git a/main/asterisk.c b/main/asterisk.c index f08a4d0202..99df3ef45d 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -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); diff --git a/main/logger.c b/main/logger.c index b7c517fa7d..dce2097d97 100644 --- a/main/logger.c +++ b/main/logger.c @@ -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; diff --git a/res/res_agi.c b/res/res_agi.c index 49aa2cfd54..caa015a7ed 100644 --- a/res/res_agi.c +++ b/res/res_agi.c @@ -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: diff --git a/res/res_musiconhold.c b/res/res_musiconhold.c index e7044eff9d..e456b3c7e0 100644 --- a/res/res_musiconhold.c +++ b/res/res_musiconhold.c @@ -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];