diff --git a/connection/server.cpp b/connection/server.cpp index 74e0400..1a0d40a 100644 --- a/connection/server.cpp +++ b/connection/server.cpp @@ -1,144 +1,144 @@ /* Copyright (C) 2017 Andreas Hartmetz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LGPL. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Alternatively, this file is available under the Mozilla Public License Version 1.1. You may obtain a copy of the License at http://www.mozilla.org/MPL/ */ #include "server.h" #include "connectaddress.h" #include "connection.h" #include "eventdispatcher_p.h" #include "icompletionlistener.h" #include "iioeventforwarder.h" #include "inewconnectionlistener.h" #include "iserver.h" #include "itransport.h" #include class ServerPrivate : public IIoEventForwarder, public ICompletionListener { public: ServerPrivate(EventDispatcher *dispatcher); // IIOEventInterposer IO::Status handleIoReady(IO::RW rw) override; // ICompletionListener void handleCompletion(void *transportServer) override; ConnectAddress listenAddress; ConnectAddress concreteAddress; EventDispatcher *eventDispatcher; Server *server; INewConnectionListener *newConnectionListener; IServer *transportServer; }; ServerPrivate::ServerPrivate(EventDispatcher *dispatcher) : IIoEventForwarder(EventDispatcherPrivate::get(dispatcher)), eventDispatcher(dispatcher) { } IO::Status ServerPrivate::handleIoReady(IO::RW rw) { const IO::Status ret = transportServer->handleIoReady(rw); - // TODO error handling + // ### error handling? But there is no possible permanent error with an already listening socket. return ret; } void ServerPrivate::handleCompletion(void *task) { assert(task == transportServer); (void) task; if (newConnectionListener) { newConnectionListener->handleNewConnection(server); } } Server::Server(EventDispatcher *dispatcher, const ConnectAddress &listenAddress) : d(new ServerPrivate(dispatcher)) { #if 0 if (ca.bus() == ConnectAddress::Bus::None || ca.socketType() == ConnectAddress::AddressType::None || ca.role() == ConnectAddress::Role::None || (ca.role() != ConnectAddress::Role::Server && ca.isServerOnly())) { cerr << "\nConnection: connection constructor Exit A\n\n"; return; } #endif d->listenAddress = listenAddress; d->server = this; d->newConnectionListener = nullptr; d->transportServer = IServer::create(listenAddress, &d->concreteAddress); if (d->transportServer) { d->addIoListener(d->transportServer); d->transportServer->setNewConnectionListener(d); } } Server::~Server() { delete d->transportServer; delete d; d = nullptr; } void Server::setNewConnectionListener(INewConnectionListener *listener) { d->newConnectionListener = listener; } INewConnectionListener *Server::newConnectionListener() const { return d->newConnectionListener; } Connection *Server::takeNextClient() { // TODO proper error handling / propagation if (!d->transportServer) { return nullptr; } ITransport *newTransport = d->transportServer->takeNextClient(); if (!newTransport) { return nullptr; } return new Connection(newTransport, d->eventDispatcher, d->concreteAddress); } bool Server::isListening() const { return d->transportServer ? d->transportServer->isListening() : false; } ConnectAddress Server::listenAddress() const { return d->listenAddress; } ConnectAddress Server::concreteAddress() const { return d->concreteAddress; } diff --git a/transport/localserver.cpp b/transport/localserver.cpp index 230a7eb..935b0fb 100644 --- a/transport/localserver.cpp +++ b/transport/localserver.cpp @@ -1,108 +1,121 @@ /* Copyright (C) 2014 Andreas Hartmetz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LGPL. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Alternatively, this file is available under the Mozilla Public License Version 1.1. You may obtain a copy of the License at http://www.mozilla.org/MPL/ */ #include "localserver.h" #include "icompletionlistener.h" #include "localsocket.h" #include #include #include #include #include #include LocalServer::LocalServer(const std::string &socketFilePath) : m_listenFd(-1) { const int fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) { return; } // don't let forks inherit the file descriptor - just in case fcntl(fd, F_SETFD, FD_CLOEXEC); struct sockaddr_un addr; addr.sun_family = PF_UNIX; bool ok = socketFilePath.length() + 1 <= sizeof(addr.sun_path); if (ok) { memcpy(addr.sun_path, socketFilePath.c_str(), socketFilePath.length() + 1); } if (!socketFilePath.empty() && socketFilePath[0] != '\0') { // not a so-called abstract socket (weird but useful Linux specialty) unlink(socketFilePath.c_str()); } ok = ok && (bind(fd, (struct sockaddr *)&addr, sizeof(sa_family_t) + socketFilePath.length()) == 0); ok = ok && (::listen(fd, /* max queued incoming connections */ 64) == 0); if (ok) { m_listenFd = fd; } else { ::close(fd); } } LocalServer::~LocalServer() { close(); } IO::Status LocalServer::handleIoReady(IO::RW rw) { if (rw != IO::RW::Read) { assert(false); return IO::Status::InternalError; } - int connFd = accept(m_listenFd, nullptr, nullptr); - if (connFd < 0) { - return IO::Status::RemoteClosed; + if (m_listenFd < 0) { + return IO::Status::LocalClosed; + } + int connFd = -1; + while (true) { + connFd = accept(m_listenFd, nullptr, nullptr); + if (connFd >= 0) { + break; + } + if (errno == EINTR) { + continue; + } + // After listen() succeeded, the only possible errors are invalid parameters (we don't do that, + // right?), EINTR, out of resource errors (which can be temporary), or aborted connection + // attempt. Just give up on this connection attempt and stay in listening state. + return IO::Status::OK; } fcntl(connFd, F_SETFD, FD_CLOEXEC); m_incomingConnections.push_back(new LocalSocket(connFd)); if (m_newConnectionListener) { m_newConnectionListener->handleCompletion(this); } return IO::Status::OK; } bool LocalServer::isListening() const { return m_listenFd >= 0; } void LocalServer::platformClose() { if (m_listenFd >= 0) { ::close(m_listenFd); m_listenFd = -1; } } FileDescriptor LocalServer::fileDescriptor() const { return m_listenFd; }