Kea 3.1.8
watch_socket.cc
Go to the documentation of this file.
1// Copyright (C) 2014-2025 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 <util/watch_socket.h>
12
13#include <fcntl.h>
14#include <errno.h>
15#include <sstream>
16#include <string.h>
17#include <sys/ioctl.h>
18#include <unistd.h>
19
20namespace isc {
21namespace util {
22
24
25const uint32_t WatchSocket::MARKER = 0xDEADBEEF;
26
28 : source_(SOCKET_NOT_VALID), sink_(SOCKET_NOT_VALID) {
29 // Open the pipe.
30 int fds[2];
31 if (pipe(fds)) {
32 const char* errstr = strerror(errno);
33 isc_throw(WatchSocketError, "Cannot construct pipe: " << errstr);
34 }
35
36 source_ = fds[1];
37 sink_ = fds[0];
38
39 if (fcntl(source_, F_SETFD, FD_CLOEXEC)) {
40 const char* errstr = strerror(errno);
41 isc_throw(WatchSocketError, "Cannot set source to close-on-exec: "
42 << errstr);
43 }
44
45 if (fcntl(sink_, F_SETFD, FD_CLOEXEC)) {
46 const char* errstr = strerror(errno);
47 isc_throw(WatchSocketError, "Cannot set sink to close-on-exec: "
48 << errstr);
49 }
50
51 if (fcntl(sink_, F_SETFL, O_NONBLOCK)) {
52 const char* errstr = strerror(errno);
53 isc_throw(WatchSocketError, "Cannot set sink to non-blocking: "
54 << errstr);
55 }
56}
57
61
62void
64 // Make sure it hasn't been orphaned! Otherwise we may get SIGPIPE. We
65 // use fcntl to check as select() on some systems may show it as ready to
66 // read.
67 if (fcntl(sink_, F_GETFL) < 0) {
69 isc_throw(WatchSocketError, "WatchSocket markReady failed:"
70 " select_fd was closed!");
71 }
72
73 if (!isReady()) {
74 int nbytes = write (source_, &MARKER, sizeof(MARKER));
75 if (nbytes != sizeof(MARKER)) {
76 // If there's an error get the error message than close
77 // the pipe. This should ensure any further use of the socket
78 // or testing the fd with select_fd will fail.
79 const char* errstr = strerror(errno);
81 isc_throw(WatchSocketError, "WatchSocket markReady failed:"
82 << " bytes written: " << nbytes << " : " << errstr);
83 }
84 }
85}
86
87bool
89 // Report it as not ready rather than error here.
90 if (sink_ == SOCKET_NOT_VALID) {
91 return (false);
92 }
93
94 // Use ioctl FIONREAD vs polling select as it is faster.
95 int len;
96 int result = ioctl(sink_, FIONREAD, &len);
97 // Return true only if read ready, treat error same as not ready.
98 return ((result == 0) && (len > 0));
99}
100
101void
103 if (isReady()) {
104 uint32_t buf = 0;
105 int nbytes = read (sink_, &buf, sizeof(buf));
106 if ((nbytes != sizeof(MARKER) || (buf != MARKER))) {
107 // If there's an error get the error message than close
108 // the pipe. This should ensure any further use of the socket
109 // or testing the fd with select_fd will fail.
110 const char* errstr = strerror(errno);
111 closeSocket();
112 isc_throw(WatchSocketError, "WatchSocket clearReady failed: "
113 "bytes read: " << nbytes << " : "
114 "value read: " << buf << " error :" << errstr);
115 }
116 }
117}
118
119bool
120WatchSocket::closeSocket(std::string& error_string) {
121 std::ostringstream s;
122 // Close the pipe fds. Technically a close can fail (hugely unlikely)
123 // but there's no recovery for it either. If one does fail we log it
124 // and go on. Plus this is called by the destructor and no one likes
125 // destructors that throw.
126 if (source_ != SOCKET_NOT_VALID) {
127 if (close(source_)) {
128 // An error occurred.
129 s << "Could not close source: " << strerror(errno);
130 }
131
132 source_ = SOCKET_NOT_VALID;
133 }
134
135 if (sink_ != SOCKET_NOT_VALID) {
136 if (close(sink_)) {
137 // An error occurred.
138 if (error_string.empty()) {
139 s << "could not close sink: " << strerror(errno);
140 }
141 }
142
143 sink_ = SOCKET_NOT_VALID;
144 }
145
146 error_string = s.str();
147
148 // If any errors have been reported, return false.
149 return (error_string.empty() ? true : false);
150}
151
152void
154 std::string error_string;
155 closeSocket(error_string);
156}
157
158int
160 return (sink_);
161}
162
163} // namespace isc::util
164} // 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.