diff --git a/CMakeLists.txt b/CMakeLists.txt index 19faead6aea16314f12fba2b06f331341d6909c4..916c774bf757eb24284d510faf3d130693ad33a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,7 @@ ELSE(DEFINED PackageOnly) add_subdirectory(rdbms) add_subdirectory(scheduler) add_subdirectory(tapeserver) + add_subdirectory(XRootdSSiRmcd) #Generate version information configure_file(${PROJECT_SOURCE_DIR}/version.hpp.in diff --git a/XRootdSSiRmcd/CMakeLists.txt b/XRootdSSiRmcd/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c2524e1fe66a0e702d6e18de86973aaab5b00b17 --- /dev/null +++ b/XRootdSSiRmcd/CMakeLists.txt @@ -0,0 +1,69 @@ +# XRootD SSI/Protocol Buffer Interface Project +# Copyright 2018 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cmake_minimum_required (VERSION 2.6) + +find_package(xrootdclient REQUIRED) +find_package(xrootd REQUIRED) +find_package(Protobuf3 REQUIRED) + +add_subdirectory(protobuf) + +# +# XRootD SSI +# +include_directories(${XROOTD_INCLUDE_DIR} ${XROOTD_INCLUDE_DIR}/private) + +# +# XRootD SSI Protocol Buffer bindings +# +include_directories(${CMAKE_SOURCE_DIR}/) + +include_directories (${CMAKE_SOURCE_DIR}/xrootd-ssi-protobuf-interface/include) + +# +# Compiled protocol buffers +# +include_directories(${CMAKE_BINARY_DIR}/XRootdSSiRmcd ${PROTOBUF3_INCLUDE_DIRS}) + +# +# Test Client +# +add_executable(cta-xsmc RmcdClient.cpp) +target_link_libraries(cta-xsmc XrdSsi-4 XrdSsiLib XrdSsiPbRmcd XrdUtils) +set_property(TARGET cta-xsmc APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH}) +install (TARGETS cta-xsmc DESTINATION /usr/bin) +install (FILES cta-xsmc-mount.1cta DESTINATION /usr/share/man/man1) +install (FILES cta-xsmc-dismount.1cta DESTINATION /usr/share/man/man1) + +# +# XRootD SSI plugin for Test Server +# +add_library(XrdSsiRmcd MODULE + RmcdServiceProvider.cpp + RmcdRequestProc.cpp + rmc_send_scsi_cmd.cpp + serrno.cpp + rmc_smcsubr.cpp +) +target_link_libraries(XrdSsiRmcd XrdSsi-4 XrdSsiLib XrdSsiPbRmcd XrdUtils) +set_property(TARGET XrdSsiRmcd APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH}) +install(TARGETS XrdSsiRmcd DESTINATION usr/${CMAKE_INSTALL_LIBDIR}) + +install (FILES cta-xrmcd.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cta) +install (FILES cta-xrmcd.logrotate DESTINATION /etc/logrotate.d RENAME cta-xrmcd) +install (FILES cta-xrmcd.sysconfig DESTINATION /etc/sysconfig RENAME cta-xrmcd) +install (FILES cta-xrmcd.service DESTINATION /etc/systemd/system) diff --git a/XRootdSSiRmcd/Castor_limits.h b/XRootdSSiRmcd/Castor_limits.h new file mode 100644 index 0000000000000000000000000000000000000000..be7c898a4fd88278265f117e4ece4d2f4715f932 --- /dev/null +++ b/XRootdSSiRmcd/Castor_limits.h @@ -0,0 +1,71 @@ +/* + * Castor_limits.h,v 1.27 2004/02/12 15:38:08 obarring Exp + */ + +/* + * Copyright (C) 1999-2003 by CERN/IT/PDP/DM + * All rights reserved + */ + +/* + * @(#)Castor_limits.h,v 1.27 2004/02/12 15:38:08 CERN IT-PDP/DM Jean-Philippe Baud + */ + +#pragma once + + /* all maximum lengths defined below do not include the trailing null */ + +#define CA_MAXACLENTRIES 300 /* maximum number of ACL entries for a file/dir */ +#define CA_MAXCLASNAMELEN 15 /* maximum length for a fileclass name */ +#define CA_MAXCOMMENTLEN 255 /* maximum length for user comments in metadata */ +#define CA_MAXDENLEN 8 /* maximum length for a alphanumeric density */ +#define CA_MAXDGNLEN 6 /* maximum length for a device group name */ +#define CA_MAXDVNLEN 63 /* maximum length for a device name */ +#define CA_MAXDVTLEN 8 /* maximum length for a device type */ +#define CA_MAXFIDLEN 17 /* maximum length for a fid (DSN) */ +#define CA_MAXFSEQLEN 14 /* maximum length for a fseq string */ +#define CA_MAXGRPNAMELEN 2 /* maximum length for a group name */ +#define CA_MAXGUIDLEN 36 /* maximum length for a guid */ +#define CA_MAXHOSTNAMELEN 63 /* maximum length for a hostname */ +#define CA_MAXSVCCLASSNAMELEN 63 /* maximum length for a svc class name */ +#define CA_MAXLBLTYPLEN 3 /* maximum length for a label type */ +#define CA_MAXLINELEN 1023 /* maximum length for a line in a log */ +#define CA_MAXMANUFLEN 12 /* maximum length for a cartridge manufacturer */ +#define CA_MAXMIGPNAMELEN 15 /* maximum length for a migration policy name */ +#define CA_MAXMIGRNAMELEN 15 /* maximum length for a migrator name */ +#define CA_MAXMLLEN 1 /* maximum length for a cartridge media_letter */ +#define CA_MAXMODELLEN 6 /* maximum length for a cartridge model */ +#define CA_MAXNAMELEN 255 /* maximum length for a pathname component */ +#define CA_MAXNBDRIVES 4 /* maximum number of tape drives per server */ +#define CA_MAXPATHLEN 1023 /* maximum length for a pathname */ +#define CA_MAXPOOLNAMELEN 15 /* maximum length for a pool name */ +#define CA_MAXPROTOLEN 7 /* maximum length for a protocol name */ +#define CA_MAXRBTNAMELEN 17 /* maximum length for a robot name */ +#define CA_MAXRECFMLEN 3 /* maximum length for a record format */ +#define CA_MAXREGEXPLEN 63 /* Maximum length for a regular expression */ +#define CA_MAXCSECNAMELEN 512 /* Maximum length for a Csec authorization id */ +#define CA_MAXCSECPROTOLEN 20 /* Maximum length for a Csec mechanism */ +#define CA_MAXSFNLEN 1103 /* maximum length for a replica */ +#define CA_MAXSHORTHOSTLEN 10 /* maximum length for a hostname without domain */ +#define CA_MAXSNLEN 24 /* maximum length for a cartridge serial nb */ +#define CA_MAXSTGRIDLEN 77 /* maximum length for a stager full request id */ + /* must be >= nb digits in CA_MAXSTGREQID + + CA_MAXHOSTNAMELEN + 8 */ +#define CA_MAXSTGREQID 999999 /* maximum value for a stager request id */ +#define CA_MAXSYMLINKS 5 /* maximum number of symbolic links */ +#define CA_MAXTAGLEN 255 /* maximum length for a volume tag */ +#define CA_MAXTAPELIBLEN 8 /* maximum length for a tape library name */ +#define CA_MAXUNMLEN 8 /* maximum length for a drive name */ +#define CA_MAXUSRNAMELEN 14 /* maximum length for a login name */ +#define CA_MAXVIDLEN 6 /* maximum length for a VID */ +#define CA_MAXVSNLEN 6 /* maximum length for a VSN */ +#define CA_MAXCKSUMNAMELEN 15 /* maximum length for a checksum algorithm name */ +#define CA_MAXCKSUMLEN 32 /* maximum length for a checksum value in an asci form */ +#define CA_MAXDMPROTNAMELEN 15 /* maximum length for Disk Mover protocol name */ +#define CA_MAXJOBIDLEN 36 /* Maximum length for the representation of the Cuuid */ +#define CA_MAXUSERTAGLEN 63 /* Maximum length for a user tag (stage request) */ +#define CA_MAXRFSLINELEN 2047 /* maximum length for the requested filesystem string */ + +/* Max allowed uid/gif */ +#define CA_MAXUID 0x7FFFFFFF /* Maximum uid */ +#define CA_MAXGID 0x7FFFFFFF /* Maximum gid */ diff --git a/XRootdSSiRmcd/RmcdApi.hpp b/XRootdSSiRmcd/RmcdApi.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2d2ca0995a2e76d11dfa429b2444c27ce10493c8 --- /dev/null +++ b/XRootdSSiRmcd/RmcdApi.hpp @@ -0,0 +1,41 @@ +/*! + * @project XRootD SSI/Protocol Buffer Interface Project + * @brief XRootD SSI/Google Protocol Buffer bindings for the rmc_test client/server + * @copyright Copyright 2018 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "XrdSsiPbServiceClientSide.hpp" //!< XRootD SSI/Protocol Buffer Service, client-side bindings +#include "protobuf/rmc_test.pb.h" //!< Auto-generated message types from .proto file + +/*! + * Bind the type of the XrdSsiService to the types defined in the .proto file + */ +typedef XrdSsiPb::ServiceClientSide<rmc_test::Request, //!< XrdSSi Request message type + rmc_test::Response, //!< XrdSsi Metadata message type + rmc_test::Data, //!< XrdSsi Data message type + rmc_test::Alert> //!< XrdSsi Alert message type + XrdSsiPbServiceType; + +/*! + * Bind the type of the XrdSsiRequest to the types defined in the .proto file + */ +typedef XrdSsiPb::Request<rmc_test::Request, //!< XrdSSi Request message type + rmc_test::Response, //!< XrdSsi Metadata message type + rmc_test::Data, //!< XrdSsi Data message type + rmc_test::Alert> //!< XrdSsi Alert message type + XrdSsiPbRequestType; + diff --git a/XRootdSSiRmcd/RmcdClient.cpp b/XRootdSSiRmcd/RmcdClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d54ed881491fe070903ab26621baf5ab4e36d0e4 --- /dev/null +++ b/XRootdSSiRmcd/RmcdClient.cpp @@ -0,0 +1,401 @@ +/*! + * @project XRootD SSI/Protocol Buffer Interface Project + * @brief Command-line rmc_test client for XRootD SSI/Protocol Buffers + * @copyright Copyright 2018 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <sstream> +#include <iostream> +#include <iomanip> +#include <algorithm> + +#include "XrdSsiPbIStreamBuffer.hpp" +#include "RmcdClient.hpp" + +#include <getopt.h> + +bool is_metadata_done = false; + +// note: this implementation does not disable this overload for array types + template<typename T, typename... Args> + std::unique_ptr<T> make_unique(Args&&... args) + { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); + } + + +// Define XRootD SSI callbacks + +namespace XrdSsiPb { + +/* + * Alert callback. + * + * Defines how Alert messages should be logged + */ +template<> +void RequestCallback<rmc_test::Alert>::operator()(const rmc_test::Alert &alert) +{ + Log::Msg(Log::INFO, LOG_SUFFIX, "Alert received:"); + + // Output message in Json format + Log::DumpProtobuf(Log::INFO, &alert); +} + +} // namespace XrdSsiPb + + +// +// RmcdClient implementation +// + +const std::string DEFAULT_ENDPOINT = "localhost:10956"; + +// Change console text colour + +const char* const TEXT_RED = "\x1b[31;1m"; +const char* const TEXT_NORMAL = "\x1b[0m\n"; + + + +// Convert string to bool + +bool to_bool(std::string str) { + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + if(str == "true") return true; + if(str == "false") return false; + + throw std::runtime_error(str + " is not a valid boolean value"); +} + +void RmcdClientCmd::throwUsage(const std::string &error_txt) const +{ + std::stringstream help; + if(error_txt != "") { + help << m_execname << ": " << error_txt << std::endl << std::endl; + } + help << "Usage:\n" + "\n" + "rmcd-client [cmdOptions]\n" + "Where cmdOptions can be one of those:\n" + "\n" + "\tmount -V vid -D drive_ordinal\n" + "\tdismount\n" + "\timport\n" + "\texport\n" + "\tread_elem [-N nbelem] [-S starting_slot] [-v]\n" + "\tfind_cartridge V [-N nbelem] [-V vid] [-v]\n" + "\tget_geom\n" + "\n"; + throw std::runtime_error(help.str()); +} + +void RmcdClientCmd::mountUsage(const std::string &error_txt) const +{ + std::stringstream help; + if(error_txt != "") { + help << m_execname << ": " << error_txt << std::endl << std::endl; + } + + help <<"Usage:\n" + "\n" + " mount [options] -V VID -D DRIVE_SLOT\n" + "\n" + "Where:\n" + "\n" + " VID The VID of the volume to be mounted.\n" + " DRIVE_SLOT The slot in the tape library where the drive is located.\n" + " The format of DRIVE_SLOT is as follows:\n" + "\n" + " ACS:LSM:panel:transport\n" + "\n" + "Options:\n" + " -h|--help Print this help message and exit.\n" + "\n" + " -t|--timeout SECONDS Time to wait for the mount to conclude. SECONDS\n" + " must be an integer value greater than 0. The\n" + " default value of SECONDS is " + "\n"; + throw std::runtime_error(help.str()); +} + +void RmcdClientCmd::dismountUsage(const std::string &error_txt) const +{ + std::stringstream help; + + if(error_txt != "") { + help << m_execname << ": " << error_txt << std::endl << std::endl; + } + + help <<"Usage:\n" + "\n" + " dismount [options] -V VID -D DRIVE_SLOT\n" + "\n" + "Where:\n" + "\n" + " VID The VID of the volume to be dismounted.\n" + " DRIVE_SLOT The slot in the tape library where the drive is located.\n" + " The format of DRIVE_SLOT is as follows:\n" + "\n" + " ACS:LSM:panel:transport\n" + "\n" + "Options:\n" + " -h|--help Print this help message and exit.\n" + "\n" + " -t|--timeout SECONDS Time to wait for the dismount to conclude. SECONDS\n" + " must be an integer value greater than 0. The\n" + " default value of SECONDS is " + "\n"; + throw std::runtime_error(help.str()); +} + +RmcdClientCmd::RmcdClientCmd(int argc, char *const *const argv) : + m_execname(argv[0]), + m_endpoint(DEFAULT_ENDPOINT) +{ + // Strip path from execname + + size_t p = m_execname.find_last_of('/'); + if(p != std::string::npos) m_execname.erase(0, p+1); + + // Parse the command + if(argc<2) { + RmcdClientCmd::throwUsage("Missing command option"); + return; + } + + std::string cmdOptions(argv[1]); + + if(cmdOptions == "help") { + throwUsage("help"); + } + else if(cmdOptions == "mount") { + if(argc==3) { + std::string cmdh(argv[2]); + + if(cmdh == "help") { + mountUsage("helping in mount"); + return; + } + } + processMount(argc, argv); + } else if(cmdOptions == "dismount") { + if(argc==3) { + std::string cmdh(argv[2]); + + if(cmdh == "help") { + dismountUsage("helping in dismount"); + return; + } + } + processDismount(argc, argv); + } else { + throwUsage("Unrecognized command: "); + } + + // Read environment variables + + XrdSsiPb::Config config; + + // If XRDDEBUG=1, switch on all logging + if(getenv("XRDDEBUG")) { + config.set("log", "all"); + } + + // XrdSsiPbLogLevel gives more fine-grained control over log level + config.getEnv("log", "XrdSsiPbLogLevel"); + + // Default response timeout + config.getEnv("request_timeout", "XRD_REQUESTTIMEOUT"); + + // Obtain a Service Provider + std::string resource("/rmc_test"); + m_rmc_test_service_ptr = make_unique<XrdSsiPbServiceType>(m_endpoint, resource, config); +} + +void RmcdClientCmd::processMount(int argc, char *const *const argv) { + int errflg = 0; + int drvord = -1; + int n; + char vid[7]; + char *dp; + char c=0; + static struct option longopts[] = { + { "help", no_argument, NULL, 'h'}, + { "Vid", required_argument, NULL, 'V'}, + { "Drive_original", required_argument, NULL, 'D' }, + { NULL, 0, NULL, '\0' } + }; + memset (vid, '\0', sizeof(vid)); + if (argc<6) { + RmcdClientCmd::mountUsage("less arguments provided"); + return; + } + if(*argv[2]!= '-') { + RmcdClientCmd::mountUsage("Unrecognised command format given\n"); + } + + while ((c = getopt_long(argc, argv, "hV:D:",longopts, NULL))!= -1) { + switch (c) { + case 'h': + RmcdClientCmd::mountUsage("help"); + break; + case 'D': /* drive ordinal */ + { + drvord = strtol (optarg, &dp, 10); + if (*dp != '\0' || drvord < 0) { + errflg++; + } + strcpy (dp, optarg); + printf("drive slot = %s\n",dp); + m_request.mutable_mount()->set_drvord(drvord); + break; + } + case 'V': /* vid */ + { + n = strlen (optarg); + if (n > 6) { + errflg++; + } else { + strcpy (vid, optarg); + printf("vid = %s\n",vid); + m_request.mutable_mount()->set_vid(vid);} + break; + } + case '?': /* Unrecognized option */ + default:{ + RmcdClientCmd::mountUsage("Unrecognised commands\n"); + break; + } + } + } +} + +void RmcdClientCmd::processDismount(int argc, char *const *const argv) { + int errflg = 0; + int drvord = -1; + int n; + char vid[7]; + char *dp; + char c; + static struct option longopts[] = { + { "help", required_argument, NULL, 'h'}, + { "Vid", required_argument, NULL, 'V'}, + { "Drive_original", required_argument, NULL, 'D' }, + { NULL, 0, NULL, '\0' } + }; + memset (vid, '\0', sizeof(vid)); + if (argc<6) { + RmcdClientCmd::dismountUsage("less arguments provided"); + return; + } + if(*argv[2]!= '-') { + RmcdClientCmd::dismountUsage("Unrecognised command format given\n"); + } + while ((c = getopt_long(argc, argv, "hV:D:",longopts, NULL))!= -1) { + switch (c) { + case 'h': + RmcdClientCmd::dismountUsage("help"); + break; + case 'D': { /* drive ordinal */ + drvord = strtol (optarg, &dp, 10); + if (*dp != '\0' || drvord < 0) { + errflg++;} + strcpy (dp, optarg); + printf("drive slot = %s\n",dp); + m_request.mutable_dismount()->set_drvord(drvord); + break; + } + case 'V': { /* vid */ + n = strlen (optarg); + if (n > 6) { + errflg++; + } else { + strcpy (vid, optarg); + printf("vid = %s\n",vid); + m_request.mutable_dismount()->set_vid(vid);} + break; + } + case '?': + default:{ + RmcdClientCmd::dismountUsage("Unrecognised command"); + break; + } + } + } +} + + +void RmcdClientCmd::Send() +{ + // Send the Request to the Service and get a Response + rmc_test::Response response; + m_rmc_test_service_ptr->Send(m_request, response); + // Handle responses + switch(response.type()) + { + using namespace rmc_test; + + case Response::RSP_SUCCESS: std::cout << response.message_txt(); break; + case Response::RSP_ERR_PROTOBUF: throw XrdSsiPb::PbException(response.message_txt()); + case Response::RSP_ERR_USER: + case Response::RSP_ERR_SERVER: throw std::runtime_error(response.message_txt()); + default: throw XrdSsiPb::PbException("Invalid response type."); + } + is_metadata_done = true; +} + + +/*! + * Start here + * + * @param argc[in] The number of command-line arguments + * @param argv[in] The command-line arguments + * + * @retval 0 Success + * @retval 1 The client threw an exception + */ +int main(int argc, char **argv) +{ + try { + // Test uninitialised logging : logging is not available until the rmc_test_service object is + // instantiated, so this message should be silently ignored + XrdSsiPb::Log::Msg(XrdSsiPb::Log::ERROR, "main", "Logging is not initialised"); + + // Parse the command line arguments + RmcdClientCmd cmd(argc, argv); + + // Send the protocol buffer + cmd.Send(); + + // Delete all global objects allocated by libprotobuf + google::protobuf::ShutdownProtobufLibrary(); + + return 0; + } catch (XrdSsiPb::PbException &ex) { + std::cerr << "Error in Google Protocol Buffers: " << ex.what() << std::endl; + } catch (XrdSsiPb::XrdSsiException &ex) { + std::cerr << "Error from XRootD SSI Framework: " << ex.what() << std::endl; + } catch (std::runtime_error &ex) { + std::cerr << ex.what() << std::endl; + } catch (std::exception &ex) { + std::cerr << "Caught exception: " << ex.what() << std::endl; + } catch (...) { + std::cerr << "Caught an unknown exception" << std::endl; + } + + return 1; +} + diff --git a/XRootdSSiRmcd/RmcdClient.hpp b/XRootdSSiRmcd/RmcdClient.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d73d0b7d47208d8bc66250459f0a513afe867da5 --- /dev/null +++ b/XRootdSSiRmcd/RmcdClient.hpp @@ -0,0 +1,51 @@ +/*! + * @project XRootD SSI/Protocol Buffer Interface Project + * @brief Command-line rmc_test client for XRootD SSI/Protocol Buffers + * @copyright Copyright 2018 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "RmcdApi.hpp" + +class RmcdClientCmd +{ +public: + RmcdClientCmd(int argc, char *const *const argv); + + /*! + * Send the protocol buffer across the XRootD SSI transport + */ + void Send(); + /*! + * Throw an exception with usage help + */ + void mountUsage(const std::string &error_txt = "") const; + void dismountUsage(const std::string &error_txt = "") const; + void throwUsage(const std::string &error_txt = "") const; + void processMount(int, char * const *const); + void processDismount(int, char * const *const); + +private: + + /* + * Member variables + */ + std::unique_ptr<XrdSsiPbServiceType> m_rmc_test_service_ptr; //!< Pointer to Service object + std::string m_execname; //!< Executable name of this program + std::string m_endpoint; //!< hostname:port of XRootD server + rmc_test::Request m_request; //!< Protocol Buffer for the command to send to the server +}; + diff --git a/XRootdSSiRmcd/RmcdRequestProc.cpp b/XRootdSSiRmcd/RmcdRequestProc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8ac90eaf44259ea1e790a64a384b752df48150f --- /dev/null +++ b/XRootdSSiRmcd/RmcdRequestProc.cpp @@ -0,0 +1,192 @@ +/*! + * @project XRootD SSI/Protocol Buffer Interface Project + * @brief XRootD SSI Responder class implementation + * @copyright Copyright 2018 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "XrdSsiPbConfig.hpp" +#include <sstream> +#include <XrdSsi/XrdSsiEntity.hh> +#include "XrdSsiPbLog.hpp" +#include "XrdSsiPbException.hpp" +#include "XrdSsiPbRequestProc.hpp" +#include "RmcdServiceProvider.hpp" +#include <iostream> +#include "rmc_smcsubr.h" +#include "rmc_smcsubr2.h" +#include "smc_struct.h" +#include <string> + +/* + * Class to process Request Messages + */ +class RequestMessage +{ +public: + char* device_file = new char(); + RequestMessage(const XrdSsiEntity &client, const RmcdServiceProvider *service) { + using namespace XrdSsiPb; + Log::Msg(Log::DEBUG, LOG_SUFFIX, "RequestMessage() constructor: request received from client ", + client.name, '@', client.host); + } + + /*! + * Process a Notification request or an Admin command request + * + * @param[in] request + * @param[out] response Response protocol buffer + * @param[out] stream Reference to Response stream pointer + */ + void process(const rmc_test::Request &request, rmc_test::Response &response, std::string &data_buffer, XrdSsiStream* &stream) { + + using namespace XrdSsiPb; + using namespace rmc_test; + if (request.has_mount()) { + std::stringstream message; + response.set_message_txt(message.str()); + response.set_type(Response::RSP_SUCCESS); + /* get robot geometry */ + int c; + robot_info m_robot_info; + + const int max_nb_attempts = 3; + int attempt_nb = 1; + for(attempt_nb = 1; attempt_nb <= max_nb_attempts; attempt_nb++) { + std::cout<<"The device file before the get_geometry is "<<device_file<<std::endl; + c = smc_get_geometry (-1, + device_file, ///dev/sgx for example + &m_robot_info); + if (c==0){ + message << "Got geometry of tape library"<< std::endl; + } + std::string driverStr = std::to_string (request.mount().drvord()); + int n = (request.mount().vid()).length(); + // declaring character array + char *ch_array = new char[n+1]; + + // copying the contents of the string to char array + strcpy(ch_array, (request.mount().vid()).c_str()); + + c = smc_mount (-1,-1, device_file, &m_robot_info, + request.mount().drvord(), ch_array, 0); + } + } else if (request.has_dismount()) { + std::stringstream message; + response.set_message_txt(message.str()); + response.set_type(Response::RSP_SUCCESS); + + /* get robot geometry */ + int c; + robot_info m_robot_info; + const int max_nb_attempts = 3; + int attempt_nb = 1; + for(attempt_nb = 1; attempt_nb <= max_nb_attempts; attempt_nb++) { + c = smc_get_geometry (-1, + device_file, &m_robot_info); + if (c==0){ + message << "Got geometry of tape library"<< std::endl; + } + std::string driverStr = std::to_string (request.dismount().drvord()); + int n = (request.dismount().vid()).length(); + + /* declaring character array */ + char *ch_array = new char[n+1]; + + /* copying the contents of the string to char array */ + strcpy(ch_array, (request.dismount().vid()).c_str()); + + c = smc_dismount (-1,-1, device_file, &m_robot_info, + request.dismount().drvord(), ch_array); + } + } + } + +private: + + // Set reply header in metadata + + void set_header(rmc_test::Response &response) { + const char* const TEXT_RED = "\x1b[31;1m"; + const char* const TEXT_NORMAL = "\x1b[0m\n"; + + std::stringstream header; + + header << TEXT_RED << "Count " + << "Int64 Value " + << "Double Value " + << "Bool String Value" << TEXT_NORMAL; + + response.set_message_txt(header.str()); + } + + static constexpr const char* const LOG_SUFFIX = "Pb::RequestMessage"; //!< Identifier for log messages +}; + + + +/* + * Implementation of XRootD SSI subclasses + */ +namespace XrdSsiPb { + +/* + * Convert a framework exception into a Response + */ + +template<> +void ExceptionHandler<rmc_test::Response, PbException>::operator()(rmc_test::Response &response, const PbException &ex) +{ + response.set_type(rmc_test::Response::RSP_ERR_PROTOBUF); + response.set_message_txt(ex.what()); +} + + + +/* + * Process the Notification Request + */ +template <> +void RequestProc<rmc_test::Request, rmc_test::Response, rmc_test::Alert>::ExecuteAction() +{ + try { + // Perform a capability query on the XrdSsiProviderServer object: it must be a RmcdServiceProvider + + RmcdServiceProvider *rmc_test_service_ptr; + + if(!(rmc_test_service_ptr = dynamic_cast<RmcdServiceProvider*>(XrdSsiProviderServer))) + { + throw std::runtime_error("XRootD Service is not the Test Service"); + } + RequestMessage request_msg(*(m_resource.client), rmc_test_service_ptr); + strcpy(request_msg.device_file,((rmc_test_service_ptr->scsi_device).c_str())); //get the filename to mount/dismount from the configuration file + request_msg.process(m_request, m_metadata, m_response_str, m_response_stream_ptr); + } catch(PbException &ex) { + m_metadata.set_type(rmc_test::Response::RSP_ERR_PROTOBUF); + m_metadata.set_message_txt(ex.what()); + } catch(std::exception &ex) { + // Serialize and send a log message + + rmc_test::Alert alert_msg; + alert_msg.set_message_txt("Something bad happened"); + Alert(alert_msg); + + // Send the metadata response + + m_metadata.set_type(rmc_test::Response::RSP_ERR_SERVER); + m_metadata.set_message_txt(ex.what()); + } +} + +} // namespace XrdSsiPb diff --git a/XRootdSSiRmcd/RmcdServiceProvider.cpp b/XRootdSSiRmcd/RmcdServiceProvider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ab8465c36f64a7b3d0a5cd9ef54224483aa4304 --- /dev/null +++ b/XRootdSSiRmcd/RmcdServiceProvider.cpp @@ -0,0 +1,114 @@ +/*! + * @project XRootD SSI/Protocol Buffer Interface Project + * @brief XRootD Service Provider class implementation + * @copyright Copyright 2018 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "XrdSsiPbConfig.hpp" +#include "XrdSsiPbAlert.hpp" +#include "XrdSsiPbService.hpp" +#include <iostream> +#include <string> + +#include "protobuf/rmc_test.pb.h" +#include "RmcdServiceProvider.hpp" + + + +/*! + * Global pointer to the Service Provider object. + * + * This must be defined at library load time (i.e. it is a file-level global static symbol). When the + * shared library is loaded, XRootD initialization fails if the appropriate symbol cannot be found (or + * it is a null pointer). + */ +XrdSsiProvider *XrdSsiProviderServer = new RmcdServiceProvider; + + + +/*! + * Initialise the Service Provider + */ +bool RmcdServiceProvider::Init(XrdSsiLogger *logP, XrdSsiCluster *clsP, const std::string cfgFn, const std::string parms, int argc, char **argv) +{ + using namespace XrdSsiPb; + + Log::Say("Called Init(\"", cfgFn, "\",\"", parms, "\")"); + + // Extract configuration items from config file + Config config(cfgFn, "rmc_test"); + auto test_log = config.getOptionList("log"); + if(!test_log.empty()) { + Log::SetLogLevel(test_log); + } else { + Log::SetLogLevel("info"); + } + + auto v_scsi_devices = config.getOptionList("cta.xrmcd.smcdev"); + scsi_device = v_scsi_devices[0]; + Log::SetLogLevel(scsi_device); + if(scsi_device.empty()) { + Log::SetLogLevel("name of the device_file not specified by the user in the configuration file"); + } + return true; +} + + + +/*! + * Instantiate a Service object + */ + +XrdSsiService* RmcdServiceProvider::GetService(XrdSsiErrInfo &eInfo, const std::string &contact, int oHold) +{ + using namespace XrdSsiPb; + + Log::Msg(Log::INFO, LOG_SUFFIX, "Called GetService(", contact, ',', oHold, ')'); + + XrdSsiService *ptr = new XrdSsiPb::Service<rmc_test::Request, rmc_test::Response, rmc_test::Alert>; + + return ptr; +} + + + +/*! + * Query whether a resource exists on a server. + * + * @param[in] rName The resource name + * @param[in] contact Used by client-initiated queries for a resource at a particular endpoint. + * It is set to NULL for server-initiated queries. + * + * @retval XrdSsiProvider::notPresent The resource does not exist + * @retval XrdSsiProvider::isPresent The resource exists + * @retval XrdSsiProvider::isPending The resource exists but is not immediately available. (Useful + * only in clustered environments where the resource may be + * immediately available on some other node.) + */ + +XrdSsiProvider::rStat RmcdServiceProvider::QueryResource(const char *rName, const char *contact) +{ + using namespace XrdSsiPb; + + // We only have one resource + XrdSsiProvider::rStat resourcePresence = (strcmp(rName, "/rmc_test") == 0) ? + XrdSsiProvider::isPresent : XrdSsiProvider::notPresent; + + Log::Msg(Log::INFO, LOG_SUFFIX, "QueryResource(", rName, "): ", + ((resourcePresence == XrdSsiProvider::isPresent) ? "isPresent" : "notPresent")); + + return resourcePresence; +} + diff --git a/XRootdSSiRmcd/RmcdServiceProvider.hpp b/XRootdSSiRmcd/RmcdServiceProvider.hpp new file mode 100644 index 0000000000000000000000000000000000000000..270df1f26418f70672abd73f04e402c2ac1190aa --- /dev/null +++ b/XRootdSSiRmcd/RmcdServiceProvider.hpp @@ -0,0 +1,76 @@ +/*! + * @project XRootD SSI/Protocol Buffer Interface Project + * @brief XRootD Service Provider class implementation + * @copyright Copyright 2018 CERN + * @license This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <XrdSsi/XrdSsiProvider.hh> +#include "protobuf/rmc_test.pb.h" +#include "XrdSsiPbConfig.hpp" + + + +/*! + * Global pointer to the Service Provider object. + */ +extern XrdSsiProvider *XrdSsiProviderServer; + + + +/*! + * Instantiates a Service to process client requests. + */ +class RmcdServiceProvider : public XrdSsiProvider +{ +public: + RmcdServiceProvider() { + // No logging here as we don't set the log level until Init() is called + } + + virtual ~RmcdServiceProvider() { + using namespace XrdSsiPb; + + Log::Msg(Log::DEBUG, LOG_SUFFIX, "Called RmcdServiceProvider() destructor"); + } + + /*! + * Initialize the object. + * + * This is always called before any other method. + */ + bool Init(XrdSsiLogger *logP, XrdSsiCluster *clsP, const std::string cfgFn, + const std::string parms, int argc, char **argv) override; + + /*! + * Called exactly once after initialisation to obtain an instance of an XrdSsiService object + */ + XrdSsiService *GetService(XrdSsiErrInfo &eInfo, const std::string &contact, int oHold=256) override; + + /*! + * Determine resource availability. Can be called any time the client asks for the resource status. + */ + XrdSsiProvider::rStat QueryResource(const char *rName, const char *contact=0) override; + + /*! + * Defines the device file to mount or dismount the volume + */ + std::string scsi_device; + +private: + static constexpr const char* const LOG_SUFFIX = "TestStream"; //!< Identifier for log messages +}; + diff --git a/XRootdSSiRmcd/cta-xrmcd.conf b/XRootdSSiRmcd/cta-xrmcd.conf new file mode 100644 index 0000000000000000000000000000000000000000..14cdf95904280da1842f3a01e5d02e8c2179b0a0 --- /dev/null +++ b/XRootdSSiRmcd/cta-xrmcd.conf @@ -0,0 +1,41 @@ +# +# XRootD SSI/Protocol Buffer Interface Project +# Copyright 2018 CERN +# + +# Set the log level for the XrdSsiPb messages. +# This is a space-separated list of one or more of the following options: +# +# none error warning info debug protobuf protoraw all +rmc_test.log info protobuf + +# Set the debug level to off/debug/all for no debug/debug/verbose debug +ssi.trace debug + +# Tell XRootD to use only the SSI framework. If you wish to also use the filesystem features of +# XRootD then add the keyword "default", or specify a second plugin. +xrootd.fslib libXrdSsi.so + +# Turn off async processing as this does not work with SSI +xrootd.async off + +# Specify the location of the shared library implementing the SSI service +#ssi.svclib /home/akaracha/CTA_build/XRootdSSiRmcd/libXrdSsiRmcd.so +ssi.svclib libXrdSsiRmcd.so + +# Specify the resource lookup function to be used +#oss.statlib -2 libXrdSsi.so + +# Specify the endpoint +#xrd.allow host localhost +xrd.port 10956 + +# Declare the valid prefix for resource names. You can have as many of these directives as you wish, +# each specifying a different prefix. Use a question mark ? to split the resource into a name and a +# CGI string. + +# Export the /test resource for SSI +all.export /rmc_test nolock r/w + +# User has to specify the name of the device file +cta.xrmcd.smcdev /dev/sg9 diff --git a/XRootdSSiRmcd/cta-xrmcd.logrotate b/XRootdSSiRmcd/cta-xrmcd.logrotate new file mode 100644 index 0000000000000000000000000000000000000000..7625211eac53b7920ad775e046301b75f1e340a7 --- /dev/null +++ b/XRootdSSiRmcd/cta-xrmcd.logrotate @@ -0,0 +1,11 @@ + +/var/log/cta/cta-xrmcd*.log { + compress + daily + missingok + rotate 500 + delaycompress + postrotate + /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true + endscript +} diff --git a/XRootdSSiRmcd/cta-xrmcd.service b/XRootdSSiRmcd/cta-xrmcd.service new file mode 100644 index 0000000000000000000000000000000000000000..50d6d8c850fe66f0c5f45c15f978c832837e0f26 --- /dev/null +++ b/XRootdSSiRmcd/cta-xrmcd.service @@ -0,0 +1,16 @@ + +[Unit] + +Description=CTA rmcd plugin for the XRoot deamon +After=network-online.target + +[Service] +ExecStart=/usr/bin/xrootd -c /etc/cta/cta-xrmcd.conf -k fifo -n cta +User=cta +Type=simple +Restart=no +LimitCORE=infinity +LimitNOFILE=65536 + +[Install] +WantedBy=default.target diff --git a/XRootdSSiRmcd/cta-xrmcd.sysconfig b/XRootdSSiRmcd/cta-xrmcd.sysconfig new file mode 100644 index 0000000000000000000000000000000000000000..01e494ed42bae981fb3c3fb5b7d433ad00fe908e --- /dev/null +++ b/XRootdSSiRmcd/cta-xrmcd.sysconfig @@ -0,0 +1,5 @@ + +# Arguments for the xrmcd server daemon +# -smc_ldr is the picker device as defined in /dev. +# -f keep process in the foreground, do not fork. +#CTA_XRMCD_OPTIONS="smc_ldr" diff --git a/XRootdSSiRmcd/cta-xsmc-dismount.1cta b/XRootdSSiRmcd/cta-xsmc-dismount.1cta new file mode 100644 index 0000000000000000000000000000000000000000..81c426f6e317960c61c85d8b5fae5558dd8b9d19 --- /dev/null +++ b/XRootdSSiRmcd/cta-xsmc-dismount.1cta @@ -0,0 +1,47 @@ +.\" The CERN Tape Archive (CTA) project +.\" Copyright (C) 2015 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 3 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program. If not, see <http://www.gnu.org/licenses/>. +.TH CTA-XSMC DISMOUNT 1CTA "October 2018" CTA CTA +.SH NAME +cta-xsmc dismount \- dismount a volume +.SH SYNOPSIS +.BI "cta-xsmc dismount [options] -V VID -D DRIVE_SLOT" + +.SH DESCRIPTION +\fBWarning\fP, \fBcta-xsmc dismount\fP is a command line tool for dismounting tapes in a library through XRootD SSI/Protocol Buffer Interface Project + +\fBcta-xsmc dismount\fP dismounts the volume with the specified +\fBVID\fP from the drive located in the specified \fBDRIVE_SLOT\fP within the +tape library. \fBDRIVE_SLOT\fP must be in one of the following two forms: +.IP +.B acsACS_NUMBER,LSM_NUMBER,PANEL_NUMBER,TRANSPORT_NUMBER +.IP +.B smcDRIVE_ORDINAL + +.SH OPTIONS +.TP +\fB\-d, \-\-debug +Turns on the printing of debug information. +.TP +\fB\-h, \-\-help +Prints the usage message. +.TP +\fB\-f, \-\-force +Force the dismount (rewind and eject the tape where necessary). + +.SH "RETURN CODES" +.TP +\fB 0 +Ok. diff --git a/XRootdSSiRmcd/cta-xsmc-mount.1cta b/XRootdSSiRmcd/cta-xsmc-mount.1cta new file mode 100644 index 0000000000000000000000000000000000000000..6ca8875d8ca18b777312a59c2159f5525109821b --- /dev/null +++ b/XRootdSSiRmcd/cta-xsmc-mount.1cta @@ -0,0 +1,48 @@ +.\" The CERN Tape Archive (CTA) project +.\" Copyright (C) 2015 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 3 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program. If not, see <http://www.gnu.org/licenses/>. +.TH CTA-XSMC MOUNT 1CTA "October 2018" CTA CTA +.SH NAME +cta-xsmc mount \- mount a volume +.SH SYNOPSIS +.BI "cta-xsmc mount [options] -V VID -D DRIVE_SLOT" + +.SH DESCRIPTION +\fBWarning\fP, \fBcta-xsmc mount\fP is a CTA command line tool to mount tapes in a tape library through XRootD SSI/Protocol Buffer Interface Project + +\fBcta-xsmc mount\fP mounts the volume with the specified +\fBVID\fP into the drive located in the specified \fBDRIVE_SLOT\fP within the +tape library. \fBDRIVE_SLOT\fP must be in one of the following two forms: +.IP +.B acsACS_NUMBER,LSM_NUMBER,PANEL_NUMBER,TRANSPORT_NUMBER +.IP +.B smcDRIVE_ORDINAL + +.SH OPTIONS +.TP +\fB\-d, \-\-debug +Turns on the printing of debug information. +.TP +\fB\-h, \-\-help +Prints the usage message. +.TP +\fB\-r, \-\-readonly +Request the volume is mounted for read-only access. + +.SH "RETURN CODES" +.TP +\fB 0 +Ok. +.TP diff --git a/XRootdSSiRmcd/protobuf/CMakeLists.txt b/XRootdSSiRmcd/protobuf/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ef46264164068afe6f2105719a254588ea102f84 --- /dev/null +++ b/XRootdSSiRmcd/protobuf/CMakeLists.txt @@ -0,0 +1,38 @@ +# XRootD SSI/Protocol Buffer Interface Project +# Copyright 2018 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +cmake_minimum_required (VERSION 2.6) + +find_package(Protobuf3 REQUIRED) + +file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto") +PROTOBUF3_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles}) +foreach(PROTO_SRC ${ProtoSources}) + set_property(SOURCE ${PROTO_SRC} PROPERTY COMPILE_FLAGS " -Wno-missing-field-initializers -fPIC -Wno-narrowing -Wno-implicit-fallthrough") + + # Add -Wno-narrowing -Wno-implicit-fallthrough compiler flags if using gcc + # version 7 or greater + if (CMAKE_COMPILER_IS_GNUCC) + if (GCC_VERSION VERSION_EQUAL 7 OR GCC_VERSION VERSION_GREATER 7) + set_property(SOURCE ${PROTO_SRC} APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-narrowing -Wno-implicit-fallthrough") + endif (GCC_VERSION VERSION_EQUAL 7 OR GCC_VERSION VERSION_GREATER 7) + endif (CMAKE_COMPILER_IS_GNUCC) +endforeach(PROTO_SRC) +set (TEST_PROTO_SRC_FILES ${ProtoSources}) + +include_directories(${PROTOBUF3_INCLUDE_DIRS}) +add_library(XrdSsiPbRmcd ${TEST_PROTO_SRC_FILES}) +target_link_libraries(XrdSsiPbRmcd ${PROTOBUF3_LIBRARIES}) + diff --git a/XRootdSSiRmcd/protobuf/rmc_test.proto b/XRootdSSiRmcd/protobuf/rmc_test.proto new file mode 100644 index 0000000000000000000000000000000000000000..160ed19bff436f84332eb446ab02d3fe20f3ed8d --- /dev/null +++ b/XRootdSSiRmcd/protobuf/rmc_test.proto @@ -0,0 +1,120 @@ +// @project XRootD SSI/Protocol Buffer Interface Project +// @brief Protocol buffer definitions for test client/server +// @copyright Copyright 2018 CERN +// @license This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +syntax = "proto3"; +package rmc_test; + + +message MountOptions { + string vid = 4; // char * template + uint32 drvord = 6; +} + +message DismountOptions { + string vid = 4; // char * template + uint32 drvord = 5; // maybe given as a string +} + + +message FindcartOptions { + uint32 uid = 1; + uint32 gid = 2; + string smc_ldr = 3; // char * + string template = 4; // char * + int32 type = 5; + int64 startaddr = 6; + int64 nbelem = 7; +} + +message GetGeomOptions { + uint32 uid = 1; + uint32 gid = 2; + string smc_ldr = 3; // char * +} + +message ImportExportOptions { + uint32 uid = 1; + uint32 gid = 2; + string smc_ldr = 3; // char * + string vid = 4; // char * template +} + +message ReadelemOptions { + int32 uid = 1; + uint32 gid = 2; + string smc_ldr = 3; // char * + int32 type = 4; + int64 startaddr = 5; + int64 nbelem = 6; +} + + +// +// Requests sent to the Test Server +// +message Record { +} + + +// +// Requests sent to the Test Server +// +message Request { + oneof cmdOptions { + MountOptions mount = 1; + DismountOptions dismount = 2; + FindcartOptions findcart = 3; + GetGeomOptions getGeometry = 4; + ImportExportOptions export = 5; + ImportExportOptions import = 6; + ReadelemOptions readelem = 7; + } +} + + +// +// Metadata responses sent by the Test Server +// +message Response { + enum ResponseType { + RSP_INVALID = 0; //< Response type was not set + RSP_SUCCESS = 1; //< Request is valid and was accepted for processing + RSP_ERR_PROTOBUF = 2; //< Framework error caused by Google Protocol Buffers layer + RSP_ERR_SERVER = 3; //< Server error + RSP_ERR_USER = 4; //< User request is invalid + } + ResponseType type = 1; //< Encode the type of this response + string message_txt = 2; //< Optional response message text +} + + + +// +// Stream/Data responses sent by the Test Server +// +message Data { + Response record = 1; //< Response record +} + + + +// +// Alert Messages +// +message Alert { + string message_txt = 1; //< Text of the alert message +} + diff --git a/XRootdSSiRmcd/rbtsubr_constants.h b/XRootdSSiRmcd/rbtsubr_constants.h new file mode 100644 index 0000000000000000000000000000000000000000..9cce18c281994db51c59bf83f7a65df4c97f02b2 --- /dev/null +++ b/XRootdSSiRmcd/rbtsubr_constants.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * + * 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 + + /* rbtsubr return codes */ + +#define RBT_OK 0 /* Ok or error should be ignored */ +#define RBT_NORETRY 1 /* Unrecoverable error (just log it) */ +#define RBT_SLOW_RETRY 2 /* Should release drive & retry in 600 seconds */ +#define RBT_FAST_RETRY 3 /* Should retry in 60 seconds */ +#define RBT_DMNT_FORCE 4 /* Should do first a demount force */ +#define RBT_CONF_DRV_DN 5 /* Should configure the drive down */ +#define RBT_OMSG_NORTRY 6 /* Should send a msg to operator and exit */ +#define RBT_OMSG_SLOW_R 7 /* Ops msg (nowait) + release drive + slow retry */ +#define RBT_OMSGR 8 /* Should send a msg to operator and wait */ +#define RBT_UNLD_DMNT 9 /* Should unload the tape and retry demount */ + diff --git a/XRootdSSiRmcd/rmc_constants.h b/XRootdSSiRmcd/rmc_constants.h new file mode 100644 index 0000000000000000000000000000000000000000..684b45dddaf2ecb599dcf8216add6fb85a43d9c1 --- /dev/null +++ b/XRootdSSiRmcd/rmc_constants.h @@ -0,0 +1,66 @@ +/* + * $Id: rmc_constants.h,v 1.1 2002/12/01 07:31:57 baud Exp $ + */ + +/* + * Copyright (C) 2001 by CERN/IT/PDP/DM + * All rights reserved + */ + +/* + */ + +#pragma once +#include "Castor_limits.h" + +#define RMC_CHECKI 5 /* max interval to check for work to be done */ +#define RMC_PRTBUFSZ 180 +#define RMC_REPBUFSZ 524288 /* must be >= max media changer server reply size */ +#define RMC_REQBUFSZ 256 /* must be >= max media changer server request size */ +#define RMC_MAGIC 0x120D0301 +#define RMC_TIMEOUT 5 /* netread timeout while receiving a request */ +#define RMC_RETRYI 60 +#define RMC_LOGBUFSZ 1024 + +#define RMC_PORT 5014 + +#define RMC_MAXRQSTATTEMPTS 10 /* Maximum number of attempts a retriable RMC request should be issued */ + + /* SCSI media changer utilities exit codes */ + +#define USERR 1 /* user error */ +#define SYERR 2 /* system error */ +#define CONFERR 4 /* configuration error */ + + /* Request types */ + +#define RMC_GETGEOM 1 /* Get robot geometry */ +#define RMC_FINDCART 2 /* Find cartridge(s) */ +#define RMC_READELEM 3 /* Read element status */ +#define RMC_MOUNT 4 /* Mount request */ +#define RMC_UNMOUNT 5 /* Unmount request */ +#define RMC_EXPORT 6 /* Export tape request */ +#define RMC_IMPORT 7 /* Import tape request */ +#define RMC_GENERICMOUNT 8 /* Generic (SCSI or ACS) mount request */ +#define RMC_GENERICUNMOUNT 9 /* Generic (SCSI or ACS) mount request */ + + /* SCSI media changer server reply types */ + +#define MSG_ERR 1 +#define MSG_DATA 2 +#define RMC_RC 3 + + /* SCSI media changer server messages */ + +#define RMC00 "RMC00 - SCSI media changer server not available on %s\n" +#define RMC01 "RMC01 - robot parameter is mandatory\n" +#define RMC02 "RMC02 - %s error : %s\n" +#define RMC03 "RMC03 - illegal function %d\n" +#define RMC04 "RMC04 - error getting request, netread = %d\n" +#define RMC05 "RMC05 - cannot allocate enough memory\n" +#define RMC06 "RMC06 - invalid value for %s\n" +#define RMC09 "RMC09 - fatal configuration error: %s %s\n" +#define RMC46 "RMC46 - request too large (max. %d)\n" +#define RMC92 "RMC92 - %s request by %d,%d from %s\n" +#define RMC98 "RMC98 - %s\n" + diff --git a/XRootdSSiRmcd/rmc_send_scsi_cmd.cpp b/XRootdSSiRmcd/rmc_send_scsi_cmd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1003b3f438f8486a1c1c78bc1c5a446619def5e1 --- /dev/null +++ b/XRootdSSiRmcd/rmc_send_scsi_cmd.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (C) 1996-2000 by CERN/IT/PDP/DM + * All rights reserved + */ + +/* rmc_send_scsi_cmd - Send a SCSI command to a device */ +/* return -5 if not supported on this platform (serrno = SEOPNOTSUP) + * -4 if SCSI error (serrno = EIO) + * -3 if CAM error (serrno = EIO) + * -2 if ioctl fails with errno (serrno = errno) + * -1 if open/stat fails with errno (message fully formatted) + * 0 if successful with no data transfer + * >0 number of bytes transferred + */ + +#include <iostream> +#include <unistd.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/stat.h> +#include <linux/version.h> +#include <sys/param.h> +/* Impossible unless very very old kernels: */ +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif +#include "/usr/include/scsi/sg.h" +#include <sys/stat.h> +#include "scsictl.h" +#include "serrno.h" +#include "rmc_send_scsi_cmd.h" +static char rmc_err_msgbuf[132]; +static const char *sk_msg[] = { + "No sense", + "Recovered error", + "Not ready", + "Medium error", + "Hardware error", + "Illegal request", + "Unit attention", + "Data protect", + "Blank check", + "Vendor unique", + "Copy aborted", + "Aborted command", + "Equal", + "Volume overflow", + "Miscompare", + "Reserved", +}; + +static void find_sgpath(char *const sgpath, const int maj, const int min) { + + /* + Find the sg device for a pair of major and minor device IDs + of a tape device. The match is done by + + . identifying the tape's st device node + . getting the device's unique ID from sysfs + . searching the sg device with the same ID (in sysfs) + + If no match is found, the returned sg path will be an empty + string. + */ + + char systape[] = "/sys/class/scsi_tape"; + char sysgen[] = "/sys/class/scsi_generic"; + char syspath[256]; + + char tlink[256]; + char glink[256]; + + int match = 0; + DIR *dir_tape, *dir_gen; + struct dirent *dirent; + char st_dev[64]; + + struct stat sbuf; + + sgpath[0] = '\0'; + + /* find the st sysfs entry */ + if (!(dir_tape = opendir(systape))) return; + while ((dirent = readdir(dir_tape))) { + + if (0 == strcmp(".", dirent->d_name)) continue; + if (0 == strcmp("..", dirent->d_name)) continue; + + sprintf(st_dev, "/dev/%s", dirent->d_name); + stat(st_dev, &sbuf); + if (maj == (int)major(sbuf.st_rdev) && min == (int)minor(sbuf.st_rdev)) { + sprintf(syspath, "%s/%s/device", systape, dirent->d_name); + match = 1; + break; + } + } + closedir(dir_tape); + + if (0 == match) return; + + memset(tlink, 0, 256); + readlink(syspath, tlink, 256); + + /* find the corresponding sg sysfs entry */ + if (!(dir_gen = opendir(sysgen))) return; + while ((dirent = readdir(dir_gen))) { + + if (0 == strcmp(".", dirent->d_name)) continue; + if (0 == strcmp("..", dirent->d_name)) continue; + + sprintf(syspath, "%s/%s/device", sysgen, dirent->d_name); + + memset(glink, 0, 256); + readlink(syspath, glink, 256); + + if (0 == strcmp(glink, tlink)) { + sprintf(sgpath, "/dev/%s", dirent->d_name); + goto out; + } + } + out: + closedir(dir_gen); + return; +} + + +int rmc_send_scsi_cmd ( + const int tapefd, + const char *const path, + const int do_not_open, + const unsigned char *const cdb, + const int cdblen, + unsigned char *const buffer, + const int buflen, + char *const sense, + const int senselen, + const int flags, + int *const nb_sense_ret, + const char **const msgaddr) +{ + /* The timeout used when sending SCSI commands through the sg driver is in */ + /* milliseconds and should equal that used by the st driver which on the */ + /* 28/01/2014 is 900 seconds and therefore 900000 milliseconds */ + const int timeout = 900000; /* milliseconds */ + int fd; + FILE *fopen(); + int n; + int resid = 0; + struct stat sbuf; + struct stat sbufa; + static char *sg_buffer; + static int sg_bufsiz = 0; + struct sg_header *sg_hd; + char sgpath[80]; + int timeout_in_jiffies = 0; + int sg_big_buff_val = SG_BIG_BUFF; + int procfd, nbread; + char procbuf[80]; + + (void)senselen; + /* First the value in /proc of the max buffer size for the sg driver */ + procfd = open("/proc/scsi/sg/def_reserved_size", O_RDONLY); + if (procfd >= 0) { + memset(procbuf, 0, sizeof(procbuf)); + nbread = read(procfd, procbuf, sizeof(procbuf) - 1); + if (nbread > 0) { + long int tmp; + char *endptr = NULL; + tmp = strtol(procbuf, &endptr, 10); + if (endptr == NULL || *endptr == '\n') { + sg_big_buff_val = (int) tmp; + } + } + close(procfd); + } + + if ((int)sizeof(struct sg_header) + cdblen + buflen > sg_big_buff_val) { + sprintf (rmc_err_msgbuf, "blocksize too large (max %zd)\n", + sg_big_buff_val - sizeof(struct sg_header) - cdblen); + *msgaddr = rmc_err_msgbuf; + serrno = EINVAL; + return (-1); + } + if ((int)sizeof(struct sg_header)+cdblen+buflen > sg_bufsiz) { + if (sg_bufsiz > 0) free (sg_buffer); + if ((sg_buffer = (char *)malloc (sizeof(struct sg_header)+cdblen+buflen)) == NULL) { + serrno = errno; + sprintf (rmc_err_msgbuf, "cannot get memory"); + *msgaddr = rmc_err_msgbuf; + return (-1); + } + sg_bufsiz = sizeof(struct sg_header) + cdblen + buflen; + } + if (do_not_open) { + fd = tapefd; + strcpy (sgpath, path); + } else { + if (stat (path, &sbuf) < 0) { + serrno = errno; + snprintf (rmc_err_msgbuf, sizeof(rmc_err_msgbuf), "%s : stat error : %s\n", path, strerror(errno)); + rmc_err_msgbuf[sizeof(rmc_err_msgbuf) - 1] = '\0'; + *msgaddr = rmc_err_msgbuf; + return (-1); + } + + /* get the major device ID of the sg devices ... */ + if (stat ("/dev/sg0", &sbufa) < 0) { + serrno = errno; + snprintf (rmc_err_msgbuf, sizeof(rmc_err_msgbuf), "/dev/sg0 : stat error : %s\n", strerror(errno)); + rmc_err_msgbuf[sizeof(rmc_err_msgbuf) - 1] = '\0'; + *msgaddr = rmc_err_msgbuf; + return (-1); + } + /* ... to detect links and use the path directly! */ + if (major(sbuf.st_rdev) == major(sbufa.st_rdev)) { + strcpy (sgpath, path); + } else { + find_sgpath(sgpath, major(sbuf.st_rdev), minor(sbuf.st_rdev)); + } + + if ((fd = open (sgpath, O_RDWR)) < 0) { + serrno = errno; + snprintf (rmc_err_msgbuf, sizeof(rmc_err_msgbuf), "%s : open error : %s\n", sgpath, strerror(errno)); + rmc_err_msgbuf[sizeof(rmc_err_msgbuf) - 1] = '\0'; + *msgaddr = rmc_err_msgbuf; + return (-1); + } + } + + /* set the sg timeout (in jiffies) */ + timeout_in_jiffies = timeout * HZ / 1000; + ioctl (fd, SG_SET_TIMEOUT, &timeout_in_jiffies); + + memset (sg_buffer, 0, sizeof(struct sg_header)); + sg_hd = (struct sg_header *) sg_buffer; + sg_hd->reply_len = sizeof(struct sg_header) + ((flags & SCSI_IN) ? buflen : 0); + sg_hd->twelve_byte = cdblen == 12; + memcpy (sg_buffer+sizeof(struct sg_header), cdb, cdblen); + n = sizeof(struct sg_header) + cdblen; + if (buflen && (flags & SCSI_OUT)) { + memcpy (sg_buffer+n, buffer, buflen); + n+= buflen; + } + if (write (fd, sg_buffer, n) < 0) { + *msgaddr = (char *) strerror(errno); + serrno = errno; + snprintf (rmc_err_msgbuf, sizeof(rmc_err_msgbuf), "%s : write error : %s\n", sgpath, *msgaddr); + rmc_err_msgbuf[sizeof(rmc_err_msgbuf) - 1] = '\0'; + *msgaddr = rmc_err_msgbuf; + if (! do_not_open) close (fd); + return (-2); + } + if ((n = read (fd, sg_buffer, sizeof(struct sg_header) + + ((flags & SCSI_IN) ? buflen : 0))) < 0) { + *msgaddr = (char *) strerror(errno); + serrno = errno; + snprintf (rmc_err_msgbuf, sizeof(rmc_err_msgbuf), "%s : read error : %s\n", sgpath, *msgaddr); + rmc_err_msgbuf[sizeof(rmc_err_msgbuf) - 1] = '\0'; + *msgaddr = rmc_err_msgbuf; + if (! do_not_open) close (fd); + return (-2); + } + if (! do_not_open) close (fd); + if (sg_hd->sense_buffer[0]) { + memcpy (sense, sg_hd->sense_buffer, sizeof(sg_hd->sense_buffer)); + *nb_sense_ret = sizeof(sg_hd->sense_buffer); + } + if (sg_hd->sense_buffer[0] & 0x80) { /* valid */ + resid = sg_hd->sense_buffer[3] << 24 | sg_hd->sense_buffer[4] << 16 | + sg_hd->sense_buffer[5] << 8 | sg_hd->sense_buffer[6]; + } + if ((sg_hd->sense_buffer[0] & 0x70) && + ((sg_hd->sense_buffer[2] & 0xE0) == 0 || + (sg_hd->sense_buffer[2] & 0xF) != 0)) { + char tmp_msgbuf[132]; + snprintf (tmp_msgbuf, sizeof(tmp_msgbuf), "%s ASC=%X ASCQ=%X", + sk_msg[*(sense+2) & 0xF], *(sense+12), *(sense+13)); + tmp_msgbuf[sizeof(tmp_msgbuf) - 1] = '\0'; + serrno = EIO; + snprintf (rmc_err_msgbuf, sizeof(rmc_err_msgbuf), "%s : scsi error : %s\n", sgpath, tmp_msgbuf); + rmc_err_msgbuf[sizeof(rmc_err_msgbuf) - 1] = '\0'; + *msgaddr = rmc_err_msgbuf; + return (-4); + } else if (sg_hd->result) { + *msgaddr = (char *) strerror(sg_hd->result); + serrno = sg_hd->result; + snprintf (rmc_err_msgbuf, sizeof(rmc_err_msgbuf), "%s : read error : %s\n", sgpath, *msgaddr); + rmc_err_msgbuf[sizeof(rmc_err_msgbuf) - 1] = '\0'; + *msgaddr = rmc_err_msgbuf; + return (-2); + } + if (n) + n -= sizeof(struct sg_header) + resid; + if (n && (flags & SCSI_IN)) + memcpy (buffer, sg_buffer+sizeof(struct sg_header), n); + return ((flags & SCSI_IN) ? n : buflen - resid); +} diff --git a/XRootdSSiRmcd/rmc_send_scsi_cmd.h b/XRootdSSiRmcd/rmc_send_scsi_cmd.h new file mode 100644 index 0000000000000000000000000000000000000000..0ea32e0692f6d8857446d6bf5f1b763ca8ac568d --- /dev/null +++ b/XRootdSSiRmcd/rmc_send_scsi_cmd.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * + * 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 + + int rmc_send_scsi_cmd ( + const int tapefd, + const char *const path, + const int do_not_open, + const unsigned char *const cdb, + const int cdblen, + unsigned char *const buffer, + const int buflen, + char *const sense, + const int senselen, + const int flags, + int *const nb_sense_ret, + const char **const msgaddr); + diff --git a/XRootdSSiRmcd/rmc_sendrep.h b/XRootdSSiRmcd/rmc_sendrep.h new file mode 100644 index 0000000000000000000000000000000000000000..04af53cf91044dfa0ec2519c150fc5cf5a7e04a1 --- /dev/null +++ b/XRootdSSiRmcd/rmc_sendrep.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 1998-2002 by CERN/IT/PDP/DM + * All rights reserved + */ + +#pragma once + +//#include "osdep.h" + +//EXTERN_C int rmc_sendrep(const int rpfd, const int rep_type, ...); +int rmc_sendrep(const int rpfd, const int rep_type, ...); + diff --git a/XRootdSSiRmcd/rmc_smcsubr.cpp b/XRootdSSiRmcd/rmc_smcsubr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..512e44585bce2a870ef724ffe64e7418ca460c8e --- /dev/null +++ b/XRootdSSiRmcd/rmc_smcsubr.cpp @@ -0,0 +1,848 @@ +/* + * Copyright (C) 1998-2003 by CERN/IT/PDP/DM + * All rights reserved + */ + +#include <sstream> +#include <iostream> +#include <iomanip> +#include <algorithm> + +#include <iostream> +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> + +#include "rbtsubr_constants.h" +#include "rmc_constants.h" +#include "rmc_send_scsi_cmd.h" +#include "rmc_smcsubr.h" +#include "rmc_smcsubr2.h" +#include "scsictl.h" +#include "serrno.h" +#include "smc_constants.h" + +#define RBT_XTRA_PROC 10 + + +int rmc_logit(const char *const func, const char *const msg, ...) +{ + return 0; +} + +char *getconfent_fromfile(const char *filename, + const char *category, + const char *name, + int flags) +{ + throw std::runtime_error("Not impemented"); +} + +static struct smc_status smc_status; +static const char *smc_msgaddr; + +static void save_error( + const int rc, + const int nb_sense, + const char *const sense, + const char *const msgaddr) +{ + smc_msgaddr = msgaddr; + smc_status.rc = rc; + smc_status.skvalid = 0; + smc_status.save_errno = serrno; + if (rc == -4 && nb_sense >= 14) { + smc_status.asc = sense[12]; + smc_status.ascq = sense[13]; + smc_status.sensekey = sense[2] &0xF; + smc_status.skvalid = 1; + } else { + smc_status.asc = 0; + smc_status.ascq = 0; + smc_status.sensekey = 0; + } +} + +static int vmatch (const char *const pattern, const char *const vid) +{ + const char *p; + const char *v; + + for (p = pattern, v = vid; *p; p++, v++) { + if (*v == 0 && *p != '*') + return (1); + switch (*p) { + case '?': /* match any single character */ + continue; + case '*': + if (*(++p) == 0) + return (0); /* trailing * matches the rest */ + while (vmatch (p, v)) { + if (*(++v) == 0) + return (1); + } + return (0); + default: + if (*p != *v) + return (1); + } + } + return (*v != 0); +} + +static int get_element_size( + const int fd, + const char *const rbtdev, + const int type) +{ + unsigned char buf[128]; + unsigned char cdb[12]; + const char *msgaddr; + int nb_sense_ret; + int rc; + char sense[MAXSENSE]; + int voltag = 0x10; + int pause_mode = 1; + int nretries = 0; + + memset (cdb, 0, sizeof(cdb)); + cdb[0] = 0xB8; /* read element status */ + cdb[1] = voltag + type; /* we request volume tag info and this type */ + cdb[5] = 1; /* we only need one element */ + cdb[9] = 128; /* limit for the report */ + + /* IBM library in pause mode */ + while (pause_mode && nretries <= 900) { + rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, buf, 128, + sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr); + if (rc < 0) { + if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04) && (sense[13] == -0X7B )) { + sleep(60); + pause_mode = 1; + } else { + pause_mode = 0; + } + } else { + pause_mode = 0; + } + nretries++; + } + + if (rc < 0) { + save_error (rc, nb_sense_ret, sense, msgaddr); + return (-1); + } + return (buf[10] * 256 + buf[11]); +} + +static int get_element_info( + const char opcode, + const int fd, + const char *const rbtdev, + const int type, + const int start, + const int nbelem, + struct smc_element_info element_info[]) +{ + int avail_elem; + unsigned char cdb[12]; + unsigned char *data; + int edl; + int element_size; + char func[16]; + int i; + int len; + const char *msgaddr; + int nb_sense_ret; + unsigned char *p; + unsigned char *page_end, *page_start; + unsigned char *q; + int rc; + char sense[MAXSENSE]; + int pause_mode = 1; + int nretries = 0; + int nbReportBytesRemaining = 0; + int nbElementsInReport = 0; + + strncpy (func, "get_elem_info", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + if (type) { + element_size = get_element_size (fd, rbtdev, type); + if (element_size < 0) return (-1); + } else { + element_size = get_element_size (fd, rbtdev, 1); /* transport */ + if (element_size < 0) return (-1); + i = get_element_size (fd, rbtdev, 2); /* slot */ + if (i < 0) return (-1); + if (i > element_size) element_size = i; + i = get_element_size (fd, rbtdev, 3); /* port */ + if (i < 0) return (-1); + if (i > element_size) element_size = i; + i = get_element_size (fd, rbtdev, 4); /* device */ + if (i < 0) return (-1); + if (i > element_size) element_size = i; + } + len = nbelem * element_size + 8; + if (type != 0 || nbelem == 1) + len += 8; /* one element header */ + else + len += 32; /* possibly four element headers */ + data = (unsigned char *)malloc (len); + memset (cdb, 0, sizeof(cdb)); + cdb[0] = opcode; /* read element status or request volume element address */ + cdb[1] = 0x10 + type; + cdb[2] = start >> 8; + cdb[3] = start & 0xFF; + cdb[4] = nbelem >> 8; + cdb[5] = nbelem & 0xFF; + cdb[7] = len >> 16; + cdb[8] = (len >> 8) & 0xFF; + cdb[9] = len & 0xFF; + + /* IBM library in pause mode */ + while (pause_mode && nretries <= 900) { + rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, data, len, + sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr); + if (rc < 0) { + if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04) && (sense[13] == -0X7B )) { + sleep(60); + pause_mode = 1; + } else { + pause_mode = 0; + } + } else { + pause_mode = 0; + } + nretries++; + } + + if (rc < 0) { + save_error (rc, nb_sense_ret, sense, msgaddr); + free (data); + return (-1); + } + avail_elem = *(data+2) * 256 + *(data+3); + nbReportBytesRemaining = *(data+5) * 256 * 256 + *(data+6) * 256 + *(data+7); + i = 0; + p = data + 8; /* point after data header */ + while (i < avail_elem && 0 < nbReportBytesRemaining) { + nbReportBytesRemaining -= 8; + edl = *(p+2) * 256 + *(p+3); + page_start = p + 8; /* point after page header */ + page_end = page_start + + (((*(p+5) * 256 + *(p+6)) * 256) + *(p+7)); + if (page_end > (data + len)) page_end = data + len; + for (p = page_start; p < page_end && i < avail_elem; p += edl, i++) { + nbElementsInReport++; + nbReportBytesRemaining -= edl; + element_info[i].element_address = *p * 256 + *(p+1); + element_info[i].element_type = *(page_start-8); + element_info[i].state = *(p+2); + element_info[i].asc = *(p+4); + element_info[i].ascq = *(p+5); + element_info[i].flags = *(p+9); + element_info[i].source_address = *(p+10) * 256 + *(p+11); + if ((*(page_start-7) & 0x80) == 0 || + (*(p+12) == '\0') || (*(p+12) == ' ')) + element_info[i].name[0] = '\0'; + else { + q = (unsigned char *) strchr ((char *)p+12, ' '); + if (q) { + strncpy (element_info[i].name, (char *)p+12, q-p-12); + element_info[i].name[q-p-12] = '\0'; + } else + strcpy (element_info[i].name, (char *)p+12); + if (strlen (element_info[i].name) > CA_MAXVIDLEN) + element_info[i].name[CA_MAXVIDLEN] = '\0'; + } + } + } + free (data); + return (nbElementsInReport); +} + +int smc_get_geometry( + const int fd, + const char *const rbtdev, + struct robot_info *const robot_info) +{ + unsigned char buf[36]; + unsigned char cdb[6]; + char func[16]; + const char *msgaddr; + int nb_sense_ret; + int rc; + char sense[MAXSENSE]; + int pause_mode = 1; + int nretries = 0; + + + strncpy(func, "get_geometry", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + memset (cdb, 0, sizeof(cdb)); + cdb[0] = 0x12; /* inquiry */ + cdb[4] = 36; + + /* IBM library in pause mode */ + while (pause_mode && nretries <= 900) { + rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 6, buf, 36, + sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr); + if (rc < 0) { + if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04) && (sense[13] == -0X7B )) { + sleep(60); + pause_mode = 1; + } else { + pause_mode = 0; + } + } else { + pause_mode = 0; + } + nretries++; + } + + if (rc < 0) { + save_error (rc, nb_sense_ret, sense, msgaddr); + return (-1); + } + memcpy (robot_info->inquiry, buf+8, 28); + robot_info->inquiry[28] = '\0'; + memset (cdb, 0, sizeof(cdb)); + cdb[0] = 0x1A; /* mode sense */ + cdb[1] = 0x08; /* DBD bit - Disable block descriptors */ + cdb[2] = 0x1D; /* element address assignment page */ + cdb[4] = 24; + pause_mode = 1; + nretries = 0; + + /* IBM library in pause mode */ + while (pause_mode && nretries<=900) { + rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 6, buf, 24, + sense, 38, SCSI_IN, &nb_sense_ret, &msgaddr); + if (rc < 0) { + if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04) && (sense[13] == -0X7B )) { + sleep(60); + pause_mode = 1; + } else { + pause_mode = 0; + } + } else { + pause_mode = 0; + } + nretries++; + } + + if (rc < 0) { + save_error (rc, nb_sense_ret, sense, msgaddr); + return (-1); + } + robot_info->transport_start = buf[6] * 256 + buf[7]; + robot_info->transport_count = buf[8] * 256 + buf[9]; + robot_info->slot_start = buf[10] * 256 + buf[11]; + robot_info->slot_count = buf[12] * 256 + buf[13]; + robot_info->port_start = buf[14] * 256 + buf[15]; + robot_info->port_count = buf[16] * 256 + buf[17]; + robot_info->device_start = buf[18] * 256 + buf[19]; + robot_info->device_count = buf[20] * 256 + buf[21]; + + return (0); +} + +int smc_read_elem_status( + const int fd, + const char *const rbtdev, + const int type, + const int start, + const int nbelem, + struct smc_element_info element_info[]) +{ + char func[16]; + + strncpy(func, "read_elem_statu", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + return get_element_info ('\xB8', fd, rbtdev, type, start, nbelem, element_info); +} + +int smc_find_cartridgeWithoutSendVolumeTag ( + const int fd, + const char *const rbtdev, + const char *const find_template, + const int type, + const int start, + const int nbelem, + struct smc_element_info element_info[]) +{ + static char err_msgbuf[132]; + int nbFound = 0; + char func[16]; + int i; + struct smc_element_info *inventory_info; + char *msgaddr; + const int patternMatching = strchr (find_template, '*') || strchr (find_template, '?'); + struct robot_info robot_info; + int tot_nbelem = 0; + int nbElementsInReport = 0; + + strncpy(func, "findWithoutVT", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + { + const int smc_get_geometry_rc = smc_get_geometry (fd, rbtdev, &robot_info); + if(smc_get_geometry_rc) { + return smc_get_geometry_rc; + } + } + + tot_nbelem = robot_info.transport_count + robot_info.slot_count + + robot_info.port_count + robot_info.device_count; + + if ((inventory_info = (struct smc_element_info *)malloc (tot_nbelem * sizeof(struct smc_element_info))) == NULL) { + serrno = errno; + sprintf (err_msgbuf, "malloc error: %s", strerror(errno)); + msgaddr = err_msgbuf; + save_error (-1, 0, NULL, msgaddr); + return (-1); + } + + nbElementsInReport = smc_read_elem_status (fd, rbtdev, type, start, tot_nbelem, inventory_info); + if(0 > nbElementsInReport) { + free (inventory_info); + return (nbElementsInReport); + } + for (i = 0 ; i < nbElementsInReport && nbFound < nbelem; i++) { + if (inventory_info[i].state & 0x1) { + if (patternMatching) { + if (vmatch (find_template, inventory_info[i].name) == 0) { + memcpy (&element_info[nbFound], &inventory_info[i], + sizeof(struct smc_element_info)); + nbFound++; + } + } else { + if (strcmp (find_template, inventory_info[i].name) == 0) { + memcpy (element_info, &inventory_info[i], + sizeof(struct smc_element_info)); + nbFound = 1; + break; + } + } + } + } + free (inventory_info); + return (nbFound); +} + + +int smc_find_cartridge( + const int fd, + const char *const rbtdev, + const char *const find_template, + const int type, + const int start, + const int nbelem, + struct smc_element_info element_info[], + const bool sendVolumeTag) +{ + unsigned char cdb[12]; + char func[16]; + const char *msgaddr; + int nb_sense_ret; + char plist[40]; + int rc; + char sense[MAXSENSE]; + int pause_mode = 1; + int nretries = 0; + + strncpy(func, "findWithVT", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + if(!sendVolumeTag) { + /* Skip the 0xB6 cdb command if the tape library is Spectra like */ + rc = smc_find_cartridgeWithoutSendVolumeTag (fd, rbtdev, find_template, type, start, nbelem, + element_info); + if (rc >= 0) + return (rc); + return (-1); + } + + memset (cdb, 0, sizeof(cdb)); + cdb[0] = 0xB6; /* send volume tag */ + cdb[1] = type; + cdb[2] = start >> 8; + cdb[3] = start & 0xFF; + cdb[5] = 5; + cdb[9] = 40; + memset (plist, 0, sizeof(plist)); + strcpy (plist, find_template); + + /* IBM library in pause mode */ + while (pause_mode && nretries <= 900) { + + rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, (unsigned char*)plist, 40, + sense, 38, SCSI_OUT, &nb_sense_ret, &msgaddr); + if (rc < 0) { + if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04) && (sense[13] == -0X7B )) { + sleep(60); + pause_mode = 1; + } else { + pause_mode = 0; + } + } else { + pause_mode = 0; + } + nretries++; + } + + if (rc < 0) { + save_error (rc, nb_sense_ret, sense, msgaddr); + if (rc == -4 && nb_sense_ret >= 14 && (sense[2] & 0xF) == 5) { + rc = smc_find_cartridgeWithoutSendVolumeTag (fd, rbtdev, find_template, type, + start, nbelem, element_info); + if (rc >= 0) + return (rc); + } + return (-1); + } + return get_element_info ('\xB5', fd, rbtdev, type, start, nbelem, element_info); +} + + +/* SCSI 3 additional sense code and additional sense qualifier */ +struct scsierr_codact { + unsigned char sensekey; + unsigned char asc; + unsigned char ascq; + short action; + const char *txt; +}; +static struct scsierr_codact scsierr_acttbl[] = { + {0x02, 0x04, 0x00, RBT_FAST_RETRY, "Logical Unit Not Ready, Cause Not Reportable"}, + {0x02, 0x04, 0x01, RBT_FAST_RETRY, "Logical Unit Is In Process of Becoming Ready"}, + {0x02, 0x04, 0x02, RBT_NORETRY, "Logical Unit Not Ready, initialization required"}, + {0x02, 0x04, 0x03, RBT_NORETRY, "Logical Unit Not Ready, Manual Intervention Required"}, + {0x0B, 0x08, 0x00, RBT_NORETRY, "Logical Unit Communication Failure"}, + {0x0B, 0x08, 0x01, RBT_NORETRY, "Logical Unit Communication Time-out"}, + {0x05, 0x1A, 0x00, RBT_NORETRY, "Parameter List Length Error"}, + {0x05, 0x20, 0x00, RBT_NORETRY, "Invalid Command Operation Code"}, + {0x05, 0x21, 0x01, RBT_NORETRY, "Invalid Element Address"}, + {0x05, 0x24, 0x00, RBT_NORETRY, "Invalid field in CDB"}, + {0x05, 0x25, 0x00, RBT_NORETRY, "Logical Unit Not Supported"}, + {0x05, 0x26, 0x00, RBT_NORETRY, "Invalid field in Parameter List"}, + {0x05, 0x26, 0x01, RBT_NORETRY, "Parameter Not Supported"}, + {0x05, 0x26, 0x02, RBT_NORETRY, "Parameter Value Invalid"}, + {0x06, 0x28, 0x00, RBT_FAST_RETRY, "Not Ready to Ready Transition"}, + {0x06, 0x28, 0x01, RBT_FAST_RETRY, "Import or Export Element Accessed"}, + {0x06, 0x29, 0x00, RBT_FAST_RETRY, "Power On, Reset, or Bus Device Reset Occurred"}, + {0x06, 0x2A, 0x01, RBT_FAST_RETRY, "Mode Parameters Changed"}, + {0x05, 0x30, 0x00, RBT_NORETRY, "Incompatible Medium Installed"}, + {0x00, 0x30, 0x03, RBT_FAST_RETRY, "Cleaning Cartridge Installed"}, + {0x05, 0x39, 0x00, RBT_NORETRY, "Saving Parameters Not Supported"}, + {0x05, 0x3A, 0x00, RBT_XTRA_PROC, "Medium Not Present"}, + {0x05, 0x3B, 0x0D, RBT_XTRA_PROC, "Medium Destination Element Full"}, + {0x05, 0x3B, 0x0E, RBT_XTRA_PROC, "Medium Source Element Empty"}, + {0x04, 0x40, 0x01, RBT_NORETRY, "Hardware Error, General"}, + {0x04, 0x40, 0x02, RBT_NORETRY, "Hardware Error, Tape Transport"}, + {0x04, 0x40, 0x03, RBT_NORETRY, "Hardware Error, CAP"}, + {0x0B, 0x43, 0x00, RBT_NORETRY, "Message Error"}, + {0x02, 0x44, 0x00, RBT_NORETRY, "Internal Target Failure"}, + {0x0B, 0x45, 0x00, RBT_NORETRY, "Select or Reselect Failure"}, + {0x0B, 0x47, 0x00, RBT_NORETRY, "SCSI Parity Error"}, + {0x0B, 0x48, 0x00, RBT_NORETRY, "Initiator Detected Error"}, + {0x02, 0x4C, 0x00, RBT_NORETRY, "Logical Unit Failed Self-Configuration"}, + {0x05, 0x4E, 0x00, RBT_NORETRY, "Overlapped Commands Attempted"}, + {0x05, 0x53, 0x02, RBT_NORETRY, "Medium Removal Prevented"}, + {0x06, 0x54, 0x00, RBT_NORETRY, "SCSI To Host System Interface Failure"}, + {0x02, 0x5A, 0x01, RBT_NORETRY, "Operator Medium Removal Request"} +}; + +static const char* action_to_str(const short action) { + switch(action) { + case RBT_FAST_RETRY: return "RBT_FAST_RETRY"; + case RBT_NORETRY : return "RBT_NORETRY"; + case RBT_XTRA_PROC : return "RBT_XTRA_PROC"; + default : return "UNKNOWN"; + } +} + +int smc_lasterror( + struct smc_status *const smc_stat, + const char **const msgaddr) +{ + unsigned int i; + char func[16]; + + strncpy (func, "lasterror", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + rmc_logit(func, "Function entered:" + " asc=%d ascq=%d save_errno=%d rc=%d sensekey=%d skvalid=%d\n", + smc_status.asc, smc_status.ascq, smc_status.save_errno, + smc_status.rc, smc_status.sensekey, smc_status.skvalid); + + smc_stat->rc = smc_status.rc; + smc_stat->skvalid = smc_status.skvalid; + *msgaddr = smc_msgaddr; + if ((smc_status.rc == -1 || smc_status.rc == -2) && + smc_status.save_errno == EBUSY) + return (EBUSY); + if (! smc_status.skvalid) + return (RBT_NORETRY); + smc_stat->sensekey = smc_status.sensekey; + smc_stat->asc = smc_status.asc; + smc_stat->ascq = smc_status.ascq; + for (i = 0; i < sizeof(scsierr_acttbl)/sizeof(struct scsierr_codact); i++) { + if (smc_status.asc == scsierr_acttbl[i].asc && + smc_status.ascq == scsierr_acttbl[i].ascq && + smc_status.sensekey == scsierr_acttbl[i].sensekey) { + const char *const action_str = + action_to_str(scsierr_acttbl[i].action); + *msgaddr = scsierr_acttbl[i].txt; + + rmc_logit(func, "Entry found in scsierr_acttbl:" + " action_str=%s\n", action_str); + + return (scsierr_acttbl[i].action); + } + } + + rmc_logit(func, "No matching entry in scsierr_acttbl\n"); + + return (RBT_NORETRY); +} + +int smc_move_medium( + const int fd, + const char *const rbtdev, + const int from, + const int to, + const int invert) +{ + unsigned char cdb[12]; + char func[16]; + const char *msgaddr; + int nb_sense_ret; + int rc; + char sense[MAXSENSE]; + int pause_mode = 1; + int nretries = 0; + + strncpy(func, "move_medium", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + memset (cdb, 0, sizeof(cdb)); + cdb[0] = 0xA5; /* move medium */ + cdb[4] = from >> 8; + cdb[5] = from & 0xFF; + cdb[6] = to >> 8; + cdb[7] = to & 0xFF; + cdb[10] = invert; + + while (pause_mode) { + rc = rmc_send_scsi_cmd (fd, rbtdev, 0, cdb, 12, NULL, 0, + sense, 38, SCSI_NONE, &nb_sense_ret, &msgaddr); + if (rc < 0) { + if (rc == -4 && nb_sense_ret >= 14 && (sense[12] == 0x04) && (sense[13] == -0X7B )) { + sleep(60); + pause_mode = 1; + } else { + pause_mode = 0; + } + } else { + pause_mode = 0; + } + nretries++; + } + + if (rc < 0) { + save_error (rc, nb_sense_ret, sense, msgaddr); + return (-1); + } + return (0); +} + +static int rmc_usrmsg( + const int rpfd, + const char *func, + const char *const msg, + ...) +{ + va_list args; + char prtbuf[RMC_PRTBUFSZ]; + const int save_errno = errno; + + va_start (args, msg); + snprintf (prtbuf, sizeof(prtbuf), "%s: ", func); + prtbuf[sizeof(prtbuf) - 1] = '\0'; + { + const size_t nbBytesUsed = strlen (prtbuf); + + /* If there is still space in the print buffer */ + if(nbBytesUsed < (sizeof(prtbuf))) { + const size_t nbBytesRemaining = sizeof(prtbuf) - + nbBytesUsed; + char *const p = prtbuf + nbBytesUsed; + vsnprintf (p, nbBytesRemaining, msg, args); + prtbuf[sizeof(prtbuf) - 1] = '\0'; + } + } +// rmc_sendrep (rpfd, MSG_ERR, "%s", prtbuf); + va_end (args); + errno = save_errno; + return (0); +} + +int smc_mount ( + const int rpfd, + const int fd, + const char *const loader, + const struct robot_info *const robot_info, + const int drvord, + const char *const vid, + const int invert) +{ + int c; + const bool sendVolumeTag = true; + struct smc_element_info element_info; + char func[16]; + const char *msgaddr; + struct smc_status smc_status; + + strncpy (func, "smc_mount", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + if ((c = smc_find_cartridge (fd, loader, vid, 0, 0, 1, &element_info, sendVolumeTag)) < 0) { + c = smc_lasterror (&smc_status, &msgaddr); + rmc_usrmsg ( rpfd, func, SR017, "find_cartridge", vid, msgaddr); + return (c); + } + if (c == 0) { + rmc_usrmsg ( rpfd, func, SR018, "mount", vid, drvord, "volume not in library"); + return (RBT_NORETRY); + } + if (element_info.element_type != 2) { + + /* compare requested and replied vid */ + rmc_usrmsg ( rpfd, func, "Asked for %s, got reply for %s\n", + vid, element_info.name ); + + /* detail on a tape's current location */ + switch (element_info.element_type) { + + case 1: + rmc_usrmsg ( rpfd, func, "Location: medium transport element (0x%x)\n", + element_info.element_type ); + break; + case 2: + /* normal case: in its home slot, not possible inside the if */ + break; + case 3: + rmc_usrmsg ( rpfd, func, "Location: import/export element (0x%x)\n", + element_info.element_type ); + break; + case 4: + rmc_usrmsg ( rpfd, func, "Location: data transfer element (0x%x)\n", + element_info.element_type ); + break; + default: + rmc_usrmsg ( rpfd, func, "Location: unknown (0x%x)\n", + element_info.element_type ); + } + + rmc_usrmsg ( rpfd, func, SR018, "mount", vid, drvord, "volume in use"); + return (RBT_SLOW_RETRY); + } + if ((c = smc_move_medium (fd, loader, element_info.element_address, + robot_info->device_start+drvord, invert)) < 0) { + c = smc_lasterror (&smc_status, &msgaddr); + rmc_usrmsg ( rpfd, func, SR018, "mount", vid, drvord, msgaddr); + return (c); + } + return (0); +} + +int smc_dismount ( + const int rpfd, + const int fd, + const char *const loader, + struct robot_info *const robot_info, + const int drvord, + const char *const vid) +{ + const bool sendVolumeTag = true; + const unsigned int max_element_status_reads = 20; + const unsigned int dismount_status_read_delay = 1; // In seconds + unsigned int nb_element_status_reads = 0; + int drive_not_unloaded = 1; + struct smc_element_info drive_element_info; + char func[16]; + const char *msgaddr = 0; + struct smc_status smc_status; + + strncpy (func, "smc_dismount", sizeof(func)); + func[sizeof(func) - 1] = '\0'; + + memset(&smc_status, '\0', sizeof(smc_status)); + + /* IBM libraries sometimes disagree with the eject of their drives. */ + /* Sometimes the access bit of the result of Read Element Status */ + /* (XB8) indicates the gripper cannot access the tape even though */ + /* the eject was successful. Reading the element status at a later */ + /* point in time eventually indicates the tape is accessible. */ + while(drive_not_unloaded && nb_element_status_reads < max_element_status_reads) { + if (0 > smc_read_elem_status (fd, loader, 4, robot_info->device_start+drvord, + 1, &drive_element_info)) { + const int smc_error = smc_lasterror (&smc_status, &msgaddr); + rmc_usrmsg ( rpfd, func, SR020, "read_elem_status", msgaddr); + return (smc_error); + } + if (0 == (drive_element_info.state & 0x1)) { + rmc_usrmsg ( rpfd, func, SR018, "demount", vid, drvord, "Medium Not Present"); + return (RBT_OK); + } + + drive_not_unloaded = (0 == (drive_element_info.state & 0x8)); + if (drive_not_unloaded) { + rmc_usrmsg ( rpfd, func, "read_elem_status of %s on drive %d detected Drive Not Unloaded\n", vid, drvord); + } + + nb_element_status_reads++; + + if(nb_element_status_reads < max_element_status_reads) { + sleep(dismount_status_read_delay); + } + } + if(drive_not_unloaded) { + rmc_usrmsg ( rpfd, func, SR018, "demount", vid, drvord, "Drive Not Unloaded"); + return (RBT_UNLD_DMNT); + } + + if (*vid && strcmp (drive_element_info.name, vid)) { + rmc_usrmsg ( rpfd, func, SR009, vid, drive_element_info.name); + return (RBT_NORETRY); + } + if (0 > smc_move_medium (fd, loader, robot_info->device_start+drvord, + drive_element_info.source_address, (drive_element_info.flags & 0x40) ? 1 : 0)) { + const int smc_error = smc_lasterror (&smc_status, &msgaddr); + rmc_usrmsg ( rpfd, func, SR018, "demount", vid, drvord, msgaddr); + return (smc_error); + } + // check that the vid is in a slot before returning + while (1) { + struct smc_element_info vol_element_info; + if (0 > smc_find_cartridge (fd, loader, drive_element_info.name, 0, 0, 1, &vol_element_info, sendVolumeTag )) { + const int smc_error = smc_lasterror (&smc_status, &msgaddr); + rmc_usrmsg ( rpfd, func, SR017, "find_cartridge", drive_element_info.name, msgaddr); + return (smc_error); + } + + // vid is in a storage slot + if (vol_element_info.element_type == 2) break; + // give time for the tape enter the slot + sleep (2); + } + + return (0); +} diff --git a/XRootdSSiRmcd/rmc_smcsubr.h b/XRootdSSiRmcd/rmc_smcsubr.h new file mode 100644 index 0000000000000000000000000000000000000000..3e2bd2eec9e3e616779f812f9cc74329173946c7 --- /dev/null +++ b/XRootdSSiRmcd/rmc_smcsubr.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * + * 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 "smc_struct.h" + +int smc_get_geometry( + const int fd, + const char *const rbtdev, + struct robot_info *const robot_info); + +int smc_read_elem_status( + const int fd, + const char *const rbtdev, + const int type, + const int start, + const int nbelem, + struct smc_element_info element_info[]); + +int smc_find_cartridgeWithoutSendVolumeTag ( + const int fd, + const char *const rbtdev, + const char *const find_template, + const int type, + const int start, + const int nbelem, + struct smc_element_info element_info[]); + +int smc_find_cartridge( + const int fd, + const char *const rbtdev, + const char *const find_template, + const int type, + const int start, + const int nbelem, + struct smc_element_info element_info[], + const bool sendVolumeTag); + +int smc_lasterror( + struct smc_status *const smc_stat, + const char **const msgaddr); + +int smc_move_medium( + const int fd, + const char *const rbtdev, + const int from, + const int to, + const int invert); + diff --git a/XRootdSSiRmcd/rmc_smcsubr2.h b/XRootdSSiRmcd/rmc_smcsubr2.h new file mode 100644 index 0000000000000000000000000000000000000000..f67f4c9b440fe567b2b000bedd270a4da396aa41 --- /dev/null +++ b/XRootdSSiRmcd/rmc_smcsubr2.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1998-2002 by CERN/IT/PDP/DM + * All rights reserved + */ + +#pragma once + +#include "smc_struct.h" + +int smc_dismount ( + const int rpfd, + const int fd, + const char *const loader, + struct robot_info *const robot_info, + const int drvord, + const char *const vid); + +int smc_export ( + const int rpfd, + const int fd, + const char *const loader, + struct robot_info *const robot_info, + const char *const vid); + +int smc_import ( + const int rpfd, + const int fd, + const char *const loader, + struct robot_info *const robot_info, + const char *const vid); + +int smc_mount ( + const int rpfd, + const int fd, + const char *const loader, + const struct robot_info *const robot_info, + const int drvord, + const char *const vid, + const int invert); + diff --git a/XRootdSSiRmcd/scsictl.h b/XRootdSSiRmcd/scsictl.h new file mode 100644 index 0000000000000000000000000000000000000000..e5e15e1e3741ca5ff58bfa9ca191cb6050738b2f --- /dev/null +++ b/XRootdSSiRmcd/scsictl.h @@ -0,0 +1,77 @@ +/* + * $Id: scsictl.h,v 1.2 1999/12/09 13:46:22 jdurand Exp $ + */ + +/* + * Copyright (C) 1995-1996 by CERN/CN/SW/SC + * All rights reserved + */ + +/* + * @(#)scsictl.h 1.1 12/10/96 CERN CN-SW/SC Fabien Collin + */ + +#pragma once + +#define MAXSENSE 255 + +/* Values of the 'flags' field for function send_cmd */ +#define SCSI_IN 1 +#define SCSI_OUT 2 +#define SCSI_IN_OUT 4 +#define SCSI_NONE 8 +#define SCSI_SEL_WITH_ATN 16 +#define SCSI_SYNC 32 +#define SCSI_WIDE 64 + +/* SCSI status values */ +#define SCSI_STATUS_GOOD 0x00 +#define SCSI_STATUS_CHECK_CONDITION 0x02 +#define SCSI_STATUS_CONDITION_MET_GOOD 0x04 +#define SCSI_STATUS_BUSY 0x08 +#define SCSI_STATUS_INTERMEDIATE_GOOD 0x10 +#define SCSI_STATUS_INTERMEDIATE_CONDITION_MET 0x14 +#define SCSI_STATUS_RESERVATION_CONFLICT 0x18 +#define SCSI_STATUS_COMMAND_TERMINATED 0x22 +#define SCSI_STATUS_QUEUE_FULL 0x28 + +/* Generic SCSI driver status */ +#define CAM_OK 0 /* No error at the CAM level */ +#define CAM_TIMEOUT 1 /* Command timeout */ +#define CAM_ERROR 2 /* Error at the CAM level */ +#define CAM_NODEVICE 3 /* Device doesn't respond (inexistant device) */ + +/* SCSI 3 Sense key */ +#define SCSI_SENSEKEY_NO_SENSE 0 +#define SCSI_SENSEKEY_RECOVERED_ERROR 1 +#define SCSI_SENSEKEY_NOT_READY 2 +#define SCSI_SENSEKEY_MEDIUM_ERROR 3 +#define SCSI_SENSEKEY_HARDWARE_ERROR 4 +#define SCSI_SENSEKEY_ILLEGAL_REQUEST 5 +#define SCSI_SENSEKEY_UNIT_ATTENTION 6 +#define SCSI_SENSEKEY_DATA_PROTECT 7 +#define SCSI_SENSEKEY_BLANK_CHECK 8 +#define SCSI_SENSEKEY_VENDOR_SPECIFIC 9 +#define SCSI_SENSEKEY_COPY_ABORTED 10 +#define SCSI_SENSEKEY_ABORTED_COMMAND 11 +#define SCSI_SENSEKEY_EQUAL 12 +#define SCSI_SENSEKEY_VOLUME_OVERFLOW 13 +#define SCSI_SENSEKEY_MISCOMPARE 14 +#define SCSI_SENSEKEY_RESERVED 15 + +/* SCSI 3 Peripheric device types */ +#define SCSI_PERIPH_DIRECT_ACCESS 0 +#define SCSI_PERIPH_SEQUENTIAL_ACCESS 1 +#define SCSI_PERIPH_PRINTER 2 +#define SCSI_PERIPH_PROCESSOR 3 +#define SCSI_PERIPH_WORM 4 +#define SCSI_PERIPH_CDROM 5 +#define SCSI_PERIPH_SCANNER 6 +#define SCSI_PERIPH_OPTICAL 7 +#define SCSI_PERIPH_AUTOCHANGER 8 +#define SCSI_PERIPH_COMMUNICATION 9 +#define SCSI_PERIPH_ASC1 10 +#define SCSI_PERIPH_ASC2 11 +#define SCSI_PERIPH_RAID 12 +#define SCSI_PERIPH_UNKNOWN 31 + diff --git a/XRootdSSiRmcd/serrno.cpp b/XRootdSSiRmcd/serrno.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f4408cfba8c68e7b152fbc4a939974fa22988b7 --- /dev/null +++ b/XRootdSSiRmcd/serrno.cpp @@ -0,0 +1 @@ +int serrno = 0; diff --git a/XRootdSSiRmcd/serrno.h b/XRootdSSiRmcd/serrno.h new file mode 100644 index 0000000000000000000000000000000000000000..7b0be7cdb077ed8fa8987b83ffa3bd2aca4a1be6 --- /dev/null +++ b/XRootdSSiRmcd/serrno.h @@ -0,0 +1,464 @@ +/* + * Copyright (C) 1990-2002 by CERN/IT/PDP/DM + * All rights reserved + */ + +/* + * serrno.h,v 1.65 2002/11/27 14:45:53 + */ + +/* serrno.h Special error numbers - not in errno.h */ + +#pragma once + +#include <sys/types.h> /* For size_t */ +#include <stddef.h> /* For size_t on _WIN32 */ + +#define SEBASEOFF 1000 /* Base offset for special err. */ +#define EDBBASEOFF 1200 /* CDB error base offset */ +#define EMSBASEOFF 1300 /* MSG error base offset */ +#define ENSBASEOFF 1400 /* NS error base offset */ +#define ERFBASEOFF 1500 /* RFIO error base offset */ +#define ERTBASEOFF 1600 /* RTCOPY error base offset */ +#define ESTBASEOFF 1700 /* STAGE error base offset */ +#define ESQBASEOFF 1800 /* SYSREQ error base offset */ +#define ETBASEOFF 1900 /* TAPE error base offset */ +#define EVMBASEOFF 2000 /* VMGR error base offset */ +#define EVQBASEOFF 2100 /* VDQM error base offset */ +#define ERMBASEOFF 2200 /* RMC error base offset */ +#define EMONBASEOFF 2300 /* Monitoring Error base offset */ +#define EUPBASEOFF 2400 /* UPV error base offset */ +#define ESECBASEOFF 2700 /* Security error base offset */ +#define EDNSBASEOFF 3000 /* DNS error base offset */ + +#define SENOERR SEBASEOFF /* No error */ +#define SENOSHOST SEBASEOFF+1 /* Host not known */ +#define SENOSSERV SEBASEOFF+2 /* Service unknown */ +#define SENOTRFILE SEBASEOFF+3 /* Not a remote file */ +#define SETIMEDOUT SEBASEOFF+4 /* Has timed out */ +#define SEBADFFORM SEBASEOFF+5 /* Bad fortran format specifier */ +#define SEBADFOPT SEBASEOFF+6 /* Bad fortran option specifier */ +#define SEINCFOPT SEBASEOFF+7 /* Incompatible fortran options */ +#define SENAMETOOLONG SEBASEOFF+8 /* File name too long */ +#define SENOCONFIG SEBASEOFF+9 /* Can't open configuration file*/ +#define SEBADVERSION SEBASEOFF+10 /* Version ID mismatch */ +#define SEUBUF2SMALL SEBASEOFF+11 /* User buffer too small */ +#define SEMSGINVRNO SEBASEOFF+12 /* Invalid reply number */ +#define SEUMSG2LONG SEBASEOFF+13 /* User message too long */ +#define SEENTRYNFND SEBASEOFF+14 /* Entry not found */ +#define SEINTERNAL SEBASEOFF+15 /* Internal error */ +#define SECONNDROP SEBASEOFF+16 /* Connection closed by rem. end*/ +#define SEBADIFNAM SEBASEOFF+17 /* Can't get interface name */ +#define SECOMERR SEBASEOFF+18 /* Communication error */ +#define SENOMAPDB SEBASEOFF+19 /* Can't open mapping database */ +#define SENOMAPFND SEBASEOFF+20 /* No user mapping */ +#define SERTYEXHAUST SEBASEOFF+21 /* Retry count exhausted */ +#define SEOPNOTSUP SEBASEOFF+22 /* Operation not supported */ +#define SEWOULDBLOCK SEBASEOFF+23 /* Resource temporarily unavailable */ +#define SEINPROGRESS SEBASEOFF+24 /* Operation now in progress */ +#define SECTHREADINIT SEBASEOFF+25 /* Cthread initialization error */ +#define SECTHREADERR SEBASEOFF+26 /* Thread interface call error */ +#define SESYSERR SEBASEOFF+27 /* System error */ +#define SENOTADMIN SEBASEOFF+32 /* requestor is not administrator */ +#define SEUSERUNKN SEBASEOFF+33 /* User unknown */ +#define SEDUPKEY SEBASEOFF+34 /* Duplicate key value */ +#define SEENTRYEXISTS SEBASEOFF+35 /* Entry already exists */ +#define SEGROUPUNKN SEBASEOFF+36 /* Group Unknown */ +#define SECHECKSUM SEBASEOFF+37 /* Bad checksum */ +#define SESVCCLASSNFND SEBASEOFF+38 /* This service class is not available for this host */ +#define SESQLERR SEBASEOFF+39 /* Got SQL exception from database */ +#define SELOOP SEBASEOFF+40 /* Too many symbolic links */ +#define SENOPORTINRANGE SEBASEOFF+41 /* No port in range */ +#define SENOVALUE SEBASEOFF+42 /* No value */ +#define SEINVALIDCONFIG SEBASEOFF+43 /* Invalid configuration */ +#define SEPYTHONEXEC SEBASEOFF+44 /* Failed to execute Python */ +#define SEMISSINGOPER SEBASEOFF+45 /* Missing operand */ +#define SEMISMATCH SEBASEOFF+46 /* Mismatch */ +#define SEREQUESTFAILED SEBASEOFF+47 /* Request failed */ +#define SEINVALIDNBARGS SEBASEOFF+48 /* Invalid number of arguments */ +#define SEALREADYINIT SEBASEOFF+49 /* Already initialized */ +#define SECMDLNNOTPRSD SEBASEOFF+50 /* Command line not parsed */ +#define SEACCPTCONNINTR SEBASEOFF+51 /* Accept connection was interrupted */ +#define SEBADALLOC SEBASEOFF+52 /* Failed to allocate memory */ +#define SENOTANOWNER SEBASEOFF+53 /* Not an owner */ + +#define SEMAXERR SEBASEOFF+53 /* Maximum error number */ + +#define SERRNO (serrno - SEBASEOFF) /* User convenience */ +/* + * Backward compatibility + */ +#define SEFNAM2LONG SENAMETOOLONG + +/* + * Package specific error messages (don't forget to update commmon/serror.c) + */ + +/* + *------------------------------------------------------------------------ + * DB errors + *------------------------------------------------------------------------ + */ +#define EDB_A_ESESSION EDBBASEOFF+1 /* Cdb api : invalid session */ +#define EDB_A_EDB EDBBASEOFF+2 /* Cdb api : invalid db */ +#define EDB_A_EINVAL EDBBASEOFF+3 /* Cdb api : invalid value */ +#define EDB_A_RESHOST EDBBASEOFF+4 /* Cdb api : host res error */ +#define EDB_A_TOOMUCH EDBBASEOFF+5 /* Cdb api : data size rejected */ +#define EDB_AS_SOCKET EDBBASEOFF+6 /* Cdb api system : socket() error */ +#define EDB_AS_SOCKOPT EDBBASEOFF+7 /* Cdb api system : [set/get]sockopt() error */ +#define EDB_AS_MALLOC EDBBASEOFF+8 /* Cdb api system : malloc() error */ +#define EDB_A_NOERROR EDBBASEOFF+9 /* Cdb api : no last error */ +#define EDB_A_IEINVAL EDBBASEOFF+10 /* Cdb api : interface invalid value */ +#define EDB_AS_BIND EDBBASEOFF+11 /* Cdb api : bind() error */ +#define EDB_AS_LISTEN EDBBASEOFF+12 /* Cdb api : listen() error */ +#define EDB_AS_GETSOCKNAME EDBBASEOFF+13 /* Cdb api : getsockname() error */ +#define EDB_AS_ACCEPT EDBBASEOFF+14 /* Cdb api : accept() error */ +#define EDB_AS_GETPEERNAME EDBBASEOFF+15 /* Cdb api : getpeername() error */ +#define EDB_A_WHOISIT EDBBASEOFF+16 /* Cdb api : Connection from bad host */ + +#define EDB_D_EINVAL EDBBASEOFF+20 /* Cdb daemon : invalid value */ +#define EDB_D_EAGAIN EDBBASEOFF+21 /* Cdb daemon : yet done */ +#define EDB_D_AUTH EDBBASEOFF+22 /* Cdb daemon : unauthorized */ +#define EDB_D_LOGIN EDBBASEOFF+23 /* Cdb daemon : login refused */ +#define EDB_D_PWDCORR EDBBASEOFF+24 /* Cdb daemon : pwd file corrupted */ +#define EDB_D_ANA EDBBASEOFF+25 /* Cdb daemon : db analysis error */ +#define EDB_D_HASHSIZE EDBBASEOFF+26 /* Cdb daemon : bad hash size */ +#define EDB_D_UNKNOWN EDBBASEOFF+27 /* Cdb daemon : unkn. db/table/key */ +#define EDB_D_NOLOCK EDBBASEOFF+28 /* Cdb daemon : lock is required */ +#define EDB_D_CORRUPT EDBBASEOFF+29 /* Cdb daemon : probably corrupted */ +#define EDB_D_TOOMUCH EDBBASEOFF+30 /* Cdb daemon : data size rejected */ +#define EDB_D_ENOENT EDBBASEOFF+31 /* Cdb daemon : no entry */ +#define EDB_D_ETYPE EDBBASEOFF+32 /* Cdb daemon : unknown member type*/ +#define EDB_D_EVALUE EDBBASEOFF+33 /* Cdb daemon : unknown member val */ +#define EDB_D_NULLVALUE EDBBASEOFF+34 /* Cdb daemon : null member value */ +#define EDB_D_LOCK EDBBASEOFF+35 /* Cdb daemon : cannot gain lock */ +#define EDB_D_FREE EDBBASEOFF+36 /* Cdb daemon : unsafe free attempt*/ +#define EDB_D_SHUTDOWN EDBBASEOFF+37 /* Cdb daemon : shutdown in progress */ +#define EDB_D_DEADLOCK EDBBASEOFF+38 /* Cdb daemon : deadlock detected */ +#define EDB_D_EXIST EDBBASEOFF+39 /* Cdb daemon : yet exists */ +#define EDB_D_NOSPC EDBBASEOFF+40 /* Cdb daemon : no more space */ +#define EDB_D_DUMPEND EDBBASEOFF+41 /* Cdb daemon : end of dump */ +#define EDB_D_UNIQUE EDBBASEOFF+42 /* Cdb daemon : uniqued key yet exist */ +#define EDB_D_LISTEND EDBBASEOFF+43 /* Cdb daemon : end of list */ +#define EDB_D_NOTDUMP EDBBASEOFF+44 /* Cdb daemon : not in dump mode */ +#define EDB_D_DNSCHECK EDBBASEOFF+45 /* Cdb daemon : double DNS check error */ +#define EDB_D_REJECTED EDBBASEOFF+46 /* Cdb daemon : Connection rejected (not authorised) */ +#define EDB_D_INIT EDBBASEOFF+47 /* Cdb daemon : init in progress */ +#define EDB_D_INCONST EDBBASEOFF+48 /* Cdb daemon : inconsistent request (unstop and no previous stop, unfreeze and no previous freeze) */ +#define EDB_D_FREEHASHSIZE EDBBASEOFF+49 /* Cdb daemon : bad free hash size */ + +#define EDB_DS_MALLOC EDBBASEOFF+50 /* Cdb daemon system : malloc() error */ +#define EDB_DS_CALLOC EDBBASEOFF+51 /* Cdb daemon system : calloc() error */ +#define EDB_DS_REALLOC EDBBASEOFF+52 /* Cdb daemon system : realloc() error */ +#define EDB_DS_OPEN EDBBASEOFF+53 /* Cdb daemon system : open() error */ +#define EDB_DS_FSTAT EDBBASEOFF+54 /* Cdb daemon system : fstat() error */ +#define EDB_DS_LSEEK EDBBASEOFF+55 /* Cdb daemon system : lseek() error */ +#define EDB_DS_READ EDBBASEOFF+56 /* Cdb daemon system : read() error */ +#define EDB_DS_WRITE EDBBASEOFF+57 /* Cdb daemon system : write() error */ +#define EDB_DS_RENAME EDBBASEOFF+58 /* Cdb daemon system : rename() error */ +#define EDB_DS_FTRUNC EDBBASEOFF+59 /* Cdb daemon system : ftruncate() error */ +#define EDB_DS_TMPNAM EDBBASEOFF+60 /* Cdb daemon system : tmpnam() error */ +#define EDB_DS_FCNTL EDBBASEOFF+61 /* Cdb daemon system : fcntl() error */ +#define EDB_DS_MKDIR EDBBASEOFF+62 /* Cdb daemon system : mkdir() error */ +#define EDB_DS_TIMES EDBBASEOFF+63 /* Cdb daemon system : times() error */ +#define EDB_DS_SYSCONF EDBBASEOFF+64 /* Cdb daemon system : sysconf() err/unav */ +#define EDB_DS_GETHOSTNAME EDBBASEOFF+65 /* Cdb daemon system : gethostname() error*/ +#define EDB_DS_GETPEERNAME EDBBASEOFF+66 /* Cdb daemon system : getpeername() error*/ +#define EDB_DS_INET_NTOA EDBBASEOFF+67 /* Cdb daemon system : getpeername() error*/ +#define EDB_DS_REMOVE EDBBASEOFF+68 /* Cdb daemon system : remove() error */ +#define EDB_DS_SIGACTION EDBBASEOFF+69 /* Cdb daemon system : sigaction() error */ +#define EDB_DS_GETSOCKNAME EDBBASEOFF+70 /* Cdb daemon system : getsockname() error*/ +#define EDB_DS_BIND EDBBASEOFF+71 /* Cdb daemon system : bind() error*/ +#define EDB_DS_LISTEN EDBBASEOFF+72 /* Cdb daemon system : listen() error*/ +#define EDB_DS_CONNECT EDBBASEOFF+73 /* Cdb daemon system : connect() error*/ +#define EDB_DS_SOCKET EDBBASEOFF+74 /* Cdb daemon system : socket() error*/ +#define EDB_DS_SOCKOPT EDBBASEOFF+75 /* Cdb daemon system : [set/get]sockopt() error*/ +#define EDB_D_RESHOST EDBBASEOFF+76 /* Cdb daemon : host res error */ +#define EDB_D_REQSIZE EDBBASEOFF+77 /* Cdb daemon : request too big */ + +#define EDB_C_EINVAL EDBBASEOFF+80 /* Cdb config : invalid value */ +#define EDB_C_ENOENT EDBBASEOFF+81 /* Cdb config : configuration error*/ +#define EDB_C_TOOMUCH EDBBASEOFF+82 /* Cdb config : conf. size rejected */ +#define EDB_CS_GETHOSTNAME EDBBASEOFF+83 /* Cdb config system : gethostname() error*/ + +#define EDB_NOMOREDB EDBBASEOFF+90 /* Cdb : nomoredb */ + +#define EDBMAXERR EDBBASEOFF+90 + +/* + *------------------------------------------------------------------------ + * MSG daemon errors + *------------------------------------------------------------------------ + */ +#define EMSMSGU2REP EMSBASEOFF+1 /* msg daemon unable to reply */ +#define EMSMSGSYERR EMSBASEOFF+2 /* msg daemon system error */ +#define EMSNOPERM EMSBASEOFF+3 /* Permission denied */ +#define EMSMAXERR EMSBASEOFF+3 /* Maximum error number of MSG */ +/* + * Backward compatibility + */ +#define SEMSGU2REP EMSMSGU2REP +#define SEMSGSYERR EMSMSGSYERR +#define SENOPERM EMSNOPERM + +/* + *------------------------------------------------------------------------ + * NS (Name Server) errors + *------------------------------------------------------------------------ + */ +#define ENSNACT ENSBASEOFF+1 /* Name server not active */ +#define ENSFILECHG ENSBASEOFF+2 /* File has been overwritten, request ignored */ +#define ENSNOSEG ENSBASEOFF+3 /* Segment had been deleted */ +#define ENSISLINK ENSBASEOFF+4 /* Is a link */ +#define ENSCLASSNOSEGS ENSBASEOFF+5 /* File class does not allow a copy on tape */ +#define ENSTOOMANYSEGS ENSBASEOFF+6 /* Too many copies on tape */ +#define ENSOVERWHENREP ENSBASEOFF+7 /* Cannot overwrite valid segment when replacing */ +#define ENHOSTNOTSET ENSBASEOFF+8 /* CNS HOST not set */ +#define ENSMAXERR ENSBASEOFF+8 + +/* + *------------------------------------------------------------------------ + * RFIO errors + *------------------------------------------------------------------------ + */ +#define ERFNORCODE ERFBASEOFF+1 /* RFIO communication error */ +#define ERFHOSTREFUSED ERFBASEOFF+2 /* RFIO rejected connect attempt*/ +#define ERFXHOST ERFBASEOFF+3 /* Cross-host link (rename()) */ +#define ERFPROTONOTSUP ERFBASEOFF+4 /* RFIO protocol not supported */ +#define ERFMAXERR ERFBASEOFF+4 /* Maximum error number of RFIO */ +/* + * Backward compatibility + */ +#define SENORCODE ERFNORCODE +#define SEHOSTREFUSED ERFHOSTREFUSED +#define SEXHOST ERFXHOST +#define SEPROTONOTSUP ERFPROTONOTSUP + +/* + *------------------------------------------------------------------------ + * RTCOPY errors + *------------------------------------------------------------------------ + */ +#define ERTTMSERR ERTBASEOFF+1 /* TMS call failed */ +#define ERTBLKSKPD ERTBASEOFF+2 /* Blocks were skipped in file */ +#define ERTTPE_LSZ ERTBASEOFF+3 /* Blocks skipped and file truncated */ +#define ERTMNYPARY ERTBASEOFF+4 /* Too many skipped blocks */ +#define ERTLIMBYSZ ERTBASEOFF+5 /* File limited by size */ +#define ERTUSINTR ERTBASEOFF+6 /* Request interrupted by user */ +#define ERTOPINTR ERTBASEOFF+7 /* Request interrupted by operator */ +#define ERTNOTCLIST ERTBASEOFF+8 /* Request list is not circular */ +#define ERTBADREQ ERTBASEOFF+9 /* Bad request structure */ +#define ERTMORETODO ERTBASEOFF+10 /* Request partially processed */ +#define ERTDBERR ERTBASEOFF+11 /* Catalogue DB error */ +#define ERTZEROSIZE ERTBASEOFF+12 /* Zero sized file */ +#define ERTWRONGSIZE ERTBASEOFF+13 /* Recalled file size incorrect */ +#define ERTWRONGFSEQ ERTBASEOFF+14 /* Inconsistent FSEQ in VMGR and Cns */ +#define ERTMAXERR ERTBASEOFF+14 + +/* + *------------------------------------------------------------------------ + * STAGE errors + *------------------------------------------------------------------------ + */ +#define ESTCLEARED ESTBASEOFF+1 /* aborted */ +#define ESTENOUGHF ESTBASEOFF+2 /* enough free space */ +#define ESTLNKNCR ESTBASEOFF+3 /* symbolic link not created */ +#define ESTLNKNSUP ESTBASEOFF+4 /* symbolic link not supported */ +#define ESTNACT ESTBASEOFF+5 /* Stager not active */ +#define ESTGROUP ESTBASEOFF+6 /* Your group is invalid */ +#define ESTGRPUSER ESTBASEOFF+7 /* No GRPUSER in configuration */ +#define ESTUSER ESTBASEOFF+8 /* Invalid user */ +#define ESTHSMHOST ESTBASEOFF+9 /* HSM HOST not specified */ +#define ESTTMSCHECK ESTBASEOFF+10 /* tmscheck error */ +#define ESTLINKNAME ESTBASEOFF+11 /* User link name processing error */ +#define ESTWRITABLE ESTBASEOFF+12 /* User path in a non-writable directory */ +#define ESTKILLED ESTBASEOFF+13 /* aborted by kill */ +#define ESTMEM ESTBASEOFF+14 /* request too long (api) */ +#define ESTCONF ESTBASEOFF+15 /* Stage configuration error */ +#define ESTSEGNOACC ESTBASEOFF+16 /* Unreadable file on tape (segments not all accessible) */ +#define ESTREPLFAILED ESTBASEOFF+17 /* File replication failed */ +#define ESTNOTAVAIL ESTBASEOFF+18 /* File is currently not available */ +#define ESTJOBKILLED ESTBASEOFF+19 /* Job killed by service administrator */ +#define ESTJOBTIMEDOUT ESTBASEOFF+20 /* Job timed out while waiting to be scheduled */ +#define ESTSCHEDERR ESTBASEOFF+21 /* Scheduler error */ +#define ESTSVCCLASSNOFS ESTBASEOFF+22 /* No filesystems available in service class */ +#define ESTNOSEGFOUND ESTBASEOFF+23 /* File has no copy on tape and no diskcopies are accessible */ +#define ESTTAPEOFFLINE ESTBASEOFF+24 /* File is on an offline tape */ +#define ESTREQCANCELED ESTBASEOFF+25 /* Request canceled while queuing */ +#define ESTTCNOTFOUND ESTBASEOFF+26 /* Tape-copy not found */ +#define ESTNOTAPEROUTE ESTBASEOFF+27 /* The file cannot be routed to tape */ +#define ESTMAXERR ESTBASEOFF+27 + +/* + *------------------------------------------------------------------------ + * SYSREQ errors + *------------------------------------------------------------------------ + */ +#define ESQTMSNOTACT ESQBASEOFF+1 /* TMS not active */ +#define ESQMAXERR ESQBASEOFF+1 /* Maximum error number of SYSREQ */ +/* + * Backward compatibility + */ +#define SETMSNOTACT ESQTMSNOTACT + +/* + *------------------------------------------------------------------------ + * TAPE errors + *------------------------------------------------------------------------ + */ +#define ETDNP ETBASEOFF+1 /* daemon not available */ +#define ETSYS ETBASEOFF+2 /* system error */ +#define ETPRM ETBASEOFF+3 /* bad parameter */ +#define ETRSV ETBASEOFF+4 /* reserv already issued */ +#define ETNDV ETBASEOFF+5 /* too many drives requested */ +#define ETIDG ETBASEOFF+6 /* invalid device group name */ +#define ETNRS ETBASEOFF+7 /* reserv not done */ +#define ETIDN ETBASEOFF+8 /* no drive with requested characteristics */ +#define ETLBL ETBASEOFF+9 /* bad label structure */ +#define ETFSQ ETBASEOFF+10 /* bad file sequence number */ +#define ETINTR ETBASEOFF+11 /* interrupted by user */ +#define ETEOV ETBASEOFF+12 /* EOV found in multivolume set */ +#define ETRLSP ETBASEOFF+13 /* release pending */ +#define ETBLANK ETBASEOFF+14 /* blank tape */ +#define ETCOMPA ETBASEOFF+15 /* compatibility problem */ +#define ETHWERR ETBASEOFF+16 /* device malfunction */ +#define ETPARIT ETBASEOFF+17 /* parity error */ +#define ETUNREC ETBASEOFF+18 /* unrecoverable media error */ +#define ETNOSNS ETBASEOFF+19 /* no sense */ +#define ETRSLT ETBASEOFF+20 /* reselect server */ +#define ETVBSY ETBASEOFF+21 /* volume busy or inaccessible */ +#define ETDCA ETBASEOFF+22 /* drive currently assigned */ +#define ETNRDY ETBASEOFF+23 /* drive not ready */ +#define ETABSENT ETBASEOFF+24 /* volume absent */ +#define ETARCH ETBASEOFF+25 /* volume archived */ +#define ETHELD ETBASEOFF+26 /* volume held or disabled */ +#define ETNXPD ETBASEOFF+27 /* file not expired */ +#define ETOPAB ETBASEOFF+28 /* operator cancel */ +#define ETVUNKN ETBASEOFF+29 /* volume unknown */ +#define ETWLBL ETBASEOFF+30 /* wrong label type */ +#define ETWPROT ETBASEOFF+31 /* cartridge write protected */ +#define ETWVSN ETBASEOFF+32 /* wrong vsn */ +#define ETBADMIR ETBASEOFF+33 /* Tape has a bad MIR */ +#define ETNETACCEPTINTR ETBASEOFF+34 /* castor::tape::net::acceptConnection interrupted */ +#define ETNOLBLINFO ETBASEOFF+35 /* Label information not found in memory */ +#define ETMLTDRVRSV ETBASEOFF+36 /* Multi-drive reservations are not supported */ +#define ETNOLBLINFOMEM ETBASEOFF+37 /* No memory available for label information */ +#define ETSESSIONERROR ETBASEOFF+38 /* Tape-session error */ +#define ETINVALIDTFSEQ ETBASEOFF+39 /* Invalid tape-file sequence-number */ +#define ETINVALIDTFSIZE ETBASEOFF+40 /* Invalid tape-file file-size */ +#define ETMOUNTFAILED ETBASEOFF+41 /* Failed to mount volume */ +#define ETDISMOUNTFAILED ETBASEOFF+42 /* Failed to dismount volume */ +#define ETQUERYVOLFAILED ETBASEOFF+43 /* Failed to query volume */ +#define ETFDISMOUNTFAILED ETBASEOFF+44 /* Failed to force dismount volume */ +#define ETDRVNOTREADYFORMNT ETBASEOFF+45 /* Drive not ready for mount */ +#define ETMAXERR ETBASEOFF+45 + +/* + *------------------------------------------------------------------------ + * VMGR (Volume Manager) errors + *------------------------------------------------------------------------ + */ +#define EVMGRNACT EVMBASEOFF+1 /* volume manager not active or service being drained */ +#define EVMGRNOHOST EVMBASEOFF+2 /* VMGR HOST not set */ +#define EVMMAXERR EVMBASEOFF+2 + +/* + *------------------------------------------------------------------------ + * UPV (User Privilege Validator) errors + *------------------------------------------------------------------------ + */ +#define ECUPVNACT EUPBASEOFF+1 /* User Privilege Validator not active or service being drained */ +#define EUPMAXERR EUPBASEOFF+1 + +/* + *------------------------------------------------------------------------ + * DNS errors - See netdb.h for details + *------------------------------------------------------------------------ + */ +#define EDNSHOSTNOTFOUND EDNSBASEOFF+1 /* Authoritative Answer Host not found. */ +#define EDNSTRYAGAIN EDNSBASEOFF+2 /* Non-Authoritative Host not found, or SERVERFAIL */ +#define EDNSNORECOVERY EDNSBASEOFF+3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP. */ +#define EDNSNODATA EDNSBASEOFF+4 /* Valid name, no data record of requested type. */ +#define EDNSNOADDRESS EDNSBASEOFF+5 /* No address, look for MX record. */ +#define EDNSMAXERR EDNSBASEOFF+6 + +/* + *------------------------------------------------------------------------ + * VDQM (Volume & Drive Queue Manager) errors + *------------------------------------------------------------------------ + */ +#define EVQSYERR EVQBASEOFF+1 /* Failed system call */ +#define EVQINCONSIST EVQBASEOFF+2 /* Internal DB inconsistency */ +#define EVQREPLICA EVQBASEOFF+3 /* DB replication failed */ +#define EVQNOVOL EVQBASEOFF+4 /* No volume request queued */ +#define EVQNODRV EVQBASEOFF+5 /* No free drive available */ +#define EVQNOSVOL EVQBASEOFF+6 /* Specified vol. req. not found */ +#define EVQNOSDRV EVQBASEOFF+7 /* Specified drv. req. not found */ +#define EVQALREADY EVQBASEOFF+8 /* Specified vol. req. already exists */ +#define EVQUNNOTUP EVQBASEOFF+9 /* Unit not up */ +#define EVQBADSTAT EVQBASEOFF+10 /* Bad unit status request */ +#define EVQBADID EVQBASEOFF+11 /* Incorrect vol.req or job ID */ +#define EVQBADJOBID EVQBASEOFF+12 /* Incorrect job ID */ +#define EVQNOTASS EVQBASEOFF+13 /* Unit not assigned */ +#define EVQBADVOLID EVQBASEOFF+14 /* Attempt to mount with wrong VOLID */ +#define EVQREQASS EVQBASEOFF+15 /* Attempt to delete an assigned req */ +#define EVQDGNINVL EVQBASEOFF+16 /* Vol. req. for non-existing DGN */ +#define EVQPIPEFULL EVQBASEOFF+17 /* Replication pipe is full */ +#define EVQHOLD EVQBASEOFF+18 /* Server is held */ +#define EVQEOQREACHED EVQBASEOFF+19 /* End of query reached */ + +#define EVQMAXERR EVQBASEOFF+19 + +/* + *------------------------------------------------------------------------ + * RMC (Remote SCSI media changer server) errors + *------------------------------------------------------------------------ + */ +#define ERMCNACT ERMBASEOFF+1 /* Remote SCSI media changer server not active or service being drained */ +#define ERMCRBTERR (ERMBASEOFF+2) /* Remote SCSI media changer error */ +#define ERMCUNREC ERMCRBTERR+1 /* Remote SCSI media changer unrec. error */ +#define ERMCSLOWR ERMCRBTERR+2 /* Remote SCSI media changer error (slow retry) */ +#define ERMCFASTR ERMCRBTERR+3 /* Remote SCSI media changer error (fast retry) */ +#define ERMCDFORCE ERMCRBTERR+4 /* Remote SCSI media changer error (demount force) */ +#define ERMCDDOWN ERMCRBTERR+5 /* Remote SCSI media changer error (drive down) */ +#define ERMCOMSGN ERMCRBTERR+6 /* Remote SCSI media changer error (ops message) */ +#define ERMCOMSGS ERMCRBTERR+7 /* Remote SCSI media changer error (ops message + retry) */ +#define ERMCOMSGR ERMCRBTERR+8 /* Remote SCSI media changer error (ops message + wait) */ +#define ERMCUNLOAD ERMCRBTERR+9 /* Remote SCSI media changer error (unload + demount) */ +#define ERMMAXERR ERMBASEOFF+11 + +/* + *------------------------------------------------------------------------ + * MONITORING ERRORS + *------------------------------------------------------------------------ + */ + +#define EMON_SYSTEM EMONBASEOFF+1 /* When a system error causes the monitoring to stop */ +#define EMON_NO_HOST EMONBASEOFF+2 /* No monitoring host defined in the configuration */ +#define EMON_NO_PORT EMONBASEOFF+3 /* No monitoring port defined in the configuration */ +#define EMON_NO_CLIENTPORT EMONBASEOFF+4 /* No port for client requests defined in the configuration */ + +#define EMONMAXERR EMONBASEOFF+4 + +/* + *------------------------------------------------------------------------ + * SECURITY ERRORS + *------------------------------------------------------------------------ + */ +#define ESEC_SYSTEM ESECBASEOFF + 1 /* System error in the security package */ +#define ESEC_BAD_CREDENTIALS ESECBASEOFF + 2 /* Bad credentials */ +#define ESEC_NO_CONTEXT ESECBASEOFF + 3 /* Could not establish context */ +#define ESEC_BAD_MAGIC ESECBASEOFF + 4 /* Bad magic number */ +#define ESEC_NO_USER ESECBASEOFF + 5 /* Could not map username to uid/gid */ +#define ESEC_NO_PRINC ESECBASEOFF + 6 /* Could not map principal to username */ +#define ESEC_NO_SECMECH ESECBASEOFF + 7 /* Could not load security mechanism */ +#define ESEC_CTX_NOT_INITIALIZED ESECBASEOFF + 8 /* Context not initialized */ +#define ESEC_PROTNOTSUPP ESECBASEOFF + 9 /* Security protocol not supported */ +#define ESEC_NO_SVC_NAME ESECBASEOFF + 10 /* Service name not set */ +#define ESEC_NO_SVC_TYPE ESECBASEOFF + 11 /* Service type not set */ +#define ESEC_NO_SECPROT ESECBASEOFF + 12 /* Could not lookup security protocol */ +#define ESEC_BAD_CSEC_VERSION ESECBASEOFF + 13 /* Csec incompatability */ +#define ESEC_BAD_PEER_RESP ESECBASEOFF + 14 /* Unexpected response from peer */ +#define ESECMAXERR ESECBASEOFF + 14 + +extern int serrno; diff --git a/XRootdSSiRmcd/smc_constants.h b/XRootdSSiRmcd/smc_constants.h new file mode 100644 index 0000000000000000000000000000000000000000..86cdf71600c28269ad465867a4d3adbd5b914a0f --- /dev/null +++ b/XRootdSSiRmcd/smc_constants.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 1998-2002 by CERN/IT/PDP/DM + * All rights reserved + */ + +#pragma once + + /* error messages */ + +#define SR001 "SR001 - drive ordinal must be a non negative integer\n" +#define SR002 "SR002 - option -%c and -%c are mutually exclusive\n" +#define SR003 "SR003 - invalid query type %c\n" +#define SR004 "SR004 - vid %s must be at most 6 characters long\n" +#define SR005 "SR005 - loader must be specified\n" +#define SR006 "SR006 - drive ordinal is mandatory for demount operations\n" +#define SR007 "SR007 - drive ordinal and vid are mandatory for mount operations\n" +#define SR008 "SR008 - invalid device ordinal (must be < %d)\n" +#define SR009 "SR009 - vid mismatch: %s on request, %s on drive\n" +#define SR010 "SR010 - number of elements must be a positive integer\n" +#define SR011 "SR011 - vid is mandatory for export operations\n" +#define SR012 "SR012 - cannot allocate enough memory\n" +#define SR013 "SR013 - export slots are full\n" +#define SR014 "SR014 - slot ordinal must be a non negative integer\n" +#define SR015 "SR015 - storage cells are full\n" +#define SR016 "SR016 - invalid slot address (must be < %d)\n" +#define SR017 "SR017 - %s %s failed : %s\n" +#define SR018 "SR018 - %s of %s on drive %d failed : %s\n" +#define SR019 "SR019 - %s : %s error : %s\n" +#define SR020 "SR020 - %s failed : %s\n" +#define SR021 "SR021 - specify source slot and target slot\n" + diff --git a/XRootdSSiRmcd/smc_struct.h b/XRootdSSiRmcd/smc_struct.h new file mode 100644 index 0000000000000000000000000000000000000000..4d518908a0734e2a175a2a827fd2e92742e25117 --- /dev/null +++ b/XRootdSSiRmcd/smc_struct.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 1998-2002 by CERN/IT/PDP/DM + * All rights reserved + */ + +#pragma once + +#include "Castor_limits.h" + +struct robot_info { + char inquiry[32]; + int transport_start; + int transport_count; + int slot_start; + int slot_count; + int port_start; + int port_count; + int device_start; + int device_count; +}; + +struct extended_robot_info { + int smc_fd; + char smc_ldr[CA_MAXRBTNAMELEN+1]; + struct robot_info robot_info; +}; + +struct smc_element_info { + int element_address; + int element_type; + int state; + unsigned char asc; + unsigned char ascq; + int flags; + int source_address; + char name[9]; +}; + +struct smc_status { + unsigned char asc; + unsigned char ascq; + int save_errno; + int rc; /* return code from send_scsi_cmd */ + unsigned char sensekey; + int skvalid; /* sense key is valid */ +}; + diff --git a/cta.spec.in b/cta.spec.in index efddfa5141a22c4a8febca2a2a8b44dab7cbd1f2..c6c39d360e2f0dc846fa95c3930be1ed8c261ee7 100644 --- a/cta.spec.in +++ b/cta.spec.in @@ -131,6 +131,51 @@ The tape server daemon %systemd_postun cta-taped.service %systemdDaemonReload +%package -n cta-xrmcd +Summary: CERN Tape Archive: Xrootd plugin +Group: Application/CTA +Requires: logrotate +Requires: cta-common = %{version}-%{release} +Requires: cta-lib = %{version}-%{release} +Requires: xrootd-server +%description -n cta-xrmcd +CERN Tape Archive: +The xroot plugin +%files -n cta-xrmcd +%defattr(0755,root,root) +%{_libdir}/libXrdSsiRmcd.so* +%attr(0644,root,root) %config(noreplace) /etc/logrotate.d/cta-xrmcd +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cta/cta-xrmcd.conf +%attr(0644,cta,tape) /etc/systemd/system/cta-xrmcd.service +%attr(0644,root,root) %config(noreplace) /etc/sysconfig/cta-xrmcd + +#xrmcd installs libraries so we need ldconfig. +%post -n cta-xrmcd +/sbin/ldconfig +%systemd_post cta-xrmcd.service +%systemdDaemonReload + +%preun -n cta-xrmcd +%systemd_preun cta-xrmcd.service + +%postun -n cta-xrmcd +/sbin/ldconfig +%systemd_post cta-xrmcd.service +%systemdDaemonReload + +%package -n cta-xsmc +Summary: CERN Tape Archive: command line interface +Group: Application/CTA +Requires: cta-lib = %{version}-%{release} +%description -n cta-xsmc +CERN Tape Archive: +The xroot plugin +%files -n cta-xsmc +%defattr(-,root,root) +%attr(0755,root,root) %{_bindir}/cta-xsmc +%attr(0644,root,root) %doc /usr/share/man/man1/cta-xsmc-mount.1cta.gz +%attr(0644,root,root) %doc /usr/share/man/man1/cta-xsmc-dismount.1cta.gz + %package -n cta-frontend Summary: CERN Tape Archive: Xrootd plugin Group: Application/CTA