Kea 2.7.5
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
21namespace isc {
22namespace util {
23
24
26const 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
62
63void
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) {
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);
82 isc_throw(WatchSocketError, "WatchSocket markReady failed:"
83 << " bytes written: " << nbytes << " : " << errstr);
84 }
85 }
86}
87
88bool
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
102void
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
120bool
121WatchSocket::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
153void
154WatchSocket::closeSocket() {
155 std::string error_string;
156 closeSocket(error_string);
157}
158
159int
161 return (sink_);
162}
163
164} // namespace isc::util
165} // namespace isc
Exception thrown if an error occurs during IO source open.
static const int SOCKET_NOT_VALID
Value used to signify an invalid descriptor.
static const uint32_t MARKER
Value written to the source when marking the socket as ready.
void clearReady()
Clears the socket's ready to read marker.
WatchSocket()
Constructor.
bool closeSocket(std::string &error_string)
Closes the descriptors associated with the socket.
virtual ~WatchSocket()
Destructor.
int getSelectFd()
Returns the file descriptor to use to monitor the socket.
bool isReady()
Returns true the if socket is marked as ready.
void markReady()
Marks the select-fd as ready to read.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Defines the logger used by the top-level component of kea-lfc.
Defines the class, WatchSocket.