From a081a83e5ce9540d803fe779575065a7ffa5e22a Mon Sep 17 00:00:00 2001 From: Eric Cano <Eric.Cano@cern.ch> Date: Thu, 14 Jan 2016 15:55:13 +0100 Subject: [PATCH] Removing dependancies on externally compiled CASTOR packages from CTA: Removed linking information for external libraries from CASTOR. Started to re-import necessary files from CASTOR. Edited files from CASTOR to replace usage of the old Cxxx compatibility layer (this was for multi-system compatibility at the time. --- CMakeLists.txt | 22 +- cmdline/CMakeLists.txt | 2 +- tapeserver/castor/io/CMakeLists.txt | 7 +- tapeserver/castor/io/StreamAddress.cpp | 37 ++ tapeserver/castor/io/StreamAddress.hpp | 75 ++++ tapeserver/castor/io/biniostream.h | 311 +++++++++++++++++ tapeserver/castor/io/io.cpp | 320 ++++++------------ tapeserver/castor/io/io.hpp | 20 +- tapeserver/castor/legacymsg/CMakeLists.txt | 8 +- .../castor/legacymsg/GenericMarshal.cpp | 204 +++++++++++ .../castor/legacymsg/GenericMarshal_1.hpp | 139 ++++++++ tapeserver/castor/legacymsg/MessageHeader.cpp | 33 ++ tapeserver/castor/legacymsg/RmcMarshal.cpp | 231 +++++++++++++ tapeserver/castor/legacymsg/RmcMarshal.hpp | 112 ++++++ tapeserver/castor/legacymsg/RmcProxyTcpIp.cpp | 234 +++++++++++++ .../castor/legacymsg/TapeLabelRqstMsgBody.cpp | 38 +++ tapeserver/castor/server/CMakeLists.txt | 10 +- tapeserver/castor/server/Daemon.cpp | 234 +++++++++++++ tapeserver/castor/server/Daemon.hpp | 11 - tapeserver/castor/server/Mutex.cpp | 49 +++ tapeserver/castor/server/ProcessCap.cpp | 140 ++++++++ tapeserver/castor/server/Semaphores.cpp | 167 +++++++++ tapeserver/castor/server/Semaphores.hpp | 3 + tapeserver/castor/server/Threading.cpp | 90 +++++ .../tape/tapeserver/daemon/CMakeLists.txt | 4 +- .../tape/tapeserver/file/CMakeLists.txt | 2 +- 26 files changed, 2231 insertions(+), 272 deletions(-) create mode 100644 tapeserver/castor/io/StreamAddress.cpp create mode 100644 tapeserver/castor/io/StreamAddress.hpp create mode 100644 tapeserver/castor/io/biniostream.h create mode 100644 tapeserver/castor/legacymsg/GenericMarshal.cpp create mode 100644 tapeserver/castor/legacymsg/GenericMarshal_1.hpp create mode 100644 tapeserver/castor/legacymsg/MessageHeader.cpp create mode 100644 tapeserver/castor/legacymsg/RmcMarshal.cpp create mode 100644 tapeserver/castor/legacymsg/RmcMarshal.hpp create mode 100644 tapeserver/castor/legacymsg/RmcProxyTcpIp.cpp create mode 100644 tapeserver/castor/legacymsg/TapeLabelRqstMsgBody.cpp create mode 100644 tapeserver/castor/server/Daemon.cpp create mode 100644 tapeserver/castor/server/Mutex.cpp create mode 100644 tapeserver/castor/server/ProcessCap.cpp create mode 100644 tapeserver/castor/server/Semaphores.cpp create mode 100644 tapeserver/castor/server/Threading.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b18e84fb3c..b49e3b51a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,21 +42,6 @@ ELSE(DEFINED PackageOnly) message (STATUS "Already set: COMPILE_PACKAGING=${COMPILE_PACKAGING}") endif (NOT DEFINED COMPILE_PACKAGING) - - # With the exception of shared-library plugins, the CASTOR rpms only install the - # /usr/lib64/libcastor*.so symbolic links for libraries used by end-user - # developers. Therefore the locations of the internal CASTOR libraries required - # by tapeserved and not by end-user developers need to be imported into cmake. - add_library(castorlegacymsg SHARED IMPORTED) - set_target_properties(castorlegacymsg PROPERTIES - IMPORTED_LOCATION /usr/lib64/libcastorlegacymsg.so.2.1) - add_library(castorserver SHARED IMPORTED) - set_target_properties(castorserver PROPERTIES - IMPORTED_LOCATION /usr/lib64/libcastorserver.so.2.1) - add_library(castortapegatewayprotocol SHARED IMPORTED) - set_target_properties(castortapegatewayprotocol PROPERTIES - IMPORTED_LOCATION /usr/lib64/libcastortapegatewayprotocol.so.2.1) - IF(NOT CMAKE_BUILD_TYPE STREQUAL "") # If the user specifies -DCMAKE_BUILD_TYPE on the command line, take their definition and dump it in the cache message(STATUS "Setting build type to ${CMAKE_BUILD_TYPE} as requested.") @@ -119,10 +104,5 @@ configure_file(tests/valgrind.suppr tests/valgrind.suppr COPYONLY) add_custom_target(test tests/unittests # COMMAND valgrind --track-fds=yes --leak-check=full --demangle=yes --gen-suppressions=all --show-reachable=yes --error-exitcode=1 --suppressions=tests/valgrind.suppr tests/unittests - #COMMAND test/castorThreadedUnitTests - #COMMAND valgrind --track-fds=yes --leak-check=full --show-reachable=yes --error-exitcode=1 test/castorThreadedUnitTests - #COMMAND valgrind --tool=helgrind -v --demangle=no --conflict-cache-size=30000000 --error-exitcode=1 test/castorThreadedUnitTests - #COMMAND test/castorMultiprocessUnitTests - #COMMAND valgrind --tool=helgrind --error-exitcode=1 test/castorMultiprocessUnitTests - DEPENDS tests/unittests #test/castorThreadedUnitTests test/castorMultiprocessUnitTests test/castorThreadedUnitTests.supp + DEPENDS tests/unittests tests/valgrind.suppr COMMENT "Running unit tests" VERBATIM) diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt index 2383645201..1a79ff46f9 100644 --- a/cmdline/CMakeLists.txt +++ b/cmdline/CMakeLists.txt @@ -9,7 +9,7 @@ target_link_libraries (cta ${XROOTD_XRDCL_LIB} ctacommon cryptopp) include_directories (${CMAKE_SOURCE_DIR}/tapeserver/) add_executable (ctaAddAdminUser CTAAddAdminUser.cpp ) -target_link_libraries (ctaAddAdminUser ctacommon castorserver ctalog +target_link_libraries (ctaAddAdminUser ctacommon ctalog castorcommon ctautils protobuf ctascheduler ctanameserver) install (TARGETS cta ctaAddAdminUser DESTINATION usr/bin) diff --git a/tapeserver/castor/io/CMakeLists.txt b/tapeserver/castor/io/CMakeLists.txt index aff418d2eb..9c65106fd2 100644 --- a/tapeserver/castor/io/CMakeLists.txt +++ b/tapeserver/castor/io/CMakeLists.txt @@ -3,7 +3,12 @@ cmake_minimum_required (VERSION 2.6) include_directories(${PROJECT_SOURCE_DIR}/tapeserver) include_directories(${PROJECT_SOURCE_DIR}/tapeserver/h) +add_library (ctaio + io.cpp + IpAndPort.cpp + StreamAddress.cpp) + add_library (ctaiounittests SHARED IoTest.cpp) target_link_libraries (ctaiounittests - castorclient) + ctaio) diff --git a/tapeserver/castor/io/StreamAddress.cpp b/tapeserver/castor/io/StreamAddress.cpp new file mode 100644 index 0000000000..60677e930d --- /dev/null +++ b/tapeserver/castor/io/StreamAddress.cpp @@ -0,0 +1,37 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +// Include Files +#include "StreamAddress.hpp" + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +castor::io::StreamAddress::StreamAddress(castor::io::biniostream& stream, + const std::string cnvSvcName, + const unsigned int cnvSvcType) : + BaseAddress(), m_stream(stream) { + setCnvSvcName(cnvSvcName); + setCnvSvcType(cnvSvcType); +} diff --git a/tapeserver/castor/io/StreamAddress.hpp b/tapeserver/castor/io/StreamAddress.hpp new file mode 100644 index 0000000000..ff8d2ba15f --- /dev/null +++ b/tapeserver/castor/io/StreamAddress.hpp @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#pragma once + +// Include Files +#include "castor/io/biniostream.h" +#include "castor/BaseAddress.hpp" +#include "castor/Constants.hpp" + +namespace castor { + + namespace io { + + /** + * An address containing a reference to a binary stream + */ + class StreamAddress : public BaseAddress { + + public: + + /** + * constructor + * @param stream the stream where to put the data + * @param cnvSvcName the conversion service able to deal with this address + * In this later case, the type will be deduced from the id. + */ + StreamAddress(biniostream& stream, + const std::string cnvSvcName, + const unsigned int cnvSvcType); + + /* + * destructor + */ + virtual ~StreamAddress() throw() {} + + /** + * gets the id of this address + */ + virtual biniostream& stream() const { return m_stream; } + + private: + + /** + * the id of this address + */ + biniostream& m_stream; + + }; + + } // end of namespace io + +} // end of namespace castor + diff --git a/tapeserver/castor/io/biniostream.h b/tapeserver/castor/io/biniostream.h new file mode 100644 index 0000000000..d1f7e84fdf --- /dev/null +++ b/tapeserver/castor/io/biniostream.h @@ -0,0 +1,311 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#pragma once + +#include <sstream> +#include "osdep.h" +#include "castor/exception/OutOfMemory.hpp" +#include <string.h> +#include <stdlib.h> + +#ifdef __APPLE__ +#define __bswap_16(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)) +#define __bswap_32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) +#else +#include <byteswap.h> +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +/* The host byte order is the same as the "inverted network byte order", + which is the reference used by Castor production setup, + so these functions are all just identity. */ +#define intohl(x) (x) +#define intohs(x) (x) +#define htoinl(x) (x) +#define htoins(x) (x) + +#else +#if __BYTE_ORDER == __BIG_ENDIAN +#define intohl(x) __bswap_32 (x) +#define intohs(x) __bswap_16 (x) +#define htoinl(x) __bswap_32 (x) +#define htoins(x) __bswap_16 (x) +#endif +#endif + +namespace castor { + + namespace io { + + /** + * A binary stream based on stringstream + */ + class biniostream : public std::stringstream { + public: + biniostream(std::string& s) : std::stringstream(s) {} + biniostream() : std::stringstream() {} + + biniostream& operator<< (char c) { + write((char*)&c, sizeof(char)); + return *this; + } + + biniostream& operator<< (unsigned char c) { + write((char*)&c, sizeof(unsigned char)); + return *this; + } + + biniostream& operator<< (signed char c) { + write((char*)&c, sizeof(signed char)); + return *this; + } + + biniostream& operator<< (int i) { + i = htoinl((unsigned int)i); + write((char*)&i, sizeof(int)); + return *this; + } + + biniostream& operator<< (unsigned int i) { + i = htoinl(i); + write((char*)&i, sizeof(unsigned int)); + return *this; + } + + biniostream& operator<< (short s) { + s = htoins((unsigned short)s); + write((char*)&s, sizeof(short)); + return *this; + } + + biniostream& operator<< (unsigned short s) { + s = htoins(s); + write((char*)&s, sizeof(unsigned short)); + return *this; + } + + biniostream& operator<< (long l) { + l = htoinl((unsigned long)l); + write((char*)&l, LONGSIZE); + return *this; + } + + biniostream& operator<< (unsigned long l) { + l = htoinl(l); + write((char*)&l, LONGSIZE); + return *this; + } + + biniostream& operator<< (const char* cp) { + int len = strlen(cp)+1; + write((char*)&len, sizeof(int)); + write(cp,len); + return *this; + } + + biniostream& operator<< (bool b) { + write((char*)&b, sizeof(bool)); + return *this; + } + + biniostream& operator<< (float f) { + write((char*)&f, sizeof(float)); + return *this; + } + + biniostream& operator<< (double d) { + write((char*)&d, sizeof(double)); + return *this; + } + + biniostream& operator<< (long double d) { + write((char*)&d, sizeof(long double)); + return *this; + } + + biniostream& operator<< (u_signed64 d) { + unsigned long n = (unsigned long)d; // Least significant part first + write((char*)&n, LONGSIZE); + n = htoinl((unsigned long)(d >> 32)); + write((char*)&n, LONGSIZE); + return *this; + } + + biniostream& operator<< (signed64 d) { + unsigned long n = (unsigned long)d; // Least significant part first + write((char*)&n, LONGSIZE); + n = htoinl((unsigned long)(d >> 32)); + write((char*)&n, LONGSIZE); + return *this; + } + + //////////////////////////////////////////////////////////// + // + // Input Binary Operators + // + //////////////////////////////////////////////////////////// + biniostream& operator>> (char& c) { + read((char*)&c, sizeof(char)); + return *this; + } + + biniostream& operator>> (unsigned char& c) { + read((char*)&c, sizeof(unsigned char)); + return *this; + } + + biniostream& operator>> (signed char& c) { + read((char*)&c, sizeof(signed char)); + return *this; + } + + biniostream& operator>> (int& i) { + read((char*)&i, sizeof(int)); + i = intohl((unsigned int)i); + return *this; + } + + biniostream& operator>> (unsigned int& i) { + read((char*)&i, sizeof(unsigned int)); + i = intohl(i); + return *this; + } + + biniostream& operator>> (short& s) { + read((char*)&s, sizeof(short)); + s = intohs((unsigned short)s); + return *this; + } + + biniostream& operator>> (unsigned short& s) { + read((char*)&s, sizeof(unsigned short)); + s = intohs(s); + return *this; + } + + biniostream& operator>> (long& l) { + l = 0; + read((char*)&l, LONGSIZE); + l = intohl((unsigned long)l); + if (((*((char*)(&l)+3)) & (1 << (7-(0)%8))) && (sizeof(long)-LONGSIZE > 0)) { + (void) memset((char *)&l+4, 255, sizeof(long)-LONGSIZE); + } + return *this; + } + + biniostream& operator>> (unsigned long& l) { + l = 0; + read((char*)&l, LONGSIZE); + l = intohl(l); + return *this; + } + + biniostream& operator>> (char* cp) { + int len; + read((char*)&len, sizeof(int)); + read(cp,len); + return *this; + } + + biniostream& operator>> (bool& b) { + read((char*)&b, sizeof(bool)); + return *this; + } + + biniostream& operator>> (float& f) { + read((char*)&f, sizeof(float)); + return *this; + } + + biniostream& operator>> (double& d) { + read((char*)&d, sizeof(double)); + return *this; + } + + biniostream& operator>> (long double& d) { + read((char*)&d, sizeof(long double)); + return *this; + } + + biniostream& operator>> (u_signed64& d) { + //read((char*)&d, sizeof(u_signed64)); + unsigned int n; + read((char*)&n, LONGSIZE); + d = intohl((unsigned int)n); // Least Significant part first + read((char*)&n, LONGSIZE); + n = intohl((unsigned int)n); + d += (u_signed64)n << 32; + return *this; + } + + biniostream& operator>> (signed64& d) { + //read((char*)&d, sizeof(signed64)); + unsigned int n; + read((char*)&n, LONGSIZE); + d = intohl((unsigned int)n); // Least Significant part first + read((char*)&n, LONGSIZE); + n = intohl((unsigned int)n); + d += (u_signed64)n << 32; + return *this; + } + + }; + + } // end of namespace io + +} // end of namespace castor + + +template <class charT, class traits, class Allocator> + castor::io::biniostream& + operator>> (castor::io::biniostream& os, + std::basic_string <charT, traits, Allocator>& s) { + s.erase(); + int len; + os >> len; + char* buf = (char*)malloc(len+1); + if(0 == buf) { + castor::exception::OutOfMemory e; + e.getMessage() << "Failed to allocate buffer of length " << len; + throw e; + } + os.read(buf, len); + buf[len] = 0; + s += buf; + free(buf); + return os; +} + +template <class charT, class traits, class Allocator> + castor::io::biniostream & + operator<< (castor::io::biniostream& os, + const std::basic_string <charT, traits, Allocator>& s) { + int len = s.length(); + os << len; + os.write(s.data(), len); + return os; +} + + diff --git a/tapeserver/castor/io/io.cpp b/tapeserver/castor/io/io.cpp index c98fee5f14..e2eaba8fe6 100644 --- a/tapeserver/castor/io/io.cpp +++ b/tapeserver/castor/io/io.cpp @@ -27,8 +27,9 @@ #include "castor/io/io.hpp" #include "castor/utils/SmartFd.hpp" #include "castor/utils/utils.hpp" -#include "serrno.h" -#include "net.h" +#include "common/Utils.hpp" +#include "common/Timer.hpp" +#include "common/exception/Errnum.hpp" #include <arpa/inet.h> #include <fcntl.h> @@ -38,6 +39,7 @@ #include <sys/select.h> #include <sys/socket.h> #include <sys/types.h> +#include <sys/poll.h> #include <time.h> #include <list> @@ -129,10 +131,9 @@ int castor::io::createListenerSock( // Create a socket utils::SmartFd sock(socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)); if(sock.get() < 0) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; - ex.getMessage() << ": Failed to create socket: " << errBuf; + ex.getMessage() << ": Failed to create socket: " + << cta::Utils::errnoToString(errno); throw ex; } @@ -141,8 +142,6 @@ int castor::io::createListenerSock( int reuseaddrOptval = 1; if(0 > setsockopt(sock.get(), SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddrOptval, sizeof(reuseaddrOptval))) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << ": Failed to set socket option" @@ -150,7 +149,7 @@ int castor::io::createListenerSock( " level=SOL_SOCKET" " optname=SO_REUSEADDR" " optval=" << reuseaddrOptval << - ": " << errBuf; + ": " << cta::Utils::errnoToString(errno); throw ex; } } @@ -179,13 +178,11 @@ int castor::io::createListenerSock( // Mark the socket as being a listener if(listen(sock.get(), LISTENBACKLOG) < 0) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << ": Failed to mark socket as being a listener" ": listenSocketFd=" << sock.get() << - ": " << errBuf; + ": " << cta::Utils::errnoToString(errno); throw ex; } @@ -201,13 +198,11 @@ int castor::io::createListenerSock( // Else throw an exception } else { - char errBuf[100]; - sstrerror_r(bindErrno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << ": Failed to bind listener socket" ": listenSocketFd=" << sock.get() << - ": " << errBuf; + ": " << cta::Utils::errnoToString(bindErrno); throw ex; } } @@ -280,9 +275,7 @@ int castor::io::acceptConnection(const int listenSocketFd) if(savedErrno == EINVAL) { reason << ": Socket is not listening for connections"; } else { - char errBuf[100]; - sstrerror_r(savedErrno, errBuf, sizeof(errBuf)); - reason << ": " << errBuf; + reason << ": " << cta::Utils::errnoToString(savedErrno); } castor::exception::Exception ex; @@ -344,11 +337,9 @@ int castor::io::acceptConnection(const int listenSocketFd, throw ex; } else { - char errBuf[100]; - sstrerror_r(selectErrno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << "Failed to accept connection: Select failed: " << - errBuf; + cta::Utils::errnoToString(selectErrno); throw ex; } break; @@ -379,9 +370,7 @@ int castor::io::acceptConnection(const int listenSocketFd, if(acceptErrno == EINVAL) { reason << ": Socket is not listening for connections"; } else { - char errBuf[100]; - sstrerror_r(acceptErrno, errBuf, sizeof(errBuf)); - reason << ": " << errBuf; + reason << ": " << cta::Utils::errnoToString(acceptErrno); } castor::exception::Exception ex; @@ -412,11 +401,9 @@ castor::io::IpAndPort castor::io::getSockIpPort( socklen_t addressLen = sizeof(address); if(getsockname(socketFd, (struct sockaddr*)&address, &addressLen) < 0) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << "Failed to get socket name: socketFd=" << socketFd << - ": " << errBuf; + ": " << cta::Utils::errnoToString(errno); throw ex; } @@ -443,11 +430,9 @@ castor::io::IpAndPort castor::io::getPeerIpPort( socklen_t addressLen = sizeof(address); if(getpeername(socketFd, (struct sockaddr*)&address, &addressLen) < 0) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << ": Failed to get peer name: socketFd=" << socketFd << - ": " << errBuf; + ": " << cta::Utils::errnoToString(errno); throw ex; } @@ -472,11 +457,9 @@ std::string castor::io::getSockHostName(const int socketFd) { socklen_t addressLen = sizeof(address); if(getsockname(socketFd, (struct sockaddr*)&address, &addressLen) < 0) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << "Failed to get socket hostname" - ": socketFd=" << socketFd << ": " << errBuf; + ": socketFd=" << socketFd << ": " << cta::Utils::errnoToString(errno); throw ex; } @@ -486,7 +469,7 @@ std::string castor::io::getSockHostName(const int socketFd) { hostName, sizeof(hostName), serviceName, sizeof(serviceName), 0); if(error != 0) { - castor::exception::Exception ex(SENOSHOST); + castor::exception::Exception ex; ex.getMessage() << ": Failed to get host information by address" ": socketFd=" << socketFd << @@ -521,13 +504,11 @@ void castor::io::getSockIpHostnamePort( socklen_t addressLen = sizeof(address); if(getsockname(socketFd, (struct sockaddr*)&address, &addressLen) < 0) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << ": Failed to get socket name" ": socketFd=" << socketFd << - ": " << errBuf; + ": " << cta::Utils::errnoToString(errno); throw ex; } @@ -540,7 +521,7 @@ void castor::io::getSockIpHostnamePort( hostName, hostNameLen, serviceName, sizeof(serviceName), 0); if(rc != 0) { - castor::exception::Exception ex(SENOSHOST); + castor::exception::Exception ex; ex.getMessage() << ": Failed to get host information by address" ": socketFd=" << socketFd << @@ -567,13 +548,11 @@ std::string castor::io::getPeerHostName(const int socketFd) { socklen_t addressLen = sizeof(address); if(getpeername(socketFd, (struct sockaddr*)&address, &addressLen) < 0) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << ": Failed to get peer name" ": socketFd=" << socketFd << - ": " << errBuf; + ": " << cta::Utils::errnoToString(errno); throw ex; } @@ -584,7 +563,7 @@ std::string castor::io::getPeerHostName(const int socketFd) { hostName, sizeof(hostName), serviceName, sizeof(serviceName), 0); if(rc != 0) { - castor::exception::Exception ex(SENOSHOST); + castor::exception::Exception ex; ex.getMessage() << ": Failed to get host information by address" ": socketFd=" << socketFd << @@ -658,103 +637,56 @@ void castor::io::readBytes( const int socketFd, const int timeout, const int nbBytes, - char *const buf) - { - + char *const buf) { // Throw an exception if socketFd is invalid if(socketFd < 0) { castor::exception::InvalidArgument ex; ex.getMessage() << - "Invalid socket file-descriptor" + "In io::readBytes: Invalid socket file-descriptor" ": socketFd=" << socketFd; throw ex; } - const bool connClosed = readBytesFromCloseable(socketFd, timeout, nbBytes, - buf); - - if(connClosed) { - std::stringstream oss; - oss << "Failed to read " << nbBytes << " bytes from socket: "; - writeSockDescription(oss, socketFd); - oss << ": Connection was closed by the remote end"; - - castor::exception::Exception ex(SECONNDROP); - ex.getMessage() << oss.str(); - throw ex; - } -} - -//------------------------------------------------------------------------------ -// readBytesFromCloseable -//------------------------------------------------------------------------------ -bool castor::io::readBytesFromCloseable( - const int socketFd, - const int timeout, - const int nbBytes, - char *const buf) { - - // Throw an exception if socketFd is invalid - if(socketFd < 0) { + if (timeout < 0) { castor::exception::InvalidArgument ex; ex.getMessage() << - "Invalid socket file-descriptor" - ": socketFd=" << socketFd; + "In io::readBytes: Invalid timeout value: " << timeout; throw ex; } - - bool connClosed = false; - const int rc = netread_timeout(socketFd, buf, nbBytes, timeout); - int savedSerrno = serrno; - - switch(rc) { - case -1: + + cta::utils::Timer timer; + size_t bytesRemaining = nbBytes; + char * readPtr = buf; + while (bytesRemaining > 0) { { - std::stringstream oss; - oss << "Failed to read " << nbBytes << " bytes from socket: " << - " socketFd=" << socketFd << ": "; - writeSockDescription(oss, socketFd); - // netread_timeout can return -1 with serrno set to 0 - if(0 == savedSerrno) { - savedSerrno = SEINTERNAL; - oss << ": Unknown error"; - } else { - char errBuf[100]; - sstrerror_r(savedSerrno, errBuf, sizeof(errBuf)); - oss << ": " << errBuf; + struct ::pollfd pollDescr; + pollDescr.fd = socketFd; + pollDescr.events = POLLIN; + pollDescr.revents = 0; + int pollRet = poll(&pollDescr, 1, (timeout * 1000) - (timer.usecs()/1000)); + cta::exception::Errnum::throwOnMinusOne(pollRet, "In io::readBytes: failed to poll socket"); + if (!pollRet) + throw cta::exception::Exception("In io::readBytes: timeout"); + if (pollRet != 1) { + std::stringstream err; + err << "In io::readBytes: unexpected return value from poll: " << pollRet; + throw cta::exception::Exception(err.str()); } - if(SETIMEDOUT == savedSerrno) { - oss << ": timeout=" << timeout; - } - - castor::exception::Exception ex(savedSerrno); - ex.getMessage() << oss.str(); - throw ex; } - break; - case 0: { - connClosed = true; - } - break; - default: - if (rc != nbBytes) { - - std::stringstream oss; - oss << "Failed to read " << nbBytes << " bytes from socket: "; - writeSockDescription(oss, socketFd); - oss - << ": Read the wrong number of bytes" - << ": Expected: " << nbBytes - << ": Read: " << rc; - - castor::exception::Exception ex(SECOMERR); - ex.getMessage() << oss.str(); - throw ex; + int recvRet = recv(socketFd, readPtr, bytesRemaining, 0); + cta::exception::Errnum::throwOnMinusOne(recvRet, "In io::readBytes: failed to receive data: "); + if (recvRet > 0) { + // We did read more data... + readPtr += recvRet; + bytesRemaining -= recvRet; + } else { + std::stringstream err; + err << "In io::readBytes: unexpected return value from recv: " << recvRet; + throw cta::exception::Exception(err.str()); + } } } - - return connClosed; } //------------------------------------------------------------------------------ @@ -765,67 +697,55 @@ void castor::io::writeBytes( const int timeout, const int nbBytes, char *const buf) - { +{ // Throw an exception if socketFd is invalid if(socketFd < 0) { castor::exception::InvalidArgument ex; ex.getMessage() << - "Invalid socket file-descriptor" + "In io::writeBytes: Invalid socket file-descriptor" ": socketFd=" << socketFd; throw ex; } - - const int rc = netwrite_timeout(socketFd, buf, nbBytes, timeout); - int savedSerrno = serrno; - - switch(rc) { - case -1: + + if (timeout < 0) { + castor::exception::InvalidArgument ex; + ex.getMessage() << + "In io::writeBytes: Invalid timeout value: " << timeout; + throw ex; + } + + cta::utils::Timer timer; + size_t bytesRemaining = nbBytes; + char * writePtr = buf; + while (bytesRemaining > 0) { { - std::stringstream oss; - oss << "Failed to write " << nbBytes << " bytes to socket: " << - " socketFd=" << socketFd << ": "; - writeSockDescription(oss, socketFd); - // netwrite_timeout can return -1 with serrno set to 0 - if(0 == savedSerrno) { - savedSerrno = SEINTERNAL; - oss << ": Unknown error"; - } else { - char errBuf[100]; - sstrerror_r(savedSerrno, errBuf, sizeof(errBuf)); - oss << ": " << errBuf; - } - if(savedSerrno == SETIMEDOUT) { - oss << ": timeout=" << timeout; + struct ::pollfd pollDescr; + pollDescr.fd = socketFd; + pollDescr.events = POLLOUT; + pollDescr.revents = 0; + int pollRet = poll(&pollDescr, 1, (timeout * 1000) - (timer.usecs()/1000)); + cta::exception::Errnum::throwOnMinusOne(pollRet, "In io::writeBytes: failed to poll socket"); + if (!pollRet) + throw cta::exception::Exception("In io::writeBytes: timeout"); + if (pollRet != 1) { + std::stringstream err; + err << "In io::writeBytes: unexpected return value from poll: " << pollRet; + throw cta::exception::Exception(err.str()); } - - castor::exception::Exception ex(SECOMERR); - ex.getMessage() << oss.str(); - throw ex; } - case 0: { - std::stringstream oss; - oss << "Failed to write " << nbBytes << " bytes to socket: "; - writeSockDescription(oss, socketFd); - oss << ": Connection dropped"; - - castor::exception::Exception ex(SECONNDROP); - ex.getMessage() << oss.str(); - throw ex; - } - default: - if(rc != nbBytes) { - std::stringstream oss; - oss << "Failed to write " << nbBytes << " bytes to socket: "; - writeSockDescription(oss, socketFd); - oss - << ": Wrote the wrong number of bytes" - << ": Expected: " << nbBytes - << ": Wrote: " << rc; - castor::exception::Exception ex(SECOMERR); - ex.getMessage() << oss.str(); - throw ex; + int sendRet = recv(socketFd, writePtr, bytesRemaining, 0); + cta::exception::Errnum::throwOnMinusOne(sendRet, "In io::writeBytes: failed to send data: "); + if (sendRet > 0) { + // We did read more data... + writePtr += sendRet; + bytesRemaining -= sendRet; + } else { + std::stringstream err; + err << "In io::writeBytes: unexpected return value from send: " << sendRet; + throw cta::exception::Exception(err.str()); + } } } } @@ -956,36 +876,30 @@ int castor::io::connectWithTimeout( // Create the socket for the new connection utils::SmartFd smartSock(socket(sockDomain, sockType, sockProtocol)); if(-1 == smartSock.get()) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << "Failed to create socket for new connection" - ": Call to socket() failed: " << errBuf; + ": Call to socket() failed: " << cta::Utils::errnoToString(errno); throw ex; } // Get the orginal file-control flags of the socket const int orginalFileControlFlags = fcntl(smartSock.get(), F_GETFL, 0); if(-1 == orginalFileControlFlags) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << "Failed to get the original file-control flags of the socket" - ": Call to fcntl() failed: " << errBuf; + ": Call to fcntl() failed: " << cta::Utils::errnoToString(errno); throw ex; } // Set the O_NONBLOCK file-control flag of the socket if(-1 == fcntl(smartSock.get(), F_SETFL, orginalFileControlFlags | O_NONBLOCK)) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << "Failed to set the O_NONBLOCK file-control flag" - ": Call to fcntl() failed: " << errBuf; + ": Call to fcntl() failed: " << cta::Utils::errnoToString(errno); throw ex; } @@ -997,12 +911,10 @@ int castor::io::connectWithTimeout( // file-control flags of the socket and return it if(0 == connectRc) { if(-1 == fcntl(smartSock.get(), F_SETFL, orginalFileControlFlags)) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; ex.getMessage() << "Failed to restore the file-control flags of the socket" - ": " << errBuf; + ": " << cta::Utils::errnoToString(errno); throw ex; } return smartSock.release(); @@ -1011,10 +923,9 @@ int castor::io::connectWithTimeout( // Throw an exception if there was any other error than // "operation in progress" when trying to start to connect if(EINPROGRESS != connectErrno) { - char errBuf[100]; - sstrerror_r(connectErrno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; - ex.getMessage() << "Call to connect() failed: " << errBuf; + ex.getMessage() << "Call to connect() failed: " + << cta::Utils::errnoToString(connectErrno); throw ex; } @@ -1032,10 +943,9 @@ int castor::io::connectWithTimeout( const int selectRc = select(smartSock.get() + 1, &readFds, &writeFds, NULL, &selectTimeout); if(-1 == selectRc) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); castor::exception::Exception ex; - ex.getMessage() << "Call to select() failed: " << errBuf; + ex.getMessage() << "Call to select() failed: " + << cta::Utils::errnoToString(errno); throw ex; } @@ -1052,8 +962,7 @@ int castor::io::connectWithTimeout( if(!FD_ISSET(smartSock.get(), &readFds) && !FD_ISSET(smartSock.get(), &writeFds)) { castor::exception::Exception ex(ECANCELED); - ex.getMessage() << - "Failed to connect" + ex.getMessage() << "Failed to connect" ": select() returned with no timneout or any descriptors set"; throw ex; } @@ -1065,35 +974,22 @@ int castor::io::connectWithTimeout( // return -1 and set errno, whereas BSD will return 0 and set sockoptError int sockoptError = 0; socklen_t sockoptErrorLen = sizeof(sockoptError); - const int getsockoptRc = getsockopt(smartSock.get(), SOL_SOCKET, SO_ERROR, - &sockoptError, &sockoptErrorLen); - const int getsockoptErrno = errno; - if(-1 == getsockoptRc) { // Solaris - char errBuf[100]; - sstrerror_r(getsockoptErrno, errBuf, sizeof(errBuf)); - castor::exception::Exception ex; - ex.getMessage() << - "Connection did not complete successfully: " << errBuf; - throw ex; - } + cta::exception::Errnum::throwOnMinusOne( + getsockopt(smartSock.get(), SOL_SOCKET, SO_ERROR, &sockoptError, + &sockoptErrorLen), + "In io::connectWithTimeout: failed to getsockopt: "); if(0 != sockoptError) { // BSD - char errBuf[100]; - sstrerror_r(sockoptError, errBuf, sizeof(errBuf)); castor::exception::Exception ex; - ex.getMessage() << - "Connection did not complete successfully: " << errBuf; + ex.getMessage() + << "In io::connectWithTimeout: Connection did not complete successfully: " + << cta::Utils::errnoToString(sockoptError); throw ex; } // Restore the original file-control flags of the socket - if(-1 == fcntl(smartSock.get(), F_SETFL, orginalFileControlFlags)) { - char errBuf[100]; - sstrerror_r(errno, errBuf, sizeof(errBuf)); - castor::exception::Exception ex; - ex.getMessage() << - "Failed to restore the file-control flags of the socket: " << errBuf; - throw ex; - } + cta::exception::Errnum::throwOnMinusOne( + fcntl(smartSock.get(), F_SETFL, orginalFileControlFlags), + "In io::connectWithTimeout: failed to restore flags with fcntl: "); return smartSock.release(); } diff --git a/tapeserver/castor/io/io.hpp b/tapeserver/castor/io/io.hpp index 1afa035130..89621d5dc2 100644 --- a/tapeserver/castor/io/io.hpp +++ b/tapeserver/castor/io/io.hpp @@ -324,25 +324,7 @@ void readBytes( const int socketFd, const int timeout, const int nbBytes, - char *const buf) - ; - -/** - * Reads the specified number of bytes from the specified closable socket - * and writes the result into the specified buffer. - * - * @param socketFd The file descriptor of the socket to be read from. - * @param timeout The timeout in seconds. - * @param nbBytes The number of bytes to be read. - * @param buf The buffer into which the bytes will be written. - * @return True if the connection was closed by the peer, else false. - */ -bool readBytesFromCloseable( - const int socketFd, - const int timeout, - const int nbBytes, - char *const buf) - ; + char *const buf); /** * Writes the specified number of bytes from the specified buffer to the diff --git a/tapeserver/castor/legacymsg/CMakeLists.txt b/tapeserver/castor/legacymsg/CMakeLists.txt index 6a9aa90134..4d96f0ad85 100644 --- a/tapeserver/castor/legacymsg/CMakeLists.txt +++ b/tapeserver/castor/legacymsg/CMakeLists.txt @@ -3,11 +3,17 @@ cmake_minimum_required (VERSION 2.6) include_directories(${PROJECT_SOURCE_DIR}/tapeserver) include_directories(${PROJECT_SOURCE_DIR}/tapeserver/h) +add_library (ctalegacymsg + TapeLabelRqstMsgBody.cpp + RmcProxyTcpIp.cpp + RmcMarshal.cpp + GenericMarshal.cpp + MessageHeader.cpp) + add_library (ctalegacymsgunittests SHARED CommonMarshalTest.cpp GenericMarshalTest.cpp TapeMarshalTest.cpp) target_link_libraries (ctalegacymsgunittests castorclient - castorlegacymsg ctamessages) diff --git a/tapeserver/castor/legacymsg/GenericMarshal.cpp b/tapeserver/castor/legacymsg/GenericMarshal.cpp new file mode 100644 index 0000000000..ca6e1e7e4c --- /dev/null +++ b/tapeserver/castor/legacymsg/GenericMarshal.cpp @@ -0,0 +1,204 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#include "castor/io/io.hpp" +#include "castor/legacymsg/GenericMarshal.hpp" + +#include <string.h> + +//----------------------------------------------------------------------------- +// marshal +//----------------------------------------------------------------------------- +size_t castor::legacymsg::marshal(char *const dst, const size_t dstLen, + const uint32_t srcMagic, const uint32_t srcReqType, + const GenericReplyMsgBody &srcBody) { + const char *const task = "marshal GenericReplyMsgBody"; + + if(dst == NULL) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Pointer to destination buffer is NULL"; + throw ex; + } + + // Calculate the length of the message body + const uint32_t bodyLen = + sizeof(srcBody.status) + + strlen(srcBody.errorMessage) + 1; + + // Calculate the total length of the message (header + body) + const uint32_t totalLen = + sizeof(uint32_t) + // magic + sizeof(uint32_t) + // reqType + sizeof(uint32_t) + // len + bodyLen; + + // Check that the message buffer is big enough + if(totalLen > dstLen) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Buffer too small: required=" << totalLen << " actual=" << dstLen; + throw ex; + } + + // Marshal message header + char *p = dst; + try { + io::marshalUint32(srcMagic, p); + io::marshalUint32(srcReqType, p); + io::marshalUint32(bodyLen, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal header: " + << ne.getMessage().str(); + throw ex; + } + + // Marshal message body + try { + io::marshalUint32(srcBody.status, p); + io::marshalString(srcBody.errorMessage, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal body: " + << ne.getMessage().str(); + throw ex; + } + + // Calculate the number of bytes actually marshalled + const size_t nbBytesMarshalled = p - dst; + + // Check that the number of bytes marshalled was what was expected + if(totalLen != nbBytesMarshalled) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Mismatch between expected total length and actual" + ": expected=" << totalLen << " actual=" << nbBytesMarshalled; + throw ex; + } + + return totalLen; +} + +//----------------------------------------------------------------------------- +// unmarshal +//----------------------------------------------------------------------------- +void castor::legacymsg::unmarshal(const char * &src, size_t &srcLen, + GenericReplyMsgBody &dst) { + try { + io::unmarshalUint32(src, srcLen, dst.status); + io::unmarshalString(src, srcLen, dst.errorMessage); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to unmarshal GenericReplyMsgBody: " << + ne.getMessage().str(); + throw ex; + } +} + +//----------------------------------------------------------------------------- +// marshal +//----------------------------------------------------------------------------- +size_t castor::legacymsg::marshal(char *const dst, const size_t dstLen, + const uint32_t srcMagic, const uint32_t srcReqType, + const GenericErrorReplyMsgBody &srcBody) { + const char *const task = "marshal GenericErrorReplyMsgBody"; + + if(dst == NULL) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Pointer to destination buffer is NULL"; + throw ex; + } + + // Calculate the length of the message body + const uint32_t bodyLen = strlen(srcBody.errorMessage) + 1; + + // Calculate the total length of the message (header + body) + const uint32_t totalLen = + sizeof(uint32_t) + // magic + sizeof(uint32_t) + // reqType + sizeof(uint32_t) + // len + bodyLen; + + // Check that the message buffer is big enough + if(totalLen > dstLen) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Buffer too small: required=" << totalLen << " actual=" << dstLen; + throw ex; + } + + // Marshal message header + char *p = dst; + try { + io::marshalUint32(srcMagic, p); + io::marshalUint32(srcReqType, p); + io::marshalUint32(bodyLen, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal header: " + << ne.getMessage().str(); + throw ex; + } + + // Marshal message body + try { + io::marshalString(srcBody.errorMessage, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal body: " + << ne.getMessage().str(); + throw ex; + } + + // Calculate the number of bytes actually marshalled + const size_t nbBytesMarshalled = p - dst; + + // Check that the number of bytes marshalled was what was expected + if(totalLen != nbBytesMarshalled) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Mismatch between expected total length and actual" + ": expected=" << totalLen << " actual=" << nbBytesMarshalled; + throw ex; + } + + return totalLen; +} + +//----------------------------------------------------------------------------- +// unmarshal +//----------------------------------------------------------------------------- +void castor::legacymsg::unmarshal(const char * &src, size_t &srcLen, + GenericErrorReplyMsgBody &dst) { + try { + io::unmarshalString(src, srcLen, dst.errorMessage); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to unmarshal GenericErrorReplyMsgBody: " << + ne.getMessage().str(); + throw ex; + } +} diff --git a/tapeserver/castor/legacymsg/GenericMarshal_1.hpp b/tapeserver/castor/legacymsg/GenericMarshal_1.hpp new file mode 100644 index 0000000000..c8ca236992 --- /dev/null +++ b/tapeserver/castor/legacymsg/GenericMarshal_1.hpp @@ -0,0 +1,139 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#pragma once + +#include "castor/exception/Exception.hpp" +#include "castor/legacymsg/GenericReplyMsgBody.hpp" +#include "castor/legacymsg/GenericErrorReplyMsgBody.hpp" + +#include <stdint.h> + +namespace castor { +namespace legacymsg { + +/** + * Marshals the specified source message into the specified destination buffer. + * + * Please note that this method marshals the length of the message body as the + * third field of the message header (message header = magic + reqType + len). + * + * @param dst The destination message buffer. + * @param dstLen The length of the destination buffer. + * @param srcMagic The magic number of the source message. + * @param srcReqType The request type of the source message. + * @param srcBody The body of the source message. + * + * @return The total length of the message (header + body). + */ +size_t marshal(char *const dst, const size_t dstLen, const uint32_t srcMagic, + const uint32_t srcReqType, const GenericReplyMsgBody &srcBody) + ; + +/** + * Marshals the specified source message into the specified destination buffer. + * + * Please note that this method marshals the length of the message body as the + * third field of the message header (message header = magic + reqType + len). + * + * @param dst The destination message buffer. + * @param srcMagic The magic number of the source message. + * @param srcReqType The request type of the source message. + * @param srcBody The body of the source message. + * @return The total length of the message (header + body). + */ +template<int n> size_t marshal(char (&dst)[n], const uint32_t srcMagic, + const uint32_t srcReqType, const GenericReplyMsgBody &srcBody) + { + return marshal(dst, n, srcMagic, srcReqType, srcBody); +} + +/** + * Unmarshals a message body with the specified destination structure type + * from the specified source buffer. + * + * @param src In/out parameter, before invocation points to the source + * buffer where the message body should be unmarshalled from and on return + * points to the byte in the source buffer immediately after the + * unmarshalled message body. + * @param srcLen In/out parameter, before invocation is the length of the + * source buffer from where the message body should be unmarshalled and on + * return is the number of bytes remaining in the source buffer. + * @param dst The destination message body structure. + */ +void unmarshal(const char * &src, size_t &srcLen, GenericReplyMsgBody &dst) ; + +/** + * Marshals the specified source message into the specified destination buffer. + * + * Please note that this method marshals the length of the message body as the + * third field of the message header (message header = magic + reqType + len). + * + * @param dst The destination message buffer. + * @param dstLen The length of the destination buffer. + * @param srcMagic The magic number of the source message. + * @param srcReqType The request type of the source message. + * @param srcBody The body of the source message. + * + * @return The total length of the message (header + body). + */ +size_t marshal(char *const dst, const size_t dstLen, const uint32_t srcMagic, + const uint32_t srcReqType, const GenericErrorReplyMsgBody &srcBody) + ; + +/** + * Marshals the specified source message into the specified destination buffer. + * + * Please note that this method marshals the length of the message body as the + * third field of the message header (message header = magic + reqType + len). + * + * @param dst The destination message buffer. + * @param srcMagic The magic number of the source message. + * @param srcReqType The request type of the source message. + * @param srcBody The body of the source message. + * @return The total length of the message (header + body). + */ +template<int n> size_t marshal(char (&dst)[n], const uint32_t srcMagic, + const uint32_t srcReqType, const GenericErrorReplyMsgBody &srcBody) + { + return marshal(dst, n, srcMagic, srcReqType, srcBody); +} + +/** + * Unmarshals a message body with the specified destination structure type + * from the specified source buffer. + * + * @param src In/out parameter, before invocation points to the source + * buffer where the message body should be unmarshalled from and on return + * points to the byte in the source buffer immediately after the + * unmarshalled message body. + * @param srcLen In/out parameter, before invocation is the length of the + * source buffer from where the message body should be unmarshalled and on + * return is the number of bytes remaining in the source buffer. + * @param dst The destination message body structure. + */ +void unmarshal(const char * &src, size_t &srcLen, GenericErrorReplyMsgBody &dst) ; + +} // namespace legacymsg +} // namespace castor diff --git a/tapeserver/castor/legacymsg/MessageHeader.cpp b/tapeserver/castor/legacymsg/MessageHeader.cpp new file mode 100644 index 0000000000..8b0a71364c --- /dev/null +++ b/tapeserver/castor/legacymsg/MessageHeader.cpp @@ -0,0 +1,33 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#include "castor/legacymsg/MessageHeader.hpp" + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +castor::legacymsg::MessageHeader::MessageHeader() throw(): + magic(0), + reqType(0), + lenOrStatus(0) { +} diff --git a/tapeserver/castor/legacymsg/RmcMarshal.cpp b/tapeserver/castor/legacymsg/RmcMarshal.cpp new file mode 100644 index 0000000000..24447fa905 --- /dev/null +++ b/tapeserver/castor/legacymsg/RmcMarshal.cpp @@ -0,0 +1,231 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#include "castor/io/io.hpp" +#include "castor/legacymsg/RmcMarshal.hpp" +#include "rmc_constants.h" + +#include <string.h> + +//----------------------------------------------------------------------------- +// marshal +//----------------------------------------------------------------------------- +size_t castor::legacymsg::marshal(char *const dst, const size_t dstLen, const RmcMountMsgBody &src) { + const char *task = "marshal RmcMountMsgBody"; + + if(dst == NULL) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Pointer to destination buffer is NULL"; + throw ex; + } + + // Calculate the length of the message body + const uint32_t bodyLen = + sizeof(src.uid) + + sizeof(src.gid) + + strlen(src.unusedLoader) + 1 + + strlen(src.vid) + 1 + + sizeof(src.side) + + sizeof(src.drvOrd); + + // Calculate the total length of the message (header + body) + const uint32_t totalLen = + sizeof(uint32_t) + // magic + sizeof(uint32_t) + // reqType + sizeof(uint32_t) + // len + bodyLen; + + // Check that the message buffer is big enough + if(totalLen > dstLen) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Buffer too small: required=" << totalLen << " actual=" << dstLen; + throw ex; + } + + // Marshal message header + char *p = dst; + try { + const uint32_t magic = RMC_MAGIC; + const uint32_t reqType = RMC_MOUNT; + io::marshalUint32(magic , p); + io::marshalUint32(reqType, p); + io::marshalUint32(totalLen, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal header: " + << ne.getMessage().str(); + throw ex; + } + + // Marshal message body + try { + io::marshalUint32(src.uid, p); + io::marshalUint32(src.gid, p); + io::marshalString(src.unusedLoader, p); + io::marshalString(src.vid, p); + io::marshalUint16(src.side, p); + io::marshalUint16(src.drvOrd, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal body: " + << ne.getMessage().str(); + throw ex; + } + + // Calculate the number of bytes actually marshalled + const size_t nbBytesMarshalled = p - dst; + + // Check that the number of bytes marshalled was what was expected + if(totalLen != nbBytesMarshalled) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Mismatch between expected total length and actual" + ": expected=" << totalLen << " actual=" << nbBytesMarshalled; + throw ex; + } + + return totalLen; +} + +//----------------------------------------------------------------------------- +// unmarshal +//----------------------------------------------------------------------------- +void castor::legacymsg::unmarshal(const char * &src, size_t &srcLen, RmcMountMsgBody &dst) { + try { + io::unmarshalUint32(src, srcLen, dst.uid); + io::unmarshalUint32(src, srcLen, dst.gid); + io::unmarshalString(src, srcLen, dst.unusedLoader); + io::unmarshalString(src, srcLen, dst.vid); + io::unmarshalUint16(src, srcLen, dst.side); + io::unmarshalUint16(src, srcLen, dst.drvOrd); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to unmarshal RmcMountMsgBody: " << + ne.getMessage().str(); + throw ex; + } +} + +//----------------------------------------------------------------------------- +// marshal +//----------------------------------------------------------------------------- +size_t castor::legacymsg::marshal(char *const dst, const size_t dstLen, const RmcUnmountMsgBody &src) { + const char *const task = "marshal RmcUnmountMsgBody"; + + if(dst == NULL) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Pointer to destination buffer is NULL"; + throw ex; + } + + // Calculate the length of the message body + const uint32_t bodyLen = + sizeof(src.uid) + + sizeof(src.gid) + + strlen(src.unusedLoader) + 1 + + strlen(src.vid) + 1 + + sizeof(src.drvOrd) + + sizeof(src.force); + + // Calculate the total length of the message (header + body) + const uint32_t totalLen = + sizeof(uint32_t) + // magic + sizeof(uint32_t) + // reqType + sizeof(uint32_t) + // len + bodyLen; + + // Check that the message buffer is big enough + if(totalLen > dstLen) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Buffer too small: required=" << totalLen << " actual=" << dstLen; + throw ex; + } + + // Marshal message header + char *p = dst; + try { + const uint32_t magic = RMC_MAGIC; + const uint32_t reqType = RMC_UNMOUNT; + io::marshalUint32(magic , p); + io::marshalUint32(reqType, p); + io::marshalUint32(totalLen, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal header: " + << ne.getMessage().str(); + throw ex; + } + + // Marshal message body + try { + io::marshalUint32(src.uid, p); + io::marshalUint32(src.gid, p); + io::marshalString(src.unusedLoader, p); + io::marshalString(src.vid, p); + io::marshalUint16(src.drvOrd, p); + io::marshalUint16(src.force, p); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << ": Failed to marshal body: " + << ne.getMessage().str(); + throw ex; + } + + // Calculate the number of bytes actually marshalled + const size_t nbBytesMarshalled = p - dst; + + // Check that the number of bytes marshalled was what was expected + if(totalLen != nbBytesMarshalled) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to " << task << + ": Mismatch between expected total length and actual" + ": expected=" << totalLen << " actual=" << nbBytesMarshalled; + throw ex; + } + + return totalLen; +} + +//----------------------------------------------------------------------------- +// unmarshal +//----------------------------------------------------------------------------- +void castor::legacymsg::unmarshal(const char * &src, size_t &srcLen, RmcUnmountMsgBody &dst) { + try { + io::unmarshalUint32(src, srcLen, dst.uid); + io::unmarshalUint32(src, srcLen, dst.gid); + io::unmarshalString(src, srcLen, dst.unusedLoader); + io::unmarshalString(src, srcLen, dst.vid); + io::unmarshalUint16(src, srcLen, dst.drvOrd); + io::unmarshalUint16(src, srcLen, dst.force); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to unmarshal RmcUnmountMsgBody: " << + ne.getMessage().str(); + throw ex; + } +} diff --git a/tapeserver/castor/legacymsg/RmcMarshal.hpp b/tapeserver/castor/legacymsg/RmcMarshal.hpp new file mode 100644 index 0000000000..b1742f36a7 --- /dev/null +++ b/tapeserver/castor/legacymsg/RmcMarshal.hpp @@ -0,0 +1,112 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#pragma once + +#include "castor/exception/Exception.hpp" +#include "castor/legacymsg/RmcMountMsgBody.hpp" +#include "castor/legacymsg/RmcUnmountMsgBody.hpp" + +namespace castor { +namespace legacymsg { + +/** + * Marshals the specified source message body structure and its implicit + * header into the specified destination buffer. + * + * @param dst The destination message buffer. + * @param dstLen The length of the destination buffer. + * @param src The source structure. + * @return The total length of the message (header + body). + */ +size_t marshal(char *const dst, const size_t dstLen, const RmcMountMsgBody &src) ; + +/** + * Marshals the specified source message body structure and its implicit + * header into the specified destination buffer. + * + * @param dst The destination message buffer. + * @param src The source structure. + * @return The total length of the message (header + body). + */ +template<int n> size_t marshal(char (&dst)[n], const RmcMountMsgBody &src) { + return marshal(dst, n, src); +} + +/** + * Unmarshals a message body with the specified destination structure type + * from the specified source buffer. + * + * @param src In/out parameter, before invocation points to the source + * buffer where the message body should be unmarshalled from and on return + * points to the byte in the source buffer immediately after the + * unmarshalled message body. + * @param srcLen In/out parameter, before invocation is the length of the + * source buffer from where the message body should be unmarshalled and on + * return is the number of bytes remaining in the source buffer. + * @param dst The destination message body structure. + */ +void unmarshal(const char * &src, size_t &srcLen, RmcMountMsgBody &dst) ; + +/** + * Marshals the specified source message body structure and its implicit + * header into the specified destination buffer. + * + * @param dst The destination message buffer. + * @param dstLen The length of the destination buffer. + * @param src The source structure. + * @return The total length of the message (header + body). + */ +size_t marshal(char *const dst, const size_t dstLen, const RmcUnmountMsgBody &src) ; + +/** + * Marshals the specified source message body structure and its implicit + * header into the specified destination buffer. + * + * @param dst The destination message buffer. + * @param src The source structure. + * @return The total length of the message (header + body). + */ +template<int n> size_t marshal(char (&dst)[n], const RmcUnmountMsgBody &src) { + return marshal(dst, n, src); +} + +/** + * Unmarshals a message body with the specified destination structure type + * from the specified source buffer. + * + * @param src In/out parameter, before invocation points to the source + * buffer where the message body should be unmarshalled from and on return + * points to the byte in the source buffer immediately after the + * unmarshalled message body. + * @param srcLen In/out parameter, before invocation is the length of the + * source buffer from where the message body should be unmarshalled and on + * return is the number of bytes remaining in the source buffer. + * @param dst The destination message body structure. + */ +void unmarshal(const char * &src, size_t &srcLen, RmcUnmountMsgBody &dst) ; + +} // namespace legacymsg +} // namespace castor + diff --git a/tapeserver/castor/legacymsg/RmcProxyTcpIp.cpp b/tapeserver/castor/legacymsg/RmcProxyTcpIp.cpp new file mode 100644 index 0000000000..ffcf8743c1 --- /dev/null +++ b/tapeserver/castor/legacymsg/RmcProxyTcpIp.cpp @@ -0,0 +1,234 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#include "castor/io/io.hpp" +#include "castor/legacymsg/CommonMarshal.hpp" +#include "castor/legacymsg/RmcMarshal.hpp" +#include "castor/legacymsg/RmcProxyTcpIp.hpp" +#include "castor/utils/SmartFd.hpp" +#include "castor/utils/utils.hpp" +#include "Castor_limits.h" +#include "rmc_constants.h" + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +castor::legacymsg::RmcProxyTcpIp::RmcProxyTcpIp( + const unsigned short rmcPort, + const int netTimeout, + const unsigned int maxRqstAttempts) throw(): + m_rmcPort(rmcPort), + m_netTimeout(netTimeout), + m_maxRqstAttempts(maxRqstAttempts) { +} + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +castor::legacymsg::RmcProxyTcpIp::~RmcProxyTcpIp() throw() { +} + +//------------------------------------------------------------------------------ +// mountTapeReadOnly +//------------------------------------------------------------------------------ +void castor::legacymsg::RmcProxyTcpIp::mountTapeReadOnly( + const std::string &vid, const mediachanger::ScsiLibrarySlot &librarySlot) { + // SCSI libraries do not support read-only mounts + mountTapeReadWrite(vid, librarySlot); +} + +//------------------------------------------------------------------------------ +// mountTapeReadWrite +//------------------------------------------------------------------------------ +void castor::legacymsg::RmcProxyTcpIp::mountTapeReadWrite( + const std::string &vid, const mediachanger::ScsiLibrarySlot &librarySlot) { + try { + RmcMountMsgBody rqstBody; + rqstBody.uid = geteuid(); + rqstBody.gid = getegid(); + castor::utils::copyString(rqstBody.vid, vid); + rqstBody.drvOrd = librarySlot.getDrvOrd(); + + rmcSendRecvNbAttempts(m_maxRqstAttempts, librarySlot.getRmcHostName(), + rqstBody); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to mount tape in SCSI tape-library for read/write access" + ": vid=" << vid << " librarySlot=" << librarySlot.str() << ": " << + ne.getMessage().str(); + throw ex; + } +} + +//------------------------------------------------------------------------------ +// dismountTape +//------------------------------------------------------------------------------ +void castor::legacymsg::RmcProxyTcpIp::dismountTape(const std::string &vid, + const mediachanger::ScsiLibrarySlot &librarySlot) { + try { + RmcUnmountMsgBody rqstBody; + rqstBody.uid = geteuid(); + rqstBody.gid = getegid(); + castor::utils::copyString(rqstBody.vid, vid); + rqstBody.drvOrd = librarySlot.getDrvOrd(); + rqstBody.force = 0; + + rmcSendRecvNbAttempts(m_maxRqstAttempts, librarySlot.getRmcHostName(), + rqstBody); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to dismount tape in SCSI tape-library" + ": vid=" << vid << " librarySlot=" << librarySlot.str() << ": " << + ne.getMessage().str(); + throw ex; + } +} + +//------------------------------------------------------------------------------ +// forceDismountTape +//------------------------------------------------------------------------------ +void castor::legacymsg::RmcProxyTcpIp::forceDismountTape(const std::string &vid, + const mediachanger::ScsiLibrarySlot &librarySlot) { + // SCSI libraries do not support forced dismounts + dismountTape(vid, librarySlot); +} + +//----------------------------------------------------------------------------- +// connectToRmc +//----------------------------------------------------------------------------- +int castor::legacymsg::RmcProxyTcpIp::connectToRmc(const std::string &rmcHost) + const { + castor::utils::SmartFd smartConnectSock; + try { + smartConnectSock.reset(io::connectWithTimeout(rmcHost, m_rmcPort, + m_netTimeout)); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to connect to rmcd: rmcHost=" << rmcHost + << " rmcPort=" << RMC_PORT << ": " << ne.getMessage().str(); + throw ex; + } + + return smartConnectSock.release(); +} + +//----------------------------------------------------------------------------- +// writeRmcMountMsg +//----------------------------------------------------------------------------- +void castor::legacymsg::RmcProxyTcpIp::writeRmcMountMsg(const int fd, + const RmcMountMsgBody &body) { + char buf[RMC_MSGBUFSIZ]; + const size_t len = marshal(buf, body); + + try { + io::writeBytes(fd, m_netTimeout, len, buf); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to write RMC_SCSI_MOUNT message: " + << ne.getMessage().str(); + throw ex; + } +} + +//----------------------------------------------------------------------------- +// readRmcMsgHeader +//----------------------------------------------------------------------------- +castor::legacymsg::MessageHeader + castor::legacymsg::RmcProxyTcpIp::readRmcMsgHeader(const int fd) { + char buf[12]; // Magic + type + len + MessageHeader header; + + try { + io::readBytes(fd, m_netTimeout, sizeof(buf), buf); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to read message header: " + << ne.getMessage().str(); + throw ex; + } + + const char *bufPtr = buf; + size_t bufLen = sizeof(buf); + unmarshal(bufPtr, bufLen, header); + + if(RMC_MAGIC != header.magic) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to read message header: " + " Header contains an invalid magic number: expected=0x" << std::hex << + RMC_MAGIC << " actual=0x" << header.magic; + throw ex; + } + + return header; +} + +//----------------------------------------------------------------------------- +// writeRmcUnmountMsg +//----------------------------------------------------------------------------- +void castor::legacymsg::RmcProxyTcpIp::writeRmcUnmountMsg(const int fd, + const RmcUnmountMsgBody &body) { + char buf[RMC_MSGBUFSIZ]; + const size_t len = marshal(buf, body); + + try { + io::writeBytes(fd, m_netTimeout, len, buf); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << "Failed to write RMC_SCSI_UNMOUNT message: " + << ne.getMessage().str(); + throw ex; + } +} + +//----------------------------------------------------------------------------- +// rmcReplyTypeToStr +//----------------------------------------------------------------------------- +std::string castor::legacymsg::RmcProxyTcpIp::rmcReplyTypeToStr( + const int replyType) { + std::ostringstream oss; + switch(replyType) { + case RMC_RC: + oss << "RMC_RC"; + break; + case MSG_ERR: + oss << "MSG_ERR"; + break; + default: + oss << "UNKNOWN(0x" << std::hex << replyType << ")"; + } + return oss.str(); +} + +//----------------------------------------------------------------------------- +// handleMSG_ERR +//----------------------------------------------------------------------------- +std::string castor::legacymsg::RmcProxyTcpIp::handleMSG_ERR( + const MessageHeader &header, + const int fd) { + char errorBuf[1024]; + const int nbBytesToRead = header.lenOrStatus > sizeof(errorBuf) ? + sizeof(errorBuf) : header.lenOrStatus; + io::readBytes(fd, m_netTimeout, nbBytesToRead, errorBuf); + errorBuf[sizeof(errorBuf) - 1] = '\0'; + return errorBuf; +} diff --git a/tapeserver/castor/legacymsg/TapeLabelRqstMsgBody.cpp b/tapeserver/castor/legacymsg/TapeLabelRqstMsgBody.cpp new file mode 100644 index 0000000000..abff6146dc --- /dev/null +++ b/tapeserver/castor/legacymsg/TapeLabelRqstMsgBody.cpp @@ -0,0 +1,38 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#include "castor/legacymsg/TapeLabelRqstMsgBody.hpp" + +#include <string.h> + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +castor::legacymsg::TapeLabelRqstMsgBody::TapeLabelRqstMsgBody() throw(): + force(0), + uid(0), + gid(0) { + memset(vid, '\0', sizeof(vid)); + memset(drive, '\0', sizeof(drive)); + memset(logicalLibrary, '\0', sizeof(logicalLibrary)); +} diff --git a/tapeserver/castor/server/CMakeLists.txt b/tapeserver/castor/server/CMakeLists.txt index 986504a02f..bab11e39ff 100644 --- a/tapeserver/castor/server/CMakeLists.txt +++ b/tapeserver/castor/server/CMakeLists.txt @@ -4,7 +4,12 @@ include_directories(${PROJECT_SOURCE_DIR}/tapeserver) include_directories(${PROJECT_SOURCE_DIR}/tapeserver/h) add_library (ctaserverutils SHARED - ProcessCapDummy.cpp) + ProcessCapDummy.cpp + Semaphores.cpp + Mutex.cpp + Threading.cpp + ProcessCap.cpp + Daemon.cpp) add_library (ctaserverunittests SHARED AtomicCounterTest.cpp @@ -15,5 +20,4 @@ add_library (ctaserverunittests SHARED ThreadingMTTests.cpp ThreadingTests.cpp) -target_link_libraries (ctaserverunittests - castorserver) +target_link_libraries (ctaserverunittests) diff --git a/tapeserver/castor/server/Daemon.cpp b/tapeserver/castor/server/Daemon.cpp new file mode 100644 index 0000000000..cf9857c8c1 --- /dev/null +++ b/tapeserver/castor/server/Daemon.cpp @@ -0,0 +1,234 @@ +/******************************************************************************* + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * @author Castor Dev team, castor-dev@cern.ch + ******************************************************************************/ + +#include "castor/dlf/Dlf.hpp" +#include "castor/exception/Errnum.hpp" +#include "castor/server/Daemon.hpp" +#include "castor/server/ThreadNotification.hpp" +#include "castor/System.hpp" +#include <getopt.h> + +#include <signal.h> +#include <stdio.h> +#include <unistd.h> + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +castor::server::Daemon::Daemon(std::ostream &stdOut, std::ostream &stdErr, + log::Logger &log) throw(): + m_stdOut(stdOut), + m_stdErr(stdErr), + m_log(log), + m_foreground(false), + m_commandLineHasBeenParsed(false) { +} + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +castor::server::Daemon::~Daemon() throw() { +} + +//------------------------------------------------------------------------------ +// parseCommandLine +//------------------------------------------------------------------------------ +void castor::server::Daemon::parseCommandLine(int argc, + char *argv[]) { + struct ::option longopts[4]; + + longopts[0].name = "foreground"; + longopts[0].has_arg = no_argument; + longopts[0].flag = NULL; + longopts[0].val = 'f'; + + longopts[1].name = "config"; + longopts[1].has_arg = required_argument; + longopts[1].flag = NULL; + longopts[1].val = 'c'; + + longopts[2].name = "help"; + longopts[2].has_arg = no_argument; + longopts[2].flag = NULL; + longopts[2].val = 'h'; + + longopts[3].name = 0; + + char c; + while ((c = getopt_long(argc, argv, "fc:h", longopts, NULL)) != -1) { + switch (c) { + case 'f': + m_foreground = true; + break; + case 'c': + setenv("PATH_CONFIG", optarg, 1); + m_stdOut << "Using configuration file " << optarg << std::endl; + break; + case 'h': + help(argv[0]); + exit(0); + break; + default: + break; + } + } + + m_commandLineHasBeenParsed = true; +} + +//------------------------------------------------------------------------------ +// help +//------------------------------------------------------------------------------ +void castor::server::Daemon::help(const std::string &programName) + throw() { + m_stdOut << "Usage: " << programName << " [options]\n" + "\n" + "where options can be:\n" + "\n" + "\t--foreground or -f \tRemain in the Foreground\n" + "\t--config <config-file> or -c \tConfiguration file\n" + "\t--metrics or -m \tEnable metrics collection\n" + "\t--help or -h \tPrint this help and exit\n" + "\n" + "Comments to: Castor.Support@cern.ch\n"; +} + +//------------------------------------------------------------------------------ +// getServerName +//------------------------------------------------------------------------------ +const std::string &castor::server::Daemon::getServerName() const throw() { + return m_log.getProgramName(); +} + +//------------------------------------------------------------------------------ +// getForeground +//------------------------------------------------------------------------------ +bool castor::server::Daemon::getForeground() const + { + if(!m_commandLineHasBeenParsed) { + castor::exception::CommandLineNotParsed ex; + ex.getMessage() << + "Failed to determine whether or not the daemon should run in the" + " foreground because the command-line has not yet been parsed"; + throw ex; + } + + return m_foreground; +} + +//----------------------------------------------------------------------------- +// setCommandLineParsed +//----------------------------------------------------------------------------- +void castor::server::Daemon::setCommandLineHasBeenParsed(const bool foreground) + throw() { + m_foreground = foreground; + m_commandLineHasBeenParsed = true; +} + +//----------------------------------------------------------------------------- +// dlfInit +//----------------------------------------------------------------------------- +void castor::server::Daemon::dlfInit(castor::dlf::Message messages[]) + { + castor::dlf::dlf_init((char*)m_log.getProgramName().c_str(), messages); + // Add framework specific messages + castor::dlf::Message frameworkMessages[] = + {{ 1, "Error while reading datagrams" }, + { 2, "Error while accepting connections" }, + { 3, "Thread pool started" }, + { 4, "Exception caught in the user thread" }, + { 5, "Thread run error" }, + { 6, "NotifierThread exception" }, + { 8, "Exception caught while initializing the child process" }, + { 9, "Error while processing an object from the pipe" }, + { 10, "Uncaught exception in a thread from pool" }, + { 11, "Uncaught GENERAL exception in a thread from pool" }, + { 12, "Caught signal - GRACEFUL STOP" }, + { 14, "Caught signal - CHILD STOPPED" }, + { 15, "Signal caught but not handled - IMMEDIATE STOP" }, + { 16, "Exception during wait for signal loop" }, + { 18, "No idle thread in pool to process request" }, + { 19, "Error while dispatching to a thread" }, + { 20, "Spawning a new thread in pool" }, + { 21, "Terminating a thread in pool" }, + { 22, "Task processed" }, + { -1, "" }}; + castor::dlf::dlf_addMessages(DLF_BASE_FRAMEWORK, frameworkMessages); +} + +//------------------------------------------------------------------------------ +// daemonizeIfNotRunInForeground +//------------------------------------------------------------------------------ +void castor::server::Daemon::daemonizeIfNotRunInForeground( + const bool runAsStagerSuperuser) { + // Do nothing if already a daemon + if (1 == getppid()) { + return; + } + + // If the daemon is to be run in the background + if (!m_foreground) { + m_log.prepareForFork(); + + { + pid_t pid = 0; + castor::exception::Errnum::throwOnNegative(pid = fork(), + "Failed to daemonize: Failed to fork"); + // If we got a good PID, then we can exit the parent process + if (0 < pid) { + exit(EXIT_SUCCESS); + } + } + + // We could set our working directory to '/' here with a call to chdir(2). + // For the time being we don't and leave it to the initd script to change + // to a suitable directory for us! + + // Change the file mode mask + umask(0); + + // Run the daemon in a new session + castor::exception::Errnum::throwOnNegative(setsid(), + "Failed to daemonize: Failed to run daemon is a new session"); + + // Redirect standard files to /dev/null + castor::exception::Errnum::throwOnNull( + freopen("/dev/null", "r", stdin), + "Failed to daemonize: Falied to freopen stdin"); + castor::exception::Errnum::throwOnNull( + freopen("/dev/null", "w", stdout), + "Failed to daemonize: Failed to freopen stdout"); + castor::exception::Errnum::throwOnNull( + freopen("/dev/null", "w", stderr), + "Failed to daemonize: Failed to freopen stderr"); + } // if (!m_foreground) + + // Change the user of the daemon process to the Castor superuser if requested + if (runAsStagerSuperuser) { + castor::System::switchToCastorSuperuser(); + } + + // Ignore SIGPIPE (connection lost with client) + // and SIGXFSZ (a file is too big) + signal(SIGPIPE, SIG_IGN); + signal(SIGXFSZ, SIG_IGN); +} + diff --git a/tapeserver/castor/server/Daemon.hpp b/tapeserver/castor/server/Daemon.hpp index 3eeb2eb932..3a953e4ec9 100644 --- a/tapeserver/castor/server/Daemon.hpp +++ b/tapeserver/castor/server/Daemon.hpp @@ -113,17 +113,6 @@ protected: */ void daemonizeIfNotRunInForeground(const bool runAsStagerSuperuser); - /** - * Sends a notification message to the given host,port - * to wake up nbThreads threads to handle pending requests. - * @param host the destination host - * @param port the destination port - * @param tpName the name of the thread pool to be signaled - * @param nbThreads the number of threads to be signaled - */ - static void sendNotification(const std::string &host, const int port, - const char tpName, const int nbThreads = 1) throw(); - /** * Stream representing standard out. */ diff --git a/tapeserver/castor/server/Mutex.cpp b/tapeserver/castor/server/Mutex.cpp new file mode 100644 index 0000000000..ad034c727f --- /dev/null +++ b/tapeserver/castor/server/Mutex.cpp @@ -0,0 +1,49 @@ +#include "castor/server/Mutex.hpp" +#include "castor/exception/Errnum.hpp" +#include "castor/exception/Exception.hpp" + +//------------------------------------------------------------------------------ +//constructor +//------------------------------------------------------------------------------ +castor::server::Mutex::Mutex() { + pthread_mutexattr_t attr; + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutexattr_init(&attr), + "Error from pthread_mutexattr_init in castor::server::Mutex::Mutex()"); + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK), + "Error from pthread_mutexattr_settype in castor::server::Mutex::Mutex()"); + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_init(&m_mutex, &attr), + "Error from pthread_mutex_init in castor::server::Mutex::Mutex()"); + try { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutexattr_destroy(&attr), + "Error from pthread_mutexattr_destroy in castor::server::Mutex::Mutex()"); + } catch (...) { + pthread_mutex_destroy(&m_mutex); + throw; + } +} +//------------------------------------------------------------------------------ +//destructor +//------------------------------------------------------------------------------ +castor::server::Mutex::~Mutex() { + pthread_mutex_destroy(&m_mutex); +} +//------------------------------------------------------------------------------ +//lock +//------------------------------------------------------------------------------ +void castor::server::Mutex::lock() { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_lock(&m_mutex), + "Error from pthread_mutex_lock in castor::server::Mutex::lock()"); +} +//------------------------------------------------------------------------------ +//unlock +//------------------------------------------------------------------------------ +void castor::server::Mutex::unlock() { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_unlock(&m_mutex), + "Error from pthread_mutex_unlock in castor::server::Mutex::unlock()"); +} diff --git a/tapeserver/castor/server/ProcessCap.cpp b/tapeserver/castor/server/ProcessCap.cpp new file mode 100644 index 0000000000..34189aa3dd --- /dev/null +++ b/tapeserver/castor/server/ProcessCap.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#include "castor/exception/Exception.hpp" +#include "castor/server/ProcessCap.hpp" +#include "castor/server/SmartCap.hpp" +#include "common/Utils.hpp" + +#include <errno.h> + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +castor::server::ProcessCap::~ProcessCap() + throw() { +} + +//------------------------------------------------------------------------------ +// getProcText +//------------------------------------------------------------------------------ +std::string castor::server::ProcessCap::getProcText() { + try { + SmartCap cap(getProc()); + return toText((cap_t)cap.get()); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to get text representation of the capabilities of the process: " + << ne.getMessage().str(); + throw ex; + } +} + +//------------------------------------------------------------------------------ +// getProc +//------------------------------------------------------------------------------ +cap_t castor::server::ProcessCap::getProc() { + cap_t cap = cap_get_proc(); + if(NULL == cap) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to get the capabilities of the process: " + << cta::Utils::errnoToString(errno); + throw ex; + } + return cap; +} + +//------------------------------------------------------------------------------ +// toText +//------------------------------------------------------------------------------ +std::string castor::server::ProcessCap::toText( + const cap_t cap) { + // Create a C++ string with the result of calling cap_to_text() + char *const text = cap_to_text(cap, NULL); + if(NULL == text) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to create string representation of capability state: " + << cta::Utils::errnoToString(errno); + throw ex; + } + std::string result(text); + + // Free the memory allocated by cap_to_text() + if(cap_free(text)) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to free string representation of capability state: " + << cta::Utils::errnoToString(errno); + throw ex; + } + + // Return the C++ string + return result; +} + +//------------------------------------------------------------------------------ +// setProcText +//------------------------------------------------------------------------------ +void castor::server::ProcessCap::setProcText(const std::string &text) { + try { + SmartCap cap(fromText(text)); + setProc(cap.get()); + } catch(castor::exception::Exception &ne) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to set capabilities of process: " << ne.getMessage().str(); + throw ex; + } +} + +//------------------------------------------------------------------------------ +// fromText +//------------------------------------------------------------------------------ +cap_t castor::server::ProcessCap::fromText(const std::string &text) { + const cap_t cap = cap_from_text(text.c_str()); + if(NULL == cap) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to create capability state from string representation" + ": text='" << text << "': " << cta::Utils::errnoToString(errno); + throw ex; + } + + return cap; +} + +//------------------------------------------------------------------------------ +// setProc +//------------------------------------------------------------------------------ +void castor::server::ProcessCap::setProc(const cap_t cap) { + if(cap_set_proc(cap)) { + castor::exception::Exception ex; + ex.getMessage() << + "Failed to set the capabilities of the process: " + << cta::Utils::errnoToString(errno); + throw ex; + } +} diff --git a/tapeserver/castor/server/Semaphores.cpp b/tapeserver/castor/server/Semaphores.cpp new file mode 100644 index 0000000000..4ad79704b8 --- /dev/null +++ b/tapeserver/castor/server/Semaphores.cpp @@ -0,0 +1,167 @@ +#include "castor/server/MutexLocker.hpp" +#include "castor/server/Semaphores.hpp" +#include "castor/server/Threading.hpp" +#include "castor/exception/Errnum.hpp" +#include "castor/exception/Exception.hpp" +#include <errno.h> +#include <sys/time.h> + +//------------------------------------------------------------------------------ +//PosixSemaphore constructor +//------------------------------------------------------------------------------ +castor::server::PosixSemaphore::PosixSemaphore(int initial) + { + castor::exception::Errnum::throwOnReturnedErrno( + sem_init(&m_sem, 0, initial), + "Error from sem_init in castor::server::PosixSemaphore::PosixSemaphore()"); +} +//------------------------------------------------------------------------------ +//PosixSemaphore destructor +//------------------------------------------------------------------------------ +castor::server::PosixSemaphore::~PosixSemaphore() { + /* There is a danger of destroying the semaphore in the consumer + while the producer is still referring to the object. + This mutex prevents this from happening. (The release method locks it). */ + MutexLocker ml(&m_mutexPosterProtection); + sem_destroy(&m_sem); +} +//------------------------------------------------------------------------------ +//acquire +//------------------------------------------------------------------------------ +void castor::server::PosixSemaphore::acquire() + { + int ret; + /* If we receive EINTR, we should just keep trying (signal interruption) */ + while((ret = sem_wait(&m_sem)) && EINTR == errno) {} + /* If it was not EINTR, it's a failure */ + castor::exception::Errnum::throwOnNonZero(ret, + "Error from sem_wait in castor::server::PosixSemaphore::acquire()"); +} +//------------------------------------------------------------------------------ +//acquire +//------------------------------------------------------------------------------ +void castor::server::PosixSemaphore::acquireWithTimeout(uint64_t timeout_us) + { + int ret; + struct timeval tv; + gettimeofday(&tv, NULL); + struct timespec ts; + // Add microseconds + ts.tv_nsec = (tv.tv_usec + (timeout_us % 1000000)) * 1000; + // Forward carry and add seconds + ts.tv_sec = tv.tv_sec + timeout_us / 1000000 + ts.tv_nsec / 1000000000; + // Clip what we carried + ts.tv_nsec %= 1000000000; + /* If we receive EINTR, we should just keep trying (signal interruption) */ + while((ret = sem_timedwait(&m_sem, &ts)) && EINTR == errno) {} + /* If we got a timeout, throw a special exception */ + if (ret && ETIMEDOUT == errno) { throw Timeout(); } + /* If it was not EINTR, it's a failure */ + castor::exception::Errnum::throwOnNonZero(ret, + "Error from sem_wait in castor::server::PosixSemaphore::acquireWithTimeout()"); +} + +//------------------------------------------------------------------------------ +//tryAcquire +//------------------------------------------------------------------------------ +bool castor::server::PosixSemaphore::tryAcquire() + { + int ret = sem_trywait(&m_sem); + if (!ret) return true; + if (ret && EAGAIN == errno) return false; + castor::exception::Errnum::throwOnNonZero(ret, + "Error from sem_trywait in castor::server::PosixSemaphore::tryAcquire()"); + /* unreacheable, just for compiler happiness */ + return false; +} +//------------------------------------------------------------------------------ +//release +//------------------------------------------------------------------------------ +void castor::server::PosixSemaphore::release(int n) + { + for (int i=0; i<n; i++) { + MutexLocker ml(&m_mutexPosterProtection); + castor::exception::Errnum::throwOnNonZero(sem_post(&m_sem), + "Error from sem_post in castor::server::PosixSemaphore::release()"); + } +} +//------------------------------------------------------------------------------ +//CondVarSemaphore constructor +//------------------------------------------------------------------------------ +castor::server::CondVarSemaphore::CondVarSemaphore(int initial) +:m_value(initial) { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_cond_init(&m_cond, NULL), + "Error from pthread_cond_init in castor::server::CondVarSemaphore::CondVarSemaphore()"); + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_init(&m_mutex, NULL), + "Error from pthread_mutex_init in castor::server::CondVarSemaphore::CondVarSemaphore()"); + } + +//------------------------------------------------------------------------------ +//CondVarSemaphore destructor +//------------------------------------------------------------------------------ +castor::server::CondVarSemaphore::~CondVarSemaphore() { + /* Barrier protecting the last user */ + pthread_mutex_lock(&m_mutex); + pthread_mutex_unlock(&m_mutex); + /* Cleanup */ + pthread_cond_destroy(&m_cond); + pthread_mutex_destroy(&m_mutex); + } +//------------------------------------------------------------------------------ +//acquire +//------------------------------------------------------------------------------ +void castor::server::CondVarSemaphore::acquire() + { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_lock(&m_mutex), + "Error from pthread_mutex_lock in castor::server::CondVarSemaphore::acquire()"); + while (m_value <= 0) { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_cond_wait(&m_cond, &m_mutex), + "Error from pthread_cond_wait in castor::server::CondVarSemaphore::acquire()"); + } + m_value--; + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_unlock(&m_mutex), + "Error from pthread_mutex_unlock in castor::server::CondVarSemaphore::acquire()"); +} +//------------------------------------------------------------------------------ +//tryAcquire +//------------------------------------------------------------------------------ +bool castor::server::CondVarSemaphore::tryAcquire() + { + bool ret; + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_lock(&m_mutex), + "Error from pthread_mutex_lock in castor::server::CondVarSemaphore::tryAcquire()"); + if (m_value > 0) { + ret = true; + m_value--; + } else { + ret = false; + } + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_unlock(&m_mutex), + "Error from pthread_mutex_unlock in castor::server::CondVarSemaphore::tryAcquire()"); + return ret; +} +//------------------------------------------------------------------------------ +//release +//------------------------------------------------------------------------------ +void castor::server::CondVarSemaphore::release(int n) + { + for (int i=0; i<n; i++) { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_lock(&m_mutex), + "Error from pthread_mutex_unlock in castor::server::CondVarSemaphore::release()"); + m_value++; + castor::exception::Errnum::throwOnReturnedErrno( + pthread_cond_signal(&m_cond), + "Error from pthread_cond_signal in castor::server::CondVarSemaphore::release()"); + castor::exception::Errnum::throwOnReturnedErrno( + pthread_mutex_unlock(&m_mutex), + "Error from pthread_mutex_unlock in castor::server::CondVarSemaphore::release()"); + } +} diff --git a/tapeserver/castor/server/Semaphores.hpp b/tapeserver/castor/server/Semaphores.hpp index 082d66b8bf..3ad7d5851f 100644 --- a/tapeserver/castor/server/Semaphores.hpp +++ b/tapeserver/castor/server/Semaphores.hpp @@ -25,6 +25,7 @@ #include <pthread.h> #include <semaphore.h> +#include <stdint.h> #include "castor/server/Mutex.hpp" namespace castor { @@ -35,9 +36,11 @@ namespace server { */ class PosixSemaphore { public: + class Timeout{}; PosixSemaphore(int initial = 0) ; ~PosixSemaphore(); void acquire() ; + void acquireWithTimeout(uint64_t timeout_us); /**< Throws an exception (Timeout) in case of timeout */ bool tryAcquire() ; void release(int n=1) ; private: diff --git a/tapeserver/castor/server/Threading.cpp b/tapeserver/castor/server/Threading.cpp new file mode 100644 index 0000000000..9dcc3bb015 --- /dev/null +++ b/tapeserver/castor/server/Threading.cpp @@ -0,0 +1,90 @@ +/****************************************************************************** + * + * This file is part of the Castor project. + * See http://castor.web.cern.ch/castor + * + * Copyright (C) 2003 CERN + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * + * @author Castor Dev team, castor-dev@cern.ch + *****************************************************************************/ + +#include "Threading.hpp" +#include <errno.h> +#include <typeinfo> +#include <stdlib.h> +#include <cxxabi.h> +#include "castor/BaseObject.hpp" + +/* Implmentations of the threading primitives */ +//------------------------------------------------------------------------------ +//start +//------------------------------------------------------------------------------ +void castor::server::Thread::start() + { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_create(&m_thread, NULL, pthread_runner, this), + "Error from pthread_create in castor::server::Thread::start()"); +} +//------------------------------------------------------------------------------ +//wait +//------------------------------------------------------------------------------ +void castor::server::Thread::wait() + { + castor::exception::Errnum::throwOnReturnedErrno( + pthread_join(m_thread, NULL), + "Error from pthread_join in castor::server::Thread::wait()"); + if (m_hadException) { + std::string w = "Uncaught exception of type \""; + w += m_type; + w += "\" in Thread.run():\n>>>>\n"; + w += m_what; + w += "<<<< End of uncaught exception"; + throw UncaughtExceptionInThread(w); + } +} +//------------------------------------------------------------------------------ +//pthread_runner +//------------------------------------------------------------------------------ +void * castor::server::Thread::pthread_runner (void * arg) { + + /* static_casting a pointer to and from void* preserves the address. + * See https://stackoverflow.com/questions/573294/when-to-use-reinterpret-cast + */ + Thread * _this = static_cast<Thread *>(arg); + + // The threading init is needing by many castor components, so better do + // it all the time (this should not have side effects) + try { + _this->run(); + } catch (std::exception & e) { + _this->m_hadException = true; + int status = -1; + char * demangled = abi::__cxa_demangle(typeid(e).name(), NULL, NULL, &status); + if (!status) { + _this->m_type += demangled; + } else { + _this->m_type = typeid(e).name(); + } + free(demangled); + _this->m_what = e.what(); + } catch (...) { + _this->m_hadException = true; + _this->m_type = "unknown"; + _this->m_what = "uncaught non-standard exception"; + } + BaseObject::resetServices(); + return NULL; +} diff --git a/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt index d1f170ef5a..7536f46f99 100644 --- a/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt +++ b/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt @@ -74,12 +74,12 @@ add_library(ctaTapeServerDaemon TpconfigLine.cpp TpconfigLines.cpp) -target_link_libraries(ctaTapeServerDaemon ctamessages ctatapereactor ctacommon ctanameserver ctaremotens protobuf ctascheduler) +target_link_libraries(ctaTapeServerDaemon ctamessages ctatapereactor ctacommon ctanameserver ctaremotens protobuf ctascheduler ctalegacymsg) add_dependencies(ctaTapeServerDaemon ctamessagesprotobuf) add_executable(cta-tapeserverd TapeDaemon.cpp) target_link_libraries(cta-tapeserverd ctaTapeServerDaemon SCSI System Utils - File TapeDrive ctacommon castorclient castorlegacymsg castorserver + File TapeDrive ctacommon castorclient ctatapereactor ${LIBCAP_LIB} ${ZLIB_LIBRARIES} ctamessages zmq) install (TARGETS cta-tapeserverd DESTINATION usr/bin) diff --git a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt index 34f5de9279..d26b42a91a 100644 --- a/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt +++ b/tapeserver/castor/tape/tapeserver/file/CMakeLists.txt @@ -30,7 +30,7 @@ add_library(File DiskFile.cpp Structures.cpp ../exception/XrootCl.cpp) -target_link_libraries (File castorrfio XrdCl cryptopp) +target_link_libraries (File castorrfio XrdCl cryptopp ctaserverutils) add_library(ctatapeserverfileunittests SHARED StructuresTest.cpp -- GitLab