1 // Copyright (C) 2014-2018 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 #include <config.h>
7 #include <util/watch_socket.h>
8
9 #include <gtest/gtest.h>
10
11 #include <sys/select.h>
12 #include <sys/ioctl.h>
13
14 #ifdef HAVE_SYS_FILIO_H
15 // FIONREAD is here on Solaris
16 #include <sys/filio.h>
17 #endif
18
19 using namespace std;
20 using namespace isc;
21 using namespace isc::util;
22
23 namespace {
24
25 /// @brief Returns the result of select() given an fd to check for read status.
26 ///
27 /// @param fd_to_check The file descriptor to test
28 ///
29 /// @return Returns less than one on an error, 0 if the fd is not ready to
30 /// read, > 0 if it is ready to read.
selectCheck(int fd_to_check)31 int selectCheck(int fd_to_check) {
32 fd_set read_fds;
33 int maxfd = 0;
34
35 FD_ZERO(&read_fds);
36
37 // Add this socket to listening set
38 FD_SET(fd_to_check, &read_fds);
39 maxfd = fd_to_check;
40
41 struct timeval select_timeout;
42 select_timeout.tv_sec = 0;
43 select_timeout.tv_usec = 0;
44
45 return (select(maxfd + 1, &read_fds, NULL, NULL, &select_timeout));
46 }
47
48 /// @brief Tests the basic functionality of WatchSocket.
TEST(WatchSocketTest,basics)49 TEST(WatchSocketTest, basics) {
50 WatchSocketPtr watch;
51
52 /// Verify that we can construct a WatchSocket.
53 ASSERT_NO_THROW(watch.reset(new WatchSocket()));
54 ASSERT_TRUE(watch);
55
56 /// Verify that post-construction the state the select-fd is valid.
57 int select_fd = watch->getSelectFd();
58 EXPECT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
59
60 /// Verify that isReady() is false and that a call to select agrees.
61 EXPECT_FALSE(watch->isReady());
62 EXPECT_EQ(0, selectCheck(select_fd));
63
64 /// Verify that the socket can be marked ready.
65 ASSERT_NO_THROW(watch->markReady());
66
67 /// Verify that we have exactly one marker waiting to be read.
68 int count = 0;
69 EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
70 EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
71
72 /// Verify that we can call markReady again without error.
73 ASSERT_NO_THROW(watch->markReady());
74
75 /// Verify that we STILL have exactly one marker waiting to be read.
76 EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
77 EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
78
79 /// Verify that isReady() is true and that a call to select agrees.
80 EXPECT_TRUE(watch->isReady());
81 EXPECT_EQ(1, selectCheck(select_fd));
82
83 /// Verify that the socket can be cleared.
84 ASSERT_NO_THROW(watch->clearReady());
85
86 /// Verify that isReady() is false and that a call to select agrees.
87 EXPECT_FALSE(watch->isReady());
88 EXPECT_EQ(0, selectCheck(select_fd));
89 }
90
91 /// @brief Checks behavior when select_fd is closed externally while in the
92 /// "cleared" state.
TEST(WatchSocketTest,closedWhileClear)93 TEST(WatchSocketTest, closedWhileClear) {
94 WatchSocketPtr watch;
95
96 /// Verify that we can construct a WatchSocket.
97 ASSERT_NO_THROW(watch.reset(new WatchSocket()));
98 ASSERT_TRUE(watch);
99
100 /// Verify that post-construction the state the select-fd is valid.
101 int select_fd = watch->getSelectFd();
102 ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
103
104 // Verify that socket does not appear ready.
105 ASSERT_EQ(0, watch->isReady());
106
107 // Interfere by closing the fd.
108 ASSERT_EQ(0, close(select_fd));
109
110 // Verify that socket does not appear ready.
111 ASSERT_EQ(0, watch->isReady());
112
113 // Verify that clear does NOT throw.
114 ASSERT_NO_THROW(watch->clearReady());
115
116 // Verify that trying to mark it fails.
117 ASSERT_THROW(watch->markReady(), WatchSocketError);
118
119 // Verify that clear does NOT throw.
120 ASSERT_NO_THROW(watch->clearReady());
121
122 // Verify that getSelectFd() returns invalid socket.
123 ASSERT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
124 }
125
126 /// @brief Checks behavior when select_fd has closed while in the "ready"
127 /// state.
TEST(WatchSocketTest,closedWhileReady)128 TEST(WatchSocketTest, closedWhileReady) {
129 WatchSocketPtr watch;
130
131 /// Verify that we can construct a WatchSocket.
132 ASSERT_NO_THROW(watch.reset(new WatchSocket()));
133 ASSERT_TRUE(watch);
134
135 /// Verify that post-construction the state the select-fd is valid.
136 int select_fd = watch->getSelectFd();
137 ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
138
139 /// Verify that the socket can be marked ready.
140 ASSERT_NO_THROW(watch->markReady());
141 EXPECT_EQ(1, selectCheck(select_fd));
142 EXPECT_TRUE(watch->isReady());
143
144 // Interfere by closing the fd.
145 ASSERT_EQ(0, close(select_fd));
146
147 // Verify that isReady() does not throw.
148 ASSERT_NO_THROW(watch->isReady());
149
150 // and return false.
151 EXPECT_FALSE(watch->isReady());
152
153 // Verify that trying to clear it does not throw.
154 ASSERT_NO_THROW(watch->clearReady());
155
156 // Verify the select_fd fails as socket is invalid/closed.
157 EXPECT_EQ(-1, selectCheck(select_fd));
158
159 // Verify that subsequent attempts to mark it will fail.
160 ASSERT_THROW(watch->markReady(), WatchSocketError);
161 }
162
163 /// @brief Checks behavior when select_fd has been marked ready but then
164 /// emptied by an external read.
TEST(WatchSocketTest,emptyReadySelectFd)165 TEST(WatchSocketTest, emptyReadySelectFd) {
166 WatchSocketPtr watch;
167
168 /// Verify that we can construct a WatchSocket.
169 ASSERT_NO_THROW(watch.reset(new WatchSocket()));
170 ASSERT_TRUE(watch);
171
172 /// Verify that post-construction the state the select-fd is valid.
173 int select_fd = watch->getSelectFd();
174 ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
175
176 /// Verify that the socket can be marked ready.
177 ASSERT_NO_THROW(watch->markReady());
178 EXPECT_TRUE(watch->isReady());
179 EXPECT_EQ(1, selectCheck(select_fd));
180
181 // Interfere by reading the fd. This should empty the read pipe.
182 uint32_t buf = 0;
183 ASSERT_EQ((read (select_fd, &buf, sizeof(buf))), sizeof(buf));
184 ASSERT_EQ(WatchSocket::MARKER, buf);
185
186 // Really nothing that can be done to protect against this, but let's
187 // make sure we aren't in a weird state.
188 ASSERT_NO_THROW(watch->clearReady());
189
190 // Verify the select_fd does not fail.
191 EXPECT_FALSE(watch->isReady());
192 EXPECT_EQ(0, selectCheck(select_fd));
193
194 // Verify that getSelectFd() returns is still good.
195 ASSERT_EQ(select_fd, watch->getSelectFd());
196 }
197
198 /// @brief Checks behavior when select_fd has been marked ready but then
199 /// contents have been "corrupted" by a partial read.
TEST(WatchSocketTest,badReadOnClear)200 TEST(WatchSocketTest, badReadOnClear) {
201 WatchSocketPtr watch;
202
203 /// Verify that we can construct a WatchSocket.
204 ASSERT_NO_THROW(watch.reset(new WatchSocket()));
205 ASSERT_TRUE(watch);
206
207 /// Verify that post-construction the state the select-fd is valid.
208 int select_fd = watch->getSelectFd();
209 ASSERT_NE(select_fd, WatchSocket::SOCKET_NOT_VALID);
210
211 /// Verify that the socket can be marked ready.
212 ASSERT_NO_THROW(watch->markReady());
213 EXPECT_TRUE(watch->isReady());
214 EXPECT_EQ(1, selectCheck(select_fd));
215
216 // Interfere by reading the fd. This should empty the read pipe.
217 uint32_t buf = 0;
218 ASSERT_EQ((read (select_fd, &buf, 1)), 1);
219 ASSERT_NE(WatchSocket::MARKER, buf);
220
221 // Really nothing that can be done to protect against this, but let's
222 // make sure we aren't in a weird state.
223 /// @todo maybe clear should never throw, log only
224 ASSERT_THROW(watch->clearReady(), WatchSocketError);
225
226 // Verify the select_fd does not evaluate to ready.
227 EXPECT_FALSE(watch->isReady());
228 EXPECT_NE(1, selectCheck(select_fd));
229
230 // Verify that getSelectFd() returns INVALID.
231 ASSERT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
232
233 // Verify that subsequent attempt to mark it fails.
234 ASSERT_THROW(watch->markReady(), WatchSocketError);
235 }
236
237 /// @brief Checks if the socket can be explicitly closed.
TEST(WatchSocketTest,explicitClose)238 TEST(WatchSocketTest, explicitClose) {
239 WatchSocketPtr watch;
240
241 // Create new instance of the socket.
242 ASSERT_NO_THROW(watch.reset(new WatchSocket()));
243 ASSERT_TRUE(watch);
244
245 // Make sure it has been opened by checking that its descriptor
246 // is valid.
247 EXPECT_NE(watch->getSelectFd(), WatchSocket::SOCKET_NOT_VALID);
248
249 // Close the socket.
250 std::string error_string;
251 ASSERT_TRUE(watch->closeSocket(error_string));
252
253 // Make sure that the descriptor is now invalid which indicates
254 // that the socket has been closed.
255 EXPECT_EQ(WatchSocket::SOCKET_NOT_VALID, watch->getSelectFd());
256 // No errors should be reported.
257 EXPECT_TRUE(error_string.empty());
258 // Not ready too.
259 ASSERT_NO_THROW(watch->isReady());
260 EXPECT_FALSE(watch->isReady());
261 }
262
263 } // end of anonymous namespace
264