Kea  1.9.9-git
watch_socket.cc
Go to the documentation of this file.
1 // Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
8 
9 #include <config.h>
10 
11 //#include <dhcp_ddns/dhcp_ddns_log.h>
12 #include <util/watch_socket.h>
13 
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <sstream>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <unistd.h>
20 
21 namespace isc {
22 namespace util {
23 
24 
26 const uint32_t WatchSocket::MARKER;
27 
29  : source_(SOCKET_NOT_VALID), sink_(SOCKET_NOT_VALID) {
30  // Open the pipe.
31  int fds[2];
32  if (pipe(fds)) {
33  const char* errstr = strerror(errno);
34  isc_throw(WatchSocketError, "Cannot construct pipe: " << errstr);
35  }
36 
37  source_ = fds[1];
38  sink_ = fds[0];
39 
40  if (fcntl(source_, F_SETFD, FD_CLOEXEC)) {
41  const char* errstr = strerror(errno);
42  isc_throw(WatchSocketError, "Cannot set source to close-on-exec: "
43  << errstr);
44  }
45 
46  if (fcntl(sink_, F_SETFD, FD_CLOEXEC)) {
47  const char* errstr = strerror(errno);
48  isc_throw(WatchSocketError, "Cannot set sink to close-on-exec: "
49  << errstr);
50  }
51 
52  if (fcntl(sink_, F_SETFL, O_NONBLOCK)) {
53  const char* errstr = strerror(errno);
54  isc_throw(WatchSocketError, "Cannot set sink to non-blocking: "
55  << errstr);
56  }
57 }
58 
60  closeSocket();
61 }
62 
63 void
65  // Make sure it hasn't been orphaned! Otherwise we may get SIGPIPE. We
66  // use fcntl to check as select() on some systems may show it as ready to
67  // read.
68  if (fcntl(sink_, F_GETFL) < 0) {
69  closeSocket();
70  isc_throw(WatchSocketError, "WatchSocket markReady failed:"
71  " select_fd was closed!");
72  }
73 
74  if (!isReady()) {
75  int nbytes = write (source_, &MARKER, sizeof(MARKER));
76  if (nbytes != sizeof(MARKER)) {
77  // If there's an error get the error message than close
78  // the pipe. This should ensure any further use of the socket
79  // or testing the fd with select_fd will fail.
80  const char* errstr = strerror(errno);
81  closeSocket();
82  isc_throw(WatchSocketError, "WatchSocket markReady failed:"
83  << " bytes written: " << nbytes << " : " << errstr);
84  }
85  }
86 }
87 
88 bool
90  // Report it as not ready rather than error here.
91  if (sink_ == SOCKET_NOT_VALID) {
92  return (false);
93  }
94 
95  // Use ioctl FIONREAD vs polling select as it is faster.
96  int len;
97  int result = ioctl(sink_, FIONREAD, &len);
98  // Return true only if read ready, treat error same as not ready.
99  return ((result == 0) && (len > 0));
100 }
101 
102 void
104  if (isReady()) {
105  uint32_t buf = 0;
106  int nbytes = read (sink_, &buf, sizeof(buf));
107  if ((nbytes != sizeof(MARKER) || (buf != MARKER))) {
108  // If there's an error get the error message than close
109  // the pipe. This should ensure any further use of the socket
110  // or testing the fd with select_fd will fail.
111  const char* errstr = strerror(errno);
112  closeSocket();
113  isc_throw(WatchSocketError, "WatchSocket clearReady failed: "
114  "bytes read: " << nbytes << " : "
115  "value read: " << buf << " error :" << errstr);
116  }
117  }
118 }
119 
120 bool
121 WatchSocket::closeSocket(std::string& error_string) {
122  std::ostringstream s;
123  // Close the pipe fds. Technically a close can fail (hugely unlikely)
124  // but there's no recovery for it either. If one does fail we log it
125  // and go on. Plus this is called by the destructor and no one likes
126  // destructors that throw.
127  if (source_ != SOCKET_NOT_VALID) {
128  if (close(source_)) {
129  // An error occurred.
130  s << "Could not close source: " << strerror(errno);
131  }
132 
133  source_ = SOCKET_NOT_VALID;
134  }
135 
136  if (sink_ != SOCKET_NOT_VALID) {
137  if (close(sink_)) {
138  // An error occurred.
139  if (error_string.empty()) {
140  s << "could not close sink: " << strerror(errno);
141  }
142  }
143 
144  sink_ = SOCKET_NOT_VALID;
145  }
146 
147  error_string = s.str();
148 
149  // If any errors have been reported, return false.
150  return (error_string.empty() ? true : false);
151 }
152 
153 void
154 WatchSocket::closeSocket() {
155  std::string error_string;
156  closeSocket(error_string);
157 }
158 
159 int
161  return (sink_);
162 }
163 
164 } // namespace isc::util
165 } // namespace isc
void clearReady()
Clears the socket's ready to read marker.
WatchSocket()
Constructor.
Definition: watch_socket.cc:28
Defines the class, WatchSocket.
static const uint32_t MARKER
Value written to the source when marking the socket as ready.
Definition: watch_socket.h:54
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
int getSelectFd()
Returns the file descriptor to use to monitor the socket.
bool closeSocket(std::string &error_string)
Closes the descriptors associated with the socket.
virtual ~WatchSocket()
Destructor.
Definition: watch_socket.cc:59
void markReady()
Marks the select-fd as ready to read.
Definition: watch_socket.cc:64
Defines the logger used by the top-level component of kea-dhcp-ddns.
Exception thrown if an error occurs during IO source open.
Definition: watch_socket.h:23
static const int SOCKET_NOT_VALID
Value used to signify an invalid descriptor.
Definition: watch_socket.h:50
bool isReady()
Returns true the if socket is marked as ready.
Definition: watch_socket.cc:89