1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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
|