void AsyncServerSocket::handlerReady(uint16_t /* events */, int fd, sa_family_t addressFamily) noexcept { assert(!callbacks_.empty()); DestructorGuard dg(this); // Only accept up to maxAcceptAtOnce_ connections at a time, // to avoid starving other I/O handlers using this EventBase. for (uint32_t n = 0; n < maxAcceptAtOnce_; ++n) { SocketAddress address; sockaddr_storage addrStorage; socklen_t addrLen = sizeof(addrStorage); sockaddr* saddr = reinterpret_cast<sockaddr*>(&addrStorage); // In some cases, accept() doesn't seem to update these correctly. saddr->sa_family = addressFamily; if (addressFamily == AF_UNIX) { addrLen = sizeof(struct sockaddr_un); } // Accept a new client socket #ifdef SOCK_NONBLOCK int clientSocket = accept4(fd, saddr, &addrLen, SOCK_NONBLOCK); #else int clientSocket = accept(fd, saddr, &addrLen); #endif address.setFromSockaddr(saddr, addrLen); if (clientSocket >= 0 && connectionEventCallback_) { connectionEventCallback_->onConnectionAccepted(clientSocket, address); } std::chrono::time_point<std::chrono::steady_clock> nowMs = std::chrono::steady_clock::now(); auto timeSinceLastAccept = std::max<int64_t>( 0, nowMs.time_since_epoch().count() - lastAccepTimestamp_.time_since_epoch().count()); lastAccepTimestamp_ = nowMs; if (acceptRate_ < 1) { acceptRate_ *= 1 + acceptRateAdjustSpeed_ * timeSinceLastAccept; if (acceptRate_ >= 1) { acceptRate_ = 1; } else if (rand() > acceptRate_ * RAND_MAX) { ++numDroppedConnections_; if (clientSocket >= 0) { closeNoInt(clientSocket); if (connectionEventCallback_) { connectionEventCallback_->onConnectionDropped(clientSocket, address); } } continue; } } if (clientSocket < 0) { if (errno == EAGAIN) { // No more sockets to accept right now. // Check for this code first, since it's the most common. return; } else if (errno == EMFILE || errno == ENFILE) { // We're out of file descriptors. Perhaps we're accepting connections // too quickly. Pause accepting briefly to back off and give the server // a chance to recover. LOG(ERROR) << "accept failed: out of file descriptors; entering accept " "back-off state"; enterBackoff(); // Dispatch the error message dispatchError("accept() failed", errno); } else { dispatchError("accept() failed", errno); } if (connectionEventCallback_) { connectionEventCallback_->onConnectionAcceptError(errno); } return; } #ifndef SOCK_NONBLOCK // Explicitly set the new connection to non-blocking mode if (fcntl(clientSocket, F_SETFL, O_NONBLOCK) != 0) { closeNoInt(clientSocket); dispatchError("failed to set accepted socket to non-blocking mode", errno); if (connectionEventCallback_) { connectionEventCallback_->onConnectionDropped(clientSocket, address); } return; } #endif // Inform the callback about the new connection dispatchSocket(clientSocket, std::move(address)); // If we aren't accepting any more, break out of the loop if (!accepting_ || callbacks_.empty()) { break; } } }
void AsyncServerSocket::bind(uint16_t port) { struct addrinfo hints, *res0; char sport[sizeof("65536")]; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; snprintf(sport, sizeof(sport), "%u", port); // On Windows the value we need to pass to bind to all available // addresses is an empty string. Everywhere else, it's nullptr. constexpr const char* kWildcardNode = kIsWindows ? "" : nullptr; if (getaddrinfo(kWildcardNode, sport, &hints, &res0)) { throw std::invalid_argument( "Attempted to bind address to socket with " "bad getaddrinfo"); } SCOPE_EXIT { freeaddrinfo(res0); }; auto setupAddress = [&] (struct addrinfo* res) { int s = fsp::socket(res->ai_family, res->ai_socktype, res->ai_protocol); // IPv6/IPv4 may not be supported by the kernel if (s < 0 && errno == EAFNOSUPPORT) { return; } CHECK_GE(s, 0); try { setupSocket(s, res->ai_family); } catch (...) { closeNoInt(s); throw; } if (res->ai_family == AF_INET6) { int v6only = 1; CHECK(0 == setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only))); } // Bind to the socket if (fsp::bind(s, res->ai_addr, socklen_t(res->ai_addrlen)) != 0) { folly::throwSystemError( errno, "failed to bind to async server socket for port ", SocketAddress::getPortFrom(res->ai_addr), " family ", SocketAddress::getFamilyNameFrom(res->ai_addr, "<unknown>")); } #if __linux__ if (noTransparentTls_) { // Ignore return value, errors are ok setsockopt(s, SOL_SOCKET, SO_NO_TRANSPARENT_TLS, nullptr, 0); } #endif SocketAddress address; address.setFromLocalAddress(s); sockets_.emplace_back(eventBase_, s, this, address.getFamily()); }; const int kNumTries = 25; for (int tries = 1; true; tries++) { // Prefer AF_INET6 addresses. RFC 3484 mandates that getaddrinfo // should return IPv6 first and then IPv4 addresses, but glibc's // getaddrinfo(nullptr) with AI_PASSIVE returns: // - 0.0.0.0 (IPv4-only) // - :: (IPv6+IPv4) in this order // See: https://sourceware.org/bugzilla/show_bug.cgi?id=9981 for (struct addrinfo* res = res0; res; res = res->ai_next) { if (res->ai_family == AF_INET6) { setupAddress(res); } } // If port == 0, then we should try to bind to the same port on ipv4 and // ipv6. So if we did bind to ipv6, figure out that port and use it. if (sockets_.size() == 1 && port == 0) { SocketAddress address; address.setFromLocalAddress(sockets_.back().socket_); snprintf(sport, sizeof(sport), "%u", address.getPort()); freeaddrinfo(res0); CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); } try { for (struct addrinfo* res = res0; res; res = res->ai_next) { if (res->ai_family != AF_INET6) { setupAddress(res); } } } catch (const std::system_error&) { // If we can't bind to the same port on ipv4 as ipv6 when using // port=0 then we will retry again before giving up after // kNumTries attempts. We do this by closing the sockets that // were opened, then restarting from scratch. if (port == 0 && !sockets_.empty() && tries != kNumTries) { for (const auto& socket : sockets_) { if (socket.socket_ <= 0) { continue; } else if (shutdownSocketSet_) { shutdownSocketSet_->close(socket.socket_); } else { closeNoInt(socket.socket_); } } sockets_.clear(); snprintf(sport, sizeof(sport), "%u", port); freeaddrinfo(res0); CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); continue; } throw; } break; } if (sockets_.size() == 0) { throw std::runtime_error( "did not bind any async server socket for port"); } }
Symbolizer::Symbolizer(ElfCacheBase* cache) : cache_(cache ?: defaultElfCache()) { } void Symbolizer::symbolize(const uintptr_t* addresses, SymbolizedFrame* frames, size_t addressCount) { size_t remaining = 0; for (size_t i = 0; i < addressCount; ++i) { auto& frame = frames[i]; if (!frame.found) { ++remaining; frame.clear(); } } if (remaining == 0) { // we're done return; } int fd = openNoInt("/proc/self/maps", O_RDONLY); if (fd == -1) { return; } char buf[PATH_MAX + 100]; // Long enough for any line LineReader reader(fd, buf, sizeof(buf)); while (remaining != 0) { StringPiece line; if (reader.readLine(line) != LineReader::kReading) { break; } // Parse line uintptr_t from; uintptr_t to; StringPiece fileName; if (!parseProcMapsLine(line, from, to, fileName)) { continue; } bool first = true; std::shared_ptr<ElfFile> elfFile; // See if any addresses are here for (size_t i = 0; i < addressCount; ++i) { auto& frame = frames[i]; if (frame.found) { continue; } uintptr_t address = addresses[i]; if (from > address || address >= to) { continue; } // Found frame.found = true; --remaining; // Open the file on first use if (first) { first = false; elfFile = cache_->getFile(fileName); } if (!elfFile) { continue; } // Undo relocation frame.set(elfFile, address - from); } } closeNoInt(fd); }
void Symbolizer::symbolize(const uintptr_t* addresses, SymbolizedFrame* frames, size_t addressCount) { size_t remaining = 0; for (size_t i = 0; i < addressCount; ++i) { auto& frame = frames[i]; if (!frame.found) { ++remaining; frame.name.clear(); frame.location = Dwarf::LocationInfo(); } } if (remaining == 0) { // we're done return; } int fd = openNoInt("/proc/self/maps", O_RDONLY); if (fd == -1) { return; } char buf[PATH_MAX + 100]; // Long enough for any line LineReader reader(fd, buf, sizeof(buf)); char fileNameBuf[PATH_MAX]; while (remaining != 0) { StringPiece line; if (reader.readLine(line) != LineReader::kReading) { break; } // Parse line uintptr_t from; uintptr_t to; StringPiece fileName; if (!parseProcMapsLine(line, from, to, fileName)) { continue; } bool first = true; ElfFile* elfFile = nullptr; // See if any addresses are here for (size_t i = 0; i < addressCount; ++i) { auto& frame = frames[i]; if (frame.found) { continue; } uintptr_t address = addresses[i]; if (from > address || address >= to) { continue; } // Found frame.found = true; --remaining; // Open the file on first use if (first) { first = false; if (fileCount_ < kMaxFiles && !fileName.empty() && fileName.size() < sizeof(fileNameBuf)) { memcpy(fileNameBuf, fileName.data(), fileName.size()); fileNameBuf[fileName.size()] = '\0'; auto& f = files_[fileCount_++]; if (f.openNoThrow(fileNameBuf) != -1) { elfFile = &f; } } } if (!elfFile) { continue; } // Undo relocation uintptr_t fileAddress = address - from + elfFile->getBaseAddress(); auto sym = elfFile->getDefinitionByAddress(fileAddress); if (!sym.first) { continue; } auto name = elfFile->getSymbolName(sym); if (name) { frame.name = name; } Dwarf(elfFile).findAddress(fileAddress, frame.location); } } closeNoInt(fd); }
void AsyncServerSocket::dispatchSocket(int socket, SocketAddress&& address) { uint32_t startingIndex = callbackIndex_; // Short circuit if the callback is in the primary EventBase thread CallbackInfo *info = nextCallback(); if (info->eventBase == nullptr) { info->callback->connectionAccepted(socket, address); return; } const SocketAddress addr(address); // Create a message to send over the notification queue QueueMessage msg; msg.type = MessageType::MSG_NEW_CONN; msg.address = std::move(address); msg.fd = socket; // Loop until we find a free queue to write to while (true) { if (info->consumer->getQueue()->tryPutMessageNoThrow(std::move(msg))) { if (connectionEventCallback_) { connectionEventCallback_->onConnectionEnqueuedForAcceptorCallback( socket, addr); } // Success! return. return; } // We couldn't add to queue. Fall through to below ++numDroppedConnections_; if (acceptRateAdjustSpeed_ > 0) { // aggressively decrease accept rate when in trouble static const double kAcceptRateDecreaseSpeed = 0.1; acceptRate_ *= 1 - kAcceptRateDecreaseSpeed; } if (callbackIndex_ == startingIndex) { // The notification queue was full // We can't really do anything at this point other than close the socket. // // This should only happen if a user's service is behaving extremely // badly and none of the EventBase threads are looping fast enough to // process the incoming connections. If the service is overloaded, it // should use pauseAccepting() to temporarily back off accepting new // connections, before they reach the point where their threads can't // even accept new messages. LOG(ERROR) << "failed to dispatch newly accepted socket:" << " all accept callback queues are full"; closeNoInt(socket); if (connectionEventCallback_) { connectionEventCallback_->onConnectionDropped(socket, addr); } return; } info = nextCallback(); } }
void AsyncServerSocket::bind(uint16_t port) { struct addrinfo hints, *res, *res0; char sport[sizeof("65536")]; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; snprintf(sport, sizeof(sport), "%u", port); if (getaddrinfo(nullptr, sport, &hints, &res0)) { throw std::invalid_argument( "Attempted to bind address to socket with " "bad getaddrinfo"); } SCOPE_EXIT { freeaddrinfo(res0); }; auto setupAddress = [&] (struct addrinfo* res) { int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); // IPv6/IPv4 may not be supported by the kernel if (s < 0 && errno == EAFNOSUPPORT) { return; } CHECK_GE(s, 0); try { setupSocket(s); } catch (...) { closeNoInt(s); throw; } if (res->ai_family == AF_INET6) { int v6only = 1; CHECK(0 == setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only))); } SocketAddress address; address.setFromLocalAddress(s); sockets_.emplace_back(eventBase_, s, this, address.getFamily()); // Bind to the socket if (::bind(s, res->ai_addr, res->ai_addrlen) != 0) { folly::throwSystemError( errno, "failed to bind to async server socket for port"); } }; const int kNumTries = 25; for (int tries = 1; true; tries++) { // Prefer AF_INET6 addresses. RFC 3484 mandates that getaddrinfo // should return IPv6 first and then IPv4 addresses, but glibc's // getaddrinfo(nullptr) with AI_PASSIVE returns: // - 0.0.0.0 (IPv4-only) // - :: (IPv6+IPv4) in this order // See: https://sourceware.org/bugzilla/show_bug.cgi?id=9981 for (res = res0; res; res = res->ai_next) { if (res->ai_family == AF_INET6) { setupAddress(res); } } // If port == 0, then we should try to bind to the same port on ipv4 and // ipv6. So if we did bind to ipv6, figure out that port and use it, // except for the last attempt when we just use any port available. if (sockets_.size() == 1 && port == 0) { SocketAddress address; address.setFromLocalAddress(sockets_.back().socket_); snprintf(sport, sizeof(sport), "%u", address.getPort()); freeaddrinfo(res0); CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); } try { for (res = res0; res; res = res->ai_next) { if (res->ai_family != AF_INET6) { setupAddress(res); } } } catch (const std::system_error& e) { // if we can't bind to the same port on ipv4 as ipv6 when using port=0 // then we will try again another 2 times before giving up. We do this // by closing the sockets that were opened, then redoing the whole thing if (port == 0 && !sockets_.empty() && tries != kNumTries) { for (const auto& socket : sockets_) { if (socket.socket_ <= 0) { continue; } else if (shutdownSocketSet_) { shutdownSocketSet_->close(socket.socket_); } else { closeNoInt(socket.socket_); } } sockets_.clear(); snprintf(sport, sizeof(sport), "%u", port); freeaddrinfo(res0); CHECK_EQ(0, getaddrinfo(nullptr, sport, &hints, &res0)); continue; } throw; } break; } if (sockets_.size() == 0) { throw std::runtime_error( "did not bind any async server socket for port"); } }