| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/nfs4/rpc.cc |
| Date: | 2026-10-18 02:40:11 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 245 | 315 | 77.8% |
| Branches: | 150 | 326 | 46.0% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include "nfs4/rpc.h" | ||
| 6 | |||
| 7 | #include <arpa/inet.h> | ||
| 8 | #include <errno.h> | ||
| 9 | #include <netinet/in.h> | ||
| 10 | #include <netinet/tcp.h> | ||
| 11 | #include <poll.h> | ||
| 12 | #include <sys/socket.h> | ||
| 13 | #include <sys/uio.h> | ||
| 14 | #include <unistd.h> | ||
| 15 | |||
| 16 | #include <cstring> | ||
| 17 | |||
| 18 | #include "nfs4/logging.h" | ||
| 19 | |||
| 20 | namespace nfs4 { | ||
| 21 | |||
| 22 | namespace { | ||
| 23 | |||
| 24 | const uint32_t kMaxAuthBody = 400; | ||
| 25 | const uint32_t kMaxAuthSysGids = 16; | ||
| 26 | const uint32_t kLastFragment = 0x80000000u; | ||
| 27 | |||
| 28 | 65 | void PutReplyHeader(uint32_t xid, XdrWriter *w) { | |
| 29 | 65 | w->PutU32(xid); | |
| 30 | 65 | w->PutU32(kRpcReply); | |
| 31 | 65 | } | |
| 32 | |||
| 33 | 65 | void PutAccepted(uint32_t xid, AcceptStat stat, XdrWriter *w) { | |
| 34 | 65 | PutReplyHeader(xid, w); | |
| 35 | 65 | w->PutU32(kMsgAccepted); | |
| 36 | 65 | w->PutU32(kAuthNone); // verifier | |
| 37 | 65 | w->PutU32(0); | |
| 38 | 65 | w->PutU32(stat); | |
| 39 | 65 | } | |
| 40 | |||
| 41 | 26 | bool ParseAuthSys(const std::string &body, Credentials *creds) { | |
| 42 | 26 | XdrReader r(reinterpret_cast<const unsigned char *>(body.data()), | |
| 43 | 52 | body.size()); | |
| 44 | uint32_t stamp, ngids; | ||
| 45 | 26 | std::string machine; | |
| 46 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | r.GetU32(&stamp); |
| 47 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | r.GetString(&machine, 255); |
| 48 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | r.GetU32(&creds->uid); |
| 49 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | r.GetU32(&creds->gid); |
| 50 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | r.GetU32(&ngids); |
| 51 |
3/6✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 26 times.
|
26 | if (!r.ok() || ngids > kMaxAuthSysGids) |
| 52 | ✗ | return false; | |
| 53 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | creds->gids.resize(ngids); |
| 54 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | for (uint32_t i = 0; i < ngids; ++i) |
| 55 | ✗ | r.GetU32(&creds->gids[i]); | |
| 56 | 26 | return r.ok(); | |
| 57 | 26 | } | |
| 58 | |||
| 59 | 130 | bool ReadFull(int fd, void *buf, size_t len) { | |
| 60 | 130 | char *p = static_cast<char *>(buf); | |
| 61 |
2/2✓ Branch 0 taken 130 times.
✓ Branch 1 taken 104 times.
|
234 | while (len > 0) { |
| 62 | 130 | const ssize_t n = read(fd, p, len); | |
| 63 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 104 times.
|
130 | if (n == 0) |
| 64 | 26 | return false; | |
| 65 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
|
104 | if (n < 0) { |
| 66 | ✗ | if (errno == EINTR) | |
| 67 | ✗ | continue; | |
| 68 | ✗ | return false; | |
| 69 | } | ||
| 70 | 104 | p += n; | |
| 71 | 104 | len -= n; | |
| 72 | } | ||
| 73 | 104 | return true; | |
| 74 | } | ||
| 75 | |||
| 76 | /** Record marker and record in one system call where possible */ | ||
| 77 | 39 | bool WriteRecord(int fd, uint32_t marker, const std::string &record) { | |
| 78 | struct iovec iov[2]; | ||
| 79 | 39 | iov[0].iov_base = ▮ | |
| 80 | 39 | iov[0].iov_len = sizeof(marker); | |
| 81 | 39 | iov[1].iov_base = const_cast<char *>(record.data()); | |
| 82 | 39 | iov[1].iov_len = record.size(); | |
| 83 | 39 | int flags = 0; | |
| 84 | #ifdef MSG_NOSIGNAL | ||
| 85 | 39 | flags = MSG_NOSIGNAL; | |
| 86 | #endif | ||
| 87 | 39 | int iovcnt = 2; | |
| 88 | 39 | struct iovec *v = iov; | |
| 89 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
|
78 | while (iovcnt > 0) { |
| 90 | struct msghdr msg; | ||
| 91 | 39 | memset(&msg, 0, sizeof(msg)); | |
| 92 | 39 | msg.msg_iov = v; | |
| 93 | 39 | msg.msg_iovlen = iovcnt; | |
| 94 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | const ssize_t n = sendmsg(fd, &msg, flags); |
| 95 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if (n < 0) { |
| 96 | ✗ | if (errno == EINTR) | |
| 97 | ✗ | continue; | |
| 98 | ✗ | return false; | |
| 99 | } | ||
| 100 | 39 | size_t done = n; | |
| 101 |
3/4✓ Branch 0 taken 78 times.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
|
117 | while (iovcnt > 0 && done >= v->iov_len) { |
| 102 | 78 | done -= v->iov_len; | |
| 103 | 78 | v++; | |
| 104 | 78 | iovcnt--; | |
| 105 | } | ||
| 106 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if (iovcnt > 0) { |
| 107 | ✗ | v->iov_base = static_cast<char *>(v->iov_base) + done; | |
| 108 | ✗ | v->iov_len -= done; | |
| 109 | } | ||
| 110 | } | ||
| 111 | 39 | return true; | |
| 112 | } | ||
| 113 | |||
| 114 | 26 | bool IsLoopback(const struct sockaddr_storage &addr) { | |
| 115 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | if (addr.ss_family == AF_INET) { |
| 116 | 26 | const struct sockaddr_in *in = reinterpret_cast<const struct sockaddr_in *>( | |
| 117 | &addr); | ||
| 118 | 26 | return (ntohl(in->sin_addr.s_addr) >> 24) == 127; | |
| 119 | } | ||
| 120 | ✗ | if (addr.ss_family == AF_INET6) { | |
| 121 | const struct sockaddr_in6 | ||
| 122 | ✗ | *in6 = reinterpret_cast<const struct sockaddr_in6 *>(&addr); | |
| 123 | ✗ | return IN6_IS_ADDR_LOOPBACK(&in6->sin6_addr); | |
| 124 | } | ||
| 125 | ✗ | return false; | |
| 126 | } | ||
| 127 | |||
| 128 | } // anonymous namespace | ||
| 129 | |||
| 130 | |||
| 131 | 78 | bool HandleRpcRecord(RpcProgram *program, const std::string &record, | |
| 132 | std::string *reply) { | ||
| 133 | 78 | XdrReader r(reinterpret_cast<const unsigned char *>(record.data()), | |
| 134 | 156 | record.size()); | |
| 135 | 78 | RpcCall call; | |
| 136 | uint32_t msg_type, rpc_version; | ||
| 137 |
8/12✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 65 times.
✓ Branch 4 taken 13 times.
✓ Branch 6 taken 65 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 65 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 65 times.
✓ Branch 12 taken 13 times.
✓ Branch 13 taken 65 times.
|
78 | if (!r.GetU32(&call.xid) || !r.GetU32(&msg_type) || msg_type != kRpcCall) |
| 138 | 13 | return false; | |
| 139 | |||
| 140 | 65 | XdrWriter w; | |
| 141 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetU32(&rpc_version); |
| 142 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetU32(&call.program); |
| 143 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetU32(&call.version); |
| 144 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetU32(&call.procedure); |
| 145 | 65 | std::string cred_body, verf_body; | |
| 146 | uint32_t verf_flavor; | ||
| 147 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetU32(&call.cred_flavor); |
| 148 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetOpaque(&cred_body, kMaxAuthBody); |
| 149 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetU32(&verf_flavor); |
| 150 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
65 | r.GetOpaque(&verf_body, kMaxAuthBody); |
| 151 |
2/2✓ Branch 1 taken 13 times.
✓ Branch 2 taken 52 times.
|
65 | if (!r.ok()) { |
| 152 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
13 | Log(kLogLevelWarn, "garbage RPC header (xid %u)", call.xid); |
| 153 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
13 | PutAccepted(call.xid, kAcceptGarbageArgs, &w); |
| 154 | 13 | reply->swap(*w.mutable_buffer()); | |
| 155 | 13 | return true; | |
| 156 | } | ||
| 157 | |||
| 158 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if (rpc_version != kRpcVersion) { |
| 159 | ✗ | PutReplyHeader(call.xid, &w); | |
| 160 | ✗ | w.PutU32(kMsgDenied); | |
| 161 | ✗ | w.PutU32(kRpcMismatch); | |
| 162 | ✗ | w.PutU32(kRpcVersion); | |
| 163 | ✗ | w.PutU32(kRpcVersion); | |
| 164 | ✗ | reply->swap(*w.mutable_buffer()); | |
| 165 | ✗ | return true; | |
| 166 | } | ||
| 167 | |||
| 168 | 52 | bool auth_ok = false; | |
| 169 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 26 times.
|
52 | if (call.cred_flavor == kAuthSys) |
| 170 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | auth_ok = ParseAuthSys(cred_body, &call.creds); |
| 171 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | else if (call.cred_flavor == kAuthNone) |
| 172 | 26 | auth_ok = true; | |
| 173 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
52 | if (!auth_ok) { |
| 174 | ✗ | PutReplyHeader(call.xid, &w); | |
| 175 | ✗ | w.PutU32(kMsgDenied); | |
| 176 | ✗ | w.PutU32(kAuthError); | |
| 177 | ✗ | w.PutU32(call.cred_flavor == kAuthSys ? kAuthBadCred : kAuthTooWeak); | |
| 178 | ✗ | reply->swap(*w.mutable_buffer()); | |
| 179 | ✗ | return true; | |
| 180 | } | ||
| 181 | |||
| 182 |
2/4✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 52 times.
|
52 | if (call.program != program->program()) { |
| 183 | ✗ | Log(kLogLevelWarn, "call for unknown program %u (xid %u)", call.program, | |
| 184 | call.xid); | ||
| 185 | ✗ | PutAccepted(call.xid, kAcceptProgUnavail, &w); | |
| 186 | ✗ | reply->swap(*w.mutable_buffer()); | |
| 187 | ✗ | return true; | |
| 188 | } | ||
| 189 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
52 | if (call.version < program->min_version() |
| 190 |
6/8✓ Branch 0 taken 39 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 39 times.
✓ Branch 7 taken 13 times.
✓ Branch 8 taken 39 times.
|
52 | || call.version > program->max_version()) { |
| 191 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
13 | PutAccepted(call.xid, kAcceptProgMismatch, &w); |
| 192 |
2/4✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
|
13 | w.PutU32(program->min_version()); |
| 193 |
2/4✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
|
13 | w.PutU32(program->max_version()); |
| 194 | 13 | reply->swap(*w.mutable_buffer()); | |
| 195 | 13 | return true; | |
| 196 | } | ||
| 197 | |||
| 198 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | Log(kLogLevelDebug, "call xid %u proc %u", call.xid, call.procedure); |
| 199 | 39 | call.args = r.cursor(); | |
| 200 | 39 | call.args_size = r.remaining(); | |
| 201 | 39 | XdrReader args(call.args, call.args_size); | |
| 202 | // Results go right behind the header, the accept status is patched in | ||
| 203 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | PutAccepted(call.xid, kAcceptSuccess, &w); |
| 204 | 39 | const size_t stat_offset = w.size() - 4; | |
| 205 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | const AcceptStat stat = program->Dispatch(call, &args, &w); |
| 206 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | if (stat != kAcceptSuccess) { |
| 207 | ✗ | w.Truncate(stat_offset + 4); | |
| 208 | ✗ | w.PatchU32(stat_offset, stat); | |
| 209 | } | ||
| 210 | 39 | reply->swap(*w.mutable_buffer()); | |
| 211 | 39 | return true; | |
| 212 | 78 | } | |
| 213 | |||
| 214 | |||
| 215 | 26 | RpcServer::Connection::~Connection() { close(fd); } | |
| 216 | |||
| 217 | |||
| 218 | 26 | RpcServer::RpcServer(RpcProgram *program, const Options &options) | |
| 219 | 26 | : program_(program) | |
| 220 | 26 | , options_(options) | |
| 221 | 26 | , listen_fd_(-1) | |
| 222 | 26 | , port_(0) | |
| 223 | 26 | , stopping_(false) | |
| 224 |
1/2✓ Branch 8 taken 26 times.
✗ Branch 9 not taken.
|
26 | , active_readers_(0) { } |
| 225 | |||
| 226 | |||
| 227 | 26 | RpcServer::~RpcServer() { | |
| 228 | 26 | Stop(); | |
| 229 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | if (listen_fd_ >= 0) |
| 230 | 26 | close(listen_fd_); | |
| 231 | 26 | } | |
| 232 | |||
| 233 | |||
| 234 | 26 | bool RpcServer::Listen(const std::string &address, uint16_t port) { | |
| 235 | struct sockaddr_in addr; | ||
| 236 | 26 | memset(&addr, 0, sizeof(addr)); | |
| 237 | 26 | addr.sin_family = AF_INET; | |
| 238 | 26 | addr.sin_port = htons(port); | |
| 239 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
|
26 | if (inet_pton(AF_INET, address.c_str(), &addr.sin_addr) != 1) { |
| 240 | ✗ | Log(kLogLevelError, "invalid listen address %s", | |
| 241 | address.c_str()); | ||
| 242 | ✗ | return false; | |
| 243 | } | ||
| 244 | |||
| 245 | 26 | listen_fd_ = socket(AF_INET, SOCK_STREAM, 0); | |
| 246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (listen_fd_ < 0) |
| 247 | ✗ | return false; | |
| 248 | 26 | const int on = 1; | |
| 249 | 26 | setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | |
| 250 | 26 | if (bind(listen_fd_, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) | |
| 251 | != 0 | ||
| 252 |
3/6✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 26 times.
|
26 | || listen(listen_fd_, 64) != 0) { |
| 253 | ✗ | Log(kLogLevelError, "failed to listen on %s:%u (%d)", address.c_str(), port, errno); | |
| 254 | ✗ | close(listen_fd_); | |
| 255 | ✗ | listen_fd_ = -1; | |
| 256 | ✗ | return false; | |
| 257 | } | ||
| 258 | |||
| 259 | 26 | socklen_t len = sizeof(addr); | |
| 260 | 26 | getsockname(listen_fd_, reinterpret_cast<struct sockaddr *>(&addr), &len); | |
| 261 | 26 | port_ = ntohs(addr.sin_port); | |
| 262 | 26 | return true; | |
| 263 | } | ||
| 264 | |||
| 265 | |||
| 266 | ✗ | bool RpcServer::Adopt(int listen_fd) { | |
| 267 | struct sockaddr_in addr; | ||
| 268 | ✗ | socklen_t len = sizeof(addr); | |
| 269 | ✗ | if (getsockname(listen_fd, reinterpret_cast<struct sockaddr *>(&addr), &len) | |
| 270 | ✗ | != 0) { | |
| 271 | ✗ | return false; | |
| 272 | } | ||
| 273 | ✗ | listen_fd_ = listen_fd; | |
| 274 | ✗ | port_ = ntohs(addr.sin_port); | |
| 275 | ✗ | return true; | |
| 276 | } | ||
| 277 | |||
| 278 | |||
| 279 | 26 | void RpcServer::Start() { | |
| 280 | 26 | stopping_ = false; | |
| 281 |
2/2✓ Branch 0 taken 234 times.
✓ Branch 1 taken 26 times.
|
260 | for (unsigned i = 0; i < options_.workers; ++i) |
| 282 |
2/4✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 234 times.
✗ Branch 5 not taken.
|
234 | workers_.push_back(std::thread(&RpcServer::WorkerLoop, this)); |
| 283 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | accept_thread_ = std::thread(&RpcServer::AcceptLoop, this); |
| 284 | 26 | } | |
| 285 | |||
| 286 | |||
| 287 | 52 | void RpcServer::Stop() { | |
| 288 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 2 taken 26 times.
|
52 | if (stopping_.exchange(true)) |
| 289 | 26 | return; | |
| 290 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | if (accept_thread_.joinable()) |
| 291 | 26 | accept_thread_.join(); | |
| 292 | |||
| 293 | { | ||
| 294 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | std::unique_lock<std::mutex> lock(connections_lock_); |
| 295 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 2 taken 26 times.
|
52 | for (unsigned i = 0; i < connections_.size(); ++i) { |
| 296 | 26 | ConnectionPtr const c = connections_[i].lock(); | |
| 297 |
2/2✓ Branch 1 taken 13 times.
✓ Branch 2 taken 13 times.
|
26 | if (c) |
| 298 | 13 | shutdown(c->fd, SHUT_RDWR); | |
| 299 | 26 | } | |
| 300 | 65 | readers_cond_.wait(lock, [this] { return active_readers_ == 0; }); | |
| 301 | 26 | connections_.clear(); | |
| 302 | 26 | } | |
| 303 | |||
| 304 | 26 | queue_cond_.notify_all(); | |
| 305 |
2/2✓ Branch 1 taken 234 times.
✓ Branch 2 taken 26 times.
|
260 | for (unsigned i = 0; i < workers_.size(); ++i) |
| 306 | 234 | workers_[i].join(); | |
| 307 | 26 | workers_.clear(); | |
| 308 | } | ||
| 309 | |||
| 310 | |||
| 311 | ✗ | unsigned RpcServer::NumConnections() { | |
| 312 | ✗ | std::lock_guard<std::mutex> const lock(connections_lock_); | |
| 313 | ✗ | unsigned n = 0; | |
| 314 | ✗ | for (unsigned i = 0; i < connections_.size(); ++i) { | |
| 315 | ✗ | if (!connections_[i].expired()) | |
| 316 | ✗ | n++; | |
| 317 | } | ||
| 318 | ✗ | return n; | |
| 319 | } | ||
| 320 | |||
| 321 | |||
| 322 | 26 | void RpcServer::AcceptLoop() { | |
| 323 |
2/2✓ Branch 1 taken 169 times.
✓ Branch 2 taken 26 times.
|
195 | while (!stopping_) { |
| 324 | struct pollfd pfd; | ||
| 325 | 169 | pfd.fd = listen_fd_; | |
| 326 | 169 | pfd.events = POLLIN; | |
| 327 | 169 | pfd.revents = 0; | |
| 328 |
1/2✓ Branch 1 taken 169 times.
✗ Branch 2 not taken.
|
169 | const int retval = poll(&pfd, 1, 200); |
| 329 |
2/2✓ Branch 0 taken 143 times.
✓ Branch 1 taken 26 times.
|
169 | if (retval <= 0) |
| 330 | 143 | continue; | |
| 331 | |||
| 332 | struct sockaddr_storage peer; | ||
| 333 | 26 | socklen_t peer_len = sizeof(peer); | |
| 334 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | const int fd = accept( |
| 335 | listen_fd_, reinterpret_cast<struct sockaddr *>(&peer), &peer_len); | ||
| 336 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if (fd < 0) |
| 337 | ✗ | continue; | |
| 338 |
3/6✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 26 times.
|
26 | if (!options_.allow_remote && !IsLoopback(peer)) { |
| 339 | ✗ | Log(kLogLevelWarn, "rejected NFS connection from non-loopback peer"); | |
| 340 | ✗ | close(fd); | |
| 341 | ✗ | continue; | |
| 342 | } | ||
| 343 | 26 | const int on = 1; | |
| 344 | 26 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); | |
| 345 | #ifdef SO_NOSIGPIPE | ||
| 346 | setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); | ||
| 347 | #endif | ||
| 348 | |||
| 349 |
2/4✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
|
26 | ConnectionPtr const connection(new Connection(fd)); |
| 350 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | std::lock_guard<std::mutex> const lock(connections_lock_); |
| 351 | // Drop entries of closed connections | ||
| 352 | 26 | std::vector<std::weak_ptr<Connection> > alive; | |
| 353 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
|
26 | for (unsigned i = 0; i < connections_.size(); ++i) { |
| 354 | ✗ | if (!connections_[i].expired()) | |
| 355 | ✗ | alive.push_back(connections_[i]); | |
| 356 | } | ||
| 357 |
1/2✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
|
26 | alive.push_back(connection); |
| 358 | 26 | connections_.swap(alive); | |
| 359 | 26 | active_readers_++; | |
| 360 |
2/4✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
|
26 | std::thread(&RpcServer::ReadLoop, this, connection).detach(); |
| 361 | 26 | } | |
| 362 | 26 | } | |
| 363 | |||
| 364 | |||
| 365 | 26 | void RpcServer::ReadLoop(ConnectionPtr connection) { | |
| 366 | 26 | std::string record; | |
| 367 |
1/2✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
|
78 | while (!stopping_) { |
| 368 | uint32_t marker; | ||
| 369 |
3/4✓ Branch 2 taken 78 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✓ Branch 5 taken 52 times.
|
78 | if (!ReadFull(connection->fd, &marker, sizeof(marker))) |
| 370 | 26 | break; | |
| 371 | 52 | marker = ntohl(marker); | |
| 372 | 52 | const uint32_t len = marker & ~kLastFragment; | |
| 373 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
|
52 | if (record.size() + len > options_.max_record) { |
| 374 | ✗ | Log(kLogLevelWarn, "RPC record exceeds %u bytes, closing connection", | |
| 375 | options_.max_record); | ||
| 376 | ✗ | break; | |
| 377 | } | ||
| 378 | 52 | const size_t offset = record.size(); | |
| 379 |
1/2✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
|
52 | record.resize(offset + len); |
| 380 |
5/10✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 52 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 52 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 52 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 52 times.
|
52 | if (len > 0 && !ReadFull(connection->fd, &record[offset], len)) |
| 381 | ✗ | break; | |
| 382 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 39 times.
|
52 | if (!(marker & kLastFragment)) |
| 383 | 13 | continue; | |
| 384 | |||
| 385 | 39 | Job job; | |
| 386 | 39 | job.connection = connection; | |
| 387 | 39 | job.xid = 0; | |
| 388 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | if (record.size() >= 4) { |
| 389 | 39 | memcpy(&job.xid, record.data(), 4); | |
| 390 | 39 | job.xid = ntohl(job.xid); | |
| 391 | } | ||
| 392 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | Log(kLogLevelDebug, "recv xid %u (%zu bytes)", job.xid, record.size()); |
| 393 | { | ||
| 394 | // A retransmission of a call that is still being processed is | ||
| 395 | // answered by the original | ||
| 396 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | std::lock_guard<std::mutex> const lock(connection->inflight_lock); |
| 397 |
2/4✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 39 times.
|
39 | if (!connection->inflight.insert(job.xid).second) { |
| 398 | ✗ | Log(kLogLevelDebug, "dropping retransmission of xid %u", job.xid); | |
| 399 | ✗ | record.clear(); | |
| 400 | ✗ | continue; | |
| 401 | } | ||
| 402 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | } |
| 403 | 39 | job.record.swap(record); | |
| 404 | { | ||
| 405 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | std::lock_guard<std::mutex> const lock(queue_lock_); |
| 406 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | queue_.push_back(Job()); |
| 407 | 39 | queue_.back().connection.swap(job.connection); | |
| 408 | 39 | queue_.back().xid = job.xid; | |
| 409 | 39 | queue_.back().record.swap(job.record); | |
| 410 | 39 | } | |
| 411 | 39 | queue_cond_.notify_one(); | |
| 412 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | } |
| 413 | |||
| 414 | 26 | shutdown(connection->fd, SHUT_RDWR); | |
| 415 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | std::lock_guard<std::mutex> const lock(connections_lock_); |
| 416 | 26 | active_readers_--; | |
| 417 | 26 | readers_cond_.notify_all(); | |
| 418 | 26 | } | |
| 419 | |||
| 420 | |||
| 421 | 273 | void RpcServer::WorkerLoop() { | |
| 422 | while (true) { | ||
| 423 | 273 | Job job; | |
| 424 | { | ||
| 425 |
1/2✓ Branch 1 taken 273 times.
✗ Branch 2 not taken.
|
273 | std::unique_lock<std::mutex> lock(queue_lock_); |
| 426 |
4/4✓ Branch 2 taken 312 times.
✓ Branch 3 taken 234 times.
✓ Branch 5 taken 39 times.
✓ Branch 6 taken 273 times.
|
819 | queue_cond_.wait(lock, [this] { return stopping_ || !queue_.empty(); }); |
| 427 |
2/2✓ Branch 1 taken 234 times.
✓ Branch 2 taken 39 times.
|
273 | if (queue_.empty()) |
| 428 | 468 | return; | |
| 429 | 39 | job.connection.swap(queue_.front().connection); | |
| 430 | 39 | job.xid = queue_.front().xid; | |
| 431 | 39 | job.record.swap(queue_.front().record); | |
| 432 | 39 | queue_.pop_front(); | |
| 433 |
2/2✓ Branch 1 taken 39 times.
✓ Branch 2 taken 234 times.
|
273 | } |
| 434 | |||
| 435 | 39 | std::string reply; | |
| 436 |
2/4✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 39 times.
✗ Branch 4 not taken.
|
39 | if (HandleRpcRecord(program_, job.record, &reply)) { |
| 437 |
1/2✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
|
39 | Send(job.connection, reply); |
| 438 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | Log(kLogLevelDebug, "sent xid %u (%zu bytes)", job.xid, reply.size()); |
| 439 | } else { | ||
| 440 | ✗ | Log(kLogLevelWarn, "ignoring record that is not a call (xid %u)", | |
| 441 | job.xid); | ||
| 442 | } | ||
| 443 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | std::lock_guard<std::mutex> const lock(job.connection->inflight_lock); |
| 444 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | job.connection->inflight.erase(job.xid); |
| 445 |
2/2✓ Branch 3 taken 39 times.
✓ Branch 4 taken 234 times.
|
312 | } |
| 446 | } | ||
| 447 | |||
| 448 | |||
| 449 | 39 | void RpcServer::Send(const ConnectionPtr &connection, | |
| 450 | const std::string &reply) { | ||
| 451 | 39 | const uint32_t marker = htonl(kLastFragment | |
| 452 | 39 | | static_cast<uint32_t>(reply.size())); | |
| 453 |
1/2✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
|
39 | std::lock_guard<std::mutex> const lock(connection->write_lock); |
| 454 |
2/4✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 39 times.
|
39 | if (!WriteRecord(connection->fd, marker, reply)) { |
| 455 | ✗ | Log(kLogLevelWarn, "failed to send reply (%d), closing connection", errno); | |
| 456 | ✗ | shutdown(connection->fd, SHUT_RDWR); | |
| 457 | } | ||
| 458 | 39 | } | |
| 459 | |||
| 460 | } // namespace nfs4 | ||
| 461 |