diff options
Diffstat (limited to 'main/alertpipe.c')
-rw-r--r-- | main/alertpipe.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/main/alertpipe.c b/main/alertpipe.c new file mode 100644 index 000000000..fa6ec7bcc --- /dev/null +++ b/main/alertpipe.c @@ -0,0 +1,166 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, Sean Bright + * + * Sean Bright <sean.bright@gmail.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Alert Pipe API + * + * \author Sean Bright + */ + +#include "asterisk.h" + +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_EVENTFD +# include <sys/eventfd.h> +#endif + +#include "asterisk/alertpipe.h" +#include "asterisk/logger.h" + +int ast_alertpipe_init(int alert_pipe[2]) +{ +#ifdef HAVE_EVENTFD + + int fd = eventfd(0, EFD_NONBLOCK | EFD_SEMAPHORE); + if (fd > -1) { + alert_pipe[0] = alert_pipe[1] = fd; + return 0; + } + + ast_log(LOG_WARNING, "Failed to create alert pipe with eventfd(), falling back to pipe(): %s\n", + strerror(errno)); + ast_alertpipe_clear(alert_pipe); + +#endif + + if (pipe(alert_pipe)) { + ast_log(LOG_WARNING, "Failed to create alert pipe: %s\n", strerror(errno)); + return -1; + } else { + int flags = fcntl(alert_pipe[0], F_GETFL); + if (fcntl(alert_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) { + ast_log(LOG_WARNING, "Failed to set non-blocking mode on alert pipe: %s\n", + strerror(errno)); + ast_alertpipe_close(alert_pipe); + return -1; + } + flags = fcntl(alert_pipe[1], F_GETFL); + if (fcntl(alert_pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) { + ast_log(LOG_WARNING, "Failed to set non-blocking mode on alert pipe: %s\n", + strerror(errno)); + ast_alertpipe_close(alert_pipe); + return -1; + } + } + + return 0; +} + +void ast_alertpipe_close(int alert_pipe[2]) +{ +#ifdef HAVE_EVENTFD + + if (alert_pipe[0] == alert_pipe[1]) { + if (alert_pipe[0] > -1) { + close(alert_pipe[0]); + ast_alertpipe_clear(alert_pipe); + } + return; + } + +#endif + + if (alert_pipe[0] > -1) { + close(alert_pipe[0]); + } + if (alert_pipe[1] > -1) { + close(alert_pipe[1]); + } + ast_alertpipe_clear(alert_pipe); +} + +ast_alert_status_t ast_alertpipe_read(int alert_pipe[2]) +{ + uint64_t tmp; + + if (!ast_alertpipe_readable(alert_pipe)) { + return AST_ALERT_NOT_READABLE; + } + + if (read(alert_pipe[0], &tmp, sizeof(tmp)) < 0) { + if (errno != EINTR && errno != EAGAIN) { + ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); + return AST_ALERT_READ_FAIL; + } + } + + return AST_ALERT_READ_SUCCESS; +} + +ssize_t ast_alertpipe_write(int alert_pipe[2]) +{ + uint64_t tmp = 1; + + if (!ast_alertpipe_writable(alert_pipe)) { + errno = EBADF; + return 0; + } + + /* preset errno in case returned size does not match */ + errno = EPIPE; + return write(alert_pipe[1], &tmp, sizeof(tmp)) != sizeof(tmp); +} + +ast_alert_status_t ast_alertpipe_flush(int alert_pipe[2]) +{ + int bytes_read; + uint64_t tmp[16]; + + if (!ast_alertpipe_readable(alert_pipe)) { + return AST_ALERT_NOT_READABLE; + } + + /* Read the alertpipe until it is exhausted. */ + for (;;) { + bytes_read = read(alert_pipe[0], tmp, sizeof(tmp)); + if (bytes_read < 0) { + if (errno == EINTR) { + continue; + } + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Would block so nothing left to read. + * This is the normal loop exit. + */ + break; + } + ast_log(LOG_WARNING, "read() failed flushing alertpipe: %s\n", + strerror(errno)); + return AST_ALERT_READ_FAIL; + } + if (!bytes_read) { + /* Read nothing so we are done */ + break; + } + } + + return AST_ALERT_READ_SUCCESS; +} |