diff options
Diffstat (limited to 'main/asterisk.c')
-rw-r--r-- | main/asterisk.c | 91 |
1 files changed, 78 insertions, 13 deletions
diff --git a/main/asterisk.c b/main/asterisk.c index 0a131fda9..d55594983 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -1170,11 +1170,10 @@ void ast_unreplace_sigchld(void) ast_mutex_unlock(&safe_system_lock); } -int ast_safe_system(const char *s) +/*! \brief fork and perform other preparations for spawning applications */ +static pid_t safe_exec_prep(int dualfork) { pid_t pid; - int res; - int status; #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) ast_replace_sigchld(); @@ -1196,35 +1195,101 @@ int ast_safe_system(const char *s) cap_free(cap); #endif #ifdef HAVE_WORKING_FORK - if (ast_opt_high_priority) + if (ast_opt_high_priority) { ast_set_priority(0); + } /* Close file descriptors and launch system command */ ast_close_fds_above_n(STDERR_FILENO); #endif - execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL); - _exit(1); - } else if (pid > 0) { + if (dualfork) { +#ifdef HAVE_WORKING_FORK + pid = fork(); +#else + pid = vfork(); +#endif + if (pid < 0) { + /* Second fork failed. */ + /* No logger available. */ + _exit(1); + } + + if (pid > 0) { + /* This is the first fork, exit so the reaper finishes right away. */ + _exit(0); + } + + /* This is the second fork. The first fork will exit immediately so + * Asterisk doesn't have to wait for completion. + * ast_safe_system("cmd &") would run in the background, but the '&' + * cannot be added with ast_safe_execvp, so we have to double fork. + */ + } + } + + if (pid < 0) { + ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); + } +#else + ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(ENOTSUP)); + pid = -1; +#endif + + return pid; +} + +/*! \brief wait for spawned application to complete and unreplace sigchld */ +static int safe_exec_wait(pid_t pid) +{ + int res = -1; + +#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) + if (pid > 0) { for (;;) { + int status; + res = waitpid(pid, &status, 0); if (res > -1) { res = WIFEXITED(status) ? WEXITSTATUS(status) : -1; break; - } else if (errno != EINTR) + } + if (errno != EINTR) { break; + } } - } else { - ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); - res = -1; } ast_unreplace_sigchld(); -#else /* !defined(HAVE_WORKING_FORK) && !defined(HAVE_WORKING_VFORK) */ - res = -1; #endif return res; } +int ast_safe_execvp(int dualfork, const char *file, char *const argv[]) +{ + pid_t pid = safe_exec_prep(dualfork); + + if (pid == 0) { + execvp(file, argv); + _exit(1); + /* noreturn from _exit */ + } + + return safe_exec_wait(pid); +} + +int ast_safe_system(const char *s) +{ + pid_t pid = safe_exec_prep(0); + + if (pid == 0) { + execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL); + _exit(1); + /* noreturn from _exit */ + } + + return safe_exec_wait(pid); +} + /*! * \brief enable or disable a logging level to a specified console */ |