/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ //------------------------------------------------------------------------------ // Author: Lukasz Janyst // Date: 07.07.2010 // File: Descriptor.cc //------------------------------------------------------------------------------ #include "namespace/utils/Descriptor.hh" #include #include #include #include #include #include #include #include #include static void resolve(const char* address, sockaddr_in& addr) { eos::DescriptorException ex; //-------------------------------------------------------------------------- // Get the ip address //-------------------------------------------------------------------------- hostent* hp; //-------------------------------------------------------------------------- // Enlarge the buffer until the call succeeds //-------------------------------------------------------------------------- #ifdef __APPLE__ hp = gethostbyname(address); if (!hp) { ex.getMessage() << "Socket: get host by name failed"; throw ex; } #else hostent hostbuf; size_t hstbuflen = 1024; int herr; int res; char* tmphstbuf = (char*)malloc(hstbuflen); while ((res = gethostbyname_r(address, &hostbuf, tmphstbuf, hstbuflen, &hp, &herr)) == ERANGE) { hstbuflen *= 2; char* ptr = (char*)realloc(tmphstbuf, hstbuflen); if (ptr == nullptr) { std::abort(); } tmphstbuf = ptr; } if (res || !hp) { free(tmphstbuf); ex.getMessage() << "Socket: get host by name failed"; throw ex; } #endif if (hp->h_addr_list == 0) { ex.getMessage() << "Socket: host unknown"; throw ex; } if (hp->h_addr_list[0] == 0) { ex.getMessage() << "Socket: host unknown"; throw ex; } memcpy(&addr.sin_addr.s_addr, hp->h_addr_list[0], sizeof(in_addr)); #ifndef __APPLE__ free(tmphstbuf); #endif } namespace eos { //---------------------------------------------------------------------------- // Initializer //---------------------------------------------------------------------------- void Socket::init(Protocol proto) { if (pFD != -1) { DescriptorException ex; ex.getMessage() << "Socket: socket is already initialized"; throw ex; } //-------------------------------------------------------------------------- // Create the socket //-------------------------------------------------------------------------- int type = SOCK_STREAM; if (proto == UDP) { type = SOCK_DGRAM; } if ((pFD = socket(PF_INET, type, 0)) == -1) { DescriptorException ex; ex.getMessage() << "Socket: Unable to create socket: "; ex.getMessage() << strerror(errno); throw ex; } } //---------------------------------------------------------------------------- // Connect the socket //---------------------------------------------------------------------------- void Socket::connect(const char* address, unsigned port) { DescriptorException ex; if (pFD == -1) { init(TCP); } //-------------------------------------------------------------------------- //! Resolve the hostname //-------------------------------------------------------------------------- sockaddr_in addr = {0}; resolve(address, addr); //-------------------------------------------------------------------------- // Set up the port //-------------------------------------------------------------------------- addr.sin_family = AF_INET; addr.sin_port = htons((unsigned short)port); //-------------------------------------------------------------------------- // Connect to the remote host //-------------------------------------------------------------------------- if ((pFD < 0) || (::connect(pFD, (sockaddr*)&addr, sizeof(addr)) != 0)) { if (pFD > 0) { ::close(pFD); } ex.getMessage() << "Socket: Connection failed: "; ex.getMessage() << strerror(errno); throw ex; } } //---------------------------------------------------------------------------- // Bind to the port //---------------------------------------------------------------------------- void Socket::bind(const char* address, unsigned port) { if (pFD == -1) { init(TCP); } //-------------------------------------------------------------------------- // Resolve the address //-------------------------------------------------------------------------- sockaddr_in localAddr; DescriptorException ex; memset(&localAddr, 0, sizeof(localAddr)); if (address) { resolve(address, localAddr); } else { localAddr.sin_addr.s_addr = INADDR_ANY; } localAddr.sin_family = AF_INET; localAddr.sin_port = htons((unsigned short)port); //-------------------------------------------------------------------------- // Bind the socket //-------------------------------------------------------------------------- if ((pFD < 0) || (::bind(pFD, (sockaddr*)&localAddr, sizeof(sockaddr_in)) == -1)) { if (pFD >= 0) { ::close(pFD); } ex.getMessage() << "Socket: Unable to bind to port: " << port << " "; ex.getMessage() << strerror(errno); throw ex; } } //---------------------------------------------------------------------------- // Listen to the incomming connections //---------------------------------------------------------------------------- void Socket::listen(unsigned queue) { DescriptorException ex; //-------------------------------------------------------------------------- // Listen to the incomming connections //-------------------------------------------------------------------------- if (::listen(pFD, 20) == -1) { ex.getMessage() << "Socket: Unable to listen: " << strerror(errno); throw ex; } } Socket* Socket::accept() { DescriptorException ex; //-------------------------------------------------------------------------- // Accept the connection //-------------------------------------------------------------------------- socklen_t sinSize = sizeof(sockaddr_in); sockaddr_in remoteAddr; int newSock = ::accept(pFD, (sockaddr*)&remoteAddr, &sinSize); if (newSock == -1) { ex.getMessage() << "Socket: Error while accpeting connection: "; ex.getMessage() << strerror(errno); throw ex; } //-------------------------------------------------------------------------- // Create a new thread to handle this connection //-------------------------------------------------------------------------- return new Socket(newSock); } //---------------------------------------------------------------------------- // Close the socket //---------------------------------------------------------------------------- void Descriptor::close() { if (pFD != -1) { ::close(pFD); pFD = -1; } } //---------------------------------------------------------------------------- // Read the buffer from the blocking descriptor (socket, pipe), it won't // return untill all the requested data is read //---------------------------------------------------------------------------- void Descriptor::readBlocking(char* buffer, unsigned len) { if (len == 0) { return; } int ret; int left = len; char* ptr = buffer; while (1) { ret = ::read(pFD, ptr, left); if (ret == -1 || ret == 0) { DescriptorException ex; ex.getMessage() << "Descriptor: Unable to read " << len << " bytes: "; ex.getMessage() << strerror(errno); throw ex; } left -= ret; if (!left) { return; } ptr += ret; } } //---------------------------------------------------------------------------- // Read the buffer from the non-blocking descriptor (file, block device), // it won't return untill all the requested data is read. //---------------------------------------------------------------------------- void Descriptor::readNonBlocking(char* buffer, unsigned len, unsigned poll) { if (len == 0) { return; } int ret; int left = len; char* ptr = buffer; while (1) { ret = ::read(pFD, ptr, left); if (ret == -1) { DescriptorException ex; ex.getMessage() << "Descriptor: Unable to read " << len << " bytes: "; ex.getMessage() << strerror(errno); throw ex; } if (ret == 0) { if (poll != 0) { usleep(poll); } else { DescriptorException ex; ex.getMessage() << "Descriptor: Not enough data to fulfill the request"; throw ex; } } left -= ret; if (!left) { return; } ptr += ret; } } //---------------------------------------------------------------------------- // Read the buffer from the non-blocking descriptor (file, block device) // at given offset, it won't return untill all the requested data is read. //---------------------------------------------------------------------------- void Descriptor::offsetReadNonBlocking(char* buffer, unsigned len, off_t offset, unsigned poll) { if (len == 0) { return; } int ret; off_t off = offset; int left = len; char* ptr = buffer; while (1) { ret = ::pread(pFD, ptr, left, off); if (ret == -1) { DescriptorException ex; ex.getMessage() << "Descriptor: Unable to read " << len << " bytes"; ex.getMessage() << "at offset " << offset << ": "; ex.getMessage() << strerror(errno); throw ex; } if (ret == 0) { if (poll != 0) { usleep(poll); } else { DescriptorException ex; ex.getMessage() << "Descriptor: Not enough data to fulfill the request"; throw ex; } } left -= ret; off += ret; if (!left) { return; } ptr += ret; } } //---------------------------------------------------------------------------- // Try to read len bytes at offset //---------------------------------------------------------------------------- unsigned Descriptor::tryRead(char* buffer, unsigned len, off_t offset) { if (len == 0) { return 0; } int ret; off_t off = offset; int left = len; char* ptr = buffer; while (1) { ret = ::pread(pFD, ptr, left, off); if (ret == -1) { DescriptorException ex; ex.getMessage() << "Descriptor: Unable to read " << len << " bytes"; ex.getMessage() << "at offset " << offset << ": "; ex.getMessage() << strerror(errno); throw ex; } if (ret == 0) { return len - left; } left -= ret; off += ret; if (!left) { return len; } ptr += ret; } return len; } //---------------------------------------------------------------------------- // Write data to the descriptor //---------------------------------------------------------------------------- void Descriptor::write(const char* buffer, unsigned len) { if (len == 0) { return; } int ret = 0; int left = len; const char* ptr = buffer; while (1) { ret = ::write(pFD, ptr, left); if (ret == -1 || ret == 0) { DescriptorException ex; ex.getMessage() << "Descriptor: Unable to write " << len << " bytes: "; ex.getMessage() << strerror(errno); throw ex; } left -= ret; if (!left) { return; } ptr += ret; } } //---------------------------------------------------------------------------- // The same as the ones in the manual //---------------------------------------------------------------------------- void Socket::setsockopt(int level, int name, void* value, socklen_t len) { if (::setsockopt(pFD, level, name, value, len) == -1) { DescriptorException ex; ex.getMessage() << "Socket: Unable to set socket option "; ex.getMessage() << level << "-" << name << ": "; ex.getMessage() << strerror(errno); throw ex; } } //---------------------------------------------------------------------------- // The same as the ones in the manual //---------------------------------------------------------------------------- void Socket::getsockopt(int level, int name, void* value, socklen_t& len) { if (::getsockopt(pFD, level, name, value, &len) == -1) { DescriptorException ex; ex.getMessage() << "Socket: Unable to set socket option"; ex.getMessage() << level << "-" << name << ": "; ex.getMessage() << strerror(errno); throw ex; } } }