summaryrefslogtreecommitdiffstats
path: root/src/lib/util/watch_socket.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/watch_socket.cc')
-rw-r--r--src/lib/util/watch_socket.cc165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/lib/util/watch_socket.cc b/src/lib/util/watch_socket.cc
new file mode 100644
index 0000000..6ffb396
--- /dev/null
+++ b/src/lib/util/watch_socket.cc
@@ -0,0 +1,165 @@
+// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file watch_socket.cc
+
+#include <config.h>
+
+//#include <dhcp_ddns/dhcp_ddns_log.h>
+#include <util/watch_socket.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sstream>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+namespace isc {
+namespace util {
+
+
+const int WatchSocket::SOCKET_NOT_VALID;
+const uint32_t WatchSocket::MARKER;
+
+WatchSocket::WatchSocket()
+ : source_(SOCKET_NOT_VALID), sink_(SOCKET_NOT_VALID) {
+ // 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(source_, F_SETFD, FD_CLOEXEC)) {
+ const char* errstr = strerror(errno);
+ isc_throw(WatchSocketError, "Cannot set source to close-on-exec: "
+ << errstr);
+ }
+
+ if (fcntl(sink_, F_SETFD, FD_CLOEXEC)) {
+ const char* errstr = strerror(errno);
+ isc_throw(WatchSocketError, "Cannot set sink to close-on-exec: "
+ << errstr);
+ }
+
+ 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_ == SOCKET_NOT_VALID) {
+ return (false);
+ }
+
+ // Use ioctl FIONREAD vs polling select as it is faster.
+ int len;
+ int result = ioctl(sink_, FIONREAD, &len);
+ // Return true only if read ready, treat error same as not ready.
+ return ((result == 0) && (len > 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);
+ }
+ }
+}
+
+bool
+WatchSocket::closeSocket(std::string& error_string) {
+ std::ostringstream s;
+ // 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_ != SOCKET_NOT_VALID) {
+ if (close(source_)) {
+ // An error occurred.
+ s << "Could not close source: " << strerror(errno);
+ }
+
+ source_ = SOCKET_NOT_VALID;
+ }
+
+ if (sink_ != SOCKET_NOT_VALID) {
+ if (close(sink_)) {
+ // An error occurred.
+ if (error_string.empty()) {
+ s << "could not close sink: " << strerror(errno);
+ }
+ }
+
+ sink_ = SOCKET_NOT_VALID;
+ }
+
+ error_string = s.str();
+
+ // If any errors have been reported, return false.
+ return (error_string.empty() ? true : false);
+}
+
+void
+WatchSocket::closeSocket() {
+ std::string error_string;
+ closeSocket(error_string);
+}
+
+int
+WatchSocket::getSelectFd() {
+ return (sink_);
+}
+
+} // namespace isc::util
+} // namespace isc