watch_socket.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. /// @file watch_socket.cc
  15. #include <dhcp_ddns/dhcp_ddns_log.h>
  16. #include <dhcp_ddns/watch_socket.h>
  17. #include <fcntl.h>
  18. #include <errno.h>
  19. #include <sys/select.h>
  20. namespace isc {
  21. namespace dhcp_ddns {
  22. const int WatchSocket::INVALID_SOCKET;
  23. const uint32_t WatchSocket::MARKER;
  24. WatchSocket::WatchSocket()
  25. : source_(INVALID_SOCKET), sink_(INVALID_SOCKET) {
  26. // Open the pipe.
  27. int fds[2];
  28. if (pipe(fds)) {
  29. const char* errstr = strerror(errno);
  30. isc_throw(WatchSocketError, "Cannot construct pipe: " << errstr);
  31. }
  32. source_ = fds[1];
  33. sink_ = fds[0];
  34. if (fcntl(sink_, F_SETFL, O_NONBLOCK)) {
  35. const char* errstr = strerror(errno);
  36. isc_throw(WatchSocketError, "Cannot set sink to non-blocking: "
  37. << errstr);
  38. }
  39. }
  40. WatchSocket::~WatchSocket() {
  41. closeSocket();
  42. }
  43. void
  44. WatchSocket::markReady() {
  45. // Make sure it hasn't been orphaned! Otherwise we may get SIGPIPE. We
  46. // use fcntl to check as select() on some systems may show it as ready to
  47. // read.
  48. if (fcntl(sink_, F_GETFL) < 0) {
  49. closeSocket();
  50. isc_throw(WatchSocketError, "WatchSocket markReady failed:"
  51. " select_fd was closed!");
  52. }
  53. if (!isReady()) {
  54. int nbytes = write (source_, &MARKER, sizeof(MARKER));
  55. if (nbytes != sizeof(MARKER)) {
  56. // If there's an error get the error message than close
  57. // the pipe. This should ensure any further use of the socket
  58. // or testing the fd with select_fd will fail.
  59. const char* errstr = strerror(errno);
  60. closeSocket();
  61. isc_throw(WatchSocketError, "WatchSocket markReady failed:"
  62. << " bytes written: " << nbytes << " : " << errstr);
  63. }
  64. }
  65. }
  66. bool
  67. WatchSocket::isReady() {
  68. // Report it as not ready rather than error here.
  69. if (sink_ == INVALID_SOCKET) {
  70. return (false);
  71. }
  72. fd_set read_fds;
  73. FD_ZERO(&read_fds);
  74. // Add select_fd socket to listening set
  75. FD_SET(sink_, &read_fds);
  76. // Set zero timeout (non-blocking).
  77. struct timeval select_timeout;
  78. select_timeout.tv_sec = 0;
  79. select_timeout.tv_usec = 0;
  80. // Return true only if read ready, treat error same as not ready.
  81. return (select(sink_ + 1, &read_fds, NULL, NULL, &select_timeout) > 0);
  82. }
  83. void
  84. WatchSocket::clearReady() {
  85. if (isReady()) {
  86. uint32_t buf = 0;
  87. int nbytes = read (sink_, &buf, sizeof(buf));
  88. if ((nbytes != sizeof(MARKER) || (buf != MARKER))) {
  89. // If there's an error get the error message than close
  90. // the pipe. This should ensure any further use of the socket
  91. // or testing the fd with select_fd will fail.
  92. const char* errstr = strerror(errno);
  93. closeSocket();
  94. isc_throw(WatchSocketError, "WatchSocket clearReady failed:"
  95. << " bytes read: " << nbytes << " : "
  96. << " value read: " << buf << " error :" <<errstr);
  97. }
  98. }
  99. }
  100. void
  101. WatchSocket::closeSocket() {
  102. // Close the pipe fds. Technically a close can fail (hugely unlikely)
  103. // but there's no recovery for it either. If one does fail we log it
  104. // and go on. Plus this is called by the destructor and no one likes
  105. // destructors that throw.
  106. if (source_ != INVALID_SOCKET) {
  107. if (close(source_)) {
  108. const char* errstr = strerror(errno);
  109. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_WATCH_SOURCE_CLOSE_ERROR)
  110. .arg(errstr);
  111. }
  112. source_ = INVALID_SOCKET;
  113. }
  114. if (sink_ != INVALID_SOCKET) {
  115. if (close(sink_)) {
  116. const char* errstr = strerror(errno);
  117. LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_WATCH_SINK_CLOSE_ERROR)
  118. .arg(errstr);
  119. }
  120. sink_ = INVALID_SOCKET;
  121. }
  122. }
  123. int
  124. WatchSocket::getSelectFd() {
  125. return (sink_);
  126. }
  127. } // namespace isc::dhcp_ddns
  128. } // namespace isc