123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- // Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- /// @file watch_socket.cc
- #include <dhcp_ddns/dhcp_ddns_log.h>
- #include <dhcp_ddns/watch_socket.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <sys/select.h>
- namespace isc {
- namespace dhcp_ddns {
- const int WatchSocket::INVALID_SOCKET;
- const uint32_t WatchSocket::MARKER;
- WatchSocket::WatchSocket()
- : source_(INVALID_SOCKET), sink_(INVALID_SOCKET) {
- // Open the pipe.
- int fds[2];
- if (pipe(fds)) {
- const char* errstr = strerror(errno);
- isc_throw(WatchSocketError, "Cannot construct pipe: " << errstr);
- }
- source_ = fds[1];
- sink_ = fds[0];
- if (fcntl(sink_, F_SETFL, O_NONBLOCK)) {
- const char* errstr = strerror(errno);
- isc_throw(WatchSocketError, "Cannot set sink to non-blocking: "
- << errstr);
- }
- }
- WatchSocket::~WatchSocket() {
- closeSocket();
- }
- void
- WatchSocket::markReady() {
- // Make sure it hasn't been orphaned! Otherwise we may get SIGPIPE. We
- // use fcntl to check as select() on some systems may show it as ready to
- // read.
- if (fcntl(sink_, F_GETFL) < 0) {
- closeSocket();
- isc_throw(WatchSocketError, "WatchSocket markReady failed:"
- " select_fd was closed!");
- }
- if (!isReady()) {
- int nbytes = write (source_, &MARKER, sizeof(MARKER));
- if (nbytes != sizeof(MARKER)) {
- // If there's an error get the error message than close
- // the pipe. This should ensure any further use of the socket
- // or testing the fd with select_fd will fail.
- const char* errstr = strerror(errno);
- closeSocket();
- isc_throw(WatchSocketError, "WatchSocket markReady failed:"
- << " bytes written: " << nbytes << " : " << errstr);
- }
- }
- }
- bool
- WatchSocket::isReady() {
- // Report it as not ready rather than error here.
- if (sink_ == INVALID_SOCKET) {
- return (false);
- }
- fd_set read_fds;
- FD_ZERO(&read_fds);
- // Add select_fd socket to listening set
- FD_SET(sink_, &read_fds);
- // Set zero timeout (non-blocking).
- struct timeval select_timeout;
- select_timeout.tv_sec = 0;
- select_timeout.tv_usec = 0;
- // Return true only if read ready, treat error same as not ready.
- return (select(sink_ + 1, &read_fds, NULL, NULL, &select_timeout) > 0);
- }
- void
- WatchSocket::clearReady() {
- if (isReady()) {
- uint32_t buf = 0;
- int nbytes = read (sink_, &buf, sizeof(buf));
- if ((nbytes != sizeof(MARKER) || (buf != MARKER))) {
- // If there's an error get the error message than close
- // the pipe. This should ensure any further use of the socket
- // or testing the fd with select_fd will fail.
- const char* errstr = strerror(errno);
- closeSocket();
- isc_throw(WatchSocketError, "WatchSocket clearReady failed:"
- << " bytes read: " << nbytes << " : "
- << " value read: " << buf << " error :" <<errstr);
- }
- }
- }
- void
- WatchSocket::closeSocket() {
- // Close the pipe fds. Technically a close can fail (hugely unlikely)
- // but there's no recovery for it either. If one does fail we log it
- // and go on. Plus this is called by the destructor and no one likes
- // destructors that throw.
- if (source_ != INVALID_SOCKET) {
- if (close(source_)) {
- const char* errstr = strerror(errno);
- LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_WATCH_SOURCE_CLOSE_ERROR)
- .arg(errstr);
- }
- source_ = INVALID_SOCKET;
- }
- if (sink_ != INVALID_SOCKET) {
- if (close(sink_)) {
- const char* errstr = strerror(errno);
- LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_WATCH_SINK_CLOSE_ERROR)
- .arg(errstr);
- }
- sink_ = INVALID_SOCKET;
- }
- }
- int
- WatchSocket::getSelectFd() {
- return (sink_);
- }
- } // namespace isc::dhcp_ddns
- } // namespace isc
|